在許多實(shí)時(shí)應(yīng)用程序中,CPU 可以在不到 5% 的代碼中花費(fèi) 95%(或更多)的時(shí)間。電機(jī)控制、發(fā)動(dòng)機(jī)控制、無(wú)線通信和許多其他對(duì)時(shí)間敏感的應(yīng)用就是這種情況。這些嵌入式系統(tǒng)通常是用 C 語(yǔ)言編寫(xiě)的,并且開(kāi)發(fā)人員經(jīng)常被迫手動(dòng)優(yōu)化代碼,可能會(huì)恢復(fù)為匯編語(yǔ)言,以滿足緊迫的期限。測(cè)量部分代碼的實(shí)際執(zhí)行時(shí)間可以幫助您找到代碼中的熱點(diǎn)。本文將展示如何輕松測(cè)量和顯示實(shí)時(shí)基于 Cortex-M 的 MCU 上的代碼執(zhí)行時(shí)間。
測(cè)量代碼的執(zhí)行時(shí)間
有很多方法可以測(cè)量代碼執(zhí)行時(shí)間。作為一名嵌入式工程師,我經(jīng)常使用一個(gè)或多個(gè)數(shù)字輸出和示波器。您只需在執(zhí)行要監(jiān)視的代碼之前將其中一個(gè)輸出設(shè)置為高電平,然后再將輸出設(shè)置為低電平。當(dāng)然,在您執(zhí)行此操作之前還有相當(dāng)多的設(shè)置工作:找到一個(gè)或多個(gè)空閑輸出,確保它們易于探測(cè),將端口配置為輸出,編寫(xiě)代碼,編譯,設(shè)置范圍等等。 收到信號(hào)后,您可能需要對(duì)其進(jìn)行一段時(shí)間的監(jiān)控以查看最小值和最大值。數(shù)字存儲(chǔ)示波器使這個(gè)過(guò)程更容易,但還有其他方法比這更容易。
測(cè)量執(zhí)行時(shí)間的另一種方法是使用具有跟蹤功能的調(diào)試探針。您只需運(yùn)行代碼、查看跟蹤、計(jì)算增量時(shí)間(通常是手動(dòng))并將 CPU 周期轉(zhuǎn)換為微秒。不幸的是,跟蹤為您提供了一個(gè)執(zhí)行實(shí)例,您可能需要進(jìn)一步查看跟蹤捕獲以找到最壞情況下的執(zhí)行時(shí)間。這可能是一個(gè)乏味的過(guò)程。
Cortex-M 周期計(jì)數(shù)器
大多數(shù)基于 Cortex-M 的處理器上的 CoreSight 調(diào)試端口都包含一個(gè) 32 位自由運(yùn)行計(jì)數(shù)器,用于計(jì)算 CPU 時(shí)鐘周期。該計(jì)數(shù)器是調(diào)試監(jiān)視和跟蹤 (DWT) 模塊的一部分,可輕松用于測(cè)量代碼的執(zhí)行時(shí)間。以下代碼是啟用和初始化這個(gè)非常有用的功能所需的全部?jī)?nèi)容。
#define ARM_CM_DEMCR (*(uint32_t *)0xE000EDFC)
#define ARM_CM_DWT_CTRL (*(uint32_t *)0xE0001000)
#define ARM_CM_DWT_CYCCNT (*(uint32_t *)0xE0001004)
if (ARM_CM_DWT_CTRL != 0) { // 看看
DWTDEMCR是否可用 ARM = 1 《《 24; // 設(shè)置位 24
ARM_CM_DWT_CYCCNT = 0;
ARM_CM_DWT_CTRL |= 1 《《 0; // 設(shè)置位 0
}
使用 DWT 循環(huán)計(jì)數(shù)器測(cè)量代碼執(zhí)行時(shí)間
您可以通過(guò)讀取該段之前和之后的循環(huán)計(jì)數(shù)器的值來(lái)測(cè)量和計(jì)算代碼段的執(zhí)行時(shí)間,如下所示。當(dāng)然,這意味著您必須檢測(cè)您的代碼,但您會(huì)得到一個(gè)非常準(zhǔn)確的值。
uint32_t 開(kāi)始;
uint32_t 停止;
uint32_t 增量;
開(kāi)始 = ARM_CM_DWT_CYCCNT;
// 測(cè)量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開(kāi)始;
因?yàn)槲覀兪褂玫氖菬o(wú)符號(hào)數(shù)學(xué),所以 delta 表示測(cè)量代碼的實(shí)際執(zhí)行時(shí)間(以 CPU 時(shí)鐘周期為單位),即即使 stop 小于 start。
當(dāng)然,在測(cè)量開(kāi)始和停止讀數(shù)之間括起來(lái)的代碼的執(zhí)行時(shí)間時(shí)可能會(huì)發(fā)生中斷,因此每次執(zhí)行此序列時(shí)很可能會(huì)有不同的值。在這種情況下,您可能希望在測(cè)量期間禁用中斷以刪除該偽影,如下所示,但要了解禁用中斷是暫時(shí)的,并且僅包含在測(cè)量中。話雖如此,包含中斷的工件可能會(huì)很有用,因?yàn)樗鼈儠?huì)影響代碼的截止日期。
禁用中斷;
開(kāi)始 = ARM_CM_DWT_CYCCNT;
// 測(cè)量
停止的代碼 = ARM_CM_DWT_CYCCNT;
啟用中斷;
delta = 停止 - 開(kāi)始;
如果被測(cè)量的代碼包含條件語(yǔ)句、循環(huán)或任何可能導(dǎo)致變化的東西,那么獲得的值可能不代表最壞情況下的執(zhí)行時(shí)間。要糾正這個(gè)問(wèn)題,您可以簡(jiǎn)單地添加一個(gè)峰值檢測(cè)器,如下所示。當(dāng)然,在進(jìn)行任何測(cè)量之前,需要聲明 max 并將其初始化為最小值(即 0)。
開(kāi)始 = ARM_CM_DWT_CYCCNT;
// 測(cè)量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開(kāi)始;
if (max 《 delta) {
max = delta;
}
同樣,了解最短執(zhí)行時(shí)間也可能很有趣且有用。在進(jìn)行任何測(cè)量之前,只需聲明 min 并將其初始化為最大可能值(即 0xFFFFFFFF)。這是新代碼:
開(kāi)始 = ARM_CM_DWT_CYCCNT;
// 測(cè)量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開(kāi)始;
if (max 《 delta) {
max = delta;
}
if (min 》 delta) {
min = delta;
}
執(zhí)行時(shí)間還取決于 CPU 是否配備高速緩存,就像某些 Cortex-M4 處理器和 Cortex-M7 一樣。如果您的系統(tǒng)使用指令或數(shù)據(jù)緩存,則同一段代碼的多次測(cè)量可能會(huì)不一致。您可能會(huì)考慮禁用緩存以測(cè)量最壞的情況。
為了顯示這些值,大多數(shù)調(diào)試器允許您實(shí)時(shí)顯示這些變量值。如果是這種情況,則需要在全局范圍內(nèi)聲明顯示的變量以保留其值并允許實(shí)時(shí)監(jiān)控。此外,不幸的是,這些值代表 CPU 時(shí)鐘周期,并且大多數(shù)調(diào)試器都不夠復(fù)雜,無(wú)法縮放變量以用于顯示目的。假設(shè) CPU 時(shí)鐘速度為 16 MHz,顯示 70.19 微秒比顯示 1123 個(gè)周期要方便得多。實(shí)際上有一種更好的方法來(lái)顯示實(shí)時(shí)變量,它還提供了縮放值的能力,因此您可以以更易讀的形式查看它們。我將很快解釋如何做到這一點(diǎn)。
經(jīng)過(guò)時(shí)間模塊
您當(dāng)然可以將代碼片段添加到您的應(yīng)用程序中,或者您可以使用我編寫(xiě)的一個(gè)簡(jiǎn)單模塊(包含在本文中)。與 elapsed_time.h 模塊一起出現(xiàn)在下方的“elapsed_time.c”模塊僅包含 4 個(gè)函數(shù)。
要使用:
只需#include
在使用 elapsed_time.c 中定義的其他函數(shù)之前調(diào)用 elapsed_time_init()。
通過(guò)設(shè)置 ELAPSED_TIME_MAX_SECTIONS 定義經(jīng)過(guò)時(shí)間測(cè)量結(jié)構(gòu)的最大數(shù)量。這對(duì)應(yīng)于您要使用停止/啟動(dòng)代碼包裝的不同代碼片段的數(shù)量。
調(diào)用 elapsed_time_start() 并將您要監(jiān)視的代碼片段的索引傳遞給它(即 0 。. ELAPSED_TIME_MAX_SECTIONS-1)。
調(diào)用 elapsed_time_stop() 并將您在 elapsed_time_start() 調(diào)用中使用的索引傳遞給它。
如果您的調(diào)試器允許您實(shí)時(shí)監(jiān)控變量(即在目標(biāo)運(yùn)行時(shí)),您可以顯示 elapsed_time_tbl[] 并查看您使用的相應(yīng)索引的 ELAPSED_TIME 結(jié)構(gòu)。
重復(fù)執(zhí)行第 4 步到第 6 步,并讓您的代碼處于最壞和最好的情況下,以便 ELAPSED_TIME 結(jié)構(gòu)的 .min 和 .max 字段很好地表示您正在測(cè)量的代碼片段的執(zhí)行時(shí)間。
您會(huì)注意到(請(qǐng)參閱 elapsed_time.c)我在測(cè)量期間沒(méi)有禁用中斷,因?yàn)榭赡苌婕?ISR,您可能想知道這如何影響感知的執(zhí)行時(shí)間。
void main (void)
{
// 一些代碼
elapsed_time_init(); // 初始化模塊
// 一些代碼
}
void MyCode (void)
{
// 這里的一些代碼
elapsed_time_start(0); // 開(kāi)始測(cè)量代碼片段 #0
// 正在測(cè)量的代碼
elapsed_time_stop(0); // 停止和
// 一些其他代碼
}
當(dāng)然,最小和最大執(zhí)行時(shí)間取決于您進(jìn)行測(cè)量的頻率以及代碼是否分別受制于其最佳和最差條件。
elapsed_time_tbl[] 中的字段可以使用 Silicon Labs 的 Micrium uC/Probe 顯示。事實(shí)上,uC/Probe 可以顯示每個(gè)字段并縮放每個(gè)值,以便可以將 CPU 時(shí)鐘周期轉(zhuǎn)換為微秒,這更加友好。與大多數(shù) Cortex-M MCU 內(nèi)置的 CoreSight 調(diào)試端口連接時(shí),uC/Probe 不需要對(duì)您的代碼進(jìn)行任何檢測(cè)。
附帶說(shuō)明一下,不需要顯示起始字段,因?yàn)樗鼉H用于記錄測(cè)量開(kāi)始時(shí) DWT 循環(huán)計(jì)數(shù)器的值。但是,開(kāi)始字段可用于顯示活動(dòng)。換句話說(shuō),當(dāng)您看到此值發(fā)生變化時(shí),您就會(huì)知道正在進(jìn)行測(cè)量。
使用 uC/Probe 的示例顯示
我將 elapsed_time.c 模塊與 uC/Probe 結(jié)合使用,并測(cè)量了四個(gè)代碼片段的執(zhí)行時(shí)間。
圖 1 顯示了使用 IAR 的 LiveWatch(左)和 uC/Probe 的 Tree View 控件(右)的原始形式的值。請(qǐng)注意,屏幕截圖是在不同時(shí)間拍攝的。elapsed_time_tbl[] 是一個(gè)數(shù)組,用于存儲(chǔ)不同代碼片段的測(cè)量值。


