在嵌入式系统中,电源的管理和功耗的控制,始终是一个非常重要的任务。所以通常面向嵌入式应用的CPU都会有各自不同的软硬件策略来辅助电源管理和功耗控制,最常见的包括各种低功耗模式,以及动态的频率切换等功能。本文的目的主要结合自己最近在Marvell的Monahans平台上所做的工作,总结一下所学习的相关知识,及软硬件框架和实现。
嵌入式设备上的电源和功耗管理,从大的方面来说,所要做的工作应该会包括以下几点:
从很大程度上来说,嵌入式系统的电源和功耗管理都是围绕着CPU和系统总线的工作状态来进行的,(当然,LCD等背光的管理也是很重要的一个方面)。所以,通常来说,我们所讨论的嵌入式系统中的系统状态,从CPU和系统总线的角度来说,大致可分为以下几种:
嵌入式系统的CPU通常不会工作在超频状态下,所以全速工作状态就是指CPU运行在手册所推荐的最大工作频率下。
除去背光所消耗的功率,在系统中,CPU本身所消耗的功率通常占据了相当大的一个比例,所以为了降低功耗,让CPU工作在非全速状态也是一个常见的做法。
ARM架构CPU的变频操作通常是通过协处理器指令来实现的。变频过程中,除了要处理CPU的Core频率,还需要处理相关总线和外设的频率变换,因为通常各种总线频率在变频过程中都有可能改变,而外设的时钟信号等也有可能相应的受到影响。
不同的CPU通常都会实现各自不同的低功耗模式,不过,如果抛开名字和具体细节上的差异,仅从本质上讨论其相类似的工作原理,那么,大致所涉及到的内容会包括:
CPU Core的时钟是否保持
CPU的CORE电压是否保持
CPU核心寄存器是否保持
外设总线时钟是否保持
LCD是否保持刷新
外部SDRAM是否保持刷新
内部SRAM是否保持刷新
睡眠唤醒用RTC时钟是否保持刷新
按照其中这些状态的组合,不同的CPU会定义各自不同阶段程度的低功耗工作模式。
低功耗模式对消费类的嵌入式系统来说尤其重要,它关系到设备的最大待机时间。
严格来说,深度休眠模式也是低功耗模式的一种,这里单独提出来是因为通常它会被作为系统的关机状态来使用。
例如手机这类设备,在关机后,通常系统也并没有完全断电,因为还要考虑到系统时间的更新以及闹钟唤醒等功能,所以系统关机后通常都是处于所谓的深度休眠模式。这时候,通常只有RTC时钟相关的一小部分电路处于工作状态,这时候也会采用和系统总线相独立的低频率的晶振作为时钟来源。
为了保证RTC的电路的不间断工作,通常还会在主板上放置一个小的备用电池。当然,如果主电源和备用电池都切断了,那系统就真的进入完全断电的状态了。
当系统工作在低功耗模式,尤其是休眠状态下,系统底电流的控制是非常重要的。首先要定义一下休眠状态的含义,这里所说的休眠状态,不是指上述关机状态下的深度休眠模式,也不是指PC上的Hibernate。我们所说的休眠模式是指在保证CPU和整个系统能够从内存中快速恢复工作的前提下,系统所可能进入的最低功耗状态。这通常指:
CPU核心电压和和时钟关闭
外设时钟关闭
外部SDRAM保持自刷新状态
内部SRAM维持刷新
在这种状态下CPU自身的功耗被降到最低,但系统总体的底电流还和外设工作状态相关,所以需要根据系统状态在恰当的时刻控制外设的供电和工作状态以保证总体电流的最优化。
在Linux内核中,这个恰当的操作时机,通常是指在统一设备驱动模型中,设备驱动所注册的Suspend和Resume函数被系统电源管理模块所调用时。设备驱动需要自己保证在Suspend函数被调用时完成自身必要的休眠或者断电等操作,而在Resume函数中重新恢复自己的正常工作状态。
除此之外,底电流的控制和主板本身的硬件设计也有很大关系。
如果说低功耗模式下系统功耗的控制关系到消费类嵌入式设备的待机时间,那么运行模式下的功耗控制则关系到系统正常使用的时间,同样也是至关重要的。
从总体来说,要降低系统运行时的功耗,所涉及到的内容大致会包括:
背光亮度的控制,例如在适当的时候自动调节系统背光亮度,时间等,在某些场合关闭系统背光。
尽量关闭不必要的外设,只有在需要使用的时候才对相关外设进行上电操作(例如Camera,蓝牙,wifi等),或者让一些不能关闭的外设在工作间隙处于休眠状态。
适时的变频操作,在CPU和总线负载较低的情况下,进行恰当的降频操作,降低CPU本身的功率消耗,同时在负载升高的时候,能够保证及时的恢复较高的工作频率,不影响用户的正常使用。
此外应该还包括恰当的器件选型,软件性能优化等其它手段。
嵌入式系统中,为了支持动态电源和功耗的管理,对硬件和CPU有着特定的需求,而不同的CPU和外设为了满足这些需求,也有自己不同的实现方式。个人所接触的硬件种类有限,所以,这里主要以Monahans系列CPU为核心的系统为例,大致列一下所涉及到的硬件及相关功能特性。
首先当然是CPU:
为了支持变频操作,CPU需要提供系统和总线频率的动态调整功能。
为了保证某些外设在系统总线频率改变时的正常工作,应该提供具体外设时钟的可变控制。
能够提供低功耗模式的状态控制和切换能力
能够提供相应的硬件唤醒机制,保证系统能从休眠状态恢复到正常工作状态。
Monahans系列的CPU还提供了一套硬件功能模块,用于监控CPU的各种核心操作。例如Cache的命中,内存控制器的请求数量统计等等,一方面可以用来为软件性能调试提供参考数据,另一方面,也可以作为系统硬件状态的一种监控手段,为动态电源管理提供信息来源。
由于CPU在低功耗模式下通常会工作在更低的核心电压下,所以要求电源管理芯片能对系统提供可以动态调整的输出电压。通过降低核心电压的手段降低系统的整体功耗。
对于外部内存SDRAM来说,为了保证系统在休眠时不丢失信息,通常要求内存能够在无需CPU内存控制器的参与下,工作在自刷新模式。
对于外设来说,理想的要求当然是能够受控或自动将自己致于尽可能低的功耗状态。
在X86架构的PC中,BIOS在很大程度上承担了核心的电源管理功能,所以APM和ACPI或多或少在一定程度上都依赖于BIOS所提供的功能。
在ARM架构的嵌入系统中,因为通常不会有BIOS,加上具体的CPU和外设千差万别,所以动态电源管理的软件实现框架也不尽相同。不过就我接触到的平台而言,基本上都会在PM的基础上实现休眠等相关的电源管理策略,而动态调频等功能,则各有各的实现方式,有基于DPM实现的,也有自己实现一套框架的。
基于Monahans系列CPU所提供的功能,我们在Marvell的BSP所提供的相关代码的基础上所实现的动态电源管理的软件框架如下图所示:
这部分代码基于PM实现,主要负责低功耗模式的进入退出相关过程的控制,它定义并实现了几种不同程度的低功耗模式,并负责完成对外设驱动的Suspend / Resume函数的调用。
动态电压频率调整模块主要负责变频操作相关过程的控制,它定义和维护了一系列的操作点,用来描述和控制CPU核心频率,电压,各种总线频率等参数。同时它还负责维护和调用外设驱动向其注册的DVFM_notifier函数,在其完成变频操作前后,给外设提供一个机会执行相应的应对措施。
设备驱动需要实现自己的DVFM_notifer函数,用来处理变频操作给自己带来的影响。或者用来通知DVFM禁止变频操作的执行。
此外,一些设备驱动还要为电源管理状态切换的决策提供输入信息,例如RTC闹钟等可能做为系统唤醒的唤醒源,而像键盘,触摸屏等的操作则标志着用户交互行为的发生等等。
系统负载采样模块同样是为电源管理状态的切换决策提供输入信息,这部分代码主要用来控制Monahans中性能监控相关硬件模块的工作。采样硬件活动信息,以监控例如内存总线的负载等。同时,在系统IDLE进程中,也利用对Idle的时间的统计等手段,为动态电源管理系统提供一个近似的CPU负载信息。
Policy Maker工作在用户层空间,它负责采集内核中上述各模块的相关信息并进行调度控制。它在用户空间实现了一套电源管理策略。作为Daemon进程,它为其它用户层代码提供一个统一的接口,用来通知其它应用程序电源状态的变化,以及接受并处理各种用户限制条件(例如播放视频时禁止熄灭背光等等)以正确进行系统状态的切换。
电源管理的决策依据,不外乎会有几个来源:
系统硬件的工作状态
上层应用程序的工作状态
用户的交互情况
从系统硬件的角度来说,大致会包括:CPU和系统总线的负载情况,输入输出类设备的工作情况:例如是否存在大量的内存操作,是否有网络数据交互等,另外还包括电池是否处于低电量状态,是否在使用某些特殊的硬件设备等等。
上层应用程序的工作状态,可能包括:是否存在视频应用,是否允许关闭背光,是否对CPU速度有限制要求,是否允许系统进入睡眠状态等等。
而用户的交互情况,包括用户是否有键盘或其它IO设备输入等行为。
应该说,上述的几类决策来源往往不会是完全独立相互无关的,例如视频类应用的存在通常会使CPU处于一个较高的负载水平,大量的用户交互也可能让IO设备处于忙碌状态等等。所以,有时候,在进行电源管理决策时,可以合并一些信息来源,或者由一类情况的数据来涵盖另一类情况的发生可能。
从CPU和硬件的角度,设备可能处在不同的频点和不同的低功耗状态。
而从用户的角度,外在的表现大体可以划分为:
系统正常工作状态:各类设备可以正常使用,背光全亮
背光变暗状态:一段时间没有交互,系统可能自动调暗背光,提醒用户状态的变化。
背光熄灭状态:背光熄灭,但是部分功能可能保持正常工作状态,如MP3的播放等。
休眠状态:所有功能关闭,但是可以快速唤醒
关机状态
因此,在Policy Maker中,需要根据实际情况维护系统的当前状态,并根据一定的顺序进行状态切换,例如按照CPU等占用情况进行变频控制,根据应用和用户的交互控制背光状态等,以求最大限度的兼顾系统的易用性和功耗。