Individual field access causes extra reads/writes?

Hi. Just tried to use individual access in UVM and encountered something weird.
Please tell me if you have ever encountered something like this or if it’s just my own mistake.

So, I’m defining some register with multiple fields…

class axi_signals_reg extends uvm_reg;                 
    `uvm_object_utils(axi_signals_reg)                                                      
    function new (string name = "axi_signals_reg");  
        super.new(name, 32, UVM_NO_COVERAGE);   
    endfunction : new
    rand uvm_reg_field  field0, field1, field2;
    virtual function void build();
        field2 = uvm_reg_field::type_id::create("field2");
        field1 = uvm_reg_field::type_id::create("field1");
        field0 = uvm_reg_field::type_id::create("field0");
        //                           access    has_reset   
        //                     lsb_pos |      reset|indv_acc 
        //                   size  |   |volatile|  |rand |   
        //                     |   |   |     |  |  |  |  |   
        field2.configure(this, 8, 24,  "RW", 0, 0, 1, 1, 1);
        field1.configure(this,16,  8,  "RW", 0, 0, 1, 1, 1);
        field0.configure(this, 8,  0,  "RW", 0, 0, 1, 1, 1);
    endfunction: build
endclass

Then I added it to the reg block (and then connect it in some way). There’s a reg adapter that supports byte enable (no warnings about it).
Regmap is set to n_butes=1 for the example.

reg_map = create_map(
            .name               ( "reg_map"          ),
            .base_addr          ( 0         ),
            .n_bytes            ( 1                 ),
            .endian             ( UVM_LITTLE_ENDIAN ),
            .byte_addressing    ( 1   ) 
        );

axi_signals = axi_signals_reg::type_id::create("axi_signals");
axi_signals.configure(this);
axi_signals.build();
reg_map.add_reg(axi_signals, base_addr + 4*15, "RW");

In the test, I’m trying to read the fields separately. field.

uvm_status_e st;
uvm_reg_data_t dt;
REGS.axi_signals.field0.read(st, dt);
REGS.axi_signals.field1.read(st, dt);
REGS.axi_signals.field2.read(st, dt);

Then, something happens.
field0 reads as the following.

UVM_INFO <...>/UVM/CDNS-1.2/sv/src/reg/uvm_reg_map.svh(2037) @ 54 ns: reporter [uvm_reg_map] Reading address 'h3c via map "reg_block.reg_map"...
UVM_INFO <...>/UVM/CDNS-1.2/sv/src/reg/uvm_reg_map.svh(2037) @ 54 ns: reporter [uvm_reg_map] Reading address 'h3d via map "reg_block.reg_map"...

field1 also, gets an extra read

<...> Reading address 'h3d via map "reg_block.reg_map"...
<...> Reading address 'h3e via map "reg_block.reg_map"...
<...> Reading address 'h3f via map "reg_block.reg_map"...

But field2 is okay, because it’s the last?

<...> Reading address 'h3f via map "reg_block.reg_map"...

Originally I noticed it with 64-bit register split into 2 32-bit fields - the first field got 5 reads, while the second got 4 (which is expected). And switching to num_bytes=4 caused the lower field to read the entire register, which wasn’t expected.

I checked - reg adapter gets these extra reads. “Correct” reads are received with correct num_bits (8 or 32, depending on regmap num_bytes), but the “extra” read gets num_bits = 0 (with byte_en still high, by the way). But I can’t just ignore it since reg2bus should produce something…

The bus doesn’t matter since “reading address” are printed from regmap - so that means that it WANTS to read extra, for some reason.

What could be wrong here? How to make a workaround, considering that I can’t edit UVM code?

I have tried to reproduce your issue but I am not seeing what you are describing.

When you read a field, you should see a read to the base register address (‘h3c in this case), and UVM will internally map the fields.

One issue that I see is that you have n_bytes set to 1. This value should represent the number of bytes on your bus. For AXI, I would expect probably 4 for a 32-bit bus, which seems to match your register width.

But shouldn’t n_bytes only affect bus operations? It is possible to read 32-bit reg through 8-bit apb bus, for example.

Any chance you could share your working example? Maybe there’s something else I’ve done wrog in map or field configuration.

I’m not sure if it was ever tested to do 32bit register accesses through an 8 bit bus, but I think it should work. The UVM register write and read functions calculate the number of accesses required by doing some internal calculations based on register and bus widths, but I didn’t really verify all parameters.

I’ll put together an example that has different variations.

Thanks. Somehow, when I tried (just in case) reducing the size of the field from 8 to 7, it worked OK - even without any extra writes.

Maybe it’s a bug/feature of Cadence UVM implementation that we’re using. Not sure.