You are on page 1of 7

System

Verilog
Testbench
adder
Example
SystemVerilog Testbench Adder Example

Here is an example of how a SystemVerilog testbench can be constructed to verify the


functionality of a simple adder. Remember that the goal here is to develop a modular and scalable
testbench architecture with all the standard verification components in a testbench.

Design

// An adder is combinational logic and does not


// have a clock

// Module definition for the adder


module my_adder(
adder_if _if // Input/output interface for the adder
);
// Combinational logic block
always_comb begin
// Check if the reset signal is asserted
if (_if.rstn) begin
// If reset is active, clear the sum and carry
_if.sum <= 0;
_if.carry <= 0;
end else begin
// If not in reset, perform addition operation
// Concurrently assign the sum of 'a' and 'b' to 'sum' and 'carry'
{_if.carry, _if.sum} <= _if.a + _if.b;
end
end
endmodule

Interface

// Adder interface contains all signals that the adder requires


// to operate
interface adder_if();
logic rstn;
logic [7:0] a;
logic [7:0] b;
logic [7:0] sum;
logic carry;
endinterface

// Although an adder does not have a clock, let us create a mock clock
// used in the testbench to synchronize when value is driven and when
// value is sampled. Typically combinational logic is used between
// sequential elements like FF in a real circuit. So, let us assume
// that inputs to the adder are provided at some posedge clock. But because
// the design does not have a clock in its input, we will keep this clock
// in a separate interface that is available only to testbench components
interface clk_if();
logic tb_clk;
initial tb_clk <= 0;
always #10 tb_clk = ~tb_clk;
endinterface

Transaction Object

// To verify that the adder adds, we also need to check that it


// does not add when rstn is 0, and hence rstn should also be
// randomized along with a and b.
class Packet;
rand bit rstn;
rand bit [7:0] a;
rand bit [7:0] b;
bit [7:0] sum;
bit carry;

// Print contents of the data packet


function void print(string tag = "");
$display("T=%0t %s a=0x%0h b=0x%0h sum=0x%0h carry=0x%0h", $time, tag, a,
b, sum, carry);
endfunction

// This is a utility function to allow copying contents in


// one Packet variable to another.
function void copy(Packet tmp);
this.a = tmp.a;
this.b = tmp.b;
this.rstn = tmp.rstn;
this.sum = tmp.sum;
this.carry = tmp.carry;
endfunction
endclass

Driver

class driver;
virtual adder_if m_adder_vif;
virtual clk_if m_clk_vif;
event drv_done;
mailbox drv_mbx;

task run();
$display("T=%0t [Driver] starting ...", $time);

// Try to get a new transaction every time and then assign


// packet contents to the interface. But do this only if the
// design is ready to accept new transactions
forever begin
Packet item;
$display("T=%0t [Driver] waiting for item ...", $time);
drv_mbx.get(item);
@(posedge m_clk_vif.tb_clk);
item.print("Driver");
m_adder_vif.rstn <= item.rstn;
m_adder_vif.a <= item.a;
m_adder_vif.b <= item.b;
->drv_done;
end
endtask
endclass

Monitor

class monitor;
virtual adder_if m_adder_vif;
virtual clk_if m_clk_vif;
mailbox scb_mbx; // Mailbox connected to scoreboard

// Mailbox connected to scoreboard


task run();
$display("T=%0t [Monitor] starting ...", $time);

// Check forever at every clock edge to see if there is a


// valid transaction and if yes, capture info into a class
// object and send it to the scoreboard when the transaction
// is over.
forever begin
Packet m_pkt = new();
@(posedge m_clk_vif.tb_clk);
#1;
m_pkt.a = m_adder_vif.a;
m_pkt.b = m_adder_vif.b;
m_pkt.rstn = m_adder_vif.rstn;
m_pkt.sum = m_adder_vif.sum;
m_pkt.carry = m_adder_vif.carry;
m_pkt.print("Monitor");
scb_mbx.put(m_pkt);
end
endtask
endclass

Generator

class generator;
int loop = 10;
event drv_done;
mailbox drv_mbx;

task run();
for (int i = 0; i < loop; i++) begin
Packet item = new;
item.randomize();
$display("T=%0t [Generator] Loop:%0d/%0d create next item", $time, i +
1, loop);
drv_mbx.put(item);
$display("T=%0t [Generator] Wait for driver to be done", $time);
@(drv_done);
end
endtask
endclass

