From f4204ec10b677dfdfec4585dc2e55e778304b442 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Tue, 12 May 2026 19:08:46 -0600 Subject: [PATCH 1/2] Fix swizzler range max width on negative numbers The example I saw in #278 had rand ints with a range of -255 to 255, which resulted in only 8 bits of swizzling. This appears to leave the MSB or sign bit unconstrained. Added test with a similar range of -7 to 7 that triggers the same failure. --- src/vsc/model/solvegroup_swizzler_partsel.py | 16 +++++++------- ve/unit/test_random_dist.py | 23 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/vsc/model/solvegroup_swizzler_partsel.py b/src/vsc/model/solvegroup_swizzler_partsel.py index 8314edd..9f8f9af 100644 --- a/src/vsc/model/solvegroup_swizzler_partsel.py +++ b/src/vsc/model/solvegroup_swizzler_partsel.py @@ -176,6 +176,12 @@ def sample_dist_weights(self, BinExprType.Eq, ExprLiteralModel(int(val), f.is_signed, f.width)) return ret + + def min_signed_bits(self, n): + if n < 0: + return (n+1).bit_length() + 1 + else: + return n.bit_length() def create_rand_domain_constraint(self, f : FieldScalarModel, @@ -205,15 +211,9 @@ def create_rand_domain_constraint(self, ExprLiteralModel(t_range[0], False, 32))) else: # Determine the max width to use for swizzling. - # max value of abs bounds - maxval = int(max(abs(t_range[0]), abs(t_range[1]))) - - d_width = 0 - - while maxval > 0: - d_width += 1 - maxval >>= 1 + d_width = int(max(self.min_signed_bits(t_range[0]), + self.min_signed_bits(t_range[1]))) if self.debug > 0: print("d_width: %d" % d_width) diff --git a/ve/unit/test_random_dist.py b/ve/unit/test_random_dist.py index 0345d6f..f6709c5 100644 --- a/ve/unit/test_random_dist.py +++ b/ve/unit/test_random_dist.py @@ -691,6 +691,29 @@ def ab_c(self): for e in a_hist: self.assertNotEqual(e, 0) + + def test_widevar_signed_small_range(self): + @vsc.randobj + class Selector: + def __init__(self): + self.a = vsc.rand_int64_t() + + @vsc.constraint + def ab_c(self): + self.a.inside(vsc.rangelist(vsc.rng(-7, 7))) + + selector = Selector() + a_hist = {} + for _ in range(20*20): + selector.randomize() + if selector.a in a_hist: + a_hist[selector.a] += 1 + else: + a_hist[selector.a] = 1 + + print("a_hist: %s" % len(a_hist)) + self.assertEqual(len(a_hist), 15) + def test_widevar_small_range_2(self): @vsc.randobj From 8346cfdefca65a51dcfc5e96f49754db78231843 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Tue, 12 May 2026 19:12:54 -0600 Subject: [PATCH 2/2] Return unsigned on comparison operators #278 The return value of comparision operators, or equality and relational operators, was inheriting the signed type of the input variables. The return values from these operators should be unsigned 1-bit values. See 1800-2017 11.8.2 --- src/vsc/model/expr_bin_model.py | 10 +++++++--- ve/unit/test_constraint_expr.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/vsc/model/expr_bin_model.py b/src/vsc/model/expr_bin_model.py index 75629dc..8921cf2 100644 --- a/src/vsc/model/expr_bin_model.py +++ b/src/vsc/model/expr_bin_model.py @@ -36,6 +36,8 @@ def __init__(self, lhs, op, rhs): self.is_composite = False self._width_valid = False self._width = -1 + self.equality_ops = [BinExprType.Eq, BinExprType.Ne] + self.relational_ops = [BinExprType.Gt, BinExprType.Ge, BinExprType.Lt, BinExprType.Le] def build_composite(self, btor, lhs, rhs): if isinstance(lhs, FieldCompositeModel): @@ -160,12 +162,14 @@ def extend(e1, ctx_width, signed, btor): return ret def is_signed(self): - return (self.lhs.is_signed() and self.rhs.is_signed()) + if self.op in (self.equality_ops + self.relational_ops): + return False + else: + return (self.lhs.is_signed() and self.rhs.is_signed()) def width(self): if not self._width_valid: - if self.op in (BinExprType.Eq, BinExprType.Ge, BinExprType.Le, - BinExprType.Gt, BinExprType.Lt, BinExprType.Ne): + if self.op in (self.equality_ops + self.relational_ops): self._width = 1 else: lhs_w = self.lhs.width() diff --git a/ve/unit/test_constraint_expr.py b/ve/unit/test_constraint_expr.py index 8254125..df81c26 100644 --- a/ve/unit/test_constraint_expr.py +++ b/ve/unit/test_constraint_expr.py @@ -81,6 +81,34 @@ def __init__(self): it.a <= it.b self.assertLessEqual(my_i.a, my_i.b) + def test_eq_rel_sign_ext(self): + @vsc.randobj + class my_c(object): + def __init__(self): + self.a = vsc.rand_int8_t() + self.eq = vsc.rand_int8_t() + self.ne = vsc.rand_int8_t() + self.gt = vsc.rand_int8_t() + self.ge = vsc.rand_int8_t() + self.lt = vsc.rand_int8_t() + self.le = vsc.rand_int8_t() + + my_i = my_c() + with my_i.randomize_with() as it: + it.a == 5 + it.eq == (it.a == 5) + it.ne == (it.a != 6) + it.gt == (it.a > 4) + it.ge == (it.a >= 4) + it.lt == (it.a < 6) + it.le == (it.a <= 6) + self.assertEqual(my_i.eq, 1) + self.assertEqual(my_i.ne, 1) + self.assertEqual(my_i.gt, 1) + self.assertEqual(my_i.ge, 1) + self.assertEqual(my_i.lt, 1) + self.assertEqual(my_i.le, 1) + def test_add(self): @vsc.randobj class my_c(object):