///  LSU EE 4755 -- Fall 2017 -- Digital Design / HDL
///
///  Elaboration, Parameters, and Generate Constructs

 // Under construction.

/// References

// :SV12: IEEE 1800-2012 -- The SystemVerilog Standard
//        http://standards.ieee.org/getieee/1800/download/1800-2012.pdf
//
// :BV3:  Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed.
//        The text used in LSU EE 2740.


//////////////////////////////////////////////////////////////////////////////
/// Elaboration

 // :Def: Elaboration [of a module]
 // The process of locating instantiated modules, applying parameter
 // values, and running generate expressions. It is roughly analogous
 // to preprocessig in C.

 // :Def: Design Hierarchy
 // Details about the design elaborated from some top-level modules
 // constructed and used by a Verilog tool, such as simulator, synthesis
 // program, etc.
 //
 // Cadence Incisive uses program ncelab for elaboration. (It is
 // automatically called by other tools.)


 // :Example:
 //
 // Module sat_add is instantiated three times by elab_demo, at two
 // different sizes.
 //
 // After elaborating elab_demo, the design hierarchy will have four
 // modules: elab_demo, two sat_add's at 5 bits, and one at 9 bits.

module sat_add
  #( int width = 8 )
   ( output uwire [width:1] sum,
     input uwire [width:1] a, b );

   localparam logic [width:1] val_max = -1; // Or ( 1 << width ) - 1

   uwire [width:1] raw_sum;
   uwire           carry_out;
   assign         { carry_out, raw_sum } = a + b;
   assign         sum = carry_out ? val_max : raw_sum;

endmodule

module elab_demo
  ( output uwire [4:0] s1, s3,  output uwire [8:0] s2, output uwire [8:0] d,
    input uwire [4:0] a, b,
    input uwire [8:0] c );

   sat_add #(5) a1( s1, a,  b);
   sat_add #(5) a2( s3, a, -b);
   sat_add #(9) a3( s2, {9'b01110} , c);

   assign d = s2 - s1;

endmodule

//////////////////////////////////////////////////////////////////////////////
/// Elaboration-Time Constants


 // :SV: Section 6.20 Constants

 // :Def: Elaboration-Time Constant
 //  - An object declared with the qualifiers:
 //    :Keywords: parameter,localparam,specparam 
 //  - An expression consisting only of constants and a subset
 //    of functions.

 // :Example:
 //
 // Consider the saturating adder from above.

module sat_add_from_above
  // width is an elaboration-time constant because it is a parameter.
  #( int width = 8 )

   ( output uwire [width:1] sum,

     // input a IS NOT an elaboration-time constant, even for instantiation a2.
     input uwire [width:1] a,

     input uwire [width:1] b );

   // Var val_max is an elaboration-time constant because it is
   // declared localparam.
   //
   localparam logic [width:1] val_max = -1;

   // The right-hand-side, width - 1, is a
   //
   localparam int wm1 = width - 1;

   uwire [wm1:0] raw_sum;
   uwire           carry_out;
   assign         { carry_out, raw_sum } = a + b;
   assign         sum = carry_out ? val_max : raw_sum;

endmodule



//////////////////////////////////////////////////////////////////////////////
/// Generate Constructs

// :SV: Chapter 27

 /// :Def: Generate Construct
 //  Verilog statements that are executed during elaboration and
 //  which essentially write new Verilog code.
 //
 /// Important Concepts
 //
 //  Generate statements execute before simulation or synthesis.
 //
 //  Expressions in generate statements can only refer to generate-time
 //    constants (expressions that can be computed in terms of parameters,
 //    literals, and genvars.)

 ///  The Generate Constructs
 //
 //   - Integer object kind for use in generate loops.
 //       :Keyword: genvar
 //        Declares an integer-valued object that can be used as
 //         an iterator in generate loops.
 //        See for construct.
 //
 //   - Loop Generate Constructs
 //       :Keyword: for
 //       :Syntax:  for ( genvar IDENT = INIT; TEST; INCR ) STATEMENT;
 //
 //       Iterator must be of kind genvar.
 //       INIT, TEST, and INCR must be expressions that consist
 //        of genvar's and elaboration-time constants.
 //
 //       STATEMENT can contain assign's and instantiations.
 //       :Sample: for ( genvar i=0; i<9; i++ ) begin assign a[i] = b[w-i]; end
 //
 //   - Conditional Generate Constructs
 //       :Keywords: if,else,case,endcase
 //       if / else
 //       case / endcase



// :Example:
//
// Use of generate for loop to construct a ripple adder.

// First, define a BFA in the usual way.
//
module bfa_structural( output uwire sum, cout, input uwire a, b, cin );
   assign  sum = a ^ b ^ cin;
   assign  cout = a & b | a & cin | b & cin;
endmodule

// Use generate statements to instantiate BFA's.
//
module ripple_4_elab
   ( output [3:0] sum,  output       cout,
     input [3:0] a, b,  input       cin);

   uwire          carry[3:-1];
   assign        carry[-1] = cin;
   assign        cout = carry[3];

   for ( genvar i = 0; i<4; i++ )
      bfa_structural bfa( sum[i], carry[i], a[i], b[i], carry[i-1] );

endmodule

