mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
avoid forking migen, we will add custom modules in litex/gen but will use upstream migen for common modules
This commit is contained in:
parent
3f43a49382
commit
619cd8e695
86 changed files with 130 additions and 4852 deletions
21
README
21
README
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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 *
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
]
|
|
@ -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
|
|
@ -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)
|
||||
)
|
||||
]
|
|
@ -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)
|
||||
]
|
|
@ -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))
|
|
@ -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")
|
||||
|
|
@ -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))
|
|
@ -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)) + ">"
|
|
@ -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")
|
|
@ -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)
|
|
@ -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])
|
|
@ -1 +0,0 @@
|
|||
from litex.gen.sim.core import Simulator, run_simulation
|
|
@ -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()
|
|
@ -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
|
|
@ -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:]))
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
|
||||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
from litex.soc.interconnect import wishbone
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
|
||||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
from litex.soc.interconnect import wishbone
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
|
||||
class Identifier(Module):
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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}))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.csr_eventmanager import *
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from litex.gen import *
|
||||
from migen import *
|
||||
|
||||
from litex.soc.interconnect.csr import CSRStatus
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from litex.gen import log2_int
|
||||
from migen import log2_int
|
||||
|
||||
|
||||
def get_sdram_phy_header(sdram_phy_settings):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue