SystemVerilog TestBench Example – with Scb

‘ADDER’ TestBench With Monitor and Scoreboard

TestBench Architecture

SystemVerilog TestBench Scoreboard block diagram
SystemVerilog TestBench

Only monitor and scoreboard are explained here, Refer to ‘ADDER’ 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 intf 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 vif.clk);
      wait(vif.valid);
      trans.a   = vif.a;
      trans.b   = vif.b;
      @(posedge vif.clk);
      trans.c   = vif.c;
      @(posedge vif.clk);
      mon2scb.put(trans);
      trans.display("[ Monitor ]");
    end
  endtask

4. Complete monitor code.

class monitor;
  
  //creating virtual interface handle
  virtual intf 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
  
  //Samples the interface signal and send the sample packet to scoreboard
  task main;
    forever begin
      transaction trans;
      trans = new();
      @(posedge vif.clk);
      wait(vif.valid);
      trans.a   = vif.a;
      trans.b   = vif.b;
      @(posedge vif.clk);
      trans.c   = vif.c;
      @(posedge vif.clk);
      mon2scb.put(trans);
      trans.display("[ Monitor ]");
    end
  endtask
  
endclass

Scoreboard

Scoreboard receives the sampled packet from the monitor and compare with the expected result, an error will be reported if the comparison results in a mismatch.

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 compare the received result with the expected result,

Note: As the DUT behavior is simple, a single line is added for generating the expected output.
Complex designs may use the reference model to generate the expected output.

(trans.a+trans.b) == trans.c

  //Compares the Actual result with the expected result
  task main;
    transaction trans;
    forever begin
      mon2scb.get(trans);
        if((trans.a+trans.b) == trans.c)
          $display("Result is as Expected");
        else
          $error("Wrong Result.\n\tExpeced: %0d Actual: %0d",(trans.a+trans.b),trans.c);
        no_transactions++;
      trans.display("[ Scoreboard ]");
    end
  endtask

3. Complete scoreboard code.

class scoreboard;
   
  //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
  
  //Compares the Actual result with the expected result
  task main;
    transaction trans;
    forever begin
      mon2scb.get(trans);
        if((trans.a+trans.b) == trans.c)
          $display("Result is as Expected");
        else
          $error("Wrong Result.\n\tExpeced: %0d Actual: %0d",(trans.a+trans.b),trans.c);
        no_transactions++;
      trans.display("[ Scoreboard ]");
    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 intf vif;

2. In Construct Method, Create

  • Mailbox (mon2scb)
  • Monitor
  • Scoreboard

and pass the interface handle through the new() method.

  //constructor
  function new(virtual intf vif);
    //get the interface from test
    this.vif = vif;
    
    //creating the mailbox (Same handle will be shared across generator and driver)
    gen2driv = new();
    mon2scb  = new();   //---NEW CODE---
    
    //creating generator and driver
    gen  = new(gen2driv);
    driv = new(vif,gen2driv);
    mon  = new(vif,mon2scb);   //---NEW CODE---
    scb  = new(mon2scb);       //---NEW CODE---
  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.

`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;
  
  //virtual interface
  virtual intf vif;
  
  //constructor
  function new(virtual intf vif);
    //get the interface from test
    this.vif = vif;
    
    //creating the mailbox (Same handle will be shared across generator and driver)
    gen2driv = new();
    mon2scb  = new();
    
    //creating generator and driver
    gen  = new(gen2driv);
    driv = new(vif,gen2driv);
    mon  = new(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); //Optional
    wait(gen.repeat_count == scb.no_transactions);
  endtask  
  
  //run task
  task run;
    pre_test();
    test();
    post_test();
    $finish;
  endtask
  
endclass

Edit and Execute Simple adder TestBench code in EDA Playground.

Click to execute on   

❮ Previous