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

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

本章介绍 BaseSoC 的继承关系的细节。

一、BaseSoC

以下是上一章节所使用的 Target 文件中的 BaseSoC 的代码:

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

class BaseSoC(SoCCore):
    #kwargs指代了继承自SoCCore类的__init__方法的除了sys_clk_freq 和 with_led_chaser之外的所有所有参数,是一个字典
    def __init__(self, sys_clk_freq=int(100e6),
        with_ethernet = False,
        eth_phy = "rgmii",
        with_led_chaser = True, **kwargs):
        platform = alinx_ax7z100.Platform()

        # CRG --------------------------------------------------------------------------------------
        # 创建一个子模块,即PLL
        self.submodules.crg = _CRG(platform, sys_clk_freq)#platform的clk200会连接到pll,pll会生成sys_clk_freq的输出时钟

        # SoCCore ----------------------------------------------------------------------------------
        #if kwargs["uart_name"] == "serial": kwargs["uart_name"] = "usb_uart" # Use USB-UART Pmod on JB.
        kwargs["uart_name"] = "serial"#强制把SoCCore的uar_name成员变量设置为serial,以和7100的platform文件中的定义对应
        SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Alinx AX7Z100", **kwargs)

        # DDR3 SDRAM -------------------------------------------------------------------------------
        if not self.integrated_main_ram_size:#该选项默认是None
            self.ddrphy = s7ddrphy.K7DDRPHY(platform.request("ddram"),
                memtype      = "DDR3",
                nphases      = 4,
                sys_clk_freq = sys_clk_freq)
            self.add_sdram("sdram",
                phy           = self.ddrphy,
                module        = MT41J256M16(sys_clk_freq, "1:4"),
                # module        = MT41J256M16(sys_clk_freq, "1:4","800"),
                l2_cache_size = kwargs.get("l2_size", 8192)
            )
        # Ethernet ---------------------------------------------------------------------------------
        if with_ethernet:
            # RGMII Ethernet PHY -------------------------------------------------------------------
            if eth_phy == "rgmii":
                # phy
                self.ethphy = LiteEthPHYRGMII(
                    clock_pads = self.platform.request("eth_clocks"),
                    pads       = self.platform.request("eth"))
            self.add_ethernet(phy=self.ethphy)
        # Leds -------------------------------------------------------------------------------------
        if with_led_chaser:
            self.submodules.leds = LedChaser(
                pads         = platform.request_all("user_led"),
                sys_clk_freq = sys_clk_freq)

# 使用示例
soc = BaseSoC(#生成一个soc对象
        sys_clk_freq = int(float(args.sys_clk_freq)),
        #这里的args.sys_clk_freq实际上就是上面的--sys-clk-freq指定的值,最终决定了pll的输出频率
        with_ethernet = args.with_ethernet,
        eth_phy = args.eth_phy,
        **soc_core_argdict(args)#传递SOC相关参数的字典给BaseSoc
    )

可以看到,BaseSoC 继承自SoCCore 类,SoCCore 类是 LiteX 所提供的类。整体的继承关系如下图:

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

PS:图中最下面的类应该是自定义的BaseSoC

图 1 BaseSoC 的继承关系

