Skip to content

Implement RFC 55: New lib.io components. #1314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion amaranth/build/res.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import warnings
from collections import OrderedDict

from ..hdl import *
from ..hdl._ast import SignalDict
from ..lib import wiring, io
from .._utils import _ignore_deprecated

from .dsl import *

@@ -247,7 +249,11 @@ def resolve(resource, dir, xdr, path, attrs):
if dir == "-":
return port
else:
pin = wiring.flipped(io.Pin(len(phys), dir, xdr=xdr, path=path))
warnings.warn(f"Using platform.request without `dir=\"-\"` is deprecated; "
f"use `amaranth.lib.io.*Buffer` components instead",
DeprecationWarning, stacklevel=2)
with _ignore_deprecated():
pin = wiring.flipped(io.Pin(len(phys), dir, xdr=xdr, path=path))
buffer = PinBuffer(pin, port)
self._pins.append((pin, port, buffer))

10 changes: 8 additions & 2 deletions amaranth/lib/io.py
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
from ..hdl import *
from ..lib import wiring, data
from ..lib.wiring import In, Out
from .._utils import deprecated, _ignore_deprecated
from .. import tracer


@@ -889,6 +890,8 @@ class Signature(wiring.Signature):
"""A signature for :class:`Pin`. The parameters are as defined on the ``Pin`` class,
and are accessible as attributes.
"""
# TODO(amaranth-0.6): remove
@deprecated("`amaranth.lib.io.Pin` is deprecated, use `amaranth.lib.io.*Buffer` instead")
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}"
@@ -942,17 +945,20 @@ def __repr__(self):
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)

# TODO(amaranth-0.6): remove
@deprecated("`amaranth.lib.io.Pin` is deprecated, use `amaranth.lib.io.*Buffer` instead")
def __init__(self, width, dir, *, xdr=0, name=None, path=None, src_loc_at=0):
if name is not None:
if path is not None:
raise ValueError("Cannot pass both name and path")
path = (name,)
if path is None:
name = tracer.get_var_name(depth=2 + src_loc_at, default="$pin")
name = tracer.get_var_name(depth=3 + src_loc_at, default="$pin")
path = (name,)
self.path = tuple(path)
self.name = "__".join(path)
signature = Pin.Signature(width, dir, xdr=xdr)
with _ignore_deprecated():
signature = Pin.Signature(width, dir, xdr=xdr)
super().__init__(signature, path=path, src_loc_at=src_loc_at + 1)

@property
4 changes: 3 additions & 1 deletion docs/changes.rst
Original file line number Diff line number Diff line change
@@ -41,7 +41,8 @@ Apply the following changes to code written against Amaranth 0.4 to migrate it t
* Remove any usage of ``name=`` with assertions, possibly replacing them with custom messages
* Ensure all elaboratables are subclasses of :class:`Elaboratable`
* Ensure clock domains aren't used outside the module that defines them, or its submodules; move clock domain definitions upwards in the hierarchy as necessary
* Remove uses of ``amaranth.lib.coding.*`` by inlining or copying the implementation of the modules.
* Remove uses of ``amaranth.lib.coding.*`` by inlining or copying the implementation of the modules
* Update uses of ``platform.request`` to pass ``dir="-"`` and use :mod:`amaranth.lib.io` buffers


Implemented RFCs
@@ -74,6 +75,7 @@ Implemented RFCs
* `RFC 50`_: ``Print`` statement and string formatting
* `RFC 51`_: Add ``ShapeCastable.from_bits`` and ``amaranth.lib.data.Const``
* `RFC 53`_: Low-level I/O primitives
* `RFC 55`_: New ``lib.io`` components
* `RFC 58`_: Core support for ``ValueCastable`` formatting
* `RFC 59`_: Get rid of upwards propagation of clock domains
* `RFC 62`_: The ``MemoryData`` class
48 changes: 32 additions & 16 deletions tests/test_build_res.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
from amaranth.lib.io import *
from amaranth.build.dsl import *
from amaranth.build.res import *
from amaranth._utils import _ignore_deprecated

from .utils import *

@@ -56,7 +57,8 @@ def test_lookup(self):

def test_request_basic(self):
r = self.cm.lookup("user_led", 0)
user_led = self.cm.request("user_led", 0)
with _ignore_deprecated():
user_led = self.cm.request("user_led", 0)

