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.