各层级的父类的主要作用如下:

  • 一级

    • Migen Module:除了常规的功能,提供了 finalize 和 do_finalize 方法,其中 finalize 调用了 do_finalize,而 do_finalize 未定义具体的操作。

    • AutoCSR:是一种 MixIn,用于提供独立于总线的、对 CSR 寄存器的访问途径;一个 Module 可以通过继承该类,来继承 get_scrs、get_memories、get_constants 三个方法,这三个方法用于扫描 CSR 和 memory 的属性,并返回相关的列表;

    • AutoDoc:自动生成 SoC 相关信息的文档的功能,例如各个外设的地址空间,此处不进行介绍;

  • 二级

    • LiteXModule:是 Migen Module 的强化版本,提供了一些处理子模块、specials 和时钟域的方便的方法;一般无需改动;

    • SoCCoreCompat:为了实现某些兼容性,定义了一些方法,目前已经在其他类中有替代性的方法,最好不要使用;包括:添加中断、添加 wb_master、添加 wb_slave、添加寄存器 memory、添加寄存器 rom 等;

  • 三级

    • SoC:该类大量实现了 SoC 的相关细节,包括总线定义、csr 定义、中断定义等,将重点介绍该类

  • 四级

    • LiteXSoC:相对于 SoC 类新增了大量的方法,包括添加 UART、UARTbone、jtagbone、sdram、ethernet、etherbone、spi_flash、spi_sdcard、sdcard、sata、pcie、video_colorbars、video_terminal、video_framebuffer;

  • 五级

    • SoCCore:进一步细化的 SoC 的细节,包括总线、CPU、ROM、SRAM、MainRAM、CSR、中断、UART、timer、JTAG 等;将详细介绍

从 BaseSoC 的构造器函数init代码和使用示例可以看出,如果该 SoC 需要某种功能的开关,则需要在构造器函数的参数中进行添加,并通过命令行参数进行传递

注意这一行代码:SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on Alinx AX7Z100", **kwargs),BaseSoC 调用了其父类的构造器函数,传递了 platform、默认时钟域的时钟频率、以及打印信息。

下面将逐步查看继承关系中的类的定义。

二、LiteXModule

该类定义了两个经常使用的方法add_moduleget_module,用于添加或获取子模块。

LiteXModule 有三个子类

  • SoCBusHandler

  • SoCLocHandler

  • SoCController

  • SoC

2.1 SoCBusHandler

该类定义了所支持的总线及其可能的参数,支持三种总线 wishbone、axi-lite 和 axi;具体如下:

# 三个类变量
supported_standard      = ["wishbone", "axi-lite", "axi"]
supported_data_width    = [32, 64, 128, 256, 512]
supported_address_width = [32, 64]

其中,wishbone 只能按字的大小寻址,其他的可以按字节寻址,还定义了 masters、slaves、regions、io_regeions 等成员变量,如下:

# Create Bus;以下都是成员变量
self.standard              = standard
self.data_width            = data_width
self.address_width         = address_width
self.addressing            = {
    "wishbone" : "word", # FIXME: Allow selection for Wishbone.
    "axi-lite" : "byte",
    "axi"      : "byte",
}[standard]
self.bursting              = bursting
self.interconnect          = interconnect
self.interconnect_register = interconnect_register
self.masters               = {}
self.slaves                = {}
self.regions               = {}
self.io_regions            = {}
self.io_regions_check      = True
self.timeout               = timeout
self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
    colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))

支持添加保留字段

# Add reserved regions.
self.logger.info("Adding {} Bus Regions...".format(colorer("reserved", color="cyan")))
for name, region in reserved_regions.items():
    if isinstance(region, int):
        # SoCRegion的构造器函数为:__init__(self, origin=None, size=None, mode="rw", cached=True, linker=False, decode=True):
        region = SoCRegion(origin=region, size=0x1000000)
    self.add_region(name, region)

self.logger.info("Bus Handler {}.".format(colorer("created", color="green")))

2.1.1 类方法

  1. add_regin(name, region)

该方法用于添加、分配或检查 region。如果一个 region 是 IO region,那么该 region 只能是不可缓存的,否则只能是可缓存的

  1. alloc_region(name, size, cached=True)

该方法用于为那些没有指定边界的 regin 进行分配。分配的region具有2的幂次方对齐的起始地址,并且确保不会和其他已有的region重叠。

  1. check_region_is_in(region, container)

检查region的下边界是否小于container的下边界,或者region的上边界是否大于等于container的上边界,满足其一则返回false。

  1. check_region_is_io(region)

