https://verificationguide.com/systemverilog-examples/systemverilog-testbench-example-memory/
//memory dut
module memory
#(
parameter ADDR_WIDTH = 2,
parameter DATA_WIDTH = 8
)
(
input clk,
input reset,
//control signals
input [ADDR_WIDTH-1:0] addr,
input wr_en,
input rd_en,
//data signals
input [DATA_WIDTH-1:0] wdata,
output [DATA_WIDTH-1:0] rdata
);
reg [DATA_WIDTH-1:0] rdata;
//Memory
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
//Reset
always @(posedge reset)
for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8'hFF;
// Write data to Memory
always @(posedge clk)
if (wr_en) mem[addr] <= wdata;
// Read data from memory
always @(posedge clk)
if (rd_en) rdata <= mem[addr];
endmodule
//testbench.sv
//-------------------------------------------------------------------------
// www.verificationguide.com testbench.sv
//-------------------------------------------------------------------------
//tbench_top or testbench top, this is the top most file, in which DUT(Design Under Test) and Verification environment are connected.
//-------------------------------------------------------------------------
//including interfcae and testcase files
`include "interface.sv"
//-------------------------[NOTE]---------------------------------
//Particular testcase can be run by uncommenting, and commenting the rest
//`include "random_test.sv"
`include "wr_rd_test.sv"
//`include "default_rd_test.sv"
//----------------------------------------------------------------
module tbench_top;
//clock and reset signal declaration
bit clk;
bit reset;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
//creatinng instance of interface, inorder to connect DUT and testcase
mem_intf intf(clk,reset);
//Testcase instance, interface handle is passed to test as an argument
test t1(intf);
//DUT instance, interface signals are connected to the DUT ports
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);
//enabling the wave dump
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule
//--------------
//transaction.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
class transaction;
//declaring the transaction items
rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;
//constaint, to generate any one among write and read
constraint wr_rd_c { wr_en != rd_en; };
//postrandomize function, displaying randomized values of items
function void post_randomize();
$display("--------- [Trans] post_randomize ------");
//$display("\t addr = %0h",addr);
if(wr_en) $display("\t addr = %0h\t wr_en = %0h\t wdata = %0h",addr,wr_en,wdata);
if(rd_en) $display("\t addr = %0h\t rd_en = %0h",addr,rd_en);
$display("-----------------------------------------");
endfunction
//deep copy method
function transaction do_copy();
transaction trans;
trans = new();
trans.addr = this.addr;
trans.wr_en = this.wr_en;
trans.rd_en = this.rd_en;
trans.wdata = this.wdata;
return trans;
endfunction
endclass
//---------------generator.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
class generator;
//declaring transaction class
rand transaction trans,tr;
//repeat count, to specify number of items to generate
int repeat_count;
//mailbox, to generate and send the packet to driver
mailbox gen2driv;
//event
event ended;
//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env, in order to share the transaction packet between the generator and driver, the same mailbox is shared between both.
this.gen2driv = gen2driv;
this.ended = ended;
trans = new();
endfunction
//main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox
task main();
repeat(repeat_count) begin
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
tr = trans.do_copy();
gen2driv.put(tr);
end
-> ended;
endtask
endclass
//-----------------------driver.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
//gets the packet from generator and drive the transaction paket items into interface (interface is connected to DUT, so the items driven into interface signal will get driven in to DUT)
`define DRIV_IF mem_vif.DRIVER.driver_cb
class driver;
//used to count the number of transactions
int no_transactions;
//creating virtual interface handle
virtual mem_intf mem_vif;
//creating mailbox handle
mailbox gen2driv;
//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handles from environment
this.gen2driv = gen2driv;
endfunction
//Reset task, Reset the Interface signals to default/initial values
task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended ---------");
endtask
//drivers the transaction items to interface signals
task drive;
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
no_transactions++;
endtask
//
task main;
forever begin
fork
//Thread-1: Waiting for reset
begin
wait(mem_vif.reset);
end
//Thread-2: Calling drive task
begin
forever
drive();
end
join_any
disable fork;
end
endtask
endclass
//------------------------enviroment.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
//generator and driver instance
generator gen;
driver driv;
monitor mon;
scoreboard scb;
//mailbox handle's
mailbox gen2driv;
mailbox mon2scb;
//event for synchronization between generator and test
event gen_ended;
//virtual interface
virtual mem_intf mem_vif;
//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;
//creating the mailbox (Same handle will be shared across generator and driver)
gen2driv = new();
mon2scb = new();
//creating generator and driver
gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_vif,mon2scb);
scb = new(mon2scb);
endfunction
//
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
mon.main();
scb.main();
join_any
endtask
task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
wait(gen.repeat_count == scb.no_transactions);
endtask
//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
//------------------------random_test.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
`include "environment.sv"
program test(mem_intf intf);
//declaring environment instance
environment env;
initial begin
//creating environment
env = new(intf);
//setting the repeat count of generator as 4, means to generate 4 packets
env.gen.repeat_count = 4;
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
//-------------------interface.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
interface mem_intf(input logic clk,reset);
//declaring the signals
logic [1:0] addr;
logic wr_en;
logic rd_en;
logic [7:0] wdata;
logic [7:0] rdata;
//driver clocking block
clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking
//monitor clocking block
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking
//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);
//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);
endinterface
///////------------wr_rd_test.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
`include "environment.sv"
program test(mem_intf intf);
class my_trans extends transaction;
bit [1:0] count;
function void pre_randomize();
wr_en.rand_mode(0);
rd_en.rand_mode(0);
addr.rand_mode(0);
if(cnt %2 == 0) begin
wr_en = 1;
rd_en = 0;
addr = count;
end
else begin
wr_en = 0;
rd_en = 1;
addr = count;
count++;
end
cnt++;
endfunction
endclass
//declaring environment instance
environment env;
my_trans my_tr;
initial begin
//creating environment
env = new(intf);
my_tr = new();
//setting the repeat count of generator as 4, means to generate 4 packets
env.gen.repeat_count = 10;
env.gen.trans = my_tr;
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
//----------------default_rd_test.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
`include "environment.sv"
program test(mem_intf intf);
class my_trans extends transaction;
bit [1:0] count;
function void pre_randomize();
wr_en.rand_mode(0);
rd_en.rand_mode(0);
addr.rand_mode(0);
wr_en = 0;
rd_en = 1;
addr = cnt;
cnt++;
endfunction
endclass
//declaring environment instance
environment env;
my_trans my_tr;
initial begin
//creating environment
env = new(intf);
my_tr = new();
//setting the repeat count of generator as 4, means to generate 4 packets
env.gen.repeat_count = 4;
env.gen.trans = my_tr;
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
//---------------monitor.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
//Samples the interface signals, captures into transaction packet and send the packet to scoreboard.
`define MON_IF mem_vif.MONITOR.monitor_cb
class monitor;
//creating virtual interface handle
virtual mem_intf mem_vif;
//creating mailbox handle
mailbox mon2scb;
//constructor
function new(virtual mem_intf mem_vif,mailbox mon2scb);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
//Samples the interface signal and send the sample packet to scoreboard
task main;
forever begin
transaction trans;
trans = new();
@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if(`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask
endclass
//-------------------------scoreboard.sv
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
//gets the packet from monitor, Generated the expected result and compares with the //actual result recived from Monitor
class scoreboard;
//creating mailbox handle
mailbox mon2scb;
//used to count the number of transactions
int no_transactions;
//array to use as local memory
bit [7:0] mem[4];
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF;
endfunction
//stores wdata and compare rdata with stored data
task main;
transaction trans;
forever begin
#50;
mon2scb.get(trans);
if(trans.rd_en) begin
if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual = %0h",trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h Actual = %0h",trans.addr,mem[trans.addr],trans.rdata);
end
else if(trans.wr_en)
mem[trans.addr] = trans.wdata;
no_transactions++;
end
endtask
endclass