Files
retroSoC/scripts/generate_vexiiriscv.py
T
maksyuki 3ac9e73ab9 feat: refactor tests for mailbox and environment validation
- Updated mailbox purpose tests to reflect new write order and sequence handling.
- Added tests to ensure Linux-ready values match LP contract and constants match Linux helper.
- Introduced tests to verify that offset changes do not hide stale Linux helpers.
- Enhanced environment tests to report current locked launcher and runtime versions.
- Added checks for SBT launcher presence in environment labels.
- Improved full diagram tests to validate storage geometry and RSA vector configurations.
- Expanded register tests to ensure crypto interrupt registers publish all events and lifecycle annotations preserve security boundaries.
- Modified script tools tests to include Vexii generation validation and updated workflow assertions.
2026-09-30 10:51:04 +08:00

128 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Generate the locked VexiiRiscv HP core below the selected build variant."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import subprocess
from pathlib import Path
from typing import Any
GENERATOR_CLASS = "vexiiriscv.GenerateRetroSocHp"
GENERATED_MODULE = "vexii_riscv_hp_generated"
def run(command: list[str], cwd: Path) -> str:
result = subprocess.run(command, cwd=cwd, check=True, capture_output=True, text=True)
return result.stdout.strip()
def git_revision(source: Path) -> str:
return run(["git", "rev-parse", "HEAD"], source)
def git_status(source: Path) -> list[str]:
output = run(["git", "status", "--short"], source)
return output.splitlines() if output else []
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def locked_revision(lock_path: Path) -> str:
document: dict[str, Any] = json.loads(lock_path.read_text(encoding="utf-8"))
source = document.get("sources", {}).get("vexiiriscv")
if not isinstance(source, dict) or not isinstance(source.get("revision"), str):
raise ValueError("dependency lock has no vexiiriscv source revision")
return source["revision"]
def validate_locked_source(source: Path, expected: str) -> str:
actual = git_revision(source)
if actual != expected:
raise ValueError(f"VexiiRiscv revision mismatch: expected {expected}, found {actual}")
status = git_status(source)
if status:
raise ValueError("VexiiRiscv source has local changes:\n" + "\n".join(status))
return actual
def generate(args: argparse.Namespace) -> None:
root = args.root.resolve()
source = args.source.resolve()
output = args.output.resolve()
expected = locked_revision(args.lock.resolve())
actual = validate_locked_source(source, expected)
scala_dir = root / "scripts" / "vexiiriscv"
output.mkdir(parents=True, exist_ok=True)
command = [
str(args.sbt),
"--batch",
f'set Compile / unmanagedSourceDirectories += file("{scala_dir}")',
f"runMain {GENERATOR_CLASS} {output}",
]
environment = os.environ.copy()
sbt_cache = root / ".cache/retrosoc/sbt"
sbt_cache.mkdir(parents=True, exist_ok=True)
inherited = environment.get("JAVA_TOOL_OPTIONS", "").strip()
java_options = (
f"-Duser.home={sbt_cache} "
f"-Dsbt.global.base={sbt_cache / 'global'} "
f"-Dsbt.boot.directory={sbt_cache / 'boot'} "
f"-Dsbt.ivy.home={sbt_cache / 'ivy'}"
" -Dsbt.server.autostart=false"
)
environment["JAVA_TOOL_OPTIONS"] = f"{inherited} {java_options}".strip()
environment["COURSIER_CACHE"] = str(sbt_cache / "coursier")
subprocess.run(command, cwd=source, check=True, env=environment)
generated = output / "vexii_riscv_hp_generated.v"
if not generated.is_file():
raise FileNotFoundError(f"VexiiRiscv generator did not create {generated}")
validate_locked_source(source, expected)
submodules = run(["git", "submodule", "status", "--recursive"], source).splitlines()
manifest = {
"schema_version": 1,
"module": GENERATED_MODULE,
"configuration": "rv64imafdc_zicbom_max",
"xlen": 64,
"physical_address_width": 32,
"mmu": "sv39",
"vexiiriscv_revision": actual,
"source_status": [],
"submodules": submodules,
"generator": str((scala_dir / "GenerateRetroSocHp.scala").relative_to(root)),
"files": {generated.name: sha256(generated)},
}
args.manifest.resolve().parent.mkdir(parents=True, exist_ok=True)
args.manifest.resolve().write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
(output / "vexiiriscv.fl").write_text(str(generated) + "\n", encoding="utf-8")
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--manifest", type=Path, required=True)
parser.add_argument("--lock", type=Path, required=True)
parser.add_argument("--sbt", type=Path, required=True)
generate(parser.parse_args())
if __name__ == "__main__":
main()