From fb0185437260db0e91d9835be62a98e36a0b694d Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 3 Jun 2019 01:58:43 +0000 Subject: [PATCH] build.{res,plat}: propagate extras to pin fragment factories. This is necessary because on some platforms, like iCE40, extras become parameters on an IO primitive, since the constraint file format is not expressive enough for all of them. --- nmigen/build/plat.py | 70 ++++++++++++++++++++--------------- nmigen/build/res.py | 55 ++++++++++++++++++--------- nmigen/test/test_build_res.py | 18 ++++++--- 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/nmigen/build/plat.py b/nmigen/build/plat.py index 2cd30f4..04e8378 100644 --- a/nmigen/build/plat.py +++ b/nmigen/build/plat.py @@ -116,27 +116,27 @@ class Platform(ConstraintManager, metaclass=ABCMeta): fragment = Fragment.get(fragment, self) - pin_fragments = [] - for pin, port in self._se_pins: + def add_pin_fragment(pin, pin_fragment): + pin_fragment = Fragment.get(pin_fragment, self) + if not isinstance(pin_fragment, Instance): + pin_fragment.flatten = True + fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name)) + + for pin, port, extras in self.iter_single_ended_pins(): if pin.dir == "i": - pin_fragments.append((pin.name, self.get_input(pin, port))) + add_pin_fragment(pin, self.get_input(pin, port, extras)) if pin.dir == "o": - pin_fragments.append((pin.name, self.get_output(pin, port))) + add_pin_fragment(pin, self.get_output(pin, port, extras)) if pin.dir == "io": - pin_fragments.append((pin.name, self.get_tristate(pin, port))) - for pin, p_port, n_port in self._dp_pins: + add_pin_fragment(pin, self.get_input(pin, port, extras)) + + for pin, p_port, n_port, extras in self.iter_differential_pins(): if pin.dir == "i": - pin_fragments.append((pin.name, self.get_diff_input(pin, p_port, n_port))) + add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port)) if pin.dir == "o": - pin_fragments.append((pin.name, self.get_diff_output(pin, p_port, n_port))) + add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port)) if pin.dir == "io": - pin_fragments.append((pin.name, self.get_diff_tristate(pin, p_port, n_port))) - - for pin_name, pin_fragment in pin_fragments: - pin_fragment = Fragment.get(pin_fragment, self) - if not isinstance(pin_fragment, Instance): - pin_fragment.flatten = True - fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin_name)) + add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port)) return self.toolchain_prepare(fragment, name, **kwargs) @@ -155,30 +155,37 @@ class Platform(ConstraintManager, metaclass=ABCMeta): raise NotImplementedError("Platform {} does not support programming" .format(self.__class__.__name__)) - def _check_feature(self, feature, pin, xdrs): - if not xdrs: + def _check_feature(self, feature, pin, extras, valid_xdrs, valid_extras): + if not valid_xdrs: raise NotImplementedError("Platform {} does not support {}" .format(self.__class__.__name__, feature)) - elif pin.xdr not in xdrs: + elif pin.xdr not in valid_xdrs: raise NotImplementedError("Platform {} does not support {} for XDR {}" .format(self.__class__.__name__, feature, pin.xdr)) - def get_input(self, pin, port): - self._check_feature("single-ended input", pin, xdrs=(1,)) + if not valid_extras and extras: + raise NotImplementedError("Platform {} does not support extras for {}" + .format(self.__class__.__name__, feature)) + + def get_input(self, pin, port, extras): + self._check_feature("single-ended input", pin, extras, + valid_xdrs=(1,), valid_extras=None) m = Module() m.d.comb += pin.i.eq(port) return m - def get_output(self, pin, port): - self._check_feature("single-ended output", pin, xdrs=(1,)) + def get_output(self, pin, port, extras): + self._check_feature("single-ended output", pin, extras, + valid_xdrs=(1,), valid_extras=None) m = Module() m.d.comb += port.eq(pin.o) return m - def get_tristate(self, pin, port): - self._check_feature("single-ended tristate", pin, xdrs=(1,)) + def get_tristate(self, pin, port, extras): + self._check_feature("single-ended tristate", pin, extras, + valid_xdrs=(1,), valid_extras=None) m = Module() m.submodules += Instance("$tribuf", @@ -190,14 +197,17 @@ class Platform(ConstraintManager, metaclass=ABCMeta): m.d.comb += pin.i.eq(port) return m - def get_diff_input(self, pin, p_port, n_port): - self._check_feature("differential input", pin, xdrs=()) + def get_diff_input(self, pin, p_port, n_port, extras): + self._check_feature("differential input", pin, extras, + valid_xdrs=(), valid_extras=None) - def get_diff_output(self, pin, p_port, n_port): - self._check_feature("differential output", pin, xdrs=()) + def get_diff_output(self, pin, p_port, n_port, extras): + self._check_feature("differential output", pin, extras, + valid_xdrs=(), valid_extras=None) - def get_diff_tristate(self, pin, p_port, n_port): - self._check_feature("differential tristate", pin, xdrs=()) + def get_diff_tristate(self, pin, p_port, n_port, extras): + self._check_feature("differential tristate", pin, extras, + valid_xdrs=(), valid_extras=None) class TemplatedPlatform(Platform): diff --git a/nmigen/build/res.py b/nmigen/build/res.py index cf810f3..5ac3c64 100644 --- a/nmigen/build/res.py +++ b/nmigen/build/res.py @@ -19,10 +19,7 @@ class ConstraintManager: self.resources = OrderedDict() self.requested = OrderedDict() self.clocks = OrderedDict() - self._ports = [] - self._se_pins = [] - self._dp_pins = [] self.add_resources(resources) for name_number, frequency in clocks: @@ -113,19 +110,12 @@ class ConstraintManager: elif isinstance(subsignal.io[0], (Pins, DiffPairs)): phys = subsignal.io[0] pin = Pin(len(phys), dir, xdr, name=name) - if isinstance(phys, Pins): port = Signal(pin.width, name="{}_io".format(pin.name)) - self._se_pins.append((pin, port)) - self._ports.append((port, phys.names, subsignal.extras)) - if isinstance(phys, DiffPairs): - p_port = Signal(pin.width, name="{}_p".format(pin.name)) - n_port = Signal(pin.width, name="{}_n".format(pin.name)) - self._dp_pins.append((pin, p_port, n_port)) - self._ports.append((p_port, phys.p.names, subsignal.extras)) - self._ports.append((n_port, phys.n.names, subsignal.extras)) - + port = (Signal(pin.width, name="{}_p".format(pin.name)), + Signal(pin.width, name="{}_n".format(pin.name))) + self._ports.append((subsignal, pin, port)) return pin else: assert False # :nocov: @@ -136,13 +126,42 @@ class ConstraintManager: self.requested[resource.name, resource.number] = value return value + def iter_single_ended_pins(self): + for resource, pin, port in self._ports: + if isinstance(resource.io[0], Pins): + yield pin, port, resource.extras + + def iter_differential_pins(self): + for resource, pin, port in self._ports: + if isinstance(resource.io[0], DiffPairs): + p_port, n_port = port + yield pin, p_port, n_port, resource.extras + def iter_ports(self): - for port, pins, extras in self._ports: - yield port + for resource, pin, port in self._ports: + if isinstance(resource.io[0], Pins): + yield port + elif isinstance(resource.io[0], DiffPairs): + p_port, n_port = port + yield p_port + yield n_port + else: + assert False - def iter_port_constraints(self): - for port, pins, extras in self._ports: - yield (port.name, pins, extras) + def iter_port_constraints(self, diff_pins="pn"): + for resource, pin, port in self._ports: + if isinstance(resource.io[0], Pins): + yield port.name, resource.io[0].names, resource.extras + elif isinstance(resource.io[0], DiffPairs): + p_port, n_port = port + # On some FPGAs like iCE40, only one pin out of two in a differential pair may be + # constrained. The other has to be completely disconnected. + if "p" in diff_pins: + yield p_port.name, resource.io[0].p.names, resource.extras + if "n" in diff_pins: + yield n_port.name, resource.io[0].n.names, resource.extras + else: + assert False def iter_clock_constraints(self): for name, number in self.clocks.keys() & self.requested.keys(): diff --git a/nmigen/test/test_build_res.py b/nmigen/test/test_build_res.py index 5ffdac8..26acc7c 100644 --- a/nmigen/test/test_build_res.py +++ b/nmigen/test/test_build_res.py @@ -85,9 +85,9 @@ class ConstraintManagerTestCase(FHDLTestCase): self.assertEqual(ports[1].name, "i2c_0__sda_io") self.assertEqual(ports[1].nbits, 1) - self.assertEqual(self.cm._se_pins, [ - (i2c.scl, scl), - (i2c.sda, sda), + self.assertEqual(list(self.cm.iter_single_ended_pins()), [ + (i2c.scl, scl, {}), + (i2c.sda, sda, {}), ]) self.assertEqual(list(self.cm.iter_port_constraints()), [ ("i2c_0__scl_io", ["N10"], {}), @@ -108,12 +108,18 @@ class ConstraintManagerTestCase(FHDLTestCase): self.assertEqual(n.name, "clk100_0_n") self.assertEqual(n.nbits, clk100.width) - self.assertEqual(self.cm._dp_pins, [ - (clk100, p, n), + self.assertEqual(list(self.cm.iter_differential_pins()), [ + (clk100, p, n, {}), ]) self.assertEqual(list(self.cm.iter_port_constraints()), [ ("clk100_0_p", ["H1"], {}), - ("clk100_0_n", ["H2"], {}) + ("clk100_0_n", ["H2"], {}), + ]) + self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="p")), [ + ("clk100_0_p", ["H1"], {}), + ]) + self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="n")), [ + ("clk100_0_n", ["H2"], {}), ]) def test_add_clock(self): -- 2.30.2