mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Updated to include support for the ICS55 PDK, allowing for conditional source inputs and configuration checks. - Added new test cases for the ICS55 platform, including PLL backend and GPIO readback functionality. - Modified existing tests to accommodate changes in configuration and source input handling. - Introduced new parameters in the testbench for better configurability. - Ensured compatibility checks for the IHP130 and ICS55 PDKs in the feasibility script. - Enhanced the publication tests to validate hardware changes more robustly.
159 lines
6.3 KiB
Python
159 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.dependency_lock import DEFAULT_LOCK, load_lock, lock_digest # noqa: E402
|
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Check the retroSoC build environment")
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
parser.add_argument("--simu", required=True)
|
|
parser.add_argument("--synth", required=True)
|
|
parser.add_argument("--sta", required=True)
|
|
parser.add_argument("--pdk", required=True)
|
|
parser.add_argument("--soc", choices=("MINI", "TINY"), default="MINI")
|
|
parser.add_argument("--have-sram-macro", choices=("YES", "NO"), default="NO")
|
|
parser.add_argument("--have-pll", choices=("YES", "NO"), default="NO")
|
|
parser.add_argument("--formal", choices=("YES", "NO"), default="NO")
|
|
parser.add_argument("--require-debug-tools", action="store_true")
|
|
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
|
|
parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
parser.add_argument("--output", type=Path)
|
|
return parser.parse_args()
|
|
|
|
|
|
def version(executable: str) -> str | None:
|
|
path = shutil.which(executable)
|
|
if path is None:
|
|
return None
|
|
name = Path(executable).name
|
|
options = {
|
|
"iverilog": "-V",
|
|
"vvp": "-V",
|
|
"sta": "-version",
|
|
}
|
|
result = subprocess.run(
|
|
[executable, options.get(name, "--version")],
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
lines = (result.stdout or result.stderr).strip().splitlines()
|
|
return lines[0] if lines else str(path)
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
root = args.root.resolve()
|
|
lock = load_lock(args.lock)
|
|
tools = ["python3", "make", "git", "riscv32-unknown-elf-gcc"]
|
|
if args.simu == "IVERILOG":
|
|
tools.extend(("iverilog", "vvp", "sv2v"))
|
|
elif args.simu == "VERILATOR":
|
|
tools.extend(("verilator", "c++"))
|
|
elif args.simu == "VCS":
|
|
tools.extend(("bsub", "vcs"))
|
|
if args.synth == "YOSYS":
|
|
tools.append("yosys")
|
|
if args.sta == "OPENSTA":
|
|
tools.append("sta")
|
|
if args.formal == "YES":
|
|
tools.extend(("sby", "yosys", "yosys-smtbmc", "sv2v", "bitwuzla"))
|
|
if args.require_debug_tools:
|
|
tools.extend(("openocd", "riscv32-unknown-elf-gdb"))
|
|
|
|
source_names = ["mpw", "hazard3", "cluster_common", "third_party_ip"]
|
|
if args.soc == "TINY":
|
|
from scripts.setup_tiny import TINY_SOURCES
|
|
source_names = [name for name in TINY_SOURCES if not name.startswith("pdk_")]
|
|
pdk_names = {
|
|
"IHP130": "pdk_ihp130",
|
|
"ICS55": "pdk_ics55",
|
|
"GF180": "pdk_gf180",
|
|
"SKY130": "pdk_sky130",
|
|
}
|
|
if args.pdk in pdk_names:
|
|
source_names.append(pdk_names[args.pdk])
|
|
if args.pdk == "ICS55" and args.soc == "TINY" and args.have_pll == "YES":
|
|
source_names.append("pdk_ics55_pll")
|
|
paths = {name: root / lock["sources"][name]["destination"] for name in source_names}
|
|
if args.pdk == "ICS55":
|
|
for corner in ("tt", "ss"):
|
|
paths[f"ics55_cells_{corner}"] = root / f".cache/retrosoc/pdk/ics55/ics55_h7cr_{corner}.lib"
|
|
if args.have_sram_macro == "YES":
|
|
for size in ("1024", "4096"):
|
|
name = f"ics55_ecos_sram_{size}x32_m8"
|
|
for view in (f"verilog/{name}_core.v", f"verilog/{name}_stub.v",
|
|
f"lib/{name}_tt1p2v25cctyp.lib", f"lib/{name}_ss1p08v125ccmax.lib"):
|
|
paths[f"{name}/{view}"] = root / ".cache/retrosoc/pdk/ics55/sram" / name / view
|
|
if args.soc == "TINY" and args.have_pll == "YES":
|
|
from physical.pdk.setup import PLL_VIEWS
|
|
pll = root / lock["sources"]["pdk_ics55_pll"]["destination"]
|
|
for view in PLL_VIEWS:
|
|
paths[f"pll/{view}"] = pll / view
|
|
if args.pdk == "SKY130" and args.have_sram_macro == "YES":
|
|
paths["sky130_openram_sram"] = root / ".cache/retrosoc/pdk/sky130/openram"
|
|
tool_results = {
|
|
tool: {"path": shutil.which(tool), "version": version(tool)}
|
|
for tool in dict.fromkeys(tools)
|
|
}
|
|
path_results = {
|
|
name: {"path": str(path), "exists": path.exists()} for name, path in paths.items()
|
|
}
|
|
failures = sum(value["path"] is None for value in tool_results.values())
|
|
failures += sum(not value["exists"] for value in path_results.values())
|
|
if "pdk_ics55_pll" in paths and paths["pdk_ics55_pll"].exists():
|
|
try:
|
|
revision = subprocess.check_output(["git", "-C", str(paths["pdk_ics55_pll"]),
|
|
"rev-parse", "HEAD"], text=True).strip()
|
|
dirty = subprocess.check_output(["git", "-C", str(paths["pdk_ics55_pll"]),
|
|
"status", "--porcelain"], text=True).strip()
|
|
matching = revision == lock["sources"]["pdk_ics55_pll"]["revision"] and not dirty
|
|
except subprocess.CalledProcessError:
|
|
matching = False
|
|
path_results["pdk_ics55_pll"]["locked_clean_checkout"] = bool(matching)
|
|
failures += int(not matching)
|
|
report = {
|
|
"schema_version": 1,
|
|
"ok": failures == 0,
|
|
"failures": failures,
|
|
"dependency_lock_sha256": lock_digest(args.lock),
|
|
"tools": tool_results,
|
|
"paths": path_results,
|
|
}
|
|
serialized = json.dumps(report, indent=2, sort_keys=True) + "\n"
|
|
if args.output:
|
|
atomic_write(args.output, serialized)
|
|
if args.format == "json":
|
|
print(serialized, end="")
|
|
else:
|
|
print("Tools:")
|
|
for name, value in tool_results.items():
|
|
print(f" {name:<28} {value['path'] or 'MISSING'}")
|
|
print("Paths:")
|
|
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)"
|
|
)
|
|
return int(failures != 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|