手把手教你verilog实现AXI总线协议读写/FPGA实现DMA数据传输/FPGA实现AMBA协议/FPGA手搓AXI协议/AXI协议详解(一)----读写模块设计

因最近好多伙伴问我AXI总线的实现方式,今天抽出时间给大家总结一下,并提供AXI读写的调用模块,兼容各种AXI相关设计

制作不易,记得三连哦,给我动力,持续更新!!!

完整工程文件下载:AXI读写模块源码 (点击蓝色字体获取)

1. 引言

简要介绍AXI协议在AMBA协议中的位置和重要性。AMBA协议(Advanced Microcontroller Bus Architecture)是ARM公司开发的总线标准,而AXI(Advanced eXtensible Interface)是其中最广泛使用的一种,用于高性能互联。
说明AXI协议的应用场景,比如FPGA中的IP核间通信、数据流传输等。
提到文章的目标:通过Verilog实现AXI协议读写操作,帮助读者理解FPGA上的AXI设计。

2. AXI协议概述

AXI的五个通道:

  • 读地址通道:用于传输读请求的地址。
  • 读数据通道:返回所请求的数据。
  • 写地址通道:用于传输写请求的地址。
  • 写数据通道:传输要写入的数据。
  • 写响应通道:确认写操作是否成功。

3. verilog代码实现

3.1写数据设计

写数据设计模块,主要包括写地址、写数据、写响应三部分组成,实现起来应该按照这个三个的顺序依次实现。
image

3.1.1 写地址通道 (Write Address Channel)

功能:用于传输写操作的目标地址。
关键信号:

  • AWADDR:写操作的目标地址。
  • AWVALID:当地址有效时,主设备(Master)驱动此信号为高。
  • AWREADY:当从设备(Slave)准备好接收地址时,此信号由从设备拉高。
  • AWSIZE:突发传输中的字节数。
  • AWBURST:突发传输类型。
  • AWLEN:突发传输长度。
    工作流程: 主设备发出AWADDR(写地址),同时将AWVALID置高以表示地址有效,等待从设备将AWREADY置高以确认接收。地址确认后,AWVALID可以被拉低。

3.1.2 写数据通道 (Write Data Channel)

功能:传输要写入的数据。
关键信号:

  • WDATA:要写入的实际数据。
  • WSTRB:写数据的字节选通信号,指示哪些字节是有效的。
  • WLAST:指示突发传输中最后一个数据拍。
  • WVALID:数据有效时,主设备将此信号置高。
  • WREADY:从设备准备好接收数据时,将此信号拉高。
    工作流程: 主设备发出WDATA(要写入的数据)和WSTRB(字节选通),并将WVALID置高,表示数据有效。当从设备将WREADY拉高时,数据被接收。对于突发传输,主设备还需要设置WLAST信号来指示最后一拍数据。

3.1.3 写响应通道 (Write Response Channel)

功能:用于从设备向主设备反馈写操作的结果,确保写操作完成。
关键信号:

  • BVALID:当写响应有效时,从设备将此信号拉高。

  • BREADY:主设备准备好接收写响应时,驱动此信号为高。

  • BRESP:反馈写操作的结果状态(如正常完成、错误等)。
    工作流程: 当从设备完成写操作后,它会通过BRESP反馈写操作的结果,并将BVALID置高。主设备收到响应后,将BREADY置高,表示写响应已被接收。从设备在BREADY和BVALID同时为高时完成响应握手。
    突发写时序
    image
    代码实现
    写数据模块,主要采用状态机三段式进行实现,大致可以分为以下五个状态

  • 写空闲(WR_IDLE):等待触发突发信号。

  • 写地址(WR_ADDR):向从机写入写地址和突发信息。

  • 写数据(WR_DATA):数据传递状态。

  • 写完成(WR_LAST):传输最后一个数据。

  • 写停止(WR_STOP):复位各自信号。
    状态转移图:
    image

状态机部分代码设计:

点击查看代码
always @ (posedge i_clk, negedge i_rst_n) begin  :   W_FMS3
        if (~i_rst_n)
            begin
                w_data          <= 0;
                w_valid         <= 0;
                w_last          <= 0;
                w_strb          <= 0;
                aw_addr         <= 0;
                aw_len          <= 0;
                aw_size         <= 0;
                aw_burst        <= 0;
                aw_valid        <= 0;

                aw_addr_cnt     <= 32'h10000000;

            end
        else
            case (n_state)

                WR_ADDR :   begin
                                w_strb          <= wstrb        ;
                                aw_size         <= awsize       ;
                                aw_burst        <= 2'd1         ;
                                aw_len          <= awlen        ;

                                aw_valid        <= 1            ;
                                aw_addr         <= aw_addr_cnt  ;
                end

                WR_DATA :   begin
                                aw_valid <= 0;
                                if (i_valid)
                                    begin
                                        if (w_ready)
                                            begin
                                                w_valid     <= 1;
                                                w_data <= i_data;

                                            end
                                        else
                                            begin
                                                w_data <= w_data;
                                            end
                                    end
                                else
                                    begin
                                        w_valid     <= 0;
                                        w_data      <= w_data;
                                    end
                end

                WR_LAST :   begin
                                if (i_valid)
                                    begin
                                        w_valid     <= 1;
                                        w_last      <= 1;
                                        w_data      <= i_data;
                                    end
                                else
                                    begin
                                        w_valid     <= 0;
                                        w_data      <= w_data;
                                    end
                end

                WR_STOP :   begin
                                w_last  <= 0        ;
                                w_valid <= 0        ;
                end

                default :   ;
            endcase
    end

3.2 读数据设计

读数据设计模块,主要包括读地址、读数据部分组成,实现起来应该按照这个两个的顺序依次实现。
image

3.2.1 读地址通道 (Read Address Channel)

功能:用于传输读操作的目标地址,发起读请求。
关键信号:

  • ARADDR:读操作的目标地址。
  • ARVALID:主设备(Master)将此信号置高,表示读地址有效。
  • ARREADY:从设备(Slave)将此信号置高,表示已准备好接收读地址。
  • ARLEN:读突发长度。
  • ARSIZE:读突发传输数据字节数。
  • ARBURST:读突发类型。
    工作流程: 主设备在ARADDR设定好目标地址后,将ARVALID置高,表示发起读请求。只有当从设备将ARREADY置高后,表示从设备已经接收了该读请求,读地址通道的握手完成。

3.2.2 读数据通道 (Read Data Channel)

功能:用于传输从设备返回的读取数据。
关键信号:

  • RDATA:从设备返回的读数据。
  • RRESP:表示读操作的响应状态(如正常完成、错误)。
  • RLAST:指示突发读操作中最后一个数据拍。
  • RVALID:从设备将此信号置高,表示返回的数据有效。
  • RREADY:主设备将此信号置高,表示准备好接收数据。
    工作流程: 当从设备准备好返回数据时,会将RVALID置高,并将数据放在RDATA线上。当主设备准备好接收数据时,将RREADY置高,完成握手。如果是突发传输,当传输最后一拍数据时,从设备将RLAST信号置高以指示突发传输结束。

突发读时序

image

代码实现

读数据模块,主要采用状态机三段式进行实现,大致可以分为以下五个状态

  • 读空闲(RD_IDLE):等待触发突发信号。
  • 读地址(RD_ADDR):向从机写入读地址和突发信息。
  • 读数据(RD_DATA):数据传递状态。
  • 读完成(RD_LAST):传输最后一个数据。
  • 读停止(RD_STOP):复位各自信号。
    读数据状态转移图:
    image
    状态机部分代码设计:
点击查看代码
//状态执行的操作 FSM33
    always @ (posedge i_clk, negedge i_rst_n) begin  :   R_FMS3
        if (~i_rst_n)
            begin
                ar_addr         <= 0;
                ar_len          <= 0;
                ar_burst        <= 0;
                ar_size         <= 0;
                ar_valid        <= 0;
                o_data          <= 0;
                o_last          <= 0;
                o_valid         <= 0;
                rd_addr_buff    <= 32'h0000_0000;
            end
        else
            case (n_state)
                WAIT_RD   :   begin
                                ar_valid <= 0;
                end

                RD_ADDR :   begin
                                ar_valid    <= 1            ;
                                ar_addr     <= rd_addr_buff ;
                                ar_len      <= arlen        ;
                                ar_burst    <= 2'd1         ;
                                ar_size     <= arsize       ;
                end

                RD_DATA :   begin
                                ar_valid <= 0;
                                if (r_valid)
                                    begin
                                        o_valid <= 1;
                                        if (i_ready)
                                            o_data <= r_data;
                                        else
                                            o_data <= o_data;
                                    end
                                else
                                    begin
                                        o_data <= o_data;
                                        o_valid <= 0; 
                                    end
                end

                RD_LAST :   begin
                                o_last <= 1;
                                if (r_valid && i_ready)
                                    begin
                                        o_data <= r_data;
                                        o_valid <= 1;
                                    end
                                else
                                    begin
                                        o_data <= o_data;
                                        o_valid <= 0;
                                    end
                end 

                RD_STOP :   begin
                                o_last <= 0;
                                o_valid <= 0;
                end 

            endcase 
    end

4. 仿真测试

建立一个仿真测试工程,自己模拟产生AXI数据,然后存储到存储模块,并读取存储模块的数据到AXI FIFO
image
然后联合modelsim进行仿真,仿真结果如下

写数据仿真图:
image

读数据仿真图:
image
image

至此,我们的AXI 读写就彻底验证完成了!!

下一节讲解如何在实际工程中调用并使用这两个模块!!

制作不易,记得三连哦,给我动力,持续更新!!!

posted @ 2024-10-15 16:52  FPGAmaster  阅读(1398)  评论(0)    收藏  举报