检查传入的region与所有的io_region是否存在in关系,即循环调用check_region_is_in进行检查。

  1. add_adapter(name, interface, direction="m2s")

其中又定义了:bus_data_width_convertbus_addressing_convertbus_standard_convert、通过使用add_adapter,将对外提供与系统总线不同的总结接口。参数interface是所需要的总线类型。

  1. add_master(name=None, master=None)

在总线上添加master。

  1. add_controller(name=None, controller=None)

实际上就是调用了add_master,将其master参数设置为controller。应该没有什么区别。

  1. add_slave(name=None, slave=None, region=None)

添加slave,会进行是否重名、是否重复添加的检查。

  1. add_peripheral(name=None, peripheral=None, region=None)

实际上就是调研了add_slave,将peripheral和region传递给它。

  1. get_address_width(standard)

该方法返回系统总线(即standard参数所代表的总线)的地址为宽,如果standard所代表的总线类型与实际的系统总线不一致,还会进行地址位宽的变换。

此外,SoCBusHandler子类会根据masters和slaves的数量来决定使用p2p还是crossbar/sharedbus。

2.2 SoCLocHandler

2.2.1 构造器函数__init__

def __init__(self, name, n_locs):
        self.name   = name
        self.locs   = {}
        self.n_locs = n_locs

2.2.2 类方法

  • alloc(name)

  • add(name, n=None, use_loc_if_exists=False)

SoCLocHandler类又有以下子类

  • SoCCSRHandler

  • SoCIRQHandler

2.2.3 子类SoCCSRHandler

2.2.3.1 类成员

SoCCSRHandler有几个类成员,如下所示,定义了所支持的寄存器的数据位宽、地址位宽、对齐位宽、分页大小、大小端等:

supported_data_width    = [8, 32]
supported_address_width = [14+i for i in range(4)]
supported_alignment     = [32]
supported_paging        = [0x800*2**i for i in range(4)] # 0x800 =>1000_0000_0000, 左移0~3bit
supported_ordering      = ["big", "little"]

2.2.3.2 构造器函数__init__

def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, ordering="big", reserved_csrs={}):
    SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging) # name = "CSR", n_locs计算的是,在指定地址位宽后,有多少个分页
    ...
    self.data_width    = data_width
    self.address_width = address_width
    self.alignment     = alignment
    self.paging        = paging
    self.ordering      = ordering
    self.masters       = {}
    self.regions       = {}
    ...
    # Add reserved CSRs.
    self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
    for name, n in reserved_csrs.items():
        self.add(name, n)
    ...

注意到masters和regions是空的。

2.2.3.3 类方法

  1. add_master(name=None, master=None)

用于添加master,会进行是否重复添加的检查和数据位宽是否匹配的检查。

  1. add_region(name, region)

添加region

  1. address_map(name, memory)

如果name未被添加,则会自动添加;如果已存在,则直接返回该name所对应的序号。

2.2.4 子类SoCIRQHandler

2.2.4.1 构造器函数__init__

def __init__(self, n_irqs=32, reserved_irqs={}):
    SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
    self.logger = logging.getLogger("SoCIRQHandler")
    self.logger.info("Creating IRQ Handler...")
    self.enabled = False
    ...
    for name, n in reserved_irqs.items():
        self.add(name, n)
    ...

 

构造器函数默认没有启用irq

2.2.4.2 类方法

  1. enable()

enabled设置为True

  1. add(name, *args, **kwargs)

检查是否启用了irq,然后才调用父类SoCLocHandleradd方法。未启用就调用,会报错。

2.3 SoCController

该类仅有一个构造器函数:

def __init__(self, with_reset=True, with_scratch=True, with_errors=True):
    if with_reset:
        self._reset = CSRStorage(fields=[
            CSRField("soc_rst", size=1, offset=0, pulse=True, description="""Write `1` to this register to reset the full SoC (Pulse Reset)"""),
            CSRField("cpu_rst", size=1, offset=1,             description="""Write `1` to this register to reset the CPU(s) of the SoC (Hold Reset)"""),
        ])
    if with_scratch:
        self._scratch = CSRStorage(32, reset=0x12345678, description="""
            Use this register as a scratch space to verify that software read/write accesses
            to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
            can be used to verify endianness.""")
    if with_errors:
        self._bus_errors = CSRStatus(32, description="Total number of Wishbone bus errors (timeouts) since start.")

    # # #

    # Reset
    if with_reset:
        self.soc_rst = self._reset.fields.soc_rst
        self.cpu_rst = self._reset.fields.cpu_rst

    # Errors
    if with_errors:
        self.bus_error = Signal()
        bus_errors     = Signal(32)
        self.sync += [
            If(bus_errors != (2**len(bus_errors)-1),
                If(self.bus_error, bus_errors.eq(bus_errors + 1))
            )
        ]
        self.comb += self._bus_errors.status.eq(bus_errors)

 

该类总的来说就是设置了一些寄存器,用于CPU复位、SOC复位和总线读写操作测试。

下面将单独介绍LiteXModule的SoC子类。

三、SoC

3.1 类成员

mem_map = {}

可以看到初始的mem_map为空。

3.2 构造器函数__init__

def __init__(self, platform, sys_clk_freq,
    bus_standard         = "wishbone",
    bus_data_width       = 32,
    bus_address_width    = 32,
    bus_timeout          = 1e6,
    bus_bursting         = False,
    bus_interconnect     = "shared",
    bus_reserved_regions = {},

    csr_data_width       = 32,
    csr_address_width    = 14,
    csr_paging           = 0x800,
    csr_ordering         = "big",
    csr_reserved_csrs    = {},

    irq_n_irqs           = 32,
    irq_reserved_irqs    = {},
    ):
    ...
    self.platform     = platform # 设置板卡
    self.sys_clk_freq = int(sys_clk_freq) # Do conversion to int here to allow passing float to SoC.
    self.constants    = {} #在add_constant中使用
    self.csr_regions  = {}
    ...
    # 调用三个子类处理相关事务
    # SoC Bus Handler --------------------------------------------------------------------------
    self.bus = SoCBusHandler(
        standard         = bus_standard,
        data_width       = bus_data_width,
        address_width    = bus_address_width,
        timeout          = bus_timeout,
        bursting         = bus_bursting,
        interconnect     = bus_interconnect,
        reserved_regions = bus_reserved_regions,
        )

    # SoC Bus Handler --------------------------------------------------------------------------
    self.csr = SoCCSRHandler(
        data_width    = csr_data_width,
        address_width = csr_address_width,
        alignment     = 32,
        paging        = csr_paging,
        ordering      = csr_ordering,
        reserved_csrs = csr_reserved_csrs,
    )

    # SoC IRQ Handler --------------------------------------------------------------------------
    self.irq = SoCIRQHandler(
        n_irqs        = irq_n_irqs,
        reserved_irqs = irq_reserved_irqs
    )
    ...
    self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
可以看到,构造器函数主要是创建了用于处理总线、寄存器和中断相关的子类,最后设置了时钟约束。

3.3 类方法

以下是SoC的一些辅助方法:

  1. check_if_exists(name)

检查name所代表的子模块是否存在。

  1. add_constant(name, value=None, check_duplicate=True)

name全都大写,并保存valueself.constants中。

  1. add_config(name, value=None, check_duplicate=True)

"CONFIG_" + name保存到self.constants中。

  1. check_bios_requirements()

检查是否有timer0romsram等外设。

以下是SoC的主要组件:

  1. add_controller(name="ctrl", **kwargs)

调用父类LiteXModule的add_module方法,添加CPU控制器SoCController

  1. add_ram(name, origin, size, contents=[], mode="rwx")

