AXI4 协议体系主要划分成三类主流标准:AXI4-Full、AXI4-Lite、AXI4-Stream

下面我们先介绍一下AXI4-FULL协议的内容与verilog的仿真
AXI4-FULL的接口信号
全局信号
写地址通道信号
写数据通道信号
写响应通道信号
读地址通道信号
读数据通道信号
低功耗接口信号
全局信号:

ACLK是信号输出的采样时钟,比如下面是一个XDMA的AXI4-FULL的配置,框里面的意思就是传输64bit的时钟就是125M,这个125M就是ACLK。
所有 AXI 组件使用同一个时钟 ACLK。
所有输入信号在 ACLK 上升沿 被采样。
所有输出信号必须在时钟上升沿之后才变化。
主机 / 从机接口不允许输入到输出存在组合逻辑路径。
为什么主机/从机接口不允许输入到输出存在组合逻辑路径?
组合逻辑路径会导致信号在时钟边沿前后出现毛刺、建立 / 保持时间违规,直接导致接口时序不稳定。AXI 是同步协议,要求所有输出信号必须由时钟同步,避免跨时钟域 / 亚稳态问题。本质就是:所有 AXI 接口信号,都必须是寄存器输出,不能是组合逻辑直接输出。
复位
AXI 使用低电平有效的全局复位信号 ARESETn。
复位可以异步断言,但撤销必须和 ACLK 上升沿同步。
复位期间的要求:
主机必须把 ARVALID、AWVALID、WVALID 拉低。
从机必须把 RVALID、BVALID 拉低。
其他信号可以是任意值。

复位期间要求将这些valid信号拉低的原因,一个是复位期间的信号不稳定,为了避免传输给从机。第二个是为了如果复位期间valid为高,它会一直要等到从机的ready为高后,才会置低,长时间的等待握手,会出现锁死的情况。

写地址通道

AWID:
写地址通道的 ID 标签,主机给每一笔写事务打上的 “编号” 。它的作用是用来匹配写响应通道的BID。从机必须返回和 AWID 相同的 BID,主机才能知道哪笔事务完成了。
支持乱序传输:主机可以同时发起多个不同 ID 的写事务,从机可以按任意顺序完成,只要响应的 ID 对应上就行。比如主机发起两个ID的写事务,先写0,再写1,但是从机可以先去接受事务1,再去接受事务0 。
支持不同优先级的事务:系统中可以用不同 ID 区分高 / 低优先级的写操作,方便仲裁和调度。调试时也很有用:通过 AWID 可以快速定位哪笔写事务出了问题。
AWLEN:
是突发长度的意思,指一次突发中包含的传输次数。AWLEN 的值都等于 “传输次数 - 1”。AXI3 支持的最大 AWLEN 是 15(16 次传输);AXI4 扩展到了 255(256 次传输)。
AWSIZE:突发大小,指单次传输的数据字节数,用 2 的幂次方表示。

突发总字节数 = (AWLEN + 1) × 2^AWSIZE
写数据通道

WSTRB 全称是 Write Strobe(写字节选通),是 AXI 写数据通道里的核心信号之一。
核心定义
每 1 位 WSTRB 对应 1 字节的 WDATA。
WSTRB[n] = 1:表示 WDATA 中第 n 个字节是有效的,需要写入。
WSTRB[n] = 0:表示 WDATA 中第 n 个字节是无效的,从机必须忽略这部分数据,不能写入。

WUSER 是 Write User-defined Signals(写数据通道用户自定义信号),是 AXI4 中可选的扩展信号。
核心特点
协议不定义它的功能:它的含义完全由你自己定义。
只在 AXI4 支持:AXI3 里没有这个信号。
可选信号:你可以选择不接、不使用,也可以接成任意你需要的信号。
写响应通道

写响应通道的作用是什么?
主机在 AW 通道发地址、W 通道发数据之后,从机需要通过 B 通道告诉主机:
这笔写事务有没有成功?
对应的是哪一笔写事务?
BID 信号:事务的 “身份证号,BID 必须和 AWID 匹配。比如AWID是主机发的事务ID,发了两个事务ID,分别为1和2,从机读完事务1,把BID为1返回给主机,这就是告诉主机,从机已经接收到你的事务1 。
BRESP:写事务的 “状态码”
BRESP 是 2bit 信号,告诉主机写的结果:

BVALID:从机说 “我发的响应是有效的”
BREADY:主机说 “我准备好了,能接收你的响应”
BUSER:AXI4 可选的自定义信号,和 WUSER 一样,协议不管你怎么用
写响应必须在该事务最后一个数据写完之后才返回。
读地址通道

读数据通道

低功耗接口


AXI4-FULL的事务机制
握手机制
所有 5 个通道都使用 VALID / READY 双向握手来传输地址、数据、控制信息。
规则:
信息源 用 VALID 表示 “地址 / 数据 / 控制信息有效”。
目的地 用 READY 表示 “可以接收”。
只有当 VALID 和 READY 同时为高时,传输才算发生。
三种握手情况:
VALID 先有效,READY 后来有效 → 传输发生在 READY 变高的那一拍。
READY 先有效,VALID 后来有效 → 传输发生在 VALID 变高的那一拍。
两者同时变高 → 当拍传输。
硬性规则:
源不允许等待 READY 才 assertion VALID。
VALID 一旦拉高,必须保持到握手成功为止。
目的地可以等 VALID 有效后再 assertion READY。
这里为什么源不允许等待 READY 才 assertion VALID?
是为了避免死锁,防止主机的valid需要等待从机的ready为高后才为1,但从机的ready也在等待主机的valid为1时才为1,两者依赖的关系,最后变为死锁状态。
通道之间的关系
AXI 协议只强制以下必须遵守的关系,其他时序不做限制:
写响应必须在该事务最后一个数据写完之后才返回。
读数据必须在对应读地址握手之后才返回。
必须遵守通道之间的依赖关系,防止死锁。
读事务依赖
ARVALID 不允许依赖 ARREADY。
RVALID 必须等待 AR 通道握手完成之后才允许拉高。
RVALID 不允许依赖 RREADY。
写事务依赖
AWVALID、WVALID 都不允许依赖对方的 READY。
BVALID 必须等待最后一个 W 数据握手完成之后才允许拉高。
AXI4 额外写响应依赖
BVALID 必须等待 AW 地址握手 + W 最后一个数据握手都完成才能拉高。
事务结构
地址结构
AXI 是突发型(burst-based)协议:
主机只发送第一个地址。
从机自己计算后续地址。
硬性规则:
突发不允许跨越 4KB 边界(防止跨从机)。
为什么只发第一个地址?
AXI 是突发协议,主机只需要告诉从机:
起始地址(AWADDR/ARADDR)
突发长度(AWLEN/ARLEN)
突发类型(AWBURST/ARBURST)
突发大小(AWSIZE/ARSIZE)
从机会根据这些参数,自动计算后续每一拍数据的地址,主机不需要每一拍都发地址。
比如 AWADDR=0x1000,AWSIZE=4字节,AWLEN=3,AWBURST=INCR
后续地址就是 0x1004 → 0x1008 → 0x100C,从机自己算,主机只发一次地址就行。
什么叫 “不允许跨越 4KB 边界”?
这是 AXI 为了简化系统互联设计的硬性规则:
一次 AXI 突发,不能跨越两个不同的 4KB 地址块。在很多 SoC 中,外设 / 内存都是按 4KB 划分地址空间的,跨边界可能会访问到不同的从机。
协议禁止跨边界,就可以保证一次突发只会访问同一个从机,避免出现 “前半段在 A 从机,后半段在 B 从机” 的混乱情况。
突发长度 AxLEN
AXI3:1~16 拍
AXI4:INCR 类型支持 1~256 拍
公式:Burst_Length = AxLEN + 1
突发大小 AxSIZE
表示每一拍数据的字节数:
0b000 = 1 字节
0b001 = 2 字节
0b010 = 4 字节(32bit)
0b011 = 8 字节(64bit)
以此类推。
突发类型 AxBURST
FIXED:地址不变(用于 FIFO)
INCR:自增(用于内存)
WRAP:回环(用于缓存行)
读写响应结构
RRESP / BRESP 响应码
0b00 OKAY:正常成功
0b01 EXOKAY:独占访问成功
0b10 SLVERR:从机错误
0b11 DECERR:地址解码错误(无此从机)
规则:
写事务:整个突发只返回一个响应。
读事务:每一拍数据都可以有独立响应。
即使出错,整个突发必须传输完成,不能中途停止。
数据读写结构
1. WSTRB 写字节选通
每 1bit 对应 1 字节(比如 32bit 数据对应 4bit WSTRB)
WSTRB[n] = 1 → 该字节有效,要写入从机
WSTRB[n] = 0 → 该字节无效,不写入,从机必须保持寄存器 / 内存中原来的值不变
通俗理解:WSTRB 就是一个「写字节掩码」,用来控制单次写操作中,只更新你想改的那部分数据,其他字节不动。
窄传输 / 非对齐传输
窄传输(Narrow Transfer):单次数据传输的字节数,小于数据总线的总宽度。例:32bit 总线,每次只写 1 字节(AWSIZE=0b000),就是窄传输,靠 WSTRB 实现。
非对齐传输(Unaligned Transfer):传输的起始地址,不是数据总线宽度的整数倍。例:32bit 总线(对齐地址必须是 4 的倍数),但从 0x0001 开始写数据,就是非对齐传输。
- 协议说明:
- AXI 原生支持非对齐传输
- 主机用 AWADDR/ARADDR 的低位地址表示非对齐偏移
- 从机不需要做任何特殊处理,按正常地址写入 / 读出即可
这俩都是 AXI 为了适配不同场景的灵活特性,从机不用额外写逻辑,主机用地址和 WSTRB 就能搞定。

上图解释
1. ARVALID → ARREADY
规则:主机拉高 ARVALID,不能等从机先拉高 ARREADY。
2. ARVALID → RVALID
规则:从机拉高 RVALID,必须先等 ARVALID 和 ARREADY 握手成功。
3. RVALID → RREADY
规则:从机拉高 RVALID,不能等主机先拉高 RREADY。

1. AWVALID → AWREADY & WVALID → WREADY
主机侧:拉高 AWVALID / WVALID,绝对不能等从机先拉高 AWREADY / WREADY。
主机逻辑:想发地址 / 数据,就直接把 VALID 拉起来,别等从机 “说准备好了” 才发
从机侧:拉高 AWREADY / WREADY,可以选择:
提前拉起来,等着主机发地址 / 数据;
也可以等主机拉高 AWVALID / WVALID 之后再拉。
两种都合法,只要主机别等就行。
2. AWVALID → WREADY & WVALID → AWREADY
这两条是为了防止 “地址和数据通道互相等” 死锁:
从机拉高 AWREADY,不能依赖主机的 WVALID(不能等数据来了才准备地址)
从机拉高 WREADY,不能依赖主机的 AWVALID(不能等地址来了才准备数据)
也就是说,地址和数据通道的 READY,都不能互相依赖,必须能独立准备好。
3. WVALID → BVALID(带†号,关键规则)
箭头表示:从机拉高 BVALID,必须先等 WVALID 和 WREADY 握手完成,而且必须等 WLAST 拉高。
事务属性(Transaction Attributes)
写地址通道:AWCACHE、AWPROT、AWQOS、AWREGION、AWLOCK
读地址通道:ARCACHE、ARPROT、ARQOS、ARREGION、ARLOCK
这几个信号的作用分别为:
定义访问的缓存策略
定义访问的安全权限
定义优先级 QoS
定义逻辑区域划分
定义原子锁定操作
存储器类型信号 AxCACHE(ARCACHE / AWCACHE)
功能作用
AxCACHE 是 4bit 信号,告诉总线和从机:
当前这次事务能不能缓存、能不能缓冲、是否可直通、是否可共享。
4bit 每一位含义(从高位到低位):
Bit3:Allocable 读分配
Bit2:Allocable 写分配
Bit1:Bufferable 可缓冲
Bit0:Cacheable 可缓存
关键规则
外设寄存器配置:AxCACHE = 0000
含义:不可缓存、不可缓冲、直通立即访问
适用:UART、GPIO、定时器、控制寄存器
必须每次读都读硬件真实值,不能用缓存旧数据
普通内存配置:AxCACHE = 0011
含义:可缓存、可缓冲
适用:DDR 内存、系统内存
AXI 协议要求:
主机必须正确打 AxCACHE
总线、MMU、Cache 硬件会根据这个信号决定事务怎么走。
AxCACHE 就是告诉系统:这次访问是寄存器(别缓存)还是内存(可以缓存)。
保护类型信号 AxPROT(ARPROT/AWPROT)
位定义(3bit)
Bit0:0 = 特权访问 1 = 用户访问
Bit1:0 = 安全域 1 = 非安全域
Bit2:0 = 取指令 1 = 读 / 写数据
作用
用来做权限保护、安全隔离:
区分 内核特权模式 / 普通用户模式
区分 安全世界 / 非安全世界(TrustZone)
区分 是取指令还是普通数据访问。
AxPROT 是给 CPU 操作系统做权限隔离用的,裸机 FPGA 基本不用。
锁定信号 AxLOCK(ARLOCK/AWLOCK)
定义
AXI3:2bit,支持普通访问、独占访问、锁定访问
AXI4:简化成 1bit
0 = 普通访问
1 = 独占访问(Exclusive Access)
独占访问作用
用来实现原子操作、信号量、互斥锁:
主机先发独占读
再发独占写
从机判断中间有没有被别人抢资源
没被抢才写入成功,否则失败
工程实际
普通 FPGA 流水、DDR 读写完全不用,直接设 0 即可。AxLOCK 是操作系统多任务加锁用的,单纯做 AXI 读写可以一直给 0。
QoS 服务质量 AxQOS(ARQOS/AWQOS)
功能
4bit 优先级编号,数值越大优先级越高。
作用
AXI 总线有多主机同时抢总线时:
仲裁器优先放行 QoS 值大 的事务
用来保证高清视频、DMA、实时业务优先抢占带宽
用法
普通设计全部设 4'b0000 即可;
做视频、实时数据流再调高 QoS。
区域编号 AxREGION(ARREGION/AWREGION)
功能
4bit 区域标识,用来把同一个物理从机划分成多个逻辑地址区域。
典型场景
一个 FPGA 从机,内部有多组独立寄存器组:
REGION0 给 CPU0 用
REGION1 给 CPU1 用
用同一个基地址,靠 REGION 区分逻辑空间。
AxCACHE:区分是寄存器 (0000) 还是 内存 (0011),最常用、必须会。
AxPROT:CPU 安全 / 权限隔离,FPGA 裸机直接置 0。
AxLOCK:操作系统独占锁、原子操作,普通工程置 0。
AxQOS:总线事务优先级,数值越大越优先,一般置 0。
AxREGION:外设逻辑区域划分,一般置 0。
互连和系统架构
多个主机、多个从机,怎么通过 AXI 总线互连在一起
包含:地址路由、仲裁、通道独立、乱序事务、Outstanding 未完成事务、系统死锁避免
AXI 独立通道架构
写地址通道 AW
写数据通道 W
写响应通道 B
读地址通道 AR
读数据通道 R
关键规则
五条通道互相独立,时序互不绑定,可以各自单独握手。
可以先发地址、晚点再发数据;也可以连续发多笔地址,不用等数据和响应。
这种分离设计,是为了提高总线吞吐、支持乱序、支持流水线。
事务路由与地址映射
协议内容
系统中有 AXI Interconnect(互连总线)
总线负责地址解码:根据主机发出的 AWADDR/ARADDR,判断这个地址属于哪个从机。
把地址、数据事务路由转发到对应的从机。
每个从机都被分配一段固定地址空间(比如 FIFO=0x1000,UART=0x2000)。
多主机仲裁机制
协议内容
当多个主机同时访问同一个从机(比如 CPU、DMA 同时读写 DDR),互连总线内部要做仲裁。
支持基于 AxQoS 优先级、固定优先级、轮询仲裁。
QoS 值越大,事务优先级越高,总线优先放行。
Outstanding 未完成事务(重点)
协议定义
主机可以不用等前一次事务结束,就连续发起多笔地址事务:
不用等写响应 B 回来
不用等读数据 R 回来
直接继续发下一笔 AW/AR 地址。
这些发了地址、还没收完响应 / 数据的事务,就叫 Outstanding 事务。
互连总线、从机要能缓存这些事务,最后按规则返回响应和数据。
对应到 AXI 写事务:
主机发 AWADDR=0x1000(ID=1),不用等 BRESP 回来。
主机立刻再发 AWADDR=0x2000(ID=2),也不用等响应。
总线和从机会缓存这两笔事务,处理完后按 ID 返回 BRESP。
对应到 AXI 读事务:
主机发 ARADDR=0x1000(ID=1),不用等 RDATA 回来。
主机立刻再发 ARADDR=0x2000(ID=2),也不用等数据。
从机可以先返回 ID=2 的数据,再返回 ID=1 的数据,主机靠 ID 区分。
地址空间:设备本身需要多少地址,就分多大;总线会给它分配一个更大的、对齐的连续块,防止冲突。
Outstanding 事务:主机可以连发多笔地址,不用等响应回来,总线帮你缓存着,靠 ID 匹配,效率更高。
乱序返回:不同 ID 的事务可以乱序返回,同 ID 的必须按顺序返回,主机靠 ID 分清每笔事务。
module axi4_full_master_top (
// =========================================================================
// 1. 全局信号 (Global Signals)
// =========================================================================
input wire ACLK,
input wire ARESETn,
// 控制触发:拉高一个周期启动整个 AXI 读写流程测试
input wire start_transfer,
// =========================================================================
// 2. 写地址通道 (Write Address Channel - AW)
// =========================================================================
output reg [3:0] AWID, // 写事务 ID
output reg [31:0] AWADDR, // 写起始地址
output reg [7:0] AWLEN, // 突发长度: AXI4 支持 0~255 (对应 1~256 拍)
output wire [2:0] AWSIZE, // 突发大小: 字节数 = 2^AWSIZE
output wire [1:0] AWBURST, // 突发类型: 00-FIXED, 01-INCR, 10-WRAP
output wire AWLOCK, // 原子锁定: 0-正常事务, 1-排他性(Exclusive)事务
output wire [3:0] AWCACHE, // 内存类型/缓存策略属性配置
output wire [2:0] AWPROT, // 保护级别: 区分安全/非安全、特权/用户级
output wire [3:0] AWQOS, // 服务质量 (Quality of Service) 优先级评分
output wire [3:0] AWREGION, // 区域标识符,用于从机内部多逻辑区寻址
output reg AWVALID, // 写地址有效指示
input wire AWREADY, // 从机地址接收准备就绪
// =========================================================================
// 3. 写数据通道 (Write Data Channel - W)
// =========================================================================
output reg [31:0] WDATA, // 写数据总线
output wire [3:0] WSTRB, // 写字节掩码 (Byte Strobe),对应 4 字节有效性
output reg WLAST, // 最后一拍数据标志
output reg WVALID, // 写数据有效指示
input wire WREADY, // 从机数据接收准备就绪
// =========================================================================
// 4. 写响应通道 (Write Response Channel - B)
// =========================================================================
input wire [3:0] BID, // 对应的写事务反馈 ID
input wire [1:0] BRESP, // 写响应状态: 00-OKAY, 01-EXOKAY, 10-SLVERR, 11-DECERR
input wire BVALID, // 写响应有效指示
output reg BREADY, // 主机准备好接收响应
// =========================================================================
// 5. 读地址通道 (Read Address Channel - AR)
// =========================================================================
output reg [3:0] ARID, // 读事务 ID
output reg [31:0] ARADDR, // 读起始地址
output reg [7:0] ARLEN, // 突发长度
output wire [2:0] ARSIZE, // 突发大小
output wire [1:0] ARBURST, // 突发类型
output wire ARLOCK, // 读原子锁定
output wire [3:0] ARCACHE, // 读内存缓存属性配置
output wire [2:0] ARPROT, // 读保护级别
output wire [3:0] ARQOS, // 读 QoS 优先级
output wire [3:0] ARREGION, // 读区域标识符
output reg ARVALID, // 读地址有效指示
input wire ARREADY, // 从机读地址接收准备就绪
// =========================================================================
// 6. 读数据通道 (Read Data Channel - R)
// =========================================================================
input wire [3:0] RID, // 返回数据的事务 ID
input wire [31:0] RDATA, // 读回的数据
input wire [1:0] RRESP, // 读响应状态 (与 BRESP 含义相同)
input wire RLAST, // 读传输最后一拍标志
input wire RVALID, // 读数据有效指示
output reg RREADY // 主机准备好接收读数据
);
// =========================================================================
// 静态配置/系统级高级信号赋值 (蓝框不常用信号)
// =========================================================================
assign AWSIZE = 3'b010; // 2^2 = 4 字节传输 (32位宽度)
assign ARSIZE = 3'b010;
assign AWBURST = 2'b01; // 01 = INCR (自增模式,适合 DDR/大容量存储器)
assign ARBURST = 2'b01;
assign WSTRB = 4'b1111; // 4个字节全有效
// LOCK: AXI4 取消了 AXI3 的总线锁定(01),仅保留排他性访问(10)。0 为正常访问
assign AWLOCK = 1'b0;
assign ARLOCK = 1'b0;
// CACHE 配置: 4'b0011 代表 Normal, Non-cacheable, Modifiable, Bufferable
// 这是 FPGA 与板载 DDR 进行裸数据传输(如大容量高速波形存取)最通用的规范配置
assign AWCACHE = 4'b0011;
assign ARCACHE = 4'b0011;
// PROT 配置: 3'b000 代表 [2]=0(数据访问), [1]=0(安全访问), [0]=0(非特权/用户级访问)
assign AWPROT = 3'b000;
assign ARPROT = 3'b000;
// QOS 质量评分: 4'b0000 默认不开启优先级调配。数值越大(最大15)在总线矩阵(Interconnect)中仲裁权重越高
assign AWQOS = 4'b0000;
assign ARQOS = 4'b0000;
// REGION 寻址路由: 用于当多端口 Slave 共享单根物理线时划分独立逻辑块,通常固定为 0
assign AWREGION = 4'b0000;
assign ARREGION = 4'b0000;
// =========================================================================
// 状态机与多事务控制器定义
// =========================================================================
localparam IDLE = 3'd0;
localparam WRITE_TX_1 = 3'd1; // 触发第 1 笔写事务 (ID = 4'd1)
localparam WRITE_TX_2 = 3'd2; // 触发第 2 笔写事务 (ID = 4'd2) 多事务并行
localparam WAIT_BRESP = 3'd3; // 等待两笔写事务的响应报告
localparam READ_TX = 3'd4; // 触发读事务
localparam WAIT_RDATA = 3'd5; // 接收读数据
reg [2:0] cur_state;
reg [7:0] w_data_cnt; // 用于统计当前 Burst 发送到了第几拍
reg [1:0] b_resp_cnt; // 用于统计收到了多少个写事务的 B 响应
// =========================================================================
// 主状态机控制逻辑
// =========================================================================
always @(posedge ACLK or negedge ARESETn) begin
if (!ARESETn) begin
cur_state <= IDLE;
AWVALID <= 1'b0;
AWADDR <= 32'd0;
AWLEN <= 8'd0;
AWID <= 4'd0;
WVALID <= 1'b0;
WDATA <= 32'd0;
WLAST <= 1'b0;
BREADY <= 1'b0;
ARVALID <= 1'b0;
ARADDR <= 32'd0;
ARLEN <= 8'd0;
ARID <= 4'd0;
RREADY <= 1'b0;
w_data_cnt <= 8'd0;
b_resp_cnt <= 2'd0;
end
else begin
case (cur_state)
// -------------------------------------------------------------
// IDLE: 等待启动信号
// -------------------------------------------------------------
IDLE: begin
if (start_transfer) begin
cur_state <= WRITE_TX_1;
b_resp_cnt <= 2'd0;
end
end
// -------------------------------------------------------------
// WRITE_TX_1: 下达第一笔写事务配置 (ID = 1, 地址 0x1000, 长度为 4 拍)
// -------------------------------------------------------------
WRITE_TX_1: begin
AWID <= 4'd1; // 设定事务 ID = 1
AWADDR <= 32'h0000_1000;
AWLEN <= 8'd3; // AXI 规范: 实际拍数 = AWLEN + 1 = 4 拍
AWVALID <= 1'b1;
// 高性能设计:地址和数据同时上总线发出
WVALID <= 1'b1;
WDATA <= 32'h1111_0001; // ID=1 的第 1 拍测试数据
w_data_cnt <= 8'd0;
WLAST <= 1'b0;
cur_state <= WRITE_TX_2;
end
// -------------------------------------------------------------
// WRITE_TX_2: 在第 1 笔地址尚未握手完或数据发送中,直接下达第 2 笔写地址 (ID = 2)
// 这正是 AXI 核心的多事务挂起 (Multiple Outstanding) 与管道化设计
// -------------------------------------------------------------
WRITE_TX_2: begin
// 处理第一笔写地址的握手
if (AWVALID && AWREADY) begin
// 第一笔地址被从机签收后,立刻更换为第二笔写事务地址
AWID <= 4'd2; // 事务 ID = 2
AWADDR <= 32'h0000_2000; // 写入另一个独立地址空间
AWLEN <= 8'd1; // 实际拍数 = 1 + 1 = 2 拍
AWVALID <= 1'b1; // 继续保持高,请求第二笔
cur_state <= WAIT_BRESP;
end
// 同步处理第一笔写事务的数据传输
if (WVALID && WREADY) begin
w_data_cnt <= w_data_cnt + 1'b1;
WDATA <= WDATA + 1'b1; // 模拟递增数据产生
if (w_data_cnt == AWLEN - 1) begin
WLAST <= 1'b1; // 预判下一拍是最后一拍,拉高 LAST
end
if (w_data_cnt == AWLEN) begin
// 第一笔写突发结束,由于多事务在 AXI4 中必须按序发完数据通道,
// 此处直接接入 ID=2 的数据。
WDATA <= 32'h2222_0001; // ID=2 的第 1 拍数据
w_data_cnt <= 8'd0;
WLAST <= 1'b1; // 因为 ID=2 只有 2 拍,所以第二拍就是最后一拍(w_data_cnt=1时)
// 此处在 0 拍时预判下一拍为最后,故直接拉高 LAST
end
end
end
// -------------------------------------------------------------
// WAIT_BRESP: 监控写数据流彻底发完,并接收从机返回的多个写响应
// -------------------------------------------------------------
WAIT_BRESP: begin
// 1. 确保第二笔地址通道握手完成
if (AWVALID && AWREADY) begin
AWVALID <= 1'b0; // 两笔写指令全部下达成功,撤销请求
end
// 2. 继续传输完第二笔写事务的数据流
if (WVALID && WREADY) begin
w_data_cnt <= w_data_cnt + 1'b1;
WDATA <= WDATA + 1'b1;
if (w_data_cnt == 8'd1) begin // ID=2 共计两拍(0和1)
WVALID <= 1'b0;
WLAST <= 1'b0;
BREADY <= 1'b1; // 数据全发完了,主机开启响应接收大门
end
end
// 3. 接收响应 (B 通道)
// 核心逻辑:从机回响应可能乱序!比如 ID=2 的轻量任务先写完,BID=2 就会先回来
if (BVALID && BREADY) begin
b_resp_cnt <= b_resp_cnt + 1'b1;
// 硬件行为诊断打印
if (BRESP == 2'b00) begin
$display("[AXI4 MASTER] 事务 ID=%d 写入成功(OKAY)!", BID);
end else begin
$display("[AXI4 MASTER] 错误!事务 ID=%d 写入失败,错误码=%b", BID, BRESP);
end
// 当发出去的两笔事务都拿到了各自的收据(B响应)后,前进到读控制状态
if (b_resp_cnt == 2'd1) begin
BREADY <= 1'b0;
cur_state <= READ_TX;
end
end
end
// -------------------------------------------------------------
// READ_TX: 发起读地址事务测试
// -------------------------------------------------------------
READ_TX: begin
ARID <= 4'd5; // 读事务配置一个不一样的 ID = 5
ARADDR <= 32'h0000_1000; // 尝试读回刚才写入 0x1000 的数据
ARLEN <= 8'd3; // 连续读 4 拍数据回来
ARVALID <= 1'b1;
if (ARVALID && ARREADY) begin
ARVALID <= 1'b0; // 读地址任务下达成功
RREADY <= 1'b1; // 主机立刻准备好接收读数据通道的数据
cur_state <= WAIT_RDATA;
end
end
// -------------------------------------------------------------
// WAIT_RDATA: 循环接收读回的数据,并监控 RLAST 边界
// -------------------------------------------------------------
WAIT_RDATA: begin
if (RVALID && RREADY) begin
// 诊断打印读到的数据,通过 RID 校验是否和刚才发出去的 ARID 一致
$display("[AXI4 MASTER] 接收读数据: ID=%d, Data=0x%h, 状态RESP=%b", RID, RDATA, RRESP);
if (RLAST) begin // 触碰到从机给出的最后一拍边界
RREADY <= 1'b0;
cur_state <= IDLE; // 整个读写生命周期圆满结束
$display("[AXI4 MASTER] AXI4-Full 五通道联合测试事务全部成功圆满完成!");
end
end
end
default: cur_state <= IDLE;
endcase
end
end
endmodule

2080

被折叠的 条评论
为什么被折叠?



