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

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

本节是主要是实战。通过为自己的开发板定义 Platform 文件和 Target 文件来创建一个简单的 SoC。

一、为开发板创建 Platform 文件

不说废话,直接上代码,我的 FPGA 开发板芯片型号是 Zynq 系列的 7100

#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2022 Yonggang Liu <ggang.liu@gmail.com>
# SPDX-License-Identifier: BSD-2-Clause

from ast import Sub
from litex.build.generic_platform import *
from litex.build.xilinx import XilinxPlatform, Xilinx7SeriesPlatform, VivadoProgrammer

# IOs ----------------------------------------------------------------------------------------------

_io = [
    # Clk / Rst
    ("clk200", 0,
     Subsignal("p", Pins("F9"), IOStandard("DIFF_SSTL15")),
     Subsignal("n", Pins("E8"), IOStandard("DIFF_SSTL15")),
    ),

    # Leds
    ("user_led", 0, Pins("AJ16"),  IOStandard("LVCMOS33")),
    ("user_led", 1, Pins("AK16"),  IOStandard("LVCMOS33")),
    ("user_led", 2, Pins("AE16"),  IOStandard("LVCMOS33")),
    ("user_led", 3, Pins("AE15"),  IOStandard("LVCMOS33")),

    # Buttons
    ("user_btn", 0, Pins("AF18"), IOStandard("LVCMOS33")),
    ("user_btn", 1, Pins("AF17"), IOStandard("LVCMOS33")),
    ("user_btn", 2, Pins("AH17"), IOStandard("LVCMOS33")),
    ("user_btn", 3, Pins("AH16"), IOStandard("LVCMOS33")),

    # Serial, There is no Pins for PL's UART, need to Pmod to convert
    ("serial", 0,
        Subsignal("tx", Pins("AC13"), IOStandard("LVCMOS33")),
        Subsignal("rx", Pins("AC14"), IOStandard("LVCMOS33")),
    ),
]

# _ps7_io = [
#     # PS7
#     ("ps7_clk",   0, Pins("A22"), IOStandard("LVCMOS33")),
#     ("ps7_porb",  0, Pins("D21"), IOStandard("LVCMOS33")),
#     ("ps7_srstb", 0, Pins("B19"), IOStandard("LVCMOS33")),
#     ("ps7_mio",   0, Pins("F24 D23 F23 C23 E23 C24 D24 B24 C21 A24",
#                           "E22 A23 E21 F22 B22 C22 L19 K21 K20 J20",
#                           "M20 J19 L20 J21 M19 G19 M17 G20 L17 H22",
#                           "L18 H21 K17 G22 K18 G21 H17 B21 A20 F18",
#                           "B20 J18 D20 E18 E20 H18 F20 A18 C19 D18",
#                           "A19 F19 D19 C18")),
#     ("ps7_ddram", 0,
#         Subsignal("addr",    Pins("L25 K26 L27 G25 J26 G24 H26 K22 F27 J23 G26 H24 K23 H23 J24")),
#         Subsignal("ba",      Pins("M27 M26 M25")),
#         Subsignal("cas_n",   Pins("M24")),
#         Subsignal("clk_n",   Pins("J25")),
#         Subsignal("clk_p",   Pins("K25")),
#         Subsignal("cke",     Pins("M22")),
#         Subsignal("cs_n",    Pins("N22")),
#         Subsignal("dm",      Pins("C27 B30 H29 K28")),
#         Subsignal("dq",      Pins("A25 E25 B27 D25 B25 E26 D26 E27 A29 A27 A30 A28 C28 D30 D28 D29 H27 G27 H28 E28 E30 F28 G30 F30 J29 K27 J30 J28 K30 M29 L30 M30")),
#         Subsignal("dqs_n",   Pins("C26 C29 G29 L28")),
#         Subsignal("dqs_p",   Pins("B26 B29 F29 L29")),
#         Subsignal("odt",     Pins("L23")),
#         Subsignal("ras_n",   Pins("N24")),
#         Subsignal("drstb",   Pins("F25")),
#         Subsignal("web",     Pins("N23")),
#         Subsignal("vrn",     Pins("N21")),
#         Subsignal("vrp",     Pins("M21")),
#     ),
# ]
#DRSTB
# Connectors ---------------------------------------------------------------------------------------

_connectors = [
    ("pmodj33",  "AC13 AC14 AB12 AC12 AE12 AF12 AD13 AD14 AG12 AH12 AE13 AF13 AH13 AH14 AJ13 AJ14 AK12 AK13 AB14 AB15 AF15 AG15 AG14 AF14 AD15 AD16 AC16 AC17 AA14 AA15 AJ15 AK15 AB17 AB16"),
]

# PMODS --------------------------------------------------------------------------------------------
# use the user-shared I/O as the serial port
_usb_uart_pmod_io = [
    # USB-UART PMOD on JB:
    # - https://store.digilentinc.com/pmod-usbuart-usb-to-uart-interface/
    ("serial", 0,
        Subsignal("tx", Pins("pmodj10:0")),
        Subsignal("rx", Pins("pmodj10:1")),
        IOStandard("LVCMOS33")
    ),
]

