Creating testbenches
There are two ways of defining a testbench.
The first way is to simply instantiate the design block(DUT) and write the code such that it directly drives the signals in the design block. In this case the stimulus block itself is the toplevel block. In the second style a dummy module acts as the top-level module and both the design(DUT) and the stimulus blocks are instantiated within it. Generally, in the stimulus block the inputs to DUT are defined as reg and outputs from DUT are defined as wire. An important point is that there is no port list for the test bench. An example of the stimulus block is given below. Note that the initial block below is used to set the various inputs of the DUT to a predefined logic state.
Test Bench with Clock generator
module counter_tb;
reg clk, reset, enable;
wire [3:0] count;
counter U0 ( .clk (clk), .reset (reset), .enable (enable), .count (count) initial begin clk = 0;
reset = 0;
enable = 0;
end
always
#5 clk = !clk;
endmodule
Initial block in verilog is executed only once. Thus, the simulator sets the value of clk, reset and enable to 0(0 makes all this signals disabled). It is a good design practice to keep file names same as the module name.
Another elaborated instance of the testbench is shown below. In this instance the usage of system tasks has been explored.
module counter_tb;
reg clk, reset, enable;
wire [3:0] count; counter U0 ( .clk (clk), .reset (reset),
.enable (enable), .
count (count)
initialbegin
clk = 0;
reset = 0;
enable = 0;
end
always
#5 clk = !clk;
initial begin
$dumpfile ( "counter.vcd" );
$dumpvars;
end
initial begin
$display( "\t\ttime,\tclk,\treset,\tenable,\tcount" )
; $monitor( "%d,\t%b,\t%b,\t%b,\t%d" ,$time, clk,reset,enable,count);
end
initial
#100 $finish;
//Rest of testbench code after this line
Endmodule
$dumpfile is used for specifying the file that simulator will use to store the waveform, that can be used later to view using a waveform viewer. (Please refer to tools section for freeware version of viewers.) $dumpvars basically instructs the Verilog compiler to start dumping all the signals to "counter.vcd". $display is used for printing text or variables to stdout (screen), \t is for inserting tab. Syntax is same as printf. Second line $monitor is bit different, $monitor keeps track of changes to the variables that are in the list (clk, reset, enable, count). When ever anyone of them changes, it prints their value, in the respective radix specified. $finish is used for terminating simulation after #100 time units (note, all the initial, always blocks start execution at time 0)
Adding the Reset Logic Once we have the basic logic to allow us to see what our testbench is doing, we can next add the reset logic, If we look at the testcases, we see that we had added a constraint that it should be possible to activate reset anytime during simulation. To achieve this we have many approaches, but the following one works quite well. There is something called 'events' in Verilog, events can be triggered, and also monitored to see, if a event has occurred. Lets code our reset logic in such a way that it waits for the trigger event "reset_trigger" to happen. When this event happens, reset logic asserts reset at negative edge of clock and deasserts on next negative edge as shown in code below. Also after de-asserting the reset, reset logic triggers another event called "reset_done_trigger". This trigger event can then be used at some where else in test bench to sync up.
Code for the reset logic
event reset_trigger;
event reset_done_trigger;
initial begin
forever begin
@ (reset_trigger);
@ (negedge clk);
reset = 1;
@ (negedge clk);
reset = 0; Î reset_done_trigger;
end
end
Adding test case logic
Moving forward, let’s add logic to generate the test cases, ok we have three testcases as in the first part of this tutorial. Let’s list them again. • Reset Test : We can start with reset deasserted, followed by asserting reset for few clock ticks and deasserting the reset, See if counter sets its output to zero. • Enable Test: Assert/deassert enable after reset is applied. Random Assert/deassert of enable and reset.
Adding compare Logic
To make any testbench self checking/automated, a model that mimics the DUT in functionality needs to be designed.For the counter defined previously the model looks similar to: Reg [3:0] count_compare;
always @ (posedge clk)
if (reset == 1'b1)
count_compare <= 0;
else if ( enable == 1'b1)
count_compare <= count_compare + 1;
Once the logic to mimic the DUT functionality has been defined, the next step is to add the checker logic. The checker logic at any given point keeps checking the expected value with the actual value. Whenever there is an error, it prints out the expected and the actual values, and, also, terminates the simulation by triggering the event “terminate_sim”. This can be appended to the code above as follows:
always @ (posedge clk)
if (count_compare != count) begin
$display ( "DUT Error at time %d" , $time);
$display ( " Expected value %d, Got Value %d" , count_compare, count);
#5 -> terminate_sim;
end
2.3 User Defined Primitives
2.3.1 Verilog comes with built in primitives like gates, transmission gates, and switches. This set sometimes seems to be rather small and a more complex primitive set needs to be constructed. Verilog provides the facility to design these primitives which are known as UDPs or User
Defined Primitives. UDPs can model:
• Combinational Logic
• Sequential Logic One can include timing information along with the UDPs to model complete ASIC library models.
Syntax
UDP begins with the keyword primitive and ends with the keyword endprimitive. UDPs must be defined outside the main module definition. This code shows how input/output ports and primitve is declared.
primitive udp_syntax
( a, // Port a
b, // Port b
c, // Port c
d // Port d
output a; input b,c,d; // UDP function code here endprimitive
Note:
• A UDP can contain only one output and up to 10 inputs max
• Output Port should be the first port followed by one or more input ports.
• All UDP ports are scalar, i.e. Vector ports are not allowed.
• UDP's can not have bidirectional ports.
Body
Functionality of primitive (both combinational and sequential) is described inside a table, and it ends with reserve word endtable (as shown in the code below). For sequential UDPs, one can use initial to assign initial value to output.
// This code shows how UDP body looks like primitive udp_body
( a, // Port a
b, // Port b
c // Port c );
input b,c;
// UDP function code here
// A = B | C;
table
// B C : A
? 1 : 1;
1 ? : 1;
0 0 : 0;
endtable
endprimitive
Note: A UDP cannot use 'z' in input table and instead it uses ‘x’.
2.3.2 Combinational UDPs
In combinational UDPs, the output is determined as a function of the current input. Whenever an input changes value, the UDP is evaluated and one of the state table rows is matched. The output state is set to the value indicated by that row. Let us consider the previously mentioned UDP.
TestBench to Check the above UDP
include "udp_body.v"
module udp_body_tb();
reg b,c;
wire a;
udp_body udp (a,b,c);
initial begin
$monitor( " B = %b C = %b A = %b" ,b,c,a);
b = 0;
c=0;
#1 b = 1;
#1 c = 1;
#1 b = 1'bx;
#1 c = 0;
#1 b = 1;
#1 c = 1'bx;
#1 b = 0;
#10 $finish;
end
endmodule
Sequential UDPs
Sequential UDP’s differ in the following manner from the combinational UDP’s
• The output of a sequential UDP is always defined as a reg
• An initial statement can be used to initialize output of sequential UDP’s
• The format of a state table entry is somewhat different
• There are 3 sections in a state table entry: inputs, current state and next state. The three states are separated by a colon(:) symbol.
• The input specification of state table can be in term of input levels or edge transitions
• The current state is the current value of the output register.
• The next state is computed based on inputs and the current state. The next state becomes the new value of the output register.
• All possible combinations of inputs must be specified to avoid unknown output.
Level sensitive UDP’s
// define level sensitive latch by using UDP
primitive latch (q, d, clock, clear)
//declarations output q;
reg q; // q declared as reg to create internal storage input d, clock, clear;
// sequential UDP initialization
// only one initial statement allowed
initial
q=0; // initialize output to value 0
// state table
table
// d clock clear : q : q+ ;
? ? 1 : ? : 0 ;// clear condition
// q+ is the new output value
1 1 0 : ? : 1 ;// latch q = data = 1
0 1 0 : ? : 0 ;// latch q = data = 0
? 0 0 : ? : - ;// retain original state if clock = 0 endtable
endprimitive Edgesensitive UDP’s //Define edge sensitive sequential UDP; primitive edge_dff(output reg q = 0 input d, clock, clear); // state table table // d clock clear : q : q+ ; ? ? 1 : ? : 0 ; // output=0 if clear =1
? ? (10): ? : - ; // ignore negative transition of clear
1 (10) 0 : ? : 1 ;// latch data on negative transition
0 (10) 0 : ? : 0 ;// clock
? (1x) 0 : ? : - ;// hold q if clock transitions to unknown state
? (0?) 0 : ? : - ;// ignore positive transitions of clock
? (x1) 0 : ? : - ;// ignore positive transitions of clock (??) ? 0 : ? : - ;
// ignore any change in d if clock is steady
endtable
endprimitive
Some Exercises
1. Task and functions
i. Define a function to multiply 2 four bit number. The output is a 32 bit value. Invoke the function by using stimulus and check results
ii. define a function to design an 8-function ALU that takes 2 bit numbers a and computes a 5 bit result out based on 3 bit select signal . Ignore overflow or underflow bits.
iii. Define a task to compute even parity of a 16 bit number. The result is a 1-bit value that is assigned to the output after 3 positive edges of clock. (Hint: use a repeat loop in the task)
iv. Create a design a using a full adder. Use a conditional compilation (idef). Compile the fulladd4 with def parameter statements in the text macro DPARAM is defined by the 'define 'statement; otherwise compile the full adder with module instance parameter values.
v. Consider a full bit adder. Write a stimulus file to do random testing of the full adder. Use a random number to generate a 32 bit random number. Pick bits 3:0 and apply them to input a; pick bits 7:4 and apply them to input b. use bit 8 and apply it to c_in. apply 20 random test vectors and see the output.
2. Timing
i) a. Consider the negative edge triggered with the asynchronous reset D-FF shown below. Write the verilog description for the module D-FF. describe path delays using parallel connection.
b Modify the above if all the path delays are 5.
ii) Assume that a six delay specification is to be specified for all the path delays. All path delays are equal. In the specify block define parameters t_01=4, t_10=5, t_0z=7,t_z1=2, t_z0=8. Using the previous DFF write the six delay specifications for all the paths.
3. UDP i. Define a positive edge triggered d-f/f with clear as a UDP. Signal clear is active low. ii. Define a level sensitive latch with a preset signal. Inputs are d, clock, and preset. Output is q. If clock=0, then q=d. If clock=1or x then q is unchanged. If preset=1, then q=1. If preset=0 then q is decided by clock and d signals. If preset=x then q=x. iii. Define a negative edge triggered JK FF, jk_ff with asynchronous preset and clear as a UDP. Q=1when preset=1 and q=0 when clear=1
T he table for JK FF is as follows
47 videos|69 docs|65 tests
|
|
Explore Courses for Computer Science Engineering (CSE) exam
|