fhdl/namer: Remove debug and add docstring comments.
This commit is contained in:
parent
6aa22271f9
commit
1e805a8789
|
@ -4,44 +4,37 @@
|
|||
# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
from collections import OrderedDict
|
||||
from itertools import combinations
|
||||
|
||||
from migen.fhdl.structure import *
|
||||
|
||||
|
||||
class _Node:
|
||||
"""A node in a hierarchy tree used for signal name resolution.
|
||||
|
||||
Attributes:
|
||||
signal_count (int): The count of signals in this node.
|
||||
numbers (set): A set containing numbers associated with this node.
|
||||
use_name (bool): Flag to determine if the node's name should be used in signal naming.
|
||||
use_number (bool): Flag to determine if the node's number should be used in signal naming.
|
||||
children (dict): A dictionary of child nodes.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.signal_count = 0
|
||||
self.numbers = set()
|
||||
self.use_name = False
|
||||
self.use_number = False
|
||||
self.children = OrderedDict()
|
||||
|
||||
|
||||
def _display_tree(filename, tree):
|
||||
from migen.util.treeviz import RenderNode
|
||||
|
||||
def _to_render_node(name, node):
|
||||
children = [_to_render_node(k, v) for k, v in node.children.items()]
|
||||
if node.use_name:
|
||||
if node.use_number:
|
||||
color = (0.5, 0.9, 0.8)
|
||||
else:
|
||||
color = (0.8, 0.5, 0.9)
|
||||
else:
|
||||
if node.use_number:
|
||||
color = (0.9, 0.8, 0.5)
|
||||
else:
|
||||
color = (0.8, 0.8, 0.8)
|
||||
label = "{0}\n{1} signals\n{2}".format(name, node.signal_count, node.numbers)
|
||||
return RenderNode(label, children, color=color)
|
||||
|
||||
top = _to_render_node("top", tree)
|
||||
top.to_svg(filename)
|
||||
|
||||
self.children = {}
|
||||
|
||||
def _build_tree(signals, basic_tree=None):
|
||||
"""Builds a hierarchical tree of nodes based on the provided signals.
|
||||
|
||||
Parameters:
|
||||
signals (iterable): An iterable of signals to be organized into a tree.
|
||||
basic_tree (_Node, optional): A basic tree structure that the new tree is based upon.
|
||||
|
||||
Returns:
|
||||
_Node: The root node of the constructed hierarchical tree.
|
||||
"""
|
||||
root = _Node()
|
||||
for signal in signals:
|
||||
current_b = basic_tree
|
||||
|
@ -71,6 +64,15 @@ def _build_tree(signals, basic_tree=None):
|
|||
|
||||
|
||||
def _set_use_name(node, node_name=""):
|
||||
"""Determines whether names should be used in signal naming by examining child nodes.
|
||||
|
||||
Parameters:
|
||||
node (_Node): The current node in the tree.
|
||||
node_name (str): The name of the node, used when the node's name needs to be included.
|
||||
|
||||
Returns:
|
||||
set: A set of tuples representing the names that are to be used.
|
||||
"""
|
||||
cnames = [(k, _set_use_name(v, k)) for k, v in node.children.items()]
|
||||
for (c1_prefix, c1_names), (c2_prefix, c2_names) in combinations(cnames, 2):
|
||||
if not c1_names.isdisjoint(c2_names):
|
||||
|
@ -92,6 +94,15 @@ def _set_use_name(node, node_name=""):
|
|||
|
||||
|
||||
def _name_signal(tree, signal):
|
||||
"""Generates a hierarchical name for a given signal based on the tree structure.
|
||||
|
||||
Parameters:
|
||||
tree (_Node): The root node of the tree used for name resolution.
|
||||
signal : The signal object whose name is to be generated.
|
||||
|
||||
Returns:
|
||||
str: The generated hierarchical name for the signal.
|
||||
"""
|
||||
elements = []
|
||||
treepos = tree
|
||||
for step_name, step_n in signal.backtrace:
|
||||
|
@ -110,10 +121,27 @@ def _name_signal(tree, signal):
|
|||
|
||||
|
||||
def _build_pnd_from_tree(tree, signals):
|
||||
"""Builds a dictionary mapping signals to their hierarchical names from a tree.
|
||||
|
||||
Parameters:
|
||||
tree (_Node): The tree that contains naming information.
|
||||
signals (iterable): An iterable of signals that need to be named.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary where keys are signals and values are their hierarchical names.
|
||||
"""
|
||||
return dict((signal, _name_signal(tree, signal)) for signal in signals)
|
||||
|
||||
|
||||
def _invert_pnd(pnd):
|
||||
"""Inverts a signal-to-name dictionary to a name-to-signals dictionary.
|
||||
|
||||
Parameters:
|
||||
pnd (dict): A dictionary mapping signals to names.
|
||||
|
||||
Returns:
|
||||
dict: An inverted dictionary where keys are names and values are lists of signals.
|
||||
"""
|
||||
inv_pnd = dict()
|
||||
for k, v in pnd.items():
|
||||
inv_pnd[v] = inv_pnd.get(v, [])
|
||||
|
@ -122,6 +150,14 @@ def _invert_pnd(pnd):
|
|||
|
||||
|
||||
def _list_conflicting_signals(pnd):
|
||||
"""Lists signals that have conflicting names in the provided mapping.
|
||||
|
||||
Parameters:
|
||||
pnd (dict): A dictionary mapping signals to names.
|
||||
|
||||
Returns:
|
||||
set: A set of signals that have name conflicts.
|
||||
"""
|
||||
inv_pnd = _invert_pnd(pnd)
|
||||
r = set()
|
||||
for k, v in inv_pnd.items():
|
||||
|
@ -131,38 +167,42 @@ def _list_conflicting_signals(pnd):
|
|||
|
||||
|
||||
def _set_use_number(tree, signals):
|
||||
"""Sets nodes in the tree to use numbers based on signal counts to resolve name conflicts.
|
||||
|
||||
Parameters:
|
||||
tree (_Node): The tree that contains naming information.
|
||||
signals (iterable): An iterable of signals that may have name conflicts.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
for signal in signals:
|
||||
current = tree
|
||||
for step_name, step_n in signal.backtrace:
|
||||
current = current.children[step_name]
|
||||
current.use_number = current.signal_count > len(current.numbers) and len(current.numbers) > 1
|
||||
|
||||
_debug = False
|
||||
|
||||
|
||||
def _build_pnd_for_group(group_n, signals):
|
||||
"""Builds a signal-to-name dictionary for a specific group of signals.
|
||||
|
||||
Parameters:
|
||||
group_n (int): The group number.
|
||||
signals (iterable): The signals within the group.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary mapping signals to their hierarchical names.
|
||||
"""
|
||||
basic_tree = _build_tree(signals)
|
||||
_set_use_name(basic_tree)
|
||||
if _debug:
|
||||
_display_tree("tree{0}_basic.svg".format(group_n), basic_tree)
|
||||
pnd = _build_pnd_from_tree(basic_tree, signals)
|
||||
|
||||
# If there are conflicts, try splitting the tree by numbers on paths taken by conflicting signals.
|
||||
conflicting_signals = _list_conflicting_signals(pnd)
|
||||
if conflicting_signals:
|
||||
_set_use_number(basic_tree, conflicting_signals)
|
||||
if _debug:
|
||||
print("namer: using split-by-number strategy (group {0})".format(group_n))
|
||||
_display_tree("tree{0}_marked.svg".format(group_n), basic_tree)
|
||||
numbered_tree = _build_tree(signals, basic_tree)
|
||||
_set_use_name(numbered_tree)
|
||||
if _debug:
|
||||
_display_tree("tree{0}_numbered.svg".format(group_n), numbered_tree)
|
||||
pnd = _build_pnd_from_tree(numbered_tree, signals)
|
||||
else:
|
||||
if _debug:
|
||||
print("namer: using basic strategy (group {0})".format(group_n))
|
||||
|
||||
# ...then add number suffixes by DUID.
|
||||
inv_pnd = _invert_pnd(pnd)
|
||||
duid_suffixed = False
|
||||
|
@ -171,13 +211,18 @@ def _build_pnd_for_group(group_n, signals):
|
|||
duid_suffixed = True
|
||||
for n, signal in enumerate(sorted(signals, key=lambda x: x.duid)):
|
||||
pnd[signal] += str(n)
|
||||
if _debug and duid_suffixed:
|
||||
print("namer: using DUID suffixes (group {0})".format(group_n))
|
||||
|
||||
return pnd
|
||||
|
||||
|
||||
def _build_signal_groups(signals):
|
||||
"""Organizes signals into related groups.
|
||||
|
||||
Parameters:
|
||||
signals (iterable): An iterable of all signals to be organized.
|
||||
|
||||
Returns:
|
||||
list: A list of sets, each containing related signals.
|
||||
"""
|
||||
r = []
|
||||
for signal in signals:
|
||||
# Build chain of related signals.
|
||||
|
@ -199,6 +244,15 @@ def _build_signal_groups(signals):
|
|||
|
||||
|
||||
def _build_pnd(signals):
|
||||
"""Builds a complete signal-to-name dictionary using a hierarchical tree.
|
||||
|
||||
Parameters:
|
||||
signals (iterable): An iterable of all signals to be named.
|
||||
tree (_Node): The root node of the tree used for name resolution.
|
||||
|
||||
Returns:
|
||||
dict: A complete dictionary mapping signals to their hierarchical names.
|
||||
"""
|
||||
groups = _build_signal_groups(signals)
|
||||
gpnds = [_build_pnd_for_group(n, gsignals) for n, gsignals in enumerate(groups)]
|
||||
pnd = dict()
|
||||
|
@ -216,6 +270,15 @@ def _build_pnd(signals):
|
|||
|
||||
|
||||
def build_namespace(signals, reserved_keywords=set()):
|
||||
"""Constructs a namespace where each signal is given a unique hierarchical name.
|
||||
|
||||
Parameters:
|
||||
signals (iterable): An iterable of all signals to be named.
|
||||
reserved_keywords (set, optional): A set of keywords that cannot be used as signal names.
|
||||
|
||||
Returns:
|
||||
Namespace: An object that contains the mapping of signals to unique names and provides methods to access them.
|
||||
"""
|
||||
pnd = _build_pnd(signals)
|
||||
ns = Namespace(pnd, reserved_keywords)
|
||||
# Register Signals with name_override.
|
||||
|
@ -226,6 +289,25 @@ def build_namespace(signals, reserved_keywords=set()):
|
|||
|
||||
|
||||
class Namespace:
|
||||
"""
|
||||
A Namespace object manages unique naming for signals within a hardware design.
|
||||
|
||||
It ensures that each signal has a unique, conflict-free name within the design's namespace. This
|
||||
includes taking into account reserved keywords and handling signals that may share the same name
|
||||
by default (due to being instances of the same hardware module or component).
|
||||
|
||||
Attributes:
|
||||
counts (dict): A dictionary to keep track of the number of times a particular name has been used.
|
||||
sigs (dict): A dictionary mapping signals to a unique identifier to avoid name conflicts.
|
||||
pnd (dict): The primary name dictionary that maps signals to their base names.
|
||||
clock_domains (dict): A dictionary managing the names of clock signals within various clock domains.
|
||||
|
||||
Methods:
|
||||
get_name(sig): Returns a unique name for the given signal. If the signal is associated with a
|
||||
clock domain, it handles naming appropriately, considering resets and clock signals. For
|
||||
regular signals, it uses overridden names or constructs names based on the signal's
|
||||
hierarchical structure.
|
||||
"""
|
||||
def __init__(self, pnd, reserved_keywords=set()):
|
||||
self.counts = {k: 1 for k in reserved_keywords}
|
||||
self.sigs = {}
|
||||
|
|
Loading…
Reference in New Issue