3、电源门控技术:电源开关单元、电压降分析、MTCMOS实现、唤醒时间优化
好,咱们接着聊低功耗设计里最硬核的技术之一——电源门控。说白了,就是把芯片里暂时不用的模块,直接把电源给它掐了。这招比动态电压频率调整狠多了,省电效果立竿见影。但代价也不小,实现起来坑特别多。
我最早接触电源门控,是在做一款移动应用处理器的时候。当时为了省电,几乎每个IP核都挂了电源开关。结果第一次流片回来,唤醒时电流冲击太大,直接把供电网络给拉崩了。嗯,从那以后,我对这门技术就多了几分敬畏。
3.1 电源开关单元
电源开关单元,就是那个负责“掐电”和“送电”的开关。它本质上是一个大尺寸的PMOS管或NMOS管。PMOS接在虚拟VDD和真实VDD之间,叫header开关;NMOS接在虚拟VSS和真实VSS之间,叫footer开关。
我个人习惯用header开关。为什么?因为PMOS的衬底可以直接接真实VDD,噪声隔离效果更好。当然,footer开关也有它的优势,比如面积更小,驱动能力更强。具体选哪种,得看你的设计目标和工艺条件。
开关单元的设计有几个关键参数:
- 导通电阻(Ron):决定了电压降。Ron越小,压降越小,但面积越大。
- 漏电流(Ioff):决定了关断时的功耗。Ioff越小,省电效果越好。
- 开关速度:决定了唤醒时间。速度越快,唤醒越快,但冲击电流也越大。
你想想看,这三个参数其实是互相矛盾的。Ron小了,Ioff可能就大了;开关快了,冲击电流就大了。所以设计时得做权衡,没有完美的方案。
重要提示:电源开关单元不是越大越好。我见过有人为了追求极低Ron,把开关尺寸做得特别大,结果漏电流比模块本身的静态功耗还大。这就本末倒置了。
3.2 电压降分析
电压降分析,也叫IR Drop分析。这是电源门控设计里最容易被忽视,但后果最严重的环节。
为什么会这样?因为电源开关本身有电阻,电流流过时会产生压降。如果压降太大,模块的供电电压就会低于正常工作电压,导致时序违例,甚至功能错误。
我在项目中遇到过这样的情况:一个模块在仿真时一切正常,但流片回来后,某些角落的触发器就是采不到正确数据。查了几个月,最后发现是电源开关的IR Drop太大,导致局部电压低了0.1V。0.1V啊,在先进工艺下足以让时序崩盘。
电压降分析通常分两步:
- 静态IR Drop分析:计算平均电流下的压降。这个比较简单,用静态功耗分析工具就能做。
- 动态IR Drop分析:计算瞬态电流下的压降。这个复杂得多,需要做向量仿真或使用红鹰等专业工具。
我个人建议,在做电源门控设计时,一定要做动态IR Drop分析。因为唤醒瞬间的电流冲击,往往是静态分析的几倍甚至几十倍。静态分析只能告诉你“平均情况”,而动态分析才能暴露“最坏情况”。
警告:千万不要忽略电源开关本身的IR Drop。很多新手只关注电源网络的IR Drop,却忘了开关单元本身也是电阻。我曾经见过一个设计,电源网络IR Drop只有20mV,但开关单元上的压降却高达80mV。加起来100mV,直接超标。
3.3 MTCMOS实现
MTCMOS,全称Multi-Threshold CMOS,是多阈值电压CMOS技术。它是实现电源门控的基础工艺手段。
简单来说,MTCMOS把晶体管分成两类:
- 高阈值电压(HVT)晶体管:漏电流小,但速度慢。用于电源开关单元。
- 低阈值电压(LVT)晶体管:速度快,但漏电流大。用于逻辑电路。
你想想看,这个搭配很巧妙。逻辑电路用LVT,保证性能;电源开关用HVT,保证关断时漏电流足够小。两者结合,既省电又不牺牲速度。
MTCMOS的实现方式主要有两种:
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| Fine-Grain MTCMOS | 每个标准单元都带独立的电源开关,控制粒度细 | 面积开销大,设计复杂度高 |
| Coarse-Grain MTCMOS | 整个模块共享一组电源开关,面积开销小 | 控制粒度粗,唤醒时间长 |
我个人更倾向于Coarse-Grain MTCMOS。为什么?因为Fine-Grain虽然控制灵活,但面积开销太大了。在先进工艺下,每个标准单元都加一个开关,面积可能增加30%以上。而Coarse-Grain只需要在模块周围加一圈开关,面积增加不到5%。
当然,Coarse-Grain也有它的缺点。比如唤醒时间更长,因为要同时给整个模块充电。但这个问题可以通过优化唤醒时序来解决,后面会讲到。
提示:在做MTCMOS设计时,别忘了考虑“保持寄存器”。当模块断电时,寄存器里的数据会丢失。如果需要在唤醒后恢复状态,就得用保持寄存器。这种寄存器在断电时能通过一个独立的“保持电源”来保存数据。我建议在关键状态寄存器上使用保持寄存器,其他非关键寄存器可以不用。
3.4 唤醒时间优化
唤醒时间,就是从发出唤醒信号到模块完全恢复正常工作的时间。这个时间越短越好,因为唤醒期间模块是不能正常工作的。
但唤醒时间不是想短就能短的。它受几个因素限制:
- 电源开关的导通速度:开关从关断到完全导通需要时间。
- 虚拟电源网络的充电时间:模块内部的寄生电容需要充电。
- 冲击电流的限制:如果充电太快,冲击电流可能超过电源的承受能力。
我曾经在一个项目中,为了追求极短的唤醒时间,把电源开关的尺寸做得很大,同时让所有开关同时导通。结果唤醒瞬间的冲击电流高达10A,直接把芯片的电源焊盘给烧了。嗯,那次教训很深刻。
优化唤醒时间,我总结了几个实用技巧:
- 分步唤醒:不要一次性打开所有开关。可以分成几组,依次打开。这样冲击电流被分散了,电源网络的压力小很多。
- 电流限制:在唤醒路径上串联一个小电阻,限制峰值电流。虽然会稍微增加唤醒时间,但能保护电源网络。
- 预充电:在正式唤醒前,先用一个小电流给虚拟电源网络预充电。这样正式唤醒时,电压已经接近目标值,充电时间大大缩短。
- 时钟门控配合:在唤醒期间,保持模块的时钟处于门控状态。等电压稳定后再释放时钟。这样可以避免在电压不稳时触发时序违例。
重要提示:唤醒时间的优化,本质上是在“速度”和“安全”之间找平衡。我个人的经验是,先保证安全,再追求速度。因为一次流片失败的成本,远大于多等几个纳秒的唤醒时间。
最后,给大家一个参考的唤醒时序代码示例。这是一个简单的分步唤醒控制器:
// 分步唤醒控制器示例
module wakeup_controller (
input clk,
input rst_n,
input wakeup_req,
output reg [3:0] switch_en, // 4组电源开关使能
output reg clk_en // 时钟使能
);
reg [2:0] state;
localparam IDLE = 3'b000;
localparam STEP1 = 3'b001;
localparam STEP2 = 3'b010;
localparam STEP3 = 3'b011;
localparam STEP4 = 3'b100;
localparam DONE = 3'b101;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
switch_en <= 4'b0000;
clk_en <= 1'b0;
end else begin
case (state)
IDLE: begin
if (wakeup_req) begin
state <= STEP1;
switch_en <= 4'b0001; // 打开第1组
end
end
STEP1: begin
// 等待电压稳定,假设需要10个时钟周期
if (wakeup_timer == 10) begin
state <= STEP2;
switch_en <= 4'b0011; // 打开第2组
end
end
STEP2: begin
if (wakeup_timer == 20) begin
state <= STEP3;
switch_en <= 4'b0111; // 打开第3组
end
end
STEP3: begin
if (wakeup_timer == 30) begin
state <= STEP4;
switch_en <= 4'b1111; // 打开第4组
end
end
STEP4: begin
if (wakeup_timer == 40) begin
state <= DONE;
clk_en <= 1'b1; // 释放时钟
end
end
DONE: begin
// 正常工作
end
endcase
end
end
endmodule
这个例子很简单,但思路是对的。实际项目中,你可能需要根据电源网络的RC时间常数来精确计算每步的等待时间。我建议在仿真时多跑几个corner,确保在最坏情况下也能稳定唤醒。
好了,电源门控技术就讲到这里。下一章咱们聊聊时钟门控,那又是另一个有意思的话题。