我最近在測(cè)試一個(gè)M0+ MCU的運(yùn)行功耗,測(cè)試代碼采用如下最簡(jiǎn)單的方式,即main函數(shù)里只跑一個(gè)while(1)空循環(huán),測(cè)試出來(lái)的電流是1.11mA,使用的IDE為KEIL MDK,優(yōu)化等級(jí)為0
當(dāng)我在while(1)的前面插入3條NOP指令,測(cè)出來(lái)的電流卻變成了0.89mA。
這是怎么回事?是測(cè)量誤差,還是事實(shí)就是如此?這可是足足差了200多uA啊,為此我又做了如下幾個(gè)對(duì)比實(shí)驗(yàn)。
測(cè)試條件 | 功耗 |
優(yōu)化等級(jí)0,while(1)前不加NOP | 1.11mA |
優(yōu)化等級(jí)0,while(1)前插入1個(gè)NOP | 0.90mA |
優(yōu)化等級(jí)0,while(1)前插入2個(gè)NOP | 1.11mA |
優(yōu)化等級(jí)0,while(1)前插入3個(gè)NOP | 0.89mA |
優(yōu)化等級(jí)0,while(1)前插入4個(gè)NOP | 1.12mA |
優(yōu)化等級(jí)0,while(1)前插入5個(gè)NOP | 0.91mA |
優(yōu)化等級(jí)0,while(1)前插入6個(gè)NOP | 1.11mA |
優(yōu)化等級(jí)0,while(1)前插入7個(gè)NOP | 0.88mA |
優(yōu)化等級(jí)0,while(1)前插入8個(gè)NOP | 1.11mA |
上述實(shí)驗(yàn)可以看到明顯的規(guī)律,只要while(1)前插入的NOP是奇數(shù)時(shí)功耗就相對(duì)小一點(diǎn)(差不多都是約0.9mA),while(1)前插入的NOP是偶數(shù)時(shí)功耗就大一點(diǎn)(差不多都是約1.11mA)。
說(shuō)到這里,我們需要來(lái)了解一下NOP指令,我之前對(duì)NOP指令的理解只停留在它可以用來(lái)做軟件延時(shí)用,其實(shí)它還有一個(gè)重要的作用是實(shí)現(xiàn)指令對(duì)齊
在調(diào)試窗口下,我們看一下匯編代碼
C代碼的while(1)被匯編成了2條指令,即NOP和B,跳轉(zhuǎn)指令B前自動(dòng)插了一個(gè)NOP。while(1)實(shí)際上是先執(zhí)行一個(gè)NOP指令,再執(zhí)行B指令,B指令跳轉(zhuǎn)的地址就是自身的地址,達(dá)到無(wú)限循環(huán)的效果??梢钥吹酱藭r(shí)while(1)里NOP指令地址是0x00000152(十進(jìn)制338),B指令地址是0x00000154(十進(jìn)制340)。
當(dāng)while(1)前插入奇數(shù)條NOP指令后,while(1)對(duì)應(yīng)的指令地址會(huì)改變。
指令地址的變化為什么會(huì)影響功耗呢?這又得需要提一下CPU執(zhí)行指令的過(guò)程。
CPU內(nèi)部一直重復(fù)執(zhí)行著 Fetch(取指令)–> Decode(指令譯碼)–> Execute(執(zhí)行指令)的過(guò)程。
CPU在執(zhí)行程序取指令的時(shí)候,每次按照Flash 4字節(jié)對(duì)齊的方式從Flash一次讀32bit的指令,如果while(1)前插入偶數(shù)(包括0)個(gè)NOP指令,那么CPU在執(zhí)行while(1)時(shí),需要從Flash讀取2次32bit內(nèi)容再Decode去執(zhí)行。如果while(1)前插入奇數(shù)個(gè)NOP指令,那么CPU在執(zhí)行while(1)時(shí),只需要從Flash讀取1次32bit內(nèi)容即可。就是這個(gè)地方的差異會(huì)引起功耗的差異,前者要執(zhí)行更多的操作所以功耗更大一點(diǎn)。
此外如果while(1)前不加入NOP,但是把優(yōu)化等級(jí)調(diào)到最高,此時(shí)while(1)里 B指令前就不會(huì)插入一條NOP指令,這時(shí)B指令的地址為0x00000152,這時(shí)效果和不開(kāi)優(yōu)化等級(jí)、while(1)之前插入奇數(shù)個(gè)NOP一樣,功耗也會(huì)低一點(diǎn)。道理其實(shí)是一樣的,因?yàn)閣hile(1)的執(zhí)行只需要從0x150地址取一次指。
最后我還做了一個(gè)實(shí)驗(yàn),就是把程序放到了RAM里,不管while(1)前加多少NOP,功耗都是一樣,都是0.58mA。程序在RAM里,就不用從Flash里讀程序了,所以功耗更低。
利用功耗的不同去做破解的行為,也是類(lèi)似的原理。
以上分析僅是猜測(cè),因?yàn)椴涣私釳CU內(nèi)部的運(yùn)行細(xì)節(jié),如果不對(duì)之處,歡迎大家指正。