1 / 45

ECE 551 Digital System Design & Synthesis

ECE 551 Digital System Design & Synthesis. Lecture 07 Parameters Code Generation Functions & Tasks. Elaboration of Verilog Code. Elaboration of Verilog Code. Elaboration is a pre-processing stage that takes place before code is synthesized.

laquinta
Download Presentation

ECE 551 Digital System Design & Synthesis

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ECE 551Digital System Design & Synthesis Lecture 07 Parameters Code Generation Functions & Tasks

  2. Elaboration of Verilog Code

  3. Elaboration of Verilog Code • Elaboration is a pre-processing stage that takes place before code is synthesized. • It allows us to automatically alter our code before Synthesis based on Compile-Time information • We’ve already examined one part of Elaboration • Unrolling of FOR Loops • Next we’ll look at other uses of Elaboration • Parameterization • Code Generation • Constant Functions • Macros

  4. Overview • Parameters • Generated Instantiation • Functions and Tasks • “Compiler” Directives

  5. Parameters • Compile-time constant parameters in Verilog • In Verilog: parameter N=8’d100; • Values are substituted during Elaboration; parameters cannot change value after synthesis • Can be used for three main reasons • Make code more readable • Make it easier to update code • Improve (re)usability of modules

  6. More Readable, Less Error-Prone parameter ADD=4’b0000; parameter SUB=4’b0100; parameter XOR=4’b0101; parameter AND=4’b1010; parameter EQ=4’b1100; always @(*) begin case (mode) ADD: … SUB: … XOR: … AND: … EQ: … default: … endcase end always @(*) begin case (mode) 4’b0000: … 4’b0100: … 4’b0101: … 4’b1010: … 4’b1100: … default: … endcase end VS

  7. modulexor_array(y_out, a, b); parameterSIZE = 8, DELAY = 15; // parameter defaults output [SIZE-1:0] y_out; input [SIZE-1:0] a,b; wire #DELAYy_out = a ^ b; endmodule xor_array G1 (y1, a1, b1); // use defaults xor_array#(4, 5) G2(y2, a2, b2); // override default parameters // SIZE = 4, DELAY = 5 Module instantiations cannot specify delays without parameters Where would delays go? What type would they be? Reusability/Extensibility of Modules

  8. Overriding Parameters • Parameters can be overridden • Generally done to “resize” module or change its delay • Implicitly: override in order of appearance • xor_array#(4, 5) G2(y2, a2, b2); • Explicitly: name association (preferred) • xor_array#(.SIZE(4), .DELAY(5)) G3(y2, a2, b2); • Explicitly: defparam • defparam G4.SIZE = 4, G4.DELAY = 15; • xor_array G4(y2, a2, b2); • localparam parameters in a module can’t be overridden • localparam SIZE = 8, DELAY = 15;

  9. Parameters With Instance Arrays module array_of_xor (y, a, b); parameter SIZE=4; input [SIZE-1:0] a,b; output [SIZE-1:0] y; xor G3[SIZE-1:0] (y, a, b); // instantiates 4 xor gates endmodule// (unless size overridden) module variable_size_register (q, data_in, clk, set, rst); parameter BITWIDTH=8; input [BITWIDTH-1:0] data_in; // one per flip-flop inputclk, set, rst; // shared signals output [BITWIDTH-1:0] q; // one per flip-flop // instantiate flip-flops to form a BITWIDTH-bit register flip_flop M [BITWIDTH-1:0] (q, data_in, clk, set, rst); endmodule very common use of parameters

  10. Parameterized Ripple Carry Adder module RCA(sum, c_out, a, b, c_in); parameter BITS=8; input [BITS-1:0] a, b; inputc_in; output [BITS-1:0] sum; output c_out; wire [BITS-1:1] c; Add_full M[BITS-1:0](sum, {c_out, c[BITS-1:1]}, a, b, {c[BITS-1:1], c_in}); endmodule • Instantiate a 16-bit ripple-carry adder: RCA #(.BITS(16)) add_16(sum, carryout, a, b, carryin);

  11. Parameterized Shift Register [2] moduleshift_bhv (outbit, out, in, clk, rst); parameterWIDTH = 8; output [WIDTH-1:0] out; output outbit; input in; always @(posedgeclk) begin if (rst) {outbit,out} <= 0; else {outbit,out} <= {out[WIDTH-1:0],in}; end endmodule • Instantiate a 16-bit shift register: shift_bhv#(16) shift_16(shiftbit, shiftout, shiftin, clk, rst);

  12. Parameters + Generate Statements • Problem: Certain types of logic structures are efficient only in certain scenarios • For example when designing an adder: • Ripple-Carry Adders are better for small operands • Carry Look-ahead Adders are better for large operands • If we change a parameter to use a larger or smaller adder size, we may also want to change the structure of the logic

  13. Generated Instantiation • Generate statements: control over the instantiation/creation of • Modules, gate primitives, continuous assignments, initial blocks, always blocks, nets and regs • Generate instantiations are resolved during Elaboration • Can alter or replace a piece of code based on compile-time information • Before the design is simulated or synthesized • Think of it as having the code help write itself

  14. Special Generate Variables • Index variables used in generate statements; declared using genvar (e.g., genvari ) • Useful when developing parameterized modules • Can override the parameters to create different-sized structures • Easier than creating different structures for all different possible bitwidths

  15. Generate-Loop • A generate-loop permits making one or more instantiations (pre-synthesis) using a for-loop. module gray2bin1 (bin, gray); parameter SIZE = 8; // this module is parameterizable output [SIZE-1:0] bin; input [SIZE-1:0] gray; genvari; generate for (i=0; i<SIZE; i=i+1) begin: bit assign bin[i] = ^gray[SIZE-1:i]; // reduction XOR end endgenerate endmodule How does this differ from a standard for loop?

  16. Generate Loops in LHC Firmware input [CLUSTER_ET_SIZE-1:0] cluster_grid[GRID_X-1:0][GRID_Y-1:0]; wire eg_grid_mask[GRID_X-1:0][GRID_Y-1:0]; wire eg_grid_mask[GRID_X-1:0][GRID_Y-1:0]; …… genvari, j; generatebegin : mask_gen for (i = 0; i < GRID_X; i=i+1) begin : xcoord for (j = 0; j < GRID_Y; j=j+1) begin : ycoord logic [CLUSTER_ET_SIZE-1:0] central_et = cluster_grid[i][j]; assigneg_grid_mask [i][j] = (central_et >= EG_THRESHOLD); assigntau_grid_mask[i][j] = (central_et >= TAU_THRESHOLD); end end end endgenerate//This must be SystemVerilog. Why?

  17. Accessing Data from other Loops generate for(i = 0; i < GRID_X; i = i + 1) begin : aliasing_x for(j = 0; j < GRID_Y; j = j + 1) begin : aliasing_y if((i >= ISO_X1) && (i <= ISO_X2) && (j >= ISO_Y1) && (j <= ISO_Y2)) begin assignet_array[i-ISO_X1][j-ISO_Y1] = mask_gen.xcoord[i].ycoord[j].central_et; assign eg_correction [i-ISO_X1][j-ISO_Y1] = eg_grid_mask[i][j]; assigntau_correction[i-ISO_X1][j-ISO_Y1] = tau_grid_mask[i][j]; end end end endgenerate

  18. Generate-Conditional • A generate-conditional allows conditional (pre-synthesis) instantiation using if-else-if constructs module multiplier(a ,b ,product); parameter A_WIDTH = 8, B_WIDTH = 8; localparam PRODUCT_WIDTH = A_WIDTH+B_WIDTH; input [A_WIDTH-1:0] a; input [B_WIDTH-1:0] b; output [PRODUCT_WIDTH-1:0] product; generate if ((A_WIDTH < 8) || (B_WIDTH < 8)) CLA_multiplier #(A_WIDTH,B_WIDTH) u1(a, b, product); else WALLACE_multiplier #(A_WIDTH,B_WIDTH) u1(a, b, product); endgenerate endmodule These are parameters, not variables!

  19. Generate-Case • A generate-case allows conditional (pre-synthesis) instantiation using case constructs • See Standard 12.1.3 for more details module adder (output co, sum, input a, b, ci); parameter WIDTH = 8; generate case (WIDTH) 1: adder_1bit x1(co, sum, a, b, ci); // 1-bit adder implementation 2: adder_2bit x1(co, sum, a, b, ci); // 2-bit adder implementation default: adder_cla #(WIDTH) x1(co, sum, a, b, ci); endcase endgenerate endmodule Can have a “default” in a generate-case

  20. module pipeline(out, in, clk, rst); parameter BITS = 8; parameter STAGES = 4; input [BITS-1:0] in; output [BITS-1:0] out; wire [BITS-1:0] stagein [0:STAGES-1]; // value from previous stage reg [BITS-1:0] stage [0:STAGES-1]; // pipeline registers assign stagein[0] = in; generate genvar s; for (s = 1; s < STAGES; s = s + 1) begin : stageinput assign stagein[s] = stage[s-1]; end endgenerate // continued on next slide Generate a Pipeline [Part 1]

  21. // continued from previous slide assign out = stage[STAGES-1]; generate genvar j; for (j = 0; j < STAGES; j = j + 1) begin : pipe always @(posedge clk) begin if (rst) stage[j] <= 0; else stage[j] <= stagein[j]; end end endgenerate endmodule What does this generate? Generate a Pipeline [Part 2]

  22. Functions and Tasks • HDL constructs that look similar to calling a function or procedure in an HLL. • Designed to allow for more code reuse • There are 3 major uses for functions/tasks • To describe logic hardware in synthesizable modules • To describe functional behavior in testbenches • To compute values for parameters and other constants for synthesizable modules before they are synthesized • When describing hardware, you must make sure the function or task can be synthesized!

  23. Functions and Tasks in Logic Design • It is critical to be aware of whether something you are designing is intended for a synthesized module • Hardware doesn’t actually “call a function” • No instruction pointer or program counter • This is an abstraction for the designer • In synthesized modules, they are used to describe the behavior we want the hardware to have • Help make HDL code shorter and easier to read • The synthesis tool will try to create hardware to match that description

  24. Functions and Tasks in Testbenches • Since testbenches do not need to synthesize, we do not have to worry about what hardware would be needed to implement a function • Be careful: This doesn’t mean that we can treat such functions & tasks as software • Even testbench code must follow Verilog standards, including the timing of the Stratified Event Queue

  25. Functions • Declared and called within a module • Used to implement combinational behavior • Contain no timing controls or tasks • Can use behavioral constructs • Inputs/outputs • At least one input, exactly one output • Return variable is the same as function name • Can specify type/range (default: 1-bit wire) • Usage rules: • May be referenced in any expression (RHS) • May call other functions • Use automatic keyword to declare recursive functions

  26. Constant Functions • A special class of functions that can always be used in a synthesizable module • Constant functions take only constant values (such as numbers or parameters) as their inputs. • All inputs are constant, so the output is also constant • The result can be computed at elaboration, so there is no reason to build hardware to do it • Other restrictions apply – see 10.3.5 • Constant functions are useful when one constant value is dependent on another. It can simplify the calculation of values in parameterized modules.

  27. moduleword_aligner (word_out, word_in); output [7: 0] word_out; input [7: 0] word_in; assign word_out = aligned_word(word_in); // invoke function function [7: 0] aligned_word; // function declaration input [7: 0] word; begin aligned_word = word; if (aligned_word != 0) while (aligned_word[7] == 0) aligned_word = aligned_word << 1; end endfunction endmodule What sort of hardware might this describe? Do you think this will synthesize? Function Example size of return value input to function

  28. modulearithmetic_unit (result_1, result_2, operand_1, operand_2,); output [4: 0] result_1; output [3: 0] result_2; input [3: 0] operand_1, operand_2; assign result_1 = sum_of_operands (operand_1, operand_2); assign result_2 = larger_operand (operand_1, operand_2); function [4: 0] sum_of_operands(input [3: 0] operand_1, operand_2); sum_of_operands = operand_1 + operand_2; endfunction function [3: 0] larger_operand(input [3: 0] operand_1, operand_2); larger_operand = (operand_1 >= operand_2) ? operand_1 : operand_2; endfunction endmodule Function Example function call function inputs function output

  29. moduleregister_file (…); parameter NUM_ENTRIES=64; localparam NUM_ADDR_BITS=ceil_log2(NUM_ENTRIES); function [31: 0] ceil_log2(input [31: 0] in_val); reg sticky; reg [31:0] temp; begin sticky = 1'b0; for (temp=32'd0; value>32'd1; temp=temp+1) begin if((value[0]) & (|value[31:1])) sticky = 1'b1; value = value>>1; end clogb2 = temp + sticky; end endfunction Constant Function Example

  30. Tasks • Declared within a module • Only used within a behavior • Tasks provide the ability to • Describe common behavior in multiple places • Divide large procedures into smaller ones • Tasks are not limited to combinational logic • Can have time-controlling statements (@, #, wait) • Some of this better for testbenches • Use automatic keyword to declare “reentrant” tasks • Can have multiple outputs, inout ports • Local variables can be declared & used

  31. module adder_task (c_out, sum, clk, reset, c_in, data_a, data_b, clk); output reg [3: 0] sum; output reg c_out; input [3: 0] data_a, data_b; input clk, reset, c_in; always @(posedge clk or posedge reset) begin if (reset) {c_out, sum} <= 0; else add_values (c_out, sum, data_a, data_b, c_in); // invoke task end // Continued on next slide Task Example [Part 1] this is NOT conditionally “creating” hardware!

  32. // Continued from previous slide taskadd_values; // task declaration outputregc_out; outputreg [3: 0] sum input [3: 0] data_a, data_b; inputc_in; {c_out, sum} <= data_a + (data_b + c_in); endtask endmodule Could have instead specified inputs/outputs using a port list. taskadd_values(outputregc_out, output reg [3: 0] sum, input [3:0] data_a, data_b, inputc_in); Could we have implemented this as a function? Task Example [Part 2] task outputs task inputs

  33. moduleadder_func (c_out, sum, clk, reset, c_in, data_a, data_b, clk); output reg [3: 0] sum; output regc_out; input [3: 0] data_a, data_b; input clk, reset, c_in; always @(posedgeclk or posedge reset) begin if (reset) {c_out, sum} <= 0; else {cout, sum} <= add_values (data_a, data_b, c_in); // invoke task end // Continued on next slide Function Example [Part 1] this is NOT conditionally “creating” hardware!

  34. // Continued from previous slide function [4:0] add_values; // function declaration input [3: 0] data_a, data_b; input c_in; add_values = data_a + (data_b + c_in); endfunction endmodule How does this differ from the task-based version? Function Example [Part 2]

  35. task leading_1(output reg [2:0] position, input [7:0] data_word); reg [7:0] temp; begin temp = data_word; position = 7; while (!temp[7]) begin temp = temp << 1; position = position - 1; end end endtask What does this task assume for it to work correctly? How do tasks differ from modules? How do tasks differ from functions? Task Example internal task variable NOTE: “while” loops usually not synthesizable!

  36. The following rules distinguish tasks from functions: A function shall execute in one simulation time unit; a task can contain time-controlling statements. A function cannot enable a task; a task can enable other tasks and functions. A function shall have at least one input type argument and shall not have an output or inouttype argument; a task can have zero or more arguments of any type. A function shall return a single value; a task shall not return a value. The purpose of a function is to respond to an input value by returning a single value. A task can support multiple goals and can calculate multiple result values. 10.1 Distinctions between tasks and functions

  37. `define Compiler Directives • Macro for text substitutions - Used within and outside of module declarations `definetext_macro_name[(arguments)] macro-text • Example 1: `define address_register_length 16 reg [`address_register_length :1] address; • Example 2: `define nord(dly) nor #dly `nord(4) g1 (N3, A, B); // becomes nor #4 g1(N3, A, B); `undefnord • Be careful of conflicting directives! • `define applies until you indicate otherwise! • Note use of ` when referencing the defined macro • Use `undefto undefine • Use parameters when appropriate to encapsulate

  38. `define Compiler Directives `define WIRES(SIZE) wire[SIZE-1:0] `WIRES(8) a, b; `WIRES(16) prod; `WIRES(32) psquared; assign prod = a * b; assign psquared = prod * prod; When used properly, macros can make code easier to read, write, and understand. Be careful: Because Macros are only substituted at Elaboration time, any syntax errors inside the Macro itself will not be found when doing compile in ModelSim!

  39. Conditional Compilation Directives • Conditional compilation directives include: • `ifdef, `ifndef , `else, `elsif, `endif • Useful when • Selecting different stimulus • Choosing different timing or structural information • Selecting different representations of modules moduleand_op (output a, input b, c); `ifdef RTL assign a = b & c; // continuous assign `else and a1 (a, b, c); // primitive `endif endmodule

  40. Macros vs Parameters • Scope • Parameters scope is limited to the module they are declared in. • Macros have global scope. • Redefinition • Parameters can only be redefined when instantiating a module or using defparam. • Macros can be redefined at any point in any file in your project – beware!

  41. Macros vs Parameters • Flexibility • Parameters only represent numerical values • Macros represent strings of text (including numbers) • Macros can take arguments • Macros can have control statements (`ifdef) • Ease of Use • Syntax errors with parameters are caught during syntax check (compile in ModelSim) • Syntax errors inside macro strings are caught in elaboration • This makes macros harder to debug • Moral – macros more powerful, but error-prone • Some designers opt to only use parameters

  42. ‘resetall • Resets all compiler directives to defaults • Often used to start each file to avoid conflicts • Important because, unlike parameters, macros have global scope

  43. ‘include Compiler Directives • `include filename • Inserts entire contents of another file at compilation • Can be placed anywhere in Verilog source • Useful for shared tasks/functions! • Example 1: module use_adder8(…); `include “adder8.v”; // include the task for adder8 • Example 2: moduleuse_incrementer(…); `include “/home/components/incrementer.v” • Can provide either relative or absolute path names • Useful for including tasks and functions in multiple modules • Also good for parameters used in more than one module!

  44. ‘timescale • `timescale • Saw this already in our testbenches • `timescaletime_unit/time_precision • time_unit - specifies unit of measurement for times and delays • time_precision  time_unit • Specifies how delays are rounded before use in simulation • The time unit for simulation is the smallest time_precision value specified over all modules • Allowable values {1, 10, 100} {s, ms, us, ns, ps, fs} • Example: `timescale 1 ns / 100 ps

  45. Review Questions • Write a module for an adder with the following interface module adder(a, b, sum); Allow the widths of a, b, and sum to be specified using the parameters in_width and out_width, where each has a default value of 8. Use a function to perform the addition. • Show how to instantiate an adder using the module above named ADD1 with in_width = 4 and out_width = 6. Use named association. • Use a generate-loop to re-implement the above adder using continuous assignment statements with bitwise operations. Don’t use a function.

More Related