Skip to content

Commit 2890701

Browse files
committed
0.5.2
- (#77) Add support for wildcard bins. Both single and array bins are supported. Signed-off-by: Matthew Ballance <[email protected]>
1 parent 8036865 commit 2890701

File tree

10 files changed

+521
-1
lines changed

10 files changed

+521
-1
lines changed

doc/Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11

2+
## 0.5.2
3+
- (#77) Add support for wildcard bins. Both single and
4+
array bins are supported.
5+
26
## 0.5.1
37
- Adjust the debug approach to collect singles, pairs,
48
triples of failing constraints.

doc/source/coverage.rst

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,69 @@ a callable is providing the target value. Consequently, the cp_t
208208
parameter is used to specify that the value being sampled is an 8-bit
209209
unsigned integer.
210210

211+
Wildcard Bins (Single)
212+
^^^^^^^^^^^^^^^^^^^^^^
213+
A wildcard specification may be used to specify the values within
214+
single bins. The checked value may either be specified as a string
215+
that contains wildcard characters ('x', '?') or may be specified
216+
as a tuple of (value, mask).
217+
218+
When using the string form of specifying a wildcard bin, the
219+
specification string must start with "0x" (hexadecimal),
220+
"0o" (octal), or "0b" (binary).
221+
222+
Here is an example showing specification of a wildcard bin that
223+
matches any value 0x80..0x8F:
224+
225+
.. code-block:: python3
226+
227+
@vsc.covergroup
228+
class cg(object):
229+
230+
def __init__(self):
231+
self.with_sample(
232+
dict(a=vsc.bit_t(8)))
233+
234+
self.cp_a = vsc.coverpoint(self.a, bins=dict(
235+
a=vsc.wildcard_bin("0x8x")))
236+
237+
Here is the same coverpoint specification using the value/mask
238+
form:
239+
240+
.. code-block:: python3
241+
242+
@vsc.covergroup
243+
class cg(object):
244+
245+
def __init__(self):
246+
self.with_sample(
247+
dict(a=vsc.bit_t(8)))
248+
249+
self.cp_a = vsc.coverpoint(self.a, bins=dict(
250+
a=vsc.wildcard_bin((0x80,0xF0))
251+
))
252+
253+
Wildcard Bins (Array)
254+
^^^^^^^^^^^^^^^^^^^^^
255+
A wildcard specification may also be used to specify arrays of bins.
256+
In this case, the wildcard characters specify a location where all
257+
possibilities must be expanded.
258+
259+
The example below creates 16 bins for the values 0x80..0x8F:
260+
261+
.. code-block:: python3
262+
263+
@vsc.covergroup
264+
class cg(object):
265+
266+
def __init__(self):
267+
self.with_sample(
268+
dict(a=vsc.bit_t(8)))
269+
270+
self.cp_a = vsc.coverpoint(self.a, bins=dict(
271+
a=vsc.wildcard_bin_array([], "0x8x")
272+
))
273+
211274
Coverpoint Crosses
212275
------------------
213276

etc/ivpm.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

22
name=pyvsc
3-
version=0.5.1
3+
version=0.5.2
44

src/vsc/coverage.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
from vsc.model.source_info import SourceInfo
4848
from vsc.types import rangelist, bit_t, to_expr, type_base, enum_t, type_enum, \
4949
expr, field_info
50+
from vsc.impl.wildcard_bin_factory import WildcardBinFactory
51+
from vsc.model.coverpoint_bin_single_wildcard_model import CoverpointBinSingleWildcardModel
52+
from vsc.model.wildcard_binspec import WildcardBinspec
5053

5154

5255
def covergroup(T):
@@ -437,6 +440,138 @@ def build_cov_model(self, parent, name):
437440

438441
return ret
439442

443+
class wildcard_bin(object):
444+
"""Specifies a single wildcard coverage bin"""
445+
def __init__(self, *args):
446+
# Capture the declaration location of this bin
447+
self.srcinfo_decl = SourceInfo.mk()
448+
449+
self.range_l = []
450+
for a in args:
451+
if isinstance(a, tuple):
452+
# Expressed in mask form
453+
# (value, mask) where 1-bits in mask are compared and 0-bits are ignored
454+
self.range_l.append(a)
455+
elif isinstance(a, str):
456+
# Expressed in string form in either oct, hex, or bin format
457+
#
458+
value, mask = WildcardBinFactory.str2bin(a)
459+
self.range_l.append((value,mask))
460+
else:
461+
raise Exception("Unknown argument %s to wildcard bin. Expect tuple or string" % (
462+
str(a)))
463+
464+
def build_cov_model(self, parent, name):
465+
ret = CoverpointBinSingleWildcardModel(
466+
name,
467+
WildcardBinspec(self.range_l))
468+
ret.srcinfo_decl = self.srcinfo_decl
469+
470+
return ret
471+
472+
class wildcard_bin_array(object):
473+
"""Specifies an array of bins using wildcard specifications"""
474+
475+
def __init__(self, nbins, *args):
476+
"""
477+
args may be one of two formats
478+
- Single list of wildcard strings (eg "0x82x")
479+
- Single list of value/mask tuples (eg [(0x820,0xFF0)])
480+
- Single string
481+
- Single tuple
482+
"""
483+
self.nbins = nbins
484+
self.range_l = []
485+
486+
# Convert the specification into canonical form: list of tuples
487+
if len(args) == 1:
488+
if isinstance(args[0], str):
489+
value, mask = WildcardBinFactory.str2bin(args[0])
490+
self.range_l.extend(WildcardBinFactory.valmask2binlist(value, mask))
491+
elif isinstance(args[0], (list,tuple)):
492+
# value/mask
493+
if len(args[0]) != 2:
494+
raise Exception("Wildcard bin specification requires two elements (value,mask)")
495+
496+
self.range_l.extend(WildcardBinFactory.valmask2binlist(args[0][0], args[0][1]))
497+
else:
498+
raise Exception("Bin specification " + str(args[0]) + " is neither string nor value/mask")
499+
pass
500+
else:
501+
# Multiple arguments. Expect each to be a single-level string or tuple
502+
for a in args:
503+
if isinstance(a,(list,tuple)):
504+
if len(a) != 2:
505+
raise Exception("Wildcard bin range " + str(a) + " must have two elements")
506+
else:
507+
self.range_l.extend(WildcardBinFactory.valmask2binlist(a[0], a[1]))
508+
elif isinstance(a,str):
509+
value, mask = WildcardBinFactory.str2bin(a)
510+
self.range_l.extend(WildcardBinFactory.valmask2binlist(value, mask))
511+
else:
512+
raise Exception("Wildcard bin specification " + str(a) + " is neither value nor range")
513+
514+
# TODO: handle overlap
515+
self.range_l.sort(key=lambda e: e[0])
516+
517+
# Now, collapse overlaps
518+
i=0
519+
while i < len(self.range_l):
520+
if i+1 < len(self.range_l) and self.range_l[i][1]+1 >= self.range_l[i][0]:
521+
self.range_l[i][1] = self.range_l[i+1][1]
522+
self.range_l.pop(i+1)
523+
else:
524+
i += 1
525+
526+
if isinstance(nbins,list):
527+
if len(nbins) not in (0,1):
528+
raise Exception("Only 0 or 1 argument can be specified to the nbins argument")
529+
self.nbins = -1 if len(nbins) == 0 else nbins[0]
530+
else:
531+
self.nbins = int(nbins)
532+
533+
if len(args) == 0:
534+
raise Exception("No bins range specified")
535+
536+
# Capture the declaration location of this bin
537+
frame = inspect.stack()[1]
538+
self.srcinfo_decl = SourceInfo(frame.filename, frame.lineno)
539+
540+
def build_cov_model(self, parent, name):
541+
ret = None
542+
543+
# First, need to determine how many total bins
544+
# Construct a range model
545+
if self.nbins == -1:
546+
# unlimited number of bins
547+
if len(self.range_l) == 1:
548+
r = self.range_l[0]
549+
ret = CoverpointBinArrayModel(name, 0, r[0], r[1])
550+
else:
551+
idx=0
552+
ret = CoverpointBinCollectionModel(name)
553+
for r in self.range_l:
554+
if len(r) == 2:
555+
b = ret.add_bin(CoverpointBinArrayModel(name, idx, r[0], r[1]))
556+
b.srcinfo_decl = self.srcinfo_decl
557+
idx += ((r[1] - r[0]) + 1)
558+
elif len(r) == 1:
559+
b = ret.add_bin(CoverpointBinSingleValModel(name + "[" + str(idx) + "]", r[0]))
560+
b.srcinfo_decl = self.srcinfo_decl
561+
idx += 1
562+
else:
563+
raise Exception("Internal error: expect bin to have 1 or 2 elements not " +
564+
str(len(r)) + " (" +
565+
name + " " + self.srcinfo_decl.filename + ":" +
566+
str(self.srcinfo_decl.lineno) + ")")
567+
else:
568+
ret = CoverpointBinCollectionModel.mk_collection(name,
569+
RangelistModel(self.range_l), self.nbins)
570+
571+
ret.srcinfo_decl = self.srcinfo_decl
572+
573+
return ret
574+
440575
class binsof(object):
441576
# TODO: future implementation of the 'binsof' operator
442577

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
'''
2+
Created on Jul 23, 2021
3+
4+
@author: mballance
5+
'''
6+
7+
8+
class WildcardBinFactory(object):
9+
10+
@classmethod
11+
def str2bin(cls, val) -> tuple:
12+
# val is string in oct, hex, or bin
13+
14+
value = 0
15+
mask = 0
16+
17+
if val.startswith("0o") or val.startswith("0O"):
18+
# octal format
19+
for c in val[2:]:
20+
if c != '_':
21+
value <<= 3
22+
mask <<= 3
23+
if c not in ['x', 'X', '?']:
24+
mask |= 0x7
25+
value |= int(c, 8)
26+
elif val.startswith("0x") or val.startswith("0X"):
27+
for c in val[2:]:
28+
if c != '_':
29+
value <<= 4
30+
mask <<= 4
31+
if c not in ['x', 'X', '?']:
32+
mask |= 0xF
33+
value |= int(c, 16)
34+
elif val.startswith("0b") or val.startswith("0B"):
35+
for c in val[2:]:
36+
if c != '_':
37+
value <<= 1
38+
mask <<= 1
39+
if c not in ['x', 'X', '?']:
40+
mask |= 0x1
41+
value |= int(c, 2)
42+
else:
43+
raise Exception("unknown base for value %s" % str(val))
44+
45+
return (value,mask)
46+
47+
@classmethod
48+
def valmask2binlist(cls, value, mask):
49+
"""Converts value/mask representation to a list of bin specifications"""
50+
51+
n_bits = 0
52+
53+
mask_t = mask
54+
bit_i = 0
55+
56+
total_mask_bits = 0
57+
directives = []
58+
while mask_t != 0:
59+
if (mask_t & 1) == 0:
60+
# Collect this grouping
61+
group_start_bit = bit_i
62+
group_n_bits = 0
63+
while (mask_t & 1) == 0:
64+
group_n_bits += 1
65+
total_mask_bits += 1
66+
mask_t >>= 1
67+
bit_i += 1
68+
pass
69+
directives.append((group_start_bit, group_n_bits))
70+
else:
71+
mask_t >>= 1
72+
bit_i += 1
73+
74+
if total_mask_bits > 20:
75+
raise Exception("Wildcard array bins limited to 20 mask bits")
76+
77+
ranges = []
78+
for val in range(0, (1 << total_mask_bits)):
79+
80+
val_i = val
81+
val_t = (value & mask)
82+
for d in directives:
83+
val_t |= ((val_i & ((1 << d[1])-1)) << d[0])
84+
val_i >>= d[1]
85+
86+
if len(ranges) > 0 and ranges[-1][1]+1 == val_t:
87+
ranges[-1] = (ranges[-1][0], val_t)
88+
else:
89+
ranges.append((val_t, val_t))
90+
91+
return ranges
92+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'''
2+
Created on Jul 23, 2021
3+
4+
@author: mballance
5+
'''
6+
from vsc.model.coverpoint_bin_model_base import CoverpointBinModelBase
7+
from vsc.model.wildcard_binspec import WildcardBinspec
8+
9+
class CoverpointBinSingleWildcardModel(CoverpointBinModelBase):
10+
11+
def __init__(self, name, specs : WildcardBinspec):
12+
super().__init__(name)
13+
self.n_bins = 1
14+
self.wildcard_binspec = specs
15+
pass
16+
17+
def finalize(self, bin_idx_base : int) -> int:
18+
super().finalize(bin_idx_base)
19+
return 1
20+
21+
def get_bin_expr(self, bin_idx):
22+
pass
23+
24+
def get_bin_name(self, bin_idx):
25+
return self.name
26+
27+
def sample(self):
28+
val = int(self.cp.get_val())
29+
30+
# Process each value/mask pair
31+
self.hit_bin_idx = -1
32+
for s in self.wildcard_binspec.specs:
33+
if (val & s[1]) == s[0]:
34+
self.hit_bin_idx = 0
35+
self.cp.coverage_ev(self.bin_idx_base)
36+
break
37+
38+
39+
def accept(self, v):
40+
v.visit_coverpoint_bin_single_wildcard(self)
41+
42+
def equals(self, oth) -> bool:
43+
eq = isinstance(oth, CoverpointBinSingleWildcardModel)
44+
45+
if eq:
46+
eq &= self.wildcard_binspec.equals(oth.wildcard_binspec)
47+
48+
return eq
49+
50+
def clone(self):
51+
return CoverpointBinSingleWildcardModel(
52+
self.name,
53+
self.wildcard_binspec.clone())

src/vsc/model/model_visitor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from vsc.model.constraint_inline_scope_model import ConstraintInlineScopeModel
4646
from vsc.model.expr_dynamic_model import ExprDynamicModel
4747
from vsc.model.constraint_solve_order_model import ConstraintSolveOrderModel
48+
from vsc.model.coverpoint_bin_single_wildcard_model import CoverpointBinSingleWildcardModel
4849

4950

5051

@@ -248,6 +249,9 @@ def visit_coverpoint_bin_collection(self, bn : CoverpointBinCollectionModel):
248249

249250
def visit_coverpoint_bin_single_range(self, bn : CoverpointBinSingleRangeModel):
250251
pass
252+
253+
def visit_coverpoint_bin_single_wildcard(self, bn : CoverpointBinSingleWildcardModel):
254+
pass
251255

252256
def visit_coverpoint_bin_enum(self, bn : CoverpointBinEnumModel):
253257
pass

0 commit comments

Comments
 (0)