圖 1,IAR 和 uC/Probe 的樹(shù)形視圖控件。
您還可以將最小/最大/當(dāng)前值分配給儀表和數(shù)字指示器,如圖 2 所示。在這里,這些值以微秒為單位顯示,因?yàn)槲覒?yīng)用了 0.0125 的縮放因子,CPU 以 80 MHz 運(yùn)行。我還決定只顯示最大執(zhí)行時(shí)間。左側(cè)的按鈕用于重置統(tǒng)計(jì)信息,從而強(qiáng)制重新計(jì)算最小值和最大值。

【圖2 | 使用 uC/Probe 的儀表之一顯示最大執(zhí)行時(shí)間。]
uC/Probe 非常強(qiáng)大的功能之一是能夠與 Microsoft 的 Excel 交互,從而在電子表格上顯示值(實(shí)時(shí)),如圖 3 所示。

【圖3 | 使用 Excel 顯示實(shí)時(shí)數(shù)據(jù)。]
概括
作為嵌入式開(kāi)發(fā)人員,我們有很多工具可以用來(lái)測(cè)試和驗(yàn)證我們的設(shè)計(jì)。我已經(jīng)演示了使用 Cortex-M 處理器的眾多功能之一是多么容易,即 DWT 循環(huán)計(jì)數(shù)器。
Micrium 的 uC/Probe 提供了許多功能,允許您使用儀表、儀表、數(shù)字指示器、Excel 界面或圖形/繪圖來(lái)監(jiān)控應(yīng)用程序中的許多變量。憑借其內(nèi)置的示波器功能,一旦滿足觸發(fā)條件,您還可以捕獲多達(dá)七個(gè)附加變量的值。
隨意使用或改進(jìn) elapsed_time.* 模塊。不要猶豫,向我發(fā)送反饋。我考慮添加的另一個(gè)功能是在最大執(zhí)行時(shí)間超過(guò)閾值時(shí)調(diào)用的回調(diào)函數(shù)。如果您想在這種情況發(fā)生時(shí)立即收到通知(打開(kāi) LED、發(fā)出警報(bào)等),這可能很有用。事實(shí)上,您甚至可以設(shè)置一個(gè)斷點(diǎn),以防您想查看是什么條件導(dǎo)致超過(guò)閾值。
審核編輯:郭婷
-
處理器
+關(guān)注
關(guān)注
68文章
20084瀏覽量
243873 -
led
+關(guān)注
關(guān)注
243文章
24338瀏覽量
684985 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2303瀏覽量
97330
發(fā)布評(píng)論請(qǐng)先 登錄
MSPM0G1507 80MHz Arm? Cortex-M0?+ MCU技術(shù)手冊(cè)
MSPM0G3506-Q1 汽車(chē) 80MHz Arm? Cortex-M0?+ MCU技術(shù)手冊(cè)
【RA-Eco-RA6M4開(kāi)發(fā)板評(píng)測(cè)】使用ULINK2開(kāi)發(fā)瑞薩MCU
請(qǐng)問(wèn)NuMicro? Cortex-M? 系列芯片是否支持 I2C 監(jiān)視器功能?
請(qǐng)問(wèn)如何在 M48x 上激活 ETM?
Analog Devices Inc. MAX32675C超低功耗Arm? Cortex?-M4F MCU數(shù)據(jù)手冊(cè)
PPEC新品發(fā)布丨圖形化編程數(shù)字電源專(zhuān)用 ARM Cortex-M4 MCU
tc397如何高精度(微秒級(jí)別)測(cè)量代碼執(zhí)行時(shí)間?
MSPM0G1505 80MHz Arm? Cortex-M0?+ MCU數(shù)據(jù)手冊(cè)
MSPM0G3106 80MHz Arm? Cortex-M0?+ MCU數(shù)據(jù)手冊(cè)
MSPM0C1103 24MHz Arm? Cortex-M0?+ MCU數(shù)據(jù)手冊(cè)
如何使用Ozone分析Cortex-M異常
MAX32675C超低功耗Arm? Cortex?-M4F MCU

測(cè)量ARM Cortex-M MCU上的代碼執(zhí)行時(shí)間
評(píng)論