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