Prof. Ryan Robucci
Modules may include parameters that can be overridden for tuning behavior and facilitating code reuse
if no type is provided parameters take on the type of the default value being assigned
module pdff(q,d,clk,arst);
parameter size=1, resetval=0;
output [size-1:0] q;
input [size-1:0] d;
input clk,arst;
always @(posedge clk, posedge arst) begin
if (arst)
q<=resetval;
else
q <= d;
end
endmodule
in SystemVerilog there is an alternative to the parameter
keyword to create the parameter port list.
module #(int size=1, logic resetval=0) pdff(q,d,clk,arst);
output [size-1:0] q;
input [size-1:0] d;
input clk,arst;
always @(posedge clk, posedge arst) begin
if (arst)
q<=resetval;
else
q <= d;
end
endmodule
Overriding the parameters in the module instantiation is optional and may be done by providing an ordered or explicit list:
no redefinition:
pdff I1 (.q(ur1q),.d(u1d),.clk(clk),.arst(rst));
implicit parameter redefinition (prefer explicit)
pdff #(8,128) I3 (.q(ur8q),.d(u8d),.clk(clk),.arst(rst));
Explicit parameter redefinition
pdff #(.size(8),.resetval(128)) I2 (.q(ur8q),.d(u8d),.clk(clk),.arst(rst));
selective parameter redefinition:
pdff #(.size( ),.resetval(1) ) I4 (.q(ur1q), .d(u1d), .clk(clk),.arst(rst));
parameters may depend on eachother by default and redefined selectively
e.g. declaration with parameter dependancy
module scalable # (int N = 5, M = N*16, type T = int, T x = 0) (....port list...)
defparam
“The defparam statement might be removed from future versions of the language”
Overriding parameters can also be done with the keyword defparam to allow redefinition through hierarchical path name referencing
pdff I2 (.q(ur8q), .d(u8d), .clk(clk), .arst(rst));
defparam I2.size = 8;
defparam I2.resetval = 128;
can be useful for overriding parameters for simulation from the testbench:
defparam topDUT.comModule1.uart2.BAUD_RATE = 100;
localparm cannot be overridden directly from the instantiation.
module pdff(q,d,clk,arst);
parameter size=1, resetval=0;
localparam msb=size-1; //** dependant
output [msb:0] q;
input [msb:0] d;
input clk,arst;
reg [msb:0] out;
always @(posedge clk, posedge arst)
if (arst)
q<=resetval;
else
q <= d;
endmodule
Table 3-1: Integer data types
integer type | description |
---|---|
shortint | 2-state SystemVerilog data type, 16 bit signed integer |
int | 2-state SystemVerilog data type, 32 bit signed integer |
longint | 2-state SystemVerilog data type, 64 bit signed integer |
byte | 2-state SystemVerilog data type, 8 bit signed integer or ASCII character |
bit | 2-state SystemVerilog data type, user-defined vector size |
logic | 4-state SystemVerilog data type, user-defined vector size |
reg | 4-state Verilog-2001 data type, user-defined vector size |
integer | 4-state Verilog-2001 data type, 32 bit signed integer |
time | 4-state Verilog-2001 data type, 64-bit unsigned |
2-state data types can simulate faster, take less memory, and are preferred in some design styles.
The data types byte, shortint, int, integer and longint default to signed. The data types bit, reg and logic default to unsigned, as do arrays of these types.
string myName = "John Smith"
module vector_subtractor
#(
parameter N = 4
)
(
clk,
clk_en,
input_valid,
vec_a,
vec_b,
output_valid,
result
);
localparam WIDTH = 32;
input wire clk;
input wire clk_en;
input wire input_valid;
input wire [N * WIDTH - 1:0] vec_a;
input wire [N * WIDTH - 1:0] vec_b;
output wire output_valid;
output reg [N * WIDTH - 1:0] result;
// Internal registers
reg [WIDTH - 1:0] a[0:N-1];
reg [WIDTH - 1:0] b[0:N-1];
wire [WIDTH - 1:0] r[0:N-1];
wire [N-1:0] output_valid_;
// Slice the vector into elements
always_comb_ begin:slicer
integer i;
for(i=0;i<N;i=i+1) begin
a[i] = vec_a[i*WIDTH +:WIDTH];
b[i] = vec_b[i*WIDTH +:WIDTH];
result[i*WIDTH+:WIDTH] = r[i];
end
end
assign output_valid = &output_valid_;
// Instatiate floating point subtractors
genvar i;
generate
for (i=0;i<N;i=i+1) begin:subtractors
fp_subtractor sub_inst (
.clk(clk),
.clk_en(clk_en),
.input_valid(input_valid),
.a(a[i]),
.b(b[i]),
.output_valid(output_valid_[i]),
.out(r[i])
);
end // block: multiplers
endgenerate
endmodule
Example instatiation (passes paramter N from local module to submodule)
vector_subtractor #(.N(N)) vsubstractor_inst (
.clk(clk),
.clk_en(clk_en),
.input_valid(scaler_output_valid),
.vec_a(vec_b),
.vec_b(row_product),
.output_valid(output_valid),
.result(vec_r)
);
Parametrization modules are useful for creating simulation for simulation that takes much less time to simulate, involves
minimal differences from the code used for hardware synthesis, and maintains high functional test coverage.
Example
Module
module ROM(q,addr);
parameter delay = 1000;
parameter memorysize = 1024*1024*1024;
wire #delay = q_int;
In testbench
ROM (10,1024) I1(q,addr)
Example 2 timer/pacer:
module clk(strobe_out,addr,clk);
parameter cycles = 1024*1024;
…
if (counter == cycles) begin
strobe_out<=1;
counter<=0;
end else begin
strobe_out<=0; counter=counter+1;
A common example of modified models for simulation is an asynchronous communication module called a UART Universal Asynchronous Receiver Transmitter.
module uart(…);
...
// clock rate (50Mhz) / (baud rate (9600) * 4)
parameter CLOCK_DIVIDER = 1302;
…
endmodule
You should always consider the options to accelerate simulation without compromising the faithfulness of the simulation model to the hardware
Minimize manual changes that can introduce random human mistakes
Try to reduce changes to as few root differences as possible and use ifdef to capture differences
Example to run a simulation with a faster pace than actual synthesized hardware
`ifdef SYNTHESIS
CLOCK_DIVIDER = 1302;
`else
CLOCK_DIVIDER = 16;
`endif
tasks are more general, like macros
functions can be used to represent zero-time computation with a single return value
global task and functions supported, but may also place in modules and packages
(quoted from IEEE manual)
Verilog-2001 has static and automatic tasks and functions.
example task syntax
task mytask1 (output int x, input logic y);
...
endtask
task mytask2;
output x;
input y;
int x;
logic y;
...
endtask
argument types
input // copy value in at beginning
output // copy value out at end
inout // copy in at beginning and out at end
ref // pass reference – Only variables, not nets (e.g. wire), can be passed by reference.
example task use
module test;
parameter CLK_HALF_PERIOD = 5;
bit clk;
bit [7:0] a;
task tick_clk;
begin
#CLK_HALF_PERIOD clk = 0;
#CLK_HALF_PERIOD clk = 1;
end
endtask // tick
task tick(output clk_arg);
begin
#CLK_HALF_PERIOD clk_arg = 0;
#CLK_HALF_PERIOD clk_arg = 1;
end
endtask // tick
task automatic tick_ref(ref bit clk_arg);
begin
#CLK_HALF_PERIOD clk_arg = 0;
#CLK_HALF_PERIOD clk_arg = 1;
end
endtask // tick
initial begin
$monitor($time,":",a);
a<=0;
tick_clk;
tick_clk;
tick_clk;
a<=3;
tick(clk);
a<=4;
tick_ref(clk);
a<=5;
end
endmodule // tick
Result
0: 0
30: 3
40: 4
50: 5
disable
functions
function logic [15:0] myfunc1(int x, int y);
...
endfunction
function logic [15:0] myfunc2;
input int x;
input int y;
...
endfunction
function [15:0] myfunc1 (input [7:0] x,y);
myfunc1 = x * y - 1; //return value is assigned to function name
endfunction
function [15:0] myfunc2 (input [7:0] x,y);
return x * y - 1; //return value is specified using return statement
endfunction
To protect arguments passed by reference from being modified by a subroutine, the const qualifier can be
used together with ref to indicate that the argument, although passed by reference, is a read-only variable.
task show ( const ref byte [] data );
for ( int j = 0; j < data.size ; j++ )
$display( data[j] ); // data can be read but not written
endtask
See Verilog Manual for argument defaults and optional arguments
task read(int j = 0, int k, int data = 1 );
...
endtask
Example function to swap byte ordering in each 16-bit field
function [255:0] swapbytes;
input [255:0] x;
swapbytes = {x[7:0],x[15:8],x[23:16],x[31:24],x[39:32],x[47:40],x[55:48],x[63:56],x[71:64],x[79:72],x[87:80],x[95:88],x[103:96],x[111:104],x[119:112],x[127:120],x[135:128],x[143:136],x[151:144],x[159:152],x[167:160],x[175:168],x[183:176],x[191:184],x[199:192],x[207:200],x[215:208],x[223:216],x[231:224],x[239:232],x[247:240],x[255:248]};
endfunction // swap
Example use to map 256 bit bus to 8 16-bit words each with reversed byte ordering
logic [255:0] BUS_REG_PCR_D,BUS_REG_PCR_Q;
assign {hw2reg.pcr[7].d,
hw2reg.pcr[6].d,
hw2reg.pcr[5].d,
hw2reg.pcr[4].d,
hw2reg.pcr[3].d,
hw2reg.pcr[2].d,
hw2reg.pcr[1].d,
hw2reg.pcr[0].d
} = swapbytes(BUS_REG_PCR_D);
another function example: rewire fields in a large array to represent matrix traspose in a packed array
function [SIZE*SIZE*WORD_LEN-1 : 0] transpose(input [SIZE*SIZE*WORD_LEN-1 : 0] A);
integer r,c,r0,c0,offset_dest,offset_src;
begin
for (r=0;r<SIZE;r=r+1) begin
for (c=0;c<SIZE;c=c+1) begin
transpose[((r*SIZE+c)*WORD_LEN)+:WORD_LEN]=A[((c*SIZE+r)*WORD_LEN)+:WORD_LEN];
end
end
end
endfunction // transpose
#
event
posedge
and negedge
event triggerName;
@(triggerName);
-> triggerName;
The event control operator may be provided with variable multiple variables using comma separation. (older syntax is to us or)
@(a,b,c)
@(a or b or c)
Negedge and posedge restrict sensitivity to the following
transitions
@(posedge clk or negedge en)
Negedge Transistions:
1→ 0
1→ x or z
x or z → 0
Posedge Transitions:
0 → 1
x or z →1
0 → x or z
When posedge and negedge modify a multi-bit operand , only the lsb is used to detect the edge.
1010 -> 0001 posedge
1011 -> 1110 negedge
The wait statement suspends execution until a condition is true.
It can be considered as a level-sensitive control since it doesn’t wait for a transition edge.
Comparison of wait to posedge:
@(posedge clk)
even if clk is already true, wait for next rising edge
wait (clk);
if clk is already true, produce without delay
Example to change data on the falling clock edge that follows a variable exceeding the value 10:
wait(a>10);
@ (negedge clk);
data<=data+1;
Any timing control may be modified so as to be repeated/multiplied, by using the keyword repeat
repeat (count) @ (event expression)
If count is positive, repeat the timing control that number of time. If count is equal or less than 0, skip
Wait fot 10 clk rising edges before proceeding execution:
repeat (10) @ (posedge clk);
Delay an assignment by 5 clk edges
a <= repeat(5) @(posedge clk) data;
Delay an assignment by 5 inverter delays:
parameter INV_DELAY = 4.5;
a <= repeat(5) #INV_DELAY data;
The initial construct is used to denote code to be executed once at the beginning of the simulation.
The always construct causes code to run repeatedly in an infinite loop. It is only useful with a delay or control construct, otherwise will create a zero delay infinite loop that can block time progression in simulation
This would run infinitely and stall the simulation time:
always x=a&b;
This prints endlessly in the beginning
of the simulation:
always begin
$display(“hello %0t”);
end
0 : hello
0 : hello
0 : hello
0 : hello
0 : hello ...
clocks can be set to realistic frequencies for timing simulation
parameter PERIOD=4;
initial clock = 1’b0;
always #(PERIOD/2) clock = ~clock;
unit and precision for the time quantity is defined by a derective at the top of a file
‘timescale unit/resolution
Example:
‘timescale 1.0ns/100ps
...
#(4/3.0) clock = 1'b1
4/3.0 = 1333ps, but specified delay rounded to 1300 ps because resolution is 100ps
With VCD Rather than store values of several variables at EVERY timestep,
only record changes.
Each entry is a time along with a list of changes
Time | Value Changes |
---|---|
0 ns | y1=0, y2=0 |
73ns | y1=1, y2=1 |
75ns | y2=0 |
90ns | y2=x |
Far more compact, and natural format given Verilog’s event-driven simulation model
on CSEE Linux Systems, can view VCD with Cadence tools:
convert the .vcd to a .trn file
/umbc/software/scripts/launch_cadence_simvisdbutil.sh cmpehdv_assert.vcd
use SimVision
/umbc/software/scripts/launch_cadence_simvision.sh
IEEE Std 1364™-2005 https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1620780
Minimal code for genearation of VCD
initial begin
$dumpfile("simulation_result.vcd");
$dumpvars;
end
For large designs dumping all signals may significantly slow simulation, increase file size, and slow loading and analysis of VCD data
limiting which singals are saved to file can have a significant impact on simulation time.
Details from IEEE Std 1364™-2005
https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1620780
$dumpfile("filename.vcd")
$dumpvars;
: dumps all variables$dumpvars(1);
: dumps all variables in the current scope$dumpvars (1, top)
: dump vars in module top
$dumpvars (0, top)
: dump vars in and below level top
$dumpvars (0, top.module1, top.module2.net1);
dump vars in and below level top.module1
as well as net1
in module2
$dumpoff;
disables dumping$dumpon;
enables dumping
initial begin
#10 $dumpvars( . . . );
#200 $dumpoff;
#800 $dumpon;
#900 $dumpoff;
end
$dumpall;
create checkpoint with the value of all selected variables$dumplimit ( filesize ) ;
maximum size before dumping is disabled$dumpflush
: flushes output, useful before crashing code, or perhaps before external library calls that potentially read dump file