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.
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 ──► mirrorFull adapter implementation
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
endclassField-by-field walkthrough
bus2reg — monitor to predictor
Cast bus_item to apb_item — $cast or type cast; wrong type = silent wrong prediction.
rw.kind — map apb.write bit to UVM_WRITE or UVM_READ; predictor uses this to update mirror correctly.
rw.addr — subtract base_addr so RAL map offset matches; #1 cause of wrong-register updates.
rw.data — PWDATA for writes; PRDATA for reads (monitor must capture both).
rw.byte_en — '1 for full-word APB; set per-byte for byte-enabled buses.
rw.status — UVM_IS_OK or UVM_NOT_OK; predictor skips mirror update on NOT_OK.
reg2bus — RAL sequence to driver
Create fresh apb_item — one item per RAL frontdoor operation.
apb.write — derived from rw.kind; drives PWRITE signal.
apb.addr — add base_addr back for absolute system addressing.
apb.data — write data from RAL field value; ignored on reads.
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_BEEFAddress 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.