///  LSU EE 4755 -- Fall 2023 -- Digital Design / HDL
///
///  Verilog Review

/// Contents

 // Review of Module Instantiation
 // Primitives
 // Module Parameter and Port Declarations and Connections


/// References

// :SV12: 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.
//        This is for those who need to review basic Verilog.


////////////////////////////////////////////////////////////////////////////////
/// Modules and Module Instantiation

// :BV2:, :BV3: 2.10 --  Good description.
// :SV12: 23 Complete details. (Note: ANSI style used in class.)

 /// Practice Problems
//
//   2017 Homework 1 - Multiplexors.
//   2016 Homework 1 Problem 3: BCD number detector.

 /// A module describes a part,
//    can be as simple as one gate -- or even just wire --
//    or more complex than an entire computer.
//
//  A module is defined once (see example) and can be /instantiated/,
//  used, within other modules or recursively.  Examples of instantiation
//  provided further below.
//
 /// Modules are interconnected,
//    using nets (such as uwire).
//    See Module Connections discussion further below.


// :Example:
//
// Definition of a module for the illustrated circuit.
//
// 
//
module pie
  ( output uwire x, y,
    input uwire a, b, c );

   uwire t1, t2;
   xor x1(t1,a,b);   // Instantiation of primitive xor.
   not n1(x,t1);
   and a1(t2,x,c);
   or  o1(y,t2,b);
   //
   /// WARNING: 
   ///   Do not use primitives unless they are absolutely necessary.
   ///   They are a tedious waste of time.

endmodule
//
// The module above instantiates four primitive gates (the XOR, NOT,
// AND, and OR). In the next example a module will be defined in terms
// of other modules.



// :Example:
//
// The two_pie module, below, itself instantiates two pie modules.
//
// Illustration of the module below:
// 
//
module two_pie
  ( output uwire o1, o2,
    input uwire f, g, h, a );

   uwire   i1, i2;

   pie apple (i1, i2,  f,  g,  h );  // Instantiation of module pie.
   pie cherry(o1, o2,  i1, i2, a );  // Instantiation of module pie.

endmodule



////////////////////////////////////////////////////////////////////////////////
/// Primitives
//
// :SV12: Section 28.4, and nearby sections.
//
 /// Primitives are the building blocks for structural code.
//
// They are instantiated like modules, but they are not themselves
// defined.
//
// There are primitives for common logic gates, and for devices
// such as tri-state buffers and transmission gates.
//
 /// In most cases primitives should NOT BE USED because other
 /// code forms are much easier to read and write.
//
// That said, the logic primitives are:
//  :Keywords: and,nand,or,nor,xor,xnor,buf,not

 /// Connections to Logic Primitives.
//
//   The first connection of every logic primitive is its output.
//   In most cases there can be any number of inputs.

// :Example:
//
// Example illustrating different number of inputs.
//
module prim_examples;

   uwire my_output, my_other_output;
   uwire i0, i1, i2, i3, i4, i5, i6, i7, i8, i9,
        i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20, i21, i22, i23,
        i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35, i36, i37,
        i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, i51,
        i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65,
        i66, i67, i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79,
        i80, i81, i82, i83, i84, i85, i86, i87, i88, i89, i90, i91, i92, i93,
        i94, i95, i96, i97, i98, i99;

   // Can have more than two inputs, three in this case.
   nand n1(my_output, i0, i1, i2);

   // Or 100 if that's what you want.
   xor x1(my_other_output, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9,
i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20, i21, i22, i23,
i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35, i36, i37,
i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, i51,
i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65,
i66, i67, i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79,
i80, i81, i82, i83, i84, i85, i86, i87, i88, i89, i90, i91, i92, i93,
i94, i95, i96, i97, i98, i99);

   // Remember: Don't use primitives unless someone is forcing you to.

endmodule



// :Example:
//
// A binary full adder described two ways, using expressions
// (bfa_implicit) and using primitives (bfa_primitive). There is
// no advantage to using primitives.

module bfa_implicit
   ( output uwire sum, cout,
     input uwire a, b, cin );

   // Note "^" is the exclusive or operator.
   assign  sum = a ^ b ^ cin;
   assign  cout = a & b | a & cin | b & cin;

endmodule

module bfa_primitive
   ( output uwire sum, cout,
     input uwire a, b, cin );

   uwire   term001, term010, term100,term111;
   uwire   ab, bc, ac;
   uwire   na, nb, nc;

   /// WARNING: Only fools use primitives when they don't have to.

   not n1( na, a );
   not n2( nb, b );
   not n3( nc, cin );

   and a10( ab, a, b );
   and a11( bc, b, cin );
   and a12( ac, a, cin );

   and a1( term001, na, nb, cin );
   and a2( term010, na, b,  nc );
   and a3( term100, a,  nb, nc );
   and a4( term111, a,  b,  cin );

   or o1( sum, term001, term010, term100, term111 );
   or o2( cout, ab, bc, ac );

   /// WARNING: Only fools use primitives when they don't have to.

endmodule



////////////////////////////////////////////////////////////////////////////////
/// Module Port & Parameter Declarations & Connections

// :SV12: Section 23.2 (declarations) and 23.3 (connections)

// The ports in and out of a module ..
// .. and the parameters that specify the module (such as the number of bits) ..
// .. are declared in the module *header*.
//
// Connections to these ports ..
// .. and values of the parameters ..
// .. are made in a module *instantiation*.
//
// There are two styles for module headers, ANSI and non-ANSI. 
//   ANSI is used in classroom examples.
//   Non-ANSI is used in older versions of Verilog.

 /// Port Declarations
//
//   These specify signals entering or leaving the module,
//    or variables shared with the instantiating module.

 /// Parameter Constant Declarations
//
//   These are constants that are specified in the instantiation of a module ..
//     .. which are used when the module is elaborated (initially processed
//     by the Verilog simulator, synthesis program, or other tool).
//   They might specify the number of bits in a uwire, optional behavior, etc.

module shift_left
  #( int wi = 32,
     int wo = wi )
   //TTT NN FFFF
   ///  ↑↑  Parameter Constant List (Above)  ↑↑

   ///  ↓↓  Port List (Below)                             ↓↓
   ( output uwire [wo-1:0]         shifted,
     input  uwire [wi-1:0]         unshifted,
     input  uwire [$clog2(wo)-1:0] amt );
   //DDDDD  KKKK  SIZE             NAME

   // D: Direction: :Keywords: input,output,inout,ref
   // K: Kind/Type. Can be all kinds of objects. For now: uwire, logic
   // S: Size. Optional. Applies to bit sized types. We'll get to that.
   // N: Identifier for declared port or parameter.

   // T: Data type.
   // F: Default value.  Just a default.

   // Notes:
   //
   //    Parameter constants, such as wi, are often used to specify
   //    the sizes of ports, such as in [wi-1:0] unshifted.
   //
   //    $clog2(i) is the ceiling-log-base-2 ( ⌈log₂ i⌉ ) function. It
   //    is useful because $clog2(i) is the number of bits needed to
   //    represent unsigned integers 0, 1, ..., i-1. For example,
   //    $clog2(4) = 2, and so two bits needed for 0,1,2,3. Also
   //    $clog2(5) = 3, and so three bits needed for 0,1,2,3,4.

   // Note: the default data type for uwire objects is logic.

   assign shifted = unshifted << amt;

endmodule

 /// Port Directions
//
// :Keywords: input,output
//
// These are used with module connections modeling hardware.
// For most assignments a direction must be one of these.
// A port declared *in* cannot be driven or assigned in the module.
// A port declared *out* cannot be driven or assigned in the 
//  instantiating module
//
// :Keyword: inout
//
// Direction inout is used with module connections modeling busses.
// Direction inout will probably never be used in this course.
//
// :Keyword: ref
//
// Direction ref is used with module connections that don't model hardware.
// It works like a reference in C++.
// It will not be used until later in the semester, if at all.


 /// Parameter Constants
//
//   Constant values specified in the instantiation of a module.
//   The module header provides a default value.
//   NEVER ASSUME that a parameter will be at its default value.


 /// Module Connections
//
//   Module connections are specified in a module instantiation.
//
//   There are two ways of specifying these: By Ordered List and By Name
//
module module_connection_example #( int vw = 32, int aw = $clog2(vw+1) )
   ( output [2*vw-1:0] sh1, sh2, input  [vw-1:0] un, input  [aw-1:0] sa );

   /// Port and Parameter Connections are By Ordered List
   //
   shift_left #( vw, 2*vw )  ls1( sh1, un, sa );
   //
   // The port connections must be in the correct order (meaning the
   // order used in the shift_left module declaration).

   /// Port and Parameter Connections are By Name
   //
   shift_left #(.wo(2*vw),.wi(vw)) ls2( .unshifted(un), .amt(sa), .shifted(sh2) );
   //
   // Port and parameter connections can be in any order. The names,
   // such as "amt", must match the name used in the module
   // declaration.
   //
   // Named connections help avoid position mix-ups, especially for
   // modules with dozens of connections.

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Module Port versus Module Parameter
//
 ///  Module ports and module parameters are two very different things.
