首页 / 资讯中心 / 文章详情

AXI协议如何用通道分离与握手破解多核SoC总线瓶颈

AXI协议如何用通道分离与握手破解多核SoC总线瓶颈 ★ FEATURED ARTICLE
简介AXIAdvanced eXtensible Interface总线是ARM AMBA 3.0协议中的核心规范文档面向SoC前端设计、嵌入式系统开发与IC验证工程师系统讲解AXI诞生背景、单向通道体系结构以及读地址、读数据、写地址、写数据、写响应五个独立通道的握手机制。内容重点覆盖猝发传输、乱序访问、非对齐数据传输与低功耗握手协议并与AHB详细对比有助于理解AXI在高性能、高带宽、低延迟SoC片内互连中的优势。资源包是一份约77KB的doc文档从总线概述、特性分析到典型应用均有展开既适合AMBA体系初学者也可作为SoC互联与IP集成设计的常备参考。目前已有429人学习下载对从事多核SoC、DMA、存储控制器等互联设计的工程师有实用价值。1. 为什么多核 SoC 的性能瓶颈是总线AXI 用“拆”解决了延迟一块多核芯片仿真通过、流片回来跑 benchmark性能却只有预估的六成这种事在芯片团队里不算少见。定位到最后瓶颈往往不在 CPU 内核也不在存储控制器而在把各个 IP 连起来的总线上。AXIAdvanced eXtensible Interface协议最反直觉的地方正是它解决总线瓶颈的方式它没有把总线做得更宽而是把传统的一根共享总线拆成了五条彼此独立的单向通道地址走地址的线数据走数据的线读和写彻底分开。这个“拆”字的收益是整个 AMBA 3.0 体系性能提升的起点。要理解 AXI 为什么能 work得顺着通道结构、握手节奏、乱序调度和低功耗协议这几条线往下拆。2. 通道分离五条单向通道为什么比一条双向总线更快2.1 五个通道怎么分工从一次读操作看数据流走向第一次接触 AXI 的人看到五个通道名会懵AR、R、AW、W、B。其实按用途归一下类就只有“读”和“写”两套独立通路外加一套写响应。读操作走两个通道。主设备先在 AR读地址通道上发出起始地址和这次读的控制信息从设备准备好数据后在 R读数据通道上把数据一拍一拍返回最后一拍用 RLAST 标识。这里有一个被很多人忽略的点地址相位和返回数据相位在时间上是完全分离的主设备发出 AR 之后不需要等 R 通道有任何动静就能继续在 AR 通道上发下一笔读请求。这就是正文里说的“地址/控制和数据相位分离”的原始含义——地址是“预约”数据是“兑现”两者可以在不同的时刻独立推进。写操作走三个通道。主设备在 AW写地址通道上发起始地址在 W写数据通道上发数据WLAST 标识最后一拍从设备收完所有数据之后在 B写响应通道上回一个状态。B 通道是很多从 AHB 迁移过来的人容易忽略的设计写操作必须有一个明确的完成确认否则主设备无法知道这比写到底是成功了还是被从设备拒绝了比如地址非法、访问错误。AXI 里这个确认专门用一条独立通道做而不是塞在数据通道里目的就是让写数据流和写响应状态流互不阻塞。2.2 单向信息流的物理收益三态、扇出与时钟域桥接单向通道体系结构最直接的收益藏在一个很容易被忽视的物理细节里传统 AMBA 总线AHB/APB的数据线是双向共享的多个主设备分时驱动同一组线这需要三态缓冲器。深纳米工艺下三态驱动是后端工程师最头疼的东西之一——多个驱动器挂在同一根线上电容叠加、噪声容限变差、保持时间窗口难以收敛每次综合都要在时序上做一堆妥协。AXI 把通道做成单向的之后每个通道只有一个固定的 driver 和一个固定的 receiver连线行为接近点对点信号不再有三态总线上的多驱动竞争。这样前端后端都省心门数量少了组合逻辑链可以更短时序收敛容易得多。正文里说的“减少门数量”“简化时钟域间的桥接”就是这么来的。举个跨时钟域的例子信号从 A 时钟域进 B 时钟域只需要沿固定方向插两级同步寄存器方向明确不会遇到双向信号先要判方向再打拍的麻烦而如果一根双向线上有多个驱动桥接逻辑就要额外处理三态控制和方向切换那点延迟和门开销在复杂的 SoC 里会被放大成可观的功耗代价。2.3 与 AHB 的对比hready 的单向时代局限AXI 的历史定位是对 AMBA 2.0 AHB 的“继承和提升”。AHB 的优点在于简单但它身上带着老一代总线的结构约束。对比表能直观看出 AXI 做了哪些结构性改变对比项AHBAMBA 2.0AXIAMBA 3.0数据总线双向共享一组线分时使用读、写通道各自独立单向传输握手机制单向的 HREADY从设备只能被动就绪每通道双向 VALID/READY 握手多笔事务一次只能有一笔事务在总线上可同时有多笔 outstanding 事务数据返回顺序严格按发起顺序可不按顺序返回靠 ID 区分写完成确认无独立响应通道独立写响应通道B非对齐访问需要额外处理逻辑协议原生支持AHB 时代总线是共享的同一时刻只允许一个主设备占用总线burst 传输期间整体的地址和数据阶段虽然可以重叠但事务之间是严格串行的。多核 SoC 出现之后这种串行结构成了吞吐瓶颈CPU 要访存、DMA 要搬运、各个外设要通信所有请求挤在同一根管子里排队总线频率再高也架不住并发请求的互相等待。协议制定方联合多家芯片厂商重新设计新一代总线时核心目标就是打破这个串行模型——把一根共享总线换成五条独立单向通道让读、写、控制流互不抢占这是 AXI 所有性能优势的结构基础。3. VALID/READY 握手每一拍数据如何被双方确认3.1 一对握手信号如何描述一次传输AXI 每个通道的传输都靠一对信号完成VALID 表示发送方这一拍的数据或控制信息已经稳定地放在通道上READY 表示接收方这一拍可以接收。当且仅当 VALID 和 READY 同时为高这一拍才算真正传输完成。传输完成后双方可以立刻开始下一拍中间不需要额外的“空闲周期”协议开销。这里的巧妙之处在于两个方向的信息不对称被一个握手规则统一了。发送方不知道接收方什么时候能收接收方不知道发送方什么时候有数据但只要各自把自己那一侧的“准备状态”亮出来两者相与就是传输发生的时刻。这天然是一种背压机制接收方来不及处理就把 READY 拉低数据就稳稳停在通道上不会丢也不会覆盖。相比 AHB 里从设备只能被动响应 HREADY 的设计AXI 的握手让主设备和从设备在通道层面是对等的——谁都可以主动要求暂停这就为后面的乱序访问和 outstanding 操作打下了基础。3.2 三种握手时序与“互相等待”的红线VALID 和 READY 谁先谁后协议没有强制。实际干活时会出现三种时序发送方先把 VALID 拉高等接收方 READY接收方先把 READY 拉高等发送方数据两边同一拍同时就绪一拍完成。三种时序在功能上等价但在时序收敛上差别很大。常见设计会让 READY 尽量早地无条件拉高只要内部还有缓冲空间因为 READY 一旦依赖 VALID 就可能在两个模块之间形成组合路径。协议最忌讳的是一条红线VALID 和 READY 之间不能形成组合逻辑环。如果发送方的 VALID 等接收方的 READY 才拉高接收方的 READY 又等发送方的 VALID 才拉高两者就像两辆车面对面堵在单行道上谁都没有第一脚油门总线整个卡死。这条规则不只在 AXI 里成立所有用 ready/valid 握手的协议比如 AXI-Stream都把它当成设计底线只是实践里翻车最多的正是这个点。3.3 最小读数据通道一个可综合的握手实现用一段最简的读数据通道代码来看握手是怎么落到 RTL 的。这个模块从一个 FIFO 取数据通过 R 通道的 RVALID/RREADY 送出// 最小AXI读数据通道从上游FIFO取数经RVALID/RREADY握手送出 module axi_rdata_min #( parameter DATA_WIDTH 32 ) ( input wire clk, input wire rst_n, // 上游FIFO接口 input wire [DATA_WIDTH-1:0] fifo_data, input wire fifo_valid, // FIFO非空 input wire fifo_last, // FIFO中最后一拍 output wire fifo_ready, // 握手完成可读下一拍 // AXI读数据通道 output reg [DATA_WIDTH-1:0] rdata, output reg rvalid, input wire rready, output reg rlast ); // 一次有效传输发送方数据有效且接收方就绪 wire handshake rvalid rready; // 状态逻辑只有FIFO有数据时才拉高RVALID always (posedge clk or negedge rst_n) begin if (!rst_n) begin rvalid 1b0; end else if (!rvalid fifo_valid) begin rvalid 1b1; // 有数据VALID随数据同拍拉起 end else if (handshake) begin rvalid 1b0; // 完成一拍撤销VALID end end // 数据在与VALID相同的周期锁存保证RVALID1数据稳定 always (posedge clk or negedge rst_n) begin if (!rst_n) begin rdata {DATA_WIDTH{1b0}}; rlast 1b0; end else if (!rvalid fifo_valid) begin rdata fifo_data; rlast fifo_last; // last跟着最后一拍走 end end // 背压握手完成才向前级取新数据 assign fifo_ready handshake; endmodule这个实现有三个关键决策值得说道。第一RVALID 不是在 FIFO 非空时直接拉高而是等一拍把数据锁存进 rdata 寄存器。这是因为 FIFO 输出的数据在fifo_valid拉高之后仍可能变化而 AXI 要求 VALID 一旦拉高通道数据必须保持稳定直到握手完成——所以必须先把数据复制到自己的寄存器里“钉住”。第二fifo_ready handshake意味着只有接收方真正取走这一拍才允许 FIFO 出下一个数据这就是背压的具体形态。第三rlast 和数据在同一拍锁存从协议角度看LAST 属于数据通道的一部分必须和最后一拍严格同拍否则从设备无法判断 burst 是否结束。这个模块没有管 burst 计数和地址实际互连设计里这两块逻辑被放在通道的“控制面”数据面只需要按拍握手。4. outstanding 与乱序为什么主设备不等结果就能继续发4.1 outstanding把等待从关键路径上拿掉一次 AXI 访问的往返延迟远不止一拍时钟。地址从主设备到从设备要经过互连、译码、仲裁数据再原路返回总线延迟能到十几拍甚至几十拍。如果主设备发完一笔请求就停下来等数据那总线利用率低得可怜生命周期的绝大部分时间都耗在“等待”上。AXI 的做法是允许主设备不等返回就发起新的读写操作这就是正文说的“主设备在没有得到返回数据的情况下可发出多个读写操作”。已发出但尚未完成的事务数叫 outstanding 深度它决定了总线上同时存在多少笔“在途”事务。把这些在途事务的数据量乘以总线位宽就是系统吞吐的上限来源——等待延迟被多个事务叠起来填满总线延迟这个固定开销被摊薄掉了。极限状态下如果总线延迟是 L 拍、outstanding 深度是 N那么总线利用率约等于 N/(NL)N 越大延迟的影响越小。这就是为什么做高性能互连的人都在堆 outstanding它不是让单笔访问变快而是让很多笔访问同时飞在传输线上让总线永远有活干。4.2 乱序返回与 BUSID数据先到先回靠 ID 认领允许 outstanding 之后一个立刻就出现的问题是多笔事务交错地在总线上飞从设备的返回顺序可能和主设备的发起顺序不一致。有的从设备响应快有的慢同一个从设备内部DDR 的行命中和行冲突之间延迟差一大截。如果强行要求返回顺序和发起顺序一致快的从设备必须排队等慢的总线利用率又掉回去了。AXI 的答案是允许乱序返回用 BUSID 来标识每笔访问的归属。主设备每次发起事务时分配一个 ID从设备返回数据时带上同一个 ID主设备看到 ID 就知道这笔数据属于哪个请求。这个机制带来的自由度很大不只不同从设备之间可以乱序同一从设备的两次访问之间也可以乱序——只要 ID 能区分数据先到先回就行。常见的实现思路是主设备侧维护一张 ID 到请求上下文的映射表返回数据按 ID 查表回到正确的处理流程而不是按发起顺序排队。需要特别注意的是 ID 的分配纪律一笔事务的 ID 从发出到收到最后一拍带 LAST之前不能被复用。协议对 ID 复用的管理是硬约束如果两笔未完成事务用了同一个 ID返回数据就彻底分不清谁是谁了这在第 5 章的踩坑里会详细展开。4.3 突发传输与首地址寻址地址算到一半怎么处理AXI 突发传输的核心特征是“只需要首地址”。主设备发控制信息时给出起始地址、突发长度多少个 beat、突发大小每个 beat 的字节数和突发类型固定地址 FIXED、递增 INCR、回卷 WRAP就足够了。后面每个 beat 的地址从设备和互连可以根据这三要素推算出来不需要主设备每拍再发一个新地址。对于 INCR 类型每拍地址按突发大小递增对于 FIXED 类型每个 beat 都打在同一个地址上典型场景是连续读写 FIFOWRAP 类型则在地址到达上界后绕回下界常用于 cache line 的突发传输。这个设计把地址带宽省了下来一次长 burst 只占一次地址通道的使用权数据通道可以连续吞吐。也正因为地址是推算出来的互连里做地址译码的路径变得规整——在一笔 burst 到达端点之前译码逻辑不需要关心每个 beat 的准确地址只需要在首拍把整笔事务的路由方向定好。4.4 非对齐传输AXI 用字节选通解决地址边界问题总线宽度是 128 位的时候一次传输就是 16 字节但如果要访问的地址正好落在非整数倍总线宽度的位置怎么办AHB 时代这种非对齐访问是设计者的负担要额外拆分逻辑。AXI 从协议层面解决了它数据总线按字节划分 lane用写选通和读选通标识哪些字节是有效数据。常见的实现思路是数据通道上每个字节带一个选通位非对齐访问时只有落在访问范围内的字节 lane 被选通其余的保持无效。比如从一个奇数地址0x03开始读 4 字节第一拍会选通高位的三个字节剩下的一个字节顺延到下一拍。这样从协议上就允许访问任意起始地址由硬件自动拼接数据而不需要软件做对齐处理。还有一个容易忽略的约束burst 传输不能跨越 4KB 地址边界这是 AXI 对从设备地址空间划分的保护——避免一笔 burst 跨越存储器管理单元或外设的页边界让从设备可以把 4KB 当作一个安全粒度的访问窗口。非对齐能力和 4KB 边界约束叠加在一起构成了 AXI 地址处理的完整画像。5. AXI 集成避坑握手死锁、last 丢失与 ID 错位5.1 VALID 与 READY 互相等待仿真时钟一停就是死锁现象仿真跑着跑着总线上某一笔事务突然卡住RVALID 或 WVALID 一直为高RREADY 或 WREADY 一直为低时钟接着走但通道上没有任何进展。原因两端的握手信号形成了组合逻辑依赖环。主设备自己约定“收到 READY 才拉 VALID”从设备又设计成“收到 VALID 才拉 READY”两边都等对方的信号没有一方主动跨出第一步。这在 RTL 仿真里不会报错只会让总线永远停在等待状态。解决规范的做法是在握手信号的产生路径上做取舍——通道的 VALID 应只依赖内部状态有没有数据READY 应只依赖接收能力缓冲是否非满切断这两个信号的相互依赖。仿真阶段加一个死锁看门狗断言比如“VALID 拉高后 N 拍内 READY 必须出现”的时序检查一跑就知道哪两个模块在互相等待。从那以后我每次都要先查握手信号是否有组合路径穿过模块边界再动仿真。5.2 握手路径穿过多级组合逻辑时序收敛翻车现象功能仿真一切正常综合之后频率上不去时序报告里 handshake 相关路径的负余量negative slack一大片拉高频率就白给。原因VALID 和 READY 在模块之间穿过两三级组合逻辑才到对方寄存器路径上还压着互连的译码和仲裁逻辑综合器怎么优化都压不到目标周期。这类问题在功能验证阶段完全看不见只在综合和后端阶段爆雷。解决在互连中插入寄存器切片register slice。它本质就是给每个通道的握手路径上打一拍寄存器把长组合路径切成两段等价于在模块之间做流水线缓冲。代价是每插一个切片通道增加一拍延迟同时会增加通道的占用周期所以切片数量和 outstanding 深度要配套调整不能无脑多插。常见互联组件里“一阶段切片”“两阶段切片”的选项都是在延迟和频率之间做取舍。5.3 last 信号没跟最后一拍同拍从设备永远在等现象burst 传输打了四拍数据数据本身都收全了但 RLAST 或 WLAST 一直没来从设备的 burst 状态机卡在收尾状态后续事务全部排队等待。原因把 last 信号用组合逻辑生成或者在数据通道里把 last 和 data 分开寄存。一旦 last 比数据晚半拍从设备认为 burst 还没结束按照协议它又必须等全部数据到齐才能回响应整个通道就悬住了。解决把 last 当作数据通道的一部分和每一拍数据严格同拍寄存。这是 AXI 协议里一条容易被低估的规则LAST 是数据通道的有效位不是独立的控制信号。比较好的自查方式是写断言“每笔 burst 中 LAST 恰好出现一次且出现在最后一个 beat 上”这是个能在仿真里直接抓出问题的小料。5.4 乱序返回但 ID 复用数据张冠李戴现象多笔 outstanding 读请求发出去返回数据的顺序确实乱了但主设备把数据接到了错误的请求上——数据错位而且无法自查因为握手指纹正常没有报错。原因主设备给新事务分配的 ID 和某笔尚未完成的事务重复了。乱序返回靠 ID 认领数据ID 本身已经冲突返回数据按 ID 查表自然落入错误上下文。多数情况下是 ID 分配逻辑没管“事务结束后才释放 ID”的纪律。解决每笔事务的 ID 必须保持唯一直到收到该事务的最后一拍之后才能释放回 ID 池。这是一个典型的资源管理问题可以用断言来约束任一时刻系统内 outstanding 事务的 ID 不允许重复。特别在把 ID 位宽做小来省线的时候ID 不够用就要靠限制最大 outstanding 深度来保证不冲突而不是靠运气。5.5 写响应通道没等 WLAST 就回事务悬在半空现象写数据的最后一拍还没发完B 通道就回了 BRESP或者反过来数据全发出去了但 B 通道迟迟没有响应。前者是协议违例后者是功能缺失。原因从设备回写响应的时机必须在收齐全部写数据之后以 WLAST 且 WVALID 且 WREADY 完成为标志常见错误是把写响应生成逻辑挂在 AW 通道的接收上地址收到就急着回压根没等数据通道的最后一拍。解决写响应逻辑必须显式结合 W 通道的完成条件——只有接收到 WLAST 且握手完成才能产生对应的 BRESP。做验证的人可以把“BVALID 只会在 WLAST 之后出现”写成断言这是 AXI 写通路最容易被忽略、又最影响正确性的顺序约束。6. 低功耗握手协议为什么关时钟也要先“打招呼”AXI 的低功耗接口不是额外挂一套电源管理总线而是把“关时钟”这个动作也纳入了握手协议。它用三组信号描述两件事设备的时钟需求和系统的低功耗请求。CACTIVE 由设备给出表示“我现在是否还需要时钟”外设想用时钟就把它拉高时钟控制模块必须马上给时钟。CSYSREQ 和 CSYSACK 则是系统时钟控制模块与设备之间的状态握手正常状态下两个信号都是高电平系统拉低 CSYSREQ 请求设备进入低功耗设备拉低 CSYSACK 确认状态迁移完成退出时系统拉高 CSYSREQ设备拉高 CSYSACK 完成退出。这三组信号的分工值得反复揣摩CSYSACK 只表示“设备状态已经迁移完整”它不表达设备是否愿意进入低功耗愿意不愿意由 CACTIVE 表达。如果设备还有 outstanding 事务需要时钟处理CACTIVE 会一直拉高相当于拒绝了这次低功耗请求系统不能硬关时钟。这样设计的原因很清楚——直接关时钟是不行的事务会悬死在总线上供电回来之后状态永远对不上。把“意愿”和“状态完成度”分成两个信号系统可以安全地知道什么能关、什么不能关。正文里给了两种低功耗管理策略它们恰好对应两种系统架构。第一种是系统轮询系统按固定节奏去查每个设备的 CACTIVE发现拉低了再发起进入低功耗的请求。这实现简单但效率低——系统并不知道设备在轮询间隙已经空闲多久了。第二种是事件驱动设备在真正空闲时自己拉低 CACTIVE系统被动收到信号后再开始 CSYSREQ/CSYSACK 流程。这种更省功耗但系统如果预测马上要用该外设可以选择不发起请求。实际 SoC 里两种策略经常混用轮询兜底、事件驱动提速由功耗管理单元统一调度。设计取舍的精华就在这个“兜底 提速”的组合里。系统强耦合轮询意味着低功耗是被动的、保守的但一定能生效事件驱动意味着响应快、粒度细但要求每个外设懂得在合适的时机表达自己的时钟需求。我在做低功耗集成时习惯先把 CACTIVE、CSYSREQ、CSYSACK 的完整时序图逐拍画出来标清楚每一拍的信号方向和依赖关系再动手接逻辑——这种三信号握手的细节一旦漏一拍仿真里表现出来的就是系统无法入睡或永远醒不过来。AXI 把这套机制做到了协议层面让所有 IP 的功耗控制行为有统一语言可依这份协议规范正好把这些握手时序和信号定义都列全了适合集成时放在手边逐条对照。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站