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

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

本章介绍如何添加一个AXI-Lite接口的Verilog IP到SoC中。

一、背景介绍

上一章,我们通过添加能被CPU控制的CSR间接实现了对简单的自定义Verilog模块的控制。现实是,大多数Veilog IP的接口通常是AXI、Avalon-MM等协议,因此本章还是以流水灯为例,介绍如何添加一个AXI-Lite IP,其他协议的Verilog IP的集成方法也大同小异。

首先,我们需要准备一个AXI-Lite接口的IP,这里我使用了Vivado的封装IP的功能,将上一章的流水灯模块封装为AXI4-Lite接口,如下图所示:

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

可以看出,该IP一共包含3个Verilog文件,其中只有my_leds.v是手动写的流水灯模块,而另外两个文件是Vivado自动生成的,通过在led_ip_v1_0_S00_AXI.v文件中,添加my_leds.v模块,然后将寄存器连接至该端口即可,同时在led_ip_v1_0_S00_AXI.vled_ip_v1_0.v中引出my_leds.v的led输出引脚,如下图所示:

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

图 在led_ip_v1_0_S00_AXI.v文件中,添加my_leds.v模块

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

图 在led_ip_v1_0_S00_AXI.v文件中,引出leds端口

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

图 在led_ip_v1_0.v文件中,引出leds端口

修改完成后,我们就制作好了一个简单的AXI4-Lite的IP,将这三个文件拷贝至target文件夹下:

cp ./edit_led_ip_v1_0.srcs/sources_1/imports/targets/my_leds.v ./led_ip_1.0/hdl/led_ip_v1_0_S00_AXI.v ./led_ip_1.0/hdl/led_ip_v1_0.v ~/wrk/litex-boards/litex_boards/targets

二、修改Platform文件

和上一次一样,需要在Platform文件中添加这三个Verilog文件,如下列代码中的add_source

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

class Platform(XilinxPlatform):
    default_clk_name   = "clk200"
    default_clk_period = 1e9/200e6

    def __init__(self):
        XilinxPlatform.__init__(self, "xc7z100ffg900-2", _io, _connectors, toolchain="vivado")
        self.add_extension(_ps7_io)
        self.add_extension(_usb_uart_pmod_io)
        self.add_source("./my_leds.v")
        self.add_source("./led_ip_v1_0_S00_AXI.v")
        self.add_source("./led_ip_v1_0.v")

    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)
        self.add_period_constraint(self.lookup_request("eth_clocks:rx", 0, loose=True), 1e9/125e6)
        self.add_period_constraint(self.lookup_request("eth_clocks:tx", 0, loose=True), 1e9/125e6)

三、封装AXI-Lite IP的Python对象

我们打开Verilog代码的顶层文件的端口定义,如下代码,可以发现,除了信号之外,还多了两个parameter,在使用Python封装带参数的Verilog代码时,需要在其参数名称前面加上p_

module led_ip_v1_0 #
	(
		// Users to add parameters here

		// User parameters ends
		// Do not modify the parameters beyond this line


		// Parameters of Axi Slave Bus Interface S00_AXI
		parameter integer C_S00_AXI_DATA_WIDTH	= 32,
		parameter integer C_S00_AXI_ADDR_WIDTH	= 4
	)
	(
		// Users to add ports here
        output wire [3:0] leds,
		// User ports ends
		// Do not modify the ports beyond this line


		// Ports of Axi Slave Bus Interface S00_AXI
		input wire  s00_axi_aclk,
		input wire  s00_axi_aresetn,
		input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
		input wire [2 : 0] s00_axi_awprot,
		input wire  s00_axi_awvalid,
		output wire  s00_axi_awready,
		input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
		input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
		input wire  s00_axi_wvalid,
		output wire  s00_axi_wready,
		output wire [1 : 0] s00_axi_bresp,
		output wire  s00_axi_bvalid,
		input wire  s00_axi_bready,
		input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
		input wire [2 : 0] s00_axi_arprot,
		input wire  s00_axi_arvalid,
		output wire  s00_axi_arready,
		output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
		output wire [1 : 0] s00_axi_rresp,
		output wire  s00_axi_rvalid,
		input wire  s00_axi_rready
	);

 

