Merge pull request #1418 from trabucayre/rework_toolchain_args

build/lattice/platform: lattice_args, lattice_argdict: refactorize toolchains args
This commit is contained in:
enjoy-digital 2022-11-06 21:15:36 +01:00 committed by GitHub
commit b9b165d25d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 392 additions and 8 deletions

View File

@ -16,6 +16,8 @@ class AlteraPlatform(GenericPlatform):
bitstream_ext = ".sof" bitstream_ext = ".sof"
create_rbf = True create_rbf = True
_supported_toolchains = ["quartus"]
def __init__(self, *args, toolchain="quartus", **kwargs): def __init__(self, *args, toolchain="quartus", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)
self.ips = set() self.ips = set()

View File

@ -15,6 +15,8 @@ from litex.build.anlogic import common, anlogic
class AnlogicPlatform(GenericPlatform): class AnlogicPlatform(GenericPlatform):
bitstream_ext = ".fs" bitstream_ext = ".fs"
_supported_toolchains = ["td"]
def __init__(self, device, *args, toolchain="td", **kwargs): def __init__(self, device, *args, toolchain="td", **kwargs):
GenericPlatform.__init__(self, device, *args, **kwargs) GenericPlatform.__init__(self, device, *args, **kwargs)
if toolchain == "td": if toolchain == "td":

View File

@ -0,0 +1,194 @@
#
# This file is part of LiteX.
#
# This file is Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
# SPDX-License-Identifier: BSD-2-Clause
import argparse
import importlib
import sys
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
# LitexArgumentParser ------------------------------------------------------------------------------
class LiteXArgumentParser(argparse.ArgumentParser):
"""
ArgumentParser subclass used to intercept parse_args call and to simplify
common arguments addition
Attributes
==========
_platform: GenericPlatform subclass
target platform
_device: str
target device family
_args: argparse.Namespace
list of args after parse_args call
_toolchain: str
toolchain used at build time
_default_toolchain: str
toolchain to use by default or when no selection is done by the user
"""
def __init__(self, platform=None, **kwargs):
"""
CTOR: create a Target options group, adds toolchain, build and load
arguments. Call builder_args and soc_core_args for fill parser with
corresponding options
Parameters
==========
platform: GenericPlatform subclass
targeted platform
kwargs: dict
all arguments passed to argparse.ArgumentParser CTOR
"""
argparse.ArgumentParser.__init__(self, kwargs)
self._platform = platform
if platform is not None:
self._device = platform.device_family
toolchains = platform.toolchains(self._device)
self._default_toolchain = toolchains[0]
else:
self._device = None
toolchains = None
self._default_toolchain = None
self._args = None
self._toolchain = None
self._target_group = self.add_argument_group(title="Target options")
if toolchains is not None:
self.add_target_argument("--toolchain",
default=self._default_toolchain,
choices=toolchains,
help="FPGA toolchain ({}).".format(" or ".join(toolchains)))
else:
self.add_target_argument("-toolchain", help="FPGA toolchain")
self.add_target_argument("--build", action="store_true", help="Build design.")
self.add_target_argument("--load", action="store_true", help="Load bitstream.")
builder_args(self)
soc_core_args(self)
def set_platform(self, platform):
""" set platform. Check first if not already set
Parameter
=========
platform: GenericPlatform subclass
the platform
"""
assert self._platform is None
self._platform = platform
self._device = platform.device_family
toolchains = platform.toolchains(self._device)
self._default_toolchain = toolchains[0]
# add a setter (LitexArgumentParserInstance.platform = myPlatform)
platform = property(None, set_platform)
@property
def target_group(self):
""" return target_group
"""
return self._target_group
def add_target_argument(self, *args, **kwargs):
""" wrapper to add argument to "Target options group" from outer of this
class
"""
self._target_group.add_argument(*args, **kwargs)
@property
def builder_argdict(self):
"""
access to builder_argdict
Return
======
builder arguments dict
"""
return builder_argdict(self._args)
@property
def soc_core_argdict(self):
"""
access to soc_core_argdict
Return
======
soc_core arguments dict
"""
return soc_core_argdict(self._args)
@property
def toolchain_argdict(self):
"""
access to target toolchain argdict
Return
======
toolchain arguments dict
"""
if self._platform is None:
return dict()
else:
return self._platform.get_argdict(self._toolchain, self._args)
def parse_args(self, args=None, namespace=None):
"""
override argparse.ArgumentParser.parse_args to inject toolchain
and soc_core args.
Checks first is platform is set: when platform is none: try to
search for a platform argument
"""
# When platform is None try to search for a user input
if self._platform is None:
platform = self.get_value_from_key("--platform", None)
if platform is not None:
self.set_platform(importlib.import_module(platform).Platform)
# Intercept selected toolchain to fill arguments.
if self._platform is not None:
self._toolchain = self.get_value_from_key("--toolchain",
self._default_toolchain)
if self._toolchain is not None:
self._platform.fill_args(self._toolchain, self)
# Intercept selected CPU to fill arguments.
cpu_cls = None
cpu_name = self.get_value_from_key("--cpu-type")
if cpu_name is not None:
cpu_cls = cpu.CPUS[cpu_name]
if cpu_cls is not None and hasattr(cpu_cls, "args_fill"):
cpu_cls.args_fill(self)
self._args = argparse.ArgumentParser.parse_args(self, args, namespace)
# Re-inject CPU read arguments.
if cpu_cls is not None and hasattr(cpu_cls, "args_read"):
cpu_cls.args_read(args)
return self._args
def get_value_from_key(self, key, default=None):
"""
search key into sys.argv
Parameters
==========
key: str
key to search
default: str
default value when key is not in sys.argv
Return
======
sys.argv corresponding value or default
"""
value = None
try:
index = [i for i, item in enumerate(sys.argv) if key in item][0]
if '=' in sys.argv[index]:
value = sys.argv[index].split('=')[1]
else:
value = sys.argv[index+1]
except IndexError:
value = default
return value

