build/efinix/efinity: Simplify/Cleanup pass and only keep mandatory information in project's xml.
This commit is contained in:
parent
a5356f78c3
commit
1932506373
|
@ -2,7 +2,7 @@
|
||||||
# This file is part of LiteX.
|
# This file is part of LiteX.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
|
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
# Copyright (c) 2015-2021 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -33,7 +33,7 @@ def get_pin_direction(fragment, platform, pinname):
|
||||||
pins = platform.constraint_manager.get_io_signals()
|
pins = 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"
|
||||||
|
|
||||||
|
@ -66,12 +66,12 @@ def _build_sdc(clocks, false_paths, vns, named_sc, build_name, additional_sdc_co
|
||||||
# Generate .sdc
|
# Generate .sdc
|
||||||
tools.write_to_file("{}.sdc".format(build_name), "\n".join(sdc))
|
tools.write_to_file("{}.sdc".format(build_name), "\n".join(sdc))
|
||||||
|
|
||||||
# Peripheral configuration ------------------------------------------------------------------------
|
# Peripheral configuration (.xml) ------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_gpio_instance(fragment, platform, sig, pins):
|
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 = get_pin_direction(fragment, platform, 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)
|
||||||
|
|
||||||
|
@ -84,9 +84,9 @@ def _format_constraint(c, signame, fmt_r, fragment, platform):
|
||||||
# IO standard property
|
# IO standard property
|
||||||
elif isinstance(c, IOStandard):
|
elif isinstance(c, IOStandard):
|
||||||
prop = ""
|
prop = ""
|
||||||
valid = ['3.3_V_LVTTL_/_LVCMOS', '2.5_V_LVCMOS', '1.8_V_LVCMOS']
|
valid = ["3.3_V_LVTTL_/_LVCMOS", "2.5_V_LVCMOS", "1.8_V_LVCMOS"]
|
||||||
if c.name in valid:
|
if c.name in valid:
|
||||||
prop = 'IO_STANDARD'
|
prop = "IO_STANDARD"
|
||||||
|
|
||||||
if prop == "":
|
if prop == "":
|
||||||
print("{} has a wrong IOStandard format [{}]".format(signame, c.name))
|
print("{} has a wrong IOStandard format [{}]".format(signame, c.name))
|
||||||
|
@ -100,13 +100,13 @@ def _format_constraint(c, signame, fmt_r, fragment, platform):
|
||||||
# Others constraints
|
# Others constraints
|
||||||
elif isinstance(c, Misc):
|
elif isinstance(c, Misc):
|
||||||
prop = ""
|
prop = ""
|
||||||
if c.misc in ['WEAK_PULLUP', 'WEAK_PULLDOWN']:
|
if c.misc in ["WEAK_PULLUP", "WEAK_PULLDOWN"]:
|
||||||
prop = 'PULL_OPTION'
|
prop = "PULL_OPTION"
|
||||||
val = c.misc
|
val = c.misc
|
||||||
|
|
||||||
if 'DRIVE_STRENGTH' in c.misc:
|
if "DRIVE_STRENGTH" in c.misc:
|
||||||
prop = 'DRIVE_STRENGTH'
|
prop = "DRIVE_STRENGTH"
|
||||||
val = c.misc.split('=')[1]
|
val = c.misc.split("=")[1]
|
||||||
|
|
||||||
if prop == "":
|
if prop == "":
|
||||||
# Print error, warning ??
|
# Print error, warning ??
|
||||||
|
@ -120,7 +120,7 @@ def _format_conf_constraint(signame, pin, others, resname, fragment, platform):
|
||||||
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 = [_format_constraint(c, signame, fmt_r, fragment, platform) 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(named_sc, named_pc, fragment, platform, excluded_ios):
|
||||||
conf = []
|
conf = []
|
||||||
|
@ -151,146 +151,121 @@ def _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios):
|
||||||
|
|
||||||
return "\n".join(conf)
|
return "\n".join(conf)
|
||||||
|
|
||||||
def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragment, platform, additional_iface_commands, excluded_ios):
|
def _build_peri(efinity_path, build_name, device, named_sc, named_pc, fragment, platform, additional_iface_commands, excluded_ios):
|
||||||
pythonpath = ""
|
pythonpath = ""
|
||||||
|
|
||||||
header = platform.toolchain.ifacewriter.header(build_name, partnumber)
|
header = platform.toolchain.ifacewriter.header(build_name, device)
|
||||||
gen = platform.toolchain.ifacewriter.generate(partnumber)
|
gen = platform.toolchain.ifacewriter.generate(device)
|
||||||
#TODO: move this to ifacewriter
|
#TODO : move this to ifacewriter
|
||||||
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios)
|
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, excluded_ios)
|
||||||
add = '\n'.join(additional_iface_commands)
|
add = "\n".join(additional_iface_commands)
|
||||||
footer = platform.toolchain.ifacewriter.footer()
|
footer = platform.toolchain.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 subprocess.call([efinity_path + '/bin/python3', 'iface.py']) != 0:
|
if subprocess.call([efinity_path + "/bin/python3", "iface.py"]) != 0:
|
||||||
raise OSError("Error occurred during Efinity peri script execution.")
|
raise OSError("Error occurred during Efinity peri script execution.")
|
||||||
|
|
||||||
|
|
||||||
# Project configuration ------------------------------------------------------------------------
|
# Project configuration (.xml) ---------------------------------------------------------------------
|
||||||
|
|
||||||
def _build_xml(family, device, timing_model, build_name, sources, additional_xml_commands):
|
def _build_xml(family, device, timing_model, build_name, sources):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
root = et.Element('efx:project')
|
# 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')}"
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
# Add Device.
|
||||||
date_str = " Date: " + now.strftime("%Y-%m-%d %H:%M") + " "
|
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 the required attributes
|
# Add Design Info.
|
||||||
root.attrib['xmlns:efx'] = 'http://www.efinixinc.com/enf_proj'
|
design_info = et.SubElement(root, "efx:design_info")
|
||||||
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
|
et.SubElement(design_info, "efx:top_module", name=build_name)
|
||||||
root.attrib['name'] = build_name
|
|
||||||
root.attrib['description'] = ''
|
|
||||||
root.attrib['last_change_date'] = date_str
|
|
||||||
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_run_state'] = ''
|
|
||||||
root.attrib['last_run_tool'] = ''
|
|
||||||
root.attrib['last_run_flow'] = ''
|
|
||||||
root.attrib['config_result_in_sync'] = 'sync'
|
|
||||||
root.attrib['design_ood'] = 'sync'
|
|
||||||
root.attrib['place_ood'] = 'sync'
|
|
||||||
root.attrib['route_ood'] = 'sync'
|
|
||||||
root.attrib['xsi:schemaLocation'] = 'http://www.efinixinc.com/enf_proj enf_proj.xsd'
|
|
||||||
|
|
||||||
device_info = et.SubElement(root, 'efx:device_info')
|
# Add Design Sources.
|
||||||
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)
|
|
||||||
|
|
||||||
design_info = et.SubElement(root, 'efx:design_info')
|
|
||||||
et.SubElement(design_info, "efx:top_module", name = build_name)
|
|
||||||
for filename, language, library in sources:
|
for filename, language, library in sources:
|
||||||
if '.vh' not in filename:
|
if ".vh" not in filename:
|
||||||
val = {'name':filename, 'version':'default', 'library':'default'}
|
et.SubElement(design_info, "efx:design_file", {
|
||||||
et.SubElement(design_info, "efx:design_file", val)
|
"name" : filename,
|
||||||
et.SubElement(design_info, "efx:top_vhdl_arch", name = "")
|
"version" : "default",
|
||||||
|
"library" : "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
# 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 = "{}.sdc".format(build_name))
|
et.SubElement(constraint_info, "efx:sdc_file", name=f"{build_name}.sdc")
|
||||||
|
|
||||||
|
# Add Misc Info.
|
||||||
misc_info = et.SubElement(root, "efx:misc_info")
|
misc_info = et.SubElement(root, "efx:misc_info")
|
||||||
|
|
||||||
|
# Add IP Info.
|
||||||
ip_info = et.SubElement(root, "efx:ip_info")
|
ip_info = et.SubElement(root, "efx:ip_info")
|
||||||
|
|
||||||
synthesis = et.SubElement(root, "efx:synthesis", tool_name="efx_map")
|
|
||||||
for l in additional_xml_commands:
|
|
||||||
if l[0] == 'efx_map':
|
|
||||||
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
|
|
||||||
et.SubElement(synthesis, "efx:param", val)
|
|
||||||
|
|
||||||
place_and_route = et.SubElement(root, "efx:place_and_route", tool_name="efx_pnr")
|
|
||||||
for l in additional_xml_commands:
|
|
||||||
if l[0] == 'efx_pnr':
|
|
||||||
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
|
|
||||||
et.SubElement(place_and_route, "efx:param", val)
|
|
||||||
|
|
||||||
bitstream_generation = et.SubElement(root, "efx:bitstream_generation", tool_name="efx_pgm")
|
|
||||||
for l in additional_xml_commands:
|
|
||||||
if l[0] == 'efx_pgm':
|
|
||||||
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
|
|
||||||
et.SubElement(bitstream_generation, "efx:param", val)
|
|
||||||
|
|
||||||
xml_string = et.tostring(root, 'utf-8')
|
|
||||||
reparsed = expatbuilder.parseString(xml_string, False)
|
|
||||||
print_string = reparsed.toprettyxml(indent=" ")
|
|
||||||
|
|
||||||
# Generate .xml
|
# Generate .xml
|
||||||
tools.write_to_file("{}.xml".format(build_name), print_string)
|
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:
|
||||||
attr_translate = {}
|
attr_translate = {}
|
||||||
|
|
||||||
def __init__(self, efinity_path):
|
def __init__(self, efinity_path):
|
||||||
self.options = {}
|
self.options = {}
|
||||||
self.clocks = dict()
|
self.clocks = dict()
|
||||||
self.false_paths = set()
|
self.false_paths = set()
|
||||||
self.efinity_path = efinity_path
|
self.efinity_path = efinity_path
|
||||||
self.additional_sdc_commands = []
|
self.ifacewriter = InterfaceWriter(efinity_path)
|
||||||
self.additional_xml_commands = [
|
self.excluded_ios = []
|
||||||
[ 'efx_pgm', 'io_weak_pullup', 'on', 'e_bool'],
|
self.additional_sdc_commands = []
|
||||||
[ 'efx_pgm', 'oscillator_clock_divider', 'DIV8', 'e_option'],
|
|
||||||
[ 'efx_pgm', 'enable_crc_check', 'on', 'e_bool'],
|
|
||||||
]
|
|
||||||
self.ifacewriter = InterfaceWriter(efinity_path)
|
|
||||||
self.excluded_ios = []
|
|
||||||
self.additional_iface_commands = []
|
self.additional_iface_commands = []
|
||||||
|
|
||||||
def build(self, platform, fragment,
|
def build(self, platform, fragment,
|
||||||
build_dir = "build",
|
build_dir = "build",
|
||||||
build_name = "top",
|
build_name = "top",
|
||||||
run = True,
|
run = True,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
family = "Trion" # FIXME: Add Titanium support.
|
family = "Trion" # FIXME: Add Titanium support.
|
||||||
|
|
||||||
self.ifacewriter.set_build_params(platform, build_name)
|
self.ifacewriter.set_build_params(platform, build_name)
|
||||||
|
|
||||||
# Create build directory
|
# Create Build Directory.
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
os.makedirs(build_dir, exist_ok=True)
|
os.makedirs(build_dir, exist_ok=True)
|
||||||
os.chdir(build_dir)
|
os.chdir(build_dir)
|
||||||
|
|
||||||
# Apply FullMemoryWE on design (Efiniy does not infer memories correctly otherwise).
|
# Apply FullMemoryWE on Design (Efiniy does not infer memories correctly otherwise).
|
||||||
FullMemoryWE()(fragment)
|
FullMemoryWE()(fragment)
|
||||||
|
|
||||||
# Finalize design
|
# Finalize Design.
|
||||||
if not isinstance(fragment, _Fragment):
|
if not isinstance(fragment, _Fragment):
|
||||||
fragment = fragment.get_fragment()
|
fragment = fragment.get_fragment()
|
||||||
platform.finalize(fragment)
|
platform.finalize(fragment)
|
||||||
|
|
||||||
# Generate verilog
|
# Generate Design.
|
||||||
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
||||||
named_sc, named_pc = platform.resolve_signals(v_output.ns)
|
v_output.write(f"{build_name}.v")
|
||||||
v_file = build_name + ".v"
|
platform.add_source(f"{build_name}.v")
|
||||||
v_output.write(v_file)
|
|
||||||
platform.add_source(v_file)
|
|
||||||
|
|
||||||
|
# Add Include Paths.
|
||||||
if platform.verilog_include_paths:
|
if platform.verilog_include_paths:
|
||||||
self.options['includ_path'] = '{' + ';'.join(platform.verilog_include_paths) + '}'
|
self.options["includ_path"] = "{" + ";".join(platform.verilog_include_paths) + "}"
|
||||||
|
|
||||||
os.environ['EFXPT_HOME'] = self.efinity_path + '/pt'
|
os.environ["EFXPT_HOME"] = self.efinity_path + "/pt"
|
||||||
|
|
||||||
# Generate design timing constraints file (.sdc)
|
# Generate Design Timing Constraints file (.sdc)
|
||||||
|
named_sc, named_pc = platform.resolve_signals(v_output.ns)
|
||||||
_build_sdc(
|
_build_sdc(
|
||||||
clocks = self.clocks,
|
clocks = self.clocks,
|
||||||
false_paths = self.false_paths,
|
false_paths = self.false_paths,
|
||||||
|
@ -302,25 +277,24 @@ class EfinityToolchain:
|
||||||
|
|
||||||
# Generate project file (.xml)
|
# Generate project file (.xml)
|
||||||
_build_xml(
|
_build_xml(
|
||||||
family = family,
|
family = family,
|
||||||
device = platform.device,
|
device = platform.device,
|
||||||
timing_model = platform.timing_model,
|
timing_model = platform.timing_model,
|
||||||
build_name = build_name,
|
build_name = build_name,
|
||||||
sources = platform.sources,
|
sources = platform.sources
|
||||||
additional_xml_commands = self.additional_xml_commands,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate constraints file (.peri.xml)
|
# Generate peripheral file (.peri.xml)
|
||||||
_build_peri(
|
_build_peri(
|
||||||
efinity_path = self.efinity_path,
|
efinity_path = self.efinity_path,
|
||||||
build_name = build_name,
|
build_name = build_name,
|
||||||
partnumber = platform.device,
|
device = platform.device,
|
||||||
named_sc = named_sc,
|
named_sc = named_sc,
|
||||||
named_pc = named_pc,
|
named_pc = named_pc,
|
||||||
fragment = fragment,
|
fragment = fragment,
|
||||||
platform = platform,
|
platform = platform,
|
||||||
additional_iface_commands = self.additional_iface_commands,
|
additional_iface_commands = self.additional_iface_commands,
|
||||||
excluded_ios = self.excluded_ios,
|
excluded_ios = self.excluded_ios
|
||||||
)
|
)
|
||||||
|
|
||||||
# DDR doesn't have Python API so we need to configure it
|
# DDR doesn't have Python API so we need to configure it
|
||||||
|
@ -395,18 +369,18 @@ class EfinityToolchain:
|
||||||
|
|
||||||
# Bitstream.
|
# Bitstream.
|
||||||
r = subprocess.call([self.efinity_path + "/bin/efx_pgm",
|
r = subprocess.call([self.efinity_path + "/bin/efx_pgm",
|
||||||
"--source", f"work_pnr/{build_name}.lbf",
|
"--source", f"work_pnr/{build_name}.lbf",
|
||||||
"--dest", f"outflow/{build_name}.hex",
|
"--dest", f"outflow/{build_name}.hex",
|
||||||
"--device", platform.device,
|
"--device", platform.device,
|
||||||
"--family", family,
|
"--family", family,
|
||||||
"--periph", f"outflow/{build_name}.lpf",
|
"--periph", f"outflow/{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",
|
||||||
"--enable_roms", "on",
|
"--enable_roms", "on",
|
||||||
"--mode", "active",
|
"--mode", "active",
|
||||||
"--width", "1",
|
"--width", "1",
|
||||||
"--enable_crc_check", "on"
|
"--enable_crc_check", "on"
|
||||||
])
|
])
|
||||||
if r != 0:
|
if r != 0:
|
||||||
raise OSError("Error occurred during efx_pgm execution.")
|
raise OSError("Error occurred during efx_pgm execution.")
|
||||||
|
|
Loading…
Reference in New Issue