Uvm_reg extension object

We know that UVM provide an extension argument in uvm_reg::read and uvm::write task. So user can add extension message when execute read/write task and get this item in adapter.
But when we use built-in sequence, like uvm_reg_bit_bash_seq… this extension message isn’t included right? So how can we add this extension in register read/write automatically?

My idea is produce a child class extends uvm_reg and add extension object as an member variable of new class. And also override the uvm_reg::read and write task. But we should ensure that uvm_reg::write/read is the lowest level task and every register RW operation need this task at least.

Any idea for my problem, thank you very much!

In reply to Ben:

It is a little bit unclear to me what you are saying and what you want to know. Of course you can extend any class in the UVM to add additional data members and methods, including overriding methods. This is the concept of OOP.
If you could elaborate in some more detail your question would be great.

Clarify the requirment.

My project communication protocol define two different write/read frame format. And some addr registers support one format and some addr registers support another format in a single register map.

UVM provides extension argument in uvm_reg::write/read. For single register read or write, can attach extension properties in command like below:


    map.reg_a.write(status, 16'hxxxx, UVM_FRONTDOOR, .parent(this), **.extension(xxx)**);
    map.reg_b.write(status, 16'hxxxx, UVM_FRONTDOOR, .parent(this), **.extension(xxx)**);

The extension argument is used to distinguish two formats for different registers.
Certainly this method is not good since top test writer usually don’t know which register supports one format and which one supports another. We just care about which register need to be set in top level.
And also when use built-in sequence like uvm_reg_bit_bash_seq…, uvm execute the uvm::write/read command internally and the default extension argument is null. So how can we realize the built-in sequence and also attach the right extension argument automatically?

Thanks.

In reply to chr_sue:

Thanks guy. I’ve clarified my requirment. Any advice pls point out.

In reply to Ben:

Why not switch to two maps?

In reply to warnerrs:

For two maps, indeed we can use driver and adapter for each to execute different format read/write. But at top level, test writer still need to know which register support one format/map and which support another format/map.

I’m wondering why not UVM set a reserved user defined memeber in uvm_reg class and also put in into uvm_reg_item so that user can attach more properties on it.

Hi all

I meet a problem when solve this problem.
I extend a child class on uvm_reg and put extension variable into it and override the write/read task.


typedef enum {FORMAT_A, FORMAT_B, FORMAT_C} user_rw_type;

class user_reg_extension extends uvm_object;

   user_rw_type rw_type;
  
  `uvm_object_utils_begin(user_reg_extension)
    `uvm_field_enum(user_rw_type, rw_type, UVM_DEFAULT)
  `uvm_object_utils_end
  
   function new(string name="user_reg_extension");
    super.new(name);
   endfunction
  
endclass : user_reg_extension

class user_reg extends uvm_reg;
  
  user_reg_extension user_extension;  //user_reg_extension is a class extends uvm_object
  ...
  virtual task write(output uvm_status_e      status,
                     input  uvm_reg_data_t    value,
                     input  uvm_path_e        path = UVM_DEFAULT_PATH,
                     input  uvm_reg_map       map = null,
                     input  uvm_sequence_base parent = null,
                     input  int               prior = -1,
                     input  uvm_object        extension = user_extension,  //in parent uvm_reg class this code is extension = null
                     input  string            fname = "",
                     input  int               lineno = 0);

  super.write(status,
              value,
              path,
              map,
              parent,
              prior,
              extension,
              fname,
              lineno);  
  endtask
...
endclass : user_reg

//Then put a user_reg into map
class user_reg_xxh extends user_reg;
...  
  function new(string name = "user_reg_xxh");
    super.new(name, 8, UVM_NO_COVERAGE);
    user_extension.rw_type = xx; //set user extension property for this register
  endfunction

endclass : user_reg_xxh 

class user_reg_map extends uvm_reg_block;
  rand user_reg_xxh user_reg_xxh;
  uvm_reg_map map;
...

  virtual function build();
    user_reg_xxh= user_reg_xxh::type_id::create("user_reg_xxh");
    user_reg_xxh.configure(this);
    user_reg_xxh.build();

    // map name, offset, number of bytes, endianess
    map = create_map("map", 'h0, 2, UVM_LITTLE_ENDIAN);

    map.add_reg(user_reg_xxh, addr, "RW");

    lock_model();
  endfunction
endclass : user_reg_map

//sequence
class user_seq extends user_reg_sequence;

  user_reg_map reg_map;
...
  task body();
    uvm_status_e status;
    uvm_reg_data_t data;
    uvm_reg rg;

...

    rg = reg_map.user_reg_xxh;
    $display("user_reg_xxh type name:", reg_map.user_reg_xxh.get_type_name());  //get the name user_reg_xxh
    $display("rg type name:", rg.get_type_name());  //Also get the name user_reg_xxh

    #1us;
    // register model access write()/read()
    reg_map.user_reg_xxh.write(status, 16'hxxxx, UVM_FRONTDOOR, .parent(this));  //first write command, RIGHT NO PROBLEM
    rg.write(status, 16'hxxxx, UVM_FRONTDOOR, .parent(this));  //second write command  MEET PROBLEM IN THIS COMMAND
  endtask
endclass : user_seq

class reg2user_adapter extends uvm_reg_adapter;
...
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
        user_base_trans_item tmp;
	uvm_reg_item item;
	user_reg_extension ext_tmp;
	logic [7:0] data_buf;
	
	tmp = user_base_trans_item::type_id::create("tmp");
	
	item = this.get_item();			
	void'($cast(ext_tmp, item.extension));

	if (ext_tmp.rw_type == FORMAT_A)  //Error occurs in second write command
        ...
	else if (ext_tmp.rw_type == FORMAT_B)
        ...
	else
        ...
...
	
    return tmp;
  endfunction
...
endclass : reg2user_adapter 


When run the test, first write command is right and adapter can get the extension property from uvm_reg_item. But second write command is wrong, Fatal: (SIGSEGV) Bad handle or reference message on the if condition sentence related to ext_tmp in adapter class, see code above.
I’m confused because uvm_reg variable rg has pointed to user_reg_xxh before write and it also get the right type name, but why user_extension member variable can’t be sent and it seems that child class user_reg::write wasn’t called???

Any advice pls point out.

Thanks all.

In reply to Ben:

Hi Ben, I think your problem is related to the object compatibility when you do your $cast inside the register adapter. Probably you are not picking up the same object as you think, Can you show to us what is inside your user_reg_extension and how do you get it? Also looking into your write operation, there’s no object being passed like the example below.


reg_map.user_reg_xxh.write(status, data, UVM_FRONTDOOR, .parent(this), .extension(some_object));

In reply to Valmor:

I’ve updated my code.
Yes, I put the extension as an internal property of register. And I override the write task. You can see my code above. The first write command is Ok. Adapter can get right extension object. While the second is wrong.

Thanks

In reply to Ben:

Oh I see… You tried to connect your register inside the uvm connect_phase (rg = reg_map.user_reg_xxh)? I say again that maybe in the way you have constructed you are facing a mismatch between objects.

Hi guys

I found the issue…
The issue point is in override write task. should be

class user_reg extends uvm_reg;
 
  user_reg_extension user_extension;  //user_reg_extension is a class extends uvm_object
  ...
  virtual task write(output uvm_status_e      status,
                     input  uvm_reg_data_t    value,
                     input  uvm_path_e        path = UVM_DEFAULT_PATH,
                     input  uvm_reg_map       map = null,
                     input  uvm_sequence_base parent = null,
                     input  int               prior = -1,
                     input  uvm_object        extension = null,  //in parent uvm_reg class this code is extension = null
                     input  string            fname = "",
                     input  int               lineno = 0);
 
  super.write(status,
              value,
              path,
              map,
              parent,
              prior,
              this.user_extension,
              fname,
              lineno);  
  endtask
...
endclass : user_reg

Thanks all for help

You could have two maps and separate the registers between the wire formats that they support. For the test writer to know, you can use uvm_reg’s get_maps() to find out which wire format is supported.

Instead of mymap.reg.write(), you can also use reg.write(.map(mymap)). If you don’t specify anything, it’ll choose the default map that’s set.

One gripe I have with this is that you’ve effectively taken out the extension as an option for the user. If later on you realize you need to use the extension for something else, you’ll have a lot of headaches.