SoC 总线与 AXI 协议

深入 RISC-V SoC 的片上互连与 AXI4 协议细节:五通道握手纪律、突发类型与 4KB 边界、ID 与乱序完成、outstanding 与 QoS、crossbar 互连与仲裁、位宽时钟桥、DMA 访存路径,并给出 SVA 协议断言、带宽建模与可综合从设备状态机。

引言

CPU 核、DMA、DDR 控制器、外设,这些模块之间怎么说话?答案是片上总线。AXI 是 ARM 提出的 AMBA 协议族里最核心的高性能成员,也是 RISC-V SoC 事实上的互连标准——几乎所有主流开源 RISC-V 核(Rocket、BOOM、CVA6、VexRiscv、NaxRiscv)都提供 AXI4 或可桥接到 AXI4 的主接口,商用 DDR 控制器与 NoC IP 也一律以 AXI 为入口。

AXI 的难点不在于信号数量,而在于它把「可以并行」和「必须有序」这两件事同时编码进了协议。五个通道彼此独立握手,读写可以乱序完成,同一主设备的多个未完成事务可以同时存在于总线上;但每个通道内部的规则又极其严格,一个信号的时序错位就可能导致偶发死锁。协议规范只给出了「什么是合法行为」,没有给「实现模板」,因此手写的 AXI 从设备经常在仿真里跑得通,在真实负载下却挂死。

本文聚焦 AXI4 全功能版的协议细节与互连工程,以及它在 RISC-V SoC 里的典型用法。外设寄存器、UART/PLIC 与中断使能链已经在 RISC-V 外设与中断控制器 中讲过,本文不重复那部分,而是把总线本身讲透:握手、突发、ID 与乱序、互连仲裁、桥接、DMA 访存路径、协议断言与带宽建模。

读本文前建议先了解 RISC-V 流水线 CPU 里访存请求是如何产生的,因为总线侧的吞吐最终要回到 CPU 的 CPI 上;如果关心总线错误如何变成 CPU 异常,可回看 RISC-V 指令集架构里的 trap 机制。

目录

  1. 片上互连的角色与拓扑
  2. 五个通道与握手规则
  3. 突发类型、长度与边界
  4. 传输属性与保护位
  5. ID 信号与乱序完成
  6. AXI4-Lite 与 AXI4-Stream
  7. 互连结构:crossbar 与共享总线
  8. 位宽转换、时钟转换与协议桥
  9. DMA 与访存路径
  10. 总线功能验证与 SVA 断言
  11. 带宽与延迟建模
  12. 从设备状态机的可综合实现

1. 片上互连的角色与拓扑

互连的任务只有一个:把 N 个主设备的请求路由到 M 个从设备,并保证协议语义不被破坏。围绕这个任务,工程上出现过三代拓扑。

第一代是共享总线(如 AHB、Wishbone),同一时刻只有一个主设备能占用总线,靠仲裁器轮转。它面积小、时序简单,但所有主设备争抢同一条通道,带宽随主设备数量下降,DMA 一跑起来 CPU 就被饿死。

第二代是多层互连(multi-layer),典型代表就是 AXI 交叉开关。每个主设备有独立的读写通道,只要访问的是不同从设备就能真正并行;只有访问同一从设备时才需要仲裁。

第三代是片上网络(NoC),把数据包化,在 2D mesh 上做路由。它解决了 crossbar 面积随端口数平方增长的问题(O(N×M) 的多路选择器规模),代价是增加了延迟和设计复杂度,通常只在 8 个以上主设备的 SoC 里才划算。

共享总线:   M0 ─┐
                 ├─[ 仲裁 ]─ 单一通道 ─┬─ S0
             M1 ─┘                     └─ S1
           同一时刻仅一个主设备可用,带宽不随主设备增加

Crossbar:   M0 ──┬── S0      每个主设备有独立通路
             M1 ──┼── S1      不同从设备可并行访问
             M2 ──┴── S2      端口数 N×M,多路选择器规模 O(N×M)

NoC:        M0 ─[R]═╦═╦═[R]─ S0   数据包化 + 路由
                     ╠═╬═╣         面积随端口线性增长
             M1 ─[R]═╩═╩═[R]─ S1   延迟增加 2~5 拍

选型经验:主设备少于 4 个、总带宽需求低于 1GB/s 时,共享总线足够;4 到 8 个主设备用 crossbar;超过 8 个(含多核集群、多个 DMA、PCIe、视频管线)再考虑 NoC。RISC-V 教学 SoC 通常是「1 个 CPU 核 + 1 个 DMA + 1 个调试模块」,crossbar 是标准答案。

2. 五个通道与握手规则

AXI4 把一次读写拆成五个独立通道,每个通道都遵守同一套 valid/ready 握手:

通道缩写方向内容
写地址AW主 → 从地址、突发长度、大小、类型、ID
写数据W主 → 从数据、字节选通 strobe、最后拍标记
写响应B从 → 主写完成响应、ID
读地址AR主 → 从地址、突发参数、ID
读数据R从 → 主数据、响应、最后拍标记、ID

握手的基本语义:发送方拉高 valid 并保持数据稳定,接收方在能接收时拉高 ready,两者同时为高的时钟上升沿完成一次传输。

三条不可违反的纪律:

  1. valid 一旦拉高,握手完成前不得撤销,数据也不得改变。 这是最常见的错误来源——把 valid 写成 assign awvalid = (state == SEND) 而没有保持逻辑,状态机一抖就撤销了。
  2. valid 不得依赖 ready。 如果 valid = ready & something,主从双方互相等待就形成组合环路死锁。ready 可以依赖 valid(从设备看到请求后才决定能否接收),反之不行。
  3. 各通道独立握手。 AW 和 W 可以相差任意多拍到达,从设备必须能分别缓存;不要假设写地址和写数据同时出现。
// 合法的 valid:进入 SEND 后一直保持,直到握手完成
always @(posedge clk or negedge rst_n)
    if (!rst_n) awvalid <= 1'b0;
    else if (awvalid && awready) awvalid <= 1'b0;   // 握手完成才撤销
    else if (req_pending && !awvalid) awvalid <= 1'b1;

// 非法的 valid:依赖 ready 会形成组合环
// assign awvalid = awready & req_pending;   // 禁止

从设备的 ready 有两种风格:前瞻型(组合逻辑,能收就立刻拉高,零等待)与后置型(寄存器输出,先拉高一拍再收)。前者延迟低但组合路径长,后者时序好但每笔事务多一拍。高频率设计中通常用后置型,把 ready 打一拍,代价是吞吐不变、延迟 +1。

3. 突发类型、长度与边界

AXI 的核心效率来自突发(burst):一次地址握手,随后多拍数据。这大幅减少了地址通道的开销,也让从设备可以做行缓冲(DDR 的行激活只做一次)。

三个关键参数:

  • AxLEN:突发拍数减一,取值 0255,即 1256 拍。
  • AxSIZE:每拍字节数,2^AxSIZE,最大为数据总线宽度对应的字节数(如 64 位总线最大 AxSIZE=3)。
  • AxBURST:突发类型,FIXED(00)、INCR(01)、WRAP(10)。
类型地址变化典型用途
FIXED每拍地址不变向同一 FIFO 端口连续写
INCR每拍地址递增(默认)内存搬运、DMA
WRAP递增到边界后回卷Cache line 填充、CPU 取指

WRAP 突发的边界是「拍数 × 每拍字节数」,且必须是 2 的幂。例如 4 拍 × 8 字节 = 32 字节边界,起始地址 0x18 的 WRAP 突发依次访问 0x18、0x20、0x28、0x00。Cache line 填充必须用 WRAP,因为 CPU 可能从行中间开始取数,填充完整个 line 才能回卷到开头。

