--- /dev/null
+Unless otherwise noted, Migen is copyright (C) 2011-2013 Sebastien Bourdeauducq.
+The simulation extension (as mentioned in the comments at the beginning of the
+corresponding source files) is copyright (C) 2012 Vermeer Manufacturing Co. All
+rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Other authors retain ownership of their contributions. If a submission can
+reasonably be considered independently copyrightable, it's yours and we
+encourage you to claim it with appropriate copyright notices. This submission
+then falls under the "otherwise noted" category. All submissions are strongly
+encouraged to use the two-clause BSD license reproduced above.
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import *
+from litex.gen.fhdl.specials import *
+from litex.gen.fhdl.bitcontainer import *
+from litex.gen.fhdl.decorators import *
+
+from litex.gen.genlib.record import *
+from litex.gen.genlib.fsm import *
--- /dev/null
+from litex.gen.fhdl import structure as f
+
+
+__all__ = ["log2_int", "bits_for", "value_bits_sign"]
+
+
+def log2_int(n, need_pow2=True):
+ l = 1
+ r = 0
+ while l < n:
+ l *= 2
+ r += 1
+ if need_pow2 and l != n:
+ raise ValueError("Not a power of 2")
+ return r
+
+
+def bits_for(n, require_sign_bit=False):
+ if n > 0:
+ r = log2_int(n + 1, False)
+ else:
+ require_sign_bit = True
+ r = log2_int(-n, False)
+ if require_sign_bit:
+ r += 1
+ return r
+
+
+def value_bits_sign(v):
+ """Bit length and signedness of a value.
+
+ Parameters
+ ----------
+ v : Value
+
+ Returns
+ -------
+ int, bool
+ Number of bits required to store `v` or available in `v`, followed by
+ whether `v` has a sign bit (included in the bit count).
+
+ Examples
+ --------
+ >>> value_bits_sign(f.Signal(8))
+ 8, False
+ >>> value_bits_sign(C(0xaa))
+ 8, False
+ """
+ if isinstance(v, (f.Constant, f.Signal)):
+ return v.nbits, v.signed
+ elif isinstance(v, (f.ClockSignal, f.ResetSignal)):
+ return 1, False
+ elif isinstance(v, f._Operator):
+ obs = list(map(value_bits_sign, v.operands))
+ if v.op == "+" or v.op == "-":
+ if not obs[0][1] and not obs[1][1]:
+ # both operands unsigned
+ return max(obs[0][0], obs[1][0]) + 1, False
+ elif obs[0][1] and obs[1][1]:
+ # both operands signed
+ return max(obs[0][0], obs[1][0]) + 1, True
+ elif not obs[0][1] and obs[1][1]:
+ # first operand unsigned (add sign bit), second operand signed
+ return max(obs[0][0] + 1, obs[1][0]) + 1, True
+ else:
+ # first signed, second operand unsigned (add sign bit)
+ return max(obs[0][0], obs[1][0] + 1) + 1, True
+ elif v.op == "*":
+ if not obs[0][1] and not obs[1][1]:
+ # both operands unsigned
+ return obs[0][0] + obs[1][0], False
+ elif obs[0][1] and obs[1][1]:
+ # both operands signed
+ return obs[0][0] + obs[1][0] - 1, True
+ else:
+ # one operand signed, the other unsigned (add sign bit)
+ return obs[0][0] + obs[1][0] + 1 - 1, True
+ elif v.op == "<<<":
+ if obs[1][1]:
+ extra = 2**(obs[1][0] - 1) - 1
+ else:
+ extra = 2**obs[1][0] - 1
+ return obs[0][0] + extra, obs[0][1]
+ elif v.op == ">>>":
+ if obs[1][1]:
+ extra = 2**(obs[1][0] - 1)
+ else:
+ extra = 0
+ return obs[0][0] + extra, obs[0][1]
+ elif v.op == "&" or v.op == "^" or v.op == "|":
+ if not obs[0][1] and not obs[1][1]:
+ # both operands unsigned
+ return max(obs[0][0], obs[1][0]), False
+ elif obs[0][1] and obs[1][1]:
+ # both operands signed
+ return max(obs[0][0], obs[1][0]), True
+ elif not obs[0][1] and obs[1][1]:
+ # first operand unsigned (add sign bit), second operand signed
+ return max(obs[0][0] + 1, obs[1][0]), True
+ else:
+ # first signed, second operand unsigned (add sign bit)
+ return max(obs[0][0], obs[1][0] + 1), True
+ elif v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" \
+ or v.op == ">" or v.op == ">=":
+ return 1, False
+ elif v.op == "~":
+ return obs[0]
+ else:
+ raise TypeError
+ elif isinstance(v, f._Slice):
+ return v.stop - v.start, value_bits_sign(v.value)[1]
+ elif isinstance(v, f.Cat):
+ return sum(value_bits_sign(sv)[0] for sv in v.l), False
+ elif isinstance(v, f.Replicate):
+ return (value_bits_sign(v.v)[0])*v.n, False
+ elif isinstance(v, f._ArrayProxy):
+ bsc = list(map(value_bits_sign, v.choices))
+ return max(bs[0] for bs in bsc), any(bs[1] for bs in bsc)
+ else:
+ raise TypeError("Can not calculate bit length of {} {}".format(
+ type(v), v))
--- /dev/null
+from operator import itemgetter
+
+
+class ConvOutput:
+ def __init__(self):
+ self.main_source = ""
+ self.data_files = dict()
+
+ def set_main_source(self, src):
+ self.main_source = src
+
+ def add_data_file(self, filename_base, content):
+ filename = filename_base
+ i = 1
+ while filename in self.data_files:
+ parts = filename_base.split(".", maxsplit=1)
+ parts[0] += "_" + str(i)
+ filename = ".".join(parts)
+ i += 1
+ self.data_files[filename] = content
+ return filename
+
+ def __str__(self):
+ r = self.main_source + "\n"
+ for filename, content in sorted(self.data_files.items(),
+ key=itemgetter(0)):
+ r += filename + ":\n" + content
+ return r
+
+ def write(self, main_filename):
+ with open(main_filename, "w") as f:
+ f.write(self.main_source)
+ for filename, content in self.data_files.items():
+ with open(filename, "w") as f:
+ f.write(content)
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+from litex.gen.fhdl.tools import insert_reset, rename_clock_domain
+
+
+__all__ = ["CEInserter", "ResetInserter", "ClockDomainsRenamer",
+ "ModuleTransformer"]
+
+
+class ModuleTransformer:
+ # overload this in derived classes
+ def transform_instance(self, i):
+ pass
+
+ # overload this in derived classes
+ def transform_fragment(self, i, f):
+ pass
+
+ def wrap_class(self, victim):
+ class Wrapped(victim):
+ def __init__(i, *args, **kwargs):
+ victim.__init__(i, *args, **kwargs)
+ self.transform_instance(i)
+
+ def get_fragment(i):
+ f = victim.get_fragment(i)
+ self.transform_fragment(i, f)
+ return f
+
+ Wrapped.__name__ = victim.__name__
+ # "{}_{}".format(self.__class__.__name__, victim.__name__)
+ return Wrapped
+
+ def wrap_instance(self, victim):
+ self.transform_instance(victim)
+ orig_get_fragment = victim.get_fragment
+
+ def get_fragment():
+ f = orig_get_fragment()
+ self.transform_fragment(victim, f)
+ return f
+
+ victim.get_fragment = get_fragment
+ return victim
+
+ def __call__(self, victim):
+ if isinstance(victim, Module):
+ return self.wrap_instance(victim)
+ else:
+ return self.wrap_class(victim)
+
+
+class ControlInserter(ModuleTransformer):
+ control_name = None # override this
+
+ def __init__(self, clock_domains=None):
+ self.clock_domains = clock_domains
+
+ def transform_instance(self, i):
+ if self.clock_domains is None:
+ ctl = Signal(name=self.control_name)
+ assert not hasattr(i, self.control_name)
+ setattr(i, self.control_name, ctl)
+ else:
+ for cd in self.clock_domains:
+ name = self.control_name + "_" + cd
+ ctl = Signal(name=name)
+ assert not hasattr(i, name)
+ setattr(i, name, ctl)
+
+ def transform_fragment(self, i, f):
+ if self.clock_domains is None:
+ if len(f.sync) != 1:
+ raise ValueError("Control signal clock domains must be specified when module has more than one domain")
+ cdn = list(f.sync.keys())[0]
+ to_insert = [(getattr(i, self.control_name), cdn)]
+ else:
+ to_insert = [(getattr(i, self.control_name + "_" + cdn), cdn)
+ for cdn in self.clock_domains]
+ self.transform_fragment_insert(i, f, to_insert)
+
+
+class CEInserter(ControlInserter):
+ control_name = "ce"
+
+ def transform_fragment_insert(self, i, f, to_insert):
+ for ce, cdn in to_insert:
+ f.sync[cdn] = [If(ce, *f.sync[cdn])]
+
+
+class ResetInserter(ControlInserter):
+ control_name = "reset"
+
+ def transform_fragment_insert(self, i, f, to_insert):
+ for reset, cdn in to_insert:
+ f.sync[cdn] = insert_reset(reset, f.sync[cdn])
+
+
+class ClockDomainsRenamer(ModuleTransformer):
+ def __init__(self, cd_remapping):
+ if isinstance(cd_remapping, str):
+ cd_remapping = {"sys": cd_remapping}
+ self.cd_remapping = cd_remapping
+
+ def transform_fragment(self, i, f):
+ for old, new in self.cd_remapping.items():
+ rename_clock_domain(f, old, new)
--- /dev/null
+from collections import OrderedDict, namedtuple
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.namer import build_namespace
+from litex.gen.fhdl.tools import list_special_ios
+from litex.gen.fhdl.structure import _Fragment
+from litex.gen.fhdl.conv_output import ConvOutput
+
+
+_Port = namedtuple("_Port", "name direction")
+_Cell = namedtuple("_Cell", "name ports")
+_Property = namedtuple("_Property", "name value")
+_Instance = namedtuple("_Instance", "name cell properties")
+_NetBranch = namedtuple("_NetBranch", "portname instancename")
+
+
+def _write_cells(cells):
+ r = ""
+ for cell in cells:
+ r += """
+ (cell {0.name}
+ (cellType GENERIC)
+ (view view_1
+ (viewType NETLIST)
+ (interface""".format(cell)
+ for port in cell.ports:
+ r += """
+ (port {0.name} (direction {0.direction}))""".format(port)
+ r += """
+ )
+ )
+ )"""
+ return r
+
+
+def _write_io(ios):
+ r = ""
+ for s in ios:
+ r += """
+ (port {0.name} (direction {0.direction}))""".format(s)
+ return r
+
+
+def _write_instantiations(instances, cell_library):
+ instantiations = ""
+ for instance in instances:
+ instantiations += """
+ (instance {0.name}
+ (viewRef view_1 (cellRef {0.cell} (libraryRef {1})))""".format(instance, cell_library)
+ for prop in instance.properties:
+ instantiations += """
+ (property {0} (string "{1}"))""".format(prop.name, prop.value)
+ instantiations += """
+ )"""
+ return instantiations
+
+
+def _write_connections(connections):
+ r = ""
+ for netname, branches in connections.items():
+ r += """
+ (net {0}
+ (joined""".format(netname)
+ for branch in branches:
+ r += """
+ (portRef {0}{1})""".format(branch.portname, "" if branch.instancename == "" else " (instanceRef {})".format(branch.instancename))
+ r += """
+ )
+ )"""
+ return r
+
+
+def _write_edif(cells, ios, instances, connections, cell_library, design_name, part, vendor):
+ r = """(edif {0}
+ (edifVersion 2 0 0)
+ (edifLevel 0)
+ (keywordMap (keywordLevel 0))
+ (external {1}
+ (edifLevel 0)
+ (technology (numberDefinition))""".format(design_name, cell_library)
+ r += _write_cells(cells)
+ r += """
+ )
+ (library {0}_lib
+ (edifLevel 0)
+ (technology (numberDefinition))
+ (cell {0}
+ (cellType GENERIC)
+ (view view_1
+ (viewType NETLIST)
+ (interface""".format(design_name)
+ r += _write_io(ios)
+ r += """
+ (designator "{0}")
+ )
+ (contents""".format(part)
+ r += _write_instantiations(instances, cell_library)
+ r += _write_connections(connections)
+ r += """
+ )
+ )
+ )
+ )
+ (design {0}
+ (cellRef {0} (libraryRef {0}_lib))
+ (property PART (string "{1}") (owner "{2}"))
+ )
+)""".format(design_name, part, vendor)
+
+ return r
+
+
+def _generate_cells(f):
+ cell_dict = OrderedDict()
+ for special in f.specials:
+ if isinstance(special, Instance):
+ port_list = []
+ for port in special.items:
+ if isinstance(port, Instance.Input):
+ port_list.append(_Port(port.name, "INPUT"))
+ elif isinstance(port, Instance.Output):
+ port_list.append(_Port(port.name, "OUTPUT"))
+ elif isinstance(port, Instance.InOut):
+ port_list.append(_Port(port.name, "INOUT"))
+ elif isinstance(port, Instance.Parameter):
+ pass
+ else:
+ raise NotImplementedError("Unsupported instance item")
+ if special.of in cell_dict:
+ if set(port_list) != set(cell_dict[special.of]):
+ raise ValueError("All instances must have the same ports for EDIF conversion")
+ else:
+ cell_dict[special.of] = port_list
+ else:
+ raise ValueError("EDIF conversion can only handle synthesized fragments")
+ return [_Cell(k, v) for k, v in cell_dict.items()]
+
+
+def _generate_instances(f, ns):
+ instances = []
+ for special in f.specials:
+ if isinstance(special, Instance):
+ props = []
+ for prop in special.items:
+ if isinstance(prop, Instance.Input):
+ pass
+ elif isinstance(prop, Instance.Output):
+ pass
+ elif isinstance(prop, Instance.InOut):
+ pass
+ elif isinstance(prop, Instance.Parameter):
+ props.append(_Property(name=prop.name, value=prop.value))
+ else:
+ raise NotImplementedError("Unsupported instance item")
+ instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props))
+ else:
+ raise ValueError("EDIF conversion can only handle synthesized fragments")
+ return instances
+
+
+def _generate_ios(f, ios, ns):
+ outs = list_special_ios(f, False, True, False)
+ inouts = list_special_ios(f, False, False, True)
+ r = []
+ for io in ios:
+ direction = "OUTPUT" if io in outs else "INOUT" if io in inouts else "INPUT"
+ r.append(_Port(name=ns.get_name(io), direction=direction))
+ return r
+
+
+def _generate_connections(f, ios, ns):
+ r = OrderedDict()
+ for special in f.specials:
+ if isinstance(special, Instance):
+ instname = ns.get_name(special)
+ for port in special.items:
+ if isinstance(port, Instance._IO):
+ s = ns.get_name(port.expr)
+ if s not in r:
+ r[s] = []
+ r[s].append(_NetBranch(portname=port.name, instancename=instname))
+ elif isinstance(port, Instance.Parameter):
+ pass
+ else:
+ raise NotImplementedError("Unsupported instance item")
+ else:
+ raise ValueError("EDIF conversion can only handle synthesized fragments")
+ for s in ios:
+ io = ns.get_name(s)
+ if io not in r:
+ r[io] = []
+ r[io].append(_NetBranch(portname=io, instancename=""))
+ return r
+
+
+def convert(f, ios, cell_library, vendor, device, name="top"):
+ if not isinstance(f, _Fragment):
+ f = f.get_fragment()
+ if f.comb != [] or f.sync != {}:
+ raise ValueError("EDIF conversion can only handle synthesized fragments")
+ if ios is None:
+ ios = set()
+ cells = _generate_cells(f)
+ ns = build_namespace(list_special_ios(f, True, True, True))
+ instances = _generate_instances(f, ns)
+ inouts = _generate_ios(f, ios, ns)
+ connections = _generate_connections(f, ios, ns)
+ src = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor)
+
+ r = ConvOutput()
+ r.set_main_source(src)
+ r.ns = ns
+ return r
--- /dev/null
+import collections
+from itertools import combinations
+
+from litex.gen.util.misc import flat_iteration
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import _Fragment
+from litex.gen.fhdl.tools import rename_clock_domain
+from litex.gen.sim.upper import gen_sim, proxy_sim
+
+__all__ = ["Module", "FinalizeError"]
+
+
+class FinalizeError(Exception):
+ pass
+
+
+def _flat_list(e):
+ if isinstance(e, collections.Iterable):
+ return flat_iteration(e)
+ else:
+ return [e]
+
+
+class _ModuleProxy:
+ def __init__(self, fm):
+ object.__setattr__(self, "_fm", fm)
+
+
+class _ModuleComb(_ModuleProxy):
+ def __iadd__(self, other):
+ self._fm._fragment.comb += _flat_list(other)
+ return self
+
+
+def _cd_append(d, key, statements):
+ try:
+ l = d[key]
+ except KeyError:
+ l = []
+ d[key] = l
+ l += _flat_list(statements)
+
+
+class _ModuleSyncCD:
+ def __init__(self, fm, cd):
+ self._fm = fm
+ self._cd = cd
+
+ def __iadd__(self, other):
+ _cd_append(self._fm._fragment.sync, self._cd, other)
+ return self
+
+
+class _ModuleSync(_ModuleProxy):
+ def __iadd__(self, other):
+ _cd_append(self._fm._fragment.sync, "sys", other)
+ return self
+
+ def __getattr__(self, name):
+ return _ModuleSyncCD(self._fm, name)
+
+ def __setattr__(self, name, value):
+ if not isinstance(value, _ModuleSyncCD):
+ raise AttributeError("Attempted to assign sync property - use += instead")
+
+
+# _ModuleForwardAttr enables user classes to do e.g.:
+# self.subm.foobar = SomeModule()
+# and then access the submodule with self.foobar.
+class _ModuleForwardAttr:
+ def __setattr__(self, name, value):
+ self.__iadd__(value)
+ setattr(self._fm, name, value)
+
+
+class _ModuleSpecials(_ModuleProxy, _ModuleForwardAttr):
+ def __iadd__(self, other):
+ self._fm._fragment.specials |= set(_flat_list(other))
+ return self
+
+
+class _ModuleSubmodules(_ModuleProxy):
+ def __setattr__(self, name, value):
+ self._fm._submodules += [(name, e) for e in _flat_list(value)]
+ setattr(self._fm, name, value)
+
+ def __iadd__(self, other):
+ self._fm._submodules += [(None, e) for e in _flat_list(other)]
+ return self
+
+
+class _ModuleClockDomains(_ModuleProxy, _ModuleForwardAttr):
+ def __iadd__(self, other):
+ self._fm._fragment.clock_domains += _flat_list(other)
+ return self
+
+
+class Module:
+ def get_fragment(self):
+ assert(not self.get_fragment_called)
+ self.get_fragment_called = True
+ self.finalize()
+ return self._fragment
+
+ def __getattr__(self, name):
+ if name == "comb":
+ return _ModuleComb(self)
+ elif name == "sync":
+ return _ModuleSync(self)
+ elif name == "specials":
+ return _ModuleSpecials(self)
+ elif name == "submodules":
+ return _ModuleSubmodules(self)
+ elif name == "clock_domains":
+ return _ModuleClockDomains(self)
+
+ # hack to have initialized regular attributes without using __init__
+ # (which would require derived classes to call it)
+ elif name == "finalized":
+ self.finalized = False
+ return self.finalized
+ elif name == "_fragment":
+ simf = None
+ try:
+ simf = self.do_simulation
+ except AttributeError:
+ try:
+ simg = self.gen_simulation
+ except AttributeError:
+ pass
+ else:
+ simf = gen_sim(simg)
+ if simf is not None:
+ simf = proxy_sim(self, simf)
+ sim = [] if simf is None else [simf]
+ self._fragment = _Fragment(sim=sim)
+ return self._fragment
+ elif name == "_submodules":
+ self._submodules = []
+ return self._submodules
+ elif name == "_clock_domains":
+ self._clock_domains = []
+ return self._clock_domains
+ elif name == "get_fragment_called":
+ self.get_fragment_called = False
+ return self.get_fragment_called
+
+ else:
+ raise AttributeError("'"+self.__class__.__name__+"' object has no attribute '"+name+"'")
+
+ def __setattr__(self, name, value):
+ if name in ["comb", "sync", "specials", "submodules", "clock_domains"]:
+ if not isinstance(value, _ModuleProxy):
+ raise AttributeError("Attempted to assign special Module property - use += instead")
+ else:
+ object.__setattr__(self, name, value)
+
+ def _collect_submodules(self):
+ r = []
+ for name, submodule in self._submodules:
+ if not submodule.get_fragment_called:
+ r.append((name, submodule.get_fragment()))
+ return r
+
+ def finalize(self, *args, **kwargs):
+ if not self.finalized:
+ self.finalized = True
+ # finalize existing submodules before finalizing us
+ subfragments = self._collect_submodules()
+ self.do_finalize(*args, **kwargs)
+ # finalize submodules created by do_finalize
+ subfragments += self._collect_submodules()
+ # resolve clock domain name conflicts
+ needs_renaming = set()
+ for (mod_name1, f1), (mod_name2, f2) in combinations(subfragments, 2):
+ f1_names = set(cd.name for cd in f1.clock_domains)
+ f2_names = set(cd.name for cd in f2.clock_domains)
+ common_names = f1_names & f2_names
+ if common_names:
+ if mod_name1 is None or mod_name2 is None:
+ raise ValueError("Multiple submodules with local clock domains cannot be anonymous")
+ if mod_name1 == mod_name2:
+ raise ValueError("Multiple submodules with local clock domains cannot have the same name")
+ needs_renaming |= common_names
+ for mod_name, f in subfragments:
+ for cd in f.clock_domains:
+ if cd.name in needs_renaming:
+ rename_clock_domain(f, cd.name, mod_name + "_" + cd.name)
+ # sum subfragments
+ for mod_name, f in subfragments:
+ self._fragment += f
+
+ def do_finalize(self):
+ pass
+
+ def do_exit(self, *args, **kwargs):
+ for name, submodule in self._submodules:
+ submodule.do_exit(*args, **kwargs)
--- /dev/null
+from collections import OrderedDict
+from itertools import combinations
+
+from litex.gen.fhdl.structure import *
+
+
+class _Node:
+ 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 litex.gen.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):
+ root = _Node()
+ for signal in signals:
+ current_b = basic_tree
+ current = root
+ current.signal_count += 1
+ 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
+ return root
+
+
+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)
+ else:
+ r |= c_names
+
+ if node.signal_count > sum(c.signal_count for c in node.children.values()):
+ node.use_name = True
+ r.add((node_name, ))
+
+ return r
+
+
+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_pnd_from_tree(tree, signals):
+ return dict((signal, _name_signal(tree, signal)) for signal in signals)
+
+
+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):
+ 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):
+ 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
+ for name, signals in inv_pnd.items():
+ 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))
+
+ return pnd
+
+
+def _build_signal_groups(signals):
+ r = []
+ 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
+
+
+def _build_pnd(signals):
+ 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
+
+
+def build_namespace(signals, reserved_keywords=set()):
+ pnd = _build_pnd(signals)
+ ns = Namespace(pnd, reserved_keywords)
+ # register signals with name_override
+ for signal in signals:
+ if signal.name_override is not None:
+ ns.get_name(signal)
+ return ns
+
+
+class Namespace:
+ def __init__(self, pnd, reserved_keywords=set()):
+ self.counts = {k: 1 for k in reserved_keywords}
+ self.sigs = {}
+ self.pnd = pnd
+ self.clock_domains = dict()
+
+ def get_name(self, sig):
+ if isinstance(sig, ClockSignal):
+ sig = self.clock_domains[sig.cd].clk
+ if isinstance(sig, ResetSignal):
+ sig = self.clock_domains[sig.cd].rst
+ if sig is None:
+ raise ValueError("Attempted to obtain name of non-existent "
+ "reset signal of domain "+sig.cd)
+
+ if sig.name_override is not None:
+ sig_name = sig.name_override
+ else:
+ sig_name = self.pnd[sig]
+ try:
+ n = self.sigs[sig]
+ except KeyError:
+ try:
+ n = self.counts[sig_name]
+ except KeyError:
+ n = 0
+ self.sigs[sig] = n
+ self.counts[sig_name] = n + 1
+ if n:
+ return sig_name + "_" + str(n)
+ else:
+ return sig_name
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.specials import Memory, _MemoryPort, WRITE_FIRST, NO_CHANGE
+from litex.gen.fhdl.decorators import ModuleTransformer
+from litex.gen.util.misc import gcd_multiple
+
+
+class FullMemoryWE(ModuleTransformer):
+ def __init__(self):
+ self.replacments = dict()
+
+ def transform_fragment(self, i, f):
+ newspecials = set()
+
+ for orig in f.specials:
+ if not isinstance(orig, Memory):
+ newspecials.add(orig)
+ continue
+ global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports])
+ if global_granularity == orig.width:
+ newspecials.add(orig) # nothing to do
+ else:
+ newmems = []
+ for i in range(orig.width//global_granularity):
+ if orig.init is None:
+ newinit = None
+ else:
+ newinit = [(v >> i*global_granularity) & (2**global_granularity - 1) for v in orig.init]
+ newmem = Memory(global_granularity, orig.depth, newinit, orig.name_override + "_grain" + str(i))
+ newspecials.add(newmem)
+ newmems.append(newmem)
+ for port in orig.ports:
+ port_granularity = port.we_granularity if port.we_granularity else orig.width
+ newport = _MemoryPort(
+ adr=port.adr,
+
+ dat_r=port.dat_r[i*global_granularity:(i+1)*global_granularity] if port.dat_r is not None else None,
+ we=port.we[i*global_granularity//port_granularity] if port.we is not None else None,
+ dat_w=port.dat_w[i*global_granularity:(i+1)*global_granularity] if port.dat_w is not None else None,
+
+ async_read=port.async_read,
+ re=port.re,
+ we_granularity=0,
+ mode=port.mode,
+ clock_domain=port.clock.cd)
+ newmem.ports.append(newport)
+ newspecials.add(newport)
+ self.replacments[orig] = newmems
+
+ f.specials = newspecials
+
+
+class MemoryToArray(ModuleTransformer):
+ def __init__(self):
+ self.replacements = dict()
+
+ def transform_fragment(self, i, f):
+ newspecials = set()
+
+ for mem in f.specials:
+ if not isinstance(mem, Memory):
+ newspecials.add(mem)
+ continue
+
+ storage = Array()
+ self.replacements[mem] = storage
+ init = []
+ if mem.init is not None:
+ init = mem.init
+ for d in init:
+ mem_storage = Signal(mem.width, reset=d)
+ storage.append(mem_storage)
+ for _ in range(mem.depth-len(init)):
+ mem_storage = Signal(mem.width)
+ storage.append(mem_storage)
+
+ for port in mem.ports:
+ if port.we_granularity:
+ raise NotImplementedError
+ try:
+ sync = f.sync[port.clock.cd]
+ except KeyError:
+ sync = f.sync[port.clock.cd] = []
+
+ # read
+ if port.async_read:
+ f.comb.append(port.dat_r.eq(storage[port.adr]))
+ else:
+ if port.mode == WRITE_FIRST and port.we is not None:
+ adr_reg = Signal.like(port.adr)
+ rd_stmt = adr_reg.eq(port.adr)
+ f.comb.append(port.dat_r.eq(storage[adr_reg]))
+ elif port.mode == NO_CHANGE and port.we is not None:
+ rd_stmt = If(~port.we, port.dat_r.eq(storage[port.adr]))
+ else: # READ_FIRST or port.we is None, simplest case
+ rd_stmt = port.dat_r.eq(storage[port.adr])
+ if port.re is None:
+ sync.append(rd_stmt)
+ else:
+ sync.append(If(port.re, rd_stmt))
+
+ # write
+ if port.we is not None:
+ if port.we_granularity:
+ n = mem.width//port.we_granularity
+ for i in range(n):
+ m = i*port.we_granularity
+ M = (i+1)*port.we_granularity
+ sync.append(If(port.we[i],
+ storage[port.adr][m:M].eq(port.dat_w)))
+ else:
+ sync.append(If(port.we,
+ storage[port.adr].eq(port.dat_w)))
+
+ f.specials = newspecials
--- /dev/null
+from operator import itemgetter
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import _Value
+from litex.gen.fhdl.bitcontainer import bits_for, value_bits_sign
+from litex.gen.fhdl.tools import *
+from litex.gen.fhdl.tracer import get_obj_var_name
+from litex.gen.fhdl.verilog import _printexpr as verilog_printexpr
+
+
+__all__ = ["TSTriple", "Instance", "Memory",
+ "READ_FIRST", "WRITE_FIRST", "NO_CHANGE"]
+
+
+class Special(DUID):
+ def iter_expressions(self):
+ for x in []:
+ yield x
+
+ def rename_clock_domain(self, old, new):
+ for obj, attr, direction in self.iter_expressions():
+ rename_clock_domain_expr(getattr(obj, attr), old, new)
+
+ def list_clock_domains(self):
+ r = set()
+ for obj, attr, direction in self.iter_expressions():
+ r |= list_clock_domains_expr(getattr(obj, attr))
+ return r
+
+ def list_ios(self, ins, outs, inouts):
+ r = set()
+ for obj, attr, direction in self.iter_expressions():
+ if (direction == SPECIAL_INPUT and ins) \
+ or (direction == SPECIAL_OUTPUT and outs) \
+ or (direction == SPECIAL_INOUT and inouts):
+ signals = list_signals(getattr(obj, attr))
+ r.update(signals)
+ return r
+
+
+class Tristate(Special):
+ def __init__(self, target, o, oe, i=None):
+ Special.__init__(self)
+ self.target = wrap(target)
+ self.o = wrap(o)
+ self.oe = wrap(oe)
+ self.i = wrap(i) if i is not None else None
+
+ def iter_expressions(self):
+ for attr, target_context in [
+ ("target", SPECIAL_INOUT),
+ ("o", SPECIAL_INPUT),
+ ("oe", SPECIAL_INPUT),
+ ("i", SPECIAL_OUTPUT)]:
+ if getattr(self, attr) is not None:
+ yield self, attr, target_context
+
+ @staticmethod
+ def emit_verilog(tristate, ns, add_data_file):
+ def pe(e):
+ return verilog_printexpr(ns, e)[0]
+ w, s = value_bits_sign(tristate.target)
+ r = "assign " + pe(tristate.target) + " = " \
+ + pe(tristate.oe) + " ? " + pe(tristate.o) \
+ + " : " + str(w) + "'bz;\n"
+ if tristate.i is not None:
+ r += "assign " + pe(tristate.i) + " = " + pe(tristate.target) + ";\n"
+ r += "\n"
+ return r
+
+
+class TSTriple:
+ def __init__(self, bits_sign=None, min=None, max=None, reset_o=0, reset_oe=0):
+ self.o = Signal(bits_sign, min=min, max=max, reset=reset_o)
+ self.oe = Signal(reset=reset_oe)
+ self.i = Signal(bits_sign, min=min, max=max)
+
+ def get_tristate(self, target):
+ return Tristate(target, self.o, self.oe, self.i)
+
+
+class Instance(Special):
+ class _IO:
+ def __init__(self, name, expr=None):
+ self.name = name
+ if expr is None:
+ expr = Signal()
+ self.expr = wrap(expr)
+ class Input(_IO):
+ pass
+ class Output(_IO):
+ pass
+ class InOut(_IO):
+ pass
+ class Parameter:
+ def __init__(self, name, value):
+ self.name = name
+ if isinstance(value, (int, bool)):
+ value = Constant(value)
+ self.value = value
+ class PreformattedParam(str):
+ pass
+
+ def __init__(self, of, *items, name="", synthesis_directive=None, **kwargs):
+ Special.__init__(self)
+ self.of = of
+ if name:
+ self.name_override = name
+ else:
+ self.name_override = of
+ self.items = list(items)
+ self.synthesis_directive = synthesis_directive
+ for k, v in sorted(kwargs.items(), key=itemgetter(0)):
+ item_type, item_name = k.split("_", maxsplit=1)
+ item_class = {
+ "i": Instance.Input,
+ "o": Instance.Output,
+ "io": Instance.InOut,
+ "p": Instance.Parameter
+ }[item_type]
+ self.items.append(item_class(item_name, v))
+
+ def get_io(self, name):
+ for item in self.items:
+ if isinstance(item, Instance._IO) and item.name == name:
+ return item.expr
+
+ def iter_expressions(self):
+ for item in self.items:
+ if isinstance(item, Instance.Input):
+ yield item, "expr", SPECIAL_INPUT
+ elif isinstance(item, Instance.Output):
+ yield item, "expr", SPECIAL_OUTPUT
+ elif isinstance(item, Instance.InOut):
+ yield item, "expr", SPECIAL_INOUT
+
+ @staticmethod
+ def emit_verilog(instance, ns, add_data_file):
+ r = instance.of + " "
+ parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items))
+ if parameters:
+ r += "#(\n"
+ firstp = True
+ for p in parameters:
+ if not firstp:
+ r += ",\n"
+ firstp = False
+ r += "\t." + p.name + "("
+ if isinstance(p.value, Constant):
+ r += verilog_printexpr(ns, p.value)[0]
+ elif isinstance(p.value, float):
+ r += str(p.value)
+ elif isinstance(p.value, Instance.PreformattedParam):
+ r += p.value
+ elif isinstance(p.value, str):
+ r += "\"" + p.value + "\""
+ else:
+ raise TypeError
+ r += ")"
+ r += "\n) "
+ r += ns.get_name(instance)
+ if parameters: r += " "
+ r += "(\n"
+ firstp = True
+ for p in instance.items:
+ if isinstance(p, Instance._IO):
+ name_inst = p.name
+ name_design = verilog_printexpr(ns, p.expr)[0]
+ if not firstp:
+ r += ",\n"
+ firstp = False
+ r += "\t." + name_inst + "(" + name_design + ")"
+ if not firstp:
+ r += "\n"
+ if instance.synthesis_directive is not None:
+ synthesis_directive = "/* synthesis {} */".format(instance.synthesis_directive)
+ r += ")" + synthesis_directive + ";\n\n"
+ else:
+ r += ");\n\n"
+ return r
+
+
+(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3)
+
+
+class _MemoryPort(Special):
+ def __init__(self, adr, dat_r, we=None, dat_w=None,
+ async_read=False, re=None, we_granularity=0, mode=WRITE_FIRST,
+ clock_domain="sys"):
+ Special.__init__(self)
+ self.adr = adr
+ self.dat_r = dat_r
+ self.we = we
+ self.dat_w = dat_w
+ self.async_read = async_read
+ self.re = re
+ self.we_granularity = we_granularity
+ self.mode = mode
+ self.clock = ClockSignal(clock_domain)
+
+ def iter_expressions(self):
+ for attr, target_context in [
+ ("adr", SPECIAL_INPUT),
+ ("we", SPECIAL_INPUT),
+ ("dat_w", SPECIAL_INPUT),
+ ("re", SPECIAL_INPUT),
+ ("dat_r", SPECIAL_OUTPUT),
+ ("clock", SPECIAL_INPUT)]:
+ yield self, attr, target_context
+
+ @staticmethod
+ def emit_verilog(port, ns, add_data_file):
+ return "" # done by parent Memory object
+
+
+class _MemoryLocation(_Value):
+ def __init__(self, memory, index):
+ _Value.__init__(self)
+ self.memory = memory
+ self.index = wrap(index)
+
+
+class Memory(Special):
+ def __init__(self, width, depth, init=None, name=None):
+ Special.__init__(self)
+ self.width = width
+ self.depth = depth
+ self.ports = []
+ self.init = init
+ self.name_override = get_obj_var_name(name, "mem")
+
+ def __getitem__(self, index):
+ # simulation only
+ return _MemoryLocation(self, index)
+
+ def get_port(self, write_capable=False, async_read=False,
+ has_re=False, we_granularity=0, mode=WRITE_FIRST,
+ clock_domain="sys"):
+ if we_granularity >= self.width:
+ we_granularity = 0
+ adr = Signal(max=self.depth)
+ dat_r = Signal(self.width)
+ if write_capable:
+ if we_granularity:
+ we = Signal(self.width//we_granularity)
+ else:
+ we = Signal()
+ dat_w = Signal(self.width)
+ else:
+ we = None
+ dat_w = None
+ if has_re:
+ re = Signal()
+ else:
+ re = None
+ mp = _MemoryPort(adr, dat_r, we, dat_w,
+ async_read, re, we_granularity, mode,
+ clock_domain)
+ self.ports.append(mp)
+ return mp
+
+ @staticmethod
+ def emit_verilog(memory, ns, add_data_file):
+ r = ""
+ def gn(e):
+ if isinstance(e, Memory):
+ return ns.get_name(e)
+ else:
+ return verilog_printexpr(ns, e)[0]
+ adrbits = bits_for(memory.depth-1)
+
+ r += "reg [" + str(memory.width-1) + ":0] " \
+ + gn(memory) \
+ + "[0:" + str(memory.depth-1) + "];\n"
+
+ adr_regs = {}
+ data_regs = {}
+ for port in memory.ports:
+ if not port.async_read:
+ if port.mode == WRITE_FIRST and port.we is not None:
+ adr_reg = Signal(name_override="memadr")
+ r += "reg [" + str(adrbits-1) + ":0] " \
+ + gn(adr_reg) + ";\n"
+ adr_regs[id(port)] = adr_reg
+ else:
+ data_reg = Signal(name_override="memdat")
+ r += "reg [" + str(memory.width-1) + ":0] " \
+ + gn(data_reg) + ";\n"
+ data_regs[id(port)] = data_reg
+
+ for port in memory.ports:
+ r += "always @(posedge " + gn(port.clock) + ") begin\n"
+ if port.we is not None:
+ if port.we_granularity:
+ n = memory.width//port.we_granularity
+ for i in range(n):
+ m = i*port.we_granularity
+ M = (i+1)*port.we_granularity-1
+ sl = "[" + str(M) + ":" + str(m) + "]"
+ r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n"
+ r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "]" + sl + " <= " + gn(port.dat_w) + sl + ";\n"
+ else:
+ r += "\tif (" + gn(port.we) + ")\n"
+ r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n"
+ if not port.async_read:
+ if port.mode == WRITE_FIRST and port.we is not None:
+ rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n"
+ else:
+ bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n"
+ if port.mode == READ_FIRST or port.we is None:
+ rd = "\t" + bassign
+ elif port.mode == NO_CHANGE:
+ rd = "\tif (!" + gn(port.we) + ")\n" \
+ + "\t\t" + bassign
+ if port.re is None:
+ r += rd
+ else:
+ r += "\tif (" + gn(port.re) + ")\n"
+ r += "\t" + rd.replace("\n\t", "\n\t\t")
+ r += "end\n\n"
+
+ for port in memory.ports:
+ if port.async_read:
+ r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n"
+ else:
+ if port.mode == WRITE_FIRST and port.we is not None:
+ r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n"
+ else:
+ r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n"
+ r += "\n"
+
+ if memory.init is not None:
+ content = ""
+ for d in memory.init:
+ content += "{:x}\n".format(d)
+ memory_filename = add_data_file(gn(memory) + ".init", content)
+
+ r += "initial begin\n"
+ r += "\t$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n"
+ r += "end\n\n"
+
+ return r
+
+
+class SynthesisDirective(Special):
+ def __init__(self, template, **signals):
+ Special.__init__(self)
+ self.template = template
+ self.signals = signals
+
+ @staticmethod
+ def emit_verilog(directive, ns, add_data_file):
+ name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items())
+ formatted = directive.template.format(**name_dict)
+ return "// synthesis " + formatted + "\n"
+
+
+class Keep(SynthesisDirective):
+ def __init__(self, signal):
+ SynthesisDirective.__init__(self, "attribute keep of {s} is true", s=signal)
--- /dev/null
+import builtins as _builtins
+import collections as _collections
+
+from litex.gen.fhdl import tracer as _tracer
+from litex.gen.util.misc import flat_iteration as _flat_iteration
+
+
+class DUID:
+ """Deterministic Unique IDentifier"""
+ __next_uid = 0
+ def __init__(self):
+ self.duid = DUID.__next_uid
+ DUID.__next_uid += 1
+
+
+class _Value(DUID):
+ """Base class for operands
+
+ Instances of `_Value` or its subclasses can be operands to
+ arithmetic, comparison, bitwise, and logic operators.
+ They can be assigned (:meth:`eq`) or indexed/sliced (using the usual
+ Python indexing and slicing notation).
+
+ Values created from integers have the minimum bit width to necessary to
+ represent the integer.
+ """
+ def __bool__(self):
+ # Special case: Constants and Signals are part of a set or used as
+ # dictionary keys, and Python needs to check for equality.
+ if isinstance(self, _Operator) and self.op == "==":
+ a, b = self.operands
+ if isinstance(a, Constant) and isinstance(b, Constant):
+ return a.value == b.value
+ if isinstance(a, Signal) and isinstance(b, Signal):
+ return a is b
+ if (isinstance(a, Constant) and isinstance(b, Signal)
+ or isinstance(a, Signal) and isinstance(a, Constant)):
+ return False
+ raise TypeError("Attempted to convert Migen value to boolean")
+
+ def __invert__(self):
+ return _Operator("~", [self])
+ def __neg__(self):
+ return _Operator("-", [self])
+
+ def __add__(self, other):
+ return _Operator("+", [self, other])
+ def __radd__(self, other):
+ return _Operator("+", [other, self])
+ def __sub__(self, other):
+ return _Operator("-", [self, other])
+ def __rsub__(self, other):
+ return _Operator("-", [other, self])
+ def __mul__(self, other):
+ return _Operator("*", [self, other])
+ def __rmul__(self, other):
+ return _Operator("*", [other, self])
+ def __lshift__(self, other):
+ return _Operator("<<<", [self, other])
+ def __rlshift__(self, other):
+ return _Operator("<<<", [other, self])
+ def __rshift__(self, other):
+ return _Operator(">>>", [self, other])
+ def __rrshift__(self, other):
+ return _Operator(">>>", [other, self])
+ def __and__(self, other):
+ return _Operator("&", [self, other])
+ def __rand__(self, other):
+ return _Operator("&", [other, self])
+ def __xor__(self, other):
+ return _Operator("^", [self, other])
+ def __rxor__(self, other):
+ return _Operator("^", [other, self])
+ def __or__(self, other):
+ return _Operator("|", [self, other])
+ def __ror__(self, other):
+ return _Operator("|", [other, self])
+
+ def __lt__(self, other):
+ return _Operator("<", [self, other])
+ def __le__(self, other):
+ return _Operator("<=", [self, other])
+ def __eq__(self, other):
+ return _Operator("==", [self, other])
+ def __ne__(self, other):
+ return _Operator("!=", [self, other])
+ def __gt__(self, other):
+ return _Operator(">", [self, other])
+ def __ge__(self, other):
+ return _Operator(">=", [self, other])
+
+ def __len__(self):
+ from litex.gen.fhdl.bitcontainer import value_bits_sign
+ return value_bits_sign(self)[0]
+
+ def __getitem__(self, key):
+ n = len(self)
+ if isinstance(key, int):
+ if key >= n:
+ raise IndexError
+ if key < 0:
+ key += n
+ return _Slice(self, key, key+1)
+ elif isinstance(key, slice):
+ start, stop, step = key.indices(n)
+ if step != 1:
+ return Cat(self[i] for i in range(start, stop, step))
+ return _Slice(self, start, stop)
+ else:
+ raise TypeError
+
+ def eq(self, r):
+ """Assignment
+
+ Parameters
+ ----------
+ r : _Value, in
+ Value to be assigned.
+
+ Returns
+ -------
+ _Assign
+ Assignment statement that can be used in combinatorial or
+ synchronous context.
+ """
+ return _Assign(self, r)
+
+ def __hash__(self):
+ raise TypeError("unhashable type: '{}'".format(type(self).__name__))
+
+
+def wrap(value):
+ """Ensures that the passed object is a Migen value. Booleans and integers
+ are automatically wrapped into ``Constant``."""
+ if isinstance(value, (bool, int)):
+ value = Constant(value)
+ if not isinstance(value, _Value):
+ raise TypeError("Object is not a Migen value")
+ return value
+
+
+class _Operator(_Value):
+ def __init__(self, op, operands):
+ _Value.__init__(self)
+ self.op = op
+ self.operands = [wrap(o) for o in operands]
+
+
+def Mux(sel, val1, val0):
+ """Multiplex between two values
+
+ Parameters
+ ----------
+ sel : _Value(1), in
+ Selector.
+ val1 : _Value(N), in
+ val0 : _Value(N), in
+ Input values.
+
+ Returns
+ -------
+ _Value(N), out
+ Output `_Value`. If `sel` is asserted, the Mux returns
+ `val1`, else `val0`.
+ """
+ return _Operator("m", [sel, val1, val0])
+
+
+class _Slice(_Value):
+ def __init__(self, value, start, stop):
+ _Value.__init__(self)
+ if not isinstance(start, int) or not isinstance(stop, int):
+ raise TypeError("Slice boundaries must be integers")
+ self.value = wrap(value)
+ self.start = start
+ self.stop = stop
+
+
+class Cat(_Value):
+ """Concatenate values
+
+ Form a compound `_Value` from several smaller ones by concatenation.
+ The first argument occupies the lower bits of the result.
+ The return value can be used on either side of an assignment, that
+ is, the concatenated value can be used as an argument on the RHS or
+ as a target on the LHS. If it is used on the LHS, it must solely
+ consist of `Signal` s, slices of `Signal` s, and other concatenations
+ meeting these properties. The bit length of the return value is the sum of
+ the bit lengths of the arguments::
+
+ len(Cat(args)) == sum(len(arg) for arg in args)
+
+ Parameters
+ ----------
+ *args : _Values or iterables of _Values, inout
+ `_Value` s to be concatenated.
+
+ Returns
+ -------
+ Cat, inout
+ Resulting `_Value` obtained by concatentation.
+ """
+ def __init__(self, *args):
+ _Value.__init__(self)
+ self.l = [wrap(v) for v in _flat_iteration(args)]
+
+
+class Replicate(_Value):
+ """Replicate a value
+
+ An input value is replicated (repeated) several times
+ to be used on the RHS of assignments::
+
+ len(Replicate(s, n)) == len(s)*n
+
+ Parameters
+ ----------
+ v : _Value, in
+ Input value to be replicated.
+ n : int
+ Number of replications.
+
+ Returns
+ -------
+ Replicate, out
+ Replicated value.
+ """
+ def __init__(self, v, n):
+ _Value.__init__(self)
+ if not isinstance(n, int) or n < 0:
+ raise TypeError("Replication count must be a positive integer")
+ self.v = wrap(v)
+ self.n = n
+
+
+class Constant(_Value):
+ """A constant, HDL-literal integer `_Value`
+
+ Parameters
+ ----------
+ value : int
+ bits_sign : int or tuple or None
+ Either an integer `bits` or a tuple `(bits, signed)`
+ specifying the number of bits in this `Constant` and whether it is
+ signed (can represent negative values). `bits_sign` defaults
+ to the minimum width and signedness of `value`.
+ """
+ def __init__(self, value, bits_sign=None):
+ from litex.gen.fhdl.bitcontainer import bits_for
+
+ _Value.__init__(self)
+
+ self.value = int(value)
+ if bits_sign is None:
+ bits_sign = bits_for(self.value), self.value < 0
+ elif isinstance(bits_sign, int):
+ bits_sign = bits_sign, self.value < 0
+ self.nbits, self.signed = bits_sign
+ if not isinstance(self.nbits, int) or self.nbits <= 0:
+ raise TypeError("Width must be a strictly positive integer")
+
+ def __hash__(self):
+ return self.value
+
+
+C = Constant # shorthand
+
+
+class Signal(_Value):
+ """A `_Value` that can change
+
+ The `Signal` object represents a value that is expected to change
+ in the circuit. It does exactly what Verilog's `wire` and
+ `reg` and VHDL's `signal` do.
+
+ A `Signal` can be indexed to access a subset of its bits. Negative
+ indices (`signal[-1]`) and the extended Python slicing notation
+ (`signal[start:stop:step]`) are supported.
+ The indices 0 and -1 are the least and most significant bits
+ respectively.
+
+ Parameters
+ ----------
+ bits_sign : int or tuple
+ Either an integer `bits` or a tuple `(bits, signed)`
+ specifying the number of bits in this `Signal` and whether it is
+ signed (can represent negative values). `signed` defaults to
+ `False`.
+ name : str or None
+ Name hint for this signal. If `None` (default) the name is
+ inferred from the variable name this `Signal` is assigned to.
+ Name collisions are automatically resolved by prepending
+ names of objects that contain this `Signal` and by
+ appending integer sequences.
+ variable : bool
+ Deprecated.
+ reset : int
+ Reset (synchronous) or default (combinatorial) value.
+ When this `Signal` is assigned to in synchronous context and the
+ corresponding clock domain is reset, the `Signal` assumes the
+ given value. When this `Signal` is unassigned in combinatorial
+ context (due to conditional assignments not being taken),
+ the `Signal` assumes its `reset` value. Defaults to 0.
+ name_override : str or None
+ Do not use the inferred name but the given one.
+ min : int or None
+ max : int or None
+ If `bits_sign` is `None`, the signal bit width and signedness are
+ determined by the integer range given by `min` (inclusive,
+ defaults to 0) and `max` (exclusive, defaults to 2).
+ related : Signal or None
+ """
+ def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None):
+ from litex.gen.fhdl.bitcontainer import bits_for
+
+ _Value.__init__(self)
+
+ # determine number of bits and signedness
+ if bits_sign is None:
+ if min is None:
+ min = 0
+ if max is None:
+ max = 2
+ max -= 1 # make both bounds inclusive
+ assert(min < max)
+ self.signed = min < 0 or max < 0
+ self.nbits = _builtins.max(bits_for(min, self.signed), bits_for(max, self.signed))
+ else:
+ assert(min is None and max is None)
+ if isinstance(bits_sign, tuple):
+ self.nbits, self.signed = bits_sign
+ else:
+ self.nbits, self.signed = bits_sign, False
+ if not isinstance(self.nbits, int) or self.nbits <= 0:
+ raise ValueError("Signal width must be a strictly positive integer")
+
+ self.variable = variable # deprecated
+ self.reset = reset
+ self.name_override = name_override
+ self.backtrace = _tracer.trace_back(name)
+ self.related = related
+
+ def __setattr__(self, k, v):
+ if k == "reset":
+ v = wrap(v)
+ _Value.__setattr__(self, k, v)
+
+ def __repr__(self):
+ return "<Signal " + (self.backtrace[-1][0] or "anonymous") + " at " + hex(id(self)) + ">"
+
+ @classmethod
+ def like(cls, other, **kwargs):
+ """Create Signal based on another.
+
+ Parameters
+ ----------
+ other : _Value
+ Object to base this Signal on.
+
+ See `migen.fhdl.bitcontainer.value_bits_sign` for details.
+ """
+ from litex.gen.fhdl.bitcontainer import value_bits_sign
+ return cls(bits_sign=value_bits_sign(other), **kwargs)
+
+ def __hash__(self):
+ return self.duid
+
+
+class ClockSignal(_Value):
+ """Clock signal for a given clock domain
+
+ `ClockSignal` s for a given clock domain can be retrieved multiple
+ times. They all ultimately refer to the same signal.
+
+ Parameters
+ ----------
+ cd : str
+ Clock domain to obtain a clock signal for. Defaults to `"sys"`.
+ """
+ def __init__(self, cd="sys"):
+ _Value.__init__(self)
+ if not isinstance(cd, str):
+ raise TypeError("Argument of ClockSignal must be a string")
+ self.cd = cd
+
+
+class ResetSignal(_Value):
+ """Reset signal for a given clock domain
+
+ `ResetSignal` s for a given clock domain can be retrieved multiple
+ times. They all ultimately refer to the same signal.
+
+ Parameters
+ ----------
+ cd : str
+ Clock domain to obtain a reset signal for. Defaults to `"sys"`.
+ allow_reset_less : bool
+ If the clock domain is resetless, return 0 instead of reporting an
+ error.
+ """
+ def __init__(self, cd="sys", allow_reset_less=False):
+ _Value.__init__(self)
+ if not isinstance(cd, str):
+ raise TypeError("Argument of ResetSignal must be a string")
+ self.cd = cd
+ self.allow_reset_less = allow_reset_less
+
+
+# statements
+
+
+class _Statement:
+ pass
+
+
+class _Assign(_Statement):
+ def __init__(self, l, r):
+ self.l = wrap(l)
+ self.r = wrap(r)
+
+
+def _check_statement(s):
+ if isinstance(s, _collections.Iterable):
+ return all(_check_statement(ss) for ss in s)
+ else:
+ return isinstance(s, _Statement)
+
+
+class If(_Statement):
+ """Conditional execution of statements
+
+ Parameters
+ ----------
+ cond : _Value(1), in
+ Condition
+ *t : Statements
+ Statements to execute if `cond` is asserted.
+
+ Examples
+ --------
+ >>> a = Signal()
+ >>> b = Signal()
+ >>> c = Signal()
+ >>> d = Signal()
+ >>> If(a,
+ ... b.eq(1)
+ ... ).Elif(c,
+ ... b.eq(0)
+ ... ).Else(
+ ... b.eq(d)
+ ... )
+ """
+ def __init__(self, cond, *t):
+ if not _check_statement(t):
+ raise TypeError("Not all test body objects are Migen statements")
+ self.cond = wrap(cond)
+ self.t = list(t)
+ self.f = []
+
+ def Else(self, *f):
+ """Add an `else` conditional block
+
+ Parameters
+ ----------
+ *f : Statements
+ Statements to execute if all previous conditions fail.
+ """
+ if not _check_statement(f):
+ raise TypeError("Not all test body objects are Migen statements")
+ _insert_else(self, list(f))
+ return self
+
+ def Elif(self, cond, *t):
+ """Add an `else if` conditional block
+
+ Parameters
+ ----------
+ cond : _Value(1), in
+ Condition
+ *t : Statements
+ Statements to execute if previous conditions fail and `cond`
+ is asserted.
+ """
+ _insert_else(self, [If(cond, *t)])
+ return self
+
+
+def _insert_else(obj, clause):
+ o = obj
+ while o.f:
+ assert(len(o.f) == 1)
+ assert(isinstance(o.f[0], If))
+ o = o.f[0]
+ o.f = clause
+
+
+class Case(_Statement):
+ """Case/Switch statement
+
+ Parameters
+ ----------
+ test : _Value, in
+ Selector value used to decide which block to execute
+ cases : dict
+ Dictionary of cases. The keys are numeric constants to compare
+ with `test`. The values are statements to be executed the
+ corresponding key matches `test`. The dictionary may contain a
+ string key `"default"` to mark a fall-through case that is
+ executed if no other key matches.
+
+ Examples
+ --------
+ >>> a = Signal()
+ >>> b = Signal()
+ >>> Case(a, {
+ ... 0: b.eq(1),
+ ... 1: b.eq(0),
+ ... "default": b.eq(0),
+ ... })
+ """
+ def __init__(self, test, cases):
+ self.test = wrap(test)
+ self.cases = dict()
+ for k, v in cases.items():
+ if isinstance(k, (bool, int)):
+ k = Constant(k)
+ if (not isinstance(k, Constant)
+ and not (isinstance(k, str) and k == "default")):
+ raise TypeError("Case object is not a Migen constant")
+ if not isinstance(v, _collections.Iterable):
+ v = [v]
+ if not _check_statement(v):
+ raise TypeError("Not all objects for case {} "
+ "are Migen statements".format(k))
+ self.cases[k] = v
+
+ def makedefault(self, key=None):
+ """Mark a key as the default case
+
+ Deletes/substitutes any previously existing default case.
+
+ Parameters
+ ----------
+ key : int or None
+ Key to use as default case if no other key matches.
+ By default, the largest key is the default key.
+ """
+ if key is None:
+ for choice in self.cases.keys():
+ if key is None or choice.value > key.value:
+ key = choice
+ self.cases["default"] = self.cases[key]
+ del self.cases[key]
+ return self
+
+
+# arrays
+
+
+class _ArrayProxy(_Value):
+ def __init__(self, choices, key):
+ _Value.__init__(self)
+ self.choices = []
+ for c in choices:
+ if isinstance(c, (bool, int)):
+ c = Constant(c)
+ self.choices.append(c)
+ self.key = key
+
+ def __getattr__(self, attr):
+ return _ArrayProxy([getattr(choice, attr) for choice in self.choices],
+ self.key)
+
+ def __getitem__(self, key):
+ return _ArrayProxy([choice.__getitem__(key) for choice in self.choices],
+ self.key)
+
+
+class Array(list):
+ """Addressable multiplexer
+
+ An array is created from an iterable of values and indexed using the
+ usual Python simple indexing notation (no negative indices or
+ slices). It can be indexed by numeric constants, `_Value` s, or
+ `Signal` s.
+
+ The result of indexing the array is a proxy for the entry at the
+ given index that can be used on either RHS or LHS of assignments.
+
+ An array can be indexed multiple times.
+
+ Multidimensional arrays are supported by packing inner arrays into
+ outer arrays.
+
+ Parameters
+ ----------
+ values : iterable of ints, _Values, Signals
+ Entries of the array. Each entry can be a numeric constant, a
+ `Signal` or a `Record`.
+
+ Examples
+ --------
+ >>> a = Array(range(10))
+ >>> b = Signal(max=10)
+ >>> c = Signal(max=10)
+ >>> b.eq(a[9 - c])
+ """
+ def __getitem__(self, key):
+ if isinstance(key, Constant):
+ return list.__getitem__(self, key.value)
+ elif isinstance(key, _Value):
+ return _ArrayProxy(self, key)
+ else:
+ return list.__getitem__(self, key)
+
+
+class ClockDomain:
+ """Synchronous domain
+
+ Parameters
+ ----------
+ name : str or None
+ Domain name. If None (the default) the name is inferred from the
+ variable name this `ClockDomain` is assigned to (stripping any
+ `"cd_"` prefix).
+ reset_less : bool
+ The domain does not use a reset signal. Registers within this
+ domain are still all initialized to their reset state once, e.g.
+ through Verilog `"initial"` statements.
+
+ Attributes
+ ----------
+ clk : Signal, inout
+ The clock for this domain. Can be driven or used to drive other
+ signals (preferably in combinatorial context).
+ rst : Signal or None, inout
+ Reset signal for this domain. Can be driven or used to drive.
+ """
+ def __init__(self, name=None, reset_less=False):
+ self.name = _tracer.get_obj_var_name(name)
+ if self.name is None:
+ raise ValueError("Cannot extract clock domain name from code, need to specify.")
+ if self.name.startswith("cd_"):
+ self.name = self.name[3:]
+ if self.name[0].isdigit():
+ raise ValueError("Clock domain name cannot start with a number.")
+ self.clk = Signal(name_override=self.name + "_clk")
+ if reset_less:
+ self.rst = None
+ else:
+ self.rst = Signal(name_override=self.name + "_rst")
+
+ def rename(self, new_name):
+ """Rename the clock domain
+
+ Parameters
+ ----------
+ new_name : str
+ New name
+ """
+ self.name = new_name
+ self.clk.name_override = new_name + "_clk"
+ if self.rst is not None:
+ self.rst.name_override = new_name + "_rst"
+
+
+class _ClockDomainList(list):
+ def __getitem__(self, key):
+ if isinstance(key, str):
+ for cd in self:
+ if cd.name == key:
+ return cd
+ raise KeyError(key)
+ else:
+ return list.__getitem__(self, key)
+
+
+(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
+
+
+class StopSimulation(Exception):
+ pass
+
+
+class _Fragment:
+ def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None):
+ if comb is None: comb = []
+ if sync is None: sync = dict()
+ if specials is None: specials = set()
+ if clock_domains is None: clock_domains = _ClockDomainList()
+ if sim is None: sim = []
+
+ self.comb = comb
+ self.sync = sync
+ self.specials = specials
+ self.clock_domains = _ClockDomainList(clock_domains)
+ self.sim = sim
+
+ def __add__(self, other):
+ newsync = _collections.defaultdict(list)
+ for k, v in self.sync.items():
+ newsync[k] = v[:]
+ for k, v in other.sync.items():
+ newsync[k].extend(v)
+ return _Fragment(self.comb + other.comb, newsync,
+ self.specials | other.specials,
+ self.clock_domains + other.clock_domains,
+ self.sim + other.sim)
+
+ def __iadd__(self, other):
+ newsync = _collections.defaultdict(list)
+ for k, v in self.sync.items():
+ newsync[k] = v[:]
+ for k, v in other.sync.items():
+ newsync[k].extend(v)
+ self.comb += other.comb
+ self.sync = newsync
+ self.specials |= other.specials
+ self.clock_domains += other.clock_domains
+ self.sim += other.sim
+ return self
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import _Slice, _Assign
+from litex.gen.fhdl.visit import NodeVisitor, NodeTransformer
+from litex.gen.fhdl.bitcontainer import value_bits_sign
+from litex.gen.util.misc import flat_iteration
+
+
+class _SignalLister(NodeVisitor):
+ def __init__(self):
+ self.output_list = set()
+
+ def visit_Signal(self, node):
+ self.output_list.add(node)
+
+
+class _TargetLister(NodeVisitor):
+ def __init__(self):
+ self.output_list = set()
+ self.target_context = False
+
+ def visit_Signal(self, node):
+ if self.target_context:
+ self.output_list.add(node)
+
+ def visit_Assign(self, node):
+ self.target_context = True
+ self.visit(node.l)
+ self.target_context = False
+
+ def visit_ArrayProxy(self, node):
+ for choice in node.choices:
+ self.visit(choice)
+
+
+class _InputLister(NodeVisitor):
+ def __init__(self):
+ self.output_list = set()
+
+ def visit_Signal(self, node):
+ self.output_list.add(node)
+
+ def visit_Assign(self, node):
+ self.visit(node.r)
+
+
+def list_signals(node):
+ lister = _SignalLister()
+ lister.visit(node)
+ return lister.output_list
+
+
+def list_targets(node):
+ lister = _TargetLister()
+ lister.visit(node)
+ return lister.output_list
+
+
+def list_inputs(node):
+ lister = _InputLister()
+ lister.visit(node)
+ return lister.output_list
+
+
+def _resort_statements(ol):
+ return [statement for i, statement in
+ sorted(ol, key=lambda x: x[0])]
+
+
+def group_by_targets(sl):
+ groups = []
+ seen = set()
+ for order, stmt in enumerate(flat_iteration(sl)):
+ targets = set(list_targets(stmt))
+ group = [(order, stmt)]
+ disjoint = targets.isdisjoint(seen)
+ seen |= targets
+ if not disjoint:
+ groups, old_groups = [], groups
+ for old_targets, old_group in old_groups:
+ if targets.isdisjoint(old_targets):
+ groups.append((old_targets, old_group))
+ else:
+ targets |= old_targets
+ group += old_group
+ groups.append((targets, group))
+ return [(targets, _resort_statements(stmts))
+ for targets, stmts in groups]
+
+
+def list_special_ios(f, ins, outs, inouts):
+ r = set()
+ for special in f.specials:
+ r |= special.list_ios(ins, outs, inouts)
+ return r
+
+
+class _ClockDomainLister(NodeVisitor):
+ def __init__(self):
+ self.clock_domains = set()
+
+ def visit_ClockSignal(self, node):
+ self.clock_domains.add(node.cd)
+
+ def visit_ResetSignal(self, node):
+ self.clock_domains.add(node.cd)
+
+ def visit_clock_domains(self, node):
+ for clockname, statements in node.items():
+ self.clock_domains.add(clockname)
+ self.visit(statements)
+
+
+def list_clock_domains_expr(f):
+ cdl = _ClockDomainLister()
+ cdl.visit(f)
+ return cdl.clock_domains
+
+
+def list_clock_domains(f):
+ r = list_clock_domains_expr(f)
+ for special in f.specials:
+ r |= special.list_clock_domains()
+ for cd in f.clock_domains:
+ r.add(cd.name)
+ return r
+
+
+def is_variable(node):
+ if isinstance(node, Signal):
+ return node.variable
+ elif isinstance(node, _Slice):
+ return is_variable(node.value)
+ elif isinstance(node, Cat):
+ arevars = list(map(is_variable, node.l))
+ r = arevars[0]
+ for x in arevars:
+ if x != r:
+ raise TypeError
+ return r
+ else:
+ raise TypeError
+
+
+def generate_reset(rst, sl):
+ targets = list_targets(sl)
+ return [t.eq(t.reset) for t in sorted(targets, key=lambda x: x.duid)]
+
+
+def insert_reset(rst, sl):
+ return [If(rst, *generate_reset(rst, sl)).Else(*sl)]
+
+
+def insert_resets(f):
+ newsync = dict()
+ for k, v in f.sync.items():
+ if f.clock_domains[k].rst is not None:
+ newsync[k] = insert_reset(ResetSignal(k), v)
+ else:
+ newsync[k] = v
+ f.sync = newsync
+
+
+class _Lowerer(NodeTransformer):
+ def __init__(self):
+ self.target_context = False
+ self.extra_stmts = []
+ self.comb = []
+
+ def visit_Assign(self, node):
+ old_target_context, old_extra_stmts = self.target_context, self.extra_stmts
+ self.extra_stmts = []
+
+ self.target_context = True
+ lhs = self.visit(node.l)
+ self.target_context = False
+ rhs = self.visit(node.r)
+ r = _Assign(lhs, rhs)
+ if self.extra_stmts:
+ r = [r] + self.extra_stmts
+
+ self.target_context, self.extra_stmts = old_target_context, old_extra_stmts
+ return r
+
+
+# Basics are FHDL structure elements that back-ends are not required to support
+# but can be expressed in terms of other elements (lowered) before conversion.
+class _BasicLowerer(_Lowerer):
+ def __init__(self, clock_domains):
+ self.clock_domains = clock_domains
+ _Lowerer.__init__(self)
+
+ def visit_ArrayProxy(self, node):
+ # TODO: rewrite without variables
+ array_muxed = Signal(value_bits_sign(node), variable=True)
+ if self.target_context:
+ k = self.visit(node.key)
+ cases = {}
+ for n, choice in enumerate(node.choices):
+ cases[n] = [self.visit_Assign(_Assign(choice, array_muxed))]
+ self.extra_stmts.append(Case(k, cases).makedefault())
+ else:
+ cases = dict((n, _Assign(array_muxed, self.visit(choice)))
+ for n, choice in enumerate(node.choices))
+ self.comb.append(Case(self.visit(node.key), cases).makedefault())
+ return array_muxed
+
+ def visit_ClockSignal(self, node):
+ return self.clock_domains[node.cd].clk
+
+ def visit_ResetSignal(self, node):
+ rst = self.clock_domains[node.cd].rst
+ if rst is None:
+ if node.allow_reset_less:
+ return 0
+ else:
+ raise ValueError("Attempted to get reset signal of resetless"
+ " domain '{}'".format(node.cd))
+ else:
+ return rst
+
+
+class _ComplexSliceLowerer(_Lowerer):
+ def visit_Slice(self, node):
+ if not isinstance(node.value, Signal):
+ slice_proxy = Signal(value_bits_sign(node.value))
+ if self.target_context:
+ a = _Assign(node.value, slice_proxy)
+ else:
+ a = _Assign(slice_proxy, node.value)
+ self.comb.append(self.visit_Assign(a))
+ node = _Slice(slice_proxy, node.start, node.stop)
+ return NodeTransformer.visit_Slice(self, node)
+
+
+def _apply_lowerer(l, f):
+ f = l.visit(f)
+ f.comb += l.comb
+
+ for special in f.specials:
+ for obj, attr, direction in special.iter_expressions():
+ if direction != SPECIAL_INOUT:
+ # inouts are only supported by Migen when connected directly to top-level
+ # in this case, they are Signal and never need lowering
+ l.comb = []
+ l.target_context = direction != SPECIAL_INPUT
+ l.extra_stmts = []
+ expr = getattr(obj, attr)
+ expr = l.visit(expr)
+ setattr(obj, attr, expr)
+ f.comb += l.comb + l.extra_stmts
+
+ return f
+
+
+def lower_basics(f):
+ return _apply_lowerer(_BasicLowerer(f.clock_domains), f)
+
+
+def lower_complex_slices(f):
+ return _apply_lowerer(_ComplexSliceLowerer(), f)
+
+
+class _ClockDomainRenamer(NodeVisitor):
+ def __init__(self, old, new):
+ self.old = old
+ self.new = new
+
+ def visit_ClockSignal(self, node):
+ if node.cd == self.old:
+ node.cd = self.new
+
+ def visit_ResetSignal(self, node):
+ if node.cd == self.old:
+ node.cd = self.new
+
+
+def rename_clock_domain_expr(f, old, new):
+ cdr = _ClockDomainRenamer(old, new)
+ cdr.visit(f)
+
+
+def rename_clock_domain(f, old, new):
+ rename_clock_domain_expr(f, old, new)
+ if new != old:
+ if old in f.sync:
+ if new in f.sync:
+ f.sync[new].extend(f.sync[old])
+ else:
+ f.sync[new] = f.sync[old]
+ del f.sync[old]
+ for special in f.specials:
+ special.rename_clock_domain(old, new)
+ try:
+ cd = f.clock_domains[old]
+ except KeyError:
+ pass
+ else:
+ cd.rename(new)
--- /dev/null
+import inspect
+from opcode import opname
+from collections import defaultdict
+
+
+def get_var_name(frame):
+ code = frame.f_code
+ call_index = frame.f_lasti
+ call_opc = opname[code.co_code[call_index]]
+ if call_opc != "CALL_FUNCTION" and call_opc != "CALL_FUNCTION_VAR":
+ return None
+ index = call_index+3
+ while True:
+ opc = opname[code.co_code[index]]
+ if opc == "STORE_NAME" or opc == "STORE_ATTR":
+ name_index = int(code.co_code[index+1])
+ return code.co_names[name_index]
+ elif opc == "STORE_FAST":
+ name_index = int(code.co_code[index+1])
+ return code.co_varnames[name_index]
+ elif opc == "STORE_DEREF":
+ name_index = int(code.co_code[index+1])
+ return code.co_cellvars[name_index]
+ elif opc == "LOAD_GLOBAL" or opc == "LOAD_ATTR" or opc == "LOAD_FAST" or opc == "LOAD_DEREF":
+ index += 3
+ elif opc == "DUP_TOP":
+ index += 1
+ elif opc == "BUILD_LIST":
+ index += 3
+ else:
+ return None
+
+
+def remove_underscore(s):
+ if len(s) > 2 and s[0] == "_" and s[1] != "_":
+ s = s[1:]
+ return s
+
+
+def get_obj_var_name(override=None, default=None):
+ if override:
+ return override
+
+ frame = inspect.currentframe().f_back
+ # We can be called via derived classes. Go back the stack frames
+ # until we reach the first class that does not inherit from us.
+ ourclass = frame.f_locals["self"].__class__
+ while "self" in frame.f_locals and isinstance(frame.f_locals["self"], ourclass):
+ frame = frame.f_back
+
+ vn = get_var_name(frame)
+ if vn is None:
+ vn = default
+ else:
+ vn = remove_underscore(vn)
+ return vn
+
+name_to_idx = defaultdict(int)
+classname_to_objs = dict()
+
+
+def index_id(l, obj):
+ for n, e in enumerate(l):
+ if id(e) == id(obj):
+ return n
+ raise ValueError
+
+
+def trace_back(varname=None):
+ l = []
+ frame = inspect.currentframe().f_back.f_back
+ while frame is not None:
+ if varname is None:
+ varname = get_var_name(frame)
+ if varname is not None:
+ varname = remove_underscore(varname)
+ l.insert(0, (varname, name_to_idx[varname]))
+ name_to_idx[varname] += 1
+
+ try:
+ obj = frame.f_locals["self"]
+ except KeyError:
+ obj = None
+ if hasattr(obj, "__del__"):
+ obj = None
+
+ if obj is None:
+ if varname is not None:
+ coname = frame.f_code.co_name
+ if coname == "<module>":
+ modules = frame.f_globals["__name__"]
+ modules = modules.split(".")
+ coname = modules[len(modules)-1]
+ coname = remove_underscore(coname)
+ l.insert(0, (coname, name_to_idx[coname]))
+ name_to_idx[coname] += 1
+ else:
+ classname = obj.__class__.__name__.lower()
+ try:
+ objs = classname_to_objs[classname]
+ except KeyError:
+ classname_to_objs[classname] = [obj]
+ idx = 0
+ else:
+ try:
+ idx = index_id(objs, obj)
+ except ValueError:
+ idx = len(objs)
+ objs.append(obj)
+ classname = remove_underscore(classname)
+ l.insert(0, (classname, idx))
+
+ varname = None
+ frame = frame.f_back
+ return l
--- /dev/null
+from functools import partial
+from operator import itemgetter
+import collections
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment
+from litex.gen.fhdl.tools import *
+from litex.gen.fhdl.bitcontainer import bits_for
+from litex.gen.fhdl.namer import build_namespace
+from litex.gen.fhdl.conv_output import ConvOutput
+
+
+_reserved_keywords = {
+ "always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
+ "case", "casex", "casez", "cell", "cmos", "config", "deassign", "default",
+ "defparam", "design", "disable", "edge", "else", "end", "endcase",
+ "endconfig", "endfunction", "endgenerate", "endmodule", "endprimitive",
+ "endspecify", "endtable", "endtask", "event", "for", "force", "forever",
+ "fork", "function", "generate", "genvar", "highz0", "highz1", "if",
+ "ifnone", "incdir", "include", "initial", "inout", "input",
+ "instance", "integer", "join", "large", "liblist", "library", "localparam",
+ "macromodule", "medium", "module", "nand", "negedge", "nmos", "nor",
+ "noshowcancelled", "not", "notif0", "notif1", "or", "output", "parameter",
+ "pmos", "posedge", "primitive", "pull0", "pull1" "pulldown",
+ "pullup", "pulsestyle_onevent", "pulsestyle_ondetect", "remos", "real",
+ "realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran",
+ "rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small",
+ "specify", "specparam", "strong0", "strong1", "supply0", "supply1",
+ "table", "task", "time", "tran", "tranif0", "tranif1", "tri", "tri0",
+ "tri1", "triand", "trior", "trireg", "unsigned", "use", "vectored", "wait",
+ "wand", "weak0", "weak1", "while", "wire", "wor","xnor", "xor"
+}
+
+
+def _printsig(ns, s):
+ if s.signed:
+ n = "signed "
+ else:
+ n = ""
+ if len(s) > 1:
+ n += "[" + str(len(s)-1) + ":0] "
+ n += ns.get_name(s)
+ return n
+
+
+def _printconstant(node):
+ if node.signed:
+ return (str(node.nbits) + "'sd" + str(2**node.nbits + node.value),
+ True)
+ else:
+ return str(node.nbits) + "'d" + str(node.value), False
+
+
+def _printexpr(ns, node):
+ if isinstance(node, Constant):
+ return _printconstant(node)
+ elif isinstance(node, Signal):
+ return ns.get_name(node), node.signed
+ elif isinstance(node, _Operator):
+ arity = len(node.operands)
+ r1, s1 = _printexpr(ns, node.operands[0])
+ if arity == 1:
+ if node.op == "-":
+ if s1:
+ r = node.op + r1
+ else:
+ r = "-$signed({1'd0, " + r1 + "})"
+ s = True
+ else:
+ r = node.op + r1
+ s = s1
+ elif arity == 2:
+ r2, s2 = _printexpr(ns, node.operands[1])
+ if node.op not in ["<<<", ">>>"]:
+ if s2 and not s1:
+ r1 = "$signed({1'd0, " + r1 + "})"
+ if s1 and not s2:
+ r2 = "$signed({1'd0, " + r2 + "})"
+ r = r1 + " " + node.op + " " + r2
+ s = s1 or s2
+ elif arity == 3:
+ assert node.op == "m"
+ r2, s2 = _printexpr(ns, node.operands[1])
+ r3, s3 = _printexpr(ns, node.operands[2])
+ if s2 and not s3:
+ r3 = "$signed({1'd0, " + r3 + "})"
+ if s3 and not s2:
+ r2 = "$signed({1'd0, " + r2 + "})"
+ r = r1 + " ? " + r2 + " : " + r3
+ s = s2 or s3
+ else:
+ raise TypeError
+ return "(" + r + ")", s
+ elif isinstance(node, _Slice):
+ # Verilog does not like us slicing non-array signals...
+ if isinstance(node.value, Signal) \
+ and len(node.value) == 1 \
+ and node.start == 0 and node.stop == 1:
+ return _printexpr(ns, node.value)
+
+ if node.start + 1 == node.stop:
+ sr = "[" + str(node.start) + "]"
+ else:
+ sr = "[" + str(node.stop-1) + ":" + str(node.start) + "]"
+ r, s = _printexpr(ns, node.value)
+ return r + sr, s
+ elif isinstance(node, Cat):
+ l = [_printexpr(ns, v)[0] for v in reversed(node.l)]
+ return "{" + ", ".join(l) + "}", False
+ elif isinstance(node, Replicate):
+ return "{" + str(node.n) + "{" + _printexpr(ns, node.v)[0] + "}}", False
+ else:
+ raise TypeError("Expression of unrecognized type: '{}'".format(type(node).__name__))
+
+
+(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3)
+
+
+def _printnode(ns, at, level, node, target_filter=None):
+ if node is None:
+ return ""
+ elif target_filter is not None and target_filter not in list_targets(node):
+ return ""
+ elif isinstance(node, _Assign):
+ if at == _AT_BLOCKING:
+ assignment = " = "
+ elif at == _AT_NONBLOCKING:
+ assignment = " <= "
+ elif is_variable(node.l):
+ assignment = " = "
+ else:
+ assignment = " <= "
+ return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n"
+ elif isinstance(node, collections.Iterable):
+ return "".join(_printnode(ns, at, level, n, target_filter) for n in node)
+ elif isinstance(node, If):
+ r = "\t"*level + "if (" + _printexpr(ns, node.cond)[0] + ") begin\n"
+ r += _printnode(ns, at, level + 1, node.t, target_filter)
+ if node.f:
+ r += "\t"*level + "end else begin\n"
+ r += _printnode(ns, at, level + 1, node.f, target_filter)
+ r += "\t"*level + "end\n"
+ return r
+ elif isinstance(node, Case):
+ if node.cases:
+ r = "\t"*level + "case (" + _printexpr(ns, node.test)[0] + ")\n"
+ css = sorted([(k, v) for (k, v) in node.cases.items() if k != "default"], key=itemgetter(0))
+ for choice, statements in css:
+ r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n"
+ r += _printnode(ns, at, level + 2, statements, target_filter)
+ r += "\t"*(level + 1) + "end\n"
+ if "default" in node.cases:
+ r += "\t"*(level + 1) + "default: begin\n"
+ r += _printnode(ns, at, level + 2, node.cases["default"], target_filter)
+ r += "\t"*(level + 1) + "end\n"
+ r += "\t"*level + "endcase\n"
+ return r
+ else:
+ return ""
+ else:
+ raise TypeError("Node of unrecognized type: "+str(type(node)))
+
+
+def _list_comb_wires(f):
+ r = set()
+ groups = group_by_targets(f.comb)
+ for g in groups:
+ if len(g[1]) == 1 and isinstance(g[1][0], _Assign):
+ r |= g[0]
+ return r
+
+
+def _printheader(f, ios, name, ns,
+ reg_initialization):
+ sigs = list_signals(f) | list_special_ios(f, True, True, True)
+ special_outs = list_special_ios(f, False, True, True)
+ inouts = list_special_ios(f, False, False, True)
+ targets = list_targets(f) | special_outs
+ wires = _list_comb_wires(f) | special_outs
+ r = "module " + name + "(\n"
+ firstp = True
+ for sig in sorted(ios, key=lambda x: x.duid):
+ if not firstp:
+ r += ",\n"
+ firstp = False
+ if sig in inouts:
+ r += "\tinout " + _printsig(ns, sig)
+ elif sig in targets:
+ if sig in wires:
+ r += "\toutput " + _printsig(ns, sig)
+ else:
+ r += "\toutput reg " + _printsig(ns, sig)
+ else:
+ r += "\tinput " + _printsig(ns, sig)
+ r += "\n);\n\n"
+ for sig in sorted(sigs - ios, key=lambda x: x.duid):
+ if sig in wires:
+ r += "wire " + _printsig(ns, sig) + ";\n"
+ else:
+ if reg_initialization:
+ r += "reg " + _printsig(ns, sig) + " = " + _printexpr(ns, sig.reset)[0] + ";\n"
+ else:
+ r += "reg " + _printsig(ns, sig) + ";\n"
+ r += "\n"
+ return r
+
+
+def _printcomb(f, ns,
+ display_run,
+ dummy_signal,
+ blocking_assign):
+ r = ""
+ if f.comb:
+ if dummy_signal:
+ # Generate a dummy event to get the simulator
+ # to run the combinatorial process once at the beginning.
+ syn_off = "// synthesis translate_off\n"
+ syn_on = "// synthesis translate_on\n"
+ dummy_s = Signal(name_override="dummy_s")
+ r += syn_off
+ r += "reg " + _printsig(ns, dummy_s) + ";\n"
+ r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
+ r += syn_on
+
+
+ from collections import defaultdict
+
+ target_stmt_map = defaultdict(list)
+
+ for statement in flat_iteration(f.comb):
+ targets = list_targets(statement)
+ for t in targets:
+ target_stmt_map[t].append(statement)
+
+ #from pprint import pprint
+ #pprint(target_stmt_map)
+
+ groups = group_by_targets(f.comb)
+
+ for n, (t, stmts) in enumerate(target_stmt_map.items()):
+ assert isinstance(t, Signal)
+ if len(stmts) == 1 and isinstance(stmts[0], _Assign):
+ r += "assign " + _printnode(ns, _AT_BLOCKING, 0, stmts[0])
+ else:
+ if dummy_signal:
+ dummy_d = Signal(name_override="dummy_d")
+ r += "\n" + syn_off
+ r += "reg " + _printsig(ns, dummy_d) + ";\n"
+ r += syn_on
+
+ r += "always @(*) begin\n"
+ if display_run:
+ r += "\t$display(\"Running comb block #" + str(n) + "\");\n"
+ if blocking_assign:
+ r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
+ r += _printnode(ns, _AT_BLOCKING, 1, stmts, t)
+ else:
+ r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
+ r += _printnode(ns, _AT_NONBLOCKING, 1, stmts, t)
+ if dummy_signal:
+ r += syn_off
+ r += "\t" + ns.get_name(dummy_d) + " = " + ns.get_name(dummy_s) + ";\n"
+ r += syn_on
+ r += "end\n"
+ r += "\n"
+ return r
+
+
+def _printsync(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 += _printnode(ns, _AT_SIGNAL, 1, v)
+ r += "end\n\n"
+ return r
+
+
+def _call_special_classmethod(overrides, obj, method, *args, **kwargs):
+ cl = obj.__class__
+ if cl in overrides:
+ cl = overrides[cl]
+ if hasattr(cl, method):
+ return getattr(cl, method)(obj, *args, **kwargs)
+ else:
+ return None
+
+
+def _lower_specials_step(overrides, specials):
+ f = _Fragment()
+ lowered_specials = set()
+ for special in sorted(specials, key=lambda x: x.duid):
+ impl = _call_special_classmethod(overrides, special, "lower")
+ if impl is not None:
+ f += impl.get_fragment()
+ lowered_specials.add(special)
+ return f, lowered_specials
+
+
+def _can_lower(overrides, specials):
+ for special in specials:
+ cl = special.__class__
+ if cl in overrides:
+ cl = overrides[cl]
+ if hasattr(cl, "lower"):
+ return True
+ return False
+
+
+def _lower_specials(overrides, specials):
+ f, lowered_specials = _lower_specials_step(overrides, specials)
+ while _can_lower(overrides, f.specials):
+ f2, lowered_specials2 = _lower_specials_step(overrides, f.specials)
+ f += f2
+ lowered_specials |= lowered_specials2
+ f.specials -= lowered_specials2
+ return f, lowered_specials
+
+
+def _printspecials(overrides, specials, ns, add_data_file):
+ r = ""
+ for special in sorted(specials, key=lambda x: x.duid):
+ pr = _call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file)
+ if pr is None:
+ raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog")
+ r += pr
+ return r
+
+
+def convert(f, ios=None, name="top",
+ special_overrides=dict(),
+ create_clock_domains=True,
+ display_run=False, asic_syntax=False):
+ r = ConvOutput()
+ if not isinstance(f, _Fragment):
+ f = f.get_fragment()
+ if ios is None:
+ ios = set()
+
+ for cd_name in sorted(list_clock_domains(f)):
+ try:
+ f.clock_domains[cd_name]
+ except KeyError:
+ if create_clock_domains:
+ cd = ClockDomain(cd_name)
+ f.clock_domains.append(cd)
+ ios |= {cd.clk, cd.rst}
+ else:
+ raise KeyError("Unresolved clock domain: '"+cd_name+"'")
+
+ f = lower_complex_slices(f)
+ insert_resets(f)
+ f = lower_basics(f)
+ fs, lowered_specials = _lower_specials(special_overrides, f.specials)
+ f += lower_basics(fs)
+
+ ns = build_namespace(list_signals(f) \
+ | list_special_ios(f, True, True, True) \
+ | ios, _reserved_keywords)
+ ns.clock_domains = f.clock_domains
+ r.ns = ns
+
+ src = "/* Machine-generated using Migen */\n"
+ src += _printheader(f, ios, name, ns,
+ reg_initialization=not asic_syntax)
+ src += _printcomb(f, ns,
+ display_run=display_run,
+ dummy_signal=not asic_syntax,
+ blocking_assign=asic_syntax)
+ src += _printsync(f, ns)
+ src += _printspecials(special_overrides, f.specials - lowered_specials, ns, r.add_data_file)
+ src += "endmodule\n"
+ r.set_main_source(src)
+
+ return r
--- /dev/null
+from copy import copy
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy,
+ _Fragment)
+
+
+class NodeVisitor:
+ def visit(self, node):
+ if isinstance(node, Constant):
+ self.visit_Constant(node)
+ elif isinstance(node, Signal):
+ self.visit_Signal(node)
+ elif isinstance(node, ClockSignal):
+ self.visit_ClockSignal(node)
+ elif isinstance(node, ResetSignal):
+ self.visit_ResetSignal(node)
+ elif isinstance(node, _Operator):
+ self.visit_Operator(node)
+ elif isinstance(node, _Slice):
+ self.visit_Slice(node)
+ elif isinstance(node, Cat):
+ self.visit_Cat(node)
+ elif isinstance(node, Replicate):
+ self.visit_Replicate(node)
+ elif isinstance(node, _Assign):
+ self.visit_Assign(node)
+ elif isinstance(node, If):
+ self.visit_If(node)
+ elif isinstance(node, Case):
+ self.visit_Case(node)
+ elif isinstance(node, _Fragment):
+ self.visit_Fragment(node)
+ elif isinstance(node, (list, tuple)):
+ self.visit_statements(node)
+ elif isinstance(node, dict):
+ self.visit_clock_domains(node)
+ elif isinstance(node, _ArrayProxy):
+ self.visit_ArrayProxy(node)
+ elif node is not None:
+ self.visit_unknown(node)
+
+ def visit_Constant(self, node):
+ pass
+
+ def visit_Signal(self, node):
+ pass
+
+ def visit_ClockSignal(self, node):
+ pass
+
+ def visit_ResetSignal(self, node):
+ pass
+
+ def visit_Operator(self, node):
+ for o in node.operands:
+ self.visit(o)
+
+ def visit_Slice(self, node):
+ self.visit(node.value)
+
+ def visit_Cat(self, node):
+ for e in node.l:
+ self.visit(e)
+
+ def visit_Replicate(self, node):
+ self.visit(node.v)
+
+ def visit_Assign(self, node):
+ self.visit(node.l)
+ self.visit(node.r)
+
+ def visit_If(self, node):
+ self.visit(node.cond)
+ self.visit(node.t)
+ self.visit(node.f)
+
+ def visit_Case(self, node):
+ self.visit(node.test)
+ for v, statements in node.cases.items():
+ self.visit(statements)
+
+ def visit_Fragment(self, node):
+ self.visit(node.comb)
+ self.visit(node.sync)
+
+ def visit_statements(self, node):
+ for statement in node:
+ self.visit(statement)
+
+ def visit_clock_domains(self, node):
+ for clockname, statements in node.items():
+ self.visit(statements)
+
+ def visit_ArrayProxy(self, node):
+ for choice in node.choices:
+ self.visit(choice)
+ self.visit(node.key)
+
+ def visit_unknown(self, node):
+ pass
+
+
+# Default methods always copy the node, except for:
+# - Signals, ClockSignals and ResetSignals
+# - Unknown objects
+# - All fragment fields except comb and sync
+# In those cases, the original node is returned unchanged.
+class NodeTransformer:
+ def visit(self, node):
+ if isinstance(node, Constant):
+ return self.visit_Constant(node)
+ elif isinstance(node, Signal):
+ return self.visit_Signal(node)
+ elif isinstance(node, ClockSignal):
+ return self.visit_ClockSignal(node)
+ elif isinstance(node, ResetSignal):
+ return self.visit_ResetSignal(node)
+ elif isinstance(node, _Operator):
+ return self.visit_Operator(node)
+ elif isinstance(node, _Slice):
+ return self.visit_Slice(node)
+ elif isinstance(node, Cat):
+ return self.visit_Cat(node)
+ elif isinstance(node, Replicate):
+ return self.visit_Replicate(node)
+ elif isinstance(node, _Assign):
+ return self.visit_Assign(node)
+ elif isinstance(node, If):
+ return self.visit_If(node)
+ elif isinstance(node, Case):
+ return self.visit_Case(node)
+ elif isinstance(node, _Fragment):
+ return self.visit_Fragment(node)
+ elif isinstance(node, (list, tuple)):
+ return self.visit_statements(node)
+ elif isinstance(node, dict):
+ return self.visit_clock_domains(node)
+ elif isinstance(node, _ArrayProxy):
+ return self.visit_ArrayProxy(node)
+ elif node is not None:
+ return self.visit_unknown(node)
+ else:
+ return None
+
+ def visit_Constant(self, node):
+ return node
+
+ def visit_Signal(self, node):
+ return node
+
+ def visit_ClockSignal(self, node):
+ return node
+
+ def visit_ResetSignal(self, node):
+ return node
+
+ def visit_Operator(self, node):
+ return _Operator(node.op, [self.visit(o) for o in node.operands])
+
+ def visit_Slice(self, node):
+ return _Slice(self.visit(node.value), node.start, node.stop)
+
+ def visit_Cat(self, node):
+ return Cat(*[self.visit(e) for e in node.l])
+
+ def visit_Replicate(self, node):
+ return Replicate(self.visit(node.v), node.n)
+
+ def visit_Assign(self, node):
+ return _Assign(self.visit(node.l), self.visit(node.r))
+
+ def visit_If(self, node):
+ r = If(self.visit(node.cond))
+ r.t = self.visit(node.t)
+ r.f = self.visit(node.f)
+ return r
+
+ def visit_Case(self, node):
+ cases = dict((v, self.visit(statements)) for v, statements in node.cases.items())
+ r = Case(self.visit(node.test), cases)
+ return r
+
+ def visit_Fragment(self, node):
+ r = copy(node)
+ r.comb = self.visit(node.comb)
+ r.sync = self.visit(node.sync)
+ return r
+
+ # NOTE: this will always return a list, even if node is a tuple
+ def visit_statements(self, node):
+ return [self.visit(statement) for statement in node]
+
+ def visit_clock_domains(self, node):
+ return dict((clockname, self.visit(statements)) for clockname, statements in node.items())
+
+ def visit_ArrayProxy(self, node):
+ return _ArrayProxy([self.visit(choice) for choice in node.choices],
+ self.visit(node.key))
+
+ def visit_unknown(self, node):
+ return node
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+from litex.gen.fhdl.specials import Special
+from litex.gen.fhdl.bitcontainer import value_bits_sign
+from litex.gen.genlib.misc import WaitTimer
+
+
+class NoRetiming(Special):
+ def __init__(self, reg):
+ Special.__init__(self)
+ self.reg = reg
+
+ # do nothing
+ @staticmethod
+ def lower(dr):
+ return Module()
+
+
+class MultiRegImpl(Module):
+ def __init__(self, i, o, odomain, n):
+ self.i = i
+ self.o = o
+ self.odomain = odomain
+
+ w, signed = value_bits_sign(self.i)
+ self.regs = [Signal((w, signed)) for i in range(n)]
+
+ ###
+
+ src = self.i
+ for reg in self.regs:
+ sd = getattr(self.sync, self.odomain)
+ sd += reg.eq(src)
+ src = reg
+ self.comb += self.o.eq(src)
+ self.specials += [NoRetiming(reg) for reg in self.regs]
+
+
+class MultiReg(Special):
+ def __init__(self, i, o, odomain="sys", n=2):
+ Special.__init__(self)
+ self.i = wrap(i)
+ self.o = wrap(o)
+ self.odomain = odomain
+ self.n = n
+
+ def iter_expressions(self):
+ yield self, "i", SPECIAL_INPUT
+ yield self, "o", SPECIAL_OUTPUT
+
+ def rename_clock_domain(self, old, new):
+ Special.rename_clock_domain(self, old, new)
+ if self.odomain == old:
+ self.odomain = new
+
+ def list_clock_domains(self):
+ r = Special.list_clock_domains(self)
+ r.add(self.odomain)
+ return r
+
+ @staticmethod
+ def lower(dr):
+ return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
+
+
+class PulseSynchronizer(Module):
+ def __init__(self, idomain, odomain):
+ self.i = Signal()
+ self.o = Signal()
+
+ ###
+
+ toggle_i = Signal()
+ toggle_o = Signal()
+ toggle_o_r = Signal()
+
+ sync_i = getattr(self.sync, idomain)
+ sync_o = getattr(self.sync, odomain)
+
+ sync_i += If(self.i, toggle_i.eq(~toggle_i))
+ self.specials += MultiReg(toggle_i, toggle_o, odomain)
+ sync_o += toggle_o_r.eq(toggle_o)
+ self.comb += self.o.eq(toggle_o ^ toggle_o_r)
+
+
+class BusSynchronizer(Module):
+ """Clock domain transfer of several bits at once.
+
+ Ensures that all the bits form a single word that was present
+ synchronously in the input clock domain (unlike direct use of
+ ``MultiReg``)."""
+ def __init__(self, width, idomain, odomain, timeout=128):
+ self.i = Signal(width)
+ self.o = Signal(width)
+
+ if width == 1:
+ self.specials += MultiReg(self.i, self.o, odomain)
+ else:
+ sync_i = getattr(self.sync, idomain)
+ sync_o = getattr(self.sync, odomain)
+
+ starter = Signal(reset=1)
+ sync_i += starter.eq(0)
+ self.submodules._ping = PulseSynchronizer(idomain, odomain)
+ self.submodules._pong = PulseSynchronizer(odomain, idomain)
+ self.submodules._timeout = WaitTimer(timeout)
+ self.comb += [
+ self._timeout.wait.eq(~self._ping.i),
+ self._ping.i.eq(starter | self._pong.o | self._timeout.done),
+ self._pong.i.eq(self._ping.i)
+ ]
+
+ ibuffer = Signal(width)
+ obuffer = Signal(width)
+ sync_i += If(self._pong.o, ibuffer.eq(self.i))
+ self.specials += MultiReg(ibuffer, obuffer, odomain)
+ sync_o += If(self._ping.o, self.o.eq(obuffer))
+
+
+class GrayCounter(Module):
+ def __init__(self, width):
+ self.ce = Signal()
+ self.q = Signal(width)
+ self.q_next = Signal(width)
+ self.q_binary = Signal(width)
+ self.q_next_binary = Signal(width)
+
+ ###
+
+ self.comb += [
+ If(self.ce,
+ self.q_next_binary.eq(self.q_binary + 1)
+ ).Else(
+ self.q_next_binary.eq(self.q_binary)
+ ),
+ self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
+ ]
+ self.sync += [
+ self.q_binary.eq(self.q_next_binary),
+ self.q.eq(self.q_next)
+ ]
--- /dev/null
+"""
+Encoders and decoders between binary and one-hot representation
+"""
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+
+
+class Encoder(Module):
+ """Encode one-hot to binary
+
+ If `n` is low, the `o` th bit in `i` is asserted, else none or
+ multiple bits are asserted.
+
+ Parameters
+ ----------
+ width : int
+ Bit width of the input
+
+ Attributes
+ ----------
+ i : Signal(width), in
+ One-hot input
+ o : Signal(max=width), out
+ Encoded binary
+ n : Signal(1), out
+ Invalid, either none or multiple input bits are asserted
+ """
+ def __init__(self, width):
+ self.i = Signal(width) # one-hot
+ self.o = Signal(max=max(2, width)) # binary
+ self.n = Signal() # invalid: none or multiple
+ act = dict((1<<j, self.o.eq(j)) for j in range(width))
+ act["default"] = self.n.eq(1)
+ self.comb += Case(self.i, act)
+
+
+class PriorityEncoder(Module):
+ """Priority encode requests to binary
+
+ If `n` is low, the `o` th bit in `i` is asserted and the bits below
+ `o` are unasserted, else `o == 0`. The LSB has priority.
+
+ Parameters
+ ----------
+ width : int
+ Bit width of the input
+
+ Attributes
+ ----------
+ i : Signal(width), in
+ Input requests
+ o : Signal(max=width), out
+ Encoded binary
+ n : Signal(1), out
+ Invalid, no input bits are asserted
+ """
+ def __init__(self, width):
+ self.i = Signal(width) # one-hot, lsb has priority
+ self.o = Signal(max=max(2, width)) # binary
+ self.n = Signal() # none
+ for j in range(width)[::-1]: # last has priority
+ self.comb += If(self.i[j], self.o.eq(j))
+ self.comb += self.n.eq(self.i == 0)
+
+
+class Decoder(Module):
+ """Decode binary to one-hot
+
+ If `n` is low, the `i` th bit in `o` is asserted, the others are
+ not, else `o == 0`.
+
+ Parameters
+ ----------
+ width : int
+ Bit width of the output
+
+ Attributes
+ ----------
+ i : Signal(max=width), in
+ Input binary
+ o : Signal(width), out
+ Decoded one-hot
+ n : Signal(1), in
+ Invalid, no output bits are to be asserted
+ """
+
+ def __init__(self, width):
+ self.i = Signal(max=max(2, width)) # binary
+ self.n = Signal() # none/invalid
+ self.o = Signal(width) # one-hot
+ act = dict((j, self.o.eq(1<<j)) for j in range(width))
+ self.comb += Case(self.i, act)
+ self.comb += If(self.n, self.o.eq(0))
+
+
+class PriorityDecoder(Decoder):
+ pass # same
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+
+
+class Divider(Module):
+ def __init__(self, w):
+ self.start_i = Signal()
+ self.dividend_i = Signal(w)
+ self.divisor_i = Signal(w)
+ self.ready_o = Signal()
+ self.quotient_o = Signal(w)
+ self.remainder_o = Signal(w)
+
+ ###
+
+ qr = Signal(2*w)
+ counter = Signal(max=w+1)
+ divisor_r = Signal(w)
+ diff = Signal(w+1)
+
+ self.comb += [
+ self.quotient_o.eq(qr[:w]),
+ self.remainder_o.eq(qr[w:]),
+ self.ready_o.eq(counter == 0),
+ diff.eq(qr[w-1:] - divisor_r)
+ ]
+ self.sync += [
+ If(self.start_i,
+ counter.eq(w),
+ qr.eq(self.dividend_i),
+ divisor_r.eq(self.divisor_i)
+ ).Elif(~self.ready_o,
+ If(diff[w],
+ qr.eq(Cat(0, qr[:2*w-1]))
+ ).Else(
+ qr.eq(Cat(1, qr[:w-1], diff[:w]))
+ ),
+ counter.eq(counter - 1)
+ )
+ ]
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+from litex.gen.fhdl.specials import Memory
+from litex.gen.fhdl.bitcontainer import log2_int
+from litex.gen.fhdl.decorators import ClockDomainsRenamer
+from litex.gen.genlib.cdc import NoRetiming, MultiReg, GrayCounter
+
+
+def _inc(signal, modulo):
+ if modulo == 2**len(signal):
+ return signal.eq(signal + 1)
+ else:
+ return If(signal == (modulo - 1),
+ signal.eq(0)
+ ).Else(
+ signal.eq(signal + 1)
+ )
+
+
+class _FIFOInterface:
+ """
+ Data written to the input interface (`din`, `we`, `writable`) is
+ buffered and can be read at the output interface (`dout`, `re`,
+ `readable`). The data entry written first to the input
+ also appears first on the output.
+
+ Parameters
+ ----------
+ width : int
+ Bit width for the data.
+ depth : int
+ Depth of the FIFO.
+
+ Attributes
+ ----------
+ din : in, width
+ Input data
+ writable : out
+ There is space in the FIFO and `we` can be asserted to load new data.
+ we : in
+ Write enable signal to latch `din` into the FIFO. Does nothing if
+ `writable` is not asserted.
+ dout : out, width
+ Output data. Only valid if `readable` is asserted.
+ readable : out
+ Output data `dout` valid, FIFO not empty.
+ re : in
+ Acknowledge `dout`. If asserted, the next entry will be
+ available on the next cycle (if `readable` is high then).
+ """
+ def __init__(self, width, depth):
+ self.we = Signal()
+ self.writable = Signal() # not full
+ self.re = Signal()
+ self.readable = Signal() # not empty
+
+ self.din = Signal(width)
+ self.dout = Signal(width)
+ self.width = width
+
+
+class SyncFIFO(Module, _FIFOInterface):
+ """Synchronous FIFO (first in, first out)
+
+ Read and write interfaces are accessed from the same clock domain.
+ If different clock domains are needed, use :class:`AsyncFIFO`.
+
+ {interface}
+ level : out
+ Number of unread entries.
+ replace : in
+ Replaces the last entry written into the FIFO with `din`. Does nothing
+ if that entry has already been read (i.e. the FIFO is empty).
+ Assert in conjunction with `we`.
+ """
+ __doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
+
+ def __init__(self, width, depth, fwft=True):
+ _FIFOInterface.__init__(self, width, depth)
+
+ self.level = Signal(max=depth+1)
+ self.replace = Signal()
+
+ ###
+
+ produce = Signal(max=depth)
+ consume = Signal(max=depth)
+ storage = Memory(self.width, depth)
+ self.specials += storage
+
+ wrport = storage.get_port(write_capable=True)
+ self.specials += wrport
+ self.comb += [
+ If(self.replace,
+ wrport.adr.eq(produce-1)
+ ).Else(
+ wrport.adr.eq(produce)
+ ),
+ wrport.dat_w.eq(self.din),
+ wrport.we.eq(self.we & (self.writable | self.replace))
+ ]
+ self.sync += If(self.we & self.writable & ~self.replace,
+ _inc(produce, depth))
+
+ do_read = Signal()
+ self.comb += do_read.eq(self.readable & self.re)
+
+ rdport = storage.get_port(async_read=fwft, has_re=not fwft)
+ self.specials += rdport
+ self.comb += [
+ rdport.adr.eq(consume),
+ self.dout.eq(rdport.dat_r)
+ ]
+ if not fwft:
+ self.comb += rdport.re.eq(do_read)
+ self.sync += If(do_read, _inc(consume, depth))
+
+ self.sync += \
+ If(self.we & self.writable & ~self.replace,
+ If(~do_read, self.level.eq(self.level + 1))
+ ).Elif(do_read,
+ self.level.eq(self.level - 1)
+ )
+ self.comb += [
+ self.writable.eq(self.level != depth),
+ self.readable.eq(self.level != 0)
+ ]
+
+
+class SyncFIFOBuffered(Module, _FIFOInterface):
+ def __init__(self, width, depth):
+ _FIFOInterface.__init__(self, width, depth)
+ self.submodules.fifo = fifo = SyncFIFO(width, depth, False)
+
+ self.writable = fifo.writable
+ self.din = fifo.din
+ self.we = fifo.we
+ self.dout = fifo.dout
+ self.level = Signal(max=depth+2)
+
+ ###
+
+ self.comb += fifo.re.eq(fifo.readable & (~self.readable | self.re))
+ self.sync += \
+ If(fifo.re,
+ self.readable.eq(1),
+ ).Elif(self.re,
+ self.readable.eq(0),
+ )
+ self.comb += self.level.eq(fifo.level + self.readable)
+
+
+class AsyncFIFO(Module, _FIFOInterface):
+ """Asynchronous FIFO (first in, first out)
+
+ Read and write interfaces are accessed from different clock domains,
+ named `read` and `write`. Use `ClockDomainsRenamer` to rename to
+ other names.
+
+ {interface}
+ """
+ __doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
+
+ def __init__(self, width, depth):
+ _FIFOInterface.__init__(self, width, depth)
+
+ ###
+
+ depth_bits = log2_int(depth, True)
+
+ produce = ClockDomainsRenamer("write")(GrayCounter(depth_bits+1))
+ consume = ClockDomainsRenamer("read")(GrayCounter(depth_bits+1))
+ self.submodules += produce, consume
+ self.comb += [
+ produce.ce.eq(self.writable & self.we),
+ consume.ce.eq(self.readable & self.re)
+ ]
+
+ produce_rdomain = Signal(depth_bits+1)
+ self.specials += [
+ NoRetiming(produce.q),
+ MultiReg(produce.q, produce_rdomain, "read")
+ ]
+ consume_wdomain = Signal(depth_bits+1)
+ self.specials += [
+ NoRetiming(consume.q),
+ MultiReg(consume.q, consume_wdomain, "write")
+ ]
+ if depth_bits == 1:
+ self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1])
+ | (produce.q[-2] == consume_wdomain[-2]))
+ else:
+ self.comb += [
+ self.writable.eq((produce.q[-1] == consume_wdomain[-1])
+ | (produce.q[-2] == consume_wdomain[-2])
+ | (produce.q[:-2] != consume_wdomain[:-2]))
+ ]
+ self.comb += self.readable.eq(consume.q != produce_rdomain)
+
+ storage = Memory(self.width, depth)
+ self.specials += storage
+ wrport = storage.get_port(write_capable=True, clock_domain="write")
+ self.specials += wrport
+ self.comb += [
+ wrport.adr.eq(produce.q_binary[:-1]),
+ wrport.dat_w.eq(self.din),
+ wrport.we.eq(produce.ce)
+ ]
+ rdport = storage.get_port(clock_domain="read")
+ self.specials += rdport
+ self.comb += [
+ rdport.adr.eq(consume.q_next_binary[:-1]),
+ self.dout.eq(rdport.dat_r)
+ ]
--- /dev/null
+from collections import OrderedDict
+
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.structure import _Statement, _Slice, _ArrayProxy
+from litex.gen.fhdl.module import Module, FinalizeError
+from litex.gen.fhdl.visit import NodeTransformer
+from litex.gen.fhdl.bitcontainer import value_bits_sign
+
+
+__all__ = ["AnonymousState", "NextState", "NextValue", "FSM"]
+
+
+class AnonymousState:
+ pass
+
+
+# do not use namedtuple here as it inherits tuple
+# and the latter is used elsewhere in FHDL
+class NextState(_Statement):
+ def __init__(self, state):
+ self.state = state
+
+
+class NextValue(_Statement):
+ def __init__(self, target, value):
+ self.target = target
+ self.value = value
+
+
+def _target_eq(a, b):
+ if type(a) != type(b):
+ return False
+ ty = type(a)
+ if ty == Constant:
+ return a.value == b.value
+ elif ty == Signal:
+ return a is b
+ elif ty == Cat:
+ return all(_target_eq(x, y) for x, y in zip(a.l, b.l))
+ elif ty == _Slice:
+ return (_target_eq(a.value, b.value)
+ and a.start == b.start
+ and a.end == b.end)
+ elif ty == _ArrayProxy:
+ return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices))
+ and _target_eq(a.key, b.key))
+ else:
+ raise ValueError("NextValue cannot be used with target type '{}'"
+ .format(ty))
+
+
+class _LowerNext(NodeTransformer):
+ def __init__(self, next_state_signal, encoding, aliases):
+ self.next_state_signal = next_state_signal
+ self.encoding = encoding
+ self.aliases = aliases
+ # (target, next_value_ce, next_value)
+ self.registers = []
+
+ def _get_register_control(self, target):
+ for x in self.registers:
+ if _target_eq(target, x[0]):
+ return x[1], x[2]
+ raise KeyError
+
+ def visit_unknown(self, node):
+ if isinstance(node, NextState):
+ try:
+ actual_state = self.aliases[node.state]
+ except KeyError:
+ actual_state = node.state
+ return self.next_state_signal.eq(self.encoding[actual_state])
+ elif isinstance(node, NextValue):
+ try:
+ next_value_ce, next_value = self._get_register_control(node.target)
+ except KeyError:
+ related = node.target if isinstance(node.target, Signal) else None
+ next_value = Signal(bits_sign=value_bits_sign(node.target), related=related)
+ next_value_ce = Signal(related=related)
+ self.registers.append((node.target, next_value_ce, next_value))
+ return next_value.eq(node.value), next_value_ce.eq(1)
+ else:
+ return node
+
+
+class FSM(Module):
+ def __init__(self, reset_state=None):
+ self.actions = OrderedDict()
+ self.state_aliases = dict()
+ self.reset_state = reset_state
+
+ self.before_entering_signals = OrderedDict()
+ self.before_leaving_signals = OrderedDict()
+ self.after_entering_signals = OrderedDict()
+ self.after_leaving_signals = OrderedDict()
+
+ def act(self, state, *statements):
+ if self.finalized:
+ raise FinalizeError
+ if self.reset_state is None:
+ self.reset_state = state
+ if state not in self.actions:
+ self.actions[state] = []
+ self.actions[state] += statements
+
+ def delayed_enter(self, name, target, delay):
+ if self.finalized:
+ raise FinalizeError
+ if delay > 0:
+ state = name
+ for i in range(delay):
+ if i == delay - 1:
+ next_state = target
+ else:
+ next_state = AnonymousState()
+ self.act(state, NextState(next_state))
+ state = next_state
+ else:
+ self.state_aliases[name] = target
+
+ def ongoing(self, state):
+ is_ongoing = Signal()
+ self.act(state, is_ongoing.eq(1))
+ return is_ongoing
+
+ def _get_signal(self, d, state):
+ if state not in self.actions:
+ self.actions[state] = []
+ try:
+ return d[state]
+ except KeyError:
+ is_el = Signal()
+ d[state] = is_el
+ return is_el
+
+ def before_entering(self, state):
+ return self._get_signal(self.before_entering_signals, state)
+
+ def before_leaving(self, state):
+ return self._get_signal(self.before_leaving_signals, state)
+
+ def after_entering(self, state):
+ signal = self._get_signal(self.after_entering_signals, state)
+ self.sync += signal.eq(self.before_entering(state))
+ return signal
+
+ def after_leaving(self, state):
+ signal = self._get_signal(self.after_leaving_signals, state)
+ self.sync += signal.eq(self.before_leaving(state))
+ return signal
+
+ def do_finalize(self):
+ nstates = len(self.actions)
+ self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys()))
+ self.state = Signal(max=nstates, reset=self.encoding[self.reset_state])
+ self.next_state = Signal(max=nstates)
+
+ ln = _LowerNext(self.next_state, self.encoding, self.state_aliases)
+ cases = dict((self.encoding[k], ln.visit(v)) for k, v in self.actions.items() if v)
+ self.comb += [
+ self.next_state.eq(self.state),
+ Case(self.state, cases).makedefault(self.encoding[self.reset_state])
+ ]
+ self.sync += self.state.eq(self.next_state)
+ for register, next_value_ce, next_value in ln.registers:
+ self.sync += If(next_value_ce, register.eq(next_value))
+
+ # drive entering/leaving signals
+ for state, signal in self.before_leaving_signals.items():
+ encoded = self.encoding[state]
+ self.comb += signal.eq((self.state == encoded) & ~(self.next_state == encoded))
+ if self.reset_state in self.after_entering_signals:
+ self.after_entering_signals[self.reset_state].reset = 1
+ for state, signal in self.before_entering_signals.items():
+ encoded = self.encoding[state]
+ self.comb += signal.eq(~(self.state == encoded) & (self.next_state == encoded))
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+from litex.gen.fhdl.specials import Special
+
+
+class DifferentialInput(Special):
+ def __init__(self, i_p, i_n, o):
+ Special.__init__(self)
+ self.i_p = wrap(i_p)
+ self.i_n = wrap(i_n)
+ self.o = wrap(o)
+
+ def iter_expressions(self):
+ yield self, "i_p", SPECIAL_INPUT
+ yield self, "i_n", SPECIAL_INPUT
+ yield self, "o", SPECIAL_OUTPUT
+
+ @staticmethod
+ def lower(dr):
+ raise NotImplementedError("Attempted to use a differential input, but platform does not support them")
+
+
+class DifferentialOutput(Special):
+ def __init__(self, i, o_p, o_n):
+ Special.__init__(self)
+ self.i = wrap(i)
+ self.o_p = wrap(o_p)
+ self.o_n = wrap(o_n)
+
+ def iter_expressions(self):
+ yield self, "i", SPECIAL_INPUT
+ yield self, "o_p", SPECIAL_OUTPUT
+ yield self, "o_n", SPECIAL_OUTPUT
+
+ @staticmethod
+ def lower(dr):
+ raise NotImplementedError("Attempted to use a differential output, but platform does not support them")
+
+
+class CRG(Module):
+ def __init__(self, clk, rst=0):
+ self.clock_domains.cd_sys = ClockDomain()
+ self.clock_domains.cd_por = ClockDomain(reset_less=True)
+
+ if hasattr(clk, "p"):
+ clk_se = Signal()
+ self.specials += DifferentialInput(clk.p, clk.n, clk_se)
+ clk = clk_se
+
+ # Power on Reset (vendor agnostic)
+ int_rst = Signal(reset=1)
+ self.sync.por += int_rst.eq(rst)
+ self.comb += [
+ self.cd_sys.clk.eq(clk),
+ self.cd_por.clk.eq(clk),
+ self.cd_sys.rst.eq(int_rst)
+ ]
+
+
+class DDRInput(Special):
+ def __init__(self, i, o1, o2, clk=ClockSignal()):
+ Special.__init__(self)
+ self.i = wrap(i)
+ self.o1 = wrap(o1)
+ self.o2 = wrap(o2)
+ self.clk = wrap(clk)
+
+ def iter_expressions(self):
+ yield self, "i", SPECIAL_INPUT
+ yield self, "o1", SPECIAL_OUTPUT
+ yield self, "o2", SPECIAL_OUTPUT
+ yield self, "clk", SPECIAL_INPUT
+
+ @staticmethod
+ def lower(dr):
+ raise NotImplementedError("Attempted to use a DDR input, but platform does not support them")
+
+
+class DDROutput(Special):
+ def __init__(self, i1, i2, o, clk=ClockSignal()):
+ Special.__init__(self)
+ self.i1 = i1
+ self.i2 = i2
+ self.o = o
+ self.clk = clk
+
+ def iter_expressions(self):
+ yield self, "i1", SPECIAL_INPUT
+ yield self, "i2", SPECIAL_INPUT
+ yield self, "o", SPECIAL_OUTPUT
+ yield self, "clk", SPECIAL_INPUT
+
+ @staticmethod
+ def lower(dr):
+ raise NotImplementedError("Attempted to use a DDR output, but platform does not support them")
+
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+from litex.gen.fhdl.bitcontainer import bits_for
+
+
+def split(v, *counts):
+ r = []
+ offset = 0
+ for n in counts:
+ if n != 0:
+ r.append(v[offset:offset+n])
+ else:
+ r.append(None)
+ offset += n
+ return tuple(r)
+
+
+def displacer(signal, shift, output, n=None, reverse=False):
+ if shift is None:
+ return output.eq(signal)
+ if n is None:
+ n = 2**len(shift)
+ w = len(signal)
+ if reverse:
+ r = reversed(range(n))
+ else:
+ r = range(n)
+ l = [Replicate(shift == i, w) & signal for i in r]
+ return output.eq(Cat(*l))
+
+
+def chooser(signal, shift, output, n=None, reverse=False):
+ if shift is None:
+ return output.eq(signal)
+ if n is None:
+ n = 2**len(shift)
+ w = len(output)
+ cases = {}
+ for i in range(n):
+ if reverse:
+ s = n - i - 1
+ else:
+ s = i
+ cases[i] = [output.eq(signal[s*w:(s+1)*w])]
+ return Case(shift, cases).makedefault()
+
+
+def timeline(trigger, events):
+ lastevent = max([e[0] for e in events])
+ counter = Signal(max=lastevent+1)
+
+ counterlogic = If(counter != 0,
+ counter.eq(counter + 1)
+ ).Elif(trigger,
+ counter.eq(1)
+ )
+ # insert counter reset if it doesn't naturally overflow
+ # (test if lastevent+1 is a power of 2)
+ if (lastevent & (lastevent + 1)) != 0:
+ counterlogic = If(counter == lastevent,
+ counter.eq(0)
+ ).Else(
+ counterlogic
+ )
+
+ def get_cond(e):
+ if e[0] == 0:
+ return trigger & (counter == 0)
+ else:
+ return counter == e[0]
+ sync = [If(get_cond(e), *e[1]) for e in events]
+ sync.append(counterlogic)
+ return sync
+
+
+class WaitTimer(Module):
+ def __init__(self, t):
+ self.wait = Signal()
+ self.done = Signal()
+
+ # # #
+
+ count = Signal(bits_for(t), reset=t)
+ self.comb += self.done.eq(count == 0)
+ self.sync += \
+ If(self.wait,
+ If(~self.done, count.eq(count - 1))
+ ).Else(count.eq(count.reset))
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.tracer import get_obj_var_name
+
+from functools import reduce
+from operator import or_
+
+
+(DIR_NONE, DIR_S_TO_M, DIR_M_TO_S) = range(3)
+
+# Possible layout elements:
+# 1. (name, size)
+# 2. (name, size, direction)
+# 3. (name, sublayout)
+# size can be an int, or a (int, bool) tuple for signed numbers
+# sublayout must be a list
+
+
+def set_layout_parameters(layout, **layout_dict):
+ def resolve(p):
+ if isinstance(p, str):
+ try:
+ return layout_dict[p]
+ except KeyError:
+ return p
+ else:
+ return p
+
+ r = []
+ for f in layout:
+ if isinstance(f[1], (int, tuple, str)): # cases 1/2
+ if len(f) == 3:
+ r.append((f[0], resolve(f[1]), f[2]))
+ else:
+ r.append((f[0], resolve(f[1])))
+ elif isinstance(f[1], list): # case 3
+ r.append((f[0], set_layout_parameters(f[1], **layout_dict)))
+ else:
+ raise TypeError
+ return r
+
+
+def layout_len(layout):
+ r = 0
+ for f in layout:
+ if isinstance(f[1], (int, tuple)): # cases 1/2
+ if len(f) == 3:
+ fname, fsize, fdirection = f
+ else:
+ fname, fsize = f
+ elif isinstance(f[1], list): # case 3
+ fname, fsublayout = f
+ fsize = layout_len(fsublayout)
+ else:
+ raise TypeError
+ if isinstance(fsize, tuple):
+ r += fsize[0]
+ else:
+ r += fsize
+ return r
+
+
+def layout_get(layout, name):
+ for f in layout:
+ if f[0] == name:
+ return f
+ raise KeyError(name)
+
+
+def layout_partial(layout, *elements):
+ r = []
+ for path in elements:
+ path_s = path.split("/")
+ last = path_s.pop()
+ copy_ref = layout
+ insert_ref = r
+ for hop in path_s:
+ name, copy_ref = layout_get(copy_ref, hop)
+ try:
+ name, insert_ref = layout_get(insert_ref, hop)
+ except KeyError:
+ new_insert_ref = []
+ insert_ref.append((hop, new_insert_ref))
+ insert_ref = new_insert_ref
+ insert_ref.append(layout_get(copy_ref, last))
+ return r
+
+
+class Record:
+ def __init__(self, layout, name=None):
+ self.name = get_obj_var_name(name, "")
+ self.layout = layout
+
+ if self.name:
+ prefix = self.name + "_"
+ else:
+ prefix = ""
+ for f in self.layout:
+ if isinstance(f[1], (int, tuple)): # cases 1/2
+ if(len(f) == 3):
+ fname, fsize, fdirection = f
+ else:
+ fname, fsize = f
+ finst = Signal(fsize, name=prefix + fname)
+ elif isinstance(f[1], list): # case 3
+ fname, fsublayout = f
+ finst = Record(fsublayout, prefix + fname)
+ else:
+ raise TypeError
+ setattr(self, fname, finst)
+
+ def eq(self, other):
+ return [getattr(self, f[0]).eq(getattr(other, f[0]))
+ for f in self.layout if hasattr(other, f[0])]
+
+ def iter_flat(self):
+ for f in self.layout:
+ e = getattr(self, f[0])
+ if isinstance(e, Signal):
+ if len(f) == 3:
+ yield e, f[2]
+ else:
+ yield e, DIR_NONE
+ elif isinstance(e, Record):
+ yield from e.iter_flat()
+ else:
+ raise TypeError
+
+ def flatten(self):
+ return [signal for signal, direction in self.iter_flat()]
+
+ def raw_bits(self):
+ return Cat(*self.flatten())
+
+ def connect(self, *slaves, leave_out=set()):
+ if isinstance(leave_out, str):
+ leave_out = {leave_out}
+ r = []
+ for f in self.layout:
+ field = f[0]
+ if field not in leave_out:
+ self_e = getattr(self, field)
+ if isinstance(self_e, Signal):
+ direction = f[2]
+ if direction == DIR_M_TO_S:
+ r += [getattr(slave, field).eq(self_e) for slave in slaves]
+ elif direction == DIR_S_TO_M:
+ r.append(self_e.eq(reduce(or_, [getattr(slave, field) for slave in slaves])))
+ else:
+ raise TypeError
+ else:
+ for slave in slaves:
+ r += self_e.connect(getattr(slave, field), leave_out=leave_out)
+ return r
+
+ def connect_flat(self, *slaves):
+ r = []
+ iter_slaves = [slave.iter_flat() for slave in slaves]
+ for m_signal, m_direction in self.iter_flat():
+ if m_direction == DIR_M_TO_S:
+ for iter_slave in iter_slaves:
+ s_signal, s_direction = next(iter_slave)
+ assert(s_direction == DIR_M_TO_S)
+ r.append(s_signal.eq(m_signal))
+ elif m_direction == DIR_S_TO_M:
+ s_signals = []
+ for iter_slave in iter_slaves:
+ s_signal, s_direction = next(iter_slave)
+ assert(s_direction == DIR_S_TO_M)
+ s_signals.append(s_signal)
+ r.append(m_signal.eq(reduce(or_, s_signals)))
+ else:
+ raise TypeError
+ return r
+
+ def __len__(self):
+ return layout_len(self.layout)
+
+ def __repr__(self):
+ return "<Record " + ":".join(f[0] for f in self.layout) + " at " + hex(id(self)) + ">"
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.specials import Special
+
+
+class AsyncResetSynchronizer(Special):
+ def __init__(self, cd, async_reset):
+ Special.__init__(self)
+ self.cd = cd
+ self.async_reset = wrap(async_reset)
+
+ def iter_expressions(self):
+ yield self.cd, "clk", SPECIAL_INPUT
+ yield self.cd, "rst", SPECIAL_OUTPUT
+ yield self, "async_reset", SPECIAL_INPUT
+
+ @staticmethod
+ def lower(dr):
+ raise NotImplementedError("Attempted to use a reset synchronizer, but platform does not support them")
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+
+
+(SP_WITHDRAW, SP_CE) = range(2)
+
+
+class RoundRobin(Module):
+ def __init__(self, n, switch_policy=SP_WITHDRAW):
+ self.request = Signal(n)
+ self.grant = Signal(max=max(2, n))
+ self.switch_policy = switch_policy
+ if self.switch_policy == SP_CE:
+ self.ce = Signal()
+
+ ###
+
+ if n > 1:
+ cases = {}
+ for i in range(n):
+ switch = []
+ for j in reversed(range(i+1, i+n)):
+ t = j % n
+ switch = [
+ If(self.request[t],
+ self.grant.eq(t)
+ ).Else(
+ *switch
+ )
+ ]
+ if self.switch_policy == SP_WITHDRAW:
+ case = [If(~self.request[i], *switch)]
+ else:
+ case = switch
+ cases[i] = case
+ statement = Case(self.grant, cases)
+ if self.switch_policy == SP_CE:
+ statement = If(self.ce, statement)
+ self.sync += statement
+ else:
+ self.comb += self.grant.eq(0)
--- /dev/null
+from litex.gen.fhdl.structure import *
+from litex.gen.fhdl.module import Module
+
+
+class BitonicSort(Module):
+ """Combinatorial sorting network
+
+ The Bitonic sort is implemented as a combinatorial sort using
+ comparators and multiplexers. Its asymptotic complexity (in terms of
+ number of comparators/muxes) is O(n log(n)**2), like mergesort or
+ shellsort.
+
+ http://www.dps.uibk.ac.at/~cosenza/teaching/gpu/sort-batcher.pdf
+
+ http://www.inf.fh-lensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm
+
+ http://www.myhdl.org/doku.php/cookbook:bitonic
+
+ Parameters
+ ----------
+ n : int
+ Number of inputs and output signals.
+ m : int
+ Bit width of inputs and outputs. Or a tuple of `(m, signed)`.
+ ascending : bool
+ Sort direction. `True` if input is to be sorted ascending,
+ `False` for descending. Defaults to ascending.
+
+ Attributes
+ ----------
+ i : list of Signals, in
+ Input values, each `m` wide.
+ o : list of Signals, out
+ Output values, sorted, each `m` bits wide.
+ """
+ def __init__(self, n, m, ascending=True):
+ self.i = [Signal(m) for i in range(n)]
+ self.o = [Signal(m) for i in range(n)]
+ self._sort(self.i, self.o, int(ascending), m)
+
+ def _sort_two(self, i0, i1, o0, o1, dir):
+ self.comb += [
+ o0.eq(i0),
+ o1.eq(i1),
+ If(dir == (i0 > i1),
+ o0.eq(i1),
+ o1.eq(i0),
+ )]
+
+ def _merge(self, i, o, dir, m):
+ n = len(i)
+ k = n//2
+ if n > 1:
+ t = [Signal(m) for j in range(n)]
+ for j in range(k):
+ self._sort_two(i[j], i[j + k], t[j], t[j + k], dir)
+ self._merge(t[:k], o[:k], dir, m)
+ self._merge(t[k:], o[k:], dir, m)
+ else:
+ self.comb += o[0].eq(i[0])
+
+ def _sort(self, i, o, dir, m):
+ n = len(i)
+ k = n//2
+ if n > 1:
+ t = [Signal(m) for j in range(n)]
+ self._sort(i[:k], t[:k], 1, m) # ascending
+ self._sort(i[k:], t[k:], 0, m) # descending
+ self._merge(t, o, dir, m)
+ else:
+ self.comb += o[0].eq(i[0])
--- /dev/null
+import warnings
+import sys
+
+from litex.gen import *
+from litex.gen.fhdl.structure import _Fragment
+
+from litex.gen.fhdl import verilog
+from litex.gen.sim.ipc import *
+from litex.gen.sim import icarus
+
+
+class TopLevel:
+ def __init__(self, vcd_name=None, vcd_level=1,
+ top_name="top", dut_type="dut", dut_name="dut",
+ cd_name="sys", clk_period=10):
+ self.vcd_name = vcd_name
+ self.vcd_level = vcd_level
+ self.top_name = top_name
+ self.dut_type = dut_type
+ self.dut_name = dut_name
+
+ self._cd_name = cd_name
+ self._clk_period = clk_period
+
+ cd = ClockDomain(self._cd_name)
+ self.clock_domains = [cd]
+ self.ios = {cd.clk, cd.rst}
+
+ def get(self, sockaddr):
+ if sys.platform == "win32":
+ sockaddr = sockaddr[0] # Get the IP address only
+
+ template1 = """`timescale 1ns / 1ps
+
+module {top_name}();
+
+reg {clk_name};
+reg {rst_name};
+
+initial begin
+ {rst_name} <= 1'b1;
+ @(posedge {clk_name});
+ {rst_name} <= 1'b0;
+end
+
+always begin
+ {clk_name} <= 1'b0;
+ #{hclk_period};
+ {clk_name} <= 1'b1;
+ #{hclk_period};
+end
+
+{dut_type} {dut_name}(
+ .{rst_name}({rst_name}),
+ .{clk_name}({clk_name})
+);
+
+initial $migensim_connect("{sockaddr}");
+always @(posedge {clk_name}) $migensim_tick;
+"""
+ template2 = """
+initial begin
+ $dumpfile("{vcd_name}");
+ $dumpvars({vcd_level}, {dut_name});
+end
+"""
+ r = template1.format(top_name=self.top_name,
+ dut_type=self.dut_type,
+ dut_name=self.dut_name,
+ clk_name=self._cd_name + "_clk",
+ rst_name=self._cd_name + "_rst",
+ hclk_period=str(self._clk_period/2),
+ sockaddr=sockaddr)
+ if self.vcd_name is not None:
+ r += template2.format(vcd_name=self.vcd_name,
+ vcd_level=str(self.vcd_level),
+ dut_name=self.dut_name)
+ r += "\nendmodule"
+ return r
+
+
+class Simulator:
+ def __init__(self, fragment, top_level=None, sim_runner=None, sockaddr="simsocket", **vopts):
+ if not isinstance(fragment, _Fragment):
+ fragment = fragment.get_fragment()
+ if top_level is None:
+ top_level = TopLevel()
+ if sim_runner is None:
+ sim_runner = icarus.Runner()
+ self.top_level = top_level
+ if sys.platform == "win32":
+ sockaddr = ("127.0.0.1", 50007)
+ self.ipc = Initiator(sockaddr)
+ else:
+ self.ipc = Initiator(sockaddr)
+
+ self.sim_runner = sim_runner
+
+ c_top = self.top_level.get(sockaddr)
+
+ fragment = fragment + _Fragment(clock_domains=top_level.clock_domains)
+ c_fragment = verilog.convert(fragment,
+ ios=self.top_level.ios,
+ name=self.top_level.dut_type,
+ **vopts)
+ self.namespace = c_fragment.ns
+
+ self.cycle_counter = -1
+
+ self.sim_runner = sim_runner
+ self.sim_runner.start(c_top, c_fragment)
+ self.ipc.accept()
+ reply = self.ipc.recv()
+ assert(isinstance(reply, MessageTick))
+
+ self.sim_functions = fragment.sim
+ self.active_sim_functions = set(f for f in fragment.sim if not hasattr(f, "passive") or not f.passive)
+ self.unreferenced = {}
+
+ def run(self, ncycles=None):
+ counter = 0
+
+ if self.active_sim_functions:
+ if ncycles is None:
+ def continue_simulation():
+ return bool(self.active_sim_functions)
+ else:
+ def continue_simulation():
+ return self.active_sim_functions and counter < ncycles
+ else:
+ if ncycles is None:
+ raise ValueError("No active simulation function present - must specify ncycles to end simulation")
+ def continue_simulation():
+ return counter < ncycles
+
+ while continue_simulation():
+ self.cycle_counter += 1
+ counter += 1
+ self.ipc.send(MessageGo())
+ reply = self.ipc.recv()
+ assert(isinstance(reply, MessageTick))
+
+ del_list = []
+ for s in self.sim_functions:
+ try:
+ s(self)
+ except StopSimulation:
+ del_list.append(s)
+ for s in del_list:
+ self.sim_functions.remove(s)
+ try:
+ self.active_sim_functions.remove(s)
+ except KeyError:
+ pass
+
+ def get_unreferenced(self, item, index):
+ try:
+ return self.unreferenced[(item, index)]
+ except KeyError:
+ if isinstance(item, Memory):
+ try:
+ init = item.init[index]
+ except (TypeError, IndexError):
+ init = 0
+ else:
+ init = item.reset
+ self.unreferenced[(item, index)] = init
+ return init
+
+ def rd(self, item, index=0):
+ try:
+ name = self.top_level.top_name + "." \
+ + self.top_level.dut_name + "." \
+ + self.namespace.get_name(item)
+ self.ipc.send(MessageRead(name, Int32(index)))
+ reply = self.ipc.recv()
+ assert(isinstance(reply, MessageReadReply))
+ value = reply.value
+ except KeyError:
+ value = self.get_unreferenced(item, index)
+ if isinstance(item, Memory):
+ signed = False
+ nbits = item.width
+ else:
+ signed = item.signed
+ nbits = len(item)
+ value = value & (2**nbits - 1)
+ if signed and (value & 2**(nbits - 1)):
+ value -= 2**nbits
+ return value
+
+ def wr(self, item, value, index=0):
+ if isinstance(item, Memory):
+ nbits = item.width
+ else:
+ nbits = len(item)
+ if value < 0:
+ value += 2**nbits
+ assert(value >= 0 and value < 2**nbits)
+ try:
+ name = self.top_level.top_name + "." \
+ + self.top_level.dut_name + "." \
+ + self.namespace.get_name(item)
+ self.ipc.send(MessageWrite(name, Int32(index), value))
+ except KeyError:
+ self.unreferenced[(item, index)] = value
+
+ def __del__(self):
+ if hasattr(self, "ipc"):
+ warnings.warn("call Simulator.close() to clean up "
+ "or use it as a contextmanager", DeprecationWarning)
+ self.close()
+
+ def close(self):
+ self.ipc.close()
+ self.sim_runner.close()
+ del self.ipc
+ del self.sim_runner
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.close()
+
+
+def run_simulation(fragment, ncycles=None, vcd_name=None, **kwargs):
+ with Simulator(fragment, TopLevel(vcd_name), icarus.Runner(**kwargs)) as s:
+ s.run(ncycles)
+
--- /dev/null
+# Copyright (C) 2012 Vermeer Manufacturing Co.
+# License: GPLv3 with additional permissions (see README).
+
+import subprocess
+import os
+import time
+
+
+class Runner:
+ def __init__(self, options=None, extra_files=None, top_file="migensim_top.v", dut_file="migensim_dut.v", vvp_file=None, keep_files=False):
+ if extra_files is None: extra_files = []
+ if vvp_file is None: vvp_file = dut_file + "vp"
+ if options is None: options = []
+ self.options = options
+ self.extra_files = extra_files
+ self.top_file = top_file
+ self.dut_file = dut_file
+ self.vvp_file = vvp_file
+ self.data_files = []
+ self.keep_files = keep_files
+
+ def start(self, c_top, c_dut):
+ with open(self.top_file, "w") as f:
+ f.write(c_top)
+ c_dut.write(self.dut_file)
+ self.data_files += c_dut.data_files.keys()
+ subprocess.check_call(["iverilog", "-o", self.vvp_file] + self.options + [self.top_file, self.dut_file] + self.extra_files)
+ self.process = subprocess.Popen(["vvp", "-mmigensim", "-Mvpi", self.vvp_file])
+
+ def close(self):
+ if hasattr(self, "process"):
+ self.process.terminate()
+ if self.process.poll() is None:
+ time.sleep(.1)
+ self.process.kill()
+ self.process.wait()
+ if not self.keep_files:
+ for f in [self.top_file, self.dut_file, self.vvp_file] + self.data_files:
+ try:
+ os.remove(f)
+ except OSError:
+ pass
+ self.data_files.clear()
--- /dev/null
+# Copyright (C) 2012 Vermeer Manufacturing Co.
+# License: GPLv3 with additional permissions (see README).
+
+import socket
+import os
+import sys
+import struct
+
+if sys.platform == "win32":
+ header_len = 2
+
+#
+# Message classes
+#
+
+class Int32(int):
+ pass
+
+
+class Message:
+ def __init__(self, *pvalues):
+ for parameter, value in zip(self.parameters, pvalues):
+ setattr(self, parameter[1], parameter[0](value))
+
+ def __str__(self):
+ p = []
+ for parameter in self.parameters:
+ p.append(parameter[1] + "=" + str(getattr(self, parameter[1])))
+ if p:
+ pf = " " + " ".join(p)
+ else:
+ pf = ""
+ return "<" + self.__class__.__name__ + pf + ">"
+
+
+class MessageTick(Message):
+ code = 0
+ parameters = []
+
+
+class MessageGo(Message):
+ code = 1
+ parameters = []
+
+
+class MessageWrite(Message):
+ code = 2
+ parameters = [(str, "name"), (Int32, "index"), (int, "value")]
+
+
+class MessageRead(Message):
+ code = 3
+ parameters = [(str, "name"), (Int32, "index")]
+
+
+class MessageReadReply(Message):
+ code = 4
+ parameters = [(int, "value")]
+
+message_classes = [MessageTick, MessageGo, MessageWrite, MessageRead, MessageReadReply]
+
+
+#
+# Packing
+#
+
+def _pack_int(v):
+ if v == 0:
+ p = [1, 0]
+ else:
+ p = []
+ while v != 0:
+ p.append(v & 0xff)
+ v >>= 8
+ p.insert(0, len(p))
+ return p
+
+
+def _pack_str(v):
+ p = [ord(c) for c in v]
+ p.append(0)
+ return p
+
+
+def _pack_int16(v):
+ return [v & 0xff,
+ (v & 0xff00) >> 8]
+
+
+def _pack_int32(v):
+ return [
+ v & 0xff,
+ (v & 0xff00) >> 8,
+ (v & 0xff0000) >> 16,
+ (v & 0xff000000) >> 24
+ ]
+
+
+def _pack(message):
+ r = [message.code]
+ for t, p in message.parameters:
+ value = getattr(message, p)
+ assert(isinstance(value, t))
+ if t == int:
+ r += _pack_int(value)
+ elif t == str:
+ r += _pack_str(value)
+ elif t == Int32:
+ r += _pack_int32(value)
+ else:
+ raise TypeError
+ if sys.platform == "win32":
+ size = _pack_int16(len(r) + header_len)
+ r = size + r
+ return bytes(r)
+
+
+#
+# Unpacking
+#
+
+def _unpack_int(i, nchunks=None):
+ v = 0
+ power = 1
+ if nchunks is None:
+ nchunks = next(i)
+ for j in range(nchunks):
+ v += power*next(i)
+ power *= 256
+ return v
+
+
+def _unpack_str(i):
+ v = ""
+ c = next(i)
+ while c:
+ v += chr(c)
+ c = next(i)
+ return v
+
+
+def _unpack(message):
+ i = iter(message)
+ code = next(i)
+ msgclass = next(filter(lambda x: x.code == code, message_classes))
+ pvalues = []
+ for t, p in msgclass.parameters:
+ if t == int:
+ v = _unpack_int(i)
+ elif t == str:
+ v = _unpack_str(i)
+ elif t == Int32:
+ v = _unpack_int(i, 4)
+ else:
+ raise TypeError
+ pvalues.append(v)
+ return msgclass(*pvalues)
+
+
+#
+# I/O
+#
+
+class PacketTooLarge(Exception):
+ pass
+
+
+class Initiator:
+ def __init__(self, sockaddr):
+ self.sockaddr = sockaddr
+ if sys.platform == "win32":
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ else:
+ self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+ self._cleanup_file()
+ self.socket.bind(self.sockaddr)
+ self.socket.listen(1)
+
+ self.ipc_rxbuffer = bytearray()
+
+ def _cleanup_file(self):
+ try:
+ os.remove(self.sockaddr)
+ except OSError:
+ pass
+
+ def accept(self):
+ self.conn, addr = self.socket.accept()
+
+ def send(self, message):
+ self.conn.send(_pack(message))
+
+ def recv_packet(self, maxlen):
+ if sys.platform == "win32":
+ while len(self.ipc_rxbuffer) < header_len:
+ self.ipc_rxbuffer += self.conn.recv(maxlen)
+ packet_len = struct.unpack("<H", self.ipc_rxbuffer[:header_len])[0]
+ while len(self.ipc_rxbuffer) < packet_len:
+ self.ipc_rxbuffer += self.conn.recv(maxlen)
+ packet = self.ipc_rxbuffer[header_len:packet_len]
+ self.ipc_rxbuffer = self.ipc_rxbuffer[packet_len:]
+ else:
+ packet = self.conn.recv(maxlen)
+ return packet
+
+ def recv(self):
+ maxlen = 2048
+ packet = self.recv_packet(maxlen)
+ if len(packet) < 1:
+ return None
+ if len(packet) >= maxlen:
+ raise PacketTooLarge
+ return _unpack(packet)
+
+ def close(self):
+ if hasattr(self, "conn"):
+ self.conn.shutdown(socket.SHUT_RDWR)
+ self.conn.close()
+ if hasattr(self, "socket"):
+ if sys.platform == "win32":
+ # don't shutdown our socket since closing connection
+ # seems to already have done it. (trigger an error
+ # otherwise)
+ self.socket.close()
+ else:
+ self.socket.shutdown(socket.SHUT_RDWR)
+ self.socket.close()
+ self._cleanup_file()
--- /dev/null
+from litex.gen.fhdl.structure import Signal, StopSimulation
+from litex.gen.fhdl.specials import Memory
+
+
+class MemoryProxy:
+ def __init__(self, simulator, obj):
+ self.simulator = simulator
+ self._simproxy_obj = obj
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return self.simulator.rd(self._simproxy_obj, key)
+ else:
+ start, stop, step = key.indices(self._simproxy_obj.depth)
+ return [self.simulator.rd(self._simproxy_obj, i) for i in range(start, stop, step)]
+
+ def __setitem__(self, key, value):
+ if isinstance(key, int):
+ self.simulator.wr(self._simproxy_obj, key, value)
+ else:
+ start, stop, step = key.indices(self.__obj.depth)
+ if len(value) != (stop - start)//step:
+ raise ValueError
+ for i, v in zip(range(start, stop, step), value):
+ self.simulator.wr(self._simproxy_obj, i, v)
+
+
+class Proxy:
+ def __init__(self, simulator, obj):
+ object.__setattr__(self, "simulator", simulator)
+ object.__setattr__(self, "_simproxy_obj", obj)
+
+ def __process_get(self, item):
+ if isinstance(item, Signal):
+ return self.simulator.rd(item)
+ elif isinstance(item, Memory):
+ return MemoryProxy(self.simulator, item)
+ else:
+ return Proxy(self.simulator, item)
+
+ def __getattr__(self, name):
+ return self.__process_get(getattr(self._simproxy_obj, name))
+
+ def __setattr__(self, name, value):
+ item = getattr(self._simproxy_obj, name)
+ assert(isinstance(item, Signal))
+ self.simulator.wr(item, value)
+
+ def __getitem__(self, key):
+ return self.__process_get(self._simproxy_obj[key])
+
+ def __setitem__(self, key, value):
+ item = self._simproxy_obj[key]
+ assert(isinstance(item, Signal))
+ self.simulator.wr(item, value)
+
+
+def gen_sim(simg):
+ gens = dict()
+ resume_cycle = 0
+
+ def do_simulation(s):
+ nonlocal resume_cycle, gens
+
+ if isinstance(s, Proxy):
+ simulator = s.simulator
+ else:
+ simulator = s
+
+ if simulator.cycle_counter >= resume_cycle:
+ try:
+ gen = gens[simulator]
+ except KeyError:
+ gen = simg(s)
+ gens[simulator] = gen
+ try:
+ n = next(gen)
+ except StopIteration:
+ del gens[simulator]
+ raise StopSimulation
+ else:
+ if n is None:
+ n = 1
+ resume_cycle = simulator.cycle_counter + n
+
+ if hasattr(simg, "passive"):
+ do_simulation.passive = simg.passive
+
+ return do_simulation
+
+
+def proxy_sim(target, simf):
+ proxies = dict()
+
+ def do_simulation(simulator):
+ nonlocal proxies
+
+ try:
+ proxy = proxies[simulator]
+ except KeyError:
+ proxy = Proxy(simulator, target)
+ proxies[simulator] = proxy
+ try:
+ simf(proxy)
+ except StopSimulation:
+ del proxies[simulator]
+ raise
+
+ if hasattr(simf, "passive"):
+ do_simulation.passive = simf.passive
+
+ return do_simulation
--- /dev/null
+from fractions import gcd
+import collections
+
+
+def flat_iteration(l):
+ for element in l:
+ if isinstance(element, collections.Iterable):
+ for element2 in flat_iteration(element):
+ yield element2
+ else:
+ yield element
+
+
+def xdir(obj, return_values=False):
+ for attr in dir(obj):
+ if attr[:2] != "__" and attr[-2:] != "__":
+ if return_values:
+ yield attr, getattr(obj, attr)
+ else:
+ yield attr
+
+
+def gcd_multiple(numbers):
+ l = len(numbers)
+ if l == 1:
+ return numbers[0]
+ else:
+ s = l//2
+ return gcd(gcd_multiple(numbers[:s]), gcd_multiple(numbers[s:]))
--- /dev/null
+INSTDIR = $(shell iverilog-vpi --install-dir)
+
+CFLAGS = -Wall -O2 $(CFLAGS_$@)
+VPI_CFLAGS := $(shell iverilog-vpi --cflags)
+# Define the below flags for a Windows build.
+# Make sure to run iverilog-vpi with -mingw and -ivl options if necessary!
+# i.e. iverilog-vpi -mingw=C:\msys64\mingw32 -ivl=C:\msys64\mingw32
+# MINGW_FLAGS=-lWs2_32
+
+OBJ=ipc.o main.o
+
+all: migensim.vpi
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(VPI_CFLAGS) -c $(INCDIRS) -o $@ $<
+
+migensim.vpi: $(OBJ)
+ iverilog-vpi $(MINGW_FLAGS) --name=migensim $^
+
+install: migensim.vpi
+ install -m755 -t $(INSTDIR) $^
+
+clean:
+ rm -f $(OBJ)
+ rm -f migensim.vpi
+
+.PHONY: install clean
--- /dev/null
+/*
+ * Copyright (C) 2012 Vermeer Manufacturing Co.
+ * License: GPLv3 with additional permissions (see README).
+ */
+
+#ifdef _WIN32
+#define WINVER 0x501
+#endif
+
+#include <assert.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+
+#include "ipc.h"
+
+struct ipc_softc {
+ int socket;
+ go_handler h_go;
+ write_handler h_write;
+ read_handler h_read;
+ void *user;
+};
+
+#define MAX_LEN 2048
+
+#ifdef _WIN32
+#define HEADER_LEN 2
+#define SOCKET_PORT "50007"
+
+unsigned char ipc_rxbuffer[2*MAX_LEN];
+int ipc_rxlen;
+#else
+#define HEADER_LEN 0
+#endif
+
+struct ipc_softc *ipc_connect(const char *sockaddr,
+ go_handler h_go, write_handler h_write, read_handler h_read, void *user)
+{
+ struct ipc_softc *sc;
+#ifdef _WIN32
+ struct addrinfo hints, *my_addrinfo;
+ WSADATA wsaData;
+ ipc_rxlen = 0;
+#else
+ struct sockaddr_un addr;
+#endif
+
+ sc = malloc(sizeof(struct ipc_softc));
+ if(!sc) return NULL;
+
+ sc->h_go = h_go;
+ sc->h_write = h_write;
+ sc->h_read = h_read;
+ sc->user = user;
+
+#ifdef _WIN32
+ /* Initialize Winsock. */
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
+ free(sc);
+ return NULL;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if(getaddrinfo(sockaddr, SOCKET_PORT, NULL, &my_addrinfo) != 0) {
+ free(sc);
+ return NULL;
+ }
+
+ sc->socket = socket(AF_INET, SOCK_STREAM, 0);
+ if(sc->socket < 0) {
+ free(sc);
+ return NULL;
+ }
+
+ if(connect(sc->socket, my_addrinfo->ai_addr, my_addrinfo->ai_addrlen) != 0) {
+ close(sc->socket);
+ free(sc);
+ return NULL;
+ }
+#else
+ sc->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if(sc->socket < 0) {
+ free(sc);
+ return NULL;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, sockaddr);
+ if(connect(sc->socket, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+ close(sc->socket);
+ free(sc);
+ return NULL;
+ }
+#endif
+
+ return sc;
+}
+
+void ipc_destroy(struct ipc_softc *sc)
+{
+ close(sc->socket);
+ free(sc);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+enum {
+ MESSAGE_TICK = 0,
+ MESSAGE_GO,
+ MESSAGE_WRITE,
+ MESSAGE_READ,
+ MESSAGE_READ_REPLY
+};
+
+static int ipc_receive_packet(struct ipc_softc *sc, unsigned char *buffer) {
+#ifdef _WIN32
+ int len;
+ int packet_len;
+ /* ensure we have packet header */
+ while(ipc_rxlen < HEADER_LEN) {
+ len = recv(sc->socket, (char *)&ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
+ if(len)
+ ipc_rxlen += len;
+ }
+
+ /* compute packet length and ensure we have the payload */
+ packet_len = (ipc_rxbuffer[1] << 8) | ipc_rxbuffer[0];
+ while(ipc_rxlen < packet_len) {
+ len = recv(sc->socket, (char *)&ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
+ if(len)
+ ipc_rxlen += len;
+ }
+
+ /* copy packet to buffer */
+ memcpy(buffer, ipc_rxbuffer + HEADER_LEN, packet_len - HEADER_LEN);
+
+ /* prepare ipc_rxbuffer for next packet */
+ ipc_rxlen = ipc_rxlen - packet_len;
+ memcpy(ipc_rxbuffer, ipc_rxbuffer + packet_len, ipc_rxlen);
+
+ return packet_len - HEADER_LEN;
+#else
+ return recv(sc->socket, buffer, MAX_LEN, 0);
+#endif
+}
+
+/*
+ * 0 -> error
+ * 1 -> success
+ * 2 -> graceful shutdown
+ */
+int ipc_receive(struct ipc_softc *sc)
+{
+ unsigned char buffer[MAX_LEN];
+ ssize_t l = 0;
+ int i;
+
+ l = ipc_receive_packet(sc, (unsigned char *)&buffer);
+ if(l == 0)
+ return 2;
+ if((l < 0) || (l >= MAX_LEN))
+ return 0;
+ i = 0;
+
+ switch(buffer[i++]) {
+ case MESSAGE_GO:
+ assert((l - i) == 0);
+
+ return sc->h_go(sc->user);
+ case MESSAGE_WRITE: {
+ char *name;
+ int nchunks;
+ unsigned char *chunks;
+ unsigned int chunk_index;
+
+ name = (char *)&buffer[i];
+ i += strlen(name) + 1;
+ assert((i+4) < l);
+ chunk_index = buffer[i] | buffer[i+1] << 8 | buffer[i+2] << 16 | buffer[i+3] << 24;
+ i += 4;
+ nchunks = buffer[i++];
+ assert(i + nchunks == l);
+ chunks = (unsigned char *)&buffer[i];
+
+ return sc->h_write(name, chunk_index, nchunks, chunks, sc->user);
+ }
+ case MESSAGE_READ: {
+ char *name;
+ unsigned int name_index;
+
+ name = (char *)&buffer[i];
+ i += strlen(name) + 1;
+ assert((i+4) == l);
+ name_index = buffer[i] | buffer[i+1] << 8 | buffer[i+2] << 16 | buffer[i+3] << 24;
+
+ return sc->h_read(name, name_index, sc->user);
+ }
+ default:
+ return 0;
+ }
+}
+
+int ipc_tick(struct ipc_softc *sc)
+{
+ ssize_t l;
+ char c[HEADER_LEN + 1];
+
+#ifdef _WIN32
+ c[0] = 3;
+ c[1] = 0;
+#endif
+ c[HEADER_LEN + 0] = MESSAGE_TICK;
+ l = send(sc->socket, c, HEADER_LEN + 1, 0);
+ if(l != (HEADER_LEN + 1))
+ return 0;
+
+ return 1;
+}
+
+int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *chunks)
+{
+ int len;
+ char buffer[MAX_LEN];
+ ssize_t l;
+
+ len = nchunks + HEADER_LEN + 2;
+ assert(len < MAX_LEN);
+ assert(nchunks < 256);
+
+#ifdef _WIN32
+ buffer[0] = len & 0xFF;
+ buffer[1] = (0xFF00 & len) >> 8;
+#endif
+ buffer[HEADER_LEN + 0] = MESSAGE_READ_REPLY;
+ buffer[HEADER_LEN + 1] = nchunks;
+ memcpy(&buffer[HEADER_LEN + 2], chunks, nchunks);
+
+ l = send(sc->socket, buffer, len, 0);
+ if(l != len)
+ return 0;
+ return 1;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2012 Vermeer Manufacturing Co.
+ * License: GPLv3 with additional permissions (see README).
+ */
+
+#ifndef __IPC_H
+#define __IPC_H
+
+struct ipc_softc;
+
+typedef int(*go_handler)(void *);
+typedef int(*write_handler)(char *, int, int, const unsigned char *, void *);
+typedef int(*read_handler)(char *, int, void *);
+
+struct ipc_softc *ipc_connect(const char *sockaddr,
+ go_handler h_go, write_handler h_write, read_handler h_read, void *user);
+void ipc_destroy(struct ipc_softc *sc);
+
+int ipc_receive(struct ipc_softc *sc);
+
+int ipc_tick(struct ipc_softc *sc);
+int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *value);
+
+#endif /* __IPC_H */
--- /dev/null
+/*
+ * Copyright (C) 2012 Vermeer Manufacturing Co.
+ * License: GPLv3 with additional permissions (see README).
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vpi_user.h>
+
+#include "ipc.h"
+
+struct migensim_softc {
+ struct ipc_softc *ipc;
+ int has_go;
+};
+
+static int h_go(void *user)
+{
+ struct migensim_softc *sc = (struct migensim_softc *)user;
+ sc->has_go = 1;
+ return 1;
+}
+
+static s_vpi_time zero_delay = {
+ .type = vpiSimTime,
+ .high = 0,
+ .low = 0
+};
+
+static int h_write(char *name, int index, int nchunks, const unsigned char *chunks, void *user)
+{
+ vpiHandle item;
+ s_vpi_vecval vector[64];
+ int i;
+ s_vpi_value value;
+
+ item = vpi_handle_by_name(name, NULL);
+ if(item == NULL) {
+ fprintf(stderr, "Attempted to write non-existing signal %s\n", name);
+ return 0;
+ }
+ if(vpi_get(vpiType, item) == vpiMemory)
+ item = vpi_handle_by_index(item, index);
+ else
+ assert(index == 0);
+
+ assert(nchunks <= 255);
+ for(i=0;i<64;i++) {
+ vector[i].aval = 0;
+ vector[i].bval = 0;
+ }
+ for(i=0;i<nchunks;i++)
+ vector[i/4].aval |= chunks[i] << 8*(i % 4);
+
+ value.format = vpiVectorVal;
+ value.value.vector = vector;
+ vpi_put_value(item, &value, &zero_delay, vpiInertialDelay);
+
+ return 1;
+}
+
+static int h_read(char *name, int index, void *user)
+{
+ struct migensim_softc *sc = (struct migensim_softc *)user;
+ vpiHandle item;
+ s_vpi_value value;
+ int size;
+ int i;
+ int nvals;
+ unsigned int vals[64];
+ int nchunks;
+ unsigned char chunks[255];
+
+ item = vpi_handle_by_name(name, NULL);
+ if(item == NULL) {
+ fprintf(stderr, "Attempted to read non-existing signal %s\n", name);
+ return 0;
+ }
+ if(vpi_get(vpiType, item) == vpiMemory)
+ item = vpi_handle_by_index(item, index);
+ else
+ assert(index == 0);
+
+ value.format = vpiVectorVal;
+ vpi_get_value(item, &value);
+ size = vpi_get(vpiSize, item);
+ nvals = (size + 31)/32;
+ assert(nvals <= 64);
+ for(i=0;i<nvals;i++)
+ vals[i] = value.value.vector[i].aval & ~value.value.vector[i].bval;
+ nchunks = (size + 7)/8;
+ assert(nchunks <= 255);
+ for(i=0;i<nchunks;i++) {
+ switch(i % 4) {
+ case 0:
+ chunks[i] = vals[i/4] & 0xff;
+ break;
+ case 1:
+ chunks[i] = (vals[i/4] & 0xff00) >> 8;
+ break;
+ case 2:
+ chunks[i] = (vals[i/4] & 0xff0000) >> 16;
+ break;
+ case 3:
+ chunks[i] = (vals[i/4] & 0xff000000) >> 24;
+ break;
+ }
+ }
+
+ if(!ipc_read_reply(sc->ipc, nchunks, chunks)) {
+ perror("ipc_read_reply");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int process_until_go(struct migensim_softc *sc)
+{
+ int r;
+
+ sc->has_go = 0;
+ while(!sc->has_go) {
+ r = ipc_receive(sc->ipc);
+ if(r != 1)
+ return r;
+ }
+ return 1;
+}
+
+static PLI_INT32 connect_calltf(PLI_BYTE8 *user)
+{
+ struct migensim_softc *sc = (struct migensim_softc *)user;
+ vpiHandle sys;
+ vpiHandle argv;
+ vpiHandle item;
+ s_vpi_value value;
+
+ sys = vpi_handle(vpiSysTfCall, 0);
+ argv = vpi_iterate(vpiArgument, sys);
+ item = vpi_scan(argv);
+ value.format = vpiStringVal;
+ vpi_get_value(item, &value);
+
+ sc->ipc = ipc_connect(value.value.str, h_go, h_write, h_read, sc);
+ if(sc->ipc == NULL) {
+ perror("ipc_connect");
+ vpi_control(vpiFinish, 1);
+ return 0;
+ }
+
+ return 0;
+}
+
+static PLI_INT32 tick_calltf(PLI_BYTE8 *user)
+{
+ struct migensim_softc *sc = (struct migensim_softc *)user;
+ int r;
+
+ if(!ipc_tick(sc->ipc)) {
+ perror("ipc_tick");
+ vpi_control(vpiFinish, 1);
+ ipc_destroy(sc->ipc);
+ sc->ipc = NULL;
+ return 0;
+ }
+ r = process_until_go(sc);
+ if(r != 1) {
+ vpi_control(vpiFinish, r == 2 ? 0 : 1);
+ ipc_destroy(sc->ipc);
+ sc->ipc = NULL;
+ return 0;
+ }
+
+ return 0;
+}
+
+static struct migensim_softc sc;
+
+static void simple_register(const char *tfname, PLI_INT32 (*calltf)(PLI_BYTE8 *))
+{
+ s_vpi_systf_data tf_data;
+
+ tf_data.type = vpiSysTask;
+ tf_data.tfname = tfname;
+ tf_data.calltf = calltf;
+ tf_data.compiletf = NULL;
+ tf_data.sizetf = 0;
+ tf_data.user_data = (void *)≻
+ vpi_register_systf(&tf_data);
+}
+
+static void migensim_register()
+{
+ simple_register("$migensim_connect", connect_calltf);
+ simple_register("$migensim_tick", tick_calltf);
+}
+
+void (*vlog_startup_routines[])() = {
+ migensim_register,
+ 0
+};