APB总线协议实战:从两拍半时序到Verilog从机实现与验证

发布时间:2026/9/29 5:08:51
APB总线协议实战:从两拍半时序到Verilog从机实现与验证 做SoC集成的朋友大概都有过这种体验外设寄存器手册写得明明白白代码一跑读出来不是 0 就是 0xFFFFFFFF抓波形看上半天最后发现是自己没吃透 APB 那两拍半的时序。AMBA 总线协议这个体系里AXI4 总线协议和 AHB 总线协议经常被拿来做性能分析APB 协议却总被当成简单到没什么可讲的那一个。但恰恰是 APB 总线几乎每一个芯片里都会出现几十上百个实例——UART、I2C、GPIO、看门狗、时钟控制器、复位控制器统统挂在它上面。它的简单是一种刻意的设计取舍理解了这个取舍你才知道什么时候该用它、什么时候必须换成 AHB 或 AXI4。这篇总结适合三类人看刚接触数字前端、第一次手写 APB 从机的同学做 SoC 集成、需要把第三方 IP 挂到总线上却总在时序上栽跟头的工程师还有做验证、要写 APB 时序断言和覆盖率的人。我会从协议定位讲到信号语义从逐拍时序讲到一份可以直接抄的 Verilog 从机实现最后把这几年踩过的坑整理成一张速查表。APB 时序这东西看十遍手册不如自己对着波形数一遍拍子下面我们就按这个思路来。1. AMBA总线家族里APB的定位与方案取舍1.1 为什么一条慢总线值得单独存在AMBA 从诞生到现在一直是分层设计的思路AXI4 总线协议负责高带宽、乱序、多 outstanding 的主干数据搬运AHB 总线协议负责中高带宽、流水线化的片上互联而 APB 协议负责把那些跑不快、也用不着跑快的寄存器型外设挂上去。这个分层不是历史包袱而是非常实在的成本取舍。你可以把整个片上总线想象成城市交通AXI 是高速公路AHB 是城市主干道APB 就是小区门口那条单车道。单车道为什么不能被主干道取代因为每条车道都要配红绿灯、监控、路牌这些都是面积和功耗。APB 的信号数量少得可怜一个最简的从机只需要 PCLK、PRESETn 加七八根线就能工作它没有流水线、没有 burst、没有 outstanding 的概念任何一笔传输都是独立的、不可打断的。这意味着从机的状态机可以极其简单往往几十行 RTL 就能搞定综合出来的面积小、时序余量大、验证工作量和出错概率都低。反过来如果你把一个 GPIO 的寄存器组接到 AXI4 上会发生什么你需要处理 ID 通道、处理 burst 边界、处理写响应通道的乱序返回、处理 4KB 边界规则。这些机制对一块只有四个寄存器的外设来说完全是浪费。所以 APB 的价值不在于能跑多快而在于用最小的代价把简单外设接进系统这是一个典型的工程性价比选择。1.2 APB2、APB3、APB4 三代协议的信号差异很多人写文档时会笼统地说APB 协议但实际项目里你用的是哪一代直接决定了从机能不能插入等待周期、能不能报错。下面这张表是我自己整理的三代对比按信号维度列出来更直观。信号APB2APB3APB4 及之后说明PCLK / PRESETn有有有时钟与低有效复位PSELx有有有从机片选每从机独立PENABLE有有有访问阶段使能PADDR有有有字节地址PWRITE有有有1 写 0 读PWDATA有有有写数据PRDATA有有有读数据PREADY无有有APB3 引入支持等待周期PSLVERR无有有APB3 引入支持错误响应PSTRB无无有字节选通PPROT无无有保护属性含特权与安全信息PWAKEUP无无后续版本补充低功耗唤醒握手这张表里最关键的判断点就是 PREADY。APB2 时代没有 PREADY意味着任何一笔传输都是固定的两拍SETUP 一拍、ACCESS 一拍从机必须在这一拍里把数据准备好。这在早期工艺下还能接受但当外设内部有跨时钟域、有 FIFO、有慢速模拟接口时固定两拍就成了灾难——你被迫在从机里加缓冲、加握手反而把简单问题复杂化了。APB3 引入 PREADY 之后从机才真正获得了拉长 ACCESS 阶段的能力想等多久等多久只要 PREADY 保持低电平就行。APB4 又补上了 PSTRB 和 PPROT前者让字节写操作不用整字读改写后者让安全和非安全访问能在总线层面区分开。所以你在选 IP 的时候一定要先确认系统互联用的是什么版本再决定从机怎么实现——给一个 APB4 互联写从机你却按 APB2 固定两拍的方式处理遇到需要等待的场景必挂。1.3 吞吐率上限的量化推算经常有人问我 APB 到底能跑多快这个问题不能凭感觉答得算。APB 没有流水线一笔传输最少两拍——SETUP 一拍加 ACCESS 一拍所以理论峰值吞吐率的公式是峰值吞吐率 PCLK 频率 × 数据位宽 / 8 / 2如果 PCLK 跑 50MHz、数据位宽 32 位那就是 50M × 4 ÷ 2 100MB/s。把 PCLK 提到 100MHz同样 32 位峰值也就 200MB/s。这个数字放到今天看其实很低AXI4 在同样频率下随便就能跑出几个 GB/s。但这个算法只对零等待传输成立。一旦从机在 ACCESS 阶段插入一个等待周期一笔传输变成三拍吞吐率立刻掉到原来的三分之二插入 N 个等待周期吞吐率就是理论峰值除以 (N2)。这也是为什么在 APB 外设里要尽量避免长等待——如果你需要频繁搬运大块数据那说明这块外设本来就不该挂在 APB 上应该换成带 burst 的 AHB 或 AXI4。反过来说APB 的这个两拍下限也给了设计者一个明确的约束如果一块外设的寄存器访问频率很低比如配置寄存器一辈子写一次插入几个等待周期完全无所谓但如果它是数据通路的一部分比如 DMA 的配置寄存器、或者一条慢速 SPI 的收发寄存器就得算清楚等待周期带来的带宽损失。我在做音频采样率转换器的时候就吃过这个亏早期版本把数据寄存器放在 APB 上每个采样点都要读一次带等待周期的寄存器最后算下来带宽刚好卡在临界点稍微加一点处理延迟就爆音了。2. APB信号语义与三态时序的逐拍拆解2.1 信号清单与每个信号的语义边界理解 APB 的关键是理解每根线的有效窗口——大部分信号不是全程有效的只在特定拍子里才有意义。这是我见过最多的翻车点有人把 PWDATA 当成全程有效结果在 SETUP 阶段就把它接进了组合逻辑也有人以为 PRDATA 在 SETUP 阶段就该准备好然后困惑为什么读出来是旧值。PSELx 是片选一旦拉高就代表这笔画到我了它会从 SETUP 一直保持到 ACCESS 结束。注意每根 PSEL 是独立的一个互联里通常有一根 AHB-to-APB 桥挂多个从机每个从机有自己的一根 PSEL。PENABLE 是访问阶段使能它在 SETUP 阶段必然是低的到 ACCESS 阶段才拉高。PADDR、PWRITE、PWDATA、PPROT 这几根属于控制与数据它们在 SETUP 阶段就必须已经有效并且在 ACCESS 阶段保持稳定直到传输完成。PRDATA 是唯一的例外它只在 ACCESS 阶段的最后一拍也就是 PREADY 拉高那一拍才被采样所以从机只要在 PREADY 拉高的同时把数据放上去就行。PREADY 由从机驱动低电平时表示我还没准备好高电平时表示这笔完成了。PSLVERR 也是从机驱动的并且只在传输的最后一拍有效表示这笔传输失败了。PSTRB 在写传输时选通哪些字节有效四位对应 32 位数据的四个字节。2.2 IDLE、SETUP、ACCESS 三态下的逐拍动作APB 的状态机只有三个状态简单到可以用一张时序表说清楚但每一拍的动作必须记牢。IDLE 阶段PSEL 为低PENABLE 为低总线空闲。此时从机不应该对任何输入做出响应PRDATA 输出建议保持默认值通常是 0PREADY 可以是任意值但推荐拉高。SETUP 阶段一笔传输的开始。PSEL 拉高PENABLE 保持低同时 PADDR、PWRITE、PWDATA、PPROT 全部有效。这个阶段固定只有一拍无论从机是否有等待都必须走完这一拍才能进 ACCESS。我在第一次写从机时曾经试图跳过SETUP 阶段在 PSEL 拉高的同一拍就直接响应结果桥那边采样不到正确的地址整条总线挂死。ACCESS 阶段PENABLE 拉高PSEL 继续保持高。如果从机不需要等待PREADY 在这个阶段的第一拍就是高那么这笔传输两拍结束。如果从机需要等待PREADY 保持低ACCESS 阶段就被拉长同时 PSEL、PENABLE、PADDR、PWRITE、PWDATA 都必须保持不变。等到从机把 PREADY 拉高的那一拍传输完成——这一拍也是采样 PRDATA读操作或真正执行写操作写操作的时间点。这里有个容易被忽略的细节PREADY 拉高的那一拍PSEL 和 PENABLE 仍然是高的紧接着的下一拍才会进入下一个传输的 SETUP或者回到 IDLE。也就是说判据永远是PSEL 高、PENABLE 高、PREADY 高三者同时成立缺一不可。我在做验证的时候见过从机把判据写成 PENABLE PREADY漏掉了 PSEL结果在总线空闲但 PENABLE 残留的情况下误触发了一次写。2.3 PREADY 握手的两种等待模型从机什么时候该插入等待周期看起来是个自由选择其实有很强的工程约束。我把实际项目里常见的做法归成两类。第一类是零等待模型PREADY 直接恒接高电平。适合那些寄存器纯粹是触发器、读写操作组合逻辑就能完成的从机比如 GPIO、简单的定时器配置寄存器。这种从机面积最小时序最好收敛验证也最省事。我个人的习惯是只要没有跨时钟域、没有 FIFO、没有需要多周期计算的寄存器一律用零等待。第二类是可变等待模型PREADY 根据内部状态动态产生。典型场景有三种一是寄存器读取需要经过慢速组合路径比如读一个模拟模块的采样值需要多打几拍二是从机内部有跨时钟域逻辑需要同步器稳定后再返回三是访问 FIFO 或存储阵列需要等读写指针更新。这时候 PREADY 的产生逻辑要特别注意——它必须是组合可控的不能因为等待期间内部状态抖动而出现毛刺。我在一个 SPI 从机项目里踩过一个典型的坑从机的 PREADY 是用一个计数器产生的计数器在 PREADY 拉高后又继续跑了一拍才复位结果下一笔传输刚进 SETUP 阶段就发现 PREADY 还是低的桥误以为从机又插入了一个等待周期导致后续时序整体错位。后来把计数器改成PREADY 拉高的同时清零就解决了。这类问题的核心是PREADY 的高低必须严格对应这笔传输是否完成任何残留状态都会污染下一笔。2.4 PSLVERR 的正确定义与三个高频误用PSLVERR 是 APB3 引入的错误响应信号语义是这笔传输失败了。但它的用法有三个非常典型的误用我觉得值得单独拉出来讲。第一个误用是把 PSLVERR 打成寄存器输出。很多人写 RTL 时习惯把所有输出都寄存一拍觉得这样时序好。但 PSLVERR 只在传输的最后一拍有效如果你把它打了一拍桥会在下一拍才看到错误信号而此时 PSEL 和 PENABLE 可能已经撤了错误就被丢掉。正确做法是让 PSLVERR 组合产生判据是 PSEL PENABLE 地址未命中或者任何错误条件。第二个误用是把 PSLVERR 当成重试请求。有些设计者希望从机报错后主机能自动重发于是在从机里保持 PSLVERR 高电平等待重发。这是错的——APB 里 PSLVERR 只表示这笔传输失败协议本身没有重试机制重发是软件层面的事。从机唯一要做的是在错误发生的那一拍拉高 PSLVERR然后立刻释放。第三个误用是错误和等待不分。有些从机发现地址没译中习惯性地把 PREADY 拉低进入等待指望主机超时放弃。这会导致总线挂死因为主机没有任何超时机制它会永远等下去。正确做法是地址没译中就立刻把 PREADY 拉高、同时把 PSLVERR 拉高一拍结束这笔传输并报错。这是 APB 里非常关键的一条经验我在做互联集成时见过不止一次因为这个问题导致整个系统上电后卡死。注意PSLVERR 和 PREADY 必须是在同一拍同时给出的组合信号任何一方的延迟都会破坏协议的原子性。3. 手写一个APB从机外设从寄存器规划到RTL落地3.1 寄存器映射与地址译码规划在动手写 RTL 之前先把寄存器映射表定清楚这一步偷懒后面会加倍还回来。拿一个最简单的例子一个带控制寄存器、分频寄存器和状态寄存器的外设地址规划大概是这样。偏移地址名称位宽读写属性说明0x000CTRL32RW使能位与模式选择0x004DIV32RW分频系数低 16 位有效0x008STS32RO状态与 FIFO 计数地址规划有两个原则要遵守。第一偏移量必须按数据位宽对齐。32 位数据宽度下寄存器偏移应该是 4 的倍数否则 PADDR 的低两位永远是 0你在译码时会发现地址比较对不上。第二地址译码要覆盖未命中情况。桥传过来的地址不一定每次都落在你这几个寄存器上未命中的地址必须能被识别出来并报 PSLVERR而不是被默默忽略。在实际的 SoC 里地址译码通常分两层桥那一层做粗译码根据 PADDR 高位选出某一根 PSEL从机这一层做细译码用 PADDR 低位区分具体寄存器。我一般会把从机的地址宽度参数化比如定义一个 ADDR_W 12只用低 12 位参与译码高位在桥里已经消化掉了这样从机可以复用换个基地址就能挂到别的位置。3.2 APB从机的Verilog实现与逐行拆解下面这份代码是我自己项目里一直在用的模板按 APB4 写的向下兼容 APB3把 PSTRB 相关逻辑去掉即可也能退化到 APB2把 PREADY 恒接 1。module apb_regs #( parameter ADDR_W 12, parameter DATA_W 32 )( input wire PCLK, input wire PRESETn, input wire [ADDR_W-1:0] PADDR, input wire PSEL, input wire PENABLE, input wire PWRITE, input wire [DATA_W-1:0] PWDATA, input wire [DATA_W/8-1:0] PSTRB, input wire [2:0] PPROT, output reg [DATA_W-1:0] PRDATA, output wire PREADY, output wire PSLVERR, output reg [31:0] cfg_ctrl, output reg [31:0] cfg_div, input wire [31:0] sts_fifo ); localparam [ADDR_W-1:0] ADDR_CTRL 12h000; localparam [ADDR_W-1:0] ADDR_DIV 12h004; localparam [ADDR_W-1:0] ADDR_STS 12h008; wire addr_hit (PADDR ADDR_CTRL) || (PADDR ADDR_DIV ) || (PADDR ADDR_STS ); assign PREADY 1b1; assign PSLVERR PSEL PENABLE ~addr_hit; wire wr_en PSEL PENABLE PWRITE PREADY addr_hit; integer i; always (posedge PCLK or negedge PRESETn) begin if (!PRESETn) begin cfg_ctrl 32h0; cfg_div 32h0; end else if (wr_en) begin if (PADDR ADDR_CTRL) begin for (i 0; i DATA_W/8; i i 1) if (PSTRB[i]) cfg_ctrl[i*8 : 8] PWDATA[i*8 : 8]; end if (PADDR ADDR_DIV) begin for (i 0; i DATA_W/8; i i 1) if (PSTRB[i]) cfg_div[i*8 : 8] PWDATA[i*8 : 8]; end end end always (*) begin PRDATA 32h0; case (PADDR) ADDR_CTRL: PRDATA cfg_ctrl; ADDR_DIV : PRDATA cfg_div; ADDR_STS : PRDATA sts_fifo; default : PRDATA 32h0; endcase end endmodule这份代码里有几个设计决定值得展开讲。PREADY 直接恒接 1是因为这个外设的所有寄存器都是触发器读写都不需要等待。PSLVERR 用组合逻辑产生判据是PSEL 高、PENABLE 高、地址没命中正好对应传输的最后一拍符合协议要求。写使能 wr_en 的表达式里我特意保留了 PREADY。虽然这里 PREADY 恒为 1写不写这个条件结果一样但如果后续要给某个寄存器插入等待周期wr_en 里带上 PREADY 能保证写操作只在传输真正完成的那一拍执行。这是我从一次翻车里学到的早期版本的 wr_en 只有 PSEL PENABLE PWRITE后来给状态寄存器加了等待逻辑忘记改 wr_en结果写操作在 ACCESS 的第一拍就执行了而那时从机其实还没准备好写进去的值直接被后续逻辑覆盖。读数据的 always 块用的是组合逻辑而且第一句就是给 PRDATA 赋默认值 0。这条看似多余的语句非常关键——没有它case 里未覆盖的分支会让综合工具推断出锁存器latch在数字设计里锁存器是能避则避的东西会带来时序分析上的麻烦。default 分支也建议显式写出即使只是赋 0也能让综合和 SDK 报出未覆盖地址的问题。PSTRB 的处理用了一个 for 循环逐字节更新。这里要注意如果从机不支持字节写很多老 IP 不支持就直接把整字写进去同时把 PSTRB 当无关信号忽略掉。但如果互联是按 APB4 传的从机却忽略了 PSTRB会有一个隐蔽的后果主机做字节写时你会把整个 32 位都覆盖掉相邻字节被意外改写。这种 bug 在小数据量测试里往往测不出来等软件跑到某个特定场景才炸排查成本极高。3.3 接入总线AHB-to-APB桥与互联结构从机写完了怎么接进系统绝大多数情况下你不会直接和主机打交道中间隔着一座 AHB-to-APB 桥或者 AXI4-to-APB 桥。桥内部做的事情大致有这几件。第一是地址译码与 PSEL 生成。桥接收主机侧的地址按高几位判断落在哪个 APB 从机区间然后把对应的 PSEL 拉高同时把地址低位转成 PADDR 传下去。第二是时序转换。主机侧可能是流水线化的地址和数据通道桥需要把它拍平成 APB 的 SETUP-ACCESS 两拍结构通常桥内部会有一个小状态机来完成这个转换。第三是时钟域处理。APB 的 PCLK 未必等于主机时钟桥里可能有一个分频器把 HCLK 二分频或四分频后作为 PCLK也可能需要真正的异步握手。这里有一个非常实用的经验在系统集成阶段一定要确认 PCLK 的实际频率。很多桥默认做二分频如果你按满频算外设的时序参数上板就会发现实际访问速度只有预期的一半。我在调试一块音频 SoC 时软件侧按 100MHz 配置了定时器的分频系数实测周期却总是差两倍查了半天才发现桥把 PCLK 分成了 50MHz改配置表就好了。还有一点关于多从机的互联。桥通常会输出一组 PSEL每个从机接一根。规范要求同一时刻只有一根 PSEL 为高如果你发现波形里有两根 PSEL 同时拉高那基本可以断定桥的地址译码有重叠或者你的从机地址范围配错了。这种情况下的典型现象是主机读某个外设两个从机同时驱动 PRDATA发生总线冲突读回来的数据时对时错。4. APB验证要点断言、覆盖率与调试手段4.1 必须落地的几条SVA断言验证 APB 从机最省力的办法是把协议规则写成 SystemVerilog 断言让仿真工具自己盯着。下面这几条是我每做一个 APB 从机都会加的覆盖了协议里最容易被破坏的规则。// 1. SETUP 之后必须进入 ACCESS 阶段 property p_setup2access; (posedge PCLK) disable iff (!PRESETn) (PSEL !PENABLE) | (PSEL PENABLE); endproperty // 2. ACCESS 阶段在 PREADY 拉高之前必须保持 PSEL/PENABLE property p_access_hold; (posedge PCLK) disable iff (!PRESETn) (PSEL PENABLE !PREADY) | (PSEL PENABLE); endproperty // 3. 等待期间控制信号不得变化 property p_ctrl_stable; (posedge PCLK) disable iff (!PRESETn) (PSEL PENABLE !PREADY) | $stable(PADDR) $stable(PWRITE) $stable(PWDATA) $stable(PSTRB); endproperty // 4. PSLVERR 只在传输最后一拍有效 property p_slverr_last_cycle; (posedge PCLK) disable iff (!PRESETn) PSLVERR |- (PSEL PENABLE PREADY); endproperty // 5. 空闲状态下不应出现访问阶段 property p_idle_no_enable; (posedge PCLK) disable iff (!PRESETn) !PSEL |- !PENABLE; endproperty assert property (p_setup2access) else $error(SETUP did not enter ACCESS); assert property (p_access_hold) else $error(ACCESS phase dropped early); assert property (p_ctrl_stable) else $error(Control signals changed during wait); assert property (p_slverr_last_cycle) else $error(PSLVERR asserted at wrong cycle); assert property (p_idle_no_enable) else $error(PENABLE high while PSEL low);第五条断言我特别想强调。它的作用是保证 PENABLE 不会在 PSEL 为低的时候冒出来这对应前面提到的那个 bug如果从机的 PENABLE 是从桥里直接引出来的而桥的复位或者中断逻辑有毛刺就可能出现 PENABLE 单独拉高的情况。我见过一次因为这条规则没加仿真跑了三天才在某个特定复位序列下复现出误写加上断言之后不到十分钟就定位了。断言的写法上有个技巧disable iff (!PRESETn)一定要加否则复位期间信号全乱断言会大量误报把真正的问题淹没掉。另外$stable比逐位比较更简洁但要注意它只比较值不比较 X 态如果你的信号在等待期间变成 X$stable可能判不出来需要配合 X 检查断言一起用。4.2 功能覆盖率怎么收才算干净APB 从机的覆盖率收集重点不是行覆盖率而是传输场景的交叉覆盖。我的习惯是定义这么几个覆盖点。传输类型上要覆盖读、写两大类并且各自再分零等待和带等待。带等待又细分单周期等待和多周期等待因为我们前面算过等待周期数直接影响吞吐率也是从机逻辑最容易出问题的地方。地址维度上每个寄存器都要被读到和写到同时要有未命中地址的采样用来验证 PSLVERR 路径。还有两个容易被漏掉的覆盖点。一个是 PSTRB 的全部组合如果从机支持字节写至少要把 0x1、0x2、0x4、0x8 和 0xF 这几种采到。另一个是连续背靠背传输——前一笔刚结束下一笔的 SETUP 紧接着就来中间没有 IDLE 间隔。这种场景最容易暴露从机状态残留的问题我前面提到的 PREADY 计数器不复位就是靠背靠背传输才测出来的。4.3 波形调试的几个抓手真到了要对着波形找问题的时候我的习惯是按固定顺序看四样东西基本上能覆盖九成的问题。先看 PSEL 和 PENABLE 的相位关系。正确的波形永远是 PSEL 先起隔一拍 PENABLE 才起且 PENABLE 起的时候 PSEL 还在。如果看到两根线同时起或者 PENABLE 比 PSEL 早那基本可以断定桥或者从机的状态机写错了。再看 PREADY 的形态。如果它一直是高但传输却比预期慢那问题不在从机在桥的分频或者主机的发起频率上。如果它一直低那总线肯定是挂了回头查从机的 PREADY 产生逻辑。第三看 PRDATA 有效的时刻。它应该在 PREADY 拉高的那一拍有效如果提前或者滞后说明从机的读数据路径多打了或者少打了一拍。这个错位在零等待场景下尤其常见因为很容易顺手写成时序逻辑。最后看 PADDR 的对齐。PADDR 的最低位如果出现非零值32 位数据宽度下应该是 2b00说明地址译码或者桥的地址转换有问题这时候即使数据看起来对也只是巧合。5. 常见问题与排查技巧实录5.1 一张速查表搞定八成故障下面这张表是我这些年攒下来的遇到问题先按现象查一遍能省掉大量抓波形的时间。现象最可能的原因排查动作读出来全是 0PRDATA 组合块缺默认赋值或地址未命中检查 case 是否覆盖、PRDATA 是否默认赋 0读回来是 0xFFFFFFFF从机没有驱动 PRDATA总线被上拉确认 PRDATA 是 output 而非 inout确认使能逻辑写进去再读出来是旧值写使能用了 PSEL 而不是 PSELPENABLE检查 wr_en 表达式是否含 PENABLE寄存器偶尔被写两次同上的另一种表现PREADY 等待期间重复写在 wr_en 里加上 PREADY 条件总线整体挂死无响应PREADY 一直为低或地址未命中却没报错查 PREADY 产生逻辑确认未命中路径综合报 latch 推断always (*) 里没有默认赋值所有组合输出先赋默认值字节写把相邻字节改了忽略 PSTRB 做了整字写按 PSTRB 逐字节更新或明确不支持字节写访问速度比预期慢一半桥对 PCLK 做了分频确认 PCLK 实际频率重新核算定时参数PSLVERR 抓不到从机把 PSLVERR 打了拍改为组合输出且只在最后一拍有效两根 PSEL 同时为高桥的地址译码重叠或从机地址范围配错核对地址映射表检查译码比较逻辑5.2 三个文档里不会写的实操经验第一个经验关于背靠背传输下的状态清理。很多从机的设计思路是一笔传输结束就把所有中间状态复位这在单笔传输时没问题但在背靠背场景下前一笔的清理逻辑可能和下一笔的 SETUP 阶段重叠导致下一笔的地址还没稳定就被采样。我在一个外设里遇到过这个问题最后解决方式是把清理逻辑做成在 PREADY 拉高的同一拍完成而不是PREADY 拉高的下一拍。这个时序细节在手册里是查不到的只能靠对着波形数拍子。第二个经验关于复位的处理。APB 的 PRESETn 是低有效异步复位但很多从机在复位释放的那一刻直接开始响应 PSEL结果复位期间桥可能还在发传输导致第一次访问莫名其妙失败。稳妥的做法是从机内部对复位做一个同步释放并且在复位释放后的前几个周期忽略一切请求。这个做法在系统上电阶段尤其重要因为那时候各个模块的时钟稳定时间不一致桥可能已经能发请求了从机还没准备好。第三个经验关于地址译码的边界。我做集成时见过从机的译码写成PADDR[11:2] 10h0然后用 PADDR 的其他位来做寄存器区分——这种写法在单个从机测试时完全没问题但一旦挂到有多从机的互联上高位地址是桥选片用的从机不该再关心。正确做法是把高位在端口上就裁掉比如从机只接 PADDR[11:0]高位由桥消化。这样从机的地址范围永远是 4KB 对齐的也不会出现两个从机同时被判中的情况。提示给你的每一个 APB 从机都加上一条未命中即报错的组合路径哪怕这个从机只有一两个寄存器。它的代价极小却能在系统集成阶段帮你快速定位地址映射错误。5.3 APB与AHB、AXI4的选型判断最后说说选型。判断一块外设该挂哪条总线我一般就看三个问题。第一有没有数据吞吐需求。如果外设需要持续搬运数据比如 DMA、以太网 MAC、显示控制器一律走 AXI4AHB 作为次选。如果只是配置寄存器加上偶尔的一两次数据读写UART、I2C、GPIOAPB 完全够用。第二需不需要乱序和 outstanding。APB 是严格顺序的一笔不完成下一笔不能开始如果外设内部有多个独立的队列需要并行访问APB 就会成为瓶颈。第三面积和功耗预算紧不紧。APB 从机的面积可能是同等功能 AHB 从机的三分之一到五分之一对于一颗芯片里几十上百个外设来说这个差距相当可观。我个人的实践是把所有人机配置类外设全部放在 APB 上把数据搬运类外设放在 AXI4 上中间用 AHB 做桥接和轻量级互联。这个分法在大多数中低端 SoC 上都够用也不会因为某条总线过于复杂而拖慢整个项目的收敛节奏。APB 协议看起来简单但它简单得很有讲究——每一个省略背后都是明确的工程取舍。把它吃透之后你会发现剩下那些复杂协议AHB 的流水线、AXI4 的乱序也不过是在 APB 这个基础上逐层叠加机制而已。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询