The Hidden Feature of vr_ad: Gray Zone Comparison

The Hidden Feature of vr_ad: Gray Zone Comparison

Posted by

Verifying RTL updatable registers (e.g. status registers) can be very difficult due to factors like slight desynchronization between model and DUT or different sampling points for the register value. Register reads in the out-of-synch window produce false errors which are a nightmare to handle in the verification environment. Luckily, the vr_ad library comes with a build-in feature to tackle this problem, called “Gray Zone Comparison”.

In this post we will see how this feature works and how we can fine tune it.


1. Device Under Test

Let’s assume that we need to verify a DUT with two registers accessible via an APB interface – one for control, and one for status. The status register contains only a counter field, which increments every 8 clock cycles, when enabled.

Name Offset Bits
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
CTRL 0xF000 reserved EN
STATUS 0xF004 reserved COUNTER

The timing diagram bellow shows how the counter is incremented:

Counter increments every 8 clock cycles, when enabled

Due to some internal logic (FSMs, pipeline registers, etc), reading STATUS register can have a wait state of up to 5 clock cycles and the return value can be sampled anywhere in this interval.

2. Gray Zone Comparison Explained

Let’s see how the uncertainty introduced by the unknown sampling point of the returned read value reflects in the check performed by the vr_ad library.

For simplicity, let’s consider that our model of STATUS.COUNTER is perfectly aligned with the one from the DUT.

In the image below we have three APB reads from the STATUS register and the value of the STATUS.COUNTER probed inside the DUT.

APB reads from the STATUS register

The important thing to notice in the above image is the stability of the STATUS.COUNTER throughout each APB read access and the read value returned by the DUT:

  • For access #1, STATUS.COUNTER is stable throughout the read access and the DUT returns the stable value: ‘h3
  • For access #2, STATUS.COUNTER changes throughout the read access and the DUT returns the new value: ‘h8
  • For access #3, STATUS.COUNTER changes throughout the read access and the DUT returns the old value: ‘hA

We know that when we connect the vr_ad register model to the APB monitor the check of the read value is done at the end of the APB transfer, when we call compare_and_update() method. So we can deduce that the expected values for each of the three accesses are:

  • For access #1 the expected value is ‘h3
  • For access #2 the expected value is ‘h8
  • For access #3 the expected value is ‘hB

Based on the above statement, it is reasonable to assume that vr_ad will trigger an error for access #3 as the check will use as expected value the model data from that point in time (‘hB), but the DUT returns the previous value (‘hA).

However, this is NOT the case as vr_ad will happily inform us that even the third access is OK from its point of view. This is the making of the Gray Zone Comparison feature in vr_ad, and this is enabled by default.

With Gray Zone Comparison, vr_ad defines a gray zone as a time window, and the comparison will accept as correct values for the register either the value from the beginning of the window or the value of the register from the end of the window.

Definition of the Gray Zone

2.1. Start of the Gray Zone

To mark the start of the Gray Zone one must call method reg_being_read() from the vr_ad_reg_file:

reg_being_read(addr : vr_ad_addr_t, data : vr_ad_data_t)

The job of this method is to add in a list called regs_being_read an address-data pair used as an accepted read value when compare_and_update() is called next time.

In the default implementation, this is called from vr_ad_sequence_driver just before vr_ad_execute_op() TCM is executed.

2.2. End of the Gray Zone

To mark the end of the Gray Zone one must call method reg_ended_being_read() from vr_ad_reg_file:

reg_ended_being_read(addr : vr_ad_addr_t)

The job of this method is to remove from list regs_being_read the first entry which matches the address used as an argument for this method.

3. The Issues of the Default Implementation

There are several issues with the default implementation of the Gray Zone Comparison but vr_ad provides enough flexibility to easily solve them.

3.1. Gray Zone Defined by the Driver

