///  LSU EE 4755 -- Fall 2023 -- Digital Design / HDL
//
/// Verilog Notes -- Synthesis of Sequential Logic Examples
//
 /// This file is a supplement to the lecture slides
 /// Visit https://www.ece.lsu.edu/v/2023/lsli-syn-seq.pdf

/// Contents
 //
 // Blocking and Non-Blocking Assignments

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


//////////////////////////////////////////////////////////////////////////////
/// Blocking and Non-Blocking Assignments
///
//
// :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
//
//   -- Non-Zero Delay Assignments and Event Controls
//      :Sample:  x = #2 y;   #3;   z <= #4 w;
//
//      Inserted automatically by synthesis tools for post-synthesis
//      simulation.
//
//      Used for writing testbenches.
//
 ///      Can't be used in synthesizable code.
//
//   -- Zero Delay Non-blocking Assignment
//      :Sample:  x <= y;
//
//      For edge-triggered devices.
//



 /// 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 uwire clock, input uwire [9:0] some_input);

   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;

      // x and y have old values.
      #1;
      // x and y have new values.

   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 example_testbench;

   logic clock;

   simple_edge_trigger set(x,aye,clock);

   initial clock = 0;
   always #1 clock = !clock;

endmodule

module simple_edge_trigger
  #( int w = 5 )
   ( output logic [w-1:0] x,
     input uwire [w-1:0] a,
     input uwire clk );


   always_ff @( posedge clk ) x = a;

endmodule

module edge_trigger
  #( int w = 5 )
   ( output logic [w-1:0] x,
     input uwire [w-1:0] a,
     input uwire start,
     input uwire clk );

   always @( posedge clk ) begin

      if ( start ) begin
         x = a;
      end else begin
         x = x - 1;
      end

   end

endmodule


module edge_trig
  #( int w = 5 )
   ( output logic [w-1:0] x,
     output logic [w-1:0] y,
     input uwire [w-1:0] a,
     input uwire start,
     input uwire clk );

   // Non-blocking assignment.
   uwire [w-1:0] newer_x = start ? a : x - 1;
   always @( posedge clk ) x <= newer_x;
   //  always @( posedge clk ) x <= start ? a : x - 1;
   always @( posedge clk ) y <= x;


endmodule





module verilog_stuff(input uwire a);

   logic [7:0] b, c, c1, c2, c3, d, e, lhs, some_expression;
   logic [7:0] some_other_expression;

   initial begin

      b = 0;
      c1 = b;

      #1;

      // Nonblocking assignment.
      lhs <= some_expression + some_other_expression;

      b <= 1;
      c2 = b;

      #2;

      c3 = b;

   end

   
   initial b = 0;
   always @( posedge a ) begin

      c <= b;
      b <= 1;

   end

   always @( posedge a ) begin

      e <= b;
      b <= 2;

   end

endmodule


/// First Examples of Inferred Registers
///

module testc;

   logic clk;

   counter myc(count, r, clk);

   initial clk = 0;
   always #10 clk = !clk;

   always @( count ) $write("The counter is now %0d\n", count);

endmodule

module counter
  #( int w = 16 )
   ( output logic [w-1:0] count,
     input uwire reset, clk );

   always_ff @( posedge clk )
     begin
        count = count + 1;
     end

endmodule

module counter
  #( int w = 16 )
   (output logic [w-1:0] count, input uwire reset, clk );

   always @( posedge clk ) begin

      if ( reset )
        count = 0;
      else
        count++;

   end

endmodule

module counter
  #( int w = 16 )
   (output logic [w-1:0] c, input uwire reset, clk );

   always_ff @( posedge clk ) begin

      c = c + 1;

      if ( reset ) c = 0;

   end


endmodule


/// Examples of Describing Common Register Types
///


// Behavioral description of d flip-flop.
//
module d_ff(output logic q, input uwire d, clk);

   always_ff @( posedge clk ) q <= d;

endmodule




module register #( int width = 16 )
   ( output logic [width-1:0] val,
     input uwire [width-1:0] data,
     input uwire clk );

   always_ff @( posedge clk ) val <= data;

endmodule

module register_en
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input uwire enable,
     input uwire [width-1:0] data,
     input uwire clk );

   always_ff @( posedge clk iff enable ) val <= data;

endmodule

module register_en
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input uwire enable, a, e2,
     input uwire [width-1:0] data,
     input uwire clk );

   always_ff @( posedge clk ) begin
      if ( a ) begin
         if ( enable ) val = data;
         val = val + 3;
         if ( e2 ) val = val * 5;
      end

      x = val + val2;
   end

   always_ff @( posedge clk ) begin

      x = val + val2;
   end

endmodule


module register_sync_reset
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input uwire reset,
     input uwire [width-1:0] data,
     input uwire clk );

   always_ff @( posedge clk ) if ( reset ) val <= 0; else val <= data;

endmodule

module register_async_reset
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input uwire reset,
     input uwire [width-1:0] data,
     input uwire clk );

   always_ff @( posedge clk or posedge reset )
     if ( reset ) val <= 0; else val <= data;

