本帖最后由 FPGA课程 于 2024-10-12 16:22 编辑
软件版本: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-full-master代码 3:理解AXI-full-master中自定义寄存器的地址分配 4:掌握通过VIVADO封装AXI-full-slave图形化IP 5:通过仿真验证AXI-full-master IP的工作是否正常。 2创建axi4-full-master总线接口IP新建fpga工程,过程省略 新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Full接口总线IP
选择使用vivado自带的AXI总线模板创建一个AXI4-FULL接口IP
设置IP的名字为maxi_full
模板支持3种协议,分别是AXI4-Full ,AXI4-Lite ,AXI4-Stream,这选择Full;总线包括Master和Slave两种模式,这里选择Master模式
这里选择Verify Peripheral IP using AXI4 VIP可以对AXI4-Lite快速验证
单击Finish后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2个IP。其中maxi_full_0就是我们自定义的IP,另外一个slave_0是用来验证maxi_full_0正确性。
采用默认地址分配即可
继续再看代码看看里面有什么东西
路径uisrc/03_ip/ maxi_full_1.0/hdl路径下的maxi_full_v1_0_M00_AXI.v就是我们的源码。另外一个maxi_full_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:产生初始化信号- //Generate a pulse to initiate AXI transaction.
- 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-full-master的axi_awvalid当(~axi_awvalid && start_single_burst_write)==1条件满足,开始一次写传输,设置axi_awvalid有效。 - always @(posedge M_AXI_ACLK)
- begin
-
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_awvalid <= 1'b0;
- end
- // If previously not valid , start next transaction
- else if (~axi_awvalid && start_single_burst_write)
- begin
- axi_awvalid <= 1'b1;
- end
- /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid
- must wait until transaction is accepted */
- else if (M_AXI_AWREADY && axi_awvalid)
- begin
- axi_awvalid <= 1'b0;
- end
- else
- axi_awvalid <= axi_awvalid;
- end
复制代码
3:axi-full-slave的axi_awaddr写通道地址每当M_AXI_AWREADY && axi_awvalid地址加1 - // Next address after AWREADY indicates previous address acceptance
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_awaddr <= 'b0;
- end
- else if (M_AXI_AWREADY && axi_awvalid)
- begin
- axi_awaddr <= axi_awaddr + burst_size_bytes;
- end
- else
- axi_awaddr <= axi_awaddr;
- end
复制代码
4:axi-full-master的axi_wvalid设置axi_wvalid <= 1'b1开始写数据。wnext信号有效代码axi_full_master写数据有效。 - assign wnext = M_AXI_WREADY & axi_wvalid;
-
- // WVALID logic, similar to the axi_awvalid always block above
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_wvalid <= 1'b0;
- end
- // If previously not valid, start next transaction
- else if (~axi_wvalid && start_single_burst_write)
- begin
- axi_wvalid <= 1'b1;
- end
- /* If WREADY and too many writes, throttle WVALID
- Once asserted, VALIDs cannot be deasserted, so WVALID
- must wait until burst is complete with WLAST */
- else if (wnext && axi_wlast)
- axi_wvalid <= 1'b0;
- else
- axi_wvalid <= axi_wvalid;
- end
复制代码
5:axi-full-master的axi_master_lastaxi_master_last信号,当条件满足(((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))==1的时候,axi_wlast <= 1'b1。这是VIVADO自带的模板,但是这里有个bug,那就是必须确保slave可以连续接收数据,假设发送last的时候wnext==0,这样就不能把最后一个数据正确写入到slave中了。 - //WLAST generation on the MSB of a counter underflow
- // WVALID logic, similar to the axi_awvalid always block above
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_wlast <= 1'b0;
- end
- // axi_wlast is asserted when the write index
- // count reaches the penultimate count to synchronize
- // with the last write data when write_index is b1111
- // else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext)
- else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))
- begin
- axi_wlast <= 1'b1;
- end
- // Deassrt axi_wlast when the last write data has been
- // accepted by the slave with a valid response
- else if (wnext)
- axi_wlast <= 1'b0;
- else if (axi_wlast && C_M_AXI_BURST_LEN == 1)
- axi_wlast <= 1'b0;
- else
- axi_wlast <= axi_wlast;
- end
复制代码删除以上代码,并且添加以下代码: - wire wlast=(write_index==C_M_AXI_BURST_LEN-1)&&wnext;
- reg wlast_r1 = 1'b0;
- always @(posedge M_AXI_ACLK)
- wlast_r <= wlast;
- assign axi_wlast = (wlast_r==1'b0)&&(wlast==1'b1);
复制代码另外需要修改reg axi_wlast;为wire axi_wlast; 这样就可以确保发送wlast的时候数据肯定是有效的。 6:写次数记录write_index计数器- /* Burst length counter. Uses extra counter register bit to indicate terminal
- count to reduce decode logic */
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1)
- begin
- write_index <= 0;
- end
- else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))
- begin
- write_index <= write_index + 1;
- end
- else
- write_index <= write_index;
- end
复制代码
7:axi-full-master的axi_wdataaxi_full_master写数据计数写数据 - /* Write Data Generator
- Data pattern is only a simple incrementing count from 0 for each burst */
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- axi_wdata <= 'b1;
- //else if (wnext && axi_wlast)
- // axi_wdata <= 'b0;
- else if (wnext)
- axi_wdata <= axi_wdata + 1;
- else
- axi_wdata <= axi_wdata;
- end
复制代码
8:axi-full-master的axi_bready设置axi_bready信号 - 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
复制代码
9:axi-full-slave的axi_arvalidAxi_full_master读通道的分析非常类似,代码是对称的。 当(~axi_arvalid && start_single_burst_read)==1条件满足,开始一次写传输,设置axi_arvalid有效。 - always @(posedge M_AXI_ACLK)
- begin
-
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
- begin
- axi_arvalid <= 1'b0;
- end
- // If previously not valid , start next transaction
- else if (~axi_arvalid && start_single_burst_read)
- begin
- axi_arvalid <= 1'b1;
- end
- else if (M_AXI_ARREADY && axi_arvalid)
- begin
- axi_arvalid <= 1'b0;
- end
- else
- axi_arvalid <= axi_arvalid;
- end
复制代码
10:axi-full-slave的axi_araddr读地址计算 - // Next address after ARREADY indicates previous address acceptance
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- axi_araddr <= 'b0;
- end
- else if (M_AXI_ARREADY && axi_arvalid)
- begin
- axi_araddr <= axi_araddr + burst_size_bytes;
- end
- else
- axi_araddr <= axi_araddr;
- end
复制代码
11:axi-full-master的axi_rready读数据准备好 - /* The Read Data channel returns the results of the read request
- In this example the data checker is always able to accept
- more data, so no need to throttle the RREADY signal */
- 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)
- begin
- if (M_AXI_RLAST && axi_rready)
- begin
- axi_rready <= 1'b0;
- end
- else
- begin
- axi_rready <= 1'b1;
- end
- end
- // retain the previous value
- end
复制代码
12:读次数记录read_index计数器读数据索引计数 - assign rnext = M_AXI_RVALID && axi_rready;
- // Burst length counter. Uses extra counter register bit to indicate
- // terminal count to reduce decode logic
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_read)
- begin
- read_index <= 0;
- end
- else if (rnext && (read_index != C_M_AXI_BURST_LEN-1))
- begin
- read_index <= read_index + 1;
- end
- else
- read_index <= read_index;
- end
复制代码
13:产生对比数据expected_rdata数据expected_rdata用于和读出的M_AXI_RDATA进行对比以此验证数据的正确性。 - //Generate expected read data to check against actual read data
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)// || M_AXI_RLAST)
- expected_rdata <= 'b1;
- else if (M_AXI_RVALID && axi_rready)
- expected_rdata <= expected_rdata + 1;
- else
- expected_rdata <= expected_rdata;
- end
复制代码
14:比对数据正确性读写数据比较 - //Check received read data against data generator
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- begin
- read_mismatch <= 1'b0;
- end
- //Only check data when RVALID is active
- else if (rnext && (M_AXI_RDATA != expected_rdata))
- begin
- read_mismatch <= 1'b1;
- end
- else
- read_mismatch <= 1'b0;
- end
复制代码
15:读写状态机读写状态机源码 - begin
- mst_exec_state <= INIT_READ;//
- end
- else
- begin
- mst_exec_state <= INIT_WRITE;
- if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)
- begin
- start_single_burst_write <= 1'b1;
- end
- else
- begin
- start_single_burst_write <= 1'b0; //Negate to generate a pulse
- end
- end
- INIT_READ:
- // This state is responsible to issue start_single_read pulse to
- // initiate a read transaction. Read transactions will be
- // issued until burst_read_active signal is asserted.
- // read controller
- if (reads_done)
- begin
- mst_exec_state <= INIT_COMPARE;
- end
- else
- begin
- mst_exec_state <= INIT_READ;
- if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read)
- begin
- start_single_burst_read <= 1'b1;
- end
- else
- begin
- start_single_burst_read <= 1'b0; //Negate to generate a pulse
- end
- end
- INIT_COMPARE:
- // This state is responsible to issue the state of comparison
- // of written data with the read data. If no error flags are set,
- // compare_done signal will be asseted to indicate success.
- //if (~error_reg)
- begin
- ERROR <= error_reg;
- mst_exec_state <= IDLE;
- compare_done <= 1'b1;
- end
- default :
- begin
- mst_exec_state <= IDLE;
- end
- endcase
- end
- end //MASTER_EXECUTION_PROC
复制代码
整理成流程图,更加容易理解:
16:正在写burst_write_activeburst_write_active代表正在写操作 - always @(posedge M_AXI_ACLK)
- begin
- if(M_AXI_ARESETN==0||init_txn_pulse==1'b1)
- burst_write_active<=1'b0;
- //The burst_write_active is asserted when a write burst transaction is initiated
- else if (start_single_burst_write)
- burst_write_active <= 1'b1;
- else if (M_AXI_BVALID && axi_bready)
- burst_write_active <= 0;
- end
复制代码
17:写完成writes_done写数据完成writes_done信号 - 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 (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
- else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready)
- writes_done <= 1'b1;
- else
- writes_done <= writes_done;
- end
复制代码
18:正在读burst_read_active读burst_read_active代表正在读数据 - // burst_read_active signal is asserted when there is a burst write transaction
- // is initiated by the assertion of start_single_burst_write. start_single_burst_read
- // signal remains asserted until the burst read is accepted by the master
- always @(posedge M_AXI_ACLK)
- begin
- if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
- burst_read_active <= 1'b0;
- //The burst_write_active is asserted when a write burst transaction is initiated
- else if (start_single_burst_read)
- burst_read_active <= 1'b1;
- else if (M_AXI_RVALID && axi_rready && M_AXI_RLAST)
- burst_read_active <= 0;
- end
复制代码
19:读完成reads_done读数据完成reads_done信号 - 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 rready response
- //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
- else if (M_AXI_RVALID && axi_rready && (read_index == C_M_AXI_BURST_LEN-1) && (read_burst_counter[C_NO_BURSTS_REQ]))
- reads_done <= 1'b1;
- else
- reads_done <= reads_done;
- end
复制代码
20:IP的更新由于修改了代码,需要先更新IP状态,完成IP更新
4实验结果仿真结果
一次axi4写操作burst lenth=16如下图所示,由于WREADY信号不是连续的,所以可以传输效率不是最高的
一共进行64次burst共计写了1024个32bit数据
一次读操作的burst lenth也是16如下图,但是可以看到读数据时连续的,所以效率最高
一共进行64次burst共计读了1024个32bit数据
可以看到读出的数据RDATA和expected_rdata一致,所以代码正确。
|