Let us consider this module for FPGA implementation:
module ticker(input clk, input step_toggle, output [7:0] value);
reg step_toggle2 = 0;
reg step_toggle3 = 0;
reg neutral = 0;
reg [7:0] counter = 0;
always @(posedge clk) begin
step_toggle2 <= step_toggle;
step_toggle3 <= step_toggle2;
if (step_toggle3 != neutral) begin
neutral <= step_toggle3;
counter <= (counter + 1);
end
end
assign value = counter;
endmodule
If we hook up clk
to the output of a PLL, it may output more than
the supported frequency while it stabilizes. We can add a reset signal
thusly:
module ticker(input clk, input reset, input step_toggle, output [7:0] value);
reg step_toggle2 = 0;
reg step_toggle3 = 0;
reg neutral = 0;
reg [7:0] counter = 0;
always @(posedge clk) begin
if (reset) begin
neutral <= 0;
counter <= 0;
end else begin
step_toggle2 <= step_toggle;
step_toggle3 <= step_toggle2;
if (step_toggle3 != neutral) begin
neutral <= step_toggle3;
counter <= (counter + 1);
end
end
end
assign value = counter;
endmodule
I believe it's correct to not reset the synchronizer stages as that would introduces multiplexers that may hinder the elimination of metastability.
While it looks like neutral
, counter
, and value
will be stuck at
0, I suspect this may not be the case. The reset logic can be thought
of as a 2:1 mux at the end of the normal logic. Thus if step_toggle3
transitions 0 -> 1 or 1 -> 0, it can cause glitches. If the clock
speed exceeds the time required for the logic to settle, erroneous
values might potentially be registered, and in the case of counter
propagate to the output on the next cycle. Of course things always get
restored on the next cycle but it's still an imperfection.
Now, let's add the reset in a different way:
module ticker(input clk, input reset, input step_toggle, output [7:0] value);
reg step_toggle2 = 0;
reg step_toggle3 = 0;
reg step_toggle4 = 0;
reg neutral = 0;
reg [7:0] counter = 0;
always @(posedge clk) begin
step_toggle2 <= step_toggle;
step_toggle3 <= step_toggle2;
step_toggle4 <= (reset ? 0 : step_toggle3);
if (step_toggle4 != neutral) begin
neutral <= step_toggle4;
counter <= (counter + 1);
end
end
assign value = counter;
endmodule
This adds one cycle of delay, but also improves the timing achievable
as the reset logic is no longer on the main paths. As long as the PLL
period remains longer than the time required to propagate from a
register through a single multiplexer and then to another register,
it will work. There is no longer the possibility of glitches as the
step_toggle4
input is fixed during PLL warmup, and the logic solves
statically; the clock rate for neutral
and counter
becomes
irrelevant. Importantly, for this work the logic can't contain a
counter that always ticks or anything similar; but this is not a big
limitation as most times there is a state machine with an idle state
that requires input to leave that state.
Regarding the PLL maximum speed, I've written a simple test program for ECP5 that repeatedly starts and stops a PLL, and continuously checks to see if the multiplexing stage worked. It has run for a few hours now without identifying faults. Also, the timing report for a single multiplexer seems to exceed the "VCO rate" parameter. I don't know if the PLL output frequency can ever exceed the "VCO rate" or not, elucidation of this point would be appreciated.
EDIT: I've become aware that many FPGAs, including the ECP5, have dedicated synchronous reset logic on the DFFs. This does not really change the nature of the question as it seems equivalent regardless whether the logic is in a LUT or in the DFF.
Currently I strongly prefer the second method, but, before using it, I thought I'd ask to see if anyone can see any flaw in it that I may have overlooked.