///  LSU EE 4755 -- Fall 2016 -- Digital Design / HDL
//
/// Verilog Notes -- Synthesis of Comb. Behavioral Code

/// Under Construction
 // Time-stamp: <3 October 2016, 12:10:38 CDT, koppel@cyc.ece.lsu.edu>


/// Contents
 //

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

//////////////////////////////////////////////////////////////////////////////
/// Inference of Combinational Behavioral Code

 /// Restrictions on Code
 //
 //  Must be in an always block.
 //
 //  Sensitivity list must contain all RHS variables.
 //
 //  A variable is either always assigned or never assigned.
 //
 //  A variable can be assigned in at most one block.

 /// Types of Code
 //
 //  -- Assignments. Re-assignments.
 //
 //  -- if / else blocks.  Complete, incomplete.
 //
 //  -- case blocks
 //
 //  -- Loops



 /// Simple Cases:  Variable assigned once.  No control flow statements.


module pie_00( output wire x, y,  input wire a, b, c );

   assign x = ~ ( a ^ b );
   assign y = x & c | b;

endmodule

module pie_01( output logic x, y,  input wire a, b, c );

   always_comb
     begin
        x = ~ ( a ^ b );
        y = x & c | b;
     end
endmodule

// 



 /// Multiple Assignments
//
//  A var cannot be assigned in more than one block.
//  A var can be assigned any number of times in a block.
//
//  Each assignment creates a new wire.
//


// :Example:
//
// The only difference between the two modules below is the name of
// the connection between the XOR and NOT gates. In pie_02 that
// connection is x, in pie_03 that connection is aeb.

module pie_02( output var logic x, y,  input wire logic a, b, c );

   always_comb
     begin
        x = a ^ b;     // Line 1.
        x = ~ x;       // Line 2.
        y = x & c | b; // Line 3.
     end

endmodule

module pie_03( output logic x, y,  input wire a, b, c );

   logic aeb;

   always_comb
     begin
        aeb = a ^ b;
        x = ~ aeb;
        y = x & c | b;
     end

endmodule


module mult_by_11_version_00
  ( output logic [15:0] prod,
    input wire [11:0] a );

   always_comb begin

      prod = a;              // L1
      prod = prod + a * 2;   // L2
      prod = prod + a * 8;   // L3

   end

endmodule



module mult_version_00
  ( output logic [15:0] prod,
    input wire [11:0] a,
    input wire [2:0] b );

   logic  [15:0] pp;
   always_comb begin

      prod = 0;
      pp = b[0] ? a << 0 : 0;
      prod = prod + pp;
      pp = b[1] ? a << 1 : 0;
      prod = prod + pp;
      pp = b[2] ? a << 2 : 0;
      prod = prod + pp;

   end

endmodule

module mult_version_01
  ( output logic [15:0] prod,
    input wire [11:0] a,
    input wire [2:0] b );

   always_comb begin

      prod =  b[0] ? a << 0 : 0;
      prod += b[1] ? a << 1 : 0;
      prod += b[2] ? a << 2 : 0;

   end

endmodule


 /// if statements
//
// Consider: if ( COND ) begin IFPART end else begin ELSEPART end
//
//  - Find all variables assigned in both IFPART and ELSEPART ..
//    .. let VARS denote these variables.
//  - For each v in VARS:
//    - Synthesize a multiplexor.
//    - Label the two inputs and output v.
//    - The select input is connected to COND.

// Consider: if ( COND ) IFPART else if ( COND2 ) IFPART2 else ELSEPART


module addborc
 ( output wire [15:0] x,
   input wire [15:0] a, b, c,
   input wire d );

   logic [15:0] t;

   always_comb begin

      if ( d ) t = b; else t = c;
      x = a + t;
   end
endmodule

// 



module mult_version_02
  #( logic [2:0] b = 3 )
   ( output logic [15:0] prod,
     input wire [11:0] a );

   always_comb begin

      if ( b[0] ) prod = a;  else prod = 0;
      
      prod =  b[0] ? a << 0 : 0;
      prod += b[1] ? a << 1 : 0;
      prod += b[2] ? a << 2 : 0;

   end

endmodule


module mult_version_03
  ( output logic [15:0] prod,
    input wire [11:0] a,
    input wire [2:0] b );

   always_comb begin
      if ( b[0] ) prod = a << 0; else prod = 0;
      if ( b[1] ) prod += a << 1;
      if ( b[2] ) prod += a << 2;
   end

endmodule

module mult_version_03b
  #( int bwidth = 3 )
   ( output logic [15:0] prod,
     input wire [11:0] a,
     input wire [bwidth-1:0] b );

   always_comb begin
      if ( b[0] ) prod = a << 0; else prod = 0;
      for ( int i=1; i<bwidth; i++ )
        if ( b[i] ) prod += a << i;
   end

endmodule

module mult_version_04
  #( logic [2:0] b = 1 )
   ( output logic [15:0] prod,
     input wire [11:0] a );

   always_comb begin
      if ( b[0] ) prod = a << 0; else prod = 0;
      if ( b[1] ) prod += a << 1;
      if ( b[2] ) prod += a << 2;
   end

