Files
retroSoC/tests/test_pin_map.py
T
maksyuki 2b6819449c feat: enhance test coverage and update system control interfaces
- Added  interface to  and implemented error handling for register writes.
- Introduced a new test for AXI4 address gate functionality in .
- Updated clock and reset domain assertions in  to reflect new domain names.
- Modified expected outputs in  to include new clock domains.
- Adjusted chip configuration assertions in  to account for additional power pads.
- Enhanced memory map tests in  with new definitions and includes.
- Updated pin mapping tests in  to include new reference clock pads.
- Improved script tools tests in  to ensure correct validation of extension bindings.
- Refined system control tests in  to include new clock control interface.
- Updated USB2 packet RAM tests in  to include new SRAM definitions.
- Enhanced user extensions tests in  to validate new product extension configurations.
- Adjusted user IP register parity tests to use legacy extensions.
- Updated Yosys synthesis recipe tests to reflect changes in STA period calculations.
2026-08-30 08:10:59 +08:00

243 lines
9.3 KiB
Python

"""Tests for the canonical Mini SoC pin-map generator."""
from __future__ import annotations
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
GENERATOR = ROOT / "rtl/mini/pin_map/generate_pin_map.py"
PIN_MAP = ROOT / "rtl/mini/pin_map/pin_map.json"
FPGA_TOP = ROOT / "fpga/mini/retrosoc_top.sv"
FPGA_XDC = ROOT / "fpga/mini/starrysky_v2.xdc"
def generate(output_dir: Path) -> None:
subprocess.run(
[
sys.executable,
str(GENERATOR),
"--map",
str(PIN_MAP),
"--output-dir",
str(output_dir),
],
check=True,
)
def validate(map_path: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(GENERATOR), "--map", str(map_path), "--check"],
text=True,
capture_output=True,
)
def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
generate(tmp_path)
ports = (tmp_path / "rtl/retrosoc_asic_ports.svh").read_text(encoding="utf-8")
pads = (tmp_path / "rtl/retrosoc_asic_pad_bindings.svh").read_text(encoding="utf-8")
core_ports = (tmp_path / "rtl/retrosoc_core_ports.svh").read_text(encoding="utf-8")
core_bindings = (tmp_path / "rtl/retrosoc_core_bindings.svh").read_text(encoding="utf-8")
fpga = (tmp_path / "rtl/retrosoc_asic_fpga_mini_bindings.svh").read_text(encoding="utf-8")
testbench = (tmp_path / "rtl/retrosoc_asic_tb_bindings.svh").read_text(encoding="utf-8")
verilator = (tmp_path / "rtl/retrosoc_asic_verilator_bindings.svh").read_text(encoding="utf-8")
assert "inout wire IOVDD," in ports
assert "inout wire IOVSS," in ports
assert "inout wire VDD," in ports
assert "inout wire VSS," in ports
assert "inout wire VDD," not in core_ports
assert "inout extclk_i_pad," in core_ports
assert "assign s_ext_clk = extclk_i_pad;" in core_bindings
assert "inout ref24clk_i_pad," in core_ports
assert "assign s_ref24_clk = ref24clk_i_pad;" in core_bindings
assert "assign gpio_0_io_pad = u_gpio_if.oe_o[0] ? u_gpio_if.do_o[0] : 1'bz;" in core_bindings
assert "assign u_sdram_if.dq_i[15] = sdram_dq15_io_pad;" in core_bindings
assert "inout extclk_i_pad," in ports
assert "inout ref24clk_i_pad," in ports
assert "inout sdram_dq15_io_pad" in ports
assert "inout jtag_tck_i_pad" in ports
assert "inout jtag_trst_n_i_pad" in ports
assert "output jtag_tdo_o_pad" in ports
assert "output sdio1_clk_o_pad," in ports
assert "inout sdio1_cmd_io_pad," in ports
assert "inout sdio1_dat3_io_pad," in ports
assert "inout usb2_ulpi_clk_i_pad," in ports
assert "inout usb2_ulpi_data7_io_pad," in ports
assert "output usb2_ulpi_stp_o_pad," in ports
assert "output usb2_ulpi_reset_n_o_pad," in ports
assert "HAVE_DEBUG" not in ports
assert "HAVE_DEBUG" not in pads
assert "u_gpio_31_io_pad" in pads
assert "tc_io_in_pad u_ref24clk_i_pad" in pads
assert "u_sdram_dq15_io_pad" in pads
assert "u_sdio1_clk_o_pad" in pads
assert "tc_io_out_pad u_sdio1_clk_o_pad" in pads
assert "u_sdio1_cmd_io_pad" in pads
assert ".c2p(u_sdio1_if.cmd_do_o)" in pads
assert ".c2p_en(u_sdio1_if.cmd_oe_o)" in pads
assert ".p2c(u_sdio1_if.cmd_di_i)" in pads
assert "tc_io_in_pad u_usb2_ulpi_clk_i_pad" in pads
assert ".p2c(s_usb2_ulpi_clk)" in pads
assert "`define RETROSOC_PAD_POWER_CONNECTIONS" in pads
assert "sg13g2_IOPadIOVdd iovdd_pad" in pads
assert "for (genvar i = 0; i < 24; i++) begin : vdd_pads" in pads
assert "for (genvar i = 0; i < 24; i++) begin : vss_pads" in pads
assert "for (genvar i = 0; i < 16; i++) begin : iovdd_pads" in pads
assert "for (genvar i = 0; i < 16; i++) begin : iovss_pads" in pads
assert "u_usb2_ulpi_data7_io_pad" in pads
assert ".c2p(u_usb2_ulpi_if.data_do_o[7])" in pads
assert ".c2p_en(u_usb2_ulpi_if.data_oe_o)" in pads
assert ".p2c(u_usb2_ulpi_if.data_di_i[7])" in pads
assert "tc_io_out_pad u_usb2_ulpi_stp_o_pad" in pads
assert ".gpio_30_io_pad(gpio_io30)" in fpga
assert ".gpio_15_io_pad()" in fpga
assert ".gpio_24_io_pad(s_psram_dat1)" in testbench
assert testbench.count(".gpio_24_io_pad(") == 1
assert ".sdram_clk_o_pad(s_sdram_clk)" in verilator
assert ".ref24clk_i_pad(s_ref24_clk)" in verilator
assert ".sdram_dq15_io_pad(s_sdram_dq[15])" in verilator
assert ".jtag_tck_i_pad(s_jtag_tck)" in verilator
assert ".jtag_tdo_o_pad(s_jtag_tdo)" in verilator
assert ".sdio1_clk_o_pad(sdio1_clk_o)" in verilator
assert ".sdio1_cmd_io_pad(sdio1_cmd_io)" in verilator
assert ".sdio1_dat3_io_pad(sdio1_dat3_io)" in verilator
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in verilator
assert ".usb2_ulpi_data7_io_pad(s_usb2_ulpi_data[7])" in verilator
assert ".sdio1_clk_o_pad(s_sdio1_clk)" in testbench
assert ".sdio1_cmd_io_pad(s_sdio1_cmd)" in testbench
assert ".sdio1_dat0_io_pad(s_sdio1_dat[0])" in testbench
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in testbench
assert ".usb2_ulpi_data0_io_pad(s_usb2_ulpi_data[0])" in testbench
assert ".sdio1_clk_o_pad()" in fpga
assert ".sdio1_cmd_io_pad()" in fpga
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in fpga
assert ".usb2_ulpi_data0_io_pad(s_usb2_ulpi_data[0])" in fpga
assert ".jtag_tck_i_pad(s_jtag_tck)" in testbench
assert "user_gpio_" not in ports
assert "user_gpio_" not in pads
assert "user_gpio_" not in testbench
def test_pin_map_rejects_duplicate_pads(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["pads"][0]["ports"].append({"name": "extclk_i_pad", "signal": "s_duplicate_clock"})
invalid_map = tmp_path / "duplicate.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "pad names must be unique" in result.stderr
def test_pin_map_rejects_non_object_port_entry(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["pads"][0]["ports"].append("extclk_i_pad")
invalid_map = tmp_path / "invalid-port.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "ports entries must be objects" in result.stderr
def test_pin_map_rejects_unknown_profile_pad(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["profiles"]["tb"]["bindings"]["unknown_pad"] = "s_unknown"
invalid_map = tmp_path / "unknown.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "unknown pad" in result.stderr
def test_pin_map_requires_explicit_bidirectional_peripheral_signals(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
bidirectional_pad = next(
pad
for pad in document["pads"]
if any("output_enable" in port for port in pad.get("ports", []))
)
bidirectional_port = next(
port for port in bidirectional_pad["ports"] if "output_enable" in port
)
bidirectional_port.pop("output_enable")
invalid_map = tmp_path / "missing-bidir-signal.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "requires input, output, and output_enable" in result.stderr
def test_fpga_constraints_reference_declared_top_ports() -> None:
top_ports = set(
re.findall(
r"^\s*(?:input|output|inout)\s+([A-Za-z_][A-Za-z0-9_]*)",
FPGA_TOP.read_text(encoding="utf-8"),
re.MULTILINE,
)
)
xdc_ports = set(
re.findall(
r"\[get_ports\s+([A-Za-z_][A-Za-z0-9_]*)\]", FPGA_XDC.read_text(encoding="utf-8")
)
)
assert xdc_ports
assert xdc_ports <= top_ports
def test_apb4_interface_bridge(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
source_list = tmp_path / "apb4_if_bridge.fl"
source_list.write_text(
"\n".join(
[
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv"),
str(ROOT / "rtl/mini/top/apb4_if_bridge.sv"),
str(ROOT / "tests/rtl/apb4_if_bridge_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "apb4_if_bridge_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 / "apb4_if_bridge_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "apb4_if_bridge_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "apb4 interface bridge test passed" in result.stdout