Files
PeakRDL-BusDecoder/tests/cocotb_lib/handle_utils.py
Arnav Sacheti a9653c8497 Tests/cocotb (#19)
* wip

* reorg

* update sv int

* apb4 working

* apb3 working

* version bump + ignore runner warning

* remove redundant check

* adding log on failure

* cleaning up verilator version issue

* devcontainer

* Fix missing libpython in GitHub Actions CI environment (#21)

* Initial plan

* Install libpython in GitHub Actions for cocotb tests

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2025-11-10 23:00:28 -08:00

70 lines
2.0 KiB
Python

"""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