Author Topic: Verilog 101: Clock Domain Crossing?  (Read 2944 times)

0 Members and 2 Guests are viewing this topic.

Offline NivagSwerdnaTopic starter

  • Super Contributor
  • ***
  • Posts: 2507
  • Country: gb
Verilog 101: Clock Domain Crossing?
« on: May 30, 2022, 06:42:18 pm »
As you know... I'm working on my first FPGA project...

Code: [Select]
reg seen_start = 0;

always @ (negedge sig_start)
begin
seen_start = 1;
end

...

always @ (posedge oclk)
begin
case(state)
...
STATE_STOPPING:
begin
begin
seen_start = 0;
state = STATE_IDLE;
end
end
endcase
end

I have a number of external inputs notably start and stop which are not synchronous to any clock... they just happen... but within my FPGA I have a state machine that needs to process stuff during the period that it is 'started' or 'stopped'

A start is encountered when there is a negative edge of the start input.  I thought I could capture the start using a always @ (negedge sig_start) block and set a reg which I later reset in the state machine (ready for next time). 

I am not allowed to synthesise this as it says...

ERROR - blinky3/source/top_level.v(75): net seen_start is constantly driven from multiple places at instance seen_start_2, on port q. VDB-1000

I can see its point but not sure how to resolve it.

Pointers appreciated

 

Offline agehall

  • Frequent Contributor
  • **
  • Posts: 389
  • Country: se
Re: Verilog 101: Clock Domain Crossing?
« Reply #1 on: May 30, 2022, 06:58:47 pm »
You can’t assign seen_start in both always blocks.

You should be able to write a simple FSM that handles this without too much trouble.

Edit: To be a bit more helpful - try thinking about the possible states, maybe draw a state diagram. You basically have an IDLE, START, WAITING_FOR_STOP and a STOP state. Not much needs to happen in the STOP state except go back to IDLE, but for completion’s sake, I like to do it that way even if it can be optimized and done with fewer states.
« Last Edit: May 30, 2022, 07:01:10 pm by agehall »
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4137
  • Country: nl
Re: Verilog 101: Clock Domain Crossing?
« Reply #2 on: May 30, 2022, 07:48:10 pm »
A better approach is to sample the start and stop signals with a master clock. Run your entire system of one clock and only use the posedge of that clock in your always blocks.

To make sure the signals are stable sample them with a shift register

Code: [Select]
reg [1:0] start_signal = 2'b0;
reg [1:0] stop_signal = 2'b0;

always @(posedge clk)
begin
  start_signal[0] <= sig_start;
  start_signal[1] <= start_signal[0];

  stop_signal[0] <= sig_stop;
  stop_signal[1] <= stop_signal[0];

  if (start_signal[1] == 1)
    begin
       if (stop_signal[1] == 0)
        seen_start <= 1;
      else
        seen_start <= 0;
    end
end

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2812
  • Country: nz
Re: Verilog 101: Clock Domain Crossing?
« Reply #3 on: May 30, 2022, 09:49:26 pm »
TBH, doing simple input properly in an FPGA is a bit of a P.I.T.A. Synchronizing signals to a clock properly requires the use of either vendor-specific macros, vendor-specific synthesis attributes, or vendor-specific constraints. All pretty advanced topics and non-portable.

My not-as-correct-but-portable version is three signals:
     X - the raw input
     X_unsafe - the first FF in the synchronizer.
     X_synced - the second FF in the synchronizer.

Code: [Select]
always @(posedge clk)
begin
  // Replace the assignments below with vendor CDC macros if highest reliability required
  btn_start_synced <= btn_start_unsafe;
  btn_start_unsafe <= btn_start;

  btn_stop_synced <= btn_stop_unsafe;
  btn_stop_unsafe <= btn_stop;
end

I put all the synchronizers in their own process, and the just use the "synced" signals elsewhere.

Following a consistent pattern makes it quite easy to have some automated scripted testing to validate that you or coworkers haven't slipped up... The more you can code review and debug a design without lifting a finger the better!

And then how to properly debouncing switches and buttons is then another level of contention. How long to debounce for? Do you want to react instantly to the input then inhibit changes for a short while, or delay the signal until it has debounced? Do you want to filter out any noise/glitches?...

Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: NivagSwerdna

Offline NivagSwerdnaTopic starter

  • Super Contributor
  • ***
  • Posts: 2507
  • Country: gb
Re: Verilog 101: Clock Domain Crossing?
« Reply #4 on: May 31, 2022, 11:07:42 am »
I am really only interested in detecting edges so my state will move if I have seen a +ve transition on START... The approaches above seem to imply a single clock... If I use that clock to check values then that implies my sample rate in START is slower compared to the state machine clock?

I stumbled across SB_IO which seems to a be vendor specific thing to do with I/O! It seems to refer to 'registered' input .. I am not sure what that means and/or if that helps!

 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2812
  • Country: nz
Re: Verilog 101: Clock Domain Crossing?
« Reply #5 on: May 31, 2022, 11:40:01 am »
I am really only interested in detecting edges so my state will move if I have seen a +ve transition on START... The approaches above seem to imply a single clock... If I use that clock to check values then that implies my sample rate in START is slower compared to the state machine clock?

I stumbled across SB_IO which seems to a be vendor specific thing to do with I/O! It seems to refer to 'registered' input .. I am not sure what that means and/or if that helps!
A single, fast (well, compared to people pushing buttons) clock domain is definitely the way to go.  :)

What do you want to happen when somebody holds down both the START and STOP buttons? If you are not mindful it will run through all the states very quickly... you may want to give STOP priority over START.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4137
  • Country: nl
Re: Verilog 101: Clock Domain Crossing?
« Reply #6 on: May 31, 2022, 11:46:01 am »
The rate of the main clock determines how fast a signal change is detected. With the shift register setup, shown in previous posts, it takes two clock cycles for the signal to change. So to make it work the sample clock has to be higher then what you like the speed of your state machine to be.

To match your state machine to the higher clock you can use a counter that has to timeout before switching a state. If possible avoid using more then one clock and make a synchronous design.

I would say read up on how the internals of an FPGA work. Learn about the different blocks and signal routing first.

The SB_IO as registered input means that the flip-flop that sits in the IO block is used to latch and buffer the external signal. For what I have seen of it most of the FPGA tools can do the same with the sample code provided in the previous posts. Use the IO block flip-flop as the first register of the shift register.

Offline NivagSwerdnaTopic starter

  • Super Contributor
  • ***
  • Posts: 2507
  • Country: gb
Re: Verilog 101: Clock Domain Crossing?
« Reply #7 on: May 31, 2022, 12:03:01 pm »
Btw... Start and stop are not buttons... These are external triggers at around 100kHz
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4263
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: Verilog 101: Clock Domain Crossing?
« Reply #8 on: May 31, 2022, 12:07:14 pm »
The fundamental issue here is that you don't know the timing relationship between your start / stop signals and the master clock that drives your state machine. This is very common, and needs handling correctly. (The same is also true of every other signal that's an input to the FPGA and that's not properly synchronised to the master clock that's driving your logic, of course).

Every logic gate, whether discrete or built into an FPGA, has setup and hold times that must be respected relative to the clock. If they're met, the gate will work reliably. If they're not, the behaviour of the gate will be unpredictable. Strange things can happen, like the output changing briefly to one state before settling some time later to another even without a further clock pulse arriving. This is termed 'metastability' and it's guaranteed to cause flaky behaviour in your FPGA.

To handle this effect - which is fundamental to the device physics, and not something that can be simply 'designed out' by the FPGA manufacturer - we use a thing called a synchroniser chain. This consists of one or more clocked D-type latches, connected in series (like a shift register). The non-synchronous input (your start or stop signal) connects to the first D input, and we know that the Q output may indeed go metastable, which would risk causing complex logic to misbehave if it were used directly in multiple places.

(As an aside - imagine, for example, the case where the metastable signal is used in two places, one physically near the latch and one on the other side of the die. If it changes state at an unexpected time, the difference in propagation delay could easily mean that one subsequent logic input sees it as a '0' while the other sees a '1', with hilarious consequences).

