mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
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:
14 files changed
+810
-543
No files matched your search
@@ -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 .
|
||||
|
||||
|
||||
@@ -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' \
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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())
|
||||
@@ -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"}}
|
||||
]
|
||||
}
|
||||
@@ -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,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
@@ -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
@@ -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")),
|
||||
(
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user