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

 // Time-stamp: <30 September 2016, 18:30:47 CDT, koppel@cyc.ece.lsu.edu>

 // 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 4720, 4730, and 4740.


//////////////////////////////////////////////////////////////////////////////
/// 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 wire [width:1] sum,
     input wire [width:1] a, b );

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

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

endmodule

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

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

   assign d = s2 - s1;

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
 //
 //   - Loop Generate Constructs
 //       for ( genvar IDENT = INIT; TEST; INCR )
 //
 //   - Conditional Generate Constructs
 //       if / else
 //       case / endcase


module gen_good
  #( int option = 0 )
   ( output wire [7:0] ab,abc,
     input wire [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

   for ( genvar i=0; i<8; i++ )
     begin:myblock
        wire 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 wire [7:0] ab,abc,
     input wire option,
     input wire [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

   for ( genvar i=0; i<8; i++ )
     begin:myblock
        wire 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 o1(abc[i],aab,c[i]);
     end

   for ( genvar i=0; i<8; i++ )
     begin:myotherblock
        wire 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 o1(eor,aab,c[i]);
        assign abc[i] = option == 0 ? ior : eor;
     end

endmodule


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


module bfa_structural
   ( output wire sum, cout,
     input wire a, b, cin );

   assign  sum = a ^ b ^ cin;
   assign  cout = a & b | a & cin | b & cin;

endmodule

 /// Four-Bit Ripple Adder
//
// Instantiate four bfa's to make a 4-bit adder.
//
module ripple_4
   ( output [3:0] sum,
     output       cout,
     input [3:0] a,
     input [3:0] b,
     input       cin);

   wire         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_4
   ( output [3:0] sum,
     output       cout,
     input [3:0] a, b,
     input       cin);

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

   // Runs at elaboration time.
   // Runs at elaboration time.
   // Runs at elaboration time.
   // Runs at elaboration time.
   for ( genvar i = 0; i<4; i++ )
      bfa_structural bfa(sum[i], carry[i], a[i], b[i], carry[i-1]);


endmodule

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

   wire          carry[width-1:-1];
   assign        carry[-1] = cin;
   assign        cout = carry[width-1];

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

endmodule


 /// Elaboration of Tree Structure


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

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

   end

endmodule