电源管理基本观念之二

来源:互联网 发布:芒果台网络直播 编辑:程序博客网 时间:2024/06/06 15:04


1. SOC 时钟体系与功耗

用黑盒子来看待PLL,PLL的功能是从一个时钟输入,经过相位同步等一系列物理变化获得新的频率的时钟。特征是输出时钟的频率依赖于输入实在的频率,会有整数倍的线性约束关系,并且倍频,调频是时间的因子。PLL树则是有多个PLL组成的硬件树形结构,能够输出多种不同频率的时钟,从而满足多数需要。SOC的时钟体系设计是大量PLL,MUX,DIV的集合。

基于ARM核的SOC大都遵循AMBA总线协议,因此在整个时钟体系的设计都会遵循AMBA协议,并在AMBA协议上来进行扩展。AMBA协议定义了两组总线,分别适用于不同的通信需求:


一条高速总线AHB用于arm core, on-chip memory,dma之间的通讯。一条从AHBA桥接出来的低速总线APB用于跟外部IP之间的通讯。所以在时钟源的设置上,必须针对这两条总线的要求提供时钟源。因此ARM体系结构的SOC,两个PLL是必须的,一个是直接提供给ARM Core使用的PLL。另一个是输出给AMBA协议定义的两条总线使用的PLL。使用AMBA协议通讯的IP所需要的时钟原则上都可以从提供总线时钟的PLL分频得到。另外,系统存在一些需要特殊频率的IP是,还会再增加PLL来专门为这些IP提供时钟。原则上讲,时钟树就是PLL,MUX,DIV的集合。从SOC层面的功耗情况看,根据系统的具体运行场景需求合理的调整来操作PLL和MUX来合理的关掉某些时钟是降低功耗的一个途径。Linux系统进入S3的情况,这部分操作放在了global platform阶段。 另外,运行时的某些特定状态,也可以在软件上增加一些状态,针对时钟做一些配置来降低功耗。例如在长时间播放音乐的场景下,则可以只打开CODEC IP的时钟,而关掉其它IP的时钟,总而达到降低场景功耗的目的。

2. DDR耗电分析

传统DDR跟mobile DDR在功耗上存在较大区别。 mobile DDR 又称为lpddr,针对嵌入式设备做了很多优化。 DDR的功耗主要由工作电压和刷新方式决定。

下面工作电压的一个参考:

TYPECore Voltage
(VDD)
IO Voltage
(VIO)
DDR2.5V2.5VLPDDR1.8V1.8V/1.2VDDR21.8V1.8VLPDDR21.2V1.2VDDR31.5V/1.35V1.5V/1.35VLPDDR31.2V1.2V

另外在工作方式上也作出了优化,去掉DLL,DLL 消失后带来读写时序上的一些变化,另外对于时延要求控制更严。

DLL (Delay Locked Loop,延时锁定回路)本来在DDR引入,主要目的就是用来提供DQS信号与CLK之间同步。

LPDDR去掉DLL带来如下好处:

(a). active mode power saving (~10mA)

(b). DDR内部时钟可以随着接口时钟(CK/CK_)的变化而调整(可以被改变,甚至停止);


 LPDDR针对功耗优化增加了两种刷新方式

温度补偿刷新Temperature Compensated Self-Refresh (TCSR)

相比DDR加入的feature,on-chip 温度感应,在低温下降低刷新率,降低在自刷新模式下的功耗(在手持设备里,自刷新模式的LPDDR功耗是设备待机功耗的主要来源).

 

部分区域的自刷新PASR (partial array self refresh)

相比DDR来说,PASR提供用户可控的部分区域自刷新,而非整个区域(ARRAY),比如1/2, 1/4, 1/8, 1/16 array; 同时也意味着,未能及时refresh的部分内容会丢失。

这里的1~1/6 array,可以覆盖从整个die(all banks) 到某个bank的部分row. 基本上每个level提供相对上一个级别50%的 power down.

 

