Compare commits
9 Commits
dev/system
...
copilot/su
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3191ab3848 | ||
|
|
227df5ad05 | ||
|
|
e95069019a | ||
|
|
b66681f46a | ||
|
|
b942c2e2d2 | ||
|
|
6bb4f08ca4 | ||
|
|
9bf1c3e944 | ||
|
|
fdac38133c | ||
|
|
bbbeab85c5 |
@@ -1,22 +0,0 @@
|
||||
FROM verilator/verilator:latest
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
python3-venv \
|
||||
python3-pip \
|
||||
python3-dev \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
git \
|
||||
curl \
|
||||
ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV UV_INSTALL_DIR=/usr/local/bin
|
||||
ENV UV_LINK_MODE=copy
|
||||
# Install uv globally so both VS Code and terminals can use it
|
||||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
RUN uv --version
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "PeakRDL BusDecoder",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"runArgs": [
|
||||
"--init"
|
||||
],
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/common-utils:2": {
|
||||
"username": "vscode",
|
||||
"uid": "1000",
|
||||
"gid": "1000",
|
||||
"installZsh": "false",
|
||||
"installOhMyZsh": "false"
|
||||
}
|
||||
},
|
||||
"remoteUser": "vscode",
|
||||
"postCreateCommand": "uv sync --frozen --all-extras --group tools --group test",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"python.defaultInterpreterPath": ".venv/bin/python",
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-vscode.cpptools",
|
||||
"charliermarsh.ruff",
|
||||
"astral-sh.ty",
|
||||
"meta.pyrefly"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
@@ -14,13 +14,11 @@ on:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: verilator/verilator:latest
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -29,21 +27,19 @@ jobs:
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: true
|
||||
|
||||
- name: Check Verilator version
|
||||
run: verilator --version
|
||||
|
||||
- name: Install Python development packages
|
||||
- name: Install Verilator
|
||||
run: |
|
||||
apt-get update && apt-get install -y python3-dev libpython3-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y verilator
|
||||
verilator --version
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv sync --all-extras --group test
|
||||
|
||||
- name: Run tests
|
||||
run: uv run pytest tests/ --cov=peakrdl_busdecoder --cov-report=xml --cov-report=term
|
||||
run: uv run pytest tests/ -v --cov=peakrdl_busdecoder --cov-report=xml --cov-report=term
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "peakrdl-busdecoder"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = ["jinja2>=3.1.6", "systemrdl-compiler~=1.31"]
|
||||
dependencies = ["jinja2>=3.1.6", "systemrdl-compiler~=1.30.1"]
|
||||
|
||||
authors = [{ name = "Arnav Sacheti" }]
|
||||
authors = [{ name = "Alex Mykyta" }]
|
||||
description = "Generate a SystemVerilog bus decoder from SystemRDL for splitting CPU interfaces to multiple sub-address spaces"
|
||||
readme = "README.md"
|
||||
license = { text = "LGPLv3" }
|
||||
|
||||
@@ -9,7 +9,6 @@ from typing import Any, Iterable
|
||||
import cocotb
|
||||
from cocotb.triggers import Timer
|
||||
|
||||
from tests.cocotb_lib.handle_utils import SignalHandle, resolve_handle
|
||||
|
||||
class _Apb3SlaveShim:
|
||||
"""Accessor for the APB3 slave signals on the DUT."""
|
||||
@@ -34,7 +33,10 @@ def _load_config() -> dict[str, Any]:
|
||||
|
||||
|
||||
def _resolve(handle, indices: Iterable[int]):
|
||||
return resolve_handle(handle, indices)
|
||||
ref = handle
|
||||
for idx in indices:
|
||||
ref = ref[idx]
|
||||
return ref
|
||||
|
||||
|
||||
def _set_value(handle, indices: Iterable[int], value: int) -> None:
|
||||
@@ -52,16 +54,16 @@ def _build_master_table(dut, masters_cfg: list[dict[str, Any]]) -> dict[str, dic
|
||||
entry = {
|
||||
"indices": [tuple(idx) for idx in master["indices"]] or [tuple()],
|
||||
"outputs": {
|
||||
"PSEL": SignalHandle(dut, f"{prefix}_PSEL"),
|
||||
"PENABLE": SignalHandle(dut, f"{prefix}_PENABLE"),
|
||||
"PWRITE": SignalHandle(dut, f"{prefix}_PWRITE"),
|
||||
"PADDR": SignalHandle(dut, f"{prefix}_PADDR"),
|
||||
"PWDATA": SignalHandle(dut, f"{prefix}_PWDATA"),
|
||||
"PSEL": getattr(dut, f"{prefix}_PSEL"),
|
||||
"PENABLE": getattr(dut, f"{prefix}_PENABLE"),
|
||||
"PWRITE": getattr(dut, f"{prefix}_PWRITE"),
|
||||
"PADDR": getattr(dut, f"{prefix}_PADDR"),
|
||||
"PWDATA": getattr(dut, f"{prefix}_PWDATA"),
|
||||
},
|
||||
"inputs": {
|
||||
"PRDATA": SignalHandle(dut, f"{prefix}_PRDATA"),
|
||||
"PREADY": SignalHandle(dut, f"{prefix}_PREADY"),
|
||||
"PSLVERR": SignalHandle(dut, f"{prefix}_PSLVERR"),
|
||||
"PRDATA": getattr(dut, f"{prefix}_PRDATA"),
|
||||
"PREADY": getattr(dut, f"{prefix}_PREADY"),
|
||||
"PSLVERR": getattr(dut, f"{prefix}_PSLVERR"),
|
||||
},
|
||||
}
|
||||
table[master["inst_name"]] = entry
|
||||
|
||||
@@ -9,7 +9,6 @@ from typing import Any, Iterable
|
||||
import cocotb
|
||||
from cocotb.triggers import Timer
|
||||
|
||||
from tests.cocotb_lib.handle_utils import SignalHandle, resolve_handle
|
||||
|
||||
class _Apb4SlaveShim:
|
||||
"""Lightweight accessor for the APB4 slave side of the DUT."""
|
||||
@@ -38,7 +37,10 @@ def _load_config() -> dict[str, Any]:
|
||||
|
||||
def _resolve(handle, indices: Iterable[int]):
|
||||
"""Index into hierarchical cocotb handles."""
|
||||
return resolve_handle(handle, indices)
|
||||
ref = handle
|
||||
for idx in indices:
|
||||
ref = ref[idx]
|
||||
return ref
|
||||
|
||||
|
||||
def _set_value(handle, indices: Iterable[int], value: int) -> None:
|
||||
@@ -57,18 +59,18 @@ def _build_master_table(dut, masters_cfg: list[dict[str, Any]]) -> dict[str, dic
|
||||
"port_prefix": port_prefix,
|
||||
"indices": [tuple(idx) for idx in master["indices"]] or [tuple()],
|
||||
"outputs": {
|
||||
"PSEL": SignalHandle(dut, f"{port_prefix}_PSEL"),
|
||||
"PENABLE": SignalHandle(dut, f"{port_prefix}_PENABLE"),
|
||||
"PWRITE": SignalHandle(dut, f"{port_prefix}_PWRITE"),
|
||||
"PADDR": SignalHandle(dut, f"{port_prefix}_PADDR"),
|
||||
"PPROT": SignalHandle(dut, f"{port_prefix}_PPROT"),
|
||||
"PWDATA": SignalHandle(dut, f"{port_prefix}_PWDATA"),
|
||||
"PSTRB": SignalHandle(dut, f"{port_prefix}_PSTRB"),
|
||||
"PSEL": getattr(dut, f"{port_prefix}_PSEL"),
|
||||
"PENABLE": getattr(dut, f"{port_prefix}_PENABLE"),
|
||||
"PWRITE": getattr(dut, f"{port_prefix}_PWRITE"),
|
||||
"PADDR": getattr(dut, f"{port_prefix}_PADDR"),
|
||||
"PPROT": getattr(dut, f"{port_prefix}_PPROT"),
|
||||
"PWDATA": getattr(dut, f"{port_prefix}_PWDATA"),
|
||||
"PSTRB": getattr(dut, f"{port_prefix}_PSTRB"),
|
||||
},
|
||||
"inputs": {
|
||||
"PRDATA": SignalHandle(dut, f"{port_prefix}_PRDATA"),
|
||||
"PREADY": SignalHandle(dut, f"{port_prefix}_PREADY"),
|
||||
"PSLVERR": SignalHandle(dut, f"{port_prefix}_PSLVERR"),
|
||||
"PRDATA": getattr(dut, f"{port_prefix}_PRDATA"),
|
||||
"PREADY": getattr(dut, f"{port_prefix}_PREADY"),
|
||||
"PSLVERR": getattr(dut, f"{port_prefix}_PSLVERR"),
|
||||
},
|
||||
}
|
||||
table[master["inst_name"]] = entry
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from peakrdl_busdecoder.cpuif.apb4.apb4_cpuif_flat import APB4CpuifFlat
|
||||
@@ -43,38 +43,17 @@ def test_apb4_smoke(tmp_path: Path, rdl_file: str, top_name: str) -> None:
|
||||
|
||||
runner = get_runner("verilator")
|
||||
sim_build = build_root / "sim_build"
|
||||
|
||||
try:
|
||||
runner.build(
|
||||
sources=sources,
|
||||
hdl_toplevel=module_path.stem,
|
||||
build_dir=sim_build,
|
||||
log_file=str(build_root / "build.log"),
|
||||
)
|
||||
except SystemExit as e:
|
||||
# Print build log on failure for easier debugging
|
||||
log_path = build_root / "build.log"
|
||||
if log_path.exists():
|
||||
logging.error("\n\n=== Build Log ===\n")
|
||||
logging.error(log_path.read_text())
|
||||
logging.error("\n=== End Build Log ===\n")
|
||||
if e.code != 0:
|
||||
raise
|
||||
|
||||
try:
|
||||
runner.test(
|
||||
hdl_toplevel=module_path.stem,
|
||||
test_module="tests.cocotb.apb4.smoke.test_register_access",
|
||||
build_dir=sim_build,
|
||||
log_file=str(build_root / "simulation.log"),
|
||||
extra_env={"RDL_TEST_CONFIG": json.dumps(config)},
|
||||
)
|
||||
except SystemExit as e:
|
||||
# Print simulation log on failure for easier debugging
|
||||
log_path = build_root / "simulation.log"
|
||||
if log_path.exists():
|
||||
logging.error("\n\n=== Simulation Log ===\n")
|
||||
logging.error(log_path.read_text())
|
||||
logging.error("\n=== End Simulation Log ===\n")
|
||||
if e.code != 0:
|
||||
raise
|
||||
runner.build(
|
||||
sources=sources,
|
||||
hdl_toplevel=module_path.stem,
|
||||
build_dir=sim_build,
|
||||
)
|
||||
|
||||
runner.test(
|
||||
hdl_toplevel=module_path.stem,
|
||||
test_module="tests.cocotb.apb4.smoke.test_register_access",
|
||||
build_dir=sim_build,
|
||||
log_file=str(build_root / "simulation.log"),
|
||||
extra_env={"RDL_TEST_CONFIG": json.dumps(config)},
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ from typing import Any, Iterable
|
||||
import cocotb
|
||||
from cocotb.triggers import Timer
|
||||
|
||||
from tests.cocotb_lib.handle_utils import SignalHandle, resolve_handle
|
||||
|
||||
class _AxilSlaveShim:
|
||||
"""Accessor for AXI4-Lite slave ports on the DUT."""
|
||||
@@ -45,7 +44,10 @@ def _load_config() -> dict[str, Any]:
|
||||
|
||||
|
||||
def _resolve(handle, indices: Iterable[int]):
|
||||
return resolve_handle(handle, indices)
|
||||
ref = handle
|
||||
for idx in indices:
|
||||
ref = ref[idx]
|
||||
return ref
|
||||
|
||||
|
||||
def _set_value(handle, indices: Iterable[int], value: int) -> None:
|
||||
@@ -63,25 +65,25 @@ def _build_master_table(dut, masters_cfg: list[dict[str, Any]]) -> dict[str, dic
|
||||
entry = {
|
||||
"indices": [tuple(idx) for idx in master["indices"]] or [tuple()],
|
||||
"outputs": {
|
||||
"AWVALID": SignalHandle(dut, f"{prefix}_AWVALID"),
|
||||
"AWADDR": SignalHandle(dut, f"{prefix}_AWADDR"),
|
||||
"AWPROT": SignalHandle(dut, f"{prefix}_AWPROT"),
|
||||
"WVALID": SignalHandle(dut, f"{prefix}_WVALID"),
|
||||
"WDATA": SignalHandle(dut, f"{prefix}_WDATA"),
|
||||
"WSTRB": SignalHandle(dut, f"{prefix}_WSTRB"),
|
||||
"ARVALID": SignalHandle(dut, f"{prefix}_ARVALID"),
|
||||
"ARADDR": SignalHandle(dut, f"{prefix}_ARADDR"),
|
||||
"ARPROT": SignalHandle(dut, f"{prefix}_ARPROT"),
|
||||
"AWVALID": getattr(dut, f"{prefix}_AWVALID"),
|
||||
"AWADDR": getattr(dut, f"{prefix}_AWADDR"),
|
||||
"AWPROT": getattr(dut, f"{prefix}_AWPROT"),
|
||||
"WVALID": getattr(dut, f"{prefix}_WVALID"),
|
||||
"WDATA": getattr(dut, f"{prefix}_WDATA"),
|
||||
"WSTRB": getattr(dut, f"{prefix}_WSTRB"),
|
||||
"ARVALID": getattr(dut, f"{prefix}_ARVALID"),
|
||||
"ARADDR": getattr(dut, f"{prefix}_ARADDR"),
|
||||
"ARPROT": getattr(dut, f"{prefix}_ARPROT"),
|
||||
},
|
||||
"inputs": {
|
||||
"AWREADY": SignalHandle(dut, f"{prefix}_AWREADY"),
|
||||
"WREADY": SignalHandle(dut, f"{prefix}_WREADY"),
|
||||
"BVALID": SignalHandle(dut, f"{prefix}_BVALID"),
|
||||
"BRESP": SignalHandle(dut, f"{prefix}_BRESP"),
|
||||
"ARREADY": SignalHandle(dut, f"{prefix}_ARREADY"),
|
||||
"RVALID": SignalHandle(dut, f"{prefix}_RVALID"),
|
||||
"RDATA": SignalHandle(dut, f"{prefix}_RDATA"),
|
||||
"RRESP": SignalHandle(dut, f"{prefix}_RRESP"),
|
||||
"AWREADY": getattr(dut, f"{prefix}_AWREADY"),
|
||||
"WREADY": getattr(dut, f"{prefix}_WREADY"),
|
||||
"BVALID": getattr(dut, f"{prefix}_BVALID"),
|
||||
"BRESP": getattr(dut, f"{prefix}_BRESP"),
|
||||
"ARREADY": getattr(dut, f"{prefix}_ARREADY"),
|
||||
"RVALID": getattr(dut, f"{prefix}_RVALID"),
|
||||
"RDATA": getattr(dut, f"{prefix}_RDATA"),
|
||||
"RRESP": getattr(dut, f"{prefix}_RRESP"),
|
||||
},
|
||||
}
|
||||
table[master["inst_name"]] = entry
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
"""Utilities for resolving cocotb signal handles across simulators."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Iterable
|
||||
|
||||
|
||||
class SignalHandle:
|
||||
"""
|
||||
Wrapper that resolves array elements even when the simulator does not expose
|
||||
unpacked arrays through ``handle[idx]``.
|
||||
"""
|
||||
|
||||
def __init__(self, dut, name: str) -> None:
|
||||
self._dut = dut
|
||||
self._name = name
|
||||
self._base = getattr(dut, name, None)
|
||||
self._cache: dict[tuple[int, ...], Any] = {}
|
||||
|
||||
def resolve(self, indices: tuple[int, ...]):
|
||||
if not indices:
|
||||
return self._base if self._base is not None else self._lookup(tuple())
|
||||
|
||||
if indices not in self._cache:
|
||||
self._cache[indices] = self._direct_or_lookup(indices)
|
||||
return self._cache[indices]
|
||||
|
||||
def _direct_or_lookup(self, indices: tuple[int, ...]):
|
||||
if self._base is not None:
|
||||
ref = self._base
|
||||
try:
|
||||
for idx in indices:
|
||||
ref = ref[idx]
|
||||
return ref
|
||||
except (IndexError, TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
return self._lookup(indices)
|
||||
|
||||
def _lookup(self, indices: tuple[int, ...]):
|
||||
suffix = "".join(f"[{idx}]" for idx in indices)
|
||||
path = f"{self._name}{suffix}"
|
||||
|
||||
try:
|
||||
return getattr(self._dut, path)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
errors: list[Exception] = []
|
||||
for extended in (False, True):
|
||||
try:
|
||||
return self._dut._id(path, extended=extended)
|
||||
except (AttributeError, ValueError) as exc:
|
||||
errors.append(exc)
|
||||
|
||||
raise AttributeError(f"Unable to resolve handle '{path}' via dut._id") from errors[-1]
|
||||
|
||||
|
||||
def resolve_handle(handle, indices: Iterable[int]):
|
||||
"""Resolve either a regular cocotb handle or a ``SignalHandle`` wrapper."""
|
||||
index_tuple = tuple(indices)
|
||||
|
||||
if isinstance(handle, SignalHandle):
|
||||
return handle.resolve(index_tuple)
|
||||
|
||||
ref = handle
|
||||
for idx in index_tuple:
|
||||
ref = ref[idx]
|
||||
return ref
|
||||
Reference in New Issue
Block a user