diff --git a/amaranth/back/rtlil.py b/amaranth/back/rtlil.py index a6367bdde..af157b9da 100644 --- a/amaranth/back/rtlil.py +++ b/amaranth/back/rtlil.py @@ -1090,35 +1090,42 @@ def emit_print(self, cell_idx, cell): args += chunk.value if type is None: type = "d" - if type == "x" or type == "X": - # TODO(yosys): "H" type + elif type == "x": type = "h" - if type == "s": - # TODO(yosys): support for single unicode character? + elif type == "X": + type = "H" + elif type == "c": + type = "U" + elif type == "s": type = "c" width = spec["width"] align = spec["align"] if align is None: - align = ">" if type != "c" else "<" - if align == "=": - # TODO(yosys): "=" alignment - align = ">" + align = "<" if type in ("c", "U") else ">" fill = spec["fill"] - if fill not in (" ", "0"): - # TODO(yosys): arbitrary fill - fill = " " - # TODO(yosys): support for options, grouping + if fill is None: + fill = ' ' + if ord(fill) >= 0x80: + raise NotImplementedError(f"non-ASCII fill character {fill!r} is not supported in RTLIL") sign = spec["sign"] - if sign != "+": - # TODO(yosys): support " " sign + if sign is None: sign = "" - if type == "c": + if type in ("c", "U"): signed = "" elif chunk.signed: signed = "s" else: signed = "u" - format.append(f"{{{len(chunk.value)}:{align}{fill}{width or ''}{type}{sign}{signed}}}") + show_base = "#" if spec["show_base"] and type != "d" else "" + grouping = spec["grouping"] or "" + if type == "U": + if align != "<" and width != 0: + format.append(fill * (width - 1)) + format.append(f"{{{len(chunk.value)}:U}}") + if align == "<" and width != 0: + format.append(fill * (width - 1)) + else: + format.append(f"{{{len(chunk.value)}:{align}{fill}{width or ''}{type}{sign}{show_base}{grouping}{signed}}}") ports = { "EN": self.sigspec(cell.en), "ARGS": self.sigspec(_nir.Value(args)), diff --git a/amaranth/back/verilog.py b/amaranth/back/verilog.py index 50bc41dca..ace266c15 100644 --- a/amaranth/back/verilog.py +++ b/amaranth/back/verilog.py @@ -9,7 +9,7 @@ def _convert_rtlil_text(rtlil_text, *, strip_internal_attrs=False, write_verilog_opts=()): # This version requirement needs to be synchronized with the one in pyproject.toml! - yosys = find_yosys(lambda ver: ver >= (0, 38)) + yosys = find_yosys(lambda ver: ver >= (0, 39, 0, 165)) script = [] script.append(f"read_ilang <[<>=^]) )? (?P[-+ ])? - (?P[#]?[0]?) + (?P[#]?) + (?P[0]?) (?P[1-9][0-9]*)? (?P[_,])? (?P[bodxXcsn])? @@ -2713,9 +2714,9 @@ def _parse_format_spec(spec: str, shape: Shape): raise ValueError(f"Cannot print signed value with format specifier {match['type']!r}") if match["align"] == "=": raise ValueError(f"Alignment {match['align']!r} is not allowed with format specifier {match['type']!r}") - if "#" in match["options"]: + if match["show_base"]: raise ValueError(f"Alternate form is not allowed with format specifier {match['type']!r}") - if "0" in match["options"]: + if match["width_zero"] != "": raise ValueError(f"Zero fill is not allowed with format specifier {match['type']!r}") if match["sign"] is not None: raise ValueError(f"Sign is not allowed with format specifier {match['type']!r}") @@ -2723,15 +2724,20 @@ def _parse_format_spec(spec: str, shape: Shape): raise ValueError(f"Cannot specify {match['grouping']!r} with format specifier {match['type']!r}") if match["type"] == "s" and shape.width % 8 != 0: raise ValueError(f"Value width must be divisible by 8 with format specifier {match['type']!r}") + fill = match["fill"] + align = match["align"] + if match["width_zero"] and align is None: + fill = "0" + align = "=" return { # Single character or None. - "fill": match["fill"], + "fill": fill, # '<', '>', '=', or None. Cannot be '=' for types 'c' and 's'. - "align": match["align"], + "align": align, # '-', '+', ' ', or None. Always None for types 'c' and 's'. "sign": match["sign"], - # "", "#", "0", or "#0". Always "" for types 'c' and 's'. - "options": match["options"], + # A bool. Always False for types 'c' and 's'. + "show_base": match["show_base"] == "#", # An int. "width": int(match["width"]) if match["width"] is not None else 0, # '_' or None. Always None for types 'c' and 's'. diff --git a/pyproject.toml b/pyproject.toml index 4844d64d9..edd8f2623 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ # - pyproject.toml: tool.pdm.dev-dependencies.test # - amaranth/back/verilog.py: _convert_rtlil_text # - docs/install.rst: yosys-version -builtin-yosys = ["amaranth-yosys>=0.38"] +builtin-yosys = ["amaranth-yosys>=0.39.0.165.post92"] remote-build = ["paramiko~=2.7"] [project.scripts] @@ -65,7 +65,7 @@ source-includes = [ [tool.pdm.dev-dependencies] # This version requirement needs to be synchronized with the one in pyproject.toml above! test = [ - "yowasp-yosys>=0.38", + "yowasp-yosys>=0.39.0.165.post92", "coverage", ] docs = [ diff --git a/tests/test_back_rtlil.py b/tests/test_back_rtlil.py index 480a3f227..f168b7eaf 100644 --- a/tests/test_back_rtlil.py +++ b/tests/test_back_rtlil.py @@ -1889,6 +1889,127 @@ def test_assume_msg(self): end """) + def test_print_char(self): + i = Signal(21) + m = Module() + m.d.comb += [ + Print(Format("{:c} {:-<5c} {:->5c}", i, i, i)), + ] + self.assertRTLIL(m, [i], R""" + attribute \generator "Amaranth" + attribute \top 1 + module \top + wire width 21 input 0 \i + wire width 1 $1 + process $2 + assign $1 [0] 1'0 + assign $1 [0] 1'1 + end + cell $print $3 + parameter \FORMAT "{21:U} {21:U}---- ----{21:U}\n" + parameter \ARGS_WIDTH 63 + parameter signed \PRIORITY 32'11111111111111111111111111111110 + parameter \TRG_ENABLE 0 + parameter \TRG_WIDTH 0 + parameter \TRG_POLARITY 0 + connect \EN $1 [0] + connect \ARGS { \i [20:0] \i [20:0] \i [20:0] } + connect \TRG { } + end + end + """) + + def test_print_base(self): + i = Signal(8) + m = Module() + m.d.comb += [ + Print(Format("{:b} {:o} {:d} {:x} {:X} {:#x} {:#d} {:#_x}", i, i, i, i, i, i, i, i)), + ] + self.assertRTLIL(m, [i], R""" + attribute \generator "Amaranth" + attribute \top 1 + module \top + wire width 8 input 0 \i + wire width 1 $1 + process $2 + assign $1 [0] 1'0 + assign $1 [0] 1'1 + end + cell $print $3 + parameter \FORMAT "{8:> bu} {8:> ou} {8:> du} {8:> hu} {8:> Hu} {8:> h#u} {8:> du} {8:> h#_u}\n" + parameter \ARGS_WIDTH 64 + parameter signed \PRIORITY 32'11111111111111111111111111111110 + parameter \TRG_ENABLE 0 + parameter \TRG_WIDTH 0 + parameter \TRG_POLARITY 0 + connect \EN $1 [0] + connect \ARGS { \i [7:0] \i [7:0] \i [7:0] \i [7:0] \i [7:0] \i [7:0] \i [7:0] \i [7:0] } + connect \TRG { } + end + end + """) + + def test_print_sign(self): + i = Signal(8) + m = Module() + m.d.comb += [ + Print(Format("{:5x} {:-5x} {:+5x} {: 5x}", i, i, i, i)), + ] + self.assertRTLIL(m, [i], R""" + attribute \generator "Amaranth" + attribute \top 1 + module \top + wire width 8 input 0 \i + wire width 1 $1 + process $2 + assign $1 [0] 1'0 + assign $1 [0] 1'1 + end + cell $print $3 + parameter \FORMAT "{8:> 5hu} {8:> 5h-u} {8:> 5h+u} {8:> 5h u}\n" + parameter \ARGS_WIDTH 32 + parameter signed \PRIORITY 32'11111111111111111111111111111110 + parameter \TRG_ENABLE 0 + parameter \TRG_WIDTH 0 + parameter \TRG_POLARITY 0 + connect \EN $1 [0] + connect \ARGS { \i [7:0] \i [7:0] \i [7:0] \i [7:0] } + connect \TRG { } + end + end + """) + + def test_print_align(self): + i = Signal(8) + m = Module() + m.d.comb += [ + Print(Format("{:<5x} {:>5x} {:=5x} {:05x} {:-<5x}", i, i, i, i, i)), + ] + self.assertRTLIL(m, [i], R""" + attribute \generator "Amaranth" + attribute \top 1 + module \top + wire width 8 input 0 \i + wire width 1 $1 + process $2 + assign $1 [0] 1'0 + assign $1 [0] 1'1 + end + cell $print $3 + parameter \FORMAT "{8:< 5hu} {8:> 5hu} {8:= 5hu} {8:=05hu} {8:<-5hu}\n" + parameter \ARGS_WIDTH 40 + parameter signed \PRIORITY 32'11111111111111111111111111111110 + parameter \TRG_ENABLE 0 + parameter \TRG_WIDTH 0 + parameter \TRG_POLARITY 0 + connect \EN $1 [0] + connect \ARGS { \i [7:0] \i [7:0] \i [7:0] \i [7:0] \i [7:0] } + connect \TRG { } + end + end + """) + + class ComponentTestCase(RTLILTestCase): def test_component(self): class MyComponent(wiring.Component): @@ -1907,4 +2028,3 @@ def elaborate(self, platform): connect \o 8'00000000 end """) -