feat: enhance test coverage and update system control interfaces

- Added  interface to  and implemented error handling for register writes.
- Introduced a new test for AXI4 address gate functionality in .
- Updated clock and reset domain assertions in  to reflect new domain names.
- Modified expected outputs in  to include new clock domains.
- Adjusted chip configuration assertions in  to account for additional power pads.
- Enhanced memory map tests in  with new definitions and includes.
- Updated pin mapping tests in  to include new reference clock pads.
- Improved script tools tests in  to ensure correct validation of extension bindings.
- Refined system control tests in  to include new clock control interface.
- Updated USB2 packet RAM tests in  to include new SRAM definitions.
- Enhanced user extensions tests in  to validate new product extension configurations.
- Adjusted user IP register parity tests to use legacy extensions.
- Updated Yosys synthesis recipe tests to reflect changes in STA period calculations.
This commit is contained in:
maksyuki committed 2026-08-30 08:10:59 +08:00
1 parent 795b3a4b1d
commit 2b6819449c
131 files changed
+5983 -1790

No files matched your search

+2
View File
@@ -12,8 +12,10 @@ from typing import Any
RESET_PRIMITIVES = {"rst_sync"}
CDC_PRIMITIVES = {
"apb4_async_bridge",
"async_fifo",
"async_reqack",
"axi4_async_bridge",
"cdc_2phase",
"cdc_fifo",
"cdc_sync",
+9 -6
View File
@@ -100,10 +100,9 @@ def render_active_manifest(manifest_path: Path, extensions_path: Path) -> str:
return "\n".join(lines) + "\n"
def generate(root: Path, output: Path) -> None:
def generate(root: Path, output: Path, extensions: Path) -> None:
managed_mpw = root / "rtl/managed/mpw"
manifest = managed_mpw / "mpw.toml"
extensions = root / "rtl/mini/integration/user_extensions.json"
if not manifest.is_file():
raise FileNotFoundError(f"MPW v2 manifest is missing: {manifest}")
descriptor, manifest_name = tempfile.mkstemp(
@@ -126,12 +125,11 @@ def generate(root: Path, output: Path) -> None:
subprocess.run(command, cwd=managed_mpw, check=True)
finally:
active_manifest.unlink(missing_ok=True)
validate_extension_bindings(root, output)
validate_extension_bindings(extensions, output)
def validate_extension_bindings(root: Path, output: Path) -> None:
def validate_extension_bindings(extensions_path: Path, output: Path) -> None:
"""Require SoC slot bindings to match the generated MPW v2 manifest."""
extensions_path = root / "rtl/mini/integration/user_extensions.json"
manifest_path = output / "manifest.json"
extensions = json.loads(extensions_path.read_text(encoding="utf-8"))
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
@@ -178,12 +176,17 @@ def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", required=True, type=Path)
parser.add_argument("--output", required=True, type=Path)
parser.add_argument("--extensions", required=True, type=Path)
parser.add_argument("--lock-file", required=True, type=Path)
arguments = parser.parse_args()
arguments.lock_file.parent.mkdir(parents=True, exist_ok=True)
with arguments.lock_file.open("a+", encoding="utf-8") as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
generate(arguments.root.resolve(), arguments.output.resolve())
generate(
arguments.root.resolve(),
arguments.output.resolve(),
arguments.extensions.resolve(),
)
return 0
+13 -6
View File
@@ -59,16 +59,23 @@ def generate(args: argparse.Namespace) -> None:
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()
java_home = environment.get("HOME")
if java_home:
inherited = environment.get("JAVA_TOOL_OPTIONS", "").strip()
environment["JAVA_TOOL_OPTIONS"] = (
f"{inherited} -Duser.home={java_home}".strip()
)
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"
+22 -2
View File
@@ -101,6 +101,26 @@ def main() -> int:
staging = Path(temp) / "package"
staging.mkdir()
timing_contract = Path(temp) / "commercial_timing_contract.tcl"
if config.get("MINI_MODE", "PRODUCT") == "MPW":
dynamic_core_filelist = (
args.variant_root
/ "generated/mpw"
/ config["SIMU"].lower()
/ "core/core.fl"
)
dynamic_ip_filelist = (
args.variant_root
/ "generated/mpw"
/ config["SIMU"].lower()
/ "ip/ip.fl"
)
else:
dynamic_core_filelist = (
args.variant_root / "generated/user_extensions/legacy_core.fl"
)
dynamic_ip_filelist = (
args.variant_root / "generated/user_extensions/legacy_ip.fl"
)
subprocess.run(
[
"python3",
@@ -137,9 +157,9 @@ def main() -> int:
"--clint-timebase-hz",
str(config.get("CLINT_TIMEBASE_HZ", 1_000_000)),
"--dynamic-core-filelist",
str(args.variant_root / "generated/mpw" / config["SIMU"].lower() / "core/core.fl"),
str(dynamic_core_filelist),
"--dynamic-ip-filelist",
str(args.variant_root / "generated/mpw" / config["SIMU"].lower() / "ip/ip.fl"),
str(dynamic_ip_filelist),
"--memory-map-filelist",
str(args.variant_root / "generated/memory_map/memory_map.fl"),
"--soc-topology-filelist",
+2
View File
@@ -33,6 +33,8 @@ TEST_SOURCES = (
"crt/src/hal/i2s_math.c",
"crt/src/hal/dma_math.c",
"crt/src/hal/sysctrl.c",
"crt/src/hal/clock.c",
"crt/src/hal/extension.c",
"crt/src/hal/user_ip.c",
"rtl/managed/clusterip/ps2/sw/src/ps2.c",
"rtl/managed/clusterip/ps2/sw/src/ps2_keyboard.c",
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Install the locked VexiiRiscv source used by Mini product profiles."""
from __future__ import annotations
import argparse
import subprocess
from pathlib import Path
from dependency_lock import source
from setup_helpers import ensure_git_repo, run
ROOT = Path(__file__).resolve().parents[1]
def repair_incomplete_checkout(destination: Path, url: str, revision: str) -> None:
if not (destination / ".git").is_dir():
return
result = subprocess.run(
["git", "-C", str(destination), "rev-parse", "--verify", "HEAD"],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if result.returncode == 0:
return
run(("git", "remote", "set-url", "origin", url), cwd=destination)
run(("git", "fetch", "--depth", "1", "origin", revision), cwd=destination)
run(("git", "checkout", "--detach", "FETCH_HEAD"), cwd=destination)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--update", action="store_true")
args = parser.parse_args()
dependency = source("vexiiriscv")
destination = ROOT / dependency["destination"]
repair_incomplete_checkout(destination, dependency["url"], dependency["revision"])
ensure_git_repo(
dependency["url"],
destination,
dependency["revision"],
recursive=dependency.get("recursive", False),
update=args.update,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+2 -2
View File
@@ -22,7 +22,7 @@ def require_string(value: Any, field: str) -> str:
return value
def period_ps(document: dict[str, Any], domain: str = "external") -> int:
def period_ps(document: dict[str, Any], domain: str = "hp") -> int:
require_string(domain, "domain")
domains = document.get("domains")
if not isinstance(domains, list) or not domains:
@@ -42,7 +42,7 @@ def period_ps(document: dict[str, Any], domain: str = "external") -> int:
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--domains", type=Path, required=True)
parser.add_argument("--domain", default="external")
parser.add_argument("--domain", default="hp")
return parser.parse_args(argv)