Jump to content

What is the proper way to trigger something when a uvm_reg field is written?

Recommended Posts

I am building a model of my DUT, and the model will include a reference to the UVM register models for the DUT. The model will need to generate predicted transactions when certain register fields in the DUT are written to, with certain values. For example, when a GO bit is set to 1, the DUT and model should both produce a transaction.

What is the proper/best way to do this with uvm_reg?

So far I have experimented with creating a callback (extending from uvm_reg_cbs), registering it with the register field I want to trigger on, and then checking for "kind == UVM_PREDICT_WRITE" in post_predict.

But I'd prefer not do have to use a bunch of callbacks if I can avoid them. I'd rather have the register class contain some event that fired on reads/writes, but it doesn't seem to exist in the BCL. That way my model could just wait on the event and then query the current register value. I realize I can get that behavior with a callback... just want to see if there is some other facility that I'm missing.



Link to comment
Share on other sites

First, let me apologize -- markups does not seem to work anymore.

Callbacks in the register model are really meant to extend the functionality of register if doesn't match one of the pre-defined registers already defined in UVM_REG. Also you can extend one of the pre-defined registers with unique behavior.

If you are performing certain functions, say waiting for an interrupt -- you should use wait_for_change. Unfortunately, it needs backdoor access and isn't implemented in the model -- so you need to provide an implementation (and there really aren't UVM_REG specific guidelines for this). This involves DPI code if you want to wait based on a string name. Your simulator vendor may have something already. This is how it is defined:


virtual function bit is_auto_updated(	uvm_reg_field 	field	)
Indicates if wait_for_change() method is implemented
Implement to return TRUE if and only if wait_for_change() is implemented to watch for changes in the HDL implementation of the specified field
virtual local task wait_for_change(	uvm_object 	element	)
Wait for a change in the value of the register or memory element in the DUT.
When this method returns, the mirror value for the register corresponding to this instance of the backdoor class will be updated via a backdoor read operation.

Section 5.6.6 Active Monitoring of the UVM User's Guide

explains how to use this.

If you don't mind having hard paths like the one in the example (this is a partial example):

virtual task wait_for_change();

@($root.tb_top.dut.rf.f1 or $root.tb_top.dut.rf.f2);


Then you can implement it quite easily without the DPI. I've done this and it works. Hard paths don't work in packages. This is one reason you need the DPI. Also you can't wait_for_change on a string name. I found some free DPI code by goggling:

wait_for_change + UVM_REG

1. There are white papers that explain it.

2. There are some free tools that give you the SV and C code you need.

3. Ask you vendor if they already have this code. I'd still look at some of the white papers.

Hope that helps:


Link to comment
Share on other sites

Thanks for the feedback. While I have the backdoor access set up for peeking/poking, in this case I really want to trigger of the bus transaction which represents a write to a specific register bit. But rather than monitor the bus transaction and decode the address again, I just want to hook into the register model.

I have it working with callbacks and will likely just stick with that. It works fine... I just wanted to see what other options there were.



Link to comment
Share on other sites

Hi Doug,

I have done this sort of thing before with callbacks & the uvm_event_pool to synchronise parts of my env to register access:

class trigger_reg_field_cbs extends uvm_reg_cbs;
  function new(string name = "trigger_reg_field_cbs");
  endfunction: new
  virtual function void post_predict(input uvm_reg_field  fld,
                                     input uvm_reg_data_t previous,
                                     inout uvm_reg_data_t value,
                                     input uvm_predict_e  kind,
                                     input uvm_path_e     path,
                                     input uvm_reg_map    map);
    uvm_event access_event;
    `uvm_info("REG_CBS", $psprintf("post_predict() called for field %s, current value: %04x, previous value: %04x, kind: %s, path: %s",
             fld.get_name(), value, previous, kind.name(), path.name()), UVM_HIGH); 
    if (kind == UVM_PREDICT_DIRECT) begin
      access_event = uvm_event_pool::get_global($psprintf("%s_PREDICT", fld.get_name()));
    if (kind == UVM_PREDICT_READ) begin
      access_event = uvm_event_pool::get_global($psprintf("%s_READ", fld.get_name()));
    if (kind == UVM_PREDICT_WRITE) begin
      access_event = uvm_event_pool::get_global($psprintf("%s_WRITE", fld.get_name()));

endclass: trigger_reg_field_cbs

I did a presentation with some callback recipes at a UK conference recently - you might find it interesting:




Link to comment
Share on other sites

  • 1 year later...


Thank you so much for your post!

I am new to UVM and I struggled to implement the register bank hooked up to the reference model. Your post was really helpful.


As a novice I had to dig up some more information which you probably consider trivial. I’ll outline some of my findings and my implementation in the hope that it will help others reach their goal more quickly.


Implementing register callbacks include four steps:

1. Extending the register callback class – See Steve’s post for a trigger callback.

2. Instantiating the class

3. Generate the instance

4. Register the instance with the register field(s) intended to invoke the callback


I instantiated the class in the register block:

  trigger_reg_field_cbs    trigger_reg_field_cb;


I generated the instance in the register block new function:

  trigger_reg_field_cb = new(‘trigger_reg_field_cb”);


The callback instance need to be registered with the register field instance.

This was implemented in the build_phase:

  uvm_callbacks#(uvm_reg_field, uvm_reg_cbs)::add(<register instance>.<field instance>, 




The trigger callback by Steve is coded in a generic way and can be used as a call back for many 


fields. I.e. it can be registered for multiple fields.


To attach the the callback to all the fields in a register I implemented a macro, though I think that the following should work as well:

  trigger_reg_field_cb.add(<register instance>);


Here is my macro:


`define add_cb_to_reg(REG, CB) \

  begin \

    uvm_reg_field_fields[$]; \

    REG.get_fields(fields); \

    foreach (fields) begin \

      uvm_callbacks#(uvm_reg_field, uvm_reg_cbs)::(fields, CB); \

    end \