Instead, we ensure that the potentially metastable output feeds exactly one input (the next D-type in the chain), so this can't happen. We also work on the assumption - and it is just an assumption - that even if it goes metastable, it'll have settled to either a 0 or a 1 in good time before the next active clock edge. The output of the second D-type should, therefore, be reliable.

There is, of course, a couple of clocks' worth of latency. This is inevitable. You'll need to find a way to deal with this, if the delay matters at all.

I speak VHDL, not Verilog, so I'd implement this as something along the following lines:
Code: [Select]
IF rising_edge (fclk) THEN

start_meta <= p_start_signal;
start_reliable <= start_meta;

stop_meta <= p_stop_signal;
stop_reliable <= stop_meta;

IF start_reliable = '1' THEN
start_seen <= '1';
END IF;

IF stop_reliable = '1' THEN
start_seen <= '0';
END IF;

END IF;
 
The following users thanked this post: NivagSwerdna

Offline asmi

  • Super Contributor
  • ***
  • Posts: 2771
  • Country: ca
Re: Verilog 101: Clock Domain Crossing?
« Reply #9 on: May 31, 2022, 03:07:33 pm »
I want to add that Xilins requires these syncronization latches to be marked with "async_reg" attribute so that the placer will place them as close to each other as possible (ideally within a single slice) to minimize propagation delay and so give the first latch the maximum amount of time to settle.
 
The following users thanked this post: NivagSwerdna

Online fourfathom

  • Super Contributor
  • ***
  • Posts: 1936
  • Country: us
Re: Verilog 101: Clock Domain Crossing?
« Reply #10 on: May 31, 2022, 04:05:24 pm »
Here is a synchronizer module (Verilog) that I use in one design:

clk is 100 - 300 MHz
d is a slower asynchronous signal, needs to transition at less than clk/2
q is just the input re-synchronized to the "clk" domain (and delayed)
rising and falling are one "clk" period wide, used for input edge detection.

synch3 has three stages of retiming registers, to minimize metastability-created setup uncertainty in the "rising" and "falling" outputs.  I've run literally trillions of shifting asynch inputs through the synch3 design while temperature cycling the FPGA without a single glitch (and a glitch is non-catastrophic in this particular design).

I don't have any reset coding, as the FPGA inits this natively.  Even if it didn't, any unknown states would be flushed out after three clocks.


module synch3(
   input clk,
   input d,
   output q,
   output rising,
   output falling
   );
   
   reg [2:0] sr;
   assign rising = (sr[2:1] == 2'b01);
   assign falling = (sr[2:1] == 2'b10);
   assign q = sr[2];
   always @(posedge clk) sr <= {sr[1:0], d};

endmodule

We'll search out every place a sick, twisted, solitary misfit might run to! -- I'll start with Radio Shack.
 
The following users thanked this post: NivagSwerdna

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 3767
  • Country: ua
Re: Verilog 101: Clock Domain Crossing?
« Reply #11 on: May 31, 2022, 11:37:10 pm »
I can see its point but not sure how to resolve it.

just remove seen_start = 0 from the second always block.

I'm not familiar with clock synchronization issues, but I caught some issues and after fighting with them can provide you some suggestions.

Don't try to change signal from two different clock domains, it may lead to compilation error or unpredictable behavior. As I understand, posedge and negedge are different clock domains, despite the fact they are synchronized. There is some phase shift between them and with delays in the gates it can lead to unstable behavior and metastability issues.

If you want to exchange events between two clock domain, you can use two signals, each should be changed from it's own clock only. You can use it to build some Finite State Machine to transfer event and get response between two clock domains.

You can read signal from another clock domain, but this is not safe due to metastability issues. So, before read signal from another clock domain you're needs to synchronize it by passing it through shift register clocked from destination clock domain. I read from some sources that 2 shift cycles is enough to avoid metastability. After that you can safely read signal from shift register output in the destination clock domain.

Here is a module that I use to synchronize signal with destination clock domain to avoid metastability:
Code: [Select]
/*
 * Synchronizes an asyncronous signal to a given clock by using a pipeline of
 * two registers.
 */
module sync_signal #(
    parameter WIDTH=1, // width of the input and output signals
    parameter N=2 // depth of synchronizer
)(
    input wire clk,
    input wire [WIDTH-1:0] in,
    output wire [WIDTH-1:0] out
);

reg [WIDTH-1:0] sync_reg[N-1:0];

/*
 * The synchronized output is the last register in the pipeline.
 */
assign out = sync_reg[N-1];

integer k;

always @(posedge clk) begin
    sync_reg[0] <= in;
    for (k = 1; k < N; k = k + 1) begin
        sync_reg[k] <= sync_reg[k-1];
    end
end

endmodule


And here is usage example:
Code: [Select]
wire uart_rxd_synced;
wire button_synced;

sync_signal #(.WIDTH(2), .N(3)) sync_signal_inst (
    .clk(clk),
    .in({uart_rxd, button}),
    .out({uart_rxd_synced, button_synced})
);

