Keywords: delay control, event triggering, edge triggering, level triggering

Verilog provides 2 major types of timing control methods: delay control and event control. Event control is mainly divided into edge-triggered event control and level-sensitive event control.

Delay Control

Delay-based timing control appears in expressions and specifies the time interval between the start and completion of a statement's execution.

The delay can be a number, an identifier, or an expression.

According to the position difference in the expression, delay control can be further divided into regular delay and embedded delay.

Regular Delay

When a regular delay is encountered, the statement needs to wait a certain amount of time, then assign the calculation result to the target signal.

The format is: #delay procedural_statement, for example:

reg  value_test ;
reg  value_general ;
#10  value_general    = value_test ;

Another way to write this delay method is to directly use the hash sign#as an independent delay execution statement, for example:

#10 ;
value_ single         = value_test ;

Embedded Delay

When an embedded delay is encountered, the statement first saves the calculation result, then waits a certain amount of time before assigning it to the target signal.

Embedded delay control is added after the assignment operator. For example:

reg  value_test ;
reg  value_embed ;
value_embed        = #10 value_test ;

It should be noted that the effects of these 2 delay control methods are different.

When the right side of the assignment operator in a delay statement is a constant, both delay control methods can achieve the same delayed assignment effect.

When the right side of the assignment operator in a delay statement is a variable, the two delay control methods may produce different delayed assignment effects.

For example, the following simulation code:

Example

`timescale 1ns/1ns
 
module test ;
    reg  value_test ;
    reg  value_general, value_embed, value_single ;
 
    //signal source
    initial begin
        value_test        = 0 ;
        #25 ;      value_test        = 1 ;
        #35 ;      value_test        = 0 ;        //absolute 60ns
        #40 ;      value_test        = 1 ;        //absolute 100ns
        #10 ;      value_test        = 0 ;        //absolute 110ns
    end
 
    //(1)general delay control
    initial begin
        value_general     = 1;
        #10 value_general  = value_test ; //10ns, value_test=0
        #45 value_general  = value_test ; //55ns, value_test=1
        #30 value_general  = value_test ; //85ns, value_test=0
        #20 value_general  = value_test ; //105ns, value_test=1
    end
 
    //(2)embedded delay control
    initial begin
        value_embed       = 1;
        value_embed  = #10 value_test ; //0ns, value_test=0
        value_embed  = #45 value_test ; //10ns, value_test=0
        value_embed  = #30 value_test ; //55ns, value_test=1
        value_embed  = #20 value_test ; //85ns, value_test=0
    end
 
    //(3)single delay control
    initial begin
        value_single      = 1;
        #10 ;
        value_single = value_test ; //10ns, value_test=0
        #45 ;
        value_single = value_test ; //55ns, value_test=1
        #30 ;
        value_single = value_test ; //85ns, value_test=0
        #20 ;
        value_single = value_test ; //105ns, value_test=1
    end
 
    always begin
        #10;
        if ($time >= 150) begin
            $finish ;
        end
    end
 
endmodule

The simulation results are as follows. From the figure, it can be seen:

  • (1) The two expressions of general delay produce the same execution results.
  • (2) General delay assignment method: after encountering the delay statement, first delay a certain time, then assign the current operand to the target signal. It does not have the characteristic of 'inertial delay', and will not miss relatively narrow pulses.
  • (3) Embedded delay assignment method: after encountering the delay statement, first calculate the result on the right side of the expression, then delay a certain time, and assign it to the target signal.

Below, the assignment process of embedded delay is analyzed:

value_embed  = #10 value_test ; //0ns, value_test=0

At 0ns, this delay statement is executed.

First assign 0 to signal value_embed, output is 0 after a delay of 10ns;

value_embed  = #45 value_test ; //10ns, value_test=0

At 10ns, this delay statement is executed.

Since value_test is still 0 at this time, the value of value_embed remains unchanged.

That is, until 55ns, the value of value_embed remains 0.

value_embed  = #30 value_test ; //55ns, value_test=1

Similarly, at 55ns, the value of value_test is 1, assign it to value_embed and delay output by 30ns.

So at 85ns, value_embed outputs 1.

value_embed  = #20 value_test ; //85ns, value_test=0

Similarly, at 105ns, value_embed outputs 0.

Edge-Triggered Event Control

In Verilog, an event refers to a change in the value of a reg or wire type variable.

Event-triggered timing control is mainly divided into the following types.

General Event Control

Event control uses the symbol@to represent.

The condition for statement execution is that the signal value undergoes a specific change.

The keyword posedge refers to a positive edge transition of the signal, negedge refers to a negative edge transition. When the transition direction is not specified, both edge changes will trigger relevant events. For example:

Example

//As long as signal clk changes, execute q<=d, dual-edge D flip-flop model
always @(clk) q <= d ;                
//At the rising edge of signal clk, execute q<=d, positive-edge D flip-flop model
always @(posedge clk) q <= d ;  
//At the falling edge of signal clk, execute q<=d, negative-edge D flip-flop model
always @(negedge clk) q <= d ;
//Immediately calculate the value of d, and assign it to q at the rising edge of clk. This writing style is not recommended.
q = @(posedge clk) d ;      

Named Event Control

Users can declare a variable of type event and trigger the variable to identify whether the event has occurred. Named events are declared with the keyword event, and the trigger signal is represented by->to represent. For example:

Example

event     start_receiving ;
always @( posedge clk_samp) begin
        -> start_receiving ;       //Sample clock rising edge as the time trigger moment
end
 
always @(start_receiving) begin
    data_buf = {data_if[0], data_if[1]} ; //At the trigger moment, integrate multi-dimensional data
end

Sensitivity List

When any one of multiple signals or events changes and can trigger the execution of a statement, Verilog uses an 'OR' expression to describe this situation, using the keywordorto connect multiple events or signals. The list composed of these events or signals is called a 'sensitivity list'. Of course, 'or' can also be replaced by a comma,to replace it. For example:

Example

//D flip-flop model with active-low reset
always @(posedge clk or negedge rstn)    begin      
//always @(posedge clk , negedge rstn)    begin      
//You can also use commas to list multiple event triggers
    if(! rstn)begin
        q <= 1'b ;      
    end
    else begin
        q <= d ;
    end
end

When there are many input variables in combinational logic, writing a sensitivity list becomes cumbersome. At this time, a more concise way to write is@*or@(*), which indicates that it is sensitive to changes in all input variables in the statement block. For example:

Example

always @(*) begin
//always @(a, b, c, d, e, f, g, h, i, j, k, l, m) begin
//The two writing styles are equivalent
    assign s = a? b+c : d ? e+f : g ? h+i : j ? k+l : m ;
end

Level-Sensitive Event Control

The event control discussed earlier all requires waiting for a change in signal value or the triggering of an event, using@+sensitivity listto represent it.

Verilog also supports using a level as a sensitive signal to control timing, that is, the execution of subsequent statements needs to wait for a condition to be true. In Verilog, the keyword wait is used to represent this level-sensitive situation. For example:

Example

initial begin
    wait (start_enable) ;      //Wait for start signal
    forever begin
        //After the start signal is enabled, integrate the data at the rising edge of clk_samp
        @(posedge clk_samp)  ;
        data_buf = {data_if[0], data_if[1]} ;      
    end
end

Source Code Download

Download