【电路笔记 通信】AXI4-Lite协议 FPGA实现 & Valid-Ready Handshake 握手协议

发布于:2025-08-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

在这里插入图片描述

AXI4-Lite Top 模块

在这里插入图片描述

  • 注:常见一种做法是 Master 直接常拉高 BREADY,参考代码未使用,这样只要 Slave 提供写响应,握手就能立即完成,不会卡住,这样可以大幅简化逻辑:

    • Master 的 RREADY、BREADY → 可以常高(随时能接收)。
    • Slave 的 AWREADY、WREADY、ARREADY → 也可以常高(随时能接收)。
  • 注:上图Slave侧只标注了 Read Channel (Slave -> Master)

`timescale 1ns / 1ps
module axi4_lite_top#(
    // 参数定义
    parameter DATA_WIDTH = 32,    // 数据宽度
    parameter ADDRESS    = 32     // 地址宽度
    )(
        input                           ACLK,       // AXI 时钟信号
        input                           ARESETN,    // AXI 复位信号(低有效)
        input                           read_s,     // 顶层控制信号:触发读事务
        input                           write_s,    // 顶层控制信号:触发写事务
        input    [ADDRESS-1:0]          address,    // 顶层输入地址
        input    [DATA_WIDTH-1:0]       W_data      // 顶层输入写数据
    );

    // AXI Master-Slave 之间交互的信号线定义
    logic  M_ARREADY, S_RVALID, M_ARVALID, M_RREADY;
    logic  S_AWREADY, S_BVALID, M_AWVALID, M_BREADY;
    logic  M_WVALID,  S_WREADY;

    logic [ADDRESS-1 : 0]  M_ARADDR;  // Master -> Slave 读地址
    logic [ADDRESS-1 : 0]  M_AWADDR;  // Master -> Slave 写地址
    logic [DATA_WIDTH-1:0] M_WDATA;   // Master -> Slave 写数据
    logic [DATA_WIDTH-1:0] S_RDATA;   // Slave -> Master 读数据
    logic [3:0]            M_WSTRB;   // 写数据字节使能(32bit=4字节,所以 4bit),写数据掩码
    logic [1:0]            S_RRESP;   // Slave -> Master 读响应
    logic [1:0]            S_BRESP;   // Slave -> Master 写响应

    // 实例化 AXI4-Lite Master
    axi4_lite_master u_axi4_lite_master0
    (
        .ACLK       (ACLK),
        .ARESETN    (ARESETN),

        // 控制信号
        .START_READ (read_s),      // 触发读
        .START_WRITE(write_s),     // 触发写
        .address    (address),     // 读写地址
        .W_data     (W_data),      // 写入数据

        // Read Channel (Master -> Slave)
        .M_ARADDR   (M_ARADDR),    // 读地址
        .M_ARVALID  (M_ARVALID),   // 地址有效
        .M_ARREADY  (M_ARREADY),   // 从机就绪
        .M_RDATA    (S_RDATA),     // 从机返回的数据
        .M_RRESP    (S_RRESP),     // 读响应(OKAY/SLVERR)
        .M_RVALID   (S_RVALID),    // 从机返回数据有效
        .M_RREADY   (M_RREADY),    // 主机准备好接收数据

        // Write Channel (Master -> Slave)
        .M_AWADDR   (M_AWADDR),    // 写地址
        .M_AWVALID  (M_AWVALID),   // 地址有效
        .M_AWREADY  (S_AWREADY),   // 从机就绪
        .M_WDATA    (M_WDATA),     // 写数据
        .M_WSTRB    (M_WSTRB),     // 写数据掩码
        .M_WVALID   (M_WVALID),    // 写数据有效
        .M_WREADY   (S_WREADY),    // 从机准备好接收数据
        .M_BRESP    (S_BRESP),     // 从机写响应
        .M_BVALID   (S_BVALID),    // 响应有效
        .M_BREADY   (M_BREADY)     // 主机准备好接收响应
    );

    // 实例化 AXI4-Lite Slave
    axi4_lite_slave u_axi4_lite_slave0
    (
        .ACLK       (ACLK),
        .ARESETN    (ARESETN),

        // Read Channel (Slave -> Master)
        .S_ARADDR   (M_ARADDR),    // 主机发来的读地址
        .S_ARVALID  (M_ARVALID),   // 地址有效
        .S_ARREADY  (M_ARREADY),   // 从机就绪
        .S_RDATA    (S_RDATA),     // 返回的数据
        .S_RRESP    (S_RRESP),     // 读响应
        .S_RVALID   (S_RVALID),    // 数据有效
        .S_RREADY   (M_RREADY),    // 主机就绪

        // Write Channel (Slave -> Master)
        .S_AWADDR   (M_AWADDR),    // 主机发来的写地址
        .S_AWVALID  (M_AWVALID),   // 地址有效
        .S_AWREADY  (S_AWREADY),   // 从机就绪
        .S_WDATA    (M_WDATA),     // 写数据
        .S_WSTRB    (M_WSTRB),     // 写数据掩码
        .S_WVALID   (M_WVALID),    // 数据有效
        .S_WREADY   (S_WREADY),    // 从机就绪
        .S_BRESP    (S_BRESP),     // 写响应
        .S_BVALID   (S_BVALID),    // 响应有效
        .S_BREADY   (M_BREADY)     // 主机准备接收响应
    );
endmodule

axi4_lite_top描述

这个 axi4_lite_top 模块就是一个 AXI4-Lite 主从对接的顶层封装

  • axi4_lite_master 接收顶层输入的 read_s / write_s 信号,发起 AXI 读写事务;
  • axi4_lite_slave 响应 Master 的请求,返回读写数据与响应;
  • 两者通过 AXI4-Lite 信号互联,形成一个完整的读写通路。

激励文件

`timescale 1ns / 1ps
module axi4_lite_top_tb();

    logic           ACLK_tb;
    logic           ARESETN_tb;
    //logic           read_s_tb;     // 注释掉读信号
    logic           write_s_tb;
    logic [31:0]    address_tb;
    logic [31:0]    W_data_tb;
    
    axi4_lite_top u_axi4_lite_top0(
        .ACLK(ACLK_tb),
        .ARESETN(ARESETN_tb),
        //.read_s(read_s_tb),   // 注释掉读接口
        .write_s(write_s_tb),
        .address(address_tb), // 顶层输入地址
        .W_data(W_data_tb)    // 顶层输入写数据
    );
     
    initial begin
        #5;
        ACLK_tb=0;
        ARESETN_tb=0;
        //read_s_tb=0;    // 注释掉读操作
        write_s_tb=0;                           
        #5;
        ACLK_tb=1;
        ARESETN_tb=1;
        write_s_tb=0;
        #15;
        // -------- 写测试开始 --------
        write_s_tb=1;
        address_tb = 5;
        W_data_tb = 4;
        #10;
        write_s_tb=0;
        #20;
        // -------- 写测试结束 --------

        // -------- 以下是读测试,注释掉 --------
        //write_s_tb=0;
        //read_s_tb=0;
        //#30;
        //read_s_tb=1;
        //address_tb = 5;
        //#10;
        //read_s_tb=0;
        // -----------------------------------

        #40;
        $finish;            
    end
    
    always begin
        #5 ACLK_tb = ~ACLK_tb;
    end