添加SRAM,SRAM的接口实际上只支持wishbone或者AXILite

  1. add_rom(name, origin, size, contents=[], mode="rx")

实际上就是调用了add_ram

  1. init_rom(name, contents=[], auto_size=True)

初始化rom,如果auto_size为True,并且name所对应的存储的读写模式设置为只读,则还会设置该存储的depth属性为contents的长度。

  1. add_csr_bridge(name="csr", origin=None, register=False)

添加一个子模块,将所选定的系统总线转换为访问CSR的总线。

  1. add_cpu(name="vexriscv", variant="standard", reset_address=None, cfu=None)

用于添加哪种CPU。name为CPU的名称,variant为CPU的变种,如果要运行linux系统,可能需要指定variant为特定的值。

要查找所支持CPU的namevariant,在litex仓库的根目录下执行以下命令:

(base) ➜  litex git:(master) ✗ find . -name "core.py"                               
./litex/gen/sim/core.py
./litex/soc/cores/cpu/rocket/core.py
./litex/soc/cores/cpu/mor1kx/core.py
./litex/soc/cores/cpu/cortex_m3/core.py
./litex/soc/cores/cpu/blackparrot/core.py
./litex/soc/cores/cpu/marocchino/core.py
./litex/soc/cores/cpu/cv32e41p/core.py
./litex/soc/cores/cpu/lm32/core.py
./litex/soc/cores/cpu/femtorv/core.py
./litex/soc/cores/cpu/cortex_m1/core.py
./litex/soc/cores/cpu/vexriscv_smp/core.py
./litex/soc/cores/cpu/firev/core.py
./litex/soc/cores/cpu/picorv32/core.py
./litex/soc/cores/cpu/ibex/core.py
./litex/soc/cores/cpu/cv32e40p/core.py
./litex/soc/cores/cpu/eos_s3/core.py
./litex/soc/cores/cpu/serv/core.py
./litex/soc/cores/cpu/zynqmp/core.py
./litex/soc/cores/cpu/cva6/core.py
./litex/soc/cores/cpu/kianv/core.py
./litex/soc/cores/cpu/zynq7000/core.py
./litex/soc/cores/cpu/microwatt/core.py
./litex/soc/cores/cpu/openc906/core.py
./litex/soc/cores/cpu/gowin_emcu/core.py
./litex/soc/cores/cpu/naxriscv/core.py
./litex/soc/cores/cpu/cva5/core.py
./litex/soc/cores/cpu/vexriscv/core.py
./litex/soc/cores/cpu/neorv32/core.py
./litex/soc/cores/cpu/minerva/core.py

 

打开其中一个文件,以vexriscv为例,其中的部分内容如下:

class VexRiscv(CPU, AutoCSR):
    category             = "softcore"
    family               = "riscv"
    name                 = "vexriscv"
    human_name           = "VexRiscv"
    variants             = CPU_VARIANTS
    data_width           = 32
    endianness           = "little"
    gcc_triple           = CPU_GCC_TRIPLE_RISCV32
    linker_output_format = "elf32-littleriscv"
    nop                  = "nop"
    io_regions           = {0x8000_0000: 0x8000_0000} # Origin, Length

    # Memory Mapping.
    @property
    def mem_map(self):
        return {
            "rom":            0x0000_0000,
            "sram":           0x1000_0000,
            "main_ram":       0x4000_0000,
            "csr":            0xf000_0000,
            "vexriscv_debug": 0xf00f_0000,
        }
    ....
# Variants -----------------------------------------------------------------------------------------

