mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- 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.
192 lines
6.5 KiB
Python
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
|