Skip to content

Commit

Permalink
lib.io: rework TSTriple/Tristate interface to use pin_layout/Pin.
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Apr 15, 2019
1 parent 50fa251 commit 287a053
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 54 deletions.
2 changes: 1 addition & 1 deletion nmigen/__init__.py
Expand Up @@ -7,4 +7,4 @@
from .hdl.xfrm import ResetInserter, CEInserter

from .lib.cdc import MultiReg
from .lib.io import TSTriple
# from .lib.io import
57 changes: 38 additions & 19 deletions nmigen/compat/fhdl/specials.py
Expand Up @@ -4,38 +4,57 @@
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,

This comment has been minimized.

Copy link
@peteut

peteut Apr 22, 2019

Contributor

shape is not defined here, causing:

self = <nmigen.compat.fhdl.specials.TSTriple object at 0x7fbee9c08208>, bits_sign = None, min = None, max = None, reset_o = 0, reset_oe = 0, reset_i = 0, name = None

    def __init__(self, bits_sign=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")
E       NameError: name 'shape' is not defined

This comment has been minimized.

Copy link
@whitequark

whitequark Apr 22, 2019

Author Contributor

Fixed

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)
Expand Down
113 changes: 79 additions & 34 deletions nmigen/lib/io.py
@@ -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))
98 changes: 98 additions & 0 deletions nmigen/test/test_lib_io.py
@@ -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)

0 comments on commit 287a053

Please sign in to comment.