4KB 边界是硬约束:任何突发都不能跨越 4KB 地址边界。原因是 4KB 是页大小,跨页突发会同时命中两个页表项,从设备无法在地址握手阶段就完成翻译。主设备在发起长突发时必须自己切分:

// 按 4KB 边界切分突发(地址 12 位以上变化即切分)
function automatic [8:0] calc_len(input [31:0] addr, input [8:0] want);
    logic [11:0] off; logic [8:0] max_in_page;
    off = addr[11:0];
    max_in_page = (12'h1000 - off) >> AxSIZE;   // 本页还能放几拍
    calc_len = (want < max_in_page) ? want : max_in_page;
endfunction

违反 4KB 规则的后果是协议未定义行为:仿真可能过,硅上可能死锁或数据错乱。AXI 协议检查器(如 ARM 的 AXI Protocol Checker 或开源 axi_protocol_checker)会把它报成协议违例,务必在验证阶段打开。

4. 传输属性与保护位

除了地址与数据,AXI 还携带一组属性信号,它们在 SoC 里承担「安全、缓存、观察点」等语义:

信号宽度作用
AxCACHE4缓存属性:bufferable、cacheable、read-allocate、write-allocate
AxPROT3保护:特权级、安全/非安全、指令/数据
AxQOS4服务质量,互连仲裁的权重依据
AxREGION4区域标识,多区域从设备的选片依据
AxUSER自定义厂商扩展,常用于跟踪 ID、安全标签

AxCACHE 的四位编码是理解系统一致性的钥匙:AxCACHE[0] 表示 bufferable(写可以在到达最终目的地前提前响应),[1] 表示 cacheable,[2] read-allocate,[3] write-allocate。全 0(4'b0000)表示「设备内存」,必须严格按序、不可合并——MMIO 寄存器一律用这个值,否则写合并会丢掉对同一寄存器的连续写。

AxQOS 是解决「DMA 饿死 CPU」的标准手段:给 CPU 主接口配高 QoS,给批量 DMA 配低 QoS,互连仲裁器在冲突时优先放行高 QoS 事务。没有 QoS 时,一个跑满带宽的 DMA 能让 CPU 的取指延迟从 10 拍涨到数百拍,整机响应卡顿。

// 主设备侧:MMIO 访问用 device 属性,内存访问用 cacheable 属性
assign araddr  = addr;
assign arcache = is_mmio ? 4'b0000 : 4'b1111;   // device / write-back
assign arprot  = {1'b0, priv_mode, 1'b0};       // 特权、非安全、数据
assign arqos   = is_cpu ? 4'hF : 4'h2;          // CPU 高优先级

5. ID 信号与乱序完成

AxID 是 AXI 里最容易被忽视、也最容易搞错的机制。 它同时承担两件事:

  1. 区分不同主设备/不同线程的事务,让从设备知道哪些响应该回给谁。
  2. 定义乱序的粒度:只有 ID 不同的响应可以乱序返回;同 ID 的响应必须按请求顺序返回。

这个规则是整个协议的基石。它意味着互连可以放心地把多个主设备的事务交织在一起,而每个主设备内部只需按自己的 ID 顺序接收即可。

主设备发出(ID 相同,必须保序):
  AR id=0 addr=A   →  R id=0 data=A  (先)
  AR id=0 addr=B   →  R id=0 data=B  (后)

主设备发出(ID 不同,可以乱序):
  AR id=0 addr=A   ┐
  AR id=1 addr=B   ┘  →  R id=1 data=B  (先返回,合法)
                         R id=0 data=A  (后返回,合法)

Outstanding(未完成事务数)能力决定了能同时在途多少笔事务。从设备声明 writeIssuingCapability / readIssuingCapability,主设备声明 combinedIssuingCapability。一个 outstanding=1 的从设备意味着主设备必须等前一笔响应回来才能发下一笔,延迟暴露无遗;而 DDR 控制器通常支持 8~32 笔 outstanding,靠并行度把行激活延迟藏起来。

CPU 侧如何用 ID?典型做法是给指令取指、数据访问、DMA 各分配一个 ID 段,避免它们互相阻塞。更细的做法是按 cache miss 的 MSHR 条目分配 ID,让多个 miss 并行。ID 位宽每增加一位,互连里需要维护的重排序缓冲就多一份,所以位宽要按需分配,常见是 4~8 位。

6. AXI4-Lite 与 AXI4-Stream

AXI4 全功能版对慢速外设来说太重了。AMBA 定义了另外两个变体,覆盖不同场景:

协议通道突发用途
AXI45支持 1~256 拍CPU、DMA、DDR 控制器
AXI4-Lite5只支持 1 拍寄存器配置、慢速外设
AXI4-Stream1无地址概念视频流、ADC 采样、片内数据搬运

AXI4-Lite 砍掉了突发、ID、QoS、cache 属性,每次只传一拍,所有通道的 xLEN = 0、xID = 0。它的价值在于「可以用一个通用模板生成」——一个几百行的 Lite 从设备模板套上不同的寄存器映射就能覆盖 UART、GPIO、SPI、定时器。SoC 里典型的层级是:CPU 走 AXI4 到 crossbar,crossbar 分出一条 AXI4-Lite 支路,再经 AXI-Lite 转 APB 桥接慢速外设。

AXI4-Stream 完全去掉地址,只剩 TDATA / TVALID / TREADY / TLAST / TKEEP / TUSER。它把「总线」退化成了「带握手的流水线」,因此可以做到每个时钟都传数据(100% 效率),适合视频像素流、神经网络特征图流这类无地址顺序流。TLAST 标记一帧结束,TUSER 常用来传帧起始或行号。

// AXI4-Stream 的极简本质:一个带 backpressure 的寄存器
always @(posedge clk or negedge rst_n)
    if (!rst_n) tvalid <= 1'b0;
    else if (tvalid && tready) begin
        tvalid <= s_valid;  tdata <= s_data;  tlast <= s_last;
    end else if (!tvalid && s_valid) begin
        tvalid <= 1'b1;     tdata <= s_data;  tlast <= s_last;
    end

7. 互连结构:crossbar 与共享总线

一个 N×M crossbar 的内部由三部分组成:地址译码、仲裁、通道复用。

地址译码把每个主设备的请求按地址区间映射到目标从设备。N 个主设备到 M 个从设备,每个从设备前面需要一个 M 选 1 的仲裁器(从设备侧的仲裁),每个主设备后面需要一个 M 选 1 的数据选择器(主设备侧的响应回送)。

仲裁策略决定公平性与实时性:

策略特点适用
轮转(round-robin)公平、无饥饿通用,默认
固定优先级实时性好、低优先级可能饥饿中断、调试
QoS 加权按 AxQOS 动态调整混合流量
基于信用(credit)无丢包、需反压NoC 链路

注意 ready 反压的传播:如果目标从设备的 ready 长期为低(例如 DDR 控制器忙于刷新),仲裁器必须把反压传回主设备,让主设备的 outstanding 缓冲填满后停止发请求。如果互连没有正确反压,数据就会丢失。这是 crossbar 实现里第二常见的 bug(第一是握手违规)。

多主设备写同一地址的顺序问题在 AXI 里没有全局保证:协议只保证同 ID 的保序,跨主设备的顺序由软件用内存屏障(fence)加锁来保证。RISC-V 的 RVWMO 内存模型对此有明确定义,跨主设备事务的顺序约束需要软件显式表达。

8. 位宽转换、时钟转换与协议桥

真实 SoC 里各模块的数据宽度和时钟频率往往不一致:CPU 核是 64 位宽、200MHz,DDR 控制器是 128 位宽、400MHz,外设是 32 位宽、50MHz。互连必须提供三类转换。

位宽转换:窄转宽需要把多个窄拍拼成宽拍(upsize),宽转窄需要把宽拍拆成多拍(downsize)。要点是 AxSIZE 要随之调整,且 strobe 要按字节正确映射,否则非对齐访问会写坏相邻字节。

// 32 位 → 64 位窄转宽:两拍拼一拍,注意字节选通拼接
always @(posedge clk) begin
    if (s_valid) begin
        if (!half_full) begin
            buf[31:0] <= s_data; buf_strb[3:0] <= s_strb;
            half_full <= 1'b1;
        end else begin
            m_data <= {s_data, buf[31:0]};
            m_strb <= {s_strb, buf_strb[3:0]};
            m_valid <= 1'b1; half_full <= 1'b0;
        end
    end
end

时钟转换:用异步 FIFO 跨时钟域,AxVALID/AxREADY 这类控制信号必须做同步(两级触发器)或用握手同步器。绝不要把 valid/ready 直接跨时钟域打两拍——虽然看起来能工作,但握手语义会被破坏,偶发丢事务。

协议桥:AXI4 → AXI4-Lite(丢弃突发,拆成多拍单次传输)、AXI → APB(三阶段状态机)、AXI → Wishbone(常见的开源场景)。桥接的通用原则是把复杂协议退化成简单协议,同时保留反压。

9. DMA 与访存路径

DMA 是总线压力的主要来源,也是 AXI 特性(突发、outstanding、QoS)最能发挥作用的地方。一个高性能 DMA 的 AXI 主接口应当具备:

  • 多 outstanding:同时挂 8 笔以上读写请求,靠并行度掩盖内存延迟。
  • 长突发:用 16~64 拍 INCR 突发,把地址握手开销摊薄到每拍不到 1 个周期。
  • 读写分离通道:AXI 天然读写分离,DMA 的读和写可以同时进行。
  • 非对齐处理:首尾拍用 strobe 处理非对齐字节,中间拍全宽传输。
// DMA 读通道:先发地址,按 ID 收数据,写进行缓冲
// AR: 一次发 addr0..addr3 四笔(ID 0..3),靠 outstanding 并行
// R : 按 ID 分别落桶,凑满一行后再统一写入目标
localparam BURST = 16;                       // 16 拍 = 64 字节(64 位总线)
assign arlen  = BURST - 1;
assign arsize = 3'd3;                        // 每拍 8 字节
assign arburst= 2'b01;                       // INCR

DMA 与缓存的交互是 RISC-V SoC 里最常见的功能性 bug:DMA 直接写内存,绕过 CPU 的 D-Cache,CPU 随后读到的仍是缓存里的旧值。三种解法:软件显式 fence + cache flush/invalidate(简单、慢);硬件一致性互连(如 CHI/ACE,复杂、昂贵);把 DMA 缓冲区映射成 non-cacheable 属性(AxCACHE = 4'b0000,最常用)。选择哪一种取决于 SoC 是否有硬件一致性域,这与操作系统侧的内存管理与缓存维护策略直接相关,可参考 操作系统 专题的相关内容。

10. 总线功能验证与 SVA 断言

总线 bug 的特点是难以复现:需要特定的时序组合、特定的反压时机。因此协议层验证必须用断言(SVA)而不是靠随机测试碰运气。

一组必备断言:

// 1. valid 不得在握手完成前撤销
property p_valid_stable;
    @(posedge clk) disable iff (!rst_n)
    (awvalid && !awready) |=> (awvalid && $stable(awaddr));
endproperty
a_valid_stable: assert property (p_valid_stable);

// 2. 突发不得跨 4KB 边界
property p_4k_boundary;
    @(posedge clk) disable iff (!rst_n)
    (awvalid && awready) |-> ((awaddr[11:0] + ((awlen + 1) << awsize)) <= 12'h1000);
endproperty
a_4k: assert property (p_4k_boundary);

// 3. 同 ID 的读响应必须保序(用计数器记录每个 ID 的未完成数)
property p_same_id_order;
    @(posedge clk) disable iff (!rst_n)
    (rvalid && rready && rlast) |-> (rid_seen[rresp_id] == expected_id);
endproperty

// 4. WLAST 必须与 AWLEN 一致
property p_wlast_count;
    @(posedge clk) disable iff (!rst_n)
    $rose(wvalid) |-> ##[0:255] (wvalid && wlast);
endproperty

除了断言,工程上还会挂一个协议检查器 IP,把 AXI 规范里所有 MUST 条款变成运行时检查。Vivado 的 AXI Protocol Checker 与开源 axi_protocol_checker 都能即插即用。验证方法论层面,interface + 约束随机 + 覆盖率驱动的完整流程见 SystemVerilog 验证与 testbench ,AXI 的验证是它最典型的应用场景。

11. 带宽与延迟建模

带宽估算决定了互连要开多宽。基本公式:

理论峰值带宽 = 数据宽度(字节) × 频率 × 每拍传输效率
实际带宽     = 理论峰值 × 突发效率 × 仲裁效率 × 反压系数

以 64 位 @ 200MHz 的 AXI 为例:

理论峰值 = 8 字节 × 200MHz = 1600 MB/s
写突发 16 拍:地址开销 1/16,突发效率 ≈ 94%
三主设备争抢同一从设备:仲裁效率 ≈ 60%
DDR 控制器周期性反压:反压系数 ≈ 0.8
实际带宽 ≈ 1600 × 0.94 × 0.6 × 0.8 ≈ 722 MB/s

延迟建模则是另一条线:CPU 的一次 cache miss 访存延迟由「CPU 到互连的流水线级数 + 互连仲裁等待 + 从设备响应延迟 + 数据回程」组成。教学 SoC 里这个数字通常是 30~80 拍,工业 SoC 里可以到 200 拍以上。延迟直接进 CPI:

CPI = 1 + (miss 率 × miss 延迟) / 每指令访存次数
例:miss 率 5%,miss 延迟 60 拍,每 3 条指令一次访存
  → CPI = 1 + (0.05 × 60) / 3 = 2.0

这解释了为什么「互连延迟优化」在高端 SoC 里和「提高频率」同等重要:延迟从 60 拍降到 40 拍,CPI 从 2.0 降到 1.67,性能提升 20%,而频率可能一点没变。这与流水线 CPU 里 CPI 分解的思路一脉相承。

12. 从设备状态机的可综合实现

一个支持单拍突发的 AXI4-Lite 从设备状态机骨架:

module axi_lite_slave (
    input  wire clk, rst_n,
    // 写地址 / 写数据 / 写响应
    input  wire [31:0] awaddr,  input wire awvalid, output reg awready,
    input  wire [31:0] wdata,   input wire [3:0] wstrb,
    input  wire wvalid,         output reg wready,
    output reg  [1:0] bresp,    output reg bvalid,  input wire bready,
    // 读地址 / 读数据
    input  wire [31:0] araddr,  input wire arvalid, output reg arready,
    output reg  [31:0] rdata,   output reg [1:0] rresp,
    output reg  rvalid,         input wire rready
);
    localparam S_IDLE=2'd0, S_WRITE=2'd1, S_RESP=2'd2, S_READ=2'd3;
    reg [1:0] wstate, rstate;
    reg [31:0] aw_addr_lat;

    // 写通道:AW 与 W 独立握手,都到达后才执行写
    reg aw_done, w_done;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            awready <= 1'b0; wready <= 1'b0; bvalid <= 1'b0;
            aw_done <= 1'b0; w_done <= 1'b0;
        end else begin
            if (awvalid && !aw_done) begin aw_addr_lat <= awaddr; aw_done <= 1'b1; end
            if (wvalid && !w_done)   begin reg_write(aw_addr_lat, wdata, wstrb); w_done <= 1'b1; end
            // 两者都到 → 回写响应
            if (aw_done && w_done && !bvalid) begin
                bvalid <= 1'b1; bresp <= 2'b00; aw_done <= 1'b0; w_done <= 1'b0;
            end
            if (bvalid && bready) bvalid <= 1'b0;
        end
    end

    // 读通道:单拍响应
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin rvalid <= 1'b0; arready <= 1'b0; end
        else begin
            if (arvalid && !rvalid) begin
                rdata <= reg_read(araddr); rresp <= 2'b00; rvalid <= 1'b1;
            end
            if (rvalid && rready) rvalid <= 1'b0;
        end
    end
endmodule

三个实现要点:AW/W 独立缓存(不要等两通道同时到才接)、响应必须有反压意识(bready 为低时保持 bvalid)、寄存器读写要用 strobe 逐字节处理(wstrb 为 0 的字节不能改)。如果从设备要支持突发,把上面的单拍逻辑换成「地址递增计数器 + 拍数计数器」,并在 xlast 时收尾即可。

权衡取舍

决策点选项 A选项 B
互连拓扑共享总线:省面积、易争抢Crossbar:并行、面积 O(N×M)
协议变体AXI4:突发高效、逻辑重AXI4-Lite:模板化、吞吐低
突发长度长突发:开销低、占总线久短突发:公平、开销高
ready 风格组合 ready:延迟低、路径长寄存 ready:时序好、延迟 +1
乱序能力多 ID 乱序:吞吐高、重排序复杂单 ID 保序:简单、延迟暴露
DMA 一致性软件 flush:简单、慢硬件一致性:快、面积大
地址属性Device(0000):严格有序Cacheable(1111):可合并、快

常见坑清单

  • valid 撤销:握手未完成就撤销 valid 或改变数据,从设备状态错乱;valid 必须保持到握手完成。
  • valid 依赖 ready:形成组合环路导致死锁,valid 的产生逻辑绝不能引用 ready。
  • 跨 4KB 突发:主设备未切分长突发,协议违例,仿真可能过、硅上必挂。
  • AW/W 到达顺序假设:假设写地址和写数据同时到达,从设备只缓存一路,另一路数据丢失。
  • 同 ID 乱序返回:互连把同 ID 的响应重排,主设备按序接收时拿到错配的数据。
  • strobe 未逐字节处理:非对齐写把相邻字节一起改写,寄存器被意外污染。
  • MMIO 用了 cacheable 属性:写合并丢掉连续写,读被缓存导致状态不更新,MMIO 必须用 4'b0000。
  • 反压未传播:从设备 ready 长期为低而互连没有回传,主设备继续发请求导致数据丢失。
  • 跨时钟域直接打拍 valid/ready:握手语义被破坏,偶发丢事务,必须用异步 FIFO 或握手同步器。
  • DMA 与缓存不一致:DMA 写内存后 CPU 读到旧缓存,需 flush/invalidate 或映射 non-cacheable。
  • wlast/rlast 计数错位:末拍标记与 AxLEN 不一致,从设备提前或延后结束突发。
  • 复位时 valid 未清零:上电后 valid 为 X,从设备误判为有效请求。

小结

AXI 协议的知识可以压缩成三句话:五个通道靠 valid/ready 握手,valid 不能撤、不能依赖 ready;突发靠 AxLEN/AxSIZE/AxBURST 描述,不能跨 4KB 边界;AxID 定义乱序粒度,同 ID 必须保序。 这三条记住了,读任何 AXI 波形都不会迷路。

工程上真正花时间的是互连与系统集成:crossbar 的仲裁与反压、位宽和时钟转换、DMA 的缓存一致性、QoS 下的公平性。这些问题不会在单个模块的仿真里暴露,只有在系统级验证和真实负载下才会浮现,所以协议检查器与 SVA 断言必须从第一天就挂上。

下一步建议阅读 RISC-V 流水线 CPU 与冒险处理 ,看访存队列与消歧如何在微架构层面利用总线的并行能力;如果关心存储系统一侧,可继续研究 MSHR 深度与总线 outstanding 能力的对应关系。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「芯片与体系结构」更多文章

  1. 可测性设计与测试
  2. 低功耗数字设计
  3. RISC-V 向量扩展 RVV