Why the call back post_predict is not call if you try to predict a register field with UVM_PREDICT_DIRECT?

Hello

I use a uvm register model, and some with user defined call back for certain register field.
I also update the value of some registers by calling predict() from my scoreboard.

When you call the uvm_reg method predict() with the argument UVM_PREDICT_DIRECT the call back post_predict() is not called.
I checked in the code of uvm_reg_field and I saw that the call back is called only if you use UVM_PREDICT_WRITE or UVM_PREDICT_READ.
Why can’t I use post_predict() with UVM_PREDICT_DIRECT ?

Pierre-Antoine D

The predict(…) function gets called with UVM_PREDICT_WRITE or UVM_PREDICT_READ from inside a reg predictor. These calls are intended to model any side effects of accessing the registers. The post_predict(…) callback allows you to inject behavior (e.g. writing some register clears some bit in some other register, clearing some bit disables access to some other fields, etc.). If you want to model register updates caused by other events (e.g. some operation completing sets a flag), you call predict(…) with UVM_PREDICT_DIRECT. In that case you know exactly what value you want the register to contain and it makes little sense to step on your own toes inside a post_predict(…) callback.

Note: I’m not a UVM developer. This was purely my opinion. Whether the VIP TSC really had this in mind is a different question.

In reply to Tudor Timi:

Here’s one example of where not having a “direct” predict call the post_predict(…) callback makes sense: https://verificationacademy.com/forums/uvm/ohh-ohhhh-one-physical-register-two-address-mapped-it-how-tackle-such-scenarios-inside-uvm-reg-model

I also recently ran into this situation where I was surprised that post_predict() callback is not called when kind is UVM_PREDICT_DIRECT. I took a quick look at the last link Tudor posted, and I agree you don’t want an endless loop, but my situation is not like that and it would be ideal for a post_predict() UVM_PREDICT_DIRECT callback (but it doesn’t exist!!)

I am modeling the behavior of interrupts. When I detect some hardware condition, in my predictor code, I can call some_int.predict(value) and this is OK. But I also need to predict the upper level interrupt which is a combination of a few different interrupt bits, their respective mask bits, and my strategy was to define this logical function once in a callback, and hook it up to each of the “input” fields that make up the upper level interrupt. The code is below:

If anyone has a suggestion or a better/more efficient way I should be implementing this, I would greatly appreciate it! I don’t want to resort to a brute-force “hardware engineer” approach of creating a predictor component which has to run and sample every clock to call get_mirrored_value() from the input interrupts and masks and then call predict on the upper level interrupt, or some other ugly solution. Having my code run in a post_predict() callback every time any of the inputs change, whether it is the mask bits being updated by a write access, or the lower interrupts being updated (and a direct predict being called), seemed to be an efficient solution to me.

class reg_update_upper_interrupt_cb extends uvm_reg_cbs;

   // references to related reg fields for this interrupt combine function
   uvm_reg_field someint;
   uvm_reg_field someintmask;
   uvm_reg_field otherint;
   uvm_reg_field otherintmask;

   uvm_reg_field upperint;

   function new(string name = "reg_update_upper_interrupt_cb");
      super.new(name);
   endfunction

   // Give the callback a pointer its associated reg block
   virtual function void configure(...my arguments...);

      // Assign references to the fields that are inputs to upper interrupt
      someint   = <some_reg_block path to field>...
      ...
      upperint  = <some_reg_block path to field>...
   endfunction

  `uvm_object_utils(reg_update_upper_interrupt_cb)

   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);
      bit int_status;  // combined interrupt request status value

      // Note: mask bits will typically predicted with UVM_PREDICT_WRITE,
      //       the int bits will be DIRECT (assigned by my testbench!)
      if (kind inside {UVM_PREDICT_WRITE, UVM_PREDICT_DIRECT}) begin
         int_status = someint.get_mirrored_value()      &&
                     !someintmask.get_mirrored_value()  ||
                      otherint.get_mirrored_value()     &&
                     !otherintmask.get_mirrored_value();
         // Update the value of the upper interrupt based on calculation            
         upperint.predict(int_status);            
      end   
   endfunction
endclass : reg_update_upper_interrupt_cb

... Registering the callbacks

   reg_update_upper_interrupt_cb upper_int_cb;

   // Add to all "input" fields that contribute to upper level interrupt,
   // but post_predict() doesn't trigger for someint and otherint because
   // they are UVM_PREDICT_DIRECT :-(
   uvm_reg_field_cb::add(someint,      reg_update_upper_interrupt_cb);
   uvm_reg_field_cb::add(someintmask,  reg_update_upper_interrupt_cb);
   uvm_reg_field_cb::add(otherint,     reg_update_upper_interrupt_cb);
   uvm_reg_field_cb::add(otherintmask, reg_update_upper_interrupt_cb);

In reply to richard_lam:

You could extend uvm_reg_field::do_predict(…) in an own subclass of uvm_reg_field and then set instance overrides on the appropriate fields. This will only work if you created your fields with the factory and with unique contexts, i.e. some_field = uvm_reg_field::type_id::create(“some_field”, null, get_full_name());.

A pragmatic solution would be to attach the callback to your mask fields and to call its post_predict(…) manually wherever you predict the value of your int fields:


some_int.predict(value);
reg_update_upper_interrupt_cb.post_predict(..., .kind(UVM_PREDICT_DIRECT), ...);

You’d need as many calls as you have calls to predict(…). This is something you can wrap in a function:


function void predict_interrupt(uvm_reg_field int_field, uvm_reg_data_t value);
  int_field.predict(value);
  reg_update_upper_interrupt_cb.post_predict(..., .kind(UVM_PREDICT_DIRECT), ...);
endfunction

In reply to Tudor Timi:

Apologies for not replying sooner…wanted to do so, but it was a bit of a crazy crunch time the last few weeks. I definitely appreciated your reply and it gave me new ideas to try and ultimately I did a variant on your second/pragmatic solution. I created my own extension of uvm_callback, which I then registered to the class (a reference model for a part of the DUT that I am checking) that is doing the uvm_reg_field::predict() calls. In my class, instead of calling .predict() on the registers, I call the wrapper function which does the .predict() and then executes my custom callback class.

I also ended up creating a generic interrupt combiner class, which takes a configurable, multiple number of interrupt sources and their mask bits (if any) and then does a direct .predict for an upper level interrupt that I can assign, along with a custom callback to hook any functionality there. For updating/recalculating this upper level interrupt, I can use the uvm_reg_cbs::post_predict() for the cases where the interrupt is cleared or any mask bits are updated (all of these happen with UVM_PREDICT_WRITE access and so the post_predict() callback runs). Only for the set of the interrupt, which happens through a UVM_PREDICT_DIRECT predict(), do I need the wrapper function/custom callback solution.

So I have a pretty clean solution that I am reasonably happy with, **but I really, really wish that the UVM Registers could be updated in a future version so that the post_predict() callback happens for UVM_PREDICT_DIRECT case as well so I wouldn’t have to hop through these unnecessary hoops.
**
(I also tried exploring your first solution of doing a derived class and class override and modifying the implementation of do_predict(), but due to the timing where the post_predict() callback needs to happen in the middle (though near the end) of the function, I can’t just do a super.do_predict() and put code around it, I had to try and copy and paste the whole function and then modify part of it, but that doesn’t work because do_predict() has to access many members that are declared local and not visible to derived classes)

But in short, thanks again for replying to my question, and I also appreciate some of your other blog and forum posts.