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