超低功耗 模式(DPD ,deep power down"),在系统供电仍然保持的状态下,牺牲掉所有的内存内容。 具体实现是可以切断对于具体的存储部分的电源;但是在退出DPD时需要重新做部分初始化 –因此相应Memory Controller部分需要重写。


切换到S3状态,针对DDR的功耗优化,软件代码一般也放在global platform阶段完成。


3. CPU Core耗电分析-Cortex A15电源状态



ARMCortex A15 Core部分 定义了多个电压域,针对不同的电压域,提供了多种Power Mode基于ARMCortex A15的SOC定义的电源状态则是ARM Cortex A15定义的电源状态的子集。不同Power Mode的区别就是在于上图各个模块的打开和关断状态。

从模块图看有如下几个模块:

  • integer core: 也就通常所说的运算器,用于做整数运算的逻辑单元,包括了指令cache,数据cache,TLB cache等。Cortex A15上把这一部分电压称为Processor,这一部分的时钟可以单独控制。从模块图上可以看出时钟是CK_GCLKCR
  • NEON & VFP:这一部分用于浮点以及向量运算。时钟控制逻辑是CK_GCLKCX
  • DEBUG APB,CTI,and CTM:调试模块。时钟控制逻辑是PCLKDBG
  • L2 RAMs: 二级缓存
  • L2 Control,IC,Timer: 二级缓存控制器,硬件定时器等。

电源状态的定义原则即使这几个模块开关状态的排列组合和cpu的工作场景。Cortex A15正好定义了10种电源状态。



cpu core 切换到不同的电源状态,ARM CORE有专门的汇编指令来完成,并且还要配合硬件时序。切换到S3状态下,针对cpu core的软件实现也放在global platform阶段。


4. Linux cupfreq driver

Linux有专门的cpufreq子系统来针对cpu的运行状态来调节cpu的工作频率,从而降低功耗。cpufreq子系统有如下组件描述调频过程中的各个功能模块

  • cpufreq notifier  触发频率调节的源头
  • cpufreq_policy    描述有cpu频率状态的数据结构
  • cpufreq governor cpu频率调控
  • cpufreq_driver   描述平台相关的cpufreq驱动的数据结构
  • frequency table   频率表


cpufreq core

跟大多数Linux子系统一样,cpufreq core部分出要是抽象出平台无关的操作上述组件的接口。

cpufreq core 的代码在driver/cpufreq/cpufreq.c种。


cpufreq core定义了两条通知,一条是处理cpu的工作频率切换到一个新的policy,另一条是用来处理当cpu频率切换后,受影响的设备的状态。


cpufreq core导出了相应的注册通知的API



cpufreq core定义了一个governor链表来保存多种频率策略。Linux内核本身也提供了多种频率调节策略,在配置内核时可选择。


在配置内核是可针对不同的需求选择不同的频率策略。就我个人的理解,在嵌入式的手持设备上,很多情况都没有使用内核提供的电源策略。governor部分,IBM Linux有一个系列文章描述的很详细。

减少 Linux 电耗,第 1 部分: CPUfreq 子系统

减少 Linux 耗电,第 2 部分: 一般设置和与调控器相关的设置

减少 Linux 耗电,第 3 部分: 调优结果

cpufreq core定义了静态全局结构指针cpufreq_driver用于记录注册到cpufreq core中的驱动:


导出注册驱动符号用于注册平台相关驱动:

具体代码如下:

/********************************************************************* *               REGISTER / UNREGISTER CPUFREQ DRIVER                * *********************************************************************//** * cpufreq_register_driver - register a CPU Frequency driver * @driver_data: A struct cpufreq_driver containing the values# * submitted by the CPU Frequency driver. * *   Registers a CPU Frequency driver to this core code. This code * returns zero on success, -EBUSY when another driver got here first * (and isn't unregistered in the meantime). * */int cpufreq_register_driver(struct cpufreq_driver *driver_data){unsigned long flags;int ret;if (cpufreq_disabled())return -ENODEV;if (!driver_data || !driver_data->verify || !driver_data->init ||    ((!driver_data->setpolicy) && (!driver_data->target)))return -EINVAL;pr_debug("trying to register driver %s\n", driver_data->name);if (driver_data->setpolicy)driver_data->flags |= CPUFREQ_CONST_LOOPS;spin_lock_irqsave(&cpufreq_driver_lock, flags);if (cpufreq_driver) {spin_unlock_irqrestore(&cpufreq_driver_lock, flags);return -EBUSY;}cpufreq_driver = driver_data;spin_unlock_irqrestore(&cpufreq_driver_lock, flags);ret = subsys_interface_register(&cpufreq_interface);if (ret)goto err_null_driver;if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) {int i;ret = -ENODEV;/* check for at least one working CPU */for (i = 0; i < nr_cpu_ids; i++)if (cpu_possible(i) && per_cpu(cpufreq_cpu_data, i)) {ret = 0;break;}/* if all ->init() calls failed, unregister */if (ret) {pr_debug("no CPU initialized for driver %s\n",driver_data->name);goto err_if_unreg;}}register_hotcpu_notifier(&cpufreq_cpu_notifier);pr_debug("driver %s up and running\n", driver_data->name);return 0;err_if_unreg:subsys_interface_unregister(&cpufreq_interface);err_null_driver:spin_lock_irqsave(&cpufreq_driver_lock, flags);cpufreq_driver = NULL;spin_unlock_irqrestore(&cpufreq_driver_lock, flags);return ret;}

整个注册过程的关键有三部分

a)把平台相关的struct cpufreq_driver类指针driver_data赋值给全局指针cpufreq_driver

b)调用平台相关cpufreq_driver中的init成员函数

c)注册cpufreq_cpu_notifier




原创粉丝点击