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),