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