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.
This commit is contained in:
whitequark 2019-06-03 04:39:05 +00:00
parent 1eee7cd76f
commit 6fae06aea9
6 changed files with 45 additions and 12 deletions

View file

@ -9,7 +9,7 @@ class Pins:
self.names = names.split() self.names = names.split()
if dir not in ("i", "o", "io"): 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)) .format(dir))
self.dir = dir self.dir = dir

View file

@ -127,16 +127,20 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
add_pin_fragment(pin, self.get_input(pin, port, extras)) add_pin_fragment(pin, self.get_input(pin, port, extras))
if pin.dir == "o": if pin.dir == "o":
add_pin_fragment(pin, self.get_output(pin, port, extras)) 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": 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(): for pin, p_port, n_port, extras in self.iter_differential_pins():
if pin.dir == "i": if pin.dir == "i":
add_pin_fragment(pin, 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": if pin.dir == "o":
add_pin_fragment(pin, 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": if pin.dir == "oe":
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port)) 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) return self.toolchain_prepare(fragment, name, **kwargs)
@ -187,6 +191,19 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
self._check_feature("single-ended tristate", pin, extras, self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(0,), valid_extras=None) 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 = Module()
m.submodules += Instance("$tribuf", m.submodules += Instance("$tribuf",
p_WIDTH=pin.width, p_WIDTH=pin.width,
@ -209,6 +226,10 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
self._check_feature("differential tristate", pin, extras, self._check_feature("differential tristate", pin, extras,
valid_xdrs=(), valid_extras=None) 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): class TemplatedPlatform(Platform):
file_templates = abstractproperty() file_templates = abstractproperty()

View file

@ -84,14 +84,14 @@ class ConstraintManager:
dir = subsignal.io[0].dir dir = subsignal.io[0].dir
if xdr is None: if xdr is None:
xdr = 0 xdr = 0
if dir not in ("i", "o", "io", "-"): if dir not in ("i", "o", "oe", "io", "-"):
raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"-\", " raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
"not {!r}" "or \"-\", not {!r}"
.format(dir)) .format(dir))
if dir != subsignal.io[0].dir and not (subsignal.io[0].dir == "io" or 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 \"{}\"; " raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; "
"direction can be changed from \"io\" to \"i\", from \"io\"" "direction can be changed from \"io\" to \"i\", \"o\", or "
"to \"o\", or from anything to \"-\"" "\"oe\", or from anything to \"-\""
.format(subsignal.io[0], subsignal.io[0].dir, dir)) .format(subsignal.io[0], subsignal.io[0].dir, dir))
if not isinstance(xdr, int) or xdr < 0: if not isinstance(xdr, int) or xdr < 0:
raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}" raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}"

View file

@ -17,7 +17,7 @@ class PinsTestCase(FHDLTestCase):
def test_wrong_dir(self): def test_wrong_dir(self):
with self.assertRaises(TypeError, 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") p = Pins("A0 A1", dir="wrong")

View file

@ -197,13 +197,14 @@ class ConstraintManagerTestCase(FHDLTestCase):
def test_wrong_request_with_dir(self): def test_wrong_request_with_dir(self):
with self.assertRaises(TypeError, 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") user_led = self.cm.request("user_led", 0, dir="wrong")
def test_wrong_request_with_dir_io(self): def test_wrong_request_with_dir_io(self):
with self.assertRaises(ValueError, with self.assertRaises(ValueError,
msg="Direction of (pins o A0) cannot be changed from \"o\" to \"i\"; direction " 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 \"-\""): "to \"-\""):
user_led = self.cm.request("user_led", 0, dir="i") user_led = self.cm.request("user_led", 0, dir="i")

View file

@ -9,7 +9,8 @@ from ...hdl.ir import *
from ...build import * from ...build import *
__all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"] __all__ = ["LatticeICE40Platform",
"IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"]
class LatticeICE40Platform(TemplatedPlatform): class LatticeICE40Platform(TemplatedPlatform):
@ -140,6 +141,16 @@ class LatticeICE40Platform(TemplatedPlatform):
return self._get_io_buffer(port, extras, lambda bit: [ return self._get_io_buffer(port, extras, lambda bit: [
# PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED # PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED
("p", "PIN_TYPE", 0b1010_00), ("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]), ("o", "D_IN_0", pin.i[bit]),
("i", "D_OUT_0", pin.o[bit]), ("i", "D_OUT_0", pin.o[bit]),
("i", "OUTPUT_ENABLE", pin.oe), ("i", "OUTPUT_ENABLE", pin.oe),