02AXI4总线axi-lite-master(AXI4总线实战)

2021/8/16 6:39:05

本文主要是介绍02AXI4总线axi-lite-master(AXI4总线实战),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

软件版本:vitis2020.2(vivado2020.2)

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA(米联客(milianke)MZU07A-EG硬件开发平台)

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

2.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.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两种模式,这里选择Slave模式

 

这里选择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可有可无。

2.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

     //Signal a new address/data command is available by user logic

     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-slave的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-slave的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-slave的axi_araddr

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     begin

     axi_araddr <= 0;

     end

     // Signals a new write address/ write data is

     // available by user logic

     else if (M_AXI_ARREADY && axi_arvalid)

     begin

     axi_araddr <= axi_araddr + 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

     expected_rdata <= C_M_START_DATA_VALUE;

     end

     // Signals a new write address/ write data is

     // available by user logic

     else if (M_AXI_RVALID && axi_rready)

     begin

     expected_rdata <= C_M_START_DATA_VALUE + read_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的状态机

     always @(posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)

     begin

     expected_rdata <= C_M_START_DATA_VALUE;

     end

     // Signals a new write address/ write data is

     // available by user logic

     else if (M_AXI_RVALID && axi_rready)

     begin

     expected_rdata <= C_M_START_DATA_VALUE + read_index;

     end

     end

     //implement master command interface state machine

     always @ ( posedge M_AXI_ACLK)

     begin

     if (M_AXI_ARESETN == 1'b0)

     begin

     // reset condition

     // All the signals are assigned default values under reset condition

     mst_exec_state <= IDLE;

     start_single_write <= 1'b0;

     write_issued <= 1'b0;

     start_single_read <= 1'b0;

     read_issued <= 1'b0;

     compare_done <= 1'b0;

     ERROR <= 1'b0;

     end

     else

     begin

     // state transition

     case (mst_exec_state)

      

     IDLE:

     // This state is responsible to initiate

     // AXI transaction when init_txn_pulse is asserted

     if ( init_txn_pulse == 1'b1 )

     begin

     mst_exec_state <= INIT_WRITE;

     ERROR <= 1'b0;

     compare_done <= 1'b0;

     end

     else

     begin

     mst_exec_state <= IDLE;

     end

    

     INIT_WRITE:

     // This state is responsible to issue start_single_write pulse to

     // initiate a write transaction. Write transactions will be

     // issued until last_write signal is asserted.

     // write controller

     if (writes_done)

     begin

     mst_exec_state <= INIT_READ;//

     end

     else

     begin

     mst_exec_state <= INIT_WRITE;

      

     if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued)

     begin

     start_single_write <= 1'b1;

     write_issued <= 1'b1;

     end

     else if (axi_bready)

     begin

     write_issued <= 1'b0;

     end

     else

     begin

     start_single_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 last_read 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 && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued)

     begin

     start_single_read <= 1'b1;

     read_issued <= 1'b1;

     end

     else if (axi_rready)

     begin

     read_issued <= 1'b0;

     end

     else

     begin

     start_single_read <= 1'b0; //Negate to generate a pulse

     end

     end

    

     INIT_COMPARE:

     begin

     // 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.

     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

整理成流程图,更加容易理解:

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

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

2.4实验结果

具体的仿真方法省略,以下是仿真结果。



这篇关于02AXI4总线axi-lite-master(AXI4总线实战)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程