Search code examples
verilogsystem-verilogclockrace-conditionedaplayground

Verilog race with clock divider using flops


I made a basic example on eda playground of the issue I got. Let s say I have two clocks 1x and 2x. 2x is divided from 1x using flop divider.

I have two registers a and b. a is clocked on 1x, b is clocked in 2x.

b is sampling value of a.

When we have rising edge of 1x and 2x clocks, b is not taking the expected value of a but it s taking the next cycle value.

This is because of this clock divider scheme, if we make division using icgs and en it works fine. But is there a way to make it work using this clock divider scheme with flops ?

EDA playground link : https://www.edaplayground.com/x/map#

module race_test;

  logic clk1x = 0;
  logic clk2x = 0;

  always
    #5ns clk1x = !clk1x;

  int a, b;


  always @(posedge clk1x) begin
    a <= a+1;
    clk2x <= !clk2x;
  end

  // Problem here is that b will sample postpone value of a
  // clk2x is not triggering at the same time than clk1x but a bit later
  // This can be workaround by putting blocking assignment for clock divider
  always @(posedge clk2x) begin
    b <= a;
  end

  initial begin
    $dumpfile("test.vcd");
    $dumpvars;
    #1us
    $stop;
  end
endmodule

Solution

  • Digital clock dividers present problems with both simulation and physical timing.

    Verilog's non-blocking assignment operator assumes that everyone reading and writing the same variables are synchronized to the same clock event. By using an NBA writing to clk2x, you have shifted the reading of a to another delta time*, and as you discovered, a has already been updated.

    In real hardware, there are considerable propagation delays that usually avoid this situation. However, you are using the same D-flop to assign to clk2x, so there will be propagation delays there as well. You last always block now represents a clock domain crossing issue. So depending on the skews between the two clocks, you could still have a race condition.

    One way of correcting this is using a clock generator module with an even higher frequency clock

    always #2.5ns clk = !clk;
    
    always @(posedge clk) begin
           clk1x <= !clk1x;
           if (clk1x == 1)
             clk2x = !clk2x;