本帖最后由 FPGA课程 于 2024-9-28 13:42 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述在前文中我们学习了AXI总线协议,而且通过VIVADO自定义了AXI-LITE总线协议的IP CORE,并且实现了寄存器的读写。 那么在实际的应用中,如果我们ARM的IO不够用了,除了在前文中使用官方自带的AXI-GPIO,我们自己也可以定义AXI-GPIO IP CORE。 本文后以几篇文章都会展示AXI-LITE 自定义IP的使用,通过加强的训练,让读者掌握这种总线接口的使用。并且后面我们自定的AXI-FDMA可以完成高速的DMA数据传输。 本文实验目的: 1:通过前文的学习,把掌握的自定义AXI-LITE-SLAVE寄存器读写方法,用于引出扩展PL的IO 2:通过VITIS-SDK实现对自定义IP中寄存器的读写访问,以此实现PL IO的控制。 2系统框图
3创建IP
3.1利用模板创建AXI-Lite IP1:打开VIVADO软件,新建一个工程。 2:单击ToolsàCreate and Package NEW IP。 3:单击Next,选择Create a new AXI4 peripheral,单击Next。
4:输入要创建的IP名字,此处命名为GPIO_LITE_ML,选择保存路径,单击Next。
5:NameàS00_AXI; Interface Type(接口类型)àLite; Data Width(Bits)(数据位宽)à32位; Number of Registers(寄存器数量)à4 ;单击next。
6:选择Edit IP,点击Finish按钮。软件自动打开一个编辑IP的工程,即edit_GPIO_LITE_ML_V1_0.xpr工程。
3.2修改IP源码1:打开的edit_GPIO_LITE_ML_V1_0.xpr工程界面如下。
查看生成IP的文件夹
2:现在生成的IP需要进行修改才能满足我们使用需求。选中Project Manager,双击GPIO_LITE_ML_v1_0_S00_AXI_inst,做如下更改。 修改1:
修改2:
将slv_reg0的值赋值给了用户输出逻辑,当我们向slv_reg0写入数据的时候,也就相当于向GPIO_LED赋值。 更改后的文件如下所示。 - `timescale 1 ns / 1 ps
- module GPIO_LITE_ML_v1_0_S00_AXI #
- (
- // Users to add parameters here
- // User parameters ends
- // Do not modify the parameters beyond this line
- // Width of S_AXI data bus
- parameter integer C_S_AXI_DATA_WIDTH = 32,
- // Width of S_AXI address bus
- parameter integer C_S_AXI_ADDR_WIDTH = 4
- )
- (
- // Users to add ports here
- output wire [3:0]GPIO_LED,
- // User ports ends
- // Do not modify the ports beyond this line
- // Global Clock Signal
- input wire S_AXI_ACLK,
- // Global Reset Signal. This Signal is Active LOW
- input wire S_AXI_ARESETN,
- // Write address (issued by master, acceped by Slave)
- input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
- // Write channel Protection type. This signal indicates the
- // privilege and security level of the transaction, and whether
- // the transaction is a data access or an instruction access.
- input wire [2 : 0] S_AXI_AWPROT,
- // Write address valid. This signal indicates that the master signaling
- // valid write address and control information.
- input wire S_AXI_AWVALID,
- // Write address ready. This signal indicates that the slave is ready
- // to accept an address and associated control signals.
- output wire S_AXI_AWREADY,
- // Write data (issued by master, acceped by Slave)
- input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
- // Write strobes. This signal indicates which byte lanes hold
- // valid data. There is one write strobe bit for each eight
- // bits of the write data bus.
- input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
- // Write valid. This signal indicates that valid write
- // data and strobes are available.
- input wire S_AXI_WVALID,
- // Write ready. This signal indicates that the slave
- // can accept the write data.
- output wire S_AXI_WREADY,
- // Write response. This signal indicates the status
- // of the write transaction.
- output wire [1 : 0] S_AXI_BRESP,
- // Write response valid. This signal indicates that the channel
- // is signaling a valid write response.
- output wire S_AXI_BVALID,
- // Response ready. This signal indicates that the master
- // can accept a write response.
- input wire S_AXI_BREADY,
- // Read address (issued by master, acceped by Slave)
- input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
- // Protection type. This signal indicates the privilege
- // and security level of the transaction, and whether the
- // transaction is a data access or an instruction access.
- input wire [2 : 0] S_AXI_ARPROT,
- // Read address valid. This signal indicates that the channel
- // is signaling valid read address and control information.
- input wire S_AXI_ARVALID,
- // Read address ready. This signal indicates that the slave is
- // ready to accept an address and associated control signals.
- output wire S_AXI_ARREADY,
- // Read data (issued by slave)
- output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
- // Read response. This signal indicates the status of the
- // read transfer.
- output wire [1 : 0] S_AXI_RRESP,
- // Read valid. This signal indicates that the channel is
- // signaling the required read data.
- output wire S_AXI_RVALID,
- // Read ready. This signal indicates that the master can
- // accept the read data and response information.
- input wire S_AXI_RREADY
- );
- // AXI4LITE signals
- reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
- reg axi_awready;
- reg axi_wready;
- reg [1 : 0] axi_bresp;
- reg axi_bvalid;
- reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
- reg axi_arready;
- reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
- reg [1 : 0] axi_rresp;
- reg axi_rvalid;
- // Example-specific design signals
- // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
- // ADDR_LSB is used for addressing 32/64 bit registers/memories
- // ADDR_LSB = 2 for 32 bits (n downto 2)
- // ADDR_LSB = 3 for 64 bits (n downto 3)
- localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
- localparam integer OPT_MEM_ADDR_BITS = 1;
- //----------------------------------------------
- //-- Signals for user logic register space example
- //------------------------------------------------
- //-- Number of Slave Registers 4
- reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0;
- reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1;
- reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2;
- reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3;
- wire slv_reg_rden;
- wire slv_reg_wren;
- reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
- integer byte_index;
- reg aw_en;
- // I/O Connections assignments
- assign S_AXI_AWREADY = axi_awready;
- assign S_AXI_WREADY = axi_wready;
- assign S_AXI_BRESP = axi_bresp;
- assign S_AXI_BVALID = axi_bvalid;
- assign S_AXI_ARREADY = axi_arready;
- assign S_AXI_RDATA = axi_rdata;
- assign S_AXI_RRESP = axi_rresp;
- assign S_AXI_RVALID = axi_rvalid;
- // Implement axi_awready generation
- // axi_awready is asserted for one S_AXI_ACLK clock cycle when both
- // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
- // de-asserted when reset is low.
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_awready <= 1'b0;
- aw_en <= 1'b1;
- end
- else
- begin
- if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
- begin
- // slave is ready to accept write address when
- // there is a valid write address and write data
- // on the write address and data bus. This design
- // expects no outstanding transactions.
- axi_awready <= 1'b1;
- aw_en <= 1'b0;
- end
- else if (S_AXI_BREADY && axi_bvalid)
- begin
- aw_en <= 1'b1;
- axi_awready <= 1'b0;
- end
- else
- begin
- axi_awready <= 1'b0;
- end
- end
- end
- // Implement axi_awaddr latching
- // This process is used to latch the address when both
- // S_AXI_AWVALID and S_AXI_WVALID are valid.
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_awaddr <= 0;
- end
- else
- begin
- if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
- begin
- // Write Address latching
- axi_awaddr <= S_AXI_AWADDR;
- end
- end
- end
- // Implement axi_wready generation
- // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
- // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
- // de-asserted when reset is low.
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_wready <= 1'b0;
- end
- else
- begin
- if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
- begin
- // slave is ready to accept write data when
- // there is a valid write address and write data
- // on the write address and data bus. This design
- // expects no outstanding transactions.
- axi_wready <= 1'b1;
- end
- else
- begin
- axi_wready <= 1'b0;
- end
- end
- end
- // Implement memory mapped register select and write logic generation
- // The write data is accepted and written to memory mapped registers when
- // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
- // select byte enables of slave registers while writing.
- // These registers are cleared when reset (active low) is applied.
- // Slave register write enable is asserted when valid address and data are available
- // and the slave is ready to accept the write address and write data.
- assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- slv_reg0 <= 0;
- slv_reg1 <= 0;
- slv_reg2 <= 0;
- slv_reg3 <= 0;
- end
- else begin
- if (slv_reg_wren)
- begin
- case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
- 2'h0:
- for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
- if ( S_AXI_WSTRB[byte_index] == 1 ) begin
- // Respective byte enables are asserted as per write strobes
- // Slave register 0
- slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
- end
- 2'h1:
- for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
- if ( S_AXI_WSTRB[byte_index] == 1 ) begin
- // Respective byte enables are asserted as per write strobes
- // Slave register 1
- slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
- end
- 2'h2:
- for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
- if ( S_AXI_WSTRB[byte_index] == 1 ) begin
- // Respective byte enables are asserted as per write strobes
- // Slave register 2
- slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
- end
- 2'h3:
- for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
- if ( S_AXI_WSTRB[byte_index] == 1 ) begin
- // Respective byte enables are asserted as per write strobes
- // Slave register 3
- slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
- end
- default : begin
- slv_reg0 <= slv_reg0;
- slv_reg1 <= slv_reg1;
- slv_reg2 <= slv_reg2;
- slv_reg3 <= slv_reg3;
- end
- endcase
- end
- end
- end
- // Implement write response logic generation
- // The write response and response valid signals are asserted by the slave
- // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
- // This marks the acceptance of address and indicates the status of
- // write transaction.
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_bvalid <= 0;
- axi_bresp <= 2'b0;
- end
- else
- begin
- if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
- begin
- // indicates a valid write response is available
- axi_bvalid <= 1'b1;
- axi_bresp <= 2'b0; // 'OKAY' response
- end // work error responses in future
- else
- begin
- if (S_AXI_BREADY && axi_bvalid)
- //check if bready is asserted while bvalid is high)
- //(there is a possibility that bready is always asserted high)
- begin
- axi_bvalid <= 1'b0;
- end
- end
- end
- end
- // Implement axi_arready generation
- // axi_arready is asserted for one S_AXI_ACLK clock cycle when
- // S_AXI_ARVALID is asserted. axi_awready is
- // de-asserted when reset (active low) is asserted.
- // The read address is also latched when S_AXI_ARVALID is
- // asserted. axi_araddr is reset to zero on reset assertion.
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_arready <= 1'b0;
- axi_araddr <= 32'b0;
- end
- else
- begin
- if (~axi_arready && S_AXI_ARVALID)
- begin
- // indicates that the slave has acceped the valid read address
- axi_arready <= 1'b1;
- // Read address latching
- axi_araddr <= S_AXI_ARADDR;
- end
- else
- begin
- axi_arready <= 1'b0;
- end
- end
- end
- // Implement axi_arvalid generation
- // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
- // S_AXI_ARVALID and axi_arready are asserted. The slave registers
- // data are available on the axi_rdata bus at this instance. The
- // assertion of axi_rvalid marks the validity of read data on the
- // bus and axi_rresp indicates the status of read transaction.axi_rvalid
- // is deasserted on reset (active low). axi_rresp and axi_rdata are
- // cleared to zero on reset (active low).
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_rvalid <= 0;
- axi_rresp <= 0;
- end
- else
- begin
- if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
- begin
- // Valid read data is available at the read data bus
- axi_rvalid <= 1'b1;
- axi_rresp <= 2'b0; // 'OKAY' response
- end
- else if (axi_rvalid && S_AXI_RREADY)
- begin
- // Read data is accepted by the master
- axi_rvalid <= 1'b0;
- end
- end
- end
- // Implement memory mapped register select and read logic generation
- // Slave register read enable is asserted when valid address is available
- // and the slave is ready to accept the read address.
- assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
- always @(*)
- begin
- // Address decoding for reading registers
- case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
- 2'h0 : reg_data_out <= slv_reg0;
- 2'h1 : reg_data_out <= slv_reg1;
- 2'h2 : reg_data_out <= slv_reg2;
- 2'h3 : reg_data_out <= slv_reg3;
- default : reg_data_out <= 0;
- endcase
- end
- // Output register or memory read data
- always @( posedge S_AXI_ACLK )
- begin
- if ( S_AXI_ARESETN == 1'b0 )
- begin
- axi_rdata <= 0;
- end
- else
- begin
- // When there is a valid read address (S_AXI_ARVALID) with
- // acceptance of read address by the slave (axi_arready),
- // output the read dada
- if (slv_reg_rden)
- begin
- axi_rdata <= reg_data_out; // register read data
- end
- end
- end
- // Add user logic here
- assign GPIO_LED[3:0] = slv_reg0[3:0];
- // User logic ends
- endmodule
复制代码
3:双击GPIO_LITE_ML_v1_0文件,做如下修改。 修改1:
修改2:
修改后的文件如下。
- `timescale 1 ns / 1 ps
- module GPIO_LITE_ML_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]GPIO_LED,
- // 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
- );
- // Instantiation of Axi Bus Interface S00_AXI
- GPIO_LITE_ML_v1_0_S00_AXI # (
- .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
- .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
- ) GPIO_LITE_ML_v1_0_S00_AXI_inst (
- .S_AXI_ACLK(s00_axi_aclk),
- .S_AXI_ARESETN(s00_axi_aresetn),
- .S_AXI_AWADDR(s00_axi_awaddr),
- .S_AXI_AWPROT(s00_axi_awprot),
- .S_AXI_AWVALID(s00_axi_awvalid),
- .S_AXI_AWREADY(s00_axi_awready),
- .S_AXI_WDATA(s00_axi_wdata),
- .S_AXI_WSTRB(s00_axi_wstrb),
- .S_AXI_WVALID(s00_axi_wvalid),
- .S_AXI_WREADY(s00_axi_wready),
- .S_AXI_BRESP(s00_axi_bresp),
- .S_AXI_BVALID(s00_axi_bvalid),
- .S_AXI_BREADY(s00_axi_bready),
- .S_AXI_ARADDR(s00_axi_araddr),
- .S_AXI_ARPROT(s00_axi_arprot),
- .S_AXI_ARVALID(s00_axi_arvalid),
- .S_AXI_ARREADY(s00_axi_arready),
- .S_AXI_RDATA(s00_axi_rdata),
- .S_AXI_RRESP(s00_axi_rresp),
- .S_AXI_RVALID(s00_axi_rvalid),
- .S_AXI_RREADY(s00_axi_rready),
- .GPIO_LED(GPIO_LED)
- );
- // Add user logic here
- // User logic ends
- endmodule
复制代码
4:进一步修改。去掉“_v1_0”。
- GPIO_LITE_ML_v1_0_S00_AXI.v中:
module GPIO_LITE_ML_v1_0_S00_AXI #修改为à module GPIO_LITE_ML_S00_AXI #
2、GPIO_LITE_ML_v1_0.v中: module GPIO_LITE_ML_v1_0 # 修改为à module GPIO_LITE_ML # GPIO_LITE_ML_v1_0_S00_AXI # 修改为à GPIO_LITE_ML_S00_AXI # GPIO_LITE_ML_v1_0_S00_AXI_inst 修改为à GPIO_LITE_ML_S00_AXI_inst 5:修改后,保存。出现如下界面,选择Automatically pick new top module。
更新后界面
6:重新封装。选择ToolsàCreat and Pakage New IP,单击Next。 7:选择Package your current project,单击Next。
路径选择原来IP所在的位置,覆盖原来的文件。
8:选择Overwrite。
9:选择Package IPàRewiew and Package àRe-Package IP
自定义IP生成完毕。 4搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 4.1SOC系统工程
1:中断设置本实验可以不设置中断
2:设置GP Master接口
3:设置复位输出
4:设置PL时钟
5:添加自定义IP设置自定义IP路径,并且添加IP
4.2设置AXI外设地址分配只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
4.3编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件 4:生成Bit文件。 5:导出到硬件: FileàExport HardwareàInclude bitstream 6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
5搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 5.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 5.2创建APP工程
6程序分析
XGpio_axi_WriteReg()函数实现的是向AXI的寄存器中写入数据,它的三个参数分别为基地址,偏移量和数据。需要注意的是此处的偏移量,AXI的相邻寄存器偏移量相差4个字节,默认slv_reg0的偏移量是0,因此,可以推导出slv_reg1,slv_reg2的偏移量分别为4和8,本课中,我们只用到了slv_reg0,所以偏移量为0。
7方案演示
7.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
7.2实验结果
|