diff --git a/litex/gen/fhdl/namer.py b/litex/gen/fhdl/namer.py index 2596e17da..9b31d03d2 100644 --- a/litex/gen/fhdl/namer.py +++ b/litex/gen/fhdl/namer.py @@ -2,271 +2,477 @@ # This file is part of LiteX (Adapted from Migen for LiteX usage). # # This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq +# This file is Copyright (c) 2023 Florent Kermarrec # SPDX-License-Identifier: BSD-2-Clause -from collections import OrderedDict from itertools import combinations from migen.fhdl.structure import * +# Hierarchy Node Class ----------------------------------------------------------------------------- -class _Node: +class _HierarchyNode: + """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() + self.children = {} + self.all_numbers = [] + def update(self, name, number, use_number, current_base=None): + """ + Updates or creates a hierarchy node based on the current position, name, and number. + If numbering is used, sorts and stores all numbers associated with the base node. -def _display_tree(filename, tree): - from migen.util.treeviz import RenderNode + 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. - 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) + Returns: + _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()) + # 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. + child.signal_count += 1 + # If numbering is used, sort and store all numbers associated with the base node. + if use_number and current_base: + child.all_numbers = sorted(current_base.numbers) + return child - top = _to_render_node("top", tree) - top.to_svg(filename) +# 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. -def _build_tree(signals, basic_tree=None): - root = _Node() + Parameters: + - 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. + """ + root = _HierarchyNode() + + # Iterate over each signal to be included in the tree. for signal in signals: - current_b = basic_tree - current = root - current.signal_count += 1 + current = root + current_base = base_tree + + # Traverse or build the hierarchy of nodes based on the signal's backtrace. for name, number in signal.backtrace: - if basic_tree is None: - use_number = False - else: - current_b = current_b.children[name] - use_number = current_b.use_number - if use_number: - key = (name, number) - else: - key = name - try: - current = current.children[key] - except KeyError: - new = _Node() - current.children[key] = new - current = new - current.numbers.add(number) - if use_number: - current.all_numbers = sorted(current_b.numbers) - current.signal_count += 1 + # Decide whether to use a numbered key based on the base tree. + use_number = False + if current_base: + current_base = current_base.children.get(name) + use_number = current_base.use_number if current_base else False + + # Update the current node in the hierarchy. + current = current.update(name, number, use_number, current_base) + return root +# Determine Name Usage Function -------------------------------------------------------------------- -def _set_use_name(node, node_name=""): - 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): - node.children[c1_prefix].use_name = True - node.children[c2_prefix].use_name = True - r = set() - for c_prefix, c_names in cnames: - if node.children[c_prefix].use_name: - for c_name in c_names: - r.add((c_prefix, ) + c_name) +def _determine_name_usage(node, node_name=""): + """ + Recursively determines if node names should be used to ensure unique signal naming. + """ + required_names = set() # This will accumulate all names that ensure unique identification of signals. + + # Recursively collect names from children, identifying if any naming conflicts occur. + child_name_sets = { + child_name: _determine_name_usage(child_node, child_name) + for child_name, child_node in node.children.items() + } + + # Check for naming conflicts between all pairs of children. + for (child1_name, names1), (child2_name, names2) in combinations(child_name_sets.items(), 2): + if names1 & names2: # If there's an intersection, we have a naming conflict. + node.children[child1_name].use_name = node.children[child2_name].use_name = True + + # Collect names, prepending child's name if necessary. + for child_name, child_names in child_name_sets.items(): + if node.children[child_name].use_name: + # Prepend the child's name to ensure uniqueness. + required_names.update((child_name,) + name for name in child_names) else: - r |= c_names + required_names.update(child_names) - if node.signal_count > sum(c.signal_count for c in node.children.values()): + # If this node has its own signals, ensure its name is used. + if node.signal_count > sum(child.signal_count for child in node.children.values()): node.use_name = True - r.add((node_name, )) + required_names.add((node_name,)) # Add this node's name only if it has additional signals. - return r + return required_names +# Build Signal Name Dict From Tree Function -------------------------------------------------------- -def _name_signal(tree, signal): - elements = [] - treepos = tree - for step_name, step_n in signal.backtrace: - try: - treepos = treepos.children[(step_name, step_n)] - use_number = True - except KeyError: - treepos = treepos.children[step_name] - use_number = False - if treepos.use_name: - elname = step_name - if use_number: - elname += str(treepos.all_numbers.index(step_n)) - elements.append(elname) - return "_".join(elements) +def _build_signal_name_dict_from_tree(tree, signals): + """ + Constructs a mapping of signals to their names derived from a tree structure. + This mapping is used to identify signals by their unique hierarchical path within the tree. The + tree structure has 'use_name' flags that influence the naming process. + """ -def _build_pnd_from_tree(tree, signals): - return dict((signal, _name_signal(tree, signal)) for signal in signals) + # Initialize a dictionary to hold the signal names. + name_dict = {} - -def _invert_pnd(pnd): - inv_pnd = dict() - for k, v in pnd.items(): - inv_pnd[v] = inv_pnd.get(v, []) - inv_pnd[v].append(k) - return inv_pnd - - -def _list_conflicting_signals(pnd): - inv_pnd = _invert_pnd(pnd) - r = set() - for k, v in inv_pnd.items(): - if len(v) > 1: - r.update(v) - return r - - -def _set_use_number(tree, signals): + # Process each signal to build its hierarchical name. for signal in signals: - current = tree + # Collect name parts for the hierarchical name. + elements = [] + # Start traversing the tree from the root. + treepos = tree + + # Walk through the signal's history to assemble its name. 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 + # Navigate the tree according to the signal's path. + treepos = treepos.children.get((step_name, step_n)) or treepos.children.get(step_name) + # Check if the number is part of the name based on the tree node. + use_number = step_n in treepos.all_numbers -_debug = False + # If the tree node's name is to be used, add it to the elements. + if treepos.use_name: + # Create the name part, including the number if necessary. + element_name = step_name if not use_number else f"{step_name}{treepos.all_numbers.index(step_n)}" + elements.append(element_name) + # Combine the name parts into the signal's full name. + name_dict[signal] = "_".join(elements) -def _build_pnd_for_group(group_n, signals): - 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) + # Return the completed name dictionary. + return name_dict - # 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)) +# Invert Signal Name Dict Function ----------------------------------------------------------------- - # ...then add number suffixes by DUID. - inv_pnd = _invert_pnd(pnd) - duid_suffixed = False - for name, signals in inv_pnd.items(): +def _invert_signal_name_dict(name_dict): + """ + Inverts a signal-to-name dictionary to a name-to-signals dictionary. + + Parameters: + name_dict (dict): A dictionary mapping signals to names. + + Returns: + dict: An inverted dictionary where keys are names and values are lists of signals with that name. + """ + inverted_dict = {} + for signal, name in name_dict.items(): + # Get the list of signals for the current name, or initialize it if not present. + signals_with_name = inverted_dict.get(name, []) + # Add the current signal to the list. + signals_with_name.append(signal) + # Place the updated list back in the dictionary. + inverted_dict[name] = signals_with_name + return inverted_dict + +# List Conflicting Signals Function ---------------------------------------------------------------- + +def _list_conflicting_signals(name_dict): + """Lists signals that have conflicting names in the provided mapping. + + Parameters: + name_dict (dict): A dictionary mapping signals to names. + + Returns: + 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) + + # Prepare a set to hold signals with conflicting names. + conflicts = set() + + # Iterate through the inverted dictionary. + for name, signals in inverted_dict.items(): + # If there is more than one signal for this name, it means there is a conflict. if len(signals) > 1: - 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)) + # Add all conflicting signals to our set. + conflicts.update(signals) - return pnd + # Return the set of all signals that have name conflicts. + return conflicts +# 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. + + Returns: + None: Tree is modified in place. + """ + for signal in signals: + node = tree # Start traversal from the root node. + + # Traverse the signal's path and decide if numbering is needed. + for step_name, _ in signal.backtrace: + node = node.children[step_name] # Proceed to the next node. + # Set use_number if signal count exceeds unique identifiers. + if not node.use_number: + node.use_number = node.signal_count > len(node.numbers) > 1 + # Once use_number is True, it stays True. + +# 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: + group_number (int): The group number. + signals (iterable): The signals within the group. + + Returns: + dict: A dictionary mapping signals to their hierarchical names. + """ + + def resolve_conflicts_and_rebuild_tree(): + conflicts = _list_conflicting_signals(name_dict) + if conflicts: + _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) + 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) + + # 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) + + # Disambiguate remaining conflicts using signal's unique identifier (DUID). + disambiguate_signals_with_duid() + + return name_dict + +# Build Signal Groups Function --------------------------------------------------------------------- def _build_signal_groups(signals): - r = [] + """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. + """ + grouped_signals = [] + + # Create groups of related signals. for signal in signals: - # Build chain of related signals. - related_list = [] - cur_signal = signal - while cur_signal is not None: - related_list.insert(0, cur_signal) - cur_signal = cur_signal.related - # Add to groups. - for _ in range(len(related_list) - len(r)): - r.append(set()) - for target_set, source_signal in zip(r, related_list): - target_set.add(source_signal) - # With the algorithm above and a list of all signals, a signal appears in all groups of a lower - # number than its. Make signals appear only in their group of highest number. - for s1, s2 in zip(r, r[1:]): - s1 -= s2 - return r + chain = [] + # Trace back the chain of related signals. + while signal is not None: + chain.insert(0, signal) + signal = signal.related + # Ensure there's a set for each level of relation. + while len(grouped_signals) < len(chain): + grouped_signals.append(set()) -def _build_pnd(signals): + # Assign signals to their respective group. + for group, sig in zip(grouped_signals, chain): + group.add(sig) + + return grouped_signals + +# Build Hierarchical Name Function ----------------------------------------------------------------- + +def _build_hierarchical_name(signal, group_number, group_name_dict_mappings): + """Builds the hierarchical name for a signal. + + Parameters: + signal (Signal): The signal to build the name for. + group_number (int): The group number of the signal. + group_name_dict_mappings (list): The list of all group name dictionaries. + + Returns: + str: The hierarchical name for the signal. + """ + hierarchical_name = group_name_dict_mappings[group_number][signal] + current_group_number = group_number + current_signal = signal + + # Traverse up the signal's group relationships to prepend parent names. + while current_signal.related is not None: + current_signal = current_signal.related + current_group_number -= 1 + parent_name = group_name_dict_mappings[current_group_number][current_signal] + hierarchical_name = f"{parent_name}_{hierarchical_name}" + + return hierarchical_name + +# 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: + name_dict (dict): The dictionary to update. + group_number (int): The current group number. + group_name_dict (dict): The name dictionary for the current group. + group_name_dict_mappings (list): The list of all group name dictionaries. + + Returns: + None: The name_dict is updated in place. + """ + for signal, name in group_name_dict.items(): + 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): + """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. + + Returns: + dict: A complete dictionary mapping signals to their hierarchical names. + """ + # Group the signals based on their relationships. groups = _build_signal_groups(signals) - gpnds = [_build_pnd_for_group(n, gsignals) for n, gsignals in enumerate(groups)] - pnd = dict() - for gn, gpnd in enumerate(gpnds): - for signal, name in gpnd.items(): - result = name - cur_gn = gn - cur_signal = signal - while cur_signal.related is not None: - cur_signal = cur_signal.related - cur_gn -= 1 - result = gpnds[cur_gn][cur_signal] + "_" + result - pnd[signal] = result - return pnd + # Generate a name mapping for each group. + group_name_dict_mappings = [ + _build_signal_name_dict_for_group(group_number, group_signals) + for group_number, group_signals in enumerate(groups) + ] -def build_namespace(signals, reserved_keywords=set()): - pnd = _build_pnd(signals) - ns = Namespace(pnd, reserved_keywords) - # Register Signals with name_override. - swno = {signal for signal in signals if signal.name_override is not None} - for signal in sorted(swno, key=lambda x: x.duid): - ns.get_name(signal) - return ns + # 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) + return name_dict -class Namespace: - def __init__(self, pnd, reserved_keywords=set()): - self.counts = {k: 1 for k in reserved_keywords} - self.sigs = {} - self.pnd = pnd +# Signal Namespace Class --------------------------------------------------------------------------- + +class _SignalNamespace: + """ + 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 + 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. + name_dict (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, name_dict, reserved_keywords=set()): + self.counts = {k: 1 for k in reserved_keywords} + self.sigs = {} + self.name_dict = name_dict self.clock_domains = dict() def get_name(self, sig): - # Get name of a Clock Signal. - # --------------------------- - if isinstance(sig, ClockSignal): - sig = self.clock_domains[sig.cd].clk - - # Get name of a Reset Signal. - # --------------------------- - if isinstance(sig, ResetSignal): - sig = self.clock_domains[sig.cd].rst + # Handle Clock and Reset Signals. + # ------------------------------- + if isinstance(sig, (ClockSignal, ResetSignal)): + # Retrieve the clock domain from the dictionary. + domain = self.clock_domains.get(sig.cd) + if domain is None: + raise ValueError(f"Clock Domain '{sig.cd}' not found.") + # Assign the appropriate signal from the clock domain. + sig = domain.clk if isinstance(sig, ClockSignal) else domain.rst + # If the signal is None, the clock domain is missing a clock or reset. if sig is None: - msg = f"Clock Domain {sig.cd} is reset-less, can't obtain name" - raise ValueError(msg) + raise ValueError(f"Clock Domain '{sig.cd}' is reset-less, can't obtain name.") # Get name of a Regular Signal. # ----------------------------- # Use Name's override when set... if sig.name_override is not None: sig_name = sig.name_override - # ... else get Name from pnd. + # ... else get Name from name_dict. else: - sig_name = self.pnd[sig] + sig_name = self.name_dict.get(sig) + # If the signal is not in the name_dict, raise an error. + if sig_name is None: + raise ValueError(f"Signal '{sig}' not found in name dictionary.") - # Check/Add numbering suffix when required. - # ----------------------------------------- - try: - n = self.sigs[sig] - except KeyError: - try: - n = self.counts[sig_name] - except KeyError: - n = 0 - self.sigs[sig] = n + + # Check/Add numbering when required. + # ---------------------------------- + # Retrieve the current count for the signal name, defaulting to 0. + n = self.sigs.get(sig) + if n is None: + n = self.counts.get(sig_name, 0) + self.sigs[sig] = n self.counts[sig_name] = n + 1 - suffix = "" if n == 0 else f"_{n}" + # If the count is greater than 0, append it to the signal name. + if n > 0: + sig_name += f"_{n}" # Return Name. - return sig_name + suffix + 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: + 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. + """ + + # Create the primary signal-to-name dictionary. + pnd = _build_signal_name_dict(signals) + + # Initialize the namespace with reserved keywords and the primary mapping. + 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 diff --git a/litex/gen/fhdl/verilog.py b/litex/gen/fhdl/verilog.py index ce52fa355..12d8953f8 100644 --- a/litex/gen/fhdl/verilog.py +++ b/litex/gen/fhdl/verilog.py @@ -17,6 +17,7 @@ import time import datetime import collections +from enum import IntEnum from operator import itemgetter from migen.fhdl.structure import * @@ -26,7 +27,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 @@ -181,19 +182,22 @@ def _generate_signal(ns, s): # Print Operator ----------------------------------------------------------------------------------- -(UNARY, BINARY, TERNARY) = (1, 2, 3) +class OperatorType(IntEnum): + UNARY = 1 + BINARY = 2 + TERNARY = 3 def _generate_operator(ns, node): operator = node.op operands = node.operands arity = len(operands) - assert arity in [UNARY, BINARY, TERNARY] + assert arity in [item.value for item in OperatorType] def to_signed(r): return f"$signed({{1'd0, {r}}})" # Unary Operator. - if arity == UNARY: + if arity == OperatorType.UNARY: r1, s1 = _generate_expression(ns, operands[0]) # Negation Operator. if operator == "-": @@ -206,7 +210,7 @@ def _generate_operator(ns, node): s = s1 # Binary Operator. - if arity == BINARY: + if arity == OperatorType.BINARY: r1, s1 = _generate_expression(ns, operands[0]) r2, s2 = _generate_expression(ns, operands[1]) # Convert all expressions to signed when at least one is signed. @@ -219,7 +223,7 @@ def _generate_operator(ns, node): s = s1 or s2 # Ternary Operator. - if arity == TERNARY: + if arity == OperatorType.TERNARY: assert operator == "m" r1, s1 = _generate_expression(ns, operands[0]) r2, s2 = _generate_expression(ns, operands[1]) @@ -292,17 +296,21 @@ def _generate_expression(ns, node): # NODES # # ------------------------------------------------------------------------------------------------ # -(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3) +class AssignType(IntEnum): + BLOCKING = 0 + NON_BLOCKING = 1 + SIGNAL = 2 def _generate_node(ns, at, level, node, target_filter=None): + assert at in [item.value for item in AssignType] if target_filter is not None and target_filter not in list_targets(node): return "" # Assignment. elif isinstance(node, _Assign): - if at == _AT_BLOCKING: + if at == AssignType.BLOCKING: assignment = " = " - elif at == _AT_NONBLOCKING: + elif at == AssignType.NON_BLOCKING: assignment = " <= " elif is_variable(node.l): assignment = " = " @@ -478,11 +486,11 @@ def _generate_combinatorial_logic_sim(f, ns): for n, (t, stmts) in enumerate(target_stmt_map.items()): assert isinstance(t, Signal) if _use_wire(stmts): - r += "assign " + _generate_node(ns, _AT_BLOCKING, 0, stmts[0]) + r += "assign " + _generate_node(ns, AssignType.BLOCKING, 0, stmts[0]) else: r += "always @(*) begin\n" r += _tab + ns.get_name(t) + " <= " + _generate_expression(ns, t.reset)[0] + ";\n" - r += _generate_node(ns, _AT_NONBLOCKING, 1, stmts, t) + r += _generate_node(ns, AssignType.NON_BLOCKING, 1, stmts, t) r += "end\n" r += "\n" return r @@ -494,12 +502,12 @@ def _generate_combinatorial_logic_synth(f, ns): for n, g in enumerate(groups): if _use_wire(g[1]): - r += "assign " + _generate_node(ns, _AT_BLOCKING, 0, g[1][0]) + r += "assign " + _generate_node(ns, AssignType.BLOCKING, 0, g[1][0]) else: r += "always @(*) begin\n" for t in sorted(g[0], key=lambda x: ns.get_name(x)): r += _tab + ns.get_name(t) + " <= " + _generate_expression(ns, t.reset)[0] + ";\n" - r += _generate_node(ns, _AT_NONBLOCKING, 1, g[1]) + r += _generate_node(ns, AssignType.NON_BLOCKING, 1, g[1]) r += "end\n" r += "\n" return r @@ -512,7 +520,7 @@ def _generate_synchronous_logic(f, ns): r = "" for k, v in sorted(f.sync.items(), key=itemgetter(0)): r += "always @(posedge " + ns.get_name(f.clock_domains[k].clk) + ") begin\n" - r += _generate_node(ns, _AT_SIGNAL, 1, v) + r += _generate_node(ns, AssignType.SIGNAL, 1, v) r += "end\n\n" return r @@ -611,9 +619,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) | diff --git a/litex/gen/sim/vcd.py b/litex/gen/sim/vcd.py index 60aa33d49..1c3f27ad8 100644 --- a/litex/gen/sim/vcd.py +++ b/litex/gen/sim/vcd.py @@ -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)) diff --git a/litex/soc/interconnect/stream.py b/litex/soc/interconnect/stream.py index c3759641e..c7d521221 100644 --- a/litex/soc/interconnect/stream.py +++ b/litex/soc/interconnect/stream.py @@ -244,8 +244,9 @@ class AsyncFIFO(_FIFOWrapper): # ClockDomainCrossing ------------------------------------------------------------------------------ -class ClockDomainCrossing(LiteXModule): +class ClockDomainCrossing(LiteXModule, DUID): def __init__(self, layout, cd_from="sys", cd_to="sys", depth=None, buffered=False, with_common_rst=False): + DUID.__init__(self) self.sink = Endpoint(layout) self.source = Endpoint(layout) @@ -259,7 +260,7 @@ class ClockDomainCrossing(LiteXModule): else: if with_common_rst: # Create intermediate Clk Domains and generate a common Rst. - _cd_id = id(self) # FIXME: Improve, used to allow build with anonymous modules. + _cd_id = self.duid # Use duid for a deterministic unique ID. _cd_rst = Signal() _cd_from = ClockDomain(f"from{_cd_id}") _cd_to = ClockDomain(f"to{_cd_id}")