测试结果如表4所列。
其中,M是v中1的个数,log
2v为v的位数。
由表4可知,算法1~5的执行效率越来越高,算法5的查表法比算法1节省80%的功耗,其CPU周期数也相应减少,但是它们的指令数却有所增加,所以算法5的查表法是以空间换取时间和功耗的算法。在内存充分大的嵌入式系统中,为尽量降低功耗,算法5是很好的选择。
3 μC/OS-II的源码级功耗优化
μC/OS-II是一种可移植、可固化、可裁减及可剥夺型的多任务实时内核(RTOS),适用于各种微处理器和微控制器。所有代码用ANSI C语言编写,具有良好的可移植性。对μC/OS-II的源码级功耗优化分以下几步实行:
①对计数器数据类型的改进。由表1可知,32位数据类型的加1操作比8位数据类型的加1操作能耗低27nJ,将μC/OS-II中常用数据的数据类型改为INT32U,如任务控制块OS_TCB中的prio、OSTCBDly、OSTCBX、OSTCBY、OSTCBBitX、OSTCBBitY等。
②对循环控制语句的改进。由表3可知,while、goto循环语句的功耗比for循环语句的功耗低。将μC/OS-II中for循环句换成while循环语句,经查看μC/OS-II的源码,发现μC/OS-II在设计时已考虑到该问题,多数循环使用while实现。在此只对OSInit()函数改进,同时μC/OS-II中固定的任务(如OS_TaskIdle、OS_TaskStat中的控制)改为goto语句,减少应用程序的功耗。
③对内联函数和宏的使用。对简短的常用函数加上inline关键字,或用宏来实现,内联函数和宏的使用使软件功耗降低。读RAM比读Flash功耗更大。处理器进入子程序时,会首先将当前处理器的寄存器推入堆栈(RAM),在离开时又将处理器的寄存器弹出堆栈,这样至少两次对RAM操作。而宏在编译时展开,处理器顺序执行指令,避免了调用子程序,同时减少了系统的功耗。μC/OS-II中常用的短函数改为内联函数,如每个时钟都要执行的OSTimeTick()和开关中断等,同时μC/OS-II中采用条件编译,也会在一定程度上降低功耗。
④对变量存储类型的优化。对于大部分嵌入式系统来说,为了提高运行速度,通常寄存器做得很大,如ARM系列处理器有31个通用寄存器。有时许多寄存器空着没使用,可以将程序中常用的常量或变量直接置于寄存器中,而不是置于内存的静态存储区或动态存储区中。这样做不仅提高了软件运行速度,而且也节省能量消耗。由表3可知,使用寄存器变量能省近50%的功耗,μC/OS-II中每个时钟周期都要使用的计数变量OSTime,将其用关键字register声明即可。还有循环控制语句的计数变量,将其声明为寄存器变量,降耗效果明显。
⑤算法级的改进。从算法级功耗的算法5可以看出,将一些运算的结果预先算好,放在Flash中,用查表的方法替代实时的计算,减少微控制器的运算工作量,可以有效地降低微控制器的功耗;不可避免的实时计算,达到精度就结束,避免“过度”计算;在精度允许的情况下,使用简单函数代替复杂函数作近似,也可以减少功耗。μC/OS-II中的任务调度和事件管理模块都采用查找就绪表的方式来提高性能和降低功耗。为此,针对μC/OS-II的内存管理机制采用查表算法,借用任务管理中的就绪表实现内存块的分配,这样不但不会增加额外的空间需求,而且使内存管理的功耗更低。
对μC/OS-II的部分功能函数进行源码级功耗优化,其优化前后的结果如图1所示。图中,纵轴表示能耗(nJ),横轴表示改进前后的功能函数。
结语
功耗较大的软件,使用了较多功耗大的操作指令或是使用了不必要的指令。本文的创新之处在于,对软件功耗优化中的源码级和算法级的功耗优化进行分析,对用不同语句实现相同功能的情况进行分类讨论,测试其功耗特征,最后将功耗测试与分析结果运用到嵌入式操作系统μC/OS-II中,对其进行源码级的功耗优化,实验结果证明,源码级的功耗优化能明显降低软件的功耗。