Skip to content

Commit e6a8884

Browse files
wanda-phiwhitequark
andcommitted
Implement RFC 50: Print and string formatting.
Co-authored-by: Catherine <whitequark@whitequark.org>
1 parent 161b014 commit e6a8884

21 files changed

+1091
-111
lines changed

amaranth/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
__all__ = [
1717
"Shape", "unsigned", "signed",
1818
"Value", "Const", "C", "Mux", "Cat", "Array", "Signal", "ClockSignal", "ResetSignal",
19+
"Format", "Print", "Assert",
1920
"Module",
2021
"ClockDomain",
2122
"Elaboratable", "Fragment", "Instance",

amaranth/asserts.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
from .hdl._ast import AnyConst, AnySeq, Initial, Assert, Assume, Cover
1+
from .hdl._ast import AnyConst, AnySeq, Initial
2+
from . import hdl as __hdl
23

34

45
__all__ = ["AnyConst", "AnySeq", "Initial", "Assert", "Assume", "Cover"]
6+
7+
8+
def __getattr__(name):
9+
import warnings
10+
if name in __hdl.__dict__ and name in __all__:
11+
if not (name.startswith("__") and name.endswith("__")):
12+
warnings.warn(f"instead of `{__name__}.{name}`, use `{__hdl.__name__}.{name}`",
13+
DeprecationWarning, stacklevel=2)
14+
return getattr(__hdl, name)
15+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

amaranth/back/rtlil.py

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,8 @@ def emit_cell_wires(self):
441441
continue # Instances use one wire per output, not per cell.
442442
elif isinstance(cell, (_nir.PriorityMatch, _nir.Matches)):
443443
continue # Inlined into assignment lists.
444-
elif isinstance(cell, (_nir.SyncProperty, _nir.AsyncProperty, _nir.Memory,
445-
_nir.SyncWritePort)):
444+
elif isinstance(cell, (_nir.SyncPrint, _nir.AsyncPrint, _nir.SyncProperty,
445+
_nir.AsyncProperty, _nir.Memory, _nir.SyncWritePort)):
446446
continue # No outputs.
447447
elif isinstance(cell, _nir.AssignmentList):
448448
width = len(cell.default)
@@ -859,37 +859,78 @@ def emit_read_port(self, cell_idx, cell):
859859
})
860860
self.builder.cell(f"$memrd_v2", ports=ports, params=params, src=_src(cell.src_loc))
861861

