SystemVerilog Adder Testbench Example
Adder Design
Adder design produces the resultant addition of two variables on the positive edge of the clock. A reset signal is used to clear ‘out’ signal to 0.
Note: Adder can be easily developed with combinational logic. A clock and reset are introduced to have the flavor of a clock and reset in testbench code.
module adder(input clk, reset, input [7:0] in1, in2, output reg [8:0] out);
always@(posedge clk or posedge reset) begin
if(reset) out <= 0;
else out <= in1 + in2;
end
endmodule
Testbench Code
Transaction
The transaction is a packet that is driven to the DUT or monitored by the monitor as a pin-level activity.
class transaction;
rand bit [7:0] ip1, ip2;
bit [8:0] out;
constraint ip_c {ip1 < 100; ip2 < 100;}
endclass
Generator
The generator creates or generates randomized transactions or stimuli and passes them to the driver.
class generator;
int count;
mailbox gen_to_drv;
transaction tr;
function new(mailbox gen_to_drv);
this.gen_to_drv = gen_to_drv;
endfunction
task run;
repeat(count) begin
tr = new();
void'(tr.randomize());
gen_to_drv.put(tr);
end
endtask
endclass
Driver
The driver interacts with DUT. It receives randomized transactions from the generator and drives them to the driven as a pin level activity.
class driver;
virtual add_if vif;
mailbox gen_to_drv;
transaction tr;
function new(mailbox gen_to_drv, virtual add_if vif);
this.gen_to_drv = gen_to_drv;
this.vif = vif;
endfunction
task run;
forever begin
// Driver to the DUT
@(posedge vif.clk);
gen_to_drv.get(tr);
vif.ip1 <= tr.ip1;
vif.ip2 <= tr.ip2;
@(posedge vif.clk);
tr.out <= vif.out;
end
endtask
endclass
Monitor
The monitor observes pin-level activity on the connected interface at the input and output of the design.
class monitor;
virtual add_if vif;
mailbox mon_to_sb;
function new(mailbox mon_to_sb, virtual add_if vif);
this.vif = vif;
this.mon_to_sb = mon_to_sb;
endfunction
task run;
forever begin
transaction mon_tr;
wait(!vif.reset);
@(posedge vif.clk);
mon_tr = new();
mon_tr.ip1 = vif.ip1;
mon_tr.ip2 = vif.ip2;
@(posedge vif.clk);
mon_tr.out = vif.out;
mon_to_sb.put(mon_tr);
end
endtask
endclass
Agent
An agent is a container that holds the generator, driver, and monitor.
class agent;
driver drv;
monitor mon;
generator gen;
mailbox gen_to_drv;
virtual add_if vif;
function new(virtual add_if vif, mailbox mon_to_sb);
gen_to_drv = new();
drv = new(gen_to_drv, vif);
mon = new(mon_to_sb, vif);
gen = new(gen_to_drv);
endfunction
task run();
fork
drv.run();
mon.run();
gen.run();
join_any
endtask
endclass
Scoreboard
The scoreboard receives the transaction packet from the monitor and compares it with the reference model.
class scoreboard;
int compare_cnt;
mailbox mon_to_sb;
function new(mailbox mon_to_sb);
this.mon_to_sb = mon_to_sb;
endfunction
task run;
forever begin
transaction tr;
tr = new();
mon_to_sb.get(tr);
if(tr.ip1 + tr.ip2 == tr.out) begin
$display("Matched: ip1 = %0d, ip2 = %0d, out = %0d", tr.ip1, tr.ip2, tr.out);
end
else begin
$display("NOT matched: ip1 = %0d, ip2 = %0d, out = %0d", tr.ip1, tr.ip2, tr.out);
end
compare_cnt++;
end
endtask
endclass
Environment
An environment allows a well-mannered hierarchy and container for agents, scoreboards.
class env;
agent agt;
scoreboard sb;
mailbox mon_to_sb;
function new(virtual add_if vif);
mon_to_sb = new();
agt = new(vif, mon_to_sb);
sb = new(mon_to_sb);
endfunction
task run();
fork
agt.run();
sb.run();
join_any
wait(agt.gen.count == sb.compare_cnt);
$finish;
endtask
endclass
Test
The test is at the top of the hierarchy that initiates the environment component construction and connection between them.
program base_test(add_if vif);
env env_o;
initial begin
env_o = new(vif);
env_o.agt.gen.count = 5;
env_o.run();
end
endprogram
Testbench top
The testbench top is a top-level component that includes interface and DUT instances. It connects design with the testbench.
module tb_top;
bit clk;
bit reset;
always #2 clk = ~clk;
add_if vif(clk, reset);
adder DUT(.clk(vif.clk),.reset(vif.reset),.in1(vif.ip1),.in2(vif.ip2),.out(vif.out));
base_test t1(vif);
initial begin
clk = 0;
reset = 1;
#5;
reset = 0;
end
endmodule
Output:
Matched: ip1 = 44, ip2 = 30, out = 74
Matched: ip1 = 86, ip2 = 27, out = 113
Matched: ip1 = 63, ip2 = 2, out = 65
Matched: ip1 = 99, ip2 = 11, out = 110
Matched: ip1 = 79, ip2 = 37, out = 116
System Verilog Tutorials