3、AHB协议详解(一):AHB信号定义、单次传输与流水线、地址与数据相位

各位同学,今天我们来啃AHB协议这块硬骨头。说实话,AHB是我在项目中打交道最多的总线协议之一,也是很多新人容易踩坑的地方。我个人习惯把AHB协议拆成三块来理解:信号怎么接、传输怎么走、时序怎么对。咱们今天先讲前两块。

3.1 AHB信号定义:别被一堆信号吓到

第一次看AHB协议文档,你可能会被几十个信号搞得头晕。其实说白了,核心信号就那么几类。我当年刚入行时,师傅跟我说:「你先记住这10个信号,其他的都是衍生品。」

咱们先看最关键的几个:

信号名 方向 宽度 说明
HCLK 输入 1 总线时钟,所有信号都在上升沿采样
HRESETn 输入 1 异步复位,低有效
HADDR[31:0] Master→Slave 32 地址总线
HWDATA[31:0] Master→Slave 32 写数据总线
HRDATA[31:0] Slave→Master 32 读数据总线
HWRITE Master→Slave 1 写使能:1为写,0为读
HSIZE[2:0] Master→Slave 3 传输大小:字节、半字、字等
HBURST[2:0] Master→Slave 3 突发类型:单次、增量、回绕等
HTRANS[1:0] Master→Slave 2 传输类型:IDLE、BUSY、NONSEQ、SEQ
HREADY Slave→Master 1 从机准备好信号,高有效
HRESP[1:0] Slave→Master 2 传输响应:OKAY、ERROR、RETRY、SPLIT
我的小技巧: 验证时先把HCLK、HRESETn、HREADY这三个信号盯死。时钟和复位出问题,后面全白搭。HREADY拉低一次,整个总线就卡住了——我在项目中遇到过因为HREADY逻辑写反,导致仿真跑了一整天都卡在同一个地址上。

3.2 单次传输:最基础的读写操作

单次传输,说白了就是Master发一个地址,Slave回一个数据。听起来简单,但时序细节不少。

先看一个最简单的写操作:

// 单次写传输时序示例
// 时钟周期 T1: Master 驱动地址和控制信号
// 时钟周期 T2: Master 驱动写数据,Slave 采样地址
// 时钟周期 T3: Slave 完成写操作,拉高 HREADY

always @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        HADDR  <= 32'b0;
        HWRITE <= 1'b0;
        HWDATA <= 32'b0;
    end else begin
        // 假设状态机控制
        if (start_transfer) begin
            HADDR  <= addr;
            HWRITE <= 1'b1;  // 写操作
            HWDATA <= wdata;
        end
    end
end

嗯,这里要注意:地址和数据不是同时有效的。地址先出来,数据要等一个周期。为什么?因为Slave需要时间解码地址,决定把数据写到哪个寄存器里。

读操作稍微有点不同:

// 单次读传输时序示例
// T1: Master 发地址
// T2: Slave 采样地址,开始准备数据
// T3: Slave 通过 HRDATA 返回数据

// Slave 侧逻辑
always @(posedge HCLK) begin
    if (HWRITE == 1'b0 && HTRANS == NONSEQ) begin
        // 采样地址,准备读数据
        read_addr <= HADDR;
        // 数据在下一个周期准备好
        HRDATA <= mem[HADDR];
    end
end
避坑指南: 我曾经在验证一个DMA控制器时,发现读回来的数据总是错位。查了三天,最后发现是Slave在HREADY为低时还在更新HRDATA。记住:HRDATA只有在HREADY为高时才有效!这是协议明确规定的,但很多新手会忽略。

3.3 地址与数据相位:流水线的核心思想

AHB最巧妙的设计就是地址相位和数据相位分离。你想想看,如果每次都要等数据回来才能发下一个地址,那效率得多低?

流水线的思路是这样的:

  • 地址相位:Master在T1发地址,持续一个周期
  • 数据相位:Slave在T2返回数据,可能持续多个周期(如果HREADY拉低)
  • 关键点:T2的地址相位可以和T1的数据相位重叠

我画个简化的时序图帮你理解:

// 流水线传输示例(背靠背传输)
// 时钟:  T1    T2    T3    T4
// 地址:  A1    A2    A3    A4
// 数据:  ---   D1    D2    D3
// HREADY: 1     1     1     1

// 注意:A2的地址相位和D1的数据相位在T2重叠

为什么会这样设计?说白了就是为了提高总线利用率。如果每次传输都要等两个周期,总线利用率只有50%。用了流水线后,理想情况下可以达到接近100%。

但这里有个坑:如果Slave来不及处理怎么办?比如Slave是个慢速外设,读一次要等10个周期。这时候Slave会把HREADY拉低,Master就得等着。

关键理解: 地址相位永远只有一个周期。数据相位可以延长。这就是为什么HREADY信号如此重要——它是Slave控制总线节奏的唯一手段。

3.4 实际项目中的经验总结

我在几个项目里验证过AHB接口,总结了几条经验:

  1. 地址对齐别搞错:HSIZE=2(字传输)时,地址最低两位必须是0。我见过有人写地址0x04传一个字,结果Slave把数据写到了0x05开始的位置——因为地址没对齐。
  2. HTRANS状态机要严谨:IDLE、BUSY、NONSEQ、SEQ这四个状态,跳转条件必须覆盖全。漏掉一个分支,仿真可能跑飞。
  3. HREADY的默认值:如果总线上只有一个Slave,HREADY默认拉高。如果有多个Slave,需要通过地址译码器给每个Slave分配地址空间,没被选中的Slave要把HREADY拉高(表示不参与传输)。
  4. 复位后的第一个传输:复位后Master必须先发一个IDLE传输,再发NONSEQ。直接发NONSEQ可能会被Slave当成非法操作。
我的验证小窍门: 写testbench时,先构造最简单的单次读写用例,确认地址相位和数据相位都对得上。然后再加流水线、加等待周期、加错误响应。一步一步来,别想一口吃成胖子。我每次带新人都是这个套路,效果很好。

好了,今天的内容就到这里。AHB的信号定义和单次传输是基础中的基础,后面的突发传输、Split传输、仲裁机制都是在这个基础上扩展的。下一讲我们会深入突发传输和地址边界对齐的问题——那个才是真正考验功底的地方。

记住:验证AHB接口,先把HCLK、HREADY、HTRANS这三个信号盯死。这三个信号不出错,后面就稳了。