introduce conversion output object (prevents file IO in FHDL backends)

This commit is contained in:
Sebastien Bourdeauducq 2015-04-08 20:28:23 +08:00
parent 8ce683964a
commit e1702c422c
10 changed files with 95 additions and 69 deletions

View file

@ -81,10 +81,10 @@ class AlteraQuartusToolchain:
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(fragment) platform.finalize(fragment)
v_src, vns = platform.get_verilog(fragment) v_output = platform.get_verilog(fragment)
named_sc, named_pc = platform.resolve_signals(vns) named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v" v_file = build_name + ".v"
tools.write_to_file(v_file, v_src) v_output.write(v_file)
sources = platform.sources | {(v_file, "verilog")} sources = platform.sources | {(v_file, "verilog")}
_build_files(platform.device, sources, platform.verilog_include_paths, named_sc, named_pc, build_name) _build_files(platform.device, sources, platform.verilog_include_paths, named_sc, named_pc, build_name)
if run: if run:
@ -92,7 +92,7 @@ class AlteraQuartusToolchain:
os.chdir("..") os.chdir("..")
return vns return v_output.ns
def add_period_constraint(self, platform, clk, period): def add_period_constraint(self, platform, clk, period):
# TODO: handle differential clk # TODO: handle differential clk

View file

@ -265,20 +265,11 @@ class GenericPlatform:
named_pc.append(template.format(**name_dict)) named_pc.append(template.format(**name_dict))
return named_sc, named_pc 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): def get_verilog(self, fragment, **kwargs):
return self._get_source(fragment, lambda f: verilog.convert(f, self.constraint_manager.get_io_signals(), return verilog.convert(fragment, self.constraint_manager.get_io_signals(), create_clock_domains=False, **kwargs)
return_ns=True, create_clock_domains=False, **kwargs))
def get_edif(self, fragment, cell_library, vendor, device, **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(), return edif.convert(fragment, self.constraint_manager.get_io_signals(), cell_library, vendor, device, **kwargs)
cell_library, vendor, device, return_ns=True, **kwargs))
def build(self, fragment): def build(self, fragment):
raise NotImplementedError("GenericPlatform.build must be overloaded") raise NotImplementedError("GenericPlatform.build must be overloaded")

View file

@ -75,10 +75,10 @@ class LatticeDiamondToolchain:
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(fragment) platform.finalize(fragment)
v_src, vns = platform.get_verilog(fragment) v_output = platform.get_verilog(fragment)
named_sc, named_pc = platform.resolve_signals(vns) named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v" v_file = build_name + ".v"
tools.write_to_file(v_file, v_src) v_output.write(v_file)
sources = platform.sources + [(v_file, "verilog")] sources = platform.sources + [(v_file, "verilog")]
_build_files(platform.device, sources, platform.verilog_include_paths, build_name) _build_files(platform.device, sources, platform.verilog_include_paths, build_name)
@ -89,7 +89,7 @@ class LatticeDiamondToolchain:
os.chdir("..") os.chdir("..")
return vns return v_output.ns
def add_period_constraint(self, platform, clk, period): def add_period_constraint(self, platform, clk, period):
# TODO: handle differential clk # TODO: handle differential clk

View file

@ -127,10 +127,9 @@ class SimVerilatorToolchain:
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(fragment) platform.finalize(fragment)
v_src, vns = platform.get_verilog(fragment) v_output = platform.get_verilog(fragment)
named_sc, named_pc = platform.resolve_signals(vns) named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = "dut.v" v_output.write("dut.v")
tools.write_to_file(v_file, v_src)
include_paths = [] include_paths = []
for source in platform.sources: for source in platform.sources:
@ -138,11 +137,11 @@ class SimVerilatorToolchain:
if path not in include_paths: if path not in include_paths:
include_paths.append(path) include_paths.append(path)
include_paths += platform.verilog_include_paths 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: if run:
_run_sim(build_name) _run_sim(build_name)
os.chdir("..") os.chdir("..")
return vns return v_output.ns

View file

