0
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. \$\endgroup\$
    – Community Bot
    Commented Dec 11, 2023 at 6:26
  • \$\begingroup\$ @Community Pretty sure Community can't hear me but I thought I'd add that I moved the "question" paragraph to the end of the question for emphasis. \$\endgroup\$
    – alfred486
    Commented Dec 11, 2023 at 18:26

1 Answer 1

1
\$\begingroup\$

In most PLLs, the output or the connection from the VCO to the output divider is gated with the PLL's locked signal, and even if it wasn't, the frequency would be roughly correct, so PLL startup is not normally a source of timing violations.

The neutral signal could just be called step_toggle4 -- that's just one extra stage of synchronization, but you can also edge-detect between the second and third stage by comparing step_toggle2 and step_toggle3.

The dedicated reset logic in DFFs is what you want -- implementing this kind of logic in LUTs means creating a latch, and there are worse issues than a bit of metastability with that. A reset should clear state, and LUTs should not have state.

Note that the reset signal needs to be deasserted synchronously to clk.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.