Skip to content

Commit 81b342a

Browse files
committed
Implement RFC 50: Print and string formatting.
1 parent 161b014 commit 81b342a

File tree

17 files changed

+877
-108
lines changed

17 files changed

+877
-108
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: 66 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,68 @@ 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+
args += chunk.value
871+
spec = _ast.Format._parse_format_spec(chunk.format_spec)
872+
type = spec["type"]
873+
if type is None:
874+
type = "d"
875+
if type == "x" or type == "X":
876+
# TODO(yosys): "H" type
877+
type = "h"
878+
if type == "s":
879+
# TODO(yosys): support for single unicode character?
880+
type = "c"
881+
width = spec["width"]
882+
align = spec["align"]
883+
if align is None:
884+
align = ">" if type != "c" else "<"
885+
if align == "=":
886+
# TODO(yosys): "=" alignment
887+
align = ">"
888+
fill = spec["fill"]
889+
if fill not in (" ", "0"):
890+
# TODO(yosys): arbitrary fill
891+
fill = " "
892+
# TODO(yosys): support for options, grouping
893+
sign = spec["sign"]
894+
if sign != "+":
895+
# TODO(yosys): support " " sign
896+
sign = ""
897+
signed = "s" if chunk.signed else "u"
898+
format.append(f"{{{len(chunk.value)}:{align}{fill}{width or ''}{type}{sign}{signed}}}")
899+
ports = {
900+
"EN": self.sigspec(cell.en),
901+
"ARGS": self.sigspec(_nir.Value(args)),
902+
}
903+
params = {
904+
"FORMAT": "".join(format),
905+
"ARGS_WIDTH": len(args),
906+
"PRIORITY": -cell_idx,
907+
}
908+
if isinstance(cell, (_nir.AsyncPrint, _nir.AsyncProperty)):
909+
ports["TRG"] = self.sigspec(_nir.Value())
910+
params["TRG_ENABLE"] = False
911+
params["TRG_WIDTH"] = 0
912+
params["TRG_POLARITY"] = 0
913+
if isinstance(cell, (_nir.SyncPrint, _nir.SyncProperty)):
914+
ports["TRG"] = self.sigspec(cell.clk)
915+
params["TRG_ENABLE"] = True
916+
params["TRG_WIDTH"] = 1
917+
params["TRG_POLARITY"] = cell.clk_edge == "pos"
918+
if isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint)):
919+
self.builder.cell(f"$print", params=params, ports=ports, src=_src(cell.src_loc))
920+
if isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
921+
params["FLAVOR"] = cell.kind
922+
ports["A"] = self.sigspec(cell.test)
923+
self.builder.cell(f"$check", params=params, ports=ports, src=_src(cell.src_loc))
893924

894925
def emit_any_value(self, cell_idx, cell):
895926
self.builder.cell(f"${cell.kind}", ports={
@@ -939,8 +970,8 @@ def emit_cells(self):
939970
self.emit_write_port(cell_idx, cell)
940971
elif isinstance(cell, (_nir.AsyncReadPort, _nir.SyncReadPort)):
941972
self.emit_read_port(cell_idx, cell)
942-
elif isinstance(cell, (_nir.AsyncProperty, _nir.SyncProperty)):
943-
self.emit_property(cell_idx, cell)
973+
elif isinstance(cell, (_nir.AsyncPrint, _nir.SyncPrint, _nir.AsyncProperty, _nir.SyncProperty)):
974+
self.emit_print(cell_idx, cell)
944975
elif isinstance(cell, _nir.AnyValue):
945976
self.emit_any_value(cell_idx, cell)
946977
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)