mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
gen/build: merge with migen 0575c749e35a7180f0dca408e426af8eef22b568 and reintegrate migen simulator
* fhdl/visit: determinism * structure/Case/makedefault: fix corner cases * fhdl/tools: apply lowerer to specials in deterministic order * fhdl/verilog: fix variable name conflict * fhdl/verilog: simpler names for IOs. Closes #40 * fhdl/namer: deterministic naming of signals with name_override * use https url for m-labs.hk * pipistrello: make PMOD an extension header * vivado: find clock nets by get_nets, not get_ports * build: support platform-independent false path designation * sim: add more signals to VCD (#36) * build/xilinx: fix error message when Xilinx toolchain directory exists but does not contain a ISE version directory. Closes #39 * kc705: make xadc an extension header * kc705: add xadc/ams gpios * Merge branch 'master' of github.com:m-labs/migen * conda: fix for conda-build > 1.19 * platforms/kc705: enable on-die termination for user_sma_clock * README: update * Revert "conda: use BUILDNUMBER from environment." This reverts commit b2eedfd2e24f0b83c2fb118a3f98cf349b256e91. * conda: use BUILDNUMBER from environment. * typo * Exception now has helpful string. * README: remove outdated build badge * sim: run MemoryToArray before lowering specials * fhdl/simplify/MemoryToArray: remove spurious memory ports from specials * sim: make unlowered specials an error * sim: lower specials, closes #34 * sim: support evaluating Replicate() * Revert "README.md->rst" * Prevent backslashes in (Windows) paths from being escaped by OpenOCD's TCL implementation. * Revert "conda: run tests as a part of package build." * Revert "setuptools: include examples as migen.examples." * Revert "test: also look for examples in [.../dist-packages]/migen/examples/." * conda: use source from the current checkout. * travis: disable (superseded by our buildbot). * test: also look for examples in [.../dist-packages]/migen/examples/. * setuptools: include examples as migen.examples. * conda: run tests as a part of package build. * build: return to current working directory after building * sim/vcd: support signals not appearing in FHDL * sim: deterministic clock iteration * sim: add support for passive generators * fhdl/structure: fix last test in _Value.__bool__ (a instead of b)
This commit is contained in:
parent
71a719be44
commit
703b30e078
21 changed files with 151 additions and 783 deletions
|
@ -168,7 +168,7 @@ class ConstraintManager:
|
||||||
if isinstance(rt, int):
|
if isinstance(rt, int):
|
||||||
obj = Signal(rt, name_override=resource_name)
|
obj = Signal(rt, name_override=resource_name)
|
||||||
else:
|
else:
|
||||||
obj = Record(rt, name=resource_name, use_name_override=True)
|
obj = Record(rt, name=resource_name)
|
||||||
|
|
||||||
for element in resource[2:]:
|
for element in resource[2:]:
|
||||||
if isinstance(element, PlatformInfo):
|
if isinstance(element, PlatformInfo):
|
||||||
|
@ -253,6 +253,15 @@ class GenericPlatform:
|
||||||
def add_period_constraint(self, clk, period):
|
def add_period_constraint(self, clk, period):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def add_false_path_constraint(self, from_, to):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def add_false_path_constraints(self, *clk):
|
||||||
|
for a in clk:
|
||||||
|
for b in clk:
|
||||||
|
if a is not b:
|
||||||
|
self.add_false_path_constraint(a, b)
|
||||||
|
|
||||||
def add_platform_command(self, *args, **kwargs):
|
def add_platform_command(self, *args, **kwargs):
|
||||||
return self.constraint_manager.add_platform_command(*args, **kwargs)
|
return self.constraint_manager.add_platform_command(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ class OpenOCD(GenericProgrammer):
|
||||||
def load_bitstream(self, bitstream):
|
def load_bitstream(self, bitstream):
|
||||||
script = "; ".join([
|
script = "; ".join([
|
||||||
"init",
|
"init",
|
||||||
"pld load 0 {}".format(bitstream),
|
"pld load 0 {{{}}}".format(bitstream),
|
||||||
"exit",
|
"exit",
|
||||||
])
|
])
|
||||||
subprocess.call(["openocd", "-f", self.config, "-c", script])
|
subprocess.call(["openocd", "-f", self.config, "-c", script])
|
||||||
|
@ -22,8 +22,8 @@ class OpenOCD(GenericProgrammer):
|
||||||
flash_proxy = self.find_flash_proxy()
|
flash_proxy = self.find_flash_proxy()
|
||||||
script = "; ".join([
|
script = "; ".join([
|
||||||
"init",
|
"init",
|
||||||
"jtagspi_init 0 {}".format(flash_proxy),
|
"jtagspi_init 0 {{{}}}".format(flash_proxy),
|
||||||
"jtagspi_program {} 0x{:x}".format(data, address),
|
"jtagspi_program {{{}}} 0x{:x}".format(data, address),
|
||||||
"fpga_program",
|
"fpga_program",
|
||||||
"exit"
|
"exit"
|
||||||
])
|
])
|
||||||
|
|
|
@ -14,12 +14,12 @@ from litex.build import tools
|
||||||
|
|
||||||
|
|
||||||
def settings(path, ver=None, sub=None):
|
def settings(path, ver=None, sub=None):
|
||||||
vers = list(tools.versions(path))
|
|
||||||
if ver is None:
|
if ver is None:
|
||||||
|
vers = list(tools.versions(path))
|
||||||
|
if not vers:
|
||||||
|
raise OSError("no version directory for Xilinx tools found in "
|
||||||
|
+ path)
|
||||||
ver = max(vers)
|
ver = max(vers)
|
||||||
else:
|
|
||||||
ver = StrictVersion(ver)
|
|
||||||
assert ver in vers
|
|
||||||
|
|
||||||
full = os.path.join(path, str(ver))
|
full = os.path.join(path, str(ver))
|
||||||
if sub:
|
if sub:
|
||||||
|
@ -39,7 +39,7 @@ def settings(path, ver=None, sub=None):
|
||||||
if os.path.exists(settings):
|
if os.path.exists(settings):
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
raise OSError("no settings file found")
|
raise OSError("no Xilinx tools settings file found")
|
||||||
|
|
||||||
|
|
||||||
class XilinxNoRetimingVivadoImpl(Module):
|
class XilinxNoRetimingVivadoImpl(Module):
|
||||||
|
|
|
@ -181,4 +181,10 @@ class XilinxISEToolchain:
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
def add_period_constraint(self, platform, clk, period):
|
||||||
platform.add_platform_command("""NET "{clk}" TNM_NET = "GRP{clk}";
|
platform.add_platform_command("""NET "{clk}" TNM_NET = "GRP{clk}";
|
||||||
TIMESPEC "TS{clk}" = PERIOD "GRP{clk}" """+str(period)+""" ns HIGH 50%;""", clk=clk)
|
TIMESPEC "TS{clk}" = PERIOD "GRP{clk}" """ + str(period) + """ ns HIGH 50%;""",
|
||||||
|
clk=clk)
|
||||||
|
|
||||||
|
def add_false_path_constraint(self, platform, from_, to):
|
||||||
|
platform.add_platform_command(
|
||||||
|
"""TIMESPEC "TS{from_}TO{to}" = FROM "GRP{from_}" TO "GRP{to}" TIG;""",
|
||||||
|
from_=from_, to=to)
|
||||||
|
|
|
@ -32,3 +32,10 @@ class XilinxPlatform(GenericPlatform):
|
||||||
if hasattr(clk, "p"):
|
if hasattr(clk, "p"):
|
||||||
clk = clk.p
|
clk = clk.p
|
||||||
self.toolchain.add_period_constraint(self, clk, period)
|
self.toolchain.add_period_constraint(self, clk, period)
|
||||||
|
|
||||||
|
def add_false_path_constraint(self, from_, to):
|
||||||
|
if hasattr(from_, "p"):
|
||||||
|
from_ = from_.p
|
||||||
|
if hasattr(to, "p"):
|
||||||
|
to = to.p
|
||||||
|
self.toolchain.add_false_path_constraint(self, from_, to)
|
||||||
|
|
|
@ -135,5 +135,11 @@ class XilinxVivadoToolchain:
|
||||||
return v_output.ns
|
return v_output.ns
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
def add_period_constraint(self, platform, clk, period):
|
||||||
platform.add_platform_command("""create_clock -name {clk} -period """ + \
|
platform.add_platform_command(
|
||||||
str(period) + """ [get_ports {clk}]""", clk=clk)
|
"create_clock -name {clk} -period " + str(period) +
|
||||||
|
" [get_nets {clk}]", clk=clk)
|
||||||
|
|
||||||
|
def add_false_path_constraint(self, platform, from_, to):
|
||||||
|
platform.add_platform_command(
|
||||||
|
"set_false_path -from [get_clocks {from_}] -to [get_clocks {to}]",
|
||||||
|
from_=from_, to=to)
|
||||||
|
|
|
@ -4,5 +4,7 @@ from litex.gen.fhdl.specials import *
|
||||||
from litex.gen.fhdl.bitcontainer import *
|
from litex.gen.fhdl.bitcontainer import *
|
||||||
from litex.gen.fhdl.decorators import *
|
from litex.gen.fhdl.decorators import *
|
||||||
|
|
||||||
|
from litex.gen.sim import *
|
||||||
|
|
||||||
from litex.gen.genlib.record import *
|
from litex.gen.genlib.record import *
|
||||||
from litex.gen.genlib.fsm import *
|
from litex.gen.genlib.fsm import *
|
||||||
|
|
|
@ -5,7 +5,7 @@ from litex.gen.util.misc import flat_iteration
|
||||||
from litex.gen.fhdl.structure import *
|
from litex.gen.fhdl.structure import *
|
||||||
from litex.gen.fhdl.structure import _Fragment
|
from litex.gen.fhdl.structure import _Fragment
|
||||||
from litex.gen.fhdl.tools import rename_clock_domain
|
from litex.gen.fhdl.tools import rename_clock_domain
|
||||||
from litex.gen.sim.upper import gen_sim, proxy_sim
|
|
||||||
|
|
||||||
__all__ = ["Module", "FinalizeError"]
|
__all__ = ["Module", "FinalizeError"]
|
||||||
|
|
||||||
|
@ -120,20 +120,7 @@ class Module:
|
||||||
self.finalized = False
|
self.finalized = False
|
||||||
return self.finalized
|
return self.finalized
|
||||||
elif name == "_fragment":
|
elif name == "_fragment":
|
||||||
simf = None
|
self._fragment = _Fragment()
|
||||||
try:
|
|
||||||
simf = self.do_simulation
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
simg = self.gen_simulation
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
simf = gen_sim(simg)
|
|
||||||
if simf is not None:
|
|
||||||
simf = proxy_sim(self, simf)
|
|
||||||
sim = [] if simf is None else [simf]
|
|
||||||
self._fragment = _Fragment(sim=sim)
|
|
||||||
return self._fragment
|
return self._fragment
|
||||||
elif name == "_submodules":
|
elif name == "_submodules":
|
||||||
self._submodules = []
|
self._submodules = []
|
||||||
|
|
|
@ -217,9 +217,9 @@ def build_namespace(signals, reserved_keywords=set()):
|
||||||
pnd = _build_pnd(signals)
|
pnd = _build_pnd(signals)
|
||||||
ns = Namespace(pnd, reserved_keywords)
|
ns = Namespace(pnd, reserved_keywords)
|
||||||
# register signals with name_override
|
# register signals with name_override
|
||||||
for signal in signals:
|
swno = {signal for signal in signals if signal.name_override is not None}
|
||||||
if signal.name_override is not None:
|
for signal in sorted(swno, key=lambda x: x.duid):
|
||||||
ns.get_name(signal)
|
ns.get_name(signal)
|
||||||
return ns
|
return ns
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ class MemoryToArray(ModuleTransformer):
|
||||||
|
|
||||||
def transform_fragment(self, i, f):
|
def transform_fragment(self, i, f):
|
||||||
newspecials = set()
|
newspecials = set()
|
||||||
|
processed_ports = set()
|
||||||
|
|
||||||
for mem in f.specials:
|
for mem in f.specials:
|
||||||
if not isinstance(mem, Memory):
|
if not isinstance(mem, Memory):
|
||||||
|
@ -111,4 +112,7 @@ class MemoryToArray(ModuleTransformer):
|
||||||
sync.append(If(port.we,
|
sync.append(If(port.we,
|
||||||
storage[port.adr].eq(port.dat_w)))
|
storage[port.adr].eq(port.dat_w)))
|
||||||
|
|
||||||
|
processed_ports.add(port)
|
||||||
|
|
||||||
|
newspecials -= processed_ports
|
||||||
f.specials = newspecials
|
f.specials = newspecials
|
||||||
|
|
|
@ -34,7 +34,7 @@ class _Value(DUID):
|
||||||
if isinstance(a, Signal) and isinstance(b, Signal):
|
if isinstance(a, Signal) and isinstance(b, Signal):
|
||||||
return a is b
|
return a is b
|
||||||
if (isinstance(a, Constant) and isinstance(b, Signal)
|
if (isinstance(a, Constant) and isinstance(b, Signal)
|
||||||
or isinstance(a, Signal) and isinstance(a, Constant)):
|
or isinstance(a, Signal) and isinstance(b, Constant)):
|
||||||
return False
|
return False
|
||||||
raise TypeError("Attempted to convert Migen value to boolean")
|
raise TypeError("Attempted to convert Migen value to boolean")
|
||||||
|
|
||||||
|
@ -107,7 +107,8 @@ class _Value(DUID):
|
||||||
return Cat(self[i] for i in range(start, stop, step))
|
return Cat(self[i] for i in range(start, stop, step))
|
||||||
return _Slice(self, start, stop)
|
return _Slice(self, start, stop)
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError("Cannot use type {} ({}) as key".format(
|
||||||
|
type(key), repr(key)))
|
||||||
|
|
||||||
def eq(self, r):
|
def eq(self, r):
|
||||||
"""Assignment
|
"""Assignment
|
||||||
|
@ -525,7 +526,7 @@ class Case(_Statement):
|
||||||
for k, v in cases.items():
|
for k, v in cases.items():
|
||||||
if isinstance(k, (bool, int)):
|
if isinstance(k, (bool, int)):
|
||||||
k = Constant(k)
|
k = Constant(k)
|
||||||
if (not isinstance(k, Constant)
|
if (not isinstance(k, Constant)
|
||||||
and not (isinstance(k, str) and k == "default")):
|
and not (isinstance(k, str) and k == "default")):
|
||||||
raise TypeError("Case object is not a Migen constant")
|
raise TypeError("Case object is not a Migen constant")
|
||||||
if not isinstance(v, _collections.Iterable):
|
if not isinstance(v, _collections.Iterable):
|
||||||
|
@ -542,16 +543,21 @@ class Case(_Statement):
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
key : int or None
|
key : int, Constant or None
|
||||||
Key to use as default case if no other key matches.
|
Key to use as default case if no other key matches.
|
||||||
By default, the largest key is the default key.
|
By default, the largest key is the default key.
|
||||||
"""
|
"""
|
||||||
if key is None:
|
if key is None:
|
||||||
for choice in self.cases.keys():
|
for choice in self.cases.keys():
|
||||||
if key is None or choice.value > key.value:
|
if (key is None
|
||||||
|
or (isinstance(choice, str) and choice == "default")
|
||||||
|
or choice.value > key.value):
|
||||||
key = choice
|
key = choice
|
||||||
self.cases["default"] = self.cases[key]
|
if not isinstance(key, str) or key != "default":
|
||||||
|
key = wrap(key)
|
||||||
|
stmts = self.cases[key]
|
||||||
del self.cases[key]
|
del self.cases[key]
|
||||||
|
self.cases["default"] = stmts
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@ -679,23 +685,17 @@ class _ClockDomainList(list):
|
||||||
(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
|
(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
|
||||||
|
|
||||||
|
|
||||||
class StopSimulation(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class _Fragment:
|
class _Fragment:
|
||||||
def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None):
|
def __init__(self, comb=None, sync=None, specials=None, clock_domains=None):
|
||||||
if comb is None: comb = []
|
if comb is None: comb = []
|
||||||
if sync is None: sync = dict()
|
if sync is None: sync = dict()
|
||||||
if specials is None: specials = set()
|
if specials is None: specials = set()
|
||||||
if clock_domains is None: clock_domains = _ClockDomainList()
|
if clock_domains is None: clock_domains = _ClockDomainList()
|
||||||
if sim is None: sim = []
|
|
||||||
|
|
||||||
self.comb = comb
|
self.comb = comb
|
||||||
self.sync = sync
|
self.sync = sync
|
||||||
self.specials = specials
|
self.specials = specials
|
||||||
self.clock_domains = _ClockDomainList(clock_domains)
|
self.clock_domains = _ClockDomainList(clock_domains)
|
||||||
self.sim = sim
|
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
newsync = _collections.defaultdict(list)
|
newsync = _collections.defaultdict(list)
|
||||||
|
@ -705,8 +705,7 @@ class _Fragment:
|
||||||
newsync[k].extend(v)
|
newsync[k].extend(v)
|
||||||
return _Fragment(self.comb + other.comb, newsync,
|
return _Fragment(self.comb + other.comb, newsync,
|
||||||
self.specials | other.specials,
|
self.specials | other.specials,
|
||||||
self.clock_domains + other.clock_domains,
|
self.clock_domains + other.clock_domains)
|
||||||
self.sim + other.sim)
|
|
||||||
|
|
||||||
def __iadd__(self, other):
|
def __iadd__(self, other):
|
||||||
newsync = _collections.defaultdict(list)
|
newsync = _collections.defaultdict(list)
|
||||||
|
@ -718,5 +717,4 @@ class _Fragment:
|
||||||
self.sync = newsync
|
self.sync = newsync
|
||||||
self.specials |= other.specials
|
self.specials |= other.specials
|
||||||
self.clock_domains += other.clock_domains
|
self.clock_domains += other.clock_domains
|
||||||
self.sim += other.sim
|
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from litex.gen.fhdl.structure import *
|
from litex.gen.fhdl.structure import *
|
||||||
from litex.gen.fhdl.structure import _Slice, _Assign
|
from litex.gen.fhdl.structure import _Slice, _Assign, _Fragment
|
||||||
from litex.gen.fhdl.visit import NodeVisitor, NodeTransformer
|
from litex.gen.fhdl.visit import NodeVisitor, NodeTransformer
|
||||||
from litex.gen.fhdl.bitcontainer import value_bits_sign
|
from litex.gen.fhdl.bitcontainer import value_bits_sign
|
||||||
from litex.gen.util.misc import flat_iteration
|
from litex.gen.util.misc import flat_iteration
|
||||||
|
@ -236,7 +236,7 @@ def _apply_lowerer(l, f):
|
||||||
f = l.visit(f)
|
f = l.visit(f)
|
||||||
f.comb += l.comb
|
f.comb += l.comb
|
||||||
|
|
||||||
for special in f.specials:
|
for special in sorted(f.specials, key=lambda s: s.duid):
|
||||||
for obj, attr, direction in special.iter_expressions():
|
for obj, attr, direction in special.iter_expressions():
|
||||||
if direction != SPECIAL_INOUT:
|
if direction != SPECIAL_INOUT:
|
||||||
# inouts are only supported by Migen when connected directly to top-level
|
# inouts are only supported by Migen when connected directly to top-level
|
||||||
|
@ -296,3 +296,44 @@ def rename_clock_domain(f, old, new):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
cd.rename(new)
|
cd.rename(new)
|
||||||
|
|
||||||
|
|
||||||
|
def call_special_classmethod(overrides, obj, method, *args, **kwargs):
|
||||||
|
cl = obj.__class__
|
||||||
|
if cl in overrides:
|
||||||
|
cl = overrides[cl]
|
||||||
|
if hasattr(cl, method):
|
||||||
|
return getattr(cl, method)(obj, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _lower_specials_step(overrides, specials):
|
||||||
|
f = _Fragment()
|
||||||
|
lowered_specials = set()
|
||||||
|
for special in sorted(specials, key=lambda x: x.duid):
|
||||||
|
impl = call_special_classmethod(overrides, special, "lower")
|
||||||
|
if impl is not None:
|
||||||
|
f += impl.get_fragment()
|
||||||
|
lowered_specials.add(special)
|
||||||
|
return f, lowered_specials
|
||||||
|
|
||||||
|
|
||||||
|
def _can_lower(overrides, specials):
|
||||||
|
for special in specials:
|
||||||
|
cl = special.__class__
|
||||||
|
if cl in overrides:
|
||||||
|
cl = overrides[cl]
|
||||||
|
if hasattr(cl, "lower"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def lower_specials(overrides, specials):
|
||||||
|
f, lowered_specials = _lower_specials_step(overrides, specials)
|
||||||
|
while _can_lower(overrides, f.specials):
|
||||||
|
f2, lowered_specials2 = _lower_specials_step(overrides, f.specials)
|
||||||
|
f += f2
|
||||||
|
lowered_specials |= lowered_specials2
|
||||||
|
f.specials -= lowered_specials2
|
||||||
|
return f, lowered_specials
|
||||||
|
|
|
@ -5,11 +5,9 @@ import collections
|
||||||
from litex.gen.fhdl.structure import *
|
from litex.gen.fhdl.structure import *
|
||||||
from litex.gen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment
|
from litex.gen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment
|
||||||
from litex.gen.fhdl.tools import *
|
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.namer import build_namespace
|
||||||
from litex.gen.fhdl.conv_output import ConvOutput
|
from litex.gen.fhdl.conv_output import ConvOutput
|
||||||
|
|
||||||
# TODO: remove printcomb_simulation when we will be using new migen simulator
|
|
||||||
|
|
||||||
_reserved_keywords = {
|
_reserved_keywords = {
|
||||||
"always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
|
"always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
|
||||||
|
@ -117,11 +115,9 @@ def _printexpr(ns, node):
|
||||||
(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3)
|
(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3)
|
||||||
|
|
||||||
|
|
||||||
def _printnode(ns, at, level, node, target_filter=None):
|
def _printnode(ns, at, level, node):
|
||||||
if node is None:
|
if node is None:
|
||||||
return ""
|
return ""
|
||||||
elif target_filter is not None and target_filter not in list_targets(node):
|
|
||||||
return ""
|
|
||||||
elif isinstance(node, _Assign):
|
elif isinstance(node, _Assign):
|
||||||
if at == _AT_BLOCKING:
|
if at == _AT_BLOCKING:
|
||||||
assignment = " = "
|
assignment = " = "
|
||||||
|
@ -133,13 +129,13 @@ def _printnode(ns, at, level, node, target_filter=None):
|
||||||
assignment = " <= "
|
assignment = " <= "
|
||||||
return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n"
|
return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n"
|
||||||
elif isinstance(node, collections.Iterable):
|
elif isinstance(node, collections.Iterable):
|
||||||
return "".join(_printnode(ns, at, level, n, target_filter) for n in node)
|
return "".join(list(map(partial(_printnode, ns, at, level), node)))
|
||||||
elif isinstance(node, If):
|
elif isinstance(node, If):
|
||||||
r = "\t"*level + "if (" + _printexpr(ns, node.cond)[0] + ") begin\n"
|
r = "\t"*level + "if (" + _printexpr(ns, node.cond)[0] + ") begin\n"
|
||||||
r += _printnode(ns, at, level + 1, node.t, target_filter)
|
r += _printnode(ns, at, level + 1, node.t)
|
||||||
if node.f:
|
if node.f:
|
||||||
r += "\t"*level + "end else begin\n"
|
r += "\t"*level + "end else begin\n"
|
||||||
r += _printnode(ns, at, level + 1, node.f, target_filter)
|
r += _printnode(ns, at, level + 1, node.f)
|
||||||
r += "\t"*level + "end\n"
|
r += "\t"*level + "end\n"
|
||||||
return r
|
return r
|
||||||
elif isinstance(node, Case):
|
elif isinstance(node, Case):
|
||||||
|
@ -149,11 +145,11 @@ def _printnode(ns, at, level, node, target_filter=None):
|
||||||
css = sorted(css, key=lambda x: x[0].value)
|
css = sorted(css, key=lambda x: x[0].value)
|
||||||
for choice, statements in css:
|
for choice, statements in css:
|
||||||
r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n"
|
r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n"
|
||||||
r += _printnode(ns, at, level + 2, statements, target_filter)
|
r += _printnode(ns, at, level + 2, statements)
|
||||||
r += "\t"*(level + 1) + "end\n"
|
r += "\t"*(level + 1) + "end\n"
|
||||||
if "default" in node.cases:
|
if "default" in node.cases:
|
||||||
r += "\t"*(level + 1) + "default: begin\n"
|
r += "\t"*(level + 1) + "default: begin\n"
|
||||||
r += _printnode(ns, at, level + 2, node.cases["default"], target_filter)
|
r += _printnode(ns, at, level + 2, node.cases["default"])
|
||||||
r += "\t"*(level + 1) + "end\n"
|
r += "\t"*(level + 1) + "end\n"
|
||||||
r += "\t"*level + "endcase\n"
|
r += "\t"*level + "endcase\n"
|
||||||
return r
|
return r
|
||||||
|
@ -196,24 +192,21 @@ def _printheader(f, ios, name, ns,
|
||||||
r += "\tinput " + _printsig(ns, sig)
|
r += "\tinput " + _printsig(ns, sig)
|
||||||
r += "\n);\n\n"
|
r += "\n);\n\n"
|
||||||
for sig in sorted(sigs - ios, key=lambda x: x.duid):
|
for sig in sorted(sigs - ios, key=lambda x: x.duid):
|
||||||
attributes = ""
|
|
||||||
if sig.attribute != "":
|
|
||||||
attributes = "(*" + sig.attribute[:-1] + "*) "
|
|
||||||
if sig in wires:
|
if sig in wires:
|
||||||
r += attributes + "wire " + _printsig(ns, sig) + ";\n"
|
r += "wire " + _printsig(ns, sig) + ";\n"
|
||||||
else:
|
else:
|
||||||
if reg_initialization:
|
if reg_initialization:
|
||||||
r += attributes + "reg " + _printsig(ns, sig) + " = " + _printexpr(ns, sig.reset)[0] + ";\n"
|
r += "reg " + _printsig(ns, sig) + " = " + _printexpr(ns, sig.reset)[0] + ";\n"
|
||||||
else:
|
else:
|
||||||
r += attributes + "reg " + _printsig(ns, sig) + ";\n"
|
r += "reg " + _printsig(ns, sig) + ";\n"
|
||||||
r += "\n"
|
r += "\n"
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def _printcomb_simulation(f, ns,
|
def _printcomb(f, ns,
|
||||||
display_run,
|
display_run,
|
||||||
dummy_signal,
|
dummy_signal,
|
||||||
blocking_assign):
|
blocking_assign):
|
||||||
r = ""
|
r = ""
|
||||||
if f.comb:
|
if f.comb:
|
||||||
if dummy_signal:
|
if dummy_signal:
|
||||||
|
@ -227,22 +220,11 @@ def _printcomb_simulation(f, ns,
|
||||||
r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
|
r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
|
||||||
r += syn_on
|
r += syn_on
|
||||||
|
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
target_stmt_map = defaultdict(list)
|
|
||||||
|
|
||||||
for statement in flat_iteration(f.comb):
|
|
||||||
targets = list_targets(statement)
|
|
||||||
for t in targets:
|
|
||||||
target_stmt_map[t].append(statement)
|
|
||||||
|
|
||||||
groups = group_by_targets(f.comb)
|
groups = group_by_targets(f.comb)
|
||||||
|
|
||||||
for n, (t, stmts) in enumerate(target_stmt_map.items()):
|
for n, g in enumerate(groups):
|
||||||
assert isinstance(t, Signal)
|
if len(g[1]) == 1 and isinstance(g[1][0], _Assign):
|
||||||
if len(stmts) == 1 and isinstance(stmts[0], _Assign):
|
r += "assign " + _printnode(ns, _AT_BLOCKING, 0, g[1][0])
|
||||||
r += "assign " + _printnode(ns, _AT_BLOCKING, 0, stmts[0])
|
|
||||||
else:
|
else:
|
||||||
if dummy_signal:
|
if dummy_signal:
|
||||||
dummy_d = Signal(name_override="dummy_d")
|
dummy_d = Signal(name_override="dummy_d")
|
||||||
|
@ -253,31 +235,6 @@ def _printcomb_simulation(f, ns,
|
||||||
r += "always @(*) begin\n"
|
r += "always @(*) begin\n"
|
||||||
if display_run:
|
if display_run:
|
||||||
r += "\t$display(\"Running comb block #" + str(n) + "\");\n"
|
r += "\t$display(\"Running comb block #" + str(n) + "\");\n"
|
||||||
if blocking_assign:
|
|
||||||
r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
|
|
||||||
r += _printnode(ns, _AT_BLOCKING, 1, stmts, t)
|
|
||||||
else:
|
|
||||||
r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
|
|
||||||
r += _printnode(ns, _AT_NONBLOCKING, 1, stmts, t)
|
|
||||||
if dummy_signal:
|
|
||||||
r += syn_off
|
|
||||||
r += "\t" + ns.get_name(dummy_d) + " = " + ns.get_name(dummy_s) + ";\n"
|
|
||||||
r += syn_on
|
|
||||||
r += "end\n"
|
|
||||||
r += "\n"
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def _printcomb_regular(f, ns, blocking_assign):
|
|
||||||
r = ""
|
|
||||||
if f.comb:
|
|
||||||
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:
|
|
||||||
r += "always @(*) begin\n"
|
|
||||||
if blocking_assign:
|
if blocking_assign:
|
||||||
for t in g[0]:
|
for t in g[0]:
|
||||||
r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
|
r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
|
||||||
|
@ -286,12 +243,15 @@ def _printcomb_regular(f, ns, blocking_assign):
|
||||||
for t in g[0]:
|
for t in g[0]:
|
||||||
r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
|
r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
|
||||||
r += _printnode(ns, _AT_NONBLOCKING, 1, g[1])
|
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 += "end\n"
|
||||||
r += "\n"
|
r += "\n"
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _printsync(f, ns):
|
def _printsync(f, ns):
|
||||||
r = ""
|
r = ""
|
||||||
for k, v in sorted(f.sync.items(), key=itemgetter(0)):
|
for k, v in sorted(f.sync.items(), key=itemgetter(0)):
|
||||||
|
@ -301,51 +261,10 @@ def _printsync(f, ns):
|
||||||
return r
|
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):
|
def _printspecials(overrides, specials, ns, add_data_file):
|
||||||
r = ""
|
r = ""
|
||||||
for special in sorted(specials, key=lambda x: x.duid):
|
for special in sorted(specials, key=lambda x: x.duid):
|
||||||
pr = _call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file)
|
pr = call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file)
|
||||||
if pr is None:
|
if pr is None:
|
||||||
raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog")
|
raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog")
|
||||||
r += pr
|
r += pr
|
||||||
|
@ -380,25 +299,24 @@ def convert(f, ios=None, name="top",
|
||||||
f = lower_complex_slices(f)
|
f = lower_complex_slices(f)
|
||||||
insert_resets(f)
|
insert_resets(f)
|
||||||
f = lower_basics(f)
|
f = lower_basics(f)
|
||||||
fs, lowered_specials = _lower_specials(special_overrides, f.specials)
|
fs, lowered_specials = lower_specials(special_overrides, f.specials)
|
||||||
f += lower_basics(fs)
|
f += lower_basics(fs)
|
||||||
|
|
||||||
|
for io in sorted(ios, key=lambda x: x.duid):
|
||||||
|
if io.name_override is None:
|
||||||
|
io_name = io.backtrace[-1][0]
|
||||||
|
if io_name:
|
||||||
|
io.name_override = io_name
|
||||||
ns = build_namespace(list_signals(f) \
|
ns = build_namespace(list_signals(f) \
|
||||||
| list_special_ios(f, True, True, True) \
|
| list_special_ios(f, True, True, True) \
|
||||||
| ios, _reserved_keywords)
|
| ios, _reserved_keywords)
|
||||||
ns.clock_domains = f.clock_domains
|
ns.clock_domains = f.clock_domains
|
||||||
r.ns = ns
|
r.ns = ns
|
||||||
|
|
||||||
src = "/* Machine-generated using LiteX gen "
|
src = "/* Machine-generated using LiteX gen */\n"
|
||||||
src += "(regular)" if regular_comb else "(simulation)"
|
|
||||||
src += " */\n"
|
|
||||||
src += _printheader(f, ios, name, ns,
|
src += _printheader(f, ios, name, ns,
|
||||||
reg_initialization=reg_initialization)
|
reg_initialization=reg_initialization)
|
||||||
if regular_comb:
|
src += _printcomb(f, ns,
|
||||||
src += _printcomb_regular(f, ns,
|
|
||||||
blocking_assign=blocking_assign)
|
|
||||||
else:
|
|
||||||
src += _printcomb_simulation(f, ns,
|
|
||||||
display_run=display_run,
|
display_run=display_run,
|
||||||
dummy_signal=dummy_signal,
|
dummy_signal=dummy_signal,
|
||||||
blocking_assign=blocking_assign)
|
blocking_assign=blocking_assign)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
from operator import itemgetter
|
||||||
|
|
||||||
from litex.gen.fhdl.structure import *
|
from litex.gen.fhdl.structure import *
|
||||||
from litex.gen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy,
|
from litex.gen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy,
|
||||||
|
@ -77,7 +78,8 @@ class NodeVisitor:
|
||||||
|
|
||||||
def visit_Case(self, node):
|
def visit_Case(self, node):
|
||||||
self.visit(node.test)
|
self.visit(node.test)
|
||||||
for v, statements in node.cases.items():
|
for v, statements in sorted(node.cases.items(),
|
||||||
|
key=lambda x: str(x[0])):
|
||||||
self.visit(statements)
|
self.visit(statements)
|
||||||
|
|
||||||
def visit_Fragment(self, node):
|
def visit_Fragment(self, node):
|
||||||
|
@ -89,7 +91,7 @@ class NodeVisitor:
|
||||||
self.visit(statement)
|
self.visit(statement)
|
||||||
|
|
||||||
def visit_clock_domains(self, node):
|
def visit_clock_domains(self, node):
|
||||||
for clockname, statements in node.items():
|
for clockname, statements in sorted(node.items(), key=itemgetter(0)):
|
||||||
self.visit(statements)
|
self.visit(statements)
|
||||||
|
|
||||||
def visit_ArrayProxy(self, node):
|
def visit_ArrayProxy(self, node):
|
||||||
|
@ -177,7 +179,9 @@ class NodeTransformer:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def visit_Case(self, node):
|
def visit_Case(self, node):
|
||||||
cases = dict((v, self.visit(statements)) for v, statements in node.cases.items())
|
cases = {v: self.visit(statements)
|
||||||
|
for v, statements in sorted(node.cases.items(),
|
||||||
|
key=lambda x: str(x[0]))}
|
||||||
r = Case(self.visit(node.test), cases)
|
r = Case(self.visit(node.test), cases)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@ -192,7 +196,9 @@ class NodeTransformer:
|
||||||
return [self.visit(statement) for statement in node]
|
return [self.visit(statement) for statement in node]
|
||||||
|
|
||||||
def visit_clock_domains(self, node):
|
def visit_clock_domains(self, node):
|
||||||
return dict((clockname, self.visit(statements)) for clockname, statements in node.items())
|
return {clockname: self.visit(statements)
|
||||||
|
for clockname, statements in sorted(node.items(),
|
||||||
|
key=itemgetter(0))}
|
||||||
|
|
||||||
def visit_ArrayProxy(self, node):
|
def visit_ArrayProxy(self, node):
|
||||||
return _ArrayProxy([self.visit(choice) for choice in node.choices],
|
return _ArrayProxy([self.visit(choice) for choice in node.choices],
|
||||||
|
|
|
@ -86,7 +86,7 @@ def layout_partial(layout, *elements):
|
||||||
|
|
||||||
|
|
||||||
class Record:
|
class Record:
|
||||||
def __init__(self, layout, name=None, use_name_override=False):
|
def __init__(self, layout, name=None):
|
||||||
self.name = get_obj_var_name(name, "")
|
self.name = get_obj_var_name(name, "")
|
||||||
self.layout = layout
|
self.layout = layout
|
||||||
|
|
||||||
|
@ -100,10 +100,7 @@ class Record:
|
||||||
fname, fsize, fdirection = f
|
fname, fsize, fdirection = f
|
||||||
else:
|
else:
|
||||||
fname, fsize = f
|
fname, fsize = f
|
||||||
if use_name_override:
|
finst = Signal(fsize, name=prefix + fname)
|
||||||
finst = Signal(fsize, name_override=prefix + fname)
|
|
||||||
else:
|
|
||||||
finst = Signal(fsize, name=prefix + fname)
|
|
||||||
elif isinstance(f[1], list): # case 3
|
elif isinstance(f[1], list): # case 3
|
||||||
fname, fsublayout = f
|
fname, fsublayout = f
|
||||||
finst = Record(fsublayout, prefix + fname)
|
finst = Record(fsublayout, prefix + fname)
|
||||||
|
|
|
@ -12,7 +12,7 @@ class BitonicSort(Module):
|
||||||
|
|
||||||
http://www.dps.uibk.ac.at/~cosenza/teaching/gpu/sort-batcher.pdf
|
http://www.dps.uibk.ac.at/~cosenza/teaching/gpu/sort-batcher.pdf
|
||||||
|
|
||||||
http://www.inf.fh-lensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm
|
http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm
|
||||||
|
|
||||||
http://www.myhdl.org/doku.php/cookbook:bitonic
|
http://www.myhdl.org/doku.php/cookbook:bitonic
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from litex.gen.sim.core import Simulator, run_simulation, passive
|
|
@ -1,231 +0,0 @@
|
||||||
import warnings
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from litex.gen import *
|
|
||||||
from litex.gen.fhdl.structure import _Fragment
|
|
||||||
|
|
||||||
from litex.gen.fhdl import verilog
|
|
||||||
from litex.gen.sim.ipc import *
|
|
||||||
from litex.gen.sim import icarus
|
|
||||||
|
|
||||||
|
|
||||||
class TopLevel:
|
|
||||||
def __init__(self, vcd_name=None, vcd_level=1,
|
|
||||||
top_name="top", dut_type="dut", dut_name="dut",
|
|
||||||
cd_name="sys", clk_period=10):
|
|
||||||
self.vcd_name = vcd_name
|
|
||||||
self.vcd_level = vcd_level
|
|
||||||
self.top_name = top_name
|
|
||||||
self.dut_type = dut_type
|
|
||||||
self.dut_name = dut_name
|
|
||||||
|
|
||||||
self._cd_name = cd_name
|
|
||||||
self._clk_period = clk_period
|
|
||||||
|
|
||||||
cd = ClockDomain(self._cd_name)
|
|
||||||
self.clock_domains = [cd]
|
|
||||||
self.ios = {cd.clk, cd.rst}
|
|
||||||
|
|
||||||
def get(self, sockaddr):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
sockaddr = sockaddr[0] # Get the IP address only
|
|
||||||
|
|
||||||
template1 = """`timescale 1ns / 1ps
|
|
||||||
|
|
||||||
module {top_name}();
|
|
||||||
|
|
||||||
reg {clk_name};
|
|
||||||
reg {rst_name};
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
{rst_name} <= 1'b1;
|
|
||||||
@(posedge {clk_name});
|
|
||||||
{rst_name} <= 1'b0;
|
|
||||||
end
|
|
||||||
|
|
||||||
always begin
|
|
||||||
{clk_name} <= 1'b0;
|
|
||||||
#{hclk_period};
|
|
||||||
{clk_name} <= 1'b1;
|
|
||||||
#{hclk_period};
|
|
||||||
end
|
|
||||||
|
|
||||||
{dut_type} {dut_name}(
|
|
||||||
.{rst_name}({rst_name}),
|
|
||||||
.{clk_name}({clk_name})
|
|
||||||
);
|
|
||||||
|
|
||||||
initial $migensim_connect("{sockaddr}");
|
|
||||||
always @(posedge {clk_name}) $migensim_tick;
|
|
||||||
"""
|
|
||||||
template2 = """
|
|
||||||
initial begin
|
|
||||||
$dumpfile("{vcd_name}");
|
|
||||||
$dumpvars({vcd_level}, {dut_name});
|
|
||||||
end
|
|
||||||
"""
|
|
||||||
r = template1.format(top_name=self.top_name,
|
|
||||||
dut_type=self.dut_type,
|
|
||||||
dut_name=self.dut_name,
|
|
||||||
clk_name=self._cd_name + "_clk",
|
|
||||||
rst_name=self._cd_name + "_rst",
|
|
||||||
hclk_period=str(self._clk_period/2),
|
|
||||||
sockaddr=sockaddr)
|
|
||||||
if self.vcd_name is not None:
|
|
||||||
r += template2.format(vcd_name=self.vcd_name,
|
|
||||||
vcd_level=str(self.vcd_level),
|
|
||||||
dut_name=self.dut_name)
|
|
||||||
r += "\nendmodule"
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
class Simulator:
|
|
||||||
def __init__(self, fragment, top_level=None, sim_runner=None, sockaddr="simsocket", **vopts):
|
|
||||||
if not isinstance(fragment, _Fragment):
|
|
||||||
fragment = fragment.get_fragment()
|
|
||||||
if top_level is None:
|
|
||||||
top_level = TopLevel()
|
|
||||||
if sim_runner is None:
|
|
||||||
sim_runner = icarus.Runner()
|
|
||||||
self.top_level = top_level
|
|
||||||
if sys.platform == "win32":
|
|
||||||
sockaddr = ("127.0.0.1", 50007)
|
|
||||||
self.ipc = Initiator(sockaddr)
|
|
||||||
else:
|
|
||||||
self.ipc = Initiator(sockaddr)
|
|
||||||
|
|
||||||
self.sim_runner = sim_runner
|
|
||||||
|
|
||||||
c_top = self.top_level.get(sockaddr)
|
|
||||||
|
|
||||||
fragment = fragment + _Fragment(clock_domains=top_level.clock_domains)
|
|
||||||
c_fragment = verilog.convert(fragment,
|
|
||||||
ios=self.top_level.ios,
|
|
||||||
name=self.top_level.dut_type,
|
|
||||||
regular_comb=False,
|
|
||||||
**vopts)
|
|
||||||
self.namespace = c_fragment.ns
|
|
||||||
|
|
||||||
self.cycle_counter = -1
|
|
||||||
|
|
||||||
self.sim_runner = sim_runner
|
|
||||||
self.sim_runner.start(c_top, c_fragment)
|
|
||||||
self.ipc.accept()
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageTick))
|
|
||||||
|
|
||||||
self.sim_functions = fragment.sim
|
|
||||||
self.active_sim_functions = set(f for f in fragment.sim if not hasattr(f, "passive") or not f.passive)
|
|
||||||
self.unreferenced = {}
|
|
||||||
|
|
||||||
def run(self, ncycles=None):
|
|
||||||
counter = 0
|
|
||||||
|
|
||||||
if self.active_sim_functions:
|
|
||||||
if ncycles is None:
|
|
||||||
def continue_simulation():
|
|
||||||
return bool(self.active_sim_functions)
|
|
||||||
else:
|
|
||||||
def continue_simulation():
|
|
||||||
return self.active_sim_functions and counter < ncycles
|
|
||||||
else:
|
|
||||||
if ncycles is None:
|
|
||||||
raise ValueError("No active simulation function present - must specify ncycles to end simulation")
|
|
||||||
def continue_simulation():
|
|
||||||
return counter < ncycles
|
|
||||||
|
|
||||||
while continue_simulation():
|
|
||||||
self.cycle_counter += 1
|
|
||||||
counter += 1
|
|
||||||
self.ipc.send(MessageGo())
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageTick))
|
|
||||||
|
|
||||||
del_list = []
|
|
||||||
for s in self.sim_functions:
|
|
||||||
try:
|
|
||||||
s(self)
|
|
||||||
except StopSimulation:
|
|
||||||
del_list.append(s)
|
|
||||||
for s in del_list:
|
|
||||||
self.sim_functions.remove(s)
|
|
||||||
try:
|
|
||||||
self.active_sim_functions.remove(s)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_unreferenced(self, item, index):
|
|
||||||
try:
|
|
||||||
return self.unreferenced[(item, index)]
|
|
||||||
except KeyError:
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
try:
|
|
||||||
init = item.init[index]
|
|
||||||
except (TypeError, IndexError):
|
|
||||||
init = 0
|
|
||||||
else:
|
|
||||||
init = item.reset
|
|
||||||
self.unreferenced[(item, index)] = init
|
|
||||||
return init
|
|
||||||
|
|
||||||
def rd(self, item, index=0):
|
|
||||||
try:
|
|
||||||
name = self.top_level.top_name + "." \
|
|
||||||
+ self.top_level.dut_name + "." \
|
|
||||||
+ self.namespace.get_name(item)
|
|
||||||
self.ipc.send(MessageRead(name, Int32(index)))
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageReadReply))
|
|
||||||
value = reply.value
|
|
||||||
except KeyError:
|
|
||||||
value = self.get_unreferenced(item, index)
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
signed = False
|
|
||||||
nbits = item.width
|
|
||||||
else:
|
|
||||||
signed = item.signed
|
|
||||||
nbits = len(item)
|
|
||||||
value = value & (2**nbits - 1)
|
|
||||||
if signed and (value & 2**(nbits - 1)):
|
|
||||||
value -= 2**nbits
|
|
||||||
return value
|
|
||||||
|
|
||||||
def wr(self, item, value, index=0):
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
nbits = item.width
|
|
||||||
else:
|
|
||||||
nbits = len(item)
|
|
||||||
if value < 0:
|
|
||||||
value += 2**nbits
|
|
||||||
assert(value >= 0 and value < 2**nbits)
|
|
||||||
try:
|
|
||||||
name = self.top_level.top_name + "." \
|
|
||||||
+ self.top_level.dut_name + "." \
|
|
||||||
+ self.namespace.get_name(item)
|
|
||||||
self.ipc.send(MessageWrite(name, Int32(index), value))
|
|
||||||
except KeyError:
|
|
||||||
self.unreferenced[(item, index)] = value
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
if hasattr(self, "ipc"):
|
|
||||||
warnings.warn("call Simulator.close() to clean up "
|
|
||||||
"or use it as a contextmanager", DeprecationWarning)
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ipc.close()
|
|
||||||
self.sim_runner.close()
|
|
||||||
del self.ipc
|
|
||||||
del self.sim_runner
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
|
|
||||||
def run_simulation(fragment, ncycles=None, vcd_name=None, **kwargs):
|
|
||||||
with Simulator(fragment, TopLevel(vcd_name), icarus.Runner(**kwargs)) as s:
|
|
||||||
s.run(ncycles)
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
# License: GPLv3 with additional permissions (see README).
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
|
||||||
def __init__(self, options=None, extra_files=None, top_file="migensim_top.v", dut_file="migensim_dut.v", vvp_file=None, keep_files=False):
|
|
||||||
if extra_files is None: extra_files = []
|
|
||||||
if vvp_file is None: vvp_file = dut_file + "vp"
|
|
||||||
if options is None: options = []
|
|
||||||
self.options = options
|
|
||||||
self.extra_files = extra_files
|
|
||||||
self.top_file = top_file
|
|
||||||
self.dut_file = dut_file
|
|
||||||
self.vvp_file = vvp_file
|
|
||||||
self.data_files = []
|
|
||||||
self.keep_files = keep_files
|
|
||||||
|
|
||||||
def start(self, c_top, c_dut):
|
|
||||||
with open(self.top_file, "w") as f:
|
|
||||||
f.write(c_top)
|
|
||||||
c_dut.write(self.dut_file)
|
|
||||||
self.data_files += c_dut.data_files.keys()
|
|
||||||
subprocess.check_call(["iverilog", "-o", self.vvp_file] + self.options + [self.top_file, self.dut_file] + self.extra_files)
|
|
||||||
self.process = subprocess.Popen(["vvp", "-mmigensim", "-Mvpi", self.vvp_file])
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if hasattr(self, "process"):
|
|
||||||
self.process.terminate()
|
|
||||||
if self.process.poll() is None:
|
|
||||||
time.sleep(.1)
|
|
||||||
self.process.kill()
|
|
||||||
self.process.wait()
|
|
||||||
if not self.keep_files:
|
|
||||||
for f in [self.top_file, self.dut_file, self.vvp_file] + self.data_files:
|
|
||||||
try:
|
|
||||||
os.remove(f)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
self.data_files.clear()
|
|
|
@ -1,228 +0,0 @@
|
||||||
# Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
# License: GPLv3 with additional permissions (see README).
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import struct
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
|
||||||
header_len = 2
|
|
||||||
|
|
||||||
#
|
|
||||||
# Message classes
|
|
||||||
#
|
|
||||||
|
|
||||||
class Int32(int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
|
||||||
def __init__(self, *pvalues):
|
|
||||||
for parameter, value in zip(self.parameters, pvalues):
|
|
||||||
setattr(self, parameter[1], parameter[0](value))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
p = []
|
|
||||||
for parameter in self.parameters:
|
|
||||||
p.append(parameter[1] + "=" + str(getattr(self, parameter[1])))
|
|
||||||
if p:
|
|
||||||
pf = " " + " ".join(p)
|
|
||||||
else:
|
|
||||||
pf = ""
|
|
||||||
return "<" + self.__class__.__name__ + pf + ">"
|
|
||||||
|
|
||||||
|
|
||||||
class MessageTick(Message):
|
|
||||||
code = 0
|
|
||||||
parameters = []
|
|
||||||
|
|
||||||
|
|
||||||
class MessageGo(Message):
|
|
||||||
code = 1
|
|
||||||
parameters = []
|
|
||||||
|
|
||||||
|
|
||||||
class MessageWrite(Message):
|
|
||||||
code = 2
|
|
||||||
parameters = [(str, "name"), (Int32, "index"), (int, "value")]
|
|
||||||
|
|
||||||
|
|
||||||
class MessageRead(Message):
|
|
||||||
code = 3
|
|
||||||
parameters = [(str, "name"), (Int32, "index")]
|
|
||||||
|
|
||||||
|
|
||||||
class MessageReadReply(Message):
|
|
||||||
code = 4
|
|
||||||
parameters = [(int, "value")]
|
|
||||||
|
|
||||||
message_classes = [MessageTick, MessageGo, MessageWrite, MessageRead, MessageReadReply]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Packing
|
|
||||||
#
|
|
||||||
|
|
||||||
def _pack_int(v):
|
|
||||||
if v == 0:
|
|
||||||
p = [1, 0]
|
|
||||||
else:
|
|
||||||
p = []
|
|
||||||
while v != 0:
|
|
||||||
p.append(v & 0xff)
|
|
||||||
v >>= 8
|
|
||||||
p.insert(0, len(p))
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_str(v):
|
|
||||||
p = [ord(c) for c in v]
|
|
||||||
p.append(0)
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_int16(v):
|
|
||||||
return [v & 0xff,
|
|
||||||
(v & 0xff00) >> 8]
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_int32(v):
|
|
||||||
return [
|
|
||||||
v & 0xff,
|
|
||||||
(v & 0xff00) >> 8,
|
|
||||||
(v & 0xff0000) >> 16,
|
|
||||||
(v & 0xff000000) >> 24
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _pack(message):
|
|
||||||
r = [message.code]
|
|
||||||
for t, p in message.parameters:
|
|
||||||
value = getattr(message, p)
|
|
||||||
assert(isinstance(value, t))
|
|
||||||
if t == int:
|
|
||||||
r += _pack_int(value)
|
|
||||||
elif t == str:
|
|
||||||
r += _pack_str(value)
|
|
||||||
elif t == Int32:
|
|
||||||
r += _pack_int32(value)
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
if sys.platform == "win32":
|
|
||||||
size = _pack_int16(len(r) + header_len)
|
|
||||||
r = size + r
|
|
||||||
return bytes(r)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Unpacking
|
|
||||||
#
|
|
||||||
|
|
||||||
def _unpack_int(i, nchunks=None):
|
|
||||||
v = 0
|
|
||||||
power = 1
|
|
||||||
if nchunks is None:
|
|
||||||
nchunks = next(i)
|
|
||||||
for j in range(nchunks):
|
|
||||||
v += power*next(i)
|
|
||||||
power *= 256
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def _unpack_str(i):
|
|
||||||
v = ""
|
|
||||||
c = next(i)
|
|
||||||
while c:
|
|
||||||
v += chr(c)
|
|
||||||
c = next(i)
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def _unpack(message):
|
|
||||||
i = iter(message)
|
|
||||||
code = next(i)
|
|
||||||
msgclass = next(filter(lambda x: x.code == code, message_classes))
|
|
||||||
pvalues = []
|
|
||||||
for t, p in msgclass.parameters:
|
|
||||||
if t == int:
|
|
||||||
v = _unpack_int(i)
|
|
||||||
elif t == str:
|
|
||||||
v = _unpack_str(i)
|
|
||||||
elif t == Int32:
|
|
||||||
v = _unpack_int(i, 4)
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
pvalues.append(v)
|
|
||||||
return msgclass(*pvalues)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# I/O
|
|
||||||
#
|
|
||||||
|
|
||||||
class PacketTooLarge(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Initiator:
|
|
||||||
def __init__(self, sockaddr):
|
|
||||||
self.sockaddr = sockaddr
|
|
||||||
if sys.platform == "win32":
|
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
else:
|
|
||||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
|
||||||
self._cleanup_file()
|
|
||||||
self.socket.bind(self.sockaddr)
|
|
||||||
self.socket.listen(1)
|
|
||||||
|
|
||||||
self.ipc_rxbuffer = bytearray()
|
|
||||||
|
|
||||||
def _cleanup_file(self):
|
|
||||||
try:
|
|
||||||
os.remove(self.sockaddr)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
self.conn, addr = self.socket.accept()
|
|
||||||
|
|
||||||
def send(self, message):
|
|
||||||
self.conn.send(_pack(message))
|
|
||||||
|
|
||||||
def recv_packet(self, maxlen):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
while len(self.ipc_rxbuffer) < header_len:
|
|
||||||
self.ipc_rxbuffer += self.conn.recv(maxlen)
|
|
||||||
packet_len = struct.unpack("<H", self.ipc_rxbuffer[:header_len])[0]
|
|
||||||
while len(self.ipc_rxbuffer) < packet_len:
|
|
||||||
self.ipc_rxbuffer += self.conn.recv(maxlen)
|
|
||||||
packet = self.ipc_rxbuffer[header_len:packet_len]
|
|
||||||
self.ipc_rxbuffer = self.ipc_rxbuffer[packet_len:]
|
|
||||||
else:
|
|
||||||
packet = self.conn.recv(maxlen)
|
|
||||||
return packet
|
|
||||||
|
|
||||||
def recv(self):
|
|
||||||
maxlen = 2048
|
|
||||||
packet = self.recv_packet(maxlen)
|
|
||||||
if len(packet) < 1:
|
|
||||||
return None
|
|
||||||
if len(packet) >= maxlen:
|
|
||||||
raise PacketTooLarge
|
|
||||||
return _unpack(packet)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if hasattr(self, "conn"):
|
|
||||||
self.conn.shutdown(socket.SHUT_RDWR)
|
|
||||||
self.conn.close()
|
|
||||||
if hasattr(self, "socket"):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
# don't shutdown our socket since closing connection
|
|
||||||
# seems to already have done it. (trigger an error
|
|
||||||
# otherwise)
|
|
||||||
self.socket.close()
|
|
||||||
else:
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
self.socket.close()
|
|
||||||
self._cleanup_file()
|
|
|
@ -1,112 +0,0 @@
|
||||||
from litex.gen.fhdl.structure import Signal, StopSimulation
|
|
||||||
from litex.gen.fhdl.specials import Memory
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryProxy:
|
|
||||||
def __init__(self, simulator, obj):
|
|
||||||
self.simulator = simulator
|
|
||||||
self._simproxy_obj = obj
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if isinstance(key, int):
|
|
||||||
return self.simulator.rd(self._simproxy_obj, key)
|
|
||||||
else:
|
|
||||||
start, stop, step = key.indices(self._simproxy_obj.depth)
|
|
||||||
return [self.simulator.rd(self._simproxy_obj, i) for i in range(start, stop, step)]
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if isinstance(key, int):
|
|
||||||
self.simulator.wr(self._simproxy_obj, key, value)
|
|
||||||
else:
|
|
||||||
start, stop, step = key.indices(self.__obj.depth)
|
|
||||||
if len(value) != (stop - start)//step:
|
|
||||||
raise ValueError
|
|
||||||
for i, v in zip(range(start, stop, step), value):
|
|
||||||
self.simulator.wr(self._simproxy_obj, i, v)
|
|
||||||
|
|
||||||
|
|
||||||
class Proxy:
|
|
||||||
def __init__(self, simulator, obj):
|
|
||||||
object.__setattr__(self, "simulator", simulator)
|
|
||||||
object.__setattr__(self, "_simproxy_obj", obj)
|
|
||||||
|
|
||||||
def __process_get(self, item):
|
|
||||||
if isinstance(item, Signal):
|
|
||||||
return self.simulator.rd(item)
|
|
||||||
elif isinstance(item, Memory):
|
|
||||||
return MemoryProxy(self.simulator, item)
|
|
||||||
else:
|
|
||||||
return Proxy(self.simulator, item)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return self.__process_get(getattr(self._simproxy_obj, name))
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
item = getattr(self._simproxy_obj, name)
|
|
||||||
assert(isinstance(item, Signal))
|
|
||||||
self.simulator.wr(item, value)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.__process_get(self._simproxy_obj[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
item = self._simproxy_obj[key]
|
|
||||||
assert(isinstance(item, Signal))
|
|
||||||
self.simulator.wr(item, value)
|
|
||||||
|
|
||||||
|
|
||||||
def gen_sim(simg):
|
|
||||||
gens = dict()
|
|
||||||
resume_cycle = 0
|
|
||||||
|
|
||||||
def do_simulation(s):
|
|
||||||
nonlocal resume_cycle, gens
|
|
||||||
|
|
||||||
if isinstance(s, Proxy):
|
|
||||||
simulator = s.simulator
|
|
||||||
else:
|
|
||||||
simulator = s
|
|
||||||
|
|
||||||
if simulator.cycle_counter >= resume_cycle:
|
|
||||||
try:
|
|
||||||
gen = gens[simulator]
|
|
||||||
except KeyError:
|
|
||||||
gen = simg(s)
|
|
||||||
gens[simulator] = gen
|
|
||||||
try:
|
|
||||||
n = next(gen)
|
|
||||||
except StopIteration:
|
|
||||||
del gens[simulator]
|
|
||||||
raise StopSimulation
|
|
||||||
else:
|
|
||||||
if n is None:
|
|
||||||
n = 1
|
|
||||||
resume_cycle = simulator.cycle_counter + n
|
|
||||||
|
|
||||||
if hasattr(simg, "passive"):
|
|
||||||
do_simulation.passive = simg.passive
|
|
||||||
|
|
||||||
return do_simulation
|
|
||||||
|
|
||||||
|
|
||||||
def proxy_sim(target, simf):
|
|
||||||
proxies = dict()
|
|
||||||
|
|
||||||
def do_simulation(simulator):
|
|
||||||
nonlocal proxies
|
|
||||||
|
|
||||||
try:
|
|
||||||
proxy = proxies[simulator]
|
|
||||||
except KeyError:
|
|
||||||
proxy = Proxy(simulator, target)
|
|
||||||
proxies[simulator] = proxy
|
|
||||||
try:
|
|
||||||
simf(proxy)
|
|
||||||
except StopSimulation:
|
|
||||||
del proxies[simulator]
|
|
||||||
raise
|
|
||||||
|
|
||||||
if hasattr(simf, "passive"):
|
|
||||||
do_simulation.passive = simf.passive
|
|
||||||
|
|
||||||
return do_simulation
|
|
Loading…
Reference in a new issue