From 000aabf85b5319680c74d760155cda6bfceb7f27 Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Fri, 17 Sep 2021 09:29:53 +0200 Subject: [PATCH] Initial Efinix Trion support --- litex/build/efinix/__init__.py | 1 + litex/build/efinix/common.py | 8 + litex/build/efinix/efinity.py | 375 +++++++++++++++++++++++++++++++ litex/build/efinix/platform.py | 52 +++++ litex/build/efinix/programmer.py | 27 +++ 5 files changed, 463 insertions(+) create mode 100644 litex/build/efinix/__init__.py create mode 100644 litex/build/efinix/common.py create mode 100644 litex/build/efinix/efinity.py create mode 100644 litex/build/efinix/platform.py create mode 100644 litex/build/efinix/programmer.py diff --git a/litex/build/efinix/__init__.py b/litex/build/efinix/__init__.py new file mode 100644 index 000000000..3d6f985ec --- /dev/null +++ b/litex/build/efinix/__init__.py @@ -0,0 +1 @@ +from litex.build.efinix.programmer import EfinixProgrammer \ No newline at end of file diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py new file mode 100644 index 000000000..9c104ab69 --- /dev/null +++ b/litex/build/efinix/common.py @@ -0,0 +1,8 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Franck Jullien +# Copyright (c) 2015-2018 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +efinix_special_overrides = {} \ No newline at end of file diff --git a/litex/build/efinix/efinity.py b/litex/build/efinix/efinity.py new file mode 100644 index 000000000..74ecae303 --- /dev/null +++ b/litex/build/efinix/efinity.py @@ -0,0 +1,375 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Franck Jullien +# Copyright (c) 2015-2018 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import os +import subprocess +import pathlib +import math +import sys +import site +import subprocess +import inspect + +from lxml import etree + +from litex.build.generic_platform import * + +from migen.fhdl.structure import _Fragment +from migen.fhdl.tools import * +from migen.fhdl.namer import build_namespace + +from litex.build.generic_platform import Pins, IOStandard, Misc +from litex.build import tools + +_reserved_keywords = { + "always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1", + "case", "casex", "casez", "cell", "cmos", "config", "deassign", "default", + "defparam", "design", "disable", "edge", "else", "end", "endcase", + "endconfig", "endfunction", "endgenerate", "endmodule", "endprimitive", + "endspecify", "endtable", "endtask", "event", "for", "force", "forever", + "fork", "function", "generate", "genvar", "highz0", "highz1", "if", + "ifnone", "incdir", "include", "initial", "inout", "input", + "instance", "integer", "join", "large", "liblist", "library", "localparam", + "macromodule", "medium", "module", "nand", "negedge", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "or", "output", "parameter", + "pmos", "posedge", "primitive", "pull0", "pull1" "pulldown", + "pullup", "pulsestyle_onevent", "pulsestyle_ondetect", "remos", "real", + "realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran", + "rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small", + "specify", "specparam", "strong0", "strong1", "supply0", "supply1", + "table", "task", "time", "tran", "tranif0", "tranif1", "tri", "tri0", + "tri1", "triand", "trior", "trireg", "unsigned", "use", "vectored", "wait", + "wand", "weak0", "weak1", "while", "wire", "wor","xnor", "xor", "do" +} + +def get_pin_direction(fragment, platform, pinname): + ios = platform.constraint_manager.get_io_signals() + sigs = list_signals(fragment) | list_special_ios(fragment, True, True, True) + special_outs = list_special_ios(fragment, False, True, True) + inouts = list_special_ios(fragment, False, False, True) + targets = list_targets(fragment) | special_outs + + ns = build_namespace(list_signals(fragment) \ + | list_special_ios(fragment, True, True, True) \ + | ios, _reserved_keywords) + ns.clock_domains = fragment.clock_domains + + dir = "Unknown" + + for sig in sorted(ios, key=lambda x: x.duid): + # Better idea ??? + if (pinname.split('[')[0] == ns.get_name(sig)): + if sig in inouts: + dir = "inout" + elif sig in targets: + dir = "output" + else: + dir = "input" + + return dir + +# 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 ------------------------------------------------------------------------ + +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'] + 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 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_conf(named_sc, named_pc, fragment, platform): + conf = [] + inst = [] + + # GPIO + for sig, pins, others, resname in named_sc: + 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)) + + # PLL + #inst.append() + + conf = inst + conf + + return "\n".join(conf) + +def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragment, platform): + pythonpath = "" + + header = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + + header += """ +import os +import sys + +home = '{0}' + +os.environ['EFXPT_HOME'] = home + '/pt' +os.environ['EFXPGM_HOME'] = home + '/pgm' +os.environ['EFXDBG_HOME'] = home + '/debugger' +os.environ['EFXIPM_HOME'] = home + '/ipm' + +sys.path.append(home + '/pt/bin') +sys.path.append(home + '/lib/python3.8/site-packages') + +from api_service.design import DesignAPI +from api_service.device import DeviceAPI + +is_verbose = {1} + +design = DesignAPI(is_verbose) +device = DeviceAPI(is_verbose) + +design.create('{2}', '{3}', './../build', overwrite=True) + +""" + + header = header.format(efinity_path, 'True', build_name, partnumber) + + conf = _build_iface_conf(named_sc, named_pc, fragment, platform) + + footer = """ +# Check design, generate constraints and reports + #design.generate(enable_bitstream=True) +# Save the configured periphery design +design.save() + """ + + tools.write_to_file("iface.py", header + conf + footer) + + subprocess.call([efinity_path + '/bin/python3', 'iface.py']) + +# Project configuration ------------------------------------------------------------------------ + +def _build_xml(name, partnumber, build_name, sources, additional_xml_commands): + + test = ' +# Copyright (c) 2015-2018 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import os + +from litex.build.generic_platform import GenericPlatform +from litex.build.efinix import common, efinity + +# EfinixPlatform ----------------------------------------------------------------------------------- + +class EfinixPlatform(GenericPlatform): + bitstream_ext = ".bit" + + def __init__(self, *args, toolchain="efinity", **kwargs): + GenericPlatform.__init__(self, *args, **kwargs) + + if 'LITEX_ENV_EFINITY' in os.environ: + self.efinity_path = os.environ['LITEX_ENV_EFINITY'].rstrip('/') + os.environ['EFINITY_HOME'] = self.efinity_path + else: + raise OSError('Unable to find Efinity toolchain, please set LITEX_ENV_EFINITY to ${install_dir}') + + if toolchain == "efinity": + self.toolchain = efinity.EfinityToolchain(self.efinity_path) + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.efinix_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, special_overrides=so, + attr_translate=self.toolchain.attr_translate, **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) + + def add_period_constraint(self, clk, period): + if clk is None: return + if hasattr(clk, "p"): + clk = clk.p + self.toolchain.add_period_constraint(self, clk, period) + + def add_false_path_constraint(self, from_, to): + if hasattr(from_, "p"): + from_ = from_.p + if hasattr(to, "p"): + to = to.p + self.toolchain.add_false_path_constraint(self, from_, to) \ No newline at end of file diff --git a/litex/build/efinix/programmer.py b/litex/build/efinix/programmer.py new file mode 100644 index 000000000..1981b5fa3 --- /dev/null +++ b/litex/build/efinix/programmer.py @@ -0,0 +1,27 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Franck Jullien +# SPDX-License-Identifier: BSD-2-Clause + +import os +import sys + +from litex.build.generic_programmer import GenericProgrammer + +class EfinixProgrammer(GenericProgrammer): + + def __init__(self, cable_name=""): + self.cable_name = cable_name + if 'LITEX_ENV_EFINITY' in os.environ: + self.efinity_path = os.environ['LITEX_ENV_EFINITY'].rstrip('/') + os.environ['EFINITY_HOME'] = self.efinity_path + else: + raise OSError('Unable to find Efinity toolchain, please set LITEX_ENV_EFINITY to ${install_dir}') + + def load_bitstream(self, bitstream_file, cable_suffix=""): + os.environ['EFXPGM_HOME'] = self.efinity_path + '/pgm' + self.call([self.efinity_path + '/bin/python3', self.efinity_path + + 'pgm/bin/efx_pgm/ftdi_program.py', bitstream_file, + "-m", "jtag" + ])