Files
retroSoC/scripts/regress.py
T
maksyuki e3ddeacea3 feat: enhance Tiny R2 Baseline and ICS55 Support
- 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.
2026-10-08 09:44:39 +08:00

345 lines
12 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from scripts.check_c_warnings import self_owned_warnings # noqa: E402
CI_SMOKE_APP_VALUE = "APP=ci_smoke"
CI_SMOKE_VERILATOR_VALUES = (
"VERILATOR_SIM_ARGS=--fast-flash",
"SIMU=VERILATOR",
"HAVE_SVA=YES",
"HAVE_CSR=YES",
"firmware",
"sim",
)
CI_SMOKE_SIM_VALUES = (
CI_SMOKE_APP_VALUE,
"LINK_TYPE=ld2_all_sram",
"SOC_SIM_TIME=1800",
*CI_SMOKE_VERILATOR_VALUES,
)
CI_SMOKE_SDRAM_SIM_VALUES = (
CI_SMOKE_APP_VALUE,
"LINK_TYPE=ld2_sdram",
# Hosted Verilator executes the expanded peripheral acceptance workload
# slowly when LP code and accelerator traffic share the SDRAM target.
"SOC_SIM_TIME=3600",
*CI_SMOKE_VERILATOR_VALUES,
)
RTL_LINT_VALUES = ("SIMU=VERILATOR", "HAVE_SVA=YES", "rtl-lint")
RTL_LINT_OBSERVATION_VALUES = (
"SIMU=VERILATOR",
"HAVE_SVA=YES",
"check-rtl-lint",
)
OBSERVATION_TARGETS = ("check-warnings", "check-metrics")
SYNTHESIS_DEPENDENT_TARGETS = frozenset(("synth", "sta", "netsim", "netsim-boot", "metrics"))
PR_COMMANDS = (
("configs/ci/ihp130.mk", RTL_LINT_VALUES),
("configs/ci/ihp130.mk", (CI_SMOKE_APP_VALUE, "firmware")),
("configs/ci/ihp130-shell.mk", ("firmware",)),
(
"configs/ci/ihp130.mk",
CI_SMOKE_SIM_VALUES,
),
("configs/ci/ihp130-debug.mk", ("SIMU=VERILATOR", "debug-sim")),
("configs/ci/ihp130.mk", ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
("configs/ci/ihp130.mk", ("SYNTH=YOSYS", "synth")),
(
"configs/ci/ihp130.mk",
(
"SIMU=IVERILOG",
"SIM_FIRMWARE_NAME=retrosoc_asm",
"RTL_SIM_TIMEOUT=5200000",
"netsim",
),
),
("configs/ci/ihp130.mk", ("STA=OPENSTA", "sta")),
)
TINY_PR_COMMANDS = (
("configs/ci/ihp130-tiny.mk", RTL_LINT_VALUES),
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "LINK_TYPE=ld2_all_sram", "SIMU=VERILATOR", "SOC_SIM_TIME=1800", "HAVE_CSR=YES", "HAVE_SVA=YES", "firmware", "sim")),
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SIMU=IVERILOG", "firmware", "sim")),
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SYNTH=YOSYS", "synth")),
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SIMU=IVERILOG", "netsim-boot")),
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "STA=OPENSTA", "sta")),
)
RTL_COMMANDS = (*PR_COMMANDS[:6], *TINY_PR_COMMANDS[:3])
TINY_PROFILES = {"IHP130": "configs/ci/ihp130-tiny.mk", "ICS55": "configs/ci/ics55-tiny.mk"}
def tiny_commands(pdk: str) -> tuple:
return tuple((TINY_PROFILES[pdk], (*values, "TINY_SIM_ARGS=+tiny_jtag_smoke")
if "sim" in values else values) for _, values in TINY_PR_COMMANDS)
PR_COMMANDS = (*PR_COMMANDS, *TINY_PR_COMMANDS)
SMOKE_COMMANDS = (
("configs/ci/ihp130.mk", RTL_LINT_VALUES),
("configs/ci/ihp130.mk", (CI_SMOKE_APP_VALUE, "firmware")),
(
"configs/ci/ihp130.mk",
("SIMU=VERILATOR", "HAVE_SVA=YES", "comp"),
),
(
"configs/ci/ihp130.mk",
("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm"),
),
)
NIGHTLY_EXTRA_COMMANDS = (
(
"configs/benchmark/ihp130-hazard3-coremark.mk",
# Pin-level serial-flash boot plus the PRODUCT benchmark needs ~80M
# emulator steps; 7200s preserves XPI coverage with runner margin.
(
"SIMU=VERILATOR",
"SOC_SIM_TIME=7200",
"HAVE_SVA=YES",
"coremark-report",
),
),
("configs/ci/ihp130.mk", ("SYNTH=YOSYS", "SYNTH_RECIPE=area", "synth")),
("configs/ci/ihp130.mk", ("STA=OPENSTA", "SYNTH_RECIPE=area", "sta")),
("configs/ci/ihp130.mk", ("SYNTH_RECIPE=area", "metrics")),
("configs/ci/ihp130.mk", ("SYNTH=YOSYS", "SYNTH_RECIPE=speed", "synth")),
("configs/ci/ihp130.mk", ("STA=OPENSTA", "SYNTH_RECIPE=speed", "sta")),
("configs/ci/ihp130.mk", ("SYNTH_RECIPE=speed", "metrics")),
)
NIGHTLY_COMMANDS = (
*PR_COMMANDS,
*NIGHTLY_EXTRA_COMMANDS,
)
PR_PROFILES = ("configs/ci/ihp130.mk", "configs/ci/ihp130-tiny.mk")
SMOKE_PROFILES: tuple[str, ...] = ()
NIGHTLY_PROFILES = PR_PROFILES
PDK_PR_PROFILES = {
"GF180": "configs/ci/gf180.mk",
"IHP130": "configs/ci/ihp130.mk",
"ICS55": "configs/ci/ics55.mk",
"SKY130": "configs/ci/sky130.mk",
}
NETSIM_BOOT_PROFILES = frozenset(
("configs/ci/gf180.mk", "configs/ci/ics55.mk")
)
def pdk_pr_commands(profile: str) -> tuple[tuple[str, tuple[str, ...]], ...]:
netsim_target = "netsim-boot" if profile in NETSIM_BOOT_PROFILES else "netsim"
verilator_values = (
CI_SMOKE_SIM_VALUES
if profile == "configs/ci/ihp130.mk"
else CI_SMOKE_SDRAM_SIM_VALUES
)
return (
(profile, RTL_LINT_VALUES),
(profile, (CI_SMOKE_APP_VALUE, "firmware")),
(profile, verilator_values),
(profile, ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
(profile, ("SYNTH=YOSYS", "synth")),
(profile, ("STA=OPENSTA", "sta")),
(
profile,
(
"SIMU=IVERILOG",
"SIM_FIRMWARE_NAME=retrosoc_asm",
"RTL_SIM_TIMEOUT=5200000",
netsim_target,
),
),
)
def select_regression(
suite: str, pdk: str | None
) -> tuple[tuple[tuple[str, tuple[str, ...]], ...], tuple[str, ...]]:
if suite == "smoke":
if pdk is not None and pdk != "IHP130":
raise ValueError("smoke regression supports only --pdk IHP130")
return SMOKE_COMMANDS, SMOKE_PROFILES
if suite == "rtl":
if pdk is not None and pdk != "IHP130":
raise ValueError("rtl regression supports only --pdk IHP130")
return RTL_COMMANDS, PR_PROFILES
if suite == "nightly-extra":
if pdk is not None and pdk != "IHP130":
raise ValueError("nightly-extra regression supports only --pdk IHP130")
return NIGHTLY_EXTRA_COMMANDS, ("configs/ci/ihp130.mk",)
if pdk:
if suite == "nightly":
if pdk != "IHP130":
raise ValueError("nightly regression supports only --pdk IHP130")
return NIGHTLY_COMMANDS, NIGHTLY_PROFILES
if pdk == "IHP130":
return PR_COMMANDS, PR_PROFILES
profile = PDK_PR_PROFILES[pdk]
if pdk == "ICS55":
return (*pdk_pr_commands(profile), *tiny_commands(pdk)), (profile, TINY_PROFILES[pdk])
return pdk_pr_commands(profile), (profile,)
if suite == "pr":
return PR_COMMANDS, PR_PROFILES
return NIGHTLY_COMMANDS, NIGHTLY_PROFILES
def regression_environment() -> dict[str, str]:
environment = os.environ.copy()
environment.setdefault(
"BUILD_TIMESTAMP", datetime.now().astimezone().strftime("%Y-%m-%d-%H-%M")
)
return environment
def with_netsim_boot_only(
commands: tuple[tuple[str, tuple[str, ...]], ...],
) -> tuple[tuple[str, tuple[str, ...]], ...]:
"""Replace only the local netlist target and retain its selected firmware image."""
transformed: list[tuple[str, tuple[str, ...]]] = []
for profile, values in commands:
if "netsim" not in values:
transformed.append((profile, values))
continue
filtered = tuple(
value
for value in values
if not value.startswith("SIM_SUCCESS_MARKER=")
and value != "netsim"
)
transformed.append((profile, (*filtered, "netsim-boot")))
return tuple(transformed)
def behavioral_only(
commands: tuple[tuple[str, tuple[str, ...]], ...],
) -> tuple[tuple[str, tuple[str, ...]], ...]:
"""Exclude synthesis and commands that consume its generated netlist."""
return tuple(
(profile, values)
for profile, values in commands
if SYNTHESIS_DEPENDENT_TARGETS.isdisjoint(values)
)
def run_command(
command: list[str], root: Path, capture_output: bool, environment: dict[str, str]
) -> str:
if not capture_output:
subprocess.run(command, cwd=root, env=environment, check=True)
return ""
result = subprocess.run(
command,
cwd=root,
env=environment,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
sys.stdout.write(result.stdout)
result.check_returncode()
return result.stdout
def run_observation(command: list[str], root: Path, environment: dict[str, str]) -> None:
"""Run a report-producing quality check without changing the regression verdict."""
try:
result = subprocess.run(command, cwd=root, env=environment, check=False)
except OSError as error:
print(
f"non-blocking observation could not start: {error}: {' '.join(command)}",
file=sys.stderr,
)
return
if result.returncode:
print(
f"non-blocking observation failed (exit {result.returncode}): {' '.join(command)}",
file=sys.stderr,
)
def main() -> int:
parser = argparse.ArgumentParser(description="Run a supported retroSoC regression suite")
parser.add_argument("--root", type=Path, required=True)
parser.add_argument(
"--suite", choices=("smoke", "rtl", "pr", "nightly", "nightly-extra"), required=True
)
parser.add_argument("--pdk", choices=tuple(PDK_PR_PROFILES), help="run one PDK matrix")
parser.add_argument("--soc", choices=("MINI", "TINY"), help="run one product only")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument(
"--netsim-boot-only",
action="store_true",
help="locally stop each Icarus assembly netlist run after Hello retroSoC!",
)
parser.add_argument(
"--behavioral-only",
action="store_true",
help="skip synthesis, STA, netlist simulation, and synthesis-recipe metrics",
)
args = parser.parse_args()
try:
if args.soc == "TINY":
pdk = args.pdk or "ICS55"
if pdk not in TINY_PROFILES or args.suite == "nightly-extra":
raise ValueError("Tiny supports IHP130/ICS55 smoke, rtl, pr and nightly suites")
commands = tiny_commands(pdk)
if args.suite == "smoke":
commands = commands[:2]
elif args.suite == "rtl":
commands = commands[:3]
profiles = (TINY_PROFILES[pdk],)
else:
commands, profiles = select_regression(args.suite, args.pdk)
except ValueError as error:
parser.error(str(error))
if args.soc == "MINI":
commands = tuple((profile, values) for profile, values in commands
if profile not in TINY_PROFILES.values())
profiles = tuple(profile for profile in profiles if profile not in TINY_PROFILES.values())
if not commands:
parser.error(f"suite {args.suite} has no {args.soc} configuration for this PDK")
if args.netsim_boot_only:
commands = with_netsim_boot_only(commands)
if args.behavioral_only:
commands = behavioral_only(commands)
environment = regression_environment()
for profile, values in commands:
command = ["make", f"CONFIG={profile}", *values]
print("+ " + " ".join(command), flush=True)
if not args.dry_run:
output = run_command(command, args.root, "firmware" in values, environment)
warnings = self_owned_warnings(args.root, output)
if warnings:
print("self-owned C compiler warnings:", flush=True)
print("\n".join(warnings), flush=True)
return 1
for profile in profiles:
command = ["make", f"CONFIG={profile}", *RTL_LINT_OBSERVATION_VALUES]
print("+ " + " ".join(command), flush=True)
if not args.dry_run:
run_observation(command, args.root, environment)
for target in OBSERVATION_TARGETS:
command = ["make", f"CONFIG={profile}", target]
print("+ " + " ".join(command), flush=True)
if not args.dry_run:
run_observation(command, args.root, environment)
return 0
if __name__ == "__main__":
raise SystemExit(main())