本帖最后由 FPGA课程 于 2024-10-12 16:21 编辑
软件版本: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概述使用XILINX 的软件工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4总线结束,并且可以灵活使用AXI-4总线技术完成数据的交换,可以让我们在构建强大的FPGA内部总线数据互联通信方面取得高效、高速、标准化的优势。 关于AXI4总线协议的部分介绍请阅读“01AXI4总线axi-lite-slave”。 本文实验目的: 1:掌握基于VIVADO工具产生AXI协议模板 2:掌握通过VIVADO工具产生AXI-lite-master代码 3:理解AXI-lite-master中自定义寄存器的地址分配 4:掌握通过VIVADO封装AXI-lite-master图形化IP 5:通过仿真验证AXI-lite-master IP的工作是否正常。 2创建axi4-lite-master总线接口IP新建fpga工程,过程省略 新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Lite接口总线IP
选择使用vivado自带的AXI总线模板创建一个AXI4-Lite接口IP
设置IP的名字为maxi_lite
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream
总线包括Master和Slave两种模式,这里选择Master模式
这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-Lite快速验证
单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中maxi_lite_0就是我们自定义的IP,另外一个slave_0是用来验证maxi_lite_0正确性。
采用默认地址分配即可
继续再看代码看看里面有什么东西
路径uisrc/03_ip/ maxi_lite_1.0/hdl路径下的maxi_lite_v1_0_M00_AXI.v就是我们的源码。另外一个maxi_lite_v1_0.v是软件产生了一个接口文件,如果我们自己定义IP可有可无。 3程序分析axi总线信号的关键无非是地址和数据,而写地址的有效取决于AXI_AWVALID和AXI_AWREADY,写数据的有效取决于S_AXI_WVALID和S_AXI_WREADY。同理,读地址的有效取决于AXI_ARVALID和AXI_ARREADY,读数据的有效取决于S_AXI_RVALID和S_AXI_RREADY。所以以下代码的阅读分析注意也是围绕以上4个信号的有效时序。 以下程序我们把关键信号的代码拆分阅读 1:产生初始化信号 - always @(posedge M_AXI_ACLK)
- begin
- // Initiates AXI transaction delay
- if (M_AXI_ARESETN == 0 )
- begin
- init_txn_ff <= 1'b0;
- init_txn_ff2 <= 1'b0;
- end
- else
- begin
- init_txn_ff <= INIT_AXI_TXN;
- init_txn_ff2 <= init_txn_ff;
- end
- end
复制代码
2:axi-lite-master的axi_awvalid当start_single_write有效,开始一次写传输,设置axi_awvalid有效。 - always @(posedge M_AXI_ACLK)
- begin
- //Only VALID signals must be deasserted during reset per AXI spec
- //Consider inverting then registering active-low reset for higher fmax
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_awvalid <= 1'b0;
- end
- //用户逻辑发出新地址/数据命令的信号
- else
- begin
- if (start_single_write)
- begin
- axi_awvalid <= 1'b1;
- end
- //Address accepted by interconnect/slave (issue of M_AXI_AWREADY by slave)
- else if (M_AXI_AWREADY && axi_awvalid)
- begin
- axi_awvalid <= 1'b0;
- end
- end
- end
复制代码
3:axi-lite-master的axi_awaddr - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_awaddr <= 0;
- end
- // Signals a new write address/ write data is
- // available by user logic
- else if (M_AXI_AWREADY && axi_awvalid)
- begin
- axi_awaddr <= axi_awaddr + 32'h00000004;
-
- end
- end
复制代码
4:axi-lite-master的axi_wvalid当M_AXI_WREADY && axi_wvalid同时有效的时候,数据才是有效的,对于axi-lite_master接口,M_AXI_WREADY && axi_wvalid同时有效的时间窗口是一个时钟周期。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_wvalid <= 1'b0;
- end
- //Signal a new address/data command is available by user logic
- else if (start_single_write)
- begin
- axi_wvalid <= 1'b1;
- end
- //Data accepted by interconnect/slave (issue of M_AXI_WREADY by slave)
- else if (M_AXI_WREADY && axi_wvalid)
- begin
- axi_wvalid <= 1'b0;
- end
- end
复制代码
5:axi-lite-master的axi_wdata产生写测试用的测试数据 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_wdata <= C_M_START_DATA_VALUE;
- end
- // Signals a new write address/ write data is
- // available by user logic
- else if (M_AXI_WREADY && axi_wvalid)
- begin
- axi_wdata <= C_M_START_DATA_VALUE + write_index;
- end
- end
复制代码
6:写次数记录write_index计数器这个demo中以start_single_wirte信号作为统计的,如果我们完全自定axi-lite_master代码可以自行优化,可以写出更好的代码。我们这里是用vivado模板产生的代码主要是教会大家如何使用现有的手段和软件工具学习axi4总线。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- write_index <= 0;
- end
- // Signals a new write address/ write data is
- // available by user logic
- else if (start_single_write)
- begin
- write_index <= write_index + 1;
- end
- end
复制代码
7:axi-lite-master的axi_bready当收到写通道的axi-lite-slave发回的M_AXI_BVALDI应答信号,设置axi_bready为1,BRESP返回AXI写操作是否有错误。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_bready <= 1'b0;
- end
- // accept/acknowledge bresp with axi_bready by the master
- // when M_AXI_BVALID is asserted by slave
- else if (M_AXI_BVALID && ~axi_bready)
- begin
- axi_bready <= 1'b1;
- end
- // deassert after one clock cycle
- else if (axi_bready)
- begin
- axi_bready <= 1'b0;
- end
- // retain the previous value
- else
- axi_bready <= axi_bready;
- end
-
- //Flag write errors
- assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);
复制代码
8:axi-lite-master的axi_arvalid - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_arvalid <= 1'b0;
- end
- //Signal a new read address command is available by user logic
- else if (start_single_read)
- begin
- axi_arvalid <= 1'b1;
- end
- //RAddress accepted by interconnect/slave (issue of M_AXI_ARREADY by slave)
- else if (M_AXI_ARREADY && axi_arvalid)
- begin
- axi_arvalid <= 1'b0;
- end
- // retain the previous value
- end
复制代码
9:axi-lite-master的axi_araddr- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_awaddr <= 0;
- end
- // Signals a new write address/ write data is
- // available by user logic
- else if (M_AXI_AWREADY && axi_awvalid)
- begin
- axi_awaddr <= axi_awaddr + 32'h00000004;
-
- end
- end
复制代码
10:axi-lite-master的axi_rready当M_AXI_RVALID && axi_rready同时有效的时候,数据才是有效的,对于axi-lite_master接口,M_AXI_RVALID && ~axi_rready==1的时候设置axi_rready=1,当axi_rready==1,再设置axi_rready=0 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_rready <= 1'b0;
- end
- // accept/acknowledge rdata/rresp with axi_rready by the master
- // when M_AXI_RVALID is asserted by slave
- else if (M_AXI_RVALID && ~axi_rready)
- begin
- axi_rready <= 1'b1;
- end
- // deassert after one clock cycle
- else if (axi_rready)
- begin
- axi_rready <= 1'b0;
- end
- // retain the previous value
- end
-
- //Flag write errors
- assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]);
复制代码
11:axi-lite-master的M_AXI_RDATA当M_AXI_RVALID && axi_rready都有效的时候,对读取的M_AXI_RDATA数据和expected_rdata数据进行比较。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- read_mismatch <= 1'b0;
- //The read data when available (on axi_rready) is compared with the expected data
- else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata))
- read_mismatch <= 1'b1;
- else
- read_mismatch <= read_mismatch;
- end
复制代码
12:产生对比数据expected_rdata数据expected_rdata用于和读出的M_AXI_RDATA进行对比以此验证数据的正确性。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_wdata <= C_M_START_DATA_VALUE;
- end
- // Signals a new write address/ write data is
- // available by user logic
- else if (M_AXI_WREADY && axi_wvalid)
- begin
- axi_wdata <= C_M_START_DATA_VALUE + write_index;
- end
- end
复制代码 13:读次数记录read_index计数器这个demo中以start_single_read信号作为统计的,如果我们完全自定axi-lite_master代码可以自行优化,可以写出更好的代码。我们这里是用vivado模板产生的代码主要线教会大家如何使用现有的手段和软件工具学习axi4总线。 - always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- read_index <= 0;
- end
- // Signals a new read address is
- // available by user logic
- else if (start_single_read)
- begin
- read_index <= read_index + 1;
- end
- end
复制代码
14:axi-lite-master的状态机
整理成流程图,更加容易理解:
15:最后一个写数据- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- last_write <= 1'b0;
- //The last write should be associated with a write address ready response
- else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY)
- last_write <= 1'b1;
- else
- last_write <= last_write;
- end
- //Check for last write completion.
- //This logic is to qualify the last write count with the final write
- //response. This demonstrates how to confirm that a write has been
- //committed.
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- writes_done <= 1'b0;
-
- //The writes_done should be associated with a bready response
- else if (last_write && M_AXI_BVALID && axi_bready)
- writes_done <= 1'b1;
- else
- writes_done <= writes_done;
- end <span style="background-color: rgb(255, 255, 255);"> </span><img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码 16:最后一个读数据- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- last_read <= 1'b0;
-
- //The last read should be associated with a read address ready response
- else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) )
- last_read <= 1'b1;
- else
- last_read <= last_read;
- end
-
- /* Check for last read completion.
- This logic is to qualify the last read count with the final read response/data. */
-
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- reads_done <= 1'b0;
- //The reads_done should be associated with a read ready response
- else if (last_read && M_AXI_RVALID && axi_rready)
- reads_done <= 1'b1;
- else
- reads_done <= reads_done;
- end
复制代码
|