gen/fhdl/namer: Use _ for private functions and remove build_namespace.

This commit is contained in:
Florent Kermarrec 2023-11-06 16:21:33 +01:00
parent af508fddc5
commit ef4235a5d9
3 changed files with 76 additions and 55 deletions

View File

@ -8,9 +8,9 @@ from itertools import combinations
from migen.fhdl.structure import *
# Private Classes/Helpers --------------------------------------------------------------------------
# Hierarchy Node Class -----------------------------------------------------------------------------
class HierarchyNode:
class _HierarchyNode:
"""A node in a hierarchy tree used for signal name resolution.
Attributes:
@ -34,18 +34,18 @@ class HierarchyNode:
If numbering is used, sorts and stores all numbers associated with the base node.
Parameters:
name (str): The name of the current hierarchy level.
number (int): The number associated with the current hierarchy level.
use_number (bool): Flag indicating whether to use the number in the hierarchy.
current_base (HierarchyNode, optional): The base node for number usage information.
name (str): The name of the current hierarchy level.
number (int): The number associated with the current hierarchy level.
use_number (bool): Flag indicating whether to use the number in the hierarchy.
current_base (_HierarchyNode, optional): The base node for number usage information.
Returns:
HierarchyNode: The updated or created child node.
_HierarchyNode: The updated or created child node.
"""
# Create the appropriate key for the node.
key = (name, number) if use_number else name
# Use setdefault to either get the existing child node or create a new one.
child = self.children.setdefault(key, HierarchyNode())
child = self.children.setdefault(key, _HierarchyNode())
# Add the number to the set of numbers associated with this node.
child.numbers.add(number)
# Increment the count of signals that have traversed this node.
@ -55,18 +55,20 @@ class HierarchyNode:
child.all_numbers = sorted(current_base.numbers)
return child
def build_hierarchy_tree(signals, base_tree=None):
# Build Hierarchy Tree Function --------------------------------------------------------------------
def _build_hierarchy_tree(signals, base_tree=None):
"""
Constructs a hierarchical tree from signals, where each signal's backtrace contributes to the tree structure.
Parameters:
- signals (list): A list of signals to process.
- base_tree (HierarchyNode, optional): A base tree to refine with number usage information.
- signals (list): A list of signals to process.
- base_tree (_HierarchyNode, optional): A base tree to refine with number usage information.
Returns:
- HierarchyNode: The root node of the constructed tree.
- _HierarchyNode: The root node of the constructed tree.
"""
root = HierarchyNode()
root = _HierarchyNode()
# Iterate over each signal to be included in the tree.
for signal in signals:
@ -86,7 +88,9 @@ def build_hierarchy_tree(signals, base_tree=None):
return root
def determine_name_usage(node, node_name=""):
# Determine Name Usage Function --------------------------------------------------------------------
def _determine_name_usage(node, node_name=""):
"""
Recursively determines if node names should be used to ensure unique signal naming.
"""
@ -94,7 +98,7 @@ def determine_name_usage(node, node_name=""):
# Recursively collect names from children, identifying if any naming conflicts occur.
child_name_sets = {
child_name: determine_name_usage(child_node, child_name)
child_name: _determine_name_usage(child_node, child_name)
for child_name, child_node in node.children.items()
}
@ -118,7 +122,9 @@ def determine_name_usage(node, node_name=""):
return required_names
def build_signal_name_dict_from_tree(tree, signals):
# Build Signal Name Dict From Tree Function --------------------------------------------------------
def _build_signal_name_dict_from_tree(tree, signals):
"""
Constructs a mapping of signals to their names derived from a tree structure.
@ -155,7 +161,9 @@ def build_signal_name_dict_from_tree(tree, signals):
# Return the completed name dictionary.
return name_dict
def invert_signal_name_dict(name_dict):
# Invert Signal Name Dict Function -----------------------------------------------------------------
def _invert_signal_name_dict(name_dict):
"""
Inverts a signal-to-name dictionary to a name-to-signals dictionary.
@ -175,7 +183,9 @@ def invert_signal_name_dict(name_dict):
inverted_dict[name] = signals_with_name
return inverted_dict
def list_conflicting_signals(name_dict):
# List Conflicting Signals Function ----------------------------------------------------------------
def _list_conflicting_signals(name_dict):
"""Lists signals that have conflicting names in the provided mapping.
Parameters:
@ -185,7 +195,7 @@ def list_conflicting_signals(name_dict):
set: A set of signals that have name conflicts.
"""
# Invert the signal-to-name mapping to a name-to-signals mapping.
inverted_dict = invert_signal_name_dict(name_dict)
inverted_dict = _invert_signal_name_dict(name_dict)
# Prepare a set to hold signals with conflicting names.
conflicts = set()
@ -200,13 +210,15 @@ def list_conflicting_signals(name_dict):
# Return the set of all signals that have name conflicts.
return conflicts
def set_number_usage(tree, signals):
# Set Number Usage Function ------------------------------------------------------------------------
def _set_number_usage(tree, signals):
"""
Updates nodes to use number suffixes to resolve naming conflicts when necessary.
Parameters:
tree (HierarchyNode): The root node of the naming tree.
signals (iterable): Signals potentially causing naming conflicts.
tree (_HierarchyNode): The root node of the naming tree.
signals (iterable): Signals potentially causing naming conflicts.
Returns:
None: Tree is modified in place.
@ -222,7 +234,9 @@ def set_number_usage(tree, signals):
node.use_number = node.signal_count > len(node.numbers) > 1
# Once use_number is True, it stays True.
def build_signal_name_dict_for_group(group_number, signals):
# Build Signal Name Dict For Group Function --------------------------------------------------------
def _build_signal_name_dict_for_group(group_number, signals):
"""Builds a signal-to-name dictionary for a specific group of signals.
Parameters:
@ -234,37 +248,39 @@ def build_signal_name_dict_for_group(group_number, signals):
"""
def resolve_conflicts_and_rebuild_tree():
conflicts = list_conflicting_signals(name_dict)
conflicts = _list_conflicting_signals(name_dict)
if conflicts:
set_number_usage(tree, conflicts)
return build_hierarchy_tree(signals, tree)
_set_number_usage(tree, conflicts)
return _build_hierarchy_tree(signals, tree)
return tree
def disambiguate_signals_with_duid():
inv_name_dict = invert_signal_name_dict(name_dict)
inv_name_dict = _invert_signal_name_dict(name_dict)
for names, sigs in inv_name_dict.items():
if len(sigs) > 1:
for idx, sig in enumerate(sorted(sigs, key=lambda s: s.duid)):
name_dict[sig] += f"{idx}"
# Construct initial naming tree and name dictionary.
tree = build_hierarchy_tree(signals)
determine_name_usage(tree)
name_dict = build_signal_name_dict_from_tree(tree, signals)
tree = _build_hierarchy_tree(signals)
_determine_name_usage(tree)
name_dict = _build_signal_name_dict_from_tree(tree, signals)
# Address naming conflicts by introducing numbers.
tree = resolve_conflicts_and_rebuild_tree()
# Re-determine name usage and rebuild the name dictionary.
determine_name_usage(tree)
name_dict = build_signal_name_dict_from_tree(tree, signals)
_determine_name_usage(tree)
name_dict = _build_signal_name_dict_from_tree(tree, signals)
# Disambiguate remaining conflicts using signal's unique identifier (DUID).
disambiguate_signals_with_duid()
return name_dict
def build_signal_groups(signals):
# Build Signal Groups Function ---------------------------------------------------------------------
def _build_signal_groups(signals):
"""Organizes signals into related groups.
Parameters:
@ -293,7 +309,9 @@ def build_signal_groups(signals):
return grouped_signals
def build_hierarchical_name(signal, group_number, group_name_dict_mappings):
# Build Hierarchical Name Function -----------------------------------------------------------------
def _build_hierarchical_name(signal, group_number, group_name_dict_mappings):
"""Builds the hierarchical name for a signal.
Parameters:
@ -317,7 +335,9 @@ def build_hierarchical_name(signal, group_number, group_name_dict_mappings):
return hierarchical_name
def update_name_dict_with_group(name_dict, group_number, group_name_dict, group_name_dict_mappings):
# Update Name Dict With Group Function -------------------------------------------------------------
def _update_name_dict_with_group(name_dict, group_number, group_name_dict, group_name_dict_mappings):
"""Updates the name dictionary with hierarchical names for a specific group.
Parameters:
@ -330,43 +350,44 @@ def update_name_dict_with_group(name_dict, group_number, group_name_dict, group_
None: The name_dict is updated in place.
"""
for signal, name in group_name_dict.items():
hierarchical_name = build_hierarchical_name(
hierarchical_name = _build_hierarchical_name(
signal, group_number, group_name_dict_mappings
)
name_dict[signal] = hierarchical_name
# Build Signal Name Dict Function ------------------------------------------------------------------
def build_signal_name_dict(signals):
def _build_signal_name_dict(signals):
"""Builds a complete signal-to-name dictionary using a hierarchical tree.
Parameters:
signals (iterable): An iterable of all signals to be named.
tree (HierarchyNode): The root node of the tree used for name resolution.
signals (iterable): An iterable of all signals to be named.
tree (_HierarchyNode): The root node of the tree used for name resolution.
Returns:
dict: A complete dictionary mapping signals to their hierarchical names.
"""
# Group the signals based on their relationships.
groups = build_signal_groups(signals)
groups = _build_signal_groups(signals)
# Generate a name mapping for each group.
group_name_dict_mappings = [
build_signal_name_dict_for_group(group_number, group_signals)
_build_signal_name_dict_for_group(group_number, group_signals)
for group_number, group_signals in enumerate(groups)
]
# Create the final signal-to-name mapping.
name_dict = {}
for group_number, group_name_dict in enumerate(group_name_dict_mappings):
update_name_dict_with_group(name_dict, group_number, group_name_dict, group_name_dict_mappings)
_update_name_dict_with_group(name_dict, group_number, group_name_dict, group_name_dict_mappings)
return name_dict
# Public Classes/Helpers ---------------------------------------------------------------------------
# Signal Namespace Class ---------------------------------------------------------------------------
class SignalNamespace:
class _SignalNamespace:
"""
A SignalNamespace object manages unique naming for signals within a hardware design.
A _SignalNamespace 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
@ -432,6 +453,8 @@ class SignalNamespace:
# Return Name.
return sig_name
# Build Signal Namespace function ------------------------------------------------------------------
def build_signal_namespace(signals, reserved_keywords=set()):
"""Constructs a namespace where each signal is given a unique hierarchical name.
Parameters:
@ -443,14 +466,12 @@ def build_signal_namespace(signals, reserved_keywords=set()):
"""
# Create the primary signal-to-name dictionary.
pnd = build_signal_name_dict(signals)
pnd = _build_signal_name_dict(signals)
# Initialize the namespace with reserved keywords and the primary mapping.
namespace = SignalNamespace(pnd, reserved_keywords)
namespace = _SignalNamespace(pnd, reserved_keywords)
# Handle signals with overridden names, ensuring they are processed in a consistent order.
signals_with_name_override = filter(lambda s: s.name_override is not None, signals)
return namespace
build_namespace = build_signal_namespace

View File

@ -26,7 +26,7 @@ from migen.fhdl.conv_output import ConvOutput
from migen.fhdl.specials import Instance, Memory
from litex.gen import LiteXContext
from litex.gen.fhdl.namer import build_namespace
from litex.gen.fhdl.namer import build_signal_namespace
from litex.gen.fhdl.hierarchy import LiteXHierarchyExplorer
from litex.build.tools import get_litex_git_revision
@ -611,9 +611,9 @@ def convert(f, ios=set(), name="top", platform=None,
if io_name:
io.name_override = io_name
# Build NameSpace.
# ----------------
ns = build_namespace(
# Build Signal Namespace.
# ----------------------
ns = build_signal_namespace(
signals = (
list_signals(f) |
list_special_ios(f, ins=True, outs=True, inouts=True) |

View File

@ -12,7 +12,7 @@ import os
from collections import OrderedDict
import shutil
from litex.gen.fhdl.namer import build_namespace
from litex.gen.fhdl.namer import build_signal_namespace
def vcd_codes():
codechars = [chr(i) for i in range(33, 127)]
@ -71,7 +71,7 @@ class VCDWriter:
# write vcd header
header = ""
ns = build_namespace(self.codes.keys())
ns = build_signal_namespace(self.codes.keys())
for signal, code in self.codes.items():
name = ns.get_name(signal)
header += "$var wire {len} {code} {name} $end\n".format(name=name, code=code, len=len(signal))