Design
module modN_ctr
# (parameter N = 10,
parameter WIDTH = 4)
( input clk,
input rstn,
output reg[WIDTH-1:0] out);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
if (out == N-1)
out <= 0;
else
out <= out + 1;
end
end
endmodule
Testbench
module tb;
parameter N = 10;
parameter WIDTH = 4;
reg clk;
reg rstn;
wire [WIDTH-1:0] out;
modN_ctr u0 ( .clk(clk),
.rstn(rstn),
.out(out));
always #10 clk = ~clk;
initial begin
{clk, rstn} <= 0;
$monitor ("T=%0t rstn=%0b out=0x%0h", $time, rstn, out);
repeat(2) @ (posedge clk);
rstn <= 1;
repeat(20) @ (posedge clk);
$finish;
end
endmodule
ncsim> run T=0 rstn=0 out=0xx T=10 rstn=0 out=0x0 T=30 rstn=1 out=0x0 T=50 rstn=1 out=0x1 T=70 rstn=1 out=0x2 T=90 rstn=1 out=0x3 T=110 rstn=1 out=0x4 T=130 rstn=1 out=0x5 T=150 rstn=1 out=0x6 T=170 rstn=1 out=0x7 T=190 rstn=1 out=0x8 T=210 rstn=1 out=0x9 T=230 rstn=1 out=0xa T=250 rstn=1 out=0x0 T=270 rstn=1 out=0x1 T=290 rstn=1 out=0x2 T=310 rstn=1 out=0x3 T=330 rstn=1 out=0x4 T=350 rstn=1 out=0x5 T=370 rstn=1 out=0x6 T=390 rstn=1 out=0x7 T=410 rstn=1 out=0x8 Simulation complete via $finish(1) at time 430 NS + 0
SystemVerilog offers much flexibility in building complicated data structures through the different types of arrays.
Static Arrays
A static array is one whose size is known before compilation time. In the example shown below, a static array of 8-bit wide is declared, assigned some value and iterated over to print its value.
module tb;
bit [7:0] m_data; // A vector or 1D packed array
initial begin
// 1. Assign a value to the vector
m_data = 8'hA2;
// 2. Iterate through each bit of the vector and print value
for (int i = 0; i < $size(m_data); i++) begin
$display ("m_data[%0d] = %b", i, m_data[i]);
end
end
endmodule
We need to have an environment known as a testbench to run any kind of simulation on the design.
Click here to refresh basic concepts of a simulation
What is the purpose of a testbench ?
A testbench allows us to verify the functionality of a design through simulations. It is a container where the design is placed and driven with different input stimulus.
- Generate different types of input stimulus
- Drive the design inputs with the generated stimulus
- Allow the design to process input and provide an output
- Check the output with expected behavior to find functional defects
- If a functional bug is found, then change the design to fix the bug
- Perform the above steps until there are no more functional defects
Components of a testbench
The example shown in Introduction is not modular, scalable, flexible or even re-usable because of the way DUT is connected, and how signals are driven. Let's take a look at a simple testbench and try to understand about the various components that facilitate data transfer from and to the DUT.
Component | Description |
---|---|
Generator | Generates different input stimulus to be driven to DUT |
Interface | Contains design signals that can be driven or monitored |
Driver | Drives the generated stimulus to the design |
Monitor | Monitor the design input-output ports to capture design activity |
Scoreboard | Checks output from the design with expected behavior |
Environment | Contains all the verification components mentioned above |
Test | Contains the environment that can be tweaked with different configuration settings |

What is DUT ?
DUT stands for Design Under Test and is the hardware design written in Verilog or VHDL. DUT is a term typically used in post validation of the silicon once the chip is fabricated. In pre validation, it is also called as Design Under Verification, DUV in short.
// All verification components are placed in this top testbench module
module tb_top;
// Declare variables that need to be connected to the design instance
// These variables are assigned some values that in turn gets transferred to
// the design as inputs because they are connected with the ports in the design
reg clk;
wire en;
wire wr;
wire data;
// Instantiate the design module and connect the variables declared above
// with the ports in the design
design myDsn ( .clk (clk),
.en (en),
.wr (wr),
. ...
.rdata);
// Develop rest of the testbench and write stimulus that can be driven to the design
endmodule
Click here for a complete SystemVerilog testbench example !
What are SystemVerilog threads or processes ?
A thread or process is any piece of code that gets executed as a separate entity. In verilog, each of the initial and always blocks are spawned off as separate threads that start to run in parallel from zero time. A fork join
block also creates different threads that run in parallel.
What are different fork - join styles ?
We have three different styles of fork join
in SystemVerilog.

Why do we need randomness in the environment ?
Directed tests take a long time to develop because you have to think about all possible scenarios to verify different features. There is a high possibility that you would miss some kind of corner cases. So we want to be able to generate random values that fall within a valid range and apply these random values to the signals we are interested in.