///  LSU EE 4755 -- Fall 2021 -- Digital Design / HDL
//
/// Synthesis Hints and Assertions


 /// References
//
// Genus HDL Modeling Guide, August 2021


///////////////////////////////////////////////////////////////////////////////
/// Rationale for Assertions and Constraints

 /// :Def: Hint
 //  A suggestion of what some tool should do.
 //  For example, to use a tech library's multiplexor rather than basic gates.
 //
 //  Hints typically impact cos and performance, but not correctness.
 //  That is, ignoring a good hint will hurt cost and performance but
 //  will not affect correctness.

 /// :Def: Assertion
 //  A description using some mechanism ..
 //  .. of something which can be assumed to be true.
 //
 //  Assertions impact cost, performance, *and* correctness.
 //
 //  Ignoring a good assertion will result in lower cost or performance.
 //  Using a bad assertion CAN RESULT IN INCORRECT hardware.
 //
 //  Note: this not the same as the C library assert macro.
 //  Such as. assert( x > 0 );

 /// :Def: Constraint
 //  A limit on certain values, such as arrival times of signals.

 /// Specifying Hints and Assertions
 //
 //  Most an be specified using a pragma.
 //  Some can be specified using SystemVerilog keywords.
 //  Some can be specified using synthesis program settings.


///////////////////////////////////////////////////////////////////////////////
/// Synthesis Pragmata
//
 /// :Def: pragma
 //  Something placed in source code written in language X ..
 //  .. which is not defined by language X ..
 //  .. but is understood by compilers or other tools for language X.
 //
 //  :Def: pragmata
 //  Plural of pragma.

 /// Using Pragmata
 //
 //  Since a pragma is not recognized by the language it must be specified
 //  in some special way.
 //
 //  Typically they are included in comments. This is true for SystemVerilog.
 //
 //  cadence PRAGMA_NAME PRAGMA_ARGS...



///////////////////////////////////////////////////////////////////////////////
/// Pragmata for Case Statements
//
// 


 /// full_case

typedef enum
  { Cmd_Reset = 0, Cmd_Nop=1, Cmd_Write=2, Cmd_Shift=3,
    Cmd_Reserved = 4, Cmd_Also_Reserved = 5,
    Cmd_SIZE } Command;

module fc_demo
  #( int wi = 8 )
   ( output logic [wi-1:0] dout,
     input uwire [3:0] cmd,
     input uwire [wi-1:0] din,
     input uwire clk );

   always_ff @( posedge clk ) begin

      // Tell synthesis program that only the four values of cmd are
      // possible. 
      //
      case ( cmd ) // cadence full_case

        Cmd_Reset: dout <= 0;
        Cmd_Nop: dout <= dout + 1;
        Cmd_Write: dout <= din;
        Cmd_Shift: dout <= dout << din;

      endcase
      //
      /// With full_case:
      //
      //  The synthesis program won't check whether cmd[2] is zero.
      //  Register dout will not need an enable signal.
      //
      /// If full_case were not used:
      //
      //  Synthesis program would expect all 8 possible values of cmd,
      //  and so would need to make sure that bit cmd[2] were zero for
      //  the four cases above, increasing the cost slightly.

   end

endmodule


 /// parallel_case

module pc_demo
  #( int kwid = 8 )
   ( output logic [7:0] mindex,
     input [kwid:1] key,
     input [kwid:1] keys [4] );

   // Use parallel_case to indicate that exactly one case is expected
   // to be true. That is, if keys[1] == key then we don't expected
   // keys[0] == key, keys[2]=key, nor keys[3]=key.
   //
   always_comb
     case ( key ) // cadence parallel_case
       keys[0]: mindex = 0;
       keys[1]: mindex = 1;
       keys[2]: mindex = 2;
       keys[3]: mindex = 3;
       default: mindex = -1;
     endcase

   // Note:
   //  unique case: At most one match.
   //  priority case: Execute the first match only.

endmodule

module quick;

   always_comb begin

      if ( a < 1 ) begin foo; end
      else if ( b > 3 ) begin bar; end

      if ( b > 3 ) begin bar; end

      if ( a >= 1 && b > 3 ) begin bar; end


   end

endmodule


///////////////////////////////////////////////////////////////////////////////
/// Inlining Pragmata

module use_mults
  #( int w = 64, logic [w-1:0] plr2 = 123 )
   ( output uwire [2*w-1:0] p10, p11, p12, p20, p21, p22,
     input uwire [w-1:0] cand, plr1 );

   /// None of the Inputs are Constant
   //
   mult_linear_inlined #(w)                       m10( p10, cand, plr1 );
   mult_linear #(w)                               m11( p11, cand, plr1 );
   mult_linear #(w) /* cadence inline_instance */ m12( p12, cand, plr1 );
   //
   // All three multipliers above should have the same cost and delay.
   // The synthesis time spent on the two inlined modules was wasted.

   /// The Multiplier (plr2) is a Constant
   //
   mult_linear_inlined #(w)                       m20( p20, cand, plr2 );
   mult_linear #(w)                               m21( p21, cand, plr2 );
   mult_linear #(w) /* cadence inline_instance */ m22( p22, cand, plr2 );
   //
   // The inlined multipliers, m20 and m22, will be fast and lower
   // cost than m10 because they were optimized for the constant
   // (plr2). The synthesis program may or may not inline m21,
   // depending on synthesis effort options. If m21 is not inlined it
   // will be the same cost and speed as m10,m11, and m12, which is
   // slower and more costly than m20 and m22.

endmodule

module mult_linear_inlined
  #(int w = 16)
   (output logic [2*w-1:0] prod, input uwire [w-1:0] cand, plier);

   // Force each instantiation of this module to be optimized separately.
   //
   // cadence inline

   uwire [2*w-1:0] b[w:0];

   assign b[0] = 0;
   assign prod = b[w];

   for ( genvar pos = 0;  pos < w;  pos++ ) begin
      uwire [2*w-1:0] pp = plier[pos] ? cand << pos : 0;
      assign b[pos+1] = pp + b[pos];
   end

endmodule


module mult_linear
  #(int w = 16)
   (output logic [2*w-1:0] prod, input uwire [w-1:0] cand, plier);

   uwire [2*w-1:0] b[w:0];

   assign b[0] = 0;
   assign prod = b[w];

   for ( genvar pos = 0;  pos < w;  pos++ ) begin
      uwire [2*w-1:0] pp = plier[pos] ? cand << pos : 0;
      assign b[pos+1] = pp + b[pos];
   end

endmodule


//////////////////////////////////////////////////////////////////////////////
/// Component Selection


 /// map_to_mux
//
// Try to use a tech library multiplexor.


//////////////////////////////////////////////////////////////////////////////
/// Don't Bother With Optimization


 /// Suppress DCE (dead-code elimination) of Nets and Registers
//
// keep_signal_name FOO
// preserve_sequential (Don't eliminate reg.)

module no_dce_demo_r
  ( output logic [7:0] val,
    input uwire clk );

   // Tell synthesis program to infer a register for next_val whether
   // it's live-out or not.
   logic [7:0] next_val /* cadence preserve_sequential */ ;

   always_ff @( posedge clk ) begin

      next_val = val + 1;
      val = next_val;

   end

endmodule



// Useful Pragmata:


// translate_on
// translate_off

//  Set attribute hdl_report_case_info to true to..

//  map_to_mux
//   Use a mux in the tech library.

// preserve_sequential
//  Don't delete flip flops during optimization.
// syn_keep
//  Used after a net declaration.
//  The logic driving that net is protected from DCE.

// inline
//  Used in a module.
//  Indicates that instances of this module will be inlined.

// Look at:

// black_box
// infer_multibit
// one_cold
// script_begin
// synthesis_on