Search code examples
verilogsimulationhdl

Why would a non-blocking assignment like this cause the process to re-enter?


always @(clk) begin #10 clk <= ~clk;end       //works
always @(clk) begin #10 clk = ~clk;end        //doesn't work

Works means execution re-enters the always block, behaves like a oscillator.
Does not work mean it execute only once.

I've read Scheduling semantics part in Verilog Standard, as I understand it now, in a time step, all statements in the process are placed in the hierarchical event queue at the same time, until the process is suspended when the timing control statement in the process is encountered. The statements are then sorted and sorted. If the statements are in a begin end block, the statements need to be sorted in the order in which they appear.

As I understand it, both blocking and non-blocking assignments can generate a flapping clock because the timing control statement in the process is detected before the assignment is updated.

Is the evaluation of events in a time step done sentence by sentence? Or there's something wrong with my understanding.

Why can a non-blocking update event trigger a sensitive event at the same time step? Could You tell me how evaluation and update events enter the hierarchical event queue?


Solution

  • To better understand this, you need to unroll the statement in the always loop.

    initial #0 clk = 0; // I'm assuming you have this code already. 
    initial begin
        @(clk) #10 clk <= ~clk;
        @(clk) #10 clk <= ~clk;
        @(clk) #10 clk <= ~clk;
    ...
    end
    

    This works because clk is set to 0 after encountering the first @clk. It waits 10 time units and then executions the nonblocking assignment statement. The next @(clk) is encountered before the nonblocking assignment updates the value of clk.

    In the second case,

    initial #0 clk = 0; // I'm assuming you have this code already. 
    initial begin
        @(clk) #10 clk = ~clk;
        @(clk) #10 clk = ~clk;
        @(clk) #10 clk = ~clk;
    ...
    end
    

    Again,clk is set to 0 after encountering the first @clk. But since you now have a blocking assignment statement, the update to clk occurs first, and then you encounter the @(clk). So it waits for a change on clk that never happens.

    The key thing about event controls they have to be encountered in the procedural thread of execution before any change to the event expression happens. Note that I added a #0 to make sure the the @ is seen first. Without the delay, there is a race condition. There is still a race condition when the original code was modeled with an always construct, but most users do not see this because simulators tend to start always processes before initial processes.

    In your case, there is no need for an event control. You can simply write:

    always #10 clk = ~clk;
    

    Even better is

    initial begin
       clk = 0; // make sure that a posedge does not occur at time 0.
       forever #10 clk = ~clk;
    end