CPU_VARIANTS = {
    "minimal":            "VexRiscv_Min",
    "minimal+debug":      "VexRiscv_MinDebug",
    "minimal+debug+hwbp": "VexRiscv_MinDebugHwBP",
    "lite":               "VexRiscv_Lite",
    "lite+debug":         "VexRiscv_LiteDebug",
    "lite+debug+hwbp":    "VexRiscv_LiteDebugHwBP",
    "standard":           "VexRiscv",
    "standard+debug":     "VexRiscv_Debug",
    "imac":               "VexRiscv_IMAC",
    "imac+debug":         "VexRiscv_IMACDebug",
    "full":               "VexRiscv_Full",
    "full+cfu":           "VexRiscv_FullCfu",
    "full+debug":         "VexRiscv_FullDebug",
    "full+cfu+debug":     "VexRiscv_FullCfuDebug",
    "linux":              "VexRiscv_Linux",
    "linux+debug":        "VexRiscv_LinuxDebug",
    "linux+no-dsp":       "VexRiscv_LinuxNoDspFmax",
    "secure":             "VexRiscv_Secure",
    "secure+debug":       "VexRiscv_SecureDebug",
}

可以发现其变种非常多,如果要支持Linux系统,需要进行指定。

说明一下cfu,即Custom Function Unit,自定义的加速单元,如果要添加加速单元,需要选择特殊的、带”cfu”的CPU变种,例如"full+cfu"

回归到add_cpu函数中,该函数还有一个reset_address=None的参数,该参数如果为None,则复位地地址会被设置为类成员mem_maprom的地址:

if reset_address is None:
    reset_address = self.mem_map["rom"]

 

add_timer(name=”timer0″)

添加计时器,并且添加该计时器产生的终端

  1. finalize(self)

进行收尾工作,比较复杂。

四、LiteXSoC

该类主要是添加了一系列方法,以添加LiteX提供的标准外设。

  1. add_identifier(name="identifier", identifier="LiteX SoC", with_build_time=True)

该方法主要是添加一个LiteX提供的模块Identifier,用于打印文字信息,参数name为模块名,identifier为所需要打印的信息,with_build_time表示是否需要在identifier后面加上时间辍信息。

  1. def add_uart(name="uart", uart_name="serial", baudrate=115200, fifo_depth=16):

顾名思义,该方法用于添加UART,name为模块名称,uart_name则是指定了UART的类型,并且会根据该类型获取platform中的引脚,支持以下类型的uart:

supported_uarts = [
            "crossover",
            "crossover+uartbone",
            "jtag_uart",
            "sim",
            "stub",
            "stream",
            "uartbone",
            "usb_acm",
            "serial(x)",
        ]

add_uartbone(name=”uartbone”, uart_name=”serial”, clk_freq=None, baudrate=115200, cd=”sys”)

不太清除其功能,应该是UART到Wishbone的转化器,可以通过串口来访问Wishbone总线;

  1. add_jtagbone(name="jtagbone", chain=1)

不太清楚其功能,应该是UART到Wishbone的转换器,可以通过JTAG来访问Wishbone总线

add_sdram(self, name="sdram", phy=None, module=None, origin=None, size=None,
         with_bist               = False,
         with_soc_interconnect   = True,
         l2_cache_size           = 8192,
         l2_cache_min_data_width = 128,
         l2_cache_reverse        = False,
         l2_cache_full_memory_we = True,
         **kwargs)

 

name:模块名称;

  • phy:SDRAM的物理接口,例如s7ddrphy.K7DDRPHY

  • module:具体的DRAM芯片型号所对应的模块,例如MT41J256M16

  • origin:主内存的起始地址,首先会使用类成员mem_mapmain_ram键所对应的值,如果没有就使用该值;

  • size:SDRAM的大小,计算如代码所示:

    # Compute/Check SDRAM size. module就是参数module
            sdram_size = 2**(module.geom_settings.bankbits +
                             module.geom_settings.rowbits +
                             module.geom_settings.colbits)*phy.settings.nranks*phy.settings.databits//8
            if size is not None:
                sdram_size = min(sdram_size, size) #从参数size和计算获得的sdram_size中选择较小值
  • with_bist:bist即为Built-inSelfTest,在片内在加一些逻辑,使得片外DDR SDRAM能脱离ATE机台测试,并且有输出响应来判断其测试结果;

  • with_soc_interconnect:如果为False,则DDR不会连接到SoC;

  • 其余参数用于控制在系统总线和DRAM之间创建缓存;

  • add_ethernet(self, name="ethmac", phy=None, phy_cd="eth", dynamic_ip=False, software_debug=False,
              data_width              = 8,
              nrxslots                = 2, rxslots_read_only  = True,
              ntxslots                = 2, txslots_write_only = False,
              with_timestamp          = False,
              with_timing_constraints = True):

    name:模块名

  • phy:一般只需要指定该参数,其类型可能为LiteEthPHYRGMII等;

  • phy_cd:是一个字符串,网卡的时钟域,默认为”eth”;该参数的作用是,当参数data_width不等于32时,会将MAC的时钟域——eth_tx/eth_rx,修改为phy_cd + _tx“phy_cd + "_rx";一般不会设置data_width参数,所以一般会执行该修改操作;

  • dynamic_ip:顾名思义,使用动态IP;

  • software_debug:不清楚作用,可能是启用UDP进行DEBUG的功能;

注意,从顶层应用来看,目前官方仅支持添加RGMII接口的以太网,不支持SGMII接口(VC707就是该接口);理论上可以自己定义一个SGMII的模块,从add_ethernet的方法可以看出,该模块必须为LiteEthPHYModel的子类,有需要的可以自己研究。

  1. add_etherbone(self, name="etherbone", phy=None, phy_cd="eth", data_width=8,
             mac_address             = 0x10e2d5000000,
             ip_address              = "192.168.1.50",
             arp_entries             = 1,
             udp_port                = 1234,
             buffer_depth            = 16,
             with_ip_broadcast       = True,
             with_timing_constraints = True,
             with_ethmac             = False):

    创建一个UDP以太网IP核,使得可以通过以太网来访问Wishbone总线。

  2. add_spi_flash(name="spiflash", mode="4x", clk_freq=None, module=None, phy=None, rate="1:1", software_debug=False, **kwargs)

添加一个SPI接口的FLASH:

  • name:模块名;

  • mode:只能是["1x", "4x"]

  • clk_freq:如果为None,则会自动使用sys_clk_freq的值;

  • module:芯片型号名称;

  • phy:SPI的物理接口,如果为None,则会自动创建;

  • rate:速度,支持1:1和1:2;assert rate in ["1:1", "1:2"]

  1. add_spi_sdcard(name="spisdcard", spi_clk_freq=400e3, with_tristate=False, software_debug=False)

添加一个SPI接口的SDCard:

  • name:该名称将用于从platfrom中请求相关引脚,即该字段需要和Platform中的定义相对应;

  • spi_clk_freq:SPImaster的工作时钟;

  • with_tristate:可选,是否使用三态门;

  1. add_sdcard(name="sdcard", sdcard_name="sdcard", mode="read+write", use_emulator=False, software_debug=False)

添加一个SDCard:

  • name:模块名称,可随意;

  • sdcard_name:用于从platform中请求相关的引脚,即该字段需要和Platform中的定义相对应;

  • mode:支持["read", "write", "read+write"]

  • use_emulator:如果是仿真的话,就不需要根据sdcard_name请求引脚了,会自动创建一个SD卡的仿真模块;

  1. add_sata(name="sata", phy=None, mode="read+write", with_identify=True)

添加SATA设备:

  • name:模块名,可随意

  • phy:物理接口模块;

  • mode:支持["read", "write", "read+write"]

  • with_identify:添加标识模块;

  • 12.

add_pcie(name="pcie", phy=None, ndmas=0, max_pending_requests=8, address_width=32, data_width=None,
          with_dma_buffering    = True, dma_buffering_depth=1024,
          with_dma_loopback     = True,
          with_dma_synchronizer = False,
          with_dma_monitor      = False,
          with_dma_status       = False,
          with_msi              = True, msi_type="msi", msi_width=32,
          with_ptm              = False,
  ):

