From 1e805a8789114e36364e35c1470bb77ea55bb2bf Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 6 Nov 2023 09:38:17 +0100 Subject: [PATCH] fhdl/namer: Remove debug and add docstring comments. --- litex/gen/fhdl/namer.py | 168 ++++++++++++++++++++++++++++++---------- 1 file changed, 125 insertions(+), 43 deletions(-) diff --git a/litex/gen/fhdl/namer.py b/litex/gen/fhdl/namer.py index 2596e17da..1a4b51fac 100644 --- a/litex/gen/fhdl/namer.py +++ b/litex/gen/fhdl/namer.py @@ -4,44 +4,37 @@ # This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq # 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 = {}