endmodule

module mult_version_05
   ( output logic [15:0] prod,
     input wire [11:0] a );

   mult_version_03 mm(prod,a,3'd3);

endmodule


// :Example:
//
// Another example.


module addborcb
   ( output logic [15:0] x,
     input wire [15:0] a, b, c,
     input wire d );

   logic [15:0] t;

   always_comb begin
      if ( d ) t = b; else t = c;  // L1
      if ( a < 8 ) t = t + 12;  // L2
      x = a + t;
   end
endmodule

// 



 /// Loop Statements
//
//   :Syntax:   for ( TYPE I = INIT;  TEST;  INCR ) BODY;
//
//   :Example:  for ( int i=0;  i<3;  i++ ) a[i] = b[2-i];
//
//   Synthesizability Rule
//
//   - Number of iterations must be known at elaboration time ..
//     .. including early exits (breaks).
//
//   Inferred Hardware
//
//     Let n denote the number of iterations.
//
//     Make n copies of the loop body.
//
//     In each copy replace I (the iteration variable) with its
//       value at that iteration.


module loop_examples
  #( int sz = 8 )
   ( output logic [sz-1:0] x, y, z,
     input wire [sz-1:0] a, b, c,
     input wire [1:0] d );

   always_comb begin

      // Good. (Though easier ways to do a bitwise exclusive or.)
      //
      for ( int i=0; i<sz; i++ ) x[i] = a[i] + b[i];

      // Bad: Number of iterations depends on c.
      //
      for ( int i=0;  i < c;  i++ ) y[i] = a[i];
      for ( int i=c;  i < sz; i++ ) y[i] = b[i];

      // Also Bad:
      //
      for ( int i=0;  i < sz;  i++ ) begin
         if ( i == c ) break;
         y[i] = a[i];
      end

      // Good: Fixed number of iterations.
      //
      for ( int i=0; i<sz; i++ ) z[i] = i < c ? a[i] : b[i];

   end

endmodule

module the_hard_way
  ( output logic x,
    input wire [3:0] a, b );

   always_comb
     begin
        x = 0;
        for ( int i=0; i<4; i++ ) x = x | ( a[i] ^ b[i] );
        x = ~x;
     end
endmodule

// :


// :Example:
//
// Take sum of selected elements. (Fall 2015 Midterm Exam Problem 2)

module ssum
  #( int n = 3,
     int f = 4,
     int swid = f + $clog2(n) )
   ( output logic [swid-1:0] sum,
     input wire [n-1:0] mask,
     input wire [f-1:0] a[n] );

   always_comb begin

      sum = 0;

      for ( int i=0; i<n; i++ )
        if ( mask[i] ) sum += a[i];

   end

endmodule

// Inferred Hardware (Before Optimization):
// 

// After Some Optimization:
// 


// :Example:
//
// Comparison example. Includes a loop and ifs inside ifs.

module compare
  ( output logic gt, lt,  input wire [2:0] a, b );

   always_comb begin

      gt = 0;
      lt = 0;

      for ( int i=2; i>=0; i-- )

        if ( !gt && !lt ) begin
           if ( a[i] < b[i] ) lt = 1;
           if ( a[i] > b[i] ) gt = 1;
        end
   end

endmodule

// :

// Optimization Plan:
// :

// After Optimization:
// :


module pop_c_good
  #(int width = 128, int bits = $clog2(width+1))
   (output logic [bits:1] pop, input [width-1:0] vector);

   always_comb begin

      pop = 0;
      for ( int i=0; i<width; i++ ) pop += vector[i];

   end

endmodule

module pop_c_bad
  #(int width = 128, int bits = $clog2(width+1))
   (output logic [bits:1] pop, input [width-1:0] vector);

   initial begin

      pop = 0;
      for ( int i=0; i<width; i++ ) pop += vector[i];

   end

endmodule

module b_syn_1
  ( output logic [7:0] x,
    input wire [7:0] a, b,
    input wire c );

   always_comb begin

      // Synthesizes to a mux with c as control.
      //
      if ( c ) x = a + b;
      else     x = a - b;

   end

endmodule

module b_syn_2
  ( output logic [7:0] x,
    input wire [7:0] a, b,
    input wire c );

   always_comb begin

      // Synthesizes to a mux with c as control.
      //
      x = a - b;
      if ( c ) x = a + b;
      //
      // But, bad style because x re-assigned ... makes it harder on humans.

   end

endmodule


module b_syn_bad_1
  ( output logic [7:0] x,
    input wire [7:0] a, b,
    input wire c );

   always_comb begin

      // Not combinational logic because x not always assigned.
      // Shouldn't synthesize when always_comb used.
      // Will synthesize with "always @*", but will not be combinational.
      //
      if ( c ) x = a + b;
   end

endmodule

module b_syn_1
  ( output logic [7:0] x,
    input wire [7:0] a, b,
    input wire c );

   always_comb begin

      // Synthesizes to a mux with c as control.
      //
      if ( c ) x = a + b;
      else     x = a - b;

   end

endmodule