Tutorials

Learn More

The UVM scoreboard is a component that checks the functionality of the DUT. It receives transactions from the monitor using the analysis export for checking purposes.

uvm_scoreboard class declaration:

virtual class uvm_scoreboard extends uvm_component

User-defined scoreboard class declaration:

The user-defined scoreboard is extended from uvm_scoreboard which is derived from uvm_component.

class <scoreboard_name> extends uvm_scoreboard;

uvm_scoreboard class hierarchy

uvm_scoreboard hierarchy

Scoreboard Usage

  1. Receive transactions from monitor using analysis export for checking purposes.
  2. The scoreboard has a reference model to compare with design behavior. The reference model is also known as a predictor that implements design behavior so that the scoreboard can compare DUT outcome with reference model outcome for the same driven stimulus.
UVM scoreboard block diagram

How to write scoreboard code in UVM?

  1. Create a user-defined scoreboard class extended from uvm_scoreboard and register it in the factory.
  2. Declare an analysis export to receive the sequence items or transactions from the monitor.
  3. Write standard new() function. Since the scoreboard is a uvm_component. The new() function has two arguments as string name and uvm_component parent.
  4. Implement build_phase and create a TLM analysis export instance.
  5. Implement a write method to receive the transactions from the monitor.
  6. Implement run_phase to check DUT functionality throughout simulation time.

Scoreboard Example

class scoreboard extends uvm_scoreboard;
  uvm_analysis_imp #(seq_item, scoreboard) item_collect_export;
  seq_item item_q[$];
  `uvm_component_utils(scoreboard)
  
  function new(string name = "scoreboard", uvm_component parent = null);
    super.new(name, parent);
    item_collect_export = new("item_collect_export", this);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
  
  function void write(seq_item req);
    `uvm_info(get_type_name, $sformatf("Received transaction = %s", req), UVM_LOW);
    item_q.push_back(req);
  endfunction
  
  task run_phase (uvm_phase phase);
    seq_item sb_item;
    forever begin
      wait(item_q.size > 0);
      
      if(item_q.size > 0) begin
        sb_item = item_q.pop_front();
        // Checking comparing logic
        ...        
      end
    end
  endtask
endclass

UVM Scoreboad types

Depends on design functionality scoreboards can be implemented in two ways.

  1. In-order scoreboard
  2. Out-of-order scoreboard

In-order scoreboard

The in-order scoreboard is useful for the design whose output order is the same as driven stimuli. The comparator will compare the expected and actual output streams in the same order. They will arrive independently. Hence, the evaluation must block until both expected and actual transactions are present.

In-order scoreboard in uvm

To implement such scoreboards, an easier way would be to implement TLM analysis FIFOs. For more details visit the TLM analysis FIFO section. In the below example, there are two monitors whose analysis port is connected to the scoreboard to provide input and output transactions.

class inorder_sb extends uvm_scoreboard;
  `uvm_component_utils(inorder_sb)
  uvm_analysis_export #(txn) in_export, out_export;
  uvm_tlm_analysis_fifo #(txn) in_fifo, out_fifo;

  function new (string name = "inorder_sb" , uvm_component parent = null) ;
    super.new(name, parent);
  endfunction


  function void build_phase (uvm_phase phase);
    in_fifo    = new("in_fifo", this);
    out_fifo   = new("out_fifo", this);
    in_export  = new("in_export", this);
    out_export = new("out_export", this);
  endfunction


  function void connect_phase (uvm_phase phase);
    in_export.connect(in_fifo.analysis_export);
    out_export.connect(out_fifo.analysis_export);
  endfunction


  task run_phase( uvm_phase phase);
    txn in_txn;
    txn exp_txn, act_txn;
    forever begin
      in_fifo.get(in_txn);
      process_data(in_txn, exp_txn); 
      out_fifo.get(act_txn);
      if (!exp_txn.compare(act_txn)) begin
        `uvm_error(get_full_name(), $sformat("%s does not match %s", exp_txn.sprint(), act_txn.sprint()), UVM_LOW);
      end
    end
  endtask
  
  // Reference model 
  task process_data(input txn in_txn, output txn exp_txn);
    // Generate expected txn for driven stimulus
    ...
    ...
  endtask
