Source code for pynq.metadata.hierarchy_dict_view

# Copyright (C) 2022 Xilinx, Inc
# SPDX-License-Identifier: BSD-3-Clause

from typing import Dict, List
from pydantic import Field

import json
from pynqmetadata import Module, ProcSysCore, SubordinatePort, Hierarchy

from .ip_dict_view import IpDictView
from .mem_dict_view import MemDictView
from pynqmetadata import MetadataExtension

from .metadata_view import MetadataView

def _default_repr(obj):
    return repr(obj)

[docs]class HierarchyDriverExetension(MetadataExtension): """Extends the metadata for hierarchies with PYNQ runtime driver information""" driver:object = Field(..., exclude=True, description="the runtime driver for this hierarchy") device:object = Field(..., exclude=True, description="the device this core has been loaded onto") overlay:object = Field(..., exclude=True, description="the overlay that this driver is associated with")
[docs]class HierarchyDictView(MetadataView): """ Provides a hierarchy view onto the Metadata object that will display all hierarchies of addressable IP from the Processing System. All IP when parsed into the HWH file is flat with no hierarchies. However, there is an additional, full_name field that can be used to reconstruct these hierarchies, which is what this view uses. This view models a dictionary where each key is the name of the hierarchy, and each entry contains: * a dictionary of all the IP contained within that level of the hierarchy. * a dictionare of all the memory objects within that level of the hierarchy. * a dictionary of sub-hierarchies contained within this level of the hierarchy. * any drivers that have been assigned to this level of the hierarchy. """ def __init__(self, module: Module, ip_view:IpDictView, mem_view:MemDictView, overlay: object, hierarchy_drivers:object, default_hierarchy:object, device:object) -> None: super().__init__(module=module) self._ip_dict = ip_view self._mem_dict = mem_view self._hierarchy_drivers = hierarchy_drivers self._default_hierarchy = default_hierarchy self._device = device self._overlay = overlay def _hierarchy_walker(self, r:Dict, h:Hierarchy)->None: """ recursive walk down the hierarchy h, adding IP that is a match in ip_dict or mem_dict to the hierarchies """ if h.name not in r: r[h.name] = {} r[h.name]["ip"] = {} r[h.name]["memories"] = {} r[h.name]["hierarchies"] = {} r[h.name]["interrupts"] = {} r[h.name]["gpio"] = {} r[h.name]["fullpath"] = h.path r[h.name]["md_ref"] = h for ip in h._core_obj.values(): if ip.hierarchy_name in self._mem_dict: name = ip.hierarchy_name.split("/")[-1] r[h.name]["memories"][name] = self._mem_dict[ip.hierarchy_name] else: if ip.hierarchy_name in self._ip_dict: name = ip.hierarchy_name.split("/")[-1] r[h.name]["ip"][name] = self._ip_dict[ip.hierarchy_name] for hier in h._hierarchies_obj.values(): self._hierarchy_walker(r[h.name]["hierarchies"], hier) def _prune_unused_walker(self, r:Dict)->bool: """ Walks down through the hierarchy dict, removing anything that is empty """ del_list = [] for i,h in r["hierarchies"].items(): if self._prune_unused_walker(h): del_list.append(i) for i in del_list: del r["hierarchies"][i] if r["md_ref"].pr_region: # Do not prune empty PR regions return False else: return len(r["ip"])==0 and len(r["memories"])==0 and len(r["hierarchies"])==0 def _replicate_subhierarchies(self, add_to_root:Dict, l:Dict)->None: """ The original hierarchy dict includes the sub-hierarchies of other hierarchies at the root of the hierarchy_dict. This walks through and appends the sub-hierarchies to the root. """ for hname,h in l["hierarchies"].items(): add_to_root[h["fullpath"]] = h self._replicate_subhierarchies(add_to_root=add_to_root, l=h) def _assign_drivers(self, hier_dict:Dict)->None: """Assigns drivers to the hierarchy if the pattern matches in the driver class. Uses metadata extensions to append the driver information. First checks to see that there is not already a driver assigned.""" for hier in hier_dict["hierarchies"].values(): self._assign_drivers(hier_dict=hier) if "driver" not in hier_dict["md_ref"].ext: driver = self._default_hierarchy for hip in self._hierarchy_drivers: if hip.checkhierarchy(hier_dict): driver = hip break #taken hier_dict["md_ref"].ext["driver"] = HierarchyDriverExetension(device=self._device, driver=driver, overlay=self._overlay) hier_dict["device"] = self._device hier_dict["driver"] = driver hier_dict["overlay"] = self._overlay else: hier_dict["device"] = hier_dict["md_ref"].ext["driver"].device hier_dict["driver"] = hier_dict["md_ref"].ext["driver"].driver hier_dict["overlay"] = hier_dict["md_ref"].ext["driver"].overlay def _cleanup_metadata_hierarchy_references(self, hier_dict:Dict)->None: """"Removes any reference to the metadata hierarchy objects from the dictionary""" if "md_ref" in hier_dict: del hier_dict["md_ref"] for hier in hier_dict["hierarchies"].values(): self._cleanup_metadata_hierarchy_references(hier) @property def view(self) -> Dict: """ Walks down the hierarchy dict and whenever it encounters an IP that is in the ip_dict or mem_dict keep it. """ repr_dict = {} top_level = self._md._hierarchies # Build up the hierarchies for hierarchy in top_level._hierarchies_obj.values(): self._hierarchy_walker(repr_dict, hierarchy) # Prune hierarchies that are not used for item in repr_dict.values(): self._prune_unused_walker(item) # Remove anything at the root that has nothing beneath it del_list = [] for i_name, i in repr_dict.items(): if len(i["ip"])==0 and len(i["memories"])==0 and len(i["hierarchies"])==0: if not i["md_ref"].pr_region: del_list.append(i_name) # Remove everything flagged for removal for d in del_list: del repr_dict[d] # the extra sub-hierarchies add them to the root add_to_root = {} for item in repr_dict.values(): self._replicate_subhierarchies(add_to_root, item) for iname, i in add_to_root.items(): repr_dict[iname] = i # Assign drivers to all the hierarchies (Writes into the central metadata with a metadata extension) # If a driver is already associated with the hierarchy then grab that for item in repr_dict.values(): self._assign_drivers(item) # remove any references to the metadata hierarchies in the dict for item in repr_dict.values(): self._cleanup_metadata_hierarchy_references(item) return repr_dict