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:
Florent Kermarrec 2016-03-21 18:06:51 +01:00
parent 71a719be44
commit 703b30e078
21 changed files with 151 additions and 783 deletions

View File

@ -168,7 +168,7 @@ class ConstraintManager:
if isinstance(rt, int):
obj = Signal(rt, name_override=resource_name)
else:
obj = Record(rt, name=resource_name, use_name_override=True)
obj = Record(rt, name=resource_name)
for element in resource[2:]:
if isinstance(element, PlatformInfo):
@ -253,6 +253,15 @@ class GenericPlatform:
def add_period_constraint(self, clk, period):
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):
return self.constraint_manager.add_platform_command(*args, **kwargs)

View File

@ -13,7 +13,7 @@ class OpenOCD(GenericProgrammer):
def load_bitstream(self, bitstream):
script = "; ".join([
"init",
"pld load 0 {}".format(bitstream),
"pld load 0 {{{}}}".format(bitstream),
"exit",
])
subprocess.call(["openocd", "-f", self.config, "-c", script])
@ -22,8 +22,8 @@ class OpenOCD(GenericProgrammer):
flash_proxy = self.find_flash_proxy()
script = "; ".join([
"init",
"jtagspi_init 0 {}".format(flash_proxy),
"jtagspi_program {} 0x{:x}".format(data, address),
"jtagspi_init 0 {{{}}}".format(flash_proxy),
"jtagspi_program {{{}}} 0x{:x}".format(data, address),
"fpga_program",
"exit"
])

View File

@ -14,12 +14,12 @@ from litex.build import tools
def settings(path, ver=None, sub=None):
vers = list(tools.versions(path))
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)
else:
ver = StrictVersion(ver)
assert ver in vers
full = os.path.join(path, str(ver))
if sub:
@ -39,7 +39,7 @@ def settings(path, ver=None, sub=None):
if os.path.exists(settings):
return settings
raise OSError("no settings file found")
raise OSError("no Xilinx tools settings file found")
class XilinxNoRetimingVivadoImpl(Module):

View File

@ -181,4 +181,10 @@ class XilinxISEToolchain:
def add_period_constraint(self, platform, clk, period):
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)

View File

@ -32,3 +32,10 @@ class XilinxPlatform(GenericPlatform):
if hasattr(clk, "p"):
clk = clk.p
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)

View File

@ -135,5 +135,11 @@ class XilinxVivadoToolchain:
return v_output.ns
def add_period_constraint(self, platform, clk, period):
platform.add_platform_command("""create_clock -name {clk} -period """ + \
str(period) + """ [get_ports {clk}]""", clk=clk)
platform.add_platform_command(
"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)

View File

@ -4,5 +4,7 @@ from litex.gen.fhdl.specials import *
from litex.gen.fhdl.bitcontainer import *
from litex.gen.fhdl.decorators import *
from litex.gen.sim import *
from litex.gen.genlib.record import *
from litex.gen.genlib.fsm import *

View File

@ -5,7 +5,7 @@ from litex.gen.util.misc import flat_iteration
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import _Fragment
from litex.gen.fhdl.tools import rename_clock_domain
from litex.gen.sim.upper import gen_sim, proxy_sim
__all__ = ["Module", "FinalizeError"]
@ -120,20 +120,7 @@ class Module:
self.finalized = False
return self.finalized
elif name == "_fragment":
simf = None
try:
simf = self.do_simulation
except AttributeError:
try:
simg = self.gen_simulation
except AttributeError:
pass
else:
simf = gen_sim(simg)
if simf is not None:
simf = proxy_sim(self, simf)
sim = [] if simf is None else [simf]
self._fragment = _Fragment(sim=sim)
self._fragment = _Fragment()
return self._fragment
elif name == "_submodules":
self._submodules = []

View File

@ -217,8 +217,8 @@ 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:
swno = {signal for signal in signals if signal.name_override is not None}
for signal in sorted(swno, key=lambda x: x.duid):
ns.get_name(signal)
return ns

View File

@ -55,6 +55,7 @@ class MemoryToArray(ModuleTransformer):
def transform_fragment(self, i, f):
newspecials = set()
processed_ports = set()
for mem in f.specials:
if not isinstance(mem, Memory):
@ -111,4 +112,7 @@ class MemoryToArray(ModuleTransformer):
sync.append(If(port.we,
storage[port.adr].eq(port.dat_w)))
processed_ports.add(port)
newspecials -= processed_ports
f.specials = newspecials

View File

@ -34,7 +34,7 @@ class _Value(DUID):
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)):
or isinstance(a, Signal) and isinstance(b, Constant)):
return False
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 _Slice(self, start, stop)
else:
raise TypeError
raise TypeError("Cannot use type {} ({}) as key".format(
type(key), repr(key)))
def eq(self, r):
"""Assignment
@ -542,16 +543,21 @@ class Case(_Statement):
Parameters
----------
key : int or None
key : int, Constant or None
Key to use as default case if no other key matches.
By default, the largest key is the default key.
"""
if key is None:
for choice in self.cases.keys():
if key is None or choice.value > key.value:
if (key is None
or (isinstance(choice, str) and choice == "default")
or choice.value > key.value):
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]
self.cases["default"] = stmts
return self
@ -679,23 +685,17 @@ class _ClockDomainList(list):
(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
class StopSimulation(Exception):
pass
class _Fragment:
def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None):
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()
if sim is None: sim = []
self.comb = comb
self.sync = sync
self.specials = specials
self.clock_domains = _ClockDomainList(clock_domains)
self.sim = sim
def __add__(self, other):
newsync = _collections.defaultdict(list)
@ -705,8 +705,7 @@ class _Fragment:
newsync[k].extend(v)
return _Fragment(self.comb + other.comb, newsync,
self.specials | other.specials,
self.clock_domains + other.clock_domains,
self.sim + other.sim)
self.clock_domains + other.clock_domains)
def __iadd__(self, other):
newsync = _collections.defaultdict(list)
@ -718,5 +717,4 @@ class _Fragment:
self.sync = newsync
self.specials |= other.specials
self.clock_domains += other.clock_domains
self.sim += other.sim
return self

