Skip to content

Commit 46c076e

Browse files
authored
Merge branch 'amaranth-lang:main' into main
2 parents 7dcfafb + e55dec9 commit 46c076e

23 files changed

+348
-105
lines changed

.env.toolchain

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
AMARANTH_USE_YOSYS=system
2+
YOSYS=yowasp-yosys
3+
SBY=yowasp-sby
4+
SMTBMC=yowasp-yosys-smtbmc
5+
# examples
6+
NEXTPNR_ICE40=yowasp-nextpnr-ice40
7+
ICEPACK=yowasp-icepack

.github/workflows/main.yaml

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,18 @@ jobs:
1515
- '3.9'
1616
- '3.10'
1717
- '3.11'
18+
- '3.12'
1819
- 'pypy-3.8'
1920
- 'pypy-3.9'
21+
- 'pypy-3.10'
2022
allow-failure:
2123
- false
2224
include:
23-
- python-version: '3.12-dev'
25+
- python-version: '3.13-dev'
2426
allow-failure: true
2527
continue-on-error: '${{ matrix.allow-failure }}'
2628
name: 'test (${{ matrix.python-version }})'
2729
steps:
28-
- name: Preserve Wasm cache
29-
uses: actions/cache@v3
30-
with:
31-
path: |
32-
~/.cache/wasmtime
33-
~/.cache/YoWASP
34-
key: ${{ runner.os }}-wasm
3530
- name: Check out source code
3631
uses: actions/checkout@v3
3732
with:
@@ -46,15 +41,16 @@ jobs:
4641
sudo add-apt-repository 'deb http://ppa.launchpad.net/sri-csl/formal-methods/ubuntu bionic main'
4742
sudo apt-get update
4843
sudo apt-get install yices2
49-
pip install codecov yowasp-yosys
44+
pip install codecov
5045
pdm install --dev
46+
- name: Cache YoWASP build products
47+
uses: actions/cache@v3
48+
with:
49+
path: ~/.cache/YoWASP
50+
key: YoWASP-${{ runner.os }}-${{ hashFiles('./.venv/**/*.wasm') }}
51+
restore-keys: |
52+
YoWASP-${{ runner.os }}-
5153
- name: Run tests
52-
env:
53-
YOSYS: yowasp-yosys
54-
NEXTPNR_ICE40: yowasp-nextpnr-ice40
55-
ICEPACK: yowasp-icepack
56-
SBY: yowasp-sby
57-
SMTBMC: yowasp-yosys-smtbmc
5854
run: |
5955
pdm run test
6056
- name: Submit code coverage
@@ -87,14 +83,14 @@ jobs:
8783
name: docs
8884
path: docs/_build
8985

90-
required: # group all required workflows into one for the required status check
86+
required: # group all required workflows into one to avoid reconfiguring this in Actions settings
9187
needs:
9288
- test
9389
- document
90+
if: always() && !contains(needs.*.result, 'cancelled')
9491
runs-on: ubuntu-latest
9592
steps:
96-
- run: |
97-
true
93+
- run: ${{ contains(needs.*.result, 'failure') && 'false' || 'true' }}
9894

9995
publish-docs:
10096
needs: document

amaranth/build/plat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def iter_clock_constraints(self):
303303
continue
304304
yield net_signal, port_signal, frequency
305305

306-
def toolchain_prepare(self, fragment, name, **kwargs):
306+
def toolchain_prepare(self, fragment, name, *, emit_src=True, **kwargs):
307307
# Restrict the name of the design to a strict alphanumeric character set. Platforms will
308308
# interpolate the name of the design in many different contexts: filesystem paths, Python
309309
# scripts, Tcl scripts, ad-hoc constraint files, and so on. It is not practical to add
@@ -319,7 +319,7 @@ def toolchain_prepare(self, fragment, name, **kwargs):
319319
# and to incorporate the Amaranth version into generated code.
320320
autogenerated = "Automatically generated by Amaranth {}. Do not edit.".format(__version__)
321321

