Part 8 · Checking & Coverage · Intermediate

ID-Based Out-of-Order Matching

Associative array keyed by transaction ID for AXI, PCIe, and reordering buses.

Why ID matching exists

High-performance buses allow multiple outstanding transactions. AXI permits many reads and writes in flight simultaneously, each tagged with an ID. The interconnect and slave may complete them in any order — response for ID=5 may arrive before response for ID=2. FIFO matching pairs by arrival order of predictions, not by logical transaction identity. ID matching pairs by the protocol's transaction ID field.

The associative array (associative array in SystemVerilog, keyed by ID) stores expected transactions until the matching actual arrives. write_exp inserts by ID; write_act looks up by ID, compares, and deletes. Order of arrival no longer matters.

diagram
[CHECK] AXI reorder — why FIFO fails

  Outstanding writes:  ID=2 addr=0x1000  ID=5 addr=0x2000  ID=1 addr=0x3000

  Predict order:  exp[id=2]  exp[id=5]  exp[id=1]

  Response order (DUT reordered):  act[id=5]  act[id=1]  act[id=2]

  FIFO: pop exp[id=2] vs act[id=5]  WRONG PAIR  false mismatch
  ID map: lookup exp_by_id[5] vs act[id=5]  CORRECT PAIR  match
diagram
[CHECK] ID associative array lifecycle

  write_exp(txn id=N):  exp_by_id[N] = clone(txn)   // store expected
  write_act(txn id=N):  exp = exp_by_id[N]          // lookup
                        compare(act, exp)
                        exp_by_id.delete(N)          // consume entry

  check_phase:  exp_by_id.num() > 0  unmatched expected (DUT dropped response)

Full ID-based scoreboard implementation

systemverilog
class axi_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(axi_scoreboard)
  `uvm_analysis_imp_decl(_exp)
  `uvm_analysis_imp_decl(_act)

  uvm_analysis_imp_exp #(axi_txn, axi_scoreboard) exp_imp;
  uvm_analysis_imp_act #(axi_txn, axi_scoreboard) act_imp;

  axi_txn exp_by_id [bit [7:0]];  // match AXI AWID/RID width
  int match_count, mismatch_count, unexpected_count;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    exp_imp = new("exp_imp", this);
    act_imp = new("act_imp", this);
  endfunction

  function void write_exp(axi_txn t);
    axi_txn snap = axi_txn::type_id::create("snap");
    snap.copy(t);
    if (exp_by_id.exists(snap.id))
      `uvm_warning("SCB", $sformatf("duplicate exp id %0h — overwrite?", snap.id))
    exp_by_id[snap.id] = snap;
    `uvm_info("SCB", $sformatf("stored exp id=%0h addr=%0h", snap.id, snap.addr), UVM_HIGH)
  endfunction

  function void write_act(axi_txn act);
    axi_txn snap = axi_txn::type_id::create("snap");
    snap.copy(act);

    if (!exp_by_id.exists(snap.id)) begin
      unexpected_count++;
      `uvm_error("SCB_UNEXPECTED",
        $sformatf("id %0h no expected entry:\n%s", snap.id, snap.sprint()))
      return;
    end

    if (snap.compare(exp_by_id[snap.id])) begin
      match_count++;
      `uvm_info("SCB", $sformatf("MATCH id=%0h", snap.id), UVM_HIGH)
    end else begin
      mismatch_count++;
      `uvm_error("SCB_MISMATCH", $sformatf("id %0h:\nEXP:\n%s\nACT:\n%s",
        snap.id, exp_by_id[snap.id].sprint(), snap.sprint()))
    end
    exp_by_id.delete(snap.id);
  endfunction

  function void check_phase(uvm_phase phase);
    super.check_phase(phase);
    if (exp_by_id.num() != 0)
      `uvm_error("SCB", $sformatf("%0d IDs never got response", exp_by_id.num()))
  endfunction
endclass
  • Key width must match protocol ID field — AXI AWID/RID typically 4–8 bits.

  • Duplicate ID warning on write_exp catches ref model double-predict bugs.

  • delete() after compare — entry consumed; lingering = check_phase error.

  • exists() check before lookup — missing ID is unexpected actual.


AXI out-of-order walkthrough

Three AXI writes with different IDs, responses arrive reversed. Step through the associative array at each event.

diagram
[CHECK] AXI 3-write OOO walkthrough

  exp_by_id = {}

  --- Predict all three (order arbitrary) ---
  write_exp(id=2, addr=0x1000)  exp_by_id = {2: exp2}
  write_exp(id=5, addr=0x2000)  exp_by_id = {2: exp2, 5: exp5}
  write_exp(id=1, addr=0x3000)  exp_by_id = {2: exp2, 5: exp5, 1: exp1}

  --- Act id=5 arrives first (DUT reordered) ---
  write_act(id=5, addr=0x2000)
    lookup exp_by_id[5]  exp5  MATCH  delete 5
  exp_by_id = {2: exp2, 1: exp1}

  --- Act id=1 arrives second ---
  write_act(id=1, addr=0x3000)  MATCH  delete 1
  exp_by_id = {2: exp2}

  --- Act id=2 arrives last ---
  write_act(id=2, addr=0x1000)  MATCH  delete 2
  exp_by_id = {}   check_phase clean
diagram
[UVM] AXI ID flow — separate write and read IDs

  Write path:  AW channel id=AWID    B response id=BID (must match AWID)
  Read path:   AR channel id=ARID    R beats id=RID (must match ARID)

  Scoreboard options:
    A) One exp_by_id map if AWID/ARID share ID space (typical)
    B) Separate exp_wr[id] and exp_rd[id] if ID spaces overlap
    C) Composite key {channel, id} for unified map

Protocol matching guide

diagram
Protocol              Outstanding   Match strategy        ID field
  ─────────────────────────────────────────────────────────────────────
  APB                   1 typical     FIFO queue              N/A (no ID)
  SPI / I2C             1             FIFO queue              N/A
  AXI3/AXI4             many          ID associative array    AWID/BID/ARID/RID
  PCIe                  many          ID associative array    Tag/Requester ID
  OCP                   many          ID associative array    Thread/ConnID
  Simple FIFO bus       1             FIFO queue              N/A
  Multi-port SoC        varies        per-port SCB or         port+id composite
                                        unified ID space

AXI ID width and collision avoidance

Size the associative array key to the protocol's ID width. If you declare bit [3:0] but the bus carries 8-bit IDs, upper bits truncate and different transactions collide on the same key — wrong pairing, silent data corruption in check results.

systemverilog
// Match DUT ID width — check your AXI interface parameters
axi_txn exp_by_id [bit [AXI_ID_WIDTH-1:0]];

// Composite key when multiple ports share one scoreboard
typedef bit [15:0] port_id_key_t;  // {port[7:0], id[7:0]}
axi_txn exp_by_port_id [port_id_key_t];
  • AXI_ID_WIDTH from interface or package parameter — single source of truth.

  • Duplicate ID from ref model = upstream bug — warn, don't silently overwrite.

  • Per-port scoreboards simplify wiring but multiply component count.

Key takeaways

  • Key expected by transaction ID for out-of-order responses.

  • write_exp inserts clone; write_act lookups, compares, deletes.

  • FIFO on reordering bus produces false mismatches — switch to ID map.

  • Size key to full protocol ID width; use composite key for multi-port.

Common pitfalls

  • FIFO on AXI/PCIe — endless false mismatches from reordering.

  • ID key truncation — different transactions collide on same key.

  • Forgetting delete() after compare — duplicate compare on re-arrival.

  • AWID vs BID mismatch in monitor — ID not propagated to response txn.