Chapter 8 – Scoreboard

The scoreboard is a crucial element in a self-checking environment, it verifies the proper operation of a design at a functional level. This component is the most difficult one to write, it varies from project to project and from designer to designer.

In our case, we decided to make the prediction of the DUT functionality in the monitors and let the scoreboard compare the prediction with the DUT’s response. But there are designers who prefer to leave the prediction to the scoreboard. So the functionality of the scoreboard is very subjective.

In the agent, we created two monitors, as a result, we will have to create two analysis exports in the scoreboard that are going to be used to retrieve transactions from both monitors. After that, a method compare() is going to be executed in the run phase and compare both transactions. If they match, it means that the testbench and the DUT both agree in the functionality and it will return an “OK” message.

But we have a problem: we have two transaction streams coming from two monitors and we need to make sure they are synchronized. This could be done manually by writing appropriated write() functions but there is an easier and cleaner way of doing this: by using UVM FIFO.

These FIFO will work as it’s represented in Figure 8.1.

Usage of FIFO in the scoreboard

Figure 8.1 – Usage of FIFO in the scoreboard

The FIFO are instantiated similarly to ports/exports, with uvm_tlm_analysis_fifo #(generic_transaction) generic_fifo and they already implement the respective write() functions that are called from the monitors. To access their data we just execute the get() method from each FIFO.

The code from the scoreboard follows in Code 8.1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class simpleadder_scoreboard extends uvm_scoreboard;
     `uvm_component_utils(simpleadder_scoreboard)
 
     uvm_analysis_export #(simpleadder_transaction) sb_export_before;
     uvm_analysis_export #(simpleadder_transaction) sb_export_after;
 
     uvm_tlm_analysis_fifo #(simpleadder_transaction) before_fifo;
     uvm_tlm_analysis_fifo #(simpleadder_transaction) after_fifo;
 
     simpleadder_transaction transaction_before;
     simpleadder_transaction transaction_after;
 
     function new(string name, uvm_component parent);
          super.new(name, parent);
          transaction_before    = new("transaction_before");
          transaction_after    = new("transaction_after");
     endfunction: new
 
     function void build_phase(uvm_phase phase);
          super.build_phase(phase);
          sb_export_before    = new("sb_export_before", this);
          sb_export_after        = new("sb_export_after", this);
 
          before_fifo        = new("before_fifo", this);
          after_fifo        = new("after_fifo", this);
     endfunction: build_phase
 
     function void connect_phase(uvm_phase phase);
          sb_export_before.connect(before_fifo.analysis_export);
          sb_export_after.connect(after_fifo.analysis_export);
     endfunction: connect_phase
 
     task run();
          forever begin
               before_fifo.get(transaction_before);
               after_fifo.get(transaction_after);
               compare();
          end
     endtask: run
 
     virtual function void compare();
          if(transaction_before.out == transaction_after.out) begin
               `uvm_info("compare", {"Test: OK!"}, UVM_LOW);
          end else begin
               `uvm_info("compare", {"Test: Fail!"}, UVM_LOW);
          end
     endfunction: compare
endclass: simpleadder_scoreboard

Code 8.1 – Code for the scoreboard

In Figure 8.2, it’s represented the current state of our testbench.

State of the testbench after the scoreboardFigure 8.2 – State of the testbench after the scoreboard

For more information about scoreboards you can consult:

The code for the scoreboard can be consulted here: