Tutorials

Learn More

Procedural timing control

The timing control is used to advance simulation time and it has two methods

  1. Delay timing control
  2. Event timing control

Delay timing control

The delay timing control allows to add up a delay between when the statement is encountered and when it is executed by the simulator. The delay is specified with the ‘#’ symbol.

Syntax:

A number, identifies or (min, typical, max) expression is used to specify delay timing control

#<number>
#<identifier>
#(<min_exp>: <typical_exp>, <max_exp>)

There are three types of delay timing control as follows

Delay timing control types

Declaration

Regular delay control

The non-zero delay is specified at the LHS of the procedural statement.

Intra-assignment delay control

Delay is specified between the assignment operator and the RHS operand.

Zero delay control

The zero delay is specified at LHS of procedural statement

Use Cases:

  1. Regular delay control delays the execution of the entire statement by a specified value.
    Examples:
    a. #5 data = i_value;
    b. #(2:3:6) data = 20;
  2. Intra-assignment delay control delays computed value assignment by a specified value. The RHS operand expression is evaluated at the current simulation time and assigned to LHS operand after a specified delay value.
    Example: data = #5 i_value;
  3. Zero delay control is used to control execution order when multiple procedural blocks try to update values of the same variable. Both always and initial blocks execution order is non-deterministic as they start evaluation at the same simulation time. The statement having zero control delay executes last, thus it avoids race conditions.
    Example:
    reg [2:0] data;
    initial begin 
      data = 2;
    end
    initial begin 
      #0 data = 3;
    end

    Without zero delay control, the ‘data’ variable may have a value of either 2 or 3 due to race conditions. Having zero delay statement as specified in the above code guarantees outcome to be 3. However, it is not recommended to assign value to the variable at the same simulation time.

    Event timing control

    The event timing control method is used to trigger a statement or procedural block execution due to a change in the value of a net or register. It can be classified into four types

    Event timing control types

    Declaration

    Regular event control

    An event control is specified using @ symbol

    Event OR control

    Multiple events are declared using the ‘or’ keyword or comma ‘,’ symbol.

    Named event control

    The event declared using 

    1. -> symbol as event triggering
    2. @ symbol as waiting for event trigger.

    Level sensitive timing control

    The ‘wait’ keyword is used in the declaration

    a. Regular event control

    The statement or procedural block is executed based on positive or negative edge transition in a signal value.

    posedge – A posedge is used to execute statement/s whenever there is a transition from 0 or X or Z to 1.

    negedge – A negedge is used to execute statement/s whenever there is a transition from 1 or X or Z to 0.

    Examples:

    Examples

    Description

    @(posedge clk) q = d;

    The q = d is executed whenever the clk signal does transition from 0/X/Z to 1.

    @(negedge clk) q = d;

    The q = d is executed whenever the clk signal does transition from 1/X/Z to 0.

    out = @(posedge clk) (a & b)

    The a & b is evaluated immediately but assigned to out at the positive edge of the clk.

    out = @(negedge clk) (a & b)

    The a & b is evaluated immediately but assigned to out at the negative edge of the clk.

    module tb;
      reg clk;
      reg [3:0] i1, i2;
      reg [3:0] out1, out2, out3;
      
      initial clk = 0;
      always #5 clk = ~clk;
      
      always @(clk) begin
        @(posedge clk) out1 = i1;
        @(negedge clk) out2 = i2;
        out3 = @(posedge clk) (i1 & i2);
      end
      
      initial begin
         $monitor("Time = %0t: i1 = %0d, i2 = %0d, out1 = %0d, out2 = %0d", $time, i1, i2, out1, out2);
        repeat(4) begin
          i1 = $random;
          i2 = $random;
          #10;
        end
        #20;
        $finish;
      end
    endmodule

    Output:

    Time = 0: i1 = 4, i2 = 1, out1 = x, out2 = x
    Time = 10: i1 = 9, i2 = 3, out1 = x, out2 = x
    Time = 15: i1 = 9, i2 = 3, out1 = 9, out2 = x
    Time = 20: i1 = 13, i2 = 13, out1 = 9, out2 = 13
    Time = 30: i1 = 5, i2 = 2, out1 = 9, out2 = 13
    Time = 35: i1 = 5, i2 = 2, out1 = 5, out2 = 13
    Time = 40: i1 = 5, i2 = 2, out1 = 5, out2 = 2
    Simulation complete via $finish(1) at time 60 NS + 0
    ./testbench.sv:25     $finish;

    b. Event OR control

    The statement or procedural block is executed based on positive or negative edge or level transition of more signals by mentioning into the sensitivity list using the ‘or’ keyword or comma ‘,’.

    Examples

    Description

    always @(clk or rst_n)

    Level sensitive for clk and rst_n signals using or keyword. 

    always @(posedge clk or negedge rst_n)

    Edge sensitive for clk and rst_n signals using or keyword.

    always @(clk, rst_n)

    Level sensitive for clk and rst_n signals using a comma.

    always @(posedge clk, negedge rst_n)

    Edge sensitive for clk and rst_n signals using a comma.

    module tb;
      reg clk_1, clk_2, clk_3;
      reg [3:0] i1, i2;
      reg [3:0] out1, out2, out3;
      
      initial begin 
        clk_1 = 0;
        clk_2 = 0;
        clk_3 = 0;
      end
      
      always #5  clk_1 = ~clk_1;
      always #10 clk_2 = ~clk_2;
      always #15 clk_3 = ~clk_3;
      
      always @(posedge clk_1 or negedge clk_2) begin
        out1 = i1;
        out2 = (i1 & i2);
      end
      
      always @(negedge clk_1, posedge clk_3) begin
        out3 = (i1 | i2);
      end
      
      initial begin
         $monitor("Time = %0t: i1 = %0d, i2 = %0d, out1 = %0d, out2 = %0d", $time, i1, i2, out1, out2);
        repeat(4) begin
          i1 = $random;
          i2 = $random;
          #10;
        end
        #20;
        $finish;
      end
      
      initial begin
        $dumpfile("dump.vcd");
        $dumpvars(1);
      end
    endmodule

    Output:

    Time = 0: i1 = 4, i2 = 1, out1 = x, out2 = x
    Time = 5: i1 = 4, i2 = 1, out1 = 4, out2 = 0
    Time = 10: i1 = 9, i2 = 3, out1 = 4, out2 = 0
    Time = 15: i1 = 9, i2 = 3, out1 = 9, out2 = 1
    Time = 20: i1 = 13, i2 = 13, out1 = 13, out2 = 13
    Time = 30: i1 = 5, i2 = 2, out1 = 13, out2 = 13
    Time = 35: i1 = 5, i2 = 2, out1 = 5, out2 = 0
    Simulation complete via $finish(1) at time 60 NS + 0
    ./testbench.sv:35     $finish;

    c. Named event control

    In Verilog, the keyword ‘event’ is used to declare ‘named events’ that trigger an event using -> symbol and it is recognized by @ symbol.

    The named events are also commonly knowns as Verilog events’.

    The named event or Verilog events are used for synchronization between two or more processes.

    Syntax:

    To trigger an event: ->

    Waiting for the event or recognize the event: @()

    Note: 

    1. The Verilog event does not hold any data.
    2. The @ symbol is used for edge-sensitive constructs.
    module tb;
      event e1;
      
      initial begin
        #10;
        $display("Triggering an event e1");
        ->e1;
      end
      
      initial begin
        @(e1) $display("Event e1 is triggered");
      end
    endmodule

    Output:

    Triggering an event e1
    Event e1 is triggered

    d. Level sensitive timing control

    Along with the edge-sensitive construct (while waiting for an event trigger), Verilog adds up the ‘wait’ level-sensitive construct to wait for a specified condition to be true, and then only one or more statements will be executed. Thus, a set of statements will be blocked until the ‘wait’ condition is evaluated to be true.

    Syntax:

    wait(<expression or variable>)
    module tb;
      integer count;
      
      initial begin 
        count = 0;
        forever begin
         count++;
         #2;
        end
      end
      
      initial begin
        wait(count == 'd5);
        $display("count has reached till %0d at time = %0t", count, $time);
        $finish;
      end
    endmodule

    Output:

    count has reached till 5 at time = 8