Other times, delay is fundamental to how a circuit works.
Structural Example with delays indicated with #:
module pulser(y, x); input x; output y; wire x_n; and #5 (y,x,x_n) not #3 (x_n,x); endmodule
Describing Concurrency is another feature of an HDL
The following code attempts to model a swap of y1 and y2, as in
y1 = old y2 concurrently with y2 = old y1
Concurrent Assignment (Cummings 2000):
module fbosc2 (y1, y2, clk, rst); output y1, y2; input clk, rst; reg y1, y2; always @(posedge clk or posedge rst) if (rst) y1 <= 0; // reset else y1 <= y2; always @(posedge clk or posedge rst) if (rst) y2 <= 1; // preset else y2 <= y1; endmodule
This is a case where handing of concurrency, is critical as two interdependent updates are at once
The IEEE standard identifies 5 regions for events to live:
$monitor
and $strobe
system tasks generate monitor events.(Quoted from IEEE Std 1364-2005 with colors added)
while (there are events){ if (no active events){ if (there are inactive events){ activate all inactive events; } else if(there are nonblocking assign update events){ activate all nonblocking assign update events; } else if(there are monitor events){ activate all monitor events; } else{ advance T to the next event time; activate all inactive events for time T; } } E = any active event; if (E is an update event){ update the modified object; add evaluation events for sensitive processes to event queue; }else { /* shall be an evaluation event */ evaluate the process; add update events to the event queue; } }
Bad Concurrent Assignment (Cummings 2000):
module fbosc1 (y1, y2, clk, rst); output y1, y2; input clk, rst; reg y1, y2; always @(posedge clk or posedge rst) if (rst) y1 = 0; // reset else y1 = y2; always @(posedge clk or posedge rst) if (rst) y2 = 1; // preset else y2 = y1; endmodule
Will not only synthesize correctly, but also simulate correctly:
Good Concurrent Assignment (Cummings 2000):
module fbosc2 (y1, y2, clk, rst); output y1, y2; input clk, rst; reg y1, y2; always @(posedge clk or posedge rst) if (rst) y1 <= 0; // reset else y1 <= y2; always @(posedge clk or posedge rst) if (rst) y2 <= 1; // preset else y2 <= y1; endmodule
module amb_parallel_swap(); reg clk, rst; reg y1, y2; reg z1, z2; initial clk = 0; always #50 clk = ~clk; initial begin rst = 1; #10; rst = 0; end initial begin #1000 $finish; end
always @(posedge clk , posedge rst) if (rst) y1 = 0; // reset else y1 = y2; always @(posedge clk , posedge rst) if (rst) y2 = 1; // preset else y2 = y1;
always @(posedge clk , posedge rst) if (rst) z1 <= 0; // reset else z1 <= z2; always @(posedge clk , posedge rst) if (rst) z2 <= 1; // preset else z2 <= z1; endmodule
Continuous assignment statements enable (a.k.a. schedule or trigger) an evaluation event when any source element of the RHS expression changes. Upon any expression’s resulting value change, an update event for the LHS is added to the active queue.
assign {cout,b} = d+a;
Left-hand-side (LHS) expression: {cout,b}
Right-hand-side (RHS) expression: d+a;
An initial evaluation always occurs at time zero to propagate constant values. The various cascades of dependanceis cause other expresssions and procedural code in the HDL models to be evaluated at time zero as a result.
In the the following, an initial evaluation and assignment will occur, but no other update to a will happen in the simulation.
assign a=1;
Blocking assignments cause an immediate evaluation of the right hand side. If delay is provide, then the assignment to the left-hand-expression is scheduled for a future time unless the delay is 0 and in which case the assignment event is entered into the inactive queue. If no delay is specified the statement performs the assignment and returns immediately. Once the assignment is performed, any change will enable any events dependent on the value.
a = #D b+c;
assignment delay D
Nonblocking assignments schedule an evaluation and assignment using the values of the inputs at the time of the scheduling. The event is placed in the nonblocking assignment update event queue for the current time or, if a delay is provided, a future time.
a <= #D b + c;
assignment delay D
Delays are for Simulation Only
Delays are only used when modeling for simulation. They should not be used to describe characteristics to a hardware synthesis tool. Typically, all # delays are stripped from code before synthesis.
In the delay before evaluation form (statement delay or evaluation delay), the delay essentially “sleeps” the process until another time before the following code is evaluated.
#D a = b + c; //delay before evaluation #D a <= b + c; //delay before evaluation
Code after these statements in a begin…end block will not execute until after the delay, nor will the block pass control
Assignment delay schedules an assignment for a future time, though the RHS expression is evaluated using the values at the time of the scheduling.
a = #D b + c;
delay before assignment, execution in begin…end block does not progress until assignment event is handled, nor will the block will pass control
a <= #D b + c;
delay before assignment: RHS evaluation is followed by scheduling a future nonblocking assignment event, execution within a begin…end block progresses and control may be passed out of the block
initial #0 $display($time," a b y z"); initial begin $monitor($time,u8a,u8b,u8y,u8z); u8a=0; u8b=0; #5; #5 u8a = 1; #5 u8b = 1; end always@* begin $display($time,u8a,u8b," enter nonblocking"); u8y <= #20 u8a+u8b; $display($time,u8a,u8b," leave nonblocking"); end always@* begin $display($time,u8a,u8b," enter blocking"); u8z = #20 u8a+u8b; $display($time,u8a,u8b," leave blocking"); end initial #100 $finish;
Result
Simulator is doing circuit initialization process.
0 0 0 enter nonblocking
0 0 0 leave nonblocking
0 0 0 enter blocking ***
Finished circuit initialization process.
0 a b y z
0 0 0 x x
10 1 0 enter nonblocking
10 1 0 leave nonblocking
10 1 0 x x
15 1 1 enter nonblocking
15 1 1 leave nonblocking
15 1 1 x x
20 1 1 leave blocking ***
20 1 1 0 0
30 1 1 1 0
35 1 1 2 0
Stopped at time : 100 ns : File "…/delay_tests.v" Line 56
Delayed Blocking assignment prevented multiple calls.
Transport Delay maintains all transitions and delays them by a specified amount. Models a delay like an ideal transmission line.
Inertial Delay also delays transitions, but it only maintains transitions to new states that are held for as long as the specified delay. Approximates models charging and pulse rejection.
Inertial Delay Pulse Rejection: pulses of a duration less than the specified inertial delay are rejected.
The following are all examples of inertial delay on a wire.
delay in the continuous assignment
wire x_n; assign #5 x_n = ~n;
delay in the implicit assignment
wire #5 x_n = ~n;
delay in the wire declaration (added to any assignment delay)
wire #5 x; assign x = ~x;
In this example, the inertial delay to a
is 4
and for b
is 3
:
wire #3 a; wire #2 b; assign #1 {a,b} = {x,x};
Inertial delay may be provide to primitives using the simplist form of the delay operator.
wire y; and #3 I1(y,a,b);
In this next example, the inertial delay to y is 4 since delay provide in the declaration is added.
wire #1 y; and #3 I1(y,a,b);
The pulse rejection behavior is based on the result, not the inputs. The result of an expression to be assigned must maintain a new value for the specified time.
wire y; assign #2 y = a&b;
wire y; and #2 I1(y,a,b);
wire y; assign #3 y = |{a,b,c};
wire y; or #3 I1(y,a,b,c);
Separate delays may be provided for rising and falling output edges
wire y; and #(3,2) I1(y,a,b); //#(rising delay,falling delay)
Rising Edges:
0 → 1, X→1 Z→1
Falling Edges:
1 → 0, X→0, Z→0
Other output edges (e.g. X→Z , 1→Z ) assume the minimum value between the of the rising and falling delays (2 in the example above)
Good Reference:http://verilog.renerta.com/source/vrg00011.htm
You can also specify Min:Typ:Max delays which are selected by the simulator:
assign #(1:2:3) y = a & b; assign #(2:3:4,1:2:3) y = a & b;
Good Reference: http://verilog.renerta.com/source/vrg00025.htm
For a reg
signal, to model inertial delay it is recommend to create a delayed wire copy. Direct modeling of inertial delay is difficult otherwise.
Augmenting reg with Inertial Delay:
wire c; reg c_nodelay assign #5 c = c_nodelay; always @(b,d) c_nodelay = ~b|d; end
parameter TC2Q; reg q; always @(posedge clk) q <= #TC2Q ~b;
parameter TC2Q; reg q; always @(posedge clk) q = #TC2Q ~b;
parameter TC2Q; reg q; always @(posedge clk) #TC2Q q <= ~b;
parameter TC2Q; reg q; always @(posedge clk) #TC2Q q = ~b;
parameter D; reg y; always @(a,b) y <= #D a&b;
always @(y) y_delayed <= #D y;
parameter D; reg y; always @(a,b) y = #D a&b;
parameter D; reg y; always @(a,b) #D y <= a&b;
parameter D; reg y; always @(a,b) #D y = a&b;
Example 1:
initial begin a <= 0; a <= #10 255; a <= #20 22; a <= #30 10; b <= 'bx; b <= #1 1193; b <= #10 122; c <= #10 92; c <= #15 93; end
Example 2: delays relative to rst signal change
initial begin a = 0; b = 0; c = 0; wait (rst==0); a <= #10 255; a <= #20 22; a <= #30 10; b <= #1 1193; b <= #10 122; c <= #10 92; c <= #15 93; end
#
event
posedge
and negedge
event triggerName;
@(triggerName);
-> triggerName;
The event control operator may be provided with variable multiple variables using comma separation. (older syntax is to us or)
@(a,b,c)
@(a or b or c)
Negedge and posedge restrict sensitivity to the following
transitions
@(posedge clk or negedge en)
Negedge:
1→ 0
1→ x or z
x or z → 0
Posedge:
0 → 1
x or z →1
0 → x or z
When posedge and negedge modify a multi-bit operand , only the lsb is used to detect the edge.
The wait statement suspends execution until a condition is true.
It can be considered as a level-sensitive control since it doesn’t wait for a transition edge.
Comparison of wait to posedge:
@(posedge clk)
even if clk is already true, wait for next rising edge
wait (clk);
if clk is already true, produce without delay
Example to change data on the falling clock edge that follows a variable exceeding the value 10:
wait(a>10); @ (negedge clk); data=data+1;
Any timing control may be modified so as to be repeated/multiplied, by using the keyword repeat
repeat (count) @ (event expression)
If count is positive, repeat the timing control that number of time. If count is equal or less than 0, skip
Wait fot 10 clk rising edges before proceeding execution:
repeat (10) @ (posedge clk);
Delay an assignment by 5 clk edges
a <= repeat(5) @(posedge clk) data;
Delay an assignment by 5 inverter delays:
parameter INV_DELAY = 4.5; a <= repeat(5) #INV_DELAY data;
The initial construct is used to denote code to be executed once at the beginning of the simulation.
The always construct causes code to run repeatedly in an infinite loop. It is only useful with a delay or control construct, otherwise will create a zero delay infinite loop that can block time progression in simulation
This would run infinitely:
always x=a&b;
This prints endlessly in the beginning
of the simulation:
always begin $display(“hello %0t”); end
0 : hello
0 : hello
0 : hello
0 : hello
0 : hello ...
DUT:
module Nand_Latch_1 (q,qbar,preset,clear); output q, qbar; input preset,clear; Nand1 #1 G1 (q,preset,bar); G2 (qbar,clear,q) endmodule
Testbench:
module test_Nand_Latch_1; // Design Unit Testbench reg preset, clear; wire q, qbar; Nand_Latch_1 M1 (q, qbar, preset, clear);// Instantiate DUT initial // Create DUTB response monitor begin $monitor ($time, "preset = %b clear = %b q = %b 1 qbar = %b", preset, clear, q, qbar); end initial begin // Create DUTB stimulus generator #10 preset =0; clear =1; #10 preset =1; $stop; // Enter, to proceed #10 clear =0; #10 clear =1; #10 preset =0; end initial #60 $finish ; // Stop watch end endmodule
Results:
0 preset = x clear = x q = x qbar = x
10 preset = 0 clear = 1 q = x qbar = x
11 preset = 0 clear = 1 q = 1 qbar = x
12 preset = 0 clear = 1 q = 1 qbar = 0
20 preset = 1 clear = 1 q = 1 qbar = 0
30 preset = 1 clear = 0 q = 1 qbar = 0
31 preset = 1 clear = 0 q = 1 qbar = 1
32 preset = 1 clear = 0 q = 0 qbar = 1
40 preset = 1 clear = 1 q = 0 qbar = 1
`timescale <reference_time_unit>/<time_precision>
reference_time_unit: what the delays should be interpreted as
precision: what the precision to use. The precision affects rounding.
example :
`timescale 1ns/1ps
Then #17.0402
repeats 17.040 ns
50% duty cycle:
initial begin clk = 0; end always begin #5 clk = 0; #5 clk = 1; end
initial begin clk = 0; end always begin #5 clk = ~clk; end
The procedural construct forever can be used the create an infinite loop:
initial begin clk = 0; forever begin #5 clk = ~clk; end end
initial begin clk = 0; forever begin repeat (16) begin #5 clk = ~clk; end #20; end end
The procedural construct repeat can be used to create a limited loop
Here, the first input ,a
, is changed every 5 time units, cycling every 10
Each subsequent input at half the rate of the previous
reg a,b,c,d; initial begin a = 0; b = 0; c = 0; d = 0; end always begin #5 a = ~a; end always begin #10 b = ~b; end always begin #20 c = ~c; end always begin #40 d = ~d; end
The same affect can be achieved using a counter:
wire a,b,c,d; reg [3:0] count; initial begin count = 0; end assign {a,b,c,d}=count; always begin #5 count = count+1; end
wire data_out; reg write; reg [3:0] addr; reg [7:0] memory_buffer [0:15]; // 16 entries 8 bit #'s reg [7:0] data_in; integer i; integer file; my_mem I0 (data_out,data_int,addr,write); initial begin $readmemh("memory_hex_in.txt", memory_buff); //init memory_buff from file file = $fopen(“memory_hex_out.txt”); //open a file for writing results #5 write = 0; addr =0; for (i=0; i<16; i++) begin //write to mem #5 write = 0; data_in= memory_buff[i]; addr= i; #5 write = 1; end #10 write = 0; //reading and writing to file for (i=0; i<16; i++) begin #5 addr =i; $fstrobe(file,“%2H”,data_out); end $fclose(file); end
The procedural construct for can be used to create simulation loops.