/// 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.