///  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<<q[10:8])) b[q[10:8]] <= #(q[7:0]) q[q[10:8]];
   end

endmodule




//////////////////////////////////////////////////////////////////////////////
/// Simulation Timing and Event Queue

// :SV12: Chapter 4

// :Def: Event
//       Sort of a to-do item for simulator. May include running a bit
//       of Verilog code or updating an object's value.
//
// :Def: Event Queue
//       Sort of a to-do list for simulator. It is divided into
//       time slots and time slot regions.
//
// :Def: Time Slot
//       A section of the event queue in which all events have
//       the same time stamp.
//
// :Def: Time Slot Region
//       A subdivision of a time slot.
//       There are many of these. Important ones:
//         active, inactive, NBA.
//
// :Def: Scheduling
//       Determining when an event should execute.
//
//       The when consists of a time slot and a time slot region.
//
//
// :Def: Update Events
//       The changing of an object's value.
//       Will cause *sensitive* objects to be scheduled.

 /// How Event Queue Works
//
//   -- At Start of Simulation
//      initial-block and other code scheduled at t=0, inactive.
//
//   -- Execute Main Loop:
//
//      ...





// Stratified event scheduler (4.4)

// Time Slot Regions
//
//  - Preponed          Sim
//  - Pre-Active
//  - Active            Sim
//  - Inactive          Sim
//  - Pre-NBA
//  - NBA               Sim
//  - Post-NBA
//  - Pre-Observed
//  - Observed          Sim
//  - Post-Observed
//  - Reactive          Sim
//  - Re-Inactive       Sim
//  - Pre-Re-NBA
//  - Re-NBA            Sim
//  - Post-Re-NBA
//  - Pre-PostPoned
//  - PostPoned         Sim
//





///  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();

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



module in_al_example();

   logic 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

   logic 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


///  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();

   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

// Supposed to describe combinational logic.
// But there are two problems.
module alu(result,op,a,b);
   input op, a, b;
   output result;

   logic [63:0] result;
   uwire [63:0] a, b;
   uwire [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;

   logic [63:0] result;
   uwire [63:0] a, b;
   uwire [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


//////////////////////////////////////////////////////////////////////////////
///  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();
   int 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

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