From ef3cd6f3839e3a57ba2b874d9ddb848f507a2c6c Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 3 Jun 2019 03:17:20 +0000 Subject: [PATCH] build.res: allow requesting raw ports, with dir="-". This provides an escape hatch for the case where the nMigen platform code is not flexible enough, and a IO buffer primitive needs to be instantiated directly. --- nmigen/build/res.py | 51 ++++++++++++++++++++--------------- nmigen/test/test_build_res.py | 25 +++++++++++++++-- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/nmigen/build/res.py b/nmigen/build/res.py index 44aa8fb..09707ef 100644 --- a/nmigen/build/res.py +++ b/nmigen/build/res.py @@ -84,13 +84,14 @@ class ConstraintManager: dir = subsignal.io[0].dir if xdr is None: xdr = 1 - if dir not in ("i", "o", "io"): - raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not {!r}" + if dir not in ("i", "o", "io", "-"): + raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"-\", " + "not {!r}" .format(dir)) - if subsignal.io[0].dir != "io" and dir != subsignal.io[0].dir: + if dir != subsignal.io[0].dir and not (subsignal.io[0].dir == "io" or dir == "-"): raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; " - "direction can be changed from \"io\" to \"i\" or from \"io\"" - "to \"o\"" + "direction can be changed from \"io\" to \"i\", from \"io\"" + "to \"o\", or from anything to \"-\"" .format(subsignal.io[0], subsignal.io[0].dir, dir)) if not isinstance(xdr, int) or xdr < 1: raise ValueError("Data rate of {!r} must be a positive integer, not {!r}" @@ -109,14 +110,19 @@ 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)) + port = Record([("io", len(phys))], name=name) if isinstance(phys, DiffPairs): - 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 + port = Record([("p", len(phys)), + ("n", len(phys))], name=name) + if dir == "-": + self._ports.append((subsignal, None, port)) + return port + else: + pin = Pin(len(phys), dir, xdr, name=name) + self._ports.append((subsignal, pin, port)) + return pin + else: assert False # :nocov: @@ -128,38 +134,39 @@ class ConstraintManager: def iter_single_ended_pins(self): for resource, pin, port in self._ports: + if pin is None: + continue if isinstance(resource.io[0], Pins): - yield pin, port, resource.extras + yield pin, port.io, resource.extras def iter_differential_pins(self): for resource, pin, port in self._ports: + if pin is None: + continue if isinstance(resource.io[0], DiffPairs): - p_port, n_port = port - yield pin, p_port, n_port, resource.extras + yield pin, port.p, port.n, resource.extras def iter_ports(self): for resource, pin, port in self._ports: if isinstance(resource.io[0], Pins): - yield port + yield port.io elif isinstance(resource.io[0], DiffPairs): - p_port, n_port = port - yield p_port - yield n_port + yield port.p + yield port.n 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 + yield port.io.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 + yield port.p.name, resource.io[0].p.names, resource.extras if "n" in diff_pins: - yield n_port.name, resource.io[0].n.names, resource.extras + yield port.n.name, resource.io[0].n.names, resource.extras else: assert False diff --git a/nmigen/test/test_build_res.py b/nmigen/test/test_build_res.py index 13b3042..1a14eb4 100644 --- a/nmigen/test/test_build_res.py +++ b/nmigen/test/test_build_res.py @@ -122,6 +122,26 @@ class ConstraintManagerTestCase(FHDLTestCase): ("clk100_0__n", ["H2"], {}), ]) + def test_request_raw(self): + clk50 = self.cm.request("clk50", 0, dir="-") + self.assertIsInstance(clk50, Record) + self.assertIsInstance(clk50.io, Signal) + + ports = list(self.cm.iter_ports()) + self.assertEqual(len(ports), 1) + self.assertIs(ports[0], clk50.io) + + def test_request_raw_diffpairs(self): + clk100 = self.cm.request("clk100", 0, dir="-") + self.assertIsInstance(clk100, Record) + self.assertIsInstance(clk100.p, Signal) + self.assertIsInstance(clk100.n, Signal) + + ports = list(self.cm.iter_ports()) + self.assertEqual(len(ports), 2) + self.assertIs(ports[0], clk100.p) + self.assertIs(ports[1], clk100.n) + def test_add_clock(self): self.cm.add_clock("clk100", 0, 10e6) self.assertEqual(self.cm.clocks["clk100", 0], 10e6) @@ -177,13 +197,14 @@ class ConstraintManagerTestCase(FHDLTestCase): def test_wrong_request_with_dir(self): with self.assertRaises(TypeError, - msg="Direction must be one of \"i\", \"o\" or \"io\", not 'wrong'"): + msg="Direction must be one of \"i\", \"o\", \"io\", or \"-\", not 'wrong'"): user_led = self.cm.request("user_led", 0, dir="wrong") def test_wrong_request_with_dir_io(self): with self.assertRaises(ValueError, msg="Direction of (pins o A0) cannot be changed from \"o\" to \"i\"; direction " - "can be changed from \"io\" to \"i\" or from \"io\"to \"o\""): + "can be changed from \"io\" to \"i\", from \"io\"to \"o\", or from anything " + "to \"-\""): user_led = self.cm.request("user_led", 0, dir="i") def test_wrong_request_with_dir_dict(self): -- 2.30.2