Jump to content
leomoon85

uvm register implimentation for same register with different instances

Recommended Posts

Hello , 

 

  I have a scenario as below , 

 

In the dut , there is a set of registers which acts as control register for different instances of same IP inside dut. There is a control register which controls the set of registers to decide which instance of IP it needs to wr/rd .

 

I am thinking of creating a reg block for the set of registers and instantiate as per the IP instance number. But I am not sure how the reg wr/rd of each block as per the control register IP selection . Please comment on this problem . 

 

Thanks

Share this post


Link to post
Share on other sites

This is a pretty uncommon scenario, that you won't be able to implement without some extra code. This reminds me of a situation a colleague of mine faced, where different registers shared the same address, but they were differentiated based on some extra bits in the bus transaction objects. In your case, you differentiate using another register. It might be problematic to map multiple registers to the same address (you get warnings, but I'm not sure if nothing else gets busted).

 

The first thing I can think of is that you can define your IP regs in a separate block (as you probably already have). You can instantiate them in your main block and not map them. At the same time you can add another instance of the IP regs block and map that one. This will serve as the gateway to the "real" registers:

class dut_reg_block extends uvm_reg_block;
  rand control_reg CONTROL;
  rand ip_reg_block IP;

  ip_reg_block IPs[4];

virtual function void build();
    // ...
    default_map.add_reg(CONTROL, 'h0);
    default_map.add_submap(IP.default_map, 'h10);

    foreach (IPs[i]) begin
      IPs[i].build();
      // ... not mapped ...
    end
  endfunction

  // ...
endclass

The use model would be that when you call IP.DATA.write(...) or read(...), you would be accessing one of the register blocks in IPs, depending on the value set in CONTROL.MUX. You need to make this hookup. You can make it so that when IP.DATA gets written, the appropriate register gets updated, via a callback:

class mux_regs_cbs extends uvm_reg_cbs;
  control_reg CONTROL;
  data_reg DATAs[4];

  virtual function void post_predict(uvm_reg_field fld, uvm_reg_data_t previous,
    inout uvm_reg_data_t value, input uvm_predict_e kind, uvm_path_e path,
      uvm_reg_map map
  );
    data_reg DATA = DATAs[CONTROL.MUX.get_mirrored_value()];
    uvm_reg_field real_fld = DATA.get_field_by_name(fld.get_name());

    if (kind == UVM_PREDICT_WRITE)
      real_fld.predict(value);

    // Won't work because 'predict(...)' is called after checking the read.
    //else if (kind == UVM_PREDICT_READ)
    //  value = real_fld.get_mirrored_value();
  endfunction
endclass

You also need to update the value of IP.DATA when reading, so that you can check what the DUT returns. You can't do this in the previous callback, since the post_predict(...) hook is called after the read data has already been checked and there isn't any pre_predict(...) hook. What you can do, however, is to update the value of IP.DATA whenever you're switching between registers (i.e. writing CONTROL):

class mux_control_cbs extends uvm_reg_cbs;
  data_reg DATA;
  data_reg DATAs[4];

  virtual function void post_predict(uvm_reg_field fld, uvm_reg_data_t previous,
    inout uvm_reg_data_t value, input uvm_predict_e kind, uvm_path_e path,
      uvm_reg_map map
  );
    if (kind == UVM_PREDICT_WRITE)
      DATA.predict(DATAs[value].get_mirrored_value());
  endfunction
endclass

You need to add the callbacks to the appropriate registers:

protected function void connect_ips();
    mux_regs_cbs regs_cbs = new();
    mux_control_cbs control_cbs = new();

    regs_cbs.CONTROL = CONTROL;
    foreach (regs_cbs.DATAs[i])
      regs_cbs.DATAs[i] = IPs[i].DATA;

    control_cbs.DATA = IP.DATA;
    foreach (control_cbs.DATAs[i])
      control_cbs.DATAs[i] = IPs[i].DATA;

    begin
      uvm_reg_field flds[$];
      IP.DATA.get_fields(flds);
      foreach (flds[i])
        uvm_reg_field_cb::add(flds[i], regs_cbs);
    end

    uvm_reg_field_cb::add(CONTROL.MUX, control_cbs);
  endfunction

You can either do this in the register block itself or outside, if you're relying on generated code that you can't touch.

 

The code I posted could be made more flexible, i.e. instead of hardcoding references to DATA, it could be made to loop over all registers of the IP reg block, etc. Maybe there are nicer ways of doing it as well. I'm most probably going to write a blog post on this in the future to detail this procedure and maybe investigate others, so stay tuned!

Share this post


Link to post
Share on other sites

Hi Tudor,

 

Thanks for the above example, I am trying to implement the same but for different scenario.

 

But I am stucking at a place that in my callback class post_predict is not called while I am reading/writing reg(with which callback is registered).

I can see the prints from pre_write/post_write/pre_read/post_read.

 

Can you please help me out where you suspect the issue.

 

Thanks,

Karandeep

Share this post


Link to post
Share on other sites

I am reading/writing reg(with which callback is registered).

 

From the quote above I take it you registered your callback with the register and not the fields. The problem is that there isn't any post_predict(...) hook defined for uvm_reg, just for uvm_reg_field. They both use the same callback class though (which is misleading). If you look at the BCL code you'll see that uvm_reg::do_predict(...) only calls predict(...) on each of the register's fields. You have to add the callback to each of these in a loop (as I've done in the example above). Here it is again:


uvm_reg_field flds[$];

some_reg.get_fields(flds);

foreach (flds)

uvm_reg_field_cb::add(flds, some_reg_cbs);

Share this post


Link to post
Share on other sites

Thanks Tudor !! You exactly hit the right place. Indeed registered callback to reg only.

I think what I wanted to acheive would have to be done in other way.

I wanted to implement "SHARED VALUE" registers which are at different address space in same map. Lets say I have

0x0 REG 1 --> Type RW
0x4 REG 2 --> Type W1S
0x8 REG 3 --> Type W1T
0xC REG 4 --> Type W1C

Now any change(write) to any register should be reflected(read) same in all the four.

For this reason I planned to make a callback, register that with all the four reg, any writeaccess to an anyone will trigger the same callback in which post_predict will update the desired value of all the four with the current updated(desired) value of accessed register.

Can you please share thoughts what wud be the better way for this type of implementation.

Thanks once again.
Karandeep.

Share this post


Link to post
Share on other sites

Thanks Tudor for sharing your thoughts, it really helps.

 

Posting here the implementation, might be helpful of someone someday  ;)  ;) (provided better ways would always be there :rolleyes: )