# Platform -----------------------------------------------------------------------------------------

class Platform(Xilinx7SeriesPlatform):
    # used in the methods: finalize and do_finalize
    default_clk_name   = "clk200"
    default_clk_period = 1e9/200e6

    def __init__(self):
        XilinxPlatform.__init__(self, "xc7z100ffg900-2", _io, _connectors, toolchain="vivado")
        self.add_extension(_usb_uart_pmod_io)

    def create_programmer(self):
        return VivadoProgrammer()

    def do_finalize(self, fragment):
        XilinxPlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk200", loose=True), 1e9/200e6)

如前面的章节所属,将 FPGA 芯片引脚中,与外设固定绑定的(即不可用作用户扩展 IO)的引脚列在**_io中,其他引脚(即可用作用户扩展 IO)列在_connector**中。

该开发板本来是有一个 UART 接口的,但是该接口连接的是 PS 端(即 ARM 核),为了在 PL 端使用串口,这里使用了开发板上的 2 个可用户扩展的 IO 作为串口,并通过add_extension添加。

设置了主时钟域的名称为“clk200″,如前面所述,该名称必须能够和_io 中的一个时钟源的名称相匹配,并且该时钟源的频率必须和所设置的时钟频率相同,即 200MHz。

do_finalize 的作用是,如果基于该 Platform 的某个 Target(某个工程)request 了”clk200“时钟,则会自动添加约束。

二、Target 文件

Target 文件对应于我们实际的一个 FPGA 工程。代码如下:

#!/usr/bin/env python3

#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2022 Yonggang Liu <ggang.liu@gmail.com>,
# SPDX-License-Identifier: BSD-2-Clause

from migen import *

from litex_boards.platforms import alinx_ax7z100
from litex.build.xilinx.vivado import vivado_build_args, vivado_build_argdict

from litex.soc.interconnect import axi
from litex.soc.interconnect import wishbone

from litex.soc.cores.clock import *
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.cores.led import LedChaser

# CRG ----------------------------------------------------------------------------------------------

class _CRG(Module):#默认的Clock/Reset生成器
    def __init__(self, platform, sys_clk_freq):
        self.rst = Signal()#创建一个复位信号
        self.clock_domains.cd_sys = ClockDomain()#创建一个时钟域

        self.submodules.pll = pll = S7PLL(speedgrade=-2)#创建一个子模块pll
        self.comb += pll.reset.eq(self.rst)#将pll的reset接口连接到本模块的rst接口
        pll.register_clkin(platform.request("clk200"), 200e6)#pll的时钟输入,连接到platform的名为clk200的时钟
        pll.create_clkout(self.cd_sys, sys_clk_freq)#输出连接到cd_sys时钟域
        platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) # Ignore sys_clk to pll.clkin path created by SoC's rst.
        
# BaseSoC ------------------------------------------------------------------------------------------

class BaseSoC(SoCCore):
    def __init__(self, sys_clk_freq=int(100e6), with_led_chaser=True, **kwargs):
        platform = alinx_ax7z100.Platform()

        # CRG --------------------------------------------------------------------------------------
        self.submodules.crg = _CRG(platform, sys_clk_freq)#platform的clk200会连接到pll,pll会生成sys_clk_freq的输出时钟

        # SoCCore ----------------------------------------------------------------------------------
        kwargs["uart_name"] = "serial"
        SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Alinx AX7Z100", **kwargs)
        
        # Leds -------------------------------------------------------------------------------------
        if with_led_chaser:
            self.submodules.leds = LedChaser(
                pads         = platform.request_all("user_led"),
                sys_clk_freq = sys_clk_freq)

# Build --------------------------------------------------------------------------------------------

def main():
    from litex.soc.integration.soc import LiteXSoCArgumentParser
    parser = LiteXSoCArgumentParser(description="LiteX SoC on zynq xc7z100")
    #create a argument group adn add to the parser
    target_group = parser.add_argument_group(title="Target options")
    target_group.add_argument("--build",        action="store_true", help="Build design")
    target_group.add_argument("--load",         action="store_true", help="Load bitstream")
    target_group.add_argument("--sys-clk-freq", default=200e6,       help="System clock frequency (default: 200MHz)")
    # add builder_args to parser
    builder_args(parser)
    # add soc_core_args to parser
    soc_core_args(parser)
    # add vivado_build_args to parser
    vivado_build_args(parser)
    args = parser.parse_args()

    soc = BaseSoC(
        sys_clk_freq = int(float(args.sys_clk_freq)),
        **soc_core_argdict(args)
    )
    builder = Builder(soc, **builder_argdict(args))
    if args.build:
        builder.build(**vivado_build_argdict(args))

    if args.load:
        prog = soc.platform.create_programmer()
        prog.load_bitstream(builder.get_bitstream_filename(mode="sram"), device=1)

