AXI4-Lite Top 模块
本文的分析基于项目:https://github.com/arhamhashmi01/Axi4-lite/tree/main/Axi4-lite-vivado
寄存器转换级略图 RTL SCHEMATIC:
重新涂色与标注的效果:
注:常见一种做法是 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 状态机,能发起 一次读事务 或 一次写事务,并根据握手机制自动切换状态。状态机流程:
- IDLE → 等待外部触发
- WRITE_CHANNEL → 发送地址和数据
- WRESP__CHANNEL → 等待写响应
- RADDR_CHANNEL → 发送读地址
- 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