/// LSU EE 4755 -- Fall 2017 -- Digital Design / HDL /// /// Elaboration, Parameters, and Generate Constructs // Under construction. /// 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 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.) // :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 // :SV: Section 6.20 Constants // :Def: Elaboration-Time Constant // - An object declared with the qualifiers: // :Keywords: parameter,localparam,specparam // - An expression consisting only of constants and a subset // of functions. // :Example: // // Consider the saturating adder from above. module sat_add_from_above // width is an elaboration-time constant because it is a parameter. #( int width = 8 ) ( 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 a // 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 // :SV: 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 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 // :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 // Use generate statements to instantiate BFA's. // module ripple_4_elab ( output [3:0] sum, output cout, input [3:0] a, b, input 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] ); endmodule // Instantiate BFA's the old fashioned way. // module ripple_4 ( output [3:0] sum, output cout, input [3:0] a, b, input 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 ); endmodule module ripple_w #( int w = 16 ) ( output [w-1:0] sum, output cout, input [w-1:0] a, b, input cin); uwire carry[w-1:-1]; assign carry[-1] = cin; assign cout = carry[w-1]; for ( genvar i = 0; i<w; i++ ) bfa_structural bfa( sum[i], carry[i], a[i], b[i], carry[i-1] ); endmodule // :Example: // // Miscellaneous correct and incorrect usage of generate statements. module gen_good #( int option = 0 ) ( output uwire logic [7:0] ab,abc, output var logic [7:0] vab,vabc, input uwire logic [7:0] a, b, c ); for ( genvar i=0; i<8; i += 2 ) // This *IS NOT* procedural code ... that's why assign is okay. begin assign ab[i] = a[i]; assign ab[i+1] = b[i+1]; end `ifdef XXX always_comb // This is procedural code (because of the always_comb) ... // ... that's why assign is *NOT OKAY*. for ( int i=0; i<i; i+=2 ) begin assign ab[i] = a[i]; assign ab[i+1] = b[i+1]; end `endif always_comb // This is procedural code. for ( int i=0; i<i; i+=2 ) begin vab[i] = a[i]; vab[i+1] = b[i+1]; end for ( genvar i=0; i<8; i++ ) begin:myblock uwire aab; and a1(aab,a[i],b[i]); if ( option == 0 ) or o1(abc[i],aab,c[i]); else xor o1(abc[i],aab,c[i]); end endmodule module gen_bad ( output uwire [7:0] ab,abc, input uwire option, input uwire [7:0] a, b, c ); for ( genvar i=0; i<8; i += 2 ) begin assign ab[i] = a[i]; assign ab[i+1] = b[i+1]; end `ifdef SHOW_BAD_CODE for ( genvar i=0; i<8; i++ ) begin:myblock uwire aab; and a1(aab,a[i],b[i]); if ( option == 0 ) /// Bad, because option not constant. or o1(abc[i],aab,c[i]); else xor x1(abc[i],aab,c[i]); end `endif for ( genvar i=0; i<8; i++ ) begin:myotherblock uwire aab, ior, eor; and a1(aab,a[i],b[i]); // if ( option == 0 ) /// Bad, because option not constant. or o1(ior,aab,c[i]); // else xor x1(eor,aab,c[i]); assign abc[i] = option == 0 ? ior : eor; end endmodule /// Elaboration of Tree Structure // :Example: // // Find the minimum of n numbers. Use generate constructs to // describe a 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 // 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 #( 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 // // // // module pop_1 (output logic [4:0] p, input [15:0] v ); always_comb begin p = 0; for ( int i=0; i<16; i++ ) p += v[i]; end endmodule module pop_n #( int bits = 16 ) ( output [4:0] p, input [bits-1:0] v ); if ( bits == 1 ) begin assign p = v[0]; end else begin uwire [4:0] p1, p2; pop_n #(bits/2) pi1(.p(p1),.v(v[bits/2-1:0])); pop_n #(bits-bits/2) pi2(p2,v[bits-1:bits/2]); assign p = p1 + p2; end endmodule