Merge pull request #1828 from enjoy-digital/verilog_improvements_2

Verilog improvements.
This commit is contained in:
enjoy-digital 2023-11-07 09:03:40 +01:00 committed by GitHub
commit d0bb837b7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 437 additions and 222 deletions

View File

@ -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 <sb@m-labs.hk>
# This file is Copyright (c) 2023 Florent Kermarrec <florent@enjoy-digital.fr>
# 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

View File

@ -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) |

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))

View File

@ -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}")