Skip to content
Merged

0.9.5 #238

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## 0.9.5
- (#237) - Add support for specifying cross-bins to ignore

## 0.9.4
- (#233) - Propagate docstring and modulename across `randobj` inheritance
Expand Down
35 changes: 35 additions & 0 deletions doc/source/coverage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,41 @@ compose the coverpoint cross.

self.cp1X2 = vsc.cross([self.cp1, self.cp2])

Coverpoint Cross Ignore Bins
............................

Coverpoint cross-bins to ignore may be specified as follows.

.. code-block:: python3
def filter(a, b):
v_set = (1, 2, 4, 8)
for i,v in enumerate(v_set):
b_set = v_set[i+1:]
if len(b_set) and a.intersect(v) and b.intersect(b_set):
print("Intersect: a: %s ; b: %s" % (str(a.range), str(b.range)))
return True
return False


@vsc.covergroup
class cg_t(object):
def __init__(self):
self.with_sample(dict(
a=vsc.int8_t(),
b=vsc.int8_t()))
self.cp_a = vsc.coverpoint(self.a,
bins=dict(rng=vsc.bin_array([], 1, 2, 4, 8)))
self.cp_b = vsc.coverpoint(self.b,
bins=dict(rng=vsc.bin_array([], 1, 2, 4, 8)))
self.cr = vsc.cross([self.cp_a, self.cp_b], ignore_bins=dict(b1=filter))

The `ignore_bins` argument must be a dictionary of bin-name and filter-method.
The filter function is invoked with a bin specification for each coverpoint
in the cross-point. The function returns `True` if the specified bin
combination should be ignored and `False`` otherwise. In this case,
bins where (b>a) are ignored.


Specifying Coverpoint Sampling Conditions
-----------------------------------------
A sampling condition can be specified on both coverpoints and coverpoint
Expand Down
7 changes: 5 additions & 2 deletions src/vsc/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,8 @@ def __init__(self,
bins=None,
options=None,
name=None,
iff=None):
iff=None,
ignore_bins=None):
for t in target_l:
if not isinstance(t, coverpoint):
raise Exception("Cross target \"" + str(t) + "\" is not a coverpoint")
Expand All @@ -912,6 +913,7 @@ def __init__(self,

self.iff_f = None
self.iff = None
self.ignore_bins = ignore_bins

if iff is not None:
with expr_mode():
Expand Down Expand Up @@ -946,7 +948,8 @@ def build_cov_model(self, parent, name):
ret = CoverpointCrossModel(
name if self.name is None else self.name,
options,
iff)
iff,
self.ignore_bins)

ret.srcinfo_decl = self.srcinfo_decl

Expand Down
2 changes: 1 addition & 1 deletion src/vsc/model/coverpoint_bin_array_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def dump(self, ind=""):
pass

def get_bin_range(self, bin_idx):
print("get_bin_range: " + str(bin_idx))
return (self.low+bin_idx,)

def get_n_bins(self):
return (self.high-self.low+1)
Expand Down
5 changes: 2 additions & 3 deletions src/vsc/model/coverpoint_bin_collection_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,14 @@ def sample(self):
return self.hit_bin_idx

def get_bin_range(self, idx):
print("get_bin_range: " + str(idx))
b = None
for i in range(len(self.bin_l)):
b = self.bin_l[i]
if b.get_n_bins() > idx:
break;
idx -= b.get_n_bins()
return b.get_bin_range(idx)
ret = b.get_bin_range(idx)
return ret

def dump(self, ind=""):
print(ind + "Bins " + self.name)
Expand Down
3 changes: 3 additions & 0 deletions src/vsc/model/coverpoint_bin_enum_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ def finalize(self, bin_idx_base:int)->int:

def get_bin_name(self, bin_idx):
return self.name

def get_bin_range(self, bin_idx):
return (self.target_val,)

def sample(self):
# Query value from the actual coverpoint or expression
Expand Down
3 changes: 3 additions & 0 deletions src/vsc/model/coverpoint_bin_model_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def get_bin_expr(self, idx):
def get_bin_name(self, bin_idx):
raise NotImplementedError()

def get_bin_range(self, bin_idx):
raise NotImplementedError()

def sample(self):
raise NotImplementedError("sample not implemented for " + str(type(self)))

Expand Down
3 changes: 3 additions & 0 deletions src/vsc/model/coverpoint_bin_single_bag_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def get_bin_expr(self, bin_idx):

def get_bin_name(self, bin_idx):
return self.name

def get_bin_range(self, bin_idx):
return self.binspec

def sample(self):
# Query value from the actual coverpoint or expression
Expand Down
3 changes: 3 additions & 0 deletions src/vsc/model/coverpoint_bin_single_range_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def get_bin_expr(self, bin_idx):

def get_bin_name(self, bin_idx):
return self.name

def get_bin_range(self, bin_idx):
return ((self.target_val_low, self.target_val_high),)

def sample(self):
val = int(self.cp.get_val())
Expand Down
5 changes: 2 additions & 3 deletions src/vsc/model/coverpoint_bin_single_val_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get_bin_expr(self, bin_idx):

def get_bin_name(self, bin_idx):
return self.name

def sample(self):
val = self.cp.get_val()
if val == self.target_val:
Expand All @@ -48,8 +48,7 @@ def sample(self):
return self.hit_bin_idx

def get_bin_range(self, idx):
print("get_bin_range: " + str(idx))
return RangelistModel([self.target_val])
return (self.target_val,)

def accept(self, v):
v.visit_coverpoint_bin_single(self)
Expand Down
79 changes: 74 additions & 5 deletions src/vsc/model/coverpoint_cross_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
#
# @author: ballance


import dataclasses as dc
import random
from typing import Set, Tuple, List

from vsc.model.coveritem_base import CoverItemBase
from vsc.model.coverpoint_model import CoverpointModel
from vsc.model.expr_model import ExprModel
from vsc.model.rand_if import RandIF
from vsc.model.expr_bin_model import ExprBinModel
Expand All @@ -32,21 +33,24 @@

class CoverpointCrossModel(CoverItemBase):

def __init__(self, name, options, iff=None):
def __init__(self, name, options, iff=None, ignore_bins=None):
super().__init__()
self.parent = None
self.name = name
self.iff = iff
self.iff_val_cache = True
self.iff_val_cache_valid = False
self.coverpoint_model_l = []
self.ignore_bins = ignore_bins
self.coverpoint_model_l : List[CoverpointModel]= []
self.finalized = False
self.n_bins = 0
self.n_ignore = 0

# Need to map (tuple)->bin_idx (for coverage recording)
# Need to map bin_idx->(tuple) (for constraint driving)
# Need to track unhit bin indexes
self.hit_l : List[int] = []
self.ignore_l : List[int] = []
self.tuple2idx_m : Dict[Tuple,int] = {}
self.idx2tuple_m : Dict[int,Tuple] = {}
self.unhit_s : Set[Tuple] = set()
Expand Down Expand Up @@ -99,6 +103,12 @@ def select_unhit_bin(self, r:RandIF)->int:

def get_bin_hits(self, bin_idx):
return self.hit_l[bin_idx]

def get_bin_valid(self, bin_idx):
ignore_i = int(bin_idx/32)
ignore_o = int(bin_idx % 32)
valid = (self.ignore_l[ignore_i] & (1 << ignore_o)) == 0
return valid

def get_bin_name(self, bin_idx)->str:
t = self.idx2tuple_m[bin_idx]
Expand All @@ -119,6 +129,11 @@ def finalize(self):
self._build_hit_map(0, [])

self.hit_l = [0]*self.n_bins

self.ignore_l = [0]*int((self.n_bins-1)/32 + 1)
bin_l = []
self._build_ignore_map(0, [], bin_l, 0)

self.finalized = True

def accept(self, v):
Expand All @@ -130,17 +145,71 @@ def _build_hit_map(self, i, key_m):

if i+1 >= len(self.coverpoint_model_l):
key = tuple(key_m)

# Reached the bottom of the list
# print("Tuple: " + str(key))
self.tuple2idx_m[key] = self.n_bins
self.idx2tuple_m[self.n_bins] = key
self.unhit_s.add(self.n_bins)
self.n_bins += 1
else:
self._build_hit_map(i+1, key_m)

key_m.pop()


def _build_ignore_map(self, i, key_m, bin_l, n_bins) -> int:
@dc.dataclass
class bin_info(object):
name : str
idx : int
range : Tuple

def intersect(self, val):
if not hasattr(val, "__iter__"):
val = (val,)
for v in val:
for r in self.range:
if type(r) == tuple:
if (v >= r[0][0] and v <= r[0][1]):
return True
else:
if (v == r):
return True
return False

# Bin needs: name,

for bin_i in range(self.coverpoint_model_l[i].get_n_bins()):
key_m.append(bin_i)
bin_l.append(bin_info(
self.coverpoint_model_l[i].get_bin_name(bin_i),
bin_i,
self.coverpoint_model_l[i].get_bin_range(bin_i)))

if i+1 >= len(self.coverpoint_model_l):
# Reached the bottom of the list
key = tuple(key_m)

ignore_i = int(n_bins/32)
ignore_o = int(n_bins%32)

ignore = False
if self.ignore_bins is not None:
for name,func in self.ignore_bins.items():
if func(*bin_l):
self.ignore_l[ignore_i] |= (1 << ignore_o)
ignore = True
if not ignore:
self.unhit_s.add(n_bins)
else:
self.n_ignore += 1
n_bins += 1
else:
n_bins = self._build_ignore_map(i+1, key_m, bin_l, n_bins)

key_m.pop()
bin_l.pop()
return n_bins

def sample(self):
if not self.finalized:
raise Exception("Cross sampled before finalization")
Expand Down
12 changes: 6 additions & 6 deletions src/vsc/model/coverpoint_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,6 @@ def select_unhit_bin(self, r:RandIF)->int:
else:
return -1

def get_bin_range(self, bin_idx) -> RangelistModel:
b,off = self._get_target_bin(bin_idx)

return b.get_bin_range(off)
pass

def coverage_ev(self, bin_idx, bin_type):
"""Called by a bin to signal that an uncovered bin has been covered"""
self.coverage_calc_valid = False
Expand Down Expand Up @@ -251,6 +245,7 @@ def get_n_hit_bins(self):
def get_bin_hits(self, bin_idx):
return self.hit_l[bin_idx]


def get_ignore_bin_hits(self, bin_idx):
return self.hit_ignore_l[bin_idx]

Expand All @@ -260,6 +255,11 @@ def get_illegal_bin_hits(self, bin_idx):
def get_bin_name(self, bin_idx)->str:
b,idx = self._get_target_bin(bin_idx)
return b.get_bin_name(idx)

def get_bin_range(self, bin_idx):
b,idx = self._get_target_bin(bin_idx)
ret = b.get_bin_range(idx)
return ret

def get_ignore_bin_name(self, bin_idx)->str:
b,idx = self._get_target_ignore_bin(bin_idx)
Expand Down
15 changes: 8 additions & 7 deletions src/vsc/visitors/coverage_save_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,14 @@ def visit_coverpoint_cross(self, cr:CoverpointCrossModel):

for bi in range(cr.get_n_bins()):
decl_location = None
bn_name = cr.get_bin_name(bi)
cr_bin = cr_scope.createBin(
bn_name,
decl_location,
at_least,
cr.get_bin_hits(bi),
bn_name)
if cr.get_bin_valid(bi):
bn_name = cr.get_bin_name(bi)
cr_bin = cr_scope.createBin(
bn_name,
decl_location,
at_least,
cr.get_bin_hits(bi),
bn_name)

def get_cg_instname(self, cg : CovergroupModel)->str:
iname = None
Expand Down
2 changes: 1 addition & 1 deletion ve/unit/test_coverage_driven_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class TestCoverageDrivenConstraints(VscTestCase):

def test_smoke(self):
def disabled_test_smoke(self):

class my_r(RandIF):
"""Defines operations to be implemented by a random generator"""
Expand Down
Loading
Loading