UVM Registers, Scoreboards and Backdoor Accesses
Today I encountered some an interesting behavior related to updating a UVM scoreboard to use backdoor register accesses. The original implementation timed behavior changes in the RTL with cycles on the bus as seen by updates to the register database model. For instance, the write function would sample the current register state of the enable bit. If there was a change, the scoreboard could predict the change in output accordingly; register changes always created a two clock delay in behavior change. This prediction model worked well until a recent change that made the delay variable. No problem, switch to backdoor accesses that require zero time and instantly reflect the value used by the DUT.
Wrong. Well, at least based on having implemented all of the scoreboard checking using functions. The get_mirrored_value method is a function but both methods (peek and read) for reading the backdoor are tasks! Ok, so just make everything a task in the scoreboard outside the write function.
Done. But wait a minute... why does the clock change in the middle of my scoreboard routine if a register read cycle hits the bus? I have no idea...
Turns out the UVM library makes all accesses atomic through their API and thus can serialize the scoreboard peek behind a frontdoor read. Luckily, the design I'm verifying updates the backend register logic in a single atomic access, so I ripped up the UVM library's DPI access mechanism and implemented a non-semaphore version within the scoreboard class. No more random timing issues and up-to-date samples for the scoreboard.
UVM Backdoor DPI Read Code
function uvm_status_e uvm_reg::backdoor_read_func(uvm_reg_item rw); uvm_hdl_path_concat paths[$]; uvm_reg_data_t val; bit ok=1; get_full_hdl_path(paths,rw.bd_kind); foreach (paths[i]) begin uvm_hdl_path_concat hdl_concat = paths[i]; val = 0; foreach (hdl_concat.slices[j]) begin `uvm_info("RegMem", {"backdoor_read from %s ", hdl_concat.slices[j].path},UVM_DEBUG) if (hdl_concat.slices[j].offset < 0) begin ok &= uvm_hdl_read(hdl_concat.slices[j].path,val); continue; end begin uvm_reg_data_t slice; int k = hdl_concat.slices[j].offset; ok &= uvm_hdl_read(hdl_concat.slices[j].path, slice); repeat (hdl_concat.slices[j].size) begin val[k++] = slice[0]; slice >>= 1; end end end val &= (1 << m_n_bits)-1; if (i == 0) rw.value[0] = val; if (val != rw.value[0]) begin `uvm_error("RegModel", $sformatf("Backdoor read of register %s with multiple HDL copies: values are not the same: %0h at path '%s', and %0h at path '%s'. Returning first value.", get_full_name(), rw.value[0], uvm_hdl_concat2string(paths[0]), val, uvm_hdl_concat2string(paths[i]))); return UVM_NOT_OK; end `uvm_info("RegMem", $sformatf("returned backdoor value 0x%0x",rw.value[0]),UVM_DEBUG); end rw.status = (ok) ? UVM_IS_OK : UVM_NOT_OK; return rw.status; endfunction
















