/// LSU EE 4755 -- Fall 2019 -- Digital Design / HDL /// /// Elaboration, Parameters, and Generate Constructs // Under construction. /// References // :SV12: IEEE 1800-2012 -- The SystemVerilog Standard // :SV17: 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. ////////////////////////////////////////////////////////////////////////////// /// Elaboration // :Def: Elaboration [of a module] // The process of locating instantiated modules, applying parameter // values, and running generate expressions. It is roughly analogous // to preprocessig in C. // :Def: Design Hierarchy // Details about the design elaborated from some top-level modules // constructed and used by a Verilog tool, such as simulator, synthesis // program, etc. // // Cadence Incisive uses program ncelab for elaboration. (It is // automatically called by other tools.) // // Using Genus, the design hierarchy can be explored in the // command-line interface using filesystem-like commands vls and vcd, // which, after the initial 'v' is removed, work like their Unix // counterparts. Using the Genus GUI the design hierarchy is visible // in the Design Browser pane. // :Example: // // Module sat_add is instantiated three times by elab_demo, at two // different sizes. // // After elaborating elab_demo, the design hierarchy will have four // modules: elab_demo, two sat_add's at 5 bits, and one at 9 bits. module sat_add #( int width = 8 ) ( output uwire [width:1] sum, input uwire [width:1] a, b ); localparam logic [width:1] val_max = -1; // Or ( 1 << width ) - 1 uwire [width:1] raw_sum; uwire carry_out; assign { carry_out, raw_sum } = a + b; assign sum = carry_out ? val_max : raw_sum; endmodule module elab_demo ( output uwire [4:0] s1, s3, output uwire [8:0] s2, output uwire [8:0] d, input uwire [4:0] a, b, input uwire [8:0] c ); sat_add #(5) a1( s1, a, b); sat_add #(5) a2( s3, a, -b); sat_add #(9) a3( s2, {9'b01110} , c); assign d = s2 - s1; endmodule ////////////////////////////////////////////////////////////////////////////// /// Elaboration-Time Constants // :SV12: Section 6.20 Constants // :Def: Elaboration-Time Constant // - An object declared with the qualifiers: // :Keywords: parameter,localparam,specparam,genvar // - A parameter declared in a module header (using #). // - An expression consisting only of constants and a subset // of functions. // :Example: // // Consider the saturating adder from above. module sat_add_from_above #( int width = 8 ) // width is an elaboration-time constant because it is a parameter. ( output uwire [width:1] sum, // input a IS NOT an elaboration-time constant, even for instantiation a2. input uwire [width:1] a, input uwire [width:1] b ); // Var val_max is an elaboration-time constant because it is // declared localparam. // localparam logic [width:1] val_max = -1; // The right-hand-side, width - 1, is an elaboration-time constant // because it is an expression consisting only of elaboration-time // constants (just width in this case), operators (just - in this // case), and constant functions (no functions here). // localparam int wm1 = width - 1; uwire [wm1:0] raw_sum; uwire carry_out; assign { carry_out, raw_sum } = a + b; assign sum = carry_out ? val_max : raw_sum; endmodule ////////////////////////////////////////////////////////////////////////////// /// Generate Constructs // :SV12: Chapter 27 /// :Def: Generate Construct // Verilog statements that are executed during elaboration and // which essentially write new Verilog code. // /// Important Concepts // // Generate statements execute before simulation or synthesis. // // Expressions in generate statements can only refer to generate-time // constants (expressions that can be computed in terms of parameters, // literals, and genvars.) /// The Generate Constructs // // - Integer object kind for use in generate loops. // :Keyword: genvar // Declares an integer-valued object that can be used as // an iterator in generate loops. // See the "for" construct. // // - Loop Generate Constructs // :Keyword: for // :Syntax: for ( genvar IDENT = INIT; TEST; INCR ) STATEMENT; // // Iterator must be of kind genvar. // INIT, TEST, and INCR must be expressions that consist // of genvar's and elaboration-time constants. // // STATEMENT can contain assign's and instantiations. // :Sample: for ( genvar i=0; i<9; i++ ) begin assign a[i] = b[w-i]; end // // - Conditional Generate Constructs // :Keywords: if,else,case,endcase // if / else // case / endcase /// :Practice Problems: // // 2017 Midterm Exam Problem 5b -- Correct the use of generate statements. // https://www.ece.lsu.edu/v/2017/mt.pdf // https://www.ece.lsu.edu/v/2017/mt_sol.pdf // :Example: // // Use of generate for loop to construct a ripple adder. // // First, define a BFA in the usual way. // module bfa_structural( output uwire sum, cout, input uwire a, b, cin ); assign sum = a ^ b ^ cin; assign cout = a & b | a & cin | b & cin; endmodule // // Here is how a ripple adder is constructed without generate statements: // module ripple_4 ( output uwire [3:0] sum, output uwire cout, input uwire [3:0] a, b, input uwire cin); uwire c0, c1, c2; bfa_structural bfa0( sum[0], c0, a[0], b[0], cin ); bfa_structural bfa1( sum[1], c1, a[1], b[1], c0 ); bfa_structural bfa2( sum[2], c2, a[2], b[2], c1 ); bfa_structural bfa3( sum[3], cout, a[3], b[3], c2 ); // // How tedious! What if we need a 16-bit ripple adder? Or 64? 128? endmodule // // Use of generate statements to instantiate BFA's .. // .. to construct 4-bit ripple adder. // module ripple_4_elab ( output uwire [3:0] sum, output uwire cout, input uwire [3:0] a, b, input uwire cin); uwire carry[3:-1]; assign carry[-1] = cin; assign cout = carry[3]; for ( genvar i=0; i<4; i++ ) bfa_structural bfa( sum[i], carry[i], a[i], b[i], carry[i-1] ); // /// Please make sure that you understand what the loop above is doing: /// It is instantiating (creating) four BFA's .. /// .. IT IS NOT using the same BFA four times. endmodule // // Use of generate statements to instantiate BFA's .. // .. to construct a ripple adder of any size (using parameter w). // module ripple_w #( int w = 4 ) ( output uwire [w-1:0] sum, output uwire cout, input uwire [w-1:0] a, b, input uwire cin); uwire carry[w-1:-1]; assign carry[-1] = cin; assign cout = carry[w-1]; for ( genvar i = 0; i 1 ) begin // Recursive case. uwire [w-1:0] xlo, xhi; // Work, a, is split into a[n/2-1:0] and a[n-1:n/2}. // Recursive instantiations. // simple_tree #(n/2,w) st_lo( xlo, a[0:n/2-1] ); simple_tree #(n-n/2,w) st_hi( xhi, a[n/2:n-1] ); // // n/2 is smaller than n (since n > 1) // n-n/2 is also smaller than n (since n > 1) // Combine outputs. assign x = xlo + xhi; // // The combination here is simple. end else begin // Non-recursive case for minimum size, n == 1 // assign x = a[0]; end endmodule // :Example: // // Find the minimum of n numbers. Use generate constructs to // describe linear- and tree-structured modules. module min_2 #( int w = 4 ) ( output uwire [w-1:0] e_min, input uwire [w-1:0] e_0, e_1 ); assign e_min = e_0 < e_1 ? e_0 : e_1; endmodule // Image:15em:ill-min_2.plain.svg module min_n #( int w = 4, int n = 8 ) ( output uwire [w-1:0] e_min, input uwire [w-1:0] e[n] ); uwire [w-1:0] im[n]; assign im[0] = e[0]; for ( genvar i = 1; i < n; i++ ) min_2 #(w) m( im[i], e[i], im[i-1] ); assign e_min = im[n-1]; endmodule module min_t_live #( int w = 4, int n = 8 ) ( output uwire [w-1:0] e_min, input uwire [w-1:0] e [ n-1:0 ] ); if ( n == 2 ) begin min_2 #(w) m2(e_min, e[1], e[0] ); end else begin uwire [w-1:0] e_min_lo, e_min_hi; localparam int nlo = n/2; localparam int nhi = n - nlo; min_t_live #(w,nlo) mtlo(e_min_lo, e[nlo-1:0] ); min_t_live #(w,nhi) mthi(e_min_hi, e[n-1:nlo] ); min_2 #(w) m2(e_min, e_min_lo, e_min_hi); end endmodule // Image:45em:ill-min_n4.plain.svg module min_t #( int w = 4, int n = 8 ) ( output uwire [w-1:0] e_min, input uwire [w-1:0] e [ n-1:0 ] ); if ( n == 1 ) begin assign e_min = e[0]; end else begin localparam int n_lo = n / 2; localparam int n_hi = n - n_lo; uwire [w-1:0] m_lo, m_hi; min_t #(w,n_lo) mlo( m_lo, e[n_lo-1:0] ); min_t #(w,n_hi) mhi( m_hi, e[n-1:n_lo] ); min_2 #(w) m2( e_min, m_lo, m_hi); end endmodule // Image:18em:ill-min_tn1.plain.svg // Image:45em:ill-min_tn.plain.svg // Image:45em:ill-min_tnn.plain.svg // Image:45em:ill-min_tn3.plain.svg module pop #( int w = 16, int wl = $clog2(w) ) ( output logic [wl-1:0] p, input uwire [w-1:0] a ); always_comb begin p=0; for ( int i=0; i