mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat(spi): implement DMA V2.2 paced FIFO integration
Add five-bit SPI DMA request support, capability discovery, source-qualified pacing, descriptor cancellation guards, and focused P2 evidence. Keep Tiny and Mini product instances default-disabled until product integration. Validation: focused SPI-P2, DMA parity, SPI parity, and SPI-P1 checks passed (53 passed, 10 deselected). Full product integration, post-edit full Pytest, and physical qualification remain pending.
This commit is contained in:
28 files changed
+884
-162
No files matched your search
@@ -32,6 +32,10 @@ and simulation verdicts under the selected Tiny build variant. It does not
|
||||
instantiate SPI in Tiny or implement central-DMA pacing. See the
|
||||
[SPI-P1 runbook](../docs/ip/spi-p1.md) for scope and pending qualification.
|
||||
|
||||
`spi_p2.py` runs the focused V2.2 credit-pacing fixture and lint boundary.
|
||||
The product DMA remains V2.0/default-disabled until shared integration is
|
||||
qualified; this entrypoint does not wire Tiny SPI.
|
||||
|
||||
`generate_apu_kws_rtl_constants.py` also emits the frozen APU-P9 APUC image
|
||||
and 15-bank layout manifest. `apu_kws_coeff.py` owns the independent APUC 1.0
|
||||
schema and release validator; generated assets remain below the selected
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ WRAPPER = ROOT / f"physical/smoke/syn/yosys/{TOP}.sv"
|
||||
SOURCES = [COMMON / "interface/apb4_if.sv", COMMON / "utils/register.sv",
|
||||
ROOT / "rtl/ip/util/soc_common_fifo.sv",
|
||||
*[IP / f"{name}.sv" for name in (
|
||||
"spi_pkg", "spi_ctrl_if", "spi_gpio_guard", "spi_phase_ctrl",
|
||||
"spi_pkg", "spi_ctrl_if", "spi_gpio_guard", "spi_phase_ctrl", "spi_dma_pacing",
|
||||
"spi_word_packer", "spi_master_core", "spi_reg", "apb4_spi")], WRAPPER]
|
||||
INCLUDES = [COMMON, COMMON / "utils", IP, ROOT / "tests/rtl"]
|
||||
CASES = ("spi_pin_checker_tb", "spi_core_tb", "spi_apb_tb", "spi_lifecycle_tb", "spi_packer_tb")
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Focused SPI-P2 DMA V2.2 evidence driver; product integration stays disabled."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
SOURCES = [ROOT / "rtl/ip/serial/spi_dma_pacing.sv",
|
||||
ROOT / "rtl/ip/peripheral/dma_define.svh", ROOT / "rtl/ip/peripheral/dma_pkg.sv",
|
||||
ROOT / "rtl/ip/peripheral/dma_core.sv", ROOT / "rtl/ip/peripheral/dma_reg.sv",
|
||||
ROOT / "rtl/ip/peripheral/apb4_dma.sv", ROOT / "tests/rtl/spi_dma_pacing_tb.sv",
|
||||
ROOT / "tests/test_spi_p2.py", ROOT / "scripts/spi_p2.py",
|
||||
ROOT / "crt/include/retrosoc/hal/dma.h",
|
||||
ROOT / "crt/include/retrosoc/hal/dma_regs.h",
|
||||
ROOT / "crt/src/hal/dma.c", ROOT / "crt/src/hal/dma_math.c",
|
||||
ROOT / "rtl/mini/formal/dma_formal.sv",
|
||||
ROOT / "rtl/mini/formal/dma_formal_props.sv",
|
||||
ROOT / "rtl/mini/formal/dma_tcd_cancel_props.sv"]
|
||||
PACING_TB = ROOT / "tests/rtl/spi_dma_pacing_tb.sv"
|
||||
|
||||
def digest(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("stage", choices=("lint", "rtl", "report"))
|
||||
parser.add_argument("--variant-root", type=Path, required=True)
|
||||
parser.add_argument("--pdk", choices=("ICS55", "IHP130"), required=True)
|
||||
args = parser.parse_args()
|
||||
variant = args.variant_root.resolve()
|
||||
variant.relative_to(ROOT / "build")
|
||||
manifest = json.loads((variant / "meta/manifest.json").read_text())
|
||||
if manifest["configuration"]["SOC"] != "TINY" or manifest["configuration"]["PDK"] != args.pdk:
|
||||
raise ValueError("profile/PDK mismatch")
|
||||
directory = variant / "meta/spi-p2" / args.stage
|
||||
if directory.exists():
|
||||
raise ValueError(f"existing SPI-P2 attempt: {directory}")
|
||||
directory.mkdir(parents=True)
|
||||
source = {str(path.relative_to(ROOT)): digest(path) for path in SOURCES}
|
||||
record = {"phase": "SPI-P2", "stage": args.stage, "status": "running",
|
||||
"source": source, "manifest": manifest, "phase_accepted": False}
|
||||
try:
|
||||
common = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
if args.stage == "rtl":
|
||||
command = ["iverilog", "-g2012", "-s", "spi_dma_pacing_tb", "-o",
|
||||
str(directory / "spi_dma_pacing_tb"), f"-I{common}", f"-I{common / 'utils'}",
|
||||
str(common / "utils/register.sv"), str(SOURCES[0]), str(PACING_TB)]
|
||||
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, check=False)
|
||||
(directory / "compile.log").write_text(result.stdout + result.stderr)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError("SPI-P2 pacing compile failed")
|
||||
run = subprocess.run(["vvp", str(directory / "spi_dma_pacing_tb")], cwd=ROOT,
|
||||
capture_output=True, text=True, check=False)
|
||||
(directory / "sim.log").write_text(run.stdout + run.stderr)
|
||||
if run.returncode != 0 or "SPI_P2_PASS spi_dma_pacing_tb" not in run.stdout:
|
||||
raise RuntimeError("SPI-P2 pacing fixture failed")
|
||||
elif args.stage == "lint":
|
||||
result = subprocess.run(["verilator", "--lint-only", "--Wall", "-Wno-fatal",
|
||||
"-Wno-DECLFILENAME", "--top-module",
|
||||
"spi_dma_pacing", f"-I{common}", str(common / "utils/register.sv"),
|
||||
str(SOURCES[0])], cwd=ROOT,
|
||||
capture_output=True, text=True, check=False)
|
||||
(directory / "lint.log").write_text(result.stdout + result.stderr)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError("SPI-P2 pacing lint failed")
|
||||
record["status"] = "passed"
|
||||
except (OSError, RuntimeError) as error:
|
||||
record.update(status="failed", error=str(error))
|
||||
print(error, file=sys.stderr)
|
||||
return 1
|
||||
record["artifacts"] = {str(path): digest(path) for path in directory.rglob("*") if path.is_file()}
|
||||
atomic_write(directory / "record.json", json.dumps(record, indent=2, sort_keys=True) + "\n")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user