转载:http://blog.sina.com.cn/s/blog_a6559d920101i58f.html
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的功耗主要由工作电压和刷新方式决定。
下面工作电压的一个参考:
TYPE |
Core Voltage (VDD) |
IO Voltage (VIO) |
DDR | 2.5V | 2.5V |
LPDDR | 1.8V | 1.8V/1.2V |
DDR2 | 1.8V | 1.8V |
LPDDR2 | 1.2V | 1.2V |
DDR3 | 1.5V/1.35V | 1.5V/1.35V |
LPDDR3 | 1.2V | 1.2V |
另外在工作方式上也作出了优化,去掉DLL,DLL 消失后带来读写时序上的一些变化,另外对于时延要求控制更严。
DLL (Delay Locked Loop,延时锁定回路)本来在DDR引入,主要目的就是用来提供DQS信号与CLK之间同步。
LPDDR去掉DLL带来如下好处:
(a). active mode power saving (~10mA)
(b). DDR内部时钟可以随着接口时钟(CK/CK_)的变化而调整(可以被改变,甚至停止);
温度补偿刷新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
从模块图看有如下几个模块:
- 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
cpufreq core定义了两条通知链,一条是处理cpu的工作频率切换到一个新的policy,另一条是用来处理当cpu频率切换后,受影响的设备的状态。
cpufreq core导出了相应的注册通知的API
cpufreq core定义了一个governor链表来保存多种频率策略。Linux内核本身也提供了多种频率调节策略,在配置内核时可选择。
在配置内核是可针对不同的需求选择不同的频率策略。就我个人的理解,在嵌入式的手持设备上,很多情况都没有使用内核提供的电源策略。governor部分,IBM Linux有一个系列文章描述的很详细。
减少
Linux 电耗,第 1 部分: CPUfreq 子系统
减少
Linux 耗电,第 2 部分: 一般设置和与调控器相关的设置
cpufreq core定义了静态全局结构体指针cpufreq_driver用于记录注册到cpufreq
core中的驱动:
并导出注册驱动符号用于注册平台相关驱动:
具体代码如下:
-
-
-
-
int
cpufreq_register_driver( structcpufreq_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; -
-
-
for (i = 0; i < nr_cpu_ids; i++) -
if (cpu_possible(i) && per_cpu(cpufreq_cpu_data, i)) { -
ret = 0; -
break; -
} -
-
-
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