# Copyright (C) 2022 Xilinx, Inc
# SPDX-License-Identifier: BSD-3-Clause
import json
import re
from typing import Dict, List
from pydantic import Field
from pynqmetadata import Core, MetadataExtension, Module, Signal
from .metadata_view import MetadataView
[docs]class InterruptControllerIndex(MetadataExtension):
index: int = Field(
..., description="The index for an interrupt controller in the PYNQ runtime"
)
[docs]class InterruptControllersView(MetadataView):
"""
Provides a view onto the Metadata object that displays all the AXI interrupt
controllers that are accessible from the processing system. Models a dictionary
where the key is the name of the interrupt controller and each entry contains:
* parent: either another controller or the PS
* index: the index of this interrupt.
The PS is the root of this hierarchy and is unnamed.
"""
def __init__(self, module: Module) -> None:
super().__init__(module=module)
self._state = {}
self._base_idx: int = 0
def _walk_for_irq_controllers(self, sig: Signal) -> List[Core]:
"""From a PS IRQ pin, walk out from it and the concat blocks collecting all
the interrupt controller blocks"""
ret: List[Core] = []
# Get the index number from the port name (stf: I don't like this)
if sig.parent().parent().vlnv.name == "xlconcat":
idx_re = re.search("In([0-9]+)", sig.name)
if len(idx_re.groups()) == 1:
# idx = int(idx_re.group(1)) + base_idx
idx = self._base_idx
else:
raise ValueError(
f"Trying to infer an interrupt index from the port name for {sig.ref} but it does not match the expected format signame={sig.name} groups={idx_re.groups()}"
)
else:
idx = self._base_idx
for dst in sig._connections.values():
if dst.parent().parent().vlnv.name == "axi_intc":
dst.parent().parent().ext[
"interrupt_controller_index"
] = InterruptControllerIndex(index=idx)
self._base_idx = self._base_idx + 1
ret.append(dst.parent().parent())
if dst.parent().parent().vlnv.name == "xlconcat":
concat = dst.parent().parent()
for port in concat.ports.values():
if port.name != "dout":
sig = port.sig()
ret = ret + self._walk_for_irq_controllers(sig)
return ret
@property
def view(self) -> Dict:
repr_dict = {}
self._base_idx
ps_list = self._md.get_processing_systems()
if len(ps_list) > 1:
raise RuntimeError(
f"There is more than one processing system in the design, not sure how to proceed."
)
ps = ps_list[list(ps_list.keys())[0]]
ps_irq_signals = ps.get_irqs
# Find all the directly connected controllers to the ps
controller_list = []
for ps_irq in ps_irq_signals.values():
controller_list = controller_list + self._walk_for_irq_controllers(ps_irq)
for controller in controller_list:
repr_dict[controller.name] = {}
repr_dict[controller.name]["parent"] = ""
if "interrupt_controller_index" in controller.ext:
repr_dict[controller.name]["index"] = controller.ext[
"interrupt_controller_index"
].index
else:
raise RuntimeError(
f"Cannot determine the index for interrupt controller {controller.ref}"
)
repr_dict[controller.name]["raw_irq"] = ps.irq_map[
controller.ext["interrupt_controller_index"].index
]
return repr_dict
def __setitem__(self, key: str, value: object) -> None:
"""Set the state of an item in the interrupt_controllers"""
self._state[key] = value