3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-07 06:33:24 +00:00

Docs: static opt macro list

Also adds `docs/tests/macro_commands.py` which checks all commands in
`code_examples/macro_commands` against the current yosys build. Format similar
to `run-test.sh` files: logging the file under test and reporting errors.
This commit is contained in:
Krystine Sherwin 2024-01-24 09:56:00 +13:00
parent 95849edbba
commit 6c8949cacc
No known key found for this signature in database
3 changed files with 107 additions and 4 deletions

View file

@ -0,0 +1,14 @@
#start: passes in the following order:
#end: When called with -fast
opt_expr
opt_merge -nomux
do
opt_muxtree
opt_reduce
opt_merge
opt_share (-full only)
opt_dff (except when called with -noff)
opt_clean
opt_expr
while <changed design>

View file

@ -14,11 +14,9 @@ includes removing unused signals and cells and const folding. It is recommended
to run this pass after each major step in the synthesis script. As listed in to run this pass after each major step in the synthesis script. As listed in
:doc:`/cmd/opt`, this macro command calls the following ``opt_*`` commands: :doc:`/cmd/opt`, this macro command calls the following ``opt_*`` commands:
.. literalinclude:: /cmd/opt.rst .. literalinclude:: /code_examples/macro_commands/opt.ys
:language: yoscrypt :language: yoscrypt
:start-after: following order: :start-after: #end:
:end-at: while <changed design>
:dedent:
:caption: Passes called by :cmd:ref:`opt` :caption: Passes called by :cmd:ref:`opt`
.. _adv_opt_expr: .. _adv_opt_expr:

91
docs/tests/macro_commands.py Executable file
View file

@ -0,0 +1,91 @@
#!/usr/bin/env python3
import logging
from pathlib import Path
import re
import subprocess
import sys
# basic logging setup
logging.basicConfig(level=logging.INFO)
# expects __file__ = yosys/docs/tests/macro_commands.py
TESTS_DIR = Path(__file__).parent
ROOT_DIR = TESTS_DIR.parent.parent
THIS_FILE = (TESTS_DIR / "macro_commands.py").relative_to(ROOT_DIR)
MACRO_SOURCE = TESTS_DIR.parent / "source" / "code_examples" / "macro_commands"
assert MACRO_SOURCE.exists(), f"can't find macro_commands in {MACRO_SOURCE}"
YOSYS = TESTS_DIR.parent.parent / "yosys"
assert YOSYS.exists(), f"can't find yosys executable in {YOSYS}"
raise_error = False
# get all macro commands being used
for macro in MACRO_SOURCE.glob("*.ys"):
# log current test
relative_path = macro.relative_to(ROOT_DIR)
logging.log(logging.INFO, f"Checking {relative_path}")
# files in macros_commands should be named {command}.ys
command = macro.stem
# run `yosys -h {command}` to capture current sub-commands
proc = subprocess.run([YOSYS, "-QTh", command], capture_output=True, text=True)
with open(macro) as f:
# {command.ys} starts with two commented lines, the first is the text
# immediately prior to the macro body. The second is the text
# immediately after.
start = f.readline()
end = f.readline()
expected_content = f.readlines()
# parse {command.ys}
if "#start:" not in start or "#end:" not in end:
logging.error(f"Missing start and/or end string in {relative_path}, see {THIS_FILE}")
raise_error = True
continue
start = start.replace("#start:", "").strip()
end = end.replace("#end:", "").strip()
# attempt to find macro body in help output
match = re.fullmatch(f".*{start}(.*){end}.*", proc.stdout, re.DOTALL)
if not match:
logging.error(f"Couldn't find {start!r} and/or {end!r} in `yosys -h {command}` output")
raise_error = True
continue
actual_content = match.group(1).strip().splitlines()
# iterate over and compare expected v actual
for (expected, actual) in zip(expected_content, actual_content):
expected = expected.strip()
actual = actual.strip()
# raw match
if expected == actual:
continue
# rip apart formatting to match line parts
pattern = r"(?P<cmd>\S+)(?P<pass> \[.*\])?(?P<opt>.*?)(?P<cond> \(.*\))?(?P<comment>\s+#.*)?"
try:
expected_dict = re.fullmatch(pattern, expected).groupdict()
except AttributeError:
logging.error(f"Bad formatting in {relative_path}: {expected!r}")
raise_error = True
continue
try:
actual_dict = re.fullmatch(pattern, actual).groupdict()
except AttributeError:
logging.error(f"Unexpected formatting in `yosys -h {command}` output, {actual!r}")
raise_error = True
continue
does_match = expected_dict["cmd"] == actual_dict["cmd"]
#todo: check expected_dict["pass"] is a subset of actual_dict["pass"]
# only check opt and cond match if they're in {command}.ys
match_keys = ["opt", "cond"]
for key in match_keys:
if expected_dict[key] and expected_dict[key] != actual_dict[key]:
does_match = False
# raise error on mismatch
if not does_match:
logging.error(f"Expected {expected!r}, got {actual!r}")
raise_error = True
sys.exit(raise_error)