build: efinix/efinity lattice/diamond osfpga xilinx/f4pga xilinx/ise xilinx/yosys_nextpnr -> GenericToolchain
This commit is contained in:
parent
a4bb65655c
commit
382ebbf661
|
@ -23,397 +23,332 @@ from migen.fhdl.simplify import FullMemoryWE
|
|||
from litex.build import tools
|
||||
from litex.build.generic_platform import *
|
||||
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 InterfaceWriter
|
||||
|
||||
def get_pin_direction(fragment, platform, pinname):
|
||||
pins = platform.constraint_manager.get_io_signals()
|
||||
for pin in sorted(pins, key=lambda x: x.duid):
|
||||
# Better idea ???
|
||||
if (pinname.split("[")[0] == pin.name):
|
||||
return pin.direction
|
||||
return "Unknown"
|
||||
|
||||
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
||||
|
||||
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 = ""
|
||||
if len(pins) > 1:
|
||||
l = ",{},0".format(len(pins) - 1)
|
||||
d = get_pin_direction(fragment, platform, sig)
|
||||
return 'design.create_{d}_gpio("{name}"{len})'.format(d=d, name=sig, len=l)
|
||||
|
||||
def _format_constraint(c, signame, fmt_r, fragment, platform):
|
||||
# IO location constraints
|
||||
if isinstance(c, Pins):
|
||||
tpl = 'design.assign_pkg_pin("{signame}","{pin}")\n'
|
||||
return tpl.format(signame=signame, name=fmt_r, pin=c.identifiers[0])
|
||||
|
||||
# IO standard property
|
||||
elif isinstance(c, IOStandard):
|
||||
prop = ""
|
||||
valid = [ "3.3_V_LVTTL_/_LVCMOS", "2.5_V_LVCMOS", "1.8_V_LVCMOS",
|
||||
"1.2_V_Differential_HSTL", "1.2_V_Differential_SSTL",
|
||||
"1.2_V_HSTL", "1.2_V_LVCMOS", "1.2_V_SSTL", "1.5_V_Differential_HSTL",
|
||||
"1.5_V_Differential_SSTL", "1.5_V_HSTL", "1.5_V_LVCMOS", "1.5_V_SSTL",
|
||||
"1.8_V_Differential_HSTL", "1.8_V_Differential_SSTL", "1.8_V_HSTL",
|
||||
"1.8_V_LVCMOS", "1.8_V_SSTL", "2.5_V_LVCMOS", "3.0_V_LVCMOS",
|
||||
"3.0_V_LVTTL", "3.3_V_LVCMOS", "3.3_V_LVTTL"
|
||||
]
|
||||
|
||||
if c.name in valid:
|
||||
prop = "IO_STANDARD"
|
||||
|
||||
if prop == "":
|
||||
print("{} has a wrong IOStandard format [{}]".format(signame, c.name))
|
||||
print("Sould be selected from {}\n".format(valid))
|
||||
# Print error, warning ??
|
||||
return ""
|
||||
|
||||
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
||||
return tpl.format(signame=signame, prop=prop, val=c.name)
|
||||
|
||||
# Others constraints
|
||||
elif isinstance(c, Misc):
|
||||
prop = ""
|
||||
if c.misc in ["WEAK_PULLUP", "WEAK_PULLDOWN"]:
|
||||
prop = "PULL_OPTION"
|
||||
val = c.misc
|
||||
|
||||
if "DRIVE_STRENGTH" in c.misc:
|
||||
prop = "DRIVE_STRENGTH"
|
||||
val = c.misc.split("=")[1]
|
||||
|
||||
if "SLEWRATE" in c.misc:
|
||||
prop = "SLEW_RATE"
|
||||
val = "1"
|
||||
|
||||
if prop == "":
|
||||
# Print error, warning ??
|
||||
return ""
|
||||
|
||||
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
||||
return tpl.format(signame=signame, prop=prop, val=val)
|
||||
|
||||
def _format_conf_constraint(signame, pin, others, resname, fragment, platform):
|
||||
fmt_r = "{}:{}".format(*resname[:2])
|
||||
if resname[2] is not None:
|
||||
fmt_r += "." + resname[2]
|
||||
fmt_c = [_format_constraint(c, signame, fmt_r, fragment, platform) for c in ([Pins(pin)] + others)]
|
||||
return "".join(fmt_c)
|
||||
|
||||
def _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios):
|
||||
conf = []
|
||||
inst = []
|
||||
|
||||
# GPIO
|
||||
for sig, pins, others, resname in named_sc:
|
||||
excluded = False
|
||||
for excluded_io in excluded_ios:
|
||||
if isinstance(excluded_io, str):
|
||||
if sig == excluded_io:
|
||||
excluded = True
|
||||
elif isinstance(excluded_io, Signal):
|
||||
if sig == excluded_io.name:
|
||||
excluded = True
|
||||
if excluded:
|
||||
continue
|
||||
inst.append(_create_gpio_instance(fragment, platform, sig, pins))
|
||||
if len(pins) > 1:
|
||||
for i, p in enumerate(pins):
|
||||
conf.append(_format_conf_constraint("{}[{}]".format(sig, i), p, others, resname, fragment, platform))
|
||||
else:
|
||||
conf.append(_format_conf_constraint(sig, pins[0], others, resname, fragment, platform))
|
||||
if named_pc:
|
||||
conf.append("\n\n".join(named_pc))
|
||||
|
||||
conf = inst + conf
|
||||
|
||||
return "\n".join(conf)
|
||||
|
||||
def _build_peri(efinity_path, build_name, device, named_sc, named_pc, fragment, platform, additional_iface_commands, excluded_ios):
|
||||
pythonpath = ""
|
||||
|
||||
header = platform.toolchain.ifacewriter.header(build_name, device)
|
||||
gen = platform.toolchain.ifacewriter.generate(device)
|
||||
#TODO : move this to ifacewriter
|
||||
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios)
|
||||
add = "\n".join(additional_iface_commands)
|
||||
footer = platform.toolchain.ifacewriter.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:
|
||||
raise OSError("Error occurred during Efinity peri script execution.")
|
||||
|
||||
|
||||
# Project configuration (.xml) ---------------------------------------------------------------------
|
||||
|
||||
def _build_xml(family, device, timing_model, build_name, sources):
|
||||
now = datetime.datetime.now()
|
||||
|
||||
# Create Project.
|
||||
root = et.Element("efx:project")
|
||||
root.attrib["xmlns:efx"] = "http://www.efinixinc.com/enf_proj"
|
||||
root.attrib["name"] = build_name
|
||||
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["last_change_date"] = f"Date : {now.strftime('%Y-%m-%d %H:%M')}"
|
||||
|
||||
# Add Device.
|
||||
device_info = et.SubElement(root, "efx:device_info")
|
||||
et.SubElement(device_info, "efx:family", name=family)
|
||||
et.SubElement(device_info, "efx:device", name=device)
|
||||
et.SubElement(device_info, "efx:timing_model", name=timing_model)
|
||||
|
||||
# Add Design Info.
|
||||
design_info = et.SubElement(root, "efx:design_info")
|
||||
et.SubElement(design_info, "efx:top_module", name=build_name)
|
||||
|
||||
# Add Design Sources.
|
||||
for filename, language, library, *copy in sources:
|
||||
if language is None:
|
||||
continue
|
||||
et.SubElement(design_info, "efx:design_file", {
|
||||
"name" : filename,
|
||||
"version" : "default",
|
||||
"library" : "default" if ".vh" not in filename else library,
|
||||
})
|
||||
|
||||
# Add Timing Constraints.
|
||||
constraint_info = et.SubElement(root, "efx:constraint_info")
|
||||
et.SubElement(constraint_info, "efx:sdc_file", name=f"{build_name}.sdc")
|
||||
|
||||
# Add Misc Info.
|
||||
misc_info = et.SubElement(root, "efx:misc_info")
|
||||
|
||||
# Add IP Info.
|
||||
ip_info = et.SubElement(root, "efx:ip_info")
|
||||
|
||||
# Generate .xml
|
||||
xml_str = et.tostring(root, "utf-8")
|
||||
xml_str = expatbuilder.parseString(xml_str, False)
|
||||
xml_str = xml_str.toprettyxml(indent=" ")
|
||||
tools.write_to_file("{}.xml".format(build_name), xml_str)
|
||||
|
||||
# Efinity Toolchain --------------------------------------------------------------------------------
|
||||
|
||||
class EfinityToolchain:
|
||||
class EfinityToolchain(GenericToolchain):
|
||||
attr_translate = {}
|
||||
|
||||
def __init__(self, efinity_path):
|
||||
super().__init__()
|
||||
self.options = {}
|
||||
self.clocks = dict()
|
||||
self.false_paths = set()
|
||||
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 build(self, platform, fragment,
|
||||
build_dir = "build",
|
||||
build_name = "top",
|
||||
run = True,
|
||||
**kwargs):
|
||||
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) + "}"
|
||||
|
||||
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)
|
||||
def build(self, platform, fragment, **kwargs):
|
||||
|
||||
# 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)
|
||||
return self._build(platform, fragment, **kwargs)
|
||||
|
||||
# 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")
|
||||
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
||||
|
||||
# Add Include Paths.
|
||||
if platform.verilog_include_paths:
|
||||
self.options["includ_path"] = "{" + ";".join(platform.verilog_include_paths) + "}"
|
||||
def build_timing_constraints(self, vns):
|
||||
sdc = []
|
||||
|
||||
os.environ["EFXPT_HOME"] = self.efinity_path + "/pt"
|
||||
# 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)))
|
||||
|
||||
# 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,
|
||||
)
|
||||
# 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)))
|
||||
|
||||
# Generate project file (.xml)
|
||||
_build_xml(
|
||||
family = platform.family,
|
||||
device = platform.device,
|
||||
timing_model = platform.timing_model,
|
||||
build_name = build_name,
|
||||
sources = platform.sources
|
||||
)
|
||||
# Add additional commands
|
||||
sdc += self.additional_sdc_commands
|
||||
|
||||
# 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
|
||||
)
|
||||
# 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):
|
||||
# Better idea ???
|
||||
if (pinname.split("[")[0] == pin.name):
|
||||
return pin.direction
|
||||
return "Unknown"
|
||||
|
||||
def _create_gpio_instance(self, sig, pins):
|
||||
l = ""
|
||||
if len(pins) > 1:
|
||||
l = ",{},0".format(len(pins) - 1)
|
||||
d = self.get_pin_direction(sig)
|
||||
return 'design.create_{d}_gpio("{name}"{len})'.format(d=d, name=sig, len=l)
|
||||
|
||||
def _format_constraint(self, c, signame, fmt_r):
|
||||
# IO location constraints
|
||||
if isinstance(c, Pins):
|
||||
tpl = 'design.assign_pkg_pin("{signame}","{pin}")\n'
|
||||
return tpl.format(signame=signame, name=fmt_r, pin=c.identifiers[0])
|
||||
|
||||
# IO standard property
|
||||
elif isinstance(c, IOStandard):
|
||||
prop = ""
|
||||
valid = [ "3.3_V_LVTTL_/_LVCMOS", "2.5_V_LVCMOS", "1.8_V_LVCMOS",
|
||||
"1.2_V_Differential_HSTL", "1.2_V_Differential_SSTL",
|
||||
"1.2_V_HSTL", "1.2_V_LVCMOS", "1.2_V_SSTL", "1.5_V_Differential_HSTL",
|
||||
"1.5_V_Differential_SSTL", "1.5_V_HSTL", "1.5_V_LVCMOS", "1.5_V_SSTL",
|
||||
"1.8_V_Differential_HSTL", "1.8_V_Differential_SSTL", "1.8_V_HSTL",
|
||||
"1.8_V_LVCMOS", "1.8_V_SSTL", "2.5_V_LVCMOS", "3.0_V_LVCMOS",
|
||||
"3.0_V_LVTTL", "3.3_V_LVCMOS", "3.3_V_LVTTL"
|
||||
]
|
||||
|
||||
if c.name in valid:
|
||||
prop = "IO_STANDARD"
|
||||
|
||||
if prop == "":
|
||||
print("{} has a wrong IOStandard format [{}]".format(signame, c.name))
|
||||
print("Sould be selected from {}\n".format(valid))
|
||||
# Print error, warning ??
|
||||
return ""
|
||||
|
||||
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
||||
return tpl.format(signame=signame, prop=prop, val=c.name)
|
||||
|
||||
# Others constraints
|
||||
elif isinstance(c, Misc):
|
||||
prop = ""
|
||||
if c.misc in ["WEAK_PULLUP", "WEAK_PULLDOWN"]:
|
||||
prop = "PULL_OPTION"
|
||||
val = c.misc
|
||||
|
||||
if "DRIVE_STRENGTH" in c.misc:
|
||||
prop = "DRIVE_STRENGTH"
|
||||
val = c.misc.split("=")[1]
|
||||
|
||||
if "SLEWRATE" in c.misc:
|
||||
prop = "SLEW_RATE"
|
||||
val = "1"
|
||||
|
||||
if prop == "":
|
||||
# Print error, warning ??
|
||||
return ""
|
||||
|
||||
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
|
||||
return tpl.format(signame=signame, prop=prop, val=val)
|
||||
|
||||
def _format_conf_constraint(self, signame, pin, others, resname):
|
||||
fmt_r = "{}:{}".format(*resname[:2])
|
||||
if resname[2] is not None:
|
||||
fmt_r += "." + resname[2]
|
||||
fmt_c = [self._format_constraint(c, signame, fmt_r) for c in ([Pins(pin)] + others)]
|
||||
return "".join(fmt_c)
|
||||
|
||||
def _build_iface_gpio(self):
|
||||
conf = []
|
||||
inst = []
|
||||
|
||||
# GPIO
|
||||
for sig, pins, others, resname in self.named_sc:
|
||||
excluded = False
|
||||
for excluded_io in self.excluded_ios:
|
||||
if isinstance(excluded_io, str):
|
||||
if sig == excluded_io:
|
||||
excluded = True
|
||||
elif isinstance(excluded_io, Signal):
|
||||
if sig == excluded_io.name:
|
||||
excluded = True
|
||||
if excluded:
|
||||
continue
|
||||
inst.append(self._create_gpio_instance(sig, pins))
|
||||
if len(pins) > 1:
|
||||
for i, p in enumerate(pins):
|
||||
conf.append(_format_conf_constraint("{}[{}]".format(sig, i), p, others, resname))
|
||||
else:
|
||||
conf.append(self._format_conf_constraint(sig, pins[0], others, resname))
|
||||
if self.named_pc:
|
||||
conf.append("\n\n".join(self.named_pc))
|
||||
|
||||
conf = inst + conf
|
||||
|
||||
return "\n".join(conf)
|
||||
|
||||
def build_io_constraints(self):
|
||||
pythonpath = ""
|
||||
|
||||
header = self.ifacewriter.header(self._build_name, self.platform.device)
|
||||
gen = self.ifacewriter.generate(self.platform.device)
|
||||
#TODO : move this to ifacewriter
|
||||
gpio = self._build_iface_gpio()
|
||||
add = "\n".join(self.additional_iface_commands)
|
||||
footer = self.ifacewriter.footer()
|
||||
|
||||
tools.write_to_file("iface.py", header + gen + gpio + add + footer)
|
||||
|
||||
if tools.subprocess_call_filtered([self.efinity_path + "/bin/python3", "iface.py"], common.colors) != 0:
|
||||
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 platform.iobank_info:
|
||||
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()
|
||||
|
||||
# Run
|
||||
if run:
|
||||
# Synthesis/Mapping.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_map",
|
||||
"--project", f"{build_name}",
|
||||
"--root", f"{build_name}",
|
||||
"--write-efx-verilog", f"outflow/{build_name}.map.v",
|
||||
"--write-premap-module", f"outflow/{build_name}.elab.vdb",
|
||||
"--binary-db", f"{build_name}.vdb",
|
||||
"--family", platform.family,
|
||||
"--device", platform.device,
|
||||
"--mode", "speed",
|
||||
"--max_ram", "-1",
|
||||
"--max_mult", "-1",
|
||||
"--infer-clk-enable", "3",
|
||||
"--infer-sync-set-reset", "1",
|
||||
"--fanout-limit", "0",
|
||||
"--bram_output_regs_packing", "1",
|
||||
"--retiming", "1",
|
||||
"--seq_opt", "1",
|
||||
"--blast_const_operand_adders", "1",
|
||||
"--mult_input_regs_packing", "1",
|
||||
"--mult_output_regs_packing", "1",
|
||||
"--veri_option", "verilog_mode=verilog_2k,vhdl_mode=vhdl_2008",
|
||||
"--work-dir", "work_syn",
|
||||
"--output-dir", "outflow",
|
||||
"--project-xml", f"{build_name}.xml",
|
||||
"--I", "./"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_map execution.")
|
||||
|
||||
# Place and Route.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3",
|
||||
self.efinity_path + "/scripts/efx_run_pt.py",
|
||||
f"{build_name}",
|
||||
platform.family,
|
||||
platform.device
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_run_pt execution.")
|
||||
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pnr",
|
||||
"--circuit", f"{build_name}",
|
||||
"--family", platform.family,
|
||||
"--device", platform.device,
|
||||
"--operating_conditions", platform.timing_model,
|
||||
"--pack",
|
||||
"--place",
|
||||
"--route",
|
||||
"--vdb_file", f"work_syn/{build_name}.vdb",
|
||||
"--use_vdb_file", "on",
|
||||
"--place_file", f"outflow/{build_name}.place",
|
||||
"--route_file", f"outflow/{build_name}.route",
|
||||
"--sdc_file", f"{build_name}.sdc",
|
||||
"--sync_file", f"outflow/{build_name}.interface.csv",
|
||||
"--seed", "1",
|
||||
"--work_dir", "work_pnr",
|
||||
"--output_dir", "outflow",
|
||||
"--timing_analysis", "on",
|
||||
"--load_delay_matrix"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_pnr execution.")
|
||||
# Project configuration (.xml) ---------------------------------------------------------------------
|
||||
|
||||
# Bitstream.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pgm",
|
||||
"--source", f"work_pnr/{build_name}.lbf",
|
||||
"--dest", f"{build_name}.hex",
|
||||
"--device", platform.device,
|
||||
"--family", platform.family,
|
||||
"--periph", f"outflow/{build_name}.lpf",
|
||||
"--oscillator_clock_divider", "DIV8",
|
||||
"--spi_low_power_mode", "off",
|
||||
"--io_weak_pullup", "on",
|
||||
"--enable_roms", "on",
|
||||
"--mode", "active",
|
||||
"--width", "1",
|
||||
"--enable_crc_check", "on"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_pgm execution.")
|
||||
def build_project(self):
|
||||
now = datetime.datetime.now()
|
||||
|
||||
os.chdir(cwd)
|
||||
# Create Project.
|
||||
root = et.Element("efx:project")
|
||||
root.attrib["xmlns:efx"] = "http://www.efinixinc.com/enf_proj"
|
||||
root.attrib["name"] = self._build_name
|
||||
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["last_change_date"] = f"Date : {now.strftime('%Y-%m-%d %H:%M')}"
|
||||
|
||||
return v_output.ns
|
||||
# Add Device.
|
||||
device_info = et.SubElement(root, "efx:device_info")
|
||||
et.SubElement(device_info, "efx:family", name=self.platform.family)
|
||||
et.SubElement(device_info, "efx:device", name=self.platform.device)
|
||||
et.SubElement(device_info, "efx:timing_model", name=self.platform.timing_model)
|
||||
|
||||
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
|
||||
# Add Design Info.
|
||||
design_info = et.SubElement(root, "efx:design_info")
|
||||
et.SubElement(design_info, "efx:top_module", name=self._build_name)
|
||||
|
||||
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))
|
||||
# Add Design Sources.
|
||||
for filename, language, library, *copy in self.platform.sources:
|
||||
if language is None:
|
||||
continue
|
||||
et.SubElement(design_info, "efx:design_file", {
|
||||
"name" : filename,
|
||||
"version" : "default",
|
||||
"library" : "default" if ".vh" not in filename else library,
|
||||
})
|
||||
|
||||
# Add Timing Constraints.
|
||||
constraint_info = et.SubElement(root, "efx:constraint_info")
|
||||
et.SubElement(constraint_info, "efx:sdc_file", name=f"{self._build_name}.sdc")
|
||||
|
||||
# Add Misc Info.
|
||||
misc_info = et.SubElement(root, "efx:misc_info")
|
||||
|
||||
# Add IP Info.
|
||||
ip_info = et.SubElement(root, "efx:ip_info")
|
||||
|
||||
# Generate .xml
|
||||
xml_str = et.tostring(root, "utf-8")
|
||||
xml_str = expatbuilder.parseString(xml_str, False)
|
||||
xml_str = xml_str.toprettyxml(indent=" ")
|
||||
tools.write_to_file("{}.xml".format(self._build_name), xml_str)
|
||||
|
||||
def build_script(self):
|
||||
return "" # not used
|
||||
|
||||
def run_script(self, script):
|
||||
# Synthesis/Mapping.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_map",
|
||||
"--project", f"{self._build_name}",
|
||||
"--root", f"{self._build_name}",
|
||||
"--write-efx-verilog", f"outflow/{self._build_name}.map.v",
|
||||
"--write-premap-module", f"outflow/{self._build_name}.elab.vdb",
|
||||
"--binary-db", f"{self._build_name}.vdb",
|
||||
"--family", platform.family,
|
||||
"--device", platform.device,
|
||||
"--mode", "speed",
|
||||
"--max_ram", "-1",
|
||||
"--max_mult", "-1",
|
||||
"--infer-clk-enable", "3",
|
||||
"--infer-sync-set-reset", "1",
|
||||
"--fanout-limit", "0",
|
||||
"--bram_output_regs_packing", "1",
|
||||
"--retiming", "1",
|
||||
"--seq_opt", "1",
|
||||
"--blast_const_operand_adders", "1",
|
||||
"--mult_input_regs_packing", "1",
|
||||
"--mult_output_regs_packing", "1",
|
||||
"--veri_option", "verilog_mode=verilog_2k,vhdl_mode=vhdl_2008",
|
||||
"--work-dir", "work_syn",
|
||||
"--output-dir", "outflow",
|
||||
"--project-xml", f"{self._build_name}.xml",
|
||||
"--I", "./"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_map execution.")
|
||||
|
||||
# Place and Route.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3",
|
||||
self.efinity_path + "/scripts/efx_run_pt.py",
|
||||
f"{self._build_name}",
|
||||
platform.family,
|
||||
platform.device
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_run_pt execution.")
|
||||
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pnr",
|
||||
"--circuit", f"{self._build_name}",
|
||||
"--family", platform.family,
|
||||
"--device", platform.device,
|
||||
"--operating_conditions", platform.timing_model,
|
||||
"--pack",
|
||||
"--place",
|
||||
"--route",
|
||||
"--vdb_file", f"work_syn/{self._build_name}.vdb",
|
||||
"--use_vdb_file", "on",
|
||||
"--place_file", f"outflow/{self._build_name}.place",
|
||||
"--route_file", f"outflow/{self._build_name}.route",
|
||||
"--sdc_file", f"{self._build_name}.sdc",
|
||||
"--sync_file", f"outflow/{self._build_name}.interface.csv",
|
||||
"--seed", "1",
|
||||
"--work_dir", "work_pnr",
|
||||
"--output_dir", "outflow",
|
||||
"--timing_analysis", "on",
|
||||
"--load_delay_matrix"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_pnr execution.")
|
||||
|
||||
# Bitstream.
|
||||
r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pgm",
|
||||
"--source", f"work_pnr/{self._build_name}.lbf",
|
||||
"--dest", f"{self._build_name}.hex",
|
||||
"--device", platform.device,
|
||||
"--family", platform.family,
|
||||
"--periph", f"outflow/{self._build_name}.lpf",
|
||||
"--oscillator_clock_divider", "DIV8",
|
||||
"--spi_low_power_mode", "off",
|
||||
"--io_weak_pullup", "on",
|
||||
"--enable_roms", "on",
|
||||
"--mode", "active",
|
||||
"--width", "1",
|
||||
"--enable_crc_check", "on"
|
||||
], common.colors)
|
||||
if r != 0:
|
||||
raise OSError("Error occurred during efx_pgm execution.")
|
||||
|
|
|
@ -45,42 +45,6 @@ class LatticeDiamondToolchain(GenericToolchain):
|
|||
|
||||
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 --------------------------------------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -12,129 +12,87 @@ from shutil import which, copyfile
|
|||
|
||||
from migen.fhdl.structure import _Fragment
|
||||
|
||||
from litex.build.generic_toolchain import GenericToolchain
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build import tools
|
||||
|
||||
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
||||
|
||||
def _build_sdc(clocks, vns, build_name):
|
||||
sdc = []
|
||||
for clk, period in sorted(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)}}}]")
|
||||
with open(f"{build_name}.sdc", "w") as f:
|
||||
f.write("\n".join(sdc))
|
||||
|
||||
# Script -------------------------------------------------------------------------------------------
|
||||
|
||||
def _build_tcl(name, device, files, build_name, include_paths):
|
||||
tcl = []
|
||||
|
||||
# Create Design.
|
||||
tcl.append(f"create_design {build_name}")
|
||||
|
||||
# Set Device.
|
||||
tcl.append(f"target_device {device.upper()}")
|
||||
|
||||
# Add Include Path.
|
||||
tcl.append("add_include_path ./")
|
||||
for include_path in include_paths:
|
||||
tcl.append(f"add_include_path {include_path}")
|
||||
|
||||
# Add Sources.
|
||||
for f, typ, lib in files:
|
||||
tcl.append(f"add_design_file {f}")
|
||||
|
||||
# Set Top Module.
|
||||
tcl.append(f"set_top_module {build_name}")
|
||||
|
||||
# Add Timings Constraints.
|
||||
tcl.append(f"add_constraint_file {build_name}.sdc")
|
||||
|
||||
# Run.
|
||||
tcl.append("synth")
|
||||
tcl.append("packing")
|
||||
tcl.append("place")
|
||||
tcl.append("route")
|
||||
tcl.append("sta")
|
||||
tcl.append("power")
|
||||
tcl.append("bitstream")
|
||||
|
||||
# Generate .tcl.
|
||||
with open("build.tcl", "w") as f:
|
||||
f.write("\n".join(tcl))
|
||||
|
||||
# OSFPGAToolchain -----------------------------------------------------------------------------------
|
||||
|
||||
class OSFPGAToolchain:
|
||||
class OSFPGAToolchain(GenericToolchain):
|
||||
attr_translate = {}
|
||||
|
||||
def __init__(self, toolchain):
|
||||
self.toolchain = toolchain
|
||||
self.clocks = dict()
|
||||
|
||||
def build(self, platform, fragment,
|
||||
build_dir = "build",
|
||||
build_name = "top",
|
||||
run = True,
|
||||
**kwargs):
|
||||
def build(self, platform, fragment, **kwargs):
|
||||
return self._build(platform, fragment, **kwargs)
|
||||
|
||||
# Create build directory.
|
||||
cwd = os.getcwd()
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
os.chdir(build_dir)
|
||||
# Constraints ----------------------------------------------------------------------------------
|
||||
|
||||
# Finalize design
|
||||
if not isinstance(fragment, _Fragment):
|
||||
fragment = fragment.get_fragment()
|
||||
platform.finalize(fragment)
|
||||
def build_io_constraints(self):
|
||||
return ("", "") # TODO
|
||||
|
||||
# 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)
|
||||
# Timing Constraints (.sdc) --------------------------------------------------------------------
|
||||
|
||||
# Generate constraints file.
|
||||
# IOs.
|
||||
# TODO.
|
||||
def build_timing_constraints(self, vns):
|
||||
sdc = []
|
||||
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)}}}]")
|
||||
with open(f"{build_name}.sdc", "w") as f:
|
||||
f.write("\n".join(sdc))
|
||||
return (self._build_name + ".sdc", "SDC")
|
||||
|
||||
# Timings (.sdc)
|
||||
_build_sdc(
|
||||
clocks = self.clocks,
|
||||
vns = v_output.ns,
|
||||
build_name = build_name,
|
||||
)
|
||||
# Project --------------------------------------------------------------------------------------
|
||||
|
||||
# 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,
|
||||
)
|
||||
def build_project(self):
|
||||
tcl = []
|
||||
|
||||
# Run
|
||||
if run:
|
||||
toolchain_sh = self.toolchain
|
||||
if which(toolchain_sh) is None:
|
||||
msg = f"Unable to find {toolchain_sh.upper()} toolchain, please:\n"
|
||||
msg += f"- Add {toolchain_sh.upper()} toolchain to your $PATH."
|
||||
raise OSError(msg)
|
||||
# Create Design.
|
||||
tcl.append(f"create_design {self._build_name}")
|
||||
|
||||
if subprocess.call([toolchain_sh, "--batch", "--script", "build.tcl"]) != 0:
|
||||
raise OSError(f"Error occured during {toolchain_sh.upper()}'s script execution.")
|
||||
# Set Device.
|
||||
tcl.append(f"target_device {self.platform.device.upper()}")
|
||||
|
||||
os.chdir(cwd)
|
||||
# Add Include Path.
|
||||
tcl.append("add_include_path ./")
|
||||
for include_path in platform.verilog_include_paths:
|
||||
tcl.append(f"add_include_path {include_path}")
|
||||
|
||||
return v_output.ns
|
||||
# Add Sources.
|
||||
for f, typ, lib in self.platform.sources:
|
||||
tcl.append(f"add_design_file {f}")
|
||||
|
||||
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
|
||||
# Set Top Module.
|
||||
tcl.append(f"set_top_module {self._build_name}")
|
||||
|
||||
# Add Timings Constraints.
|
||||
tcl.append(f"add_constraint_file {self._build_name}.sdc")
|
||||
|
||||
# Run.
|
||||
tcl.append("synth")
|
||||
tcl.append("packing")
|
||||
tcl.append("place")
|
||||
tcl.append("route")
|
||||
tcl.append("sta")
|
||||
tcl.append("power")
|
||||
tcl.append("bitstream")
|
||||
|
||||
# Generate .tcl.
|
||||
with open("build.tcl", "w") as f:
|
||||
f.write("\n".join(tcl))
|
||||
|
||||
# Script ---------------------------------------------------------------------------------------
|
||||
|
||||
def build_script(self):
|
||||
return "" # unused
|
||||
|
||||
def run_script(self, script):
|
||||
toolchain_sh = self.toolchain
|
||||
if which(toolchain_sh) is None:
|
||||
msg = f"Unable to find {toolchain_sh.upper()} toolchain, please:\n"
|
||||
msg += f"- Add {toolchain_sh.upper()} toolchain to your $PATH."
|
||||
raise OSError(msg)
|
||||
|
||||
if subprocess.call([toolchain_sh, "--batch", "--script", "build.tcl"]) != 0:
|
||||
raise OSError(f"Error occured during {toolchain_sh.upper()}'s script execution.")
|
||||
|
|
|
@ -10,6 +10,7 @@ import math
|
|||
|
||||
from migen.fhdl.structure import _Fragment
|
||||
|
||||
from litex.build.generic_toolchain import GenericToolchain
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build.xilinx.vivado import _xdc_separator, _build_xdc
|
||||
from litex.build import tools
|
||||
|
@ -28,7 +29,7 @@ F4CACHEPATH = '.f4cache'
|
|||
# F4PGAToolchain -------------------------------------------------------------------------------
|
||||
# Formerly SymbiflowToolchain, Symbiflow has been renamed to F4PGA -----------------------------
|
||||
|
||||
class F4PGAToolchain:
|
||||
class F4PGAToolchain(GenericToolchain):
|
||||
attr_translate = {
|
||||
"keep": ("dont_touch", "true"),
|
||||
"no_retiming": ("dont_touch", "true"),
|
||||
|
@ -40,59 +41,11 @@ class F4PGAToolchain:
|
|||
}
|
||||
|
||||
def __init__(self):
|
||||
self.clocks = dict()
|
||||
self.false_paths = set()
|
||||
super().__init__()
|
||||
self._partname = 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)
|
||||
self._flow = None
|
||||
|
||||
def build(self, platform, fragment,
|
||||
build_dir = "build",
|
||||
build_name = "top",
|
||||
run = True,
|
||||
enable_xpm = False,
|
||||
**kwargs):
|
||||
|
||||
|
@ -103,57 +56,67 @@ class F4PGAToolchain:
|
|||
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
||||
}.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)
|
||||
|
||||
# Generate design constraints
|
||||
tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc))
|
||||
return self._build(platform, fragment, **kwargs)
|
||||
|
||||
flow = self._generate_prj_flow(
|
||||
platform = platform,
|
||||
build_name = build_name
|
||||
def build_io_contraints(self):
|
||||
# Generate design constraints
|
||||
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:
|
||||
try:
|
||||
flow.execute()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("\nProject status:")
|
||||
self._flow.print_resolved_dependencies(0)
|
||||
print("")
|
||||
|
||||
def run_script(self, script):
|
||||
try:
|
||||
flow.execute()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
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):
|
||||
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
||||
return
|
||||
|
|
|
@ -17,163 +17,14 @@ from shutil import which
|
|||
|
||||
from migen.fhdl.structure import _Fragment
|
||||
|
||||
from litex.build.generic_toolchain import GenericToolchain
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build import tools
|
||||
from litex.build.xilinx import common
|
||||
|
||||
# Constraints (.ucf) -------------------------------------------------------------------------------
|
||||
|
||||
def _format_constraint(c):
|
||||
if isinstance(c, Pins):
|
||||
return "LOC=" + c.identifiers[0]
|
||||
elif isinstance(c, IOStandard):
|
||||
return "IOSTANDARD=" + c.name
|
||||
elif isinstance(c, Drive):
|
||||
return "DRIVE=" + str(c.strength)
|
||||
elif isinstance(c, Misc):
|
||||
return c.misc
|
||||
|
||||
def _format_ucf(signame, pin, others, resname):
|
||||
fmt_c = []
|
||||
for c in [Pins(pin)] + others:
|
||||
fc = _format_constraint(c)
|
||||
if fc is not None:
|
||||
fmt_c.append(fc)
|
||||
fmt_r = resname[0] + ":" + str(resname[1])
|
||||
if resname[2] is not None:
|
||||
fmt_r += "." + resname[2]
|
||||
return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
|
||||
|
||||
def _build_ucf(named_sc, named_pc):
|
||||
r = ""
|
||||
for sig, pins, others, resname in named_sc:
|
||||
if len(pins) > 1:
|
||||
for i, p in enumerate(pins):
|
||||
r += _format_ucf(sig + "(" + str(i) + ")", p, others, resname)
|
||||
else:
|
||||
r += _format_ucf(sig, pins[0], others, resname)
|
||||
if named_pc:
|
||||
r += "\n" + "\n\n".join(named_pc)
|
||||
return r
|
||||
|
||||
# Project (.xst) -----------------------------------------------------------------------------------
|
||||
|
||||
def _build_xst(device, sources, vincpaths, build_name, xst_opt):
|
||||
prj_contents = ""
|
||||
for filename, language, library, *copy in sources:
|
||||
prj_contents += language + " " + library + " " + tools.cygpath(filename) + "\n"
|
||||
tools.write_to_file(build_name + ".prj", prj_contents)
|
||||
|
||||
xst_contents = """run
|
||||
-ifn {build_name}.prj
|
||||
-top {build_name}
|
||||
{xst_opt}
|
||||
-ofn {build_name}.ngc
|
||||
-p {device}
|
||||
""".format(build_name=build_name, xst_opt=xst_opt, device=device)
|
||||
if vincpaths:
|
||||
xst_contents += "-vlgincdir {"
|
||||
for path in vincpaths:
|
||||
xst_contents += tools.cygpath(path) + " "
|
||||
xst_contents += "}"
|
||||
tools.write_to_file(build_name + ".xst", xst_contents)
|
||||
|
||||
# Yosys Run ----------------------------------------------------------------------------------------
|
||||
|
||||
def _run_yosys(device, sources, vincpaths, build_name):
|
||||
ys_contents = ""
|
||||
incflags = ""
|
||||
for path in vincpaths:
|
||||
incflags += " -I" + path
|
||||
for filename, language, library, *copy in sources:
|
||||
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
|
||||
|
||||
family = ""
|
||||
if (device.startswith("xc7") or device.startswith("xa7") or device.startswith("xq7")):
|
||||
family = "xc7"
|
||||
elif (device.startswith("xc6s") or device.startswith("xa6s") or device.startswith("xq6s")):
|
||||
family = "xc6s"
|
||||
else:
|
||||
raise OSError("Unsupported device")
|
||||
|
||||
ys_contents += """hierarchy -top top
|
||||
synth_xilinx -top top -family {family} -ise
|
||||
write_edif -pvector bra {build_name}.edif""".format(build_name=build_name, family=family)
|
||||
|
||||
ys_name = build_name + ".ys"
|
||||
tools.write_to_file(ys_name, ys_contents)
|
||||
r = subprocess.call(["yosys", ys_name])
|
||||
if r != 0:
|
||||
raise OSError("Subprocess failed")
|
||||
|
||||
# ISE Run ------------------------------------------------------------------------------------------
|
||||
|
||||
def _run_ise(build_name, mode, ngdbuild_opt, toolchain, platform):
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
script_ext = ".bat"
|
||||
shell = ["cmd", "/c"]
|
||||
build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n"
|
||||
fail_stmt = " || exit /b"
|
||||
else:
|
||||
script_ext = ".sh"
|
||||
shell = ["bash"]
|
||||
build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
|
||||
if os.getenv("LITEX_ENV_ISE", False):
|
||||
build_script_contents += "source " + os.path.join(os.getenv("LITEX_ENV_ISE"), "settings64.sh\n")
|
||||
fail_stmt = ""
|
||||
if mode == "edif":
|
||||
ext = "ngo"
|
||||
build_script_contents += """
|
||||
edif2ngd {build_name}.edif {build_name}.{ext}{fail_stmt}
|
||||
"""
|
||||
else:
|
||||
ext = "ngc"
|
||||
build_script_contents += """
|
||||
xst -ifn {build_name}.xst{fail_stmt}
|
||||
"""
|
||||
|
||||
# This generates a .v file for post synthesis simulation
|
||||
build_script_contents += """
|
||||
netgen -ofmt verilog -w -sim {build_name}.{ext} {build_name}_synth.v
|
||||
"""
|
||||
|
||||
build_script_contents += """
|
||||
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt}
|
||||
"""
|
||||
if mode == "cpld":
|
||||
build_script_contents += """
|
||||
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}
|
||||
hprep6 -s IEEE1532 -i {build_name}.vm6{fail_stmt}
|
||||
"""
|
||||
else:
|
||||
build_script_contents += """
|
||||
map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {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}
|
||||
"""
|
||||
build_script_contents = build_script_contents.format(build_name=build_name,
|
||||
ngdbuild_opt=ngdbuild_opt, bitgen_opt=toolchain.bitgen_opt, ext=ext,
|
||||
par_opt=toolchain.par_opt, map_opt=toolchain.map_opt,
|
||||
device=platform.device, fail_stmt=fail_stmt)
|
||||
build_script_contents += toolchain.ise_commands.format(build_name=build_name)
|
||||
build_script_file = "build_" + build_name + script_ext
|
||||
tools.write_to_file(build_script_file, build_script_contents, force_unix=False)
|
||||
command = shell + [build_script_file]
|
||||
|
||||
if which("ise") is None and os.getenv("LITEX_ENV_ISE", False) == False:
|
||||
msg = "Unable to find or source ISE toolchain, please either:\n"
|
||||
msg += "- Source ISE's settings manually.\n"
|
||||
msg += "- Or set LITEX_ENV_ISE environment variant to ISE's settings path.\n"
|
||||
msg += "- Or add ISE toolchain to your $PATH."
|
||||
raise OSError(msg)
|
||||
|
||||
if tools.subprocess_call_filtered(command, common.colors) != 0:
|
||||
raise OSError("Error occured during ISE's script execution.")
|
||||
|
||||
# XilinxISEToolchain --------------------------------------------------------------------------------
|
||||
|
||||
class XilinxISEToolchain:
|
||||
class XilinxISEToolchain(GenericToolchain):
|
||||
attr_translate = {
|
||||
"keep": ("keep", "true"),
|
||||
"no_retiming": ("register_balancing", "no"),
|
||||
|
@ -185,71 +36,197 @@ class XilinxISEToolchain:
|
|||
}
|
||||
|
||||
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,
|
||||
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)
|
||||
self._mode = mode
|
||||
self._isemode = mode if mode in ["xst", "cpld"] else "edif"
|
||||
|
||||
# Finalize design
|
||||
if not isinstance(fragment, _Fragment):
|
||||
fragment = fragment.get_fragment()
|
||||
platform.finalize(fragment)
|
||||
return self._build(platform, fragment, **kwargs)
|
||||
|
||||
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)
|
||||
# Constraints (.ucf) -------------------------------------------------------------------------------
|
||||
|
||||
# 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
|
||||
@classmethod
|
||||
def _format_constraint(cls, c):
|
||||
if isinstance(c, Pins):
|
||||
return "LOC=" + c.identifiers[0]
|
||||
elif isinstance(c, IOStandard):
|
||||
return "IOSTANDARD=" + c.name
|
||||
elif isinstance(c, Drive):
|
||||
return "DRIVE=" + str(c.strength)
|
||||
elif isinstance(c, Misc):
|
||||
return c.misc
|
||||
|
||||
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"
|
||||
def _format_ucf(self, signame, pin, others, resname):
|
||||
fmt_c = []
|
||||
for c in [Pins(pin)] + others:
|
||||
fc = self._format_constraint(c)
|
||||
if fc is not None:
|
||||
fmt_c.append(fc)
|
||||
fmt_r = resname[0] + ":" + str(resname[1])
|
||||
if resname[2] is not None:
|
||||
fmt_r += "." + resname[2]
|
||||
return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
|
||||
|
||||
# Generate design constraints (.ucf)
|
||||
tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))
|
||||
def build_io_constraints(self):
|
||||
r = ""
|
||||
for sig, pins, others, resname in self.named_sc:
|
||||
if len(pins) > 1:
|
||||
for i, p in enumerate(pins):
|
||||
r += self._format_ucf(sig + "(" + str(i) + ")", p, others, resname)
|
||||
else:
|
||||
r += self._format_ucf(sig, pins[0], others, resname)
|
||||
if self.named_pc:
|
||||
r += "\n" + "\n\n".join(self.named_pc)
|
||||
tools.write_to_file(self._build_name + ".ucf", r)
|
||||
return (self._build_name + ".ucf", "UCF")
|
||||
|
||||
# Run ISE
|
||||
if run:
|
||||
_run_ise(build_name, isemode, self.ngdbuild_opt, self, platform)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
# Project (.xst) -------------------------------------------------------------------------------
|
||||
|
||||
return vns
|
||||
def build_project(self):
|
||||
if self.mode not in ["xst", "cpld"]:
|
||||
return ("", "")
|
||||
prj_contents = ""
|
||||
for filename, language, library, *copy in self.platform.sources:
|
||||
prj_contents += language + " " + library + " " + tools.cygpath(filename) + "\n"
|
||||
tools.write_to_file(self._build_name + ".prj", prj_contents)
|
||||
|
||||
xst_contents = """run
|
||||
-ifn {build_name}.prj
|
||||
-top {build_name}
|
||||
{xst_opt}
|
||||
-ofn {build_name}.ngc
|
||||
-p {device}
|
||||
""".format(build_name=self._build_name, xst_opt=self.xst_opt, device=self.platform.device)
|
||||
if self.platform.verilog_include_paths:
|
||||
xst_contents += "-vlgincdir {"
|
||||
for path in self.platform.verilog_include_paths:
|
||||
xst_contents += tools.cygpath(path) + " "
|
||||
xst_contents += "}"
|
||||
tools.write_to_file(self._build_name + ".xst", xst_contents)
|
||||
|
||||
# Yosys Run ----------------------------------------------------------------------------------------
|
||||
|
||||
def _run_yosys(build_name):
|
||||
device = self.platform.device
|
||||
ys_contents = ""
|
||||
incflags = ""
|
||||
for path in platform.verilog_include_paths:
|
||||
incflags += " -I" + path
|
||||
for filename, language, library, *copy in self.platform.sources:
|
||||
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
|
||||
|
||||
family = ""
|
||||
if (device.startswith("xc7") or device.startswith("xa7") or device.startswith("xq7")):
|
||||
family = "xc7"
|
||||
elif (device.startswith("xc6s") or device.startswith("xa6s") or device.startswith("xq6s")):
|
||||
family = "xc6s"
|
||||
else:
|
||||
raise OSError("Unsupported device")
|
||||
|
||||
ys_contents += """hierarchy -top top
|
||||
synth_xilinx -top top -family {family} -ise
|
||||
write_edif -pvector bra {build_name}.edif""".format(build_name=self._build_name, family=family)
|
||||
|
||||
ys_name = self._build_name + ".ys"
|
||||
tools.write_to_file(ys_name, ys_contents)
|
||||
r = subprocess.call(["yosys", ys_name])
|
||||
if r != 0:
|
||||
raise OSError("Subprocess failed")
|
||||
|
||||
# ISE Run ------------------------------------------------------------------------------------------
|
||||
|
||||
def build_script(self):
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
script_ext = ".bat"
|
||||
shell = ["cmd", "/c"]
|
||||
build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n"
|
||||
fail_stmt = " || exit /b"
|
||||
else:
|
||||
script_ext = ".sh"
|
||||
shell = ["bash"]
|
||||
build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
|
||||
if os.getenv("LITEX_ENV_ISE", False):
|
||||
build_script_contents += "source " + os.path.join(os.getenv("LITEX_ENV_ISE"), "settings64.sh\n")
|
||||
fail_stmt = ""
|
||||
if self._isemode == "edif":
|
||||
ext = "ngo"
|
||||
build_script_contents += """
|
||||
edif2ngd {build_name}.edif {build_name}.{ext}{fail_stmt}
|
||||
"""
|
||||
else:
|
||||
ext = "ngc"
|
||||
build_script_contents += """
|
||||
xst -ifn {build_name}.xst{fail_stmt}
|
||||
"""
|
||||
|
||||
# This generates a .v file for post synthesis simulation
|
||||
build_script_contents += """
|
||||
netgen -ofmt verilog -w -sim {build_name}.{ext} {build_name}_synth.v
|
||||
"""
|
||||
|
||||
build_script_contents += """
|
||||
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt}
|
||||
"""
|
||||
if self._isemode == "cpld":
|
||||
build_script_contents += """
|
||||
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}
|
||||
hprep6 -s IEEE1532 -i {build_name}.vm6{fail_stmt}
|
||||
"""
|
||||
else:
|
||||
build_script_contents += """
|
||||
map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {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}
|
||||
"""
|
||||
build_script_contents = build_script_contents.format(build_name=self._build_name,
|
||||
ngdbuild_opt=self.ngdbuild_opt, bitgen_opt=self.bitgen_opt, ext=ext,
|
||||
par_opt=self.par_opt, map_opt=self.map_opt,
|
||||
device=self.platform.device, fail_stmt=fail_stmt)
|
||||
build_script_contents += self.ise_commands.format(build_name=self._build_name)
|
||||
build_script_file = "build_" + self._build_name + script_ext
|
||||
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]
|
||||
|
||||
if which("ise") is None and os.getenv("LITEX_ENV_ISE", False) == False:
|
||||
msg = "Unable to find or source ISE toolchain, please either:\n"
|
||||
msg += "- Source ISE's settings manually.\n"
|
||||
msg += "- Or set LITEX_ENV_ISE environment variant to ISE's settings path.\n"
|
||||
msg += "- Or add ISE toolchain to your $PATH."
|
||||
raise OSError(msg)
|
||||
|
||||
if tools.subprocess_call_filtered(command, common.colors) != 0:
|
||||
raise OSError("Error occured during ISE's script execution.")
|
||||
|
||||
# ISE is broken and you must use *separate* TNM_NET objects for period
|
||||
# constraints and other constraints otherwise it will be unable to trace
|
||||
|
|
|
@ -17,6 +17,7 @@ from shutil import which
|
|||
from migen.fhdl.structure import _Fragment, wrap, Constant
|
||||
from migen.fhdl.specials import Instance
|
||||
|
||||
from litex.build.generic_toolchain import GenericToolchain
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build.xilinx.vivado import _xdc_separator, _format_xdc, _build_xdc
|
||||
from litex.build import tools
|
||||
|
@ -86,7 +87,7 @@ def _run_make():
|
|||
|
||||
# YosysNextpnrToolchain -------------------------------------------------------------------------------
|
||||
|
||||
class YosysNextpnrToolchain:
|
||||
class YosysNextpnrToolchain(GenericToolchain):
|
||||
attr_translate = {
|
||||
#"keep": ("dont_touch", "true"),
|
||||
#"no_retiming": ("dont_touch", "true"),
|
||||
|
@ -98,13 +99,12 @@ class YosysNextpnrToolchain:
|
|||
}
|
||||
|
||||
def __init__(self):
|
||||
self.clocks = dict()
|
||||
self.false_paths = set()
|
||||
super().__init__()
|
||||
self.f4pga_device = None
|
||||
self.bitstream_device = None
|
||||
self._partname = None
|
||||
|
||||
def _check_properties(self, platform):
|
||||
def _check_properties(self):
|
||||
if not self.f4pga_device:
|
||||
try:
|
||||
self.f4pga_device = {
|
||||
|
@ -113,7 +113,7 @@ class YosysNextpnrToolchain:
|
|||
"xc7a100tcsg324-1" : "xc7a35t",
|
||||
"xc7z010clg400-1" : "xc7z010",
|
||||
"xc7z020clg400-1" : "xc7z020",
|
||||
}[platform.device]
|
||||
}[self.platform.device]
|
||||
except KeyError:
|
||||
raise ValueError(f"f4pga_device is not specified")
|
||||
if not self.bitstream_device:
|
||||
|
@ -123,9 +123,9 @@ class YosysNextpnrToolchain:
|
|||
self.bitstream_device = {
|
||||
"xc7a": "artix7", # xc7a35t, xc7a50t, xc7a100t, xc7a200t
|
||||
"xc7z": "zynq7", # xc7z010, xc7z020
|
||||
}[platform.device[:4]]
|
||||
}[self.platform.device[:4]]
|
||||
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
|
||||
self._partname = {
|
||||
"xc7a35ticsg324-1L" : "xc7a35tcsg324-1",
|
||||
|
@ -133,15 +133,15 @@ class YosysNextpnrToolchain:
|
|||
"xc7a200t-sbg484-1" : "xc7a200tsbg484-1",
|
||||
"xc7z010clg400-1" : "xc7z010clg400-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
|
||||
Rule = _MakefileGenerator.Rule
|
||||
|
||||
makefile = _MakefileGenerator([
|
||||
"# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n",
|
||||
Var("TOP", build_name),
|
||||
Var("TOP", self._build_name),
|
||||
Var("PARTNAME", self._partname),
|
||||
Var("DEVICE", self.f4pga_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("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("SDC", f"{build_name}.sdc"),
|
||||
Var("XDC", f"{build_name}.xdc"),
|
||||
Var("SDC", f"{self._build_name}.sdc"),
|
||||
Var("XDC", f"{self._build_name}.xdc"),
|
||||
Var("ARTIFACTS", [
|
||||
"$(TOP).fasm", "$(TOP).frames",
|
||||
"*.bit", "*.fasm", "*.json", "*.log", "*.rpt",
|
||||
|
@ -182,75 +182,38 @@ class YosysNextpnrToolchain:
|
|||
])
|
||||
|
||||
tools.write_to_file("Makefile", makefile.generate())
|
||||
return "Makefile"
|
||||
|
||||
def _build_clock_constraints(self, platform):
|
||||
platform.add_platform_command(_xdc_separator("Clock constraints"))
|
||||
def build_timing_constraints(self, vns):
|
||||
self.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)
|
||||
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):
|
||||
pass
|
||||
|
||||
def build(self, platform, fragment,
|
||||
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)
|
||||
|
||||
def finalize(self):
|
||||
# toolchain-specific fixes
|
||||
for instance in fragment.specials:
|
||||
for instance in self.fragment.specials:
|
||||
if isinstance(instance, Instance):
|
||||
self._fix_instance(instance)
|
||||
|
||||
# Generate timing constraints
|
||||
self._build_clock_constraints(platform)
|
||||
def build(self, platform, fragment,
|
||||
enable_xpm = False,
|
||||
**kwargs):
|
||||
|
||||
# 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)
|
||||
#FIXME
|
||||
self.platform = platform
|
||||
self._check_properties()
|
||||
|
||||
self._generate_makefile(
|
||||
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
|
||||
return self._build(platform, fragment, **kwargs)
|
||||
|
||||
def add_false_path_constraint(self, platform, from_, to):
|
||||
# FIXME: false path constraints are currently not supported by the F4PGA toolchain
|
||||
|
|
Loading…
Reference in New Issue