@ -145,10 +145,11 @@ class XilinxISEToolchain:
vns = None vns = None
if mode == "xst" or mode == "yosys": 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) named_sc, named_pc = platform.resolve_signals(vns)
v_file = build_name + ".v" v_file = build_name + ".v"
tools.write_to_file(v_file, v_src) v_output.write(v_file)
sources = platform.sources | {(v_file, "verilog")} sources = platform.sources | {(v_file, "verilog")}
if mode == "xst": if mode == "xst":
_build_xst_files(platform.device, sources, platform.verilog_include_paths, build_name, self.xst_opt) _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()) synthesize(fragment, platform.constraint_manager.get_io_signals())
if mode == "edif" or mode == "mist": 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) named_sc, named_pc = platform.resolve_signals(vns)
e_file = build_name + ".edif" e_file = build_name + ".edif"
tools.write_to_file(e_file, e_src) e_output.write(e_file)
isemode = "edif" isemode = "edif"
tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc)) tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))

View file

@ -111,10 +111,10 @@ class XilinxVivadoToolchain:
if not isinstance(fragment, _Fragment): if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(fragment) platform.finalize(fragment)
v_src, vns = platform.get_verilog(fragment) v_output = platform.get_verilog(fragment)
named_sc, named_pc = platform.resolve_signals(vns) named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v" v_file = build_name + ".v"
tools.write_to_file(v_file, v_src) v_output.write(v_file)
sources = platform.sources | {(v_file, "verilog")} sources = platform.sources | {(v_file, "verilog")}
self._build_batch(platform, sources, build_name) self._build_batch(platform, sources, build_name)
tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc)) tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc))
@ -123,7 +123,7 @@ class XilinxVivadoToolchain:
os.chdir("..") os.chdir("..")
return vns 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("""create_clock -name {clk} -period """ + \

35
migen/fhdl/conv_output.py Normal file
View file

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

View file

@ -1,10 +1,12 @@
from collections import OrderedDict from collections import OrderedDict
from collections import namedtuple
from migen.fhdl.std import * from migen.fhdl.std import *
from migen.fhdl.namer import build_namespace from migen.fhdl.namer import build_namespace
from migen.fhdl.tools import list_special_ios from migen.fhdl.tools import list_special_ios
from migen.fhdl.structure import _Fragment from migen.fhdl.structure import _Fragment
from migen.fhdl.conv_output import ConvOutput
from collections import namedtuple
_Port = namedtuple("_Port", "name direction") _Port = namedtuple("_Port", "name direction")
_Cell = namedtuple("_Cell", "name ports") _Cell = namedtuple("_Cell", "name ports")
@ -125,7 +127,7 @@ def _generate_cells(f):
else: else:
cell_dict[special.of] = port_list cell_dict[special.of] = port_list
else: 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()] return [_Cell(k, v) for k, v in cell_dict.items()]
def _generate_instances(f,ns): def _generate_instances(f,ns):
@ -146,7 +148,7 @@ def _generate_instances(f,ns):
raise NotImplementedError("Unsupported instance item") raise NotImplementedError("Unsupported instance item")
instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props)) instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props))
else: else:
raise ValueError("Edif conversion can only handle synthesized fragments") raise ValueError("EDIF conversion can only handle synthesized fragments")
return instances return instances
def _generate_ios(f, ios, ns): def _generate_ios(f, ios, ns):
@ -174,7 +176,7 @@ def _generate_connections(f, ios, ns):
else: else:
raise NotImplementedError("Unsupported instance item") raise NotImplementedError("Unsupported instance item")
else: else:
raise ValueError("Edif conversion can only handle synthesized fragments") raise ValueError("EDIF conversion can only handle synthesized fragments")
for s in ios: for s in ios:
io = ns.get_name(s) io = ns.get_name(s)
if io not in r: if io not in r:
@ -182,11 +184,11 @@ def _generate_connections(f, ios, ns):
r[io].append(_NetBranch(portname=io, instancename="")) r[io].append(_NetBranch(portname=io, instancename=""))
return r 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): if not isinstance(f, _Fragment):
f = f.get_fragment() f = f.get_fragment()
if f.comb != [] or f.sync != {}: 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: if ios is None:
ios = set() ios = set()
cells = _generate_cells(f) 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) instances = _generate_instances(f, ns)
inouts = _generate_ios(f, ios, ns) inouts = _generate_ios(f, ios, ns)
connections = _generate_connections(f, ios, ns) connections = _generate_connections(f, ios, ns)
r = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor) src = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor)
if return_ns:
return r, ns r = ConvOutput()
else: r.set_main_source(src)
return r r.ns = ns
return r

View file

