Skip to content

Commit

Permalink
0.8.9
Browse files Browse the repository at this point in the history
- (#205) - Ensure that recursive references between objects don't cause recursion

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
  • Loading branch information
mballance committed Apr 12, 2024
1 parent fc51652 commit 1df7609
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 32 deletions.
3 changes: 3 additions & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

## 0.8.9
- (#205) - Ensure that recursive references between objects don't cause recursion

## 0.8.8
- Ensure covergroup type names are properly reflected in saved coverage data.
- Test suite updates to adapt to newer Python versions
Expand Down
2 changes: 1 addition & 1 deletion etc/ivpm.info
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

name=pyvsc
version=0.8.8
version=0.8.9

18 changes: 7 additions & 11 deletions src/vsc/model/field_array_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@ def name_elems(self):
for i,f in enumerate(self.field_l):
f.name = self.name + "[" + str(i) + "]"

def pre_randomize(self):
def pre_randomize(self, visited):
# Set the size field for arrays that don't
# have a random size
if self.is_rand_sz:
self.size.set_used_rand(True)
else:
self._set_size(len(self.field_l))
FieldCompositeModel.pre_randomize(self)
FieldCompositeModel.pre_randomize(self, visited)

def post_randomize(self):
FieldCompositeModel.post_randomize(self)
def post_randomize(self, visited):
FieldCompositeModel.post_randomize(self, visited)
self.sum_expr = None
self.sum_expr_btor = None

Expand All @@ -116,13 +116,9 @@ def build(self, builder):
self._set_size(len(self.field_l))
super().build(builder)

# def set_used_rand(self, is_rand, level=0):
# if self.is_rand_sz:
# self.size.set_used_rand(is_rand)
# FieldCompositeModel.set_used_rand(self, is_rand, level=level)
def set_used_rand(self, is_rand, level=0):
super().set_used_rand(is_rand, level)
self.size.set_used_rand(is_rand, level+1)
def set_used_rand(self, is_rand, level=0, in_set=None):
super().set_used_rand(is_rand, level, in_set)
self.size.set_used_rand(is_rand, level+1, in_set)

def get_sum_expr(self):
if self.sum_expr is None:
Expand Down
27 changes: 19 additions & 8 deletions src/vsc/model/field_composite_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ def is_declared_rand(self, v):
self.__is_declared_rand = bool(v)
self.rand_mode = bool(v)

def set_used_rand(self, is_rand, level=0):
def set_used_rand(self, is_rand, level=0, in_set=None):
self.is_used_rand = (is_rand and
((self.is_declared_rand and self.rand_mode) or level==0))

if in_set is None:
in_set = set()

for f in self.field_l:
f.set_used_rand(self.is_used_rand, level+1)
if f not in in_set:
in_set.add(f)
f.set_used_rand(self.is_used_rand, level+1, in_set)

def build(self, builder):
# First, build the fields
Expand Down Expand Up @@ -128,27 +133,33 @@ def get_fields(self, field_l):
else:
field_l.append(f)

def pre_randomize(self):
def pre_randomize(self, visited):
"""Called during the randomization process to propagate `pre_randomize` event"""

# Perform a phase callback if available. Note,
# only trigger pre_randomize callbacks on composite
# fields that are actually being used as random
if self.is_used_rand and self.rand_if is not None:
self.rand_if.do_pre_randomize()


visited.append(self)
for f in self.field_l:
f.pre_randomize()
if f not in visited:
f.pre_randomize(visited)
visited.remove(self)

def post_randomize(self):
def post_randomize(self, visited):
"""Called during the randomization process to propagate `post_randomize` event"""

# Perform a phase callback if available
if self.is_used_rand and self.rand_if is not None:
self.rand_if.do_post_randomize()


visited.append(self)
for f in self.field_l:
f.post_randomize()
if f not in visited:
f.post_randomize(visited)
visited.remove(self)

def accept(self, v):
v.visit_composite_field(self)
Expand Down
8 changes: 5 additions & 3 deletions src/vsc/model/field_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ def __init__(self, name):
def build(self, builder):
raise Exception("FieldModel::build unimplemented for type " + str(type(self)))

def pre_randomize(self):
def pre_randomize(self, visited):
pass

def post_randomize(self):
def post_randomize(self, visited):
pass

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

@property
def fullname(self):
visited = []
ret = self.name
p = self.parent

while p is not None:
while p is not None and p not in visited:
visited.append(p)
if p.name is not None:
ret = p.name + "." + ret
p = p.parent
Expand Down
6 changes: 3 additions & 3 deletions src/vsc/model/field_scalar_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self,
self.var = None
self.val = ValueScalar(0)

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

def pre_randomize(self):
def pre_randomize(self, visited):
if self.rand_if is not None:
self.rand_if.do_pre_randomize()

Expand All @@ -95,7 +95,7 @@ def set_val(self, val : Value):
def get_val(self):
return self.val

def post_randomize(self):
def post_randomize(self, visited):
if self.var is not None:
# Convert to a Python base-10 integer (unsigned)
val = int(self.var.assignment, 2)
Expand Down
6 changes: 5 additions & 1 deletion src/vsc/model/model_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,19 @@
class ModelVisitor(object):

def __init__(self):
self.field_visited = []
pass

def visit_rand_obj(self, r):
self.visit_composite_field(r)

def visit_composite_field(self, f : FieldCompositeModel):
# Visit fields
self.field_visited.append(f)
for fi in f.field_l:
fi.accept(self)
if fi not in self.field_visited:
fi.accept(self)
self.field_visited.remove(f)

# Visit constraints
for c in f.constraint_model_l:
Expand Down
11 changes: 7 additions & 4 deletions src/vsc/model/randomizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ def randomize(self, ri : RandInfo, bound_m : Dict[FieldModel,VariableBoundModel]
while x < rs_i:
rs = ri.randsets()[x]
for f in rs.all_fields():
f.post_randomize()
visited = []
f.post_randomize(visited)
f.set_used_rand(False, 0)
f.dispose() # Get rid of the solver var, since we're done with it
f.accept(reset_v)
Expand Down Expand Up @@ -537,8 +538,9 @@ def do_randomize(
print(" " + ModelPrettyPrinter.print(fm))

# First, invoke pre_randomize on all elements
visited = []
for fm in field_model_l:
fm.pre_randomize()
fm.pre_randomize(visited)

if constraint_l is None:
constraint_l = []
Expand Down Expand Up @@ -601,9 +603,10 @@ def do_randomize(
randomize_done(srcinfo, solve_info)
for fm in field_model_l:
ConstraintOverrideRollbackVisitor.rollback(fm)


visited = []
for fm in field_model_l:
fm.post_randomize()
fm.post_randomize(visited)


# Process constraints to identify variable/constraint sets
Expand Down
8 changes: 8 additions & 0 deletions src/vsc/visitors/clear_soft_priority_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@
@author: mballance
'''
from vsc.model.constraint_soft_model import ConstraintSoftModel
from vsc.model.field_composite_model import FieldCompositeModel
from vsc.model.model_visitor import ModelVisitor


class ClearSoftPriorityVisitor(ModelVisitor):

def __init__(self):
super().__init__()
self.visited = set()

def clear(self, e):
self.visited.clear()
e.accept(self)

def visit_constraint_soft(self, c:ConstraintSoftModel):
c.priority = 0

def visit_composite_field(self, f: FieldCompositeModel):
if f not in self.visited:
self.visited.add(f)
super().visit_composite_field(f)

2 changes: 2 additions & 0 deletions src/vsc/visitors/model_pretty_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
class ModelPrettyPrinter(ModelVisitor):

def __init__(self):
super().__init__()
self.out = StringIO()
self.ind = ""
self.print_values = False
Expand Down Expand Up @@ -69,6 +70,7 @@ def visit_composite_field(self, f : FieldCompositeModel):
self.writeln(name + " {")
self.inc_indent()
super().visit_composite_field(f)

self.dec_indent()
self.writeln("}")

Expand Down
3 changes: 2 additions & 1 deletion src/vsc/visitors/ref_fields_postrand_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ class RefFieldsPostRandVisitor(ModelVisitor):

def __init__(self):
super().__init__()
self.in_set = set()

def visit_expr_fieldref(self, e):
# Capture solving values and mark fields not-used-rand
e.fm.post_randomize()
e.fm.set_used_rand(False)
e.fm.set_used_rand(False, in_set=self.in_set)

54 changes: 54 additions & 0 deletions ve/unit/test_list_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,57 @@ def __init__(self):
else:
self.assertGreater(it.a, it.b)

def test_heterogenous_content(self):
@vsc.randobj
class base_constraints_class(object):
pass


@vsc.randobj
class base_rand_class(object):
def __init__(self, name):
self.name = name
self.constraints = vsc.rand_list_t(base_constraints_class(), 0)

def add_constraints(self, c):
self.constraints.append(c)


@vsc.randobj
class user_constraints_class(base_constraints_class):
def __init__(self, ptr):
self.ptr = vsc.rand_attr(ptr)
# self.ptr = ptr
# self.ptr = vsc.rand_attr(user_rand_class("user_1"))
pass

@vsc.constraint
def my_ptr_c(self):
self.ptr.a == 8
self.ptr.b == 1999


@vsc.randobj
class user_rand_class(base_rand_class):
def __init__(self, name):
super().__init__(name)
self.a = vsc.rand_bit_t(16)
self.b = vsc.rand_bit_t(16)


@vsc.constraint
def ab_c(self):
self.a in vsc.rangelist(vsc.rng(1,1000))
self.b in vsc.rangelist(vsc.rng(1000,2000))
pass

def print_fields(self):
print(f"{self.name}: a={self.a}, b={self.b}")


usr1 = user_rand_class("user_1")
c = user_constraints_class(usr1)
usr1.add_constraints(c)
usr1.randomize(debug=0, solve_fail_debug=0)
print("--------------------\n")
usr1.print_fields()

0 comments on commit 1df7609

Please sign in to comment.