|
19 | 19 | __all__ = ["SyntaxError", "SyntaxWarning", "Module"] |
20 | 20 |
|
21 | 21 |
|
22 | | -class _Visitor: |
23 | | - def __init__(self): |
24 | | - self.driven_signals = SignalSet() |
25 | | - |
26 | | - def visit_stmt(self, stmt): |
27 | | - if isinstance(stmt, _StatementList): |
28 | | - for s in stmt: |
29 | | - self.visit_stmt(s) |
30 | | - elif isinstance(stmt, Assign): |
31 | | - self.visit_lhs(stmt.lhs) |
32 | | - self.visit_rhs(stmt.rhs) |
33 | | - elif isinstance(stmt, Print): |
| 22 | +def _check_stmt(stmt): |
| 23 | + if isinstance(stmt, _StatementList): |
| 24 | + for s in stmt: |
| 25 | + _check_stmt(s) |
| 26 | + elif isinstance(stmt, Assign): |
| 27 | + _check_lhs(stmt.lhs) |
| 28 | + _check_rhs(stmt.rhs) |
| 29 | + elif isinstance(stmt, Print): |
| 30 | + for chunk in stmt.message._chunks: |
| 31 | + if not isinstance(chunk, str): |
| 32 | + obj, format_spec = chunk |
| 33 | + _check_rhs(obj) |
| 34 | + elif isinstance(stmt, Property): |
| 35 | + _check_rhs(stmt.test) |
| 36 | + if stmt.message is not None: |
34 | 37 | for chunk in stmt.message._chunks: |
35 | 38 | if not isinstance(chunk, str): |
36 | 39 | obj, format_spec = chunk |
37 | | - self.visit_rhs(obj) |
38 | | - elif isinstance(stmt, Property): |
39 | | - self.visit_rhs(stmt.test) |
40 | | - if stmt.message is not None: |
41 | | - for chunk in stmt.message._chunks: |
42 | | - if not isinstance(chunk, str): |
43 | | - obj, format_spec = chunk |
44 | | - self.visit_rhs(obj) |
45 | | - elif isinstance(stmt, Switch): |
46 | | - self.visit_rhs(stmt.test) |
47 | | - for _patterns, stmts, _src_loc in stmt.cases: |
48 | | - self.visit_stmt(stmts) |
49 | | - elif isinstance(stmt, _LateBoundStatement): |
50 | | - pass |
51 | | - else: |
52 | | - assert False # :nocov: |
53 | | - |
54 | | - def visit_lhs(self, value): |
55 | | - if isinstance(value, Operator) and value.operator in ("u", "s"): |
56 | | - self.visit_lhs(value.operands[0]) |
57 | | - elif isinstance(value, (Signal, ClockSignal, ResetSignal)): |
58 | | - self.driven_signals.add(value) |
59 | | - elif isinstance(value, Slice): |
60 | | - self.visit_lhs(value.value) |
61 | | - elif isinstance(value, Part): |
62 | | - self.visit_lhs(value.value) |
63 | | - self.visit_rhs(value.offset) |
64 | | - elif isinstance(value, Concat): |
65 | | - for part in value.parts: |
66 | | - self.visit_lhs(part) |
67 | | - elif isinstance(value, SwitchValue): |
68 | | - self.visit_rhs(value.test) |
69 | | - for _patterns, elem in value.cases: |
70 | | - self.visit_lhs(elem) |
71 | | - elif isinstance(value, MemoryData._Row): |
72 | | - raise ValueError(f"Value {value!r} can only be used in simulator processes") |
73 | | - else: |
74 | | - raise ValueError(f"Value {value!r} cannot be assigned to") |
75 | | - |
76 | | - def visit_rhs(self, value): |
77 | | - if isinstance(value, (Const, Signal, ClockSignal, ResetSignal, Initial, AnyValue)): |
78 | | - pass |
79 | | - elif isinstance(value, Operator): |
80 | | - for op in value.operands: |
81 | | - self.visit_rhs(op) |
82 | | - elif isinstance(value, Slice): |
83 | | - self.visit_rhs(value.value) |
84 | | - elif isinstance(value, Part): |
85 | | - self.visit_rhs(value.value) |
86 | | - self.visit_rhs(value.offset) |
87 | | - elif isinstance(value, Concat): |
88 | | - for part in value.parts: |
89 | | - self.visit_rhs(part) |
90 | | - elif isinstance(value, SwitchValue): |
91 | | - self.visit_rhs(value.test) |
92 | | - for _patterns, elem in value.cases: |
93 | | - self.visit_rhs(elem) |
94 | | - elif isinstance(value, MemoryData._Row): |
95 | | - raise ValueError(f"Value {value!r} can only be used in simulator processes") |
96 | | - else: |
97 | | - assert False # :nocov: |
| 40 | + _check_rhs(obj) |
| 41 | + elif isinstance(stmt, Switch): |
| 42 | + _check_rhs(stmt.test) |
| 43 | + for _patterns, stmts, _src_loc in stmt.cases: |
| 44 | + _check_stmt(stmts) |
| 45 | + elif isinstance(stmt, _LateBoundStatement): |
| 46 | + pass |
| 47 | + else: |
| 48 | + assert False # :nocov: |
| 49 | + |
| 50 | +def _check_lhs(value): |
| 51 | + if isinstance(value, Operator) and value.operator in ("u", "s"): |
| 52 | + _check_lhs(value.operands[0]) |
| 53 | + elif isinstance(value, (Signal, ClockSignal, ResetSignal)): |
| 54 | + pass |
| 55 | + elif isinstance(value, Slice): |
| 56 | + _check_lhs(value.value) |
| 57 | + elif isinstance(value, Part): |
| 58 | + _check_lhs(value.value) |
| 59 | + _check_rhs(value.offset) |
| 60 | + elif isinstance(value, Concat): |
| 61 | + for part in value.parts: |
| 62 | + _check_lhs(part) |
| 63 | + elif isinstance(value, SwitchValue): |
| 64 | + _check_rhs(value.test) |
| 65 | + for _patterns, elem in value.cases: |
| 66 | + _check_lhs(elem) |
| 67 | + elif isinstance(value, MemoryData._Row): |
| 68 | + raise ValueError(f"Value {value!r} can only be used in simulator processes") |
| 69 | + else: |
| 70 | + raise ValueError(f"Value {value!r} cannot be assigned to") |
| 71 | + |
| 72 | +def _check_rhs(value): |
| 73 | + if isinstance(value, (Const, Signal, ClockSignal, ResetSignal, Initial, AnyValue)): |
| 74 | + pass |
| 75 | + elif isinstance(value, Operator): |
| 76 | + for op in value.operands: |
| 77 | + _check_rhs(op) |
| 78 | + elif isinstance(value, Slice): |
| 79 | + _check_rhs(value.value) |
| 80 | + elif isinstance(value, Part): |
| 81 | + _check_rhs(value.value) |
| 82 | + _check_rhs(value.offset) |
| 83 | + elif isinstance(value, Concat): |
| 84 | + for part in value.parts: |
| 85 | + _check_rhs(part) |
| 86 | + elif isinstance(value, SwitchValue): |
| 87 | + _check_rhs(value.test) |
| 88 | + for _patterns, elem in value.cases: |
| 89 | + _check_rhs(elem) |
| 90 | + elif isinstance(value, MemoryData._Row): |
| 91 | + raise ValueError(f"Value {value!r} can only be used in simulator processes") |
| 92 | + else: |
| 93 | + assert False # :nocov: |
98 | 94 |
|
99 | 95 |
|
100 | 96 | class _ModuleBuilderProxy: |
@@ -632,16 +628,27 @@ def _add_statement(self, assigns, domain, depth): |
632 | 628 |
|
633 | 629 | stmt._MustUse__used = True |
634 | 630 |
|
635 | | - visitor = _Visitor() |
636 | | - visitor.visit_stmt(stmt) |
637 | | - for signal in visitor.driven_signals: |
638 | | - if signal not in self._driving: |
639 | | - self._driving[signal] = domain |
640 | | - elif self._driving[signal] != domain: |
641 | | - cd_curr = self._driving[signal] |
642 | | - raise SyntaxError( |
643 | | - f"Driver-driver conflict: trying to drive {signal!r} from d.{domain}, but it is " |
644 | | - f"already driven from d.{cd_curr}") |
| 631 | + _check_stmt(stmt) |
| 632 | + |
| 633 | + lhs_masks = LHSMaskCollector() |
| 634 | + # This is an opportunistic early check — not much harm skipping it, since |
| 635 | + # the whole-design check will be later done in NIR emitter. |
| 636 | + if not isinstance(stmt, _LateBoundStatement): |
| 637 | + lhs_masks.visit_stmt(stmt) |
| 638 | + |
| 639 | + for sig, mask in lhs_masks.masks(): |
| 640 | + if sig not in self._driving: |
| 641 | + self._driving[sig] = [None] * len(sig) |
| 642 | + sig_domain = self._driving[sig] |
| 643 | + for bit in range(len(sig)): |
| 644 | + if not (mask & (1 << bit)): |
| 645 | + continue |
| 646 | + if sig_domain[bit] is None: |
| 647 | + sig_domain[bit] = domain |
| 648 | + if sig_domain[bit] != domain: |
| 649 | + raise SyntaxError( |
| 650 | + f"Driver-driver conflict: trying to drive {sig!r} bit {bit} from d.{domain}, but it is " |
| 651 | + f"already driven from d.{sig_domain[bit]}") |
645 | 652 |
|
646 | 653 | self._statements.setdefault(domain, []).append(stmt) |
647 | 654 |
|
|
0 commit comments