View File

@ -18,6 +18,8 @@ from litex.build.efinix import EfinixDbParser
class EfinixPlatform(GenericPlatform): class EfinixPlatform(GenericPlatform):
bitstream_ext = ".bit" bitstream_ext = ".bit"
_supported_toolchains = ["efinity"]
def __init__(self, *args, iobank_info=None, toolchain="efinity", **kwargs): def __init__(self, *args, iobank_info=None, toolchain="efinity", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)

View File

@ -322,6 +322,8 @@ class ConstraintManager:
# Generic Platform --------------------------------------------------------------------------------- # Generic Platform ---------------------------------------------------------------------------------
class GenericPlatform: class GenericPlatform:
device_family = None
def __init__(self, device, io, connectors=[], name=None): def __init__(self, device, io, connectors=[], name=None):
self.toolchain = None self.toolchain = None
self.device = device self.device = device
@ -467,3 +469,54 @@ class GenericPlatform:
@property @property
def support_mixed_language(self): def support_mixed_language(self):
return self.toolchain.support_mixed_language return self.toolchain.support_mixed_language
@classmethod
def fill_args(cls, toolchain, parser):
"""
pass parser to the specific toolchain to
fill this with toolchain args
Parameters
==========
toolchain: str
toolchain name
parser: argparse.ArgumentParser
parser to be filled
"""
pass # pass must be overloaded (if required)
@classmethod
def get_argdict(cls, toolchain, args):
"""
return a dict of args
Parameters
==========
toolchain: str
toolchain name
Return
======
a dict of key/value for each args or an empty dict
"""
return {} # Empty must be overloaded (if required)
@classmethod
def toolchains(cls, device):
"""
Returns list of toolchains compatible with device
Parameters
==========
device: str
device name (ice40, ecp5, nexus)
Return
======
A list of compatible toolchains (str) or an empty list
"""
if type(cls._supported_toolchains) == dict:
assert device is not None
return cls._supported_toolchains[device]
else:
return cls._supported_toolchains

View File

@ -15,6 +15,8 @@ from litex.build.gowin import common, gowin
class GowinPlatform(GenericPlatform): class GowinPlatform(GenericPlatform):
bitstream_ext = ".fs" bitstream_ext = ".fs"
_supported_toolchains = ["gowin", "apicula"]
def __init__(self, device, *args, toolchain="gowin", devicename=None, **kwargs): def __init__(self, device, *args, toolchain="gowin", devicename=None, **kwargs):
GenericPlatform.__init__(self, device, *args, **kwargs) GenericPlatform.__init__(self, device, *args, **kwargs)
if not devicename: if not devicename:

View File

@ -1,2 +1,2 @@
from litex.build.lattice.platform import LatticePlatform from litex.build.lattice.platform import LatticePlatform, LatticeiCE40Platform, LatticeECP5Platform, LatticeNexusPlatform
from litex.build.lattice.programmer import LatticeProgrammer from litex.build.lattice.programmer import LatticeProgrammer

View File

@ -106,7 +106,7 @@ class LatticeIceStormToolchain(YosysNextPNRToolchain):
def icestorm_args(parser): def icestorm_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options") toolchain_group = parser.add_argument_group(title="Icestorm toolchain options")
yosys_nextpnr_args(toolchain_group) yosys_nextpnr_args(toolchain_group)
def icestorm_argdict(args): def icestorm_argdict(args):

View File

