4. AHB协议详解(上):AHB总线架构、AHB主从设备、AHB传输类型(SINGLE/INCR/WRAP)、AHB流水线操作
各位同学,欢迎来到AHB协议的深度解析。这一章我们重点聊聊AHB总线——AMBA家族里最核心、最常用的高性能总线。说实话,我做了十几年SoC设计,接触最多的就是AHB。无论是CPU访问内存,还是DMA搬运数据,背后都是AHB在默默工作。
AHB的全称是Advanced High-performance Bus。你想想看,为什么叫“高性能”?因为它支持流水线操作、突发传输,还能同时处理多个主设备。嗯,咱们一个一个来拆解。
4.1 AHB总线架构
AHB总线架构其实不复杂。我习惯把它想象成一个“中央枢纽”——所有高速设备都挂在这个枢纽上。典型的AHB系统包含以下几个部分:
- AHB主设备(Master):发起传输的模块,比如CPU、DMA控制器
- AHB从设备(Slave):响应传输的模块,比如SRAM、Flash、外设寄存器
- AHB仲裁器(Arbiter):决定哪个主设备获得总线控制权
- AHB译码器(Decoder):根据地址选择对应的从设备
- AHB多路选择器(Mux):将选中的从设备数据路由回主设备
说白了,主设备想读写数据,先向仲裁器申请总线。仲裁器批准后,主设备发出地址和控制信号。译码器根据地址选中对应的从设备,然后数据就在主从设备之间流动。整个过程就像一场精心编排的交通调度。
核心要点:AHB总线采用共享总线架构,所有主从设备共用地址总线和数据总线。这意味着同一时刻只能有一个主设备占用总线。
我在项目中遇到过一个问题:两个高性能主设备同时请求总线,如果仲裁策略设计不好,低优先级的设备可能会被“饿死”。所以仲裁器的设计非常关键,后面我们会专门讲。
4.2 AHB主从设备
咱们来聊聊主设备和从设备的具体行为。我刚开始学AHB时,总觉得主设备和从设备就是“谁发起谁响应”这么简单。但实际项目中,细节远比想象中多。
4.2.1 主设备(Master)
主设备是总线的“发起者”。它负责:
- 发起读写请求
- 提供地址和控制信号
- 写操作时提供数据
- 响应从设备的等待和错误信号
主设备通过一组信号与总线交互:
| 信号名 | 方向 | 描述 |
|---|---|---|
| HCLK | 输入 | 总线时钟,所有操作同步于此 |
| HRESETn | 输入 | 复位信号,低电平有效 |
| HADDR[31:0] | 输出 | 32位地址总线 |
| HWDATA[31:0] | 输出 | 写数据总线 |
| HRDATA[31:0] | 输入 | 读数据总线 |
| HWRITE | 输出 | 写使能:1=写,0=读 |
| HSIZE[2:0] | 输出 | 传输大小:字节、半字、字等 |
| HBURST[2:0] | 输出 | 突发类型:SINGLE、INCR、WRAP等 |
| HTRANS[1:0] | 输出 | 传输类型:IDLE、BUSY、NONSEQ、SEQ |
| HREADY | 输入 | 从设备准备好信号 |
| HRESP[1:0] | 输入 | 从设备响应:OKAY、ERROR、RETRY、SPLIT |
个人经验:我习惯在设计主设备状态机时,把HREADY作为关键判断条件。如果HREADY为低,主设备必须保持当前地址和控制信号不变,直到从设备准备好。这个细节很容易被忽略,但一旦出错,整个总线就会乱套。
4.2.2 从设备(Slave)
从设备是总线的“响应者”。它负责:
- 根据地址译码判断是否被选中
- 执行读写操作
- 通过HREADY控制传输节奏
- 通过HRESP返回传输状态
从设备的核心信号包括:
- HSELx:片选信号,由译码器产生
- HREADYOUT:从设备输出的准备好信号
- HRESP:响应信号
我曾经踩过一个坑:从设备的HREADY信号在复位后必须为高,否则总线会死锁。因为主设备在等待HREADY为高才能继续,如果从设备一直拉低HREADY,整个系统就卡住了。
4.3 AHB传输类型
AHB支持三种基本的传输类型:SINGLE、INCR和WRAP。这三种类型对应不同的应用场景。我个人觉得,理解它们的关键在于“地址变化规律”。
4.3.1 SINGLE传输
SINGLE就是单次传输。一次只传一个数据,地址不递增。说白了就是“点对点”的读写。
// SINGLE传输示例
// 地址:0x1000,传输一个字
HADDR = 0x1000;
HWRITE = 1; // 写操作
HSIZE = 0b010; // 字传输(4字节)
HBURST = 0b000; // SINGLE
HTRANS = NONSEQ; // 非连续传输
// 一个时钟周期后,数据写入0x1000
SINGLE传输适用于访问寄存器、状态寄存器等不需要连续地址的场景。
4.3.2 INCR传输(增量突发)
INCR是增量突发传输。地址在每个节拍后递增,递增步长由HSIZE决定。INCR又分为INCR4、INCR8、INCR16和未指定长度的INCR。
| HBURST[2:0] | 类型 | 长度 | 地址变化 |
|---|---|---|---|
| 000 | SINGLE | 1 | 不变 |
| 001 | INCR | 未指定 | 递增,直到主设备停止 |
| 010 | WRAP4 | 4 | 回环 |
| 011 | INCR4 | 4 | 递增 |
| 100 | WRAP8 | 8 | 回环 |
| 101 | INCR8 | 8 | 递增 |
| 110 | WRAP16 | 16 | 回环 |
| 111 | INCR16 | 16 | 递增 |
// INCR4传输示例(字传输)
// 起始地址:0x1000
// 传输4个字:0x1000, 0x1004, 0x1008, 0x100C
HADDR = 0x1000; // 第一个节拍
HADDR = 0x1004; // 第二个节拍
HADDR = 0x1008; // 第三个节拍
HADDR = 0x100C; // 第四个节拍
INCR传输非常适合访问连续内存区域,比如DMA搬运数据、CPU执行代码等。
4.3.3 WRAP传输(回环突发)
WRAP是回环突发传输。地址递增到边界后,自动回绕到起始地址。边界由突发长度和传输大小决定。
举个例子:WRAP4传输,字传输(4字节),起始地址0x100C。边界计算如下:
- 突发长度=4,传输大小=4字节,所以边界=4*4=16字节
- 起始地址0x100C,对齐到16字节边界是0x1000
- 地址序列:0x100C → 0x1010 → 0x1014 → 0x1000(回环)
// WRAP4传输示例
// 起始地址:0x100C
// 传输4个字:0x100C, 0x1010, 0x1014, 0x1000
HADDR = 0x100C; // 第一个节拍
HADDR = 0x1010; // 第二个节拍
HADDR = 0x1014; // 第三个节拍
HADDR = 0x1000; // 第四个节拍(回环)
注意:WRAP传输的起始地址必须与边界对齐。如果起始地址不对齐,从设备的行为是未定义的。我曾经在项目中因为地址计算错误,导致WRAP传输读回了错误的数据,排查了整整两天才发现是地址对齐的问题。
WRAP传输常用于Cache line fill操作。Cache一次加载一整行数据,地址回环正好符合Cache的行结构。
4.4 AHB流水线操作
AHB最牛的地方就是流水线操作。它把地址阶段和数据阶段分开,让总线可以同时处理多个传输。你想想看,这就像工厂的流水线——上一个产品还在组装,下一个产品的零件已经开始准备了。
AHB的流水线分为两个阶段:
- 地址阶段(Address Phase):主设备输出地址和控制信号
- 数据阶段(Data Phase):从设备返回数据或接收数据
关键点:地址阶段和数据阶段相差一个时钟周期。也就是说,当前传输的数据阶段,正好是下一个传输的地址阶段。
// AHB流水线时序示例
// 时钟周期1:传输A的地址阶段
// 时钟周期2:传输A的数据阶段 + 传输B的地址阶段
// 时钟周期3:传输B的数据阶段 + 传输C的地址阶段
// 伪代码表示
always @(posedge HCLK) begin
if (HREADY) begin
// 当前传输完成,准备下一个
current_addr <= next_addr;
current_data <= next_data;
end
end
流水线操作的好处是提高了总线利用率。如果没有流水线,每个传输需要两个时钟周期(一个地址+一个数据)。有了流水线,平均每个传输只需要一个时钟周期(地址和数据重叠)。
核心要点:流水线操作依赖于HREADY信号。如果从设备需要插入等待周期,它会在数据阶段拉低HREADY。此时,地址阶段也会被冻结——下一个传输的地址必须保持,直到当前传输完成。
我记得有一次调试一个AHB接口的IP,发现总线吞吐率上不去。后来用示波器抓波形,发现从设备的HREADY经常被拉低,导致流水线被打断。优化了从设备的响应速度后,吞吐率提升了30%。
嗯,这一章的内容就到这里。AHB协议还有很多细节,比如仲裁机制、SPLIT传输、错误处理等,我们下一章继续聊。
避坑指南:我曾经在设计AHB从设备时,忘记处理HTRANS=BUSY的情况。结果主设备插入BUSY周期时,从设备误以为传输结束,导致数据丢失。记住:从设备必须正确处理所有HTRANS类型,包括IDLE和BUSY。
公众号:蓝海资料掘金营,微信deep3321