"""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