self.assertIsInstance(flipped(user_led), Pin)
self.assertEqual(user_led.o.name, "user_led_0__o")
@@ -74,15 +76,17 @@ def test_request_basic(self):
self.assertEqual(port.invert, (False,))

def test_request_with_dir(self):
i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
with _ignore_deprecated():
i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
self.assertIsInstance(flipped(i2c.sda), Pin)
self.assertEqual(i2c.sda.dir, "o")
((_, _, scl_buffer), (_, _, sda_buffer)) = self.cm.iter_pins()
scl_buffer._MustUse__silence = True
sda_buffer._MustUse__silence = True

def test_request_tristate(self):
i2c = self.cm.request("i2c", 0)
with _ignore_deprecated():
i2c = self.cm.request("i2c", 0)
self.assertEqual(i2c.sda.dir, "io")

((scl_pin, scl_port, scl_buffer), (sda_pin, sda_port, sda_buffer)) = self.cm.iter_pins()
@@ -97,7 +101,8 @@ def test_request_tristate(self):
self.assertEqual(sda_port.io.metadata[0].name, "N11")

def test_request_diffpairs(self):
clk100 = self.cm.request("clk100", 0)
with _ignore_deprecated():
clk100 = self.cm.request("clk100", 0)
self.assertIsInstance(flipped(clk100), Pin)
self.assertEqual(clk100.dir, "i")
self.assertEqual(clk100.width, 1)
@@ -120,8 +125,10 @@ def test_request_inverted(self):
]
self.cm.add_resources(new_resources)

cs = self.cm.request("cs")
clk = self.cm.request("clk")
with _ignore_deprecated():
cs = self.cm.request("cs")
clk = self.cm.request("clk")

(
(cs_pin, cs_port, cs_buffer),
(clk_pin, clk_port, clk_buffer),
@@ -154,7 +161,8 @@ def test_request_via_connector(self):
Subsignal("copi", Pins("4", conn=("pmod", 0))),
)
])
spi0 = self.cm.request("spi", 0)
with _ignore_deprecated():
spi0 = self.cm.request("spi", 0)
(
(cs_pin, cs_port, cs_buffer),
(clk_pin, clk_port, clk_buffer),
@@ -187,7 +195,8 @@ def test_request_via_nested_connector(self):
Subsignal("copi", Pins("4", conn=("pmod_extension", 0))),
)
])
spi0 = self.cm.request("spi", 0)
with _ignore_deprecated():
spi0 = self.cm.request("spi", 0)
(
(cs_pin, cs_port, cs_buffer),
(clk_pin, clk_port, clk_buffer),
@@ -208,8 +217,9 @@ def test_request_via_nested_connector(self):
self.assertEqual(copi_port.io.metadata[0].name, "B3")

def test_request_clock(self):
clk100 = self.cm.request("clk100", 0)
clk50 = self.cm.request("clk50", 0, dir="i")
with _ignore_deprecated():
clk100 = self.cm.request("clk100", 0)
clk50 = self.cm.request("clk50", 0, dir="i")
(
(clk100_pin, clk100_port, clk100_buffer),
(clk50_pin, clk50_port, clk50_buffer),
@@ -222,7 +232,8 @@ def test_request_clock(self):
])