//
 ///  Ports:  (Of an instantiated module)
 //
 //    Can change value during simulation.
 //
 //    They model wires into and out of a module.
 //
 //
 ///  Parameters:  (Of an instantiated module)
 //
 //    Value never changes.
 //
 //    Default value can be overridden in an instantiation.
 //
 //      It is wrong to assume a parameter is at its default value.
 //
 //    They may or may not model hardware.
 //
 //      In shift_left (above) wi specifies the number of bits in
 //      module port unshifted. Port unshifted models the wires going
 //      in to shift_left. Suppose shift_left were fabricated. Then
 //      you could point to the wires corresponding to unshifted. But
 //      one could not point to wi.

 /// Practice Problems
//
//   2020 Homework 1 -- Variations on the c1x_c2y modules from this section.



 /// Ordinary Multiplier versus Mult by Constant Multiplier
//
// Ordinary Multiplier:
//
//   Multiplier (a) and multiplicand (b) values are provided through
//   input ports.
//
//   Synthesis program will emit hardware for a multiplier.
//
module mult
  #( int w = 8, int w2 = 2 * w )         // <-- These are parameters.
   ( output uwire signed [w2-1:0] prod,  // <-- These are ports.
     input uwire signed [w-1:0] a, b );  // <-- These are ports.

   assign prod = a * b;
   //
   // Note: a and b are both inputs and so they can change any time.

endmodule

// Multiply by Constant Multiplier:
//
//   Multiplier (a) value provided through port, multiplicand (c)
//   value is a constant.
//
//   Synthesis program will start with an ordinary multiplier, and
//   then optimize (simplify) it based on the value of c. In the
//   illustration c is chosen as 1, so the multiplier can be replaced
//   by just wire.
//
module mult_by_c
  #( int w = 8,
     int c = 16,
     int w2 = w+$clog2(c) )
   ( output uwire signed [w2-1:0] prod,
     input uwire signed [w-1:0] a );

   assign prod = a * c;
   //
   // Note: c is a constant, not a module input. It is chosen when
   // the module is elaborated (put together), it can't be changed.

endmodule

 // 




// :Example:
//
// Modules to compute s = c1 x + c2 y,
//   where c1 and c2 are constants.
//
//  c1x_c2y_good:   Good use of parameters to instantiate module.
//  c1x_c2y_okay:   Rely on optimization to clean things up.
//  c1x_c2y_error:  Won't work because of misuse of parameters.
//
//
module c1x_c2y_good
  #( int c1 = 7,
     int c2 = 12,
     int w = 15,
     int w2 = w + $clog2(c1) + $clog2(c2) )
   ( output logic signed [w2-1:0] s,
     input uwire signed [w-1:0] x, y );

   uwire [w2-1:0] p1, p2;

   mult_by_c #(w,c1,w2) m1(p1,x);
   mult_by_c #(w,c2,w2) m2(p2,y);

   assign  s = p1 + p2;

endmodule
// 

module c1x_c2y_okay
  #( int c1 = 7,
     int c2 = 12,
     int w = 15,
     int w2 = w + $clog2(c1) + $clog2(c2) )
   ( output logic signed [w2-1:0] s,
     input uwire signed [w-1:0] x, y );

   uwire [w2-1:0] p1, p2;

   uwire [w:1] C1 = c1, C2 = c2;  // Convert constants to desired size.

   mult #(w,w2) m1(p1, x, C1);
   mult #(w,w2) m2(p2, y, C2);

   assign  s = p1 + p2;

endmodule
// 

`ifdef XXXX
module c1x_c2y_error
  #( int w = 15,
     int w2 = 2 * w )
   ( output logic signed [w2-1:0] s,
     input uwire signed [w-1:0] x, y,
     input int c1, c2 );

   uwire [w2-1:0] p1, p2;

   mult_by_c #(w,c1,w2) m1(p1, x);
   mult_by_c #(w,c2,w2) m2(p2, y);

   assign  s = p1 + p2;

endmodule
`endif


////////////////////////////////////////////////////////////////////////////////
/// Slicing and Array Access Basics

// Covered in more detail in l020-types.v

 /// Vector [aka Packed Array] Declarations
//
//   :Sample: logic [MSB:LSB] my_var;
//   :Sample: logic [10:0] my_var;      // An 11-bit variable. LSB is my_var[0]
//   :Sample: logic [11:1] my_var3;     // Also 11-bits. LSB is my_var3[1].
//   :Sample: logic [0:10] my_var4;     // Also 11-bits. LSB is my_var4[10].
//   :Sample: logic [-1:9] my_var4;     // Also 11-bits. LSB is my_var4[9].
//   :Sample: uwire [MSB:LSB] my_var2;

 /// Array Declarations
