feat: integrate formal verification for bus and nmi2apb protocols

- Added formal verification support for bus and nmi2apb protocols using SymbiYosys and Bitwuzla.
- Introduced new formal design and properties files for both protocols.
- Updated the build system to include formal verification steps in the Makefile.
- Enhanced the doctor script to check for formal tool availability.
- Added scripts for generating formal file lists and SymbiYosys configurations.
- Implemented a wrapper for Bitwuzla to adapt Yosys legacy arguments.
- Updated documentation to reflect new formal verification capabilities.
- Added tests to ensure correctness of formal file generation and result summarization.
This commit is contained in:
maksyuki committed 2026-07-25 12:33:58 +08:00
1 parent 6f0e053d58
commit dd03baecca
17 files changed
+893 -8

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Adapt Yosys' legacy Bitwuzla invocation to the Bitwuzla 0.9 CLI."""
from __future__ import annotations
import os
import sys
from pathlib import Path
def translate_arguments(arguments: list[str]) -> list[str]:
"""Replace obsolete Yosys solver flags with Bitwuzla 0.9 equivalents."""
translated = [argument for argument in arguments if argument not in ("--smt2", "-i")]
return ["--lang", "smt2", *translated]
def main() -> int:
executable = os.environ.get("RETROSOC_BITWUZLA")
if not executable:
print("RETROSOC_BITWUZLA must name the Bitwuzla 0.9 executable", file=sys.stderr)
return 2
wrapper = Path(__file__).resolve()
if Path(executable).resolve() == wrapper:
print(
"RETROSOC_BITWUZLA must name the Bitwuzla executable, not its compatibility wrapper",
file=sys.stderr,
)
return 2
os.execvp(executable, [executable, *translate_arguments(sys.argv[1:])])
return 127
if __name__ == "__main__":
raise SystemExit(main())
+9 -4
View File
@@ -25,6 +25,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--pdk", required=True)
parser.add_argument("--core", required=True)
parser.add_argument("--ip", required=True)
parser.add_argument("--formal", choices=("YES", "NO"), default="NO")
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
parser.add_argument("--format", choices=("text", "json"), default="text")
parser.add_argument("--output", type=Path)
@@ -65,6 +66,8 @@ def main() -> int:
tools.append("yosys")
if args.sta == "OPENSTA":
tools.append("sta")
if args.formal == "YES":
tools.extend(("sby", "yosys", "yosys-smtbmc", "sv2v", "bitwuzla"))
source_names = ["mpw", "cluster_common", "third_party_ip"]
pdk_names = {
@@ -75,9 +78,7 @@ def main() -> int:
}
if args.pdk in pdk_names:
source_names.append(pdk_names[args.pdk])
paths = {
name: root / lock["sources"][name]["destination"] for name in source_names
}
paths = {name: root / lock["sources"][name]["destination"] for name in source_names}
tool_results = {
tool: {"path": shutil.which(tool), "version": version(tool)}
for tool in dict.fromkeys(tools)
@@ -108,7 +109,11 @@ def main() -> int:
for name, value in path_results.items():
suffix = "[ok]" if value["exists"] else "[MISSING]"
print(f" {name:<28} {value['path']} {suffix}")
print("doctor: all selected requirements are available" if not failures else f"doctor found {failures} missing requirement(s)")
print(
"doctor: all selected requirements are available"
if not failures
else f"doctor found {failures} missing requirement(s)"
)
return int(failures != 0)