if __name__ == "__main__":
    main()

在上述代码中,我们定义了main方法,该方法中,添加了一系列的用于解析命令行参数的工具,以生成对应的SoC,这部分内容稍后介绍。

虽然该板卡本身具有ARM核,但是如何在LiteX中使用该ARM核比较复杂,目前先跳过PS部分,仅使用PL端的资源生成SoC。

2.1 _CRG模块

CRG为Clock Generate Module的缩写,即时钟生成模块。我们在_io中添加了板卡上的200MHz的时钟源,该时钟源一般不直使用,而是通过PLL后使用。

所以我们基于Migen的Module类,定义了一个_CRG模块,该模块的构造器函数接收两个参数,一个是platform,即所使用的开发板的platform类,另一个则为所需要通过PLL生成的时钟的频率sys_clk_freq

  • 为该模块定义一个复位信号rst:

    self.rst = Signal()#创建一个复位信号
  • 创建一个名称为sys的时钟,注意如前所述,前缀“cd_”将会被自动忽略;

    self.clock_domains.cd_sys = ClockDomain()#创建一个时钟域
  • 在_CRG模块中创建一个pll子模块,并将该子模块的复位连接到_CRG模块的复位信号;

    self.submodules.pll = pll = S7PLL(speedgrade=-2)#创建一个子模块pll
    self.comb += pll.reset.eq(self.rst)#将pll的reset接口连接到本模块的rst接口
  • 将PLL的时钟输入连接到platform的clk200上,频率为200MHz,注意这里使用了request来获取platform中的io信息;

    pll.register_clkin(platform.request("clk200"), 200e6)#pll的时钟输入,连接到platform的名为clk200的时钟

     

  • pll创建一个频率为sys_clk_freq的时钟,连接到_CRG模块的sys时钟,并添加约束;

    pll.create_clkout(self.cd_sys, sys_clk_freq)#输出连接到cd_sys时钟域
    platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) # Ignore sys_clk to pll.clkin path created by SoC's rst

     

    2.2 BaseSoC

本章仅简单介绍该类的继承关系,如下图所示:

图1 BaseSoC的继承关系

# BaseSoC ------------------------------------------------------------------------------------------

class BaseSoC(SoCCore):
    def __init__(self, sys_clk_freq=int(100e6), with_led_chaser=True, **kwargs):
        platform = alinx_ax7z100.Platform()

        # CRG --------------------------------------------------------------------------------------
        self.submodules.crg = _CRG(platform, sys_clk_freq)#platform的clk200会连接到pll,pll会生成sys_clk_freq的输出时钟

        # SoCCore ----------------------------------------------------------------------------------
        kwargs["uart_name"] = "serial"
        SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Alinx AX7Z100", **kwargs)
        
        # Leds -------------------------------------------------------------------------------------
        if with_led_chaser:
            self.submodules.leds = LedChaser(
                pads         = platform.request_all("user_led"),
                sys_clk_freq = sys_clk_freq)

该类进行了如下工作:

  • 确定所使用的platform

  • 添加所创建的_CRG模块,并传递platfrom和时钟频率;

  • 确保串口组建的名称与platform定义的一致;

  • 使用了一个官方的流水灯作为子模块,连接到platform的”user_led”IO;

2.3 main方法

main方法除了解析命令行参数,还提供了编译生成bit文件、烧写bit文件的功能,至于是否执行这些步骤,则取决于命令行参数。

def main():
    from litex.soc.integration.soc import LiteXSoCArgumentParser
    parser = LiteXSoCArgumentParser(description="LiteX SoC on zynq xc7z100")
    #create a argument group adn add to the parser
    target_group = parser.add_argument_group(title="Target options")
    target_group.add_argument("--build",        action="store_true", help="Build design")
    target_group.add_argument("--load",         action="store_true", help="Load bitstream")
    target_group.add_argument("--sys-clk-freq", default=200e6,       help="System clock frequency (default: 200MHz)")
    # add builder_args to parser
    builder_args(parser)
    # add soc_core_args to parser
    soc_core_args(parser)
    # add vivado_build_args to parser
    vivado_build_args(parser)
    args = parser.parse_args()

    soc = BaseSoC(
        sys_clk_freq = int(float(args.sys_clk_freq)),
        **soc_core_argdict(args)
    )
    builder = Builder(soc, **builder_argdict(args))
    if args.build:
        builder.build(**vivado_build_argdict(args))

    if args.load:
        prog = soc.platform.create_programmer()
        prog.load_bitstream(builder.get_bitstream_filename(mode="sram"), device=1)

三、编译生成bit文件

在target文件所在目录执行命令,编译生成bit文件,我的命令如下:

./alinx_ax7z100_target.py --build

最终编译生成了bit文件:

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

烧写该bit文件,并通过串口连接FPGA与主机,即可看到SoC的启动过程的打印。稍后有机会再进行实机展示。
请登录后发表评论

    没有回复内容