From: Florent Kermarrec Date: Mon, 21 Mar 2016 17:06:51 +0000 (+0100) Subject: gen/build: merge with migen 0575c749e35a7180f0dca408e426af8eef22b568 and reintegrate... X-Git-Tag: 24jan2021_ls180~1989 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=703b30e0786f827bde827b2c0e0ea9d04c8da0a4;p=litex.git 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) --- diff --git a/litex/build/generic_platform.py b/litex/build/generic_platform.py index d96e3d76..226bc9a4 100644 --- a/litex/build/generic_platform.py +++ b/litex/build/generic_platform.py @@ -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) diff --git a/litex/build/openocd.py b/litex/build/openocd.py index da83ba5c..ec9753c6 100644 --- a/litex/build/openocd.py +++ b/litex/build/openocd.py @@ -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" ]) diff --git a/litex/build/xilinx/common.py b/litex/build/xilinx/common.py index 2f069e6a..dcd47b25 100644 --- a/litex/build/xilinx/common.py +++ b/litex/build/xilinx/common.py @@ -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): diff --git a/litex/build/xilinx/ise.py b/litex/build/xilinx/ise.py index 374bffc0..65cf36bd 100644 --- a/litex/build/xilinx/ise.py +++ b/litex/build/xilinx/ise.py @@ -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) diff --git a/litex/build/xilinx/platform.py b/litex/build/xilinx/platform.py index b2667810..8a625186 100644 --- a/litex/build/xilinx/platform.py +++ b/litex/build/xilinx/platform.py @@ -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) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index f199c075..424cb1fb 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -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) diff --git a/litex/gen/__init__.py b/litex/gen/__init__.py index 3f70cb1f..264a79e2 100644 --- a/litex/gen/__init__.py +++ b/litex/gen/__init__.py @@ -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 * diff --git a/litex/gen/fhdl/module.py b/litex/gen/fhdl/module.py index 73848bba..a8607514 100644 --- a/litex/gen/fhdl/module.py +++ b/litex/gen/fhdl/module.py @@ -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 = [] diff --git a/litex/gen/fhdl/namer.py b/litex/gen/fhdl/namer.py index 5e8ecdd3..c75480d2 100644 --- a/litex/gen/fhdl/namer.py +++ b/litex/gen/fhdl/namer.py @@ -217,9 +217,9 @@ def build_namespace(signals, reserved_keywords=set()): pnd = _build_pnd(signals) ns = Namespace(pnd, reserved_keywords) # register signals with name_override - for signal in signals: - if signal.name_override is not None: - ns.get_name(signal) + 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 diff --git a/litex/gen/fhdl/simplify.py b/litex/gen/fhdl/simplify.py index 747f5628..c85b19c2 100644 --- a/litex/gen/fhdl/simplify.py +++ b/litex/gen/fhdl/simplify.py @@ -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 diff --git a/litex/gen/fhdl/structure.py b/litex/gen/fhdl/structure.py index 3316571d..959b6ce9 100644 --- a/litex/gen/fhdl/structure.py +++ b/litex/gen/fhdl/structure.py @@ -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 @@ -525,7 +526,7 @@ class Case(_Statement): for k, v in cases.items(): if isinstance(k, (bool, int)): k = Constant(k) - if (not isinstance(k, Constant) + if (not isinstance(k, Constant) and not (isinstance(k, str) and k == "default")): raise TypeError("Case object is not a Migen constant") if not isinstance(v, _collections.Iterable): @@ -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 diff --git a/litex/gen/fhdl/tools.py b/litex/gen/fhdl/tools.py index 3db91831..8da2f496 100644 --- a/litex/gen/fhdl/tools.py +++ b/litex/gen/fhdl/tools.py @@ -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 diff --git a/litex/gen/fhdl/verilog.py b/litex/gen/fhdl/verilog.py index 1dc4635c..b0906182 100644 --- a/litex/gen/fhdl/verilog.py +++ b/litex/gen/fhdl/verilog.py @@ -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,24 +192,21 @@ 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, - display_run, - dummy_signal, - blocking_assign): +def _printcomb(f, ns, + display_run, + dummy_signal, + blocking_assign): r = "" if f.comb: if dummy_signal: @@ -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) diff --git a/litex/gen/fhdl/visit.py b/litex/gen/fhdl/visit.py index 0ccd5095..84bb8adc 100644 --- a/litex/gen/fhdl/visit.py +++ b/litex/gen/fhdl/visit.py @@ -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], diff --git a/litex/gen/genlib/record.py b/litex/gen/genlib/record.py index f31a2c24..0038b069 100644 --- a/litex/gen/genlib/record.py +++ b/litex/gen/genlib/record.py @@ -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,10 +100,7 @@ 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) + finst = Signal(fsize, name=prefix + fname) elif isinstance(f[1], list): # case 3 fname, fsublayout = f finst = Record(fsublayout, prefix + fname) diff --git a/litex/gen/genlib/sort.py b/litex/gen/genlib/sort.py index 4c1f46c4..ff5b6175 100644 --- a/litex/gen/genlib/sort.py +++ b/litex/gen/genlib/sort.py @@ -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 diff --git a/litex/gen/sim/__init__.py b/litex/gen/sim/__init__.py index e69de29b..853486a6 100644 --- a/litex/gen/sim/__init__.py +++ b/litex/gen/sim/__init__.py @@ -0,0 +1 @@ +from litex.gen.sim.core import Simulator, run_simulation, passive diff --git a/litex/gen/sim/generic.py b/litex/gen/sim/generic.py deleted file mode 100644 index 1144ac88..00000000 --- a/litex/gen/sim/generic.py +++ /dev/null @@ -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) - diff --git a/litex/gen/sim/icarus.py b/litex/gen/sim/icarus.py deleted file mode 100644 index 59dc2b7d..00000000 --- a/litex/gen/sim/icarus.py +++ /dev/null @@ -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() diff --git a/litex/gen/sim/ipc.py b/litex/gen/sim/ipc.py deleted file mode 100644 index c6124bb6..00000000 --- a/litex/gen/sim/ipc.py +++ /dev/null @@ -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("= 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() diff --git a/litex/gen/sim/upper.py b/litex/gen/sim/upper.py deleted file mode 100644 index 03733775..00000000 --- a/litex/gen/sim/upper.py +++ /dev/null @@ -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