4. AHB-Lite总线协议
AHB-Lite,说白了就是ARM公司为高性能系统准备的一条“高速公路”。它只支持一个主机,所以仲裁逻辑简单很多。我在做第一个SoC项目时,用的就是AHB-Lite。当时觉得它比APB复杂不少,但跑起来是真的快。
4.1 AHB-Lite信号定义
先看信号。AHB-Lite的信号可以分为几组:全局信号、地址/控制信号、数据信号、还有响应信号。我习惯把它们记成“谁控制、谁传输、谁应答”。
| 信号名 | 方向 | 描述 |
|---|---|---|
| HCLK | 输入 | 总线时钟,所有操作同步于此 |
| HRESETn | 输入 | 低电平复位 |
| HADDR[31:0] | 主机→从机 | 32位地址总线 |
| HWRITE | 主机→从机 | 写使能:1为写,0为读 |
| HSIZE[2:0] | 主机→从机 | 传输大小:字节、半字、字等 |
| HBURST[2:0] | 主机→从机 | 突发类型:单次、4拍、8拍、16拍等 |
| HWDATA[31:0] | 主机→从机 | 写数据总线 |
| HRDATA[31:0] | 从机→主机 | 读数据总线 |
| HREADY | 从机→主机 | 传输完成信号,高有效 |
| HRESP[1:0] | 从机→主机 | 传输响应:OKAY、ERROR、RETRY、SPLIT |
| HSEL | 译码器→从机 | 从机选择信号 |
关键点:AHB-Lite只有HRESP[1:0]中的OKAY和ERROR两种响应。RETRY和SPLIT在Lite版本中不用。我刚开始设计时,还傻傻地给从机加了RETRY逻辑,结果仿真一直挂死。后来查手册才发现,Lite版本不支持这俩。
4.2 单次传输与突发传输
单次传输,就是一次只传一笔数据。地址发出去,数据回来,完事。突发传输则是一次地址,连续传多笔数据。你想想看,如果CPU要加载一段代码到Cache,用单次传输得发几十次地址,效率太低了。
突发传输的类型由HBURST决定:
| HBURST[2:0] | 类型 | 说明 |
|---|---|---|
| 000 | SINGLE | 单次传输 |
| 001 | INCR | 未指定长度的增量突发 |
| 010 | WRAP4 | 4拍回环突发 |
| 011 | INCR4 | 4拍增量突发 |
| 100 | WRAP8 | 8拍回环突发 |
| 101 | INCR8 | 8拍增量突发 |
| 110 | WRAP16 | 16拍回环突发 |
| 111 | INCR16 | 16拍增量突发 |
回环突发(WRAP)常用于Cache line fill。地址会回绕到对齐边界。举个例子,WRAP4在字传输时,地址只会在4字对齐的范围内循环。增量突发(INCR)则是一直往上加。
我的经验:设计从机时,一定要检查HBURST和HSIZE的组合是否合法。比如HSIZE=3(64位)配HBURST=WRAP4,地址对齐就有讲究。我曾经遇到一个从机,没检查这个组合,结果地址计算出了bug,仿真跑了一周才发现。
4.3 流水线操作
AHB-Lite最牛的地方,就是流水线操作。地址阶段和数据阶段是分开的。当前拍的地址,对应的数据在下一拍才出现。这样总线可以一直发地址,不用等数据回来。
我画个图你就明白了:
看到没?地址A在T1发出,数据A在T2才回来。但T2同时可以发地址B。这样总线利用率就上去了。如果从机来不及处理,可以把HREADY拉低,插入等待周期。
注意:流水线虽然快,但设计从机时要注意地址和数据的对应关系。我曾经见过一个从机,地址阶段用了寄存器,数据阶段也用了寄存器,结果地址和数据错了一拍。查了三天才发现是流水线深度没对齐。
4.4 从机接口设计
从机接口设计,是AHB-Lite中最容易出问题的地方。我总结了一个“三步法”:
- 地址译码:根据HADDR和HSEL判断是否命中本从机
- 传输控制:根据HWRITE、HSIZE、HBURST决定读写操作
- 数据响应:准备好HRDATA或接收HWDATA,并给出HREADY和HRESP
下面是一个简单的从机接口代码框架:
module ahb_slave (
input HCLK,
input HRESETn,
input HSEL,
input [31:0] HADDR,
input HWRITE,
input [2:0] HSIZE,
input [2:0] HBURST,
input [31:0] HWDATA,
output [31:0] HRDATA,
output HREADY,
output [1:0] HRESP
);
// 状态机
reg [1:0] state;
localparam IDLE = 2'b00,
BUSY = 2'b01;
// 地址锁存
reg [31:0] addr_r;
always @(posedge HCLK or negedge HRESETn) begin
if (!HRESETn)
addr_r <= 32'b0;
else if (HSEL && HREADY)
addr_r <= HADDR;
end
// 读写控制
reg we_r;
always @(posedge HCLK or negedge HRESETn) begin
if (!HRESETn)
we_r <= 1'b0;
else if (HSEL && HREADY)
we_r <= HWRITE;
end
// 数据输出(读操作)
reg [31:0] rdata;
always @(*) begin
case (addr_r[3:2]) // 字对齐地址
2'b00: rdata = mem[0];
2'b01: rdata = mem[1];
2'b10: rdata = mem[2];
2'b11: rdata = mem[3];
endcase
end
assign HRDATA = rdata;
assign HREADY = 1'b1; // 简单从机,总是准备好
assign HRESP = 2'b00; // OKAY
endmodule
避坑指南:我曾经设计一个从机,HREADY直接拉高。结果主机发突发传输时,从机来不及处理,数据全乱了。后来我加了FIFO缓冲,才解决问题。所以,如果你的从机处理速度跟不上总线,一定要用HREADY插入等待周期。
从机接口设计时,还有几个细节要注意:
- 地址对齐:HSIZE=0(字节)时地址任意;HSIZE=1(半字)时地址必须2字节对齐;HSIZE=2(字)时地址必须4字节对齐
- 突发边界:WRAP4在4字边界回绕,INCR4则一直递增。从机要能正确处理地址回绕
- 响应时序:HRESP必须在HREADY为高时有效。如果HREADY拉低,HRESP要保持不变
嗯,AHB-Lite其实不难。关键是理解流水线的思想,以及从机如何与主机配合。我当年第一次写AHB从机时,也是对着手册一行一行地看。现在回想起来,其实就那么几个要点。你只要把地址、数据、控制、响应这四类信号理清楚,设计起来就顺了。
可可精品资料库