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