Hi
I had to work a lot to get clock synchronization working in a CPLD, so I thought I should share my experience.
What I have been doing is to use an external clock pulse and synchronizing this to an internal clock with a counter.
During the external clock pulse (an 1MHz clock), the internal clock (running at 55MHz) is used to trigger an increase to a counter. This counter increases by 1 for each internal clock pulse so that it counts to around 54 before being reset back to 0 (by the external clock having a negative edge).
Originally I thought this was going to be an easy exercise, but experience has proven me blue eyed (which I am) and naive.
The original code looked something like this:
Code:
always @(posedge fpga_clk)
begin
counting <= counting + 8'd1;
if (counting==8'd56) // Overflow, so return to zero
begin
counting <= 8'd0;
end
if (count_reset!=count_check)
begin
counting <= 8'd0;
end
count_check=count_reset;
end
always @(negedge clk)
begin
count_reset=~count_reset;
end
(some init code and variable declaration has been removed)
This code seems to work, but it doesn't. There are several problems with running this code, and it is not that I originally used blocking statements in the always block (which many also say that can generate problems in its own).
The problem with this code is that it is unstable. In 99.9% of the time, the "counting" variable goes back to zero during the clk negative edge (that generates a reset), but once in a while it will either miss the reset condition or it will reset to some arbitrary value (!). The reason for this is that the reset condition (count_reset=~count_reset) can happen just before the reset clearing (count_check=count_reset) so that we miss the reset altogether.
Even stranger is that this situation can endure even if one put the reset clearing (count_check=count_reset) inside the last IF statement.
Anyway, the solution to this involves using a two-stage reset. E.g. start with a reset (count_reset=~count_reset), then propagate that into a binary reset counter that counts two stages:
Code:
always @(posedge fpga_clk)
begin
if (count_reset==2'b10 || count_reset==2'b01)
counting <= 8'b00000000; // This is held for two cycles to ensure zeroing
else counting <= counting + 8'b00000001;
count_reset[1]<=count_reset[0];
end
always @(negedge fpga_clk)
begin
if (old_count_reset!=init_count_reset)
begin
old_count_reset<=init_count_reset;
count_reset[0]<=1'b1; // Start counter reset (two cycles)
end
else
count_reset[0]<=1'b0;
end
always @(negedge clk)
begin
init_count_reset<=~init_count_reset;
end
count_reset is here a two-bit value.
Now, this doesn't work either. And the reason for that was the external 1MHz clk which transits from 1 to 0 (and back again) does so in 20+ ns. That is apparently enough to confuse the (negedge clk) statement (probably since the transition can look like a high impedance state without being 0 or 1.. at least that is my theory).
The solution to this was again to work with a two-stage process to ensure that two stable values had been reached. So I changed the "always @(negedge clk)" into something else:
Code:
always @(posedge fpga_clk)
begin
if (oclk==2'b10) // Look for clk2 edge to zero (PS: only way to ensure proper detection
begin
init_count_reset <= ~init_count_reset; // Reset counter
end
oclk<={oclk[0],clk};
end
In which oclk is a two-bit variable.
At last this code works. E.g its stable and generates a counting variable that goes from 0 to 54 and back to 0. It does so 100% of the time without missing anything.
Now, if someone else here has experienced the same troubles with external clocks and counters, it would be nice to hear their experience.