322-
rtlil_text, self._name_map = rtlil.convert_fragment(fragment, name=name)
322+
rtlil_text, self._name_map = rtlil.convert_fragment(fragment, name=name, emit_src=emit_src)
323323

324324
# Retrieve an override specified in either the environment or as a kwarg.
325325
# expected_type parameter is used to assert the type of kwargs, passing `None` will disable

amaranth/build/res.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,22 @@ def resolve(resource, dir, xdr, name, attrs):
137137
# ignore it as well.
138138
if isinstance(phys, Pins):
139139
phys_names = phys.names
140-
port = wiring.Signature({"io": wiring.In(len(phys))}).create(path=(name,))
140+
port = Record([("io", len(phys))], name=name)
141+
port.signature = wiring.Signature({"io": wiring.In(len(phys))})
141142
if isinstance(phys, DiffPairs):
142143
phys_names = []
143-
members = {}
144+
rec_members = []
145+
sig_members = {}
144146
if not self.should_skip_port_component(None, attrs, "p"):
145147
phys_names += phys.p.names
146-
members["p"] = wiring.In(len(phys))
148+
rec_members.append(("p", len(phys)))
149+
sig_members["p"] = wiring.In(len(phys))
147150
if not self.should_skip_port_component(None, attrs, "n"):
148151
phys_names += phys.n.names
149-
members["n"] = wiring.In(len(phys))
150-
port = wiring.Signature(members).create(path=(name,))
152+
rec_members.append(("n", len(phys)))
153+
sig_members["n"] = wiring.In(len(phys))
154+
port = Record(rec_members, name=name)
155+
port.signature = wiring.Signature(sig_members)
151156
if dir == "-":
152157
pin = None
153158
else:

amaranth/hdl/ast.py

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,25 @@ def signed(width):
156156
return Shape(width, signed=True)
157157

158158

159+
def _overridable_by_reflected(method_name):
160+
"""Allow overriding the decorated method.
161+
162+
Allows :class:`ValueCastable` to override the decorated method by implementing
163+
a reflected method named ``method_name``. Intended for operators, but
164+
also usable for other methods that have a reflected counterpart.
165+
"""
166+
def decorator(f):
167+
@functools.wraps(f)
168+
def wrapper(self, other):
169+
if isinstance(other, ValueCastable) and hasattr(other, method_name):
170+
res = getattr(other, method_name)(self)
171+
if res is not NotImplemented:
172+
return res
173+
return f(self, other)
174+
return wrapper
175+
return decorator
176+
177+
159178
class Value(metaclass=ABCMeta):
160179
@staticmethod
161180
def cast(obj):
@@ -195,26 +214,31 @@ def __invert__(self):
195214
def __neg__(self):
196215
return Operator("-", [self])
197216

217+
@_overridable_by_reflected("__radd__")
198218
def __add__(self, other):
199-
return Operator("+", [self, other])
219+
return Operator("+", [self, other], src_loc_at=1)
200220
def __radd__(self, other):
201221
return Operator("+", [other, self])
222+
@_overridable_by_reflected("__rsub__")
202223
def __sub__(self, other):
203-
return Operator("-", [self, other])
224+
return Operator("-", [self, other], src_loc_at=1)
204225
def __rsub__(self, other):
205226
return Operator("-", [other, self])
206227

228+
@_overridable_by_reflected("__rmul__")
207229
def __mul__(self, other):
208-
return Operator("*", [self, other])
230+
return Operator("*", [self, other], src_loc_at=1)
209231
def __rmul__(self, other):
210232
return Operator("*", [other, self])
211233

234+
@_overridable_by_reflected("__rmod__")
212235
def __mod__(self, other):
213-
return Operator("%", [self, other])
236+
return Operator("%", [self, other], src_loc_at=1)
214237
def __rmod__(self, other):
215238
return Operator("%", [other, self])
239+
@_overridable_by_reflected("__rfloordiv__")
216240
def __floordiv__(self, other):
217-
return Operator("//", [self, other])
241+
return Operator("//", [self, other], src_loc_at=1)
218242
def __rfloordiv__(self, other):
219243
return Operator("//", [other, self])
220244

@@ -224,46 +248,57 @@ def __check_shamt(self):
224248
# by a signed value to make sure the shift amount can always be interpreted as
225249
# an unsigned value.
226250
raise TypeError("Shift amount must be unsigned")
251+
@_overridable_by_reflected("__rlshift__")
227252
def __lshift__(self, other):
228253
other = Value.cast(other)
229254
other.__check_shamt()
230-
return Operator("<<", [self, other])
255+
return Operator("<<", [self, other], src_loc_at=1)
231256
def __rlshift__(self, other):
232257
self.__check_shamt()
233258
return Operator("<<", [other, self])
259+
@_overridable_by_reflected("__rrshift__")
234260
def __rshift__(self, other):
235261
other = Value.cast(other)
236262
other.__check_shamt()
237-
return Operator(">>", [self, other])
263+
return Operator(">>", [self, other], src_loc_at=1)
238264
def __rrshift__(self, other):
239265
self.__check_shamt()
240266
return Operator(">>", [other, self])
241267

268+
@_overridable_by_reflected("__rand__")
242269
def __and__(self, other):
243-
return Operator("&", [self, other])
270+
return Operator("&", [self, other], src_loc_at=1)
244271
def __rand__(self, other):
245272
return Operator("&", [other, self])
273+
@_overridable_by_reflected("__rxor__")
246274
def __xor__(self, other):
247-
return Operator("^", [self, other])
275+
return Operator("^", [self, other], src_loc_at=1)
248276
def __rxor__(self, other):
249277
return Operator("^", [other, self])
278+
@_overridable_by_reflected("__ror__")
250279
def __or__(self, other):
251-
return Operator("|", [self, other])
280+
return Operator("|", [self, other], src_loc_at=1)
252281
def __ror__(self, other):
253282
return Operator("|", [other, self])
254283

284+
@_overridable_by_reflected("__eq__")
255285
def __eq__(self, other):
256-
return Operator("==", [self, other])
286+
return Operator("==", [self, other], src_loc_at=1)
287+
@_overridable_by_reflected("__ne__")
257288
def __ne__(self, other):
258-
return Operator("!=", [self, other])
289+
return Operator("!=", [self, other], src_loc_at=1)
290+
@_overridable_by_reflected("__gt__")
259291
def __lt__(self, other):
260-
return Operator("<", [self, other])
292+
return Operator("<", [self, other], src_loc_at=1)
293+
@_overridable_by_reflected("__ge__")
261294
def __le__(self, other):
262-
return Operator("<=", [self, other])
295+
return Operator("<=", [self, other], src_loc_at=1)
296+
@_overridable_by_reflected("__lt__")
263297
def __gt__(self, other):
264-
return Operator(">", [self, other])
298+
return Operator(">", [self, other], src_loc_at=1)
299+
@_overridable_by_reflected("__le__")
265300
def __ge__(self, other):
266-
return Operator(">=", [self, other])
301+
return Operator(">=", [self, other], src_loc_at=1)
267302

268303
def __abs__(self):
269304
if self.shape().signed:

