Tudor timisescu also known as the verification gentleman in verification community posted this question on twitter.
His question was, can we create transition coverage using cross between two different types of objects? He named it as heterogeneous cross.
His requirement has very useful application in CPU verification to cover transitions of different instructions. For RISC-V (and basically all other ISAs), different instructions have different formats, so you end up with cases where you get such heterogeneous transitions.
So, let’s jump into understanding the question further. I know it’s not easy to understand it on first impression. So let’s do bit of deep dive into question. Followed by that we will take a look in to one of the proposed solution and scalable automation using the code generation approach.
Can we do heterogeneous cross coverage in SystemVerilog?
Partial screenshot of the question on twitter.
Tudor clarifies the question in his own words.
Heterogeneous cross coverage is cross between two different object types.
Let me clarify by what I mean with heterogeneous. First, I’m trying to model some more involved form of transition coverage. I imagine the best way to do this is using cross coverage between the current operation and the previous operation.
Assuming you only have one category of operations, O, each with a set of properties P0, P1, P2, … it’s pretty easy to write this transition coverage. Let the tilde (‘) denote previous. The cross would be between the values of P0, P1, P2, … and P0′, P1′, P2′, …
If you have two categories of operations, Oa and Ob, each with different sets of properties: Pa0, Pa1, …, Pam for Oa and Pb0, Pb1, …, Pbn (with m and n possibly different), the cross gets a bit more involved.
If the current operation is of type Oa and the previous is of type Oa, then you want to cover like in the case where all operations are the same (i.e. Pa0, Pa1, …, Pa0′, Pa1′). This also goes for when both are of type Ob.
If the current operation is of type Oa and the previous is of type Ob, then what you want to cross is something like Pa0, Pa1, Pa2, …, Pb0′, Pb1′, Pb2’, … The complementary case with the operation types switched is analogous to this one.
I don’t see any way of writing this in #SystemVerilog without having 4 distinct covergroups (one for each type transition).
Imagine you add a third operation type, Oc, and suddenly you need 9 covergroups you need to write.
The more you add, the more code you need and it’s all pretty much boilerplate.
The only thing that the test bench writer needs to provide are definitions for the cross of all properties of each operation. Since it’s not possible to define covergroup items (coverpoints and crosses) in such a way that they can be reused inside multiple covergroup definitions, the only solution I see is using macros.
Code generation would be a more robust solution, but that might be more difficult to set up.
He was kind enough to provide the solution for it as well. So what was he looking for? He was looking for, is there any easier and scalable ways to solve it?
Following are the two different data types that we want to cross.
When you create all 4 possible combinations of transition crosses, it would look as following:
I thought we could follow the precedence of scientific community and refer the heterogeneous cross as “Tudor cross” for formulating the problem and defining the solution.
Okay, before we invest our valuable time understanding automation are there any real life use cases?
Tudor was facing this real problem for project he worked on related to critical pieces of security. For confidentiality reasons he could not provide any more details about it. He was kind enough to share another example where this type of problem would be faced again and hence the solution would be useful.
In Tudor’s own words, an example from the top of my head (completely unrelated to the one I was working on) where this might be useful is if you have to cover transitions of different instructions. For RISC-V (and basically all other ISAs), different instructions have different formats, so you end up with cases where you get such heterogeneous transitions.
The same CPU will be executing all of those instructions and you can get into situations that the previous instruction busted something that will cause the current instruction to lock up, which is why you want to at least test all transitions.
One step even further is if you also add the state of the CPU to the cross. Different parts of the state are relevant to different instructions. It could be that transition a -> b is fine in state Sa0, but is buggy in state Sa1.
The CPU example is maybe even better than my concrete use case where only a history of 1 is needed. A fancy CPU has a deep pipeline and it may well be the case that the depth of the pipeline should be the length of the history for which you collect these transitions. Even for lengths of 2 it becomes a huge problem, too laborious to write by hand.
Here we apply two concepts:
High-level modeling of operation is done in very limited way to keep the solution simple. We have done it for USB power delivery protocol layer over here, which is much more involved than this problem.
Solution can easily scale for:
Yes, you can write it. It’s not such a big effort for specific problem. But if you are embarking in this direction, you will need more and more capabilities. That’s where the well thought out and tested library will save you lot of time allowing you to focus on the real problem.
Steps involved in automation are following:
If you are interested in programmable number of stages solution, just drop me an email: anand@verifsudha.com
Following code shows both the python input and generated SystemVerilog code output.
#!/home/utils/Python-2.7.5/bin/python
########################################################################################
#
# Copyright (C) VerifSudha Technologies Pvt. Ltd. - All Rights Reserved 2016-18
# Unauthorized copying of this file, via any medium is strictly prohibited
# Proprietary and confidential
# Written by Anand Shirahatti ,
#
# Title: Heterogeneous transition coverage using cross
# Description: @TudorTimi posed this question on twitter
# Let me clarify by what I mean with heterogeneous. First, I'm trying to model some more
# involved form of transition coverage. I imagine the best way to do this is using cross
# coverage between the current operation and the previous operation.
#
# Assuming you only have one category of operations, O, each with a set of properties
# P0, P1, P2, ..., it's pretty easy to write this transition coverage. Let the tilde (')
# denote previous. The cross would be between the values of P0, P1, P2, ... and P0', P1', P2', ...
#
# If you have two categories of operations, Oa and Ob, each with different sets of properties:
# Pa0, Pa1, ..., Pam for Oa and Pb0, Pb1, ..., Pbn (with m and n possibly different), the cross
# gets a bit more involved.
#
# If the current operation is of type Oa and the previous is of type Oa, then you want to cover
# like in the case where all operations are the same (i.e. Pa0, Pa1, ..., Pa0', Pa1'). This also
# goes for when both are of type Ob.
#
#
# If the current operation is of type Oa and the previous is of type Ob, then what you want to cross
# is something like Pa0, Pa1, Pa2, ..., Pb0', Pb1', Pb2', ... The complementary case with the operation
# types switched is analogous to this one.
#
# I don't see any way of writing this in #SystemVerilog without having 4 distinct covergroups
# (one for each type transition).
#
# Imagine you add a third operation type, Oc, and suddenly you need 9 covergroups you need to write.
#
# The more you add, the more code you need and it's all pretty much boilerplate. The only thing that the
# TB writer needs to provide are definitions for the cross of all properties of each operation.
# Since it's not possible to define covergroup items (coverpoints and crosses) in such a way that they can be
# reused inside multiple covergroup definitions, the only solution I see is using macros.
# Code generation would be a more robust solution, but that might be more difficult to set up.
#
# Category: REALLIFE
# Status: PUBLISH
#
########################################################################################
from curiosity_user_common_utils import *
import itertools
class curiosity_heterogeneous_cross(curiosity_user_lib):
def set_dut_spec(self, **dut_spec_dict):
self.dut_spec = copy.deepcopy(dut_spec_dict)
# Set default value for all entries
self.set_default_value_wbv_entry(
WBV_CLOCK = self.dut_spec['__DUT_CLK__'],
WBV_RESET = self.dut_spec['__DUT_RSTN__'],
WBV_USR_META_VAR2VAL = self.dut_spec,
WBV_LOOP_LIST = [''],)
# Helper routine that creates the cross from properties of operations
def add_cross_for_property(self, wbv_entry, wbv_name_str, prop_list, prefix_str, cross_name):
with add_cross_entry(wbv_entry,
CROSS_NAME = cross_name,
)as cross_entry:
for prop_entry in prop_list:
cp_name_str = wbv_name_str + '.cp_' + prefix_str + '_' + prop_entry
print cp_name_str
cross_entry.add_coverpoint(
COVERPOINT_NAME = cp_name_str)
# Helper routine to create cross of crosses from list
def add_cross_of_cross(self, wbv_entry, wbv_name_str, cross_list, cross_name):
with add_cross_entry(wbv_entry,
CROSS_NAME = cross_name,
)as cross_entry:
for cross_name_str in cross_list:
cr_name_str = wbv_name_str + '.' + cross_name_str
cross_entry.add_cross(
CROSS_NAME = cr_name_str)
# Helper routine that adds the properties of operations to create a coverpoint
def add_coverpoints_for_property(self, wbv_entry, property_list, prefix_str):
for prop_name_str in property_list:
si_name_str = prefix_str + '_' + prop_name_str
print si_name_str
obj_prop_str = prefix_str + '.' + prop_name_str
wbv_entry.add_wbv_si_entry(
SI_NAME = si_name_str,
SIGNAL_OR_EXPR = obj_prop_str)
# Creates a covergroup of transitions of specified depth defined the number of elements in operations_tuple
# For each element in the argument operations_tuple the operations_dict provides the information about its
# object data type and name of the property. Property data type and values can be added in needed
# Using this information it creates the covergroup, coverpoint for each of properties and then
# transition of defined depth using cross of all individual stage coverpoints
def add_covergroup_operation_transitions(self, operations_tuple, operations_dict):
# Create the CG name like a_after_a or a_after_b of programmable depth
# Create the object to be passed as arguments to CG
sample_arguments = ''
cg_name_str = ''
operation_prev = operations_tuple[0] # Ex: 0c
operation_curr = operations_tuple[1] # Ex: 0a
data_type_prev = operations_dict[operation_prev]['DATA_TYPE'] + ' ' + 'prev'
data_type_curr = operations_dict[operation_curr]['DATA_TYPE'] + ' ' + 'curr'
# Creates ex: operation_c prev , operation_a curr
sample_arguments = data_type_prev + ' ' + 'prev' + ' , ' + data_type_curr + ' ' + 'curr'
sample_event = 'with function sample' + '(' + sample_arguments + ')'
# Creates ex: Oc_after_Oa
cg_name_str = operation_prev + '_after_' + operation_curr
# Curiosity Library object: Create the CG, coverpoints and crosses
with add_wbv_entry(self,
WBV_NAME = cg_name_str,
WBV_CLOCK = sample_event,
WBV_TYPE = 'WBV_SIGNAL_VALUE',
WBV_FUNCTIONAL_COV = '1',
WBV_DESCRIPTION = 'Multistage transition coverage') as wbv_entry:
# Add properties of prev object type as coverpoints
self.add_coverpoints_for_property(wbv_entry, operations_dict[operation_prev]['PROPERTY_LIST'], 'prev')
# Add properties of curr object type as coverpoints
self.add_coverpoints_for_property(wbv_entry, operations_dict[operation_curr]['PROPERTY_LIST'], 'curr')
# Add a cross from all the coverpoints of prev object
self.add_cross_for_property(wbv_entry, cg_name_str, operations_dict[operation_prev]['PROPERTY_LIST'], 'prev', 'prev_cross')
# Add a cross from all the coverpoints of curr object
self.add_cross_for_property(wbv_entry, cg_name_str, operations_dict[operation_curr]['PROPERTY_LIST'], 'curr', 'curr_cross')
# Add the cross of curr properties and prev properties
self.add_cross_of_cross(wbv_entry, cg_name_str, ['prev_cross', 'curr_cross'], 'all_cur_after_all_prev')
# This is the top level function where all the top level actions takes place
def gen_coverage(self):
# Number of repeated permutations (cartesian product) of operations required
transition_depth_int = 2
# High level specification of operations, their data type and property names
# Values of properties or data types of properties can be added here if required in future
operations_dict = {
'Oa' : {
'DATA_TYPE' : 'operation_a',
'PROPERTY_LIST' : ['prop_0_a', 'prop_1_a'],
},
'Ob' : {
'DATA_TYPE' : 'operation_b',
'PROPERTY_LIST' : ['prop_0_b', 'prop_1_b', 'prop_2_b'],
},
'Oc' : {
'DATA_TYPE' : 'operation_c',
'PROPERTY_LIST' : ['prop_0_c', 'prop_1_c', 'prop_2_c', 'prop_3_c'],
},
}
# Get the names of all operations
operations_list = operations_dict.keys()
# permutation with repetitions for among all operations types for defined depth of transitions called as stages
operations_cartesian_product = list(itertools.product(operations_list, repeat=transition_depth_int))
# For each combinations [( Oa, Oa), (Ob, Ob), (Oa, Ob), (Ob, Oa)] crate a covregroup with
# with transition crosses
for operation_combo_tuple in operations_cartesian_product:
self.add_covergroup_operation_transitions(operation_combo_tuple, operations_dict)
def main():
curiosity_heterogeneous_cross_obj = curiosity_heterogeneous_cross() # Create the user lib for easing the wb_input entries creation
# Setup DUT attributes
curiosity_heterogeneous_cross_obj.set_dut_spec(
__DUT_CLK__ = 'dut.clk',
__DUT_RSTN__ = '!dut.resetn',
)
curiosity_heterogeneous_cross_obj.gen_coverage()
## Call the main ##
if __name__ == '__main__':
main()
//File Name: curiosity_wb_fcov_cg.sv
//##################################################################################################################
//
// Copyright (C) VerifSudha Technologies Pvt. Ltd. - All Rights Reserved 2018
// Unauthorized copying of this file, via any medium is strictly prohibited
// Proprietary and confidential
// Written by curiosity ,
//
// Generated file - Donot edit manually. It will be overwritten !
//
// File generated with the command:
// ./curiosity -ips ../../examples/curiosity_ex_heterogeneous_prev_curr.py
//
//##################################################################################################################
covergroup Oc_after_Oc with function sample(operation_c prev prev , operation_c curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oc_after_Oc";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_c : coverpoint prev.prop_0_c iff (!dut.resetn);
cp_prev_prop_1_c : coverpoint prev.prop_1_c iff (!dut.resetn);
cp_prev_prop_2_c : coverpoint prev.prop_2_c iff (!dut.resetn);
cp_prev_prop_3_c : coverpoint prev.prop_3_c iff (!dut.resetn);
cp_curr_prop_0_c : coverpoint curr.prop_0_c iff (!dut.resetn);
cp_curr_prop_1_c : coverpoint curr.prop_1_c iff (!dut.resetn);
cp_curr_prop_2_c : coverpoint curr.prop_2_c iff (!dut.resetn);
cp_curr_prop_3_c : coverpoint curr.prop_3_c iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_c, cp_prev_prop_1_c, cp_prev_prop_2_c, cp_prev_prop_3_c;
curr_cross : cross cp_curr_prop_0_c, cp_curr_prop_1_c, cp_curr_prop_2_c, cp_curr_prop_3_c;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oc_after_Oc
covergroup Oc_after_Ob with function sample(operation_c prev prev , operation_b curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oc_after_Ob";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_c : coverpoint prev.prop_0_c iff (!dut.resetn);
cp_prev_prop_1_c : coverpoint prev.prop_1_c iff (!dut.resetn);
cp_prev_prop_2_c : coverpoint prev.prop_2_c iff (!dut.resetn);
cp_prev_prop_3_c : coverpoint prev.prop_3_c iff (!dut.resetn);
cp_curr_prop_0_b : coverpoint curr.prop_0_b iff (!dut.resetn);
cp_curr_prop_1_b : coverpoint curr.prop_1_b iff (!dut.resetn);
cp_curr_prop_2_b : coverpoint curr.prop_2_b iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_c, cp_prev_prop_1_c, cp_prev_prop_2_c, cp_prev_prop_3_c;
curr_cross : cross cp_curr_prop_0_b, cp_curr_prop_1_b, cp_curr_prop_2_b;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oc_after_Ob
covergroup Oc_after_Oa with function sample(operation_c prev prev , operation_a curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oc_after_Oa";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_c : coverpoint prev.prop_0_c iff (!dut.resetn);
cp_prev_prop_1_c : coverpoint prev.prop_1_c iff (!dut.resetn);
cp_prev_prop_2_c : coverpoint prev.prop_2_c iff (!dut.resetn);
cp_prev_prop_3_c : coverpoint prev.prop_3_c iff (!dut.resetn);
cp_curr_prop_0_a : coverpoint curr.prop_0_a iff (!dut.resetn);
cp_curr_prop_1_a : coverpoint curr.prop_1_a iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_c, cp_prev_prop_1_c, cp_prev_prop_2_c, cp_prev_prop_3_c;
curr_cross : cross cp_curr_prop_0_a, cp_curr_prop_1_a;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oc_after_Oa
covergroup Ob_after_Oc with function sample(operation_b prev prev , operation_c curr curr);
option.comment = "Multistage transition coverage";
option.name = "Ob_after_Oc";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_b : coverpoint prev.prop_0_b iff (!dut.resetn);
cp_prev_prop_1_b : coverpoint prev.prop_1_b iff (!dut.resetn);
cp_prev_prop_2_b : coverpoint prev.prop_2_b iff (!dut.resetn);
cp_curr_prop_0_c : coverpoint curr.prop_0_c iff (!dut.resetn);
cp_curr_prop_1_c : coverpoint curr.prop_1_c iff (!dut.resetn);
cp_curr_prop_2_c : coverpoint curr.prop_2_c iff (!dut.resetn);
cp_curr_prop_3_c : coverpoint curr.prop_3_c iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_b, cp_prev_prop_1_b, cp_prev_prop_2_b;
curr_cross : cross cp_curr_prop_0_c, cp_curr_prop_1_c, cp_curr_prop_2_c, cp_curr_prop_3_c;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Ob_after_Oc
covergroup Ob_after_Ob with function sample(operation_b prev prev , operation_b curr curr);
option.comment = "Multistage transition coverage";
option.name = "Ob_after_Ob";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_b : coverpoint prev.prop_0_b iff (!dut.resetn);
cp_prev_prop_1_b : coverpoint prev.prop_1_b iff (!dut.resetn);
cp_prev_prop_2_b : coverpoint prev.prop_2_b iff (!dut.resetn);
cp_curr_prop_0_b : coverpoint curr.prop_0_b iff (!dut.resetn);
cp_curr_prop_1_b : coverpoint curr.prop_1_b iff (!dut.resetn);
cp_curr_prop_2_b : coverpoint curr.prop_2_b iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_b, cp_prev_prop_1_b, cp_prev_prop_2_b;
curr_cross : cross cp_curr_prop_0_b, cp_curr_prop_1_b, cp_curr_prop_2_b;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Ob_after_Ob
covergroup Ob_after_Oa with function sample(operation_b prev prev , operation_a curr curr);
option.comment = "Multistage transition coverage";
option.name = "Ob_after_Oa";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_b : coverpoint prev.prop_0_b iff (!dut.resetn);
cp_prev_prop_1_b : coverpoint prev.prop_1_b iff (!dut.resetn);
cp_prev_prop_2_b : coverpoint prev.prop_2_b iff (!dut.resetn);
cp_curr_prop_0_a : coverpoint curr.prop_0_a iff (!dut.resetn);
cp_curr_prop_1_a : coverpoint curr.prop_1_a iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_b, cp_prev_prop_1_b, cp_prev_prop_2_b;
curr_cross : cross cp_curr_prop_0_a, cp_curr_prop_1_a;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Ob_after_Oa
covergroup Oa_after_Oc with function sample(operation_a prev prev , operation_c curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oa_after_Oc";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_a : coverpoint prev.prop_0_a iff (!dut.resetn);
cp_prev_prop_1_a : coverpoint prev.prop_1_a iff (!dut.resetn);
cp_curr_prop_0_c : coverpoint curr.prop_0_c iff (!dut.resetn);
cp_curr_prop_1_c : coverpoint curr.prop_1_c iff (!dut.resetn);
cp_curr_prop_2_c : coverpoint curr.prop_2_c iff (!dut.resetn);
cp_curr_prop_3_c : coverpoint curr.prop_3_c iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_a, cp_prev_prop_1_a;
curr_cross : cross cp_curr_prop_0_c, cp_curr_prop_1_c, cp_curr_prop_2_c, cp_curr_prop_3_c;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oa_after_Oc
covergroup Oa_after_Ob with function sample(operation_a prev prev , operation_b curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oa_after_Ob";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_a : coverpoint prev.prop_0_a iff (!dut.resetn);
cp_prev_prop_1_a : coverpoint prev.prop_1_a iff (!dut.resetn);
cp_curr_prop_0_b : coverpoint curr.prop_0_b iff (!dut.resetn);
cp_curr_prop_1_b : coverpoint curr.prop_1_b iff (!dut.resetn);
cp_curr_prop_2_b : coverpoint curr.prop_2_b iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_a, cp_prev_prop_1_a;
curr_cross : cross cp_curr_prop_0_b, cp_curr_prop_1_b, cp_curr_prop_2_b;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oa_after_Ob
covergroup Oa_after_Oa with function sample(operation_a prev prev , operation_a curr curr);
option.comment = "Multistage transition coverage";
option.name = "Oa_after_Oa";
option.per_instance = 1;
// Cover points
cp_prev_prop_0_a : coverpoint prev.prop_0_a iff (!dut.resetn);
cp_prev_prop_1_a : coverpoint prev.prop_1_a iff (!dut.resetn);
cp_curr_prop_0_a : coverpoint curr.prop_0_a iff (!dut.resetn);
cp_curr_prop_1_a : coverpoint curr.prop_1_a iff (!dut.resetn);
// Cross coverage
prev_cross : cross cp_prev_prop_0_a, cp_prev_prop_1_a;
curr_cross : cross cp_curr_prop_0_a, cp_curr_prop_1_a;
all_cur_after_all_prev : cross prev_cross, curr_cross;
endgroup : Oa_after_Oa