Skip to content

Commit eb6be4a

Browse files
committed
build.{plat,res}: post-lib.io cleanup.
1 parent b36e7e0 commit eb6be4a

File tree

3 files changed

+179
-273
lines changed

3 files changed

+179
-273
lines changed

amaranth/build/plat.py

Lines changed: 8 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -142,34 +142,14 @@ def prepare(self, elaboratable, name="top", **kwargs):
142142
def missing_domain_error(name):
143143
raise RuntimeError("Missing domain in pin fragment")
144144

145-
def add_pin_fragment(pin, pin_fragment):
146-
pin_fragment = Fragment.get(pin_fragment, self)
147-
pin_fragment._propagate_domains(missing_domain_error)
148-
pin_fragment = DomainLowerer()(pin_fragment)
149-
fragment.add_subfragment(pin_fragment, name=f"pin_{pin.name}")
150-
151-
for pin, port, attrs, invert in self.iter_single_ended_pins():
152-
if pin.dir == "i":
153-
add_pin_fragment(pin, self.get_input(pin, port, attrs, invert))
154-
if pin.dir == "o":
155-
add_pin_fragment(pin, self.get_output(pin, port, attrs, invert))
156-
if pin.dir == "oe":
157-
add_pin_fragment(pin, self.get_tristate(pin, port, attrs, invert))
158-
if pin.dir == "io":
159-
add_pin_fragment(pin, self.get_input_output(pin, port, attrs, invert))
160-
161-
for pin, port, attrs, invert in self.iter_differential_pins():
162-
if pin.dir == "i":
163-
add_pin_fragment(pin, self.get_diff_input(pin, port, attrs, invert))
164-
if pin.dir == "o":
165-
add_pin_fragment(pin, self.get_diff_output(pin, port, attrs, invert))
166-
if pin.dir == "oe":
167-
add_pin_fragment(pin, self.get_diff_tristate(pin, port, attrs, invert))
168-
if pin.dir == "io":
169-
add_pin_fragment(pin, self.get_diff_input_output(pin, port, attrs, invert))
170-
171-
fragment = Design(fragment, [], hierarchy=(name,))
172-
return self.toolchain_prepare(fragment, name, **kwargs)
145+
for pin, port, buffer in self.iter_pins():
146+
buffer = Fragment.get(buffer, self)
147+
buffer._propagate_domains(missing_domain_error)
148+
buffer = DomainLowerer()(buffer)
149+
fragment.add_subfragment(buffer, name=f"pin_{pin.name}")
150+
151+
design = Design(fragment, list(self.iter_ports()), hierarchy=(name,))
152+
return self.toolchain_prepare(design, name, **kwargs)
173153

174154
@abstractmethod
175155
def toolchain_prepare(self, fragment, name, **kwargs):
@@ -186,108 +166,6 @@ def toolchain_program(self, products, name, **kwargs):
186166
raise NotImplementedError("Platform '{}' does not support programming"
187167
.format(type(self).__name__))
188168

