From e1702c422c8141c4010eef0c61f8099825512d82 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 8 Apr 2015 20:28:23 +0800 Subject: [PATCH] introduce conversion output object (prevents file IO in FHDL backends) --- mibuild/altera/quartus.py | 8 ++++---- mibuild/generic_platform.py | 13 ++----------- mibuild/lattice/diamond.py | 8 ++++---- mibuild/sim/verilator.py | 11 +++++------ mibuild/xilinx/ise.py | 10 ++++++---- mibuild/xilinx/vivado.py | 8 ++++---- migen/fhdl/conv_output.py | 35 +++++++++++++++++++++++++++++++++++ migen/fhdl/edif.py | 25 ++++++++++++++----------- migen/fhdl/specials.py | 20 ++++++++------------ migen/fhdl/verilog.py | 26 +++++++++++++------------- 10 files changed, 95 insertions(+), 69 deletions(-) create mode 100644 migen/fhdl/conv_output.py diff --git a/mibuild/altera/quartus.py b/mibuild/altera/quartus.py index 0c0a99543..bdf14a685 100644 --- a/mibuild/altera/quartus.py +++ b/mibuild/altera/quartus.py @@ -81,10 +81,10 @@ class AlteraQuartusToolchain: fragment = fragment.get_fragment() platform.finalize(fragment) - v_src, vns = platform.get_verilog(fragment) - named_sc, named_pc = platform.resolve_signals(vns) + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" - tools.write_to_file(v_file, v_src) + v_output.write(v_file) sources = platform.sources | {(v_file, "verilog")} _build_files(platform.device, sources, platform.verilog_include_paths, named_sc, named_pc, build_name) if run: @@ -92,7 +92,7 @@ class AlteraQuartusToolchain: os.chdir("..") - return vns + return v_output.ns def add_period_constraint(self, platform, clk, period): # TODO: handle differential clk diff --git a/mibuild/generic_platform.py b/mibuild/generic_platform.py index 6d3e77e8c..4c13fe72d 100644 --- a/mibuild/generic_platform.py +++ b/mibuild/generic_platform.py @@ -265,20 +265,11 @@ class GenericPlatform: named_pc.append(template.format(**name_dict)) return named_sc, named_pc - def _get_source(self, fragment, gen_fn): - if not isinstance(fragment, _Fragment): - fragment = fragment.get_fragment() - # generate source - src, vns = gen_fn(fragment) - return src, vns - def get_verilog(self, fragment, **kwargs): - return self._get_source(fragment, lambda f: verilog.convert(f, self.constraint_manager.get_io_signals(), - return_ns=True, create_clock_domains=False, **kwargs)) + return verilog.convert(fragment, self.constraint_manager.get_io_signals(), create_clock_domains=False, **kwargs) def get_edif(self, fragment, cell_library, vendor, device, **kwargs): - return self._get_source(fragment, lambda f: edif.convert(f, self.constraint_manager.get_io_signals(), - cell_library, vendor, device, return_ns=True, **kwargs)) + return edif.convert(fragment, self.constraint_manager.get_io_signals(), cell_library, vendor, device, **kwargs) def build(self, fragment): raise NotImplementedError("GenericPlatform.build must be overloaded") diff --git a/mibuild/lattice/diamond.py b/mibuild/lattice/diamond.py index 3a12456cf..b04f31546 100644 --- a/mibuild/lattice/diamond.py +++ b/mibuild/lattice/diamond.py @@ -75,10 +75,10 @@ class LatticeDiamondToolchain: fragment = fragment.get_fragment() platform.finalize(fragment) - v_src, vns = platform.get_verilog(fragment) - named_sc, named_pc = platform.resolve_signals(vns) + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" - tools.write_to_file(v_file, v_src) + v_output.write(v_file) sources = platform.sources + [(v_file, "verilog")] _build_files(platform.device, sources, platform.verilog_include_paths, build_name) @@ -89,7 +89,7 @@ class LatticeDiamondToolchain: os.chdir("..") - return vns + return v_output.ns def add_period_constraint(self, platform, clk, period): # TODO: handle differential clk diff --git a/mibuild/sim/verilator.py b/mibuild/sim/verilator.py index b3d221d52..5a4bd251d 100644 --- a/mibuild/sim/verilator.py +++ b/mibuild/sim/verilator.py @@ -127,10 +127,9 @@ class SimVerilatorToolchain: fragment = fragment.get_fragment() platform.finalize(fragment) - v_src, vns = platform.get_verilog(fragment) - named_sc, named_pc = platform.resolve_signals(vns) - v_file = "dut.v" - tools.write_to_file(v_file, v_src) + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_output.write("dut.v") include_paths = [] for source in platform.sources: @@ -138,11 +137,11 @@ class SimVerilatorToolchain: if path not in include_paths: include_paths.append(path) include_paths += platform.verilog_include_paths - _build_sim(platform, vns, build_name, include_paths, sim_path, serial, verbose) + _build_sim(platform, v_output.ns, build_name, include_paths, sim_path, serial, verbose) if run: _run_sim(build_name) os.chdir("..") - return vns + return v_output.ns diff --git a/mibuild/xilinx/ise.py b/mibuild/xilinx/ise.py index 703e1bcca..1ab01b91e 100644 --- a/mibuild/xilinx/ise.py +++ b/mibuild/xilinx/ise.py @@ -145,10 +145,11 @@ class XilinxISEToolchain: vns = None if mode == "xst" or mode == "yosys": - v_src, vns = platform.get_verilog(fragment) + v_output = platform.get_verilog(fragment) + vns = v_output.ns named_sc, named_pc = platform.resolve_signals(vns) v_file = build_name + ".v" - tools.write_to_file(v_file, v_src) + v_output.write(v_file) sources = platform.sources | {(v_file, "verilog")} if mode == "xst": _build_xst_files(platform.device, sources, platform.verilog_include_paths, build_name, self.xst_opt) @@ -163,10 +164,11 @@ class XilinxISEToolchain: synthesize(fragment, platform.constraint_manager.get_io_signals()) if mode == "edif" or mode == "mist": - e_src, vns = platform.get_edif(fragment) + e_output = platform.get_edif(fragment) + vns = e_output.ns named_sc, named_pc = platform.resolve_signals(vns) e_file = build_name + ".edif" - tools.write_to_file(e_file, e_src) + e_output.write(e_file) isemode = "edif" tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc)) diff --git a/mibuild/xilinx/vivado.py b/mibuild/xilinx/vivado.py index 065552e5f..5ef4c2974 100644 --- a/mibuild/xilinx/vivado.py +++ b/mibuild/xilinx/vivado.py @@ -111,10 +111,10 @@ class XilinxVivadoToolchain: if not isinstance(fragment, _Fragment): fragment = fragment.get_fragment() platform.finalize(fragment) - v_src, vns = platform.get_verilog(fragment) - named_sc, named_pc = platform.resolve_signals(vns) + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" - tools.write_to_file(v_file, v_src) + v_output.write(v_file) sources = platform.sources | {(v_file, "verilog")} self._build_batch(platform, sources, build_name) tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc)) @@ -123,7 +123,7 @@ class XilinxVivadoToolchain: os.chdir("..") - return vns + return v_output.ns def add_period_constraint(self, platform, clk, period): platform.add_platform_command("""create_clock -name {clk} -period """ + \ diff --git a/migen/fhdl/conv_output.py b/migen/fhdl/conv_output.py new file mode 100644 index 000000000..793fad200 --- /dev/null +++ b/migen/fhdl/conv_output.py @@ -0,0 +1,35 @@ +from operator import itemgetter + + +class ConvOutput: + def __init__(self): + self.main_source = "" + self.data_files = dict() + + def set_main_source(self, src): + self.main_source = src + + def add_data_file(self, filename_base, content): + filename = filename_base + i = 1 + while filename in self.data_files: + parts = filename_base.split(".", maxsplit=1) + parts[0] += "_" + str(i) + filename = ".".join(parts) + i += 1 + self.data_files[filename] = content + return filename + + def __str__(self): + r = self.main_source + "\n" + for filename, content in sorted(self.data_files.items(), + key=itemgetter(0)): + r += filename + ":\n" + content + return r + + def write(self, main_filename): + with open(main_filename, "w") as f: + f.write(self.main_source) + for filename, content in self.data_files.items(): + with open(filename, "w") as f: + f.write(content) diff --git a/migen/fhdl/edif.py b/migen/fhdl/edif.py index de5e4f3e3..a3acc4668 100644 --- a/migen/fhdl/edif.py +++ b/migen/fhdl/edif.py @@ -1,10 +1,12 @@ from collections import OrderedDict +from collections import namedtuple + from migen.fhdl.std import * from migen.fhdl.namer import build_namespace from migen.fhdl.tools import list_special_ios from migen.fhdl.structure import _Fragment +from migen.fhdl.conv_output import ConvOutput -from collections import namedtuple _Port = namedtuple("_Port", "name direction") _Cell = namedtuple("_Cell", "name ports") @@ -125,7 +127,7 @@ def _generate_cells(f): else: cell_dict[special.of] = port_list else: - raise ValueError("Edif conversion can only handle synthesized fragments") + raise ValueError("EDIF conversion can only handle synthesized fragments") return [_Cell(k, v) for k, v in cell_dict.items()] def _generate_instances(f,ns): @@ -146,7 +148,7 @@ def _generate_instances(f,ns): raise NotImplementedError("Unsupported instance item") instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props)) else: - raise ValueError("Edif conversion can only handle synthesized fragments") + raise ValueError("EDIF conversion can only handle synthesized fragments") return instances def _generate_ios(f, ios, ns): @@ -174,7 +176,7 @@ def _generate_connections(f, ios, ns): else: raise NotImplementedError("Unsupported instance item") else: - raise ValueError("Edif conversion can only handle synthesized fragments") + raise ValueError("EDIF conversion can only handle synthesized fragments") for s in ios: io = ns.get_name(s) if io not in r: @@ -182,11 +184,11 @@ def _generate_connections(f, ios, ns): r[io].append(_NetBranch(portname=io, instancename="")) return r -def convert(f, ios, cell_library, vendor, device, name="top", return_ns=False): +def convert(f, ios, cell_library, vendor, device, name="top"): if not isinstance(f, _Fragment): f = f.get_fragment() if f.comb != [] or f.sync != {}: - raise ValueError("Edif conversion can only handle synthesized fragments") + raise ValueError("EDIF conversion can only handle synthesized fragments") if ios is None: ios = set() cells = _generate_cells(f) @@ -194,8 +196,9 @@ def convert(f, ios, cell_library, vendor, device, name="top", return_ns=False): instances = _generate_instances(f, ns) inouts = _generate_ios(f, ios, ns) connections = _generate_connections(f, ios, ns) - r = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor) - if return_ns: - return r, ns - else: - return r + src = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor) + + r = ConvOutput() + r.set_main_source(src) + r.ns = ns + return r diff --git a/migen/fhdl/specials.py b/migen/fhdl/specials.py index 768f55e4b..764fa7912 100644 --- a/migen/fhdl/specials.py +++ b/migen/fhdl/specials.py @@ -48,7 +48,7 @@ class Tristate(Special): yield self, attr, target_context @staticmethod - def emit_verilog(tristate, ns): + def emit_verilog(tristate, ns, add_data_file): def pe(e): return verilog_printexpr(ns, e)[0] w, s = value_bits_sign(tristate.target) @@ -123,7 +123,7 @@ class Instance(Special): yield item, "expr", SPECIAL_INOUT @staticmethod - def emit_verilog(instance, ns): + def emit_verilog(instance, ns, add_data_file): r = instance.of + " " parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items)) if parameters: @@ -198,7 +198,7 @@ class _MemoryPort(Special): yield self, attr, target_context @staticmethod - def emit_verilog(port, ns): + def emit_verilog(port, ns, add_data_file): return "" # done by parent Memory object class Memory(Special): @@ -237,7 +237,7 @@ class Memory(Special): return mp @staticmethod - def emit_verilog(memory, ns): + def emit_verilog(memory, ns, add_data_file): r = "" def gn(e): if isinstance(e, Memory): @@ -307,14 +307,10 @@ class Memory(Special): r += "\n" if memory.init is not None: - memory_filename = gn(memory) + ".init" - - # XXX move I/O to mibuild? - # (Implies mem init won't work with simple Migen examples?) - f = open(memory_filename, "w") + content = "" for d in memory.init: - f.write("{:x}\n".format(d)) - f.close() + content += "{:x}\n".format(d) + memory_filename = add_data_file(gn(memory) + ".init", content) r += "initial begin\n" r += "$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n" @@ -330,7 +326,7 @@ class SynthesisDirective(Special): self.signals = signals @staticmethod - def emit_verilog(directive, ns): + def emit_verilog(directive, ns, add_data_file): name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items()) formatted = directive.template.format(**name_dict) return "// synthesis " + formatted + "\n" diff --git a/migen/fhdl/verilog.py b/migen/fhdl/verilog.py index bc0504414..c1cfc690e 100644 --- a/migen/fhdl/verilog.py +++ b/migen/fhdl/verilog.py @@ -6,6 +6,7 @@ from migen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment from migen.fhdl.tools import * from migen.fhdl.bitcontainer import bits_for, flen from migen.fhdl.namer import Namespace, build_namespace +from migen.fhdl.conv_output import ConvOutput def _printsig(ns, s): if s.signed: @@ -257,20 +258,20 @@ def _lower_specials(overrides, specials): f.specials -= lowered_specials2 return f, lowered_specials -def _printspecials(overrides, specials, ns): +def _printspecials(overrides, specials, ns, add_data_file): r = "" for special in sorted(specials, key=lambda x: x.huid): - pr = _call_special_classmethod(overrides, special, "emit_verilog", ns) + pr = _call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file) if pr is None: raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog") r += pr return r def convert(f, ios=None, name="top", - return_ns=False, special_overrides=dict(), create_clock_domains=True, display_run=False): + r = ConvOutput() if not isinstance(f, _Fragment): f = f.get_fragment() if ios is None: @@ -296,15 +297,14 @@ def convert(f, ios=None, name="top", ns = build_namespace(list_signals(f) \ | list_special_ios(f, True, True, True) \ | ios) + r.ns = ns - r = "/* Machine-generated using Migen */\n" - r += _printheader(f, ios, name, ns) - r += _printcomb(f, ns, display_run) - r += _printsync(f, ns) - r += _printspecials(special_overrides, f.specials - lowered_specials, ns) - r += "endmodule\n" + src = "/* Machine-generated using Migen */\n" + src += _printheader(f, ios, name, ns) + src += _printcomb(f, ns, display_run) + src += _printsync(f, ns) + src += _printspecials(special_overrides, f.specials - lowered_specials, ns, r.add_data_file) + src += "endmodule\n" + r.set_main_source(src) - if return_ns: - return r, ns - else: - return r + return r