In the default implementation, the start and end of the gray zone are defined by the vr_ad sequence driver. This has two major drawbacks:

  1. This will not work if the user access the registers via physical sequences and not the default vr_ad API (e.g. call of reg.write() or reg.read())
  2. This will not work if the IP environment is reused at system level and the register access interface (e.g. APB) is suddenly driven by some other RTL module (e.g. CPU) and not by the active part of the IP environment.

One more robust approach is to let the monitor define the start and end of the gray zone.

First, we need to disable in vr_ad the automatic call of the reg_being_read() method. This is easily controlled like so:

extend CFS_REG_FILE vr_ad_reg_file {
   reset() is also {
      // Set this to FALSE if you want to disable the default 
      // definition of Gray Zone Comparison, the default value is TRUE
      set_automatic_update_of_read_start(FALSE);
   };
};

Next, we need to hook up to the analysis port coming from the APB monitor. When we detect the beginning of a read transfer from a register, we sample the register value at that point in time and use it to start the gray zone:

extend cfs_dut_env {
   write_apb(transfer : acme_apb_mon_transfer) is first {
      if(transfer.is_active()) {
        if(transfer.dir == READ) {
           
           var relative_addr := transfer.addr - reg_file.get_address();
           var reg           := reg_file.get_reg_by_address(relative_addr);
           
           if(reg != NULL) {
             // This is the beginning of the custom Gray Zone ->
             //	  When the read is first detected on the bus
             reg_file.reg_being_read(relative_addr, reg.read_reg_val());
           };
        };
      };
   };
};

To define the end of the gray zone we need to do some hacking because vr_ad does not provide some control to stop the call of reg_ended_being_read() at the end of vr_ad_execute_op().

First, we need to override this method and make it do nothing:

extend CFS_REG_FILE vr_ad_reg_file {
  reg_ended_being_read(addr : vr_ad_addr_t) is only {
     // Do nothing
  };
};

Next, we need to do the old job of reg_ended_being_read(), which is deleting the entry from regs_being_read, but at the end of the APB transfer, after the comparison is finished. We can take advantage of the fact that regs_being_read is a public field in vr_ad_reg_file:

extend cfs_dut_env {
   write_apb(transfer : acme_apb_mon_transfer) is also {
      if(transfer.is_active() == FALSE) {
        if(transfer.dir == READ) {
           
           var relative_addr := transfer.addr - reg_file.get_address();
           var reg           := reg_file.get_reg_by_address(relative_addr);
           
           if(reg != NULL) {
             // This is the end of the custom Gray Zone ->
             //	   When the read ends on the bus
             
             var index_to_delete := reg_file.regs_being_read.first_index(
                it.addr == relative_addr);
             
             if(index_to_delete != UNDEF) {
                reg_file.regs_being_read.delete(index_to_delete);
             };
           };
        };
      };
   };
};

It is important here to make sure that this “is also” definition of write_apb() is called after compare_and_update() is executed.

Defining the gray zone based on the monitor information makes the gray zone window a little bit smaller but it is still effective:

Gray zone window defined on information coming from the monitor

3.2. Gray Zone Messes Up the Register Model

Gray Zone Comparison is a great feature to add some flexibility to the check of the read value but most of the times we don’t want this check to influence our values in the register model.

However, in the default behavior of the vr_ad, when a register is read, the check is performed and the value is updated – hence the compare_and_update() method name. This has the potential of desynchronizing our register model with the DUT, like in the waveforms below:

The automatic update from compare_and_update() desynchronizes the model

Fortunately, this automatic update can be easily disabled:

extend CFS_REG_FILE vr_ad_reg_file {

   reset() is also {
      // Disable the automatic update of the STATUS register
      // during the compare_and_update() method
      status.static_info.do_update_on_compare = FALSE;
   };
};

This will produce the following behavior:

Disabling the update during compare keeps the model in synch

That’s all there is regarding the Gray Zone Comparison feature – it is a great feature of the vr_ad library which requires just a little bit of fine tuning in order to use it properly.

You can also play around with an example I build to showcase this feature on EDA Playground.

Hope you found this useful 🙂


Cristian Slav

Leave a Reply

Your email address will not be published.