/// LSU EE 4755 -- Fall 2019 -- Digital Design / HDL /// /// Simulator Timing Related Material // Currently a mixture of new and old material. ( 6 November 2019) /// 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 // Intra-Assignment Timing Controls // 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.) ////////////////////////////////////////////////////////////////////////////// /// 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; // Executes at t=2; // 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< 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 /// fork/join Types // /// fork S1; S2; .. Sn; join // // Wait for all statements, S1-Sn, to finish. // /// fork S1; S2; .. Sn; join_any // // Wait for any statement to finish. // /// fork S1; S2; .. Sn; join_none // // Don't wait for any to finish. // 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 (output var int foo_finish, bar_finish, input uwire foo_done, bar_done, my_sig, my_signal); int a, b, c, d, xa, xb, count; logic clock; initial begin // There's not much point to this. fork a = 1; b = 2; join end initial fork #15 a = 4; // Assigned at t=15; a = 1; // Assigned at t=0; #5 a = 2; // Assigned at t=5; #10 a = 3; // Assigned at t=10; join initial begin a = 0; 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_none $write("a is %h\n",a); end 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(); int 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.