From 49027d92b58d950c4b2adb8e2d11d27c8c26cc77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:57:33 +0000 Subject: [PATCH 1/3] Initial plan From 782db3c3f60bfb9a916ec245624d8d12558af118 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:10:33 +0000 Subject: [PATCH 2/3] Fix bin_array to retain duplicate values per IEEE 1800-2023 Co-authored-by: mballance <1340805+mballance@users.noreply.github.com> --- src/vsc/coverage.py | 6 +- ve/unit/cov.xml | 191 ++++++++++++++++++++++++ ve/unit/test_bin_array_duplicates.py | 214 +++++++++++++++++++++++++++ 3 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 ve/unit/cov.xml create mode 100644 ve/unit/test_bin_array_duplicates.py diff --git a/src/vsc/coverage.py b/src/vsc/coverage.py index 4ab0db6..313d822 100644 --- a/src/vsc/coverage.py +++ b/src/vsc/coverage.py @@ -435,7 +435,11 @@ def __init__(self, nbins, *args): if len(args) == 0: raise Exception("No bins range specified") - self.ranges.compact() + # NOTE: Do NOT call self.ranges.compact() here! + # According to IEEE 1800-2023 (page 585): + # "Duplicate values are retained; thus the same value can be assigned to multiple bins" + # Calling compact() would merge overlapping ranges and remove duplicates, + # which is incorrect for bin distribution. # Capture the declaration location of this bin frame = inspect.stack()[1] diff --git a/ve/unit/cov.xml b/ve/unit/cov.xml new file mode 100644 index 0000000..9ab15f8 --- /dev/null +++ b/ve/unit/cov.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ve/unit/test_bin_array_duplicates.py b/ve/unit/test_bin_array_duplicates.py new file mode 100644 index 0000000..8e2eed5 --- /dev/null +++ b/ve/unit/test_bin_array_duplicates.py @@ -0,0 +1,214 @@ +''' +Created on 2024 + +@author: copilot + +Test for bin_array duplicate value handling according to IEEE 1800-2023 +''' +import vsc +from vsc_test_case import VscTestCase + +class TestBinArrayDuplicates(VscTestCase): + + def test_bin_array_with_duplicates_from_range(self): + """Test that duplicate values from a range are properly retained and distributed. + + According to IEEE 1800-2023 (page 585): + "Duplicate values are retained; thus the same value can be assigned to multiple bins" + + bins fixed [4] = {[1:10], 1, 4, 7} should distribute 13 values as: + - Bin 0: 1, 2, 3 + - Bin 1: 4, 5, 6 + - Bin 2: 7, 8, 9 + - Bin 3: 10, 1, 4, 7 (including the duplicates) + + Note: Values 1, 4, and 7 will hit MULTIPLE bins when sampled (both their range bin + and bin 3), which is the correct behavior per the standard. + """ + + @vsc.covergroup + class my_cg(object): + def __init__(self): + self.with_sample(dict(a=vsc.uint8_t())) + self.a_cp = vsc.coverpoint( + self.a, bins=dict( + fixed=vsc.bin_array([4], [1, 10], 1, 4, 7) + )) + + cg = my_cg() + cg_model = cg.get_model() + cp_model = cg_model.coverpoint_l[0] + + # Should have 4 bins total (distributed from 13 values) + self.assertEqual(cp_model.get_n_bins(), 4) + + # Verify bin structure by checking ranges + bin_ranges = [] + for idx in range(cp_model.get_n_bins()): + bin_range = cp_model.get_bin_range(idx) + bin_ranges.append(bin_range) + + # Value 1 should hit multiple bins (bin 0 from range and bin 3 from duplicate) + cg.sample(1) + self.assertGreater(cp_model.get_bin_hits(0), 0, "Value 1 should hit bin 0") + self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 1 should also hit bin 3 (duplicate)") + + # Value 4 should hit multiple bins + cg.sample(4) + self.assertGreater(cp_model.get_bin_hits(1), 0, "Value 4 should hit bin 1") + self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 4 should also hit bin 3 (duplicate)") + + # Value 7 should hit multiple bins + cg.sample(7) + self.assertGreater(cp_model.get_bin_hits(2), 0, "Value 7 should hit bin 2") + self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 7 should also hit bin 3 (duplicate)") + + # Value 10 should only hit bin 3 + cg.sample(10) + self.assertGreater(cp_model.get_bin_hits(3), 0, "Value 10 should hit bin 3") + + # Value 2 should only hit bin 0 (no duplicate) + cg.sample(2) + hits_before = cp_model.get_bin_hits(3) + cg.sample(2) + hits_after = cp_model.get_bin_hits(3) + # Bin 3 should not get additional hits from value 2 + self.assertEqual(hits_before, hits_after, "Value 2 should not hit bin 3") + + def test_bin_array_with_separate_values(self): + """Test that separate values including duplicates are properly distributed. + + bins fixed [4] = {[4:12], 1, 2, 8} should distribute values as: + - Bin 0: 4, 5, 6 + - Bin 1: 7, 8, 9 + - Bin 2: 10, 11, 12 + - Bin 3: 1, 2, 8 (separate values, 8 is also in bin 1) + """ + + @vsc.covergroup + class my_cg(object): + def __init__(self): + self.with_sample(dict(a=vsc.uint8_t())) + self.a_cp = vsc.coverpoint( + self.a, bins=dict( + fixed=vsc.bin_array([4], [4, 12], 1, 2, 8) + )) + + cg = my_cg() + cg_model = cg.get_model() + cp_model = cg_model.coverpoint_l[0] + + # Should have 4 bins + self.assertEqual(cp_model.get_n_bins(), 4) + + # Verify bin 0: 4, 5, 6 + cg.sample(4) + self.assertGreater(cp_model.get_bin_hits(0), 0) + + # Verify bin 3 contains the separate values including 8 which is also in bin 1 + cg.sample(8) + self.assertGreater(cp_model.get_bin_hits(1), 0) # hits bin 1 (from range [4:12]) + self.assertGreater(cp_model.get_bin_hits(3), 0) # also hits bin 3 (from separate value 8) + + def test_bin_array_multiple_single_values(self): + """Test distribution of multiple single values without ranges. + + bins fixed [3] = {1, 2, 3, 4, 5, 6} should distribute as: + - Bin 0: 1, 2 + - Bin 1: 3, 4 + - Bin 2: 5, 6 + """ + + @vsc.covergroup + class my_cg(object): + def __init__(self): + self.with_sample(dict(a=vsc.uint8_t())) + self.a_cp = vsc.coverpoint( + self.a, bins=dict( + fixed=vsc.bin_array([3], 1, 2, 3, 4, 5, 6) + )) + + cg = my_cg() + cg_model = cg.get_model() + cp_model = cg_model.coverpoint_l[0] + + # Should have 3 bins + self.assertEqual(cp_model.get_n_bins(), 3) + + # Sample each value and verify they hit the correct bins + cg.sample(1) + self.assertGreater(cp_model.get_bin_hits(0), 0) + cg.sample(2) + self.assertGreater(cp_model.get_bin_hits(0), 0) + + cg.sample(3) + self.assertGreater(cp_model.get_bin_hits(1), 0) + cg.sample(4) + self.assertGreater(cp_model.get_bin_hits(1), 0) + + cg.sample(5) + self.assertGreater(cp_model.get_bin_hits(2), 0) + cg.sample(6) + self.assertGreater(cp_model.get_bin_hits(2), 0) + + def test_bin_array_exact_duplicate_values(self): + """Test that exact duplicate values are retained. + + bins fixed [2] = {1, 1, 2, 2} should distribute 4 values across 2 bins: + - Bin 0: 1, 1 + - Bin 1: 2, 2 + """ + + @vsc.covergroup + class my_cg(object): + def __init__(self): + self.with_sample(dict(a=vsc.uint8_t())) + self.a_cp = vsc.coverpoint( + self.a, bins=dict( + fixed=vsc.bin_array([2], 1, 1, 2, 2) + )) + + cg = my_cg() + cg_model = cg.get_model() + cp_model = cg_model.coverpoint_l[0] + + # Should have 2 bins + self.assertEqual(cp_model.get_n_bins(), 2) + + # Sample value 1 - should hit both entries in bin 0 + cg.sample(1) + self.assertGreater(cp_model.get_bin_hits(0), 0) + + # Sample value 2 - should hit both entries in bin 1 + cg.sample(2) + self.assertGreater(cp_model.get_bin_hits(1), 0) + + def test_bin_array_no_duplicates_simple(self): + """Test that the fix doesn't break the simple case without duplicates. + + bins a1 [4] = {[0:16]} should work as before. + """ + + @vsc.covergroup + class my_cg(object): + def __init__(self): + self.with_sample(dict(a=vsc.uint8_t())) + self.a_cp = vsc.coverpoint( + self.a, bins=dict( + a1=vsc.bin_array([4], [0, 16]) + )) + + cg = my_cg() + cg_model = cg.get_model() + cp_model = cg_model.coverpoint_l[0] + + # Should have 4 bins for 17 values + self.assertEqual(cp_model.get_n_bins(), 4) + + # Test coverage calculation + cg.sample(0) + cg.sample(3) + self.assertEqual(cg.a_cp.get_coverage(), 25) + cg.sample(4) + cg.sample(7) + self.assertEqual(cg.a_cp.get_coverage(), 50) From 856c9556e284f863c8dc07ef69ecb4ef2c0f03da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:11:17 +0000 Subject: [PATCH 3/3] Remove test output file and update gitignore Co-authored-by: mballance <1340805+mballance@users.noreply.github.com> --- .gitignore | 1 + ve/unit/cov.xml | 191 ------------------------------------------------ 2 files changed, 1 insertion(+), 191 deletions(-) delete mode 100644 ve/unit/cov.xml diff --git a/.gitignore b/.gitignore index 5c96703..cd5dbc3 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,4 @@ junit/ .venv/ .venv.bld +cov.xml diff --git a/ve/unit/cov.xml b/ve/unit/cov.xml deleted file mode 100644 index 9ab15f8..0000000 --- a/ve/unit/cov.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -