Skip to content

Commit 4e07832

Browse files
committed
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.
1 parent 33c2246 commit 4e07832

File tree

4 files changed

+51
-30
lines changed

4 files changed

+51
-30
lines changed

amaranth/build/res.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,29 +122,36 @@ def resolve(resource, dir, xdr, name, attrs):
122122
fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
123123
name="{}__{}".format(name, sub.name),
124124
attrs={**attrs, **sub.attrs})
125-
return Record([
125+
rec = Record([
126126
(f_name, f.layout) for (f_name, f) in fields.items()
127127
], fields=fields, name=name)
128+
rec.signature = wiring.Signature({
129+
f_name: wiring.Out(f.signature) for (f_name, f) in fields.items()
130+
})
131+
return rec
128132

129133
elif isinstance(resource.ios[0], (Pins, DiffPairs)):
130134
phys = resource.ios[0]
135+
# The flow is `In` below regardless of requested pin direction. The flow should
136+
# never be used as it's not used internally and anyone using `dir="-"` should
137+
# ignore it as well.
131138
if isinstance(phys, Pins):
132139
phys_names = phys.names
133-
port = Record([("io", len(phys))], name=name)
140+
port = wiring.Signature({"io": wiring.In(len(phys))}).create(path=(name,))
134141
if isinstance(phys, DiffPairs):
135142
phys_names = []
136-
record_fields = []
143+
members = {}
137144
if not self.should_skip_port_component(None, attrs, "p"):
138145
phys_names += phys.p.names
139-
record_fields.append(("p", len(phys)))
146+
members["p"] = wiring.In(len(phys))
140147
if not self.should_skip_port_component(None, attrs, "n"):
141148
phys_names += phys.n.names
142-
record_fields.append(("n", len(phys)))
143-
port = Record(record_fields, name=name)
149+
members["n"] = wiring.In(len(phys))
150+
port = wiring.Signature(members).create(path=(name,))
144151
if dir == "-":
145152
pin = None
146153
else:
147-
pin = Pin(len(phys), dir, xdr=xdr, name=name)
154+
pin = wiring.flipped(Pin(len(phys), dir, xdr=xdr, name=name))
148155

149156
for phys_name in phys_names:
150157
if phys_name in self._phys_reqd:

amaranth/hdl/_rec.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .. import tracer
88
from .._utils import union
99
from .ast import *
10+
from ..lib import wiring
1011

1112

1213
__all__ = ["Direction", "DIR_NONE", "DIR_FANOUT", "DIR_FANIN", "Layout", "Record"]
@@ -132,6 +133,8 @@ def concat(a, b):
132133
if fields is not None and field_name in fields:
133134
field = fields[field_name]
134135
if isinstance(field_shape, Layout):
136+
if isinstance(field, wiring.FlippedInterface):
137+
field = wiring.flipped(field)
135138
assert isinstance(field, Record) and field_shape == field.layout
136139
else:
137140
assert isinstance(field, Signal) and Shape.cast(field_shape) == field.shape()

amaranth/lib/io.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,13 @@
44
with warnings.catch_warnings():
55
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
66
from ..hdl.rec import *
7+
from ..lib.wiring import In, Out, Signature
78

89

910
__all__ = ["pin_layout", "Pin"]
1011

1112

12-
def pin_layout(width, dir, xdr=0):
13-
"""
14-
Layout of the platform interface of a pin or several pins, which may be used inside
15-
user-defined records.
16-
17-
See :class:`Pin` for details.
18-
"""
13+
def _pin_signature(width, dir, xdr=0):
1914
if not isinstance(width, int) or width < 0:
2015
raise TypeError("Width must be a non-negative integer, not {!r}"
2116
.format(width))
@@ -26,29 +21,42 @@ def pin_layout(width, dir, xdr=0):
2621
raise TypeError("Gearing ratio must be a non-negative integer, not {!r}"
2722
.format(xdr))
2823

29-
fields = []
24+
members = {}
3025
if dir in ("i", "io"):
3126
if xdr > 0:
32-
fields.append(("i_clk", 1))
27+
members["i_clk"] = In(1)
3328
if xdr > 2:
34-
fields.append(("i_fclk", 1))
29+
members["i_fclk"] = In(1)
3530
if xdr in (0, 1):
36-
fields.append(("i", width))
31+
members["i"] = In(width)
3732
else:
3833
for n in range(xdr):
39-
fields.append(("i{}".format(n), width))
34+
members["i{}".format(n)] = In(width)
4035
if dir in ("o", "oe", "io"):
4136
if xdr > 0:
42-
fields.append(("o_clk", 1))
37+
members["o_clk"] = Out(1)
4338
if xdr > 2:
44-
fields.append(("o_fclk", 1))
39+
members["o_fclk"] = Out(1)
4540
if xdr in (0, 1):
46-
fields.append(("o", width))
41+
members["o"] = Out(width)
4742
else:
4843
for n in range(xdr):
49-
fields.append(("o{}".format(n), width))
44+
members["o{}".format(n)] = Out(width)
5045
if dir in ("oe", "io"):
51-
fields.append(("oe", 1))
46+
members["oe"] = In(1)
47+
return Signature(members)
48+
49+
50+
def pin_layout(width, dir, xdr=0):
51+
"""
52+
Layout of the platform interface of a pin or several pins, which may be used inside
53+
user-defined records.
54+
55+
See :class:`Pin` for details.
56+
"""
57+
fields = []
58+
for name, member in _pin_signature(width, dir, xdr).members.items():
59+
fields.append((name, member.shape))
5260
return Layout(fields)
5361

5462

@@ -118,3 +126,7 @@ def __init__(self, width, dir, *, xdr=0, name=None, src_loc_at=0):
118126

119127
super().__init__(pin_layout(self.width, self.dir, self.xdr),
120128
name=name, src_loc_at=src_loc_at + 1)
129+
130+
@property
131+
def signature(self):
132+
return _pin_signature(self.width, self.dir, self.xdr)

tests/test_build_res.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
with warnings.catch_warnings():
77
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
88
from amaranth.hdl.rec import *
9+
from amaranth.lib.wiring import *
910
from amaranth.lib.io import *
1011
from amaranth.build.dsl import *
1112
from amaranth.build.res import *
@@ -62,7 +63,7 @@ def test_request_basic(self):
6263
r = self.cm.lookup("user_led", 0)
6364
user_led = self.cm.request("user_led", 0)
6465

65-
self.assertIsInstance(user_led, Pin)
66+
self.assertIsInstance(flipped(user_led), Pin)
6667
self.assertEqual(user_led.name, "user_led_0")
6768
self.assertEqual(user_led.width, 1)
6869
self.assertEqual(user_led.dir, "o")
@@ -91,11 +92,11 @@ def test_request_tristate(self):
9192
self.assertEqual(ports[1].width, 1)
9293

9394
scl_info, sda_info = self.cm.iter_single_ended_pins()
94-
self.assertIs(scl_info[0], i2c.scl)
95+
self.assertIs(flipped(scl_info[0]), i2c.scl)
9596
self.assertIs(scl_info[1].io, scl)
9697
self.assertEqual(scl_info[2], {})
9798
self.assertEqual(scl_info[3], False)
98-
self.assertIs(sda_info[0], i2c.sda)
99+
self.assertIs(flipped(sda_info[0]), i2c.sda)
99100
self.assertIs(sda_info[1].io, sda)
100101

101102
self.assertEqual(list(self.cm.iter_port_constraints()), [
@@ -105,7 +106,7 @@ def test_request_tristate(self):
105106

106107
def test_request_diffpairs(self):
107108
clk100 = self.cm.request("clk100", 0)
108-
self.assertIsInstance(clk100, Pin)
109+
self.assertIsInstance(flipped(clk100), Pin)
109110
self.assertEqual(clk100.dir, "i")
110111
self.assertEqual(clk100.width, 1)
111112

@@ -155,7 +156,6 @@ def test_request_inverted(self):
155156

156157
def test_request_raw(self):
157158
clk50 = self.cm.request("clk50", 0, dir="-")
158-
self.assertIsInstance(clk50, Record)
159159
self.assertIsInstance(clk50.io, Signal)
160160

161161
ports = list(self.cm.iter_ports())
@@ -164,7 +164,6 @@ def test_request_raw(self):
164164

165165
def test_request_raw_diffpairs(self):
166166
clk100 = self.cm.request("clk100", 0, dir="-")
167-
self.assertIsInstance(clk100, Record)
168167
self.assertIsInstance(clk100.p, Signal)
169168
self.assertIsInstance(clk100.n, Signal)
170169

0 commit comments

Comments
 (0)