@ -48,7 +48,7 @@ class Tristate(Special):
yield self, attr, target_context yield self, attr, target_context
@staticmethod @staticmethod
def emit_verilog(tristate, ns): def emit_verilog(tristate, ns, add_data_file):
def pe(e): def pe(e):
return verilog_printexpr(ns, e)[0] return verilog_printexpr(ns, e)[0]
w, s = value_bits_sign(tristate.target) w, s = value_bits_sign(tristate.target)
@ -123,7 +123,7 @@ class Instance(Special):
yield item, "expr", SPECIAL_INOUT yield item, "expr", SPECIAL_INOUT
@staticmethod @staticmethod
def emit_verilog(instance, ns): def emit_verilog(instance, ns, add_data_file):
r = instance.of + " " r = instance.of + " "
parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items)) parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items))
if parameters: if parameters:
@ -198,7 +198,7 @@ class _MemoryPort(Special):
yield self, attr, target_context yield self, attr, target_context
@staticmethod @staticmethod
def emit_verilog(port, ns): def emit_verilog(port, ns, add_data_file):
return "" # done by parent Memory object return "" # done by parent Memory object
class Memory(Special): class Memory(Special):
@ -237,7 +237,7 @@ class Memory(Special):
return mp return mp
@staticmethod @staticmethod
def emit_verilog(memory, ns): def emit_verilog(memory, ns, add_data_file):
r = "" r = ""
def gn(e): def gn(e):
if isinstance(e, Memory): if isinstance(e, Memory):
@ -307,14 +307,10 @@ class Memory(Special):
r += "\n" r += "\n"
if memory.init is not None: if memory.init is not None:
memory_filename = gn(memory) + ".init" content = ""
# XXX move I/O to mibuild?
# (Implies mem init won't work with simple Migen examples?)
f = open(memory_filename, "w")
for d in memory.init: for d in memory.init:
f.write("{:x}\n".format(d)) content += "{:x}\n".format(d)
f.close() memory_filename = add_data_file(gn(memory) + ".init", content)
r += "initial begin\n" r += "initial begin\n"
r += "$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n" r += "$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n"
@ -330,7 +326,7 @@ class SynthesisDirective(Special):
self.signals = signals self.signals = signals
@staticmethod @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()) name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items())
formatted = directive.template.format(**name_dict) formatted = directive.template.format(**name_dict)
return "// synthesis " + formatted + "\n" return "// synthesis " + formatted + "\n"

View file

@ -6,6 +6,7 @@ from migen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment
from migen.fhdl.tools import * from migen.fhdl.tools import *
from migen.fhdl.bitcontainer import bits_for, flen from migen.fhdl.bitcontainer import bits_for, flen
from migen.fhdl.namer import Namespace, build_namespace from migen.fhdl.namer import Namespace, build_namespace
from migen.fhdl.conv_output import ConvOutput
def _printsig(ns, s): def _printsig(ns, s):
if s.signed: if s.signed:
@ -257,20 +258,20 @@ def _lower_specials(overrides, specials):
f.specials -= lowered_specials2 f.specials -= lowered_specials2
return f, lowered_specials return f, lowered_specials
def _printspecials(overrides, specials, ns): def _printspecials(overrides, specials, ns, add_data_file):
r = "" r = ""
for special in sorted(specials, key=lambda x: x.huid): 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: 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
return r return r
def convert(f, ios=None, name="top", def convert(f, ios=None, name="top",
return_ns=False,
special_overrides=dict(), special_overrides=dict(),
create_clock_domains=True, create_clock_domains=True,
display_run=False): display_run=False):
r = ConvOutput()
if not isinstance(f, _Fragment): if not isinstance(f, _Fragment):
f = f.get_fragment() f = f.get_fragment()
if ios is None: if ios is None:
@ -296,15 +297,14 @@ def convert(f, ios=None, name="top",
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) | ios)
r.ns = ns
r = "/* Machine-generated using Migen */\n" src = "/* Machine-generated using Migen */\n"
r += _printheader(f, ios, name, ns) src += _printheader(f, ios, name, ns)
r += _printcomb(f, ns, display_run) src += _printcomb(f, ns, display_run)
r += _printsync(f, ns) src += _printsync(f, ns)
r += _printspecials(special_overrides, f.specials - lowered_specials, ns) src += _printspecials(special_overrides, f.specials - lowered_specials, ns, r.add_data_file)
r += "endmodule\n" src += "endmodule\n"
r.set_main_source(src)
if return_ns: return r
return r, ns
else:
return r