mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: add test status handling to sysctrl and integrate with simulation framework
- Introduced test status signals (test_done_o, test_pass_o, test_code_o) in sysctrl interface and module. - Updated memory map to include TEST_STATUS offset. - Enhanced retrosoc_tb to monitor test completion and report results. - Modified Emulator to handle test status and return appropriate exit codes. - Updated formal verification to include assertions for test status. - Added CoreMark log parsing script and integrated it into the regression tests. - Implemented tests for test status handling in sysctrl_fault_tb. - Adjusted simulation success and failure markers in various scripts and tests. - Ensured compatibility with existing performance logging and regression testing frameworks.
This commit is contained in:
45 files changed
+1209
-81
No files matched your search
+4
-2
@@ -20,8 +20,10 @@ URL.
|
||||
|
||||
`parse_performance_log.py` converts the `APP=benchmark` UART `PERF` records
|
||||
into `meta/performance.json`. The parser requires the terminal
|
||||
`PERF_BENCHMARK_PASS` marker and deliberately does not alter the common
|
||||
simulation success-marker policy.
|
||||
`PERF_BENCHMARK_PASS` marker. `parse_coremark_log.py` converts the fixed
|
||||
SRAM CoreMark quick report into `meta/coremark.json`; it requires one valid
|
||||
`COREMARK_RESULT` record and `COREMARK_PASS`. Both reports complement, rather
|
||||
than replace, the common `SIM_TEST_PASS` simulation verdict.
|
||||
|
||||
`run_debug_session.py` is the local Hazard3 debug acceptance driver. It starts
|
||||
the Verilator remote-bitbang endpoint, the lock-pinned OpenOCD binary, and
|
||||
|
||||
@@ -14,9 +14,10 @@ sys.path.insert(0, str(ROOT))
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
|
||||
DEFAULT_SUCCESS = "retroSoC: A Customized ASIC for Retro Stuff"
|
||||
DEFAULT_SUCCESS = "SIM_TEST_PASS"
|
||||
DEFAULT_FAILURE = re.compile(
|
||||
r"(?:\bFAILED?\b|\bFATAL\b|assertion failed|%Error)", re.IGNORECASE
|
||||
r"(?:\bFAILED?\b|\bFATAL\b|assertion failed|%Error|SIM_TEST_FAIL|SIM_TEST_TIMEOUT)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert the quick Hazard3 CoreMark UART report into structured JSON."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
RESULT_PREFIX = "COREMARK_RESULT "
|
||||
PASS_MARKER = "COREMARK_PASS"
|
||||
FAIL_PREFIX = "COREMARK_FAIL"
|
||||
REQUIRED_FIELDS = {"mode", "qualified", "memory", "iterations", "cycles", "cpu_hz"}
|
||||
TOKEN = re.compile(r"([a-z_]+)=([^\s]+)")
|
||||
|
||||
|
||||
def parse_number(value: str) -> int:
|
||||
return int(value, 0)
|
||||
|
||||
|
||||
def parse_log(content: str) -> dict[str, object]:
|
||||
results: list[dict[str, object]] = []
|
||||
for line in content.splitlines():
|
||||
if not line.startswith(RESULT_PREFIX):
|
||||
continue
|
||||
fields = {name: value for name, value in TOKEN.findall(line)}
|
||||
missing = REQUIRED_FIELDS.difference(fields)
|
||||
if missing:
|
||||
raise ValueError(f"CoreMark result is missing fields: {sorted(missing)}")
|
||||
mode = fields["mode"]
|
||||
if mode not in {"quick", "standard"}:
|
||||
raise ValueError(f"unsupported CoreMark mode: {mode}")
|
||||
memory = fields["memory"]
|
||||
if memory != "sram":
|
||||
raise ValueError(f"CoreMark result must execute from SRAM, got: {memory}")
|
||||
qualified = parse_number(fields["qualified"])
|
||||
if qualified not in {0, 1}:
|
||||
raise ValueError("CoreMark qualified flag must be zero or one")
|
||||
iterations = parse_number(fields["iterations"])
|
||||
cycles = parse_number(fields["cycles"])
|
||||
cpu_hz = parse_number(fields["cpu_hz"])
|
||||
if iterations <= 0 or cycles <= 0 or cpu_hz <= 0:
|
||||
raise ValueError("CoreMark iterations, cycles, and cpu_hz must be positive")
|
||||
try:
|
||||
score = (Decimal(iterations) * Decimal(1_000_000) / Decimal(cycles)).quantize(
|
||||
Decimal("0.001"), rounding=ROUND_HALF_UP
|
||||
)
|
||||
except (InvalidOperation, ZeroDivisionError) as error:
|
||||
raise ValueError("invalid CoreMark score inputs") from error
|
||||
results.append(
|
||||
{
|
||||
"mode": mode,
|
||||
"qualified": bool(qualified),
|
||||
"memory": memory,
|
||||
"iterations": iterations,
|
||||
"cycles": cycles,
|
||||
"cpu_hz": cpu_hz,
|
||||
"coremark_per_mhz": str(score),
|
||||
}
|
||||
)
|
||||
|
||||
pass_marker = PASS_MARKER in content
|
||||
failure_marker = any(line.startswith(FAIL_PREFIX) for line in content.splitlines())
|
||||
quick_results = [result for result in results if result["mode"] == "quick"]
|
||||
passed = pass_marker and not failure_marker and len(quick_results) == 1
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"status": "passed" if passed else "failed",
|
||||
"failure_marker": failure_marker,
|
||||
"pass_marker": pass_marker,
|
||||
"results": results,
|
||||
}
|
||||
|
||||
|
||||
def atomic_write(path: Path, content: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
temporary = path.with_suffix(path.suffix + ".tmp")
|
||||
temporary.write_text(content, encoding="utf-8")
|
||||
temporary.replace(path)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--log", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
report = parse_log(args.log.read_text(encoding="utf-8", errors="replace"))
|
||||
atomic_write(args.output, json.dumps(report, indent=2, sort_keys=True) + "\n")
|
||||
if report["status"] != "passed":
|
||||
print("CoreMark did not emit one complete quick passing report", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+7
-3
@@ -37,7 +37,6 @@ PR_COMMANDS = (
|
||||
(
|
||||
"SIMU=IVERILOG",
|
||||
"SIM_FIRMWARE_NAME=retrosoc_asm",
|
||||
"SIM_SUCCESS_MARKER=Mem wr/rd test success",
|
||||
"RTL_SIM_TIMEOUT=5200000",
|
||||
"netsim",
|
||||
),
|
||||
@@ -56,7 +55,13 @@ SMOKE_COMMANDS = (
|
||||
("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm"),
|
||||
),
|
||||
)
|
||||
NIGHTLY_COMMANDS = PR_COMMANDS
|
||||
NIGHTLY_COMMANDS = (
|
||||
*PR_COMMANDS,
|
||||
(
|
||||
"configs/benchmark/ihp130-hazard3-coremark.mk",
|
||||
("SIMU=VERILATOR", "HAVE_SVA=YES", "coremark-report"),
|
||||
),
|
||||
)
|
||||
PR_PROFILES = ("configs/ci/ihp130.mk",)
|
||||
SMOKE_PROFILES: tuple[str, ...] = ()
|
||||
NIGHTLY_PROFILES = PR_PROFILES
|
||||
@@ -82,7 +87,6 @@ def pdk_pr_commands(profile: str) -> tuple[tuple[str, tuple[str, ...]], ...]:
|
||||
(
|
||||
"SIMU=IVERILOG",
|
||||
"SIM_FIRMWARE_NAME=retrosoc_asm",
|
||||
"SIM_SUCCESS_MARKER=Mem wr/rd test success",
|
||||
"RTL_SIM_TIMEOUT=5200000",
|
||||
"netsim",
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user