海门市建设局网站,西湖区建设局网站,营销网站建设报价,网页美工设计参考文献以下内容仅记录本人工作中发现的问题以及解决方法#xff0c;仅供参考。 文章记录了作者在基于zynq平台PL与PS的数据交互开发过程中#xff0c;总结的AXI4_LITE通信协议开发经验#xff0c;并在文章中详细介绍了VIVADO软件中AXI4_LITE IP核的生成、测试版本及正式版本工程BD…以下内容仅记录本人工作中发现的问题以及解决方法仅供参考。文章记录了作者在基于zynq平台PL与PS的数据交互开发过程中总结的AXI4_LITE通信协议开发经验并在文章中详细介绍了VIVADO软件中AXI4_LITE IP核的生成、测试版本及正式版本工程BDBLOCK DESIGN的搭建、从机PL端AXI4_LITE IP核的协议层代码开发、主机PS端应用层的代码开发、仿真测试工程以及上板工程的完整代码及测试结果。本文着重介绍PL与PS的AXI4-Lite协议交互AXI4-Lite协议的PL端应用开发详见另一篇笔记vivado PL端串口通信协议及AXI_Uartlite 2.0IP驱动开发https://blog.csdn.net/weixin_46168087/article/details/155612353?spm1001.2014.3001.5502一、AXI4_LITE协议概述AXI协议官方下载链接https://developer.arm.com/documentation/ihi0022/latestAXI4-Lite 是 AXI4 协议的轻量级子集专为低速、低带宽的控制类交互设计如寄存器读写、外设配置是 Zynq-7000中 PS与 PL通信的核心总线之一也是嵌入式硬件开发中最常用的 AXI 子协议。协议适用场景核心差异AXI4-Lite低速控制寄存器读写无突发、固定 32/64 位、轻量AXI4高速数据传输如 DDR支持突发最大 256 拍、缓存AXI-Stream流式数据如视频 / 音频无地址、连续数据流、低延迟1.AXI4-LITE协议接口AXI4-Lite 包括全局时钟复位和 5 个独立通道写地址/写数据/写响应/读地址/读数据通道主设备如 Zynq PS发起请求从设备如 PL 自定义 IP响应AXI4-Lite协议接口如下序号类型命名位宽bit输入/输出定义1全局信号ACLK1input时钟2ARESET1input复位低有效3写地址通道AWVALID1input写地址有效位高有效4AWREADY1output从机写地址通道准备就绪信号高有效5AWDATA4input写地址7写数据通道WVALID1input写数据有效位高有效8WREADY1output从机写数据通道准备就绪信号高有效9WDATA32input写数据10WSTRB4input写数据选择bit[0]1data[7:0]bit[1]1data[15:8]以此类推默认全高11写响应通道BVALID1output写入响应有效位高有效12BREADY1input主机接收响应通道准备就绪信号高有效13BRESP2output写响应状态(一般不用)14读地址通道ARVALID1input读地址有效位高有效15ARREADY1output从机读地址通道准备就绪信号高有效16ARDATA4input读地址18读数据通道RVALID1output读数据有效位高有效19RREADY1input主机读数据通道准备就绪信号高有效20RDATA32output读数据21RRESP2output读响应状态00 成功一般不用)2.AXI4-Lite 写数据工作流程写入、读取之间存在通道依赖关系。读写流程示意图中的单向箭头表示可在箭头起点信号前或后触发的信号。双向箭头表示必须在箭头起点信号触发后才能触发的信号。根据写数据流程图可知写数据只与三个通道有关写地址通道/写数据通道/写响应通道简单概括一下写数据的流程为step1写入数据WDATA、写入地址AWDATA、写数据有效位拉高WVALID、写地址有效位拉高AWVALIDstep2等待从机写数据通道准备就绪WREADY、等待从机写地址通道准备就绪AWREADYstep3等待写入响应有效BVALID、主机接收响应通道准备就绪信号拉高BREADY、返回step1循环3.AXI4-Lite 读数据 工作流程根据读数据流程图可知读数据只与两个通道有关读地址通道/读数据通道简单概括一下读数据的流程为step1写入读地址ARDATA、读地址有效位拉高WVALIDstep2等待从机读地址通道准备就绪ARREADYstep3主机读数据通道准备就绪信号拉高RREADYstep4等待从机读数据有效位拉高RVALID、提取数据RDATA、返回step1循环二、AXI4_LITE IP核1.AXI4-Lite IP核的生成打开vivado软件点击Tools下拉选项点击Create and Package New IP。点击next。这里选择IP存放的路径以及更改自定义IP的命名为axi4_lite_slave。接口模式选择从机模式寄存器数量先随便写4后面再改。选择编辑IP进行下一步根据实际应用来更改IP。2.AXI4-Lite IP核的应用在上一章节点击完成后可以看到IP编辑界面已经打开IP已经生成观察代码可以发现读数据状态机的内容即为重复4个寄存器的读写若前面设置寄存器数量为128个则状态机会重复128次简化状态机以及根据自己的需求定义读写数据寄存器的个数定义AXI4_lite协议的基地址将需要读出的值提出来需要写入的值发出去就是本节IP核的应用中改造这段代码的目的。本实验自定义PL端从地址0x43c00000中读取来自PS端的6个32位的数data_out0~data_out5PL端向PS端给地址0x43c00000写入4个32位的数data_in0~data_in3。更改后的axi4_lite_slave_v1_0_S00_AXI.v文件module axi4_lite_slave_v1_0_S00_AXI #( parameter integer C_S_AXI_DATA_WIDTH 32, parameter integer C_S_AXI_ADDR_WIDTH 32, parameter integer S_AXI_RD_DATA_DEPTH 6, parameter integer S_AXI_WR_DATA_DEPTH 4, parameter integer S_AXI_RDADDR_BASE 32h43c00000 )( input wire S_AXI_ACLK , input wire S_AXI_ARESETN , input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR , input wire [2 : 0] S_AXI_AWPROT , input wire S_AXI_AWVALID , output wire S_AXI_AWREADY , input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA , input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB , input wire S_AXI_WVALID , output wire S_AXI_WREADY , output wire [1 : 0] S_AXI_BRESP , output wire S_AXI_BVALID , input wire S_AXI_BREADY , input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR , input wire [2 : 0] S_AXI_ARPROT , input wire S_AXI_ARVALID , output wire S_AXI_ARREADY , output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA , output wire [1 : 0] S_AXI_RRESP , output wire S_AXI_RVALID , input wire S_AXI_RREADY , input wire [31:0] data_in0 , input wire [31:0] data_in1 , input wire [31:0] data_in2 , input wire [31:0] data_in3 , output wire [31:0] data_out0 , output wire [31:0] data_out1 , output wire [31:0] data_out2 , output wire [31:0] data_out3 , output wire [31:0] data_out4 , output wire [31:0] data_out5 ); function integer clogb2 (input integer bit_depth); begin for(clogb20; bit_depth0; clogb2clogb21) bit_depth bit_depth 1; end endfunction localparam integer ADDR_LSB (C_S_AXI_DATA_WIDTH/32) 1; localparam integer ADDR_HSB clogb2(S_AXI_RD_DATA_DEPTH-1)ADDR_LSB; 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; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg [0:S_AXI_RD_DATA_DEPTH-1]; wire slv_reg_rden; wire slv_reg_wren; integer byte_index; reg aw_en; wire [7:0] write_index; wire [7:0] read_index ; assign write_index axi_awaddr[ADDR_HSB:ADDR_LSB]; assign read_index axi_araddr[ADDR_HSB:ADDR_LSB]; assign slv_reg_wren axi_wready S_AXI_WVALID axi_awready S_AXI_AWVALID; assign slv_reg_rden axi_arready S_AXI_ARVALID ~axi_rvalid; 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; assign data_out0 slv_reg[0]; assign data_out1 slv_reg[1]; assign data_out2 slv_reg[2]; assign data_out3 slv_reg[3]; assign data_out4 slv_reg[4]; assign data_out5 slv_reg[5]; always ( posedge S_AXI_ACLK )begin if( S_AXI_ARESETN 1b0 )begin axi_awready 1b0; aw_en 1b1; end else begin if (~axi_awready S_AXI_AWVALID S_AXI_WVALID aw_en)begin axi_awready 1b1; aw_en 1b0; end else if (S_AXI_BREADY axi_bvalid) begin aw_en 1b1; axi_awready 1b0; end else begin axi_awready 1b0; end end end always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin axi_awaddr 0; end else begin if (~axi_awready S_AXI_AWVALID S_AXI_WVALID aw_en)begin axi_awaddr S_AXI_AWADDR; end end end always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin axi_wready 1b0; end else begin if (~axi_wready S_AXI_WVALID S_AXI_AWVALID aw_en )begin axi_wready 1b1; end else begin axi_wready 1b0; end end end reg [7:0] slv_reg_cnt; always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin for(slv_reg_cnt0;slv_reg_cntS_AXI_RD_DATA_DEPTH;slv_reg_cnt slv_reg_cnt1) slv_reg[slv_reg_cnt] 0; end else begin if (slv_reg_wren)begin for ( byte_index 0; byte_index (C_S_AXI_DATA_WIDTH/8)-1; byte_index byte_index1 ) if ( S_AXI_WSTRB[byte_index] 1 ) begin slv_reg[write_index][(byte_index*8) : 8] S_AXI_WDATA[(byte_index*8) : 8]; end end end end always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin axi_bvalid 0; axi_bresp 2b0; end else begin if (axi_awready S_AXI_AWVALID ~axi_bvalid axi_wready S_AXI_WVALID)begin axi_bvalid 1b1; axi_bresp 2b0; end else begin if (S_AXI_BREADY axi_bvalid) begin axi_bvalid 1b0; end end end end always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin axi_arready 1b0; axi_araddr 32b0; end else begin if (~axi_arready S_AXI_ARVALID)begin axi_arready 1b1; axi_araddr S_AXI_ARADDR; end else begin axi_arready 1b0; end end end always ( posedge S_AXI_ACLK )begin if ( S_AXI_ARESETN 1b0 )begin axi_rvalid 0; axi_rresp 0; end else begin if (axi_arready S_AXI_ARVALID ~axi_rvalid)begin axi_rvalid 1b1; axi_rresp 2b0; end else if (axi_rvalid S_AXI_RREADY)begin axi_rvalid 1b0; end end end always(posedge S_AXI_ACLK or negedge S_AXI_ARESETN)begin if (!S_AXI_ARESETN)begin axi_rdata d0; end else begin if (slv_reg_rden)begin case(axi_araddr) 32h43c00000 :begin axi_rdata data_in0 ;end 32h43c00004 :begin axi_rdata data_in1 ;end 32h43c00008 :begin axi_rdata data_in2 ;end 32h43c0000C :begin axi_rdata data_in3 ;end default :begin axi_rdata axi_rdata; end endcase end end end endmodule更改后的axi4_lite_slave_v1_0.v文件timescale 1 ns / 1 ps module axi4_lite_slave_v1_0 # ( parameter integer C_S_AXI_DATA_WIDTH 32, parameter integer C_S_AXI_ADDR_WIDTH 32, parameter integer S_AXI_RD_DATA_DEPTH 6, parameter integer S_AXI_WR_DATA_DEPTH 4, parameter integer S_AXI_RDADDR_BASE 32h43c00000 ) ( input wire s00_axi_aclk, input wire s00_axi_aresetn, input wire [C_S_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_S_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata, input wire [(C_S_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_S_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_S_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, input wire [31:0] data_in0 , input wire [31:0] data_in1 , input wire [31:0] data_in2 , input wire [31:0] data_in3 , output wire [31:0] data_out0 , output wire [31:0] data_out1 , output wire [31:0] data_out2 , output wire [31:0] data_out3 , output wire [31:0] data_out4 , output wire [31:0] data_out5 ); // Instantiation of Axi Bus Interface S00_AXI axi4_lite_slave_v1_0_S00_AXI # ( .C_S_AXI_DATA_WIDTH (C_S_AXI_DATA_WIDTH ), .C_S_AXI_ADDR_WIDTH (C_S_AXI_ADDR_WIDTH ), .S_AXI_RD_DATA_DEPTH (S_AXI_RD_DATA_DEPTH), .S_AXI_WR_DATA_DEPTH (S_AXI_WR_DATA_DEPTH), .S_AXI_RDADDR_BASE (S_AXI_RDADDR_BASE ) ) axi4_lite_slave_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), .data_in0 (data_in0 ), .data_in1 (data_in1 ), .data_in2 (data_in2 ), .data_in3 (data_in3 ), .data_out0 (data_out0 ), .data_out1 (data_out1 ), .data_out2 (data_out2 ), .data_out3 (data_out3 ), .data_out4 (data_out4 ), .data_out5 (data_out5 ) ); endmodule改完代码后回到IP封装界面如图将自定义的参数读写数据位宽、寄存器深度、读写基地址拖到可视窗口界面在BD中即可实时更改IP参数。点击重新生成IP退出IP编辑界面。按照下图所示可以看到IP路径已经自动添加进工程更新一下IP。回到BD点击加号输入IP命名在BD中添加上一步生成的IP。双击IP 可以看到刚刚添加到可视窗口的参数编辑界面。三、仿真测试工程在没有硬件的情况下PL端单独进行仿真验证刚刚改过的从机IP需要生成一个主机IP与从机互联在PL端进行仿真测试。还是一样的生成流程选择主机模式。主机IP直接创建用于测试不需要编辑。主机测试IP创建完成后添加进BD。双击主机IP设置测试的起始值为0xabcd0001设置基地址与从机一致均为0x43C00000由于从机设置测试读6个写4个但主机逻辑为读写数据量一致故这里读写寄存器的值设为大值6。按照图示连接导出design_1_wrapper.v文件。sim_tb.v如下module sim_tb( ); reg clk ; reg rst ; reg test_en; design_1_wrapper design_1_wrapper( .data_in0_0 (32haa550001), .data_in1_0 (32haa550002), .data_in2_0 (32haa550003), .data_in3_0 (32haa550004), .data_out0_0 ( ), .data_out1_0 ( ), .data_out2_0 ( ), .data_out3_0 ( ), .data_out4_0 ( ), .data_out5_0 ( ), .m00_axi_aclk_0 (clk ), .m00_axi_aresetn_0 (rst ), .m00_axi_error_0 ( ), .m00_axi_init_axi_txn_0 (test_en ), .m00_axi_txn_done_0 ( ) ); initial begin clk 0; rst 0; test_en 0; #1000 rst 1; #100 test_en 1; #10 test_en 0; end always #5 clk ~clk; endmodule下图所示为从机仿真时序为方便测试主机IP生成后未修改未修改的主机逻辑为读写数据量一致因此可以看到读数据后两个值不变这里仅供理解AXI4_lite协议的读写时序若要主从读写数据量完全一致可以按照前面的方法根据个人需求再次编辑主从IP。四、在线测试工程在线测试即为上板工程删除仿真测试的主机IP在BD中创建ZYNQ最小系统定义地址为0x43C0_0000按照图示连接导出design_1_wrapper.v文件。新建一个工程顶层top.v编译生成bit文件导出xsa文件新建一个vitis的helloword空工程。module top( inout wire [14:0]DDR_0_addr, inout wire [2:0]DDR_0_ba, inout wire DDR_0_cas_n, inout wire DDR_0_ck_n, inout wire DDR_0_ck_p, inout wire DDR_0_cke, inout wire DDR_0_cs_n, inout wire [3:0]DDR_0_dm, inout wire [31:0]DDR_0_dq, inout wire [3:0]DDR_0_dqs_n, inout wire [3:0]DDR_0_dqs_p, inout wire DDR_0_odt, inout wire DDR_0_ras_n, inout wire DDR_0_reset_n, inout wire DDR_0_we_n, inout wire FIXED_IO_0_ddr_vrn, inout wire FIXED_IO_0_ddr_vrp, inout wire [53:0]FIXED_IO_0_mio, inout wire FIXED_IO_0_ps_clk, inout wire FIXED_IO_0_ps_porb, inout wire FIXED_IO_0_ps_srstb ); wire [31:0] data_out0_0 ; wire [31:0] data_out1_0 ; wire [31:0] data_out2_0 ; wire [31:0] data_out3_0 ; wire [31:0] data_out4_0 ; wire [31:0] data_out5_0 ; wire ps_aclk ; ila_0 ila_0 ( .clk (ps_aclk ), // input wire clk .probe0(data_out0_0 ), // input wire [31:0] probe0 .probe1(data_out1_0 ), // input wire [31:0] probe1 .probe2(data_out2_0 ), // input wire [31:0] probe2 .probe3(data_out3_0 ), // input wire [31:0] probe3 .probe4(data_out4_0 ), // input wire [31:0] probe4 .probe5(data_out5_0 ) // input wire [31:0] probe5 ); design_1_wrapper design_1_wrapper( .DDR_0_addr (DDR_0_addr ), .DDR_0_ba (DDR_0_ba ), .DDR_0_cas_n (DDR_0_cas_n ), .DDR_0_ck_n (DDR_0_ck_n ), .DDR_0_ck_p (DDR_0_ck_p ), .DDR_0_cke (DDR_0_cke ), .DDR_0_cs_n (DDR_0_cs_n ), .DDR_0_dm (DDR_0_dm ), .DDR_0_dq (DDR_0_dq ), .DDR_0_dqs_n (DDR_0_dqs_n ), .DDR_0_dqs_p (DDR_0_dqs_p ), .DDR_0_odt (DDR_0_odt ), .DDR_0_ras_n (DDR_0_ras_n ), .DDR_0_reset_n (DDR_0_reset_n ), .DDR_0_we_n (DDR_0_we_n ), .FIXED_IO_0_ddr_vrn (FIXED_IO_0_ddr_vrn ), .FIXED_IO_0_ddr_vrp (FIXED_IO_0_ddr_vrp ), .FIXED_IO_0_mio (FIXED_IO_0_mio ), .FIXED_IO_0_ps_clk (FIXED_IO_0_ps_clk ), .FIXED_IO_0_ps_porb (FIXED_IO_0_ps_porb ), .FIXED_IO_0_ps_srstb (FIXED_IO_0_ps_srstb), .data_in0_0 (32haa550001 ), .data_in1_0 (32haa550002 ), .data_in2_0 (32haa550003 ), .data_in3_0 (32haa550004 ), .data_out0_0 (data_out0_0 ), .data_out1_0 (data_out1_0 ), .data_out2_0 (data_out2_0 ), .data_out3_0 (data_out3_0 ), .data_out4_0 (data_out4_0 ), .data_out5_0 (data_out5_0 ), .ps_aclk (ps_aclk ), .ps_aresetn ( ) ); endmodulevitis中helloword.c如下#include stdio.h #include platform.h #include xil_printf.h #include xil_io.h #define AXI_LITE_BASEADDR XPAR_AXI4_LITE_SLAVE_0_S00_AXI_BASEADDR int main() { init_platform(); u32 test_in0 ; u32 test_in1 ; u32 test_in2 ; u32 test_in3 ; test_in0 Xil_In32(AXI_LITE_BASEADDR); test_in1 Xil_In32(AXI_LITE_BASEADDR 0x04); test_in2 Xil_In32(AXI_LITE_BASEADDR 0x08); test_in3 Xil_In32(AXI_LITE_BASEADDR 0x0c); Xil_Out32(AXI_LITE_BASEADDR, 0xabcd0001 ); Xil_Out32(AXI_LITE_BASEADDR4 , 0xabcd0002 ); Xil_Out32(AXI_LITE_BASEADDR8 , 0xabcd0003 ); Xil_Out32(AXI_LITE_BASEADDR12, 0xabcd0004 ); Xil_Out32(AXI_LITE_BASEADDR16, 0xabcd0005 ); Xil_Out32(AXI_LITE_BASEADDR20, 0xabcd0006 ); print(Hello World\n\r); print(Successfully ran Hello World application); cleanup_platform(); return 0; }板卡上电运行程序vitis中可以看到PL写给PS的四组数据与代码中设置的测试值一致。vivado中可以看到PS写给PL的六组数据与代码中设置的测试值一致。