UVM Scoreboard

The judge of the testbench. Comparing Actual vs Expected, handling Orphans, and debugging mismatches.

The Judicial Role

What Does a Scoreboard Do?

The Simple Version: The Scoreboard is the "Judge" or "Referee" of your Verification environment. It doesn't check how the signals toggled (the Monitor does that) or what was sent (the Sequence does that).

It strictly checks: Did the Output match the Input?

The Data Flow

graph LR subgraph Testbench DUT(DUT) -->|Interface| Mon[Monitor] Mon -->|Analysis Port| SCB[Scoreboard] end subgraph Scoreboard_Internals ["Scoreboard Internals"] SCB --> FIFO_Act[FIFO: Actual] Pred[Predictor] -->|Analysis Port| FIFO_Exp[FIFO: Expected] FIFO_Act --> CMP{Comparator} FIFO_Exp --> CMP end CMP -->|Match?| Res[Pass/Fail] style Scoreboard_Internals fill:#f1f8e9,stroke:#8bc34a,stroke-width:2px style CMP fill:#fff9c4,stroke:#fbc02d,stroke-width:2px,shape:diamond
Scoreboard vs. Coverage: What's the difference?
  • Scoreboard (Correctness): "Did the design produce the right answer?" (e.g., 2+2=4)
  • Functional Coverage (Completeness): "Did we ask enough questions?" (e.g., Did we test 0+0, max+max, etc.?)

Comparison Strategies

How you store data inside the scoreboard depends entirely on your design's latency and ordering rules.

1. In-Order (FIFO) Comparison

Used for simple pipelines where Packet A always leaves before Packet B. Implementation usually involves a uvm_tlm_analysis_fifo.

2. Out-of-Order (Associative Map) Comparison

Used for complex protocols like AXI or NVMe where transactions can complete in any order based on IDs or Tags. You store expected items in an Associative Array (indexed by transaction ID) and delete them when found.

→ Deep Dive: Out-of-Order Scoreboarding

Professional Implementation (Debug Focused)

A robust scoreboard doesn't just check data; it helps you find bugs. This implementation features Orphan Detection (missing packets) and Detailed Reporting.

Robust In-Order Scoreboard

class eth_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(eth_scoreboard)
    // FIFOs decouple the 'write' function from the 'compare' task
    uvm_tlm_analysis_fifo #(eth_pkt) exp_fifo;
    uvm_tlm_analysis_fifo #(eth_pkt) act_fifo;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        exp_fifo = new("exp_fifo", this);
        act_fifo = new("act_fifo", this);
    endfunction
    // Main comparison loop
    task run_phase(uvm_phase phase);
        eth_pkt exp, act;
        forever begin
            // 1. Wait for BOTH streams to have data
            exp_fifo.get(exp);
            act_fifo.get(act);
            // 2. Compare with detailed Diffing
            if (!act.compare(exp)) begin
                `uvm_error("MISMATCH", $sformatf("Packet Mismatch!\nExpected: %0h\nActual:   %0h", 
                                                 exp.data, act.data))
            end else begin
                `uvm_info("PASS", $sformatf("Match! ID: %0d", exp.id), UVM_HIGH)
            end
        end
    endtask
    // The "Orphan" Check: Did we lose any packets?
    function void check_phase(uvm_phase phase);
        if (exp_fifo.is_empty() && act_fifo.is_empty()) begin
            `uvm_info("CHECK", "Scoreboard clean. No orphans.", UVM_LOW)
        end else begin
            `uvm_error("ORPHAN", $sformatf("Orphans detected! Exp Remaining: %0d, Act Remaining: %0d", 
                                           exp_fifo.used(), act_fifo.used()))
        end
    endfunction
endclass
                            

Pro-Tip: Transaction Recording

Stop debugging with just 1s and 0s.

Use begin_tr(tr) and end_tr(tr) in your monitor. This tells the simulator (SimVision, Verdi) to draw a colored bubble or bar in the waveform window for the duration of the packet. You can then click the bubble to see the transaction fields!