endmodule

AXI4-Lite 主设备(Master)

时序分析

  • 仿真的写时序

在这里插入图片描述

VALID & READY 握手基本规则
  • VALID:由 发送方 发出,表示“数据/地址/响应已经准备好,可以被接收”。

  • READY:由 接收方 发出,表示“我已经准备好接收”。

  • 握手成功条件: 当 VALID = 1 且 READY = 1 时,数据才算真正传输成功。

  • 握手特点

    • VALID 不能依赖 READY 才拉高(发送方必须主动把数据放出来)。
    • READY 可以提前为高,也可以等到看到 VALID 才拉高。
    • 只有 VALID 和 READY 同时为高的那个时钟周期,传输的数据才被“采纳”。例如:

assign write_addr = M_AWVALID && M_AWREADY; // 地址握手完成条件
         // 写地址和写数据握手都完成 → 等待写响应
         WRITE_CHANNEL : if (write_addr && write_data)
                 next_state = WRESP__CHANNEL;

握手条件是 M_ARVALID && M_ARREADY,FSM 中体现为:
        RADDR_CHANNEL : if (M_ARVALID && M_ARREADY)
                  next_state = RDATA__CHANNEL; // 读地址握手成功,进入等待读数据状态。

  • 官方写时序 :https://docs.amd.com/r/en-US/pg202-mipi-dphy/AXI4-Lite-Interface
    在这里插入图片描述

状态机

  • AXI4-Lite Master 状态机,能发起 一次读事务一次写事务,并根据握手机制自动切换状态。状态机流程:
  1. IDLE → 等待外部触发
  2. WRITE_CHANNEL → 发送地址和数据
  3. WRESP__CHANNEL → 等待写响应
  4. RADDR_CHANNEL → 发送读地址
  5. RDATA__CHANNEL → 等待读数据

代码实现

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 05/04/2024 05:52:55 PM
// Design Name: 
// Module Name: axi4_lite_master
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: AXI4-Lite Master 实现,包含读写通道握手逻辑和有限状态机
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module axi4_lite_master #(
    parameter ADDRESS = 32,       // 地址总线宽度
    parameter DATA_WIDTH = 32     // 数据总线宽度
    )
    (
        // ================= 全局信号 ==================
        input                           ACLK,       // AXI 时钟
        input                           ARESETN,    // AXI 低电平复位 (active low)

        input                           START_READ,   // 触发一次读事务
        input                           START_WRITE,  // 触发一次写事务

        input          [ADDRESS-1 : 0]  address,     // 读/写地址,来自TOP
        input          [DATA_WIDTH-1:0] W_data,      // 要写入的数据,来自TOP

        // =============== AXI4-Lite 输入信号(从 Slave 来) ===============
        // Read Address Channel
        input                           M_ARREADY,   // 从机准备好接收读地址
        // Read Data Channel
        input          [DATA_WIDTH-1:0] M_RDATA,     // 从机返回的数据
        input               [1:0]       M_RRESP,     // 从机返回的响应
        input                           M_RVALID,    // 从机提供的数据有效
        // Write Address Channel
        input                           M_AWREADY,   // 从机准备好接收写地址
        // Write Data Channel
        input                           M_WREADY,    // 从机准备好接收写数据
        // Write Response Channel
        input             [1:0]         M_BRESP,     // 从机返回的写响应
        input                           M_BVALID,    // 从机写响应有效

        // =============== AXI4-Lite 输出信号(Master 发出) ===============
        // Read Address Channel
        output logic    [ADDRESS-1 : 0] M_ARADDR,    // 读地址
        output logic                    M_ARVALID,   // 读地址有效
        // Read Data Channel
        output logic                    M_RREADY,    // Master 准备好接收数据
        // Write Address Channel
        output logic    [ADDRESS-1 : 0] M_AWADDR,    // 写地址
        output logic                    M_AWVALID,   // 写地址有效
        // Write Data  Channel
        output logic   [DATA_WIDTH-1:0] M_WDATA,     // 写数据
        output logic   [3:0]            M_WSTRB,     // 写掩码(字节选通)
        output logic                    M_WVALID,    // 写数据有效
        // Write Response Channel
        output logic                    M_BREADY     // Master 准备好接收写响应
    );

    // ============== 内部寄存器与信号 ==============
    logic read_start;     // 记录一次读事务开始
    logic write_addr;     // 写地址握手完成标志
    logic write_data;     // 写数据握手完成标志
    logic write_start;    // 记录一次写事务开始

    // 状态机定义:AXI4-Lite 事务状态
    typedef enum logic [2 : 0] {
        IDLE,            // 空闲状态
        WRITE_CHANNEL,   // 发送写地址和写数据
        WRESP__CHANNEL,  // 等待写响应
        RADDR_CHANNEL,   // 发送读地址
        RDATA__CHANNEL   // 等待读数据返回
    } state_type;

    state_type state , next_state;

    // ============== AXI4-Lite 接口输出逻辑 ==============

    // --- Read Address Channel ---
    assign M_ARADDR  = (state == RADDR_CHANNEL) ? address : 32'h0;  // 只有在发读地址时有效
    assign M_ARVALID = (state == RADDR_CHANNEL) ? 1 : 0;            // 发出读地址握手信号

    // --- Read Data Channel ---
    assign M_RREADY  = (state == RDATA__CHANNEL || state == RADDR_CHANNEL) ? 1 : 0; // 进入读阶段时,准备接收数据

    // --- Write Address Channel ---
    assign M_AWVALID = (state == WRITE_CHANNEL) ? 1 : 0;            // 发出写地址握手信号
    assign M_AWADDR  = (state == WRITE_CHANNEL) ? address : 32'h0;  // 写地址
    assign write_addr = M_AWVALID && M_AWREADY;                     // 地址握手完成条件
    assign write_data = M_WVALID  && M_WREADY;                      // 数据握手完成条件

    // --- Write Data Channel ---
    assign M_WVALID  = (state == WRITE_CHANNEL) ? 1 : 0;            // 写数据有效
    assign M_WDATA   = (state == WRITE_CHANNEL) ? W_data : 32'h0;   // 写数据
    assign M_WSTRB   = (state == WRITE_CHANNEL) ? 4'b1111 : 0;      // 默认写全字节

    // --- Write Response Channel ---
    assign M_BREADY  = ((state == WRITE_CHANNEL)||(state == WRESP__CHANNEL)) ? 1 : 0; // Master 随时准备接收写响应


    // ============== 状态机寄存器(时序逻辑) ==============
    always_ff @(posedge ACLK) begin
        if (~ARESETN) begin
            state <= IDLE;   // 复位回到空闲态
        end else begin
            state <= next_state; // 状态跳转
        end
    end
    
    // 捕捉输入控制信号,打一拍
    always_ff @(posedge ACLK) begin
        if (~ARESETN) begin
           read_start  <= 0;
           write_start <= 0;
        end 
        else begin
           read_start  <= START_READ;   // 外部触发读
           write_start <= START_WRITE;  // 外部触发写
        end
    end
    
    // ============== 状态机组合逻辑 ==============
    always_comb begin
		case (state)
            // 空闲态:等待开始信号
            IDLE : begin
                if (write_start) begin
                    next_state = WRITE_CHANNEL;   // 进入写地址/数据阶段
                end 
                else if (read_start) begin
                    next_state = RADDR_CHANNEL;   // 进入读地址阶段
                end 
                else begin
                    next_state = IDLE;
                end
            end

            // 发读地址 → 等待读数据
			RADDR_CHANNEL  : if (M_ARVALID && M_ARREADY) 
                                 next_state = RDATA__CHANNEL;

            // 读数据握手成功 → 回到空闲
			RDATA__CHANNEL : if (M_RVALID && M_RREADY) 
                                 next_state = IDLE;

            // 写地址和写数据握手都完成 → 等待写响应
            WRITE_CHANNEL  : if (write_addr && write_data) 
                                 next_state = WRESP__CHANNEL;

            // 写响应握手成功 → 回到空闲
            WRESP__CHANNEL : if (M_BVALID && M_BREADY) 
                                 next_state = IDLE;

            // 默认兜底
			default : next_state = IDLE;
		endcase
	end
endmodule

AXI4-Lite 从设备(Slave)

  • IDLE → AW (写地址握手) → W (写数据握手) → B (写应答) → IDLE
  • IDLE → AR (读地址握手) → R (读数据握手) → IDLE
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 05/04/2024 05:52:55 PM
// Design Name: 
// Module Name: axi4_lite_slave
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: AXI4-Lite 从设备,内部包含一个寄存器阵列,用于存储和返回数据。
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

模块定义

module axi4_lite_slave #(
    parameter ADDRESS = 32,      // 地址宽度(通常为 32 bit)
    parameter DATA_WIDTH = 32    // 数据总线宽度(通常为 32 bit)
    )
    (
        // 全局信号
        input                           ACLK,      // 时钟信号
        input                           ARESETN,   // 复位信号,低有效

        //// Read Address Channel (读地址通道) 输入
        input           [ADDRESS-1:0]   S_ARADDR,  // 读地址
        input                           S_ARVALID, // 读地址有效
        // Read Data Channel (读数据通道) 输入
        input                           S_RREADY,  // 主设备准备好接收数据

        // Write Address Channel (写地址通道) 输入
        input           [ADDRESS-1:0]   S_AWADDR,  // 写地址
        input                           S_AWVALID, // 写地址有效
        // Write Data Channel (写数据通道) 输入
        input          [DATA_WIDTH-1:0] S_WDATA,   // 写数据
        input          [3:0]            S_WSTRB,   // 写字节使能(通常忽略,写全字)
        input                           S_WVALID,  // 写数据有效
        // Write Response Channel (写响应通道) 输入
        input                           S_BREADY,  // 主设备准备好接收写响应	

        // Read Address Channel 输出
        output logic                    S_ARREADY, // 从设备准备好接收读地址
        // Read Data Channel 输出
        output logic    [DATA_WIDTH-1:0]S_RDATA,   // 从设备返回的数据
        output logic         [1:0]      S_RRESP,   // 读响应(OKAY=00)
        output logic                    S_RVALID,  // 读数据有效
        // Write Address Channel 输出
        output logic                    S_AWREADY, // 从设备准备好接收写地址
        output logic                    S_WREADY,  // 从设备准备好接收写数据
        // Write Response Channel 输出
        output logic         [1:0]      S_BRESP,   // 写响应(OKAY=00)
        output logic                    S_BVALID   // 写响应有效
    );

内部变量和寄存器定义

    localparam no_of_registers = 32;               // 定义 32 个寄存器(寄存器阵列)

    logic [DATA_WIDTH-1 : 0] register [no_of_registers-1 : 0]; // 从设备内部存储器
    logic [ADDRESS-1 : 0]    addr;                 // 保存当前访问地址
    logic  write_addr;                             // 写地址握手成功标志
    logic  write_data;                             // 写数据握手成功标志

FSM(有限状态机)状态定义

    typedef enum logic [2 : 0] {
        IDLE,           // 空闲
        WRITE_CHANNEL,  // 等待写地址 + 写数据
        WRESP__CHANNEL, // 写响应
        RADDR_CHANNEL,  // 接收读地址
        RDATA__CHANNEL  // 返回读数据
    } state_type;

    state_type state , next_state;

信号分配

    // 读地址握手
    assign S_ARREADY = (state == RADDR_CHANNEL) ? 1 : 0;

    // 读数据返回
    assign S_RVALID = (state == RDATA__CHANNEL) ? 1 : 0;
    assign S_RDATA  = (state == RDATA__CHANNEL) ? register[addr] : 0; // 从寄存器阵列读数据
    assign S_RRESP  = (state == RDATA__CHANNEL) ? 2'b00 : 0;          // OKAY

    // 写地址握手
    assign S_AWREADY = (state == WRITE_CHANNEL) ? 1 : 0;
    // 写数据握手
    assign S_WREADY = (state == WRITE_CHANNEL) ? 1 : 0;
    assign write_addr = S_AWVALID && S_AWREADY;   // 地址握手成功
    assign write_data = S_WREADY && S_WVALID;     // 数据握手成功

    // 写响应
    assign S_BVALID = (state == WRESP__CHANNEL) ? 1 : 0;
    assign S_BRESP  = (state == WRESP__CHANNEL) ? 2'b00 : 0;          // OKAY

寄存器读写逻辑

    integer i;

    always_ff @(posedge ACLK) begin
        // 异步复位:清零寄存器
        if (~ARESETN) begin
            for (i = 0; i < 32; i++) begin
                register[i] <= 32'b0;
            end
        end
        else begin
            // 写操作
            if (state == WRITE_CHANNEL) begin
                register[S_AWADDR] <= S_WDATA;  // 将写数据存入地址对应寄存器
            end
            // 读操作
            else if (state == RADDR_CHANNEL) begin
                addr <= S_ARADDR;               // 保存读地址
            end
        end
    end

状态机转换

    // 状态寄存器
    always_ff @(posedge ACLK) begin
        if (!ARESETN) begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end

    // 下一个状态逻辑
    always_comb begin
        case (state)
            IDLE : begin
                if (S_AWVALID) begin
                    next_state = WRITE_CHANNEL;   // 有写请求
                end 
                else if (S_ARVALID) begin
                    next_state = RADDR_CHANNEL;   // 有读请求
                end 
                else begin
                    next_state = IDLE;
                end
            end

            RADDR_CHANNEL   : if (S_ARVALID && S_ARREADY ) next_state = RDATA__CHANNEL;
            RDATA__CHANNEL  : if (S_RVALID  && S_RREADY  ) next_state = IDLE;
            WRITE_CHANNEL   : if (write_addr && write_data) next_state = WRESP__CHANNEL;
            WRESP__CHANNEL  : if (S_BVALID  && S_BREADY  ) next_state = IDLE;

            default : next_state = IDLE;
        endcase
    end
endmodule

网站公告

今日签到

点亮在社区的每一天
去签到