/// Notes and Demos for LSU EE 4702-1 Spring 2001
// Simulator Timing Related Material
// Sources: Ci: Ciletti, Modeling, synthesis, and rapid prototyping
// with the Verilog HDL
// LRM: IEEE, Verilog Language Reference Manual
/// Contents
// Simulator Timing: Stratified Event Queue, Basics
// Delay Control (#)
// initial, always
// Intra Assignment Delay. ( foo = # bar;)
// Event Control (@)
// Wait Statement (wait)
// Parallel Blocks (fork/join)
// timescale
// Simulator Timing: Stratified Event Queue, Details (To be cleaned up.)
/// Simulator Timing: Stratified Event Queue, Basics
// LRM 5
// Event Queue: Sort of a to-do list for simulator.
// Event: A to-do list item including a time. (Re-start this procedure at t=5.)
// Main loop of simulator might remove and execute event at head of
// event queue until the queue is empty.
// Queue stratified into five layers:
//
// 1: Active Events
// 2: Inactive Events
// 3: Non-blocking Assignment Events (covered soon)
// 4: Monitor Events (maybe covered soon)
// 5: Future Events
//
// Items in layer 1-4 have THE SAME timestamp.
// Your choice: spend a satisfying 30 minutes figuring out how this works
// or spend frustrating hours debugging your Verilog descriptions.
// See topics below for more on how queue is used.
/// Delay Control
// LRM 9.7
// Delays have been used before, a few more details given here.
// Forms of delay control, both are single statements.
//
// Form 1: # DELAY;
// Form 1a: # (DELAY_EXPRESSION);
// Form 2: # DELAY STATEMENT;
// Form 2a: # (DELAY_EXPRESSION) STATEMENT;
//
// DELAY is a number, cast to a time (64-bit unsigned).
//
// DELAY_EXPRESSION is an expression (can include variables),
// the result is cast to a time (64-bit unsigned).
//
// Let DELAY or DELAY_EXPRESSION evaluate to d.
// Let t be the current simulation time.
//
// Form 1: Schedule next statement (if any) for t + d;
// Form 2: Schedule STATEMENT and next statement (if any) for t + d;
// If d x or z, treat d as zero.
// If d negative LRM specifies that it be cast to an unsigned value.
// The Modelsim simulator however issues a warning and treats the
// delay as zero.
module delay_examples();
integer a, b, x;
initial begin
/// Delay Syntax Examples
// Delay for a+b cycles. (Delays need not be constant expressions.)
#(a+b);
// The two lines below do the same thing.
#5 a = 1; // Single statement.
#5; a = 1; // Two statements.
// The two lines below do different things.
// In the first a is incremented only if x==3, in the second
// a is always incremented. The second is an example of bad style.
if( x == 3 ) #2 a = a + 1;
if( x == 3 ) #2; a = a + 1;
// The three lines below do the same thing.
if( x == 4 ) #3 begin a = a + 1; b = b + 1; end
if( x == 4 ) begin #3 a = a + 1; b = b + 1; end
if( x == 4 ) begin #3; a = a + 1; b = b + 1; end
/// Delay Special Value Examples
// Delay for zero cycles. See "Delay and the Event Queue".
#0;
// Verilog LRM compliant simulator would delay for
// 18446744073709551615 (eighteen quintillion, four hundred
// forty-six quadrillion, seven hundred forty-four trillion,
// seventy-three billion, seven hundred nine million, five
// hundred fifty-one thousand, six hundred fifteen) cycles.
//
// Here's way: -1 is cast to a time value which is 64 bits unsigned.
//
// Modelsim however issues a warning and delays for zero cycles.
#(-1);
// Interpreted as a zero-cycle delay.
#(1'bx);
// Interpreted as a zero-cycle delay.
#(1'bz);
end
// One way to implement a clock.
reg clock;
initial clock = 0;
initial forever #1 clock = ~clock;
// A better way to implement a clock.
always #1 clock = ~clock;
integer foo;
initial begin
/// Delay and the Event Queue
// At this point simulation time should be at 0 cycles.
// Delay for 1 cycle, meaning:
// Enqueue an event with timestamp 1 to resume execution at foo = 1,
// the event will initially be placed in layer 5 (future events)
// of the event queue.
#1;
foo = 1;
// At this point simulation time should be at 1 cycle.
// Delay for 4 cycles, meaning:
// Enqueue an event with timestamp 5 to resume execution at foo = 2,
// the event will initially be placed in layer 5 (future events)
// of the event queue.
#4;
foo = 2;
// At this point simulation time should be at 5 cycles.
// Delay for 0 cycles, meaning:
// Enqueue an event with timestamp 5 (the current time) to
// resume execution at foo = 3, the event will initially be
// placed in LAYER 2 (INACTIVE EVENTS) of the event queue.
#0;
$display("Greetings from foo = 3. t = %t (After foo = 4.)",$time);
foo = 3;
end
initial begin
#5;
$display("Greetings and felicitations from foo = 4. t = %t",$time);
foo = 4;
end
endmodule
/// initial, always
// LRM 9.9
// Starts procedural code.
//
// Procedural code always starts at an initial or always.
// initial STATEMENT;
//
// At t=0 execute STATEMENT.
// always STATEMENT;
//
// At t=0 execute STATEMENT, then execute STATEMENT, ...
// The initial and always keywords cannot be within another block.
module in_al_example();
reg a,b;
// No guarantee that a assigned before b. A particular simulator
// might consistently assign a first, or it just might seem like
// that. Regardless, that simulator might be upgraded to a newer
// version or the Verilog might have to be run on a different
// simulator (the company was bought out,...).
initial a = 0;
initial b = 0;
initial begin
a = 0;
b = 0;
end
reg clock;
initial begin
// Syntax error: can't have always in an initial.
// always #1 clock = ~clock;
// Do this instead:
forever #1 clock = ~clock;
end
/// Several ways to implement a clock.
initial clock = 0;
always begin
#1;
clock = ~clock;
end
// More compact.
initial clock = 0;
always #1 clock = ~clock;
initial clock = 0;
initial forever #1 clock = ~clock;
endmodule
/// Intra-assignment Timing Controls
// LRM 9.7
// Evaluate the right-hand side (RHS) now, assign the result later.
// Blocking Intra-assignment timing control.
//
// VAR = DELAY RHS;
// STATEMENT; // If any.
//
// 1. Evaluate RHS immediately, call result rhs.
// 2. Evaluate DELAY, call result delay.
// 3. After delay cycles assign rhs to VAR.
// 4. Continue with next statement, if any.
// Non blocking Intra-assignment timing control.
//
// VAR <= RHS;
// VAR <= DELAY RHS;
//
// 1. Evaluate RHS immediately, call result rhs.
// 2. Evaluate DELAY, call result delay. If DELAY not present use 0.
// 3. Continue with next statement, if any.
// 4. After delay cycles, when layer 3 reached assign rhs to VAR.
//
// Note: Non-blocking assignments placed in layer 3 of event queue.
module intra_examples();
reg a,b,c, foo, x, y;
/// Blocking Intra-Assignment Timing Control
initial begin
// Evaluate b+c at t=0, assign a at t = 1.
a = #1 b + c;
// Executes at t=1;
foo = a; // b + c
end
// Equivalent to above.
reg temp;
initial begin
temp = b + c;
#1;
a = temp;
foo = a;
end
/// Non-Blocking Intra-Assignment Timing Control
initial begin
// Evaluate b+c at t=0, assign a at t = 1.
a <= #1 b + c;
// Executes at t=0;
foo = a; // "old" value of a.
end
initial begin
// Swapping the value of x and y the conventional way.
temp = x; x = y; y = temp;
// Swapping the value of x and y the Verilog way.
y <= x; x <= y;
end
always @( b ) begin
x <= 4;
a <= 2 * x; // This uses the old value of x.
end
initial begin
// Schedule four changes in a: immediately, t=10, t=20, and t=30.
a = 0; b = 0;
a <= #10 1;
a <= #20 0;
a <= #30 1;
$display("Done scheduling tests.");
#20;
b = 1; // Change b at t=20.
end
initial begin
a <= #1 b;
a <= @( c ) b + 1;
end
endmodule
module test_timing();
integer a;
initial begin
/// Non-Blocking Assignments and the Event Queue
a <= 1; // Assignment event put in layer 3.
#0; // Continuation event put in layer 2 (inactive).
a = 2; // Therefore, this assignment occurs before a<=1.
#1;
// a should be 1.
$display("a is %d\n",a);
#1;
a <= #1 3;
#1;
#0;
a = 5; // This occurs before non-blocking assignment.
#2;
// a should be 3.
$display("Now a is %d\n",a);
end
initial a = 3;
endmodule
module alt_loop();
// Three ways of generating an 8-bit signal that starts at 0 and
// is incremented each cycle until it reaches 255.
// The first method is how it should be done. The methods
// following that show how non-blocking assignments can be used to
// do the same thing in a way which is more bug-prone, possibly
// simulator inefficient, and lots of fun (for some people).
// This is the way it should be done.
//
integer i;
wire [7:0] c = i; // Count signal.
initial for(i=0; i<255; i = i + 1) #1;
// This fills the event queue at the beginning of the simulation.
// That might slow the simulator down.
integer j;
reg [7:0] d; // Count signal.
initial for(j=0; j<256; j = j + 1) d <= #(j) j;
// Using delayed assignment to have a count from 0 to 255 the hard way.
// Rather than change a all at once, bits are changed individually,
// and only when they need to be changed.
reg [7:0] a; // Count signal.
initial begin:I
integer pos;
time t;
for(pos = 0; pos < 8; pos = pos + 1)
for(t = 0; t < 256; t = t + ( 1 << pos ) )
a[pos] <= #(t) t[pos];
end
// Using delayed assignment to have b count from 0 to 255 the harder way.
// Same as above but uses just one loop.
reg [7:0] b;
initial begin:J
reg [11:0] q;
for(q=0; !q[11]; q = q + (1<<q[10:8])) b[q[10:8]] <= #(q[7:0]) q[q[10:8]];
end
endmodule
/// Event Control (@)
// LRM 9.7
// @( EVENT_EXPR ) STATEMENT
//
// EVENT_EXPR can be
// EXPR (an expression that evaluates to an integer or logic value)
// EVENT_EXPR or EVENT_EXPR or ...
// posedge EVENT_EXPR
// negedge EVENT_EXPR
//
// The different cases are explained below.
// @( EXPR ) STATEMENT
//
// Wait for a change in EXPR. Details:
//
// 1. Evaluate expression, call result e0.
// 2. Continue simulation, go to step 3 if any variable in EXPR changes.
// 3. Evaluate expression, call result e1.
// 4. If e0 != e1 continue with following statement, otherwise goto step 2.
// @( EXPR1 or EXPR2 ) STATEMENT
//
// Wait for a change in EXPR1 or a change in EXPR2.
// This is NOT the same as a change in EXPR1 || EXPR2.
// @( posedge EXPR ) STATEMENT
//
// Wait for a change in EXPR from 0 to 1 (ignoring x and z).
// Including x and z,
// wait for a change in EXPR from 0 to anything or anything to 1.
// @( negedge EXPR ) STATEMENT
//
// Wait for a change in EXPR from 1 to 0 (ignoring x and z).
// Including x and z,
// wait for a change in EXPR from 1 to anything or anything to 0.
// posedge: From 0 to {1xz}, from {0xz} to 1.
// negedge: From 1 to {0xz}, from {1xz} to 0
module event_examples();
integer start;
initial begin #1; start = 0; #1; start = 1; #1; start = 0; #1; end
initial begin
@( start );
// This point reached at cycle 1. (start from x to 0).
$display("A: At the tone sim time will be %t cycles... beeeeeeep.",$time);
@( start );
// This point reached at cycle 2. (start from 0 to 1)
$display("A: At the tone sim time will be %t cycles... beeeeeeep.",$time);
end
// The initial below is equivalent to the one above.
initial @( start )
$display("B: At the tone sim time will be %t cycles... beeeeeeep.",$time);
initial begin
@( posedge start );
$display("C: At the tone sim time will be %t cycles... beeeeeeep.",$time);
end
/// Poor style: execution depends on which initial executed first.
integer start2;
initial begin start2 = 0; #1; start2 = 1; end
initial begin
@( start2 );
$display("D: At the tone sim time will be %t cycles... beeeeeeep.",$time);
@( start2 );
$display("D: At the tone sim time will be %t cycles... beeeeeeep.",$time);
end
integer a;
initial begin
a = 0;
#1;
a=a+1;
#1;
a=a+1;
#1;
a=a+1;
end
reg b;
always @( a || b ) begin
$display("a changed to %d",a);
b = 2 * a;
end
initial @( a ) $display("a changed to %d",a);
always @( b ) $display("b changed.");
endmodule
// Supposed to describe combinational logic.
// But there are two problems.
module alu(result,op,a,b);
input op, a, b;
output result;
reg [63:0] result;
wire [63:0] a, b;
wire [2:0] op;
/// Won't work.
always @( op )
case( op )
0: result = a + b;
1: result = a - b;
2: result = a;
3: result = b;
4: result = a & b;
5: result = a | b;
endcase
endmodule
module fixed_alu(result,op,a,b);
input op, a, b;
output result;
reg [63:0] result;
wire [63:0] a, b;
wire [2:0] op;
/// Will (probably) work.
always @( op or a or b )
case( op )
0: result = a + b;
1: result = a - b;
2: result = a;
3: result = b;
4: result = a & b;
5: result = a | b;
default: result = 0;
endcase
endmodule
// Behavioral description of d flip-flop.
module d_ff(q,d,c);
output q;
input d,c;
reg q;
wire d;
wire c;
always @( posedge c ) q <= d;
endmodule // d_ff
module lt_ff(q,d,c);
output q;
input d,c;
reg q;
wire d;
wire c;
always @( c or d ) if( c ) q <= d;
endmodule // d_ff
// Behavioral description of master-slave edge triggered flip-flop.
module master_slave_ff(q,d,c);
output q;
input d,c;
reg q, nextq;
wire d;
wire c;
always @( posedge c ) nextq <= d;
always @( negedge c ) q <= nextq;
endmodule // master_slave_ff
module clock();
reg clock, clock2, clock4;
initial begin clock = 0; clock2 = 0; clock4 = 0; end
// A clock.
always #1 clock = ~clock;
// This stops the simulation after 20 cycles.
initial #20 $stop;
// Half the frequency of clock.
always @( posedge clock ) clock2 = ~clock2;
// One quarter the frequency of clock.
always @( posedge clock2 ) clock4 = ~clock4;
/// Code below won't work.
// Problem occurs because several signals change in the same cycle.
// This can easily be "fixed" for behavioral code however
// one should not try to detect simultaneous events in
// synthesizable descriptions.
reg five_pulse;
initial five_pulse = 0;
always @( posedge clock )
if( !clock2 && clock4 )
begin
five_pulse = 1;
five_pulse <= #1 0;
end
endmodule
module up_down_count(count,dir,clk,reset);
input dir, clk, reset;
output count;
reg [15:0] count;
wire dir, clk, reset;
always @( posedge clk )
if( reset )
count = 0;
else
count = count + ( dir ? 1 : -1 );
endmodule
module use_up_down_count();
wire [15:0] count;
reg dir, clk, r;
up_down_count udc(count,dir,clk,r);
initial clk = 0;
always #5 clk = ~clk;
initial #100 $stop;
initial begin
dir = 0;
r = 1;
@( posedge clk );
/// Not finished.
r = 0;
end
endmodule
/// Wait Statement (wait)
// LRM 9.7
// wait ( EXPR ) STATEMENT
//
// 1. Evaluate EXPR, call result expr.
// 2. If expr non-zero, execute STATEMENT (and any following statements).
// 3. Otherwise, when a variable in EXPR changes go to step 1.
module wait_example();
integer a, b;
initial begin
wait( a ); // Wait until a is true (non-zero).
wait( a < 3 ); // Wait until a < 3.
// Wait until either:
// a > 3 and b > 1
// or a <=3 and b > 0.
wait( 3 < a < b );
// What did you think it means?
// The two lines below are NOT equivalent.
wait( b ); // No waiting if b already 1.
@( posedge b ); // If b already 1, wait for it to go to 0 then 1 again.
end
endmodule
/// Parallel Blocks (fork/join)
// Parallel Block
//
// fork
// STATEMENT1;
// STATEMENT2;
// STATEMENT3;
// ...
// join
// Sequential Block
//
// begin
// STATEMENT1;
// STATEMENT2;
// STATEMENT3;
// ...
// end
//
// Execute statements simultaneously.
// More precisely place events to start executing all the statements in
// layer 1 of the event queue.
// Note: begin ... end is called a sequential block.
module fork_join_examples();
initial begin
// There's not much point to this.
fork
a = 1;
b = 2;
join
end
initial fork
a = 1; // Assigned at t=0;
#5 a = 2; // Assigned at t=5;
#10 a = 3; // Assigned at t=10;
#15 a = 4; // Assigned at t=15;
join
initial begin
a = 1; // Assigned at t=0;
#5 a = 2; // Assigned at t=5;
#10 a = 3; // Assigned at t=15;
#15 a = 4; // Assigned at t=30;
end
initial begin
// Record the finish times for foo and bar, regardless of
// which changes first.
fork
wait( foo_done ) foo_finish = $time;
wait( bar_done ) bar_finish = $time;
join
// Record the next values of a and b, regardless of
// which one changes first.
fork
@( a ) xa = a;
@( b ) xb = b;
join
// my_sig
count = 0;
fork:X
@( my_sig ) count = 0;
forever #1 count = count + 1;
wait( count == 20 ) disable X;
join
// Within a sequential block things occur in order, it doesn't
// matter if the sequential block is within a parallel block.
// The two sequential blocks below can execute simultaneously.
a = 0;
fork
// b definitely gets new value of a, 1.
begin a = 1; b = a; end;
// d might get old value of a, 0.
begin c = 1; d = a; end;
join
// Delays change things.
a = 0;
fork
// b definitely gets new value of a, 1.
begin #1 a = 1; #1 b = a; end;
// d definitely gets new value of a, 1.
// Assignment to b and d occur at same sim time.
begin #1 c = 1; #1 d = a; end;
join
fork:A
// STATEMENT1 is a clock.
// This is a case where forever isn't forever.
forever #1 clock = ~clock;
// STATEMENT2 exits after 20 cycles.
#20 disable A;
join
// Wait until no change in my_signal for 20 cycles.
begin:OUTER
forever
fork:INNER
#20 disable OUTER;
@( my_signal ) disable INNER;
join
end
end // initial begin
endmodule // fork_join_examples
/// timescale
// timescale TIMEUNIT / PRECISION
// Normally placed at top.
// Cycle is 1 ms, resolution is 100 microseconds.
`timescale 1ms/100us
module test();
integer foo;
initial begin
foo = 0;
#1;
$display("At the tone sim time will be %f ... beeeeeeep.",$realtime);
foo = 1;
#(1.5);
$display("At the tone sim time will be %f ... beeeeeeep.",$realtime);
foo = 2;
#2;
$display("At the tone sim time will be %f ... beeeeeeep.",$realtime);
foo = 3;
#1;
$display("At the tone sim time will be %f ... beeeeeeep.",$realtime);
end
endmodule
/// Simulator Timing: Stratified Event Queue, Details
// LRM 5
// Under Construction
// Stratified Event Queue
// 1 Active events: Current time (that or which?) can be processed in any order.
// 2 Inactive events: Current time after active events have completed.
// 3 Non-blocking assign update events.
// 4 Monitor events: after non-blocking assign.
// 5 Future events.