diff --git a/src/vsc/model/rand_info_builder.py b/src/vsc/model/rand_info_builder.py index 5b16be7..8b49e36 100644 --- a/src/vsc/model/rand_info_builder.py +++ b/src/vsc/model/rand_info_builder.py @@ -133,15 +133,14 @@ def build( if f in builder._order_m.keys(): rs_deps[f] = builder._order_m[f] - if len(rs_deps) > 0: - rs.rand_order_l = [] - for fs in list(toposort(rs_deps)): - field_l = [] - for fi in fs: - if fi in rs.fields(): - field_l.append(fi) - if len(field_l) > 0: - rs.rand_order_l.append(field_l) + if len(rs_deps) > 0: + rs.rand_order_l = [] + # Random stability warning: toposort uses sets and is unordered + for fs in list(toposort(rs_deps)): + # Add fields in predictable randset order + field_l = [fi for fi in rs.fields() if fi in fs] + if len(field_l) > 0: + rs.rand_order_l.append(field_l) # It's important to maintain a fixed order for the # unconstrained fields, since this affects their diff --git a/ve/unit/test_constraint_solve_order.py b/ve/unit/test_constraint_solve_order.py index 8fd36d4..19b1d37 100644 --- a/ve/unit/test_constraint_solve_order.py +++ b/ve/unit/test_constraint_solve_order.py @@ -323,3 +323,42 @@ def loop_c(self): self.assertNotEqual(0, num_of_nested_loop_hist[1]) self.assertNotEqual(0, num_of_nested_loop_hist[2]) + + def test_toposort_random_stability(self): + @vsc.randobj + class my_s(object): + + def __init__(self): + self.a = vsc.rand_bit_t(8) + self.list_0 = vsc.rand_list_t(vsc.rand_bit_t(8), 10) + self.list_1 = vsc.rand_list_t(vsc.rand_bit_t(8), 10) + + @vsc.constraint + def a_and_lists_c(self): + # Excercise rand_order/toposort on lists + vsc.solve_order(self.a, self.list_0) + vsc.solve_order(self.list_0, self.list_1) + + # Make lists unique to better detect ordering differences + vsc.unique(self.list_0) + vsc.unique(self.list_1) + + # Tie all variables into single randset + self.a > 10 + self.a < 20 + with vsc.foreach(self.list_0) as it: + it < self.a + with vsc.foreach(self.list_1) as it: + it < self.a + + first = my_s() + first.set_randstate(vsc.RandState.mkFromSeed(0)) + first.randomize() + + for _ in range(20): + repeat = my_s() + repeat.set_randstate(vsc.RandState.mkFromSeed(0)) + repeat.randomize() + self.assertEqual(first.a, repeat.a, "Mismatch on a") + self.assertListEqual(list(first.list_0), list(repeat.list_0), "Mismatch on list_0") + self.assertListEqual(list(first.list_1), list(repeat.list_1), "Mismatch on list_1")