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
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
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
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
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 @(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
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