lib.io: make Pin
an interface object.
Tracking #879. The directions of signals in `Pin` make it convenient to use a pin signature in a component, such as in: class LEDDriver(Component): pins: Out(Signature({"o": Out(1)})) led_driver = LEDDriver() connect(led_driver.pins, platform.request("led")) The `platform.request` call, correspondingly, returns a flipped `Pin` object.
This commit is contained in:
parent
33c2246311
commit
4e078322a0
|
@ -122,29 +122,36 @@ class ResourceManager:
|
||||||
fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
|
fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
|
||||||
name="{}__{}".format(name, sub.name),
|
name="{}__{}".format(name, sub.name),
|
||||||
attrs={**attrs, **sub.attrs})
|
attrs={**attrs, **sub.attrs})
|
||||||
return Record([
|
rec = Record([
|
||||||
(f_name, f.layout) for (f_name, f) in fields.items()
|
(f_name, f.layout) for (f_name, f) in fields.items()
|
||||||
], fields=fields, name=name)
|
], fields=fields, name=name)
|
||||||
|
rec.signature = wiring.Signature({
|
||||||
|
f_name: wiring.Out(f.signature) for (f_name, f) in fields.items()
|
||||||
|
})
|
||||||
|
return rec
|
||||||
|
|
||||||
elif isinstance(resource.ios[0], (Pins, DiffPairs)):
|
elif isinstance(resource.ios[0], (Pins, DiffPairs)):
|
||||||
phys = resource.ios[0]
|
phys = resource.ios[0]
|
||||||
|
# The flow is `In` below regardless of requested pin direction. The flow should
|
||||||
|
# never be used as it's not used internally and anyone using `dir="-"` should
|
||||||
|
# ignore it as well.
|
||||||
if isinstance(phys, Pins):
|
if isinstance(phys, Pins):
|
||||||
phys_names = phys.names
|
phys_names = phys.names
|
||||||
port = Record([("io", len(phys))], name=name)
|
port = wiring.Signature({"io": wiring.In(len(phys))}).create(path=(name,))
|
||||||
if isinstance(phys, DiffPairs):
|
if isinstance(phys, DiffPairs):
|
||||||
phys_names = []
|
phys_names = []
|
||||||
record_fields = []
|
members = {}
|
||||||
if not self.should_skip_port_component(None, attrs, "p"):
|
if not self.should_skip_port_component(None, attrs, "p"):
|
||||||
phys_names += phys.p.names
|
phys_names += phys.p.names
|
||||||
record_fields.append(("p", len(phys)))
|
members["p"] = wiring.In(len(phys))
|
||||||
if not self.should_skip_port_component(None, attrs, "n"):
|
if not self.should_skip_port_component(None, attrs, "n"):
|
||||||
phys_names += phys.n.names
|
phys_names += phys.n.names
|
||||||
record_fields.append(("n", len(phys)))
|
members["n"] = wiring.In(len(phys))
|
||||||
port = Record(record_fields, name=name)
|
port = wiring.Signature(members).create(path=(name,))
|
||||||
if dir == "-":
|
if dir == "-":
|
||||||
pin = None
|
pin = None
|
||||||
else:
|
else:
|
||||||
pin = Pin(len(phys), dir, xdr=xdr, name=name)
|
pin = wiring.flipped(Pin(len(phys), dir, xdr=xdr, name=name))
|
||||||
|
|
||||||
for phys_name in phys_names:
|
for phys_name in phys_names:
|
||||||
if phys_name in self._phys_reqd:
|
if phys_name in self._phys_reqd:
|
||||||
|
|
|
@ -7,6 +7,7 @@ from functools import reduce, wraps
|
||||||
from .. import tracer
|
from .. import tracer
|
||||||
from .._utils import union
|
from .._utils import union
|
||||||
from .ast import *
|
from .ast import *
|
||||||
|
from ..lib import wiring
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Direction", "DIR_NONE", "DIR_FANOUT", "DIR_FANIN", "Layout", "Record"]
|
__all__ = ["Direction", "DIR_NONE", "DIR_FANOUT", "DIR_FANIN", "Layout", "Record"]
|
||||||
|
@ -132,6 +133,8 @@ class Record(ValueCastable):
|
||||||
if fields is not None and field_name in fields:
|
if fields is not None and field_name in fields:
|
||||||
field = fields[field_name]
|
field = fields[field_name]
|
||||||
if isinstance(field_shape, Layout):
|
if isinstance(field_shape, Layout):
|
||||||
|
if isinstance(field, wiring.FlippedInterface):
|
||||||
|
field = wiring.flipped(field)
|
||||||
assert isinstance(field, Record) and field_shape == field.layout
|
assert isinstance(field, Record) and field_shape == field.layout
|
||||||
else:
|
else:
|
||||||
assert isinstance(field, Signal) and Shape.cast(field_shape) == field.shape()
|
assert isinstance(field, Signal) and Shape.cast(field_shape) == field.shape()
|
||||||
|
|
|
@ -4,18 +4,13 @@ from .. import *
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
||||||
from ..hdl.rec import *
|
from ..hdl.rec import *
|
||||||
|
from ..lib.wiring import In, Out, Signature
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["pin_layout", "Pin"]
|
__all__ = ["pin_layout", "Pin"]
|
||||||
|
|
||||||
|
|
||||||
def pin_layout(width, dir, xdr=0):
|
def _pin_signature(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.
|
|
||||||
"""
|
|
||||||
if not isinstance(width, int) or width < 0:
|
if not isinstance(width, int) or width < 0:
|
||||||
raise TypeError("Width must be a non-negative integer, not {!r}"
|
raise TypeError("Width must be a non-negative integer, not {!r}"
|
||||||
.format(width))
|
.format(width))
|
||||||
|
@ -26,29 +21,42 @@ def pin_layout(width, dir, xdr=0):
|
||||||
raise TypeError("Gearing ratio must be a non-negative integer, not {!r}"
|
raise TypeError("Gearing ratio must be a non-negative integer, not {!r}"
|
||||||
.format(xdr))
|
.format(xdr))
|
||||||
|
|
||||||
fields = []
|
members = {}
|
||||||
if dir in ("i", "io"):
|
if dir in ("i", "io"):
|
||||||
if xdr > 0:
|
if xdr > 0:
|
||||||
fields.append(("i_clk", 1))
|
members["i_clk"] = In(1)
|
||||||
if xdr > 2:
|
if xdr > 2:
|
||||||
fields.append(("i_fclk", 1))
|
members["i_fclk"] = In(1)
|
||||||
if xdr in (0, 1):
|
if xdr in (0, 1):
|
||||||
fields.append(("i", width))
|
members["i"] = In(width)
|
||||||
else:
|
else:
|
||||||
for n in range(xdr):
|
for n in range(xdr):
|
||||||
fields.append(("i{}".format(n), width))
|
members["i{}".format(n)] = In(width)
|
||||||
if dir in ("o", "oe", "io"):
|
if dir in ("o", "oe", "io"):
|
||||||
if xdr > 0:
|
if xdr > 0:
|
||||||
fields.append(("o_clk", 1))
|
members["o_clk"] = Out(1)
|
||||||
if xdr > 2:
|
if xdr > 2:
|
||||||
fields.append(("o_fclk", 1))
|
members["o_fclk"] = Out(1)
|
||||||
if xdr in (0, 1):
|
if xdr in (0, 1):
|
||||||
fields.append(("o", width))
|
members["o"] = Out(width)
|
||||||
else:
|
else:
|
||||||
for n in range(xdr):
|
for n in range(xdr):
|
||||||
fields.append(("o{}".format(n), width))
|
members["o{}".format(n)] = Out(width)
|
||||||
if dir in ("oe", "io"):
|
if dir in ("oe", "io"):
|
||||||
fields.append(("oe", 1))
|
members["oe"] = In(1)
|
||||||
|
return Signature(members)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
return Layout(fields)
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,3 +126,7 @@ class Pin(Record):
|
||||||
|
|
||||||
super().__init__(pin_layout(self.width, self.dir, self.xdr),
|
super().__init__(pin_layout(self.width, self.dir, self.xdr),
|
||||||
name=name, src_loc_at=src_loc_at + 1)
|
name=name, src_loc_at=src_loc_at + 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def signature(self):
|
||||||
|
return _pin_signature(self.width, self.dir, self.xdr)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from amaranth import *
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
||||||
from amaranth.hdl.rec import *
|
from amaranth.hdl.rec import *
|
||||||
|
from amaranth.lib.wiring import *
|
||||||
from amaranth.lib.io import *
|
from amaranth.lib.io import *
|
||||||
from amaranth.build.dsl import *
|
from amaranth.build.dsl import *
|
||||||
from amaranth.build.res import *
|
from amaranth.build.res import *
|
||||||
|
@ -62,7 +63,7 @@ class ResourceManagerTestCase(FHDLTestCase):
|
||||||
r = self.cm.lookup("user_led", 0)
|
r = self.cm.lookup("user_led", 0)
|
||||||
user_led = self.cm.request("user_led", 0)
|
user_led = self.cm.request("user_led", 0)
|
||||||
|
|
||||||
self.assertIsInstance(user_led, Pin)
|
self.assertIsInstance(flipped(user_led), Pin)
|
||||||
self.assertEqual(user_led.name, "user_led_0")
|
self.assertEqual(user_led.name, "user_led_0")
|
||||||
self.assertEqual(user_led.width, 1)
|
self.assertEqual(user_led.width, 1)
|
||||||
self.assertEqual(user_led.dir, "o")
|
self.assertEqual(user_led.dir, "o")
|
||||||
|
@ -91,11 +92,11 @@ class ResourceManagerTestCase(FHDLTestCase):
|
||||||
self.assertEqual(ports[1].width, 1)
|
self.assertEqual(ports[1].width, 1)
|
||||||
|
|
||||||
scl_info, sda_info = self.cm.iter_single_ended_pins()
|
scl_info, sda_info = self.cm.iter_single_ended_pins()
|
||||||
self.assertIs(scl_info[0], i2c.scl)
|
self.assertIs(flipped(scl_info[0]), i2c.scl)
|
||||||
self.assertIs(scl_info[1].io, scl)
|
self.assertIs(scl_info[1].io, scl)
|
||||||
self.assertEqual(scl_info[2], {})
|
self.assertEqual(scl_info[2], {})
|
||||||
self.assertEqual(scl_info[3], False)
|
self.assertEqual(scl_info[3], False)
|
||||||
self.assertIs(sda_info[0], i2c.sda)
|
self.assertIs(flipped(sda_info[0]), i2c.sda)
|
||||||
self.assertIs(sda_info[1].io, sda)
|
self.assertIs(sda_info[1].io, sda)
|
||||||
|
|
||||||
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
||||||
|
@ -105,7 +106,7 @@ class ResourceManagerTestCase(FHDLTestCase):
|
||||||
|
|
||||||
def test_request_diffpairs(self):
|
def test_request_diffpairs(self):
|
||||||
clk100 = self.cm.request("clk100", 0)
|
clk100 = self.cm.request("clk100", 0)
|
||||||
self.assertIsInstance(clk100, Pin)
|
self.assertIsInstance(flipped(clk100), Pin)
|
||||||
self.assertEqual(clk100.dir, "i")
|
self.assertEqual(clk100.dir, "i")
|
||||||
self.assertEqual(clk100.width, 1)
|
self.assertEqual(clk100.width, 1)
|
||||||
|
|
||||||
|
@ -155,7 +156,6 @@ class ResourceManagerTestCase(FHDLTestCase):
|
||||||
|
|
||||||
def test_request_raw(self):
|
def test_request_raw(self):
|
||||||
clk50 = self.cm.request("clk50", 0, dir="-")
|
clk50 = self.cm.request("clk50", 0, dir="-")
|
||||||
self.assertIsInstance(clk50, Record)
|
|
||||||
self.assertIsInstance(clk50.io, Signal)
|
self.assertIsInstance(clk50.io, Signal)
|
||||||
|
|
||||||
ports = list(self.cm.iter_ports())
|
ports = list(self.cm.iter_ports())
|
||||||
|
@ -164,7 +164,6 @@ class ResourceManagerTestCase(FHDLTestCase):
|
||||||
|
|
||||||
def test_request_raw_diffpairs(self):
|
def test_request_raw_diffpairs(self):
|
||||||
clk100 = self.cm.request("clk100", 0, dir="-")
|
clk100 = self.cm.request("clk100", 0, dir="-")
|
||||||
self.assertIsInstance(clk100, Record)
|
|
||||||
self.assertIsInstance(clk100.p, Signal)
|
self.assertIsInstance(clk100.p, Signal)
|
||||||
self.assertIsInstance(clk100.n, Signal)
|
self.assertIsInstance(clk100.n, Signal)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue