Files
retroSoC/tests/test_pin_map.py
T
maksyuki c8187284d0 feat(tiny): add scripts for SDC generation and packaging
- Introduced  to create pad-aware timing constraints for the Tiny IHP130 chip flow.
- Added  to create a checksummed delivery archive for the Tiny LibreLane implementation.
- Created  for synthesis-only header of the locked IHP SRAM macro physical views.
- Implemented  to limit OpenSTA report enumeration for path counts.
- Updated  to include power pad definitions.
- Enhanced  to handle power pad counts and validation.
- Added tests for Tiny flow and pin map validation, including power pad overrides and constraints.
2026-09-27 08:48:12 +08:00

289 lines
11 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 / "scripts/rtl/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_pin_map_tiny_power_pads_override(tmp_path: Path) -> None:
subprocess.run(
[
sys.executable,
str(GENERATOR),
"--map",
str(ROOT / "rtl/tiny/pin_map/pin_map.json"),
"--output-dir",
str(tmp_path),
],
check=True,
)
pads = (tmp_path / "rtl/retrosoc_asic_pad_bindings.svh").read_text(encoding="utf-8")
assert "for (genvar i = 0; i < 8; i++) begin : vdd_pads" in pads
assert "for (genvar i = 0; i < 8; i++) begin : vss_pads" in pads
assert "for (genvar i = 0; i < 8; i++) begin : iovdd_pads" in pads
assert "for (genvar i = 0; i < 8; i++) begin : iovss_pads" in pads
assert "i < 24" not in pads
assert "i < 16" not in pads
def test_pin_map_rejects_incomplete_power_pads(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["power_pads"] = {"vdd": 24, "vss": 24, "iovdd": 16}
invalid_map = tmp_path / "incomplete-power-pads.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "power_pads" in result.stderr
def test_pin_map_rejects_non_positive_power_pads(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["power_pads"] = {"vdd": 0, "vss": 24, "iovdd": 16, "iovss": 16}
invalid_map = tmp_path / "non-positive-power-pads.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "power_pads.vdd" 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/ip/interconnect/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 / "scripts/rtl/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