189-
def _check_feature(self, feature, pin, attrs, valid_xdrs, valid_attrs):
190-
if len(valid_xdrs) == 0:
191-
raise NotImplementedError("Platform '{}' does not support {}"
192-
.format(type(self).__name__, feature))
193-
elif pin.xdr not in valid_xdrs:
194-
raise NotImplementedError("Platform '{}' does not support {} for XDR {}"
195-
.format(type(self).__name__, feature, pin.xdr))
196-
197-
if not valid_attrs and attrs:
198-
raise NotImplementedError("Platform '{}' does not support attributes for {}"
199-
.format(type(self).__name__, feature))
200-
201-
@staticmethod
202-
def _invert_if(invert, value):
203-
if invert:
204-
return ~value
205-
else:
206-
return value
207-
208-
def get_input(self, pin, port, attrs, invert):
209-
self._check_feature("input", pin, attrs,
210-
valid_xdrs=(0, 1, 2), valid_attrs=True)
211-
212-
m = Module()
213-
if pin.xdr == 0:
214-
m.submodules.buf = buf = io.Buffer(io.Direction.Input, port)
215-
m.d.comb += pin.i.eq(buf.i)
216-
elif pin.xdr == 1:
217-
m.domains.input = cd_input = ClockDomain(reset_less=True)
218-
m.submodules.buf = buf = io.FFBuffer(io.Direction.Input, port, i_domain="input")
219-
m.d.comb += pin.i.eq(buf.i)
220-
m.d.comb += cd_input.clk.eq(pin.i_clk)
221-
elif pin.xdr == 2:
222-
m.domains.input = cd_input = ClockDomain(reset_less=True)
223-
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Input, port, i_domain="input")
224-
m.d.comb += pin.i0.eq(buf.i[0])
225-
m.d.comb += pin.i1.eq(buf.i[1])
226-
m.d.comb += cd_input.clk.eq(pin.i_clk)
227-
return m
228-
229-
def get_output(self, pin, port, attrs, invert):
230-
self._check_feature("output", pin, attrs,
231-
valid_xdrs=(0, 1, 2), valid_attrs=True)
232-
233-
m = Module()
234-
if pin.xdr == 0:
235-
m.submodules.buf = buf = io.Buffer(io.Direction.Output, port)
236-
m.d.comb += buf.o.eq(pin.o)
237-
elif pin.xdr == 1:
238-
m.domains.output = cd_output = ClockDomain(reset_less=True)
239-
m.submodules.buf = buf = io.FFBuffer(io.Direction.Output, port, o_domain="output")
240-
m.d.comb += buf.o.eq(pin.o)
241-
m.d.comb += cd_output.clk.eq(pin.o_clk)
242-
elif pin.xdr == 2:
243-
m.domains.output = cd_output = ClockDomain(reset_less=True)
244-
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Output, port, o_domain="output")
245-
m.d.comb += buf.o[0].eq(pin.o0)
246-
m.d.comb += buf.o[1].eq(pin.o1)
247-
m.d.comb += cd_output.clk.eq(pin.o_clk)
248-
if pin.dir == "oe":
249-
m.d.comb += buf.oe.eq(pin.oe)
250-
return m
251-
252-
get_tristate = get_output
253-
254-
def get_input_output(self, pin, port, attrs, invert):
255-
self._check_feature("single-ended input/output", pin, attrs,
256-
valid_xdrs=(0, 1, 2), valid_attrs=True)
257-
258-
m = Module()
259-
if pin.xdr == 0:
260-
m.submodules.buf = buf = io.Buffer(io.Direction.Bidir, port)
261-
m.d.comb += pin.i.eq(buf.i)
262-
m.d.comb += buf.o.eq(pin.o)
263-
m.d.comb += buf.oe.eq(pin.oe)
264-
elif pin.xdr == 1:
265-
m.domains.input = cd_input = ClockDomain(reset_less=True)
266-
m.domains.output = cd_output = ClockDomain(reset_less=True)
267-
m.submodules.buf = buf = io.FFBuffer(io.Direction.Bidir, port, i_domain="input", o_domain="output")
268-
m.d.comb += pin.i.eq(buf.i)
269-
m.d.comb += buf.o.eq(pin.o)
270-
m.d.comb += buf.oe.eq(pin.oe)
271-
m.d.comb += cd_input.clk.eq(pin.i_clk)
272-
m.d.comb += cd_output.clk.eq(pin.o_clk)
273-
elif pin.xdr == 2:
274-
m.domains.input = cd_input = ClockDomain(reset_less=True)
275-
m.domains.output = cd_output = ClockDomain(reset_less=True)
276-
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Bidir, port, i_domain="input", o_domain="output")
277-
m.d.comb += pin.i0.eq(buf.i[0])
278-
m.d.comb += pin.i1.eq(buf.i[1])
279-
m.d.comb += buf.o[0].eq(pin.o0)
280-
m.d.comb += buf.o[1].eq(pin.o1)
281-
m.d.comb += buf.oe.eq(pin.oe)
282-
m.d.comb += cd_input.clk.eq(pin.i_clk)
283-
m.d.comb += cd_output.clk.eq(pin.o_clk)
284-
return m
285-
286-
get_diff_input = get_input
287-
get_diff_output = get_output
288-
get_diff_tristate = get_tristate
289-
get_diff_input_output = get_input_output
290-
291169