// Instantiate BFA's the old fashioned way.
//
module ripple_4
   ( output [3:0] sum,  output       cout,
     input [3:0] a, b,  input       cin);

   uwire         c0, c1, c2;

   bfa_structural bfa0( sum[0], c0,   a[0], b[0], cin );
   bfa_structural bfa1( sum[1], c1,   a[1], b[1], c0  );
   bfa_structural bfa2( sum[2], c2,   a[2], b[2], c1  );
   bfa_structural bfa3( sum[3], cout, a[3], b[3], c2  );

endmodule

module ripple_w
  #( int w = 16 )
   ( output [w-1:0] sum,   output       cout,
     input [w-1:0] a, b,   input       cin);

   uwire           carry[w-1:-1];
   assign        carry[-1] = cin;
   assign        cout = carry[w-1];

   for ( genvar i = 0; i<w; i++ )
     bfa_structural bfa( sum[i], carry[i], a[i], b[i], carry[i-1] );

endmodule


// :Example:
//
// Miscellaneous correct and incorrect usage of generate statements. 

module gen_good
  #( int option = 0 )
   ( output uwire logic [7:0] ab,abc,
     output var logic [7:0] vab,vabc,
     input uwire logic [7:0] a, b, c );

   for ( genvar i=0; i<8; i += 2 )
     // This *IS NOT* procedural code ... that's why assign is okay.
     begin
        assign ab[i] = a[i];
        assign ab[i+1] = b[i+1];
     end

   `ifdef XXX
   always_comb
     // This is procedural code (because of the always_comb) ...
     // ... that's why assign is *NOT OKAY*.
     for ( int i=0; i<i; i+=2 ) begin
        assign ab[i] = a[i];
        assign ab[i+1] = b[i+1];
     end
   `endif

   always_comb
     // This is procedural code.
     for ( int i=0; i<i; i+=2 ) begin
        vab[i] = a[i];
        vab[i+1] = b[i+1];
     end

   for ( genvar i=0; i<8; i++ )
     begin:myblock
        uwire aab;
        and a1(aab,a[i],b[i]);
        if ( option == 0 )
          or  o1(abc[i],aab,c[i]);
        else
          xor o1(abc[i],aab,c[i]);
     end

endmodule

module gen_bad
   ( output uwire [7:0] ab,abc,
     input uwire option,
     input uwire [7:0] a, b, c );

   for ( genvar i=0; i<8; i += 2 )
     begin
        assign ab[i] = a[i];
        assign ab[i+1] = b[i+1];
     end

   `ifdef SHOW_BAD_CODE
   for ( genvar i=0; i<8; i++ )
     begin:myblock
        uwire aab;
        and a1(aab,a[i],b[i]);
        if ( option == 0 )  /// Bad, because option not constant.
          or  o1(abc[i],aab,c[i]);
        else
          xor x1(abc[i],aab,c[i]);
     end
    `endif

   for ( genvar i=0; i<8; i++ )
     begin:myotherblock
        uwire aab, ior, eor;
        and a1(aab,a[i],b[i]);
        //  if ( option == 0 )  /// Bad, because option not constant.
          or  o1(ior,aab,c[i]);
                //  else
          xor x1(eor,aab,c[i]);
        assign abc[i] = option == 0 ? ior : eor;
     end

endmodule




 /// Elaboration of Tree Structure


// :Example:
//
// Find the minimum of n numbers. Use generate constructs to
// describe a linear- and tree-structured modules.

module min_2
  #( int w = 4 )
   ( output uwire [w-1:0] e_min,
     input  uwire [w-1:0] e_0, e_1 );

   assign e_min = e_0 < e_1 ? e_0 : e_1;

endmodule

 // 

module min_n
  #( int w = 4,
     int n = 8 )
   ( output uwire [w-1:0] e_min,
     input  uwire [w-1:0] e[n] );

   uwire [w-1:0] im[n];

   assign im[0] = e[0];

   for ( genvar i = 1; i < n; i++ )
     min_2 #(w) m( im[i], e[i], im[i-1] );

   assign e_min = im[n-1];

endmodule

 // 

module min_t
  #( int w = 4,
     int n = 8 )
   ( output uwire [w-1:0] e_min,
     input uwire [w-1:0] e [ n-1:0 ] );

   if ( n == 1 ) begin

      assign e_min = e[0];

   end else begin

      localparam int n_lo = n / 2;
      localparam int n_hi = n - n_lo;

      uwire [w-1:0] m_lo, m_hi;

      min_t #(w,n_lo) mlo( m_lo, e[n_lo-1:0] );
      min_t #(w,n_hi) mhi( m_hi, e[n-1:n_lo] );

      min_2 #(w) m2( e_min, m_lo, m_hi);

   end

endmodule

 // 

 // 

 // 

 // 




module pop_1
  (output logic [4:0] p,
   input [15:0] v );

   always_comb begin
      p = 0;
      for ( int i=0; i<16; i++ ) p += v[i];
   end

endmodule

module pop_n
  #( int bits = 16 )
   ( output [4:0] p,
     input [bits-1:0] v );

   if ( bits == 1 ) begin

      assign p = v[0];

   end else begin

      uwire [4:0]         p1, p2;
      pop_n #(bits/2) pi1(.p(p1),.v(v[bits/2-1:0]));
      pop_n #(bits-bits/2) pi2(p2,v[bits-1:bits/2]);
      assign             p = p1 + p2;

   end

endmodule