软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
关于AXI4总线协议的部分介绍请阅读"01AXI4总线axi-lite-slave"。
2:掌握通过VIVADO工具产生AXI-full-slave代码
3:理解AXI-full-slave中自定义寄存器的地址分配
4:掌握通过VIVADO封装AXI-full-slave图形化IP
5:通过仿真验证AXI-full-slave IP的工作是否正常。
2创建axi4-full-slave总线接口IP
新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Full接口总线IP
选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream,这里选择full;
总线包括Master和Slave两种模式,这里选择Slave模式
这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-FULL快速验证
3程序分析
1:axi-full-slave的axi_awready
// 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; axi_awv_awr_flag <= 1'b0; end else begin if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag) begin // slave is ready to accept an address and // associated control signals axi_awready <= 1'b1; axi_awv_awr_flag <= 1'b1; // used for generation of bresp() and bvalid end else if (S_AXI_WLAST && axi_wready) // preparing to accept next address after current write burst tx completion begin axi_awv_awr_flag <= 1'b0; end else begin axi_awready <= 1'b0; end end end |
2:axi-full-slave的axi_awaddr
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
//This process is used to latch the address when both //S_AXI_ARVALID and S_AXI_RVALID are valid. always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_araddr <= 0; axi_arlen_cntr <= 0; axi_arburst <= 0; axi_arlen <= 0; axi_rlast <= 1'b0; axi_ruser <= 0; end else begin if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag) begin // address latching axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0]; axi_arburst <= S_AXI_ARBURST; axi_arlen <= S_AXI_ARLEN; // start address of transfer axi_arlen_cntr <= 0; axi_rlast <= 1'b0; end else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY) begin
axi_arlen_cntr <= axi_arlen_cntr + 1; axi_rlast <= 1'b0;
case (axi_arburst) 2'b00: // fixed burst // The read address for all the beats in the transaction are fixed begin axi_araddr <= axi_araddr; //for arsize = 4 bytes (010) end 2'b01: //incremental burst // The read address for all the beats in the transaction are increments by awsize begin axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1; //araddr aligned to 4 byte boundary axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}}; //for awsize = 4 bytes (010) end 2'b10: //Wrapping burst // The read address wraps when the address reaches wrap boundary if (ar_wrap_en) begin axi_araddr <= (axi_araddr - ar_wrap_size); end else begin axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1; //araddr aligned to 4 byte boundary axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}}; end default: //reserved (incremental burst for example) begin axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1; //for arsize = 4 bytes (010) end endcase end else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag ) begin axi_rlast <= 1'b1; end else if (S_AXI_RREADY) begin axi_rlast <= 1'b0; end end end |
3:axi-full-slave的axi_wready
// 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 && axi_awv_awr_flag) begin // slave can accept the write data axi_wready <= 1'b1; end //else if (~axi_awv_awr_flag) else if (S_AXI_WLAST && axi_wready) begin axi_wready <= 1'b0; end end end |
4:axi-full-slave的axi_bvalid信号
axi_bvalid用于告知axi master axi-slave端已经完成数据接收了
给出ACK,写操作LAST信号的下一个时钟,AXI-SLAVE给出ACK信号
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_bvalid <= 0; axi_bresp <= 2'b0; axi_buser <= 0; end else begin if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST ) begin axi_bvalid <= 1'b1; axi_bresp <= 2'b0; // 'OKAY' response end 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 |
5:axi-full-slave的axi_arready信号
// 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_arv_arr_flag <= 1'b0; end else begin if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag) begin axi_arready <= 1'b1; axi_arv_arr_flag <= 1'b1; end else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen) // preparing to accept next address after current read completion begin axi_arv_arr_flag <= 1'b0; end else begin axi_arready <= 1'b0; end end end |
6:axi-full-slave的axi_araddr信号
AXI-的读写操作几乎是相对的代码,AXI的burst模式包括3种:
3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。
|
7:axi-full-slave的axi_rvalid信号
在用VIVADO模板产生的demo中,读操作数据不是连续读的,通过axi_rvalid设置AXI-SLAVE FULL读数据有效。
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_rvalid <= 0; axi_rresp <= 0; end else begin if (axi_arv_arr_flag && ~axi_rvalid) begin axi_rvalid <= 1'b1; axi_rresp <= 2'b0; // 'OKAY' response end else if (axi_rvalid && S_AXI_RREADY) begin axi_rvalid <= 1'b0; end end end |
8:数据保存到bock ram
// implement Block RAM(s) generate for(i=0; i<= USER_NUM_MEM-1; i=i+1) begin:BRAM_GEN wire mem_rden; wire mem_wren;
assign mem_wren = axi_wready && S_AXI_WVALID ;
assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1) begin:BYTE_BRAM_GEN wire [8-1:0] data_in ; wire [8-1:0] data_out; reg [8-1:0] byte_ram [0 : 15]; integer j;
//assigning 8 bit data assign data_in = S_AXI_WDATA[(mem_byte_index*8+7) -: 8]; assign data_out = byte_ram[mem_address];
always @( posedge S_AXI_ACLK ) begin if (mem_wren && S_AXI_WSTRB[mem_byte_index]) begin byte_ram[mem_address] <= data_in; end end
always @( posedge S_AXI_ACLK ) begin if (mem_rden) begin mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out; end end
end end endgenerate |