292170
class TemplatedPlatform(Platform):
293171
toolchain = property(abstractmethod(lambda: None))

amaranth/build/res.py

Lines changed: 104 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from collections import OrderedDict
22

3-
from ..hdl._ast import *
4-
from ..lib.io import *
5-
from ..lib import wiring
3+
from ..hdl import *
4+
from ..hdl._ast import SignalDict
5+
from ..lib import wiring, io
66

77
from .dsl import *
88

@@ -24,6 +24,78 @@ def __init__(self, name, attrs):
2424
self.attrs = attrs
2525

2626

27+
class PinBuffer(Elaboratable):
28+
def __init__(self, pin, port):
29+
if pin.xdr not in (0, 1, 2):
30+
raise ValueError(f"Unsupported 'xdr' value {pin.xdr}")
31+
self.pin = pin
32+
self.port = port
33+
34+
def elaborate(self, platform):
35+
m = Module()
36+
37+
if self.pin.dir == "i":
38+
if self.pin.xdr == 0:
39+
m.submodules.buf = buf = io.Buffer(io.Direction.Input, self.port)
40+
m.d.comb += self.pin.i.eq(buf.i)
41+
elif self.pin.xdr == 1:
42+
m.domains.input = cd_input = ClockDomain(reset_less=True)
43+
m.submodules.buf = buf = io.FFBuffer(io.Direction.Input, self.port, i_domain="input")
44+
m.d.comb += self.pin.i.eq(buf.i)
45+
m.d.comb += cd_input.clk.eq(self.pin.i_clk)
46+
elif self.pin.xdr == 2:
47+
m.domains.input = cd_input = ClockDomain(reset_less=True)
48+
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Input, self.port, i_domain="input")
49+
m.d.comb += self.pin.i0.eq(buf.i[0])
50+
m.d.comb += self.pin.i1.eq(buf.i[1])
51+
m.d.comb += cd_input.clk.eq(self.pin.i_clk)
52+
if self.pin.dir in ("o", "oe"):
53+
if self.pin.xdr == 0:
54+
m.submodules.buf = buf = io.Buffer(io.Direction.Output, self.port)
55+
m.d.comb += buf.o.eq(self.pin.o)
56+
elif self.pin.xdr == 1:
57+
m.domains.output = cd_output = ClockDomain(reset_less=True)
58+
m.submodules.buf = buf = io.FFBuffer(io.Direction.Output, self.port, o_domain="output")
59+
m.d.comb += buf.o.eq(self.pin.o)
60+
m.d.comb += cd_output.clk.eq(self.pin.o_clk)
61+
elif self.pin.xdr == 2:
62+
m.domains.output = cd_output = ClockDomain(reset_less=True)
63+
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Output, self.port, o_domain="output")
64+
m.d.comb += buf.o[0].eq(self.pin.o0)
65+
m.d.comb += buf.o[1].eq(self.pin.o1)
66+
m.d.comb += cd_output.clk.eq(self.pin.o_clk)
67+
if self.pin.dir == "oe":
68+
m.d.comb += buf.oe.eq(self.pin.oe)
69+
if self.pin.dir == "io":
70+
if self.pin.xdr == 0:
71+
m.submodules.buf = buf = io.Buffer(io.Direction.Bidir, self.port)
72+
m.d.comb += self.pin.i.eq(buf.i)
73+
m.d.comb += buf.o.eq(self.pin.o)
74+
m.d.comb += buf.oe.eq(self.pin.oe)
75+
elif self.pin.xdr == 1:
76+
m.domains.input = cd_input = ClockDomain(reset_less=True)
77+
m.domains.output = cd_output = ClockDomain(reset_less=True)
78+
m.submodules.buf = buf = io.FFBuffer(io.Direction.Bidir, self.port, i_domain="input", o_domain="output")
79+
m.d.comb += self.pin.i.eq(buf.i)
80+
m.d.comb += buf.o.eq(self.pin.o)
81+
m.d.comb += buf.oe.eq(self.pin.oe)
82+
m.d.comb += cd_input.clk.eq(self.pin.i_clk)
83+
m.d.comb += cd_output.clk.eq(self.pin.o_clk)
84+
elif self.pin.xdr == 2:
85+
m.domains.input = cd_input = ClockDomain(reset_less=True)
86+
m.domains.output = cd_output = ClockDomain(reset_less=True)
87+
m.submodules.buf = buf = io.DDRBuffer(io.Direction.Bidir, self.port, i_domain="input", o_domain="output")
88+
m.d.comb += self.pin.i0.eq(buf.i[0])
89+
m.d.comb += self.pin.i1.eq(buf.i[1])
90+
m.d.comb += buf.o[0].eq(self.pin.o0)
91+
m.d.comb += buf.o[1].eq(self.pin.o1)
92+
m.d.comb += buf.oe.eq(self.pin.oe)
93+
m.d.comb += cd_input.clk.eq(self.pin.i_clk)
94+
m.d.comb += cd_output.clk.eq(self.pin.o_clk)
95+
96+
return m
97+
98+
2799
class ResourceManager:
28100
def __init__(self, resources, connectors):
29101
self.resources = OrderedDict()
@@ -33,8 +105,11 @@ def __init__(self, resources, connectors):
33105
self.connectors = OrderedDict()
34106
self._conn_pins = OrderedDict()
35107

