Memory Model TestBench With Monitor and Scoreboard
Table of Contents
TestBench Architecture:

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
1. Writing monitor class.
class monitor; ------ endclass
2. Declare interface and mailbox, Get the interface and mailbox handle through the constructor.
//creating virtual interface handle
virtual mem_intf mem_vif;
//creating mailbox handle
mailbox mon2scb;
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
3. Sampling logic and sending the sampled transaction to the 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
4. Complete monitor code.
`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
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,
//creating mailbox handle
mailbox mon2scb;
//used to count the number of transactions
int no_transactions;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
2. logic to store wdata and compare rdata with stored data,
//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
3. Complete scoreboard code.
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
Environment
Here only updates are mentioned. i.e adding monitor and scoreboard to the previous example.
1. Declare the handles,
//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;
2. In Construct Method, Create
- Mailbox (mon2scb)
- Monitor
- Scoreboard
and pass the interface handle through the new() method.
//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
3. Calling monitor and scoreboard tasks,
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
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);
wait(gen.repeat_count == scb.no_transactions); //---NEW CODE--- endtask
4. Complete environment class code,
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
Edit and Execute Memory Model TestBench code in EDA Playground.
