使用LiteX快速创建FPGA SoC工程(5)-LiteX社区-FPGA CPLD-ChipDebug

使用LiteX快速创建FPGA SoC工程(5)

本节介绍Platform类在LiteX中的定义,继承关系如下:

为某个自定义的板卡创建一个自定义的Platform时,其继承关系如下(以Xilinx7系列FPGA为例):

图片[1]-使用LiteX快速创建FPGA SoC工程(5)-LiteX社区-FPGA CPLD-ChipDebug

图1 自定义的Platform的继承关系

下面将从下至上的对这些类进行介绍。

一、GenericPlatform

GenericPlatform的所有代码如下:

# Generic Platform ---------------------------------------------------------------------------------

class GenericPlatform:
    device_family  = None
    _jtag_support  = True # JTAGBone can't be used with all FPGAs.
    _bitstream_ext = None # None by default, overridden by vendor platform, may
                          # be a string when same extension is used for sram and
                          # flash. A dict must be provided otherwise

    def __init__(self, device, io, connectors=[], name=None):


    def request(self, *args, **kwargs):

    def request_all(self, *args, **kwargs):

    def request_remaining(self, *args, **kwargs):

    def lookup_request(self, *args, **kwargs):

    def add_period_constraint(self, clk, period, keep=True, name=None):

    def add_false_path_constraint(self, from_, to):

    def add_false_path_constraints(self, *clk):

    def add_platform_command(self, *args, **kwargs):

    def add_extension(self, *args, **kwargs):

    def add_connector(self, *args, **kwargs):

    def finalize(self, fragment, *args, **kwargs):
       
    def do_finalize(self, fragment, *args, **kwargs):
        
    def add_source(self, filename, language=None, library=None, copy=False):
        
    def add_sources(self, path, *filenames, language=None, library=None, copy=False):
        
    def add_source_dir(self, path, recursive=True, language=None, library=None):
        
    def add_verilog_include_path(self, path):

    def resolve_signals(self, vns):

    def get_verilog(self, fragment, **kwargs):


    def get_edif(self, fragment, cell_library, vendor, device, **kwargs):
       
    def build(self, fragment):

    def get_bitstream_extension(self, mode="sram"):
        
    def create_programmer(self):

    @property
    def jtag_support(self):

    @property
    def support_mixed_language(self):

    @classmethod
    def fill_args(cls, toolchain, parser):

    @classmethod
    def get_argdict(cls, toolchain, args):
  
    @classmethod
    def toolchains(cls, device):

 

1.1 类变量

类变量指的是直接定义在类内部的变量(不是定义在方法中的变量)。GenericPlatform有三个类变量,如下所示:

device_family  = None
 _jtag_support  = True # JTAGBone can't be used with all FPGAs.
 _bitstream_ext = None # None by default, overridden by vendor platform, may
                          # be a string when same extension is used for sram and
                          # flash. A dict must be provided otherwise

其中device_family表示了FPGA芯片的系列,该字段将在Xilinx***Platform派生类中确定,对于Xilinx来说,可能的值为:

spartan6、7series、ultrascale、ultrascale+

_jtag_support是一个列表,定义了哪些芯片支持JTAG。在GenericPlatform的直接子类,即XilinxPlatform,中确定。

_bitstream_ext是一个列表,定义了烧写sram或者烧写flash时,所烧写的文件名后缀。GenericPlatform的直接子类,即XilinxPlatform,中确定。

1.2 __init__构造器函数

def __init__(self, device, io, connectors=[], name=None):
        self.toolchain          = None
        self.device             = device
        self.constraint_manager = ConstraintManager(io, connectors)
        if name is None:
            # Get name from Platform file.
            name = self.__module__.split(".")[-1]
        if name == "__main__":
            # If no Platform file, use script filename,
            name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
        self.name                  = name
        self.sources               = []
        self.verilog_include_paths = []
        self.output_dir            = None
        self.finalized             = False
        self.use_default_clk       = False

        # Set Platform/Device to LiteXContext.
        LiteXContext.platform  = self
        LiteXContext.device    = device

构造器函数的输入参数包括:

  • device:即FPGA芯片的名称;

  • io:非用户可扩展的IO引脚及其所连接的外设;

  • connectors:用户可扩展的IO引脚;

  • name:一般不指定,将自动从文件名或者模块名中获取;

此外,构造器函数还为Platform定义了如下成员变量

  • toolchain:默认为None;即使用哪个EDA工具,例如vivado或者ISE;

  • sources:添加的外部源代码文件的列表,默认为空;

  • verilog_iinclude_paths:添加的外部头文件的列表,默认为空;

  • output_dir:默认为None;

  • finalized:该模块是否已经进行了Finalization过程,默认为False;

  • use_defualt_clk:是否使用默认的时钟域的名称;默认为False;

1.3 类方法

(1)以下三个方法用于从已定义好的Platform中获取对应的引脚信息

def request(self, *args, **kwargs):
        return self.constraint_manager.request(*args, **kwargs)

    def request_all(self, *args, **kwargs):
        return self.constraint_manager.request_all(*args, **kwargs)

    def request_remaining(self, *args, **kwargs):
        return self.constraint_manager.request_remaining(*args, **kwargs)

 

