Skip to content

Commit a64852d

Browse files
committed
platforms/silicon: make platform.request() return PortLike objects.
1 parent 371fef2 commit a64852d

File tree

2 files changed

+177
-20
lines changed

2 files changed

+177
-20
lines changed

chipflow_lib/platforms/silicon.py

Lines changed: 176 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,208 @@
11
# SPDX-License-Identifier: BSD-2-Clause
22

33
import os
4-
import sys
5-
import tempfile
64
import subprocess
75

86
from amaranth import *
7+
from amaranth.lib import io
8+
99
from amaranth.back import rtlil
1010
from amaranth.hdl import Fragment
11-
from amaranth.lib.io import Direction, Buffer
1211
from amaranth.hdl._ir import PortDirection
1312

1413
from .. import ChipFlowError
1514

1615

17-
__all__ = ["SiliconPlatform"]
16+
__all__ = ["SiliconPlatformPort", "SiliconPlatform"]
1817

1918

20-
class SiliconPlatform:
21-
from ..providers import silicon as providers
19+
class SiliconPlatformPort(io.PortLike):
20+
def __init__(self, name, direction, width, *, invert=False):
21+
if not isinstance(name, str):
22+
raise TypeError(f"Name must be a string, not {name!r}")
23+
if not (isinstance(width, int) and width >= 0):
24+
raise TypeError(f"Width must be a non-negative integer, not {width!r}")
25+
if not isinstance(invert, bool):
26+
raise TypeError(f"'invert' must be a bool, not {invert!r}")
27+
28+
self._direction = io.Direction(direction)
29+
self._invert = invert
30+
31+
self._i = self._o = self._oe = None
32+
if self._direction in (io.Direction.Input, io.Direction.Bidir):
33+
self._i = Signal(width, name=f"{name}__i")
34+
if self._direction in (io.Direction.Output, io.Direction.Bidir):
35+
self._o = Signal(width, name=f"{name}__o")
36+
self._oe = Signal(width, name=f"{name}__oe")
37+
38+
@property
39+
def i(self):
40+
if self._i is None:
41+
raise AttributeError("SiliconPlatformPort with output direction does not have an "
42+
"input signal")
43+
return self._i
44+
45+
@property
46+
def o(self):
47+
if self._o is None:
48+
raise AttributeError("SiliconPlatformPort with input direction does not have an "
49+
"output signal")
50+
return self._o
51+
52+
@property
53+
def oe(self):
54+
if self._oe is None:
55+
raise AttributeError("SiliconPlatformPort with input direction does not have an "
56+
"output enable signal")
57+
return self._oe
58+
59+
@property
60+
def direction(self):
61+
return self._direction
62+
63+
@property
64+
def invert(self):
65+
return self._invert
66+
67+
def __len__(self):
68+
if self._direction is io.Direction.Input:
69+
return len(self._i)
70+
if self._direction is io.Direction.Output:
71+
assert len(self._o) == len(self._oe)
72+
return len(self._o)
73+
if self._direction is io.Direction.Bidir:
74+
assert len(self._i) == len(self._o) == len(self._oe)
75+
return len(self._i)
76+
assert False # :nocov:
77+
78+
def __getitem__(self, key):
79+
result = object.__new__(type(self))
80+
result._i = None if self._i is None else self._i[key]
81+
result._o = None if self._o is None else self._o[key]
82+
result._oe = None if self._oe is None else self._oe[key]
83+
result._invert = self._invert
84+
result._direction = self._direction
85+
return result
86+
87+
def __invert__(self):
88+
result = object.__new__(type(self))
89+
result._i = self._i
90+
result._o = self._o
91+
result._oe = self._oe
92+
result._invert = not self._invert
93+
result._direction = self._direction
94+
return result
95+
96+
def __add__(self, other):
97+
direction = self._direction & other._direction
98+
result = object.__new__(type(self))
99+
result._i = None if direction is io.Direction.Output else Cat(self._i, other._i)
100+
result._o = None if direction is io.Direction.Input else Cat(self._o, other._o)
101+
result._oe = None if direction is io.Direction.Input else Cat(self._oe, other._oe)
102+
result._invert = self._invert
103+
result._direction = direction
104+
return result
105+
106+
107+
class IOBuffer(io.Buffer):
108+
def elaborate(self, platform):
109+
if not isinstance(self.port, SiliconPlatformPort):
110+
raise TypeError(f"Cannot elaborate SiliconPlatform buffer with port {self.port!r}")
111+
112+
m = Module()
113+
114+
if self.direction is not io.Direction.Input:
115+
if self.port.invert:
116+
o_inv = Signal.like(self.o)
117+
m.d.comb += o_inv.eq(~self.o)
118+
else:
119+
o_inv = self.o
22120

