From 8471a04de9449b8cfb1641c893b240e5f7cb69be Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 3 Jun 2019 04:39:05 +0000 Subject: [PATCH] build.{dsl,plat,res}: allow dir="oe". Although a dir="oe" pin is generally equivalent to dir="io" pin with the i* signal(s) disconnected, they are not equivalent, because some pins may not be able to support input buffers at all, either because there are no input buffers, or because the input buffers are consumed by some other resource. E.g. this can happen on iCE40 when the input buffer is consumed by a PLL. --- nmigen/build/dsl.py | 2 +- nmigen/build/plat.py | 25 +++++++++++++++++++++++-- nmigen/build/res.py | 10 +++++----- nmigen/test/test_build_dsl.py | 2 +- nmigen/test/test_build_res.py | 5 +++-- nmigen/vendor/fpga/lattice_ice40.py | 13 ++++++++++++- 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/nmigen/build/dsl.py b/nmigen/build/dsl.py index 7b3c38d..222193a 100644 --- a/nmigen/build/dsl.py +++ b/nmigen/build/dsl.py @@ -9,7 +9,7 @@ class Pins: self.names = names.split() if dir not in ("i", "o", "io"): - raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not {!r}" + raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not {!r}" .format(dir)) self.dir = dir diff --git a/nmigen/build/plat.py b/nmigen/build/plat.py index f2ff789..0dfa752 100644 --- a/nmigen/build/plat.py +++ b/nmigen/build/plat.py @@ -127,16 +127,20 @@ class Platform(ConstraintManager, metaclass=ABCMeta): 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 == "oe": + add_pin_fragment(pin, self.get_tristate(pin, port, extras)) if pin.dir == "io": - add_pin_fragment(pin, self.get_input(pin, port, extras)) + add_pin_fragment(pin, self.get_input_output(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": + if pin.dir == "oe": add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port)) + if pin.dir == "io": + add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port)) return self.toolchain_prepare(fragment, name, **kwargs) @@ -187,6 +191,19 @@ class Platform(ConstraintManager, metaclass=ABCMeta): self._check_feature("single-ended tristate", pin, extras, valid_xdrs=(0,), valid_extras=None) + m = Module() + m.submodules += Instance("$tribuf", + p_WIDTH=pin.width, + i_EN=pin.oe, + i_A=pin.o, + o_Y=port, + ) + return m + + def get_input_output(self, pin, port, extras): + self._check_feature("single-ended input/output", pin, extras, + valid_xdrs=(0,), valid_extras=None) + m = Module() m.submodules += Instance("$tribuf", p_WIDTH=pin.width, @@ -209,6 +226,10 @@ class Platform(ConstraintManager, metaclass=ABCMeta): self._check_feature("differential tristate", pin, extras, valid_xdrs=(), valid_extras=None) + def get_diff_input_output(self, pin, p_port, n_port, extras): + self._check_feature("differential input/output", pin, extras, + valid_xdrs=(), valid_extras=None) + class TemplatedPlatform(Platform): file_templates = abstractproperty() diff --git a/nmigen/build/res.py b/nmigen/build/res.py index 88e6006..4985293 100644 --- a/nmigen/build/res.py +++ b/nmigen/build/res.py @@ -84,14 +84,14 @@ class ConstraintManager: dir = subsignal.io[0].dir if xdr is None: xdr = 0 - if dir not in ("i", "o", "io", "-"): - raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"-\", " - "not {!r}" + if dir not in ("i", "o", "oe", "io", "-"): + raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", " + "or \"-\", not {!r}" .format(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\", from \"io\"" - "to \"o\", or from anything to \"-\"" + "direction can be changed from \"io\" to \"i\", \"o\", or " + "\"oe\", or from anything to \"-\"" .format(subsignal.io[0], subsignal.io[0].dir, dir)) if not isinstance(xdr, int) or xdr < 0: raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}" diff --git a/nmigen/test/test_build_dsl.py b/nmigen/test/test_build_dsl.py index 15ef9ad..a3f75a4 100644 --- a/nmigen/test/test_build_dsl.py +++ b/nmigen/test/test_build_dsl.py @@ -17,7 +17,7 @@ class PinsTestCase(FHDLTestCase): def test_wrong_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\", \"oe\", or \"io\", not 'wrong'"): p = Pins("A0 A1", dir="wrong") diff --git a/nmigen/test/test_build_res.py b/nmigen/test/test_build_res.py index 1455619..c7cff57 100644 --- a/nmigen/test/test_build_res.py +++ b/nmigen/test/test_build_res.py @@ -197,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\", \"io\", or \"-\", not 'wrong'"): + msg="Direction must be one of \"i\", \"o\", \"oe\", \"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\", from \"io\"to \"o\", or from anything " + "can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything " "to \"-\""): user_led = self.cm.request("user_led", 0, dir="i") diff --git a/nmigen/vendor/fpga/lattice_ice40.py b/nmigen/vendor/fpga/lattice_ice40.py index 3f55bc3..0583451 100644 --- a/nmigen/vendor/fpga/lattice_ice40.py +++ b/nmigen/vendor/fpga/lattice_ice40.py @@ -9,7 +9,8 @@ from ...hdl.ir import * from ...build import * -__all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"] +__all__ = ["LatticeICE40Platform", + "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"] class LatticeICE40Platform(TemplatedPlatform): @@ -140,6 +141,16 @@ class LatticeICE40Platform(TemplatedPlatform): return self._get_io_buffer(port, extras, lambda bit: [ # PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED ("p", "PIN_TYPE", 0b1010_00), + ("i", "D_OUT_0", pin.o[bit]), + ("i", "OUTPUT_ENABLE", pin.oe), + ]) + + def get_input_output(self, pin, port, extras): + self._check_feature("single-ended input/output", pin, extras, + valid_xdrs=(0,), valid_extras=True) + return self._get_io_buffer(port, extras, lambda bit: [ + # PIN_OUTPUT_TRISTATE|PIN_INPUT + ("p", "PIN_TYPE", 0b1010_01), ("o", "D_IN_0", pin.i[bit]), ("i", "D_OUT_0", pin.o[bit]), ("i", "OUTPUT_ENABLE", pin.oe), -- 2.30.2