@ -62,7 +62,7 @@ class LatticeOxideToolchain(YosysNextPNRToolchain):
return (self._build_name + ".pdc", "PDC") return (self._build_name + ".pdc", "PDC")
def oxide_args(parser): def oxide_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options") toolchain_group = parser.add_argument_group(title="Oxide toolchain options")
yosys_nextpnr_args(toolchain_group) yosys_nextpnr_args(toolchain_group)
toolchain_group.add_argument("--nexus-es-device", action="store_true", help="Use Nexus-ES1 part.") toolchain_group.add_argument("--nexus-es-device", action="store_true", help="Use Nexus-ES1 part.")

View File

@ -13,6 +13,12 @@ from litex.build.lattice import common, diamond, icestorm, trellis, radiant, oxi
class LatticePlatform(GenericPlatform): class LatticePlatform(GenericPlatform):
bitstream_ext = ".bit" bitstream_ext = ".bit"
_supported_toolchains = {
"ice40": ["icestorm"],
"ecp5": ["trellis", "diamond"],
"nexus": ["radiant", "oxide"],
}
def __init__(self, *args, toolchain="diamond", **kwargs): def __init__(self, *args, toolchain="diamond", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)
if toolchain == "diamond": if toolchain == "diamond":
@ -38,7 +44,6 @@ class LatticePlatform(GenericPlatform):
attr_translate = self.toolchain.attr_translate, attr_translate = self.toolchain.attr_translate,
**kwargs) **kwargs)
def build(self, *args, **kwargs): def build(self, *args, **kwargs):
return self.toolchain.build(self, *args, **kwargs) return self.toolchain.build(self, *args, **kwargs)
@ -54,3 +59,67 @@ class LatticePlatform(GenericPlatform):
if hasattr(to, "p"): if hasattr(to, "p"):
to = to.p to = to.p
self.toolchain.add_false_path_constraint(self, from_, to) self.toolchain.add_false_path_constraint(self, from_, to)
@classmethod
def fill_args(cls, toolchain, parser):
"""
pass parser to the specific toolchain to
fill this with toolchain args
Parameters
==========
toolchain: str
toolchain name
parser: argparse.ArgumentParser
parser to be filled
"""
if toolchain == "radiant":
radiant.radiant_build_args(parser)
elif toolchain == "oxide":
oxide.oxide_args(parser)
elif toolchain == "trellis":
trellis.trellis_args(parser)
elif toolchain == "icestorm":
icestorm.icestorm_args(parser)
# nothing for diamond
@classmethod
def get_argdict(cls, toolchain, args):
"""
return a dict of args
Parameters
==========
toolchain: str
toolchain name
Return
======
a dict of key/value for each args or an empty dict
"""
if toolchain == "radiant":
return radiant.radiant_build_argdict(args)
elif toolchain == "oxide":
return oxide.oxide_argdict(args)
elif toolchain == "trellis":
return trellis.trellis_argdict(args)
elif toolchain == "icestorm":
return icestorm.icestorm_argdict(args)
else:
return {}
# nothing for diamond
# LatticeiCE40Platform -----------------------------------------------------------------------------
class LatticeiCE40Platform(LatticePlatform):
device_family = "ice40"
# LatticeECP5Platform ------------------------------------------------------------------------------
class LatticeECP5Platform(LatticePlatform):
device_family = "ecp5"
# LatticeNexusPlatform -----------------------------------------------------------------------------
class LatticeNexusPlatform(LatticePlatform):
device_family = "nexus"

View File

@ -273,7 +273,7 @@ class LatticeRadiantToolchain(GenericToolchain):
def radiant_build_args(parser): def radiant_build_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options") toolchain_group = parser.add_argument_group(title="Radiant toolchain options")
toolchain_group.add_argument("--synth-mode", default="synplify", help="Synthesis mode (synplify or yosys).") toolchain_group.add_argument("--synth-mode", default="synplify", help="Synthesis mode (synplify or yosys).")
def radiant_build_argdict(args): def radiant_build_argdict(args):

View File

@ -150,7 +150,7 @@ class LatticeTrellisToolchain(YosysNextPNRToolchain):
freq=str(float(1/period)*1000), clk="{clk}"), clk=clk) freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
def trellis_args(parser): def trellis_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options") toolchain_group = parser.add_argument_group(title="Trellis toolchain options")
yosys_nextpnr_args(toolchain_group) yosys_nextpnr_args(toolchain_group)
toolchain_group.add_argument("--ecppack-bootaddr", default=0, help="Set boot address for next image.") toolchain_group.add_argument("--ecppack-bootaddr", default=0, help="Set boot address for next image.")
toolchain_group.add_argument("--ecppack-spimode", default=None, help="Set slave SPI programming mode.") toolchain_group.add_argument("--ecppack-spimode", default=None, help="Set slave SPI programming mode.")

View File

