Files
retroSoC/tests/test_soc_topology.py
T
maksyuki d886cd992c feat: refactor SoC topology and NMI integration
- Updated the  file to improve code readability and maintainability by removing commented-out code and adding generated GPIO alternate-function wiring.
- Modified  to streamline simulation command configurations and removed unnecessary variables.
- Introduced a new testbench  to validate NMI routing functionality within the SoC topology.
- Added comprehensive tests in  to ensure the correctness of generated bindings and validate topology constraints.
- Adjusted  to reflect changes in regression command imports and ensure uniform timeout settings for Verilator simulations.
2026-07-23 12:07:45 +08:00

192 lines
6.5 KiB
Python

"""Tests for the generated Mini SoC internal integration bindings."""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TOPOLOGY = ROOT / "rtl/mini/integration/soc_topology.json"
GENERATOR = ROOT / "rtl/mini/integration/generate_soc_topology.py"
MEMORY_MAP = ROOT / "rtl/mini/address_map/memory_map.json"
MEMORY_MAP_GENERATOR = ROOT / "rtl/mini/address_map/generate_memory_map.py"
def generate(output_dir: Path) -> None:
subprocess.run(
[
sys.executable,
str(GENERATOR),
"--map",
str(TOPOLOGY),
"--memory-map",
str(MEMORY_MAP),
"--output-dir",
str(output_dir),
],
check=True,
)
def validate(topology: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[
sys.executable,
str(GENERATOR),
"--map",
str(topology),
"--memory-map",
str(MEMORY_MAP),
"--check",
],
text=True,
capture_output=True,
)
def write_invalid_topology(tmp_path: Path, document: dict[str, object]) -> Path:
path = tmp_path / "invalid_topology.json"
path.write_text(json.dumps(document), encoding="utf-8")
return path
def test_topology_generates_complete_native_and_gpio_bindings(tmp_path: Path) -> None:
generate(tmp_path)
interfaces = (tmp_path / "rtl/soc_nmi_interfaces.svh").read_text(encoding="utf-8")
routes = (tmp_path / "rtl/soc_nmi_routes.svh").read_text(encoding="utf-8")
gpio = (tmp_path / "rtl/soc_gpio_alt_bindings.svh").read_text(encoding="utf-8")
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
assert interfaces.count("nmi_if u_") == 18
assert "assign s_slv_sel_d[17] = u_i2c1_nmi_if.valid;" in routes
assert "assign s_xpi_region_sel[1] = (nmi.addr <= `SOC_ADDR_FLASH_END);" in routes
assert "assign u_sdio_nmi_if.valid = 1'b0;" in routes
assert gpio.count("// GPIO") == 64
assert "assign u_uart1_if.rx_i = u_gpio_if.di_i[0];" in gpio
assert "assign u_gpio_if.alt1_do_i[22] = u_psram_if.nss_o[0];" in gpio
assert filelist.startswith("+incdir+")
def test_topology_rejects_unknown_or_non_native_regions(tmp_path: Path) -> None:
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["native_targets"][0]["regions"] = ["UNKNOWN"]
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "unknown region" in result.stderr
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["native_targets"][0]["regions"] = ["SRAM"]
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "not an active native region" in result.stderr
def test_topology_rejects_duplicate_region_and_disabled_owner(tmp_path: Path) -> None:
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["native_targets"][1]["regions"] = ["NMI_UART0"]
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "multiple targets" in result.stderr
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["native_targets"][15]["regions"] = ["NMI_SDIO"]
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "disabled but declares regions" in result.stderr
def test_topology_rejects_invalid_gpio_coverage_and_expression(tmp_path: Path) -> None:
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["gpio_alt_functions"][1]["pin"] = 0
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "gpio pin 0 is duplicated" in result.stderr
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["gpio_alt_functions"].pop()
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "one entry for every GPIO pin" in result.stderr
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["gpio_alt_functions"][0]["alt0"]["do"] = "left + right"
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "scalar SystemVerilog reference or constant" in result.stderr
def test_generated_nmi_routes_select_and_return_the_expected_target(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
sv2v = shutil.which("sv2v")
vvp = shutil.which("vvp")
if iverilog is None or sv2v is None or vvp is None:
return
memory_map_output = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(MEMORY_MAP_GENERATOR),
"--map",
str(MEMORY_MAP),
"--output-dir",
str(memory_map_output),
"--have-sram-if",
"NO",
"--ip",
"NONE",
],
check=True,
)
topology_output = tmp_path / "topology"
generate(topology_output)
source_list = tmp_path / "soc_topology_nmi.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{memory_map_output / 'rtl'}",
f"+incdir+{topology_output / 'rtl'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/nmi_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "tests/rtl/soc_topology_nmi_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "soc_topology_nmi_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "soc_topology_nmi_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"soc_topology_nmi_tb",
"-o",
str(simulation),
str(converted),
],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "SoC topology NMI routing test passed" in result.stdout