Professional Documents
Culture Documents
Before writing/creating the verification plan need to know about design, so will go
through the design specification.
Signal Definition:
1/3
wr_en input write enable signal, indicates the write
operation
Operations:
Write Operation:
address, wr_en, and wdata should be driven at the same clock cycle.
Read Operation:
address and rd_en should be driven on the same clock cycle, Design will respond with the
data in the next clock cycle.
Design Features,
2/3
TestBench Hierarchy and Architecture
3/3
SystemVerilog TestBench Example 01
verificationguide.com/systemverilog-examples/systemverilog-testbench-example-01
TestBench Architecture
SystemVerilog TestBench
Transaction Class
Fields required to generate the stimulus are declared in the transaction class
Transaction class can also be used as a placeholder for the activity monitored by the
monitor on DUT signals
So, the first step is to declare the Fields‘ in the transaction class
Below are the steps to write a transaction class
class transaction;
endclass
1/17
class transaction;
endclass
class transaction;
endclass
Generator Class
Generator class is responsible for,
class generator;
------
endclass
class generator;
endclass
2/17
class generator;
//main task, generates(create and randomizes) the packets and puts into mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
endtask
endclass
This involves,
Declaring the Mailbox
Getting the Mailbox handle from the env class. ( because the same mailbox will be
shared across generator and driver)
class generator;
//declaring mailbox
mailbox gen2driv;
//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
//main task, generates(create and randomizes) the packets and puts into mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
endtask
endclass
3/17
class generator;
//declaring mailbox
mailbox gen2driv;
//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
endclass
5. Adding an event to indicate the completion of the generation process, the event will
be triggered on the completion of the Generation process.
4/17
class generator;
//declaring mailbox
mailbox gen2driv;
//event
event ended;
//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
this.ended = ended;
endfunction
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
end
-> ended;
endtask
endclass
Interface:
Interface will group the signals, specifies the direction (Modport) and Synchronize the
signals(Clocking Block).
5/17
2. Monitor Clocking Block,
//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);
//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);
6/17
interface mem_intf(input logic clk,reset);
//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);
//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);
endinterface
Driver Class
Driver class is responsible for,
receive the stimulus generated from the generator and drive to DUT by assigning
transaction class values to interface signals.
class driver;
----
endclass
1. Declare interface and mailbox, Get the interface and mailbox handle through the
constructor
7/17
//creating virtual interface handle
virtual mem_intf mem_vif;
//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction
2. Adding a reset task, which initializes the Interface signals to default values
3. Adding a drive task to drive the transaction packet to the interface signal
8/17
//drive the transaction items to interface signals
task drive;
forever begin
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++;
end
endtask
4. Adding a local variable to track the number of packets driven, and increment the
variable in drive task
(This
will be useful to end the test-case/Simulation. i.e compare the
generated pkt’s and driven pkt’s, if both are same then end the
simulation)
//used to count the number of transactions
int no_transactions;
9/17
class driver;
//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction
10/17
no_transactions++;
end
endtask
endclass
Environment
Environment is container class contains Mailbox, Generator and Driver.
Creates the mailbox, generator and driver shares the mailbox handle across the
Generator and Driver.
class environment;
---
endclass
//mailbox handle's
mailbox gen2driv;
//virtual interface
virtual mem_intf mem_vif;
Mailbox
Generator
Driver
//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();
11/17
3. For better accessibility.
Generator and Driver activity can be divided and controlled in three methods.
pre_test() – Method to call Initialization. i.e, reset method.
test() – Method to call Stimulus Generation and Stimulus Driving.
post_test() – Method to wait the completion of generation and driving.
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
join_any
endtask
task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask
12/17
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
class environment;
//mailbox handle's
mailbox gen2driv;
//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();
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
join_any
endtask
task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask
//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask
13/17
endclass
Test
Test code is written with the program block.
program test;
----
endprogram
initial begin
//creating environment
env = new(intf);
end
//calling run of env, it interns calls generator and driver main tasks.
env.run();
14/17
`include "environment.sv"
program test(mem_intf intf);
initial begin
//creating environment
env = new(intf);
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
TestBench Top
This is the topmost file, which connects the DUT and TestBench.
TestBench top consists of DUT, Test and Interface instances.
The interface connects the DUT and TestBench.
module tbench_top;
---
endmodule
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
15/17
//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)
);
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
16/17
`include "interface.sv"
`include "random_test.sv"
module tbench_top;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
17/17
Memory Model TestBench With Monitor and
SystemVerilog TestBench
Only monitor and scoreboard are explained here, Refer to ‘Memory Model’ TestBench
Without Monitor, Agent, and Scoreboard for other components.
Monitor
Samples the interface signals and converts the signal level activity to the
transaction level
Send the sampled transaction to Scoreboard via Mailbox
Below are the steps to write a monitor
class monitor;
------
endclass
2. Declare interface and mailbox, Get the interface and mailbox handle through the
constructor.
1/9
//creating virtual interface handle
virtual mem_intf mem_vif;
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
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
2/9
`define MON_IF mem_vif.MONITOR.monitor_cb
class monitor;
//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
Scoreboard receives the sampled packet from monitor,
if the transaction type is “read”, compares the read data with the local memory
data if the transaction type is “write”, local memory will be written with the wdata
class scoreboard;
------
endclass
1. Declaring the mailbox and variable to keep count of transactions, connecting handle
through the constructor,
3/9
//creating mailbox handle
mailbox mon2scb;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
no_transactions++;
end
endtask
4/9
class scoreboard;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF;
endfunction
no_transactions++;
end
endtask
endclass
Environment
Here only updates are mentioned. i.e adding monitor and scoreboard to the previous
example.
5/9
//generator and driver instance
generator gen;
driver driv;
monitor mon; //---NEW CODE---
scoreboard scb; //---NEW CODE---
//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; //---NEW CODE---
//virtual interface
virtual mem_intf mem_vif;
Mailbox (mon2scb)
Monitor
Scoreboard
//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();
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
6/9
mon.main(); //---NEW CODE---
scb.main(); //---NEW CODE---
join_any
endtask
task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
7/9
class environment;
//mailbox handle's
mailbox gen2driv;
mailbox mon2scb;
//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();
//
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
8/9
task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
Edit and Execute the Memory Model TestBench code in EDA Playground.
https://www.edaplayground.com/x/3QTT
9/9