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>
|
# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from itertools import combinations
|
from itertools import combinations
|
||||||
|
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
|
|
||||||
|
|
||||||
class _Node:
|
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):
|
def __init__(self):
|
||||||
self.signal_count = 0
|
self.signal_count = 0
|
||||||
self.numbers = set()
|
self.numbers = set()
|
||||||
self.use_name = False
|
self.use_name = False
|
||||||
self.use_number = False
|
self.use_number = False
|
||||||
self.children = OrderedDict()
|
self.children = {}
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_tree(signals, basic_tree=None):
|
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()
|
root = _Node()
|
||||||
for signal in signals:
|
for signal in signals:
|
||||||
current_b = basic_tree
|
current_b = basic_tree
|
||||||
|
@ -71,6 +64,15 @@ def _build_tree(signals, basic_tree=None):
|
||||||
|
|
||||||
|
|
||||||
def _set_use_name(node, node_name=""):
|
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()]
|
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):
|
for (c1_prefix, c1_names), (c2_prefix, c2_names) in combinations(cnames, 2):
|
||||||
if not c1_names.isdisjoint(c2_names):
|
if not c1_names.isdisjoint(c2_names):
|
||||||
|
@ -92,6 +94,15 @@ def _set_use_name(node, node_name=""):
|
||||||
|
|
||||||
|
|
||||||
def _name_signal(tree, signal):
|
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 = []
|
elements = []
|
||||||
treepos = tree
|
treepos = tree
|
||||||
for step_name, step_n in signal.backtrace:
|
for step_name, step_n in signal.backtrace:
|
||||||
|
@ -110,10 +121,27 @@ def _name_signal(tree, signal):
|
||||||
|
|
||||||
|
|
||||||
def _build_pnd_from_tree(tree, signals):
|
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)
|
return dict((signal, _name_signal(tree, signal)) for signal in signals)
|
||||||
|
|
||||||
|
|
||||||
def _invert_pnd(pnd):
|
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()
|
inv_pnd = dict()
|
||||||
for k, v in pnd.items():
|
for k, v in pnd.items():
|
||||||
inv_pnd[v] = inv_pnd.get(v, [])
|
inv_pnd[v] = inv_pnd.get(v, [])
|
||||||
|
@ -122,6 +150,14 @@ def _invert_pnd(pnd):
|
||||||
|
|
||||||
|
|
||||||
def _list_conflicting_signals(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)
|
inv_pnd = _invert_pnd(pnd)
|
||||||
r = set()
|
r = set()
|
||||||
for k, v in inv_pnd.items():
|
for k, v in inv_pnd.items():
|
||||||
|
@ -131,38 +167,42 @@ def _list_conflicting_signals(pnd):
|
||||||
|
|
||||||
|
|
||||||
def _set_use_number(tree, signals):
|
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:
|
for signal in signals:
|
||||||
current = tree
|
current = tree
|
||||||
for step_name, step_n in signal.backtrace:
|
for step_name, step_n in signal.backtrace:
|
||||||
current = current.children[step_name]
|
current = current.children[step_name]
|
||||||
current.use_number = current.signal_count > len(current.numbers) and len(current.numbers) > 1
|
current.use_number = current.signal_count > len(current.numbers) and len(current.numbers) > 1
|
||||||
|
|
||||||
_debug = False
|
|
||||||
|
|
||||||
|
|
||||||
def _build_pnd_for_group(group_n, signals):
|
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)
|
basic_tree = _build_tree(signals)
|
||||||
_set_use_name(basic_tree)
|
_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)
|
pnd = _build_pnd_from_tree(basic_tree, signals)
|
||||||
|
|
||||||
# If there are conflicts, try splitting the tree by numbers on paths taken by conflicting signals.
|
# If there are conflicts, try splitting the tree by numbers on paths taken by conflicting signals.
|
||||||
conflicting_signals = _list_conflicting_signals(pnd)
|
conflicting_signals = _list_conflicting_signals(pnd)
|
||||||
if conflicting_signals:
|
if conflicting_signals:
|
||||||
_set_use_number(basic_tree, 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)
|
numbered_tree = _build_tree(signals, basic_tree)
|
||||||
_set_use_name(numbered_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)
|
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.
|
# ...then add number suffixes by DUID.
|
||||||
inv_pnd = _invert_pnd(pnd)
|
inv_pnd = _invert_pnd(pnd)
|
||||||
duid_suffixed = False
|
duid_suffixed = False
|
||||||
|
@ -171,13 +211,18 @@ def _build_pnd_for_group(group_n, signals):
|
||||||
duid_suffixed = True
|
duid_suffixed = True
|
||||||
for n, signal in enumerate(sorted(signals, key=lambda x: x.duid)):
|
for n, signal in enumerate(sorted(signals, key=lambda x: x.duid)):
|
||||||
pnd[signal] += str(n)
|
pnd[signal] += str(n)
|
||||||
if _debug and duid_suffixed:
|
|
||||||
print("namer: using DUID suffixes (group {0})".format(group_n))
|
|
||||||
|
|
||||||
return pnd
|
return pnd
|
||||||
|
|
||||||
|
|
||||||
def _build_signal_groups(signals):
|
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 = []
|
r = []
|
||||||
for signal in signals:
|
for signal in signals:
|
||||||
# Build chain of related signals.
|
# Build chain of related signals.
|
||||||
|
@ -199,6 +244,15 @@ def _build_signal_groups(signals):
|
||||||
|
|
||||||
|
|
||||||
def _build_pnd(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)
|
groups = _build_signal_groups(signals)
|
||||||
gpnds = [_build_pnd_for_group(n, gsignals) for n, gsignals in enumerate(groups)]
|
gpnds = [_build_pnd_for_group(n, gsignals) for n, gsignals in enumerate(groups)]
|
||||||
pnd = dict()
|
pnd = dict()
|
||||||
|
@ -216,6 +270,15 @@ def _build_pnd(signals):
|
||||||
|
|
||||||
|
|
||||||
def build_namespace(signals, reserved_keywords=set()):
|
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)
|
pnd = _build_pnd(signals)
|
||||||
ns = Namespace(pnd, reserved_keywords)
|
ns = Namespace(pnd, reserved_keywords)
|
||||||
# Register Signals with name_override.
|
# Register Signals with name_override.
|
||||||
|
@ -226,6 +289,25 @@ def build_namespace(signals, reserved_keywords=set()):
|
||||||
|
|
||||||
|
|
||||||
class Namespace:
|
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()):
|
def __init__(self, pnd, reserved_keywords=set()):
|
||||||
self.counts = {k: 1 for k in reserved_keywords}
|
self.counts = {k: 1 for k in reserved_keywords}
|
||||||
self.sigs = {}
|
self.sigs = {}
|
||||||
|
|
Loading…
Reference in New Issue