src: initial source import
From zephyr commit f5409dec01c84aa8eda1830c64309abe3d5868d0. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
35f9424338
commit
9488c68bc3
3 changed files with 4766 additions and 0 deletions
1977
src/devicetree/dtlib.py
Normal file
1977
src/devicetree/dtlib.py
Normal file
File diff suppressed because it is too large
Load diff
2628
src/devicetree/edtlib.py
Normal file
2628
src/devicetree/edtlib.py
Normal file
File diff suppressed because it is too large
Load diff
161
src/devicetree/grutils.py
Normal file
161
src/devicetree/grutils.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
# Copyright 2009-2013, 2019 Peter A. Bigot
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This implementation is derived from the one in
|
||||
# [PyXB](https://github.com/pabigot/pyxb), stripped down and modified
|
||||
# specifically to manage edtlib Node instances.
|
||||
|
||||
import collections
|
||||
|
||||
class Graph:
|
||||
"""
|
||||
Represent a directed graph with edtlib Node objects as nodes.
|
||||
|
||||
This is used to determine order dependencies among nodes in a
|
||||
devicetree. An edge from C{source} to C{target} indicates that
|
||||
some aspect of C{source} requires that some aspect of C{target}
|
||||
already be available.
|
||||
"""
|
||||
|
||||
def __init__(self, root=None):
|
||||
self.__roots = None
|
||||
if root is not None:
|
||||
self.__roots = {root}
|
||||
self.__edge_map = collections.defaultdict(set)
|
||||
self.__reverse_map = collections.defaultdict(set)
|
||||
self.__nodes = set()
|
||||
|
||||
def add_edge(self, source, target):
|
||||
"""
|
||||
Add a directed edge from the C{source} to the C{target}.
|
||||
|
||||
The nodes are added to the graph if necessary.
|
||||
"""
|
||||
self.__edge_map[source].add(target)
|
||||
if source != target:
|
||||
self.__reverse_map[target].add(source)
|
||||
self.__nodes.add(source)
|
||||
self.__nodes.add(target)
|
||||
|
||||
def roots(self):
|
||||
"""
|
||||
Return the set of nodes calculated to be roots (i.e., those
|
||||
that have no incoming edges).
|
||||
|
||||
This caches the roots calculated in a previous invocation.
|
||||
|
||||
@rtype: C{set}
|
||||
"""
|
||||
if not self.__roots:
|
||||
self.__roots = set()
|
||||
for n in self.__nodes:
|
||||
if n not in self.__reverse_map:
|
||||
self.__roots.add(n)
|
||||
return self.__roots
|
||||
|
||||
def _tarjan(self):
|
||||
# Execute Tarjan's algorithm on the graph.
|
||||
#
|
||||
# Tarjan's algorithm
|
||||
# (http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
|
||||
# computes the strongly-connected components
|
||||
# (http://en.wikipedia.org/wiki/Strongly_connected_component)
|
||||
# of the graph: i.e., the sets of nodes that form a minimal
|
||||
# closed set under edge transition. In essence, the loops.
|
||||
# We use this to detect groups of components that have a
|
||||
# dependency cycle, and to impose a total order on components
|
||||
# based on dependencies.
|
||||
|
||||
self.__stack = []
|
||||
self.__scc_order = []
|
||||
self.__index = 0
|
||||
self.__tarjan_index = {}
|
||||
self.__tarjan_low_link = {}
|
||||
for v in self.__nodes:
|
||||
self.__tarjan_index[v] = None
|
||||
roots = sorted(self.roots(), key=node_key)
|
||||
if self.__nodes and not roots:
|
||||
raise Exception('TARJAN: No roots found in graph with {} nodes'.format(len(self.__nodes)))
|
||||
|
||||
for r in roots:
|
||||
self._tarjan_root(r)
|
||||
|
||||
# Assign ordinals for edtlib
|
||||
ordinal = 0
|
||||
for scc in self.__scc_order:
|
||||
# Zephyr customization: devicetree Node graphs should have
|
||||
# no loops, so all SCCs should be singletons. That may
|
||||
# change in the future, but for now we only give an
|
||||
# ordinal to singletons.
|
||||
if len(scc) == 1:
|
||||
scc[0].dep_ordinal = ordinal
|
||||
ordinal += 1
|
||||
|
||||
def _tarjan_root(self, v):
|
||||
# Do the work of Tarjan's algorithm for a given root node.
|
||||
|
||||
if self.__tarjan_index.get(v) is not None:
|
||||
# "Root" was already reached.
|
||||
return
|
||||
self.__tarjan_index[v] = self.__tarjan_low_link[v] = self.__index
|
||||
self.__index += 1
|
||||
self.__stack.append(v)
|
||||
source = v
|
||||
for target in sorted(self.__edge_map[source], key=node_key):
|
||||
if self.__tarjan_index[target] is None:
|
||||
self._tarjan_root(target)
|
||||
self.__tarjan_low_link[v] = min(self.__tarjan_low_link[v], self.__tarjan_low_link[target])
|
||||
elif target in self.__stack:
|
||||
self.__tarjan_low_link[v] = min(self.__tarjan_low_link[v], self.__tarjan_low_link[target])
|
||||
|
||||
if self.__tarjan_low_link[v] == self.__tarjan_index[v]:
|
||||
scc = []
|
||||
while True:
|
||||
scc.append(self.__stack.pop())
|
||||
if v == scc[-1]:
|
||||
break
|
||||
self.__scc_order.append(scc)
|
||||
|
||||
def scc_order(self):
|
||||
"""Return the strongly-connected components in order.
|
||||
|
||||
The data structure is a list, in dependency order, of strongly
|
||||
connected components (which can be single nodes). Appearance
|
||||
of a node in a set earlier in the list indicates that it has
|
||||
no dependencies on any node that appears in a subsequent set.
|
||||
This order is preferred over a depth-first-search order for
|
||||
code generation, since it detects loops.
|
||||
"""
|
||||
if not self.__scc_order:
|
||||
self._tarjan()
|
||||
return self.__scc_order
|
||||
__scc_order = None
|
||||
|
||||
def depends_on(self, node):
|
||||
"""Get the nodes that 'node' directly depends on."""
|
||||
return sorted(self.__edge_map[node], key=node_key)
|
||||
|
||||
def required_by(self, node):
|
||||
"""Get the nodes that directly depend on 'node'."""
|
||||
return sorted(self.__reverse_map[node], key=node_key)
|
||||
|
||||
def node_key(node):
|
||||
# This sort key ensures that sibling nodes with the same name will
|
||||
# use unit addresses as tiebreakers. That in turn ensures ordinals
|
||||
# for otherwise indistinguishable siblings are in increasing order
|
||||
# by unit address, which is convenient for displaying output.
|
||||
|
||||
if node.parent:
|
||||
parent_path = node.parent.path
|
||||
else:
|
||||
parent_path = '/'
|
||||
|
||||
if node.unit_addr is not None:
|
||||
name = node.name.rsplit('@', 1)[0]
|
||||
unit_addr = node.unit_addr
|
||||
else:
|
||||
name = node.name
|
||||
unit_addr = -1
|
||||
|
||||
return (parent_path, name, unit_addr)
|
||||
Loading…
Add table
Add a link
Reference in a new issue