X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=litex%2Fbuild%2Fxilinx%2Fvivado.py;h=10a795bd7665533e1f88f5a63179b51621ddf803;hb=b8be9545cc9df790c1038b0b030f0058e0466095;hp=032b78bbec673e77cab835fcb3267cc9bc66d83a;hpb=e8262ed44792eb46fb1164e62446f289a9613159;p=litex.git diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index 032b78bb..10a795bd 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -5,7 +5,8 @@ import os import subprocess import sys -from litex.gen.fhdl.structure import _Fragment +from migen.fhdl.structure import _Fragment + from litex.build.generic_platform import * from litex.build import tools from litex.build.xilinx import common @@ -20,6 +21,8 @@ def _format_constraint(c): return "set_property DRIVE " + str(c.strength) elif isinstance(c, Misc): return "set_property " + c.misc.replace("=", " ") + elif isinstance(c, Inverted): + return None else: raise ValueError("unknown constraint {}".format(c)) @@ -31,7 +34,8 @@ def _format_xdc(signame, resname, *constraints): fmt_r += "." + resname[2] r = " ## {}\n".format(fmt_r) for c in fmt_c: - r += c + " [get_ports " + signame + "]\n" + if c is not None: + r += c + " [get_ports " + signame + "]\n" return r @@ -56,40 +60,85 @@ def _run_vivado(build_name, vivado_path, source, ver=None): build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_file = "build_" + build_name + ".bat" tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call([build_script_file]) + command = build_script_file else: build_script_contents = "# Autogenerated by LiteX\nset -e\n" - settings = common.settings(vivado_path, ver) + + # For backwards compatibility with ISE paths, also + # look for a version in a subdirectory named "Vivado" + # under the current directory. + paths_to_try = [vivado_path, os.path.join(vivado_path, "Vivado")] + for p in paths_to_try: + try: + settings = common.settings(p, ver) + except OSError: + continue + break + else: + raise OSError("Unable to locate Vivado directory or settings.") + build_script_contents += "source " + settings + "\n" build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_file = "build_" + build_name + ".sh" tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call(["bash", build_script_file]) - + command = ["bash", build_script_file] + r = tools.subprocess_call_filtered(command, common.colors) if r != 0: raise OSError("Subprocess failed") class XilinxVivadoToolchain: + attr_translate = { + "keep": ("dont_touch", "true"), + "no_retiming": ("dont_touch", "true"), + "async_reg": ("async_reg", "true"), + "mr_ff": ("mr_ff", "true"), # user-defined attribute + "ars_ff1": ("ars_ff1", "true"), # user-defined attribute + "ars_ff2": ("ars_ff2", "true"), # user-defined attribute + "no_shreg_extract": None + } + def __init__(self): self.bitstream_commands = [] self.additional_commands = [] self.pre_synthesis_commands = [] self.with_phys_opt = False + self.clocks = dict() + self.false_paths = set() - def _build_batch(self, platform, sources, build_name): + def _build_batch(self, platform, sources, edifs, ips, build_name): tcl = [] + tcl.append("create_project -force -name {} -part {}".format(build_name, platform.device)) + tcl.append("set_property XPM_LIBRARIES {XPM_CDC XPM_MEMORY} [current_project]") for filename, language, library in sources: filename_tcl = "{" + filename + "}" tcl.append("add_files " + filename_tcl) tcl.append("set_property library {} [get_files {}]" .format(library, filename_tcl)) + for filename in edifs: + filename_tcl = "{" + filename + "}" + tcl.append("read_edif " + filename_tcl) + + for filename in ips: + filename_tcl = "{" + filename + "}" + ip = os.path.splitext(os.path.basename(filename))[0] + tcl.append("read_ip " + filename_tcl) + tcl.append("upgrade_ip [get_ips {}]".format(ip)) + tcl.append("generate_target all [get_ips {}]".format(ip)) + tcl.append("synth_ip [get_ips {}] -force".format(ip)) + tcl.append("get_files -all -of_objects [get_files {}]".format(filename_tcl)) tcl.append("read_xdc {}.xdc".format(build_name)) tcl.extend(c.format(build_name=build_name) for c in self.pre_synthesis_commands) - tcl.append("synth_design -top top -part {} -include_dirs {{{}}}".format(platform.device, " ".join(platform.verilog_include_paths))) + # "-include_dirs {}" crashes Vivado 2016.4 + if platform.verilog_include_paths: + tcl.append("synth_design -top {} -part {} -include_dirs {{{}}}".format(build_name, platform.device, " ".join(platform.verilog_include_paths))) + else: + tcl.append("synth_design -top {} -part {}".format(build_name, platform.device)) + tcl.append("report_timing_summary -file {}_timing_synth.rpt".format(build_name)) tcl.append("report_utilization -hierarchical -file {}_utilization_hierarchical_synth.rpt".format(build_name)) tcl.append("report_utilization -file {}_utilization_synth.rpt".format(build_name)) + tcl.append("opt_design") tcl.append("place_design") if self.with_phys_opt: tcl.append("phys_opt_design -directive AddRetime") @@ -99,9 +148,10 @@ class XilinxVivadoToolchain: tcl.append("report_control_sets -verbose -file {}_control_sets.rpt".format(build_name)) tcl.append("report_clock_utilization -file {}_clock_utilization.rpt".format(build_name)) tcl.append("route_design") + tcl.append("write_checkpoint -force {}_route.dcp".format(build_name)) tcl.append("report_route_status -file {}_route_status.rpt".format(build_name)) tcl.append("report_drc -file {}_drc.rpt".format(build_name)) - tcl.append("report_timing_summary -max_paths 10 -file {}_timing.rpt".format(build_name)) + tcl.append("report_timing_summary -datasheet -max_paths 10 -file {}_timing.rpt".format(build_name)) tcl.append("report_power -file {}_power.rpt".format(build_name)) for bitstream_command in self.bitstream_commands: tcl.append(bitstream_command.format(build_name=build_name)) @@ -111,21 +161,73 @@ class XilinxVivadoToolchain: tcl.append("quit") tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) + def _convert_clocks(self, platform): + for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid): + platform.add_platform_command( + "create_clock -name {clk} -period " + str(period) + + " [get_nets {clk}]", clk=clk) + for from_, to in sorted(self.false_paths, + key=lambda x: (x[0].duid, x[1].duid)): + platform.add_platform_command( + "set_clock_groups " + "-group [get_clocks -include_generated_clocks -of [get_nets {from_}]] " + "-group [get_clocks -include_generated_clocks -of [get_nets {to}]] " + "-asynchronous", + from_=from_, to=to) + + # make sure add_*_constraint cannot be used again + del self.clocks + del self.false_paths + + def _constrain(self, platform): + # The asynchronous input to a MultiReg is a false path + platform.add_platform_command( + "set_false_path -quiet " + "-to [get_nets -filter {{mr_ff == TRUE}}]" + ) + # The asychronous reset input to the AsyncResetSynchronizer is a false + # path + platform.add_platform_command( + "set_false_path -quiet " + "-to [get_pins -filter {{REF_PIN_NAME == PRE}} " + "-of [get_cells -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" + ) + # clock_period-2ns to resolve metastability on the wire between the + # AsyncResetSynchronizer FFs + platform.add_platform_command( + "set_max_delay 2 -quiet " + "-from [get_pins -filter {{REF_PIN_NAME == Q}} " + "-of [get_cells -filter {{ars_ff1 == TRUE}}]] " + "-to [get_pins -filter {{REF_PIN_NAME == D}} " + "-of [get_cells -filter {{ars_ff2 == TRUE}}]]" + ) + def build(self, platform, fragment, build_dir="build", build_name="top", - toolchain_path="/opt/Xilinx/Vivado", source=True, run=True, **kwargs): - tools.mkdir_noerror(build_dir) + toolchain_path=None, source=True, run=True, **kwargs): + if toolchain_path is None: + if sys.platform == "win32": + toolchain_path = "C:\\Xilinx\\Vivado" + elif sys.platform == "cygwin": + toolchain_path = "/cygdrive/c/Xilinx/Vivado" + else: + toolchain_path = "/opt/Xilinx/Vivado" + os.makedirs(build_dir, exist_ok=True) cwd = os.getcwd() os.chdir(build_dir) if not isinstance(fragment, _Fragment): fragment = fragment.get_fragment() platform.finalize(fragment) + self._convert_clocks(platform) + self._constrain(platform) v_output = platform.get_verilog(fragment, name=build_name, **kwargs) named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" v_output.write(v_file) - sources = platform.sources | {(v_file, "verilog", "work")} - self._build_batch(platform, sources, build_name) + sources = platform.sources + [(v_file, "verilog", "work")] + edifs = platform.edifs + ips = platform.ips + self._build_batch(platform, sources, edifs, ips, build_name) tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc)) if run: _run_vivado(build_name, toolchain_path, source) @@ -135,5 +237,10 @@ 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) + if clk in self.clocks: + raise ValueError("A period constraint already exists") + self.clocks[clk] = period + + def add_false_path_constraint(self, platform, from_, to): + if (to, from_) not in self.false_paths: + self.false_paths.add((from_, to))