//
//   :Sample: int my_int_var [NUM_ELEMENTS];
//   :Sample: int my_ivar [20];   // An array of 20 elements.
//   :Sample: int my_ivar2 [19:0];  // An array of 20 elements.
//   :Sample: int my_ivar3 [20:1];  // An array of 20 elements.
//   :Sample: int my_ivar4 [0:19];  // An array of 20 elements.
//   :Sample: int my_ivar5 [30:11]; // An array of 20 elements.
//   :Sample: int my_ent_var [LAST_IDX:FIRST_IDX];
//   :Sample: logic [MSB:LSB] my_other_var [NUM_ELEMENTS];
//   :Sample: logic [31:0] my_other_var [7];
//   :Sample: logic [MSB:LSB] my_other_var2 [LAST_IDX:FIRST_IDX];

 /// Slicing Operators
//
//   :Syntax: ARRAY[ MSB_IDX : LSB_IDX ]
//   :Sample: ARRAY[ POS+WIDTH-1 : POS ]
//
//    POS: An integer expression.
//    WIDTH: A constant integer expression.
//
//    Returns at array of elements:
//        { ARRAY[POS], ARRAY[POS+1], ..., ARRAY[POS+WIDTH-1] }
//


// :Example:
//
// Use arrays to describe a 2- and n-input multiplexor.
//

// First, a 2-input multiplexor done without arrays, the hard way.
//
module muxtoo
  #( int w = 5 )
   ( output uwire [w-1:0] x,
     input uwire s,
     input uwire [w-1:0] a0, a1 );

   // Without arrays: need to individually identify each input.
   //
   assign x = s == 0 ? a0 : a1;
   //
   // Could you imagine doing this for a 16-input mux?!?
endmodule

// Using arrays it is easier to describe a 2-input mux.
//
module mux2
  #( int w = 5 )
   ( output uwire [w-1:0] x,
     input uwire s,
     input uwire [w-1:0] a[2] );

   // With arrays: just use the array index operator.
   //
   assign x = a[s];
   //
   // It's  that easy!
endmodule

// Using arrays it is much easier to describe an n-input mux.
//
module muxn
  #( int w = 5, n = 10, ws = $clog2(n) )
   ( output uwire [w-1:0] x,
     input uwire [ws-1:0] s,
     input uwire [w-1:0] a[n] );

   // This works for any positive value of n.
   //
   assign x = a[s];

endmodule





////////////////////////////////////////////////////////////////////////////////
/// Module Contents

// :SV12: 23.2.4 Complete details

`ifdef xxx
 /// Typical Module Contents
//
//   The contents below come after the port and parameter declarations.
//
 ///  - Object Declarations
//     Used for wires and variables.
//
       uwire [7:0] my_wire, my_other_wire;
       logic [15:0] my_var, my_other_var;
//
//     Basic usage already covered.
//     Details in l020-types.v


 ///  - Constant Declarations
//     Used for constants.  To make code more readable.
//
       localparam int my_value_cannot_change = 20;
       localparam logic [100:0] any_type_works = 123;
//
//     Basic usage just covered.
//     Details in l025-gen-elab.v


 ///  - Module and Primitive Instantiations
//     So we don't need to put all our Verilog in one big module.
//
       mod_name #(param_val) instance_name( connection1, connection2 );
       and my_professor_says_not_to_use_these(x,a,b);
//
//     Details covered earlier in this set.


 ///  - Continuous Assignments
//     These execute whenever right-hand-side objects change.
//
       assign my_wire = my_var + my_other_var;
       uwire [15:0] this_is_NOT_an_initialization = my_wire + my_other_wire;
//
//     Basic usage covered.
//     Timing information in l007-simulation-basic.v


 ///  - Procedural Code
//     Several kinds. They differ in when code executes.
//
       initial begin my_var = 1; my_other_var = my_var * 7; end
       always_comb begin $display("My var val: %0h\n", my_other_var); end
//
//     Basics covered soon.


 ///  - Generate Statements
//     These ARE NOT procedural statements. Will be covered later.
//
       if ( i_must_be_constant < such_as_a_param )
         mult sometimes(a,b,c);
       for ( genvar i=0; i<10; i++ )
         assign a[i] = b[i] + c[i];
//
//     Not yet covered.
//     Will be covered in l025-gen-elab.v

`endif







module two_pie_demo;

   logic f, g, h, a;
   uwire  o1, o2;

   two_pie our_two_pie(o1,o2, f,g,h,a);

   initial begin

      f = 0;
      g = 0;
      h = 0;
      a = 0;

      #1;

      f = 1;

   end

endmodule