View File

@ -1,5 +1,5 @@
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.bitcontainer import value_bits_sign
from litex.gen.util.misc import flat_iteration
@ -236,7 +236,7 @@ def _apply_lowerer(l, f):
f = l.visit(f)
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():
if direction != SPECIAL_INOUT:
# inouts are only supported by Migen when connected directly to top-level
@ -296,3 +296,44 @@ def rename_clock_domain(f, old, new):
pass
else:
cd.rename(new)
def call_special_classmethod(overrides, obj, method, *args, **kwargs):
cl = obj.__class__
if cl in overrides:
cl = overrides[cl]
if hasattr(cl, method):
return getattr(cl, method)(obj, *args, **kwargs)
else:
return None
def _lower_specials_step(overrides, specials):
f = _Fragment()
lowered_specials = set()
for special in sorted(specials, key=lambda x: x.duid):
impl = call_special_classmethod(overrides, special, "lower")
if impl is not None:
f += impl.get_fragment()
lowered_specials.add(special)
return f, lowered_specials
def _can_lower(overrides, specials):
for special in specials:
cl = special.__class__
if cl in overrides:
cl = overrides[cl]
if hasattr(cl, "lower"):
return True
return False
def lower_specials(overrides, specials):
f, lowered_specials = _lower_specials_step(overrides, specials)
while _can_lower(overrides, f.specials):
f2, lowered_specials2 = _lower_specials_step(overrides, f.specials)
f += f2
lowered_specials |= lowered_specials2
f.specials -= lowered_specials2
return f, lowered_specials

View File

