This is a deepdive into uvm_reg_bit_bash_seq
. It’s one of UVM RAL builtin sequences to test registers. uvm_reg_bit_bash_seq
can be used as by setting the model
and start the sequence and that’s it.
seq = uvm_reg_bit_bash_seq::type_id::create("seq",this);
seq.model = env.regmodel;
seq.start(null);
seq.wait_for_sequence_state(FINISHED);
Starting with body()
, it seems reset_blk
is empty in src/reg/sequences/uvm_reg_bit_bash_seq.svh
reg_seq = uvm_reg_single_bit_bash_seq::type_id::create("reg_single_bit_bash_seq");
this.reset_blk(model);
model.reset();
do_block(model);
The loop works on all registers and calls vm_reg_single_bit_bash_seq
on each register
protected virtual task do_block(uvm_reg_block blk);
uvm_reg regs[$];
...
...
// Iterate over all registers, checking accesses
blk.get_registers(regs, UVM_NO_HIER);
foreach (regs[i]) begin
// Registers with some attributes are not to be tested
if (uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
"NO_REG_TESTS", 0) != null ||
uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
"NO_REG_BIT_BASH_TEST", 0) != null )
continue;
reg_seq.rg = regs[i];
reg_seq.start(null,this);
end
And then call do_block
recursively
begin
uvm_reg_block blks[$];
blk.get_blocks(blks);
foreach (blks[i]) begin
do_block(blks[i]);
end
end
endtask: do_block
Ok, let’s look at vm_reg_single_bit_bash_seq
. Note there are few knobs to disable the checks in case the user wants to. NO_REG_TESTS
is set into the uvm_resource_db
for registers that we don’t checked.
Surprisingly, body()
is well documented with comments (thank you, whoever wrote it!). But to sump, It loops over that fields and bits in each field, Calculate mask based on which bits are writable and then goes these bits and change the value and check the new value.
virtual task body();
// Registers with some attributes are not to be tested
if (uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
"NO_REG_TESTS", 0) != null ||
uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
"NO_REG_BIT_BASH_TEST", 0) != null )
return;
n_bits = rg.get_n_bytes() * 8;
// Let's see what kind of bits we have...
rg.get_fields(fields);
// Registers may be accessible from multiple physical interfaces (maps)
rg.get_maps(maps);
// Bash the bits in the register via each map
foreach (maps[j]) begin
uvm_status_e status;
uvm_reg_data_t val, exp, v;
int next_lsb;
next_lsb = 0;
dc_mask = 0;
foreach (fields[k]) begin
int lsb, w, dc;
dc = (fields[k].get_compare() == UVM_NO_CHECK);
lsb = fields[k].get_lsb_pos();
w = fields[k].get_n_bits();
// Ignore Write-only fields because
// you are not supposed to read them
case (fields[k].get_access(maps[j]))
"WO", "WOC", "WOS", "WO1": dc = 1;
endcase
// Any unused bits on the right side of the LSB?
while (next_lsb < lsb) mode[next_lsb++] = "RO";
repeat (w) begin
mode[next_lsb] = fields[k].get_access(maps[j]);
dc_mask[next_lsb] = dc;
next_lsb++;
end
end
// Any unused bits on the left side of the MSB?
while (next_lsb < `UVM_REG_DATA_WIDTH)
mode[next_lsb++] = "RO";
`uvm_info("uvm_reg_bit_bash_seq", $sformatf("Verifying bits in register %s in map \"%s\"...",
rg.get_full_name(), maps[j].get_full_name()),UVM_LOW);
// Bash the kth bit
for (int k = 0; k < n_bits; k++) begin
// Cannot test unpredictable bit behavior
if (dc_mask[k]) continue;
bash_kth_bit(rg, k, mode[k], maps[j], dc_mask);
end
end
endtask: body
In bash_kth_bit
, bit is written then read back and finally, The comparison at the end of task reports is there is a mismatch
task bash_kth_bit(uvm_reg rg,
int k,
string mode,
uvm_reg_map map,
uvm_reg_data_t dc_mask);
uvm_status_e status;
uvm_reg_data_t val, exp, v;
bit bit_val;
`uvm_info("uvm_reg_bit_bash_seq", $sformatf("...Bashing %s bit #%0d", mode, k),UVM_HIGH);
repeat (2) begin
val = rg.get();
v = val;
exp = val;
val[k] = ~val[k];
bit_val = val[k];
rg.write(status, val, UVM_FRONTDOOR, map, this);
if (status != UVM_IS_OK) begin
`uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when writing to register \"%s\" through map \"%s\".",
status.name(), rg.get_full_name(), map.get_full_name()));
end
exp = rg.get() & ~dc_mask;
rg.read(status, val, UVM_FRONTDOOR, map, this);
if (status != UVM_IS_OK) begin
`uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when reading register \"%s\" through map \"%s\".",
status.name(), rg.get_full_name(), map.get_full_name()));
end
val &= ~dc_mask;
if (val !== exp) begin
`uvm_error("uvm_reg_bit_bash_seq", $sformatf("Writing a %b in bit #%0d of register \"%s\" with initial value 'h%h yielded 'h%h instead of 'h%h",
bit_val, k, rg.get_full_name(), v, val, exp));
end
end
endtask: bash_kth_bit