litex.soc.interconnect.axi目录下,我们能找到LiteX对各种总线协议的定义和支持,如下图所示:

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

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

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

我们这里使用的是AXI-Lite协议,为了和上面的Verilog代码对应,创建一个新的Python封装类,代码如下:

class my_axi_leds(LiteXModule):
    def __init__(self, pads, clk=None, rst=None):
        self.leds = Signal(len(pads))
        self.bus  = bus = axi_lite.AXILiteInterface(data_width=32, address_width=32)

        self.specials += [
            Instance("led_ip_v1_0",
                        # i_s00_axi_aclk= bus.clock_domain.clk,
                        # i_s00_axi_aresetn = ~bus.clock_domain.rst,
                        i_s00_axi_aclk= clk,
                        i_s00_axi_aresetn = ~rst,
                        o_leds = self.leds,
                        i_s00_axi_awaddr = bus.aw.addr,
                        i_s00_axi_awprot = bus.aw.prot,
                        i_s00_axi_awvalid = bus.aw.valid,
                        o_s00_axi_awready = bus.aw.ready,
                        i_s00_axi_wdata = bus.w.data,
                        i_s00_axi_wstrb = bus.w.strb,
                        i_s00_axi_wvalid = bus.w.valid,
                        o_s00_axi_wready = bus.w.ready,
                        o_s00_axi_bresp = bus.b.resp,
                        o_s00_axi_bvalid = bus.b.valid,
                        i_s00_axi_bready = bus.b.ready,
                        i_s00_axi_araddr = bus.ar.addr,
                        i_s00_axi_arprot = bus.ar.prot,
                        i_s00_axi_arvalid = bus.ar.valid,
                        o_s00_axi_arready = bus.ar.ready,
                        o_s00_axi_rdata = bus.r.data,
                        o_s00_axi_rresp = bus.r.resp,
                        o_s00_axi_rvalid = bus.r.valid,
                        i_s00_axi_rready = bus.r.ready,
                        p_C_S00_AXI_DATA_WIDTH = 32,
                        p_C_S00_AXI_ADDR_WIDTH = 32,
                    )
        ]
        self.comb += pads.eq(self.leds)

主要注意点:

  • 参数要以p_开头;

  • Instance的第一个参数,即字符串,要与Verilog模块的顶层模块名对应;

然后在Target文件中,使用封装好的Python IP:

if with_led_chaser:
            # self.submodules.leds = LedChaser(
            #     pads         = platform.request_all("user_led"),
            #     sys_clk_freq = sys_clk_freq)
            
            # for CSR test
            # self.submodules.leds = my_leds(pads=platform.request_all("user_led"), clk=self.crg.cd_sys.clk, rst=self.crg.cd_sys.rst)

            # for AXI-lite test
            self.my_leds = my_axi_leds(pads=platform.request_all("user_led"), clk=self.crg.cd_sys.clk, rst=self.crg.cd_sys.rst)
            self.bus.add_slave(name="my_axi_leds", slave=self.my_leds.bus, region=SoCRegion(origin=0x2000_0000,size=16))

直接调用SoC的bus的add_slave即可,会自动完成总线的连接和适配,这里我们指定了该IP的起始访问地址为0x2000_0000,可访问的空间为16个字节,即4个32-bit寄存器。

四、上板测试

同样的,与上一章一样,启动Linux后,使用devmem工具直接访问内存地址,实现对流水灯的控制,如下图,首先是访问第一个32bit地址,即对应Verilog IP中的slv_reg0,该寄存器连接至流水灯模块的mode端口,将模式设置为1,即流水灯受CPU控制,然后分别向第二个32bit地址,写入1,3,7,分别控制亮1~3个灯。

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

图 修改流水灯模式,亮1~3个灯

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

图 AXI-Lite IP亮3个灯

 

请登录后发表评论

    没有回复内容