ASPM 关键机制:L0s、L1、L1.1、L1.2 链路状态详解
做 PCIe 功耗优化,绕不开 ASPM 的四种低功耗状态。很多人只看过 L0s 和 L1,但 L1.1 和 L1.2 才是省电的大头。我个人习惯先把这四种状态的入口条件和退出延迟理清楚,否则后面量化分析都是瞎蒙。
L0s 状态
L0s 是最浅的睡眠。链路空闲一小会儿就能进去。它只停发数据,不关时钟,不关电源。所以退出很快——几个 ns 级别。适合那种频繁小数据包的场景,比如网卡。
退出延迟:约几十到几百 ns,取决于链路宽度和速率。
我在项目中遇到过一块老固态硬盘,死活不进 L0s。抓了 Link Control Register 一看,软件没写 ASPM L0s Enable 位。嗯,这是个低级坑,但也容易忘。
L1 状态
L1 比 L0s 深一些。它关掉主时钟,只留辅助时钟。功耗能再降一档。但退出就慢了——us 级别。因为要从 PLL 重新锁相。
你想想看,如果链路空闲时间不到 10 μs,进 L1 反而更耗电。因为进进出出的开销比待机省的那点还多。
L1.1 与 L1.2 状态
到了 L1.1 和 L1.2,才真正开始“砍掉”电源。我称之为“深度睡眠”变种。它们是在 L1 基础上再砍东西:
- L1.1:关掉主电源,只保留 Vaux(辅助电源)。退出时得重新建立电压,再等 PLL 锁相。延迟比 L1 大一个数量级。
- L1.2:连 Vaux 都砍掉一部分,只维持链路检测的最小功耗。退出最慢,但省电也最猛。适合长时间不活动的设备,比如外接硬盘盒休眠时。
我曾经调一个笔记本坞站,发现它屏幕熄了功耗还高。一量,PCIe 链路没进 L1.2。问题出在 BIOS 没开 L1.2 的 L1 PM Substates 寄存器。唉,又是固件没认全特性。
入口条件与退出延迟对比
| 状态 | 入口条件(必要) | 退出延迟(典型) |
|---|---|---|
| L0s | 发送端空闲,送 Electrical Idle | 几十 ns ~ 几百 ns |
| L1 | 双方协商 PM_Enter_L1 DLLP | 几 μs |
| L1.1 | L1 基础上关主电源,需 CLKREQ# 置低 | 几十 μs ~ 上百 μs |
| L1.2 | L1 基础上关主电 + 大部分辅助电,CLKREQ# 置低 | 几百 μs~几 ms |
看这张表,延迟跨度从 ns 到 ms。选哪种状态,完全看你业务流的空闲间隙有多长。我一般建议:间歇小于 5 μs 的只开 L0s;5 μs 到 100 μs 可以上 L1;超过 100 μs 才考虑 L1.1;超过 1 ms 的,闭眼开 L1.2。
状态转换框架
下面这张图是我自己总结的状态机,画出来方便你对照寄存器。
图中只画了主要路径,实际固件还会受 LTR(延迟容忍报告)影响,我在这里先不展开。记住这个骨架就够了。
刚才提到 LTR,其实还有个坑:操作系统可能用 LTR 值来阻止 ASPM 进入深层状态。比如某个驱动报了个很小的 LTR,硬件就不会进 L1.2。我曾经碰到一个无线网卡,Win10 下功耗比 Linux 高一截,原因就是系统 LTR 设置不同。后来改了注册表才解决。这种问题不扒寄存器很难发现。
再啰嗦一句:实际测量的时候,别只看功耗仪。要用 PCIe 分析仪抓状态切换,同时读 Link Status 寄存器。我见过有人测 L1 功耗,结果链路其实一直在 L0s 抖动,根本就没降下去。画蛇添足了属于是。