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

/// Contents

 // Review of Module Instantiation
 // Primitives
 // Module Parameter and Port Declarations and Connections
 // Verilog Simulation Basics


/// 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 a few gates
//    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.
//
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
//
// Illustration of the module above:
// 



////////////////////////////////////////////////////////////////////////////////
/// 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 less tedious.
//
//
// That said, the logic primitives are:
//  :Keywords: and,nand,or,nor,xor,xnor,buf,not

 /// Connections to Logic Primitives.
//
//   The first output 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 they are required.

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.

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

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

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

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

endmodule



////////////////////////////////////////////////////////////////////////////////
/// Module Port Declarations and Connections

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

// The ports in and out of a module are declared in the module header.
//
// Connections to these ports 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.

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


 /// Parameter Constants
//
//   Constant values specified in the instantiation of a module.
//   The module header provides a 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.
 //
 //    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.




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



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