第三章:AXI通道详解——五条通道的“分工协作”
各位同学,今天我们来聊聊AXI总线最核心的部分——那五条通道。说实话,我刚接触AXI时,看着这五条通道的名字就头大。读地址、读数据、写地址、写数据、写响应……感觉像五个兄弟各干各的。但等你真正在项目里调过几次时序,你就会发现,这五条通道的设计其实非常巧妙。
我个人习惯把AXI的通道比作一个“快递系统”。你想想看,发快递需要填地址单(地址通道),需要把包裹寄出去(数据通道),最后还要确认签收(响应通道)。AXI也是这个道理。只不过它把读和写分开了,这样就能同时进行读写操作,效率高了不少。
3.1 读地址通道(AR)——告诉从设备“我要什么”
读地址通道,说白了就是主设备告诉从设备:“嘿,我要读你某个地址的数据。”这个通道上传递的信号不多,但每个都很关键。
| 信号 | 宽度 | 说明 |
|---|---|---|
| ARADDR | 地址宽度 | 读操作的起始地址 |
| ARLEN | 8位 | 突发传输长度(实际传输次数 = ARLEN + 1) |
| ARSIZE | 3位 | 每次传输的字节数(2^ARSIZE) |
| ARBURST | 2位 | 突发类型:00=FIXED, 01=INCR, 10=WRAP |
| ARVALID | 1位 | 主设备表示地址有效 |
| ARREADY | 1位 | 从设备表示可以接收地址 |
这里有个坑,我必须要提醒大家。ARLEN的值是“传输次数减1”。比如你想读4个数据,ARLEN要设成3。我在项目中遇到过一位同事,他直接把ARLEN设成了4,结果从设备多传了一个数据,整个系统就乱了。嗯,这种错误排查起来特别费劲。
3.2 读数据通道(R)——从设备把数据送回来
读数据通道是从设备响应主设备的通道。主设备发完地址后,从设备就开始往回传数据。这个通道上除了数据本身,还有一个很重要的信号——RLAST。
RLAST信号会在最后一次数据传输时拉高。为什么需要这个信号?因为突发传输的长度是可变的,从设备需要告诉主设备:“这是最后一个数据了,别再等了。”我刚开始做AXI设计时,曾经忘记检查RLAST信号,结果主设备一直在等下一个数据,系统就卡死了。
核心要点:读数据通道的握手协议是RVALID和RREADY。主设备准备好接收数据时拉高RREADY,从设备有数据要发送时拉高RVALID。两者同时为高,数据就传输成功了。
3.3 写地址通道(AW)——告诉从设备“我要写哪里”
写地址通道和读地址通道非常相似,信号也差不多。AWADDR、AWLEN、AWSIZE、AWBURST……这些信号的含义和读地址通道完全一样。我建议你直接把读地址通道的信号名中的“AR”换成“AW”,就得到写地址通道的信号了。
不过有一点要注意:写地址通道和写数据通道是独立的。也就是说,主设备可以先发地址,再发数据;也可以先发数据,再发地址;甚至可以同时发。这种灵活性在实际设计中非常有用。
我的经验:在大多数设计中,我习惯先发地址再发数据。这样从设备可以提前准备好存储空间,减少等待时间。但如果你在做高性能设计,可以考虑让地址和数据同时发出,能节省一个时钟周期。
3.4 写数据通道(W)——把数据写进去
写数据通道是五条通道中唯一一个带“字节使能”的通道。WSTRB信号就是干这个用的。每个bit对应一个字节,1表示这个字节有效,0表示无效。
举个例子:你想写一个32位数据,但只想修改低16位。那么WSTRB就应该设为4'b0011。这样从设备就知道,只有低两个字节需要更新,高两个字节保持不变。
我曾经在一个项目中遇到过这样的问题:一个DMA控制器往内存写数据时,WSTRB信号一直设为全1。结果每次写操作都把整个32位覆盖了,导致其他数据被意外修改。排查了整整两天才发现是WSTRB的问题。从那以后,我每次设计都会特别检查WSTRB的生成逻辑。
// 写数据通道的典型时序
// 假设我们要写4个32位数据,地址从0x1000开始
// 时钟周期1: AWADDR = 0x1000, AWVALID = 1, AWREADY = 1
// 时钟周期2: WDATA = data0, WSTRB = 4'b1111, WVALID = 1, WREADY = 1
// 时钟周期3: WDATA = data1, WSTRB = 4'b1111, WVALID = 1, WREADY = 1
// 时钟周期4: WDATA = data2, WSTRB = 4'b1111, WVALID = 1, WREADY = 1
// 时钟周期5: WDATA = data3, WSTRB = 4'b1111, WVALID = 1, WLAST = 1, WREADY = 1
3.5 写响应通道(B)——确认写操作完成
写响应通道是五条通道中最简单的一条。它只有一个信号——BRESP,用来表示写操作的结果。BRESP只有两位,取值如下:
| BRESP值 | 含义 |
|---|---|
| 00 | OKAY:正常完成 |
| 01 | EXOKAY:独占访问成功 |
| 10 | SLVERR:从设备错误 |
| 11 | DECERR:地址译码错误 |
你可能会问:“为什么读数据通道没有响应信号?”其实读数据通道的响应是通过RRESP信号来传递的,它和BRESP的编码方式完全一样。只不过读数据通道的响应是和数据一起传回来的,而写数据通道的响应是单独一个通道。
重要提醒:写响应通道的BVALID信号必须等到所有写数据都传输完成后才能拉高。也就是说,从设备必须收到WLAST信号后,才能发出写响应。这个顺序不能乱。
3.6 五条通道的“握手”机制
AXI的每条通道都使用独立的握手协议。VALID和READY信号就是用来握手的。VALID表示发送方有数据要发送,READY表示接收方可以接收数据。只有当VALID和READY同时为高时,数据才被传输。
这里有个小技巧:如果你想让接收方一直处于“准备好”状态,可以把READY信号固定拉高。但这样做有个风险——如果发送方还没准备好,VALID一直为低,那READY拉高也没用。我建议在大多数情况下,让READY信号在VALID为高后的下一个时钟周期再拉高,这样更稳妥。
好了,五条通道的基本内容就讲到这里。你想想看,AXI的设计其实很人性化——读和写分开,地址和数据分开,还有专门的响应通道。这种设计让总线可以同时处理多个事务,效率自然就高了。
我的建议:刚开始学习AXI时,不要急着看复杂的互联结构。先把这五条通道的握手时序搞明白。你可以用Vivado或者ModelSim写几个简单的测试用例,模拟一下读写操作。等你能熟练画出每条通道的时序图,AXI就算入门了。
这张图把五条通道的关系画得很清楚了。你看,读操作需要两条通道(AR和R),写操作需要三条通道(AW、W和B)。这就是为什么AXI能支持同时读写——因为读和写的通道是完全独立的。
最后再啰嗦一句:在实际项目中,你可能会遇到各种奇怪的时序问题。比如地址通道的握手超时、数据通道的乱序返回、写响应迟迟不来……这些问题的根源,往往就是对这五条通道的理解不够深入。把基础打牢,后面才能走得更远。