|
13 | 13 | from ._ir import * |
14 | 14 | from ._cd import * |
15 | 15 | from ._xfrm import * |
| 16 | +from ._mem import MemoryData |
16 | 17 |
|
17 | 18 |
|
18 | 19 | __all__ = ["SyntaxError", "SyntaxWarning", "Module"] |
19 | 20 |
|
20 | 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): |
| 34 | + for chunk in stmt.message._chunks: |
| 35 | + if not isinstance(chunk, str): |
| 36 | + 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: |
| 98 | + |
| 99 | + |
21 | 100 | class _ModuleBuilderProxy: |
22 | 101 | def __init__(self, builder, depth): |
23 | 102 | object.__setattr__(self, "_builder", builder) |
@@ -545,15 +624,16 @@ def _add_statement(self, assigns, domain, depth): |
545 | 624 |
|
546 | 625 | stmt._MustUse__used = True |
547 | 626 |
|
548 | | - if isinstance(stmt, Assign): |
549 | | - for signal in stmt._lhs_signals(): |
550 | | - if signal not in self._driving: |
551 | | - self._driving[signal] = domain |
552 | | - elif self._driving[signal] != domain: |
553 | | - cd_curr = self._driving[signal] |
554 | | - raise SyntaxError( |
555 | | - f"Driver-driver conflict: trying to drive {signal!r} from d.{domain}, but it is " |
556 | | - f"already driven from d.{cd_curr}") |
| 627 | + visitor = _Visitor() |
| 628 | + visitor.visit_stmt(stmt) |
| 629 | + for signal in visitor.driven_signals: |
| 630 | + if signal not in self._driving: |
| 631 | + self._driving[signal] = domain |
| 632 | + elif self._driving[signal] != domain: |
| 633 | + cd_curr = self._driving[signal] |
| 634 | + raise SyntaxError( |
| 635 | + f"Driver-driver conflict: trying to drive {signal!r} from d.{domain}, but it is " |
| 636 | + f"already driven from d.{cd_curr}") |
557 | 637 |
|
558 | 638 | self._statements.setdefault(domain, []).append(stmt) |
559 | 639 |
|
@@ -595,10 +675,14 @@ def elaborate(self, platform): |
595 | 675 | for domain, statements in self._statements.items(): |
596 | 676 | statements = resolve_statements(statements) |
597 | 677 | fragment.add_statements(domain, statements) |
598 | | - for signal in statements._lhs_signals(): |
| 678 | + visitor = _Visitor() |
| 679 | + visitor.visit_stmt(statements) |
| 680 | + for signal in visitor.driven_signals: |
599 | 681 | fragment.add_driver(signal, domain) |
600 | 682 | fragment.add_statements("comb", self._top_comb_statements) |
601 | | - for signal in self._top_comb_statements._lhs_signals(): |
| 683 | + visitor = _Visitor() |
| 684 | + visitor.visit_stmt(self._top_comb_statements) |
| 685 | + for signal in visitor.driven_signals: |
602 | 686 | fragment.add_driver(signal, "comb") |
603 | 687 | fragment.add_domains(self._domains.values()) |
604 | 688 | fragment.generated.update(self._generated) |
|
0 commit comments