ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PLL已锁定却无响应?SoC低功耗唤醒调试全链路排查指南

PLL已锁定却无响应?SoC低功耗唤醒调试全链路排查指南 最近在调一块 SoC 的低功耗唤醒碰到一个印象很深的问题唤醒后 PLL 的 lock 位都已经置 1按手册说时钟已经稳定可整机就是没反应。打印没有中断不进来外设纹丝不动看起来就像“醒了但没完全醒”。这个现象在低功耗唤醒调试里非常典型而且坑很深。这篇文章把我实际排查的路径、看过的寄存器、踩过的雷拆开来讲适合做 BSP、低功耗系统集成以及芯片验证的工程师参考。如果你刚开始接触 SoC 唤醒也能照着这个思路一步一步定位而不是只盯着 PLL lock 位打转。1. 先看现象PLL lock 到底能证明什么1.1 PLL 锁定只是“电源和频率”确认PLL 锁相环的作用是把一个参考时钟倍频成高频时钟送给 CPU、总线和各类外设。比如参考时钟是 24MHz 晶振通过压控振荡器和反馈环路倍频成 500MHz 甚至 1GHz。所谓 lock是 PLL 的鉴相器检测到反馈时钟和参考时钟的相位差足够小环路已经收敛输出频率基本稳定。所以看到 lock 标志置位第一反应是“时钟稳了可以跑高速逻辑了”。这句话没毛病但不完整。问题在于PLL lock 只能回答“这个 PLL 的输出频率对不对”它回答不了后面这一串问题PLL 输出有没有被正确接到 CPU 时钟线上CPU 是不是真的退出了复位内存控制器、Flash 控制器是不是已经准备好了外设的时钟门控有没有打开中断控制器有没有把唤醒事件正确路由给 CPU如果上面任何一环没跟上CPU 即使拿到时钟也会因为取指失败、总线挂死、中断进不来而表现得“无响应”。我后面排查时就发现过 PLL 完全锁好了但 CPU 的主时钟源还被选到一棵没有信号的时钟树上导致所有访问全部悬空。所以PLL lock 应该被理解成一个必要条件而不是充分条件。1.2 “已 lock 却无响应”的常见假象可以把系统唤醒比作一栋楼恢复供电。发电厂并网成功、输出电压稳定不代表整栋楼的照明、空调、电梯都立刻能转。楼层配电柜没合闸、电梯控制器没复位、应急灯还在用电池PLL 管不着这些。实际调试中我见过几种“假 PLL lock”第一种lock 信号本身带毛刺。模拟 PLL 锁定瞬间lock 输出可能会有几十纳秒到几微秒的抖动有些芯片手册会要求软件连续读两次以上甚至加延时后再确认。如果启动代码看到 lock 立刻往下走可能刚好踩在毛刺窗口里。第二种lock 是对某一个特定频点而言的。唤醒后倍频系数被别的条件改写PLL 输出频率和预期不一致但 lock 照样置位。此时 CPU 跑起来时序错误访问外设就是随机挂死。第三种系统里不止一个 PLL。你看到的是 A 频段 PLL 的 lock可 CPU 用的是另一个 PLL 的输出来源。很多人只检查自己熟悉的那个 lock 位忽略了芯片有多个 PLL 和多个时钟源选择位。所以遇到“PLL 已 lock 但无响应”第一步不是怀疑 lock 位坏了而是把它当成一个提示去检查系统恢复的完整链路。这也是我这篇文章想强调的核心低功耗唤醒是一个状态机不是一个“电源键”按下去就完事。2. 低功耗唤醒全链路拆解lock 只是中间一站2.1 一条典型的唤醒序列不同 SoC 的低功耗模式千差万别但绝大多数唤醒序列可以抽象成下面这个流程。我把它画成一张核对表每次调试都对着看步骤典型操作可能卡点1唤醒源触发RTC、GPIO、定时器、外部中断唤醒源没使能或者电平极性配置反了2开启低频参考时钟参考时钟源没稳定PLL 没有可用参考3给目标电源域上电等待电压稳定PMIC 时序不对电压斜坡没完成4使能 PLL等待 lockPLL 配置错误、lock 信号毛刺5时钟树切换到高速时钟配置分频MUX 选错、分频系数不对6释放 CPU 及外设复位复位释放顺序错Flash/SRAM 没 ready7从固定入口取指执行恢复入口地址错、向量表错8软件恢复外设状态、清唤醒标志、开中断唤醒标志没清系统再次睡回去关键是这个顺序是横向的每一步都有各自的 ready 状态。PLL lock 在第四步后面还压着第五到第八步。很多芯片的第 6、7 步是硬件自动执行的但第 8 步往往要软件做。如果软件恢复代码写得有问题整个唤醒在最后一步翻车看起来同样是无响应。我还遇到过一种情况硬件状态机认为唤醒已经完成自动把 CPU 总线的时钟切到 PLL 输出了但软件恢复代码因为在低功耗期间被“搬”到别的地方或者恢复入口地址没配置好PC 直接跳到一块无效内存。此时 PLL lock 肯定置位CPU 却立即进入 hardfault外设不动行为和无响应一模一样。2.2 电源域上电和复位释放顺序SoC 级别的唤醒最容易被忽略的就是电源域和复位释放的时序。现在的芯片普遍有多个电源域CPU 核域、片上 SRAM 域、Flash 控制器域、IO 域、备份域。不同域的上电斜坡时间不一样上电顺序也常由 PMIC 或内部上电控制单元统一管理。常见问题唤醒开始后PMIC 先给 CPU 域加电但 SRAM 或 Flash 域的电源还没稳定。这时候如果 CPU 复位释放CPU 第一件事就是去取指而内存子系统的电源还没到规定电平总线握手失败CPU 就会长时间 stall甚至触发总线超时。调试器连上去PC 停在取指阶段但你可能觉得“寄存器还能读CPU 明明活着”——实际上系统卡在总线等待上。复位释放顺序也类似。很多外设模块在低功耗模式下被复位唤醒时需要先解除模块复位、等它初始化完成再释放它的时钟门控。如果反过来时钟先开了复位还按着模块内部寄存器处于复位态驱动去写配置就全部被忽略外设当然不响应。所以我会在唤醒恢复代码里把“电源域状态寄存器”“复位状态寄存器”“时钟 ready 寄存器”全部读一遍确认没有哪个域停留在低功耗状态。这个习惯救了我很多次。2.3 时钟树恢复的二级管链时钟树不是“PLL 输出直接接 CPU”这么简单。中间有若干级PLL 输出后可能先经过一个固定的 VCO 分频器然后是系统时钟 MUX选择 PLL 还是别的时钟源再经过 CPU 分频、总线分频、外设分频最后每一路还有单独的时钟门控 enable。检查时钟恢复时我会带一个“时钟树清单”逐级核对参考时钟源是否已在运行PLL lock 状态是否持续有效系统时钟 MUX 是否切到了该 PLLCPU 分频系数是否为预期值AHB/APB 总线时钟是否开启每个关键外设的 clock enable 是否打开如果 MUX 还是低功耗时的默认低频源CPU 可能跑在几百 kHz看起来“能响应但奇慢无比”或者某些轮询超时判定失败被误认为无响应。更狠的是如果 MUX 选到一个没有任何时钟输入的死端CPU 访问内部总线的频率直接归零程序完全卡死。3. 从“无响应”反推卡点三分法排查3.1 拆解“无响应”的具体表现先别急着查 PLL。拿到问题我会把“无响应”拆成几个子问题有无任何电流变化如果唤醒后电流跳变明显说明电源域确实起来了如果电流纹丝不动大概率唤醒源没触发或 PMIC 没收到请求。串口有没有打印没有打印可能是 UART 外设没恢复也可能是 CPU 根本没跑起来。要区分调试器连上后有没有心跳、程序计数器是否变化。中断有没有响应可以手动触发一个软件中断看 CPU 是否跳进 ISR。如果手动中断能进去说明 CPU 和中断控制器没问题问题在唤醒源/中断配置上。外设是否可访问直接读一个外设版本的只读寄存器如果读到 0xFFFFFFFF多半总线上没有回应时钟门控或者电源域有问题。把“无响应”细化成可以测量的现象就成功了一半。否则很容易被表象带偏明明 CPU 在跑却一直去查 PLL。3.2 看复位状态寄存器判断复位来源几乎所有 SoC 都有复位原因寄存器。唤醒后第一件事我会读这个寄存器。它能告诉你是哪一种复位让系统走到当前状态如果是上电复位说明系统可能经历了一次完整重启不是低功耗唤醒如果是外部引脚复位问题可能出在复位按键或 PMIC 时序如果是看门狗复位那说明 CPU 在唤醒后其实跑过一段代码然后跑飞看门狗把它拽回来我们看到的“无响应”是复位循环如果是低功耗唤醒复位说明唤醒流程已经走了一部分但可能卡在后半段或者复位释放条件没满足。我碰到的几次诡异现象最后都靠这个寄存器真相大白。有一次看门狗复位配合 PC 停在某个地址立刻反应过来是唤醒后立即进入了未初始化的 while 循环而不是“没唤醒”。3.3 跟进调试器定位 PC 和栈能接调试器是最好的。连上后第一步暂停 CPU看 PC、LR、SP以及关键控制寄存器。PC 停在 0x00000000 或像一个无效地址说明入口地址/启动配置有问题。PC 停在某个 while 循环比如等待外设就绪的死等说明某个 ready 信号没来。SP 是一个无效地址表示低功耗恢复代码没有正确恢复栈指针进 ISR 或函数调用就直接 hardfault。读 fault 状态寄存器比如 ARM 的 CFSR、HFSR、BFAR可以拿到是总线错误、用法错误还是取指错误。这一点我强烈建议写进 BSP 的唤醒调试工具里。如果芯片支持 trace可以把恢复过程中的关键地址打出来看 PC 跑到哪里停住。比如写一个很小的“心跳函数”在唤醒恢复路径开头、中段、结尾各翻转一次 GPIO用逻辑分析仪抓波形就知道卡在哪个函数。3.4 外围总线与外设时钟门控检查如果 CPU 还在跑只是外设不动重点查总线时钟与门控。AHB/APB 总线是否被别的模块占用比如 DMA 还挂着低功耗前的传输总线仲裁器可能死锁。外设寄存器读写是否有回应读一个外设识别寄存器比如 ID 寄存器或版本寄存器如果返回正确说明时钟门控和电源域没问题如果总线错误直接锁定在外设时钟或电源。检查外设的低功耗配置寄存器。很多外设有专门的 low power mode 位唤醒后硬件不会自动清除需要软件写控制位退出低功耗模式。UART、I2C、SPI 都可能有这种机制。3.5 Flash 和 SRAM 控制器的恢复状态内存子系统是“无响应”的重灾区特别是有 Flash 的 SoC。唤醒后 Flash 控制器可能还停留在低功耗模式或者其参考时钟还没恢复CPU 取指时读不到代码。此时 PLL lock 没有用因为 PLL 只是给 Flash 控制器提供一部分时钟Flash 内部的 charge pump、sense amplifier 还没有 ready。SRAM 可能有保持电压或者阵列进入低功耗唤醒后需要等待 memory ready 信号。如果 datapath 还没稳定CPU 读 SRAM 里保存的上下文就会读到随机数据。最稳妥的验证方法在进入低功耗前把一小段变量地址的 8 字节写入魔数唤醒恢复代码里先把这个地址读出来比对。如果魔数错乱说明内存恢复有问题。另外还有一部分 SoC 要求在进入低功耗前关闭 cache唤醒后 invalidate cache。如果 cache 里有脏数据CPU 读到的是低功耗前的老状态或者 cache tag 错乱同样会表现为程序逻辑异常和无响应。4. 现场实录几个容易把 PLL lock 带沟里的坑4.1 Flash 还没醒CPU 已经开跑有个项目低功耗唤醒后系统跑不起来PLL lock 正常。接上调试器PC 停在系统启动代码的前几条指令怎么都读不进来。后来看 SoC 手册里的 Flash 唤醒时序才发现唤醒流程要求先等待 Flash 退出低功耗再释放 CPU 复位或者软件在读 Flash 前必须轮询 Flash 控制器的 ready 位。我们当时的启动代码没有这个等待CPU 复位一释放就去取指Flash 控制器还在“半醒”状态数据总线返回的都是垃圾CPU 把垃圾当指令执行自然乱跳。修复方法也简单唤醒后第一步把 Flash 控制器的状态寄存器读一遍等 ready 置位再往下走。把这个等待加在复位向量最早的汇编代码里问题就没了。4.2 唤醒后中断总开关是关的还有一种情况CPU 在跑main 循环也在转外设状态寄存器也正常但就是该响应的事件都不响应。查了半天发现是进入低功耗前主程序把全局中断关了比如 ARM 的 PRIMASK 置 1唤醒后没有恢复。唤醒事件内核已经记录在 NVIC 的 pending 位但 CPU 的中断 mask 没打开ISR 永远不执行从外部看就是“无响应”。这种问题在代码审查里很容易漏因为它不影响打印也不影响轮询只影响中断。我的习惯是在进入低功耗的函数里把中断屏蔽状态保存下来唤醒后第一件事不是开中断而是恢复原来的屏蔽状态。如果原状态就是关的那就继续关着避免在初始化没完成时被中断打断如果原状态是开的再开中断。同时还要检查优先级分组有没有被低功耗流程改掉。4.3 外设寄存器被锁键“焊死”芯片验证阶段碰到过一个更隐蔽的坑唤醒后去配置外部内存接口寄存器写不进去写什么都读出来是零。后来发现是低功耗前某段代码为了防止误写把寄存器区域上了锁类似 write lock唤醒后没有解锁。这个 lock 和 PLL lock 完全是两码事但如果你只盯着 PLL lock会以为系统没问题结果外设的配置接口一直被锁着所有写操作都被硬件忽略。这类锁键寄存器通常要求先写一个固定的 unlock 序列再写数据。排查时我一般会在唤醒恢复路径里把涉及外设的 lock/unlock 状态统一打印出来对比进入睡眠前的值。如果有问题先把解锁函数挂上再去操作外设。带安全特性的 SoC 上这种坑非常常见。4.4 PLL lock 标志有毛刺需要二次确认有个平台PLL lock 置位很快但紧接着会有一个短暂的翻转。如果软件在 lock 后的 10μs 内配置分频器和 MUX配置结果就可能落在不稳态导致系统时钟异常。后来我们干脆写了一个等待 PLL 锁稳定的函数连续读 5 次 lock 位全部为 1 才算稳定并且延迟 50μs。改完之后再也没出现过“PLL lock 了但时钟不对”的怪问题。不同 PLL 的 lock 时间差异很大模拟环路带宽、参考频率、倍频次数都会影响。不要只相信手册给的最大 lock time实际板子上冷启动、热启动、从不同低功耗模式唤醒的 lock 时间也可能不一样。最保险的做法是在睡眠前测一遍 lock 时序记录最小值和最大值作为唤醒超时判断的依据。4.5 唤醒标志没清干净系统反复睡回去最后这个坑一度让我怀疑是 PLL 没锁好。现象是唤醒后系统亮一下过几百毫秒又睡死没有任何打印好像被什么力量“瞬移”回低功耗。查来查去发现是唤醒源的中断状态寄存器没有清。唤醒事件产生了中断ISR 跑了开头还没来得及清标志主流程就检测到“唤醒事件仍然有效”认为是误唤醒立刻重新进入睡眠。于是在外部看来设备就是“无响应”——它其实一直在睡眠循环里打转。解决方法是在唤醒 ISR 或者低功耗恢复代码里第一时间读取并清掉唤醒源对应的 pending 位同时读取唤醒事件的时间戳或额外条件做二次确认避免被一个尚未消失的边沿反复唤醒。我自己的习惯是在 BSP 的唤醒路径里维护一份“恢复状态快照”把 PLL lock、复位原因、内存控制器 ready、外设时钟门控、中断屏蔽值、唤醒源标志全部填到一个结构体里挂在调试通道上。这样一旦唤醒失败直接读快照就能知道卡在哪里而不是反复猜。最后再分享一个建议做低功耗唤醒调试先去看 SoC 手册里的唤醒时序图把它打印出来贴在工位上。PLL lock 只是这张大图里的一个方框。把时钟、电源、复位三条链路按顺序过一遍绝大多数“已经 lock 却无响应”的问题都能在两三个小时内定位。
返回列表