where uart_rxd and button are external signals (not synchronized with clk clock).
And uart_rxd_synced, button_synced are signals synchronized with clock clk.
« Last Edit: June 01, 2022, 12:04:44 am by radiolistener »
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2812
  • Country: nz
Re: Verilog 101: Clock Domain Crossing?
« Reply #12 on: June 01, 2022, 12:20:18 am »
If you are on Xilinx, for a full bells-and-whistles synchronizer you can use:

Code: [Select]
   xpm_cdc_single #(
      .DEST_SYNC_FF(2),   // DECIMAL; range: 2-10
      .INIT_SYNC_FF(0),   // DECIMAL; 0=disable simulation init values, 1=enable simulation init values
      .SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
      .SRC_INPUT_REG(0)   // DECIMAL; 0=do not register input, 1=register input
   )
   xpm_cdc_single_inst (
      .src_clk(src_clk),
      .src_in(src_in),
      .dest_clk(dest_clk),
      .dest_out(dest_out)
   );

There is little reason to use anything else or roll your own, IMO.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: agehall

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4263
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: Verilog 101: Clock Domain Crossing?
« Reply #13 on: June 01, 2022, 08:58:55 am »
Don't try to change signal from two different clock domains, it may lead to compilation error or unpredictable behavior. As I understand, posedge and negedge are different clock domains, despite the fact they are synchronized. There is some phase shift between them and with delays in the gates it can lead to unstable behavior and metastability issues.

This should be taken care of by the synthesis tools. It's perfectly valid to change a signal on one edge of a clock, and read it on the other edge of the same clock.

If the clock is fast then the design may fail timing analysis, but that's simply because you only have half the time (or less) for signals to propagate around the device. You also need to ensure that the clock symmetry (the proportion of time for which it's high vs low), along with the uncertainty in this figure, are correctly set in your .SDC file.

Getting this right, and knowing it's right, is hard. For that reason alone, good practice is to always use the same edge wherever you can.

Offline NivagSwerdnaTopic starter

  • Super Contributor
  • ***
  • Posts: 2507
  • Country: gb
Re: Verilog 101: Clock Domain Crossing?
« Reply #14 on: June 01, 2022, 02:58:31 pm »
Due to my real job I haven't had much time to look at this yet but I have come to the conclusion that I am going to operate two clock domains. The start, stop and data signals have an externally driven clock of their own (sort of) and then the internals of my application can be synchronously clocked at a different rate. There is a data transfer between the domains after stop is encountered.

I have been looking at some Cummings papers which seem pretty useful.

I imagine this is a very common scenario... For instance if you are connecting to say an ethernet PHY then you would want that part clocked by the PHY but the internals clocked separately?
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15070
  • Country: fr
Re: Verilog 101: Clock Domain Crossing?
« Reply #15 on: June 01, 2022, 05:08:54 pm »
Yes this is pretty common.
As you saw, synchronizing is a matter of cascading flip-flops to prevent metastability issues. Of course there can be subtleties.

One very straigthforward way of crossing clock domains is to use dual-clock, dual-port FIFOs, which are available on all FPGAs that I know of as basic primitives/IPs. Those FIFOs handle the dirty work for you.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf