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