def test_add_clock(self):
i2c = self.cm.request("i2c")
with _ignore_deprecated():
i2c = self.cm.request("i2c")
self.cm.add_clock_constraint(i2c.scl.o, 100e3)
self.assertEqual(list(self.cm.iter_clock_constraints()), [
(i2c.scl.o, None, 100e3)
@@ -267,24 +278,28 @@ def test_wrong_clock_frequency(self):
self.cm.add_clock_constraint(Signal(), None)

def test_wrong_request_duplicate(self):
self.cm.request("user_led", 0)
with _ignore_deprecated():
self.cm.request("user_led", 0)
(pin, port, buffer), = self.cm.iter_pins()
buffer._MustUse__silence = True
with self.assertRaisesRegex(ResourceError,
r"^Resource user_led#0 has already been requested$"):
self.cm.request("user_led", 0)
with _ignore_deprecated():
self.cm.request("user_led", 0)

def test_wrong_request_duplicate_physical(self):
self.cm.add_resources([
Resource("clk20", 0, Pins("H1", dir="i")),
])
self.cm.request("clk100", 0)
with _ignore_deprecated():
self.cm.request("clk100", 0)
(pin, port, buffer), = self.cm.iter_pins()
buffer._MustUse__silence = True
with self.assertRaisesRegex(ResourceError,
(r"^Resource component clk20_0 uses physical pin H1, but it is already "
r"used by resource component clk100_0 that was requested earlier$")):
self.cm.request("clk20", 0)
with _ignore_deprecated():
self.cm.request("clk20", 0)

def test_wrong_request_with_dir(self):
with self.assertRaisesRegex(TypeError,
@@ -319,7 +334,8 @@ def test_wrong_request_with_xdr_dict(self):
i2c = self.cm.request("i2c", 0, xdr=2)

def test_wrong_clock_constraint_twice(self):
clk100 = self.cm.request("clk100")
with _ignore_deprecated():
clk100 = self.cm.request("clk100")
(pin, port, buffer), = self.cm.iter_pins()
buffer._MustUse__silence = True
with self.assertRaisesRegex(ValueError,
91 changes: 61 additions & 30 deletions tests/test_lib_io.py
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
from amaranth.lib.io import *
from amaranth.lib.wiring import *
from amaranth.lib import wiring, data
from amaranth._utils import _ignore_deprecated

from .utils import *

@@ -701,49 +702,57 @@ def assertSignatureEqual(self, signature, expected):

class PinSignatureCombTestCase(PinSignatureTestCase):
def test_signature_i(self):
sig_1 = Pin.Signature(1, dir="i")
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="i")
self.assertSignatureEqual(sig_1, {
"i": In(1),
})

sig_2 = Pin.Signature(2, dir="i")
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="i")
self.assertSignatureEqual(sig_2, {
"i": In(2),
})

def test_signature_o(self):
sig_1 = Pin.Signature(1, dir="o")
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="o")
self.assertSignatureEqual(sig_1, {
"o": Out(1),
})

sig_2 = Pin.Signature(2, dir="o")
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="o")
self.assertSignatureEqual(sig_2, {
"o": Out(2),
})

def test_signature_oe(self):
sig_1 = Pin.Signature(1, dir="oe")
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="oe")
self.assertSignatureEqual(sig_1, {
"o": Out(1),
"oe": Out(1),
})

sig_2 = Pin.Signature(2, dir="oe")
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="oe")
self.assertSignatureEqual(sig_2, {
"o": Out(2),
"oe": Out(1),
})

def test_signature_io(self):
sig_1 = Pin.Signature(1, dir="io")
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="io")
self.assertSignatureEqual(sig_1, {
"i": In(1),
"o": Out(1),
"oe": Out(1),
})

sig_2 = Pin.Signature(2, dir="io")
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="io")
self.assertSignatureEqual(sig_2, {
"i": In(2),
"o": Out(2),
@@ -753,48 +762,55 @@ def test_signature_io(self):

class PinSignatureSDRTestCase(PinSignatureTestCase):
def test_signature_i(self):
sig_1 = Pin.Signature(1, dir="i", xdr=1)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="i", xdr=1)
self.assertSignatureEqual(sig_1, {
"i_clk": Out(1),
"i": In(1),
})

sig_2 = Pin.Signature(2, dir="i", xdr=1)
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="i", xdr=1)
self.assertSignatureEqual(sig_2, {
"i_clk": Out(1),
"i": In(2),
})

def test_signature_o(self):
sig_1 = Pin.Signature(1, dir="o", xdr=1)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="o", xdr=1)
self.assertSignatureEqual(sig_1, {
"o_clk": Out(1),
"o": Out(1),
})

sig_2 = Pin.Signature(2, dir="o", xdr=1)
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="o", xdr=1)
self.assertSignatureEqual(sig_2, {
"o_clk": Out(1),
"o": Out(2),
})

def test_signature_oe(self):
sig_1 = Pin.Signature(1, dir="oe", xdr=1)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="oe", xdr=1)
self.assertSignatureEqual(sig_1, {
"o_clk": Out(1),
"o": Out(1),
"oe": Out(1),
})

sig_2 = Pin.Signature(2, dir="oe", xdr=1)
with _ignore_deprecated():
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_signature_io(self):
sig_1 = Pin.Signature(1, dir="io", xdr=1)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="io", xdr=1)
self.assertSignatureEqual(sig_1, {
"i_clk": Out(1),
"i": In(1),
@@ -803,7 +819,8 @@ def test_signature_io(self):
"oe": Out(1),
})

