Showing posts with label UVM RAL. Show all posts
Showing posts with label UVM RAL. Show all posts

Friday, August 8, 2014

Theory vs. Practice - Reserved Fields in UVM RAL

A seemingly simple question that comes up every now and then is "How do I properly handle reserved fields in UVM RAL?". The answer seems straightforward, right? You just don't model them. This is what the UVM guys tell you. While this does work in most cases, sometimes things aren't so simple. This may have been the intention of how it's supposed to work, but this is not what made it into the BCL implementation.

Let's take a step back and first look at what reserved fields are. They are usually empty bit locations in a register, where no "real" field is defined. Typically there are no storage elements behind them. These locations are reserved for future use, for example adding new fields in a register for a new version of the product.

But what if we have some register bits that are declared as reserved in the spec, but actually have some storage elements behind them? This is the opposite case from above. In this case some storage elements are implemented, but are not intended to be used anymore. This may be because the feature controlled by those bits is supposed to be phased out (but the RTL was not yet modified) or it is just plain hidden.

As luck would have it a colleague of mine had this situation about a month back. He was writing some random values to a register that included reserved bits, but when he was reading them back he didn't get 0s at those positions. When asked how he should handle this, I gave him the naïve answer from the beginning of the post: "Just don't model them and they won't be checked.". We tried this, but this isn't what happened.

I've distilled a little example for this case. Here is a simple register with two fields and a gap between them:

class example_reg_type extends uvm_reg;
  `uvm_object_utils(example_reg_type)
  
  rand uvm_reg_field field1;
  rand uvm_reg_field field2;
  
  function new(string name = "example_reg_type");
    super.new(name, 32, UVM_NO_COVERAGE);
  endfunction
  
  virtual function void build();
    field1 = uvm_reg_field::type_id::create("field1");
    field2 = uvm_reg_field::type_id::create("field2");
    
    field1.configure(this,  8, 24, "RW", 0, 0, 1, 1, 0);
    // there is a gap of 16 bits in between
    field2.configure(this,  8,  0, "RW", 0, 0, 1, 1, 0);
  endfunction
endclass

Using a test harness (the details of which I will spare you in this post), we can emulate bus accesses to this register. Let's do a little sanity check to see how errors are handled in the register model. We write all ones to the register, but when reading it back, let's say that the DUT has a bug in it and it didn't update the value of field1:

class example_reg_test extends uvm_test;
  // ...
  
  task run_phase(uvm_phase phase);
    example_reg_item item = new("item");
    
    // write all ones
    item.write = 1;
    item.data = '1;
    aport.write(item);
    $display("register value: %h", example_reg_block.example_reg.get());
    
    // read the register
    // - the DUT didn't update field1
    item.write = 0;
    item.data = 'hff;
    aport.write(item);
  endtask
  
endclass

In this case, the error message will look like this:

[RegModel] Register "example_reg_block.example_reg" value read from DUT (0x00000000000000ff) does not match mirrored value (0x00000000ff0000ff)
[RegModel] Field field1 (example_reg_block.example_reg[31:24]) mismatch read=8'h0 mirrored=8'hff

We can clearly see from the error message that our shadow register's field1 is out of sync with the one inside the DUT.

Now let's model the situation where our reserved bits are not so reserved. After writing all ones to the register, let's say that the DUT actually had some flip-flops for the bits between the two fields and that these were updated. Remember, we didn't model any field between bits 23 to 8 and because of this we aren't expecting the register model to care what happens at these locations. Here is the code for this:

class example_reg_test extends uvm_test;
  // ...
  
  task run_phase(uvm_phase phase);
    // ...
    
    // read the register
    // - the DUT delivers 1s for the reserved bits
    item.write = 0;
    item.data = 'hff_ffff;
    aport.write(item);
  endtask
  
endclass

I guess you already figured out that I did this little exercise to show you something goes wrong in this situation. The error message we get in this case is:

[RegModel] Register "example_reg_block.example_reg" value read from DUT (0x0000000000ffffff) does not match mirrored value (0x00000000000000ff)

There is no mention of what field has a different value. But how come? I thought that any "fields" left un-modeled won't get checked. We'll this isn't really true. If we look at the UVM source code (lines 2875 to 2885 of uvm_reg.svh in case you're interested) we see that any locations not containing fields do not get masked off. This means the value we receive from the DUT is compared with the 0s inside the shadow register. Coming back to the discussion at the start of the post, handling "don't care" locations like this (leaving them un-modeled) may have been the intention of the UVM developers, but this isn't what is implemented. There's even a Mantis entry for this issue (http://www.eda.org/mantis/view.php?id=4806), though I don't know what the status of this is since it's still marked as TBD. A thing like this could have been easily found by a unit test, but that's a different story...

It seems like what we need to do is disable the comparison for the specific locations. UVM RAL doesn't have a register compare mask per se as in vr_ad. Compare masks are set on a per field basis (and this is a good thing; if you want to have the same for e have a look at my post about disabling checks for fields in vr_ad). This means we need to define a dummy field at the problematic location. The access policy for it is irrelevant seeing as how we don't care about its value. What is key here is setting it as volatile.

class example_reg_type extends uvm_reg;
  `uvm_object_utils(example_reg_type)
  
  rand uvm_reg_field field1;
  rand uvm_reg_field rsvd;
  rand uvm_reg_field field2;
  
  function new(string name = "example_reg_type");
    super.new(name, 32, UVM_NO_COVERAGE);
  endfunction
  
  virtual function void build();
    field1 = uvm_reg_field::type_id::create("field1");
    rsvd   = uvm_reg_field::type_id::create("rsvd");
    field2 = uvm_reg_field::type_id::create("field2");
    
    field1.configure(this,  8, 24, "RW", 0, 0, 1, 1, 0);
    rsvd  .configure(this, 16, 8,  "RW", 1, 0, 1, 1, 0);
    field2.configure(this,  8,  0, "RW", 0, 0, 1, 1, 0);
  endfunction
