09-19-2019 10:36 PM - edited 09-22-2019 05:31 PM
I noticed something unusual when simulating a design that uses a Xilinx PLL (simulated with unisims). Here's a minimal example that demonstrates the effect:
`default_nettype none `include "PLLE2_BASE.v" `include "PLLE2_ADV.v" `include "glbl.v" `timescale 1ns/1ps module top; reg base_clk = 0; reg fst_clk = 0; wire clk_fb; wire pll_lock; wire clk_100mhz; PLLE2_BASE #( .CLKFBOUT_MULT (24), .DIVCLK_DIVIDE (1), .CLKOUT0_DIVIDE (8), .CLKIN1_PERIOD (30) ) PLLE2_BASE_120mhz ( .CLKOUT0 (clk_100mhz), .LOCKED (pll_lock), .CLKIN1 (base_clk), .RST (1'b0), .CLKFBOUT (clk_fb), .CLKFBIN (clk_fb) ); reg cond = 1'b0; reg some_reg = 1'b0; reg other_reg = 1'b0; always #15 base_clk = !base_clk; always #5 fst_clk = !fst_clk; initial begin $dumpfile("top.vcd"); $dumpvars(0, top); #10000 $finish; end always @(posedge base_clk) begin if (pll_lock) cond <= 1'b1; end always @(posedge fst_clk) begin if (pll_lock) if (cond) some_reg <= 1'b1; end always @(posedge clk_100mhz) begin if (cond) other_reg <= 1'b1; end endmodule
And here's a screenshot of the corresponding waveform:
The interesting part is from 2775ns to 2785ns. I've got 3 clocks here: (1) the base clock, (2) a clock that is 3 times faster generated with an always delay, and (3) a PLL derived clock from the base clock with the same frequency and phase shift as the 2nd clock. Why does other_reg register the new value at 2775ns instead of at 2785ns like some_reg? Since base_clk and clk_100mhz are fully synchronous (right?) shouldn't other_reg see a 0 at it's input? Have I triggered a race condition somehow or otherwise misused the simulation? How would this behave in actual hardware?
09-26-2019 02:23 AM
Yes, it looks a race condition issue.
I'd suggest that you don't change 'cond' at the same time as rising_edge clk.
09-26-2019 09:42 AM - edited 09-26-2019 09:43 AM
Thanks for the reply @graces . Would you mind explaining why this is a race condition? Since the PLL is locked with the base clock, the edges should occur simultaneously (or at least the timing difference would be much less than the propagation delay of a register). So, I don't see how there's any worry that cond violates the hold timing requirement of other_reg. In any event, I thought this was the main point of using a PLL. Or, is it the case that PLL locking doesn't really provide any guarantee of synchronization? By the way, I've asked a related question here (albeit in general terms, not Xilinx-specific) and people seem to echo my thinking that this shouldn't create a race condition. Thanks.
09-26-2019 03:59 PM
Another thing: if it truly were a race condition wouldn't the simulator decide the outcome based on if cond changed before the clock edge? However, this is not the case: if you run that simulation and zoom in on the clock edge, cond and the clock edge occur at the exact same picosecond (the simulation has picosecond resolution). I belive nonblocking assignments all have to occur at the same time unit (correct?). In which case this shouldn't be reading the new value of cond. Maybe the PLL simulation file uses blocking and non-blocking assignments incorrectly?
09-26-2019 06:12 PM
When you change data and clock at the same time, event based simulators may schedule delta events differently, which may result in unexpected result. This is typically seen in behavioral simulation.
You may take a look at UG900, pg228, section "Race Conditions and Delta Cycles".
You should also be able to find quite a few resources online if you search the topic. In Modelsim, you can view the delta events in List window.