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.
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.
Figure 8.2 – State of the testbench after the scoreboard
For more information about scoreboards you can consult:
- Accellera’s UVM 1.1 User’s Guide, page 72
- Verification Academy’s UVM Cookbook, pages 155 and 163
- Comprehensive Functional Verification: The Complete Industry Cycle, page 82
The code for the scoreboard can be consulted here: