avoid forking migen, we will add custom modules in litex/gen but will use upstream migen for common modules

This commit is contained in:
Florent Kermarrec 2015-11-11 12:10:55 +01:00
parent 3f43a49382
commit 619cd8e695
86 changed files with 130 additions and 4852 deletions

21
README
View file

@ -2,29 +2,32 @@
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/\__/\__/_/|_|
Migen inside
Build your hardware, easily!
Copyright 2015 Enjoy-Digital
[> Intro
---------
LiteX is an alternative (fork) to Migen/MiSoC maintained and used by Enjoy-Digital
to build our cores, integrate them in complete SoC and load/flash them to the
hardware.
LiteX is an alternative to MiSoC maintained and used by Enjoy-Digital to build
our cores, integrate them in complete SoC and load/flash them to the hardware.
The structure of LiteX is kept close to Migen/MiSoC to ease collaboration
between projects.
The structure of LiteX is kept close to MiSoC to ease collaboration between
projects.
LiteX is based on Migen.
[> License
-----------
LiteX is copyright (c) 2015 Enjoy-Digital under BSD Lisense.
Since it is based on MiSoC/Migen, please also refer to LICENSE files in soc/gen
directories or git history to get correct copyrights.
Since it is based on MiSoC, please also refer to LICENSE file in soc directory
or git history to get correct copyrights.
[> Sub-packages
-----------
----------------
gen:
Provides tools and simple modules to generate HDL.
Provides specific or experimentatl modules to generate HDL that are not integrated
in Migen.
build:
Provides tools to build FPGA bitstreams (interface to vendor toolchains) and to

View file

@ -2,7 +2,7 @@
import argparse
from litex.gen import *
from migen import *
from litex.boards.platforms import de0nano
from litex.soc.cores.sdram.settings import IS42S16160

View file

@ -2,8 +2,8 @@
import argparse
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import kc705
from litex.soc.cores.sdram.settings import MT8JTF12864

View file

@ -3,8 +3,8 @@
import argparse
from fractions import Fraction
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import minispartan6
from litex.soc.cores.sdram.settings import AS4C16M16

View file

@ -3,8 +3,8 @@
import argparse
from fractions import Fraction
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import papilio_pro
from litex.soc.cores.sdram.settings import MT48LC4M16

View file

@ -3,8 +3,8 @@
import argparse
from fractions import Fraction
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import pipistrello
from litex.soc.cores.sdram_settings import MT46H32M16

View file

@ -3,9 +3,9 @@
import argparse
import importlib
from litex.gen import *
from migen import *
from litex.boards.platforms import sim
from litex.gen.genlib.io import CRG
from migen.genlib.io import CRG
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *

View file

@ -3,8 +3,8 @@
import argparse
import importlib
from litex.gen import *
from litex.gen.genlib.io import CRG
from migen import *
from migen.genlib.io import CRG
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *

View file

@ -1,6 +1,6 @@
from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Instance
from litex.gen.genlib.io import DifferentialInput, DifferentialOutput
from migen.fhdl.module import Module
from migen.fhdl.specials import Instance
from migen.genlib.io import DifferentialInput, DifferentialOutput
class AlteraDifferentialInputImpl(Module):

View file

@ -4,7 +4,7 @@
import os
import subprocess
from litex.gen.fhdl.structure import _Fragment
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import Pins, IOStandard, Misc
from litex.build import tools

View file

@ -1,9 +1,9 @@
import os
from litex.gen.fhdl.structure import Signal
from litex.gen.genlib.record import Record
from litex.gen.genlib.io import CRG
from litex.gen.fhdl import verilog, edif
from migen.fhdl.structure import Signal
from migen.genlib.record import Record
from migen.genlib.io import CRG
from migen.fhdl import verilog, edif
from litex.build import tools

View file

@ -1,7 +1,7 @@
from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Instance
from litex.gen.genlib.io import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from migen.fhdl.module import Module
from migen.fhdl.specials import Instance
from migen.genlib.io import *
from migen.genlib.resetsync import AsyncResetSynchronizer
class LatticeAsyncResetSynchronizerImpl(Module):

View file

@ -6,7 +6,7 @@ import sys
import subprocess
import shutil
from litex.gen.fhdl.structure import _Fragment
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build import tools

View file

@ -4,7 +4,7 @@
import os
import subprocess
from litex.gen.fhdl.structure import _Fragment
from migen.fhdl.structure import _Fragment
from litex.build import tools
from litex.build.generic_platform import *

View file

@ -2,13 +2,13 @@ import os
import sys
from distutils.version import StrictVersion
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.specials import Instance
from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import SynthesisDirective
from litex.gen.genlib.cdc import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen.genlib.io import *
from migen.fhdl.structure import *
from migen.fhdl.specials import Instance
from migen.fhdl.module import Module
from migen.fhdl.specials import SynthesisDirective
from migen.genlib.cdc import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.io import *
from litex.build import tools

View file

@ -2,7 +2,7 @@ import os
import subprocess
import sys
from litex.gen.fhdl.structure import _Fragment
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build import tools
from litex.build.xilinx import common

View file

@ -5,7 +5,7 @@ import os
import subprocess
import sys
from litex.gen.fhdl.structure import _Fragment
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build import tools
from litex.build.xilinx import common

View file

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

View file

@ -1,10 +0,0 @@
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.module import *
from litex.gen.fhdl.specials import *
from litex.gen.fhdl.bitcontainer import *
from litex.gen.fhdl.decorators import *
from litex.gen.sim import *
from litex.gen.genlib.record import *
from litex.gen.genlib.fsm import *

View file

@ -1,121 +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):
l = 1
r = 0
while l < n:
l *= 2
r += 1
if need_pow2 and l != n:
raise ValueError("Not a power of 2")
return r
def bits_for(n, require_sign_bit=False):
if n > 0:
r = log2_int(n + 1, False)
else:
require_sign_bit = True
r = log2_int(-n, False)
if require_sign_bit:
r += 1
return r
def value_bits_sign(v):
"""Bit length and signedness of a value.
Parameters
----------
v : Value
Returns
-------
int, bool
Number of bits required to store `v` or available in `v`, followed by
whether `v` has a sign bit (included in the bit count).
Examples
--------
>>> value_bits_sign(f.Signal(8))
8, False
>>> value_bits_sign(C(0xaa))
8, False
"""
if isinstance(v, (f.Constant, f.Signal)):
return v.nbits, v.signed
elif isinstance(v, (f.ClockSignal, f.ResetSignal)):
return 1, False
elif isinstance(v, f._Operator):
obs = list(map(value_bits_sign, v.operands))
if v.op == "+" or v.op == "-":
if not obs[0][1] and not obs[1][1]:
# both operands unsigned
return max(obs[0][0], obs[1][0]) + 1, False
elif obs[0][1] and obs[1][1]:
# both operands signed
return max(obs[0][0], obs[1][0]) + 1, True
elif not obs[0][1] and obs[1][1]:
# first operand unsigned (add sign bit), second operand signed
return max(obs[0][0] + 1, obs[1][0]) + 1, True
else:
# first signed, second operand unsigned (add sign bit)
return max(obs[0][0], obs[1][0] + 1) + 1, True
elif v.op == "*":
if not obs[0][1] and not obs[1][1]:
# both operands unsigned
return obs[0][0] + obs[1][0], False
elif obs[0][1] and obs[1][1]:
# both operands signed
return obs[0][0] + obs[1][0] - 1, True
else:
# one operand signed, the other unsigned (add sign bit)
return obs[0][0] + obs[1][0] + 1 - 1, True
elif v.op == "<<<":
if obs[1][1]:
extra = 2**(obs[1][0] - 1) - 1
else:
extra = 2**obs[1][0] - 1
return obs[0][0] + extra, obs[0][1]
elif v.op == ">>>":
if obs[1][1]:
extra = 2**(obs[1][0] - 1)
else:
extra = 0
return obs[0][0] + extra, obs[0][1]
elif v.op == "&" or v.op == "^" or v.op == "|":
if not obs[0][1] and not obs[1][1]:
# both operands unsigned
return max(obs[0][0], obs[1][0]), False
elif obs[0][1] and obs[1][1]:
# both operands signed
return max(obs[0][0], obs[1][0]), True
elif not obs[0][1] and obs[1][1]:
# first operand unsigned (add sign bit), second operand signed
return max(obs[0][0] + 1, obs[1][0]), True
else:
# first signed, second operand unsigned (add sign bit)
return max(obs[0][0], obs[1][0] + 1), True
elif v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" \
or v.op == ">" or v.op == ">=":
return 1, False
elif v.op == "~":
return obs[0]
else:
raise TypeError
elif isinstance(v, f._Slice):
return v.stop - v.start, value_bits_sign(v.value)[1]
elif isinstance(v, f.Cat):
return sum(value_bits_sign(sv)[0] for sv in v.l), False
elif isinstance(v, f.Replicate):
return (value_bits_sign(v.v)[0])*v.n, False
elif isinstance(v, f._ArrayProxy):
bsc = list(map(value_bits_sign, v.choices))
return max(bs[0] for bs in bsc), any(bs[1] for bs in bsc)
else:
raise TypeError("Can not calculate bit length of {} {}".format(
type(v), v))

View file

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

View file

@ -1,107 +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__
# "{}_{}".format(self.__class__.__name__, victim.__name__)
return Wrapped
def wrap_instance(self, victim):
self.transform_instance(victim)
orig_get_fragment = victim.get_fragment
def get_fragment():
f = orig_get_fragment()
self.transform_fragment(victim, f)
return f
victim.get_fragment = get_fragment
return victim
def __call__(self, victim):
if isinstance(victim, Module):
return self.wrap_instance(victim)
else:
return self.wrap_class(victim)
class ControlInserter(ModuleTransformer):
control_name = None # override this
def __init__(self, clock_domains=None):
self.clock_domains = clock_domains
def transform_instance(self, i):
if self.clock_domains is None:
ctl = Signal(name=self.control_name)
assert not hasattr(i, self.control_name)
setattr(i, self.control_name, ctl)
else:
for cd in self.clock_domains:
name = self.control_name + "_" + cd
ctl = Signal(name=name)
assert not hasattr(i, name)
setattr(i, name, ctl)
def transform_fragment(self, i, f):
if self.clock_domains is None:
if len(f.sync) != 1:
raise ValueError("Control signal clock domains must be specified when module has more than one domain")
cdn = list(f.sync.keys())[0]
to_insert = [(getattr(i, self.control_name), cdn)]
else:
to_insert = [(getattr(i, self.control_name + "_" + cdn), cdn)
for cdn in self.clock_domains]
self.transform_fragment_insert(i, f, to_insert)
class CEInserter(ControlInserter):
control_name = "ce"
def transform_fragment_insert(self, i, f, to_insert):
for ce, cdn in to_insert:
f.sync[cdn] = [If(ce, *f.sync[cdn])]
class ResetInserter(ControlInserter):
control_name = "reset"
def transform_fragment_insert(self, i, f, to_insert):
for reset, cdn in to_insert:
f.sync[cdn] = insert_reset(reset, f.sync[cdn])
class ClockDomainsRenamer(ModuleTransformer):
def __init__(self, cd_remapping):
if isinstance(cd_remapping, str):
cd_remapping = {"sys": cd_remapping}
self.cd_remapping = cd_remapping
def transform_fragment(self, i, f):
for old, new in self.cd_remapping.items():
rename_clock_domain(f, old, new)

View file

@ -1,213 +0,0 @@
from collections import OrderedDict, namedtuple
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.namer import build_namespace
from litex.gen.fhdl.tools import list_special_ios
from litex.gen.fhdl.structure import _Fragment
from litex.gen.fhdl.conv_output import ConvOutput
_Port = namedtuple("_Port", "name direction")
_Cell = namedtuple("_Cell", "name ports")
_Property = namedtuple("_Property", "name value")
_Instance = namedtuple("_Instance", "name cell properties")
_NetBranch = namedtuple("_NetBranch", "portname instancename")
def _write_cells(cells):
r = ""
for cell in cells:
r += """
(cell {0.name}
(cellType GENERIC)
(view view_1
(viewType NETLIST)
(interface""".format(cell)
for port in cell.ports:
r += """
(port {0.name} (direction {0.direction}))""".format(port)
r += """
)
)
)"""
return r
def _write_io(ios):
r = ""
for s in ios:
r += """
(port {0.name} (direction {0.direction}))""".format(s)
return r
def _write_instantiations(instances, cell_library):
instantiations = ""
for instance in instances:
instantiations += """
(instance {0.name}
(viewRef view_1 (cellRef {0.cell} (libraryRef {1})))""".format(instance, cell_library)
for prop in instance.properties:
instantiations += """
(property {0} (string "{1}"))""".format(prop.name, prop.value)
instantiations += """
)"""
return instantiations
def _write_connections(connections):
r = ""
for netname, branches in connections.items():
r += """
(net {0}
(joined""".format(netname)
for branch in branches:
r += """
(portRef {0}{1})""".format(branch.portname, "" if branch.instancename == "" else " (instanceRef {})".format(branch.instancename))
r += """
)
)"""
return r
def _write_edif(cells, ios, instances, connections, cell_library, design_name, part, vendor):
r = """(edif {0}
(edifVersion 2 0 0)
(edifLevel 0)
(keywordMap (keywordLevel 0))
(external {1}
(edifLevel 0)
(technology (numberDefinition))""".format(design_name, cell_library)
r += _write_cells(cells)
r += """
)
(library {0}_lib
(edifLevel 0)
(technology (numberDefinition))
(cell {0}
(cellType GENERIC)
(view view_1
(viewType NETLIST)
(interface""".format(design_name)
r += _write_io(ios)
r += """
(designator "{0}")
)
(contents""".format(part)
r += _write_instantiations(instances, cell_library)
r += _write_connections(connections)
r += """
)
)
)
)
(design {0}
(cellRef {0} (libraryRef {0}_lib))
(property PART (string "{1}") (owner "{2}"))
)
)""".format(design_name, part, vendor)
return r
def _generate_cells(f):
cell_dict = OrderedDict()
for special in f.specials:
if isinstance(special, Instance):
port_list = []
for port in special.items:
if isinstance(port, Instance.Input):
port_list.append(_Port(port.name, "INPUT"))
elif isinstance(port, Instance.Output):
port_list.append(_Port(port.name, "OUTPUT"))
elif isinstance(port, Instance.InOut):
port_list.append(_Port(port.name, "INOUT"))
elif isinstance(port, Instance.Parameter):
pass
else:
raise NotImplementedError("Unsupported instance item")
if special.of in cell_dict:
if set(port_list) != set(cell_dict[special.of]):
raise ValueError("All instances must have the same ports for EDIF conversion")
else:
cell_dict[special.of] = port_list
else:
raise ValueError("EDIF conversion can only handle synthesized fragments")
return [_Cell(k, v) for k, v in cell_dict.items()]
def _generate_instances(f, ns):
instances = []
for special in f.specials:
if isinstance(special, Instance):
props = []
for prop in special.items:
if isinstance(prop, Instance.Input):
pass
elif isinstance(prop, Instance.Output):
pass
elif isinstance(prop, Instance.InOut):
pass
elif isinstance(prop, Instance.Parameter):
props.append(_Property(name=prop.name, value=prop.value))
else:
raise NotImplementedError("Unsupported instance item")
instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props))
else:
raise ValueError("EDIF conversion can only handle synthesized fragments")
return instances
def _generate_ios(f, ios, ns):
outs = list_special_ios(f, False, True, False)
inouts = list_special_ios(f, False, False, True)
r = []
for io in ios:
direction = "OUTPUT" if io in outs else "INOUT" if io in inouts else "INPUT"
r.append(_Port(name=ns.get_name(io), direction=direction))
return r
def _generate_connections(f, ios, ns):
r = OrderedDict()
for special in f.specials:
if isinstance(special, Instance):
instname = ns.get_name(special)
for port in special.items:
if isinstance(port, Instance._IO):
s = ns.get_name(port.expr)
if s not in r:
r[s] = []
r[s].append(_NetBranch(portname=port.name, instancename=instname))
elif isinstance(port, Instance.Parameter):
pass
else:
raise NotImplementedError("Unsupported instance item")
else:
raise ValueError("EDIF conversion can only handle synthesized fragments")
for s in ios:
io = ns.get_name(s)
if io not in r:
r[io] = []
r[io].append(_NetBranch(portname=io, instancename=""))
return r
def convert(f, ios, cell_library, vendor, device, name="top"):
if not isinstance(f, _Fragment):
f = f.get_fragment()
if f.comb != [] or f.sync != {}:
raise ValueError("EDIF conversion can only handle synthesized fragments")
if ios is None:
ios = set()
cells = _generate_cells(f)
ns = build_namespace(list_special_ios(f, True, True, True))
instances = _generate_instances(f, ns)
inouts = _generate_ios(f, ios, ns)
connections = _generate_connections(f, ios, ns)
src = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor)
r = ConvOutput()
r.set_main_source(src)
r.ns = ns
return r

View file

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

View file

@ -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
for signal in signals:
if signal.name_override is not None:
ns.get_name(signal)
return ns
class Namespace:
def __init__(self, pnd, reserved_keywords=set()):
self.counts = {k: 1 for k in reserved_keywords}
self.sigs = {}
self.pnd = pnd
self.clock_domains = dict()
def get_name(self, sig):
if isinstance(sig, ClockSignal):
sig = self.clock_domains[sig.cd].clk
if isinstance(sig, ResetSignal):
sig = self.clock_domains[sig.cd].rst
if sig is None:
raise ValueError("Attempted to obtain name of non-existent "
"reset signal of domain "+sig.cd)
if sig.name_override is not None:
sig_name = sig.name_override
else:
sig_name = self.pnd[sig]
try:
n = self.sigs[sig]
except KeyError:
try:
n = self.counts[sig_name]
except KeyError:
n = 0
self.sigs[sig] = n
self.counts[sig_name] = n + 1
if n:
return sig_name + "_" + str(n)
else:
return sig_name

View file

@ -1,114 +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
class FullMemoryWE(ModuleTransformer):
def __init__(self):
self.replacments = dict()
def transform_fragment(self, i, f):
newspecials = set()
for orig in f.specials:
if not isinstance(orig, Memory):
newspecials.add(orig)
continue
global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports])
if global_granularity == orig.width:
newspecials.add(orig) # nothing to do
else:
newmems = []
for i in range(orig.width//global_granularity):
if orig.init is None:
newinit = None
else:
newinit = [(v >> i*global_granularity) & (2**global_granularity - 1) for v in orig.init]
newmem = Memory(global_granularity, orig.depth, newinit, orig.name_override + "_grain" + str(i))
newspecials.add(newmem)
newmems.append(newmem)
for port in orig.ports:
port_granularity = port.we_granularity if port.we_granularity else orig.width
newport = _MemoryPort(
adr=port.adr,
dat_r=port.dat_r[i*global_granularity:(i+1)*global_granularity] if port.dat_r is not None else None,
we=port.we[i*global_granularity//port_granularity] if port.we is not None else None,
dat_w=port.dat_w[i*global_granularity:(i+1)*global_granularity] if port.dat_w is not None else None,
async_read=port.async_read,
re=port.re,
we_granularity=0,
mode=port.mode,
clock_domain=port.clock.cd)
newmem.ports.append(newport)
newspecials.add(newport)
self.replacments[orig] = newmems
f.specials = newspecials
class MemoryToArray(ModuleTransformer):
def __init__(self):
self.replacements = dict()
def transform_fragment(self, i, f):
newspecials = set()
for mem in f.specials:
if not isinstance(mem, Memory):
newspecials.add(mem)
continue
storage = Array()
self.replacements[mem] = storage
init = []
if mem.init is not None:
init = mem.init
for d in init:
mem_storage = Signal(mem.width, reset=d)
storage.append(mem_storage)
for _ in range(mem.depth-len(init)):
mem_storage = Signal(mem.width)
storage.append(mem_storage)
for port in mem.ports:
if port.we_granularity:
raise NotImplementedError
try:
sync = f.sync[port.clock.cd]
except KeyError:
sync = f.sync[port.clock.cd] = []
# read
if port.async_read:
f.comb.append(port.dat_r.eq(storage[port.adr]))
else:
if port.mode == WRITE_FIRST and port.we is not None:
adr_reg = Signal.like(port.adr)
rd_stmt = adr_reg.eq(port.adr)
f.comb.append(port.dat_r.eq(storage[adr_reg]))
elif port.mode == NO_CHANGE and port.we is not None:
rd_stmt = If(~port.we, port.dat_r.eq(storage[port.adr]))
else: # READ_FIRST or port.we is None, simplest case
rd_stmt = port.dat_r.eq(storage[port.adr])
if port.re is None:
sync.append(rd_stmt)
else:
sync.append(If(port.re, rd_stmt))
# write
if port.we is not None:
if port.we_granularity:
n = mem.width//port.we_granularity
for i in range(n):
m = i*port.we_granularity
M = (i+1)*port.we_granularity
sync.append(If(port.we[i],
storage[port.adr][m:M].eq(port.dat_w)))
else:
sync.append(If(port.we,
storage[port.adr].eq(port.dat_w)))
f.specials = newspecials

View file

@ -1,360 +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, **kwargs):
Special.__init__(self)
self.of = of
if name:
self.name_override = name
else:
self.name_override = of
self.items = list(items)
self.synthesis_directive = synthesis_directive
for k, v in sorted(kwargs.items(), key=itemgetter(0)):
item_type, item_name = k.split("_", maxsplit=1)
item_class = {
"i": Instance.Input,
"o": Instance.Output,
"io": Instance.InOut,
"p": Instance.Parameter
}[item_type]
self.items.append(item_class(item_name, v))
def get_io(self, name):
for item in self.items:
if isinstance(item, Instance._IO) and item.name == name:
return item.expr
def iter_expressions(self):
for item in self.items:
if isinstance(item, Instance.Input):
yield item, "expr", SPECIAL_INPUT
elif isinstance(item, Instance.Output):
yield item, "expr", SPECIAL_OUTPUT
elif isinstance(item, Instance.InOut):
yield item, "expr", SPECIAL_INOUT
@staticmethod
def emit_verilog(instance, ns, add_data_file):
r = instance.of + " "
parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items))
if parameters:
r += "#(\n"
firstp = True
for p in parameters:
if not firstp:
r += ",\n"
firstp = False
r += "\t." + p.name + "("
if isinstance(p.value, Constant):
r += verilog_printexpr(ns, p.value)[0]
elif isinstance(p.value, float):
r += str(p.value)
elif isinstance(p.value, Instance.PreformattedParam):
r += p.value
elif isinstance(p.value, str):
r += "\"" + p.value + "\""
else:
raise TypeError
r += ")"
r += "\n) "
r += ns.get_name(instance)
if parameters: r += " "
r += "(\n"
firstp = True
for p in instance.items:
if isinstance(p, Instance._IO):
name_inst = p.name
name_design = verilog_printexpr(ns, p.expr)[0]
if not firstp:
r += ",\n"
firstp = False
r += "\t." + name_inst + "(" + name_design + ")"
if not firstp:
r += "\n"
if instance.synthesis_directive is not None:
synthesis_directive = "/* synthesis {} */".format(instance.synthesis_directive)
r += ")" + synthesis_directive + ";\n\n"
else:
r += ");\n\n"
return r
(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3)
class _MemoryPort(Special):
def __init__(self, adr, dat_r, we=None, dat_w=None,
async_read=False, re=None, we_granularity=0, mode=WRITE_FIRST,
clock_domain="sys"):
Special.__init__(self)
self.adr = adr
self.dat_r = dat_r
self.we = we
self.dat_w = dat_w
self.async_read = async_read
self.re = re
self.we_granularity = we_granularity
self.mode = mode
self.clock = ClockSignal(clock_domain)
def iter_expressions(self):
for attr, target_context in [
("adr", SPECIAL_INPUT),
("we", SPECIAL_INPUT),
("dat_w", SPECIAL_INPUT),
("re", SPECIAL_INPUT),
("dat_r", SPECIAL_OUTPUT),
("clock", SPECIAL_INPUT)]:
yield self, attr, target_context
@staticmethod
def emit_verilog(port, ns, add_data_file):
return "" # done by parent Memory object
class _MemoryLocation(_Value):
def __init__(self, memory, index):
_Value.__init__(self)
self.memory = memory
self.index = wrap(index)
class Memory(Special):
def __init__(self, width, depth, init=None, name=None):
Special.__init__(self)
self.width = width
self.depth = depth
self.ports = []
self.init = init
self.name_override = get_obj_var_name(name, "mem")
def __getitem__(self, index):
# simulation only
return _MemoryLocation(self, index)
def get_port(self, write_capable=False, async_read=False,
has_re=False, we_granularity=0, mode=WRITE_FIRST,
clock_domain="sys"):
if we_granularity >= self.width:
we_granularity = 0
adr = Signal(max=self.depth)
dat_r = Signal(self.width)
if write_capable:
if we_granularity:
we = Signal(self.width//we_granularity)
else:
we = Signal()
dat_w = Signal(self.width)
else:
we = None
dat_w = None
if has_re:
re = Signal()
else:
re = None
mp = _MemoryPort(adr, dat_r, we, dat_w,
async_read, re, we_granularity, mode,
clock_domain)
self.ports.append(mp)
return mp
@staticmethod
def emit_verilog(memory, ns, add_data_file):
r = ""
def gn(e):
if isinstance(e, Memory):
return ns.get_name(e)
else:
return verilog_printexpr(ns, e)[0]
adrbits = bits_for(memory.depth-1)
r += "reg [" + str(memory.width-1) + ":0] " \
+ gn(memory) \
+ "[0:" + str(memory.depth-1) + "];\n"
adr_regs = {}
data_regs = {}
for port in memory.ports:
if not port.async_read:
if port.mode == WRITE_FIRST and port.we is not None:
adr_reg = Signal(name_override="memadr")
r += "reg [" + str(adrbits-1) + ":0] " \
+ gn(adr_reg) + ";\n"
adr_regs[id(port)] = adr_reg
else:
data_reg = Signal(name_override="memdat")
r += "reg [" + str(memory.width-1) + ":0] " \
+ gn(data_reg) + ";\n"
data_regs[id(port)] = data_reg
for port in memory.ports:
r += "always @(posedge " + gn(port.clock) + ") begin\n"
if port.we is not None:
if port.we_granularity:
n = memory.width//port.we_granularity
for i in range(n):
m = i*port.we_granularity
M = (i+1)*port.we_granularity-1
sl = "[" + str(M) + ":" + str(m) + "]"
r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n"
r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "]" + sl + " <= " + gn(port.dat_w) + sl + ";\n"
else:
r += "\tif (" + gn(port.we) + ")\n"
r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n"
if not port.async_read:
if port.mode == WRITE_FIRST and port.we is not None:
rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n"
else:
bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n"
if port.mode == READ_FIRST or port.we is None:
rd = "\t" + bassign
elif port.mode == NO_CHANGE:
rd = "\tif (!" + gn(port.we) + ")\n" \
+ "\t\t" + bassign
if port.re is None:
r += rd
else:
r += "\tif (" + gn(port.re) + ")\n"
r += "\t" + rd.replace("\n\t", "\n\t\t")
r += "end\n\n"
for port in memory.ports:
if port.async_read:
r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n"
else:
if port.mode == WRITE_FIRST and port.we is not None:
r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n"
else:
r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n"
r += "\n"
if memory.init is not None:
content = ""
for d in memory.init:
content += "{:x}\n".format(d)
memory_filename = add_data_file(gn(memory) + ".init", content)
r += "initial begin\n"
r += "\t$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n"
r += "end\n\n"
return r
class SynthesisDirective(Special):
def __init__(self, template, **signals):
Special.__init__(self)
self.template = template
self.signals = signals
@staticmethod
def emit_verilog(directive, ns, add_data_file):
name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items())
formatted = directive.template.format(**name_dict)
return "// synthesis " + formatted + "\n"
class Keep(SynthesisDirective):
def __init__(self, signal):
SynthesisDirective.__init__(self, "attribute keep of {s} is true", s=signal)

View file

@ -1,713 +0,0 @@
import builtins as _builtins
import collections as _collections
from litex.gen.fhdl import tracer as _tracer
from litex.gen.util.misc import flat_iteration as _flat_iteration
class DUID:
"""Deterministic Unique IDentifier"""
__next_uid = 0
def __init__(self):
self.duid = DUID.__next_uid
DUID.__next_uid += 1
class _Value(DUID):
"""Base class for operands
Instances of `_Value` or its subclasses can be operands to
arithmetic, comparison, bitwise, and logic operators.
They can be assigned (:meth:`eq`) or indexed/sliced (using the usual
Python indexing and slicing notation).
Values created from integers have the minimum bit width to necessary to
represent the integer.
"""
def __bool__(self):
# Special case: Constants and Signals are part of a set or used as
# dictionary keys, and Python needs to check for equality.
if isinstance(self, _Operator) and self.op == "==":
a, b = self.operands
if isinstance(a, Constant) and isinstance(b, Constant):
return a.value == b.value
if isinstance(a, Signal) and isinstance(b, Signal):
return a is b
if (isinstance(a, Constant) and isinstance(b, Signal)
or isinstance(a, Signal) and isinstance(a, Constant)):
return False
raise TypeError("Attempted to convert Migen value to boolean")
def __invert__(self):
return _Operator("~", [self])
def __neg__(self):
return _Operator("-", [self])
def __add__(self, other):
return _Operator("+", [self, other])
def __radd__(self, other):
return _Operator("+", [other, self])
def __sub__(self, other):
return _Operator("-", [self, other])
def __rsub__(self, other):
return _Operator("-", [other, self])
def __mul__(self, other):
return _Operator("*", [self, other])
def __rmul__(self, other):
return _Operator("*", [other, self])
def __lshift__(self, other):
return _Operator("<<<", [self, other])
def __rlshift__(self, other):
return _Operator("<<<", [other, self])
def __rshift__(self, other):
return _Operator(">>>", [self, other])
def __rrshift__(self, other):
return _Operator(">>>", [other, self])
def __and__(self, other):
return _Operator("&", [self, other])
def __rand__(self, other):
return _Operator("&", [other, self])
def __xor__(self, other):
return _Operator("^", [self, other])
def __rxor__(self, other):
return _Operator("^", [other, self])
def __or__(self, other):
return _Operator("|", [self, other])
def __ror__(self, other):
return _Operator("|", [other, self])
def __lt__(self, other):
return _Operator("<", [self, other])
def __le__(self, other):
return _Operator("<=", [self, other])
def __eq__(self, other):
return _Operator("==", [self, other])
def __ne__(self, other):
return _Operator("!=", [self, other])
def __gt__(self, other):
return _Operator(">", [self, other])
def __ge__(self, other):
return _Operator(">=", [self, other])
def __len__(self):
from litex.gen.fhdl.bitcontainer import value_bits_sign
return value_bits_sign(self)[0]
def __getitem__(self, key):
n = len(self)
if isinstance(key, int):
if key >= n:
raise IndexError
if key < 0:
key += n
return _Slice(self, key, key+1)
elif isinstance(key, slice):
start, stop, step = key.indices(n)
if step != 1:
return Cat(self[i] for i in range(start, stop, step))
return _Slice(self, start, stop)
else:
raise TypeError
def eq(self, r):
"""Assignment
Parameters
----------
r : _Value, in
Value to be assigned.
Returns
-------
_Assign
Assignment statement that can be used in combinatorial or
synchronous context.
"""
return _Assign(self, r)
def __hash__(self):
raise TypeError("unhashable type: '{}'".format(type(self).__name__))
def wrap(value):
"""Ensures that the passed object is a Migen value. Booleans and integers
are automatically wrapped into ``Constant``."""
if isinstance(value, (bool, int)):
value = Constant(value)
if not isinstance(value, _Value):
raise TypeError("Object is not a Migen value")
return value
class _Operator(_Value):
def __init__(self, op, operands):
_Value.__init__(self)
self.op = op
self.operands = [wrap(o) for o in operands]
def Mux(sel, val1, val0):
"""Multiplex between two values
Parameters
----------
sel : _Value(1), in
Selector.
val1 : _Value(N), in
val0 : _Value(N), in
Input values.
Returns
-------
_Value(N), out
Output `_Value`. If `sel` is asserted, the Mux returns
`val1`, else `val0`.
"""
return _Operator("m", [sel, val1, val0])
class _Slice(_Value):
def __init__(self, value, start, stop):
_Value.__init__(self)
if not isinstance(start, int) or not isinstance(stop, int):
raise TypeError("Slice boundaries must be integers")
self.value = wrap(value)
self.start = start
self.stop = stop
class Cat(_Value):
"""Concatenate values
Form a compound `_Value` from several smaller ones by concatenation.
The first argument occupies the lower bits of the result.
The return value can be used on either side of an assignment, that
is, the concatenated value can be used as an argument on the RHS or
as a target on the LHS. If it is used on the LHS, it must solely
consist of `Signal` s, slices of `Signal` s, and other concatenations
meeting these properties. The bit length of the return value is the sum of
the bit lengths of the arguments::
len(Cat(args)) == sum(len(arg) for arg in args)
Parameters
----------
*args : _Values or iterables of _Values, inout
`_Value` s to be concatenated.
Returns
-------
Cat, inout
Resulting `_Value` obtained by concatentation.
"""
def __init__(self, *args):
_Value.__init__(self)
self.l = [wrap(v) for v in _flat_iteration(args)]
class Replicate(_Value):
"""Replicate a value
An input value is replicated (repeated) several times
to be used on the RHS of assignments::
len(Replicate(s, n)) == len(s)*n
Parameters
----------
v : _Value, in
Input value to be replicated.
n : int
Number of replications.
Returns
-------
Replicate, out
Replicated value.
"""
def __init__(self, v, n):
_Value.__init__(self)
if not isinstance(n, int) or n < 0:
raise TypeError("Replication count must be a positive integer")
self.v = wrap(v)
self.n = n
class Constant(_Value):
"""A constant, HDL-literal integer `_Value`
Parameters
----------
value : int
bits_sign : int or tuple or None
Either an integer `bits` or a tuple `(bits, signed)`
specifying the number of bits in this `Constant` and whether it is
signed (can represent negative values). `bits_sign` defaults
to the minimum width and signedness of `value`.
"""
def __init__(self, value, bits_sign=None):
from litex.gen.fhdl.bitcontainer import bits_for
_Value.__init__(self)
self.value = int(value)
if bits_sign is None:
bits_sign = bits_for(self.value), self.value < 0
elif isinstance(bits_sign, int):
bits_sign = bits_sign, self.value < 0
self.nbits, self.signed = bits_sign
if not isinstance(self.nbits, int) or self.nbits <= 0:
raise TypeError("Width must be a strictly positive integer")
def __hash__(self):
return self.value
C = Constant # shorthand
class Signal(_Value):
"""A `_Value` that can change
The `Signal` object represents a value that is expected to change
in the circuit. It does exactly what Verilog's `wire` and
`reg` and VHDL's `signal` do.
A `Signal` can be indexed to access a subset of its bits. Negative
indices (`signal[-1]`) and the extended Python slicing notation
(`signal[start:stop:step]`) are supported.
The indices 0 and -1 are the least and most significant bits
respectively.
Parameters
----------
bits_sign : int or tuple
Either an integer `bits` or a tuple `(bits, signed)`
specifying the number of bits in this `Signal` and whether it is
signed (can represent negative values). `signed` defaults to
`False`.
name : str or None
Name hint for this signal. If `None` (default) the name is
inferred from the variable name this `Signal` is assigned to.
Name collisions are automatically resolved by prepending
names of objects that contain this `Signal` and by
appending integer sequences.
variable : bool
Deprecated.
reset : int
Reset (synchronous) or default (combinatorial) value.
When this `Signal` is assigned to in synchronous context and the
corresponding clock domain is reset, the `Signal` assumes the
given value. When this `Signal` is unassigned in combinatorial
context (due to conditional assignments not being taken),
the `Signal` assumes its `reset` value. Defaults to 0.
name_override : str or None
Do not use the inferred name but the given one.
min : int or None
max : int or None
If `bits_sign` is `None`, the signal bit width and signedness are
determined by the integer range given by `min` (inclusive,
defaults to 0) and `max` (exclusive, defaults to 2).
related : Signal or None
"""
def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None):
from litex.gen.fhdl.bitcontainer import bits_for
_Value.__init__(self)
# determine number of bits and signedness
if bits_sign is None:
if min is None:
min = 0
if max is None:
max = 2
max -= 1 # make both bounds inclusive
assert(min < max)
self.signed = min < 0 or max < 0
self.nbits = _builtins.max(bits_for(min, self.signed), bits_for(max, self.signed))
else:
assert(min is None and max is None)
if isinstance(bits_sign, tuple):
self.nbits, self.signed = bits_sign
else:
self.nbits, self.signed = bits_sign, False
if not isinstance(self.nbits, int) or self.nbits <= 0:
raise ValueError("Signal width must be a strictly positive integer")
self.variable = variable # deprecated
self.reset = reset
self.name_override = name_override
self.backtrace = _tracer.trace_back(name)
self.related = related
def __setattr__(self, k, v):
if k == "reset":
v = wrap(v)
_Value.__setattr__(self, k, v)
def __repr__(self):
return "<Signal " + (self.backtrace[-1][0] or "anonymous") + " at " + hex(id(self)) + ">"
@classmethod
def like(cls, other, **kwargs):
"""Create Signal based on another.
Parameters
----------
other : _Value
Object to base this Signal on.
See `migen.fhdl.bitcontainer.value_bits_sign` for details.
"""
from litex.gen.fhdl.bitcontainer import value_bits_sign
return cls(bits_sign=value_bits_sign(other), **kwargs)
def __hash__(self):
return self.duid
class ClockSignal(_Value):
"""Clock signal for a given clock domain
`ClockSignal` s for a given clock domain can be retrieved multiple
times. They all ultimately refer to the same signal.
Parameters
----------
cd : str
Clock domain to obtain a clock signal for. Defaults to `"sys"`.
"""
def __init__(self, cd="sys"):
_Value.__init__(self)
if not isinstance(cd, str):
raise TypeError("Argument of ClockSignal must be a string")
self.cd = cd
class ResetSignal(_Value):
"""Reset signal for a given clock domain
`ResetSignal` s for a given clock domain can be retrieved multiple
times. They all ultimately refer to the same signal.
Parameters
----------
cd : str
Clock domain to obtain a reset signal for. Defaults to `"sys"`.
allow_reset_less : bool
If the clock domain is resetless, return 0 instead of reporting an
error.
"""
def __init__(self, cd="sys", allow_reset_less=False):
_Value.__init__(self)
if not isinstance(cd, str):
raise TypeError("Argument of ResetSignal must be a string")
self.cd = cd
self.allow_reset_less = allow_reset_less
# statements
class _Statement:
pass
class _Assign(_Statement):
def __init__(self, l, r):
self.l = wrap(l)
self.r = wrap(r)
def _check_statement(s):
if isinstance(s, _collections.Iterable):
return all(_check_statement(ss) for ss in s)
else:
return isinstance(s, _Statement)
class If(_Statement):
"""Conditional execution of statements
Parameters
----------
cond : _Value(1), in
Condition
*t : Statements
Statements to execute if `cond` is asserted.
Examples
--------
>>> a = Signal()
>>> b = Signal()
>>> c = Signal()
>>> d = Signal()
>>> If(a,
... b.eq(1)
... ).Elif(c,
... b.eq(0)
... ).Else(
... b.eq(d)
... )
"""
def __init__(self, cond, *t):
if not _check_statement(t):
raise TypeError("Not all test body objects are Migen statements")
self.cond = wrap(cond)
self.t = list(t)
self.f = []
def Else(self, *f):
"""Add an `else` conditional block
Parameters
----------
*f : Statements
Statements to execute if all previous conditions fail.
"""
if not _check_statement(f):
raise TypeError("Not all test body objects are Migen statements")
_insert_else(self, list(f))
return self
def Elif(self, cond, *t):
"""Add an `else if` conditional block
Parameters
----------
cond : _Value(1), in
Condition
*t : Statements
Statements to execute if previous conditions fail and `cond`
is asserted.
"""
_insert_else(self, [If(cond, *t)])
return self
def _insert_else(obj, clause):
o = obj
while o.f:
assert(len(o.f) == 1)
assert(isinstance(o.f[0], If))
o = o.f[0]
o.f = clause
class Case(_Statement):
"""Case/Switch statement
Parameters
----------
test : _Value, in
Selector value used to decide which block to execute
cases : dict
Dictionary of cases. The keys are numeric constants to compare
with `test`. The values are statements to be executed the
corresponding key matches `test`. The dictionary may contain a
string key `"default"` to mark a fall-through case that is
executed if no other key matches.
Examples
--------
>>> a = Signal()
>>> b = Signal()
>>> Case(a, {
... 0: b.eq(1),
... 1: b.eq(0),
... "default": b.eq(0),
... })
"""
def __init__(self, test, cases):
self.test = wrap(test)
self.cases = dict()
for k, v in cases.items():
if isinstance(k, (bool, int)):
k = Constant(k)
if (not isinstance(k, Constant)
and not (isinstance(k, str) and k == "default")):
raise TypeError("Case object is not a Migen constant")
if not isinstance(v, _collections.Iterable):
v = [v]
if not _check_statement(v):
raise TypeError("Not all objects for case {} "
"are Migen statements".format(k))
self.cases[k] = v
def makedefault(self, key=None):
"""Mark a key as the default case
Deletes/substitutes any previously existing default case.
Parameters
----------
key : int or None
Key to use as default case if no other key matches.
By default, the largest key is the default key.
"""
if key is None:
for choice in self.cases.keys():
if key is None or choice.value > key.value:
key = choice
self.cases["default"] = self.cases[key]
del self.cases[key]
return self
# arrays
class _ArrayProxy(_Value):
def __init__(self, choices, key):
_Value.__init__(self)
self.choices = []
for c in choices:
if isinstance(c, (bool, int)):
c = Constant(c)
self.choices.append(c)
self.key = key
def __getattr__(self, attr):
return _ArrayProxy([getattr(choice, attr) for choice in self.choices],
self.key)
def __getitem__(self, key):
return _ArrayProxy([choice.__getitem__(key) for choice in self.choices],
self.key)
class Array(list):
"""Addressable multiplexer
An array is created from an iterable of values and indexed using the
usual Python simple indexing notation (no negative indices or
slices). It can be indexed by numeric constants, `_Value` s, or
`Signal` s.
The result of indexing the array is a proxy for the entry at the
given index that can be used on either RHS or LHS of assignments.
An array can be indexed multiple times.
Multidimensional arrays are supported by packing inner arrays into
outer arrays.
Parameters
----------
values : iterable of ints, _Values, Signals
Entries of the array. Each entry can be a numeric constant, a
`Signal` or a `Record`.
Examples
--------
>>> a = Array(range(10))
>>> b = Signal(max=10)
>>> c = Signal(max=10)
>>> b.eq(a[9 - c])
"""
def __getitem__(self, key):
if isinstance(key, Constant):
return list.__getitem__(self, key.value)
elif isinstance(key, _Value):
return _ArrayProxy(self, key)
else:
return list.__getitem__(self, key)
class ClockDomain:
"""Synchronous domain
Parameters
----------
name : str or None
Domain name. If None (the default) the name is inferred from the
variable name this `ClockDomain` is assigned to (stripping any
`"cd_"` prefix).
reset_less : bool
The domain does not use a reset signal. Registers within this
domain are still all initialized to their reset state once, e.g.
through Verilog `"initial"` statements.
Attributes
----------
clk : Signal, inout
The clock for this domain. Can be driven or used to drive other
signals (preferably in combinatorial context).
rst : Signal or None, inout
Reset signal for this domain. Can be driven or used to drive.
"""
def __init__(self, name=None, reset_less=False):
self.name = _tracer.get_obj_var_name(name)
if self.name is None:
raise ValueError("Cannot extract clock domain name from code, need to specify.")
if self.name.startswith("cd_"):
self.name = self.name[3:]
if self.name[0].isdigit():
raise ValueError("Clock domain name cannot start with a number.")
self.clk = Signal(name_override=self.name + "_clk")
if reset_less:
self.rst = None
else:
self.rst = Signal(name_override=self.name + "_rst")
def rename(self, new_name):
"""Rename the clock domain
Parameters
----------
new_name : str
New name
"""
self.name = new_name
self.clk.name_override = new_name + "_clk"
if self.rst is not None:
self.rst.name_override = new_name + "_rst"
class _ClockDomainList(list):
def __getitem__(self, key):
if isinstance(key, str):
for cd in self:
if cd.name == key:
return cd
raise KeyError(key)
else:
return list.__getitem__(self, key)
(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
class _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

View file

@ -1,298 +0,0 @@
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import _Slice, _Assign
from litex.gen.fhdl.visit import NodeVisitor, NodeTransformer
from litex.gen.fhdl.bitcontainer import value_bits_sign
from litex.gen.util.misc import flat_iteration
class _SignalLister(NodeVisitor):
def __init__(self):
self.output_list = set()
def visit_Signal(self, node):
self.output_list.add(node)
class _TargetLister(NodeVisitor):
def __init__(self):
self.output_list = set()
self.target_context = False
def visit_Signal(self, node):
if self.target_context:
self.output_list.add(node)
def visit_Assign(self, node):
self.target_context = True
self.visit(node.l)
self.target_context = False
def visit_ArrayProxy(self, node):
for choice in node.choices:
self.visit(choice)
class _InputLister(NodeVisitor):
def __init__(self):
self.output_list = set()
def visit_Signal(self, node):
self.output_list.add(node)
def visit_Assign(self, node):
self.visit(node.r)
def list_signals(node):
lister = _SignalLister()
lister.visit(node)
return lister.output_list
def list_targets(node):
lister = _TargetLister()
lister.visit(node)
return lister.output_list
def list_inputs(node):
lister = _InputLister()
lister.visit(node)
return lister.output_list
def _resort_statements(ol):
return [statement for i, statement in
sorted(ol, key=lambda x: x[0])]
def group_by_targets(sl):
groups = []
seen = set()
for order, stmt in enumerate(flat_iteration(sl)):
targets = set(list_targets(stmt))
group = [(order, stmt)]
disjoint = targets.isdisjoint(seen)
seen |= targets
if not disjoint:
groups, old_groups = [], groups
for old_targets, old_group in old_groups:
if targets.isdisjoint(old_targets):
groups.append((old_targets, old_group))
else:
targets |= old_targets
group += old_group
groups.append((targets, group))
return [(targets, _resort_statements(stmts))
for targets, stmts in groups]
def list_special_ios(f, ins, outs, inouts):
r = set()
for special in f.specials:
r |= special.list_ios(ins, outs, inouts)
return r
class _ClockDomainLister(NodeVisitor):
def __init__(self):
self.clock_domains = set()
def visit_ClockSignal(self, node):
self.clock_domains.add(node.cd)
def visit_ResetSignal(self, node):
self.clock_domains.add(node.cd)
def visit_clock_domains(self, node):
for clockname, statements in node.items():
self.clock_domains.add(clockname)
self.visit(statements)
def list_clock_domains_expr(f):
cdl = _ClockDomainLister()
cdl.visit(f)
return cdl.clock_domains
def list_clock_domains(f):
r = list_clock_domains_expr(f)
for special in f.specials:
r |= special.list_clock_domains()
for cd in f.clock_domains:
r.add(cd.name)
return r
def is_variable(node):
if isinstance(node, Signal):
return node.variable
elif isinstance(node, _Slice):
return is_variable(node.value)
elif isinstance(node, Cat):
arevars = list(map(is_variable, node.l))
r = arevars[0]
for x in arevars:
if x != r:
raise TypeError
return r
else:
raise TypeError
def generate_reset(rst, sl):
targets = list_targets(sl)
return [t.eq(t.reset) for t in sorted(targets, key=lambda x: x.duid)]
def insert_reset(rst, sl):
return [If(rst, *generate_reset(rst, sl)).Else(*sl)]
def insert_resets(f):
newsync = dict()
for k, v in f.sync.items():
if f.clock_domains[k].rst is not None:
newsync[k] = insert_reset(ResetSignal(k), v)
else:
newsync[k] = v
f.sync = newsync
class _Lowerer(NodeTransformer):
def __init__(self):
self.target_context = False
self.extra_stmts = []
self.comb = []
def visit_Assign(self, node):
old_target_context, old_extra_stmts = self.target_context, self.extra_stmts
self.extra_stmts = []
self.target_context = True
lhs = self.visit(node.l)
self.target_context = False
rhs = self.visit(node.r)
r = _Assign(lhs, rhs)
if self.extra_stmts:
r = [r] + self.extra_stmts
self.target_context, self.extra_stmts = old_target_context, old_extra_stmts
return r
# Basics are FHDL structure elements that back-ends are not required to support
# but can be expressed in terms of other elements (lowered) before conversion.
class _BasicLowerer(_Lowerer):
def __init__(self, clock_domains):
self.clock_domains = clock_domains
_Lowerer.__init__(self)
def visit_ArrayProxy(self, node):
# TODO: rewrite without variables
array_muxed = Signal(value_bits_sign(node), variable=True)
if self.target_context:
k = self.visit(node.key)
cases = {}
for n, choice in enumerate(node.choices):
cases[n] = [self.visit_Assign(_Assign(choice, array_muxed))]
self.extra_stmts.append(Case(k, cases).makedefault())
else:
cases = dict((n, _Assign(array_muxed, self.visit(choice)))
for n, choice in enumerate(node.choices))
self.comb.append(Case(self.visit(node.key), cases).makedefault())
return array_muxed
def visit_ClockSignal(self, node):
return self.clock_domains[node.cd].clk
def visit_ResetSignal(self, node):
rst = self.clock_domains[node.cd].rst
if rst is None:
if node.allow_reset_less:
return 0
else:
raise ValueError("Attempted to get reset signal of resetless"
" domain '{}'".format(node.cd))
else:
return rst
class _ComplexSliceLowerer(_Lowerer):
def visit_Slice(self, node):
if not isinstance(node.value, Signal):
slice_proxy = Signal(value_bits_sign(node.value))
if self.target_context:
a = _Assign(node.value, slice_proxy)
else:
a = _Assign(slice_proxy, node.value)
self.comb.append(self.visit_Assign(a))
node = _Slice(slice_proxy, node.start, node.stop)
return NodeTransformer.visit_Slice(self, node)
def _apply_lowerer(l, f):
f = l.visit(f)
f.comb += l.comb
for special in f.specials:
for obj, attr, direction in special.iter_expressions():
if direction != SPECIAL_INOUT:
# inouts are only supported by Migen when connected directly to top-level
# in this case, they are Signal and never need lowering
l.comb = []
l.target_context = direction != SPECIAL_INPUT
l.extra_stmts = []
expr = getattr(obj, attr)
expr = l.visit(expr)
setattr(obj, attr, expr)
f.comb += l.comb + l.extra_stmts
return f
def lower_basics(f):
return _apply_lowerer(_BasicLowerer(f.clock_domains), f)
def lower_complex_slices(f):
return _apply_lowerer(_ComplexSliceLowerer(), f)
class _ClockDomainRenamer(NodeVisitor):
def __init__(self, old, new):
self.old = old
self.new = new
def visit_ClockSignal(self, node):
if node.cd == self.old:
node.cd = self.new
def visit_ResetSignal(self, node):
if node.cd == self.old:
node.cd = self.new
def rename_clock_domain_expr(f, old, new):
cdr = _ClockDomainRenamer(old, new)
cdr.visit(f)
def rename_clock_domain(f, old, new):
rename_clock_domain_expr(f, old, new)
if new != old:
if old in f.sync:
if new in f.sync:
f.sync[new].extend(f.sync[old])
else:
f.sync[new] = f.sync[old]
del f.sync[old]
for special in f.specials:
special.rename_clock_domain(old, new)
try:
cd = f.clock_domains[old]
except KeyError:
pass
else:
cd.rename(new)

View file

@ -1,115 +0,0 @@
import inspect
from opcode import opname
from collections import defaultdict
def get_var_name(frame):
code = frame.f_code
call_index = frame.f_lasti
call_opc = opname[code.co_code[call_index]]
if call_opc != "CALL_FUNCTION" and call_opc != "CALL_FUNCTION_VAR":
return None
index = call_index+3
while True:
opc = opname[code.co_code[index]]
if opc == "STORE_NAME" or opc == "STORE_ATTR":
name_index = int(code.co_code[index+1])
return code.co_names[name_index]
elif opc == "STORE_FAST":
name_index = int(code.co_code[index+1])
return code.co_varnames[name_index]
elif opc == "STORE_DEREF":
name_index = int(code.co_code[index+1])
return code.co_cellvars[name_index]
elif opc == "LOAD_GLOBAL" or opc == "LOAD_ATTR" or opc == "LOAD_FAST" or opc == "LOAD_DEREF":
index += 3
elif opc == "DUP_TOP":
index += 1
elif opc == "BUILD_LIST":
index += 3
else:
return None
def remove_underscore(s):
if len(s) > 2 and s[0] == "_" and s[1] != "_":
s = s[1:]
return s
def get_obj_var_name(override=None, default=None):
if override:
return override
frame = inspect.currentframe().f_back
# We can be called via derived classes. Go back the stack frames
# until we reach the first class that does not inherit from us.
ourclass = frame.f_locals["self"].__class__
while "self" in frame.f_locals and isinstance(frame.f_locals["self"], ourclass):
frame = frame.f_back
vn = get_var_name(frame)
if vn is None:
vn = default
else:
vn = remove_underscore(vn)
return vn
name_to_idx = defaultdict(int)
classname_to_objs = dict()
def index_id(l, obj):
for n, e in enumerate(l):
if id(e) == id(obj):
return n
raise ValueError
def trace_back(varname=None):
l = []
frame = inspect.currentframe().f_back.f_back
while frame is not None:
if varname is None:
varname = get_var_name(frame)
if varname is not None:
varname = remove_underscore(varname)
l.insert(0, (varname, name_to_idx[varname]))
name_to_idx[varname] += 1
try:
obj = frame.f_locals["self"]
except KeyError:
obj = None
if hasattr(obj, "__del__"):
obj = None
if obj is None:
if varname is not None:
coname = frame.f_code.co_name
if coname == "<module>":
modules = frame.f_globals["__name__"]
modules = modules.split(".")
coname = modules[len(modules)-1]
coname = remove_underscore(coname)
l.insert(0, (coname, name_to_idx[coname]))
name_to_idx[coname] += 1
else:
classname = obj.__class__.__name__.lower()
try:
objs = classname_to_objs[classname]
except KeyError:
classname_to_objs[classname] = [obj]
idx = 0
else:
try:
idx = index_id(objs, obj)
except ValueError:
idx = len(objs)
objs.append(obj)
classname = remove_underscore(classname)
l.insert(0, (classname, idx))
varname = None
frame = frame.f_back
return l

View file

@ -1,361 +0,0 @@
from functools import partial
from operator import itemgetter
import collections
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment
from litex.gen.fhdl.tools import *
from litex.gen.fhdl.bitcontainer import bits_for
from litex.gen.fhdl.namer import build_namespace
from litex.gen.fhdl.conv_output import ConvOutput
_reserved_keywords = {
"always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
"case", "casex", "casez", "cell", "cmos", "config", "deassign", "default",
"defparam", "design", "disable", "edge", "else", "end", "endcase",
"endconfig", "endfunction", "endgenerate", "endmodule", "endprimitive",
"endspecify", "endtable", "endtask", "event", "for", "force", "forever",
"fork", "function", "generate", "genvar", "highz0", "highz1", "if",
"ifnone", "incdir", "include", "initial", "inout", "input",
"instance", "integer", "join", "large", "liblist", "library", "localparam",
"macromodule", "medium", "module", "nand", "negedge", "nmos", "nor",
"noshowcancelled", "not", "notif0", "notif1", "or", "output", "parameter",
"pmos", "posedge", "primitive", "pull0", "pull1" "pulldown",
"pullup", "pulsestyle_onevent", "pulsestyle_ondetect", "remos", "real",
"realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran",
"rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small",
"specify", "specparam", "strong0", "strong1", "supply0", "supply1",
"table", "task", "time", "tran", "tranif0", "tranif1", "tri", "tri0",
"tri1", "triand", "trior", "trireg", "unsigned", "use", "vectored", "wait",
"wand", "weak0", "weak1", "while", "wire", "wor","xnor", "xor"
}
def _printsig(ns, s):
if s.signed:
n = "signed "
else:
n = ""
if len(s) > 1:
n += "[" + str(len(s)-1) + ":0] "
n += ns.get_name(s)
return n
def _printconstant(node):
if node.signed:
return (str(node.nbits) + "'sd" + str(2**node.nbits + node.value),
True)
else:
return str(node.nbits) + "'d" + str(node.value), False
def _printexpr(ns, node):
if isinstance(node, Constant):
return _printconstant(node)
elif isinstance(node, Signal):
return ns.get_name(node), node.signed
elif isinstance(node, _Operator):
arity = len(node.operands)
r1, s1 = _printexpr(ns, node.operands[0])
if arity == 1:
if node.op == "-":
if s1:
r = node.op + r1
else:
r = "-$signed({1'd0, " + r1 + "})"
s = True
else:
r = node.op + r1
s = s1
elif arity == 2:
r2, s2 = _printexpr(ns, node.operands[1])
if node.op not in ["<<<", ">>>"]:
if s2 and not s1:
r1 = "$signed({1'd0, " + r1 + "})"
if s1 and not s2:
r2 = "$signed({1'd0, " + r2 + "})"
r = r1 + " " + node.op + " " + r2
s = s1 or s2
elif arity == 3:
assert node.op == "m"
r2, s2 = _printexpr(ns, node.operands[1])
r3, s3 = _printexpr(ns, node.operands[2])
if s2 and not s3:
r3 = "$signed({1'd0, " + r3 + "})"
if s3 and not s2:
r2 = "$signed({1'd0, " + r2 + "})"
r = r1 + " ? " + r2 + " : " + r3
s = s2 or s3
else:
raise TypeError
return "(" + r + ")", s
elif isinstance(node, _Slice):
# Verilog does not like us slicing non-array signals...
if isinstance(node.value, Signal) \
and len(node.value) == 1 \
and node.start == 0 and node.stop == 1:
return _printexpr(ns, node.value)
if node.start + 1 == node.stop:
sr = "[" + str(node.start) + "]"
else:
sr = "[" + str(node.stop-1) + ":" + str(node.start) + "]"
r, s = _printexpr(ns, node.value)
return r + sr, s
elif isinstance(node, Cat):
l = [_printexpr(ns, v)[0] for v in reversed(node.l)]
return "{" + ", ".join(l) + "}", False
elif isinstance(node, Replicate):
return "{" + str(node.n) + "{" + _printexpr(ns, node.v)[0] + "}}", False
else:
raise TypeError("Expression of unrecognized type: '{}'".format(type(node).__name__))
(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3)
def _printnode(ns, at, level, node):
if node is None:
return ""
elif isinstance(node, _Assign):
if at == _AT_BLOCKING:
assignment = " = "
elif at == _AT_NONBLOCKING:
assignment = " <= "
elif is_variable(node.l):
assignment = " = "
else:
assignment = " <= "
return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n"
elif isinstance(node, collections.Iterable):
return "".join(list(map(partial(_printnode, ns, at, level), node)))
elif isinstance(node, If):
r = "\t"*level + "if (" + _printexpr(ns, node.cond)[0] + ") begin\n"
r += _printnode(ns, at, level + 1, node.t)
if node.f:
r += "\t"*level + "end else begin\n"
r += _printnode(ns, at, level + 1, node.f)
r += "\t"*level + "end\n"
return r
elif isinstance(node, Case):
if node.cases:
r = "\t"*level + "case (" + _printexpr(ns, node.test)[0] + ")\n"
css = [(k, v) for k, v in node.cases.items() if isinstance(k, Constant)]
css = sorted(css, key=lambda x: x[0].value)
for choice, statements in css:
r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n"
r += _printnode(ns, at, level + 2, statements)
r += "\t"*(level + 1) + "end\n"
if "default" in node.cases:
r += "\t"*(level + 1) + "default: begin\n"
r += _printnode(ns, at, level + 2, node.cases["default"])
r += "\t"*(level + 1) + "end\n"
r += "\t"*level + "endcase\n"
return r
else:
return ""
else:
raise TypeError("Node of unrecognized type: "+str(type(node)))
def _list_comb_wires(f):
r = set()
groups = group_by_targets(f.comb)
for g in groups:
if len(g[1]) == 1 and isinstance(g[1][0], _Assign):
r |= g[0]
return r
def _printheader(f, ios, name, ns,
reg_initialization):
sigs = list_signals(f) | list_special_ios(f, True, True, True)
special_outs = list_special_ios(f, False, True, True)
inouts = list_special_ios(f, False, False, True)
targets = list_targets(f) | special_outs
wires = _list_comb_wires(f) | special_outs
r = "module " + name + "(\n"
firstp = True
for sig in sorted(ios, key=lambda x: x.duid):
if not firstp:
r += ",\n"
firstp = False
if sig in inouts:
r += "\tinout " + _printsig(ns, sig)
elif sig in targets:
if sig in wires:
r += "\toutput " + _printsig(ns, sig)
else:
r += "\toutput reg " + _printsig(ns, sig)
else:
r += "\tinput " + _printsig(ns, sig)
r += "\n);\n\n"
for sig in sorted(sigs - ios, key=lambda x: x.duid):
if sig in wires:
r += "wire " + _printsig(ns, sig) + ";\n"
else:
if reg_initialization:
r += "reg " + _printsig(ns, sig) + " = " + _printexpr(ns, sig.reset)[0] + ";\n"
else:
r += "reg " + _printsig(ns, sig) + ";\n"
r += "\n"
return r
def _printcomb(f, ns,
display_run,
dummy_signal,
blocking_assign):
r = ""
if f.comb:
if dummy_signal:
# Generate a dummy event to get the simulator
# to run the combinatorial process once at the beginning.
syn_off = "// synthesis translate_off\n"
syn_on = "// synthesis translate_on\n"
dummy_s = Signal(name_override="dummy_s")
r += syn_off
r += "reg " + _printsig(ns, dummy_s) + ";\n"
r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
r += syn_on
groups = group_by_targets(f.comb)
for n, g in enumerate(groups):
if len(g[1]) == 1 and isinstance(g[1][0], _Assign):
r += "assign " + _printnode(ns, _AT_BLOCKING, 0, g[1][0])
else:
if dummy_signal:
dummy_d = Signal(name_override="dummy_d")
r += "\n" + syn_off
r += "reg " + _printsig(ns, dummy_d) + ";\n"
r += syn_on
r += "always @(*) begin\n"
if display_run:
r += "\t$display(\"Running comb block #" + str(n) + "\");\n"
if blocking_assign:
for t in g[0]:
r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
r += _printnode(ns, _AT_BLOCKING, 1, g[1])
else:
for t in g[0]:
r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
r += _printnode(ns, _AT_NONBLOCKING, 1, g[1])
if dummy_signal:
r += syn_off
r += "\t" + ns.get_name(dummy_d) + " <= " + ns.get_name(dummy_s) + ";\n"
r += syn_on
r += "end\n"
r += "\n"
return r
def _printsync(f, ns):
r = ""
for k, v in sorted(f.sync.items(), key=itemgetter(0)):
r += "always @(posedge " + ns.get_name(f.clock_domains[k].clk) + ") begin\n"
r += _printnode(ns, _AT_SIGNAL, 1, v)
r += "end\n\n"
return r
def _call_special_classmethod(overrides, obj, method, *args, **kwargs):
cl = obj.__class__
if cl in overrides:
cl = overrides[cl]
if hasattr(cl, method):
return getattr(cl, method)(obj, *args, **kwargs)
else:
return None
def _lower_specials_step(overrides, specials):
f = _Fragment()
lowered_specials = set()
for special in sorted(specials, key=lambda x: x.duid):
impl = _call_special_classmethod(overrides, special, "lower")
if impl is not None:
f += impl.get_fragment()
lowered_specials.add(special)
return f, lowered_specials
def _can_lower(overrides, specials):
for special in specials:
cl = special.__class__
if cl in overrides:
cl = overrides[cl]
if hasattr(cl, "lower"):
return True
return False
def _lower_specials(overrides, specials):
f, lowered_specials = _lower_specials_step(overrides, specials)
while _can_lower(overrides, f.specials):
f2, lowered_specials2 = _lower_specials_step(overrides, f.specials)
f += f2
lowered_specials |= lowered_specials2
f.specials -= lowered_specials2
return f, lowered_specials
def _printspecials(overrides, specials, ns, add_data_file):
r = ""
for special in sorted(specials, key=lambda x: x.duid):
pr = _call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file)
if pr is None:
raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog")
r += pr
return r
def convert(f, ios=None, name="top",
special_overrides=dict(),
create_clock_domains=True,
display_run=False, asic_syntax=False):
r = ConvOutput()
if not isinstance(f, _Fragment):
f = f.get_fragment()
if ios is None:
ios = set()
for cd_name in sorted(list_clock_domains(f)):
try:
f.clock_domains[cd_name]
except KeyError:
if create_clock_domains:
cd = ClockDomain(cd_name)
f.clock_domains.append(cd)
ios |= {cd.clk, cd.rst}
else:
raise KeyError("Unresolved clock domain: '"+cd_name+"'")
f = lower_complex_slices(f)
insert_resets(f)
f = lower_basics(f)
fs, lowered_specials = _lower_specials(special_overrides, f.specials)
f += lower_basics(fs)
ns = build_namespace(list_signals(f) \
| list_special_ios(f, True, True, True) \
| ios, _reserved_keywords)
ns.clock_domains = f.clock_domains
r.ns = ns
src = "/* Machine-generated using LiteX */\n"
src += _printheader(f, ios, name, ns,
reg_initialization=not asic_syntax)
src += _printcomb(f, ns,
display_run=display_run,
dummy_signal=not asic_syntax,
blocking_assign=asic_syntax)
src += _printsync(f, ns)
src += _printspecials(special_overrides, f.specials - lowered_specials, ns, r.add_data_file)
src += "endmodule\n"
r.set_main_source(src)
return r

View file

@ -1,202 +0,0 @@
from copy import copy
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy,
_Fragment)
class NodeVisitor:
def visit(self, node):
if isinstance(node, Constant):
self.visit_Constant(node)
elif isinstance(node, Signal):
self.visit_Signal(node)
elif isinstance(node, ClockSignal):
self.visit_ClockSignal(node)
elif isinstance(node, ResetSignal):
self.visit_ResetSignal(node)
elif isinstance(node, _Operator):
self.visit_Operator(node)
elif isinstance(node, _Slice):
self.visit_Slice(node)
elif isinstance(node, Cat):
self.visit_Cat(node)
elif isinstance(node, Replicate):
self.visit_Replicate(node)
elif isinstance(node, _Assign):
self.visit_Assign(node)
elif isinstance(node, If):
self.visit_If(node)
elif isinstance(node, Case):
self.visit_Case(node)
elif isinstance(node, _Fragment):
self.visit_Fragment(node)
elif isinstance(node, (list, tuple)):
self.visit_statements(node)
elif isinstance(node, dict):
self.visit_clock_domains(node)
elif isinstance(node, _ArrayProxy):
self.visit_ArrayProxy(node)
elif node is not None:
self.visit_unknown(node)
def visit_Constant(self, node):
pass
def visit_Signal(self, node):
pass
def visit_ClockSignal(self, node):
pass
def visit_ResetSignal(self, node):
pass
def visit_Operator(self, node):
for o in node.operands:
self.visit(o)
def visit_Slice(self, node):
self.visit(node.value)
def visit_Cat(self, node):
for e in node.l:
self.visit(e)
def visit_Replicate(self, node):
self.visit(node.v)
def visit_Assign(self, node):
self.visit(node.l)
self.visit(node.r)
def visit_If(self, node):
self.visit(node.cond)
self.visit(node.t)
self.visit(node.f)
def visit_Case(self, node):
self.visit(node.test)
for v, statements in node.cases.items():
self.visit(statements)
def visit_Fragment(self, node):
self.visit(node.comb)
self.visit(node.sync)
def visit_statements(self, node):
for statement in node:
self.visit(statement)
def visit_clock_domains(self, node):
for clockname, statements in node.items():
self.visit(statements)
def visit_ArrayProxy(self, node):
for choice in node.choices:
self.visit(choice)
self.visit(node.key)
def visit_unknown(self, node):
pass
# Default methods always copy the node, except for:
# - Signals, ClockSignals and ResetSignals
# - Unknown objects
# - All fragment fields except comb and sync
# In those cases, the original node is returned unchanged.
class NodeTransformer:
def visit(self, node):
if isinstance(node, Constant):
return self.visit_Constant(node)
elif isinstance(node, Signal):
return self.visit_Signal(node)
elif isinstance(node, ClockSignal):
return self.visit_ClockSignal(node)
elif isinstance(node, ResetSignal):
return self.visit_ResetSignal(node)
elif isinstance(node, _Operator):
return self.visit_Operator(node)
elif isinstance(node, _Slice):
return self.visit_Slice(node)
elif isinstance(node, Cat):
return self.visit_Cat(node)
elif isinstance(node, Replicate):
return self.visit_Replicate(node)
elif isinstance(node, _Assign):
return self.visit_Assign(node)
elif isinstance(node, If):
return self.visit_If(node)
elif isinstance(node, Case):
return self.visit_Case(node)
elif isinstance(node, _Fragment):
return self.visit_Fragment(node)
elif isinstance(node, (list, tuple)):
return self.visit_statements(node)
elif isinstance(node, dict):
return self.visit_clock_domains(node)
elif isinstance(node, _ArrayProxy):
return self.visit_ArrayProxy(node)
elif node is not None:
return self.visit_unknown(node)
else:
return None
def visit_Constant(self, node):
return node
def visit_Signal(self, node):
return node
def visit_ClockSignal(self, node):
return node
def visit_ResetSignal(self, node):
return node
def visit_Operator(self, node):
return _Operator(node.op, [self.visit(o) for o in node.operands])
def visit_Slice(self, node):
return _Slice(self.visit(node.value), node.start, node.stop)
def visit_Cat(self, node):
return Cat(*[self.visit(e) for e in node.l])
def visit_Replicate(self, node):
return Replicate(self.visit(node.v), node.n)
def visit_Assign(self, node):
return _Assign(self.visit(node.l), self.visit(node.r))
def visit_If(self, node):
r = If(self.visit(node.cond))
r.t = self.visit(node.t)
r.f = self.visit(node.f)
return r
def visit_Case(self, node):
cases = dict((v, self.visit(statements)) for v, statements in node.cases.items())
r = Case(self.visit(node.test), cases)
return r
def visit_Fragment(self, node):
r = copy(node)
r.comb = self.visit(node.comb)
r.sync = self.visit(node.sync)
return r
# NOTE: this will always return a list, even if node is a tuple
def visit_statements(self, node):
return [self.visit(statement) for statement in node]
def visit_clock_domains(self, node):
return dict((clockname, self.visit(statements)) for clockname, statements in node.items())
def visit_ArrayProxy(self, node):
return _ArrayProxy([self.visit(choice) for choice in node.choices],
self.visit(node.key))
def visit_unknown(self, node):
return node

View file

@ -1,141 +0,0 @@
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Special
from litex.gen.fhdl.bitcontainer import value_bits_sign
from litex.gen.genlib.misc import WaitTimer
class NoRetiming(Special):
def __init__(self, reg):
Special.__init__(self)
self.reg = reg
# do nothing
@staticmethod
def lower(dr):
return Module()
class MultiRegImpl(Module):
def __init__(self, i, o, odomain, n):
self.i = i
self.o = o
self.odomain = odomain
w, signed = value_bits_sign(self.i)
self.regs = [Signal((w, signed)) for i in range(n)]
###
src = self.i
for reg in self.regs:
sd = getattr(self.sync, self.odomain)
sd += reg.eq(src)
src = reg
self.comb += self.o.eq(src)
self.specials += [NoRetiming(reg) for reg in self.regs]
class MultiReg(Special):
def __init__(self, i, o, odomain="sys", n=2):
Special.__init__(self)
self.i = wrap(i)
self.o = wrap(o)
self.odomain = odomain
self.n = n
def iter_expressions(self):
yield self, "i", SPECIAL_INPUT
yield self, "o", SPECIAL_OUTPUT
def rename_clock_domain(self, old, new):
Special.rename_clock_domain(self, old, new)
if self.odomain == old:
self.odomain = new
def list_clock_domains(self):
r = Special.list_clock_domains(self)
r.add(self.odomain)
return r
@staticmethod
def lower(dr):
return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
class PulseSynchronizer(Module):
def __init__(self, idomain, odomain):
self.i = Signal()
self.o = Signal()
###
toggle_i = Signal()
toggle_o = Signal()
toggle_o_r = Signal()
sync_i = getattr(self.sync, idomain)
sync_o = getattr(self.sync, odomain)
sync_i += If(self.i, toggle_i.eq(~toggle_i))
self.specials += MultiReg(toggle_i, toggle_o, odomain)
sync_o += toggle_o_r.eq(toggle_o)
self.comb += self.o.eq(toggle_o ^ toggle_o_r)
class BusSynchronizer(Module):
"""Clock domain transfer of several bits at once.
Ensures that all the bits form a single word that was present
synchronously in the input clock domain (unlike direct use of
``MultiReg``)."""
def __init__(self, width, idomain, odomain, timeout=128):
self.i = Signal(width)
self.o = Signal(width)
if width == 1:
self.specials += MultiReg(self.i, self.o, odomain)
else:
sync_i = getattr(self.sync, idomain)
sync_o = getattr(self.sync, odomain)
starter = Signal(reset=1)
sync_i += starter.eq(0)
self.submodules._ping = PulseSynchronizer(idomain, odomain)
self.submodules._pong = PulseSynchronizer(odomain, idomain)
self.submodules._timeout = WaitTimer(timeout)
self.comb += [
self._timeout.wait.eq(~self._ping.i),
self._ping.i.eq(starter | self._pong.o | self._timeout.done),
self._pong.i.eq(self._ping.i)
]
ibuffer = Signal(width)
obuffer = Signal(width)
sync_i += If(self._pong.o, ibuffer.eq(self.i))
self.specials += MultiReg(ibuffer, obuffer, odomain)
sync_o += If(self._ping.o, self.o.eq(obuffer))
class GrayCounter(Module):
def __init__(self, width):
self.ce = Signal()
self.q = Signal(width)
self.q_next = Signal(width)
self.q_binary = Signal(width)
self.q_next_binary = Signal(width)
###
self.comb += [
If(self.ce,
self.q_next_binary.eq(self.q_binary + 1)
).Else(
self.q_next_binary.eq(self.q_binary)
),
self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
]
self.sync += [
self.q_binary.eq(self.q_next_binary),
self.q.eq(self.q_next)
]

View file

@ -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<<j, self.o.eq(j)) for j in range(width))
act["default"] = self.n.eq(1)
self.comb += Case(self.i, act)
class PriorityEncoder(Module):
"""Priority encode requests to binary
If `n` is low, the `o` th bit in `i` is asserted and the bits below
`o` are unasserted, else `o == 0`. The LSB has priority.
Parameters
----------
width : int
Bit width of the input
Attributes
----------
i : Signal(width), in
Input requests
o : Signal(max=width), out
Encoded binary
n : Signal(1), out
Invalid, no input bits are asserted
"""
def __init__(self, width):
self.i = Signal(width) # one-hot, lsb has priority
self.o = Signal(max=max(2, width)) # binary
self.n = Signal() # none
for j in range(width)[::-1]: # last has priority
self.comb += If(self.i[j], self.o.eq(j))
self.comb += self.n.eq(self.i == 0)
class Decoder(Module):
"""Decode binary to one-hot
If `n` is low, the `i` th bit in `o` is asserted, the others are
not, else `o == 0`.
Parameters
----------
width : int
Bit width of the output
Attributes
----------
i : Signal(max=width), in
Input binary
o : Signal(width), out
Decoded one-hot
n : Signal(1), in
Invalid, no output bits are to be asserted
"""
def __init__(self, width):
self.i = Signal(max=max(2, width)) # binary
self.n = Signal() # none/invalid
self.o = Signal(width) # one-hot
act = dict((j, self.o.eq(1<<j)) for j in range(width))
self.comb += Case(self.i, act)
self.comb += If(self.n, self.o.eq(0))
class PriorityDecoder(Decoder):
pass # same

View file

@ -1,40 +0,0 @@
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.module import Module
class Divider(Module):
def __init__(self, w):
self.start_i = Signal()
self.dividend_i = Signal(w)
self.divisor_i = Signal(w)
self.ready_o = Signal()
self.quotient_o = Signal(w)
self.remainder_o = Signal(w)
###
qr = Signal(2*w)
counter = Signal(max=w+1)
divisor_r = Signal(w)
diff = Signal(w+1)
self.comb += [
self.quotient_o.eq(qr[:w]),
self.remainder_o.eq(qr[w:]),
self.ready_o.eq(counter == 0),
diff.eq(qr[w-1:] - divisor_r)
]
self.sync += [
If(self.start_i,
counter.eq(w),
qr.eq(self.dividend_i),
divisor_r.eq(self.divisor_i)
).Elif(~self.ready_o,
If(diff[w],
qr.eq(Cat(0, qr[:2*w-1]))
).Else(
qr.eq(Cat(1, qr[:w-1], diff[:w]))
),
counter.eq(counter - 1)
)
]

View file

@ -1,214 +0,0 @@
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Memory
from litex.gen.fhdl.bitcontainer import log2_int
from litex.gen.fhdl.decorators import ClockDomainsRenamer
from litex.gen.genlib.cdc import NoRetiming, MultiReg, GrayCounter
def _inc(signal, modulo):
if modulo == 2**len(signal):
return signal.eq(signal + 1)
else:
return If(signal == (modulo - 1),
signal.eq(0)
).Else(
signal.eq(signal + 1)
)
class _FIFOInterface:
"""
Data written to the input interface (`din`, `we`, `writable`) is
buffered and can be read at the output interface (`dout`, `re`,
`readable`). The data entry written first to the input
also appears first on the output.
Parameters
----------
width : int
Bit width for the data.
depth : int
Depth of the FIFO.
Attributes
----------
din : in, width
Input data
writable : out
There is space in the FIFO and `we` can be asserted to load new data.
we : in
Write enable signal to latch `din` into the FIFO. Does nothing if
`writable` is not asserted.
dout : out, width
Output data. Only valid if `readable` is asserted.
readable : out
Output data `dout` valid, FIFO not empty.
re : in
Acknowledge `dout`. If asserted, the next entry will be
available on the next cycle (if `readable` is high then).
"""
def __init__(self, width, depth):
self.we = Signal()
self.writable = Signal() # not full
self.re = Signal()
self.readable = Signal() # not empty
self.din = Signal(width)
self.dout = Signal(width)
self.width = width
class SyncFIFO(Module, _FIFOInterface):
"""Synchronous FIFO (first in, first out)
Read and write interfaces are accessed from the same clock domain.
If different clock domains are needed, use :class:`AsyncFIFO`.
{interface}
level : out
Number of unread entries.
replace : in
Replaces the last entry written into the FIFO with `din`. Does nothing
if that entry has already been read (i.e. the FIFO is empty).
Assert in conjunction with `we`.
"""
__doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
def __init__(self, width, depth, fwft=True):
_FIFOInterface.__init__(self, width, depth)
self.level = Signal(max=depth+1)
self.replace = Signal()
###
produce = Signal(max=depth)
consume = Signal(max=depth)
storage = Memory(self.width, depth)
self.specials += storage
wrport = storage.get_port(write_capable=True)
self.specials += wrport
self.comb += [
If(self.replace,
wrport.adr.eq(produce-1)
).Else(
wrport.adr.eq(produce)
),
wrport.dat_w.eq(self.din),
wrport.we.eq(self.we & (self.writable | self.replace))
]
self.sync += If(self.we & self.writable & ~self.replace,
_inc(produce, depth))
do_read = Signal()
self.comb += do_read.eq(self.readable & self.re)
rdport = storage.get_port(async_read=fwft, has_re=not fwft)
self.specials += rdport
self.comb += [
rdport.adr.eq(consume),
self.dout.eq(rdport.dat_r)
]
if not fwft:
self.comb += rdport.re.eq(do_read)
self.sync += If(do_read, _inc(consume, depth))
self.sync += \
If(self.we & self.writable & ~self.replace,
If(~do_read, self.level.eq(self.level + 1))
).Elif(do_read,
self.level.eq(self.level - 1)
)
self.comb += [
self.writable.eq(self.level != depth),
self.readable.eq(self.level != 0)
]
class SyncFIFOBuffered(Module, _FIFOInterface):
def __init__(self, width, depth):
_FIFOInterface.__init__(self, width, depth)
self.submodules.fifo = fifo = SyncFIFO(width, depth, False)
self.writable = fifo.writable
self.din = fifo.din
self.we = fifo.we
self.dout = fifo.dout
self.level = Signal(max=depth+2)
###
self.comb += fifo.re.eq(fifo.readable & (~self.readable | self.re))
self.sync += \
If(fifo.re,
self.readable.eq(1),
).Elif(self.re,
self.readable.eq(0),
)
self.comb += self.level.eq(fifo.level + self.readable)
class AsyncFIFO(Module, _FIFOInterface):
"""Asynchronous FIFO (first in, first out)
Read and write interfaces are accessed from different clock domains,
named `read` and `write`. Use `ClockDomainsRenamer` to rename to
other names.
{interface}
"""
__doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
def __init__(self, width, depth):
_FIFOInterface.__init__(self, width, depth)
###
depth_bits = log2_int(depth, True)
produce = ClockDomainsRenamer("write")(GrayCounter(depth_bits+1))
consume = ClockDomainsRenamer("read")(GrayCounter(depth_bits+1))
self.submodules += produce, consume
self.comb += [
produce.ce.eq(self.writable & self.we),
consume.ce.eq(self.readable & self.re)
]
produce_rdomain = Signal(depth_bits+1)
self.specials += [
NoRetiming(produce.q),
MultiReg(produce.q, produce_rdomain, "read")
]
consume_wdomain = Signal(depth_bits+1)
self.specials += [
NoRetiming(consume.q),
MultiReg(consume.q, consume_wdomain, "write")
]
if depth_bits == 1:
self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1])
| (produce.q[-2] == consume_wdomain[-2]))
else:
self.comb += [
self.writable.eq((produce.q[-1] == consume_wdomain[-1])
| (produce.q[-2] == consume_wdomain[-2])
| (produce.q[:-2] != consume_wdomain[:-2]))
]
self.comb += self.readable.eq(consume.q != produce_rdomain)
storage = Memory(self.width, depth)
self.specials += storage
wrport = storage.get_port(write_capable=True, clock_domain="write")
self.specials += wrport
self.comb += [
wrport.adr.eq(produce.q_binary[:-1]),
wrport.dat_w.eq(self.din),
wrport.we.eq(produce.ce)
]
rdport = storage.get_port(clock_domain="read")
self.specials += rdport
self.comb += [
rdport.adr.eq(consume.q_next_binary[:-1]),
self.dout.eq(rdport.dat_r)
]

View file

@ -1,176 +0,0 @@
from collections import OrderedDict
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import _Statement, _Slice, _ArrayProxy
from litex.gen.fhdl.module import Module, FinalizeError
from litex.gen.fhdl.visit import NodeTransformer
from litex.gen.fhdl.bitcontainer import value_bits_sign
__all__ = ["AnonymousState", "NextState", "NextValue", "FSM"]
class AnonymousState:
pass
# do not use namedtuple here as it inherits tuple
# and the latter is used elsewhere in FHDL
class NextState(_Statement):
def __init__(self, state):
self.state = state
class NextValue(_Statement):
def __init__(self, target, value):
self.target = target
self.value = value
def _target_eq(a, b):
if type(a) != type(b):
return False
ty = type(a)
if ty == Constant:
return a.value == b.value
elif ty == Signal:
return a is b
elif ty == Cat:
return all(_target_eq(x, y) for x, y in zip(a.l, b.l))
elif ty == _Slice:
return (_target_eq(a.value, b.value)
and a.start == b.start
and a.end == b.end)
elif ty == _ArrayProxy:
return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices))
and _target_eq(a.key, b.key))
else:
raise ValueError("NextValue cannot be used with target type '{}'"
.format(ty))
class _LowerNext(NodeTransformer):
def __init__(self, next_state_signal, encoding, aliases):
self.next_state_signal = next_state_signal
self.encoding = encoding
self.aliases = aliases
# (target, next_value_ce, next_value)
self.registers = []
def _get_register_control(self, target):
for x in self.registers:
if _target_eq(target, x[0]):
return x[1], x[2]
raise KeyError
def visit_unknown(self, node):
if isinstance(node, NextState):
try:
actual_state = self.aliases[node.state]
except KeyError:
actual_state = node.state
return self.next_state_signal.eq(self.encoding[actual_state])
elif isinstance(node, NextValue):
try:
next_value_ce, next_value = self._get_register_control(node.target)
except KeyError:
related = node.target if isinstance(node.target, Signal) else None
next_value = Signal(bits_sign=value_bits_sign(node.target), related=related)
next_value_ce = Signal(related=related)
self.registers.append((node.target, next_value_ce, next_value))
return next_value.eq(node.value), next_value_ce.eq(1)
else:
return node
class FSM(Module):
def __init__(self, reset_state=None):
self.actions = OrderedDict()
self.state_aliases = dict()
self.reset_state = reset_state
self.before_entering_signals = OrderedDict()
self.before_leaving_signals = OrderedDict()
self.after_entering_signals = OrderedDict()
self.after_leaving_signals = OrderedDict()
def act(self, state, *statements):
if self.finalized:
raise FinalizeError
if self.reset_state is None:
self.reset_state = state
if state not in self.actions:
self.actions[state] = []
self.actions[state] += statements
def delayed_enter(self, name, target, delay):
if self.finalized:
raise FinalizeError
if delay > 0:
state = name
for i in range(delay):
if i == delay - 1:
next_state = target
else:
next_state = AnonymousState()
self.act(state, NextState(next_state))
state = next_state
else:
self.state_aliases[name] = target
def ongoing(self, state):
is_ongoing = Signal()
self.act(state, is_ongoing.eq(1))
return is_ongoing
def _get_signal(self, d, state):
if state not in self.actions:
self.actions[state] = []
try:
return d[state]
except KeyError:
is_el = Signal()
d[state] = is_el
return is_el
def before_entering(self, state):
return self._get_signal(self.before_entering_signals, state)
def before_leaving(self, state):
return self._get_signal(self.before_leaving_signals, state)
def after_entering(self, state):
signal = self._get_signal(self.after_entering_signals, state)
self.sync += signal.eq(self.before_entering(state))
return signal
def after_leaving(self, state):
signal = self._get_signal(self.after_leaving_signals, state)
self.sync += signal.eq(self.before_leaving(state))
return signal
def do_finalize(self):
nstates = len(self.actions)
self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys()))
self.state = Signal(max=nstates, reset=self.encoding[self.reset_state])
self.next_state = Signal(max=nstates)
ln = _LowerNext(self.next_state, self.encoding, self.state_aliases)
cases = dict((self.encoding[k], ln.visit(v)) for k, v in self.actions.items() if v)
self.comb += [
self.next_state.eq(self.state),
Case(self.state, cases).makedefault(self.encoding[self.reset_state])
]
self.sync += self.state.eq(self.next_state)
for register, next_value_ce, next_value in ln.registers:
self.sync += If(next_value_ce, register.eq(next_value))
# drive entering/leaving signals
for state, signal in self.before_leaving_signals.items():
encoded = self.encoding[state]
self.comb += signal.eq((self.state == encoded) & ~(self.next_state == encoded))
if self.reset_state in self.after_entering_signals:
self.after_entering_signals[self.reset_state].reset = 1
for state, signal in self.before_entering_signals.items():
encoded = self.encoding[state]
self.comb += signal.eq(~(self.state == encoded) & (self.next_state == encoded))

View file

@ -1,96 +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):
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")

View file

@ -1,88 +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))

View file

@ -1,179 +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
if(len(f) == 3):
fname, fsize, fdirection = f
else:
fname, fsize = f
finst = Signal(fsize, name=prefix + fname)
elif isinstance(f[1], list): # case 3
fname, fsublayout = f
finst = Record(fsublayout, prefix + fname)
else:
raise TypeError
setattr(self, fname, finst)
def eq(self, other):
return [getattr(self, f[0]).eq(getattr(other, f[0]))
for f in self.layout if hasattr(other, f[0])]
def iter_flat(self):
for f in self.layout:
e = getattr(self, f[0])
if isinstance(e, Signal):
if len(f) == 3:
yield e, f[2]
else:
yield e, DIR_NONE
elif isinstance(e, Record):
yield from e.iter_flat()
else:
raise TypeError
def flatten(self):
return [signal for signal, direction in self.iter_flat()]
def raw_bits(self):
return Cat(*self.flatten())
def connect(self, *slaves, leave_out=set()):
if isinstance(leave_out, str):
leave_out = {leave_out}
r = []
for f in self.layout:
field = f[0]
if field not in leave_out:
self_e = getattr(self, field)
if isinstance(self_e, Signal):
direction = f[2]
if direction == DIR_M_TO_S:
r += [getattr(slave, field).eq(self_e) for slave in slaves]
elif direction == DIR_S_TO_M:
r.append(self_e.eq(reduce(or_, [getattr(slave, field) for slave in slaves])))
else:
raise TypeError
else:
for slave in slaves:
r += self_e.connect(getattr(slave, field), leave_out=leave_out)
return r
def connect_flat(self, *slaves):
r = []
iter_slaves = [slave.iter_flat() for slave in slaves]
for m_signal, m_direction in self.iter_flat():
if m_direction == DIR_M_TO_S:
for iter_slave in iter_slaves:
s_signal, s_direction = next(iter_slave)
assert(s_direction == DIR_M_TO_S)
r.append(s_signal.eq(m_signal))
elif m_direction == DIR_S_TO_M:
s_signals = []
for iter_slave in iter_slaves:
s_signal, s_direction = next(iter_slave)
assert(s_direction == DIR_S_TO_M)
s_signals.append(s_signal)
r.append(m_signal.eq(reduce(or_, s_signals)))
else:
raise TypeError
return r
def __len__(self):
return layout_len(self.layout)
def __repr__(self):
return "<Record " + ":".join(f[0] for f in self.layout) + " at " + hex(id(self)) + ">"

View file

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

View file

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

View file

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

View file

@ -1 +0,0 @@
from litex.gen.sim.core import Simulator, run_simulation

View file

@ -1,335 +0,0 @@
import operator
import collections
import inspect
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import (_Value, _Statement,
_Operator, _Slice, _ArrayProxy,
_Assign, _Fragment)
from litex.gen.fhdl.bitcontainer import value_bits_sign
from litex.gen.fhdl.tools import list_signals, list_targets, insert_resets
from litex.gen.fhdl.simplify import MemoryToArray
from litex.gen.fhdl.specials import _MemoryLocation
from litex.gen.sim.vcd import VCDWriter, DummyVCDWriter
class ClockState:
def __init__(self, high, half_period, time_before_trans):
self.high = high
self.half_period = half_period
self.time_before_trans = time_before_trans
class TimeManager:
def __init__(self, description):
self.clocks = dict()
for k, period_phase in description.items():
if isinstance(period_phase, tuple):
period, phase = period_phase
else:
period = period_phase
phase = 0
half_period = period//2
if phase >= half_period:
phase -= half_period
high = True
else:
high = False
self.clocks[k] = ClockState(high, half_period, half_period - phase)
def tick(self):
rising = set()
falling = set()
dt = min(cs.time_before_trans for cs in self.clocks.values())
for k, cs in self.clocks.items():
if cs.time_before_trans == dt:
cs.high = not cs.high
if cs.high:
rising.add(k)
else:
falling.add(k)
cs.time_before_trans -= dt
if not cs.time_before_trans:
cs.time_before_trans += cs.half_period
return dt, rising, falling
str2op = {
"~": operator.invert,
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
">>>": operator.rshift,
"<<<": operator.lshift,
"&": operator.and_,
"^": operator.xor,
"|": operator.or_,
"<": operator.lt,
"<=": operator.le,
"==": operator.eq,
"!=": operator.ne,
">": operator.gt,
">=": operator.ge,
}
def _truncate(value, nbits, signed):
value = value & (2**nbits - 1)
if signed and (value & 2**(nbits - 1)):
value -= 2**nbits
return value
class Evaluator:
def __init__(self, clock_domains, replaced_memories):
self.clock_domains = clock_domains
self.replaced_memories = replaced_memories
self.signal_values = dict()
self.modifications = dict()
def commit(self):
r = set()
for k, v in self.modifications.items():
if k not in self.signal_values or self.signal_values[k] != v:
self.signal_values[k] = v
r.add(k)
self.modifications.clear()
return r
def eval(self, node, postcommit=False):
if isinstance(node, Constant):
return node.value
elif isinstance(node, Signal):
if postcommit:
try:
return self.modifications[node]
except KeyError:
pass
try:
return self.signal_values[node]
except KeyError:
return node.reset.value
elif isinstance(node, _Operator):
operands = [self.eval(o, postcommit) for o in node.operands]
if node.op == "-":
if len(operands) == 1:
return -operands[0]
else:
return operands[0] - operands[1]
elif node.op == "m":
return operands[1] if operands[0] else operands[2]
else:
return str2op[node.op](*operands)
elif isinstance(node, _Slice):
v = self.eval(node.value, postcommit)
idx = range(node.start, node.stop)
return sum(((v >> i) & 1) << j for j, i in enumerate(idx))
elif isinstance(node, Cat):
shift = 0
r = 0
for element in node.l:
nbits = len(element)
# make value always positive
r |= (self.eval(element, postcommit) & (2**nbits-1)) << shift
shift += nbits
return r
elif isinstance(node, _ArrayProxy):
return self.eval(node.choices[self.eval(node.key, postcommit)],
postcommit)
elif isinstance(node, _MemoryLocation):
array = self.replaced_memories[node.memory]
return self.eval(array[self.eval(node.index, postcommit)], postcommit)
elif isinstance(node, ClockSignal):
return self.eval(self.clock_domains[node.cd].clk, postcommit)
elif isinstance(node, ResetSignal):
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 self.eval(rst, postcommit)
else:
raise NotImplementedError
def assign(self, node, value):
if isinstance(node, Signal):
assert not node.variable
self.modifications[node] = _truncate(value,
node.nbits, node.signed)
elif isinstance(node, Cat):
for element in node.l:
nbits = len(element)
self.assign(element, value & (2**nbits-1))
value >>= nbits
elif isinstance(node, _Slice):
full_value = self.eval(node.value, True)
# clear bits assigned to by the slice
full_value &= ~((2**node.stop-1) - (2**node.start-1))
# set them to the new value
value &= 2**(node.stop - node.start)-1
full_value |= value << node.start
self.assign(node.value, full_value)
elif isinstance(node, _ArrayProxy):
self.assign(node.choices[self.eval(node.key)], value)
elif isinstance(node, _MemoryLocation):
array = self.replaced_memories[node.memory]
self.assign(array[self.eval(node.index)], value)
else:
raise NotImplementedError
def execute(self, statements):
for s in statements:
if isinstance(s, _Assign):
self.assign(s.l, self.eval(s.r))
elif isinstance(s, If):
if self.eval(s.cond) & (2**len(s.cond) - 1):
self.execute(s.t)
else:
self.execute(s.f)
elif isinstance(s, Case):
nbits, signed = value_bits_sign(s.test)
test = _truncate(self.eval(s.test), nbits, signed)
found = False
for k, v in s.cases.items():
if isinstance(k, Constant) and k.value == test:
self.execute(v)
found = True
break
if not found and "default" in s.cases:
self.execute(s.cases["default"])
elif isinstance(s, collections.Iterable):
self.execute(s)
else:
raise NotImplementedError
# TODO: instances via Iverilog/VPI
class Simulator:
def __init__(self, fragment_or_module, generators, clocks={"sys": 10}, vcd_name=None):
if isinstance(fragment_or_module, _Fragment):
self.fragment = fragment_or_module
else:
self.fragment = fragment_or_module.get_fragment()
if not isinstance(generators, dict):
generators = {"sys": generators}
self.generators = dict()
for k, v in generators.items():
if (isinstance(v, collections.Iterable)
and not inspect.isgenerator(v)):
self.generators[k] = list(v)
else:
self.generators[k] = [v]
self.time = TimeManager(clocks)
for clock in clocks.keys():
if clock not in self.fragment.clock_domains:
cd = ClockDomain(name=clock, reset_less=True)
cd.clk.reset = C(self.time.clocks[clock].high)
self.fragment.clock_domains.append(cd)
mta = MemoryToArray()
mta.transform_fragment(None, self.fragment)
insert_resets(self.fragment)
# comb signals return to their reset value if nothing assigns them
self.fragment.comb[0:0] = [s.eq(s.reset)
for s in list_targets(self.fragment.comb)]
self.evaluator = Evaluator(self.fragment.clock_domains,
mta.replacements)
if vcd_name is None:
self.vcd = DummyVCDWriter()
else:
signals = list_signals(self.fragment)
for cd in self.fragment.clock_domains:
signals.add(cd.clk)
if cd.rst is not None:
signals.add(cd.rst)
for memory_array in mta.replacements.values():
signals |= set(memory_array)
signals = sorted(signals, key=lambda x: x.duid)
self.vcd = VCDWriter(vcd_name, signals)
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def close(self):
self.vcd.close()
def _commit_and_comb_propagate(self):
# TODO: optimize
all_modified = set()
modified = self.evaluator.commit()
all_modified |= modified
while modified:
self.evaluator.execute(self.fragment.comb)
modified = self.evaluator.commit()
all_modified |= modified
for signal in all_modified:
self.vcd.set(signal, self.evaluator.signal_values[signal])
def _evalexec_nested_lists(self, x):
if isinstance(x, list):
return [self._evalexec_nested_lists(e) for e in x]
elif isinstance(x, _Value):
return self.evaluator.eval(x)
elif isinstance(x, _Statement):
self.evaluator.execute([x])
return None
else:
raise ValueError
def _process_generators(self, cd):
exhausted = []
for generator in self.generators[cd]:
reply = None
while True:
try:
request = generator.send(reply)
if request is None:
break # next cycle
else:
reply = self._evalexec_nested_lists(request)
except StopIteration:
exhausted.append(generator)
break
for generator in exhausted:
self.generators[cd].remove(generator)
def _continue_simulation(self):
# TODO: passive generators
return any(self.generators.values())
def run(self):
self.evaluator.execute(self.fragment.comb)
self._commit_and_comb_propagate()
while True:
dt, rising, falling = self.time.tick()
self.vcd.delay(dt)
for cd in rising:
self.evaluator.assign(self.fragment.clock_domains[cd].clk, 1)
if cd in self.fragment.sync:
self.evaluator.execute(self.fragment.sync[cd])
if cd in self.generators:
self._process_generators(cd)
for cd in falling:
self.evaluator.assign(self.fragment.clock_domains[cd].clk, 0)
self._commit_and_comb_propagate()
if not self._continue_simulation():
break
def run_simulation(*args, **kwargs):
with Simulator(*args, **kwargs) as s:
s.run()

View file

@ -1,75 +0,0 @@
from itertools import count
from litex.gen.fhdl.namer import build_namespace
def vcd_codes():
codechars = [chr(i) for i in range(33, 127)]
for n in count():
q, r = divmod(n, len(codechars))
code = codechars[r]
while q > 0:
q, r = divmod(q, len(codechars))
code = codechars[r] + code
yield code
class VCDWriter:
def __init__(self, filename, signals):
self.fo = open(filename, "w")
self.codes = dict()
self.signal_values = dict()
self.t = 0
try:
ns = build_namespace(signals)
codes = vcd_codes()
for signal in signals:
name = ns.get_name(signal)
code = next(codes)
self.codes[signal] = code
self.fo.write("$var wire {len} {code} {name} $end\n"
.format(name=name, code=code, len=len(signal)))
self.fo.write("$dumpvars\n")
for signal in signals:
value = signal.reset.value
self._write_value(signal, value)
self.signal_values[signal] = value
self.fo.write("$end\n")
self.fo.write("#0\n")
except:
self.close()
raise
def _write_value(self, signal, value):
l = len(signal)
if value < 0:
value += 2**l
if l > 1:
fmtstr = "b{:0" + str(l) + "b} {}\n"
else:
fmtstr = "{}{}\n"
self.fo.write(fmtstr.format(value, self.codes[signal]))
def set(self, signal, value):
if self.signal_values[signal] != value:
self._write_value(signal, value)
self.signal_values[signal] = value
def delay(self, delay):
self.t += delay
self.fo.write("#{}\n".format(self.t))
def close(self):
self.fo.close()
class DummyVCDWriter:
def set(self, signal, value):
pass
def delay(self, delay):
pass
def close(self):
pass

View file

@ -1,29 +0,0 @@
from fractions import gcd
import collections
def flat_iteration(l):
for element in l:
if isinstance(element, collections.Iterable):
for element2 in flat_iteration(element):
yield element2
else:
yield element
def xdir(obj, return_values=False):
for attr in dir(obj):
if attr[:2] != "__" and attr[-2:] != "__":
if return_values:
yield attr, getattr(obj, attr)
else:
yield attr
def gcd_multiple(numbers):
l = len(numbers)
if l == 1:
return numbers[0]
else:
s = l//2
return gcd(gcd_multiple(numbers[:s]), gcd_multiple(numbers[s:]))

View file

@ -1,6 +1,6 @@
import os
from litex.gen import *
from migen import *
from litex.soc.interconnect import wishbone

View file

@ -1,6 +1,6 @@
import os
from litex.gen import *
from migen import *
from litex.soc.interconnect import wishbone

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.fsm import FSM, NextState
from migen import *
from migen.genlib.fsm import FSM, NextState
from litex.soc.interconnect import wishbone

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.misc import timeline
from migen import *
from migen.genlib.misc import timeline
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRStatus

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.cdc import MultiReg
from migen import *
from migen.genlib.cdc import MultiReg
from litex.soc.interconnect.csr import *

View file

@ -1,4 +1,4 @@
from litex.gen import *
from migen import *
class Identifier(Module):

View file

@ -1,7 +1,7 @@
from litex.gen import *
from litex.gen.genlib.roundrobin import *
from litex.gen.genlib.fsm import FSM, NextState
from litex.gen.genlib.fifo import SyncFIFO
from migen import *
from migen.genlib.roundrobin import *
from migen.genlib.fsm import FSM, NextState
from migen.genlib.fifo import SyncFIFO
from litex.soc.cores.sdram.lasmicon.multiplexer import *

View file

@ -1,4 +1,4 @@
from litex.gen import *
from migen import *
from litex.soc.interconnect import dfi, lasmi_bus
from litex.soc.cores.sdram.lasmicon.refresher import *

View file

@ -1,9 +1,9 @@
from functools import reduce
from operator import or_, and_
from litex.gen import *
from litex.gen.genlib.roundrobin import *
from litex.gen.genlib.fsm import FSM, NextState
from migen import *
from migen.genlib.roundrobin import *
from migen.genlib.fsm import FSM, NextState
from litex.soc.cores.sdram.lasmicon.perf import Bandwidth
from litex.soc.interconnect.csr import AutoCSR

View file

@ -1,4 +1,4 @@
from litex.gen import *
from migen import *
from litex.soc.interconnect.csr import *

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.genlib.misc import timeline
from litex.gen.genlib.fsm import FSM
from migen import *
from migen.genlib.misc import timeline
from migen.genlib.fsm import FSM
from litex.soc.cores.sdram.lasmicon.multiplexer import *

View file

@ -1,9 +1,9 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib.fsm import FSM, NextState
from litex.gen.genlib.misc import WaitTimer
from migen import *
from migen.genlib.fsm import FSM, NextState
from migen.genlib.misc import WaitTimer
from litex.soc.interconnect import dfi as dfibus
from litex.soc.interconnect import wishbone

View file

@ -6,8 +6,8 @@
# TODO:
# - add $display support to LiteX gen and manage timing violations?
from litex.gen import *
from litex.gen.fhdl.specials import *
from migen import *
from migen.fhdl.specials import *
from litex.soc.interconnect.dfi import *
from functools import reduce

View file

@ -21,9 +21,9 @@
# This PHY only supports CAS Latency 2.
#
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.fhdl.specials import Tristate
from migen import *
from migen.genlib.record import *
from migen.fhdl.specials import Tristate
from litex.soc.interconnect.dfi import *
from litex.soc.cores.sdram import settings as sdram_settings

View file

@ -1,6 +1,6 @@
# tCK=5ns CL=7 CWL=6
from litex.gen import *
from migen import *
from litex.soc.interconnect.dfi import *
from litex.soc.interconnect.csr import *

View file

@ -19,8 +19,8 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib.record import *
from migen import *
from migen.genlib.record import *
from litex.soc.interconnect.dfi import *
from litex.soc.cores.sdram import settings as sdram_settings

View file

@ -1,7 +1,7 @@
from math import ceil
from collections import namedtuple
from litex.gen import *
from migen import *
PhySettingsT = namedtuple("PhySettings", "memtype dfi_databits nphases rdphase wrphase rdcmdphase wrcmdphase cl cwl read_latency write_latency")

View file

@ -1,7 +1,7 @@
from functools import reduce
from operator import xor
from litex.gen import *
from migen import *
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import dma_lasmi
@ -114,8 +114,8 @@ class _LFSRTB(Module):
print("{0:032x}".format(selfp.dut.o))
if __name__ == "__main__":
from litex.gen.fhdl import verilog
from litex.gen.sim.generic import run_simulation
from migen.fhdl import verilog
from migen.sim.generic import run_simulation
lfsr = LFSR(3, 4, [3, 2])
print(verilog.convert(lfsr, ios={lfsr.ce, lfsr.reset, lfsr.o}))

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.bank.description import *
from litex.gen.genlib.fsm import FSM, NextState
from migen import *
from migen.bank.description import *
from migen.genlib.fsm import FSM, NextState
class SPIMaster(Module, AutoCSR):

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.sim.generic import run_simulation
from migen import *
from migen.genlib.record import *
from migen.sim.generic import run_simulation
from litex.soc.com.spi import SPIMaster

View file

@ -1,4 +1,4 @@
from litex.gen import *
from migen import *
from litex.soc.interconnect.csr import *
from litex.soc.interconnect.csr_eventmanager import *

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.genlib.record import Record
from litex.gen.genlib.cdc import MultiReg
from migen import *
from migen.genlib.record import Record
from migen.genlib.cdc import MultiReg
from litex.soc.interconnect.csr import *
from litex.soc.interconnect.csr_eventmanager import *

View file

@ -1,4 +1,4 @@
from litex.gen import *
from migen import *
from litex.soc.interconnect.csr import CSRStatus

View file

@ -1,4 +1,4 @@
from litex.gen import log2_int
from migen import log2_int
def get_sdram_phy_header(sdram_phy_settings):

View file

@ -1,6 +1,6 @@
from operator import itemgetter
from litex.gen import *
from migen import *
from litex.soc.cores import identifier, timer, uart
from litex.soc.cores.cpu import lm32, mor1kx

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.record import *
from migen import *
from migen.genlib.record import *
from litex.soc.interconnect import wishbone, wishbone2lasmi, lasmi_bus
from litex.soc.interconnect.csr import AutoCSR
@ -84,7 +84,7 @@ class SoCSDRAM(SoCCore):
# Remove this workaround when fixed by Xilinx.
from litex.build.xilinx.vivado import XilinxVivadoToolchain
if isinstance(self.platform.toolchain, XilinxVivadoToolchain):
from litex.gen.fhdl.simplify import FullMemoryWE
from migen.fhdl.simplify import FullMemoryWE
self.submodules.l2_cache = FullMemoryWE()(l2_cache)
else:
self.submodules.l2_cache = l2_cache
@ -97,7 +97,7 @@ class SoCSDRAM(SoCCore):
# Remove this workaround when fixed by Xilinx.
from litex.build.xilinx.vivado import XilinxVivadoToolchain
if isinstance(self.platform.toolchain, XilinxVivadoToolchain):
from litex.gen.fhdl.simplify import FullMemoryWE
from migen.fhdl.simplify import FullMemoryWE
self.submodules.l2_cache = FullMemoryWE()(l2_cache)
else:
self.submodules.l2_cache = l2_cache

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.util.misc import xdir
from litex.gen.fhdl.tracer import get_obj_var_name
from migen import *
from migen.util.misc import xdir
from migen.fhdl.tracer import get_obj_var_name
class _CSRBase(DUID):

View file

@ -1,7 +1,7 @@
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.genlib.misc import chooser
from litex.gen.util.misc import xdir
from migen import *
from migen.genlib.record import *
from migen.genlib.misc import chooser
from migen.util.misc import xdir
from litex.soc.interconnect import csr
from litex.soc.interconnect.csr import CSRStorage

View file

@ -1,8 +1,8 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.util.misc import xdir
from migen import *
from migen.util.misc import xdir
from litex.soc.interconnect.csr import *

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.record import *
from migen import *
from migen.genlib.record import *
def phase_cmd_description(addressbits, bankbits):

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.fifo import SyncFIFO
from migen import *
from migen.genlib.fifo import SyncFIFO
class Reader(Module):

View file

@ -1,9 +1,9 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib import roundrobin
from litex.gen.genlib.record import *
from migen import *
from migen.genlib import roundrobin
from migen.genlib.record import *
class Interface(Record):

View file

@ -1,6 +1,6 @@
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.genlib import fifo
from migen import *
from migen.genlib.record import *
from migen.genlib import fifo
def _make_m2s(layout):
@ -154,7 +154,7 @@ class Demultiplexer(Module):
# XXX
from copy import copy
from litex.gen.util.misc import xdir
from migen.util.misc import xdir
def pack_layout(l, n):
return [("chunk"+str(i), l) for i in range(n)]

View file

@ -1,11 +1,11 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib import roundrobin
from litex.gen.genlib.record import *
from litex.gen.genlib.misc import split, displacer, chooser
from litex.gen.genlib.fsm import FSM, NextState
from migen import *
from migen.genlib import roundrobin
from migen.genlib.record import *
from migen.genlib.misc import split, displacer, chooser
from migen.genlib.fsm import FSM, NextState
from litex.soc.interconnect import csr

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.misc import timeline
from migen import *
from migen.genlib.misc import timeline
from litex.soc.interconnect import csr_bus, wishbone

View file

@ -1,5 +1,5 @@
from litex.gen import *
from litex.gen.genlib.fsm import FSM, NextState
from migen import *
from migen.genlib.fsm import FSM, NextState
class WB2LASMI(Module):