36-
# Constraint lists
108+
# List of all IOPort instances created
37109
self._ports = []
110+
# List of (pin, port, buffer) pairs for non-dir="-" requests.
111+
self._pins = []
112+
# Constraint list
38113
self._clocks = SignalDict()
39114

40115
self.add_resources(resources)
@@ -139,11 +214,12 @@ def resolve(resource, dir, xdr, path, attrs):
139214
direction = phys.dir
140215
if isinstance(phys, Pins):
141216
phys_names = phys.map_names(self._conn_pins, resource)
142-
io = IOPort(len(phys), name="__".join(path) + "__io", metadata=[
217+
iop = IOPort(len(phys), name="__".join(path) + "__io", metadata=[
143218
PortMetadata(name, attrs)
144219
for name in phys_names
145220
])
146-
port = SingleEndedPort(io, invert=phys.invert, direction=direction)
221+
self._ports.append(iop)
222+
port = io.SingleEndedPort(iop, invert=phys.invert, direction=direction)
147223
if isinstance(phys, DiffPairs):
148224
phys_names_p = phys.p.map_names(self._conn_pins, resource)
149225
phys_names_n = phys.n.map_names(self._conn_pins, resource)
@@ -156,11 +232,8 @@ def resolve(resource, dir, xdr, path, attrs):
156232
PortMetadata(name, attrs)
157233
for name in phys_names_n
158234
])
159-
port = DifferentialPort(p, n, invert=phys.invert, direction=direction)
160-
if dir == "-":
161-
pin = None
162-
else:
163-
pin = wiring.flipped(Pin(len(phys), dir, xdr=xdr, path=path))
235+
self._ports += [p, n]
236+
port = io.DifferentialPort(p, n, invert=phys.invert, direction=direction)
164237

165238
for phys_name in phys_names:
166239
if phys_name in self._phys_reqd:
@@ -171,12 +244,16 @@ def resolve(resource, dir, xdr, path, attrs):
171244
".".join(self._phys_reqd[phys_name])))
172245
self._phys_reqd[phys_name] = path
173246

174-
self._ports.append((resource, pin, port, attrs))
175-
176-
if pin is not None and resource.clock is not None:
177-
self.add_clock_constraint(pin.i, resource.clock.frequency)
247+
if dir == "-":
248+
return port
249+
else:
250+
pin = wiring.flipped(io.Pin(len(phys), dir, xdr=xdr, path=path))
251+
buffer = PinBuffer(pin, port)
252+
self._pins.append((pin, port, buffer))
178253

