2. 嵌入式低功耗基础

做嵌入式开发的朋友,尤其是搞过电池供电设备的,应该都有这种体会:产品功能做完了,一测功耗,傻眼了。待机电流几十毫安,电池撑不过三天。这时候你才想起来,哦,原来低功耗不是靠「少写几行代码」就能解决的。

这一章,我跟你聊聊低功耗设计的四个核心策略。说白了,就是怎么让芯片在干活的时候少耗电,不干活的时候几乎不耗电。这些策略我在多个量产项目里都用过,有些坑也踩过,今天一并分享给你。

核心观点:低功耗设计不是事后补救,而是从架构层面就要考虑的事情。时钟、电源、电压、频率,这四个维度你都得管。
低功耗设计四大策略 低功耗设计 时钟门控 Clock Gating 电源门控 Power Gating 多阈值电压 Multi-Vt DVFS 动态调压调频 策略选择原则 • 时钟门控:最基础、最容易实现,适合模块级空闲控制 • 电源门控:效果最显著,但需要状态保存和唤醒逻辑 • 多阈值电压:芯片设计阶段决定,软件无法改变 • DVFS:运行时动态调整,需要硬件支持和调度策略

时钟门控:最基础的省电手段

时钟门控,英文叫 Clock Gating。这玩意儿说白了就是:模块不用的时候,把它的时钟关掉。

你想想看,CMOS 电路的功耗主要来自动态功耗,而动态功耗跟时钟翻转频率成正比。时钟每跳一次,门电路就要充放电一次。如果模块闲着没事干,时钟还在那啪啪啪地跳,电就白白浪费了。

经验之谈:我做过一个传感器采集项目,ADC 模块采样率只有 100Hz,但时钟一直开着。加上时钟门控后,ADC 模块的功耗直接降了 70%。这还是在没有改任何硬件的情况下。

实现方式其实不复杂。一般有两种:

  • 组合逻辑门控:用 AND 门或 OR 门把时钟使能信号和时钟信号组合起来。简单粗暴,但容易产生毛刺。
  • 锁存器门控:在使能信号后面加一个锁存器,再用锁存器的输出控制时钟。这样能避免毛刺,是工业界的主流做法。
// 组合逻辑门控(不推荐,有毛刺风险)
assign gated_clk = clk & clk_en;

// 锁存器门控(推荐,无毛刺)
always_latch begin
    if (!clk)
        gated_en <= clk_en;
end
assign gated_clk = clk & gated_en;
注意:时钟门控的粒度要把握好。粒度太粗,省电效果有限;粒度太细,控制逻辑本身也会耗电,而且时序收敛会变得很麻烦。我个人习惯是:以功能模块为单位,一个模块一个门控。

电源门控:断它的电

时钟门控虽然好,但有个问题:时钟关了,漏电流还在。对于深亚微米工艺,漏电流占比越来越高,有时候甚至能占到总功耗的 40% 以上。这时候就得请出电源门控了。

电源门控,就是直接把模块的电源切断。用一组开关(通常是高阈值电压的 PMOS 或 NMOS)把模块的电源轨和全局电源轨隔开。模块断电后,漏电流几乎为零。

对比项 时钟门控 电源门控
节省功耗类型 动态功耗 动态功耗 + 静态功耗
恢复时间 几个时钟周期 几十到几百微秒(需要重新初始化)
状态保持 寄存器状态保留 状态丢失,需要保存/恢复
实现复杂度 低 高(需要电源开关、隔离单元、状态保持寄存器)
适用场景 短时间空闲 长时间休眠

嗯,这里要注意。电源门控不是你想断就能断的。模块断电前,你得把关键状态保存到不掉电的区域(比如 retention register 或者外部存储器)。上电后,还得做初始化。我曾经在一个项目里吃过亏——模块断电后,输出端口浮空,导致下游模块的输入漏电。后来加了隔离单元(Isolation Cell)才解决。

避坑指南:电源门控的唤醒时间一定要算清楚。如果模块需要频繁唤醒,电源门控反而可能更耗电——因为上电瞬间的浪涌电流和状态恢复的功耗,可能比一直开着还大。

多阈值电压:芯片设计阶段的省电术

多阈值电压,Multi-Vt,这个策略是芯片设计阶段定下来的,软件工程师基本改不了。但我还是建议你了解一下,因为选芯片的时候用得上。

阈值电压 Vt 越高,晶体管的开关速度越慢,但漏电流越小。反过来,Vt 越低,速度越快,漏电流越大。芯片设计的时候,工程师会把不同 Vt 的单元混用:

  • 高 Vt 单元:用在非关键路径上,省电
  • 低 Vt 单元:用在关键路径上,保证性能
  • 标准 Vt 单元:用在一般路径上,平衡功耗和性能

我记得有一次选型,A 芯片和 B 芯片功能完全一样,但 A 芯片用了更多的低 Vt 单元,跑同样的频率,漏电流比 B 芯片大了将近一倍。最后我们选了 B 芯片,待机功耗直接达标。

小技巧:看芯片数据手册的时候,留意一下「漏电流」或者「待机电流」这个参数。如果同工艺、同频率下,某款芯片的漏电流明显偏大,很可能就是低 Vt 单元用多了。

动态电压频率调整:要多少给多少

DVFS,全称 Dynamic Voltage and Frequency Scaling。这玩意儿是低功耗设计里最灵活、也最考验系统设计能力的一个策略。

原理很简单:芯片的功耗跟电压的平方成正比,跟频率成正比。所以,降低电压和频率,功耗会大幅下降。但问题是,电压降得太低,芯片就跑不动高频了。所以 DVFS 的核心就是:根据当前负载,动态调整电压和频率,做到「要多少给多少」。

// DVFS 调度策略伪代码
void dvfs_scheduler(void) {
    uint32_t load = get_cpu_load();  // 获取当前CPU负载
    
    if (load < 20) {
        set_voltage(0.8V);
        set_frequency(50MHz);   // 低频低电压,省电
    } else if (load < 60) {
        set_voltage(1.0V);
        set_frequency(200MHz);  // 中频中电压
    } else {
        set_voltage(1.2V);
        set_frequency(400MHz);  // 高频高电压,全力输出
    }
}

实际项目中,DVFS 的实现比这个复杂得多。你需要考虑:

  • 电压切换时间:稳压器调整电压需要时间,通常几十到几百微秒
  • 频率切换时间:PLL 重新锁定也需要时间
  • 切换顺序:升频时先升压再升频,降频时先降频再降压——顺序搞反了,芯片会死机
  • 负载预测:不能等负载上来了才调,要提前预测
血的教训:我曾经在一个项目里,DVFS 的切换频率设得太高,每 10 毫秒就切一次。结果电压还没稳定,频率又变了,导致芯片频繁复位。后来我把切换间隔拉长到 100 毫秒,加了一个「稳定等待」状态,问题才解决。

四种策略怎么选?

说了这么多,你可能会问:我到底该用哪种?

我的建议是:

  1. 先上时钟门控:这是最基础、最安全的。几乎所有芯片都支持,几乎没有副作用。不做白不做。
  2. 再考虑 DVFS:如果芯片支持,而且你的系统负载变化比较大,DVFS 能省不少电。但要做好负载预测和切换策略。
  3. 最后上电源门控:如果待机功耗要求特别严苛(比如微安级),而且模块可以长时间休眠,那就上电源门控。但要做好状态保存和唤醒逻辑。
  4. 多阈值电压:这个你选芯片的时候就要看。如果两款芯片价格差不多,选漏电流小的那个。

四种策略不是互斥的,可以组合使用。比如:模块空闲时先关时钟(时钟门控),如果空闲时间超过 1 秒,再断电(电源门控)。运行的时候根据负载动态调压调频(DVFS)。这样组合下来,功耗能降到最低。

总结一下:低功耗设计没有银弹。时钟门控是基础,电源门控是狠招,多阈值电压是芯片底子,DVFS 是运行时调优。把这四招用好,你的产品续航不会差。