SystemVerilog TestBench Example - Memory Model with Monitor and Scoreboard


-: Internal Links :-       
 Monitor                      
 Scoreboard                  
 Environment         
 EDAPlayground Link  

 'Memory Model' TestBench With Monitor and Scoreboard 


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 convert the signal level activity to transaction level.
  • Send the sampled transaction to Scoreboard via Mailbox.
  • Below are the steps to write monitor.
1. Writing monitor class.

class monitor;
  ------

endclass


2. Declare interface and mailbox, Get the interface and mailbox handle through 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 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 receive's 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 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 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 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.


Execute the above code on 

Click on EDA playground image.

Leave your Comments here,

"Your valuable inputs are required to improve the quality"