Viktorinox
Viktorinox

Reputation: 140

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

Upvotes: 1

Views: 1773

Answers (2)

Vito
Vito

Reputation: 1

Of course you have solved this problem, but I think there is a better way. The book says one can use blocking assignment to avoid race. But blocking assignemnt causes errors in synopsys lint check. So, one way to avoid race problem without lint error is to use dummy logic, like this

wire [31:0] a_logic;
wire dummy_sel;
assign dummy_sel = 1'b0;
assign a_logic = dummy_sel ? ~a : a;
always @(posedge clk2x) begin
  b <= a_logic;
end

Upvotes: 0

dave_59
dave_59

Reputation: 42698

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;

Upvotes: 1

Related Questions