lib.io: rework TSTriple/Tristate interface to use pin_layout/Pin.

This commit is contained in:
whitequark 2019-04-15 16:27:23 +00:00
parent 50fa2516fa
commit 287a0531b3
4 changed files with 215 additions and 53 deletions

View file

@ -7,4 +7,4 @@ from .hdl.rec import Record
from .hdl.xfrm import ResetInserter, CEInserter from .hdl.xfrm import ResetInserter, CEInserter
from .lib.cdc import MultiReg from .lib.cdc import MultiReg
from .lib.io import TSTriple # from .lib.io import

View file

@ -4,38 +4,57 @@ from ...tools import deprecated, extend
from ...hdl.ast import * from ...hdl.ast import *
from ...hdl.mem import Memory as NativeMemory from ...hdl.mem import Memory as NativeMemory
from ...hdl.ir import Fragment, Instance from ...hdl.ir import Fragment, Instance
from ...lib.io import TSTriple as NativeTSTriple, Tristate as NativeTristate
from .module import Module as CompatModule from .module import Module as CompatModule
__all__ = ["TSTriple", "Instance", "Memory", "READ_FIRST", "WRITE_FIRST", "NO_CHANGE"] __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, def __init__(self, bits_sign=None, min=None, max=None, reset_o=0, reset_oe=0, reset_i=0,
name=None): name=None):
super().__init__(shape=bits_sign, min=min, max=max, self.o = Signal(shape, min=min, max=max, reset=reset_o,
reset_o=reset_o, reset_oe=reset_oe, reset_i=reset_i, name=None if name is None else name + "_o")
name=name) 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)
def elaborate(self, platform):
return Fragment()
def get_tristate(self, io):
return Tristate(io, self.o, self.oe, self.i)
class CompatTristate(NativeTristate): class Tristate:
def __init__(self, target, o, oe, i=None): def __init__(self, target, o, oe, i=None):
triple = TSTriple() self.target = target
triple.o = o self.triple = TSTriple()
triple.oe = oe self.triple.o = o
self.triple.oe = oe
if i is not None: if i is not None:
triple.i = i self.triple.i = i
super().__init__(triple, target)
@property def elaborate(self, platform):
@deprecated("instead of `Tristate.target`, use `Tristate.io`") if hasattr(platform, "get_tristate"):
def target(self): return platform.get_tristate(self.triple, self.io)
return 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,
)
TSTriple = CompatTSTriple f = m.elaborate(platform)
Tristate = CompatTristate f.flatten = True
return f
(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3) (READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3)

View file

@ -1,47 +1,92 @@
from .. import * from .. import *
from ..hdl.rec import *
__all__ = ["TSTriple", "Tristate"] __all__ = ["pin_layout", "Pin"]
class TSTriple: def pin_layout(width, dir, xdr=1):
def __init__(self, shape=None, min=None, max=None, reset_o=0, reset_oe=0, reset_i=0, """
name=None): Layout of the platform interface of a pin or several pins, which may be used inside
self.o = Signal(shape, min=min, max=max, reset=reset_o, user-defined records.
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): See :class:`Pin` for details.
return len(self.o) """
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): fields = []
return Fragment() if dir in ("i", "io"):
if xdr == 1:
def get_tristate(self, io): fields.append(("i", width))
return Tristate(self, io) 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)
class Tristate: class Pin(Record):
def __init__(self, triple, io): """
self.triple = triple An interface to an I/O buffer or a group of them that provides uniform access to input, output,
self.io = io or tristate buffers that may include a 1:n gearbox. (A 1:2 gearbox is typically called "DDR".)
def elaborate(self, platform): A :class:`Pin` is identical to a :class:`Record` that uses the corresponding :meth:`pin_layout`
if hasattr(platform, "get_tristate"): except that it allos accessing the parameters like ``width`` as attributes. It is legal to use
return platform.get_tristate(self.triple, self.io) a plain :class:`Record` anywhere a :class:`Pin` is used, provided that these attributes are
not necessary.
m = Module() Parameters
m.d.comb += self.triple.i.eq(self.io) ----------
m.submodules += Instance("$tribuf", width : int
p_WIDTH=len(self.io), Width of the ``i``/``iN`` and ``o``/``oN`` signals.
i_EN=self.triple.oe, dir : ``"i"``, ``"o"``, ``"io"``
i_A=self.triple.o, Direction of the buffers. If ``"i"`` is specified, only the ``i``/``iN`` signals are
o_Y=self.io, 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.
f = m.elaborate(platform) Attributes
f.flatten = True ----------
return f 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
super().__init__(pin_layout(self.width, self.dir, self.xdr))

View file

@ -0,0 +1,98 @@
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)