@ -5,11 +5,9 @@ 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
# TODO: remove printcomb_simulation when we will be using new migen simulator
_reserved_keywords = {
"always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
@ -117,11 +115,9 @@ def _printexpr(ns, node):
(_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:
return ""
elif target_filter is not None and target_filter not in list_targets(node):
return ""
elif isinstance(node, _Assign):
if at == _AT_BLOCKING:
assignment = " = "
@ -133,13 +129,13 @@ def _printnode(ns, at, level, node, target_filter=None):
assignment = " <= "
return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n"
elif isinstance(node, collections.Iterable):
return "".join(_printnode(ns, at, level, n, target_filter) for n in node)
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, target_filter)
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, target_filter)
r += _printnode(ns, at, level + 1, node.f)
r += "\t"*level + "end\n"
return r
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)
for choice, statements in css:
r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n"
r += _printnode(ns, at, level + 2, statements, target_filter)
r += _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"], target_filter)
r += _printnode(ns, at, level + 2, node.cases["default"])
r += "\t"*(level + 1) + "end\n"
r += "\t"*level + "endcase\n"
return r
@ -196,21 +192,18 @@ def _printheader(f, ios, name, ns,
r += "\tinput " + _printsig(ns, sig)
r += "\n);\n\n"
for sig in sorted(sigs - ios, key=lambda x: x.duid):
attributes = ""
if sig.attribute != "":
attributes = "(*" + sig.attribute[:-1] + "*) "
if sig in wires:
r += attributes + "wire " + _printsig(ns, sig) + ";\n"
r += "wire " + _printsig(ns, sig) + ";\n"
else:
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:
r += attributes + "reg " + _printsig(ns, sig) + ";\n"
r += "reg " + _printsig(ns, sig) + ";\n"
r += "\n"
return r
def _printcomb_simulation(f, ns,
def _printcomb(f, ns,
display_run,
dummy_signal,
blocking_assign):
@ -227,22 +220,11 @@ def _printcomb_simulation(f, ns,
r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
r += syn_on
from collections import defaultdict
target_stmt_map = defaultdict(list)
for statement in flat_iteration(f.comb):
targets = list_targets(statement)
for t in targets:
target_stmt_map[t].append(statement)
groups = group_by_targets(f.comb)
for n, (t, stmts) in enumerate(target_stmt_map.items()):
assert isinstance(t, Signal)
if len(stmts) == 1 and isinstance(stmts[0], _Assign):
r += "assign " + _printnode(ns, _AT_BLOCKING, 0, stmts[0])
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")
@ -253,31 +235,6 @@ def _printcomb_simulation(f, ns,
r += "always @(*) begin\n"
if display_run:
r += "\t$display(\"Running comb block #" + str(n) + "\");\n"
if blocking_assign:
r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n"
r += _printnode(ns, _AT_BLOCKING, 1, stmts, t)
else:
r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n"
r += _printnode(ns, _AT_NONBLOCKING, 1, stmts, t)
if dummy_signal:
r += syn_off
r += "\t" + ns.get_name(dummy_d) + " = " + ns.get_name(dummy_s) + ";\n"
r += syn_on
r += "end\n"
r += "\n"
return r
def _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:
for t in g[0]:
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]:
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)):
@ -301,51 +261,10 @@ def _printsync(f, ns):
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)
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
@ -380,25 +299,24 @@ def convert(f, ios=None, name="top",
f = lower_complex_slices(f)
insert_resets(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)
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) \
| list_special_ios(f, True, True, True) \
| ios, _reserved_keywords)
ns.clock_domains = f.clock_domains
r.ns = ns
src = "/* Machine-generated using LiteX gen "
src += "(regular)" if regular_comb else "(simulation)"
src += " */\n"
src = "/* Machine-generated using LiteX gen */\n"
src += _printheader(f, ios, name, ns,
reg_initialization=reg_initialization)
if regular_comb:
src += _printcomb_regular(f, ns,
blocking_assign=blocking_assign)
else:
src += _printcomb_simulation(f, ns,
src += _printcomb(f, ns,
display_run=display_run,
dummy_signal=dummy_signal,
blocking_assign=blocking_assign)

View File

@ -1,4 +1,5 @@
from copy import copy
from operator import itemgetter
from litex.gen.fhdl.structure import *
from litex.gen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy,
@ -77,7 +78,8 @@ class NodeVisitor:
def visit_Case(self, node):
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)
def visit_Fragment(self, node):
@ -89,7 +91,7 @@ class NodeVisitor:
self.visit(statement)
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)
def visit_ArrayProxy(self, node):
@ -177,7 +179,9 @@ class NodeTransformer:
return r
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)
return r
@ -192,7 +196,9 @@ class NodeTransformer:
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())
return {clockname: self.visit(statements)
for clockname, statements in sorted(node.items(),
key=itemgetter(0))}
def visit_ArrayProxy(self, node):
return _ArrayProxy([self.visit(choice) for choice in node.choices],

View File

@ -86,7 +86,7 @@ def layout_partial(layout, *elements):
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.layout = layout
@ -100,9 +100,6 @@ class Record:
fname, fsize, fdirection = f
else:
fname, fsize = f
if use_name_override:
finst = Signal(fsize, name_override=prefix + fname)
else:
finst = Signal(fsize, name=prefix + fname)
elif isinstance(f[1], list): # case 3
fname, fsublayout = f

View File

@ -12,7 +12,7 @@ class BitonicSort(Module):
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

View File

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

View File

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

View File

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

View File

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

View 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