Skip to content

Commit 1df7609

Browse files
committed
0.8.9
- (#205) - Ensure that recursive references between objects don't cause recursion Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
1 parent fc51652 commit 1df7609

12 files changed

+116
-32
lines changed

doc/Changelog.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11

2+
## 0.8.9
3+
- (#205) - Ensure that recursive references between objects don't cause recursion
4+
25
## 0.8.8
36
- Ensure covergroup type names are properly reflected in saved coverage data.
47
- Test suite updates to adapt to newer Python versions

etc/ivpm.info

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

22
name=pyvsc
3-
version=0.8.8
3+
version=0.8.9
44

src/vsc/model/field_array_model.py

+7-11
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ def name_elems(self):
8080
for i,f in enumerate(self.field_l):
8181
f.name = self.name + "[" + str(i) + "]"
8282

83-
def pre_randomize(self):
83+
def pre_randomize(self, visited):
8484
# Set the size field for arrays that don't
8585
# have a random size
8686
if self.is_rand_sz:
8787
self.size.set_used_rand(True)
8888
else:
8989
self._set_size(len(self.field_l))
90-
FieldCompositeModel.pre_randomize(self)
90+
FieldCompositeModel.pre_randomize(self, visited)
9191

92-
def post_randomize(self):
93-
FieldCompositeModel.post_randomize(self)
92+
def post_randomize(self, visited):
93+
FieldCompositeModel.post_randomize(self, visited)
9494
self.sum_expr = None
9595
self.sum_expr_btor = None
9696

@@ -116,13 +116,9 @@ def build(self, builder):
116116
self._set_size(len(self.field_l))
117117
super().build(builder)
118118

119-
# def set_used_rand(self, is_rand, level=0):
120-
# if self.is_rand_sz:
121-
# self.size.set_used_rand(is_rand)
122-
# FieldCompositeModel.set_used_rand(self, is_rand, level=level)
123-
def set_used_rand(self, is_rand, level=0):
124-
super().set_used_rand(is_rand, level)
125-
self.size.set_used_rand(is_rand, level+1)
119+
def set_used_rand(self, is_rand, level=0, in_set=None):
120+
super().set_used_rand(is_rand, level, in_set)
121+
self.size.set_used_rand(is_rand, level+1, in_set)
126122

127123
def get_sum_expr(self):
128124
if self.sum_expr is None:

src/vsc/model/field_composite_model.py

+19-8
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,17 @@ def is_declared_rand(self, v):
5757
self.__is_declared_rand = bool(v)
5858
self.rand_mode = bool(v)
5959

60-
def set_used_rand(self, is_rand, level=0):
60+
def set_used_rand(self, is_rand, level=0, in_set=None):
6161
self.is_used_rand = (is_rand and
6262
((self.is_declared_rand and self.rand_mode) or level==0))
63+
64+
if in_set is None:
65+
in_set = set()
6366

6467
for f in self.field_l:
65-
f.set_used_rand(self.is_used_rand, level+1)
68+
if f not in in_set:
69+
in_set.add(f)
70+
f.set_used_rand(self.is_used_rand, level+1, in_set)
6671

6772
def build(self, builder):
6873
# First, build the fields
@@ -128,27 +133,33 @@ def get_fields(self, field_l):
128133
else:
129134
field_l.append(f)
130135

131-
def pre_randomize(self):
136+
def pre_randomize(self, visited):
132137
"""Called during the randomization process to propagate `pre_randomize` event"""
133138

134139
# Perform a phase callback if available. Note,
135140
# only trigger pre_randomize callbacks on composite
136141
# fields that are actually being used as random
137142
if self.is_used_rand and self.rand_if is not None:
138143
self.rand_if.do_pre_randomize()
139-
144+
145+
visited.append(self)
140146
for f in self.field_l:
141-
f.pre_randomize()
147+
if f not in visited:
148+
f.pre_randomize(visited)
149+
visited.remove(self)
142150

143-
def post_randomize(self):
151+
def post_randomize(self, visited):
144152
"""Called during the randomization process to propagate `post_randomize` event"""
145153

146154
# Perform a phase callback if available
147155
if self.is_used_rand and self.rand_if is not None:
148156
self.rand_if.do_post_randomize()
149-
157+
158+
visited.append(self)
150159
for f in self.field_l:
151-
f.post_randomize()
160+
if f not in visited:
161+
f.post_randomize(visited)
162+
visited.remove(self)
152163

153164
def accept(self, v):
154165
v.visit_composite_field(self)

src/vsc/model/field_model.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ def __init__(self, name):
3939
def build(self, builder):
4040
raise Exception("FieldModel::build unimplemented for type " + str(type(self)))
4141

42-
def pre_randomize(self):
42+
def pre_randomize(self, visited):
4343
pass
4444

45-
def post_randomize(self):
45+
def post_randomize(self, visited):
4646
pass
4747

4848
def get_val(self) -> Value:
@@ -53,10 +53,12 @@ def set_val(self, v : Value):
5353

5454
@property
5555
def fullname(self):
56+
visited = []
5657
ret = self.name
5758
p = self.parent
5859

59-
while p is not None:
60+
while p is not None and p not in visited:
61+
visited.append(p)
6062
if p.name is not None:
6163
ret = p.name + "." + ret
6264
p = p.parent

src/vsc/model/field_scalar_model.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def __init__(self,
4545
self.var = None
4646
self.val = ValueScalar(0)
4747

48-
def set_used_rand(self, is_rand, level=0):
48+
def set_used_rand(self, is_rand, level=0, in_set=None):
4949
# Field is considered rand when
5050
# - It is a root field, on which 'randomize' is called
5151
# - It is declared as random, and 'rand_mode' is true
@@ -85,7 +85,7 @@ def __str__(self):
8585
def get_constraints(self, constraint_l):
8686
pass
8787

88-
def pre_randomize(self):
88+
def pre_randomize(self, visited):
8989
if self.rand_if is not None:
9090
self.rand_if.do_pre_randomize()
9191

@@ -95,7 +95,7 @@ def set_val(self, val : Value):
9595
def get_val(self):
9696
return self.val
9797

98-
def post_randomize(self):
98+
def post_randomize(self, visited):
9999
if self.var is not None:
100100
# Convert to a Python base-10 integer (unsigned)
101101
val = int(self.var.assignment, 2)

src/vsc/model/model_visitor.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,19 @@
5353
class ModelVisitor(object):
5454

5555
def __init__(self):
56+
self.field_visited = []
5657
pass
5758

5859
def visit_rand_obj(self, r):
5960
self.visit_composite_field(r)
6061

6162
def visit_composite_field(self, f : FieldCompositeModel):
6263
# Visit fields
64+
self.field_visited.append(f)
6365
for fi in f.field_l:
64-
fi.accept(self)
66+
if fi not in self.field_visited:
67+
fi.accept(self)
68+
self.field_visited.remove(f)
6569

6670
# Visit constraints
6771
for c in f.constraint_model_l:

src/vsc/model/randomizer.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ def randomize(self, ri : RandInfo, bound_m : Dict[FieldModel,VariableBoundModel]
274274
while x < rs_i:
275275
rs = ri.randsets()[x]
276276
for f in rs.all_fields():
277-
f.post_randomize()
277+
visited = []
278+
f.post_randomize(visited)
278279
f.set_used_rand(False, 0)
279280
f.dispose() # Get rid of the solver var, since we're done with it
280281
f.accept(reset_v)
@@ -537,8 +538,9 @@ def do_randomize(
537538
print(" " + ModelPrettyPrinter.print(fm))
538539

539540
# First, invoke pre_randomize on all elements
541+
visited = []
540542
for fm in field_model_l:
541-
fm.pre_randomize()
543+
fm.pre_randomize(visited)
542544

543545
if constraint_l is None:
544546
constraint_l = []
@@ -601,9 +603,10 @@ def do_randomize(
601603
randomize_done(srcinfo, solve_info)
602604
for fm in field_model_l:
603605
ConstraintOverrideRollbackVisitor.rollback(fm)
604-
606+
607+
visited = []
605608
for fm in field_model_l:
606-
fm.post_randomize()
609+
fm.post_randomize(visited)
607610

608611

609612
# Process constraints to identify variable/constraint sets

src/vsc/visitors/clear_soft_priority_visitor.py

+8
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,25 @@
44
@author: mballance
55
'''
66
from vsc.model.constraint_soft_model import ConstraintSoftModel
7+
from vsc.model.field_composite_model import FieldCompositeModel
78
from vsc.model.model_visitor import ModelVisitor
89

910

1011
class ClearSoftPriorityVisitor(ModelVisitor):
1112

1213
def __init__(self):
1314
super().__init__()
15+
self.visited = set()
1416

1517
def clear(self, e):
18+
self.visited.clear()
1619
e.accept(self)
1720

1821
def visit_constraint_soft(self, c:ConstraintSoftModel):
1922
c.priority = 0
23+
24+
def visit_composite_field(self, f: FieldCompositeModel):
25+
if f not in self.visited:
26+
self.visited.add(f)
27+
super().visit_composite_field(f)
2028

src/vsc/visitors/model_pretty_printer.py

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
class ModelPrettyPrinter(ModelVisitor):
3434

3535
def __init__(self):
36+
super().__init__()
3637
self.out = StringIO()
3738
self.ind = ""
3839
self.print_values = False
@@ -69,6 +70,7 @@ def visit_composite_field(self, f : FieldCompositeModel):
6970
self.writeln(name + " {")
7071
self.inc_indent()
7172
super().visit_composite_field(f)
73+
7274
self.dec_indent()
7375
self.writeln("}")
7476

src/vsc/visitors/ref_fields_postrand_visitor.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ class RefFieldsPostRandVisitor(ModelVisitor):
1111

1212
def __init__(self):
1313
super().__init__()
14+
self.in_set = set()
1415

1516
def visit_expr_fieldref(self, e):
1617
# Capture solving values and mark fields not-used-rand
1718
e.fm.post_randomize()
18-
e.fm.set_used_rand(False)
19+
e.fm.set_used_rand(False, in_set=self.in_set)
1920

ve/unit/test_list_object.py

+54
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,57 @@ def __init__(self):
154154
else:
155155
self.assertGreater(it.a, it.b)
156156

157+
def test_heterogenous_content(self):
158+
@vsc.randobj
159+
class base_constraints_class(object):
160+
pass
161+
162+
163+
@vsc.randobj
164+
class base_rand_class(object):
165+
def __init__(self, name):
166+
self.name = name
167+
self.constraints = vsc.rand_list_t(base_constraints_class(), 0)
168+
169+
def add_constraints(self, c):
170+
self.constraints.append(c)
171+
172+
173+
@vsc.randobj
174+
class user_constraints_class(base_constraints_class):
175+
def __init__(self, ptr):
176+
self.ptr = vsc.rand_attr(ptr)
177+
# self.ptr = ptr
178+
# self.ptr = vsc.rand_attr(user_rand_class("user_1"))
179+
pass
180+
181+
@vsc.constraint
182+
def my_ptr_c(self):
183+
self.ptr.a == 8
184+
self.ptr.b == 1999
185+
186+
187+
@vsc.randobj
188+
class user_rand_class(base_rand_class):
189+
def __init__(self, name):
190+
super().__init__(name)
191+
self.a = vsc.rand_bit_t(16)
192+
self.b = vsc.rand_bit_t(16)
193+
194+
195+
@vsc.constraint
196+
def ab_c(self):
197+
self.a in vsc.rangelist(vsc.rng(1,1000))
198+
self.b in vsc.rangelist(vsc.rng(1000,2000))
199+
pass
200+
201+
def print_fields(self):
202+
print(f"{self.name}: a={self.a}, b={self.b}")
203+
204+
205+
usr1 = user_rand_class("user_1")
206+
c = user_constraints_class(usr1)
207+
usr1.add_constraints(c)
208+
usr1.randomize(debug=0, solve_fail_debug=0)
209+
print("--------------------\n")
210+
usr1.print_fields()

0 commit comments

Comments
 (0)