From 18ff8f38d1eee15da625310c8e7c5bbcf7ab278d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sun, 8 Dec 2019 12:08:17 +0100 Subject: [PATCH] build/xilinx/vivado: cleanup/simplify --- litex/build/xilinx/vivado.py | 155 ++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 56 deletions(-) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index fb87bccb..5331d447 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -13,8 +13,9 @@ from litex.build.generic_platform import * from litex.build import tools from litex.build.xilinx import common +# Constraints (.xdc) ------------------------------------------------------------------------------- -def _format_constraint(c): +def _format_xdc_constraint(c): if isinstance(c, Pins): return "set_property LOC " + c.identifiers[0] elif isinstance(c, IOStandard): @@ -30,7 +31,7 @@ def _format_constraint(c): def _format_xdc(signame, resname, *constraints): - fmt_c = [_format_constraint(c) for c in constraints] + fmt_c = [_format_xdc_constraint(c) for c in constraints] fmt_r = resname[0] + ":" + str(resname[1]) if resname[2] is not None: fmt_r += "." + resname[2] @@ -55,16 +56,16 @@ def _build_xdc(named_sc, named_pc): r += "\n" + "\n\n".join(named_pc) return r +# Script ------------------------------------------------------------------------------------------- -def _run_vivado(build_name, vivado_path, source, ver=None): - if sys.platform == "win32" or sys.platform == "cygwin": - build_script_contents = "REM Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n" - 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) - command = build_script_file +def _build_script(build_name, vivado_path, source, ver=None): + if sys.platform in ["win32", "cygwin"]: + script_contents = "REM Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n" + script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" + script_file = "build_" + build_name + ".bat" + tools.write_to_file(script_file, script_contents) else: - build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n" + script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n" # Only source Vivado settings if not already in our $PATH if not find_executable("vivado"): # For backwards compatibility with ISE paths, also @@ -79,52 +80,65 @@ def _run_vivado(build_name, vivado_path, source, ver=None): 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) - command = ["bash", build_script_file] - r = tools.subprocess_call_filtered(command, common.colors) - if r != 0: + script_contents += "source " + settings + "\n" + + script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" + script_file = "build_" + build_name + ".sh" + tools.write_to_file(script_file, script_contents) + return script_file + +def _run_script(script): + if sys.platform in ["win32", "cygwin"]: + shell = ["cmd", "/c"] + else: + shell = ["bash"] + + if tools.subprocess_call_filtered(shell + [script], common.colors) != 0: raise OSError("Subprocess failed") +# XilinxVivadoToolchain ---------------------------------------------------------------------------- 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 + "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.incremental_implementation = False - self.vivado_synth_directive = "default" - self.opt_directive = "default" - self.vivado_place_directive = "default" + self.bitstream_commands = [] + self.additional_commands = [] + self.pre_synthesis_commands = [] + self.incremental_implementation = False + self.vivado_synth_directive = "default" + self.opt_directive = "default" + self.vivado_place_directive = "default" self.vivado_post_place_phys_opt_directive = None - self.vivado_route_directive = "default" + self.vivado_route_directive = "default" self.vivado_post_route_phys_opt_directive = "default" - self.clocks = dict() + self.clocks = dict() self.false_paths = set() - def _build_batch(self, platform, sources, edifs, ips, build_name, synth_mode, enable_xpm): + def _build_tcl(self, platform, build_name, synth_mode, enable_xpm): assert synth_mode in ["vivado", "yosys"] tcl = [] + + # Create project tcl.append("create_project -force -name {} -part {}".format(build_name, platform.device)) tcl.append("set_msg_config -id {Common 17-55} -new_severity {Warning}") + + # Enable Xilinx Parameterized Macros if enable_xpm: tcl.append("set_property XPM_LIBRARIES {XPM_CDC XPM_MEMORY} [current_project]") + + # Add sources (when Vivado used for synthesis) if synth_mode == "vivado": # "-include_dirs {}" crashes Vivado 2016.4 - for filename, language, library in sources: + for filename, language, library in platform.sources: filename_tcl = "{" + filename + "}" if (language == "systemverilog"): tcl.append("read_verilog -sv " + filename_tcl) @@ -136,11 +150,14 @@ class XilinxVivadoToolchain: .format(library, filename_tcl)) else: tcl.append("add_files " + filename_tcl) - for filename in edifs: + + # Add EDIFs + for filename in platform.edifs: filename_tcl = "{" + filename + "}" tcl.append("read_edif " + filename_tcl) - for filename in ips: + # Add Ips + for filename in platform.ips: filename_tcl = "{" + filename + "}" ip = os.path.splitext(os.path.basename(filename))[0] tcl.append("read_ip " + filename_tcl) @@ -149,9 +166,13 @@ class XilinxVivadoToolchain: tcl.append("synth_ip [get_ips {}] -force".format(ip)) tcl.append("get_files -all -of_objects [get_files {}]".format(filename_tcl)) + # Add constraints tcl.append("read_xdc {}.xdc".format(build_name)) + + # Add pre-synthesis commands tcl.extend(c.format(build_name=build_name) for c in self.pre_synthesis_commands) + # Design flow if synth_mode == "vivado": synth_cmd = "synth_design -directive {} -top {} -part {}".format(self.vivado_synth_directive, build_name, platform.device) @@ -194,7 +215,7 @@ class XilinxVivadoToolchain: tcl.append("quit") tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) - def _convert_clocks(self, platform): + def _build_clock_constraints(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) + @@ -207,26 +228,23 @@ class XilinxVivadoToolchain: "-group [get_clocks -include_generated_clocks -of [get_nets {to}]] " "-asynchronous", from_=from_, to=to) - - # make sure add_*_constraint cannot be used again + # Make sure add_*_constraint cannot be used again del self.clocks del self.false_paths - def _constrain(self, platform): + def _build_false_path_constraints(self, platform): # The asynchronous input to a MultiReg is a false path platform.add_platform_command( "set_false_path -quiet " "-to [get_nets -quiet -filter {{mr_ff == TRUE}}]" ) - # The asychronous reset input to the AsyncResetSynchronizer is a false - # path + # The asychronous reset input to the AsyncResetSynchronizer is a false path platform.add_platform_command( "set_false_path -quiet " "-to [get_pins -quiet -filter {{REF_PIN_NAME == PRE}} " "-of [get_cells -quiet -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" ) - # clock_period-2ns to resolve metastability on the wire between the - # AsyncResetSynchronizer FFs + # 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 -quiet -filter {{REF_PIN_NAME == Q}} " @@ -235,34 +253,59 @@ class XilinxVivadoToolchain: "-of [get_cells -quiet -filter {{ars_ff2 == TRUE}}]]" ) - def build(self, platform, fragment, build_dir="build", build_name="top", - toolchain_path="/opt/Xilinx/Vivado", source=True, run=True, - synth_mode="vivado", enable_xpm=False, **kwargs): + def build(self, platform, fragment, + build_dir = "build", + build_name = "top", + toolchain_path = None, + source = True, run=True, + synth_mode = "vivado", + enable_xpm = False, + **kwargs): + + # Get default toolchain path (if not specified) if toolchain_path is None: toolchain_path = "/opt/Xilinx/Vivado" + + # Create build directory os.makedirs(build_dir, exist_ok=True) cwd = os.getcwd() os.chdir(build_dir) + # Finalize design if not isinstance(fragment, _Fragment): fragment = fragment.get_fragment() platform.finalize(fragment) - self._convert_clocks(platform) - self._constrain(platform) + + # Generate timing constraints + self._build_clock_constraints(platform) + self._build_false_path_constraints(platform) + + # Generate verilog 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) platform.add_source(v_file) - sources = platform.sources - edifs = platform.edifs - ips = platform.ips - self._build_batch(platform, sources, edifs, ips, build_name, synth_mode, enable_xpm) + + # Generate design project (.tcl) + self._build_tcl( + platform = platform, + build_name = build_name, + synth_mode = synth_mode, + enable_xpm = enable_xpm + ) + + # Generate design constraints (.xdc) tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc)) + + # Generate build script + script = _build_script(build_name, toolchain_path, source) + + # Run if run: if synth_mode == "yosys": - common._run_yosys(platform.device, sources, platform.verilog_include_paths, build_name) - _run_vivado(build_name, toolchain_path, source) + common._run_yosys(platform.device, platform.sources, platform.verilog_include_paths, build_name) + _run_script(script) os.chdir(cwd) -- 2.30.2