endclass

Out-of-order scoreboard

The out-of-order scoreboard is useful for the design whose output order is different from driven input stimuli. Based on the input stimuli reference model will generate the expected outcome of DUT and the actual output is expected to come in any order. So, it is required to store such unmatched transactions generated from the input stimulus until the corresponding output has been received from the DUT to be compared. To store such transactions, an associative array is widely used. Based on index value, transactions are stored in the expected and actual associative arrays. The entries from associative arrays are deleted when comparison happens for the matched array index.

Out-of-order scoreboard in uvm
class txn extends uvm_sequence_item;
  int id;  
  // other class properties
  //...
endclass

class out_of_order_sb extends uvm_scoreboard;
  `uvm_component_utils(out_of_order_sb)
  uvm_analysis_export #(txn) in_export, out_export;
  uvm_tlm_analysis_fifo #(txn) in_fifo, out_fifo;
  // associative array of class type as txn and indexed by int
  txn expected_out_array[int];
  txn actual_out_array[int];

  // Store idx in separate queues.
  int expected_out_q[$], actaul_out_q[$];

  function new (string name = "out_of_order_sb" , uvm_component parent = null) ;
    super.new(name, parent);
  endfunction


  function void build_phase (uvm_phase phase);
    in_fifo    = new("in_fifo", this);
    out_fifo   = new("out_fifo", this);
    in_export  = new("in_export", this);
    out_export = new("out_export", this);
  endfunction


  function void connect_phase (uvm_phase phase);
    in_export.connect(in_fifo.analysis_export);
    out_export.connect(out_fifo.analysis_export);
  endfunction


  task run_phase( uvm_phase phase);
    txn in_txn, out_txn;
    forever begin
      fork      
        begin 
          in_fifo.get(in_txn);
          process_data(in_txn);
        end
        begin
          out_fifo.get(out_txn);
          actual_out_array[out_txn.id] = out_txn;
          actaul_out_q.push_back(out_txn.id);
        end

      join
      compare_data();
    end
  endtask
  
  // check_phase to check whether any entry is pending in queues.
  function void check_phase(uvm_phase phase);
    super. check_phase(phase);
    if(expected_out_q.size() != 0) `uvm_info (get_full_name(), $sformatf("expected_out_q size = %0d", expected_out_q.size()), UVM_LOW);
    if(actaul_out_q.size() != 0) `uvm_info (get_full_name(), $sformatf("actaul_out_q size = %0d", actaul_out_q.size()), UVM_LOW);
  endfunction

  task process_data(txn in_txn);
    txn exp_out_txn;
    // Using reference models, generate output for input stimulus.
    // store expected output (exp_out_txn) in expected_out_array
    ...
    ...
    expected_out_array[in_txn.id] = exp_out_txn;
    expected_out_q.push_back(in_txn.id);
  endtask
  
  task compare_data();
    int idx;
    txn exp_txn, act_txn;
    if(expected_out_q.size() > && actaul_out_q.size() > 0) begin
      idx = expected_out_q.pop_front();
      
      // Look for idx in actual_out_array to see whether the output has been received for a driven stimulus or not.
      if(actual_out_array.exists(idx)) begin 
        exp_txn = expected_out_array[idx];
        act_txn = actual_out_array[idx];
        
        if(!exp_txn.compare(act_txn)) begin
          `uvm_error(get_full_name(), $sformat("%s does not match %s", exp_txn.sprint(), act_txn.sprint()), UVM_LOW);
        end
        else begin
          expected_out_array.delete(idx);
          actual_out_array.delete(idx);
        end
      end
      else expected_out_q.push_back(idx); // exp_idx is not found in actual_out_array.
    end
  endtask
endclass