From: Florent Kermarrec Date: Fri, 23 Feb 2018 12:37:26 +0000 (+0100) Subject: remove migen fork from litex X-Git-Tag: 24jan2021_ls180~1738 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=43164b9a2c1e49842a1b4b94dad5c7df66bd4c8d;p=litex.git remove migen fork from litex --- diff --git a/litex/gen/MIGEN_LICENSE b/litex/gen/MIGEN_LICENSE deleted file mode 100644 index 4f290601..00000000 --- a/litex/gen/MIGEN_LICENSE +++ /dev/null @@ -1,31 +0,0 @@ -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. diff --git a/litex/gen/fhdl/bitcontainer.py b/litex/gen/fhdl/bitcontainer.py deleted file mode 100644 index e7894bce..00000000 --- a/litex/gen/fhdl/bitcontainer.py +++ /dev/null @@ -1,120 +0,0 @@ -from litex.gen.fhdl import structure as f - - -__all__ = ["log2_int", "bits_for", "value_bits_sign"] - - -def log2_int(n, need_pow2=True): - if n == 0: - return 0 - r = (n - 1).bit_length() - if need_pow2 and (1 << r) != 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 _bitwise_binary_bits_sign(a, b): - if not a[1] and not b[1]: - # both operands unsigned - return max(a[0], b[0]), False - elif a[1] and b[1]: - # both operands signed - return max(a[0], b[0]), True - elif not a[1] and b[1]: - # first operand unsigned (add sign bit), second operand signed - return max(a[0] + 1, b[0]), True - else: - # first signed, second operand unsigned (add sign bit) - return max(a[0], b[0] + 1), True - - -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 len(obs) == 1: - if v.op == "-" and not obs[0][1]: - return obs[0][0] + 1, True - else: - return obs[0] - n, s = _bitwise_binary_bits_sign(*obs) - return n + 1, s - 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 == "|": - return _bitwise_binary_bits_sign(*obs) - 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] - elif v.op == "m": - return _bitwise_binary_bits_sign(obs[1], obs[2]) - 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)) diff --git a/litex/gen/fhdl/conv_output.py b/litex/gen/fhdl/conv_output.py deleted file mode 100644 index 793fad20..00000000 --- a/litex/gen/fhdl/conv_output.py +++ /dev/null @@ -1,35 +0,0 @@ -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) diff --git a/litex/gen/fhdl/decorators.py b/litex/gen/fhdl/decorators.py deleted file mode 100644 index d33ee712..00000000 --- a/litex/gen/fhdl/decorators.py +++ /dev/null @@ -1,110 +0,0 @@ -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__ - Wrapped.__doc__ = victim.__doc__ - Wrapped.__module__ = victim.__module__ - 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 not f.sync: - return - 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) diff --git a/litex/gen/fhdl/module.py b/litex/gen/fhdl/module.py deleted file mode 100644 index a8607514..00000000 --- a/litex/gen/fhdl/module.py +++ /dev/null @@ -1,185 +0,0 @@ -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 - - -__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": - self._fragment = _Fragment() - 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) diff --git a/litex/gen/fhdl/namer.py b/litex/gen/fhdl/namer.py deleted file mode 100644 index c75480d2..00000000 --- a/litex/gen/fhdl/namer.py +++ /dev/null @@ -1,258 +0,0 @@ -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 - swno = {signal for signal in signals if signal.name_override is not None} - for signal in sorted(swno, key=lambda x: x.duid): - ns.get_name(signal) - return ns - - -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 diff --git a/litex/gen/fhdl/simplify.py b/litex/gen/fhdl/simplify.py deleted file mode 100644 index 864ab37e..00000000 --- a/litex/gen/fhdl/simplify.py +++ /dev/null @@ -1,204 +0,0 @@ -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 -from litex.gen.fhdl.bitcontainer import log2_int - - -class FullMemoryWE(ModuleTransformer): - def __init__(self): - self.replacements = 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.replacements[orig] = newmems - - f.specials = newspecials - for oldmem in self.replacements.keys(): - f.specials -= set(oldmem.ports) - - -class MemoryToArray(ModuleTransformer): - def __init__(self): - self.replacements = dict() - - def transform_fragment(self, i, f): - newspecials = set() - processed_ports = 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: - 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[m:M]))) - else: - sync.append(If(port.we, - storage[port.adr].eq(port.dat_w))) - - processed_ports.add(port) - - newspecials -= processed_ports - f.specials = newspecials - - -class SplitMemory(ModuleTransformer): - """Split memories with depths that are not powers of two into smaller - power-of-two memories. - - This prevents toolchains from rounding up and wasting resources.""" - - def transform_fragment(self, i, f): - old_specials, f.specials = f.specials, set() - old_ports = set() - - for old in old_specials: - if not isinstance(old, Memory): - f.specials.add(old) - continue - try: - log2_int(old.depth, need_pow2=True) - f.specials.add(old) - except ValueError: - new, comb, sync = self._split_mem(old) - old_ports |= set(old.ports) - f.specials.update(new) - f.comb += comb - for cd, sy in sync.items(): - s = f.sync.setdefault(cd, []) - s += sy - f.specials -= old_ports - - def _split_mem(self, mem): - depths = [1 << i for i in range(log2_int(mem.depth, need_pow2=False)) - if mem.depth & (1 << i)] - depths.reverse() - inits = None - if mem.init is not None: - inits = list(mem.init) - mems = [] - for i, depth in enumerate(depths): - init = None - if inits is not None: - init = inits[:depth] - del inits[:depth] - name = "{}_part{}".format(mem.name_override, i) - mems.append(Memory(width=mem.width, depth=depth, - init=init, name=name)) - ports = [] - comb = [] - sync = {} - for port in mem.ports: - p, c, s = self._split_port(port, mems) - ports += p - comb += c - sy = sync.setdefault(port.clock.cd, []) - sy += s - return mems + ports, comb, sync - - def _split_port(self, port, mems): - ports = [mem.get_port(write_capable=port.we is not None, - async_read=port.async_read, - has_re=port.re is not None, - we_granularity=port.we_granularity, - mode=port.mode, - clock_domain=port.clock.cd) - for mem in mems] - - sel = Signal(max=len(ports), reset=len(ports) - 1) - sel_r = Signal.like(sel) - eq = sel_r.eq(sel) - if port.re is not None: - eq = If(port.re, eq) - comb, sync = [], [] - if port.async_read: - comb += [eq] - else: - sync += [eq] - comb += reversed([If(~port.adr[len(p.adr)], sel.eq(i)) - for i, p in enumerate(ports)]) - comb += [p.adr.eq(port.adr) for p in ports] - comb.append(port.dat_r.eq(Array([p.dat_r for p in ports])[sel_r])) - if port.we is not None: - comb.append(Array([p.we for p in ports])[sel].eq(port.we)) - comb += [p.dat_w.eq(port.dat_w) for p in ports] - if port.re is not None: - comb += [p.re.eq(port.re) for p in ports] - return ports, comb, sync diff --git a/litex/gen/fhdl/specials.py b/litex/gen/fhdl/specials.py deleted file mode 100644 index e3fdafdf..00000000 --- a/litex/gen/fhdl/specials.py +++ /dev/null @@ -1,358 +0,0 @@ -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, - attr=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 - if attr is None: - attr = set() - self.attr = attr - for k, v in sorted(kwargs.items(), key=itemgetter(0)): - try: - item_type, item_name = k.split("_", maxsplit=1) - except ValueError: - raise TypeError("Wrong format for value '" + str(k) + - "', format should be 'type_name'") - - 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" - - directives = instance.synthesis_directive - if directives is None: - directives = [] - elif type(directives) == str : - directives = [directives,] - - r += ")"; - for directive in directives: - r += "\n\t/* synthesis {} */".format(directive) - 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 diff --git a/litex/gen/fhdl/structure.py b/litex/gen/fhdl/structure.py deleted file mode 100644 index c4afc433..00000000 --- a/litex/gen/fhdl/structure.py +++ /dev/null @@ -1,765 +0,0 @@ -import builtins as _builtins -import collections as _collections -import re as _re - -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(b, 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("Cannot use type {} ({}) as key".format( - type(key), repr(key))) - - 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 '{}' of type {} is not a Migen value" - .format(value, type(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. - reset_less : bool - If `True`, do not generate reset logic for this `Signal` in - synchronous statements. The `reset` value is only used as a - combinatorial default or as the initial value. Defaults to `False`. - 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 - attr : set of synthesis attributes - """ - _name_re = _re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$") - - def __init__(self, bits_sign=None, name=None, variable=False, reset=0, - reset_less=False, name_override=None, min=None, max=None, - related=None, attr=None): - from litex.gen.fhdl.bitcontainer import bits_for - - _Value.__init__(self) - - for n in [name, name_override]: - if n is not None and not self._name_re.match(n): - raise ValueError("Signal name {} is not a valid Python identifier" - .format(repr(n))) - - # 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 isinstance(reset, (bool, int)): - reset = Constant(reset, (self.nbits, self.signed)) - if not isinstance(self.nbits, int) or self.nbits <= 0: - raise ValueError("Signal width must be a strictly positive integer") - if attr is None: - attr = set() - - self.variable = variable # deprecated - self.reset = reset - self.reset_less = reset_less - self.name_override = name_override - self.backtrace = _tracer.trace_back(name) - self.related = related - self.attr = attr - - def __setattr__(self, k, v): - if k == "reset": - v = wrap(v) - _Value.__setattr__(self, k, v) - - def __repr__(self): - return "" - - @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 - kw = dict(bits_sign=value_bits_sign(other)) - if isinstance(other, cls): - kw.update(variable=other.variable, - reset=other.reset.value, reset_less=other.reset_less, - related=other.related, attr=set(other.attr)) - kw.update(kwargs) - return cls(**kw) - - 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, Constant 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 (isinstance(choice, str) and choice == "default") - or choice.value > key.value): - key = choice - if not isinstance(key, str) or key != "default": - key = wrap(key) - stmts = self.cases[key] - del self.cases[key] - self.cases["default"] = stmts - 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) - - def __contains__(self, cd_or_name): - if isinstance(cd_or_name, str): - for cd in self: - if cd.name == cd_or_name: - return True - return False - else: - return list.__contains__(self, cd_or_name) - - -(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3) - - -class _Fragment: - def __init__(self, comb=None, sync=None, specials=None, clock_domains=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() - - self.comb = comb - self.sync = sync - self.specials = specials - self.clock_domains = _ClockDomainList(clock_domains) - - 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) - - 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 - return self - - -class Display(_Statement): - def __init__(self, s, *args): - self.s = s - self.args = args - -class Finish(_Statement): - pass diff --git a/litex/gen/fhdl/tools.py b/litex/gen/fhdl/tools.py deleted file mode 100644 index eefb5e01..00000000 --- a/litex/gen/fhdl/tools.py +++ /dev/null @@ -1,340 +0,0 @@ -from litex.gen.fhdl.structure import * -from litex.gen.fhdl.structure import _Slice, _Assign, _Fragment -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) - if not t.reset_less] - - -def insert_reset(rst, sl): - return sl + [If(rst, *generate_reset(rst, 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 sorted(f.specials, key=lambda s: s.duid): - 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) - - -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 diff --git a/litex/gen/fhdl/tracer.py b/litex/gen/fhdl/tracer.py deleted file mode 100644 index a4b1b909..00000000 --- a/litex/gen/fhdl/tracer.py +++ /dev/null @@ -1,136 +0,0 @@ -import inspect -from sys import version_info -from opcode import opname -from collections import defaultdict - -# All opcodes are 2 bytes in length in Python 3.6 -def _bytecode_length_version_guard(old_len): - return old_len if version_info[1] < 6 else 2 - -_call_opcodes = { - "CALL_FUNCTION" : _bytecode_length_version_guard(3), - "CALL_FUNCTION_KW" : _bytecode_length_version_guard(3), -} - -if version_info[1] < 6: - _call_opcodes["CALL_FUNCTION_VAR"] = 3 - _call_opcodes["CALL_FUNCTION_VAR_KW"] = 3 -else: - _call_opcodes["CALL_FUNCTION_VAR_KW"] = 2 - -_load_build_opcodes = { - "LOAD_GLOBAL" : _bytecode_length_version_guard(3), - "LOAD_ATTR" : _bytecode_length_version_guard(3), - "LOAD_FAST" : _bytecode_length_version_guard(3), - "LOAD_DEREF" : _bytecode_length_version_guard(3), - "DUP_TOP" : _bytecode_length_version_guard(1), - "BUILD_LIST" : _bytecode_length_version_guard(3), -} - - -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 not in _call_opcodes: - return None - index = call_index+_call_opcodes[call_opc] - 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 in _load_build_opcodes: - index += _load_build_opcodes[opc] - 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 == "": - 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 diff --git a/litex/gen/fhdl/visit.py b/litex/gen/fhdl/visit.py deleted file mode 100644 index 676ceb96..00000000 --- a/litex/gen/fhdl/visit.py +++ /dev/null @@ -1,206 +0,0 @@ -from copy import copy -from operator import itemgetter - -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) - else: - 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 sorted(node.cases.items(), - key=lambda x: str(x[0])): - 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 sorted(node.items(), key=itemgetter(0)): - 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) - else: - return self.visit_unknown(node) - - 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 = {v: self.visit(statements) - for v, statements in sorted(node.cases.items(), - key=lambda x: str(x[0]))} - 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 {clockname: self.visit(statements) - for clockname, statements in sorted(node.items(), - key=itemgetter(0))} - - 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 diff --git a/litex/gen/genlib/__init__.py b/litex/gen/genlib/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/litex/gen/genlib/cdc.py b/litex/gen/genlib/cdc.py deleted file mode 100644 index 3ba1a262..00000000 --- a/litex/gen/genlib/cdc.py +++ /dev/null @@ -1,253 +0,0 @@ -""" -Clock domain crossing module -""" -from math import gcd - -from litex.gen.fhdl.structure import * -from litex.gen.fhdl.module import Module -from litex.gen.fhdl.specials import Special, Memory -from litex.gen.fhdl.bitcontainer import value_bits_sign -from litex.gen.fhdl.decorators import ClockDomainsRenamer -from litex.gen.genlib.misc import WaitTimer -from litex.gen.genlib.resetsync import AsyncResetSynchronizer - - -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), reset_less=True) - 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) - for reg in self.regs: - reg.attr.add("no_retiming") - - -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(reset_less=True) - toggle_o = Signal() # registered reset_less by MultiReg - toggle_o_r = Signal(reset_less=True) - - 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, reset_less=True) - - 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 = ClockDomainsRenamer(idomain)( - 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, reset_less=True) - obuffer = Signal(width) # registered reset_less by MultiReg - sync_i += If(self._pong.o, ibuffer.eq(self.i)) - ibuffer.attr.add("no_retiming") - 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) - ] - - -class GrayDecoder(Module): - def __init__(self, width): - self.i = Signal(width) - self.o = Signal(width, reset_less=True) - - # # # - - o_comb = Signal(width) - self.comb += o_comb[-1].eq(self.i[-1]) - for i in reversed(range(width-1)): - self.comb += o_comb[i].eq(o_comb[i+1] ^ self.i[i]) - self.sync += self.o.eq(o_comb) - - -class ElasticBuffer(Module): - def __init__(self, width, depth, idomain, odomain): - self.din = Signal(width) - self.dout = Signal(width) - - # # # - - reset = Signal() - cd_write = ClockDomain() - cd_read = ClockDomain() - self.comb += [ - cd_write.clk.eq(ClockSignal(idomain)), - cd_read.clk.eq(ClockSignal(odomain)), - reset.eq(ResetSignal(idomain) | ResetSignal(odomain)) - ] - self.specials += [ - AsyncResetSynchronizer(cd_write, reset), - AsyncResetSynchronizer(cd_read, reset) - ] - self.clock_domains += cd_write, cd_read - - wrpointer = Signal(max=depth, reset=depth//2) - rdpointer = Signal(max=depth) - - storage = Memory(width, depth) - self.specials += storage - - wrport = storage.get_port(write_capable=True, clock_domain="write") - rdport = storage.get_port(clock_domain="read") - self.specials += wrport, rdport - - self.sync.write += wrpointer.eq(wrpointer + 1) - self.sync.read += rdpointer.eq(rdpointer + 1) - - self.comb += [ - wrport.we.eq(1), - wrport.adr.eq(wrpointer), - wrport.dat_w.eq(self.din), - - rdport.adr.eq(rdpointer), - self.dout.eq(rdport.dat_r) - ] - - -def lcm(a, b): - """Compute the lowest common multiple of a and b""" - return (a*b)//gcd(a, b) - - -class Gearbox(Module): - def __init__(self, iwidth, idomain, owidth, odomain): - self.i = Signal(iwidth) - self.o = Signal(owidth, reset_less=True) - - # # # - - rst = Signal() - cd_write = ClockDomain() - cd_read = ClockDomain() - self.comb += [ - rst.eq(ResetSignal(idomain) | ResetSignal(odomain)), - cd_write.clk.eq(ClockSignal(idomain)), - cd_read.clk.eq(ClockSignal(odomain)), - cd_write.rst.eq(rst), - cd_read.rst.eq(rst) - ] - self.clock_domains += cd_write, cd_read - - storage = Signal(2*lcm(iwidth, owidth), reset_less=True) - wrchunks = len(storage)//iwidth - rdchunks = len(storage)//owidth - wrpointer = Signal(max=wrchunks, reset=0 if iwidth > owidth else wrchunks//2) - rdpointer = Signal(max=rdchunks, reset=rdchunks//2 if iwidth > owidth else 0) - - self.sync.write += \ - If(wrpointer == wrchunks-1, - wrpointer.eq(0) - ).Else( - wrpointer.eq(wrpointer + 1) - ) - cases = {} - for i in range(wrchunks): - cases[i] = [storage[iwidth*i:iwidth*(i+1)].eq(self.i)] - self.sync.write += Case(wrpointer, cases) - - - self.sync.read += \ - If(rdpointer == rdchunks-1, - rdpointer.eq(0) - ).Else( - rdpointer.eq(rdpointer + 1) - ) - cases = {} - for i in range(rdchunks): - cases[i] = [self.o.eq(storage[owidth*i:owidth*(i+1)])] - self.sync.read += Case(rdpointer, cases) \ No newline at end of file diff --git a/litex/gen/genlib/coding.py b/litex/gen/genlib/coding.py deleted file mode 100644 index 80d62d6d..00000000 --- a/litex/gen/genlib/coding.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -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<>> self.active = Signal() - >>> self.bitno = Signal(3) - >>> - >>> fsm = FSM(reset_state="START") - >>> self.submodules += fsm - >>> - >>> fsm.act("START", - ... self.active.eq(1), - ... If(strobe, - ... NextState("DATA") - ... ) - ... ) - >>> fsm.act("DATA", - ... self.active.eq(1), - ... If(strobe, - ... NextValue(self.bitno, self.bitno + 1) - ... If(self.bitno == 7, - ... NextState("END") - ... ) - ... ) - ... ) - >>> fsm.act("END", - ... self.active.eq(0), - ... NextState("STOP") - ... ) - - """ - 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): - """ - Schedules `statements` to be executed in `state`. Statements may include: - - * combinatorial statements of form `a.eq(b)`, equivalent to - `self.comb += a.eq(b)` when the FSM is in the given `state`; - * synchronous statements of form `NextValue(a, b)`, equivalent to - `self.sync += a.eq(b)` when the FSM is in the given `state`; - * a statement of form `NextState(new_state)`, selecting the next state; - * `If`, `Case`, etc. - """ - 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): - """ - Returns a signal that has the value 1 when the FSM is in the given `state`, - and 0 otherwise. - """ - 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)) diff --git a/litex/gen/genlib/io.py b/litex/gen/genlib/io.py deleted file mode 100644 index a30218ca..00000000 --- a/litex/gen/genlib/io.py +++ /dev/null @@ -1,98 +0,0 @@ -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): - """ Clock and Reset Generator """ - - 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") - diff --git a/litex/gen/genlib/misc.py b/litex/gen/genlib/misc.py deleted file mode 100644 index a28e30d9..00000000 --- a/litex/gen/genlib/misc.py +++ /dev/null @@ -1,104 +0,0 @@ -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)) - - -class BitSlip(Module): - def __init__(self, dw): - self.i = Signal(dw) - self.o = Signal(dw) - self.value = Signal(max=dw) - - # # # - - r = Signal(2*dw) - self.sync += r.eq(Cat(r[dw:], self.i)) - cases = {} - for i in range(dw): - cases[i] = self.o.eq(r[i:dw+i]) - self.sync += Case(self.value, cases) diff --git a/litex/gen/genlib/record.py b/litex/gen/genlib/record.py deleted file mode 100644 index a82f9168..00000000 --- a/litex/gen/genlib/record.py +++ /dev/null @@ -1,195 +0,0 @@ -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 - freset_less = False - if(len(f) == 4): - fname, fsize, fdirection, freset_less = f - elif(len(f) == 3): - fname, fsize, fdirection = f - else: - fname, fsize = f - finst = Signal(fsize, name=prefix + fname, reset_less=freset_less) - 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, keep=None, omit=None): - if keep is None: - _keep = set([f[0] for f in self.layout]) - elif isinstance(keep, list): - _keep = set(keep) - else: - _keep = keep - if omit is None: - _omit = set() - elif isinstance(omit, list): - _omit = set(omit) - else: - _omit = omit - - _keep = _keep - _omit - - r = [] - for f in self.layout: - field = f[0] - self_e = getattr(self, field) - if isinstance(self_e, Signal): - if field in _keep: - 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), keep=keep, omit=omit) - 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 "" diff --git a/litex/gen/genlib/resetsync.py b/litex/gen/genlib/resetsync.py deleted file mode 100644 index 061f65bc..00000000 --- a/litex/gen/genlib/resetsync.py +++ /dev/null @@ -1,18 +0,0 @@ -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") diff --git a/litex/gen/genlib/roundrobin.py b/litex/gen/genlib/roundrobin.py deleted file mode 100644 index b9903dbb..00000000 --- a/litex/gen/genlib/roundrobin.py +++ /dev/null @@ -1,41 +0,0 @@ -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) diff --git a/litex/gen/genlib/sort.py b/litex/gen/genlib/sort.py deleted file mode 100644 index ff5b6175..00000000 --- a/litex/gen/genlib/sort.py +++ /dev/null @@ -1,71 +0,0 @@ -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-flensburg.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]) diff --git a/litex/gen/util/__init__.py b/litex/gen/util/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/litex/gen/util/misc.py b/litex/gen/util/misc.py deleted file mode 100644 index 0eadc2bf..00000000 --- a/litex/gen/util/misc.py +++ /dev/null @@ -1,29 +0,0 @@ -from math 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:]))