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

/// Contents

 // Review of Module Instantiation
 // Verilog Simulation Basics
 // Primitives
 // Module 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 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:
// 


//////////////////////////////////////////////////////////////////////////////
/// Verilog Simulation Basics
//
//  :SV12: Section 4.3.  Complete details, not the basics.
//
//   Simulation is the "running" of a Verilog description.
//
 /// Structural code executes in reaction to changes in signal values.
//
//   Here's a simplified description:
//
//     An event queue (a to-do list) contains update events,
//       which are signals that have changed or will change.
//
//     An update event (signal) from the queue is dequeued (removed),
//       or if the queue is empty we're done. 
//     Suppose signal f from two_pie is dequeued.
//
//     The simulator will find the primitive gates and behavioral
//       constructs (assign, always, etc) that the signal connects
//       to. 
//     In the example above, f connects to primitive gate x1 in apple.
//
//     The simulator will evaluate code associated with the primitive
//       or behavioral construct. As a result of this evaluation new
//       update events (signal changes) may be generated and placed in
//       the event queue.

//     In the example above, t1 in apple changes value.
//
//     Go to the first step.


// :Example:
//
// Possible order of execution for two_pie given a change in f:

// Dequeued     Evaluated Item      New Update Events
// --------     --------------      -----------------
// f = apple.a 
//              -> xor apple.x1,    changing apple.t1
//
// apple.t1
//              -> not apple.n1,    changing apple.x.
//
// apple.x = i1 = cherry.a
//              -> and apple.a1,    changing apple.t2
//              -> xor cherry.x1,   changing cherry.t1
// Note: two values have changed.
//
// apple.t2
//               -> or apple.o1,    changing apple.y
//
// cherry.t1
//               -> not cherry n1,  changing cherry.x
//
// Subsequent execution not shown.

 /// Notes:
//
//   It's easy to create infinite loops.
//
//   It is possible to add timing controls to affect the ordering.
//
 ///  The execution order above is not affected by instantiation order.
//      For example swapping the position of apple and cherry would
//      have no affect.

module pie_two
  ( output uwire o1, o2,
    input uwire f, g, h, a );

   uwire   i1, i2;
   pie cherry(o1, o2,  i1, i2, a );
   pie apple (i1, i2,  f,  g,  h );        // Instantiation of module pie.
   // Note: This module, pie_two, is equivalent to module two_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 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 Connections

// :SV12: Section 23.2

// The connections in and out of a module are declared in the module header.
//
// 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 Connections
//
//   These specify signals entering or leaving the module,
//    or variables shared with the instantiating module.

 /// Parameter Constant Connections
//
//   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 [1:wi] unshifted,
     input  uwire [wi:1] unshifted,
     input  uwire [wi+1:2] unshifted,
     input  uwire [wi-1:0] unshifted,
     input  uwire [$clog2(wo):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.

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

   assign shifted = unshifted << amt;

endmodule

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


// :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.
//
//
// Constructed from two kinds of multiplier module:
//   mult:      Ordinary multiplier.
//   mult_by_c: Multiplies by a constant.

module mult
  #( int w = 8,
     int w2 = 2 * w )
   ( output uwire signed [w2-1:0] prod,
     input uwire signed [w-1:0] a,
     input uwire signed [w-1:0] b );

   assign prod = a * b;

endmodule

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;

endmodule

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;

   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