--------------------// CALLBACK IMPLEMENTATION //---------------------------

class my_cb extends uvm_reg_cbs;
`uvm_object_utils(my_cb)
  virtual task post_write( uvm_reg_item rw);
	uvm_reg my_reg;
	uvm_reg rg;
	int unsigned offset_addr;

	if (!$cast(my_reg ,(rw.element))) begin  //TODO: remove this once able to use get_offset() api with rw.element
      		`uvm_error("WRONG_TYPE","Provided casting failed")
      		return;
    end
	offset_addr = (my_reg.get_offset() & 'hFFF0);
	for(int i = offset_addr; i <= (offset_addr+'hC); i= i+4)begin

		rg = rw.map.get_reg_by_offset('h0000+i, (rw.kind == UVM_READ));
		if (rg == null) begin
		       `uvm_fatal("REG_NULL",$sformatf("Unable to reg by offset = 0x%8x : called from %s",('h0000+i), get_type_name() ))
		end
		rg.predict(my_reg.get_mirrored_value()); // Updating the mirror values for all the four reg
	end
  endtask:post_write
endclass:my_cb

----------------------// IN REG BLOCK //---------------------
virtual function void build();

    my_cb cb_reg = new();
    // Now create all registers

    REG1 = REG_1::type_id::create("REG_1", , get_full_name());
    REG_1.configure(this, null, "");
    REG_1.build();
    // Similar for REG_2/REG_3/REG_4
    uvm_reg_cb::add(REG_1, cb_reg);
    uvm_reg_cb::add(REG_2, cb_reg);
    uvm_reg_cb::add(REG_3, cb_reg);
    uvm_reg_cb::add(REG_4, cb_reg);

Share this post


Link to post
Share on other sites

I wouldn't use post_write(...) since that is only called when you call reg.write(...), i.e. when you are driving the registers. If you do any kind of vertical reuse, you might not be driving the registers from sequences, but other parts of the RTL will be doing this. Explicit prediction and post_predict(...) are the scalable way to go.

Share this post


Link to post
Share on other sites

Agreed !!

 

But the problem is that there isn't any post_predict(...) hook defined for uvm_reg, do need to make n number of callbacks for n/4 registers. 

Scarifying the vertical reuse for sake of using single callback for n registers.

 

Indeed at deep of my heart I was feeling it not a good way, Thanks for pointing. Now I will for sure try to find some other way out.

 

Implementing this behavior in predictor will help ??? I will try to check this implementing at predictor level.

 

Thanks for your valuable feedback.

Share this post


Link to post
Share on other sites

You could still implement it as a callback, but just attach it to any of the registers' fields:

class my_cb extends uvm_reg_cbs;
`uvm_object_utils(my_cb)
virtual task post_predict(input uvm_reg_field fld, ...);
    uvm_reg my_reg = fld.get_parent();
    
    // everything else stays the same
    // ...
endtask:post_predict
endclass:my_cb

----------------------// IN REG BLOCK //---------------------
virtual function void build();

my_cb cb_reg = new();
// Now create all registers

REG1 = REG_1::type_id::create("REG_1", , get_full_name());
REG_1.configure(this, null, "");
REG_1.build();
// Similar for REG_2/REG_3/REG_4

uvm_reg_field fields[$];
REG_1.get_fields(fields);
uvm_reg_cb::add(fields[0], cb_reg);
// Similar for REG_2/REG_3/REG_4

It doesn't matter on which field you add it. I chose the first one, but it could just as well be any of them. You just use the field to make sure post_predict(...) gets called and to get the register that was accessed.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×