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.
This commit is contained in:
maksyuki committed 2026-07-23 12:07:45 +08:00
1 parent 235ad7224e
commit d886cd992c
14 files changed
+817 -550

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
`timescale 1ns / 1ps
`include "mmap_define.svh"
module soc_topology_nmi_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
nmi_if nmi ();
`include "soc_nmi_interfaces.svh"
`include "soc_nmi_routes.svh"
always #5 clk_i = ~clk_i;
task automatic expect_route(input logic [31:0] address, input integer slot,
input logic [31:0] expected_rdata);
begin
@(negedge clk_i);
nmi.addr = address;
nmi.valid = 1'b1;
#1;
if (s_slv_sel_d !== (18'd1 << slot)) begin
$fatal(1, "unexpected target slot for %h", address);
end
@(posedge clk_i);
#1;
if (!nmi.ready || nmi.rdata !== expected_rdata) begin
$fatal(1, "unexpected response for %h", address);
end
@(negedge clk_i);
nmi.valid = 1'b0;
@(posedge clk_i);
end
endtask
task automatic expect_disabled(input logic [31:0] address);
begin
@(negedge clk_i);
nmi.addr = address;
nmi.valid = 1'b1;
#1;
if (s_slv_sel_d !== '0) begin
$fatal(1, "disabled target accepted %h", address);
end
@(posedge clk_i);
#1;
if (nmi.ready || nmi.rdata !== '0) begin
$fatal(1, "disabled target responded to %h", address);
end
@(negedge clk_i);
nmi.valid = 1'b0;
@(posedge clk_i);
end
endtask
initial begin
nmi.valid = 1'b0;
nmi.addr = '0;
nmi.wdata = '0;
nmi.wstrb = '0;
u_uart_nmi_if.ready = 1'b1;
u_gpio_nmi_if.ready = 1'b1;
u_tim0_nmi_if.ready = 1'b1;
u_tim1_nmi_if.ready = 1'b1;
u_psram_nmi_if.ready = 1'b1;
u_spisd_nmi_if.ready = 1'b1;
u_i2c0_nmi_if.ready = 1'b1;
u_i2s_nmi_if.ready = 1'b1;
u_onewire_nmi_if.ready = 1'b1;
u_xpi_nmi_if.ready = 1'b1;
u_dma_nmi_if.ready = 1'b1;
u_sysctrl_nmi_if.ready = 1'b1;
u_clint_nmi_if.ready = 1'b1;
u_sdram_nmi_if.ready = 1'b1;
u_dvp_nmi_if.ready = 1'b1;
u_sdio_nmi_if.ready = 1'b1;
u_opipsram_nmi_if.ready = 1'b1;
u_i2c1_nmi_if.ready = 1'b1;
u_uart_nmi_if.rdata = 32'h0000_0000;
u_gpio_nmi_if.rdata = 32'h1111_1111;
u_tim0_nmi_if.rdata = 32'h2222_2222;
u_tim1_nmi_if.rdata = 32'h3333_3333;
u_psram_nmi_if.rdata = 32'h4444_4444;
u_spisd_nmi_if.rdata = 32'h5555_5555;
u_i2c0_nmi_if.rdata = 32'h6666_6666;
u_i2s_nmi_if.rdata = 32'h7777_7777;
u_onewire_nmi_if.rdata = 32'h8888_8888;
u_xpi_nmi_if.rdata = 32'h9999_9999;
u_dma_nmi_if.rdata = 32'hAAAA_AAAA;
u_sysctrl_nmi_if.rdata = 32'hBBBB_BBBB;
u_clint_nmi_if.rdata = 32'hCCCC_CCCC;
u_sdram_nmi_if.rdata = 32'hDDDD_DDDD;
u_dvp_nmi_if.rdata = 32'hEEEE_EEEE;
u_sdio_nmi_if.rdata = 32'hF0F0_F0F0;
u_opipsram_nmi_if.rdata = 32'h0F0F_0F0F;
u_i2c1_nmi_if.rdata = 32'h1234_5678;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
expect_route(`SOC_ADDR_NMI_GPIO_BASE, 1, 32'h1111_1111);
expect_route(`SOC_ADDR_PSRAM_BASE, 4, 32'h4444_4444);
expect_route(`SOC_ADDR_FLASH_BASE, 9, 32'h9999_9999);
expect_route(`SOC_ADDR_NMI_I2C1_BASE, 17, 32'h1234_5678);
expect_disabled(`SOC_ADDR_NMI_SDIO_BASE);
$display("SoC topology NMI routing test passed");
$finish;
end
endmodule
+8 -14
View File
@@ -27,7 +27,7 @@ from scripts.check_format import format_files # noqa: E402
from scripts.dependency_lock import LockError, load_lock # noqa: E402
from scripts.generate_mpw import migrate_user_gpio_interfaces # noqa: E402
from scripts.install_toolchain import safe_extract # noqa: E402
from scripts.regress import MDD_VERILATOR_SIM_TIME, PR_COMMANDS, regression_environment # noqa: E402
from scripts.regress import NIGHTLY_COMMANDS, PR_COMMANDS, regression_environment # noqa: E402
from scripts.setup_helpers import download_file, ensure_git_repo # noqa: E402
@@ -298,20 +298,14 @@ def test_regression_runner_uses_one_build_timestamp(monkeypatch) -> None:
)
def test_mdd_regressions_allow_uart_boot_to_complete() -> None:
mdd_profiles = {
"configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk",
"configs/ci/mdd-rv32im-ihp130.mk",
}
expected_values = (
"SIMU=VERILATOR",
f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}",
"firmware",
"sim",
)
def test_verilator_simulations_use_uniform_timeout() -> None:
verilator_makefile = ROOT / "rtl/mini/mk/verilator.mk"
assert "SOC_SIM_TIME ?= 180" in verilator_makefile.read_text(encoding="utf-8")
assert MDD_VERILATOR_SIM_TIME == 180
assert {profile for profile, values in PR_COMMANDS if values == expected_values} == mdd_profiles
regression_commands = (*PR_COMMANDS, *NIGHTLY_COMMANDS)
for _, values in regression_commands:
if "SIMU=VERILATOR" in values:
assert not any(value.startswith("SOC_SIM_TIME=") for value in values)
def test_run_flow_writes_structured_result(tmp_path: Path) -> None:
+191
View File
@@ -0,0 +1,191 @@
"""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