And I used the macro in the reg block build phase:

`add_cb_to_reg(<register instance>, trigger_reg_field_cb)


The other piece was to implement the 'listening side'.  On the listening side I wanted to respond to multiple configuration register chagnes. Any of these chages would trigger a reconfiguration of the reference model based on the configuration registers.


I implemented an array of uvm events:

  uvm_event cfg_e[];


In the build_phase I initialized the array, assigning event names as expected by the trigger callback function.


cfg_e = new[N];


In some cases I spelled out the event name, in other cases I used a for-loop to construct the event names. E.g.

  cfg_e[Z] = uvm_event_pool::get_global($psprintf("Bla_Bla_%1d_Bla_Bla",Z));


In the run_phase I started a child task:






And in the check_config task I implemented a System Verilog (SV) event:

  event m_cfg_e_detected;


The body of the check_config task implemented a forever loop starting with the following code waiting on any of the N events to trigger:


foreach (cfg_e) begin


    automatic int k = i;  // Create a local variable k in the child process


      cfg_e[k].wait_trigger(); // Wait for event trigger



   join_none  // Spawn all child processes waiting for triggers


@m_cfg_e_detected; // Wait for any of the child process to trigger

disable fork;      // Kill all other forked trigger checking child processes


After the reconfiguring the model, the forever loop rolled back to the begining waiting for another trigger.




One more note about the post_predict callback.


The trigger callback from Steve was designed to process any of the three POST_PREDICT types: UVM_PREDICT_DIRECT, UVM_PREDICT_WRITE and UVM_PREDICT_READ. The UVM register code doesn't call the post_predict callback function if the predict type is UVM_PREDICT_DIRECT, which is the default. One should make sure the predict function calls use the UVM_PREDICT_WRITE or UVM_PREDICT_READ. You can find the code in uvm_reg.svh predict() function which will lead you to the uvm_reg_field.svh.


Hope someone will find this post helpful :-)


Best Regards,

Link to comment
Share on other sites

  • 11 months later...

I realize that this is an old thread, but the topic is new to me, and probably others.

Steve's code above was extremely useful for getting register triggers working.

Eldad, I think that you are making the implementation a bit more complicated than it really needs to be.


I chose to put the trigger code into my test_base instead of the register model itself.


Assume that you have the following declarations in your test base:

   my_reg_block           my_rm;

  trigger_reg_field_cbs cb; 
  uvm_reg_field            all_fields[$];
Instantiate Steve's code (from previous post) in the build phase:
  cb = trigger_reg_field_cbs::type_id::create("cb"); // callback
And then you can add callbacks to all of the register fields during the run phase with a foreach loop:
  foreach (all_fields) begin
For the "listener" side, you can use the same technique to simplify the process.
Assume that you have the following declarations in your scoreboard:
  my_reg_block  my_rm;
  uvm_event     ev_reg_write[string];
  uvm_reg_field all_fields[$];
You can create event triggers during the run_phase with a foreach loop:
    foreach (all_fields) begin
      ev_reg_write[all_fields.get_name()] = uvm_event_pool::get_global($psprintf("%s_WRITE",all_fields.get_name()));
I used an associative array for the events in order to make the trigger checking code easier to read.
I create a forked process for each register that I am interested in watching:
      forever begin
        reset_var = my_rm.MyCtrl_reg.MyReset.get_mirrored_value();
        // < do some stuff >
      forever begin
        mode_var = my_rm.MyMode_reg.MyMode.get_mirrored_value();

There is one important caveat: using field names for triggers and arrays assumes that your field names are unique (or at least the ones you care about).

If you have duplicate field names, then the event items and the array may need some renaming.

Keep in mind that <field>.get_full_name() returns a string of the form <reg_block_name>.<reg_name>.<field_name>




Link to comment
Share on other sites

  • 2 years later...

Here my two cents,

In my opinion, it would be good that UVM would do the backdoor access not as it currently does. It would be good that once provided the hdl_path to a field, the UVM library somehow would be able to generate an unique interface for that hdl_path and a bind that correspond to that interface. That generated interface should be published to the config data base and would be available in any part of your design through the ConfigDB. Of  course, the UVM backdoor functions would still be able to do the checks they do now, for example, check that the register is not being accessed by the frontdoor/busy. The difference is that they will update the values over an interface that is published with a logic name in the configdatabase.

Having the fields as interfaces in the config data base have the advantage that the user can download that interface handler in the scoreboard and performs things like waiting for an event/trigger on that signal. In other words, signal change detection which is very useful for interrupts.

I understand that UVM cannot create code/an interface for you. However, UVM can define a macro for this.

I think working with an interface exposer in the config data base is a lot easier and intuitive for interrupt handling on registers.


In relation to the topic. I just detect any write on registers using the TLM channel of the reg predictor (a monitor is connected to the predictor and the predictor makes the updates in the register model (explicit implementation)). That TLM channel is triggered on any register change. Then, i ask the name of the register and if it is a READ or a WRITE.


Best regards,



Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...