endclass

When testing it we'll see that the error message goes away. Success!

Now we're fully equipped with how to handle problematic reserved fields in UVM RAL. Read-only locations without any flip-flops can be left un-modeled, but reserved fields with storage elements behind them must be modeled as volatile fields to disable checking. If you want to play around with this example you can find it on SourceForge.

Until next time! Also, don't forget to subscribe if you don't want to miss any future UVM posts.

Sunday, April 13, 2014

Custom Field Access Policies in UVM RAL

As promised in my last post today we're going to look at how to define a custom field access policy in the UVM register package.

Let's use the same access policy from last time: "writing to the field is allowed, except when trying to write the value 0; reading from the field is always allowed".

We'll start out by defining our register field class. The uvm_reg_field class provides a set of hook methods for developers to use when extending it. You might be tempted to think that we could use the pre_write() method in our case, but this will only work if we do implicit prediction (i.e. using calls to read() or write() inside our sequences). If we want to make it portable for cases when have traffic to our registers coming from other sources (such as legacy VIP or other DUT blocks when doing vertical reuse) we will want it to work for explicit prediction as well. To achieve this we have to extend the predict() method.

The uvm_reg_field class doesn't provide any pre-/post- hooks for the predict() method, but we can use callbacks for that. First we define our own callback class:

class vgm_wri0_cbs extends uvm_reg_cbs;
  `uvm_object_utils(vgm_wri0_cbs)
  
  function new(string name = "vgm_wri0_cbs");
    super.new(name);
  endfunction
  
  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);
    if (kind == UVM_PREDICT_WRITE && fld.get_access() == "RWI0" && value == 0)
      value = previous;
  endfunction
endclass

We also have to declare our own register field class that defines the new RWI0 policy:

class vgm_rwi0_reg_field extends uvm_reg_field;
  `uvm_object_utils(vgm_rwi0_reg_field)
  
  local static bit m_wri0 = define_access("RWI0");
  
  function new(string name = "vgm_rwi0_reg_field");
    super.new(name);
  endfunction  
endclass

When declaring new access policies, it's best to use all uppercase characters as define_access(...) calls toupper() on the string we give it as an input argument and adds that to the table of access policies. What I would have liked to do at this point is create a callback object inside the constructor and add it to the field. This was unfortunately not possible as adding a callback does a call to get_full_name() which in turn requires a references to the field's parent register be set up. We have to postpone this step for higher up in the instance tree.

The last thing we need to do is declare the register:

class example_reg_type extends uvm_reg;
  `uvm_object_utils(example_reg_type)
  
  rand uvm_reg_field      field1;
  rand vgm_rwi0_reg_field field2;
  
  function new(string name = "example_reg_type");
    super.new(name, 32, UVM_NO_COVERAGE);
  endfunction
  
  virtual function void build();
    field1 = uvm_reg_field::type_id::create("field1");
    field2 = vgm_rwi0_reg_field::type_id::create("field2");
    
    field1.configure(this,  16, 16, "RW",   0, 0, 1, 1, 0);
    field2.configure(this,  16,  0, "RWI0", 0, 0, 1, 1, 0);
    
    // register callback
    // - can't be done in vgm_rwi0_reg_field because it calls get_full_name()
    //   which requires the field's parent
    begin
      vgm_wri0_cbs wri0_cbs = new("wri0_cbs");
      uvm_reg_field_cb::add(field2, wri0_cbs);
    end
  endfunction
endclass

We've defined field2 as being of type vgm_rwi0_reg_field and as having the RWI0 access policy. We've also added a callback to this field to actually implement the access policy.

This is the only way UVM RAL allows us to define custom field access policies and I can't say I'm particularly pleased about it. Using callbacks is slow and it is bloated. We've had to split our code into three sections: the callback class, the register field class and the register class). Had we have had a post_predict() hook in uvm_reg_field we could have just implemented everything inside our register field class, resulting in less lines of code and better encapsulation. This is definitely something I hope they will improve in a future release.

As usual, the code above including everything else needed to test it can be found on the blog's Sourceforge page.

You can also subscribe here if you don't want to miss any of my future posts.