amaranth/hdl/mem.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ def elaborate(self, platform):
187187
parts.append(Mux(cond, write_port.data[bits], data[bits]))
188188
data = Cat(parts)
189189
else:
190-
data = Mux(write_port.en, write_port.data, data)
190+
cond = write_port.en & (port.addr == write_port.addr)
191+
data = Mux(cond, write_port.data, data)
191192
f.add_statements(
192193
Switch(port.en, {
193194
1: port.data.eq(data)

amaranth/lib/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def const(self, init):
229229
mask = ((1 << cast_field_shape.width) - 1) << field.offset
230230
int_value &= ~mask
231231
int_value |= (key_value.value << field.offset) & mask
232-
return Const(int_value, self.as_shape())
232+
return View(self, Const(int_value, self.as_shape()))
233233

234234

235235
class StructLayout(Layout):

amaranth/lib/enum.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,23 @@ def __new__(metacls, name, bases, namespace, shape=None, **kwargs):
8383
category=SyntaxWarning,
8484
stacklevel=2)
8585
# Actually instantiate the enumeration class.
86-
cls = py_enum.EnumMeta.__new__(metacls, name, bases, namespace, **kwargs)
8786
if shape is not None:
87+
cls = py_enum.EnumMeta.__new__(metacls, name, bases, namespace, **kwargs)
8888
# Shape is provided explicitly. Set the `_amaranth_shape_` attribute, and check that
8989
# the values of every member can be cast to the provided shape without truncation.
9090
cls._amaranth_shape_ = shape
9191
else:
9292
# Shape is not provided explicitly. Behave the same as a standard enumeration;
9393
# the lack of `_amaranth_shape_` attribute is used to emit a warning when such
9494
# an enumeration is used in a concatenation.
95-
pass
95+
bases = tuple(
96+
py_enum.Enum if base is Enum else
97+
py_enum.IntEnum if base is IntEnum else
98+
py_enum.Flag if base is Flag else
99+
py_enum.IntFlag if base is IntFlag else base
100+
for base in bases
101+
)
102+
cls = py_enum.EnumMeta.__new__(py_enum.EnumMeta, name, bases, namespace, **kwargs)
96103
return cls
97104

98105
def as_shape(cls):
@@ -118,7 +125,7 @@ def as_shape(cls):
118125
# replacement for `enum.Enum`.
119126
return Shape._cast_plain_enum(cls)
120127

121-
def __call__(cls, value):
128+
def __call__(cls, value, *args, **kwargs):
122129
# :class:`py_enum.Enum` uses ``__call__()`` for type casting: ``E(x)`` returns
123130
# the enumeration member whose value equals ``x``. In this case, ``x`` must be a concrete
124131
# value.
@@ -130,7 +137,7 @@ def __call__(cls, value):
130137
# comparisons with enum members of the wrong type.
131138
if isinstance(value, Value):
132139
return value
133-
return super().__call__(value)
140+
return super().__call__(value, *args, **kwargs)
134141

135142
def const(cls, init):
136143
# Same considerations apply as above.
@@ -144,21 +151,28 @@ def const(cls, init):
144151
return Const(member.value, cls.as_shape())
145152

146153

147-
class Enum(py_enum.Enum, metaclass=EnumMeta):
154+
class Enum(py_enum.Enum):
148155
"""Subclass of the standard :class:`enum.Enum` that has :class:`EnumMeta` as
149156
its metaclass."""
150157

151158

152-
class IntEnum(py_enum.IntEnum, metaclass=EnumMeta):
159+
class IntEnum(py_enum.IntEnum):
153160
"""Subclass of the standard :class:`enum.IntEnum` that has :class:`EnumMeta` as
154161
its metaclass."""
155162

156163

157-
class Flag(py_enum.Flag, metaclass=EnumMeta):
164+
class Flag(py_enum.Flag):
158165
"""Subclass of the standard :class:`enum.Flag` that has :class:`EnumMeta` as
159166
its metaclass."""
160167

161168

162-
class IntFlag(py_enum.IntFlag, metaclass=EnumMeta):
169+
class IntFlag(py_enum.IntFlag):
163170
"""Subclass of the standard :class:`enum.IntFlag` that has :class:`EnumMeta` as
164171
its metaclass."""
172+
173+
# Fix up the metaclass after the fact: the metaclass __new__ requires these classes
174+
# to already be present, and also would not install itself on them due to lack of shape.
175+
Enum.__class__ = EnumMeta
176+
IntEnum.__class__ = EnumMeta
177+
Flag.__class__ = EnumMeta
178+
IntFlag.__class__ = EnumMeta

0 commit comments

Comments
 (0)