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):
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]
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
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)
.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)
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)
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) +
"-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}} "
"-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)