Skip to content

Commit 40aa1d6

Browse files
authored
Merge pull request #252 from fvutils/copilot/fix-bin-array-duplicate-values
Fix bin_array to retain duplicate values per IEEE 1800-2023
2 parents b6df483 + 856c955 commit 40aa1d6

File tree

3 files changed

+220
-1
lines changed

3 files changed

+220
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,4 @@ junit/
110110

111111
.venv/
112112
.venv.bld
113+
cov.xml

src/vsc/coverage.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,11 @@ def __init__(self, nbins, *args):
435435
if len(args) == 0:
436436
raise Exception("No bins range specified")
437437

438-
self.ranges.compact()
438+
# NOTE: Do NOT call self.ranges.compact() here!
439+
# According to IEEE 1800-2023 (page 585):
440+
# "Duplicate values are retained; thus the same value can be assigned to multiple bins"
441+
# Calling compact() would merge overlapping ranges and remove duplicates,
442+
# which is incorrect for bin distribution.
439443

440444
# Capture the declaration location of this bin
441445
frame = inspect.stack()[1]
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
'''
2+
Created on 2024
3+
4+
@author: copilot
5+
6+
Test for bin_array duplicate value handling according to IEEE 1800-2023
7+
'''
8+
import vsc
9+
from vsc_test_case import VscTestCase
10+
11+
class TestBinArrayDuplicates(VscTestCase):
12+
13+
def test_bin_array_with_duplicates_from_range(self):
14+
"""Test that duplicate values from a range are properly retained and distributed.
15+
16+
According to IEEE 1800-2023 (page 585):
17+
"Duplicate values are retained; thus the same value can be assigned to multiple bins"
18+
19+
bins fixed [4] = {[1:10], 1, 4, 7} should distribute 13 values as:
20+
- Bin 0: 1, 2, 3
21+
- Bin 1: 4, 5, 6
22+
- Bin 2: 7, 8, 9
23+
- Bin 3: 10, 1, 4, 7 (including the duplicates)
24+
25+
Note: Values 1, 4, and 7 will hit MULTIPLE bins when sampled (both their range bin
26+
and bin 3), which is the correct behavior per the standard.
27+
"""
28+
29+
@vsc.covergroup
30+
class my_cg(object):
31+
def __init__(self):
32+
self.with_sample(dict(a=vsc.uint8_t()))
33+
self.a_cp = vsc.coverpoint(
34+
self.a, bins=dict(
35+
fixed=vsc.bin_array([4], [1, 10], 1, 4, 7)
36+
))
37+
38+
cg = my_cg()
39+
cg_model = cg.get_model()
40+
cp_model = cg_model.coverpoint_l[0]
41+
42+
# Should have 4 bins total (distributed from 13 values)
43+
self.assertEqual(cp_model.get_n_bins(), 4)
44+
45+
# Verify bin structure by checking ranges
46+
bin_ranges = []
47+
for idx in range(cp_model.get_n_bins()):
48+
bin_range = cp_model.get_bin_range(idx)
49+
bin_ranges.append(bin_range)
50+
51+
# Value 1 should hit multiple bins (bin 0 from range and bin 3 from duplicate)
52+
cg.sample(1)
53+
self.assertGreater(cp_model.get_bin_hits(0), 0, "Value 1 should hit bin 0")
54+
self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 1 should also hit bin 3 (duplicate)")
55+
56+
# Value 4 should hit multiple bins
57+
cg.sample(4)
58+
self.assertGreater(cp_model.get_bin_hits(1), 0, "Value 4 should hit bin 1")
59+
self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 4 should also hit bin 3 (duplicate)")
60+
61+
# Value 7 should hit multiple bins
62+
cg.sample(7)
63+
self.assertGreater(cp_model.get_bin_hits(2), 0, "Value 7 should hit bin 2")
64+
self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 7 should also hit bin 3 (duplicate)")
65+
66+
# Value 10 should only hit bin 3
67+
cg.sample(10)
68+
self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 10 should hit bin 3")
69+
70+
# Value 2 should only hit bin 0 (no duplicate)
71+
cg.sample(2)
72+
hits_before = cp_model.get_bin_hits(3)
73+
cg.sample(2)
74+
hits_after = cp_model.get_bin_hits(3)
75+
# Bin 3 should not get additional hits from value 2
76+
self.assertEqual(hits_before, hits_after, "Value 2 should not hit bin 3")
77+
78+
def test_bin_array_with_separate_values(self):
79+
"""Test that separate values including duplicates are properly distributed.
80+
81+
bins fixed [4] = {[4:12], 1, 2, 8} should distribute values as:
82+
- Bin 0: 4, 5, 6
83+
- Bin 1: 7, 8, 9
84+
- Bin 2: 10, 11, 12
85+
- Bin 3: 1, 2, 8 (separate values, 8 is also in bin 1)
86+
"""
87+
88+
@vsc.covergroup
89+
class my_cg(object):
90+
def __init__(self):
91+
self.with_sample(dict(a=vsc.uint8_t()))
92+
self.a_cp = vsc.coverpoint(
93+
self.a, bins=dict(
94+
fixed=vsc.bin_array([4], [4, 12], 1, 2, 8)
95+
))
96+
97+
cg = my_cg()
98+
cg_model = cg.get_model()
99+
cp_model = cg_model.coverpoint_l[0]
100+
101+
# Should have 4 bins
102+
self.assertEqual(cp_model.get_n_bins(), 4)
103+
104+
# Verify bin 0: 4, 5, 6
105+
cg.sample(4)
106+
self.assertGreater(cp_model.get_bin_hits(0), 0)
107+
108+
# Verify bin 3 contains the separate values including 8 which is also in bin 1
109+
cg.sample(8)
110+
self.assertGreater(cp_model.get_bin_hits(1), 0) # hits bin 1 (from range [4:12])
111+
self.assertGreater(cp_model.get_bin_hits(3), 0) # also hits bin 3 (from separate value 8)
112+
113+
def test_bin_array_multiple_single_values(self):
114+
"""Test distribution of multiple single values without ranges.
115+
116+
bins fixed [3] = {1, 2, 3, 4, 5, 6} should distribute as:
117+
- Bin 0: 1, 2
118+
- Bin 1: 3, 4
119+
- Bin 2: 5, 6
120+
"""
121+
122+
@vsc.covergroup
123+
class my_cg(object):
124+
def __init__(self):
125+
self.with_sample(dict(a=vsc.uint8_t()))
126+
self.a_cp = vsc.coverpoint(
127+
self.a, bins=dict(
128+
fixed=vsc.bin_array([3], 1, 2, 3, 4, 5, 6)
129+
))
130+
131+
cg = my_cg()
132+
cg_model = cg.get_model()
133+
cp_model = cg_model.coverpoint_l[0]
134+
135+
# Should have 3 bins
136+
self.assertEqual(cp_model.get_n_bins(), 3)
137+
138+
# Sample each value and verify they hit the correct bins
139+
cg.sample(1)
140+
self.assertGreater(cp_model.get_bin_hits(0), 0)
141+
cg.sample(2)
142+
self.assertGreater(cp_model.get_bin_hits(0), 0)
143+
144+
cg.sample(3)
145+
self.assertGreater(cp_model.get_bin_hits(1), 0)
146+
cg.sample(4)
147+
self.assertGreater(cp_model.get_bin_hits(1), 0)
148+
149+
cg.sample(5)
150+
self.assertGreater(cp_model.get_bin_hits(2), 0)
151+
cg.sample(6)
152+
self.assertGreater(cp_model.get_bin_hits(2), 0)
153+
154+
def test_bin_array_exact_duplicate_values(self):
155+
"""Test that exact duplicate values are retained.
156+
157+
bins fixed [2] = {1, 1, 2, 2} should distribute 4 values across 2 bins:
158+
- Bin 0: 1, 1
159+
- Bin 1: 2, 2
160+
"""
161+
162+
@vsc.covergroup
163+
class my_cg(object):
164+
def __init__(self):
165+
self.with_sample(dict(a=vsc.uint8_t()))
166+
self.a_cp = vsc.coverpoint(
167+
self.a, bins=dict(
168+
fixed=vsc.bin_array([2], 1, 1, 2, 2)
169+
))
170+
171+
cg = my_cg()
172+
cg_model = cg.get_model()
173+
cp_model = cg_model.coverpoint_l[0]
174+
175+
# Should have 2 bins
176+
self.assertEqual(cp_model.get_n_bins(), 2)
177+
178+
# Sample value 1 - should hit both entries in bin 0
179+
cg.sample(1)
180+
self.assertGreater(cp_model.get_bin_hits(0), 0)
181+
182+
# Sample value 2 - should hit both entries in bin 1
183+
cg.sample(2)
184+
self.assertGreater(cp_model.get_bin_hits(1), 0)
185+
186+
def test_bin_array_no_duplicates_simple(self):
187+
"""Test that the fix doesn't break the simple case without duplicates.
188+
189+
bins a1 [4] = {[0:16]} should work as before.
190+
"""
191+
192+
@vsc.covergroup
193+
class my_cg(object):
194+
def __init__(self):
195+
self.with_sample(dict(a=vsc.uint8_t()))
196+
self.a_cp = vsc.coverpoint(
197+
self.a, bins=dict(
198+
a1=vsc.bin_array([4], [0, 16])
199+
))
200+
201+
cg = my_cg()
202+
cg_model = cg.get_model()
203+
cp_model = cg_model.coverpoint_l[0]
204+
205+
# Should have 4 bins for 17 values
206+
self.assertEqual(cp_model.get_n_bins(), 4)
207+
208+
# Test coverage calculation
209+
cg.sample(0)
210+
cg.sample(3)
211+
self.assertEqual(cg.a_cp.get_coverage(), 25)
212+
cg.sample(4)
213+
cg.sample(7)
214+
self.assertEqual(cg.a_cp.get_coverage(), 50)

0 commit comments

Comments
 (0)