from .hdl.xfrm import ResetInserter, CEInserter
from .lib.cdc import MultiReg
-from .lib.io import TSTriple
+# from .lib.io import
from ...hdl.ast import *
from ...hdl.mem import Memory as NativeMemory
from ...hdl.ir import Fragment, Instance
-from ...lib.io import TSTriple as NativeTSTriple, Tristate as NativeTristate
from .module import Module as CompatModule
__all__ = ["TSTriple", "Instance", "Memory", "READ_FIRST", "WRITE_FIRST", "NO_CHANGE"]
-class CompatTSTriple(NativeTSTriple):
+class TSTriple:
def __init__(self, bits_sign=None, min=None, max=None, reset_o=0, reset_oe=0, reset_i=0,
name=None):
- super().__init__(shape=bits_sign, min=min, max=max,
- reset_o=reset_o, reset_oe=reset_oe, reset_i=reset_i,
- name=name)
+ self.o = Signal(shape, min=min, max=max, reset=reset_o,
+ name=None if name is None else name + "_o")
+ self.oe = Signal(reset=reset_oe,
+ name=None if name is None else name + "_oe")
+ self.i = Signal(shape, min=min, max=max, reset=reset_i,
+ name=None if name is None else name + "_i")
+ def __len__(self):
+ return len(self.o)
-class CompatTristate(NativeTristate):
- def __init__(self, target, o, oe, i=None):
- triple = TSTriple()
- triple.o = o
- triple.oe = oe
- if i is not None:
- triple.i = i
- super().__init__(triple, target)
+ def elaborate(self, platform):
+ return Fragment()
- @property
- @deprecated("instead of `Tristate.target`, use `Tristate.io`")
- def target(self):
- return self.io
+ def get_tristate(self, io):
+ return Tristate(io, self.o, self.oe, self.i)
-TSTriple = CompatTSTriple
-Tristate = CompatTristate
+class Tristate:
+ def __init__(self, target, o, oe, i=None):
+ self.target = target
+ self.triple = TSTriple()
+ self.triple.o = o
+ self.triple.oe = oe
+ if i is not None:
+ self.triple.i = i
+
+ def elaborate(self, platform):
+ if hasattr(platform, "get_tristate"):
+ return platform.get_tristate(self.triple, self.io)
+
+ m = Module()
+ m.d.comb += self.triple.i.eq(self.io)
+ m.submodules += Instance("$tribuf",
+ p_WIDTH=len(self.io),
+ i_EN=self.triple.oe,
+ i_A=self.triple.o,
+ o_Y=self.io,
+ )
+
+ f = m.elaborate(platform)
+ f.flatten = True
+ return f
(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3)
from .. import *
+from ..hdl.rec import *
-__all__ = ["TSTriple", "Tristate"]
+__all__ = ["pin_layout", "Pin"]
-class TSTriple:
- def __init__(self, shape=None, min=None, max=None, reset_o=0, reset_oe=0, reset_i=0,
- name=None):
- self.o = Signal(shape, min=min, max=max, reset=reset_o,
- name=None if name is None else name + "_o")
- self.oe = Signal(reset=reset_oe,
- name=None if name is None else name + "_oe")
- self.i = Signal(shape, min=min, max=max, reset=reset_i,
- name=None if name is None else name + "_i")
+def pin_layout(width, dir, xdr=1):
+ """
+ Layout of the platform interface of a pin or several pins, which may be used inside
+ user-defined records.
- def __len__(self):
- return len(self.o)
+ See :class:`Pin` for details.
+ """
+ if not isinstance(width, int) or width < 1:
+ raise TypeError("Width must be a positive integer, not '{!r}'"
+ .format(width))
+ if dir not in ("i", "o", "io"):
+ raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not '{!r}'"""
+ .format(dir))
+ if not isinstance(xdr, int) or xdr < 1:
+ raise TypeError("Gearing ratio must be a positive integer, not '{!r}'"
+ .format(xdr))
- def elaborate(self, platform):
- return Fragment()
+ fields = []
+ if dir in ("i", "io"):
+ if xdr == 1:
+ fields.append(("i", width))
+ else:
+ for n in range(xdr):
+ fields.append(("i{}".format(n), width))
+ if dir in ("o", "io"):
+ if xdr == 1:
+ fields.append(("o", width))
+ else:
+ for n in range(xdr):
+ fields.append(("o{}".format(n), width))
+ if dir == "io":
+ fields.append(("oe", 1))
+ return Layout(fields)
- def get_tristate(self, io):
- return Tristate(self, io)
+class Pin(Record):
+ """
+ An interface to an I/O buffer or a group of them that provides uniform access to input, output,
+ or tristate buffers that may include a 1:n gearbox. (A 1:2 gearbox is typically called "DDR".)
-class Tristate:
- def __init__(self, triple, io):
- self.triple = triple
- self.io = io
+ A :class:`Pin` is identical to a :class:`Record` that uses the corresponding :meth:`pin_layout`
+ except that it allos accessing the parameters like ``width`` as attributes. It is legal to use
+ a plain :class:`Record` anywhere a :class:`Pin` is used, provided that these attributes are
+ not necessary.
- def elaborate(self, platform):
- if hasattr(platform, "get_tristate"):
- return platform.get_tristate(self.triple, self.io)
+ Parameters
+ ----------
+ width : int
+ Width of the ``i``/``iN`` and ``o``/``oN`` signals.
+ dir : ``"i"``, ``"o"``, ``"io"``
+ Direction of the buffers. If ``"i"`` is specified, only the ``i``/``iN`` signals are
+ present. If ``"o"`` is specified, only the ``o``/``oN`` signals are present. If ``"io"``
+ is specified, both the ``i``/``iN`` and ``o``/``oN`` signals are present, and an ``oe``
+ signal is present.
+ xdr : int
+ Gearbox ratio. If equal to 1, the I/O buffer is SDR, and only ``i``/``o`` signals are
+ present. If greater than 1, the I/O buffer includes a gearbox, and ``iN``/``oN`` signals
+ are present instead, where ``N in range(0, N)``. For example, if ``xdr=2``, the I/O buffer
+ is DDR; the signal ``i0`` reflects the value at the rising edge, and the signal ``i1``
+ reflects the value at the falling edge.
- m = Module()
- m.d.comb += self.triple.i.eq(self.io)
- m.submodules += Instance("$tribuf",
- p_WIDTH=len(self.io),
- i_EN=self.triple.oe,
- i_A=self.triple.o,
- o_Y=self.io,
- )
+ Attributes
+ ----------
+ i : Signal, out
+ I/O buffer input, without gearing. Present if ``dir="i"`` or ``dir="io"``, and ``xdr`` is
+ equal to 1.
+ i0, i1, ... : Signal, out
+ I/O buffer inputs, with gearing. Present if ``dir="i"`` or ``dir="io"``, and ``xdr`` is
+ greater than 1.
+ o : Signal, in
+ I/O buffer output, without gearing. Present if ``dir="o"`` or ``dir="io"``, and ``xdr`` is
+ equal to 1.
+ o0, o1, ... : Signal, in
+ I/O buffer outputs, with gearing. Present if ``dir="o"`` or ``dir="io"``, and ``xdr`` is
+ greater than 1.
+ oe : Signal, in
+ I/O buffer output enable. Present if ``dir="io"``. Buffers generally cannot change
+ direction more than once per cycle, so at most one output enable signal is present.
+ """
+ def __init__(self, width, dir, xdr=1):
+ self.width = width
+ self.dir = dir
+ self.xdr = xdr
- f = m.elaborate(platform)
- f.flatten = True
- return f
+ super().__init__(pin_layout(self.width, self.dir, self.xdr))
--- /dev/null
+from .tools import *
+from ..hdl.ast import *
+from ..hdl.rec import *
+from ..lib.io import *
+
+
+class PinLayoutSDRTestCase(FHDLTestCase):
+ def test_pin_layout_i(self):
+ layout_1 = pin_layout(1, dir="i")
+ self.assertEqual(layout_1.fields, {
+ "i": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="i")
+ self.assertEqual(layout_2.fields, {
+ "i": (2, DIR_NONE),
+ })
+
+ def test_pin_layout_o(self):
+ layout_1 = pin_layout(1, dir="o")
+ self.assertEqual(layout_1.fields, {
+ "o": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="o")
+ self.assertEqual(layout_2.fields, {
+ "o": (2, DIR_NONE),
+ })
+
+ def test_pin_layout_io(self):
+ layout_1 = pin_layout(1, dir="io")
+ self.assertEqual(layout_1.fields, {
+ "i": (1, DIR_NONE),
+ "o": (1, DIR_NONE),
+ "oe": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="io")
+ self.assertEqual(layout_2.fields, {
+ "i": (2, DIR_NONE),
+ "o": (2, DIR_NONE),
+ "oe": (1, DIR_NONE),
+ })
+
+
+class PinLayoutDDRTestCase(FHDLTestCase):
+ def test_pin_layout_i(self):
+ layout_1 = pin_layout(1, dir="i", xdr=2)
+ self.assertEqual(layout_1.fields, {
+ "i0": (1, DIR_NONE),
+ "i1": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="i", xdr=2)
+ self.assertEqual(layout_2.fields, {
+ "i0": (2, DIR_NONE),
+ "i1": (2, DIR_NONE),
+ })
+
+ def test_pin_layout_o(self):
+ layout_1 = pin_layout(1, dir="o", xdr=2)
+ self.assertEqual(layout_1.fields, {
+ "o0": (1, DIR_NONE),
+ "o1": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="o", xdr=2)
+ self.assertEqual(layout_2.fields, {
+ "o0": (2, DIR_NONE),
+ "o1": (2, DIR_NONE),
+ })
+
+ def test_pin_layout_io(self):
+ layout_1 = pin_layout(1, dir="io", xdr=2)
+ self.assertEqual(layout_1.fields, {
+ "i0": (1, DIR_NONE),
+ "i1": (1, DIR_NONE),
+ "o0": (1, DIR_NONE),
+ "o1": (1, DIR_NONE),
+ "oe": (1, DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="io", xdr=2)
+ self.assertEqual(layout_2.fields, {
+ "i0": (2, DIR_NONE),
+ "i1": (2, DIR_NONE),
+ "o0": (2, DIR_NONE),
+ "o1": (2, DIR_NONE),
+ "oe": (1, DIR_NONE),
+ })
+
+
+class PinTestCase(FHDLTestCase):
+ def test_attributes(self):
+ pin = Pin(2, dir="io", xdr=2)
+ self.assertEqual(pin.width, 2)
+ self.assertEqual(pin.dir, "io")
+ self.assertEqual(pin.xdr, 2)