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
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;