From d886cd992cb29a30e244974d407629b9d0b14254 Mon Sep 17 00:00:00 2001 From: maksyuki Date: Thu, 23 Jul 2026 12:07:45 +0800 Subject: [PATCH] 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. --- .github/workflows/quality.yml | 3 + Makefile | 4 +- README.md | 3 + rtl/mini/Makefile | 23 +- rtl/mini/integration/README.md | 27 ++ rtl/mini/integration/generate_soc_topology.py | 374 ++++++++++++++++++ rtl/mini/integration/soc_topology.json | 58 +++ rtl/mini/mk/verilator.mk | 2 +- rtl/mini/top/ip_nmi_wrapper.sv | 239 +---------- rtl/mini/top/retrosoc.sv | 292 +------------- scripts/regress.py | 17 +- tests/rtl/soc_topology_nmi_tb.sv | 112 ++++++ tests/test_script_tools.py | 22 +- tests/test_soc_topology.py | 191 +++++++++ 14 files changed, 817 insertions(+), 550 deletions(-) create mode 100644 rtl/mini/integration/README.md create mode 100644 rtl/mini/integration/generate_soc_topology.py create mode 100644 rtl/mini/integration/soc_topology.json create mode 100644 tests/rtl/soc_topology_nmi_tb.sv create mode 100644 tests/test_soc_topology.py diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 0bd5de18..d7b33c36 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -44,6 +44,9 @@ jobs: - name: Validate dependency lock run: python3 scripts/dependency_lock.py --lock config/dependencies.lock.json + - name: Validate SoC integration topology + run: make check-soc-topology + - name: Lint Python run: ruff check . diff --git a/Makefile b/Makefile index 60b5f03c..faf77e3b 100644 --- a/Makefile +++ b/Makefile @@ -167,7 +167,7 @@ endif clean-all purge-cache manifest check-warnings metrics check-metrics package \ regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \ mk-format-check rtl-format rtl-format-check sw-policy-check sw-host-test \ - pin-map check-pin-map + pin-map check-pin-map soc-topology check-soc-topology .NOTPARALLEL: setup help: @@ -186,6 +186,8 @@ help: ' check-memory-map validate the canonical address map' \ ' pin-map generate the selected pin-map artifacts' \ ' check-pin-map validate the canonical SoC pin map' \ + ' soc-topology generate the selected internal SoC integration artifacts' \ + ' check-soc-topology validate the canonical internal SoC integration map' \ ' check-warnings | metrics analyze flow logs and reports' \ ' check-metrics apply the committed metrics policy' \ ' format format self-owned C, Makefile, and RTL sources' \ diff --git a/README.md b/README.md index 160d3616..0868f8a5 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,9 @@ variant identifier; `make help` lists all available targets. Netlist simulation analysis consume the Yosys netlist, so run the synthesis command first. The CI-proven netlist regression uses the assembly self-test image: +Verilator simulations run for 180 seconds by default. Set `SOC_SIM_TIME` explicitly only when +an exploratory local run needs a different limit. + ```sh make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG RTL_SIM_TIMEOUT=5200000 sim-asm make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SYNTH=YOSYS synth diff --git a/rtl/mini/Makefile b/rtl/mini/Makefile index 8e1e5272..8a51359c 100644 --- a/rtl/mini/Makefile +++ b/rtl/mini/Makefile @@ -12,6 +12,11 @@ PIN_MAP_SCRIPT := $(RTL_PATH)/pin_map/generate_pin_map.py PIN_MAP_DIR := $(VARIANT_ROOT)/generated/pin_map PIN_MAP_STAMP := $(PIN_MAP_DIR)/.stamp PIN_MAP_FILELIST := $(PIN_MAP_DIR)/pin_map.fl +SOC_TOPOLOGY_SOURCE := $(RTL_PATH)/integration/soc_topology.json +SOC_TOPOLOGY_SCRIPT := $(RTL_PATH)/integration/generate_soc_topology.py +SOC_TOPOLOGY_DIR := $(VARIANT_ROOT)/generated/soc_topology +SOC_TOPOLOGY_STAMP := $(SOC_TOPOLOGY_DIR)/.stamp +SOC_TOPOLOGY_FILELIST := $(SOC_TOPOLOGY_DIR)/soc_topology.fl MPW_STAMP := $(CACHE_ROOT)/stamps/mpw-$(LOCK_DIGEST).prepared MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME) MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp @@ -57,8 +62,19 @@ pin-map: $(PIN_MAP_STAMP) check-pin-map: python3 $(PIN_MAP_SCRIPT) --map $(PIN_MAP_SOURCE) --check +$(SOC_TOPOLOGY_STAMP): $(SOC_TOPOLOGY_SOURCE) $(SOC_TOPOLOGY_SCRIPT) $(MEMORY_MAP_SOURCE) + python3 $(SOC_TOPOLOGY_SCRIPT) --map $(SOC_TOPOLOGY_SOURCE) \ + --memory-map $(MEMORY_MAP_SOURCE) --output-dir $(SOC_TOPOLOGY_DIR) + @touch $@ + +soc-topology: $(SOC_TOPOLOGY_STAMP) + +check-soc-topology: + python3 $(SOC_TOPOLOGY_SCRIPT) --map $(SOC_TOPOLOGY_SOURCE) \ + --memory-map $(MEMORY_MAP_SOURCE) --check + $(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \ - $(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) + $(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) python3 $(RTL_PATH)/script/generate_filelist.py \ --output-dir $(GENERATED_FL_DIR) \ $(foreach define,$(DEF_LIST),--define $(define)) @@ -85,7 +101,7 @@ NET_FLIST += -f $(GENERATED_FL_DIR)/sys_def.fl NET_FLIST += -f $(GENERATED_FL_DIR)/commonip.fl NET_FLIST += -f $(GENERATED_FL_DIR)/inc.fl -RTL_FLIST := -f $(MEMORY_MAP_FILELIST) -f $(PIN_MAP_FILELIST) $(NET_FLIST) +RTL_FLIST := -f $(MEMORY_MAP_FILELIST) -f $(PIN_MAP_FILELIST) -f $(SOC_TOPOLOGY_FILELIST) $(NET_FLIST) RTL_FLIST += -f $(GENERATED_FL_DIR)/clusterip.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/ip.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/tech.fl @@ -113,4 +129,5 @@ else ifeq ($(SIMU), IVERILOG) include $(RTL_PATH)/mk/iverilog.mk endif -.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map firmware \ No newline at end of file +.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map \ + soc-topology check-soc-topology firmware \ No newline at end of file diff --git a/rtl/mini/integration/README.md b/rtl/mini/integration/README.md new file mode 100644 index 00000000..0eba80c5 --- /dev/null +++ b/rtl/mini/integration/README.md @@ -0,0 +1,27 @@ +# Mini SoC Topology + +`soc_topology.json` is the source of truth for internal Mini SoC integration +that is not part of the software address-map ABI or the package pad map. + +The topology generator validates the native NMI target ownership against +`../address_map/memory_map.json` and emits generated SystemVerilog include +files for `ip_nmi_wrapper.sv` and `retrosoc.sv`. + +The `native_targets` list uses stable response slots. Every active native +memory-map region must appear exactly once. Disabled targets keep their slot +and interface declaration but cannot own an address region. + +Each `gpio_alt_functions` entry defines both alternate modes for one GPIO pin. +`inputs` are driven from the selected GPIO input, and `do` and `oe` define the +peripheral output path. Values are restricted to scalar signal references and +one-bit constants so malformed expressions are rejected before RTL generation. + +Use these commands to validate or generate the selected build artifacts: + +```sh +make check-soc-topology +make soc-topology +``` + +Generated files are written under `build//generated/soc_topology/` and +must not be committed. diff --git a/rtl/mini/integration/generate_soc_topology.py b/rtl/mini/integration/generate_soc_topology.py new file mode 100644 index 00000000..c4465311 --- /dev/null +++ b/rtl/mini/integration/generate_soc_topology.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python3 +"""Generate Mini SoC internal integration bindings from one topology map.""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +SV_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$") +SV_REFERENCE_RE = re.compile( + r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*(?:\[[0-9]+\])?$" +) +SV_CONSTANT_RE = re.compile(r"(?:1'[bB][01]|'[01])$") + + +@dataclass(frozen=True) +class NativeTarget: + slot: int + name: str + interface: str + regions: tuple[str, ...] + disabled: bool + + +@dataclass(frozen=True) +class GpioMode: + inputs: tuple[str, ...] + do: str + oe: str + + +@dataclass(frozen=True) +class GpioFunction: + pin: int + alt0: GpioMode + alt1: GpioMode + + +def atomic_write(path: Path, content: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + if path.exists() and path.read_text(encoding="utf-8") == content: + return + descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent) + try: + with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as output: + output.write(content) + os.replace(temporary_name, path) + except BaseException: + Path(temporary_name).unlink(missing_ok=True) + raise + + +def require_object(value: Any, field: str) -> dict[str, Any]: + if not isinstance(value, dict): + raise ValueError(f"{field} must be an object") + return value + + +def require_string(value: Any, field: str) -> str: + if not isinstance(value, str) or not value: + raise ValueError(f"{field} must be a non-empty string") + return value + + +def require_identifier(value: Any, field: str) -> str: + identifier = require_string(value, field) + if SV_IDENTIFIER_RE.fullmatch(identifier) is None: + raise ValueError(f"{field} must be a SystemVerilog identifier") + return identifier + + +def require_reference(value: Any, field: str) -> str: + reference = require_string(value, field) + if SV_REFERENCE_RE.fullmatch(reference) is None: + raise ValueError(f"{field} must be a scalar SystemVerilog reference") + return reference + + +def require_value(value: Any, field: str) -> str: + rendered = require_string(value, field) + if SV_REFERENCE_RE.fullmatch(rendered) is None and SV_CONSTANT_RE.fullmatch(rendered) is None: + raise ValueError(f"{field} must be a scalar SystemVerilog reference or constant") + return rendered + + +def read_memory_regions(path: Path) -> dict[str, dict[str, Any]]: + document = require_object(json.loads(path.read_text(encoding="utf-8")), "memory map") + regions = document.get("regions") + if not isinstance(regions, list) or not regions: + raise ValueError("memory map regions must be a non-empty list") + parsed: dict[str, dict[str, Any]] = {} + for index, entry in enumerate(regions): + region = require_object(entry, f"memory map regions[{index}]") + symbol = require_identifier(region.get("symbol"), f"memory map regions[{index}].symbol") + if symbol in parsed: + raise ValueError(f"memory map region {symbol} is duplicated") + parsed[symbol] = region + return parsed + + +def parse_regions(value: Any, field: str) -> tuple[str, ...]: + if not isinstance(value, list) or not value: + raise ValueError(f"{field} must be a non-empty list") + regions = tuple(require_identifier(item, f"{field} entry") for item in value) + if len(regions) != len(set(regions)): + raise ValueError(f"{field} contains duplicate regions") + return regions + + +def parse_native_targets(value: Any, memory_regions: dict[str, dict[str, Any]]) -> list[NativeTarget]: + if not isinstance(value, list) or not value: + raise ValueError("native_targets must be a non-empty list") + targets: list[NativeTarget] = [] + claimed_regions: set[str] = set() + names: set[str] = set() + interfaces: set[str] = set() + slots: set[int] = set() + for index, entry in enumerate(value): + target = require_object(entry, f"native_targets[{index}]") + slot = target.get("slot") + if not isinstance(slot, int) or slot < 0: + raise ValueError(f"native_targets[{index}].slot must be a non-negative integer") + name = require_identifier(target.get("name"), f"native_targets[{index}].name") + interface = require_identifier( + target.get("interface"), f"native_targets[{index}].interface" + ) + disabled = target.get("disabled", False) + if not isinstance(disabled, bool): + raise ValueError(f"native_targets[{index}].disabled must be boolean") + if disabled: + if "regions" in target: + raise ValueError(f"native_targets[{index}] is disabled but declares regions") + regions: tuple[str, ...] = () + else: + regions = parse_regions(target.get("regions"), f"native_targets[{index}].regions") + for region_name in regions: + region = memory_regions.get(region_name) + if region is None: + raise ValueError( + f"native_targets[{index}] references unknown region {region_name}" + ) + if region.get("route") != "native" or region.get("kind") != "active": + raise ValueError( + f"native_targets[{index}] region {region_name} is not an active native region" + ) + if region_name in claimed_regions: + raise ValueError(f"native region {region_name} has multiple targets") + claimed_regions.add(region_name) + if name in names: + raise ValueError(f"native target name {name} is duplicated") + if interface in interfaces: + raise ValueError(f"native target interface {interface} is duplicated") + if slot in slots: + raise ValueError(f"native target slot {slot} is duplicated") + names.add(name) + interfaces.add(interface) + slots.add(slot) + targets.append(NativeTarget(slot, name, interface, regions, disabled)) + + ordered = sorted(targets, key=lambda item: item.slot) + if [target.slot for target in ordered] != list(range(len(ordered))): + raise ValueError("native target slots must be contiguous from zero") + active_native_regions = { + symbol + for symbol, region in memory_regions.items() + if region.get("route") == "native" and region.get("kind") == "active" + } + if claimed_regions != active_native_regions: + missing = sorted(active_native_regions - claimed_regions) + extra = sorted(claimed_regions - active_native_regions) + details: list[str] = [] + if missing: + details.append(f"missing {', '.join(missing)}") + if extra: + details.append(f"extra {', '.join(extra)}") + raise ValueError(f"native target regions do not cover the memory map ({'; '.join(details)})") + return ordered + + +def parse_gpio_mode(value: Any, field: str) -> GpioMode: + mode = require_object(value, field) + inputs_value = mode.get("inputs") + if not isinstance(inputs_value, list): + raise ValueError(f"{field}.inputs must be a list") + inputs = tuple( + require_reference(item, f"{field}.inputs[{index}]") + for index, item in enumerate(inputs_value) + ) + if len(inputs) != len(set(inputs)): + raise ValueError(f"{field}.inputs contains duplicates") + return GpioMode( + inputs=inputs, + do=require_value(mode.get("do"), f"{field}.do"), + oe=require_value(mode.get("oe"), f"{field}.oe"), + ) + + +def parse_gpio_functions(value: Any, gpio_pins: int) -> list[GpioFunction]: + if not isinstance(value, list) or len(value) != gpio_pins: + raise ValueError("gpio_alt_functions must contain one entry for every GPIO pin") + parsed: list[GpioFunction] = [] + pins: set[int] = set() + for index, entry in enumerate(value): + function = require_object(entry, f"gpio_alt_functions[{index}]") + pin = function.get("pin") + if not isinstance(pin, int) or not 0 <= pin < gpio_pins: + raise ValueError(f"gpio_alt_functions[{index}].pin is out of range") + if pin in pins: + raise ValueError(f"gpio pin {pin} is duplicated") + pins.add(pin) + parsed.append( + GpioFunction( + pin=pin, + alt0=parse_gpio_mode(function.get("alt0"), f"gpio_alt_functions[{index}].alt0"), + alt1=parse_gpio_mode(function.get("alt1"), f"gpio_alt_functions[{index}].alt1"), + ) + ) + if pins != set(range(gpio_pins)): + raise ValueError("gpio_alt_functions must cover contiguous pins from zero") + return sorted(parsed, key=lambda item: item.pin) + + +def read_topology( + topology_path: Path, memory_map_path: Path +) -> tuple[list[NativeTarget], list[GpioFunction]]: + document = require_object(json.loads(topology_path.read_text(encoding="utf-8")), "topology") + if document.get("schema_version") != 1: + raise ValueError("schema_version must be 1") + gpio_pins = document.get("gpio_pins") + if not isinstance(gpio_pins, int) or gpio_pins <= 0: + raise ValueError("gpio_pins must be a positive integer") + memory_regions = read_memory_regions(memory_map_path) + return ( + parse_native_targets(document.get("native_targets"), memory_regions), + parse_gpio_functions(document.get("gpio_alt_functions"), gpio_pins), + ) + + +def render_nmi_interfaces(targets: list[NativeTarget]) -> str: + lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."] + lines.extend(f" nmi_if {target.interface} ();" for target in targets) + return "\n".join(lines) + "\n" + + +def render_region_select(region: str, memory_region: dict[str, Any]) -> str: + base = int(require_string(memory_region.get("base"), f"memory map region {region}.base"), 0) + if base == 0: + return f"(nmi.addr <= `SOC_ADDR_{region}_END)" + return f"`SOC_ADDR_IS_{region}(nmi.addr)" + + +def render_nmi_routes( + targets: list[NativeTarget], memory_regions: dict[str, dict[str, Any]] +) -> str: + count = len(targets) + lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."] + lines.extend( + [ + f" logic [{count - 1}:0] s_slv_sel_d, s_slv_sel_q;", + f" logic [{count - 1}:0] s_slv_ready;", + f" logic [31:0] s_slv_rdata [0:{count - 1}];", + "", + ] + ) + for target in targets: + if not target.disabled: + lines.append( + f" logic [{len(target.regions) - 1}:0] s_{target.name}_region_sel;" + ) + lines.append("") + for target in targets: + if target.disabled: + lines.append(f" assign {target.interface}.valid = 1'b0;") + else: + for index, region in enumerate(target.regions): + lines.append( + f" assign s_{target.name}_region_sel[{index}] = " + f"{render_region_select(region, memory_regions[region])};" + ) + lines.append( + f" assign {target.interface}.valid = nmi.valid && (|s_{target.name}_region_sel);" + ) + lines.extend( + [ + f" assign {target.interface}.addr = nmi.addr;", + f" assign {target.interface}.wdata = nmi.wdata;", + f" assign {target.interface}.wstrb = nmi.wstrb;", + f" assign s_slv_sel_d[{target.slot}] = {target.interface}.valid;", + f" assign s_slv_ready[{target.slot}] = {target.interface}.ready;", + f" assign s_slv_rdata[{target.slot}] = {target.interface}.rdata;", + "", + ] + ) + lines.extend( + [ + f" dffr #({count}) u_slv_sel_dffr (", + " clk_i,", + " rst_n_i,", + " s_slv_sel_d,", + " s_slv_sel_q", + " );", + "", + " assign nmi.ready = |(s_slv_sel_q & s_slv_ready);", + "", + " always_comb begin", + " nmi.rdata = '0;", + f" for (int index = 0; index < {count}; index++) begin", + " if (s_slv_sel_q[index]) begin", + " nmi.rdata = nmi.rdata | s_slv_rdata[index];", + " end", + " end", + " end", + ] + ) + return "\n".join(lines) + "\n" + + +def render_gpio_bindings(functions: list[GpioFunction]) -> str: + lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."] + for function in functions: + for mode_name, mode in (("alt0", function.alt0), ("alt1", function.alt1)): + lines.append(f" // GPIO{function.pin} {mode_name}") + lines.extend( + f" assign {input_signal} = u_gpio_if.di_i[{function.pin}];" + for input_signal in mode.inputs + ) + lines.extend( + [ + f" assign u_gpio_if.{mode_name}_do_i[{function.pin}] = {mode.do};", + f" assign u_gpio_if.{mode_name}_oe_i[{function.pin}] = {mode.oe};", + "", + ] + ) + return "\n".join(lines) + "\n" + + +def generate(topology_path: Path, memory_map_path: Path, output_dir: Path) -> None: + targets, gpio_functions = read_topology(topology_path, memory_map_path) + memory_regions = read_memory_regions(memory_map_path) + rtl_dir = output_dir / "rtl" + atomic_write(rtl_dir / "soc_nmi_interfaces.svh", render_nmi_interfaces(targets)) + atomic_write(rtl_dir / "soc_nmi_routes.svh", render_nmi_routes(targets, memory_regions)) + atomic_write(rtl_dir / "soc_gpio_alt_bindings.svh", render_gpio_bindings(gpio_functions)) + atomic_write(output_dir / "soc_topology.fl", f"+incdir+{rtl_dir}\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--map", required=True, type=Path) + parser.add_argument("--memory-map", required=True, type=Path) + parser.add_argument("--output-dir", type=Path) + parser.add_argument("--check", action="store_true", help="validate only; write nothing") + arguments = parser.parse_args() + try: + if arguments.check: + read_topology(arguments.map, arguments.memory_map) + else: + if arguments.output_dir is None: + parser.error("--output-dir is required unless --check is used") + generate(arguments.map, arguments.memory_map, arguments.output_dir) + except (OSError, ValueError, json.JSONDecodeError) as error: + parser.error(str(error)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/rtl/mini/integration/soc_topology.json b/rtl/mini/integration/soc_topology.json new file mode 100644 index 00000000..fb897252 --- /dev/null +++ b/rtl/mini/integration/soc_topology.json @@ -0,0 +1,58 @@ +{ + "schema_version": 1, + "gpio_pins": 32, + "native_targets": [ + {"slot": 0, "name": "uart0", "interface": "u_uart_nmi_if", "regions": ["NMI_UART0"]}, + {"slot": 1, "name": "gpio", "interface": "u_gpio_nmi_if", "regions": ["NMI_GPIO"]}, + {"slot": 2, "name": "tim0", "interface": "u_tim0_nmi_if", "regions": ["NMI_TIM0"]}, + {"slot": 3, "name": "tim1", "interface": "u_tim1_nmi_if", "regions": ["NMI_TIM1"]}, + {"slot": 4, "name": "psram", "interface": "u_psram_nmi_if", "regions": ["NMI_PSRAM", "PSRAM"]}, + {"slot": 5, "name": "spisd", "interface": "u_spisd_nmi_if", "regions": ["NMI_SPISD", "SPISD"]}, + {"slot": 6, "name": "i2c0", "interface": "u_i2c0_nmi_if", "regions": ["NMI_I2C0"]}, + {"slot": 7, "name": "i2s", "interface": "u_i2s_nmi_if", "regions": ["NMI_I2S"]}, + {"slot": 8, "name": "onewire", "interface": "u_onewire_nmi_if", "regions": ["NMI_ONEWIRE"]}, + {"slot": 9, "name": "xpi", "interface": "u_xpi_nmi_if", "regions": ["NMI_XPI", "FLASH", "XPI"]}, + {"slot": 10, "name": "dma", "interface": "u_dma_nmi_if", "regions": ["NMI_DMA"]}, + {"slot": 11, "name": "sysctrl", "interface": "u_sysctrl_nmi_if", "regions": ["NMI_SYSCTRL"]}, + {"slot": 12, "name": "clint", "interface": "u_clint_nmi_if", "regions": ["NMI_CLINT"]}, + {"slot": 13, "name": "sdram", "interface": "u_sdram_nmi_if", "regions": ["NMI_SDRAM", "SDRAM"]}, + {"slot": 14, "name": "dvp", "interface": "u_dvp_nmi_if", "regions": ["NMI_DVP"]}, + {"slot": 15, "name": "sdio", "interface": "u_sdio_nmi_if", "disabled": true}, + {"slot": 16, "name": "opipsram", "interface": "u_opipsram_nmi_if", "disabled": true}, + {"slot": 17, "name": "i2c1", "interface": "u_i2c1_nmi_if", "regions": ["NMI_I2C1"]} + ], + "gpio_alt_functions": [ + {"pin": 0, "alt0": {"inputs": ["u_uart1_if.rx_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_clk_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 1, "alt0": {"inputs": [], "do": "u_uart1_if.tx_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_ps2_if.ps2_dat_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 2, "alt0": {"inputs": ["s_tmr_capch"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": [], "do": "u_onewire_if.dat_o", "oe": "1'b1"}}, + {"pin": 3, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[0]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.scl_i"], "do": "u_i2c1_if.scl_o", "oe": "u_i2c1_if.scl_oe_o"}}, + {"pin": 4, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[1]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.sda_i"], "do": "u_i2c1_if.sda_o", "oe": "u_i2c1_if.sda_oe_o"}}, + {"pin": 5, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[2]", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "clkdiv4_i", "oe": "1'b1"}}, + {"pin": 6, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[3]", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_spisd_if.sck_o", "oe": "1'b1"}}, + {"pin": 7, "alt0": {"inputs": ["u_i2c0_if.scl_i"], "do": "u_i2c0_if.scl_o", "oe": "u_i2c0_if.scl_oe_o"}, "alt1": {"inputs": [], "do": "u_spisd_if.nss_o", "oe": "1'b1"}}, + {"pin": 8, "alt0": {"inputs": ["u_i2c0_if.sda_i"], "do": "u_i2c0_if.sda_o", "oe": "u_i2c0_if.sda_oe_o"}, "alt1": {"inputs": [], "do": "u_spisd_if.mosi_o", "oe": "1'b1"}}, + {"pin": 9, "alt0": {"inputs": [], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_spisd_if.miso_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 10, "alt0": {"inputs": [], "do": "u_i2s_if.mclk_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.pclk_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 11, "alt0": {"inputs": [], "do": "u_i2s_if.sclk_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.href_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 12, "alt0": {"inputs": [], "do": "u_i2s_if.lrck_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.vsync_i"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 13, "alt0": {"inputs": [], "do": "u_i2s_if.dacdat_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.dat_i[0]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 14, "alt0": {"inputs": ["u_i2s_if.adcdat_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_dvp_if.dat_i[1]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 15, "alt0": {"inputs": [], "do": "u_sdio_if.sck_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.dat_i[2]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 16, "alt0": {"inputs": ["u_sdio_if.cmd_di_i"], "do": "u_sdio_if.cmd_do_o", "oe": "u_sdio_if.cmd_oe_o"}, "alt1": {"inputs": ["u_dvp_if.dat_i[3]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 17, "alt0": {"inputs": ["u_sdio_if.dat_di_i[0]"], "do": "u_sdio_if.dat_do_o[0]", "oe": "u_sdio_if.dat_oe_o[0]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[4]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 18, "alt0": {"inputs": ["u_sdio_if.dat_di_i[1]"], "do": "u_sdio_if.dat_do_o[1]", "oe": "u_sdio_if.dat_oe_o[1]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[5]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 19, "alt0": {"inputs": ["u_sdio_if.dat_di_i[2]"], "do": "u_sdio_if.dat_do_o[2]", "oe": "u_sdio_if.dat_oe_o[2]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[6]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 20, "alt0": {"inputs": ["u_sdio_if.dat_di_i[3]"], "do": "u_sdio_if.dat_do_o[3]", "oe": "u_sdio_if.dat_oe_o[3]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[7]"], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 21, "alt0": {"inputs": [], "do": "u_opipsram_if.sck_o", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_psram_if.sck_o", "oe": "1'b1"}}, + {"pin": 22, "alt0": {"inputs": [], "do": "u_opipsram_if.ce_o", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[0]", "oe": "1'b1"}}, + {"pin": 23, "alt0": {"inputs": ["u_opipsram_if.io_di_i[0]"], "do": "u_opipsram_if.io_do_o[0]", "oe": "u_opipsram_if.io_oe_o[0]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[0]"], "do": "u_psram_if.io_do_o[0]", "oe": "u_psram_if.io_oe_o[0]"}}, + {"pin": 24, "alt0": {"inputs": ["u_opipsram_if.io_di_i[1]"], "do": "u_opipsram_if.io_do_o[1]", "oe": "u_opipsram_if.io_oe_o[1]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[1]"], "do": "u_psram_if.io_do_o[1]", "oe": "u_psram_if.io_oe_o[1]"}}, + {"pin": 25, "alt0": {"inputs": ["u_opipsram_if.io_di_i[2]"], "do": "u_opipsram_if.io_do_o[2]", "oe": "u_opipsram_if.io_oe_o[2]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[2]"], "do": "u_psram_if.io_do_o[2]", "oe": "u_psram_if.io_oe_o[2]"}}, + {"pin": 26, "alt0": {"inputs": ["u_opipsram_if.io_di_i[3]"], "do": "u_opipsram_if.io_do_o[3]", "oe": "u_opipsram_if.io_oe_o[3]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[3]"], "do": "u_psram_if.io_do_o[3]", "oe": "u_psram_if.io_oe_o[3]"}}, + {"pin": 27, "alt0": {"inputs": ["u_opipsram_if.io_di_i[4]"], "do": "u_opipsram_if.io_do_o[4]", "oe": "u_opipsram_if.io_oe_o[4]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[1]", "oe": "1'b1"}}, + {"pin": 28, "alt0": {"inputs": ["u_opipsram_if.io_di_i[5]"], "do": "u_opipsram_if.io_do_o[5]", "oe": "u_opipsram_if.io_oe_o[5]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[2]", "oe": "1'b1"}}, + {"pin": 29, "alt0": {"inputs": ["u_opipsram_if.io_di_i[6]"], "do": "u_opipsram_if.io_do_o[6]", "oe": "u_opipsram_if.io_oe_o[6]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[3]", "oe": "1'b1"}}, + {"pin": 30, "alt0": {"inputs": ["u_opipsram_if.io_di_i[7]"], "do": "u_opipsram_if.io_do_o[7]", "oe": "u_opipsram_if.io_oe_o[7]"}, "alt1": {"inputs": [], "do": "1'b0", "oe": "1'b0"}}, + {"pin": 31, "alt0": {"inputs": ["u_opipsram_if.dqs_di_i"], "do": "u_opipsram_if.dqs_do_o", "oe": "u_opipsram_if.dqs_oe_o"}, "alt1": {"inputs": [], "do": "1'b0", "oe": "1'b0"}} + ] +} diff --git a/rtl/mini/mk/verilator.mk b/rtl/mini/mk/verilator.mk index 02b407ce..6672a9cf 100644 --- a/rtl/mini/mk/verilator.mk +++ b/rtl/mini/mk/verilator.mk @@ -27,7 +27,7 @@ VERILATOR_FLAGS += -o $(BUILD_DIR)/emu VERILATOR_FLAGS += -Mdir $(SOC_COMPILE_HOME) VERILATOR_FLAGS += $(SOC_VSRC_INCLPATH) $(SOC_CXXFILES) $(SOC_VXXFILES) -SOC_SIM_TIME ?= 40 +SOC_SIM_TIME ?= 180 VERILATOR_STAMP := $(BUILD_DIR)/verilate.stamp VERILATOR_DEPFILE := $(BUILD_DIR)/verilate.d VERILATOR_EMU := $(BUILD_DIR)/emu diff --git a/rtl/mini/top/ip_nmi_wrapper.sv b/rtl/mini/top/ip_nmi_wrapper.sv index cb3d0380..b80d0ce1 100644 --- a/rtl/mini/top/ip_nmi_wrapper.sv +++ b/rtl/mini/top/ip_nmi_wrapper.sv @@ -5,10 +5,9 @@ // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -// addr range: [31:28]: 4'h1(reg), 4'h4(psram), 4'h5(spisd) `include "mmap_define.svh" module ip_nmi_wrapper ( @@ -17,7 +16,6 @@ module ip_nmi_wrapper ( input logic rst_n_i, input logic clk_aud_i, input logic rst_aud_n_i, - // natv if nmi_if.slave nmi, gpio_if.dut gpio, user_gpio_if.padctrl user_gpio, @@ -40,236 +38,29 @@ module ip_nmi_wrapper ( input logic [31:0] fault_addr_i, input logic [3:0] fault_wstrb_i, input logic fault_reserved_i, - // irq output logic [9:0] irq_o // verilog_format: on ); - // bus interface - nmi_if u_uart_nmi_if (); - nmi_if u_gpio_nmi_if (); - nmi_if u_tim0_nmi_if (); - nmi_if u_tim1_nmi_if (); - nmi_if u_psram_nmi_if (); - nmi_if u_spisd_nmi_if (); - nmi_if u_i2c0_nmi_if (); - nmi_if u_i2s_nmi_if (); - nmi_if u_onewire_nmi_if (); - nmi_if u_xpi_nmi_if (); - nmi_if u_dma_nmi_if (); - nmi_if u_sysctrl_nmi_if (); - nmi_if u_clint_nmi_if (); - nmi_if u_sdram_nmi_if (); - nmi_if u_dvp_nmi_if (); - nmi_if u_sdio_nmi_if (); - nmi_if u_opipsram_nmi_if (); - nmi_if u_i2c1_nmi_if (); + // Generated NMI target declarations preserve scalar-interface compatibility. + `include "soc_nmi_interfaces.svh" - // ip interface - simp_clint_if u_clint_if (); +simp_clint_if u_clint_if (); dma_hw_trg_if u_dma_hw_trg_if (); - // cfg or mem - logic s_psram_cfg_sel, s_psram_mem_sel; - logic s_opipsram_cfg_sel, s_opipsram_mem_sel; - logic s_spisd_cfg_sel; - logic s_sdram_cfg_sel, s_sdram_mem_sel; - logic s_xpi_cfg_sel, s_xpi_mem_sel; - // dma logic s_dma_i2s_tx_stall, s_dma_i2s_rx_stall; logic s_dma_xpi_tx_stall, s_dma_xpi_rx_stall; logic s_dma_xfer_done; - // irq logic s_tim0_irq, s_tim1_irq; - - // dma channel - assign u_dma_hw_trg_if.i2s_tx_proc = ~s_dma_i2s_tx_stall; - assign u_dma_hw_trg_if.i2s_rx_proc = ~s_dma_i2s_rx_stall; + assign u_dma_hw_trg_if.i2s_tx_proc = ~s_dma_i2s_tx_stall; + assign u_dma_hw_trg_if.i2s_rx_proc = ~s_dma_i2s_rx_stall; assign u_dma_hw_trg_if.qspi_tx_proc = ~s_dma_xpi_tx_stall; assign u_dma_hw_trg_if.qspi_rx_proc = ~s_dma_xpi_rx_stall; - // gpio - assign u_gpio_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_GPIO(nmi.addr); - assign u_gpio_nmi_if.addr = nmi.addr; - assign u_gpio_nmi_if.wdata = nmi.wdata; - assign u_gpio_nmi_if.wstrb = nmi.wstrb; - // uart - assign u_uart_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_UART0(nmi.addr); - assign u_uart_nmi_if.addr = nmi.addr; - assign u_uart_nmi_if.wdata = nmi.wdata; - assign u_uart_nmi_if.wstrb = nmi.wstrb; - // tim0 - assign u_tim0_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_TIM0(nmi.addr); - assign u_tim0_nmi_if.addr = nmi.addr; - assign u_tim0_nmi_if.wdata = nmi.wdata; - assign u_tim0_nmi_if.wstrb = nmi.wstrb; - // tim1 - assign u_tim1_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_TIM1(nmi.addr); - assign u_tim1_nmi_if.addr = nmi.addr; - assign u_tim1_nmi_if.wdata = nmi.wdata; - assign u_tim1_nmi_if.wstrb = nmi.wstrb; - // psram - assign s_psram_cfg_sel = `SOC_ADDR_IS_NMI_PSRAM(nmi.addr); - assign s_psram_mem_sel = `SOC_ADDR_IS_PSRAM(nmi.addr); - assign u_psram_nmi_if.valid = nmi.valid && (s_psram_mem_sel || s_psram_cfg_sel); - assign u_psram_nmi_if.addr = nmi.addr; - assign u_psram_nmi_if.wdata = nmi.wdata; - assign u_psram_nmi_if.wstrb = nmi.wstrb; - // spisd - assign s_spisd_cfg_sel = `SOC_ADDR_IS_NMI_SPISD(nmi.addr); - assign u_spisd_nmi_if.valid = nmi.valid && (`SOC_ADDR_IS_SPISD(nmi.addr) || s_spisd_cfg_sel); - assign u_spisd_nmi_if.addr = nmi.addr; - assign u_spisd_nmi_if.wdata = nmi.wdata; - assign u_spisd_nmi_if.wstrb = nmi.wstrb; - // i2c0 - assign u_i2c0_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_I2C0(nmi.addr); - assign u_i2c0_nmi_if.addr = nmi.addr; - assign u_i2c0_nmi_if.wdata = nmi.wdata; - assign u_i2c0_nmi_if.wstrb = nmi.wstrb; - // i2s - assign u_i2s_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_I2S(nmi.addr); - assign u_i2s_nmi_if.addr = nmi.addr; - assign u_i2s_nmi_if.wdata = nmi.wdata; - assign u_i2s_nmi_if.wstrb = nmi.wstrb; - // onewire - assign u_onewire_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_ONEWIRE(nmi.addr); - assign u_onewire_nmi_if.addr = nmi.addr; - assign u_onewire_nmi_if.wdata = nmi.wdata; - assign u_onewire_nmi_if.wstrb = nmi.wstrb; - // xpi - assign s_xpi_cfg_sel = `SOC_ADDR_IS_NMI_XPI(nmi.addr); - assign s_xpi_mem_sel = `SOC_ADDR_IS_FLASH(nmi.addr) || `SOC_ADDR_IS_XPI(nmi.addr); - assign u_xpi_nmi_if.valid = nmi.valid && (s_xpi_cfg_sel || s_xpi_mem_sel); - assign u_xpi_nmi_if.addr = nmi.addr; - assign u_xpi_nmi_if.wdata = nmi.wdata; - assign u_xpi_nmi_if.wstrb = nmi.wstrb; - // dma - assign u_dma_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_DMA(nmi.addr); - assign u_dma_nmi_if.addr = nmi.addr; - assign u_dma_nmi_if.wdata = nmi.wdata; - assign u_dma_nmi_if.wstrb = nmi.wstrb; - // sysctrl - assign u_sysctrl_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_SYSCTRL(nmi.addr); - assign u_sysctrl_nmi_if.addr = nmi.addr; - assign u_sysctrl_nmi_if.wdata = nmi.wdata; - assign u_sysctrl_nmi_if.wstrb = nmi.wstrb; - // clint - assign u_clint_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_CLINT(nmi.addr); - assign u_clint_nmi_if.addr = nmi.addr; - assign u_clint_nmi_if.wdata = nmi.wdata; - assign u_clint_nmi_if.wstrb = nmi.wstrb; - // sdram - assign s_sdram_cfg_sel = `SOC_ADDR_IS_NMI_SDRAM(nmi.addr); - assign s_sdram_mem_sel = `SOC_ADDR_IS_SDRAM(nmi.addr); - assign u_sdram_nmi_if.valid = nmi.valid && (s_sdram_cfg_sel || s_sdram_mem_sel); - assign u_sdram_nmi_if.addr = nmi.addr; - assign u_sdram_nmi_if.wdata = nmi.wdata; - assign u_sdram_nmi_if.wstrb = nmi.wstrb; - // dvp - assign u_dvp_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_DVP(nmi.addr); - assign u_dvp_nmi_if.addr = nmi.addr; - assign u_dvp_nmi_if.wdata = nmi.wdata; - assign u_dvp_nmi_if.wstrb = nmi.wstrb; - // sdio - assign u_sdio_nmi_if.valid = 1'b0; - assign u_sdio_nmi_if.addr = nmi.addr; - assign u_sdio_nmi_if.wdata = nmi.wdata; - assign u_sdio_nmi_if.wstrb = nmi.wstrb; - // opipsram - assign s_opipsram_cfg_sel = 1'b0; - assign s_opipsram_mem_sel = 1'b0; - assign u_opipsram_nmi_if.valid = 1'b0; - assign u_opipsram_nmi_if.addr = nmi.addr; - assign u_opipsram_nmi_if.wdata = nmi.wdata; - assign u_opipsram_nmi_if.wstrb = nmi.wstrb; - // i2c1 - assign u_i2c1_nmi_if.valid = nmi.valid && `SOC_ADDR_IS_NMI_I2C1(nmi.addr); - assign u_i2c1_nmi_if.addr = nmi.addr; - assign u_i2c1_nmi_if.wdata = nmi.wdata; - assign u_i2c1_nmi_if.wstrb = nmi.wstrb; - // verilog_format: off - // Registered one-hot slave select for response mux (breaks comb fanin) - logic [17:0] s_slv_sel_d, s_slv_sel_q; - assign s_slv_sel_d = { u_i2c1_nmi_if.valid, - u_opipsram_nmi_if.valid, - u_sdio_nmi_if.valid, - u_dvp_nmi_if.valid, - u_sdram_nmi_if.valid, - u_clint_nmi_if.valid, - u_sysctrl_nmi_if.valid, - u_dma_nmi_if.valid, - u_xpi_nmi_if.valid, - u_onewire_nmi_if.valid, - u_i2s_nmi_if.valid, - u_i2c0_nmi_if.valid, - u_spisd_nmi_if.valid, - u_psram_nmi_if.valid, - u_tim1_nmi_if.valid, - u_tim0_nmi_if.valid, - u_gpio_nmi_if.valid, - u_uart_nmi_if.valid }; - dffr #(18) u_slv_sel_dffr ( - clk_i, - rst_n_i, - s_slv_sel_d, - s_slv_sel_q - ); + // Uses ClusterIP common nmi_if and register.sv dffr through generated bindings. + `include "soc_nmi_routes.svh" - // Collect per-slave ready signals into a vector - logic [17:0] s_slv_ready; - assign s_slv_ready = { u_i2c1_nmi_if.ready, - u_opipsram_nmi_if.ready, - u_sdio_nmi_if.ready, - u_dvp_nmi_if.ready, - u_sdram_nmi_if.ready, - u_clint_nmi_if.ready, - u_sysctrl_nmi_if.ready, - u_dma_nmi_if.ready, - u_xpi_nmi_if.ready, - u_onewire_nmi_if.ready, - u_i2s_nmi_if.ready, - u_i2c0_nmi_if.ready, - u_spisd_nmi_if.ready, - u_psram_nmi_if.ready, - u_tim1_nmi_if.ready, - u_tim0_nmi_if.ready, - u_gpio_nmi_if.ready, - u_uart_nmi_if.ready }; - - assign nmi.ready = |(s_slv_sel_q & s_slv_ready); - - // Registered select mux for rdata (reduces fanin from 18 to a clean one-hot mux) - logic [31:0] s_slv_rdata [0:17]; - assign s_slv_rdata[0] = u_uart_nmi_if.rdata; - assign s_slv_rdata[1] = u_gpio_nmi_if.rdata; - assign s_slv_rdata[2] = u_tim0_nmi_if.rdata; - assign s_slv_rdata[3] = u_tim1_nmi_if.rdata; - assign s_slv_rdata[4] = u_psram_nmi_if.rdata; - assign s_slv_rdata[5] = u_spisd_nmi_if.rdata; - assign s_slv_rdata[6] = u_i2c0_nmi_if.rdata; - assign s_slv_rdata[7] = u_i2s_nmi_if.rdata; - assign s_slv_rdata[8] = u_onewire_nmi_if.rdata; - assign s_slv_rdata[9] = u_xpi_nmi_if.rdata; - assign s_slv_rdata[10] = u_dma_nmi_if.rdata; - assign s_slv_rdata[11] = u_sysctrl_nmi_if.rdata; - assign s_slv_rdata[12] = u_clint_nmi_if.rdata; - assign s_slv_rdata[13] = u_sdram_nmi_if.rdata; - assign s_slv_rdata[14] = u_dvp_nmi_if.rdata; - assign s_slv_rdata[15] = u_sdio_nmi_if.rdata; - assign s_slv_rdata[16] = u_opipsram_nmi_if.rdata; - assign s_slv_rdata[17] = u_i2c1_nmi_if.rdata; - - // One-hot mux using registered slave select - always_comb begin - nmi.rdata = '0; - for (int i = 0; i < 18; i++) begin - if (s_slv_sel_q[i]) nmi.rdata = nmi.rdata | s_slv_rdata[i]; - end - end - // verilog_format: on - - // irq assign irq_o[0] = u_clint_if.sfr_irq_o; assign irq_o[1] = u_clint_if.tmr_irq_o; assign irq_o[2] = uart.irq_o; @@ -281,7 +72,6 @@ module ip_nmi_wrapper ( assign irq_o[8] = i2s.irq_o; assign irq_o[9] = xpi.irq_o; - nmi_gpio u_nmi_gpio ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -290,7 +80,6 @@ module ip_nmi_wrapper ( .user_gpio(user_gpio) ); - nmi_uart u_nmi_uart ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -298,7 +87,6 @@ module ip_nmi_wrapper ( .uart (uart) ); - nmi_timer u_nmi_timer0 ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -306,7 +94,6 @@ module ip_nmi_wrapper ( .irq_o (s_tim0_irq) ); - nmi_timer u_nmi_timer1 ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -314,7 +101,6 @@ module ip_nmi_wrapper ( .irq_o (s_tim1_irq) ); - nmi_psram u_nmi_psram ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -322,7 +108,6 @@ module ip_nmi_wrapper ( .psram (psram) ); - nmi_spisd u_nmi_spisd ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -330,7 +115,6 @@ module ip_nmi_wrapper ( .spi (spisd) ); - nmi_i2c u_nmi_i2c0 ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -338,7 +122,6 @@ module ip_nmi_wrapper ( .i2c (i2c0) ); - nmi_i2s u_nmi_i2s ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -350,7 +133,6 @@ module ip_nmi_wrapper ( .i2s (i2s) ); - nmi_onewire u_nmi_onewire ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -396,7 +178,6 @@ module ip_nmi_wrapper ( .clint (u_clint_if) ); - nmi_sdram u_nmi_sdram ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -404,7 +185,6 @@ module ip_nmi_wrapper ( .sdram (sdram) ); - nmi_dvp u_nmi_dvp ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -412,7 +192,6 @@ module ip_nmi_wrapper ( .dvp (dvp) ); - nmi_sdio u_nmi_sdio ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -420,7 +199,6 @@ module ip_nmi_wrapper ( .sdio (sdio) ); - nmi_opipsram u_nmi_opipsram ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -428,7 +206,6 @@ module ip_nmi_wrapper ( .psram (opipsram) ); - nmi_i2c u_nmi_i2c1 ( .clk_i (clk_i), .rst_n_i(rst_n_i), diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index 53072fc0..ac543536 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -5,7 +5,7 @@ // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. `include "mmap_define.svh" @@ -32,7 +32,6 @@ module retrosoc ( // verilog_format: on ); - // verilog_format: off // bus interface nmi_if u_core_nmi_if (); @@ -82,286 +81,14 @@ module retrosoc ( assign u_sysctrl_if.core_sel_i = '0; `endif - // irq - assign s_irq[9:0] = s_nmi_irq; - assign s_irq[16:10] = s_apb_irq; - assign s_irq[31:17] = 15'd0; + assign s_irq[9:0] = s_nmi_irq; + assign s_irq[16:10] = s_apb_irq; + assign s_irq[31:17] = 15'd0; - // bind io - // TODO: update & double check - // ===================================================== - // | GPIO0 | FUNC0 | FUNC1 | - // ===================================================== - // | 0 | uart1_rx_i_pad | ps2_clk_i_pad | - // | 1 | uart1_tx_o_pad | ps2_dat_i_pad | - // | 2 | tmr_capch_i_pad | onewire_dat_o_pad | - // | 3 | pwm_0_o_pad | i2c1_scl_io_pad | - // | 4 | pwm_1_o_pad | i2c1_sda_io_pad | - // | 5 | pwm_2_o_pad | sys_clkdiv4_o_pad | - // | 6 | pwm_3_o_pad | spisd_sck_pad | - // | 7 | i2c0_scl_io_pad | spisd_nss_pad | - // | 8 | i2c0_sda_io_pad | spisd_mosi_pad | - // | 9 | __NONE_FUNC__ | spisd_miso_pad | - // | 10 | i2s_0_mclk_o_pad | dvp_pclk_i_pad | - // | 11 | i2s_0_sck_io_pad | dvp_href_i_pad | - // | 12 | i2s_lrck_o_pad | dvp_vsync_i_pad | - // | 13 | i2s_dacdat_o_pad | dvp_dat0_i_pad | - // | 14 | i2s_adcdat_i_pad | dvp_dat1_i_pad | - // | 15 | sdio_sck_o_pad | dvp_dat2_i_pad | - // | 16 | sdio_cmd_o_pad | dvp_dat3_i_pad | - // | 17 | sdio_dat0_io_pad | dvp_dat4_i_pad | - // | 18 | sdio_dat1_io_pad | dvp_dat5_i_pad | - // | 19 | sdio_dat2_io_pad | dvp_dat6_i_pad | - // | 20 | sdio_dat3_io_pad | dvp_dat7_i_pad | - // | 21 | opipsram_sck_o_pad | psram_sck_o_pad | - // | 22 | opipsram_ce_o_pad | psram_ce0_o_pad | - // | 23 | opipsram_dat0_io_pad | psram_dat0_io_pad | - // | 24 | opipsram_dat1_io_pad | psram_dat1_io_pad | - // | 25 | opipsram_dat2_io_pad | psram_dat2_io_pad | - // | 26 | opipsram_dat3_io_pad | psram_dat3_io_pad | - // | 27 | opipsram_dat4_io_pad | psram_ce1_o_pad | - // | 28 | opipsram_dat5_io_pad | psram_ce2_o_pad | - // | 29 | opipsram_dat6_io_pad | psram_ce3_o_pad | - // | 30 | opipsram_dat7_io_pad | __NONE_FUNC__ | - // | 31 | opipsram_dqs_o_pad | __NONE_FUNC__ | - // ===================================================== - // GPIO0 FUNC0 - // pad0 - assign u_uart1_if.rx_i = u_gpio_if.di_i[0]; - assign u_gpio_if.alt0_do_i[0] = '0; - assign u_gpio_if.alt0_oe_i[0] = '0; - // pad1 - assign u_gpio_if.alt0_do_i[1] = u_uart1_if.tx_o; - assign u_gpio_if.alt0_oe_i[1] = '1; - // pad2 - assign s_tmr_capch = u_gpio_if.di_i[2]; - assign u_gpio_if.alt0_do_i[2] = '0; - assign u_gpio_if.alt0_oe_i[2] = '0; - // pad3 - assign u_gpio_if.alt0_do_i[3] = u_pwm_if.do_o[0]; - assign u_gpio_if.alt0_oe_i[3] = '1; - // pad4 - assign u_gpio_if.alt0_do_i[4] = u_pwm_if.do_o[1]; - assign u_gpio_if.alt0_oe_i[4] = '1; - // pad5 - assign u_gpio_if.alt0_do_i[5] = u_pwm_if.do_o[2]; - assign u_gpio_if.alt0_oe_i[5] = '1; - // pad6 - assign u_gpio_if.alt0_do_i[6] = u_pwm_if.do_o[3]; - assign u_gpio_if.alt0_oe_i[6] = '1; - // pad7 - assign u_i2c0_if.scl_i = u_gpio_if.di_i[7]; - assign u_gpio_if.alt0_do_i[7] = u_i2c0_if.scl_o; - assign u_gpio_if.alt0_oe_i[7] = u_i2c0_if.scl_oe_o; - // pad8 - assign u_i2c0_if.sda_i = u_gpio_if.di_i[8]; - assign u_gpio_if.alt0_do_i[8] = u_i2c0_if.sda_o; - assign u_gpio_if.alt0_oe_i[8] = u_i2c0_if.sda_oe_o; - // pad9 - assign u_gpio_if.alt0_do_i[9] = '0; - assign u_gpio_if.alt0_oe_i[9] = '0; - // pad10 - assign u_gpio_if.alt0_do_i[10] = u_i2s_if.mclk_o; - assign u_gpio_if.alt0_oe_i[10] = '1; - // pad11 - assign u_gpio_if.alt0_do_i[11] = u_i2s_if.sclk_o; - assign u_gpio_if.alt0_oe_i[11] = '1; - // pad12 - assign u_gpio_if.alt0_do_i[12] = u_i2s_if.lrck_o; - assign u_gpio_if.alt0_oe_i[12] = '1; - // pad13 - assign u_gpio_if.alt0_do_i[13] = u_i2s_if.dacdat_o; - assign u_gpio_if.alt0_oe_i[13] = '1; - // pad14 - assign u_i2s_if.adcdat_i = u_gpio_if.di_i[14]; - assign u_gpio_if.alt0_do_i[14] = '0; - assign u_gpio_if.alt0_oe_i[14] = '0; - // pad15 - assign u_gpio_if.alt0_do_i[15] = u_sdio_if.sck_o; - assign u_gpio_if.alt0_oe_i[15] = '1; - // pad16 - assign u_sdio_if.cmd_di_i = u_gpio_if.di_i[16]; - assign u_gpio_if.alt0_do_i[16] = u_sdio_if.cmd_do_o; - assign u_gpio_if.alt0_oe_i[16] = u_sdio_if.cmd_oe_o; - // pad17 - assign u_sdio_if.dat_di_i[0] = u_gpio_if.di_i[17]; - assign u_gpio_if.alt0_do_i[17] = u_sdio_if.dat_do_o[0]; - assign u_gpio_if.alt0_oe_i[17] = u_sdio_if.dat_oe_o[0]; - // pad18 - assign u_sdio_if.dat_di_i[1] = u_gpio_if.di_i[18]; - assign u_gpio_if.alt0_do_i[18] = u_sdio_if.dat_do_o[1]; - assign u_gpio_if.alt0_oe_i[18] = u_sdio_if.dat_oe_o[1]; - // pad19 - assign u_sdio_if.dat_di_i[2] = u_gpio_if.di_i[19]; - assign u_gpio_if.alt0_do_i[19] = u_sdio_if.dat_do_o[2]; - assign u_gpio_if.alt0_oe_i[19] = u_sdio_if.dat_oe_o[2]; - // pad20 - assign u_sdio_if.dat_di_i[3] = u_gpio_if.di_i[20]; - assign u_gpio_if.alt0_do_i[20] = u_sdio_if.dat_do_o[3]; - assign u_gpio_if.alt0_oe_i[20] = u_sdio_if.dat_oe_o[3]; - // pad21 - assign u_gpio_if.alt0_do_i[21] = u_opipsram_if.sck_o; - assign u_gpio_if.alt0_oe_i[21] = '1; - // pad22 - assign u_gpio_if.alt0_do_i[22] = u_opipsram_if.ce_o; - assign u_gpio_if.alt0_oe_i[22] = '1; - // pad23 - assign u_opipsram_if.io_di_i[0] = u_gpio_if.di_i[23]; - assign u_gpio_if.alt0_do_i[23] = u_opipsram_if.io_do_o[0]; - assign u_gpio_if.alt0_oe_i[23] = u_opipsram_if.io_oe_o[0]; - // pad24 - assign u_opipsram_if.io_di_i[1] = u_gpio_if.di_i[24]; - assign u_gpio_if.alt0_do_i[24] = u_opipsram_if.io_do_o[1]; - assign u_gpio_if.alt0_oe_i[24] = u_opipsram_if.io_oe_o[1]; - // pad25 - assign u_opipsram_if.io_di_i[2] = u_gpio_if.di_i[25]; - assign u_gpio_if.alt0_do_i[25] = u_opipsram_if.io_do_o[2]; - assign u_gpio_if.alt0_oe_i[25] = u_opipsram_if.io_oe_o[2]; - // pad26 - assign u_opipsram_if.io_di_i[3] = u_gpio_if.di_i[26]; - assign u_gpio_if.alt0_do_i[26] = u_opipsram_if.io_do_o[3]; - assign u_gpio_if.alt0_oe_i[26] = u_opipsram_if.io_oe_o[3]; - // pad27 - assign u_opipsram_if.io_di_i[4] = u_gpio_if.di_i[27]; - assign u_gpio_if.alt0_do_i[27] = u_opipsram_if.io_do_o[4]; - assign u_gpio_if.alt0_oe_i[27] = u_opipsram_if.io_oe_o[4]; - // pad28 - assign u_opipsram_if.io_di_i[5] = u_gpio_if.di_i[28]; - assign u_gpio_if.alt0_do_i[28] = u_opipsram_if.io_do_o[5]; - assign u_gpio_if.alt0_oe_i[28] = u_opipsram_if.io_oe_o[5]; - // pad29 - assign u_opipsram_if.io_di_i[6] = u_gpio_if.di_i[29]; - assign u_gpio_if.alt0_do_i[29] = u_opipsram_if.io_do_o[6]; - assign u_gpio_if.alt0_oe_i[29] = u_opipsram_if.io_oe_o[6]; - // pad30 - assign u_opipsram_if.io_di_i[7] = u_gpio_if.di_i[30]; - assign u_gpio_if.alt0_do_i[30] = u_opipsram_if.io_do_o[7]; - assign u_gpio_if.alt0_oe_i[30] = u_opipsram_if.io_oe_o[7]; - // pad31 - assign u_opipsram_if.dqs_di_i = u_gpio_if.di_i[31]; - assign u_gpio_if.alt0_do_i[31] = u_opipsram_if.dqs_do_o; - assign u_gpio_if.alt0_oe_i[31] = u_opipsram_if.dqs_oe_o; + // Generated GPIO alternate-function wiring is checked against soc_topology.json. + `include "soc_gpio_alt_bindings.svh" - // GPIO0 FUNC1 - // pad0 - assign u_ps2_if.ps2_clk_i = u_gpio_if.di_i[0]; - assign u_gpio_if.alt1_do_i[0] = '0; - assign u_gpio_if.alt1_oe_i[0] = '0; - // pad1 - assign u_ps2_if.ps2_dat_i = u_gpio_if.di_i[1]; - assign u_gpio_if.alt1_do_i[1] = '0; - assign u_gpio_if.alt1_oe_i[1] = '0; - // pad2 - assign u_gpio_if.alt1_do_i[2] = u_onewire_if.dat_o; - assign u_gpio_if.alt1_oe_i[2] = '1; - // pad3 - assign u_i2c1_if.scl_i = u_gpio_if.di_i[3]; - assign u_gpio_if.alt1_do_i[3] = u_i2c1_if.scl_o; - assign u_gpio_if.alt1_oe_i[3] = u_i2c1_if.scl_oe_o; - // pad4 - assign u_i2c1_if.sda_i = u_gpio_if.di_i[4]; - assign u_gpio_if.alt1_do_i[4] = u_i2c1_if.sda_o; - assign u_gpio_if.alt1_oe_i[4] = u_i2c1_if.sda_oe_o; - // pad5 - assign u_gpio_if.alt1_do_i[5] = clkdiv4_i; - assign u_gpio_if.alt1_oe_i[5] = '1; - // pad6 - assign u_gpio_if.alt1_do_i[6] = u_spisd_if.sck_o; - assign u_gpio_if.alt1_oe_i[6] = '1; - // pad7 - assign u_gpio_if.alt1_do_i[7] = u_spisd_if.nss_o; - assign u_gpio_if.alt1_oe_i[7] = '1; - // pad8 - assign u_gpio_if.alt1_do_i[8] = u_spisd_if.mosi_o; - assign u_gpio_if.alt1_oe_i[8] = '1; - // pad9 - assign u_spisd_if.miso_i = u_gpio_if.di_i[9]; - assign u_gpio_if.alt1_do_i[9] = '0; - assign u_gpio_if.alt1_oe_i[9] = '0; - // pad10 - assign u_dvp_if.pclk_i = u_gpio_if.di_i[10]; - assign u_gpio_if.alt1_do_i[10] = '0; - assign u_gpio_if.alt1_oe_i[10] = '0; - // pad11 - assign u_dvp_if.href_i = u_gpio_if.di_i[11]; - assign u_gpio_if.alt1_do_i[11] = '0; - assign u_gpio_if.alt1_oe_i[11] = '0; - // pad12 - assign u_dvp_if.vsync_i = u_gpio_if.di_i[12]; - assign u_gpio_if.alt1_do_i[12] = '0; - assign u_gpio_if.alt1_oe_i[12] = '0; - // pad13 - assign u_dvp_if.dat_i[0] = u_gpio_if.di_i[13]; - assign u_gpio_if.alt1_do_i[13] = '0; - assign u_gpio_if.alt1_oe_i[13] = '0; - // pad14 - assign u_dvp_if.dat_i[1] = u_gpio_if.di_i[14]; - assign u_gpio_if.alt1_do_i[14] = '0; - assign u_gpio_if.alt1_oe_i[14] = '0; - // pad15 - assign u_dvp_if.dat_i[2] = u_gpio_if.di_i[15]; - assign u_gpio_if.alt1_do_i[15] = '0; - assign u_gpio_if.alt1_oe_i[15] = '0; - // pad16 - assign u_dvp_if.dat_i[3] = u_gpio_if.di_i[16]; - assign u_gpio_if.alt1_do_i[16] = '0; - assign u_gpio_if.alt1_oe_i[16] = '0; - // pad17 - assign u_dvp_if.dat_i[4] = u_gpio_if.di_i[17]; - assign u_gpio_if.alt1_do_i[17] = '0; - assign u_gpio_if.alt1_oe_i[17] = '0; - // pad18 - assign u_dvp_if.dat_i[5] = u_gpio_if.di_i[18]; - assign u_gpio_if.alt1_do_i[18] = '0; - assign u_gpio_if.alt1_oe_i[18] = '0; - // pad19 - assign u_dvp_if.dat_i[6] = u_gpio_if.di_i[19]; - assign u_gpio_if.alt1_do_i[19] = '0; - assign u_gpio_if.alt1_oe_i[19] = '0; - // pad20 - assign u_dvp_if.dat_i[7] = u_gpio_if.di_i[20]; - assign u_gpio_if.alt1_do_i[20] = '0; - assign u_gpio_if.alt1_oe_i[20] = '0; - // pad21 - assign u_gpio_if.alt1_do_i[21] = u_psram_if.sck_o; - assign u_gpio_if.alt1_oe_i[21] = '1; - // pad22 - assign u_gpio_if.alt1_do_i[22] = u_psram_if.nss_o[0]; - assign u_gpio_if.alt1_oe_i[22] = '1; - // pad23 - assign u_psram_if.io_di_i[0] = u_gpio_if.di_i[23]; - assign u_gpio_if.alt1_do_i[23] = u_psram_if.io_do_o[0]; - assign u_gpio_if.alt1_oe_i[23] = u_psram_if.io_oe_o[0]; - // pad24 - assign u_psram_if.io_di_i[1] = u_gpio_if.di_i[24]; - assign u_gpio_if.alt1_do_i[24] = u_psram_if.io_do_o[1]; - assign u_gpio_if.alt1_oe_i[24] = u_psram_if.io_oe_o[1]; - // pad25 - assign u_psram_if.io_di_i[2] = u_gpio_if.di_i[25]; - assign u_gpio_if.alt1_do_i[25] = u_psram_if.io_do_o[2]; - assign u_gpio_if.alt1_oe_i[25] = u_psram_if.io_oe_o[2]; - // pad26 - assign u_psram_if.io_di_i[3] = u_gpio_if.di_i[26]; - assign u_gpio_if.alt1_do_i[26] = u_psram_if.io_do_o[3]; - assign u_gpio_if.alt1_oe_i[26] = u_psram_if.io_oe_o[3]; - // pad27 - assign u_gpio_if.alt1_do_i[27] = u_psram_if.nss_o[1]; - assign u_gpio_if.alt1_oe_i[27] = '1; - // pad28 - assign u_gpio_if.alt1_do_i[28] = u_psram_if.nss_o[2]; - assign u_gpio_if.alt1_oe_i[28] = '1; - // pad29 - assign u_gpio_if.alt1_do_i[29] = u_psram_if.nss_o[3]; - assign u_gpio_if.alt1_oe_i[29] = '1; - // pad30 - assign u_gpio_if.alt1_do_i[30] = '0; - assign u_gpio_if.alt1_oe_i[30] = '0; - // pad31 - assign u_gpio_if.alt1_do_i[31] = '0; - assign u_gpio_if.alt1_oe_i[31] = '0; - - - core_wrapper u_core_wrapper ( +core_wrapper u_core_wrapper ( .clk_i (clk_i), .rst_n_i (rst_n_i), `ifdef CORE_MDD @@ -371,17 +98,14 @@ module retrosoc ( .irq_i (s_irq) ); - bus u_bus ( .clk_i (clk_i), .rst_n_i (rst_n_i), `ifdef HAVE_SRAM_IF .ram (ram), `endif - // master .core_nmi (u_core_nmi_if), .dma_nmi (u_dma_nmi_if), - // slave .natv_nmi (u_nmi_nmi_if), .apb_nmi (u_apb_nmi_if), .fault_valid_o (s_bus_fault_valid), @@ -390,7 +114,6 @@ module retrosoc ( .fault_reserved_o(s_bus_fault_reserved) ); - ip_nmi_wrapper u_ip_nmi_wrapper ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -421,7 +144,6 @@ module retrosoc ( .irq_o (s_nmi_irq) ); - ip_apb_wrapper u_ip_apb_wrapper ( .clk_i (clk_i), .rst_n_i (rst_n_i), diff --git a/scripts/regress.py b/scripts/regress.py index 08e0f288..bf20d4c3 100644 --- a/scripts/regress.py +++ b/scripts/regress.py @@ -15,30 +15,17 @@ sys.path.insert(0, str(ROOT)) from scripts.check_c_warnings import self_owned_warnings # noqa: E402 -MDD_VERILATOR_SIM_TIME = 180 - - PR_COMMANDS = ( ("configs/ci/hazard3-rv32im-ihp130.mk", ("firmware",)), ("configs/ci/hazard3-rv32im-ihp130-shell.mk", ("firmware",)), ("configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk", ("firmware",)), ( "configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk", - ( - "SIMU=VERILATOR", - f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}", - "firmware", - "sim", - ), + ("SIMU=VERILATOR", "firmware", "sim"), ), ( "configs/ci/mdd-rv32im-ihp130.mk", - ( - "SIMU=VERILATOR", - f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}", - "firmware", - "sim", - ), + ("SIMU=VERILATOR", "firmware", "sim"), ), ("configs/ci/hazard3-rv32im-ihp130.mk", ("SIMU=VERILATOR", "sim")), ( diff --git a/tests/rtl/soc_topology_nmi_tb.sv b/tests/rtl/soc_topology_nmi_tb.sv new file mode 100644 index 00000000..287f309a --- /dev/null +++ b/tests/rtl/soc_topology_nmi_tb.sv @@ -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 diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index d102c807..9e0afb09 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -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: diff --git a/tests/test_soc_topology.py b/tests/test_soc_topology.py new file mode 100644 index 00000000..aa58070b --- /dev/null +++ b/tests/test_soc_topology.py @@ -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