build: efinix/efinity lattice/diamond osfpga xilinx/f4pga xilinx/ise xilinx/yosys_nextpnr -> GenericToolchain
This commit is contained in:
parent
a4bb65655c
commit
382ebbf661
|
@ -23,57 +23,88 @@ from migen.fhdl.simplify import FullMemoryWE
|
||||||
from litex.build import tools
|
from litex.build import tools
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build.generic_platform import Pins, IOStandard, Misc
|
from litex.build.generic_platform import Pins, IOStandard, Misc
|
||||||
|
from litex.build.generic_toolchain import GenericToolchain
|
||||||
|
|
||||||
from litex.build.efinix import common
|
from litex.build.efinix import common
|
||||||
from litex.build.efinix import InterfaceWriter
|
from litex.build.efinix import InterfaceWriter
|
||||||
|
|
||||||
def get_pin_direction(fragment, platform, pinname):
|
|
||||||
pins = platform.constraint_manager.get_io_signals()
|
# Efinity Toolchain --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class EfinityToolchain(GenericToolchain):
|
||||||
|
attr_translate = {}
|
||||||
|
|
||||||
|
def __init__(self, efinity_path):
|
||||||
|
super().__init__()
|
||||||
|
self.options = {}
|
||||||
|
self.efinity_path = efinity_path
|
||||||
|
os.environ["EFXPT_HOME"] = self.efinity_path + "/pt"
|
||||||
|
self.ifacewriter = InterfaceWriter(efinity_path)
|
||||||
|
self.excluded_ios = []
|
||||||
|
self.additional_sdc_commands = []
|
||||||
|
self.additional_iface_commands = []
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
self.ifacewriter.set_build_params(self.platform, self._build_name)
|
||||||
|
# Add Include Paths.
|
||||||
|
if self.platform.verilog_include_paths:
|
||||||
|
self.options["includ_path"] = "{" + ";".join(self.platform.verilog_include_paths) + "}"
|
||||||
|
|
||||||
|
def build(self, platform, fragment, **kwargs):
|
||||||
|
|
||||||
|
# Apply FullMemoryWE on Design (Efiniy does not infer memories correctly otherwise).
|
||||||
|
FullMemoryWE()(fragment)
|
||||||
|
|
||||||
|
return self._build(platform, fragment, **kwargs)
|
||||||
|
|
||||||
|
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_timing_constraints(self, vns):
|
||||||
|
sdc = []
|
||||||
|
|
||||||
|
# Clock constraints
|
||||||
|
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
||||||
|
is_port = False
|
||||||
|
for sig, pins, others, resname in self.named_sc:
|
||||||
|
if sig == self._vns.get_name(clk):
|
||||||
|
is_port = True
|
||||||
|
if is_port:
|
||||||
|
tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]"
|
||||||
|
sdc.append(tpl.format(clk=self._vns.get_name(clk), period=str(period)))
|
||||||
|
else:
|
||||||
|
tpl = "create_clock -name {clk} -period {period} [get_nets {{{clk}}}]"
|
||||||
|
sdc.append(tpl.format(clk=self._vns.get_name(clk), period=str(period)))
|
||||||
|
|
||||||
|
# False path constraints
|
||||||
|
for from_, to in sorted(self.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_=self._vns.get_name(from_), to=self._vns.get_name(to)))
|
||||||
|
|
||||||
|
# Add additional commands
|
||||||
|
sdc += self.additional_sdc_commands
|
||||||
|
|
||||||
|
# Generate .sdc
|
||||||
|
tools.write_to_file("{}.sdc".format(self._build_name), "\n".join(sdc))
|
||||||
|
return (self._build_name + ".sdc", "SDC")
|
||||||
|
|
||||||
|
# Peripheral configuration (.xml) ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_pin_direction(self, pinname):
|
||||||
|
pins = self.platform.constraint_manager.get_io_signals()
|
||||||
for pin in sorted(pins, key=lambda x: x.duid):
|
for pin in sorted(pins, key=lambda x: x.duid):
|
||||||
# Better idea ???
|
# Better idea ???
|
||||||
if (pinname.split("[")[0] == pin.name):
|
if (pinname.split("[")[0] == pin.name):
|
||||||
return pin.direction
|
return pin.direction
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
def _create_gpio_instance(self, sig, pins):
|
||||||
|
|
||||||
def _build_sdc(clocks, false_paths, vns, named_sc, build_name, additional_sdc_commands):
|
|
||||||
sdc = []
|
|
||||||
|
|
||||||
# Clock constraints
|
|
||||||
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
|
|
||||||
is_port = False
|
|
||||||
for sig, pins, others, resname in named_sc:
|
|
||||||
if sig == vns.get_name(clk):
|
|
||||||
is_port = True
|
|
||||||
if is_port:
|
|
||||||
tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]"
|
|
||||||
sdc.append(tpl.format(clk=vns.get_name(clk), period=str(period)))
|
|
||||||
else:
|
|
||||||
tpl = "create_clock -name {clk} -period {period} [get_nets {{{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)))
|
|
||||||
|
|
||||||
# Add additional commands
|
|
||||||
sdc += additional_sdc_commands
|
|
||||||
|
|
||||||
# Generate .sdc
|
|
||||||
tools.write_to_file("{}.sdc".format(build_name), "\n".join(sdc))
|
|
||||||
|
|
||||||
# Peripheral configuration (.xml) ------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _create_gpio_instance(fragment, platform, sig, pins):
|
|
||||||
l = ""
|
l = ""
|
||||||
if len(pins) > 1:
|
if len(pins) > 1:
|
||||||
l = ",{},0".format(len(pins) - 1)
|
l = ",{},0".format(len(pins) - 1)
|
||||||
d = get_pin_direction(fragment, platform, sig)
|
d = self.get_pin_direction(sig)
|
||||||
return 'design.create_{d}_gpio("{name}"{len})'.format(d=d, name=sig, len=l)
|
return 'design.create_{d}_gpio("{name}"{len})'.format(d=d, name=sig, len=l)
|
||||||
|
|
||||||
def _format_constraint(c, signame, fmt_r, fragment, platform):
|
def _format_constraint(self, c, signame, fmt_r):
|
||||||
# IO location constraints
|
# IO location constraints
|
||||||
if isinstance(c, Pins):
|
if isinstance(c, Pins):
|
||||||
tpl = 'design.assign_pkg_pin("{signame}","{pin}")\n'
|
tpl = 'design.assign_pkg_pin("{signame}","{pin}")\n'
|
||||||
|
@ -125,21 +156,21 @@ def _format_constraint(c, signame, fmt_r, fragment, platform):
|
||||||
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
||||||
return tpl.format(signame=signame, prop=prop, val=val)
|
return tpl.format(signame=signame, prop=prop, val=val)
|
||||||
|
|
||||||
def _format_conf_constraint(signame, pin, others, resname, fragment, platform):
|
def _format_conf_constraint(self, signame, pin, others, resname):
|
||||||
fmt_r = "{}:{}".format(*resname[:2])
|
fmt_r = "{}:{}".format(*resname[:2])
|
||||||
if resname[2] is not None:
|
if resname[2] is not None:
|
||||||
fmt_r += "." + resname[2]
|
fmt_r += "." + resname[2]
|
||||||
fmt_c = [_format_constraint(c, signame, fmt_r, fragment, platform) for c in ([Pins(pin)] + others)]
|
fmt_c = [self._format_constraint(c, signame, fmt_r) for c in ([Pins(pin)] + others)]
|
||||||
return "".join(fmt_c)
|
return "".join(fmt_c)
|
||||||
|
|
||||||
def _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios):
|
def _build_iface_gpio(self):
|
||||||
conf = []
|
conf = []
|
||||||
inst = []
|
inst = []
|
||||||
|
|
||||||
# GPIO
|
# GPIO
|
||||||
for sig, pins, others, resname in named_sc:
|
for sig, pins, others, resname in self.named_sc:
|
||||||
excluded = False
|
excluded = False
|
||||||
for excluded_io in excluded_ios:
|
for excluded_io in self.excluded_ios:
|
||||||
if isinstance(excluded_io, str):
|
if isinstance(excluded_io, str):
|
||||||
if sig == excluded_io:
|
if sig == excluded_io:
|
||||||
excluded = True
|
excluded = True
|
||||||
|
@ -148,60 +179,71 @@ def _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios):
|
||||||
excluded = True
|
excluded = True
|
||||||
if excluded:
|
if excluded:
|
||||||
continue
|
continue
|
||||||
inst.append(_create_gpio_instance(fragment, platform, sig, pins))
|
inst.append(self._create_gpio_instance(sig, pins))
|
||||||
if len(pins) > 1:
|
if len(pins) > 1:
|
||||||
for i, p in enumerate(pins):
|
for i, p in enumerate(pins):
|
||||||
conf.append(_format_conf_constraint("{}[{}]".format(sig, i), p, others, resname, fragment, platform))
|
conf.append(_format_conf_constraint("{}[{}]".format(sig, i), p, others, resname))
|
||||||
else:
|
else:
|
||||||
conf.append(_format_conf_constraint(sig, pins[0], others, resname, fragment, platform))
|
conf.append(self._format_conf_constraint(sig, pins[0], others, resname))
|
||||||
if named_pc:
|
if self.named_pc:
|
||||||
conf.append("\n\n".join(named_pc))
|
conf.append("\n\n".join(self.named_pc))
|
||||||
|
|
||||||
conf = inst + conf
|
conf = inst + conf
|
||||||
|
|
||||||
return "\n".join(conf)
|
return "\n".join(conf)
|
||||||
|
|
||||||
def _build_peri(efinity_path, build_name, device, named_sc, named_pc, fragment, platform, additional_iface_commands, excluded_ios):
|
def build_io_constraints(self):
|
||||||
pythonpath = ""
|
pythonpath = ""
|
||||||
|
|
||||||
header = platform.toolchain.ifacewriter.header(build_name, device)
|
header = self.ifacewriter.header(self._build_name, self.platform.device)
|
||||||
gen = platform.toolchain.ifacewriter.generate(device)
|
gen = self.ifacewriter.generate(self.platform.device)
|
||||||
#TODO : move this to ifacewriter
|
#TODO : move this to ifacewriter
|
||||||
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios)
|
gpio = self._build_iface_gpio()
|
||||||
add = "\n".join(additional_iface_commands)
|
add = "\n".join(self.additional_iface_commands)
|
||||||
footer = platform.toolchain.ifacewriter.footer()
|
footer = self.ifacewriter.footer()
|
||||||
|
|
||||||
tools.write_to_file("iface.py", header + gen + gpio + add + footer)
|
tools.write_to_file("iface.py", header + gen + gpio + add + footer)
|
||||||
|
|
||||||
if tools.subprocess_call_filtered([efinity_path + "/bin/python3", "iface.py"], common.colors) != 0:
|
if tools.subprocess_call_filtered([self.efinity_path + "/bin/python3", "iface.py"], common.colors) != 0:
|
||||||
raise OSError("Error occurred during Efinity peri script execution.")
|
raise OSError("Error occurred during Efinity peri script execution.")
|
||||||
|
|
||||||
|
# Some IO blocks don't have Python API so we need to configure them
|
||||||
|
# directly in the peri.xml file
|
||||||
|
# We also need to configure the bank voltage here
|
||||||
|
if self.ifacewriter.xml_blocks or self.platform.iobank_info:
|
||||||
|
self.ifacewriter.generate_xml_blocks()
|
||||||
|
|
||||||
|
# Because the Python API is sometimes bugged, we need to tweak the generated xml
|
||||||
|
if self.ifacewriter.fix_xml:
|
||||||
|
self.ifacewriter.fix_xml_values()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Project configuration (.xml) ---------------------------------------------------------------------
|
# Project configuration (.xml) ---------------------------------------------------------------------
|
||||||
|
|
||||||
def _build_xml(family, device, timing_model, build_name, sources):
|
def build_project(self):
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
# Create Project.
|
# Create Project.
|
||||||
root = et.Element("efx:project")
|
root = et.Element("efx:project")
|
||||||
root.attrib["xmlns:efx"] = "http://www.efinixinc.com/enf_proj"
|
root.attrib["xmlns:efx"] = "http://www.efinixinc.com/enf_proj"
|
||||||
root.attrib["name"] = build_name
|
root.attrib["name"] = self._build_name
|
||||||
root.attrib["location"] = str(pathlib.Path().resolve())
|
root.attrib["location"] = str(pathlib.Path().resolve())
|
||||||
root.attrib["sw_version"] = "2021.1.165.2.19" # TODO: read it from sw_version.txt
|
root.attrib["sw_version"] = "2021.1.165.2.19" # TODO: read it from sw_version.txt
|
||||||
root.attrib["last_change_date"] = f"Date : {now.strftime('%Y-%m-%d %H:%M')}"
|
root.attrib["last_change_date"] = f"Date : {now.strftime('%Y-%m-%d %H:%M')}"
|
||||||
|
|
||||||
# Add Device.
|
# Add Device.
|
||||||
device_info = et.SubElement(root, "efx:device_info")
|
device_info = et.SubElement(root, "efx:device_info")
|
||||||
et.SubElement(device_info, "efx:family", name=family)
|
et.SubElement(device_info, "efx:family", name=self.platform.family)
|
||||||
et.SubElement(device_info, "efx:device", name=device)
|
et.SubElement(device_info, "efx:device", name=self.platform.device)
|
||||||
et.SubElement(device_info, "efx:timing_model", name=timing_model)
|
et.SubElement(device_info, "efx:timing_model", name=self.platform.timing_model)
|
||||||
|
|
||||||
# Add Design Info.
|
# Add Design Info.
|
||||||
design_info = et.SubElement(root, "efx:design_info")
|
design_info = et.SubElement(root, "efx:design_info")
|
||||||
et.SubElement(design_info, "efx:top_module", name=build_name)
|
et.SubElement(design_info, "efx:top_module", name=self._build_name)
|
||||||
|
|
||||||
# Add Design Sources.
|
# Add Design Sources.
|
||||||
for filename, language, library, *copy in sources:
|
for filename, language, library, *copy in self.platform.sources:
|
||||||
if language is None:
|
if language is None:
|
||||||
continue
|
continue
|
||||||
et.SubElement(design_info, "efx:design_file", {
|
et.SubElement(design_info, "efx:design_file", {
|
||||||
|
@ -212,7 +254,7 @@ def _build_xml(family, device, timing_model, build_name, sources):
|
||||||
|
|
||||||
# Add Timing Constraints.
|
# Add Timing Constraints.
|
||||||
constraint_info = et.SubElement(root, "efx:constraint_info")
|
constraint_info = et.SubElement(root, "efx:constraint_info")
|
||||||
et.SubElement(constraint_info, "efx:sdc_file", name=f"{build_name}.sdc")
|
et.SubElement(constraint_info, "efx:sdc_file", name=f"{self._build_name}.sdc")
|
||||||
|
|
||||||
# Add Misc Info.
|
# Add Misc Info.
|
||||||
misc_info = et.SubElement(root, "efx:misc_info")
|
misc_info = et.SubElement(root, "efx:misc_info")
|
||||||
|
@ -224,107 +266,19 @@ def _build_xml(family, device, timing_model, build_name, sources):
|
||||||
xml_str = et.tostring(root, "utf-8")
|
xml_str = et.tostring(root, "utf-8")
|
||||||
xml_str = expatbuilder.parseString(xml_str, False)
|
xml_str = expatbuilder.parseString(xml_str, False)
|
||||||
xml_str = xml_str.toprettyxml(indent=" ")
|
xml_str = xml_str.toprettyxml(indent=" ")
|
||||||
tools.write_to_file("{}.xml".format(build_name), xml_str)
|
tools.write_to_file("{}.xml".format(self._build_name), xml_str)
|
||||||
|
|
||||||
# Efinity Toolchain --------------------------------------------------------------------------------
|
def build_script(self):
|
||||||
|
return "" # not used
|
||||||
|
|
||||||
class EfinityToolchain:
|
def run_script(self, script):
|
||||||
attr_translate = {}
|
|
||||||
|
|
||||||
def __init__(self, efinity_path):
|
|
||||||
self.options = {}
|
|
||||||
self.clocks = dict()
|
|
||||||
self.false_paths = set()
|
|
||||||
self.efinity_path = efinity_path
|
|
||||||
self.ifacewriter = InterfaceWriter(efinity_path)
|
|
||||||
self.excluded_ios = []
|
|
||||||
self.additional_sdc_commands = []
|
|
||||||
self.additional_iface_commands = []
|
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
|
||||||
build_dir = "build",
|
|
||||||
build_name = "top",
|
|
||||||
run = True,
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
self.ifacewriter.set_build_params(platform, build_name)
|
|
||||||
|
|
||||||
# Create Build Directory.
|
|
||||||
cwd = os.getcwd()
|
|
||||||
os.makedirs(build_dir, exist_ok=True)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
# Apply FullMemoryWE on Design (Efiniy does not infer memories correctly otherwise).
|
|
||||||
FullMemoryWE()(fragment)
|
|
||||||
|
|
||||||
# Finalize Design.
|
|
||||||
if not isinstance(fragment, _Fragment):
|
|
||||||
fragment = fragment.get_fragment()
|
|
||||||
platform.finalize(fragment)
|
|
||||||
|
|
||||||
# Generate Design.
|
|
||||||
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
|
||||||
v_output.write(f"{build_name}.v")
|
|
||||||
platform.add_source(f"{build_name}.v")
|
|
||||||
|
|
||||||
# Add Include Paths.
|
|
||||||
if platform.verilog_include_paths:
|
|
||||||
self.options["includ_path"] = "{" + ";".join(platform.verilog_include_paths) + "}"
|
|
||||||
|
|
||||||
os.environ["EFXPT_HOME"] = self.efinity_path + "/pt"
|
|
||||||
|
|
||||||
# Generate Design Timing Constraints file (.sdc)
|
|
||||||
named_sc, named_pc = platform.resolve_signals(v_output.ns)
|
|
||||||
_build_sdc(
|
|
||||||
clocks = self.clocks,
|
|
||||||
false_paths = self.false_paths,
|
|
||||||
vns = v_output.ns,
|
|
||||||
named_sc = named_sc,
|
|
||||||
build_name = build_name,
|
|
||||||
additional_sdc_commands = self.additional_sdc_commands,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate project file (.xml)
|
|
||||||
_build_xml(
|
|
||||||
family = platform.family,
|
|
||||||
device = platform.device,
|
|
||||||
timing_model = platform.timing_model,
|
|
||||||
build_name = build_name,
|
|
||||||
sources = platform.sources
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate peripheral file (.peri.xml)
|
|
||||||
_build_peri(
|
|
||||||
efinity_path = self.efinity_path,
|
|
||||||
build_name = build_name,
|
|
||||||
device = platform.device,
|
|
||||||
named_sc = named_sc,
|
|
||||||
named_pc = named_pc,
|
|
||||||
fragment = fragment,
|
|
||||||
platform = platform,
|
|
||||||
additional_iface_commands = self.additional_iface_commands,
|
|
||||||
excluded_ios = self.excluded_ios
|
|
||||||
)
|
|
||||||
|
|
||||||
# Some IO blocks don't have Python API so we need to configure them
|
|
||||||
# directly in the peri.xml file
|
|
||||||
# We also need to configure the bank voltage here
|
|
||||||
if self.ifacewriter.xml_blocks or platform.iobank_info:
|
|
||||||
self.ifacewriter.generate_xml_blocks()
|
|
||||||
|
|
||||||
# Because the Python API is sometimes bugged, we need to tweak the generated xml
|
|
||||||
if self.ifacewriter.fix_xml:
|
|
||||||
self.ifacewriter.fix_xml_values()
|
|
||||||
|
|
||||||
# Run
|
|
||||||
if run:
|
|
||||||
# Synthesis/Mapping.
|
# Synthesis/Mapping.
|
||||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_map",
|
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_map",
|
||||||
"--project", f"{build_name}",
|
"--project", f"{self._build_name}",
|
||||||
"--root", f"{build_name}",
|
"--root", f"{self._build_name}",
|
||||||
"--write-efx-verilog", f"outflow/{build_name}.map.v",
|
"--write-efx-verilog", f"outflow/{self._build_name}.map.v",
|
||||||
"--write-premap-module", f"outflow/{build_name}.elab.vdb",
|
"--write-premap-module", f"outflow/{self._build_name}.elab.vdb",
|
||||||
"--binary-db", f"{build_name}.vdb",
|
"--binary-db", f"{self._build_name}.vdb",
|
||||||
"--family", platform.family,
|
"--family", platform.family,
|
||||||
"--device", platform.device,
|
"--device", platform.device,
|
||||||
"--mode", "speed",
|
"--mode", "speed",
|
||||||
|
@ -342,7 +296,7 @@ class EfinityToolchain:
|
||||||
"--veri_option", "verilog_mode=verilog_2k,vhdl_mode=vhdl_2008",
|
"--veri_option", "verilog_mode=verilog_2k,vhdl_mode=vhdl_2008",
|
||||||
"--work-dir", "work_syn",
|
"--work-dir", "work_syn",
|
||||||
"--output-dir", "outflow",
|
"--output-dir", "outflow",
|
||||||
"--project-xml", f"{build_name}.xml",
|
"--project-xml", f"{self._build_name}.xml",
|
||||||
"--I", "./"
|
"--I", "./"
|
||||||
], common.colors)
|
], common.colors)
|
||||||
if r != 0:
|
if r != 0:
|
||||||
|
@ -351,7 +305,7 @@ class EfinityToolchain:
|
||||||
# Place and Route.
|
# Place and Route.
|
||||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3",
|
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3",
|
||||||
self.efinity_path + "/scripts/efx_run_pt.py",
|
self.efinity_path + "/scripts/efx_run_pt.py",
|
||||||
f"{build_name}",
|
f"{self._build_name}",
|
||||||
platform.family,
|
platform.family,
|
||||||
platform.device
|
platform.device
|
||||||
], common.colors)
|
], common.colors)
|
||||||
|
@ -359,19 +313,19 @@ class EfinityToolchain:
|
||||||
raise OSError("Error occurred during efx_run_pt execution.")
|
raise OSError("Error occurred during efx_run_pt execution.")
|
||||||
|
|
||||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pnr",
|
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pnr",
|
||||||
"--circuit", f"{build_name}",
|
"--circuit", f"{self._build_name}",
|
||||||
"--family", platform.family,
|
"--family", platform.family,
|
||||||
"--device", platform.device,
|
"--device", platform.device,
|
||||||
"--operating_conditions", platform.timing_model,
|
"--operating_conditions", platform.timing_model,
|
||||||
"--pack",
|
"--pack",
|
||||||
"--place",
|
"--place",
|
||||||
"--route",
|
"--route",
|
||||||
"--vdb_file", f"work_syn/{build_name}.vdb",
|
"--vdb_file", f"work_syn/{self._build_name}.vdb",
|
||||||
"--use_vdb_file", "on",
|
"--use_vdb_file", "on",
|
||||||
"--place_file", f"outflow/{build_name}.place",
|
"--place_file", f"outflow/{self._build_name}.place",
|
||||||
"--route_file", f"outflow/{build_name}.route",
|
"--route_file", f"outflow/{self._build_name}.route",
|
||||||
"--sdc_file", f"{build_name}.sdc",
|
"--sdc_file", f"{self._build_name}.sdc",
|
||||||
"--sync_file", f"outflow/{build_name}.interface.csv",
|
"--sync_file", f"outflow/{self._build_name}.interface.csv",
|
||||||
"--seed", "1",
|
"--seed", "1",
|
||||||
"--work_dir", "work_pnr",
|
"--work_dir", "work_pnr",
|
||||||
"--output_dir", "outflow",
|
"--output_dir", "outflow",
|
||||||
|
@ -383,11 +337,11 @@ class EfinityToolchain:
|
||||||
|
|
||||||
# Bitstream.
|
# Bitstream.
|
||||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pgm",
|
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pgm",
|
||||||
"--source", f"work_pnr/{build_name}.lbf",
|
"--source", f"work_pnr/{self._build_name}.lbf",
|
||||||
"--dest", f"{build_name}.hex",
|
"--dest", f"{self._build_name}.hex",
|
||||||
"--device", platform.device,
|
"--device", platform.device,
|
||||||
"--family", platform.family,
|
"--family", platform.family,
|
||||||
"--periph", f"outflow/{build_name}.lpf",
|
"--periph", f"outflow/{self._build_name}.lpf",
|
||||||
"--oscillator_clock_divider", "DIV8",
|
"--oscillator_clock_divider", "DIV8",
|
||||||
"--spi_low_power_mode", "off",
|
"--spi_low_power_mode", "off",
|
||||||
"--io_weak_pullup", "on",
|
"--io_weak_pullup", "on",
|
||||||
|
@ -398,22 +352,3 @@ class EfinityToolchain:
|
||||||
], common.colors)
|
], common.colors)
|
||||||
if r != 0:
|
if r != 0:
|
||||||
raise OSError("Error occurred during efx_pgm execution.")
|
raise OSError("Error occurred during efx_pgm execution.")
|
||||||
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
return v_output.ns
|
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
|
||||||
clk.attr.add("keep")
|
|
||||||
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
|
|
||||||
if clk in self.clocks:
|
|
||||||
if period != self.clocks[clk]:
|
|
||||||
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
|
|
||||||
.format(self.clocks[clk], period))
|
|
||||||
self.clocks[clk] = period
|
|
||||||
|
|
||||||
def add_false_path_constraint(self, platform, from_, to):
|
|
||||||
from_.attr.add("keep")
|
|
||||||
to.attr.add("keep")
|
|
||||||
if (to, from_) not in self.false_paths:
|
|
||||||
self.false_paths.add((from_, to))
|
|
||||||
|
|
|
@ -45,42 +45,6 @@ class LatticeDiamondToolchain(GenericToolchain):
|
||||||
|
|
||||||
return self._build(platform, fragment, **kwargs)
|
return self._build(platform, fragment, **kwargs)
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
## Generate design constraints file (.lpf)
|
|
||||||
#_build_lpf(named_sc, named_pc, self.clocks, v_output.ns, build_name)
|
|
||||||
|
|
||||||
## Generate design script file (.tcl)
|
|
||||||
#_build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name)
|
|
||||||
|
|
||||||
## Generate build script
|
|
||||||
#script = _build_script(build_name, platform.device)
|
|
||||||
|
|
||||||
## Run
|
|
||||||
#if run:
|
|
||||||
# _run_script(script)
|
|
||||||
# if timingstrict:
|
|
||||||
# _check_timing(build_name)
|
|
||||||
|
|
||||||
#os.chdir(cwd)
|
|
||||||
|
|
||||||
#return v_output.ns
|
|
||||||
|
|
||||||
# Helpers --------------------------------------------------------------------------------------
|
# Helpers --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -12,43 +12,62 @@ from shutil import which, copyfile
|
||||||
|
|
||||||
from migen.fhdl.structure import _Fragment
|
from migen.fhdl.structure import _Fragment
|
||||||
|
|
||||||
|
from litex.build.generic_toolchain import GenericToolchain
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build import tools
|
from litex.build import tools
|
||||||
|
|
||||||
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
# OSFPGAToolchain -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _build_sdc(clocks, vns, build_name):
|
class OSFPGAToolchain(GenericToolchain):
|
||||||
|
attr_translate = {}
|
||||||
|
|
||||||
|
def __init__(self, toolchain):
|
||||||
|
self.toolchain = toolchain
|
||||||
|
self.clocks = dict()
|
||||||
|
|
||||||
|
def build(self, platform, fragment, **kwargs):
|
||||||
|
return self._build(platform, fragment, **kwargs)
|
||||||
|
|
||||||
|
# Constraints ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_io_constraints(self):
|
||||||
|
return ("", "") # TODO
|
||||||
|
|
||||||
|
# Timing Constraints (.sdc) --------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_timing_constraints(self, vns):
|
||||||
sdc = []
|
sdc = []
|
||||||
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
|
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
||||||
sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]")
|
sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]")
|
||||||
with open(f"{build_name}.sdc", "w") as f:
|
with open(f"{build_name}.sdc", "w") as f:
|
||||||
f.write("\n".join(sdc))
|
f.write("\n".join(sdc))
|
||||||
|
return (self._build_name + ".sdc", "SDC")
|
||||||
|
|
||||||
# Script -------------------------------------------------------------------------------------------
|
# Project --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _build_tcl(name, device, files, build_name, include_paths):
|
def build_project(self):
|
||||||
tcl = []
|
tcl = []
|
||||||
|
|
||||||
# Create Design.
|
# Create Design.
|
||||||
tcl.append(f"create_design {build_name}")
|
tcl.append(f"create_design {self._build_name}")
|
||||||
|
|
||||||
# Set Device.
|
# Set Device.
|
||||||
tcl.append(f"target_device {device.upper()}")
|
tcl.append(f"target_device {self.platform.device.upper()}")
|
||||||
|
|
||||||
# Add Include Path.
|
# Add Include Path.
|
||||||
tcl.append("add_include_path ./")
|
tcl.append("add_include_path ./")
|
||||||
for include_path in include_paths:
|
for include_path in platform.verilog_include_paths:
|
||||||
tcl.append(f"add_include_path {include_path}")
|
tcl.append(f"add_include_path {include_path}")
|
||||||
|
|
||||||
# Add Sources.
|
# Add Sources.
|
||||||
for f, typ, lib in files:
|
for f, typ, lib in self.platform.sources:
|
||||||
tcl.append(f"add_design_file {f}")
|
tcl.append(f"add_design_file {f}")
|
||||||
|
|
||||||
# Set Top Module.
|
# Set Top Module.
|
||||||
tcl.append(f"set_top_module {build_name}")
|
tcl.append(f"set_top_module {self._build_name}")
|
||||||
|
|
||||||
# Add Timings Constraints.
|
# Add Timings Constraints.
|
||||||
tcl.append(f"add_constraint_file {build_name}.sdc")
|
tcl.append(f"add_constraint_file {self._build_name}.sdc")
|
||||||
|
|
||||||
# Run.
|
# Run.
|
||||||
tcl.append("synth")
|
tcl.append("synth")
|
||||||
|
@ -63,60 +82,12 @@ def _build_tcl(name, device, files, build_name, include_paths):
|
||||||
with open("build.tcl", "w") as f:
|
with open("build.tcl", "w") as f:
|
||||||
f.write("\n".join(tcl))
|
f.write("\n".join(tcl))
|
||||||
|
|
||||||
# OSFPGAToolchain -----------------------------------------------------------------------------------
|
# Script ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class OSFPGAToolchain:
|
def build_script(self):
|
||||||
attr_translate = {}
|
return "" # unused
|
||||||
|
|
||||||
def __init__(self, toolchain):
|
def run_script(self, script):
|
||||||
self.toolchain = toolchain
|
|
||||||
self.clocks = dict()
|
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
|
||||||
build_dir = "build",
|
|
||||||
build_name = "top",
|
|
||||||
run = True,
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Generate constraints file.
|
|
||||||
# IOs.
|
|
||||||
# TODO.
|
|
||||||
|
|
||||||
# Timings (.sdc)
|
|
||||||
_build_sdc(
|
|
||||||
clocks = self.clocks,
|
|
||||||
vns = v_output.ns,
|
|
||||||
build_name = build_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate build script (.tcl)
|
|
||||||
script = _build_tcl(
|
|
||||||
name = platform.devicename,
|
|
||||||
device = platform.device,
|
|
||||||
files = platform.sources,
|
|
||||||
build_name = build_name,
|
|
||||||
include_paths = platform.verilog_include_paths,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run
|
|
||||||
if run:
|
|
||||||
toolchain_sh = self.toolchain
|
toolchain_sh = self.toolchain
|
||||||
if which(toolchain_sh) is None:
|
if which(toolchain_sh) is None:
|
||||||
msg = f"Unable to find {toolchain_sh.upper()} toolchain, please:\n"
|
msg = f"Unable to find {toolchain_sh.upper()} toolchain, please:\n"
|
||||||
|
@ -125,16 +96,3 @@ class OSFPGAToolchain:
|
||||||
|
|
||||||
if subprocess.call([toolchain_sh, "--batch", "--script", "build.tcl"]) != 0:
|
if subprocess.call([toolchain_sh, "--batch", "--script", "build.tcl"]) != 0:
|
||||||
raise OSError(f"Error occured during {toolchain_sh.upper()}'s script execution.")
|
raise OSError(f"Error occured during {toolchain_sh.upper()}'s script execution.")
|
||||||
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
return v_output.ns
|
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
|
||||||
clk.attr.add("keep")
|
|
||||||
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
|
|
||||||
if clk in self.clocks:
|
|
||||||
if period != self.clocks[clk]:
|
|
||||||
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
|
|
||||||
.format(self.clocks[clk], period))
|
|
||||||
self.clocks[clk] = period
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import math
|
||||||
|
|
||||||
from migen.fhdl.structure import _Fragment
|
from migen.fhdl.structure import _Fragment
|
||||||
|
|
||||||
|
from litex.build.generic_toolchain import GenericToolchain
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build.xilinx.vivado import _xdc_separator, _build_xdc
|
from litex.build.xilinx.vivado import _xdc_separator, _build_xdc
|
||||||
from litex.build import tools
|
from litex.build import tools
|
||||||
|
@ -28,7 +29,7 @@ F4CACHEPATH = '.f4cache'
|
||||||
# F4PGAToolchain -------------------------------------------------------------------------------
|
# F4PGAToolchain -------------------------------------------------------------------------------
|
||||||
# Formerly SymbiflowToolchain, Symbiflow has been renamed to F4PGA -----------------------------
|
# Formerly SymbiflowToolchain, Symbiflow has been renamed to F4PGA -----------------------------
|
||||||
|
|
||||||
class F4PGAToolchain:
|
class F4PGAToolchain(GenericToolchain):
|
||||||
attr_translate = {
|
attr_translate = {
|
||||||
"keep": ("dont_touch", "true"),
|
"keep": ("dont_touch", "true"),
|
||||||
"no_retiming": ("dont_touch", "true"),
|
"no_retiming": ("dont_touch", "true"),
|
||||||
|
@ -40,59 +41,11 @@ class F4PGAToolchain:
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.clocks = dict()
|
super().__init__()
|
||||||
self.false_paths = set()
|
|
||||||
self._partname = None
|
self._partname = None
|
||||||
|
self._flow = None
|
||||||
def _generate_prj_flow(self, platform, build_name):
|
|
||||||
target = "bitstream"
|
|
||||||
|
|
||||||
prj_flow_cfg_dict = {}
|
|
||||||
prj_flow_cfg_dict["dependencies"] = {}
|
|
||||||
prj_flow_cfg_dict["values"] = {}
|
|
||||||
prj_flow_cfg_dict[self._partname] = {}
|
|
||||||
|
|
||||||
deps_cfg = prj_flow_cfg_dict["dependencies"]
|
|
||||||
deps_cfg["sources"] = [f for f,language,_ in platform.sources if language in ["verilog", "system_verilog"]]
|
|
||||||
deps_cfg["xdc"] = f"{build_name}.xdc"
|
|
||||||
deps_cfg["sdc"] = f"{build_name}.sdc"
|
|
||||||
deps_cfg["build_dir"] = os.getcwd()
|
|
||||||
deps_cfg["synth_log"] = f"{build_name}_synth.log"
|
|
||||||
deps_cfg["pack_log"] = f"{build_name}_pack.log"
|
|
||||||
deps_cfg["json"] = f"{build_name}.json"
|
|
||||||
|
|
||||||
values_cfg = prj_flow_cfg_dict["values"]
|
|
||||||
values_cfg["top"] = build_name
|
|
||||||
values_cfg["part_name"] = self._partname
|
|
||||||
|
|
||||||
prj_flow_cfg = ProjectFlowConfig("")
|
|
||||||
prj_flow_cfg.flow_cfg = prj_flow_cfg_dict
|
|
||||||
|
|
||||||
flow_cfg = make_flow_config(prj_flow_cfg, self._partname)
|
|
||||||
|
|
||||||
flow = Flow(
|
|
||||||
target=target,
|
|
||||||
cfg=flow_cfg,
|
|
||||||
f4cache=F4Cache(F4CACHEPATH)
|
|
||||||
)
|
|
||||||
|
|
||||||
print("\nProject status:")
|
|
||||||
flow.print_resolved_dependencies(0)
|
|
||||||
print("")
|
|
||||||
|
|
||||||
return flow
|
|
||||||
|
|
||||||
def _build_clock_constraints(self, platform):
|
|
||||||
platform.add_platform_command(_xdc_separator("Clock constraints"))
|
|
||||||
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
|
||||||
platform.add_platform_command(
|
|
||||||
"create_clock -period " + str(period) +
|
|
||||||
" {clk}", clk=clk)
|
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
def build(self, platform, fragment,
|
||||||
build_dir = "build",
|
|
||||||
build_name = "top",
|
|
||||||
run = True,
|
|
||||||
enable_xpm = False,
|
enable_xpm = False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
|
@ -103,37 +56,60 @@ class F4PGAToolchain:
|
||||||
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
||||||
}.get(platform.device, platform.device)
|
}.get(platform.device, platform.device)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Generate timing constraints
|
|
||||||
self._build_clock_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)
|
|
||||||
|
|
||||||
set_verbosity_level(2)
|
set_verbosity_level(2)
|
||||||
|
|
||||||
# Generate design constraints
|
return self._build(platform, fragment, **kwargs)
|
||||||
tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc))
|
|
||||||
|
|
||||||
flow = self._generate_prj_flow(
|
def build_io_contraints(self):
|
||||||
platform = platform,
|
# Generate design constraints
|
||||||
build_name = build_name
|
tools.write_to_file(self._build_name + ".xdc", _build_xdc(self.named_sc, self.named_pc))
|
||||||
|
return (self._build_name + ".xdc", "XDC")
|
||||||
|
|
||||||
|
def build_timing_constraints(self):
|
||||||
|
self.platform.add_platform_command(_xdc_separator("Clock constraints"))
|
||||||
|
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
||||||
|
self.platform.add_platform_command(
|
||||||
|
"create_clock -period " + str(period) +
|
||||||
|
" {clk}", clk=clk)
|
||||||
|
return ("", "")
|
||||||
|
|
||||||
|
def build_project(self):
|
||||||
|
target = "bitstream"
|
||||||
|
|
||||||
|
prj_flow_cfg_dict = {}
|
||||||
|
prj_flow_cfg_dict["dependencies"] = {}
|
||||||
|
prj_flow_cfg_dict["values"] = {}
|
||||||
|
prj_flow_cfg_dict[self._partname] = {}
|
||||||
|
|
||||||
|
deps_cfg = prj_flow_cfg_dict["dependencies"]
|
||||||
|
deps_cfg["sources"] = [f for f,language,_ in self.platform.sources if language in ["verilog", "system_verilog"]]
|
||||||
|
deps_cfg["xdc"] = f"{self._build_name}.xdc"
|
||||||
|
deps_cfg["sdc"] = f"{self._build_name}.sdc"
|
||||||
|
deps_cfg["build_dir"] = os.getcwd()
|
||||||
|
deps_cfg["synth_log"] = f"{self._build_name}_synth.log"
|
||||||
|
deps_cfg["pack_log"] = f"{self._build_name}_pack.log"
|
||||||
|
deps_cfg["json"] = f"{self._build_name}.json"
|
||||||
|
|
||||||
|
values_cfg = prj_flow_cfg_dict["values"]
|
||||||
|
values_cfg["top"] = self._build_name
|
||||||
|
values_cfg["part_name"] = self._partname
|
||||||
|
|
||||||
|
prj_flow_cfg = ProjectFlowConfig("")
|
||||||
|
prj_flow_cfg.flow_cfg = prj_flow_cfg_dict
|
||||||
|
|
||||||
|
flow_cfg = make_flow_config(prj_flow_cfg, self._partname)
|
||||||
|
|
||||||
|
self._flow = Flow(
|
||||||
|
target=target,
|
||||||
|
cfg=flow_cfg,
|
||||||
|
f4cache=F4Cache(F4CACHEPATH)
|
||||||
)
|
)
|
||||||
|
|
||||||
if run:
|
print("\nProject status:")
|
||||||
|
self._flow.print_resolved_dependencies(0)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
def run_script(self, script):
|
||||||
try:
|
try:
|
||||||
flow.execute()
|
flow.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -141,19 +117,6 @@ class F4PGAToolchain:
|
||||||
|
|
||||||
flow.f4cache.save()
|
flow.f4cache.save()
|
||||||
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
return v_output.ns
|
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
|
||||||
clk.attr.add("keep")
|
|
||||||
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
|
|
||||||
if clk in self.clocks:
|
|
||||||
if period != self.clocks[clk]:
|
|
||||||
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
|
|
||||||
.format(self.clocks[clk], period))
|
|
||||||
self.clocks[clk] = period
|
|
||||||
|
|
||||||
def add_false_path_constraint(self, platform, from_, to):
|
def add_false_path_constraint(self, platform, from_, to):
|
||||||
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
||||||
return
|
return
|
||||||
|
|
|
@ -17,13 +17,48 @@ from shutil import which
|
||||||
|
|
||||||
from migen.fhdl.structure import _Fragment
|
from migen.fhdl.structure import _Fragment
|
||||||
|
|
||||||
|
from litex.build.generic_toolchain import GenericToolchain
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build import tools
|
from litex.build import tools
|
||||||
from litex.build.xilinx import common
|
from litex.build.xilinx import common
|
||||||
|
|
||||||
|
# XilinxISEToolchain --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class XilinxISEToolchain(GenericToolchain):
|
||||||
|
attr_translate = {
|
||||||
|
"keep": ("keep", "true"),
|
||||||
|
"no_retiming": ("register_balancing", "no"),
|
||||||
|
"async_reg": None,
|
||||||
|
"mr_ff": None,
|
||||||
|
"ars_ff1": None,
|
||||||
|
"ars_ff2": None,
|
||||||
|
"no_shreg_extract": ("shreg_extract", "no")
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.xst_opt = "-ifmt MIXED\n-use_new_parser yes\n-opt_mode SPEED\n-register_balancing yes"
|
||||||
|
self.map_opt = "-ol high -w"
|
||||||
|
self.par_opt = "-ol high -w"
|
||||||
|
self.ngdbuild_opt = ""
|
||||||
|
self.bitgen_opt = "-g Binary:Yes -w"
|
||||||
|
self.ise_commands = ""
|
||||||
|
self.mode = "xst"
|
||||||
|
self.isemode = "xst"
|
||||||
|
|
||||||
|
def build(self, platform, fragment,
|
||||||
|
mode = "xst",
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
|
self._mode = mode
|
||||||
|
self._isemode = mode if mode in ["xst", "cpld"] else "edif"
|
||||||
|
|
||||||
|
return self._build(platform, fragment, **kwargs)
|
||||||
|
|
||||||
# Constraints (.ucf) -------------------------------------------------------------------------------
|
# Constraints (.ucf) -------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _format_constraint(c):
|
@classmethod
|
||||||
|
def _format_constraint(cls, c):
|
||||||
if isinstance(c, Pins):
|
if isinstance(c, Pins):
|
||||||
return "LOC=" + c.identifiers[0]
|
return "LOC=" + c.identifiers[0]
|
||||||
elif isinstance(c, IOStandard):
|
elif isinstance(c, IOStandard):
|
||||||
|
@ -33,10 +68,10 @@ def _format_constraint(c):
|
||||||
elif isinstance(c, Misc):
|
elif isinstance(c, Misc):
|
||||||
return c.misc
|
return c.misc
|
||||||
|
|
||||||
def _format_ucf(signame, pin, others, resname):
|
def _format_ucf(self, signame, pin, others, resname):
|
||||||
fmt_c = []
|
fmt_c = []
|
||||||
for c in [Pins(pin)] + others:
|
for c in [Pins(pin)] + others:
|
||||||
fc = _format_constraint(c)
|
fc = self._format_constraint(c)
|
||||||
if fc is not None:
|
if fc is not None:
|
||||||
fmt_c.append(fc)
|
fmt_c.append(fc)
|
||||||
fmt_r = resname[0] + ":" + str(resname[1])
|
fmt_r = resname[0] + ":" + str(resname[1])
|
||||||
|
@ -44,25 +79,28 @@ def _format_ucf(signame, pin, others, resname):
|
||||||
fmt_r += "." + resname[2]
|
fmt_r += "." + resname[2]
|
||||||
return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
|
return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
|
||||||
|
|
||||||
def _build_ucf(named_sc, named_pc):
|
def build_io_constraints(self):
|
||||||
r = ""
|
r = ""
|
||||||
for sig, pins, others, resname in named_sc:
|
for sig, pins, others, resname in self.named_sc:
|
||||||
if len(pins) > 1:
|
if len(pins) > 1:
|
||||||
for i, p in enumerate(pins):
|
for i, p in enumerate(pins):
|
||||||
r += _format_ucf(sig + "(" + str(i) + ")", p, others, resname)
|
r += self._format_ucf(sig + "(" + str(i) + ")", p, others, resname)
|
||||||
else:
|
else:
|
||||||
r += _format_ucf(sig, pins[0], others, resname)
|
r += self._format_ucf(sig, pins[0], others, resname)
|
||||||
if named_pc:
|
if self.named_pc:
|
||||||
r += "\n" + "\n\n".join(named_pc)
|
r += "\n" + "\n\n".join(self.named_pc)
|
||||||
return r
|
tools.write_to_file(self._build_name + ".ucf", r)
|
||||||
|
return (self._build_name + ".ucf", "UCF")
|
||||||
|
|
||||||
# Project (.xst) -----------------------------------------------------------------------------------
|
# Project (.xst) -------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _build_xst(device, sources, vincpaths, build_name, xst_opt):
|
def build_project(self):
|
||||||
|
if self.mode not in ["xst", "cpld"]:
|
||||||
|
return ("", "")
|
||||||
prj_contents = ""
|
prj_contents = ""
|
||||||
for filename, language, library, *copy in sources:
|
for filename, language, library, *copy in self.platform.sources:
|
||||||
prj_contents += language + " " + library + " " + tools.cygpath(filename) + "\n"
|
prj_contents += language + " " + library + " " + tools.cygpath(filename) + "\n"
|
||||||
tools.write_to_file(build_name + ".prj", prj_contents)
|
tools.write_to_file(self._build_name + ".prj", prj_contents)
|
||||||
|
|
||||||
xst_contents = """run
|
xst_contents = """run
|
||||||
-ifn {build_name}.prj
|
-ifn {build_name}.prj
|
||||||
|
@ -70,22 +108,23 @@ def _build_xst(device, sources, vincpaths, build_name, xst_opt):
|
||||||
{xst_opt}
|
{xst_opt}
|
||||||
-ofn {build_name}.ngc
|
-ofn {build_name}.ngc
|
||||||
-p {device}
|
-p {device}
|
||||||
""".format(build_name=build_name, xst_opt=xst_opt, device=device)
|
""".format(build_name=self._build_name, xst_opt=self.xst_opt, device=self.platform.device)
|
||||||
if vincpaths:
|
if self.platform.verilog_include_paths:
|
||||||
xst_contents += "-vlgincdir {"
|
xst_contents += "-vlgincdir {"
|
||||||
for path in vincpaths:
|
for path in self.platform.verilog_include_paths:
|
||||||
xst_contents += tools.cygpath(path) + " "
|
xst_contents += tools.cygpath(path) + " "
|
||||||
xst_contents += "}"
|
xst_contents += "}"
|
||||||
tools.write_to_file(build_name + ".xst", xst_contents)
|
tools.write_to_file(self._build_name + ".xst", xst_contents)
|
||||||
|
|
||||||
# Yosys Run ----------------------------------------------------------------------------------------
|
# Yosys Run ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _run_yosys(device, sources, vincpaths, build_name):
|
def _run_yosys(build_name):
|
||||||
|
device = self.platform.device
|
||||||
ys_contents = ""
|
ys_contents = ""
|
||||||
incflags = ""
|
incflags = ""
|
||||||
for path in vincpaths:
|
for path in platform.verilog_include_paths:
|
||||||
incflags += " -I" + path
|
incflags += " -I" + path
|
||||||
for filename, language, library, *copy in sources:
|
for filename, language, library, *copy in self.platform.sources:
|
||||||
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
|
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
|
||||||
|
|
||||||
family = ""
|
family = ""
|
||||||
|
@ -98,9 +137,9 @@ def _run_yosys(device, sources, vincpaths, build_name):
|
||||||
|
|
||||||
ys_contents += """hierarchy -top top
|
ys_contents += """hierarchy -top top
|
||||||
synth_xilinx -top top -family {family} -ise
|
synth_xilinx -top top -family {family} -ise
|
||||||
write_edif -pvector bra {build_name}.edif""".format(build_name=build_name, family=family)
|
write_edif -pvector bra {build_name}.edif""".format(build_name=self._build_name, family=family)
|
||||||
|
|
||||||
ys_name = build_name + ".ys"
|
ys_name = self._build_name + ".ys"
|
||||||
tools.write_to_file(ys_name, ys_contents)
|
tools.write_to_file(ys_name, ys_contents)
|
||||||
r = subprocess.call(["yosys", ys_name])
|
r = subprocess.call(["yosys", ys_name])
|
||||||
if r != 0:
|
if r != 0:
|
||||||
|
@ -108,7 +147,7 @@ write_edif -pvector bra {build_name}.edif""".format(build_name=build_name, famil
|
||||||
|
|
||||||
# ISE Run ------------------------------------------------------------------------------------------
|
# ISE Run ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _run_ise(build_name, mode, ngdbuild_opt, toolchain, platform):
|
def build_script(self):
|
||||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||||
script_ext = ".bat"
|
script_ext = ".bat"
|
||||||
shell = ["cmd", "/c"]
|
shell = ["cmd", "/c"]
|
||||||
|
@ -121,7 +160,7 @@ def _run_ise(build_name, mode, ngdbuild_opt, toolchain, platform):
|
||||||
if os.getenv("LITEX_ENV_ISE", False):
|
if os.getenv("LITEX_ENV_ISE", False):
|
||||||
build_script_contents += "source " + os.path.join(os.getenv("LITEX_ENV_ISE"), "settings64.sh\n")
|
build_script_contents += "source " + os.path.join(os.getenv("LITEX_ENV_ISE"), "settings64.sh\n")
|
||||||
fail_stmt = ""
|
fail_stmt = ""
|
||||||
if mode == "edif":
|
if self._isemode == "edif":
|
||||||
ext = "ngo"
|
ext = "ngo"
|
||||||
build_script_contents += """
|
build_script_contents += """
|
||||||
edif2ngd {build_name}.edif {build_name}.{ext}{fail_stmt}
|
edif2ngd {build_name}.edif {build_name}.{ext}{fail_stmt}
|
||||||
|
@ -140,7 +179,7 @@ netgen -ofmt verilog -w -sim {build_name}.{ext} {build_name}_synth.v
|
||||||
build_script_contents += """
|
build_script_contents += """
|
||||||
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt}
|
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt}
|
||||||
"""
|
"""
|
||||||
if mode == "cpld":
|
if self._isemode == "cpld":
|
||||||
build_script_contents += """
|
build_script_contents += """
|
||||||
cpldfit -ofmt verilog {par_opt} -p {device} {build_name}.ngd{fail_stmt}
|
cpldfit -ofmt verilog {par_opt} -p {device} {build_name}.ngd{fail_stmt}
|
||||||
taengine -f {build_name}.vm6 -detail -iopath -l {build_name}.tim{fail_stmt}
|
taengine -f {build_name}.vm6 -detail -iopath -l {build_name}.tim{fail_stmt}
|
||||||
|
@ -152,13 +191,31 @@ map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf{fail_stm
|
||||||
par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf{fail_stmt}
|
par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf{fail_stmt}
|
||||||
bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt}
|
bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt}
|
||||||
"""
|
"""
|
||||||
build_script_contents = build_script_contents.format(build_name=build_name,
|
build_script_contents = build_script_contents.format(build_name=self._build_name,
|
||||||
ngdbuild_opt=ngdbuild_opt, bitgen_opt=toolchain.bitgen_opt, ext=ext,
|
ngdbuild_opt=self.ngdbuild_opt, bitgen_opt=self.bitgen_opt, ext=ext,
|
||||||
par_opt=toolchain.par_opt, map_opt=toolchain.map_opt,
|
par_opt=self.par_opt, map_opt=self.map_opt,
|
||||||
device=platform.device, fail_stmt=fail_stmt)
|
device=self.platform.device, fail_stmt=fail_stmt)
|
||||||
build_script_contents += toolchain.ise_commands.format(build_name=build_name)
|
build_script_contents += self.ise_commands.format(build_name=self._build_name)
|
||||||
build_script_file = "build_" + build_name + script_ext
|
build_script_file = "build_" + self._build_name + script_ext
|
||||||
tools.write_to_file(build_script_file, build_script_contents, force_unix=False)
|
tools.write_to_file(build_script_file, build_script_contents, force_unix=False)
|
||||||
|
return build_script_file
|
||||||
|
|
||||||
|
def run_script(self, script):
|
||||||
|
|
||||||
|
if self.mode == "yosys":
|
||||||
|
self._run_yosys()
|
||||||
|
self.ngdbuild_opt += "-p " + self.platform.device
|
||||||
|
|
||||||
|
if self.mode == "edif":
|
||||||
|
# Generate edif
|
||||||
|
e_output = self.platform.get_edif(self._fragment)
|
||||||
|
self._vns = e_output.ns
|
||||||
|
self.named_sc, self.named_pc = self.platform.resolve_signals(self._vns)
|
||||||
|
e_file = build_name + ".edif"
|
||||||
|
e_output.write(e_file)
|
||||||
|
self.build_io_constraints()
|
||||||
|
|
||||||
|
|
||||||
command = shell + [build_script_file]
|
command = shell + [build_script_file]
|
||||||
|
|
||||||
if which("ise") is None and os.getenv("LITEX_ENV_ISE", False) == False:
|
if which("ise") is None and os.getenv("LITEX_ENV_ISE", False) == False:
|
||||||
|
@ -171,86 +228,6 @@ bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt}
|
||||||
if tools.subprocess_call_filtered(command, common.colors) != 0:
|
if tools.subprocess_call_filtered(command, common.colors) != 0:
|
||||||
raise OSError("Error occured during ISE's script execution.")
|
raise OSError("Error occured during ISE's script execution.")
|
||||||
|
|
||||||
# XilinxISEToolchain --------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class XilinxISEToolchain:
|
|
||||||
attr_translate = {
|
|
||||||
"keep": ("keep", "true"),
|
|
||||||
"no_retiming": ("register_balancing", "no"),
|
|
||||||
"async_reg": None,
|
|
||||||
"mr_ff": None,
|
|
||||||
"ars_ff1": None,
|
|
||||||
"ars_ff2": None,
|
|
||||||
"no_shreg_extract": ("shreg_extract", "no")
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.xst_opt = "-ifmt MIXED\n-use_new_parser yes\n-opt_mode SPEED\n-register_balancing yes"
|
|
||||||
self.map_opt = "-ol high -w"
|
|
||||||
self.par_opt = "-ol high -w"
|
|
||||||
self.ngdbuild_opt = ""
|
|
||||||
self.bitgen_opt = "-g Binary:Yes -w"
|
|
||||||
self.ise_commands = ""
|
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
|
||||||
build_dir = "build",
|
|
||||||
build_name = "top",
|
|
||||||
run = True,
|
|
||||||
mode = "xst",
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
vns = None
|
|
||||||
try:
|
|
||||||
if mode in ["xst", "yosys", "cpld"]:
|
|
||||||
# Generate verilog
|
|
||||||
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
|
||||||
vns = v_output.ns
|
|
||||||
named_sc, named_pc = platform.resolve_signals(vns)
|
|
||||||
v_file = build_name + ".v"
|
|
||||||
v_output.write(v_file)
|
|
||||||
platform.add_source(v_file)
|
|
||||||
|
|
||||||
# Generate design project (.xst)
|
|
||||||
if mode in ["xst", "cpld"]:
|
|
||||||
_build_xst(platform.device, platform.sources, platform.verilog_include_paths, build_name, self.xst_opt)
|
|
||||||
isemode = mode
|
|
||||||
else:
|
|
||||||
# Run Yosys
|
|
||||||
if run:
|
|
||||||
_run_yosys(platform.device, platform.sources, platform.verilog_include_paths, build_name)
|
|
||||||
isemode = "edif"
|
|
||||||
self.ngdbuild_opt += "-p " + platform.device
|
|
||||||
|
|
||||||
if mode in ["edif"]:
|
|
||||||
# Generate edif
|
|
||||||
e_output = platform.get_edif(fragment)
|
|
||||||
vns = e_output.ns
|
|
||||||
named_sc, named_pc = platform.resolve_signals(vns)
|
|
||||||
e_file = build_name + ".edif"
|
|
||||||
e_output.write(e_file)
|
|
||||||
isemode = "edif"
|
|
||||||
|
|
||||||
# Generate design constraints (.ucf)
|
|
||||||
tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))
|
|
||||||
|
|
||||||
# Run ISE
|
|
||||||
if run:
|
|
||||||
_run_ise(build_name, isemode, self.ngdbuild_opt, self, platform)
|
|
||||||
finally:
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
return vns
|
|
||||||
|
|
||||||
# ISE is broken and you must use *separate* TNM_NET objects for period
|
# ISE is broken and you must use *separate* TNM_NET objects for period
|
||||||
# constraints and other constraints otherwise it will be unable to trace
|
# constraints and other constraints otherwise it will be unable to trace
|
||||||
# them through clock objects like DCM and PLL objects.
|
# them through clock objects like DCM and PLL objects.
|
||||||
|
|
|
@ -17,6 +17,7 @@ from shutil import which
|
||||||
from migen.fhdl.structure import _Fragment, wrap, Constant
|
from migen.fhdl.structure import _Fragment, wrap, Constant
|
||||||
from migen.fhdl.specials import Instance
|
from migen.fhdl.specials import Instance
|
||||||
|
|
||||||
|
from litex.build.generic_toolchain import GenericToolchain
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build.xilinx.vivado import _xdc_separator, _format_xdc, _build_xdc
|
from litex.build.xilinx.vivado import _xdc_separator, _format_xdc, _build_xdc
|
||||||
from litex.build import tools
|
from litex.build import tools
|
||||||
|
@ -86,7 +87,7 @@ def _run_make():
|
||||||
|
|
||||||
# YosysNextpnrToolchain -------------------------------------------------------------------------------
|
# YosysNextpnrToolchain -------------------------------------------------------------------------------
|
||||||
|
|
||||||
class YosysNextpnrToolchain:
|
class YosysNextpnrToolchain(GenericToolchain):
|
||||||
attr_translate = {
|
attr_translate = {
|
||||||
#"keep": ("dont_touch", "true"),
|
#"keep": ("dont_touch", "true"),
|
||||||
#"no_retiming": ("dont_touch", "true"),
|
#"no_retiming": ("dont_touch", "true"),
|
||||||
|
@ -98,13 +99,12 @@ class YosysNextpnrToolchain:
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.clocks = dict()
|
super().__init__()
|
||||||
self.false_paths = set()
|
|
||||||
self.f4pga_device = None
|
self.f4pga_device = None
|
||||||
self.bitstream_device = None
|
self.bitstream_device = None
|
||||||
self._partname = None
|
self._partname = None
|
||||||
|
|
||||||
def _check_properties(self, platform):
|
def _check_properties(self):
|
||||||
if not self.f4pga_device:
|
if not self.f4pga_device:
|
||||||
try:
|
try:
|
||||||
self.f4pga_device = {
|
self.f4pga_device = {
|
||||||
|
@ -113,7 +113,7 @@ class YosysNextpnrToolchain:
|
||||||
"xc7a100tcsg324-1" : "xc7a35t",
|
"xc7a100tcsg324-1" : "xc7a35t",
|
||||||
"xc7z010clg400-1" : "xc7z010",
|
"xc7z010clg400-1" : "xc7z010",
|
||||||
"xc7z020clg400-1" : "xc7z020",
|
"xc7z020clg400-1" : "xc7z020",
|
||||||
}[platform.device]
|
}[self.platform.device]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(f"f4pga_device is not specified")
|
raise ValueError(f"f4pga_device is not specified")
|
||||||
if not self.bitstream_device:
|
if not self.bitstream_device:
|
||||||
|
@ -123,9 +123,9 @@ class YosysNextpnrToolchain:
|
||||||
self.bitstream_device = {
|
self.bitstream_device = {
|
||||||
"xc7a": "artix7", # xc7a35t, xc7a50t, xc7a100t, xc7a200t
|
"xc7a": "artix7", # xc7a35t, xc7a50t, xc7a100t, xc7a200t
|
||||||
"xc7z": "zynq7", # xc7z010, xc7z020
|
"xc7z": "zynq7", # xc7z010, xc7z020
|
||||||
}[platform.device[:4]]
|
}[self.platform.device[:4]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(f"Unsupported device: {platform.device}")
|
raise ValueError(f"Unsupported device: {self.platform.device}")
|
||||||
# FIXME: prjxray-db doesn't have xc7a35ticsg324-1L - use closest replacement
|
# FIXME: prjxray-db doesn't have xc7a35ticsg324-1L - use closest replacement
|
||||||
self._partname = {
|
self._partname = {
|
||||||
"xc7a35ticsg324-1L" : "xc7a35tcsg324-1",
|
"xc7a35ticsg324-1L" : "xc7a35tcsg324-1",
|
||||||
|
@ -133,15 +133,15 @@ class YosysNextpnrToolchain:
|
||||||
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
||||||
"xc7z010clg400-1" : "xc7z010clg400-1",
|
"xc7z010clg400-1" : "xc7z010clg400-1",
|
||||||
"xc7z020clg400-1" : "xc7z020clg400-1",
|
"xc7z020clg400-1" : "xc7z020clg400-1",
|
||||||
}.get(platform.device, platform.device)
|
}.get(self.platform.device, self.platform.device)
|
||||||
|
|
||||||
def _generate_makefile(self, platform, build_name):
|
def build_script(self):
|
||||||
Var = _MakefileGenerator.Var
|
Var = _MakefileGenerator.Var
|
||||||
Rule = _MakefileGenerator.Rule
|
Rule = _MakefileGenerator.Rule
|
||||||
|
|
||||||
makefile = _MakefileGenerator([
|
makefile = _MakefileGenerator([
|
||||||
"# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n",
|
"# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n",
|
||||||
Var("TOP", build_name),
|
Var("TOP", self._build_name),
|
||||||
Var("PARTNAME", self._partname),
|
Var("PARTNAME", self._partname),
|
||||||
Var("DEVICE", self.f4pga_device),
|
Var("DEVICE", self.f4pga_device),
|
||||||
Var("BITSTREAM_DEVICE", self.bitstream_device),
|
Var("BITSTREAM_DEVICE", self.bitstream_device),
|
||||||
|
@ -149,10 +149,10 @@ class YosysNextpnrToolchain:
|
||||||
Var("DB_DIR", "/usr/share/nextpnr/prjxray-db"), #FIXME: resolve path
|
Var("DB_DIR", "/usr/share/nextpnr/prjxray-db"), #FIXME: resolve path
|
||||||
Var("CHIPDB_DIR", "/usr/share/nextpnr/xilinx-chipdb"), #FIXME: resolve path
|
Var("CHIPDB_DIR", "/usr/share/nextpnr/xilinx-chipdb"), #FIXME: resolve path
|
||||||
"",
|
"",
|
||||||
Var("VERILOG", [f for f,language,_ in platform.sources if language in ["verilog", "system_verilog"]]),
|
Var("VERILOG", [f for f,language,_ in self.platform.sources if language in ["verilog", "system_verilog"]]),
|
||||||
Var("MEM_INIT", [f"{name}" for name in os.listdir() if name.endswith(".init")]),
|
Var("MEM_INIT", [f"{name}" for name in os.listdir() if name.endswith(".init")]),
|
||||||
Var("SDC", f"{build_name}.sdc"),
|
Var("SDC", f"{self._build_name}.sdc"),
|
||||||
Var("XDC", f"{build_name}.xdc"),
|
Var("XDC", f"{self._build_name}.xdc"),
|
||||||
Var("ARTIFACTS", [
|
Var("ARTIFACTS", [
|
||||||
"$(TOP).fasm", "$(TOP).frames",
|
"$(TOP).fasm", "$(TOP).frames",
|
||||||
"*.bit", "*.fasm", "*.json", "*.log", "*.rpt",
|
"*.bit", "*.fasm", "*.json", "*.log", "*.rpt",
|
||||||
|
@ -182,75 +182,38 @@ class YosysNextpnrToolchain:
|
||||||
])
|
])
|
||||||
|
|
||||||
tools.write_to_file("Makefile", makefile.generate())
|
tools.write_to_file("Makefile", makefile.generate())
|
||||||
|
return "Makefile"
|
||||||
|
|
||||||
def _build_clock_constraints(self, platform):
|
def build_timing_constraints(self, vns):
|
||||||
platform.add_platform_command(_xdc_separator("Clock constraints"))
|
self.platform.add_platform_command(_xdc_separator("Clock constraints"))
|
||||||
#for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
#for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
|
||||||
# platform.add_platform_command(
|
# platform.add_platform_command(
|
||||||
# "create_clock -period " + str(period) +
|
# "create_clock -period " + str(period) +
|
||||||
# " {clk}", clk=clk)
|
# " {clk}", clk=clk)
|
||||||
pass #clock constraints not supported
|
pass #clock constraints not supported
|
||||||
|
|
||||||
|
def build_io_constraints(self):
|
||||||
|
tools.write_to_file(self._build_name + ".xdc", _build_xdc(self.named_sc, self.named_pc))
|
||||||
|
return (self._build_name + ".xdc", "XDC")
|
||||||
|
|
||||||
def _fix_instance(self, instance):
|
def _fix_instance(self, instance):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
def finalize(self):
|
||||||
build_dir = "build",
|
|
||||||
build_name = "top",
|
|
||||||
run = True,
|
|
||||||
enable_xpm = False,
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
self._check_properties(platform)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# toolchain-specific fixes
|
# toolchain-specific fixes
|
||||||
for instance in fragment.specials:
|
for instance in self.fragment.specials:
|
||||||
if isinstance(instance, Instance):
|
if isinstance(instance, Instance):
|
||||||
self._fix_instance(instance)
|
self._fix_instance(instance)
|
||||||
|
|
||||||
# Generate timing constraints
|
def build(self, platform, fragment,
|
||||||
self._build_clock_constraints(platform)
|
enable_xpm = False,
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
# Generate verilog
|
#FIXME
|
||||||
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
self.platform = platform
|
||||||
named_sc, named_pc = platform.resolve_signals(v_output.ns)
|
self._check_properties()
|
||||||
v_file = build_name + ".v"
|
|
||||||
v_output.write(v_file)
|
|
||||||
platform.add_source(v_file)
|
|
||||||
|
|
||||||
self._generate_makefile(
|
return self._build(platform, fragment, **kwargs)
|
||||||
platform = platform,
|
|
||||||
build_name = build_name
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate design constraints
|
|
||||||
tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc))
|
|
||||||
|
|
||||||
if run:
|
|
||||||
_run_make()
|
|
||||||
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
return v_output.ns
|
|
||||||
|
|
||||||
def add_period_constraint(self, platform, clk, period):
|
|
||||||
clk.attr.add("keep")
|
|
||||||
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
|
|
||||||
if clk in self.clocks:
|
|
||||||
if period != self.clocks[clk]:
|
|
||||||
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
|
|
||||||
.format(self.clocks[clk], period))
|
|
||||||
self.clocks[clk] = period
|
|
||||||
|
|
||||||
def add_false_path_constraint(self, platform, from_, to):
|
def add_false_path_constraint(self, platform, from_, to):
|
||||||
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
||||||
|
|
Loading…
Reference in New Issue