A verification testbench is a hardware verification language (HVL) code written in Verilog or SystemVerilog that is used to verify the functionality of a digital design. The testbench is a simulation environment that generates stimulus for the design under test (DUT) and checks the response of the DUT against expected results. The testbench may also include functional coverage and assertions to ensure that all functional scenarios have been exercised and the DUT behaves as expected. The testbench typically consists of three main parts: the testbench framework, the stimulus generator, and the response checker.
Evolution
Since the advent of Verilog in the 1980s, testbenches have undergone significant evolution to become more powerful, automated, and efficient.
Constrained-random testbenches: In the 1990s, constraint-random testbenches were introduced as a way to generate input stimuli and testcases automatically. This allowed designers to test their designs more thoroughly and to explore a wider range of input scenarios than was possible with linear testbenches.
The ASIC Design Flow consists of several steps, including design specification, design entry, design synthesis, design verification, physical design, and design sign-off.
Design verification (DV) typically refers to the pre-silicon effort of functional validation of the design using simulation tools.
Verilog has system tasks and functions that can open files, output values into files, read values from files and load into other variables and close files.
Opening and closing files
module tb;
// Declare a variable to store the file handler
integer fd;
initial begin
// Open a new file by the name "my_file.txt"
// with "write" permissions, and store the file
// handler pointer in variable "fd"
fd = $fopen("my_file.txt", "w");
// Close the file handle pointed to by "fd"
$fclose(fd);
end
endmodule
Most programming languages have a characteristic feature called scope which defines the visibility of certain sections of code to variables and methods. The scope defines a namespace to avoid collision between different object names within the same namespace.
Verilog defines a new scope for modules, functions, tasks, named blocks and generate blocks.
module tb;
reg signal;
// Another variable cannot be declared with
// an already existing name in the same scope
reg signal;
// However, the name 'signal' can be reused inside
// a task because it belongs to a different scope.
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
An identifier, like a signal name, can be used to declare only one type of item in a given scope. This means that two variables of different or same data types cannot have the same name, or a task and a variable of the same name, or even a net and gate instance with the same name in the same scope.
Verilog math functions can be used in place of constant expressions and supports both integer and real maths.
Integer Math Functions
The function $clog2
returns the ceiling of log2 of the given argument. This is typically used to calculate the minimum width required to address a memory of given size.
For example, if the design has 7 parallel adders, then the minimum number of bits required to represent all 7 adders is $clog2
of 7 that yields 3.
module des
#(parameter NUM_UNITS = 7)
// Use of this system function helps to reduce the
// number of input wires to this module
(input [$clog2(NUM_UNITS)-1:0] active_unit);
initial
$monitor("active_unit = %d", active_unit);
endmodule
`define NUM_UNITS 5
module tb;
integer i;
reg [`NUM_UNITS-1:0] active_unit;
des #(.NUM_UNITS(`NUM_UNITS)) u0(active_unit);
initial begin
active_unit = 1;
#10 active_unit = 7;
#10 active_unit = 8;
end
endmodule
Note that the signal active_unit has 3-bits to store total 5 units.