Verilog edge-sensitive event controls

In Verilog the @ character specifies an edge-sensitive event control that blocks until there is a transition in value (an edge) for one of the identifiers in the event expression.  I’ve encountered a misconception that edge events are queued and then serviced by @(…) guards, rather than @(…) being a guard that waits on edge events, blocking until an edge event happens.

I’ve needed to remind people more than once that the only edge events that matter to an @(…) guard are those that happen while it is waiting.  Edge events that happen before reaching the guard are irrelevant to the guard.

For example,

       @(a or b)

will block until there is a change in the value of a or b.  posedge and negedge just mean to be sensitive only to a posedge (0 to 1 transition) or negedge (1 to 0 transition).

For example,

       @(posedge clk)

will block until clk transitions from 0 to 1.

A possible reason for the misunderstanding? In traditional Verilog synthesis, event expressions with posedge/negedge qualifiers get more serious treatment than ones without.

For example, consider the following example

  module test(input in, output reg previous_in);
  reg temp;
  always @(in) begin
    previous_in = temp;
    temp = in;
  end
  endmodule

An always procedure is an infinite loop.  Usually, as here, its flow is punctuated by one or more event controls.  In this example, each time the input changes value, the infinite loop resumes.  Then previous_in is updated with the value of temp, which has saved the previous value of in.  Then temp is updated with the new value of in.  And then the loop is again blocked by the @(in) until the input changes again, and so on.

In traditional Verilog synthesis, however, this example yields the same netlist as the very different

  module test(input in, output reg previous_in);
  reg temp;
  always @(in) begin
     previous_in = in;
  end
  endmodule

Advertisements

4 Comments

  1. If, as you (and the LRM) say, it is “a misconception that edge events are queued and then serviced by @(…) guards, rather than @(…) being a guard that waits on edge events, blocking until an edge event happens.”, then my first example (without the conditional statement) should have been enough to infer a 3-bit shift register.

    Changes to temp1 and temp2 occur in blocking assignments before the event list is “resensitized”. Theoretically, they’d never trigger the always block. Of course, synthesis cannot know that until it has examined all processes to which temp1 and temp2 are visible.

    Much as I’d like to believe that these concepts inform synthesis, I’m forced to conclude that it is driven mostly by RTL coding idioms which are not the sum of their parts

    Like

    • The example is indeed highlighting a difference between simulation and synthesis. But I don’t agree that synthesis therefore “should” do something different. If there’s significant customer demand to expand the synthesizable subset in that direction, synthesis tool vendors will, of course, consider that request, taking into account both feasibility and competing requests. But the only “should” for synthesis here is to make sure there is awareness in the user community of where the boundary of the synthesizable subset is, so that they don’t unwittingly stumble across it. That was the goal of my blog entry.

      Like

  2. I don’t think “event expressions with posedge/negedge qualifiers get more serious treatment than ones without.” explains these examples. For instance, simply reducing the sensitivity list of your last two examples to @(posedge in) only changes what warnings you get. The process still wires previous_in=in.

    Pushing your example one step further:

    module test(input in, output reg previous_in);
    reg temp1, temp2;
    always @(in or temp1 or temp2) begin
    previous_in = temp2;
    temp2 = temp1;
    temp1 = in;
    end
    endmodule

    When I leave off “or temp1 or temp2”, synthesis warns that I forgot to put them in the sensitivity list. But the idea that those form a shift register turns on whether the block triggers when they change. When it does, a straight wire becomes the right solution. The warnings tell us that synthesis is unwilling to consider event statements on their own as logical conditions.

    Declaring these “reg”s hasn’t inferred any sequential (memory) cells either. To do that takes conditional assignments as in:

    module test(input in, output reg previous_in);
    reg temp1, temp2;
    always @(in or temp1 or temp2) begin
    if( in != temp1 ) begin
    previous_in = temp2;
    temp2 = temp1;
    temp1 = in;
    end
    end
    endmodule

    This, at last, gives us the multi-stage shift register that the partial sensitivity list has been hinting-at all along.

    Like

  3. Yes, this is true. I’ve seen code like this…

    always @(… or trigger_me or …)
    begin : name_block

    end

    always @(…)
    begin

    if (bad event)
    begin
    disable name_block;
    ->trigger_me;

    end

    This code doesn’t (always) work because the block ‘name_block’ isn’t waiting for the trigger_me event.
    The disable statement doesn’t pause, allowing event
    controls to be resensitized.

    Like

Tell me (anonymous OK)

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s