build/efinix/efinity: Simplify/Cleanup pass and only keep mandatory information in project's xml.

This commit is contained in:
Florent Kermarrec 2021-11-11 10:08:17 +01:00
parent a5356f78c3
commit 1932506373
1 changed files with 97 additions and 123 deletions

View File

@ -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.")