/// LSU EE 4755 -- Fall 2021 -- Digital Design / HDL /// /// Simulator Timing Related Material // Still a mixture of new and old material. (20 October 2021) /// References // :SV12: IEEE 1800-2012 -- The SystemVerilog Standard // :SV17: IEEE 1800-2017 -- The SystemVerilog Standard // https://ieeexplore.ieee.org/document/8299595/ // This is for those already familiar with Verilog. // // :BV3: Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed. // The text used in LSU EE 2740. /// Contents // Non-Blocking Assignments, and Other Intra-Assignment Timing Controls // Event Control (@) ////////////////////////////////////////////////////////////////////////////// /// Non-Blocking Assignments, and Other Intra-Assignment Timing Controls // :SV12: Section 9.4.5 // :Def: Intra-Assignment Timing Control // Delay between computation of right-hand side .. // .. and assignment of variable on left-hand side. // :Sample: x = #1 y; // The "#1" is the timing control. // // Note: A "Blocking Assignment" and "Non-Blocking Assignment" // can both contain an intra-assignment timing control. // :Def: Blocking Assignment // A procedural assignment .. // .. that delays the next statement until the assignment is complete. // :Sample: x = #1 y; z = 2; // z delayed by 1. // :Sample: x = y; z = 2; // No delays, but still called a BA. // :Def: Non-Blocking Assignment // A procedural assignment .. // .. that DOES NOT delay the next statement. // :Sample: x <= #1 y; z = x; // z not delayed, gets old x val (not y). // :Sample: x <= y; z = x; // z not delayed, gets old x val (not y). /// Uses of Intra-Assignment Timing Controls // // -- Edge-Triggered Flip-Flops and Registers // // For edge-triggered devices a zero-delay non-blocking assignment // can be used. // // This is the most common use for intra-assignment delays. // // // -- Specifying the expected delay of hardware. // // Such delays would be inserted by a synthesis tool. /// Intra-Assignment Timing Control // // Compute the result of the right-hand side (RHS) now .. // .. and assign the result later. /// Blocking Intra-assignment timing control. // // :Syntax: VAR = DELAY RHS; // STATEMENT; // If any. // // :Sample: x = #1 y; // z = 2; // // 1. Evaluate RHS immediately, call result rhs. // 2. Evaluate DELAY, call result delay. // 3. After delay (1) cycles assign rhs to VAR. // 4. Continue with next statement, if any. /// Non-blocking Intra-assignment timing control. // // :Syntax: 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. // // :Example: // // Examples of intra-assignment delays. module intra_examples(input clock); logic [9:0] a,b,c, foo, bar, x, y; /// Blocking Intra-Assignment Timing Control initial begin a = 0; // Evaluate b+c at t=0, assign a at t = 2. // a = #2 b + c; foo = a; // b + c end // Equivalent to above. logic bpc; initial begin bpc = b + c; #2; a = bpc; foo = a; end /// Non-Blocking Intra-Assignment Timing Control initial begin a = 0; b = 5; c = 1; // Evaluate b+c at t=0, assign a at t = 1. // a <= #1 b + c; // Executes at t=0. // foo = a; // foo is assigned 0 (old value of a.) #2; // Executes at t=2. // bar = a; // foo is assigned 6. end initial begin // Swapping the value of x and y the conventional way. // bpc = x; x = y; y = bpc; // Swapping the value of x and y the Verilog way. // y <= x; x <= y; end always @( posedge clock ) begin x <= some_input; 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. // t=0 a = 0; b = 0; a <= #10 1; a <= #20 0; a <= #30 1; // Still t=0 $display("Done scheduling tests."); #20; b = 1; // Change b at t=20. end initial begin a = 0; b = 0; // t= 0 a = #10 1; // t = 10 a = #20 0; // t = 30 a = #30 1; // t = 60 $display("Done scheduling tests."); #20; b = 1; // Change b at t=80. end initial begin a <= #2 b; a <= @( c ) b + 1; // t-> 0 1 2 3 4 // c-> 0 1 // a b b+1 // t-> 0 1 2 3 4 // c-> 0 1 // a b+1 b end endmodule module test_timing(); int 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. // int i; uwire [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. int j; logic [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. logic [7:0] a; // Count signal. initial begin:I int 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. logic [7:0] b; initial begin:J logic [11:0] q; for ( q=0; !q[11]; q += (1<<q[10:8])) b[q[10:8]] <= #(q[7:0]) q[q[10:8]]; end endmodule module delay_examples(); int 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. #0; // // This will suspend execution of this process and schedule // a resumption in the inactive region of the current time step. end // One way to implement a clock. logic clock; initial clock = 0; initial forever #1 clock = ~clock; // A better way to implement a clock. always #1 clock = ~clock; int 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 /// 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. // /// WARNING: Do not use: always @ ( EXPR1 or EXPR2 ) begin .. // Instead: always_comb begin .. // @( 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(); int 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. int 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 int a; initial begin a = 0; #1; a=a+1; #1; a=a+1; #1; a=a+1; end logic 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