/// EE 4755 - Digital Design Using HDLs
//
 ///  Introductory Review


//////////////////////////////////////////////////////////////////////////////
/// This Note/Demo Set

 /// Prerequisite
 //
 //  Some familiarity with Verilog.
 //  Familiarity with digital design.

 /// Goal
 //
 //  Refresh your memory of Verilog, Verilog simulation, and synthesis.
 //  Exposure to some SystemVerilog features.


//////////////////////////////////////////////////////////////////////////////
/// References
//

 /// Web page with references for this class:
 //  http://www.ece.lsu.edu/v/ref.html

 /// :SV12: SystemVerilog -- IEEE Std 1800-2012
 //  http://standards.ieee.org/getieee/1800/download/1800-2012.pdf
 //
 //  The current Verilog language standard.
 //  The primary Verilog reference for this course.

 /// :BV3:  Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed.
 //
 //  The text used in LSU EE 2740.
 //  Includes material on logic design and elementary Verilog.



//////////////////////////////////////////////////////////////////////////////
/// Logical Right Shifter
//

 /// Logical Right Shifter Informal Description
//
// This should be something everyone has seen before.
//
// There are two inputs, amt and din
// There is one output: dout
// The value at dout is set to the value at din right-shifted by amt bits.
// Zeros are placed in vacated positions.
//

// 

// :Example: Behavior of right shift.
//
// Input - amt  : 1
// Input - din  : 01101001
// Output- dout : 00110100
//
// Input - amt  : 3
// Input - din  : 01101001
// Output- dout : 00001101


 /// Terminology
//
//   w - Number of bits in din and dout.



 /// Things to Review and Think About
//
//    What should the hardware look like?
//
//    How can we describe the shifter in Verilog?
//
//    What is the relationship between the Verilog description of the
//    shifter and the hardware that's synthesized?


 /// How to Implement a Shifter in Hardware
//
//
//   Brute Force Method (Usually not a good idea)
//
//     Use a multiplexor with w inputs, each of w bits.
//     Cost is about 3w2 before optimization.

// 

//
//   Efficient Method, The Logarithmic Shifter
//
//     Use lg(w) multiplexors, each with two w-bit inputs.
//     Cost is about 6 w lg(w).



//////////////////////////////////////////////////////////////////////////////
/// Logical Right Shifter Implementations

// Appearing below are several different ways of writing a logical
// right shift module in Verilog. All of them are correct but when
// coding style is taken into account some are better than others.
//
// Coding style is important because the cost of designing a system
// includes the time it takes engineers to read HDL descriptions
// and to find bugs in HDL.


// :Example: Simple Behavioral Shifter Description
//
// This description is good for an ordinary shifter. We are relying on
// the synthesis program to do a good job with the logical right shift
// operator, >>.
// 
module shift_right_operator
  ( output wire [15:0] shifted,
    input wire [15:0] unshifted,
    input wire [3:0] amt );

   // Note: Reasonable code.  We expect synthesis prog to DTRT here.
   //
   assign shifted = unshifted >> amt;

endmodule


// :Example: Unnecessarily Complicated Behavioral Shifter Description
//
// This description is correct but unnecessarily complicated and so
// should not be used. It is also not synthesizable for reasons to be
// covered later. (If you must know: it's because the number of
// iterations in the loops depends upon a non-constant value, the
// module amt input.)
//
module shift_right_behavioral
   ( output logic [15:0] shifted,
     input wire [15:0] unshifted,
     input wire [3:0] amt );

   localparam int width = 16;

   always_comb begin

      // Correct, but more complicated than it needs to be. Also,
      // cannot be synthesized for reasons that we'll cover later.
      //
      for ( int i=0; i<width-amt; i++ ) shifted[i] = unshifted[i+amt];
      for ( int i=width-amt; i<width; i++ ) shifted[i] = 0;

   end

endmodule


