ASPM 关键机制:L0s、L1、L1.1、L1.2 链路状态详解

做 PCIe 功耗优化,绕不开 ASPM 的四种低功耗状态。很多人只看过 L0s 和 L1,但 L1.1 和 L1.2 才是省电的大头。我个人习惯先把这四种状态的入口条件和退出延迟理清楚,否则后面量化分析都是瞎蒙。

L0s 状态

L0s 是最浅的睡眠。链路空闲一小会儿就能进去。它只停发数据,不关时钟,不关电源。所以退出很快——几个 ns 级别。适合那种频繁小数据包的场景,比如网卡。

入口条件:发送端发现没有待发数据,且接收端准备好,可以发一个 Electrical Idle 序列。
退出延迟:约几十到几百 ns,取决于链路宽度和速率。

我在项目中遇到过一块老固态硬盘,死活不进 L0s。抓了 Link Control Register 一看,软件没写 ASPM L0s Enable 位。嗯,这是个低级坑,但也容易忘。

L1 状态

L1 比 L0s 深一些。它关掉主时钟,只留辅助时钟。功耗能再降一档。但退出就慢了——us 级别。因为要从 PLL 重新锁相。

你想想看,如果链路空闲时间不到 10 μs,进 L1 反而更耗电。因为进进出出的开销比待机省的那点还多。

注意:L1 的入口需要双方协商。硬件通过发送 PM_Enter_L1 DLLP 来请求,对方确认后才能降。如果对面忙,可能拒绝。

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.1L1 基础上关主电源,需 CLKREQ# 置低几十 μs ~ 上百 μs
L1.2L1 基础上关主电 + 大部分辅助电,CLKREQ# 置低几百 μs~几 ms

看这张表,延迟跨度从 ns 到 ms。选哪种状态,完全看你业务流的空闲间隙有多长。我一般建议:间歇小于 5 μs 的只开 L0s;5 μs 到 100 μs 可以上 L1;超过 100 μs 才考虑 L1.1;超过 1 ms 的,闭眼开 L1.2。

状态转换框架

下面这张图是我自己总结的状态机,画出来方便你对照寄存器。

ASPM 链路状态转换图 L0(全速) L0s L1 L1.1 L1.2 空闲 有数据可发 协商后空闲 退出一 关主电 + CLKREQ# 低 恢复主电 关主电 + 关辅助电 恢复所有电源 注意: L1.1 和 L1.2 都需要链路双方支持 CLKREQ# 边带信号,且 L1.2 还要求 Vaux 能独立关断。很多老设备即使寄存器写了 1,硬件也不响应。

图中只画了主要路径,实际固件还会受 LTR(延迟容忍报告)影响,我在这里先不展开。记住这个骨架就够了。

关键记忆点:退出延迟决定你能用哪一级 ASPM。L0s 适合高吞吐间歇,L1 适合短时空闲,L1.1/1.2 只适合长时间静默。千万别在 10 μs 的空闲里开 L1.2,否则功耗反而更高。

刚才提到 LTR,其实还有个坑:操作系统可能用 LTR 值来阻止 ASPM 进入深层状态。比如某个驱动报了个很小的 LTR,硬件就不会进 L1.2。我曾经碰到一个无线网卡,Win10 下功耗比 Linux 高一截,原因就是系统 LTR 设置不同。后来改了注册表才解决。这种问题不扒寄存器很难发现。

再啰嗦一句:实际测量的时候,别只看功耗仪。要用 PCIe 分析仪抓状态切换,同时读 Link Status 寄存器。我见过有人测 L1 功耗,结果链路其实一直在 L0s 抖动,根本就没降下去。画蛇添足了属于是。