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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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 *
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 = []

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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