UVM RAL Callbacks for modifying number of bits of data transfer (32b AHB)

I am working on UVM register model, where I have registers with various size from 1 to 512 bits. Interface is AHB 32 bits and with HSEL signal I access to 8bits, 16bits and 32 bits. How can I impact using callbacks on the number of accesses without modifying driver? For example, when I have 53 bits register or when I have 24 bits register, I would like to have 1 access of 16 bits and 1 access of 8 bits, instead of having 32bits access for 24bits register.
Another example are registers modeled like this:

 *0x03 | 0x02 |  0x01 |  0x00* |
 ------------- ------- -------
 | reg2_1     | reg1  |  reg0 |
 ------------- ------- -------
 *0x07 | 0x06 |  0x05 |  0x04 |*
 ------------- ------- -------
 |       reg3       |  reg2_2 |
 ------------- ------- -------

size(reg0) = 8 bits on addr: 0x00
size(reg1) = 1 bits on addr: 0x01
size(reg2) = 24 bits on addr: 0x02, 0x03, 0x04
size(reg3) = 24 bits on addr: 0x05, 0x06, 0x07

I receive uvm_error when I try to write to reg2.

Is there a way using for example pre_write() callback to modify n_bits even before reaching the adapter, and to cut data item n_bits in smaller desired pieces?

Thank you in advance!

Kristina

In reply to dolovk:
I do not understand your intention of modelling registers.
reg2 is one 24 bit register distributed over 3 addresses or should it be 3 registers with a width of 8. TThe same would be valid reg3.
And reg2 has only 1 bit.
The RAL approach is you have a certain size of registers. In my opinion it woould be 8 bit.
Or do I misunderstand your idea?

In reply to chr_sue:

Hi, thank you for your replay.

Yes, reg2 is one 24 bit register distributed over 3 addresses. The same reg3.
reg2 contains only one bit, and it is presented as 8 bit distributed over one address.

(pseudo code)

class reg0 extends uvm_reg;
`uvm_object_utils(reg0)

rand uvm_reg_field f0;

function new(string name = “reg0”);
super.new(name, 8, UVM_NO_COVERAGE);
endfunction

virtual function void build();
f0 = uvm_reg_field::type_id::create(“f0”);
f0.configure(this, 8, 0, “RW”, 0, 0, 1, 1, 0);
endfunction
endclass : reg0

class reg1 extends uvm_reg;
`uvm_object_utils(reg1)

rand uvm_reg_field f0;

function new(string name = “reg1”);
super.new(name, 8, UVM_NO_COVERAGE);
endfunction

virtual function void build();
f0 = uvm_reg_field::type_id::create(“f0”);
f0.configure(this, 1, 0, “RW”, 0, 0, 1, 1, 0);
endfunction
endclass : reg1

class reg2 extends uvm_reg;
`uvm_object_utils(reg2)

rand uvm_reg_field f0;

function new(string name = “reg2”);
super.new(name, 24, UVM_NO_COVERAGE);
endfunction

virtual function void build();
f0 = uvm_reg_field::type_id::create(“f0”);
f0.configure(this, 24, 0, “RW”, 0, 0, 1, 1, 0);
endfunction
endclass : reg2

class reg3 extends uvm_reg;
`uvm_object_utils(reg3)

rand uvm_reg_field f0;

function new(string name = “reg3”);
super.new(name, 24, UVM_NO_COVERAGE);
endfunction

virtual function void build();
f0 = uvm_reg_field::type_id::create(“f0”);
f0.configure(this, 24, 0, “RW”, 0, 0, 1, 1, 0);
endfunction
endclass : reg3

bus_map = create_map(“bus_map”, 'h00, 4, UVM_LITTLE_ENDIAN);

bus_map.add_reg(reg0_inst, 'h00, “RW”);
bus_map.add_reg(reg1_inst, 'h01, “RW”);
bus_map.add_reg(reg2_inst, 'h02, “RW”);
bus_map.add_reg(reg3_inst, 'h05, “RW”);


I am trying to implement RAL for the existing registers with various widths. Some of them are 53 bit or 304 bit, hundreds of them, all diverse. I use one bus_map with n_bytes 4 which is aligned with ahb interface of 32bits.

When I have, for example, one 24 bit register, instead of 32 bit access that RAL does automatically, my idea is to implement pre-write() callback in order to modify RAL default behavior.
For one 24 bit register, in pre-write() I would like to overwrite information, using n_bits (of uvm_reg_item) and to tell to adapter: if n_bits = 24 => write 16bits on addresses 0x02 and 0x03, and 8bits on address 0x04.

Thank you!

In reply to dolovk:

It is completely unclear to me how 1 register has 3 different addresses. Do you mean indirect adressing?

In reply to chr_sue:

Register environment is non-standard. The size of registers often is not multiple of 8. The addresses are based on byte level. One address presents 1 byte. Therefore, when I have 24bits register, it is distributed on 3 addresses (locations)

In reply to dolovk:

I believe you have to adopt your interpretation of your registers to the UVM RL approach and this is based on a fixed register width.
I’m always assuming a storage place which has a specific address as 1 register, resulting in 3 registers with a width of 8 bits. You can access only 1 regsiter at the same time. If you need the data from the 3 registers in a series you can do this by accessing your registers in the defined order.

In reply to chr_sue:

Yes, I agree with that. It should be like that. But this is very particular case in which I am facing the problem where I have thousands of registers already, 9 testbenches, various interfaces and the register environment is particularly made, non-standard. Various registers width and non-aligned addresses. I need to develop UVM RAL for all these registers, to translate the old way of registers (API…) into the standard ones, according to the UVM.
Designer created on this way several years ago in order to reduce space and to have one access at the time for several registers, among other advantages and needs.
I would like to understand can this project be realized at all. If so, the idea that I have now is to make “user-defined write()” using callbacks, pre_write(), post_write(), in order to manipulate with number of bits, accesses and addresses. I must override the automatic RAL behavior, adjusting workaround.
Is it possible to have information about the register’s width and address in callback pre_write() at the moment when we call write() for the certain register?
If I would know the reg width and the parity of that address, I could calculate and set the exact number of access and the right address (for that uvm_reg_item rw).
It is similar with the sequences when we have start_item and item_done, calling write() is the same. Now I want with calling only once write() to actually have several times “start_item - item_done” by incrementing the addresses.

In reply to dolovk:

The UVM approach in general is you cannot modify your environment on the fly, i.e. in the run_phase. Each test can configure the environment for certain test requirements. But this happens prior to starting the simulation. Even it woulkd be possible to modify your environment on the fly you ahd a standalone, non-reusable environment. It would work for your specific application but you could not reuse it.
I believe it is worth to clean-up your register models, at least for your toplevel testbench. I know this will cost some effort, but it results in a clean and reusable environment.

In reply to chr_sue:

Hi, thank you for your answers. I temporarily solved the problem, but the solution is not with callbacks and not very elegant. So I still research.
But at the moment, I modified adapter. Based on the n_bits in reg2bus() I modified access that will be given to driver (byte, half-word, word). And modifying driver to increment address based on that access type.
I still think it must be a way to somehow override write() method.
If the bus_map is created of n_bytes 4, (AHB 32 bit data path), there must be a way for the cases when registers width is not multiple of 8 and start on odd physical addresses, to have the correct operations.
In regards of memories, in the same bus_map (n_bytes = 4), if the memory is 8192 locations of 8 bits and starts on odd physical address, I will have again the same problem.