|
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