179-
return pin if pin is not None else port
254+
if resource.clock is not None:
255+
self.add_clock_constraint(pin.i, resource.clock.frequency)
256+
return pin
180257

181258
else:
182259
assert False # :nocov:
@@ -188,56 +265,19 @@ def resolve(resource, dir, xdr, path, attrs):
188265
self._requested[resource.name, resource.number] = value
189266
return value
190267

191-
def iter_single_ended_pins(self):
192-
for res, pin, port, attrs in self._ports:
193-
if pin is None:
194-
continue
195-
if isinstance(res.ios[0], Pins):
196-
yield pin, port, attrs, res.ios[0].invert
197-
198-
def iter_differential_pins(self):
199-
for res, pin, port, attrs in self._ports:
200-
if pin is None:
201-
continue
202-
if isinstance(res.ios[0], DiffPairs):
203-
yield pin, port, attrs, res.ios[0].invert
204-
205-
def should_skip_port_component(self, port, attrs, component):
206-
return False
268+
def iter_pins(self):
269+
yield from self._pins
207270

208271
def iter_ports(self):
209-
for res, pin, port, attrs in self._ports:
210-
if isinstance(res.ios[0], Pins):
211-
if not self.should_skip_port_component(port, attrs, "io"):
212-
yield port.io
213-
elif isinstance(res.ios[0], DiffPairs):
214-
if not self.should_skip_port_component(port, attrs, "p"):
215-
yield port.p
216-
if not self.should_skip_port_component(port, attrs, "n"):
217-
yield port.n
218-
else:
219-
assert False
220-
221-
def iter_port_constraints(self):
222-
for res, pin, port, attrs in self._ports:
223-
if isinstance(res.ios[0], Pins):
224-
if not self.should_skip_port_component(port, attrs, "io"):
225-
yield port.io.name, res.ios[0].map_names(self._conn_pins, res), attrs
226-
elif isinstance(res.ios[0], DiffPairs):
227-
if not self.should_skip_port_component(port, attrs, "p"):
228-
yield port.p.name, res.ios[0].p.map_names(self._conn_pins, res), attrs
229-
if not self.should_skip_port_component(port, attrs, "n"):
230-
yield port.n.name, res.ios[0].n.map_names(self._conn_pins, res), attrs
231-
else:
232-
assert False
272+
yield from self._ports
233273

234274
def iter_port_constraints_bits(self):
235-
for port_name, pin_names, attrs in self.iter_port_constraints():
236-
if len(pin_names) == 1:
237-
yield port_name, pin_names[0], attrs
275+
for port in self._ports:
276+
if len(port) == 1:
277+
yield port.name, port.metadata[0].name, port.metadata[0].attrs
238278
else:
239-
for bit, pin_name in enumerate(pin_names):
240-
yield f"{port_name}[{bit}]", pin_name, attrs
279+
for bit, meta in enumerate(port.metadata):
280+
yield f"{port.name}[{bit}]", meta.name, meta.attrs
241281

242282
def add_clock_constraint(self, clock, frequency):
243283
if isinstance(clock, ClockSignal):
@@ -267,11 +307,11 @@ def iter_clock_constraints(self):
267307
# Constraints on nets with no corresponding input pin (e.g. PLL or SERDES outputs) are not
268308
# affected.
269309
pin_i_to_port = SignalDict()
270-
for res, pin, port, attrs in self._ports:
310+
for pin, port, _fragment in self._pins:
271311
if hasattr(pin, "i"):
272-
if isinstance(res.ios[0], Pins):
312+
if isinstance(port, io.SingleEndedPort):
273313
pin_i_to_port[pin.i] = port.io
274-
elif isinstance(res.ios[0], DiffPairs):
314+
elif isinstance(port, io.DifferentialPort):
275315
pin_i_to_port[pin.i] = port.p
276316
else:
277317
assert False

0 commit comments

Comments
 (0)