862-
def emit_property(self, cell_idx, cell):
863-
if isinstance(cell, _nir.AsyncProperty):
864-
ports = {
865-
"A": self.sigspec(cell.test),
866-
"EN": self.sigspec(cell.en),
867-
}
868-
if isinstance(cell, _nir.SyncProperty):
869-
test = self.builder.wire(1, attrs={"init": _ast.Const(0, 1)})
870-
en = self.builder.wire(1, attrs={"init": _ast.Const(0, 1)})
871-
for (d, q) in [
872-
(cell.test, test),
873-
(cell.en, en),
874-
]:
875-
ports = {
876-
"D": self.sigspec(d),
877-
"Q": q,
878-
"CLK": self.sigspec(cell.clk),
879-
}
880-
params = {
881-
"WIDTH": 1,
882-
"CLK_POLARITY": {
883-
"pos": True,
884-
"neg": False,
885-
}[cell.clk_edge],
886-
}
887-
self.builder.cell(f"$dff", ports=ports, params=params, src=_src(cell.src_loc))
888-
ports = {
889-
"A": test,
890-
"EN": en,
891-
}
892-
self.builder.cell(f"${cell.kind}", name=cell.name, ports=ports, src=_src(cell.src_loc))
862+
def emit_print(self, cell_idx, cell):
863+
args = []
864+
format = []
865+
if cell.format is not None:
866+
for chunk in cell.format.chunks:
867+
if isinstance(chunk, str):
868+
format.append(chunk)
869+
else:
870+
spec = _ast.Format._parse_format_spec(chunk.format_desc, _ast.Shape(len(chunk.value), chunk.signed))
871+
type = spec["type"]
872+
if type == "s":
873+
assert len(chunk.value) % 8 == 0
874+
for bit in reversed(range(0, len(chunk.value), 8)):
875+
args += chunk.value[bit:bit+8]
876+
else:
877+
args += chunk.value
878+
if type is None:
879+
type = "d"
880+
if type == "x" or type == "X":
881+
# TODO(yosys): "H" type
882+
type = "h"
883+
if type == "s":
884+
# TODO(yosys): support for single unicode character?
885+
type = "c"
886+
width = spec["width"]
887+
align = spec["align"]
888+
if align is None:
889+
align = ">" if type != "c" else "<"
890+
if align == "=":
891+
# TODO(yosys): "=" alignment
892+
align = ">"
893+
fill = spec["fill"]
894+
if fill not in (" ", "0"):
895+
# TODO(yosys): arbitrary fill
896+
fill = " "
897+
# TODO(yosys): support for options, grouping
898+
sign = spec["sign"]
899+
if sign != "+":
900+
# TODO(yosys): support " " sign
901+
sign = ""
902+
if type == "c":
903+
signed = ""
904+
elif chunk.signed:
905+
signed = "s"
906+
else:
907+
signed = "u"
908+
format.append(f"{{{len(chunk.value)}:{align}{fill}{width or ''}{type}{sign}{signed}}}")
909+
ports = {
910+
"EN": self.sigspec(cell.en),
911+
"ARGS": self.sigspec(_nir.Value(args)),
912+
}
913+
params = {
914+
"FORMAT": "".join(format),
915+
"ARGS_WIDTH": len(args),
916+
"PRIORITY": -cell_idx,
917+
}
918+
if isinstance(cell, (_nir.AsyncPrint, _nir.AsyncProperty)):
919+
ports["TRG"] = self.sigspec(_nir.Value())
920+
params["TRG_ENABLE"] = False
921+
params["TRG_WIDTH"] = 0
922+
params["TRG_POLARITY"] = 0
923+
if isinstance(cell, (_nir.SyncPrint, _nir.SyncProperty)):
924+
ports["TRG"] = self.sigspec(cell.clk)
925+
params["TRG_ENABLE"] = True
926+
params["TRG_WIDTH"] = 1
927+
params["TRG_POLARITY"] = cell.clk_edge == "pos"
928+
if isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint)):
929+
self.builder.cell(f"$print", params=params, ports=ports, src=_src(cell.src_loc))
930+
if isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
931+
params["FLAVOR"] = cell.kind
932+
ports["A"] = self.sigspec(cell.test)
933+
self.builder.cell(f"$check", params=params, ports=ports, src=_src(cell.src_loc))
893934

894935
def emit_any_value(self, cell_idx, cell):
895936
self.builder.cell(f"${cell.kind}", ports={
@@ -939,8 +980,8 @@ def emit_cells(self):
939980
self.emit_write_port(cell_idx, cell)
940981
elif isinstance(cell, (_nir.AsyncReadPort, _nir.SyncReadPort)):
941982
self.emit_read_port(cell_idx, cell)
942-
elif isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
943-
self.emit_property(cell_idx, cell)
983+
elif isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint, _nir.AsyncProperty, _nir.SyncProperty)):
984+
self.emit_print(cell_idx, cell)
944985
elif isinstance(cell, _nir.AnyValue):
945986
self.emit_any_value(cell_idx, cell)
946987
elif isinstance(cell, _nir.Initial):

amaranth/hdl/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from ._ast import Shape, unsigned, signed, ShapeCastable, ShapeLike
22
from ._ast import Value, ValueCastable, ValueLike
33
from ._ast import Const, C, Mux, Cat, Array, Signal, ClockSignal, ResetSignal
4+
from ._ast import Format, Print, Assert, Assume, Cover
45
from ._dsl import SyntaxError, SyntaxWarning, Module
56
from ._cd import DomainError, ClockDomain
67
from ._ir import UnusedElaboratable, Elaboratable, DriverConflict, Fragment, Instance
@@ -14,6 +15,7 @@
1415
"Shape", "unsigned", "signed", "ShapeCastable", "ShapeLike",
1516
"Value", "ValueCastable", "ValueLike",
1617
"Const", "C", "Mux", "Cat", "Array", "Signal", "ClockSignal", "ResetSignal",
18+
"Format", "Print", "Assert", "Assume", "Cover",
1719
# _dsl
1820
"SyntaxError", "SyntaxWarning", "Module",
1921
# _cd

0 commit comments

Comments
 (0)