from litex.build.generic_platform import Pins, IOStandard, Misc
from litex.build import tools
+# IO/Placement Constraints (.qsf) ------------------------------------------------------------------
def _format_constraint(c, signame, fmt_r):
+ # IO location constraints
if isinstance(c, Pins):
- return "set_location_assignment -comment \"{name}\" " \
- "-to {signame} Pin_{pin}".format(
- signame=signame,
- name=fmt_r,
- pin=c.identifiers[0])
+ tpl = "set_location_assignment -comment \"{name}\" -to {signame} Pin_{pin}"
+ return tpl.format(signame=signame, name=fmt_r, pin=c.identifiers[0])
+
+ # IO standard constraints
elif isinstance(c, IOStandard):
- return "set_instance_assignment -name io_standard " \
- "-comment \"{name}\" \"{std}\" -to {signame}".format(
- signame=signame,
- name=fmt_r,
- std=c.name)
+ tpl = "set_instance_assignment -name io_standard -comment \"{name}\" \"{std}\" -to {signame}"
+ return tpl.format(signame=signame, name=fmt_r, std=c.name)
+
+ # Others constraints
elif isinstance(c, Misc):
if not isinstance(c.misc, str) and len(c.misc) == 2:
- return "set_instance_assignment -comment \"{name}\" " \
- "-name {misc[0]} \"{misc[1]}\" -to {signame}".format(
- signame=signame,
- name=fmt_r,
- misc=c.misc)
+ tpl = "set_instance_assignment -comment \"{name}\" -name {misc[0]} \"{misc[1]}\" -to {signame}"
+ return tpl.format(signame=signame, name=fmt_r, misc=c.misc)
else:
- return "set_instance_assignment -comment \"{name}\" " \
- "-name {misc} " \
- "-to {signame}".format(
- signame=signame,
- name=fmt_r,
- misc=c.misc)
+ tpl = "set_instance_assignment -comment \"{name}\" -name {misc} -to {signame}"
+ return tpl.format(signame=signame, name=fmt.r, misc=c.misc)
-
-def _format_qsf(signame, pin, others, resname):
+def _format_qsf_constraint(signame, pin, others, resname):
fmt_r = "{}:{}".format(*resname[:2])
if resname[2] is not None:
fmt_r += "." + resname[2]
-
- fmt_c = [_format_constraint(c, signame, fmt_r) for c in
- ([Pins(pin)] + others)]
-
+ fmt_c = [_format_constraint(c, signame, fmt_r) for c in ([Pins(pin)] + others)]
return '\n'.join(fmt_c)
-
-def _build_qsf(named_sc, named_pc, build_name):
- lines = []
+def _build_qsf_constraints(named_sc, named_pc):
+ qsf = []
for sig, pins, others, resname in named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
- lines.append(
- _format_qsf("{}[{}]".format(sig, i), p, others, resname))
+ qsf.append(_format_qsf_constraint("{}[{}]".format(sig, i), p, others, resname))
else:
- lines.append(_format_qsf(sig, pins[0], others, resname))
-
+ qsf.append(_format_qsf_constraint(sig, pins[0], others, resname))
if named_pc:
- lines.append("")
- lines.append("\n\n".join(named_pc))
-
- # Set top level name to "build_name" in .qsf file instead always use "top" name
- lines.append("set_global_assignment -name top_level_entity " + build_name)
- return "\n".join(lines)
+ qsf.append("\n\n".join(named_pc))
+ return "\n".join(qsf)
+# Timing Constraints (.sdc) ------------------------------------------------------------------------
def _build_sdc(clocks, false_paths, vns, build_name):
- lines = []
+ sdc = []
+ # Clock constraints
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
- lines.append(
- "create_clock -name {clk} -period ".format(clk=vns.get_name(clk)) + str(period) +
- " [get_ports {{{clk}}}]".format(clk=vns.get_name(clk)))
- for from_, to in sorted(false_paths,
- key=lambda x: (x[0].duid, x[1].duid)):
- lines.append(
- "set_false_path "
- "-from [get_clocks {{{from_}}}] "
- "-to [get_clocks {{{to}}}]".format(
- from_=vns.get_name(from_), to=vns.get_name(to)))
- tools.write_to_file("{}.sdc".format(build_name), "\n".join(lines))
-
-
-def _build_files(device, ips, sources, vincpaths, named_sc, named_pc, build_name):
- lines = []
- for filename in ips:
- lines.append("set_global_assignment -name QSYS_FILE {path} ".format(
- path=filename.replace("\\", "/")))
+ tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]"
+ sdc.append(tpl.format(clk=vns.get_name(clk), period=str(period)))
+ # False path constraints
+ for from_, to in sorted(false_paths, key=lambda x: (x[0].duid, x[1].duid)):
+ tpl = "set_false_path -from [get_clocks {{{from_}}}] -to [get_clocks {{{to}}}]"
+ sdc.append(tpl.format(from_=vns.get_name(from_), to=vns.get_name(to)))
+ tools.write_to_file("{}.sdc".format(build_name), "\n".join(sdc))
+
+# Project (.qsf) -----------------------------------------------------------------------------------
+
+def _build_qsf(device, ips, sources, vincpaths, named_sc, named_pc, build_name):
+ qsf = []
+
+ # Set device
+ qsf.append("set_global_assignment -name DEVICE {}".format(device))
+
+ # Add sources
for filename, language, library in sources:
- # Enforce use of SystemVerilog since Quartus does not support global parameters in Verilog
- if language == "verilog":
- language = "systemverilog"
- lines.append(
- "set_global_assignment -name {lang}_FILE {path} "
- "-library {lib}".format(
- lang=language.upper(),
- path=filename.replace("\\", "/"),
- lib=library))
- lines.append("set_global_assignment -name SDC_FILE {}.sdc".format(build_name))
+ if language == "verilog": language = "systemverilog" # Enforce use of SystemVerilog
+ tpl = "set_global_assignment -name {lang}_FILE {path} -library {lib}"
+ qsf.append(tpl.format(lang=language.upper(), path=filename.replace("\\", "/"), lib=library))
+ # Add ips
+ for filename in ips:
+ tpl = "set_global_assignment -name QSYS_FILE {filename}"
+ qsf.append(tpl.replace(filename=filename.replace("\\", "/")))
+
+ # Add include paths
for path in vincpaths:
- lines.append("set_global_assignment -name SEARCH_PATH {}".format(
- path.replace("\\", "/")))
+ qsf.append("set_global_assignment -name SEARCH_PATH {}".format(path.replace("\\", "/")))
- lines.append(_build_qsf(named_sc, named_pc, build_name))
- lines.append("set_global_assignment -name DEVICE {}".format(device))
- tools.write_to_file("{}.qsf".format(build_name), "\n".join(lines))
+ # Set top level
+ qsf.append("set_global_assignment -name top_level_entity " + build_name)
+ # Add io, placement constraints
+ qsf.append(_build_qsf_constraints(named_sc, named_pc))
-def _run_quartus(build_name, quartus_path, create_rbf):
- if sys.platform == "win32" or sys.platform == "cygwin":
- build_script_contents = "REM Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n"
- build_script_file = "build_" + build_name + ".bat"
- command = build_script_file
- else:
- build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
- build_script_file = "build_" + build_name + ".sh"
- command = ["bash", build_script_file]
- build_script_contents += """
+ # Set timing constraints
+ qsf.append("set_global_assignment -name SDC_FILE {}.sdc".format(build_name))
-set -e
+ # Generate qsf
+ tools.write_to_file("{}.qsf".format(build_name), "\n".join(qsf))
-quartus_map --read_settings_files=on --write_settings_files=off {build_name} -c {build_name}
+# Script -------------------------------------------------------------------------------------------
+
+def _build_script(build_name, quartus_path, create_rbf):
+ if sys.platform in ["win32", "cygwin"]:
+ script_contents = "REM Autogenerated by LiteX / git: " + tools.get_litex_git_revision()
+ script_file = "build_" + build_name + ".bat"
+ else:
+ script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision()
+ script_file = "build_" + build_name + ".sh"
+ script_contents += """
+quartus_map --read_settings_files=on --write_settings_files=off {build_name} -c {build_name}
quartus_fit --read_settings_files=off --write_settings_files=off {build_name} -c {build_name}
quartus_asm --read_settings_files=off --write_settings_files=off {build_name} -c {build_name}
quartus_sta {build_name} -c {build_name}"""
if create_rbf:
- build_script_contents +="""
+ script_contents += """
if [ -f "{build_name}.sof" ]
then
quartus_cpf -c {build_name}.sof {build_name}.rbf
fi
-
"""
- build_script_contents = build_script_contents.format(build_name=build_name) # noqa
- tools.write_to_file(build_script_file,
- build_script_contents,
- force_unix=True)
+ script_contents = script_contents.format(build_name=build_name)
+ tools.write_to_file(script_file, script_contents, force_unix=True)
- if subprocess.call(command):
+ return script_file
+
+def _run_script(script):
+ if sys.platform in ["win32", "cygwin"]:
+ shell = ["cmd", "/c"]
+ else:
+ shell = ["bash"]
+
+ if subprocess.call(shell + [script]) != 0:
raise OSError("Subprocess failed")
+# AlteraQuartusToolchain ---------------------------------------------------------------------------
class AlteraQuartusToolchain:
def __init__(self):
- self.clocks = dict()
+ self.clocks = dict()
self.false_paths = set()
- def build(self, platform, fragment, build_dir="build", build_name="top",
- toolchain_path=None, run=True, **kwargs):
+ def build(self, platform, fragment,
+ build_dir = "build",
+ build_name = "top",
+ toolchain_path = None,
+ run = True,
+ **kwargs):
+
+ # Get default toolchain path (if not specified)
if toolchain_path is None:
- toolchain_path="/opt/Altera"
+ toolchain_path = "/opt/Altera"
+
+ # Create build directory
cwd = os.getcwd()
os.makedirs(build_dir, exist_ok=True)
os.chdir(build_dir)
+ # Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
+ # 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)
- _build_files(platform.device,
- platform.ips,
- platform.sources,
- platform.verilog_include_paths,
- named_sc,
- named_pc,
- build_name)
-
- _build_sdc(self.clocks, self.false_paths, v_output.ns, build_name)
+
+ # Generate design timing constraints file (.sdc)
+ _build_sdc(
+ clocks = self.clocks,
+ false_paths = self.false_paths,
+ vns = v_output.ns,
+ build_name = build_name)
+
+ # Generate design project and location constraints file (.qsf)
+ _build_qsf(
+ device = platform.device,
+ ips = platform.ips,
+ sources = platform.sources,
+ vincpaths = platform.verilog_include_paths,
+ named_sc = named_sc,
+ named_pc = named_pc,
+ build_name = build_name)
+
+ # Generate build script
+ script = _build_script(build_name, toolchain_path, platform.create_rbf)
+
+ # Run
if run:
- _run_quartus(build_name, toolchain_path, platform.create_rbf)
+ _run_script(script)
os.chdir(cwd)
def add_period_constraint(self, platform, clk, period):
if clk in self.clocks:
raise ValueError("A period constraint already exists")
- period = math.floor(period*1e3)/1e3 # round to lowest picosecond
+ period = math.floor(period*1e3)/1e3 # Round to lowest picosecond
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):