例如,假设有一个流水灯模块LedChaser,下列代码将从平台中获取对应的led灯的引脚信息,并与LedChaser模块的pads端口绑定:

if with_led_chaser:
    led_pads = platform.request_all("user_led_n")
    self.leds = LedChaser(
        pads         = led_pads,
        sys_clk_freq = sys_clk_freq)

(2)以下方法用于在平台本身的定义中的do_finalize方法中,用于查询某个引脚外设是否被使用,如果被使用,则采取一定的措施,例如新增约束等:

def lookup_request(self, *args, **kwargs):
    return self.constraint_manager.lookup_request(*args, **kwargs)
# usage
def do_finalize(self, fragment):
    LatticeiCE40Platform.do_finalize(self, fragment)
    self.add_period_constraint(self.lookup_request("clk12", loose=True), 1e9/12e6)

(3)以下方法用于添加时钟约束

def add_period_constraint(self, clk, period, keep=True, name=None):
    self.toolchain.add_period_constraint(self, clk, period, keep=keep, name=name)

def add_false_path_constraint(self, from_, to):
    raise NotImplementedError

def add_false_path_constraints(self, *clk):
    for a in clk:
        for b in clk:
            if a is not b:
                self.add_false_path_constraint(a, b)

(4)以下方法用于添加其他的、时钟无关的约束

def add_platform_command(self, *args, **kwargs):
    return self.constraint_manager.add_platform_command(*args, **kwargs)

(5)以下方法一般用于添加通过用户可扩展的IO(即connector)连接的外设

def add_extension(self, *args, **kwargs):
  return self.constraint_manager.add_extension(*args, **kwargs)

 

(6)以下方法用于临时添加其他的connector到平台中

def add_connector(self, *args, **kwargs):
    self.constraint_manager.add_connector(*args, **kwargs)
# usage
self.add_connector(_dock_connectors)

(7)以下方法用于添加外部的源代码或头文件

  • def add_source(self, filename, language=None, library=None, copy=False):
        filename = os.path.abspath(filename)
        if language is None:
            language = tools.language_by_filename(filename)
        if library is None:
            library = "work"
        for f, *_ in self.sources:
            if f == filename:
                return
        if copy:
            self.sources.append((filename, language, library, True))
        else:
            self.sources.append((filename, language, library))
    
    def add_sources(self, path, *filenames, language=None, library=None, copy=False):
        for f in filenames:
            self.add_source(os.path.join(path, f), language, library, copy)
    
    def add_source_dir(self, path, recursive=True, language=None, library=None):
        dir_files = []
        if recursive:
            for root, dirs, files in os.walk(path):
                for filename in files:
                    dir_files.append(os.path.join(root, filename))
        else:
            for item in os.listdir(path):
                if os.path.isfile(os.path.join(path, item)):
                    dir_files.append(os.path.join(path, item))
        for filename in dir_files:
            _language = language
            if _language is None:
                _language = tools.language_by_filename(filename)
            if _language is not None:
                self.add_source(filename, _language, library)
    
    def add_verilog_include_path(self, path):
        self.verilog_include_paths.append(os.path.abspath(path))

    (8)以下命令作用未知:

    def get_edif(self, fragment, cell_library, vendor, device, **kwargs):
        return edif.convert(
            fragment,
            self.constraint_manager.get_io_signals(),
            cell_library, vendor, device, **kwargs)
    def resolve_signals(self, vns):
        # Resolve signal names in constraints.
        sc = self.constraint_manager.get_sig_constraints()
        named_sc = [(vns.get_name(sig), pins, others, resource)
                    for sig, pins, others, resource in sc]
        # Resolve signal names in platform commands.
        pc = self.constraint_manager.get_platform_commands()
        named_pc = []
        for template, args in pc:
            name_dict = dict((k, vns.get_name(sig)) for k, sig in args.items())
            named_pc.append(template.format(**name_dict))
    
        return named_sc, named_pc

 

(9)以下方法用于编译生成bit文件和获取bit文件的扩展名

def build(self, fragment):
    raise NotImplementedError("GenericPlatform.build must be overloaded")

def get_bitstream_extension(self, mode="sram"):
    """
    Return the bitstream's extension according to mode (sram / flash).
    The default (generic) implementation check if `self._bitstream_ext`
    is a dict or a string. For former case it return extension using `mode`
    parameter, in latter case simply return `self._bitstream_ext`'s value.
    When this behaviour is not adapted this method must be overriden by
    a specific one at vendor level.

    Parameters
    ----------
    mode: str
        bitstream destination (must be sram or flash)

    Returns
    -------
        bitstream extension: str
    """
    if self._bitstream_ext is None:
        return None
    elif type(self._bitstream_ext) == dict:
        return self._bitstream_ext[mode]
    else:
        return self._bitstream_ext

(10)其他:

def create_programmer(self):
    raise NotImplementedError

@property
def jtag_support(self):
    if isinstance(self._jtag_support, bool):
        return self._jtag_support
    else:
        for dev in self._jtag_support:
            if self.device.startswith(dev):
                return True
        return False

@property
def support_mixed_language(self):
    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

二、XilinxPlatform

继承自GenericPlatform的XilinxPlatform,进一步规定了一些细节。

2.1 类成员

XilinxPlatform对GenericPlatform的三个类成员进行了具体的赋值,如下所示:

class XilinxPlatform(GenericPlatform):
    _bitstream_ext = {
        "sram"  : ".bit",
        "flash" : ".bin"
    }

    _supported_toolchains = {
        "spartan6"    : ["ise"],
        "7series"     : ["vivado", "f4pga", "yosys+nextpnr", "openxc7"],
        "ultrascale"  : ["vivado"],
        "ultrascale+" : ["vivado"],
    }

    _jtag_support = [
        "xc6",
        "xc7a", "xc7k", "xc7v", "xc7z",
        "xcau", "xcku", "xcvu", "xczu"
    ]

2.2 __init__构造器函数

主要是新增了对toolchain的判断和赋值,以及新增了edifs和ips两个成员变量

def __init__(self, *args, toolchain="ise", **kwargs):
    GenericPlatform.__init__(self, *args, **kwargs)
    self.edifs = set()
    self.ips   = {}
    if toolchain == "ise":
        from litex.build.xilinx import ise
        self.toolchain = ise.XilinxISEToolchain()
    elif toolchain == "vivado":
        from litex.build.xilinx import vivado
        self.toolchain = vivado.XilinxVivadoToolchain()
    elif toolchain == "symbiflow" or toolchain == "f4pga":
        from litex.build.xilinx import f4pga
        self.toolchain = f4pga.F4PGAToolchain()
    elif toolchain in ["yosys+nextpnr", "openxc7"]:
        from litex.build.xilinx import yosys_nextpnr
        self.toolchain = yosys_nextpnr.XilinxYosysNextpnrToolchain(toolchain)
    else:
        raise ValueError(f"Unknown toolchain {toolchain}")

2.3 类方法

(1)添加网表文件和IP:

def add_edif(self, filename):
    self.edifs.add((os.path.abspath(filename)))

def add_ip(self, filename, disable_constraints=False):
    self.ips.update({os.path.abspath(filename): disable_constraints})

(2)add_platform_command方法中进行了进一步的检查,检查通过后才可以调用GenericPlatform的add_platform_command方法;

def add_platform_command(self, command, **signals):
    skip = False
    from litex.build.xilinx import yosys_nextpnr
    if isinstance(self.toolchain, yosys_nextpnr.XilinxYosysNextpnrToolchain):
        # FIXME: Add support for INTERNAL_VREF to yosys+nextpnr flow.
        if "set_property INTERNAL_VREF" in command:
            print("WARNING: INTERNAL_VREF constraint removed since not yet supported by yosys-nextpnr flow.")
            skip = True
        if "set_property CFGBVS" in command:
            print("WARNING: CFGBVS constraint removed since not yet supported by yosys-nextpnr flow.")
            skip = True
        if "set_property CONFIG_VOLTAGE" in command:
            print("WARNING: CONFIG_VOLTAGE constraint removed since not yet supported by yosys-nextpnr flow.")
            skip = True
    if not skip:
        GenericPlatform.add_platform_command(self, command, **signals)

(3)对ger_verilog、get_edif、build、add_false_path_constraint等方法进行了进一步细化:

def get_verilog(self, *args, special_overrides=dict(), **kwargs):
    so = dict(common.xilinx_special_overrides)
    if self.device[:3] == "xc6":
        so.update(common.xilinx_s6_special_overrides)
    if self.device[:3] == "xc7":
        so.update(common.xilinx_s7_special_overrides)
    if self.device[:4] == "xcku":
        so.update(common.xilinx_us_special_overrides)
    if self.device[:4] == "xcau":
        so.update(common.xilinx_us_special_overrides)
    so.update(special_overrides)
    return GenericPlatform.get_verilog(self, *args,
        special_overrides = so,
        attr_translate    = self.toolchain.attr_translate,
        **kwargs
    )

def get_edif(self, fragment, **kwargs):
    return GenericPlatform.get_edif(self, fragment, "UNISIMS", "Xilinx", self.device, **kwargs)

def build(self, *args, **kwargs):
    return self.toolchain.build(self, *args, **kwargs)

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)

@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()

三、Xilinx***Platform

该类仅设置了GenericPlatform的device_family类成员变量,如下:

# XilinxSpartan6Platform ---------------------------------------------------------------------------
class XilinxSpartan6Platform(XilinxPlatform):
    device_family = "spartan6"

# Xilinx7SeriesPlatform ----------------------------------------------------------------------------
class Xilinx7SeriesPlatform(XilinxPlatform):
    device_family = "7series"

# XilinxUSPlatform ---------------------------------------------------------------------------------
class XilinxUSPlatform(XilinxPlatform):
    device_family = "ultrascale"

# XilinxUSPPlatform --------------------------------------------------------------------------------
class XilinxUSPPlatform(XilinxPlatform):
    device_family = "ultrascale+"

至此,我们学习完了自定义Platform的所有父类。

请登录后发表评论

    没有回复内容