121+
if self.direction is not io.Direction.Output:
122+
if self.port.invert:
123+
i_inv = Signal.like(self.i)
124+
m.d.comb += self.i.eq(~i_inv)
125+
else:
126+
i_inv = self.i
127+
128+
if self.direction in (io.Direction.Input, io.Direction.Bidir):
129+
m.d.comb += i_inv.eq(self.port.i)
130+
if self.direction in (io.Direction.Output, io.Direction.Bidir):
131+
m.d.comb += self.port.o.eq(o_inv)
132+
m.d.comb += self.port.oe.eq(self.oe)
133+
134+
return m
135+
136+
137+
class FFBuffer(io.FFBuffer):
138+
def elaborate(self, platform):
139+
if not isinstance(self.port, SiliconPlatformPort):
140+
raise TypeError(f"Cannot elaborate SiliconPlatform buffer with port {self.port!r}")
141+
142+
m = Module()
143+
144+
m.submodules.io_buffer = io_buffer = IOBuffer(self.direction, self.port)
145+
146+
if self.direction is not io.Direction.Output:
147+
i_ff = Signal(reset_less=True)
148+
m.d[self.i_domain] += i_ff.eq(io_buffer.i)
149+
m.d.comb += self.i.eq(i_ff)
150+
151+
if self.direction is not io.Direction.Input:
152+
o_ff = Signal(reset_less=True)
153+
oe_ff = Signal(reset_less=True)
154+
m.d[self.o_domain] += o_ff.eq(self.o)
155+
m.d[self.o_domain] += oe_ff.eq(self.oe)
156+
m.d.comb += io_buffer.o.eq(o_ff)
157+
m.d.comb += io_buffer.oe.eq(oe_ff)
158+
159+
return m
160+
161+
162+
class SiliconPlatform:
23163
def __init__(self, pads):
24164
self._pads = pads
25-
self._pins = {}
165+
self._ports = {}
26166
self._files = {}
27167

28168
def request(self, name):
29169
if "$" in name:
30170
raise NameError(f"Reserved character `$` used in pad name `{name}`")
31171
if name not in self._pads:
32172
raise NameError(f"Pad `{name}` is not defined in chipflow.toml")
33-
if name in self._pins:
173+
if name in self._ports:
34174
raise NameError(f"Pad `{name}` has already been requested")
35175

36176
pad_type = self._pads[name]["type"]
37177
# `clk` is used for clock tree synthesis, but treated as `i` in frontend
38178
if pad_type in ("i", "clk"):
39-
direction = Direction.Input
179+
direction = io.Direction.Input
40180
elif pad_type in ("o", "oe"):
41-
direction = Direction.Output
181+
direction = io.Direction.Output
42182
elif pad_type == "io":
43-
direction = Direction.Bidir
183+
direction = io.Direction.Bidir
44184
else:
45185
assert False
46186

47-
self._pins[name] = pin = Buffer.Signature(direction, 1).create(path=(name,))
48-
return pin
187+
self._ports[name] = port = SiliconPlatformPort(name, direction, 1)
188+
return port
189+
190+
def get_io_buffer(self, buffer):
191+
if isinstance(buffer, io.Buffer):
192+
result = IOBuffer(buffer.direction, buffer.port)
193+
elif isinstance(buffer, io.FFBuffer):
194+
result = FFBuffer(buffer.direction, buffer.port,
195+
i_domain=buffer.i_domain, o_domain=buffer.o_domain)
196+
else:
197+
raise TypeError(f"Unsupported buffer type {buffer!r}")
198+
199+
if buffer.direction is not io.Direction.Output:
200+
result.i = buffer.i
201+
if buffer.direction is not io.Direction.Input:
202+
result.o = buffer.o
203+
result.oe = buffer.oe
204+
205+
return result
49206

50207
def add_file(self, filename, content):
51208
if hasattr(content, "read"):
@@ -73,12 +230,12 @@ def _prepare(self, elaboratable, name="top"):
73230

74231
# Prepare toplevel ports according to chipflow.toml.
75232
ports = []
76-
for pin_name, pin in self._pins.items():
77-
if pin.signature.direction in (Direction.Input, Direction.Bidir):
78-
ports.append((f"io${pin_name}$i", pin.i, PortDirection.Input))
79-
if pin.signature.direction in (Direction.Output, Direction.Bidir):
80-
ports.append((f"io${pin_name}$o", pin.o, PortDirection.Output))
81-
ports.append((f"io${pin_name}$oe", pin.oe, PortDirection.Output))
233+
for port_name, port in self._ports.items():
234+
if port.direction in (io.Direction.Input, io.Direction.Bidir):
235+
ports.append((f"io${port_name}$i", port.i, PortDirection.Input))
236+
if port.direction in (io.Direction.Output, io.Direction.Bidir):
237+
ports.append((f"io${port_name}$o", port.o, PortDirection.Output))
238+
ports.append((f"io${port_name}$oe", port.oe, PortDirection.Output))
82239

83240
# Prepare design for RTLIL conversion.
84241
return fragment.prepare(ports)

chipflow_lib/steps/silicon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def submit(self, rtlil_path, *, dry_run=False):
9797
"pad_ring": self.silicon_config["pad_ring"],
9898
"pads": {
9999
pad_name: self.silicon_config["pads"][pad_name]
100-
for pad_name in self.platform._pins
100+
for pad_name in self.platform._ports
101101
},
102102
"power": self.silicon_config.get("power", {})
103103
}

0 commit comments

Comments
 (0)