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.
345 lines
12 KiB
Python
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())
|