Part 8 · Checking & Coverage · Intermediate

Reg Adapter: bus2reg and reg2bus

Full apb_reg_adapter implementation bridging bus items and uvm_reg_bus_op.

Why the adapter exists

RAL speaks uvm_reg_bus_op — a protocol-neutral struct with kind, addr, data, byte_en, and status. Your agent speaks apb_item (or AXI item, etc.). The uvm_reg_adapter translates between them in both directions.

diagram
Legend: [RAL] [UVM]

  STIMULUS direction (reg2bus):
    RAL frontdoor seq ──► uvm_reg_bus_op ──► reg2bus() ──► apb_item ──► driver

  OBSERVE direction (bus2reg):
    monitor ──► apb_item ──► bus2reg() ──► uvm_reg_bus_op ──► predictor ──► mirror

Full adapter implementation

systemverilog
class apb_reg_adapter extends uvm_reg_adapter;
  `uvm_object_utils(apb_reg_adapter)

  // Absolute base address of this reg block in the system map.
  // Subtract from monitor's absolute addr to get RAL offset.
  uvm_reg_addr_t base_addr = '0;

  function new(string name = "apb_reg_adapter");
    super.new(name);
    supports_byte_enable = 0;  // APB has no byte enables
  endfunction

  // bus2reg: monitor item → RAL bus op (predictor path)
  function uvm_reg_bus_op bus2reg(uvm_sequence_item bus_item);
    apb_item apb = apb_item'(bus_item);
    uvm_reg_bus_op rw;

    rw.kind    = apb.write ? UVM_WRITE : UVM_READ;
    rw.addr    = apb.addr - base_addr;   // map offset, not absolute
    rw.data    = apb.data;
    rw.byte_en = '1;                     // all bytes valid
    rw.status  = apb.slverr ? UVM_NOT_OK : UVM_IS_OK;
    return rw;
  endfunction

  // reg2bus: RAL bus op → sequence item (frontdoor stimulus path)
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    apb_item apb = apb_item::type_id::create("apb");
    apb.write = (rw.kind == UVM_WRITE);
    apb.addr  = rw.addr + base_addr;     // restore absolute address
    apb.data  = rw.data;
    return apb;
  endfunction
endclass

Field-by-field walkthrough

bus2reg — monitor to predictor

  1. Cast bus_item to apb_item — $cast or type cast; wrong type = silent wrong prediction.

  2. rw.kind — map apb.write bit to UVM_WRITE or UVM_READ; predictor uses this to update mirror correctly.

  3. rw.addr — subtract base_addr so RAL map offset matches; #1 cause of wrong-register updates.

  4. rw.data — PWDATA for writes; PRDATA for reads (monitor must capture both).

  5. rw.byte_en — '1 for full-word APB; set per-byte for byte-enabled buses.

  6. rw.status — UVM_IS_OK or UVM_NOT_OK; predictor skips mirror update on NOT_OK.

reg2bus — RAL sequence to driver

  1. Create fresh apb_item — one item per RAL frontdoor operation.

  2. apb.write — derived from rw.kind; drives PWRITE signal.

  3. apb.addr — add base_addr back for absolute system addressing.

  4. apb.data — write data from RAL field value; ignored on reads.

diagram
ADAPTER DEBUG — print bus2reg for known transaction

  Input apb_item:  { write=1, addr=0x4000_0100, data=0xDEAD_BEEF }
  base_addr:       0x4000_0000

  bus2reg output:  { kind=UVM_WRITE, addr=0x100, data=0xDEAD_BEEF,
                     byte_en=0xF, status=UVM_IS_OK }

  RAL map lookup:  offset 0x100  ctrl_reg field  mirror = 0xDEAD_BEEF

Address map alignment

bus2reg addr must match RAL map offsets exactly. If the monitor reports absolute addresses (e.g. 0x4000_0100) but the RAL map starts at offset 0x0000_0100, set base_addr = 32'h4000_0000 in the adapter constructor or via config_db.

Key takeaways

  • Same adapter instance serves reg2bus (stimulus) and bus2reg (predictor).

  • base_addr is the most common fix for predictor updating the wrong register.

  • Always verify bus2reg output against a known APB transaction before env debug.

Common pitfalls

  • Wrong addr offset — predictor updates wrong register field silently.

  • Monitor captures address before phase completes — partial transaction prediction.

  • Separate adapters for stimulus and observe with mismatched base_addr.