From db2617dbe15dedcbb01995491a60708a0de584b1 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 20 Dec 2018 23:38:01 +0000 Subject: [PATCH] ir: allow non-Signals in Instance ports. --- examples/inst.py | 1 + nmigen/back/rtlil.py | 11 ++++------- nmigen/hdl/ir.py | 27 ++++++++++++++++----------- nmigen/hdl/xfrm.py | 17 +++++++++++++---- nmigen/test/test_hdl_ir.py | 12 +++++++++--- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/examples/inst.py b/examples/inst.py index 9b011d2..5bfdaaa 100644 --- a/examples/inst.py +++ b/examples/inst.py @@ -16,6 +16,7 @@ class System: i_d_adr =self.adr, i_d_dat_r=self.dat_r, o_d_dat_w=self.dat_w, + i_d_we =self.we, ) return m.lower(platform) diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index e46d929..a4c8a61 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -567,15 +567,12 @@ class _StatementCompiler(xfrm.AbstractStatementTransformer): def convert_fragment(builder, fragment, name, top): - if fragment.black_box is not None: + if isinstance(fragment, ir.Instance): port_map = OrderedDict() - for signal in fragment.ports: - port_map["\\{}".format(fragment.port_names[signal])] = signal + for port_name, value in fragment.named_ports.items(): + port_map["\\{}".format(port_name)] = value - return "\\{}".format(fragment.black_box), port_map - else: - assert not fragment.port_names - assert not fragment.parameters + return "\\{}".format(fragment.type), port_map with builder.module(name or "anonymous", attrs={"top": 1} if top else {}) as module: compiler_state = _ValueCompilerState(module) diff --git a/nmigen/hdl/ir.py b/nmigen/hdl/ir.py index adc5bc4..3d90f6b 100644 --- a/nmigen/hdl/ir.py +++ b/nmigen/hdl/ir.py @@ -15,10 +15,6 @@ class DriverConflict(UserWarning): class Fragment: def __init__(self): - self.black_box = None - self.port_names = SignalDict() - self.parameters = OrderedDict() - self.ports = SignalDict() self.drivers = OrderedDict() self.statements = [] @@ -241,8 +237,11 @@ class Fragment: def _propagate_ports(self, ports): # Collect all signals we're driving (on LHS of statements), and signals we're using # (on RHS of statements, or in clock domains). - self_driven = union(s._lhs_signals() for s in self.statements) or SignalSet() - self_used = union(s._rhs_signals() for s in self.statements) or SignalSet() + self_driven = union((s._lhs_signals() for s in self.statements), start=SignalSet()) + self_used = union((s._rhs_signals() for s in self.statements), start=SignalSet()) + if isinstance(self, Instance): + self_used |= union((p._rhs_signals() for p in self.named_ports.values()), + start=SignalSet()) for domain, _ in self.iter_sync(): cd = self.domains[domain] self_used.add(cd.clk) @@ -296,18 +295,24 @@ class Fragment: class Instance(Fragment): def __init__(self, type, **kwargs): super().__init__() - self.black_box = type + + self.type = type + self.parameters = OrderedDict() + self.named_ports = OrderedDict() + for kw, arg in kwargs.items(): if kw.startswith("p_"): self.parameters[kw[2:]] = arg elif kw.startswith("i_"): - self.port_names[arg] = kw[2:] - self.add_ports(arg, dir="i") + self.named_ports[kw[2:]] = arg + # Unlike with "o_" and "io_", "i_" ports can be assigned an arbitrary value; + # this includes unresolved ClockSignals etc. We rely on Fragment.prepare to + # populate fragment ports for these named ports. elif kw.startswith("o_"): - self.port_names[arg] = kw[2:] + self.named_ports[kw[2:]] = arg self.add_ports(arg, dir="o") elif kw.startswith("io_"): - self.port_names[arg] = kw[3:] + self.named_ports[kw[3:]] = arg self.add_ports(arg, dir="io") else: raise NameError("Instance argument '{}' does not start with p_, i_, o_, or io_" diff --git a/nmigen/hdl/xfrm.py b/nmigen/hdl/xfrm.py index 9dc0e06..3aa10cb 100644 --- a/nmigen/hdl/xfrm.py +++ b/nmigen/hdl/xfrm.py @@ -179,6 +179,13 @@ class FragmentTransformer: for port, dir in fragment.ports.items(): new_fragment.add_ports(port, dir=dir) + def map_named_ports(self, fragment, new_fragment): + if hasattr(self, "on_value"): + for name, value in fragment.named_ports.items(): + new_fragment.named_ports[name] = self.on_value(value) + else: + new_fragment.named_ports = OrderedDict(fragment.named_ports.items()) + def map_domains(self, fragment, new_fragment): for domain in fragment.iter_domains(): new_fragment.add_domains(fragment.domains[domain]) @@ -194,10 +201,12 @@ class FragmentTransformer: new_fragment.add_driver(signal, domain) def on_fragment(self, fragment): - new_fragment = Fragment() - new_fragment.black_box = fragment.black_box - new_fragment.parameters = OrderedDict(fragment.parameters) - new_fragment.port_names = SignalDict(fragment.port_names.items()) + if isinstance(fragment, Instance): + new_fragment = Instance(fragment.type) + new_fragment.parameters = OrderedDict(fragment.parameters) + self.map_named_ports(fragment, new_fragment) + else: + new_fragment = Fragment() self.map_ports(fragment, new_fragment) self.map_subfragments(fragment, new_fragment) self.map_domains(fragment, new_fragment) diff --git a/nmigen/test/test_hdl_ir.py b/nmigen/test/test_hdl_ir.py index a90aac0..5f5081d 100644 --- a/nmigen/test/test_hdl_ir.py +++ b/nmigen/test/test_hdl_ir.py @@ -412,11 +412,17 @@ class InstanceTestCase(FHDLTestCase): rst = Signal() stb = Signal() pins = Signal(8) - inst = Instance("cpu", p_RESET=0x1234, i_rst=rst, o_stb=stb, io_pins=pins) - self.assertEqual(inst.black_box, "cpu") + inst = Instance("cpu", + p_RESET=0x1234, + i_clk=ClockSignal(), + i_rst=rst, + o_stb=stb, + io_pins=pins + ) + self.assertEqual(inst.type, "cpu") self.assertEqual(inst.parameters, OrderedDict([("RESET", 0x1234)])) + self.assertEqual(list(inst.named_ports.keys()), ["clk", "rst", "stb", "pins"]) self.assertEqual(inst.ports, SignalDict([ - (rst, "i"), (stb, "o"), (pins, "io"), ])) -- 2.30.2