Environment

// The environment class manages the verification components - generator,


driver, monitor, and scoreboard.
class env;
generator g0; // Generator instance for generating random
transactions
driver d0; // Driver instance for driving transactions to the
design
monitor m0; // Monitor instance for capturing transactions and
sending to the scoreboard
scoreboard s0; // Scoreboard instance for checking data integrity
mailbox scb_mbx; // Mailbox for communication between scoreboard and
monitor
virtual adder_if m_adder_vif; // Virtual interface handle for the adder
virtual clk_if m_clk_vif; // Virtual interface handle for the clock
event drv_done; // Event indicating driver completion
mailbox drv_mbx; // Mailbox for communication between
generator and driver

function new();
// Initialize instances
d0 = new;
m0 = new;
s0 = new;
scb_mbx = new();
g0 = new;
drv_mbx = new;
endfunction

virtual task run();


// Connect virtual interface handles
d0.m_adder_vif = m_adder_vif;
m0.m_adder_vif = m_adder_vif;
d0.m_clk_vif = m_clk_vif;
m0.m_clk_vif = m_clk_vif;

// Connect mailboxes between components


d0.drv_mbx = drv_mbx;
g0.drv_mbx = drv_mbx;
m0.scb_mbx = scb_mbx;
s0.scb_mbx = scb_mbx;

// Connect event handles


d0.drv_done = drv_done;
g0.drv_done = drv_done;

// Start all components using fork join_any


fork
s0.run();
d0.run();
m0.run();
g0.run();
join_any
endtask
endclass

Scoreboard

class scoreboard;
mailbox scb_mbx;

task run();
forever begin
Packet item, ref_item;
scb_mbx.get(item);
item.print("Scoreboard");

// Copy contents from received packet into a new packet so


// just to get a and b.
ref_item = new();
ref_item.copy(item);

// Let us calculate the expected values in carry and sum


if (ref_item.rstn) begin
{ref_item.carry, ref_item.sum} = ref_item.a + ref_item.b;
end else begin
{ref_item.carry, ref_item.sum} = 0;
end

// Now, carry and sum outputs in the reference variable can be compared
// with those in the received packet
if (ref_item.carry != item.carry) begin
$display("[%0t] Scoreboard Error! Carry mismatch ref_item=0x%0h
item=0x%0h", $time, ref_item.carry, item.carry);
end else begin
$display("[%0t] Scoreboard Pass! Carry match ref_item=0x%0h
item=0x%0h", $time, ref_item.carry, item.carry);
end

if (ref_item.sum != item.sum) begin


$display("[%0t] Scoreboard Error! Sum mismatch ref_item=0x%0h
item=0x%0h", $time, ref_item.sum, item.sum);
end else begin
$display("[%0t] Scoreboard Pass! Sum match ref_item=0x%0h
item=0x%0h", $time, ref_item.sum, item.sum);
end
end
endtask
endclass
Test

// The test class is responsible for instantiating the environment and


running the test.
class test;
env e0; // Environment instance for verification components
mailbox drv_mbx; // Mailbox for communication between the test and the
environment

function new();
drv_mbx = new(); // Initialize mailbox for driver communication
e0 = new(); // Instantiate the environment
endfunction

virtual task run();


e0.d0.drv_mbx = drv_mbx; // Connect the driver mailbox from the
environment to the test
e0.run(); // Run the environment
endtask
endclass

Testbench TOP

// Testbench module to instantiate design, interface, start clocks, and run


the test
module tb;
bit tb_clk; // Testbench clock
clk_if m_clk_if(); // Clock interface instance
adder_if m_adder_if(); // Adder interface instance
my_adder u0(m_adder_if); // Instantiate the adder module

initial begin
test t0; // Test instance
t0 = new; // Create a new test instance

// Connect interfaces between test and environment


t0.e0.m_adder_vif = m_adder_if;
t0.e0.m_clk_vif = m_clk_if;

t0.run(); // Run the test

// Once the main stimulus is over, wait for some time


// until all transactions are finished and then end
// simulation. Note that $finish is required because
// there are components that are running forever in
// the background like clk, monitor, driver, etc
#50 $finish;
end
endmodule

You might also like