-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib.io: rework TSTriple/Tristate interface to use pin_layout/Pin.
whitequark
committed
Apr 15, 2019
1 parent
50fa251
commit 287a053
Showing
4 changed files
with
216 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,92 @@ | ||
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
shape
is not defined here, causing: