Transcript Week 7
ECE 551
Digital System Design & Synthesis
Lecture 07
Parameters
Code Generation
Functions & Tasks
Elaboration of Verilog Code
2
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
3
Overview
Parameters
Generated Instantiation
Functions and Tasks
“Compiler” Directives
4
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
5
More Readable, Less Error-Prone
parameter
parameter
parameter
parameter
parameter
always @(*) begin
case (mode)
4’b0000: …
4’b0100: …
4’b0101: …
4’b1010: …
4’b1100: …
default: …
endcase
end
VS
ADD=4’b0000;
SUB=4’b0100;
XOR=4’b0101;
AND=4’b1010;
EQ=4’b1100;
always @(*) begin
case (mode)
ADD: …
SUB: …
XOR: …
AND: …
EQ: …
default: …
endcase
end
6
Reusability/Extensibility of Modules
module xor_array(y_out, a, b);
parameter SIZE = 8, DELAY = 15;
output [SIZE-1:0]
y_out;
input [SIZE-1:0]
a,b;
wire #DELAY y_out = a ^ b;
endmodule
xor_array G1 (y1, a1, b1);
xor_array #(4, 5) G2(y2, a2, b2);
// parameter defaults
// use defaults
// override default parameters
// SIZE = 4, DELAY = 5
Module instantiations cannot specify delays without
parameters
Where would delays go? What type would they be?
7
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;
8
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);
endmodule
// instantiates 4 xor gates
// (unless size overridden)
very common use
of parameters
module variable_size_register (q, data_in, clk, set, rst);
parameter BITWIDTH=8;
input [BITWIDTH-1:0] data_in;
// one per flip-flop
input clk, 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
9
Parameterized Ripple Carry Adder
module RCA(sum, c_out, a, b, c_in);
parameter BITS=8;
input [BITS-1:0] a, b;
input c_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);
10
Parameterized Shift Register [2]
module shift_bhv (outbit, out, in, clk, rst);
parameter WIDTH = 8;
output [WIDTH-1:0] out;
output outbit;
input in;
always @(posedge clk) 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 compiletime 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., genvar i )
Useful when developing parameterized modules
Can override the parameters to create differentsized 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;
genvar i;
How does this
differ from a
generate
standard for loop?
for (i=0; i<SIZE; i=i+1) begin: bit
assign bin[i] = ^gray[SIZE-1:i]; // reduction XOR
end
endgenerate
endmodule
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];
……
genvar i, j;
generate begin : 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];
assign eg_grid_mask [i][j] = (central_et >= EG_THRESHOLD);
assign tau_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
assign et_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];
assign tau_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;
These are
parameters,
output [PRODUCT_WIDTH-1:0] product;
not variables!
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
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
Can have a “default” in
endgenerate
a generate-case
endmodule
20
Generate a Pipeline [Part 1]
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
21
Generate a Pipeline [Part 2]
// 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?
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
Function Example
module word_aligner (word_out, word_in);
output
[7: 0] word_out;
input
[7: 0] word_in;
assign word_out = aligned_word(word_in);
size of return value
// invoke function
function
[7: 0] aligned_word;
// function declaration
input
[7: 0] word;
begin
aligned_word = word;
input to function
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?
28
Function Example
module arithmetic_unit (result_1, result_2, operand_1, operand_2,);
output
[4: 0] result_1;
output
[3: 0] result_2;
function call
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 inputs
function output
function [3: 0] larger_operand(input [3: 0] operand_1, operand_2);
larger_operand = (operand_1 >= operand_2) ? operand_1 : operand_2;
endfunction
endmodule
29
Constant Function Example
module register_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
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
Task Example [Part 1]
module adder_task (c_out, sum, clk, reset, c_in, data_a, data_b, clk);
output reg [3: 0] sum;
output reg
c_out;
this is NOT conditionally
input [3: 0] data_a, data_b;
“creating” hardware!
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
32
Task Example [Part 2]
// Continued from previous slide
task add_values;
// task declaration
output reg
c_out;
task outputs
output reg
[3: 0] sum
input
[3: 0] data_a, data_b;
task inputs
input
c_in;
{c_out, sum} <= data_a + (data_b + c_in);
endtask
endmodule
Could have instead specified inputs/outputs using a port list.
task add_values (output reg c_out, output reg [3: 0] sum,
input [3:0] data_a, data_b, input c_in);
Could we have implemented this as a function?
33
Function Example [Part 1]
module adder_func (c_out, sum, clk, reset, c_in, data_a, data_b, clk);
output reg [3: 0] sum;
output reg
c_out;
this is NOT conditionally
input [3: 0] data_a, data_b;
“creating” hardware!
input
clk, reset, c_in;
always @(posedge clk 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
34
Function Example [Part 2]
// 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?
35
Task Example
task leading_1(output reg [2:0] position, input [7:0] data_word);
reg
[7:0] temp;
begin
internal task variable
temp = data_word;
position = 7;
while (!temp[7]) begin
NOTE:
temp = temp << 1;
“while” loops usually
position = position - 1;
not synthesizable!
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?
36
10.1 Distinctions between tasks and functions
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 inout type 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.
37
`define Compiler Directives
Macro for text substitutions - Used within and
outside of module declarations
`define text_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);
`undef nord
Be careful of conflicting directives!
`define applies until you indicate otherwise!
Note use of ` when referencing the defined macro
Use `undef to undefine
Use parameters when appropriate to encapsulate
38
`define Compiler Directives
`define WIRES(SIZE) wire[SIZE-1:0]
`WIRES(8)
`WIRES(16)
`WIRES(32)
a, b;
prod;
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
module and_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
Ease of Use
Parameters only represent numerical values
Macros represent strings of text (including numbers)
Macros can take arguments
Macros can have control statements (`ifdef)
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:
module use_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
`timescale time_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.
46