Skip to content

Commit 54bd5f1

Browse files
committed
hdl._nir, back.rtlil: use Format.* to emit enum attributes and wires for fields.
1 parent 8751e6c commit 54bd5f1

File tree

7 files changed

+187
-9
lines changed

7 files changed

+187
-9
lines changed

amaranth/back/rtlil.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ def emit(self):
421421
self.emit_cell_wires()
422422
self.emit_submodule_wires()
423423
self.emit_connects()
424+
self.emit_signal_details()
424425
self.emit_submodules()
425426
self.emit_cells()
426427

@@ -491,12 +492,13 @@ def emit_signal_wires(self):
491492
attrs.update(signal.attrs)
492493
self.value_src_loc[value] = signal.src_loc
493494

494-
for repr in signal._value_repr:
495-
if repr.path == () and isinstance(repr.format, _repr.FormatEnum):
496-
enum = repr.format.enum
497-
attrs["enum_base_type"] = enum.__name__
498-
for enum_value in enum:
499-
attrs["enum_value_{:0{}b}".format(enum_value.value, len(signal))] = enum_value.name
495+
_value, _signed, enum = self.netlist.signal_details[signal][()]
496+
if enum is not None:
497+
enum_name, variants = enum
498+
if enum_name is not None:
499+
attrs["enum_base_type"] = enum_name
500+
for var_val, var_name in variants.items():
501+
attrs["enum_value_{:0{}b}".format(var_val, len(signal))] = var_name
500502

501503
if name in self.module.ports:
502504
port_value, _flow = self.module.ports[name]
@@ -666,6 +668,32 @@ def emit_connects(self):
666668
if name not in self.driven_sigports:
667669
self.builder.connect(wire.name, self.sigspec(value))
668670

671+
def emit_signal_details(self):
672+
for signal, name in self.module.signal_names.items():
673+
details = self.netlist.signal_details[signal]
674+
for path, (value, signed, enum) in details.items():
675+
if path == ():
676+
continue
677+
name_parts = [name]
678+
for comp in path:
679+
if isinstance(comp, str):
680+
name_parts.append(f".{comp}")
681+
elif isinstance(comp, int):
682+
name_parts.append(f"[{comp}]")
683+
else:
684+
assert False # :nocov:
685+
attrs = {}
686+
if enum is not None:
687+
enum_name, variants = enum
688+
if enum_name is not None:
689+
attrs["enum_base_type"] = enum_name
690+
for var_val, var_name in variants.items():
691+
attrs["enum_value_{:0{}b}".format(var_val, len(value))] = var_name
692+
wire = self.builder.wire(width=len(value), signed=signed, attrs=attrs,
693+
name="".join(name_parts),
694+
src_loc=signal.src_loc)
695+
self.builder.connect(wire.name, self.sigspec(value))
696+
669697
def emit_submodules(self):
670698
for submodule_idx in self.module.submodules:
671699
submodule = self.netlist.modules[submodule_idx]

amaranth/hdl/_ast.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,6 +2095,11 @@ def __init__(self, shape=None, *, name=None, init=None, reset=None, reset_less=F
20952095

20962096
self._attrs = OrderedDict(() if attrs is None else attrs)
20972097

2098+
if isinstance(orig_shape, ShapeCastable):
2099+
self._format = orig_shape.format(orig_shape(self), "")
2100+
else:
2101+
self._format = Format("{}", self)
2102+
20982103
if decoder is not None:
20992104
# The value representation is specified explicitly. Since we do not expose `hdl._repr`,
21002105
# this is the only way to add a custom filter to the signal right now.

amaranth/hdl/_ir.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ def emit_value(self, builder):
690690

691691

692692
class NetlistEmitter:
693-
def __init__(self, netlist: _nir.Netlist, design, *, all_undef_to_ff=False):
693+
def __init__(self, netlist: _nir.Netlist, design: Design, *, all_undef_to_ff=False):
694694
self.netlist = netlist
695695
self.design = design
696696
self.all_undef_to_ff = all_undef_to_ff
@@ -776,7 +776,7 @@ def extend(self, value: _nir.Value, signed: bool, width: int):
776776
def emit_operator(self, module_idx: int, operator: str, *inputs: _nir.Value, src_loc):
777777
op = _nir.Operator(module_idx, operator=operator, inputs=inputs, src_loc=src_loc)
778778
return self.netlist.add_value_cell(op.width, op)
779-
779+
780780
def emit_matches(self, module_idx: int, value: _nir.Value, patterns, *, src_loc):
781781
key = module_idx, value, patterns, src_loc
782782
try:
@@ -1334,6 +1334,40 @@ def emit_top_ports(self, fragment: _ir.Fragment):
13341334
else:
13351335
raise ValueError(f"Invalid port direction {dir!r}")
13361336

1337+
def emit_signal_details(self):
1338+
for signal, fragment in self.design.signal_lca.items():
1339+
module_idx = self.fragment_module_idx[fragment]
1340+
details = {}
1341+
def emit_format(path, fmt):
1342+
if isinstance(fmt, _ast.Format):
1343+
specs = [
1344+
chunk[0]
1345+
for chunk in fmt._chunks
1346+
if not isinstance(chunk, str)
1347+
]
1348+
if len(specs) != 1:
1349+
return
1350+
val, signed = self.emit_rhs(module_idx, specs[0])
1351+
details[path] = (val, signed, None)
1352+
elif isinstance(fmt, _ast.Format.Enum):
1353+
val, signed = self.emit_rhs(module_idx, fmt._value)
1354+
details[path] = (val, signed, (fmt._name, fmt._variants))
1355+
elif isinstance(fmt, _ast.Format.Struct):
1356+
val, signed = self.emit_rhs(module_idx, fmt._value)
1357+
details[path] = (val, signed, None)
1358+
for name, subfmt in fmt._fields.items():
1359+
emit_format(path + (name,), subfmt)
1360+
elif isinstance(fmt, _ast.Format.Array):
1361+
val, signed = self.emit_rhs(module_idx, fmt._value)
1362+
details[path] = (val, signed, None)
1363+
for idx, subfmt in enumerate(fmt._fields):
1364+
emit_format(path + (idx,), subfmt)
1365+
emit_format((), signal._format)
1366+
val, signed = self.emit_rhs(module_idx, signal)
1367+
if () not in details or details[()][0] != val:
1368+
details[()] = (val, signed, None)
1369+
self.netlist.signal_details[signal] = details
1370+
13371371
def emit_drivers(self):
13381372
for driver in self.drivers.values():
13391373
if (driver.domain is not None and
@@ -1452,6 +1486,7 @@ def emit_fragment(self, fragment: _ir.Fragment, parent_module_idx: 'int | None',
14521486
for subfragment, _name, sub_src_loc in fragment.subfragments:
14531487
self.emit_fragment(subfragment, module_idx, cell_src_loc=sub_src_loc)
14541488
if parent_module_idx is None:
1489+
self.emit_signal_details()
14551490
self.emit_drivers()
14561491
self.emit_top_ports(fragment)
14571492
if self.all_undef_to_ff:

amaranth/hdl/_nir.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ def __init__(self):
329329
self.connections: dict[Net, Net] = {}
330330
self.io_ports: list[_ast.IOPort] = []
331331
self.signals = SignalDict()
332+
self.signal_details = SignalDict()
332333
self.last_late_net = 0
333334

334335
def resolve_net(self, net: Net):
@@ -345,6 +346,11 @@ def resolve_all_nets(self):
345346
cell.resolve_nets(self)
346347
for sig in self.signals:
347348
self.signals[sig] = self.resolve_value(self.signals[sig])
349+
for signal in self.signals:
350+
self.signal_details[signal] = {
351+
path: (self.resolve_value(value), signed, enum)
352+
for path, (value, signed, enum) in self.signal_details[signal].items()
353+
}
348354

349355
def __repr__(self):
350356
result = ["("]

tests/test_back_rtlil.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from amaranth.back import rtlil
55
from amaranth.hdl import *
66
from amaranth.hdl._ast import *
7-
from amaranth.lib import memory, wiring
7+
from amaranth.lib import memory, wiring, data, enum
88

99
from .utils import *
1010

@@ -2010,6 +2010,67 @@ def test_print_align(self):
20102010
""")
20112011

20122012

2013+
class DetailTestCase(RTLILTestCase):
2014+
def test_enum(self):
2015+
class MyEnum(enum.Enum, shape=unsigned(2)):
2016+
A = 0
2017+
B = 1
2018+
C = 2
2019+
2020+
sig = Signal(MyEnum)
2021+
m = Module()
2022+
m.d.comb += sig.eq(MyEnum.A)
2023+
self.assertRTLIL(m, [sig.as_value()], R"""
2024+
attribute \generator "Amaranth"
2025+
attribute \top 1
2026+
module \top
2027+
attribute \enum_base_type "MyEnum"
2028+
attribute \enum_value_00 "A"
2029+
attribute \enum_value_01 "B"
2030+
attribute \enum_value_10 "C"
2031+
wire width 2 output 0 \sig
2032+
connect \sig 2'00
2033+
end
2034+
""")
2035+
2036+
def test_struct(self):
2037+
class MyEnum(enum.Enum, shape=unsigned(2)):
2038+
A = 0
2039+
B = 1
2040+
C = 2
2041+
2042+
class Meow(data.Struct):
2043+
a: MyEnum
2044+
b: 3
2045+
c: signed(4)
2046+
d: data.ArrayLayout(2, 2)
2047+
2048+
sig = Signal(Meow)
2049+
m = Module()
2050+
self.assertRTLIL(m, [sig.as_value()], R"""
2051+
attribute \generator "Amaranth"
2052+
attribute \top 1
2053+
module \top
2054+
wire width 13 input 0 \sig
2055+
attribute \enum_base_type "MyEnum"
2056+
attribute \enum_value_00 "A"
2057+
attribute \enum_value_01 "B"
2058+
attribute \enum_value_10 "C"
2059+
wire width 2 \sig.a
2060+
wire width 3 \sig.b
2061+
wire width 4 signed \sig.c
2062+
wire width 4 \sig.d
2063+
wire width 2 \sig.d[0]
2064+
wire width 2 \sig.d[1]
2065+
connect \sig.a \sig [1:0]
2066+
connect \sig.b \sig [4:2]
2067+
connect \sig.c \sig [8:5]
2068+
connect \sig.d \sig [12:9]
2069+
connect \sig.d[0] \sig [10:9]
2070+
connect \sig.d[1] \sig [12:11]
2071+
end
2072+
""")
2073+
20132074
class ComponentTestCase(RTLILTestCase):
20142075
def test_component(self):
20152076
class MyComponent(wiring.Component):

tests/test_hdl_ir.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from amaranth.hdl._ir import *
99
from amaranth.hdl._mem import *
1010

11+
from amaranth.lib import enum, data
12+
1113
from .utils import *
1214

1315

@@ -3501,3 +3503,41 @@ def test_undef_to_ff_partial(self):
35013503
(cell 3 0 (flipflop 3.0:5 10 pos 0 0))
35023504
)
35033505
""")
3506+
3507+
3508+
class DetailsTestCase(FHDLTestCase):
3509+
def test_details(self):
3510+
class MyEnum(enum.Enum, shape=unsigned(2)):
3511+
A = 0
3512+
B = 1
3513+
C = 2
3514+
l = data.StructLayout({"a": MyEnum, "b": signed(3)})
3515+
s1 = Signal(l)
3516+
s2 = Signal(MyEnum)
3517+
s3 = Signal(signed(3))
3518+
s4 = Signal(unsigned(4))
3519+
nl = build_netlist(Fragment.get(Module(), None), [
3520+
s1.as_value(), s2.as_value(), s3, s4,
3521+
])
3522+
self.assertEqual(nl.signal_details[s1.as_value()], {
3523+
(): (nl.signals[s1.as_value()], False, None),
3524+
('a',): (nl.signals[s1.as_value()][0:2], False, ("MyEnum", {
3525+
0: "A",
3526+
1: "B",
3527+
2: "C",
3528+
})),
3529+
('b',): (nl.signals[s1.as_value()][2:5], True, None)
3530+
})
3531+
self.assertEqual(nl.signal_details[s2.as_value()], {
3532+
(): (nl.signals[s2.as_value()], False, ("MyEnum", {
3533+
0: "A",
3534+
1: "B",
3535+
2: "C",
3536+
})),
3537+
})
3538+
self.assertEqual(nl.signal_details[s3], {
3539+
(): (nl.signals[s3], True, None),
3540+
})
3541+
self.assertEqual(nl.signal_details[s4], {
3542+
(): (nl.signals[s4], False, None),
3543+
})

tests/test_lib_data.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ def const(self, init):
647647
def from_bits(self, bits):
648648
return bits
649649

650+
def format(self, value, spec):
651+
return Format("")
652+
650653
v = Signal(data.StructLayout({
651654
"f": WrongCastable()
652655
}))

0 commit comments

Comments
 (0)