mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
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:
14 files changed
+817
-550
No files matched your search
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user