// :Example: Unnecessarily Complicated Behavioral Shifter Description
//
// This description is correct but unnecessarily complicated and so
// should not be used.
//
module shift_right_mux_b
   ( output logic [15:0] shifted,
     input wire [15:0] unshifted,
     input wire [3:0] amt );

   always_comb begin

      // Correct, but susceptible to accidental changes. Loose bits sink chips.
      //
      case ( amt )
        0: shifted = unshifted;
        1: shifted = { 1'b0, unshifted[15:1] };
        2: shifted = { 2'b0, unshifted[15:2] };
        3: shifted = { 3'b0, unshifted[15:3] };
        4: shifted = { 4'b0, unshifted[15:4] };
        5: shifted = { 5'b0, unshifted[15:5] };
        6: shifted = { 6'b0, unshifted[15:6] };
        7: shifted = { 7'b0, unshifted[15:7] };
        8: shifted = { 8'b0, unshifted[15:8] };
        9: shifted = { 9'b0, unshifted[15:9] };
        10: shifted = { 10'b0, unshifted[15:10] };
        11: shifted = { 11'b0, unshifted[15:11] };
        12: shifted = { 12'b0, unshifted[15:12] };
        13: shifted = { 13'b0, unshifted[15:13] };
        14: shifted = { 14'b0, unshifted[15:14] };
        15: shifted = { 15'b0, unshifted[15:15] };
      endcase

   end

endmodule


// :Example: Unnecessarily Complicated Structural Shifter Description
//
// This description is correct but unnecessarily complicated and so
// should not be used.
//
module shift_right_mux_s
   ( output wire [15:0] shifted,
     input wire [15:0] unshifted,
     input wire [3:0] amt );

   // Correct, but susceptible to accidental changes.
   //
   mux16 m( shifted, amt,
            unshifted,
            { 1'b0, unshifted[15:1] },
            { 2'b0, unshifted[15:2] },
            { 3'b0, unshifted[15:3] },
            { 4'b0, unshifted[15:4] },
            { 5'b0, unshifted[15:5] },
            { 6'b0, unshifted[15:6] },
            { 7'b0, unshifted[15:7] },
            { 8'b0, unshifted[15:8] },
            { 9'b0, unshifted[15:9] },
            { 10'b0, unshifted[15:10] },
            { 11'b0, unshifted[15:11] },
            { 12'b0, unshifted[15:12] },
            { 13'b0, unshifted[15:13] },
            { 14'b0, unshifted[15:14] },
            { 15'b0, unshifted[15:15] } );

endmodule

module mux16
  ( output logic [15:0] x,
    input wire [3:0] select,
    input wire [15:0] a0, a1, a2, a3, a4, a5, a6, a7,
    input wire [15:0] a8, a9, a10, a11, a12, a13, a14, a15 );

   // Correct, but susceptible to accidental changes.
   //
   always_comb
     case ( select )
       0: x = a0;
       1: x = a1;
       2: x = a2;
       3: x = a3;
       4: x = a4;
       5: x = a5;
       6: x = a6;
       7: x = a7;
       8: x = a8;
       9: x = a9;
       10: x = a10;
       11: x = a11;
       12: x = a12;
       13: x = a13;
       14: x = a14;
       15: x = a15;
     endcase

endmodule

// :Example: Structural Description Of Logarithmic Shifter
//
// This is a structural description of a particular way of building a
// w-bit shifter, one using lg w stages, each stage consisting of a
// multiplexor. Though it is far simpler than the 16-input mux
// variations, it is still more complicated than it needs to be. A
// better solution would use what's called a generate loop.
//
module shift_right_logarithmic
  ( output wire [15:0] sh,
    input wire [15:0] s0,
    input wire [3:0] amt );

   wire [15:0] s1, s2, s3;

   // Shift by either 0 or 1 bits.
   //
   mux2 st0( s1, amt[0], s0, {1'b0, s0[15:1]} );

   // Shift by either 0 or 2 bits.
   //
   mux2 st1( s2, amt[1], s1, {2'b0, s1[15:2]} );

   // Shift by either 0 or 4 bits.
   //
   mux2 st2( s3, amt[2], s2, {4'b0, s2[15:4]} );

   // Shift by either 0 or 8 bits.
   //
   mux2 st3( sh, amt[3], s3, {8'b0, s3[15:8]} );

endmodule

module mux2
  ( output wire [15:0] x,
    input wire select,
    input wire [15:0] a0, a1 );

    assign x = select ? a1 : a0;

endmodule



//////////////////////////////////////////////////////////////////////////////
/// Review Concepts
//

 /// Module declaration

 /// Variable v. Net

 /// Continuous assignment.

 /// always, initial

 /// Module Instantiation

// Simulation

// Testbenches

// Synthesis

// Synthesis optimization goals.

// Scripting



//////////////////////////////////////////////////////////////////////////////
/// Synthesis
//

 /// Synthesis Software
//
//   Cadence Encounter(R) RTL Compiler
//
//

 /// Elaboration [of a Top-Level Module]
 //
 //  The process of generating and gathering all the HDL needed for
 //  the top-level module.
 //
 //  Start with some top-level module (shift_right2) ..
 //  .. gather instantiated modules ..
 //  .. and perform elaboration on them.
 //
 //  For those who know what "generate statements" are:
 //   Generate statements are executed during elaboration.


 /// Synthesis Target
 //
 //  The kind of thing you want to make. For example,
 //
 //  ASIC
 //  FPGA.

 /// Inference
 //
 //  Figuring out what hardware to use for a given piece of Verilog.


 /// Mapping [of one piece of hardware to another]
 //
 //  The replacing one piece of hardware (Verilog module or primitive)
 //  with another.


 /// Generic Target

 /// Mapped (To Technology) Target

 /// Area and Timing

 /// Design Constraints

 /// Scripting


// read_hdl shifter.v
// elaborate shift_right2
// gui_show
// synthesize -to_generic -effort high
// synthesize -to_mapped -effort high
// report area
// report timing
// define_clock -name clk -period 100
// external_delay -clock clk -output 0 [find /designs/*/ports_out/ -port *]
// external_delay -clock clk -input 0 [find /designs/*/ports_in/ -port *]
// synthesize -to_mapped -effort high
// report timing
// report area


//////////////////////////////////////////////////////////////////////////////
/// Testbench Code
//
//  The code below instantiates shift_right1 and shift_right2,
//  provides test inputs and verifies the outputs.
//

// cadence translate_off

module testbench();

   uwire logic [15:0] sout1, sout2;
   logic [15:0] sin;
   logic [3:0] amt;

   shift_right_behavioral my_sr1(sout1, sin, amt);
   shift_right_logarithmic my_sr2(sout2, sin, amt);

   // Width of shifters' input and output.
   // The parameter is used only by this testbench.
   //
   localparam int width = 16;
   //
   // To keep things simple the shifter modules themselves are written
   // with a hardcoded width of 16 bits. That's bad style since
   // changing the width would be tedious and error prone. (The
   // shifter modules could have used a parameter to specify the width
   // or a user-defined type.)

   // Provide names for the modules for use in error messages.
   //
   localparam string name[2] = '{"shift_right1", "shift_right2"};

   localparam tests_per_sa = 100;
   localparam num_tests = width * tests_per_sa;

   initial begin

      // Count of errors for each module.
      //
      automatic int err_count[2] = '{0,0};
      //
      // Note: The automatic qualifier is needed so that the initialization
      // could appear on the same line as the declaration.

      // Number of test inputs (stimuli).
      //
      automatic int test_count = 0;

      // Provide one test pattern per shift amount.
      //
      for ( int i=0; i<num_tests; i++ ) begin

         int shadow_sout;
         test_count++;

         sin = $random;
         amt = i / tests_per_sa;

         shadow_sout = sin >> amt;

         #1;

         // Check the output of each Module Under Test.
         //
         foreach ( name[ mut ] ) begin

            automatic logic [15:0] sout = mut == 0 ? sout1 : sout2;

            if ( shadow_sout !== sout ) begin
               err_count[mut]++;
               if ( err_count[mut] < 5 )
                 $display
                   ("MUT %s wrong result for 0x%h >> %d:  0x%h != 0x%h (correct)\n",
                    name[mut], sin, amt, sout, shadow_sout);
            end

         end

      end

      $display("Ran %d tests, %d, %d errors found.\n",
               test_count, err_count[0], err_count[1]);

   end

endmodule

// cadence translate_on