From 2f88d90ad25ed64420f498ccb0feeddefc594e2c Mon Sep 17 00:00:00 2001 From: Wanda Date: Tue, 27 Feb 2024 10:42:57 +0100 Subject: [PATCH] lib.io, build.res: Make `Pin` and related objects interfaces. Fixes #1040. --- amaranth/build/res.py | 47 +++---- amaranth/lib/io.py | 159 +++++++++++----------- tests/test_build_res.py | 21 +-- tests/test_lib_io.py | 288 ++++++++++++++++++++-------------------- 4 files changed, 249 insertions(+), 266 deletions(-) diff --git a/amaranth/build/res.py b/amaranth/build/res.py index e5be84b70..74bccea5c 100644 --- a/amaranth/build/res.py +++ b/amaranth/build/res.py @@ -1,10 +1,6 @@ from collections import OrderedDict -import warnings from ..hdl._ast import * -with warnings.catch_warnings(): - warnings.filterwarnings(action="ignore", category=DeprecationWarning) - from ..hdl.rec import * from ..lib.io import * from ..lib import wiring @@ -106,7 +102,7 @@ def merge_options(subsignal, dir, xdr): .format(subsignal.ios[0], xdr)) return dir, xdr - def resolve(resource, dir, xdr, name, attrs): + def resolve(resource, dir, xdr, path, attrs): for attr_key, attr_value in attrs.items(): if hasattr(attr_value, "__call__"): attr_value = attr_value(self) @@ -117,18 +113,21 @@ def resolve(resource, dir, xdr, name, attrs): attrs[attr_key] = attr_value if isinstance(resource.ios[0], Subsignal): - fields = OrderedDict() + members = OrderedDict() + sig_members = OrderedDict() for sub in resource.ios: - fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name], - name=f"{name}__{sub.name}", + member = resolve(sub, dir[sub.name], xdr[sub.name], + path=path + (sub.name,), attrs={**attrs, **sub.attrs}) - rec = Record([ - (f_name, f.layout) for (f_name, f) in fields.items() - ], fields=fields, name=name) - rec.signature = wiring.Signature({ - f_name: wiring.Out(f.signature) for (f_name, f) in fields.items() - }) - return rec + members[sub.name] = member + sig_members[sub.name] = wiring.Out(member.signature) + signature = wiring.Signature(sig_members) + # Provide members ourselves instead of having the constructor + # create ones for us. + intf = object.__new__(wiring.PureInterface) + intf.signature = signature + intf.__dict__.update(members) + return intf elif isinstance(resource.ios[0], (Pins, DiffPairs)): phys = resource.ios[0] @@ -137,34 +136,30 @@ def resolve(resource, dir, xdr, name, attrs): # ignore it as well. if isinstance(phys, Pins): phys_names = phys.names - port = Record([("io", len(phys))], name=name) - port.signature = wiring.Signature({"io": wiring.In(len(phys))}) + port = wiring.Signature({"io": wiring.In(len(phys))}).create(path=path) if isinstance(phys, DiffPairs): phys_names = [] - rec_members = [] sig_members = {} if not self.should_skip_port_component(None, attrs, "p"): phys_names += phys.p.names - rec_members.append(("p", len(phys))) sig_members["p"] = wiring.In(len(phys)) if not self.should_skip_port_component(None, attrs, "n"): phys_names += phys.n.names - rec_members.append(("n", len(phys))) sig_members["n"] = wiring.In(len(phys)) - port = Record(rec_members, name=name) - port.signature = wiring.Signature(sig_members) + port = wiring.Signature(sig_members).create(path=path) if dir == "-": pin = None else: - pin = wiring.flipped(Pin(len(phys), dir, xdr=xdr, name=name)) + pin = wiring.flipped(Pin(len(phys), dir, xdr=xdr, path=path)) for phys_name in phys_names: if phys_name in self._phys_reqd: raise ResourceError("Resource component {} uses physical pin {}, but it " "is already used by resource component {} that was " "requested earlier" - .format(name, phys_name, self._phys_reqd[phys_name])) - self._phys_reqd[phys_name] = name + .format(".".join(path), phys_name, + ".".join(self._phys_reqd[phys_name]))) + self._phys_reqd[phys_name] = path self._ports.append((resource, pin, port, attrs)) @@ -178,7 +173,7 @@ def resolve(resource, dir, xdr, name, attrs): value = resolve(resource, *merge_options(resource, dir, xdr), - name=f"{resource.name}_{resource.number}", + path=(f"{resource.name}_{resource.number}",), attrs=resource.attrs) self._requested[resource.name, resource.number] = value return value diff --git a/amaranth/lib/io.py b/amaranth/lib/io.py index 54bd9c09e..2a179ec9c 100644 --- a/amaranth/lib/io.py +++ b/amaranth/lib/io.py @@ -1,74 +1,18 @@ -import warnings - from .. import * -with warnings.catch_warnings(): - warnings.filterwarnings(action="ignore", category=DeprecationWarning) - from ..hdl.rec import * -from ..lib.wiring import In, Out, Signature, flipped, FlippedInterface - - -__all__ = ["pin_layout", "Pin"] - - -def _pin_signature(width, dir, xdr=0): - if not isinstance(width, int) or width < 0: - raise TypeError("Width must be a non-negative integer, not {!r}" - .format(width)) - if dir not in ("i", "o", "oe", "io"): - raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"oe\", not {!r}""" - .format(dir)) - if not isinstance(xdr, int) or xdr < 0: - raise TypeError("Gearing ratio must be a non-negative integer, not {!r}" - .format(xdr)) - - members = {} - if dir in ("i", "io"): - if xdr > 0: - members["i_clk"] = In(1) - if xdr > 2: - members["i_fclk"] = In(1) - if xdr in (0, 1): - members["i"] = In(width) - else: - for n in range(xdr): - members[f"i{n}"] = In(width) - if dir in ("o", "oe", "io"): - if xdr > 0: - members["o_clk"] = Out(1) - if xdr > 2: - members["o_fclk"] = Out(1) - if xdr in (0, 1): - members["o"] = Out(width) - else: - for n in range(xdr): - members[f"o{n}"] = Out(width) - if dir in ("oe", "io"): - members["oe"] = Out(1) - return Signature(members) +from ..lib import wiring +from ..lib.wiring import In, Out -def pin_layout(width, dir, xdr=0): - """ - Layout of the platform interface of a pin or several pins, which may be used inside - user-defined records. - - See :class:`Pin` for details. - """ - fields = [] - for name, member in _pin_signature(width, dir, xdr).members.items(): - fields.append((name, member.shape)) - return Layout(fields) +__all__ = ["Pin"] -class Pin(Record): +class Pin(wiring.PureInterface): """ 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".) - A :class:`Pin` is identical to a :class:`Record` that uses the corresponding :meth:`pin_layout` - except that it allows 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. + This is an interface object using :class:`Pin.Signature` as its signature. The signature flows + are defined from the point of view of a component that drives the I/O buffer. Parameters ---------- @@ -87,8 +31,8 @@ class Pin(Record): 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. - name : str - Name of the underlying record. + path : tuple of str + As in :class:`PureInterface`, used to name the created signals. Attributes ---------- @@ -119,23 +63,76 @@ class Pin(Record): cannot change direction more than once per cycle, so at most one output enable signal is present. """ - def __init__(self, width, dir, *, xdr=0, name=None, src_loc_at=0): - self.width = width - self.dir = dir - self.xdr = xdr - super().__init__(pin_layout(self.width, self.dir, self.xdr), - name=name, src_loc_at=src_loc_at + 1) + class Signature(wiring.Signature): + """A signature for :class:`Pin`. The parameters are as defined on the ``Pin`` class, + and are accessible as attributes. + """ + def __init__(self, width, dir, *, xdr=0): + if not isinstance(width, int) or width < 0: + raise TypeError("Width must be a non-negative integer, not {!r}" + .format(width)) + if dir not in ("i", "o", "oe", "io"): + raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"oe\", not {!r}""" + .format(dir)) + if not isinstance(xdr, int) or xdr < 0: + raise TypeError("Gearing ratio must be a non-negative integer, not {!r}" + .format(xdr)) + + self.width = width + self.dir = dir + self.xdr = xdr + + members = {} + if dir in ("i", "io"): + if xdr > 0: + members["i_clk"] = Out(1) + if xdr > 2: + members["i_fclk"] = Out(1) + if xdr in (0, 1): + members["i"] = In(width) + else: + for n in range(xdr): + members[f"i{n}"] = In(width) + if dir in ("o", "oe", "io"): + if xdr > 0: + members["o_clk"] = Out(1) + if xdr > 2: + members["o_fclk"] = Out(1) + if xdr in (0, 1): + members["o"] = Out(width) + else: + for n in range(xdr): + members[f"o{n}"] = Out(width) + if dir in ("oe", "io"): + members["oe"] = Out(1) + super().__init__(members) + + def __eq__(self, other): + return (type(self) is type(other) and + self.width == other.width and + self.dir == other.dir and + self.xdr == other.xdr) + + def create(self, *, path=None, src_loc_at=0): + return Pin(self.width, self.dir, xdr=self.xdr, path=path, src_loc_at=1 + src_loc_at) + + def __init__(self, width, dir, *, xdr=0, name=None, path=None, src_loc_at=0): + if name is not None: + if path is None: + raise ValueError("Cannot pass both name and path") + path = (name,) + signature = Pin.Signature(width, dir, xdr=xdr) + super().__init__(signature, path=path, src_loc_at=src_loc_at + 1) @property - def signature(self): - return _pin_signature(self.width, self.dir, self.xdr) - - def eq(self, other): - first_field, _, _ = next(iter(Pin(1, dir="o").layout)) - warnings.warn(f"`pin.eq(...)` is deprecated; use `pin.{first_field}.eq(...)` here", - DeprecationWarning, stacklevel=2) - if isinstance(self, FlippedInterface): - return Record.eq(flipped(self), other) - else: - return Record.eq(self, other) + def width(self): + return self.signature.width + + @property + def dir(self): + return self.signature.dir + + @property + def xdr(self): + return self.signature.xdr diff --git a/tests/test_build_res.py b/tests/test_build_res.py index edb32a0f4..0100d9bbe 100644 --- a/tests/test_build_res.py +++ b/tests/test_build_res.py @@ -64,7 +64,7 @@ def test_request_basic(self): user_led = self.cm.request("user_led", 0) self.assertIsInstance(flipped(user_led), Pin) - self.assertEqual(user_led.name, "user_led_0") + self.assertEqual(user_led.o.name, "user_led_0__o") self.assertEqual(user_led.width, 1) self.assertEqual(user_led.dir, "o") @@ -77,12 +77,14 @@ def test_request_basic(self): def test_request_with_dir(self): i2c = self.cm.request("i2c", 0, dir={"sda": "o"}) - self.assertIsInstance(i2c, Record) - self.assertIsInstance(i2c.sda, Pin) + self.assertIsInstance(i2c, PureInterface) + self.assertTrue(i2c.signature.is_compliant(i2c)) + self.assertIsInstance(flipped(i2c.sda), Pin) self.assertEqual(i2c.sda.dir, "o") def test_request_tristate(self): i2c = self.cm.request("i2c", 0) + self.assertTrue(i2c.signature.is_compliant(i2c)) self.assertEqual(i2c.sda.dir, "io") ports = list(self.cm.iter_ports()) @@ -92,11 +94,11 @@ def test_request_tristate(self): self.assertEqual(ports[1].width, 1) scl_info, sda_info = self.cm.iter_single_ended_pins() - self.assertIs(flipped(scl_info[0]), i2c.scl) + self.assertIs(scl_info[0], i2c.scl) self.assertIs(scl_info[1].io, scl) self.assertEqual(scl_info[2], {}) self.assertEqual(scl_info[3], False) - self.assertIs(flipped(sda_info[0]), i2c.sda) + self.assertIs(sda_info[0], i2c.sda) self.assertIs(sda_info[1].io, sda) self.assertEqual(list(self.cm.iter_port_constraints()), [ @@ -315,12 +317,3 @@ def test_wrong_clock_constraint_twice(self): (r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already " r"constrained to 100000000\.0 Hz$")): self.cm.add_clock_constraint(clk100.i, 1e6) - - def test_eq_deprecation(self): - user_led = self.cm.request("user_led", 0) - m = Module() - with self.assertWarns(DeprecationWarning): - m.d.sync += user_led.eq(1) - p = Pin(4, "o") - with self.assertWarns(DeprecationWarning): - m.d.sync += p.eq(1) diff --git a/tests/test_lib_io.py b/tests/test_lib_io.py index a9451b6f1..ae41c0711 100644 --- a/tests/test_lib_io.py +++ b/tests/test_lib_io.py @@ -1,207 +1,201 @@ import warnings from amaranth.hdl import * -with warnings.catch_warnings(): - warnings.filterwarnings(action="ignore", category=DeprecationWarning) - from amaranth.hdl.rec import * from amaranth.sim import * from amaranth.lib.io import * +from amaranth.lib.wiring import * from .utils import * -class PinLayoutTestCase(FHDLTestCase): - def assertLayoutEqual(self, layout, expected): - casted_layout = {} - for name, (shape, dir) in layout.items(): - casted_layout[name] = (Shape.cast(shape), dir) +class PinSignatureTestCase(FHDLTestCase): + def assertSignatureEqual(self, signature, expected): + self.assertEqual(signature.members, Signature(expected).members) - self.assertEqual(casted_layout, expected) - -class PinLayoutCombTestCase(PinLayoutTestCase): - def test_pin_layout_i(self): - layout_1 = pin_layout(1, dir="i") - self.assertLayoutEqual(layout_1.fields, { - "i": (unsigned(1), DIR_NONE), +class PinSignatureCombTestCase(PinSignatureTestCase): + def test_signature_i(self): + sig_1 = Pin.Signature(1, dir="i") + self.assertSignatureEqual(sig_1, { + "i": In(1), }) - layout_2 = pin_layout(2, dir="i") - self.assertLayoutEqual(layout_2.fields, { - "i": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="i") + self.assertSignatureEqual(sig_2, { + "i": In(2), }) - def test_pin_layout_o(self): - layout_1 = pin_layout(1, dir="o") - self.assertLayoutEqual(layout_1.fields, { - "o": (unsigned(1), DIR_NONE), + def test_signature_o(self): + sig_1 = Pin.Signature(1, dir="o") + self.assertSignatureEqual(sig_1, { + "o": Out(1), }) - layout_2 = pin_layout(2, dir="o") - self.assertLayoutEqual(layout_2.fields, { - "o": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="o") + self.assertSignatureEqual(sig_2, { + "o": Out(2), }) - def test_pin_layout_oe(self): - layout_1 = pin_layout(1, dir="oe") - self.assertLayoutEqual(layout_1.fields, { - "o": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_oe(self): + sig_1 = Pin.Signature(1, dir="oe") + self.assertSignatureEqual(sig_1, { + "o": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="oe") - self.assertLayoutEqual(layout_2.fields, { - "o": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="oe") + self.assertSignatureEqual(sig_2, { + "o": Out(2), + "oe": Out(1), }) - def test_pin_layout_io(self): - layout_1 = pin_layout(1, dir="io") - self.assertLayoutEqual(layout_1.fields, { - "i": (unsigned(1), DIR_NONE), - "o": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_io(self): + sig_1 = Pin.Signature(1, dir="io") + self.assertSignatureEqual(sig_1, { + "i": In(1), + "o": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="io") - self.assertLayoutEqual(layout_2.fields, { - "i": (unsigned(2), DIR_NONE), - "o": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="io") + self.assertSignatureEqual(sig_2, { + "i": In(2), + "o": Out(2), + "oe": Out(1), }) -class PinLayoutSDRTestCase(PinLayoutTestCase): - def test_pin_layout_i(self): - layout_1 = pin_layout(1, dir="i", xdr=1) - self.assertLayoutEqual(layout_1.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i": (unsigned(1), DIR_NONE), +class PinSignatureSDRTestCase(PinSignatureTestCase): + def test_signature_i(self): + sig_1 = Pin.Signature(1, dir="i", xdr=1) + self.assertSignatureEqual(sig_1, { + "i_clk": Out(1), + "i": In(1), }) - layout_2 = pin_layout(2, dir="i", xdr=1) - self.assertLayoutEqual(layout_2.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="i", xdr=1) + self.assertSignatureEqual(sig_2, { + "i_clk": Out(1), + "i": In(2), }) - def test_pin_layout_o(self): - layout_1 = pin_layout(1, dir="o", xdr=1) - self.assertLayoutEqual(layout_1.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(1), DIR_NONE), + def test_signature_o(self): + sig_1 = Pin.Signature(1, dir="o", xdr=1) + self.assertSignatureEqual(sig_1, { + "o_clk": Out(1), + "o": Out(1), }) - layout_2 = pin_layout(2, dir="o", xdr=1) - self.assertLayoutEqual(layout_2.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="o", xdr=1) + self.assertSignatureEqual(sig_2, { + "o_clk": Out(1), + "o": Out(2), }) - def test_pin_layout_oe(self): - layout_1 = pin_layout(1, dir="oe", xdr=1) - self.assertLayoutEqual(layout_1.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_oe(self): + sig_1 = Pin.Signature(1, dir="oe", xdr=1) + self.assertSignatureEqual(sig_1, { + "o_clk": Out(1), + "o": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="oe", xdr=1) - self.assertLayoutEqual(layout_2.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="oe", xdr=1) + self.assertSignatureEqual(sig_2, { + "o_clk": Out(1), + "o": Out(2), + "oe": Out(1), }) - def test_pin_layout_io(self): - layout_1 = pin_layout(1, dir="io", xdr=1) - self.assertLayoutEqual(layout_1.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i": (unsigned(1), DIR_NONE), - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_io(self): + sig_1 = Pin.Signature(1, dir="io", xdr=1) + self.assertSignatureEqual(sig_1, { + "i_clk": Out(1), + "i": In(1), + "o_clk": Out(1), + "o": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="io", xdr=1) - self.assertLayoutEqual(layout_2.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i": (unsigned(2), DIR_NONE), - "o_clk": (unsigned(1), DIR_NONE), - "o": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="io", xdr=1) + self.assertSignatureEqual(sig_2, { + "i_clk": Out(1), + "i": In(2), + "o_clk": Out(1), + "o": Out(2), + "oe": Out(1), }) -class PinLayoutDDRTestCase(PinLayoutTestCase): - def test_pin_layout_i(self): - layout_1 = pin_layout(1, dir="i", xdr=2) - self.assertLayoutEqual(layout_1.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i0": (unsigned(1), DIR_NONE), - "i1": (unsigned(1), DIR_NONE), +class PinSignatureDDRTestCase(PinSignatureTestCase): + def test_signature_i(self): + sig_1 = Pin.Signature(1, dir="i", xdr=2) + self.assertSignatureEqual(sig_1, { + "i_clk": Out(1), + "i0": In(1), + "i1": In(1), }) - layout_2 = pin_layout(2, dir="i", xdr=2) - self.assertLayoutEqual(layout_2.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i0": (unsigned(2), DIR_NONE), - "i1": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="i", xdr=2) + self.assertSignatureEqual(sig_2, { + "i_clk": Out(1), + "i0": In(2), + "i1": In(2), }) - def test_pin_layout_o(self): - layout_1 = pin_layout(1, dir="o", xdr=2) - self.assertLayoutEqual(layout_1.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(1), DIR_NONE), - "o1": (unsigned(1), DIR_NONE), + def test_signature_o(self): + sig_1 = Pin.Signature(1, dir="o", xdr=2) + self.assertSignatureEqual(sig_1, { + "o_clk": Out(1), + "o0": Out(1), + "o1": Out(1), }) - layout_2 = pin_layout(2, dir="o", xdr=2) - self.assertLayoutEqual(layout_2.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(2), DIR_NONE), - "o1": (unsigned(2), DIR_NONE), + sig_2 = Pin.Signature(2, dir="o", xdr=2) + self.assertSignatureEqual(sig_2, { + "o_clk": Out(1), + "o0": Out(2), + "o1": Out(2), }) - def test_pin_layout_oe(self): - layout_1 = pin_layout(1, dir="oe", xdr=2) - self.assertLayoutEqual(layout_1.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(1), DIR_NONE), - "o1": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_oe(self): + sig_1 = Pin.Signature(1, dir="oe", xdr=2) + self.assertSignatureEqual(sig_1, { + "o_clk": Out(1), + "o0": Out(1), + "o1": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="oe", xdr=2) - self.assertLayoutEqual(layout_2.fields, { - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(2), DIR_NONE), - "o1": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="oe", xdr=2) + self.assertSignatureEqual(sig_2, { + "o_clk": Out(1), + "o0": Out(2), + "o1": Out(2), + "oe": Out(1), }) - def test_pin_layout_io(self): - layout_1 = pin_layout(1, dir="io", xdr=2) - self.assertLayoutEqual(layout_1.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i0": (unsigned(1), DIR_NONE), - "i1": (unsigned(1), DIR_NONE), - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(1), DIR_NONE), - "o1": (unsigned(1), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + def test_signature_io(self): + sig_1 = Pin.Signature(1, dir="io", xdr=2) + self.assertSignatureEqual(sig_1, { + "i_clk": Out(1), + "i0": In(1), + "i1": In(1), + "o_clk": Out(1), + "o0": Out(1), + "o1": Out(1), + "oe": Out(1), }) - layout_2 = pin_layout(2, dir="io", xdr=2) - self.assertLayoutEqual(layout_2.fields, { - "i_clk": (unsigned(1), DIR_NONE), - "i0": (unsigned(2), DIR_NONE), - "i1": (unsigned(2), DIR_NONE), - "o_clk": (unsigned(1), DIR_NONE), - "o0": (unsigned(2), DIR_NONE), - "o1": (unsigned(2), DIR_NONE), - "oe": (unsigned(1), DIR_NONE), + sig_2 = Pin.Signature(2, dir="io", xdr=2) + self.assertSignatureEqual(sig_2, { + "i_clk": Out(1), + "i0": In(2), + "i1": In(2), + "o_clk": Out(1), + "o0": Out(2), + "o1": Out(2), + "oe": Out(1), }) @@ -211,3 +205,7 @@ def test_attributes(self): self.assertEqual(pin.width, 2) self.assertEqual(pin.dir, "io") self.assertEqual(pin.xdr, 2) + self.assertEqual(pin.signature.width, 2) + self.assertEqual(pin.signature.dir, "io") + self.assertEqual(pin.signature.xdr, 2) +