Skip to content

Commit

Permalink
dep analysis: improve
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Jan 9, 2024
1 parent 17c26bd commit ae65673
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 35 deletions.
61 changes: 45 additions & 16 deletions rtlrepair/dependency_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class VarInfo:
depends_on: set = field(default_factory=set)

def is_register(self) -> bool:
return self.clocking is not None
return self.clocking is not None and self.clocking.is_sync()

def render(self) -> str:
out = ""
Expand Down Expand Up @@ -103,13 +103,35 @@ def visit_Always(self, node: vast.Always):
self.proc_info = None

def visit_NonblockingSubstitution(self, node: vast.NonblockingSubstitution):
if self.proc_info.is_comb():
print("WARN: non-blocking assignment in comb logic process!")
else:
name = get_lvar(node.left)
old_clocking = self.vars[name].clocking
assert old_clocking is None or old_clocking == self.proc_info
self.vars[name].clocking = self.proc_info
self.visit_assignment(is_blocking=False, left=node.left, right=node.right)

def visit_BlockingSubstitution(self, node: vast.BlockingSubstitution):
self.visit_assignment(is_blocking=True, left=node.left, right=node.right)

def visit_Assign(self, node: vast.Assign):
# fake a proc info
self.proc_info = ProcInfo()
# treat like an blocking assignment in a comb process
self.visit_assignment(is_blocking=True, left=node.left, right=node.right)
self.proc_info = None

def visit_assignment(self, is_blocking: bool, left: vast.Lvalue, right: vast.Node):
# warn about mixed assignments
if is_blocking and self.proc_info.is_sync():
print("[DependencyAnalysis] WARN: blocking assignment in sync logic process!")
if not is_blocking and self.proc_info.is_comb():
print("[DependencyAnalysis] WARN: non-blocking assignment in comb logic process!")

names = sorted(get_lvars(left))
for name in names:
vv = self.vars[name]
if vv.clocking is None:
vv.clocking = self.proc_info
else:
assert vv.clocking == self.proc_info
if is_blocking:
# we only track comb dependencies
vv.depends_on |= get_rvars(right)

def visit_IfStatement(self, node: vast.IfStatement):
cond_vars = get_rvars(node.cond)
Expand All @@ -118,25 +140,32 @@ def visit_IfStatement(self, node: vast.IfStatement):
self.visit(node.false_statement)
self.path_stack.pop()


def get_lvar(expr: vast.Node) -> str:
def get_lvars(expr: vast.Node) -> set[str]:
""" returns variable that is being assigned """
if isinstance(expr, vast.Lvalue):
return get_lvar(expr.var)
return get_lvars(expr.var)
elif isinstance(expr, vast.Identifier):
return expr.name
return { expr.name }
elif isinstance(expr, vast.Concat) or isinstance(expr, vast.LConcat):
out = set()
for e in expr.list:
out |= get_lvars(e)
return out
else:
raise NotImplementedError(f"TODO: implement get_lvar for {expr} : {type(expr)}")


def get_rvars(expr: vast.Node) -> set[str]:
""" returns variables that are being read """
if isinstance(expr, vast.Identifier):
return set(expr.name)
elif isinstance(expr, vast.UnaryOperator):
return get_rvars(expr.right)
return {expr.name}
elif isinstance(expr, vast.Rvalue):
return get_rvars(expr.var)
else:
raise NotImplementedError(f"TODO: implement get_rvars for {expr} : {type(expr)}")
out = set()
for e in expr.children():
out |= get_rvars(e)
return out


def find_clock_and_reset(sens: vast.SensList) -> ProcInfo:
Expand Down
43 changes: 24 additions & 19 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,57 +639,60 @@ def _make_histogram(widths: dict) -> dict:
return dict(hist)


from rtlrepair import parse_verilog
from rtlrepair.dependency_analysis import analyze_dependencies

class TestTypeInference(unittest.TestCase):
""" actual unittests for code in rtlrepair/types.py """

def test_flip_flop_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(flip_flop_dir / "tff.v")
widths = infer_widths(ast)
self.assertEqual({None: 1, 1: 6}, _make_histogram(widths))

def test_flip_flop_buggy1_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(flip_flop_dir / "tff_wadden_buggy1.v")
widths = infer_widths(ast)
self.assertEqual({None: 1, 1: 5}, _make_histogram(widths))

def test_decoder_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(decoder_dir / "decoder_3_to_8.v")
widths = infer_widths(ast)
hist = _make_histogram(widths)
self.assertEqual({None: 1, 1: 13, 4: 8, 8: 17}, hist)

def test_counter_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(counter_dir / "first_counter_overflow.v")
widths = infer_widths(ast)
hist = _make_histogram(widths)
self.assertEqual({None: 1, 1: 8, 4: 5}, hist)

def test_fsm_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(fsm_dir / "fsm_full.v")
widths = infer_widths(ast)
hist = _make_histogram(widths)
self.assertEqual({None: 1, 1: 19, 3: 8}, hist)

def test_left_shift_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(left_shift_dir / "lshift_reg.v")
widths = infer_widths(ast)
hist = _make_histogram(widths)
self.assertEqual({None: 1, 1: 4, 8: 7, 32: 5}, hist)

def test_sdram_controller_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(sd_dir / "sdram_controller.no_tri_state.v")
# ast.show()
Expand All @@ -699,7 +702,7 @@ def test_sdram_controller_widths(self):
self.assertEqual(expected, hist)

def test_reed_solomon_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(reed_dir / "BM_lamda.v")
widths = infer_widths(ast)
Expand All @@ -708,7 +711,7 @@ def test_reed_solomon_widths(self):
self.assertEqual(expected, hist)

def test_i2c_bit_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(i2c_dir / "i2c_master_bit_ctrl.sync_reset.v", i2c_dir)
widths = infer_widths(ast)
Expand All @@ -717,7 +720,7 @@ def test_i2c_bit_widths(self):
self.assertEqual(expected, hist)

def test_mux_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(mux_dir / "mux_4_1.v")
widths = infer_widths(ast)
Expand All @@ -727,7 +730,7 @@ def test_mux_widths(self):

def test_axis_adapter_widths(self):
""" This file contains the `indexed part selector`: [... +: ... ] """
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(s3_dir / "axis_adapter.v")
widths = infer_widths(ast)
Expand All @@ -736,7 +739,7 @@ def test_axis_adapter_widths(self):
self.assertEqual(expected, hist)

def test_sd_spi_widths(self):
from rtlrepair import parse_verilog

from rtlrepair.types import infer_widths
ast = parse_verilog(zip_cpu_sdspi_dir / "sdspi.v")
widths = infer_widths(ast)
Expand All @@ -749,7 +752,7 @@ class TestExposeBranches(unittest.TestCase):
""" unittests for code in rtlrepair/expose_branches.py """

def test_flip_flop(self):
from rtlrepair import parse_verilog

from rtlrepair.expose_branches import expose_branches
ast = parse_verilog(flip_flop_dir / "tff.v")
expose_branches(ast)
Expand All @@ -758,8 +761,7 @@ def test_flip_flop(self):
class TestDependencyAnalysis(unittest.TestCase):
""" actual unittests for code in rtlrepair/dependency_analysis.py """

def check(self, expected: list, vvs: list):
print_actual = False
def check(self, expected: list, vvs: list, print_actual: bool = False):
if print_actual:
vstr = ", ".join(f'"{v.render()}"' for v in vvs)
print(f"[{vstr}]")
Expand All @@ -768,13 +770,16 @@ def check(self, expected: list, vvs: list):
self.assertEqual(len(expected), len(vvs))

def test_flip_flop_deps(self):
from rtlrepair import parse_verilog
from rtlrepair.dependency_analysis import analyze_dependencies
ast = parse_verilog(flip_flop_dir / "tff.v")
expected = ["inp clk: {}", "out reg (@posedge clk) q: {}", "inp rstn: {}", "inp t: {}"]
self.check(expected, analyze_dependencies(ast))


def test_decoder(self):
ast = parse_verilog(decoder_dir / "decoder_3_to_8.v")
expected = ["inp A: {}", "inp B: {}", "inp C: {}", "out Y0: {A, B, C, en}", "out Y1: {A, B, C, en}",
"out Y2: {A, B, C, en}", "out Y3: {A, B, C, en}", "out Y4: {A, B, C, en}", "out Y5: {A, B, C, en}",
"out Y6: {A, B, C, en}", "out Y7: {A, B, C, en}", "inp en: {}"]
self.check(expected, analyze_dependencies(ast))


class TestPyVerilog(unittest.TestCase):
Expand All @@ -791,7 +796,7 @@ def test_reg_initial_value(self):
fp.write(src)
fp.close()

from rtlrepair import parse_verilog, serialize
from rtlrepair import serialize
ast = parse_verilog(Path(fp.name))
out = serialize(ast)
self.assertIn("reg test = 1'b0", out)
Expand Down

0 comments on commit ae65673

Please sign in to comment.