name:模块名,可随意;

  • phy:例如S7PCIEPHY类,由LiteX提供;

  • ndmas:DMA的通道数量;

  • with_msi:消息中断机制;如果ndmas不为0,则该选项必须设置为True;

  1. add_video_colorbars(self, name="video_colorbars", phy=None, timings="800x600@60Hz", clock_domain="sys")
      
      
      add_video_terminal(self, name="video_terminal", phy=None, timings="800x600@60Hz", clock_domain="sys")
      
      
      add_video_framebuffer(self, name="video_framebuffer", phy=None, timings="800x600@60Hz", clock_domain="sys", format="rgb888"

    视频相关的功能,不太明白。

五、SoCCore

5.1 类成员

# Default register/interrupt/memory mappings (can be redefined by user)
    csr_map       = {}
    interrupt_map = {}
    mem_map       = {
        "rom":      0x00000000,
        "sram":     0x01000000,
        "main_ram": 0x40000000,
    }

其中mem_map在其父类SoC中就存在,只是为空。

5.2 构造器函数

def __init__(self, platform, clk_freq,
        # Bus parameters
        bus_standard             = "wishbone",
        bus_data_width           = 32,
        bus_address_width        = 32,
        bus_timeout              = 1e6,
        bus_bursting             = False,
        bus_interconnect         = "shared",

        # CPU parameters
        cpu_type                 = "vexriscv",
        cpu_reset_address        = None,
        cpu_variant              = None,
        cpu_cfu                  = None,

        # CFU parameters
        cfu_filename             = None,

        # ROM parameters
        integrated_rom_size      = 0,
        integrated_rom_mode      = "rx",
        integrated_rom_init      = [],

        # SRAM parameters
        integrated_sram_size     = 0x2000,
        integrated_sram_init     = [],

        # MAIN_RAM parameters
        integrated_main_ram_size = 0,
        integrated_main_ram_init = [],

        # CSR parameters
        csr_data_width           = 32,
        csr_address_width        = 14,
        csr_paging               = 0x800,
        csr_ordering             = "big",

        # Interrupt parameters
        irq_n_irqs               = 32,

        # Identifier parameters
        ident                    = "",
        ident_version            = False,

        # UART parameters
        with_uart                = True,
        uart_name                = "serial",
        uart_baudrate            = 115200,
        uart_fifo_depth          = 16,

        # Timer parameters
        with_timer               = True,
        timer_uptime             = False,

        # Controller parameters
        with_ctrl                = True,

        # JTAGBone
        with_jtagbone            = False,
        jtagbone_chain           = 1,

        # UARTBone
        with_uartbone            = False,

        # Others
        **kwargs):

参数的具体含义看上面的代码就基本上能清楚,在该构造器函数中,调用了其父类LiteXSoC的构造器函数。

5.3 类方法

  1. add_csr(csr_name, csr_id=None, use_loc_if_exists=False)

实际上是调用了SoCCSRHandler类的add方法;添加CSR寄存器:

  • csr_name:字符串;

  • csr_id:Int数字了;

  1. initialize_rom(data)

实际上是调用了父类SoCinit_rom方法:init_rom(name="rom", contents=data)

  1. def add_memory_region(self, name, origin, length, type="cached"):
         self.bus.add_region(name, SoCRegion(origin=origin, size=length,
             cached="cached" in type,
             linker="linker" in type)
         )

    可以看出,实际上是调用了SoCBusHandler类的add_region方法。

  2. def add_csr_region(self, name, origin, busword, obj):
             self.csr_regions[name] = SoCCSRRegion(origin, busword, obj)

    实际上是将父类SoC的参数csr_regions添加了一个元素;

请登录后发表评论

    没有回复内容