2、常见总线协议(一):AMBA AHB总线协议详解、信号定义与传输时序
各位同学,今天我们来聊聊AMBA AHB总线。说实话,AHB是我在项目里打交道最多的总线之一。记得我刚入行那会儿,第一次接触AHB时被那一堆信号搞得晕头转向。后来做多了才发现,AHB的设计其实非常优雅——它把复杂的总线操作抽象成了几个核心阶段,理解了这个,你就抓住了它的精髓。
2.1 AHB总线概述
AHB,全称Advanced High-performance Bus,是ARM公司AMBA总线家族里的高性能选手。它主要用来连接高性能模块,比如CPU、DMA控制器、外部存储器接口这些。
我个人习惯把AHB想象成一个高速公路系统:
- 主机(Master):就像路上的车,发起传输请求
- 从机(Slave):就像目的地,响应传输请求
- 仲裁器(Arbiter):就像交通警察,决定谁先走
- 译码器(Decoder):就像导航,告诉你去哪个出口
为什么要搞这么复杂?你想想看,一个SoC里可能有十几个模块同时想访问内存,没有一套规则的话,那不乱套了?
核心特点:
- 高性能:支持突发传输,一次地址可以传多笔数据
- 流水线操作:地址阶段和数据阶段可以重叠
- 多主机:支持多个总线主机,通过仲裁决定谁占用总线
- 非三态实现:所有信号都是点到点连接,方便综合
2.2 信号定义详解
AHB的信号说多不多,说少不少。我建议你先把核心信号记住,其他的用到再查。下面这张表是我自己整理的,项目里经常用到。
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
| HCLK | 输入 | 1 | 总线时钟,所有信号都在HCLK上升沿采样 |
| HRESETn | 输入 | 1 | 复位信号,低电平有效 |
| HADDR[31:0] | 主机→从机 | 32 | 地址总线 |
| HTRANS[1:0] | 主机→从机 | 2 | 传输类型:00=IDLE, 01=BUSY, 10=NONSEQ, 11=SEQ |
| HWRITE | 主机→从机 | 1 | 读写指示:1=写,0=读 |
| HSIZE[2:0] | 主机→从机 | 3 | 传输大小:000=8bit, 001=16bit, 010=32bit... |
| HBURST[2:0] | 主机→从机 | 3 | 突发类型:000=单次, 001=INCR, 010=WRAP4... |
| HWDATA[31:0] | 主机→从机 | 32 | 写数据总线 |
| HRDATA[31:0] | 从机→主机 | 32 | 读数据总线 |
| HREADY | 从机→主机 | 1 | 传输完成指示:1=完成,0=需要等待 |
| HRESP[1:0] | 从机→主机 | 2 | 传输响应:00=OKAY, 01=ERROR, 10=RETRY, 11=SPLIT |
| HSELx | 译码器→从机 | 1 | 从机选择信号 |
我的小技巧:刚开始记不住信号?你就记住三组:地址相关(HADDR、HSIZE、HBURST)、数据相关(HWDATA、HRDATA)、控制相关(HTRANS、HWRITE、HREADY、HRESP)。分组记忆会轻松很多。
2.3 传输时序详解
AHB的传输分为两个阶段:地址阶段和数据阶段。地址阶段只占一个时钟周期,数据阶段可能需要多个周期(如果从机插入等待的话)。
嗯,这里要注意:地址阶段和数据阶段是流水线重叠的。也就是说,当前传输的数据阶段,和下一笔传输的地址阶段,发生在同一个时钟周期里。这样做的好处很明显——总线利用率更高。
2.3.1 基本读传输
先看一个最简单的单次读操作:
// 时序描述(伪代码)
// Cycle 1: 主机驱动地址和控制信号
HADDR = 0x1000
HWRITE = 0 // 读操作
HTRANS = NONSEQ // 新的一笔传输
HSIZE = 010 // 32位传输
// Cycle 2: 从机返回数据
HRDATA = 0xA5A5A5A5
HREADY = 1 // 一次完成,无需等待
我在项目中遇到过一个问题:从机响应太慢,HREADY一直拉低,结果主机卡在那里动不了。后来查了协议才发现,从机最多只能插入16个等待周期,否则主机可能会超时。这个坑大家要注意。
2.3.2 基本写传输
写操作和读操作类似,但数据方向相反:
// Cycle 1: 地址阶段
HADDR = 0x2000
HWRITE = 1 // 写操作
HWDATA = 0x12345678 // 写数据在地址阶段就准备好
// Cycle 2: 数据阶段
HREADY = 1 // 写完成
曾经踩过的坑:写操作时,HWDATA必须在地址阶段的下一个时钟沿就准备好。有些新手会把HWDATA延迟一拍,结果数据对不上。记住:地址阶段之后紧跟着的就是数据阶段,没有缓冲时间。
2.3.3 突发传输
突发传输是AHB的亮点。一次地址,多笔数据,效率高很多。常见的突发类型有:
- INCR4/INCR8/INCR16:递增突发,地址连续递增
- WRAP4/WRAP8/WRAP16:回环突发,地址在边界处回绕
- INCR:未指定长度的递增突发
举个例子,4拍的递增突发:
// 地址阶段(Cycle 1)
HADDR = 0x1000
HBURST = 011 // INCR4
HTRANS = NONSEQ // 第一拍
// 数据阶段 + 地址阶段重叠
// Cycle 2: 传第一笔数据,同时发第二笔地址
HRDATA = D0
HADDR = 0x1004 // 地址自动递增
HTRANS = SEQ // 后续传输
// Cycle 3: 传第二笔数据,同时发第三笔地址
HRDATA = D1
HADDR = 0x1008
HTRANS = SEQ
// Cycle 4: 传第三笔数据,同时发第四笔地址
HRDATA = D2
HADDR = 0x100C
HTRANS = SEQ
// Cycle 5: 传第四笔数据
HRDATA = D3
你想想看,如果不用突发,4笔数据需要4个地址阶段+4个数据阶段=8个周期。用了突发,只需要1个地址阶段+4个数据阶段=5个周期。效率提升了将近一倍。
2.4 传输类型详解
HTRANS[1:0]这个信号很多人搞不清楚,我重点说一下:
| HTRANS | 类型 | 说明 |
|---|---|---|
| 00 | IDLE | 主机不占用总线,从机忽略地址和数据 |
| 01 | BUSY | 主机在突发传输中间插入空闲周期,地址保持 |
| 10 | NONSEQ | 新的一笔传输,地址与上一笔无关 |
| 11 | SEQ | 突发传输的后续拍,地址自动递增 |
重点提醒:BUSY状态只在突发传输中间使用。我曾经见过一个设计,在单次传输里插了BUSY,结果从机直接报错。协议里写得很清楚:BUSY只能出现在SEQ之后,而且地址必须保持不变。
2.5 响应信号HRESP
从机通过HRESP告诉主机传输结果。常见的响应有:
- OKAY:传输成功,这是最常见的
- ERROR:传输失败,比如访问了不存在的地址
- RETRY:从机忙,请重试(不改变传输顺序)
- SPLIT:从机忙,请重试(可以改变传输顺序)
我个人建议:除非你有特殊需求,否则尽量只用OKAY和ERROR。RETRY和SPLIT的实现比较复杂,容易引入死锁。我在一个项目里就吃过RETRY的亏——主机重试了三次都失败,最后超时复位了。
2.6 知识体系总览
下面这张图是我用SVG画的,把AHB的核心知识点串了起来。你可以把它当作复习提纲:
2.7 实战经验总结
最后,分享几个我在项目中积累的经验:
- 地址对齐很重要:32位传输时,地址必须4字节对齐。我曾经见过一个bug,HSIZE=010(32位)但HADDR=0x1001,结果从机返回的数据全是乱的。
- HREADY信号要小心处理:从机拉低HREADY时,所有信号必须保持稳定。我见过一个设计,HREADY拉低后地址还在变,结果数据全对不上。
- 突发长度别超限:INCR16最多16拍,WRAP4最多4拍。超限的话,从机可能直接报ERROR。
- 复位后先发IDLE:主机复位后,第一笔传输必须是IDLE,不能直接发NONSEQ。这是协议规定的,不遵守的话仲裁器可能不给你总线权限。
我的建议:如果你刚开始学AHB,先别急着看所有细节。把单次读写和INCR4突发搞明白,其他的都是变种。等你做了一两个IP集成项目后,自然就熟练了。
好了,AHB的内容就讲到这里。下一章我们会讲APB总线,那个比AHB简单多了,但应用场景完全不同。到时候再聊。
可可精品资料库