feat: refactor SoC topology and NMI integration

- Updated the  file to improve code readability and maintainability by removing commented-out code and adding generated GPIO alternate-function wiring.
- Modified  to streamline simulation command configurations and removed unnecessary variables.
- Introduced a new testbench  to validate NMI routing functionality within the SoC topology.
- Added comprehensive tests in  to ensure the correctness of generated bindings and validate topology constraints.
- Adjusted  to reflect changes in regression command imports and ensure uniform timeout settings for Verilator simulations.
This commit is contained in:
maksyuki committed 2026-07-23 12:07:45 +08:00
1 parent 235ad7224e
commit d886cd992c
14 files changed
+810 -543

No files matched your search

+3
View File
@@ -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 .
+3 -1
View File
@@ -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' \
+3
View File
@@ -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
+20 -3
View File
@@ -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
.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map \
soc-topology check-soc-topology firmware
+27
View File
@@ -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/<variant>/generated/soc_topology/` and
must not be committed.
@@ -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())
+58
View File
@@ -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"}}
]
}
+1 -1
View File
@@ -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
+5 -228
View File
@@ -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 ();
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.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),
+3 -281
View File
@@ -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,284 +81,12 @@ 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;
// 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;
// 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;
// Generated GPIO alternate-function wiring is checked against soc_topology.json.
`include "soc_gpio_alt_bindings.svh"
core_wrapper u_core_wrapper (
.clk_i (clk_i),
@@ -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),
+2 -15
View File
@@ -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")),
(
+112
View File
@@ -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
+8 -14
View File
@@ -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:
+191
View File
@@ -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