Accessing Multiple Registers at Once

As I already mentioned, I gave a talk at DVCon Europe this year on how to implement burst accesses to memories modeled using UVM_REG. The motivation for that was that the register package can already handle bus protocols that don't support burst operation, but it requires more user guidance for protocols that do support it. A question that came up afterwards on the conference floor was what the best way to handle burst accesses to multiple registers might be. I tried to sketch out an answer on a piece of paper, but it was rather late in the day and I couldn't really gather my thoughts. I also have a difficult time expressing myself verbally when talking about abstract concepts. Talking is much more difficult than writing, because you don't get the chance to go back and iterate over certain aspects of the topic. Talking about coding problems is also particularly difficult to do when not in front of a computer. People who've worked with me know that I always like to have an editor window open and sketch out some pseudo-code when discussing something in more detail.

This is a companion discussion topic for the original entry at

Hello Tudor,

Do you have any working example for this approach?

Best regards,

The example on Sourceforge/Github is the only thing I have at the moment. I’ve never had to implement this productively.

Thanks Tudor, I am looking at that example.

As I understand from your env. that you do not use an adapter if you use translation.

1- How the register model gets updated when you write to the register of the DUT? In the case when the adapter used, it is done by the predictor.
2- Is it still possible to use extension with this translation case if I want to pass different items than the predefined reg_items?

Thanks for helping.

  1. The examples only focus on how to generate stimulus, so there’s no code in there for handling model updates. Conceptually, prediction is done on a bus cycle level. This means that whenever you see a transfer, you want to predict what its effect was. An adapter as described in the UVM user guide is enough for this. How you handle prediction is totally independent from how you handle stimulus generation.

  2. You can always get the extension argument if you really need it.

Hi Timi,

How to handle the registers which needs to be access using I2C slave - which is a DUT.
Ie. To read the other blocks registers of the SOC - the I2C VIP writes the address into the I2C slave dut config registers and then the DUT will access the internal registers and gets the data and will places the data into data register. This data will be sent back to I2C VIP when it tries to read the data.

Since the reg adaptor has reg2bus and bus2reg which are functions - we can not implement them in those functions.

Vijay Gampa

This is something a lot of people have questions about. It’s going to be the topic of a future blog post.

Any tip on how to write the bus2reg function in the extension option?
shadow mem works OK but it is a series of single operations and not a true burst.

A shadow memory allows you to start bursts via the ‘burst_write/read(…)’ tasks. This will trigger one translation ‘step’ for each complete burst that you want to start. In that translation step you can decide whether you want to start multiple sequence items (for a protocol that doesn’t support bursts, like APB) or a single one (for a protocol that does support bursts, like AHB).

Hi Tudor,
How can we extract the “extension” from translation sequence? Inside the adapter’s reg2bus function, we can use get_item to return a uvm_reg_item and cast it. In the translation case, how do we do it?

How to implement bus2reg? It may need to return array of read data?

Hi Timi, Did you discussed about this topic ? If so, can you point some link ?

bus2reg only updates a uvm_class uvm_reg_bus_op and object name is rw. It is a void function

I’m having a similar issue,
But in my case the “burst” is more complicated and I need to generate a custom made packet.
Why not just override the “update” task of uvm_reg_block? (Assumption is that I’ll use the overriden task all the times)

I try to avoid adding extensions via inheritance, as it’s not possible to implement multiple extensions this way (no multiple inheritance).

Hello ,
Thanks for the post .
could you please give me an example for back to back register access (both write and read )

I went with an orthogonal approach. The approach I went with is generic, doesn’t require an extension object or an extra memory. It also doesn’t use a custom sequence because that would bypass any frontdoor policy the user might have set up.

I want the usage model to be as painless and UVM-ish as possible.

The downside is that it does require more code and you can’t control exactly which registers are bursted - you have to work with the entire block.

My approach was to create a new intermediate base class for uvm_reg_block and then created two tasks: burst_update(), burst_mirror(). Analogous to block.update(), block.mirror().

The burst_update task figures out which registers need to be updated in the block and if they’re adjacent, then it combines them into one transaction. It then sends a series of burst writes. It uses the uvm_reg_item/reg.do_write() API because it can handle burst information.

The burst_mirror task just combines adjacent registers and sends a series of burst reads. It can also handle UVM_CHECK.

The adapters and predictors also have to be updated to understand the burst information in uvm_reg_item, so that’s another downside.

But this approach leaves all TB-specific needs un-trampled (such as extensions and frontdoor policies) and is TB agnostic.