1. 背景
随着电源管理机制越来越完善,处理器和其他系统组件的功耗越来越低,而像 PCIe 组件这样的外围设备在 PC 系统功耗中占据的比重越来越大。虽然早期的 PCIe 允许一些软件和硬件电源管理,但是并没有把电源管理策略与系统的协调放在一个高优先级,所以软件的控制也很有限。
缺乏系统与电源管理策略之间的协调会导致一个很明显的问题:某些时候,系统可能已经进入睡眠状态 (sleep state)了,而 PCIe 设备可能还处于工作状态。这些 PCIe 设备可能会产生中断或者 DMA 数据传输请求,虽然这些请求的优先级可能并不高,但也会导致系统从睡眠状态唤醒,这样就破坏了节能的目标。
还有一种情况,PCIe 设备在向系统提交请求后,并不一定需要即时响应,只要在某个时间范围内得到响应即可。如果系统不知道 PCIe 设备能够容忍的响应时间,那就会默认立即响应,这样会导致系统进入低功耗模式的时间比较短或者频繁的在低功耗与非低功耗模式之间来回切换。如果系统能知道 PCIe 设备能够容忍的响应时间,就可以合理的安排对设备请求的响应,在允许的时间范围内,可以把请求积攒起来,在某一个时刻统一进行响应。这样,系统就能有比较集中的时间处于低功耗模式。
2. OBFF(Optimized Buffer Flush Fill)
OBFF 给 PCIe 设备提供了一种获取系统电源状态的机制。利用 OBFF,PCIe 设备就可以选择与系统进行数据交互的最优时间。
2.1 问题
对于具有总线主控功能的设备(bus-master capable devices)来说,如果不知道系统当前的电源状态,那么它们可能会在某些不合适的时间提交请求。
Figure 16-31 展示一个比较差的电源管理系统场景:系统中有 3 个 PCIe 终端设备(Endpoint),而且每个设备提交申请的时间比较散乱。这种场景下,系统能够进入 Idle 状态的时间就很短。
Figure 16-32 展示一个改善后的电源管理系统场景:该场景下,相同类型的请求会被攒到一起进行统一响应。这样,系统就可以有大块的时间进入 Idle 状态。很明显,这种模式能够达到不错的电源管理效果,而且实现起来并不复杂。只要 PCIe 设备能够查询当前系统的电源状态,并且知道在当前状态下该做些什么就可以。
2.2 解决方案
通过 OBFF,系统告知 PCIe 设备在哪些时间进行数据传输。OBFF 只是提供一个建议时间,PCIe 设备可以忽略 OBFF 信息,在它想要发请求的任意时刻发送请求(这样就导致功耗比较大,所以最好还是按照 OBFF 的建议来做)。有两种方式完成 OBFF 信息的交互:(1)通过向 Endpoint 发送消息(message);(2)控制 WAKE# 引脚。在两种方式都支持的情况下,优先选择 WAKE# 引脚的方式。只有在 WAKE# 引脚不能用的时候,再去选择发送消息(message)的方式。
Figure 16-33 展示了 OBFF 的信息传输过程。在本例中,由于两个 Switch 之间不支持 WAKE# 引脚连接,所以同时用到了消息(message)和 WAKE# 引脚两种信息传输方式。上面的 Switch 收到来自 Root Complex WAKE# 引脚的信息后,会将其转化成消息(message)发送给下面的 Switch。
2.2.1 使用 WAKE# 引脚
以前,WAKE# 只有一个功能:PCIe 组件使用该引脚通知系统恢复该 PCIe 组件的主电源供电(低功耗情况下,系统可能会移除 PCIe 组件的主电源)。
引入 OBFF 机制后,该引脚被用来向 PCIe 组件传递当前系统的电源状态。这种方式的协议很简单,只需要按照一定规律改变 WAKE# 引脚的电平,就可以表示出三种状态:
- CPU Active:系统处于活跃状态,可以进行任意类型的事务交互。
- OBFF:可以进行系统内存访问,可以读写系统内存;除此之外其他类型的事务交互都需要等到更高等级的电源状态。
- Idle:等待进入更高等级的电源状态之后,启动。
当处于 CPU Active 或者 OBFF 状态时,建议在 10 us 之内不要进入 Idle 状态。这样可以给 PCIe 组件足够的时间去处理之前 Idle 状态下积攒的未被处理的事务。但是,由于 10 us 只是一个建议,所以 PCIe Endpoint 也不要盲目的认为系统会在 CPU Active 或者 OBFF 状态保持足够的时间。
系统可以在真正进入 Idle 状态之前,先通过 WAKE# 提前发出切换到 Idle 状态的信号,这样收到信号的 PCIe 组件就知道该结束事务传输了。
这种提前通知的机制,可以避免 PCIe 组件在系统刚进入 Idle 状态时就发起事务传输,导致系统从 Idle 状态唤醒。
按照协议的建议,系统的这种提前通知与真正进入 Idle 状态的通知之间的间隔时间要尽量的短一些。
有趣的是,WAKE# 引脚仍然能支持它最初的功能:PCIe 组件使用该引脚通知系统恢复该 PCIe 组件的主电源供电。这种情况下,当某个 PCIe 组件尝试通知系统恢复它的主电源供电时,可能会被其他监听 WAKE# 引脚的 PCIe 组件误认为是 OBFF 信息。为了解决这个问题,协议规定:如果 PCIe 组件无法清晰的理解 WAKE# 引脚的变化所指示的信息,那么该组件就可以认为当前系统处于 CPU Active 状态。
2.2.2 使用 OBFF 消息(OBFF Message)
OBFF 消息(OBFF Message)只能从 Root Complex 发送到其他 PCIe 组件。消息格式如下图:
Routing Type = 0b100,表示 Point-to-Point 类型。
OBFF Code 有 3 中有效值,其它都是 reserved code:
- 0b1111:CPU Active
- 0b0001:OBFF
- 0b000b:Idle
- 其他:reserved code
如果收到的 OBFF 消息中,OBFF Code的值是 reserved code,那么 PCIe 组件可以认为当前系统处于 CPU Active 状态。
如果 PCIe 组件不支持 OBFF 或者还没有使能 OBFF,那么当它收到 OBFF 消息时,需要按照 Unsupported Request类型来处理(返回的 Completion 中,将 UR 状态位设置为 1)。
PCIe 配置空间中的 Device Capability 2 寄存器指示了 PCIe 设备是否支持 OBFF,如下图所示:
PCIe 配置空间中的 Device Capability 2 寄存器指示了 PCIe 设备是否使能 OBFF,如下图所示:
本文中的图片来自MindShare, Inc 经典书籍《PCI Express Technology》。