Monday, March 17, 2014

Keeping Your Constraints in One Basket

Doing constrained random verification is pretty easy on paper. You know what the transactions look like and what constraints you want to apply to their fields. It gets trickier when you want to convert this intent into a real life verification environment by building all the sequence items and sequences. If done improperly, this can lead to a lot of doubled up work. A problem I've hit a couple of times is how to handle my constraints so as to avoid scattering them all over the place.

Let's take as an example an AHB UVC. If you're not familiar with the AMBA High Performance Bus protocol, the specification is available here (only for registered ARM customers, but you can also find more info by searching the net).

Here is how a sequence item could look like:

classvgm_ahb_itemextendsuvm_sequence_item;randbit[31:0]addr;randvgm_ahb_size_esize;randvgm_ahb_burst_eburst;// other fields (omitted for clarity)// ...// address must be aligned to the size of the transferconstraintaligned_address_c{size==VGM_AHB_BYTES_2->addr[0:0]==0;size==VGM_AHB_BYTES_4->addr[1:0]==0;size==VGM_AHB_BYTES_8->addr[2:0]==0;size==VGM_AHB_BYTES_16->addr[3:0]==0;size==VGM_AHB_BYTES_32->addr[4:0]==0;size==VGM_AHB_BYTES_64->addr[5:0]==0;size==VGM_AHB_BYTES_128->addr[6:0]==0;}// bursts must not cross a 1kB address boundaryconstraintdont_cross_1kb_c{burstinside{VGM_AHB_INCR4,VGM_AHB_INCR8,VGM_AHB_INCR16}->addr[10:0]<='h4_00-vgm_ahb_get_num_transfers(burst)*2**size;}endclass

The specification restricts the values of the addr field based on the values of the size and burst fields. This gets translated into the two constraints: aligned_address_c and don't_cross_1kb_c.

Now that we have our groundwork laid out, we want to start verifying our DUT. We want to stress it by accessing consecutive address locations with different sizes and burst types. Our sequence will have the burst type, size and the first address location as a fields. An additional field will configure how many address clusters we access. Naturally we want to adhere to the protocol so, for example, starting a 4-byte wide transfer to address 0x431 would be illegal. The constraints we defined for the item have to hold here as well.

You might be tempted to do something like this:

classvgm_ahb_consec_sequenceextendsuvm_sequence#(vgm_ahb_item);randintcount;randbit[31:0]addr;randvgm_ahb_size_esize;randvgm_ahb_burst_eburst;// address must be aligned to the size of the transferconstraintaligned_address_c{size==VGM_AHB_BYTES_2->addr[0:0]==0;size==VGM_AHB_BYTES_4->addr[1:0]==0;size==VGM_AHB_BYTES_8->addr[2:0]==0;size==VGM_AHB_BYTES_16->addr[3:0]==0;size==VGM_AHB_BYTES_32->addr[4:0]==0;size==VGM_AHB_BYTES_64->addr[5:0]==0;size==VGM_AHB_BYTES_128->addr[6:0]==0;}// bursts must not cross a 1kB address boundaryconstraintdont_cross_1kb_c{burstinside{VGM_AHB_INCR4,VGM_AHB_INCR8,VGM_AHB_INCR16}->addr[10:0]<='h4_00-vgm_ahb_get_num_transfers(burst)*2**size;}// body() definition// ...endclass

Copy-pasting is rarely a good idea in software development. It just gives us more places to update our code should our constraints change (for example we want to add more because our initial constraints were not enough). It also creates more places where mistakes can creep in.

Ideally we wouldn't have to do any duplication. Couldn't we somehow make use of the AHB sequence item class? It already defines all the constraints we need...

It turns out we can. We can tweak our previous example by defining a vgm_ahb_item object as a protected field. By adding constraints to our API fields that tie them to the fields of this field we are "exporting" the sequence item constraints to the sequence. Here's how all this would look like:

The cool thing about this approach is that it can be retrofitted to existing code because there are no modifications to the API. I favor this approach over just defining one field of type vgm_ahb_item and using that for inline constraints. Should our sequence item contain any extra fields (for direction, data, protection, etc.) it would not be clear which ones are actually used in the sequence. By explicitly defining the sequence fields we make it clear what the API is.

A working example that includes the complete code can be found at the blog's SourceForge repository here.

It should be interpreted as a conditional in thit context. The idea is that the solver chooses a value for 'burst' and then based on the value it chose it constraints 'addr[10:0]'.

The constraint is uni-directional (because given an 'addr' we can't constrain the 'burst'), but it doesn't create any cyclical dependency, just an ordering. This is described in the IEEE 1800-2012 standard in section '18.5.12 Functions in constraints'.

About

I am a Verification Engineer at Infineon Technologies, where I get the chance to work with both e and SystemVerilog.
I started the Verification Gentleman blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.