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: - if pin.dir == "i": - pin_fragments.append((pin.name, self.get_input(pin, port))) - if pin.dir == "o": - pin_fragments.append((pin.name, self.get_output(pin, port))) - if pin.dir == "io": - pin_fragments.append((pin.name, self.get_tristate(pin, port))) - for pin, p_port, n_port in self._dp_pins: - if pin.dir == "i": - pin_fragments.append((pin.name, 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))) - 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: + 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)) + fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name)) + + for pin, port, extras in self.iter_single_ended_pins(): + if pin.dir == "i": + add_pin_fragment(pin, self.get_input(pin, port, extras)) + if pin.dir == "o": + add_pin_fragment(pin, self.get_output(pin, port, extras)) + if pin.dir == "io": + 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": + add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port)) + if pin.dir == "o": + add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port)) + if pin.dir == "io": + 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_ports(self): - for port, pins, extras in self._ports: - yield port + 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_port_constraints(self): - for port, pins, extras in self._ports: - yield (port.name, pins, 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 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, 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):