Chapter 6: Hierarchical Structural
Modeling
Objectives
After completing this chapter, you will be able to:
Describe the features of hierarchical structural modeling in Verilog HDL
Describe the features of Verilog modules
Describe how to define and override the parameters within a module
Describe the port connection rules
Describe how to write a parameterized module
Describe how to use generate block statements
Modules (p. 163 in LRM)
Two major parts:
Interface: communicates with the outside of the module
Body: defines the functionality of the module.
Module Definitions
// port list style
module module_name [#(parameter_declarations)][port_list];
parameter_declarations; // if parameter ports are used port_declarations;
other_declaration;
statements;
endmodule
// port list declaration style
module module_name [#(parameter_declarations)][port_declarations];
parameter_declarations; // if parameter ports are used other_declarations;
statements;
endmodule
Port Declarations
Three types:
input declares a group of signals as input ports.
output declares a group of signals as output ports.
inout declares a group of signals as bidirectional ports.
net net net
variable net
net net variable
module adder(x, y, c_in, sum, c_out);
Parameters (p. 170 in LRM)
Parameters are not variables; they are constant.
cannot modify their values at run time
can be modified at compile time
Two types of parameters
module parameters
• parameter
• localparam
specify parameters (chap7, p. 211 in LRM)
• specparam
Differences between specparams amd parameters
Constants Specified Options
`define compiler directive
is used to create a macro for text substitution.
is usually placed at the head of a file or a separated file.
can be used both inside and outside module definitions.
An example: `define BUS_WIDTH 8
parameter
is the most common approach used to define parameters.
can be modified by defparam or module instance parameter value assignment.
An example: parameter BUS_WIDTH = 8;
localparam
is identical to parameter but cannot be be overridden.
An example: localparam BUS_WIDTH = 8;
Parameter Ports
Parameter port: parameters are placed between module name and port list/port list declarations.
module module_name
#(parameter SIZE = 7,
parameter WIDTH_BUSA = 24, WIDTH_BUSB = 8, parameter signed [3:0] mux_selector = 4’b0 )
(port list or port list declarations) ...
endmodule
Module Instantiations
In Verilog HDL, module definitions cannot be nested.
A module can incorporate a copy (called an instance) of another module into itself through instantiation.
One or more instances can be created in a single module
instantiation statement
Module Instantiations
Port Connection Rules
Port connection rules
named association
.port_id1(port_expr1),..., .port_idn(port_exprn)
positional association
port_expr1, ..., port_exprn
each port_expr can be
• an identifier (a net or a variable)
• a bit-select of a vector
• a part-select of a vector
• a concatenation of the above
• an expression for input ports
A Parameterized Module
Parameterized modules: the features or operations of a
module can be changed, called parameter override, when a module is instantiated by a higher-level module.
module adder_nbit(x, y, c_in, sum, c_out);
// I/O port declarations parameter N = 4; // set default value input [N-1:0] x, y;
input c_in;
output [N-1:0] sum;
output c_out;
Module Parameters Values
How to change module parameters values
defparam statement
module instance parameter value assignment
Overriding Parameters
Using the defparam statement
// define top level module
module two_counters(clock, clear, qout4b, qout8b);
input clock, clear;
output [3:0] qout4b;
output [7:0] qout8b;
module counter_nbits (clock, clear, qout);
parameter N = 4; // define counter size input clock, clear;
output reg [N-1:0] qout;
always @( posedge clear or negedge clock) begin // qout <= (qout + 1) % 2^n;
if (clear) qout <= {N{1'b0}};
else qout <= (qout + 1) ; end
endmodule
// define top level module
module two_counters(clock, clear, qout4b, qout8b);
input clock, clear;
output [3:0] qout4b;
output [7:0] qout8b;
// instantiate two counter modules
counter_nbits #(4) cnt_4b (clock, clear, qout4b);
counter_nbits #(8) cnt_8b (clock, clear, qout8b);
Overriding Parameters
Using module instance parameter value assignment--- one parameter
module counter_nbits (clock, clear, qout);
parameter N = 4; // define counter size input clock, clear;
output reg [N-1:0] qout;
always @( posedge clear or negedge clock) begin // qout <= (qout + 1) % 2^n;
if (clear) qout <= {N{1'b0}};
else qout <= (qout + 1) ; end
endmodule
Overriding Parameters
Using module instance parameter value assignment --- two parameters
// define top level module
module parameter_overriding_example(x, y, z, f);
input x, y, z;
output f;
hazard_static #(4, 8) example (x, y, z, f);
endmodule module hazard_static (x, y, z, f);
parameter delay1 = 2, delay2 = 5;
input x, y, z;
output f;
wire a, b, c; // internal net // logic circuit body
and #delay2 a1 (b, x, y);
hazard_static #(.delay2(4), .delay1(6)) example (x, y, z, f);
• parameter value assignment by name ---
minimize the chance of error!
Hierarchical Names
An identifier can be defined within
Modules
Tasks
Functions
Named blocks (See Section 7.1.3)
Hierarchical names
4bit_adder // top level --- 4bit_adder 4bit_adder.fa_1 // fa_1 within 4bit_adder
4bit_adder.fa_1.ha_1 // ha_1 within fa_1
4bit_adder.fa_1.ha_1.xor1 // xor1 within ha_1
4bit_adder.fa_1.ha_1.xor1.S // net s within xor1
Overriding Parameters
module vdff (out, in, clk);
parameter size=1, delay=1;
input [0:size-1] in;
input clk;
output [0:size-1] o;
reg [0:size-1] out;
always @(posedge clk)
#delay out = inn;
endmodule module top;
reg clk;
reg [0:4] in1;
reg [0:9] in2;
wire [0:4] o1;
wire [0:9] o2;
vdff m1(o1,in1, clk);
vdff m2(o2, in2, clk);
endmodule
module annotate;
defparam
top.m1.size=5;
generate Block Structures
The keywords used are generate and endgenerate.
It is evaluated during the elaboration
Generate statements allows control over
the declaration of variables, functions, tasks, and module instantiation.
// an example illustrating the usage of generate statement.
// an example of converting Gray code into binary code.
module gray2bin1 (gray, bin);
parameter SIZE = 8; // this module is parameterized input [SIZE-1:0] gray;
output [SIZE-1:0] bin;
genvar i; // generate statement variable.
generate for (i = 0; i < SIZE; i = i + 1) begin: bit assign bin[i] = ^gray[SIZE-1:i];
end
endgenerate
generate Block Structures
The data types allowed in generate block structure:
net, reg
integer, real, time, realtime,
event
What not permitted in a generate block
parameters, local parameters
input, output, inout declarations
specify blocks
Ways to create generate blocks:
The generate Loop Construct
Generate loop can be nested. Within a generate loop
The loop index is declared by the keyword genvar.
genvar is used as an integer during elaboration to create instances of the generate block, but it does not exist during simulation time.
Two generate loops using the same genvar as an index cannot be nested.
A generate loop allows to instantiate
modules, gate primitives, UDPs
continuous assignments
initial and always blocks
The generate Loop Construct
// an example illustrating the usage of generate statement.
// an example of converting Gray code into binary code.
module gray2bin2 (gray, bin);
parameter SIZE = 8; // this module is parameterized.
input [SIZE-1:0] gray;
output [SIZE-1:0] bin;
reg [SIZE-1:0] bin;
genvar i; // generate variable //generate loop
generate for (i = 0; i < SIZE; i = i + 1) begin:bit
always @(*) // using always @(*) to avoid synthesized warning.
The generate Conditional Construct
Based on an if-else-if conditional expression, a generate conditional allows to instantiate
modules, gate primitives, UDPs
continuous assignments
initial and always blocks
The generate Conditional Construct --- An n-bit Adder
Three examples are used to illustrate the usage of generate statement:
Modules
Continuous assignment
always statement
// define a full adder at dataflow level.
module full_adder(x, y, c_in, sum, c_out);
// I/O port declarations
input x, y, c_in;
The generate Conditional Construct --- An n-bit Adder
module adder_nbit(x, y, c_in, sum, c_out);
parameter N = 4; // define n as a parameter input [N-1:0] x, y;
input c_in;
output [N-1:0] sum;
output c_out;
// specify the function of an n-bit adder using generate.
genvar i;
wire [N-2:0] c; // internal carries declared as nets.
generate for (i = 0; i < N; i = i + 1) begin: adder if (i == 0) // specify LSB
full_adder fa (x[i], y[i], c_in, sum[i], c[i]);
else if (i == N-1) // specify MSB
full_adder fa (x[i], y[i], c[i-1], sum[i], c_out);
else // specify other bits
full_adder fa (x[i], y[i], c[i-1], sum[i], c[i]);
end
endgenerate
Instantiate modules inside generate block.
The generate Conditional Construct --- An n-bit Adder
module adder_nbit(x, y, c_in, sum, c_out);
parameter N = 4; // define N as a parameter input [N-1:0] x, y;
input c_in;
output [N-1:0] sum;
output c_out;
// specify the function of an n-bit adder using generate genvar i;
wire [N-2:0] c; // internal carries declared as nets.
generate for (i = 0; i < N; i = i + 1) begin: adder if (i == 0) // specify LSB
assign {c[i], sum[i]} = x[i] + y[i] + c_in;
else if (i == N-1) // specify MSB
Using continuous assignments inside generate block.
The generate Conditional Construct --- An n-bit Adder
module adder_nbit(x, y, c_in, sum, c_out);
parameter N = 4; // define N as a parameter input [N-1:0] x, y;
input c_in;
output reg [N-1:0] sum;
output reg c_out;
// specify the function of an n-bit adder using generate genvar i;
reg [N-2:0] c; // internal carries declared as nets.
generate for (i = 0; i < N; i = i + 1) begin: adder if (i == 0) // specify LSB
always @(*) {c[i], sum[i]} = x[i] + y[i] + c_in;
else if (i == N-1) // specify MSB
always @(*) {c_out, sum[i]} = x[i] + y[i] + c[i-1];
else // specify other bits
always @(*) {c[i], sum[i]} = x[i] + y[i] + c[i-1];
end endgenerate endmodule
An always block corresponds to a piece of logic.
Q: How can we specify a combinational logic or a sequential logic?
The generate Conditional Construct ---
2’s Complement Addermodule twos_adder_nbit(x, y, mode, sum, c_out);
// I/O port declarations
parameter N = 4; // define N as a parameter input [N-1:0] x, y;
input mode; // mode = 1: subtraction; =0: addition output [N-1:0] sum;
output c_out;
// specify the function of an n-bit two’s complement adder using generate genvar i;
wire [N-2:0] c; // internal carries declared as nets.
wire [N-1:0] t; // true/ones complement outputs generate for (i = 0; i < N; i = i + 1) begin:
The generate Conditional Construct ---
2’s Complement Adder generate for (i = 0; i < N; i = i + 1) begin: adderif (i == 0) // specify LSB
full_adder fa (x[i], t[i], mode, sum[i], c[i]);
else if (i == N-1) // specify MSB
full_adder fa (x[i], t[i], c[i-1], sum[i], c_out);
else // specify other bits
full_adder fa (x[i], t[i], c[i-1], sum[i], c[i]);
end endgenerate endmodule
// define a full adder at dataflow level.
module full_adder(x, y, c_in, sum, c_out);
// I/O port declarations output sum, c_out;
input x, y, c_in;
// specify the function of a full adder assign {c_out, sum} = x + y + c_in;
The generate Conditional Construct ---
2’s Complement Adder The RTL schematic from Synplify Pro.
full_adder
adder\[0\].fa
full_adder
adder\[3\].fa
full_adder
adder\[1\].fa
full_adder
adder\[2\].fa t[0]
t[1] t[2] t[3]
[0] x
[0] y
c_in sum [0]
c_out [0]
[3] x
[3] y
[2] c_in
sum [3]
c_out
[1] x
[1] y
[0] c_in
sum [1]
c_out [1]
[2] x
[2] y
[1] c_in
sum [2]
c_out [2]
[0] [0]
[1] [1]
[2] [2]
[3] [3]
mode y[3:0] [3:0]
x[3:0] [3:0]
c_out sum[3:0]
[3:0]
The generate Conditional Construct ---
2’s Complement Adder After dissolving the second and the third bits.
[1]
[1]
[2]
full_adder [2]
adder\[0\].fa
full_adder
adder\[3\].fa
t[0]
t[1] t[2]
t[3]
adder\[1\].fa.sum_1[1:0]
+ adder\[2\].fa.sum_1[1:0]
+
[0] x
[0] y
c_in sum [0]
c_out [0]
[3] x
[3] y
[2] c_in
sum [3]
c_out
[0] [0]
[1] [1]
[2] [2]
[3] [3]
[1]
[1]
[0]
[2]
[2]
[1]
mode y[3:0] [3:0]
x[3:0] [3:0]
c_out sum[3:0]
[3:0]
The generate Case Construct
Based on a case expression, a generate conditional allows to instantiate
modules, gate primitives, UDPs,
continuous assignments,
initial and always blocks.
generate for (i = 0; i < N; i = i + 1) begin: adder case (i)
0: assign {c[i], sum[i]} = x[i] + y[i] + c_in;
N-1: assign {c_out, sum[i]} = x[i] + y[i] + c[i-1];