endmodule



/// Examples of Counters
///

module count
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     input uwire clk );

   initial c = 0;

   always @( posedge clk ) c++;

endmodule

module count_reset
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     input uwire reset,
     input uwire clk );

   always_ff @( posedge clk ) if ( reset ) c <= 0; else c <= c + 1;

endmodule


module count_thd
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input uwire [bits-1:0] threshold,
     input uwire clk );

   always_ff @( posedge clk )
     begin
        c++;
        over_th = c > threshold;
     end

endmodule

module count_thd_alt2
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input uwire [bits-1:0] threshold,
     input uwire clk );

   always_ff @( posedge clk )
     begin
        over_th = c > threshold;
        c++;
     end

      // No delay constraints.
// over_th_reg/CLK   setup                    0  +277    4272 R
//   count_thd_alt2     92      15928         0       15928    <none> (D)
      // 100 ps delay target
//   c_reg[12]/CLK   setup                    0  +276    1355 R
//   count_thd_alt2    191      24492         0       24492    <none> (D)



endmodule

module count_thd_alt
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input uwire [bits-1:0] threshold,
     input uwire clk );

   always_ff @( posedge clk ) c++;

   always_comb over_th = c > threshold;

      // No delay constraints.
//   over_th        out port                      +0    3973 F
//   count_thd_alt     91      15544         0       15544    <none> (D)
   // 100 ps delay target
//   c_reg[15]/CLK   setup                    0  +276    1340 R
//   count_thd_alt    189      23160         0       23160    <none> (D)



endmodule



module count_limit
  #( int max_val = 9,
     int bits = $clog2(max_val) )
   ( output logic [bits-1:0] c,
     input uwire reset,
     input uwire clk );

   always_ff @( posedge clk ) begin

      if ( reset || c == max_val ) c = 0; else c++;

   end

endmodule

`ifdef classroom_live
module bcd_count
  #( int num_digits = 4 )
   ( output uwire [num_digits-1:0][3:0] count,
     input uwire clk );

   count_limit_en #(9) cl0( count[0], 1'b1, clk );
   assign  en1 = count[0] == 9;
   count_limit_en #(9) cl1( count[1], en1,  clk );
   assign  en2 = count[1] == 9 && en1;
   count_limit_en #(9) cl2( count[2], en2,  clk );

endmodule
`endif



module count_limit_en
  #( int max_val = 9,
     int bits = $clog2(max_val) )
   ( output logic [bits-1:0] c,
     input uwire reset,
     input uwire enable,
     input uwire clk );

   always_ff @( posedge clk )
     if ( reset ) c = 0;
     else if ( enable ) begin
        if ( c == max_val ) c = 0; else c++;
     end

endmodule


module bcd_count
  #( int num_digits = 4 )
   ( output uwire [num_digits-1:0][3:0] count,
     input uwire reset,
     input uwire clk );

   uwire    nines[num_digits:-1];

   assign  nines[-1] = 1;

   for ( genvar d=0; d<num_digits; d++ )
     begin
        count_limit_en #(9) cl0( count[d], reset, nines[d-1], clk );
        assign nines[d] = nines[d-1] && count[d] == 9;
     end
endmodule


/// Examples of Mixed Registers and Logic
//


// :Example:
//
// Mixed registers and logic. From 2018 Midterm Exam, Problem 5
module regs
  #( int w = 10, int k1 = 20, int k2 = 30 )
   ( output logic [w-1:0] y,
     input logic [w-1:0] b, c,
     input uwire clk );

   logic [w-1:0] a, x, z;

   always_ff @( posedge clk ) begin

      a = b + c;
      if ( a > k1 ) x = b + 10;
      if ( a > k2 ) z = b + x; else z = c - x;
      y = x + z;

   end

endmodule

// :




// cadence translate_off

module testbench;

   localparam int width = 4;
   localparam int num_cyc = 115;

   logic      clk;
   logic      reset;
   bit        done;
   int cycle;

   localparam int max_muts = 4;

   uwire [width-1:0] cnt[max_muts];
   localparam       int num_digits = 3;
   uwire [num_digits-1:0] [3:0] bcd;

   count #(width) cm1(cnt[0], clk);
   count_reset #(width) cm2(cnt[1], reset, clk);
   count_limit #(9) cm3(cnt[2], reset, clk);
   bcd_count #(num_digits) cm4(bcd,reset,clk);

   initial begin
      clk = 0;
      cycle = 0;
      done = 0;
      repeat ( 2 * num_cyc ) #10 cycle += clk++;
      done = 1;
   end

   initial begin

      reset = 1;
      @( posedge clk ); @( negedge clk );
      reset = 0;

      do @ ( negedge clk ) begin

         $write("Cyc %4d  cnt %2d  %2d  %2d  %3x\n",
                cycle, cnt[0], cnt[1], cnt[2], bcd);

      end while ( !done );

   end

endmodule

// cadence translate_on