sig_2 = Pin.Signature(2, dir="io", xdr=1)
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="io", xdr=1)
self.assertSignatureEqual(sig_2, {
"i_clk": Out(1),
"i": In(2),
@@ -815,45 +832,51 @@ def test_signature_io(self):

class PinSignatureDDRTestCase(PinSignatureTestCase):
def test_signature_i(self):
sig_1 = Pin.Signature(1, dir="i", xdr=2)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="i", xdr=2)
self.assertSignatureEqual(sig_1, {
"i_clk": Out(1),
"i0": In(1),
"i1": In(1),
})

sig_2 = Pin.Signature(2, dir="i", xdr=2)
with _ignore_deprecated():
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_signature_o(self):
sig_1 = Pin.Signature(1, dir="o", xdr=2)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="o", xdr=2)
self.assertSignatureEqual(sig_1, {
"o_clk": Out(1),
"o0": Out(1),
"o1": Out(1),
})

sig_2 = Pin.Signature(2, dir="o", xdr=2)
with _ignore_deprecated():
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_signature_oe(self):
sig_1 = Pin.Signature(1, dir="oe", xdr=2)
with _ignore_deprecated():
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),
})

sig_2 = Pin.Signature(2, dir="oe", xdr=2)
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="oe", xdr=2)
self.assertSignatureEqual(sig_2, {
"o_clk": Out(1),
"o0": Out(2),
@@ -862,7 +885,8 @@ def test_signature_oe(self):
})

def test_signature_io(self):
sig_1 = Pin.Signature(1, dir="io", xdr=2)
with _ignore_deprecated():
sig_1 = Pin.Signature(1, dir="io", xdr=2)
self.assertSignatureEqual(sig_1, {
"i_clk": Out(1),
"i0": In(1),
@@ -873,7 +897,8 @@ def test_signature_io(self):
"oe": Out(1),
})

sig_2 = Pin.Signature(2, dir="io", xdr=2)
with _ignore_deprecated():
sig_2 = Pin.Signature(2, dir="io", xdr=2)
self.assertSignatureEqual(sig_2, {
"i_clk": Out(1),
"i0": In(2),
@@ -887,17 +912,21 @@ def test_signature_io(self):

class PinSignatureReprCase(FHDLTestCase):
def test_repr(self):
sig_0 = Pin.Signature(1, dir="i")
with _ignore_deprecated():
sig_0 = Pin.Signature(1, dir="i")
self.assertRepr(sig_0, "Pin.Signature(1, dir='i')")
sig_0 = Pin.Signature(2, dir="o", xdr=1)
with _ignore_deprecated():
sig_0 = Pin.Signature(2, dir="o", xdr=1)
self.assertRepr(sig_0, "Pin.Signature(2, dir='o', xdr=1)")
sig_0 = Pin.Signature(3, dir="io", xdr=2)
with _ignore_deprecated():
sig_0 = Pin.Signature(3, dir="io", xdr=2)
self.assertRepr(sig_0, "Pin.Signature(3, dir='io', xdr=2)")


class PinTestCase(FHDLTestCase):
def test_attributes(self):
pin = Pin(2, dir="io", xdr=2)
with _ignore_deprecated():
pin = Pin(2, dir="io", xdr=2)
self.assertEqual(pin.width, 2)
self.assertEqual(pin.dir, "io")
self.assertEqual(pin.xdr, 2)
@@ -907,11 +936,13 @@ def test_attributes(self):
self.assertEqual(pin.name, "pin")
self.assertEqual(pin.path, ("pin",))
self.assertEqual(pin.i0.name, "pin__i0")
pin = Pin(2, dir="io", xdr=2, name="testpin")
with _ignore_deprecated():
pin = Pin(2, dir="io", xdr=2, name="testpin")
self.assertEqual(pin.name, "testpin")
self.assertEqual(pin.path, ("testpin",))
self.assertEqual(pin.i0.name, "testpin__i0")
pin = Pin(2, dir="io", xdr=2, path=["a", "b"])
with _ignore_deprecated():
pin = Pin(2, dir="io", xdr=2, path=["a", "b"])
self.assertEqual(pin.name, "a__b")
self.assertEqual(pin.path, ("a", "b"))
self.assertEqual(pin.i0.name, "a__b__i0")