@ -12,6 +12,8 @@ from litex.build.microsemi import common, libero_soc
class MicrosemiPlatform(GenericPlatform): class MicrosemiPlatform(GenericPlatform):
bitstream_ext = ".bit" bitstream_ext = ".bit"
_supported_toolchains = ["libero_soc_polarfire"]
def __init__(self, *args, toolchain="libero_soc_polarfire", **kwargs): def __init__(self, *args, toolchain="libero_soc_polarfire", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)
if toolchain == "libero_soc_polarfire": if toolchain == "libero_soc_polarfire":

View File

@ -14,6 +14,8 @@ from litex.build.osfpga import common, osfpga
class OSFPGAPlatform(GenericPlatform): class OSFPGAPlatform(GenericPlatform):
bitstream_ext = ".bin" bitstream_ext = ".bin"
_supported_toolchains = ["osfpga"]
def __init__(self, device, *args, toolchain="foedag", devicename=None, **kwargs): def __init__(self, device, *args, toolchain="foedag", devicename=None, **kwargs):
GenericPlatform.__init__(self, device, *args, **kwargs) GenericPlatform.__init__(self, device, *args, **kwargs)
self.devicename = devicename self.devicename = devicename

View File

@ -14,6 +14,8 @@ from litex.build.quicklogic import common, f4pga
class QuickLogicPlatform(GenericPlatform): class QuickLogicPlatform(GenericPlatform):
bitstream_ext = ".bit" bitstream_ext = ".bit"
_supported_toolchains = ["f4pga"]
def __init__(self, *args, toolchain="f4pga", **kwargs): def __init__(self, *args, toolchain="f4pga", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)
if toolchain == "symbiflow" or toolchain == "f4pga": if toolchain == "symbiflow" or toolchain == "f4pga":

View File

@ -16,6 +16,9 @@ from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStorage
class SimPlatform(GenericPlatform): class SimPlatform(GenericPlatform):
_supported_toolchains = ["verilator"]
def __init__(self, device, io, name="sim", toolchain="verilator", **kwargs): def __init__(self, device, io, name="sim", toolchain="verilator", **kwargs):
if "sim_trace" not in (iface[0] for iface in io): if "sim_trace" not in (iface[0] for iface in io):
io.append(("sim_trace", 0, Pins(1))) io.append(("sim_trace", 0, Pins(1)))

View File

@ -1,2 +1,2 @@
from litex.build.xilinx.platform import XilinxPlatform from litex.build.xilinx.platform import XilinxPlatform, XilinxSpartan6Platform, Xilinx7SeriesPlatform
from litex.build.xilinx.programmer import UrJTAG, XC3SProg, FpgaProg, VivadoProgrammer, iMPACT, Adept from litex.build.xilinx.programmer import UrJTAG, XC3SProg, FpgaProg, VivadoProgrammer, iMPACT, Adept

View File

@ -9,13 +9,18 @@
import os import os
from litex.build.generic_platform import GenericPlatform from litex.build.generic_platform import GenericPlatform
from litex.build.xilinx import common from litex.build.xilinx import common, vivado, ise, yosys_nextpnr
# XilinxPlatform ----------------------------------------------------------------------------------- # XilinxPlatform -----------------------------------------------------------------------------------
class XilinxPlatform(GenericPlatform): class XilinxPlatform(GenericPlatform):
bitstream_ext = ".bit" bitstream_ext = ".bit"
_supported_toolchains = {
"serie7": ["vivado", "f4pga", "yosys+nextpnr"],
"spartan6": ["ise"],
}
def __init__(self, *args, toolchain="ise", **kwargs): def __init__(self, *args, toolchain="ise", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs) GenericPlatform.__init__(self, *args, **kwargs)
self.edifs = set() self.edifs = set()
@ -84,3 +89,49 @@ class XilinxPlatform(GenericPlatform):
if hasattr(to, "p"): if hasattr(to, "p"):
to = to.p to = to.p
self.toolchain.add_false_path_constraint(self, from_, to) self.toolchain.add_false_path_constraint(self, from_, to)
@classmethod
def fill_args(cls, toolchain, parser):
"""
pass parser to the specific toolchain to
fill this with toolchain args
Parameters
==========
toolchain: str
toolchain name
parser: argparse.ArgumentParser
parser to be filled
"""
if toolchain == "vivado":
vivado.vivado_build_args(parser)
@classmethod
def get_argdict(cls, toolchain, args):
"""
return a dict of args
Parameters
==========
toolchain: str
toolchain name
Return
======
a dict of key/value for each args or an empty dict
"""
if toolchain == "vivado":
return vivado.vivado_build_argdict(args)
else:
return dict()
# Xilinx7SeriesPlatform -----------------------------------------------------------------------------
class Xilinx7SeriesPlatform(XilinxPlatform):
device_family = "serie7"
# XilinxSpartan6Platform ---------------------------------------------------------------------------
class XilinxSpartan6Platform(XilinxPlatform):
device_family = "spartan6"