Thursday, June 19, 2014

Disabling Field Checks in vr_ad

Register packages are an awesome tool to easily implement checks on the behavior of our DUT by holding a copy of its state inside a register model. When everything is properly hooked up, writing to the DUT will update the state of this model, while reading from it will check that the output from the DUT matches to what we expect based on the register model's state.

Things are not as clearly cut as above, though. Aside from different field access policies with respect to what can be read or written, there are also register fields that we cannot easily check like this. Some fields are not only written/read from the outside, but the hardware itself can update them, which means we need to model this as well. What is sometimes the case, however, is that our model is not fully accurate.

For example, the fields of a status register will get updated based on the internal operation of the DUT, but we may not know the exact clock cycle when a status flag is supposed to be updated. This leads to a window of uncertainty when a read of that flag might be flagged as an error, because the hardware was updated, but our model was not. Another example is at SoC level, where we assume that all blocks are working correctly (because they are pre-verified at module level). For performance reasons we might not want to have all of the modeling logic running for fields that are updated by hardware.

What we need to do in such cases is disable the comparison of these fields. vr_ad provides a mechanism inside vr_ad_reg to control which fields are considered for checking based on a compare mask. The problem with this is that there is one compare mask for the whole register and it doesn't support per-field operations directly (individual bits can be excluded, but the user has to figure out which ones). In contrast, UVM RAL allows individual fields to be easily excluded from the comparison.

What we would like to have is a similar mechanism for vr_ad. The reason behind it is that hardcoding specific bits of the compare mask (corresponding to specific fields) to 0 will make our code inflexible should the offsets of these fields ever change. Let's start off slowly and gradually work our way up.

Let's suppose we have a register called SOME_REG that contains 3 fields which are both readable and writable (for simplicity).

<'
reg_def SOME_REG SOME_REG_FILE 0x0 {
  reg_fld FOO : uint(bits : 8);
  reg_fld BAR : uint(bits : 8);
  reg_fld QUX : uint(bits : 16);
};
'>

Let's say that field FOO is updated by hardware and we want to disable comparing it and take the value that we read from it as it is. To do that we can declare a method called disable_check_foo() and call it from set_static_info().

<'
extend SOME_REG vr_ad_reg {
  disable_foo_check() is {
    var fields_info := get_fields_info().all(.fld_name == "FOO");
    var cmask := get_compare_mask();
    var lo := fields_info[0].fld_fidx;
    var hi := lo + fields_info[0].fld_size - 1;
    cmask[hi:lo] = 0;
    set_compare_mask(cmask);
  };
  
  set_static_info() is also {
    disable_foo_check();
  };
};
'>

Every register contains a struct that holds information about its fields. We use this struct to get the offset and size of FOO and set the bits corresponding to it to 0 inside the compare mask.

We can test our new method by doing a compare_and_update(...) with a value different than the one we have in the register model.

<'
extend sys {
  run() is also {
    message(LOW, "Simulating update by hardware on field FOO with value 0xff");
    compute some_reg.compare_and_update(0x0, { 0x00; 0x00; 0x00; 0xff });
  };
};
'>

Without the call to disable_check_foo() the comparison would fail. Go ahead and try it out for yourself to see.

This is all fine and dandy, it does what it's supposed to, but what if we have multiple fields we want to disable? Copying the method and adapting it for each field is out of the question. Well, we can have the field name as a parameter.

<'
extend SOME_REG vr_ad_reg {
  disable_field_check(fld_name : string) is {
    var fields_info := get_fields_info();
    
    // essential to check that the field actually exists
    assert(fields_info.has(.fld_name == fld_name));
    
    fields_info = fields_info.all(.fld_name == fld_name);
    var cmask := get_compare_mask();
    var lo := fields_info[0].fld_fidx;
    var hi := lo + fields_info[0].fld_size - 1;
    cmask[hi:lo] = 0;
    set_compare_mask(cmask);
  };
  
  set_static_info() is also {
    disable_field_check("FOO");
    disable_field_check("QUX");
  };
};
'>

What we definitely need to make sure of is that the field exists in the register. We can't do this at compile time, so we have to add the assert to check at run-time. Let's give this one a test as well.

<'
extend sys {  
  run() is also {
    message(LOW, "Simulating update by hardware on field QUX with value 0xff");
    compute some_reg.compare_and_update(0x0, { 0xff; 0x00; 0x00; 0x00 });
  };
};
'>

Now the comparison for both FOO and QUX is disabled.

The next question that naturally comes to mind is "What if I have multiple registers whose fields I don't want to check?". What I first thought was "I'll just declare the disable_field_check(...) method inside sys or util in such a way that it takes the register instance and the field name as a parameter.". That's a bad idea; I've been doing too much SystemVerilog the past few months and it's skewed my thinking. We've got the power of AOP on our side so we can just declare disable_field_check(...) directly inside vr_ad_reg and use it in all of its subtypes.

<'
extend vr_ad_reg {
  disable_field_check(fld_name : string) is {
    // same implementation as above
    // ...
  };
};

extend SOME_REG vr_ad_reg {
  set_static_info() is also {
    disable_field_check("FOO");
  };
};

extend SOME_OTHER_REG vr_ad_reg {
  set_static_info() is also {
    disable_field_check("BAZ");
  };
};
'>

What have we achieved here? We can disable checking of any field in any register by just referring to the field's name. We don't have to jump to the spec and see where a specific field is located, then set the compare mask to 0 for those bits. By not hardcoding the bits to exclude in our verification environment we are also protected from future changes in field offsets.

The disable_field_check(...) method doesn't necessarily have to be called from set_static_info(). It can be called whenever and wherever during the simulation. It could also be paired with a function that re-enables checking to allow for time windows where specific fields are not checked. It would be nice if vr_ad incorporated this or something similar in a future release.

I've uploaded the code to SourceForge, if anyone wants to play with it hands-on. Until next time!

No comments:

Post a Comment