在許多實時應用程序中,CPU 可以在不到 5% 的代碼中花費 95%(或更多)的時間。電機控制、發動機控制、無線通信和許多其他對時間敏感的應用就是這種情況。這些嵌入式系統通常是用 C 語言編寫的,并且開發人員經常被迫手動優化代碼,可能會恢復為匯編語言,以滿足緊迫的期限。測量部分代碼的實際執行時間可以幫助您找到代碼中的熱點。本文將展示如何輕松測量和顯示實時基于 Cortex-M 的 MCU 上的代碼執行時間。
測量代碼的執行時間
有很多方法可以測量代碼執行時間。作為一名嵌入式工程師,我經常使用一個或多個數字輸出和示波器。您只需在執行要監視的代碼之前將其中一個輸出設置為高電平,然后再將輸出設置為低電平。當然,在您執行此操作之前還有相當多的設置工作:找到一個或多個空閑輸出,確保它們易于探測,將端口配置為輸出,編寫代碼,編譯,設置范圍等等。 收到信號后,您可能需要對其進行一段時間的監控以查看最小值和最大值。數字存儲示波器使這個過程更容易,但還有其他方法比這更容易。
測量執行時間的另一種方法是使用具有跟蹤功能的調試探針。您只需運行代碼、查看跟蹤、計算增量時間(通常是手動)并將 CPU 周期轉換為微秒。不幸的是,跟蹤為您提供了一個執行實例,您可能需要進一步查看跟蹤捕獲以找到最壞情況下的執行時間。這可能是一個乏味的過程。
Cortex-M 周期計數器
大多數基于 Cortex-M 的處理器上的 CoreSight 調試端口都包含一個 32 位自由運行計數器,用于計算 CPU 時鐘周期。該計數器是調試監視和跟蹤 (DWT) 模塊的一部分,可輕松用于測量代碼的執行時間。以下代碼是啟用和初始化這個非常有用的功能所需的全部內容。
#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; // 設置位 24
ARM_CM_DWT_CYCCNT = 0;
ARM_CM_DWT_CTRL |= 1 《《 0; // 設置位 0
}
使用 DWT 循環計數器測量代碼執行時間
您可以通過讀取該段之前和之后的循環計數器的值來測量和計算代碼段的執行時間,如下所示。當然,這意味著您必須檢測您的代碼,但您會得到一個非常準確的值。
uint32_t 開始;
uint32_t 停止;
uint32_t 增量;
開始 = ARM_CM_DWT_CYCCNT;
// 測量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開始;
因為我們使用的是無符號數學,所以 delta 表示測量代碼的實際執行時間(以 CPU 時鐘周期為單位),即即使 stop 小于 start。
當然,在測量開始和停止讀數之間括起來的代碼的執行時間時可能會發生中斷,因此每次執行此序列時很可能會有不同的值。在這種情況下,您可能希望在測量期間禁用中斷以刪除該偽影,如下所示,但要了解禁用中斷是暫時的,并且僅包含在測量中。話雖如此,包含中斷的工件可能會很有用,因為它們會影響代碼的截止日期。
禁用中斷;
開始 = ARM_CM_DWT_CYCCNT;
// 測量
停止的代碼 = ARM_CM_DWT_CYCCNT;
啟用中斷;
delta = 停止 - 開始;
如果被測量的代碼包含條件語句、循環或任何可能導致變化的東西,那么獲得的值可能不代表最壞情況下的執行時間。要糾正這個問題,您可以簡單地添加一個峰值檢測器,如下所示。當然,在進行任何測量之前,需要聲明 max 并將其初始化為最小值(即 0)。
開始 = ARM_CM_DWT_CYCCNT;
// 測量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開始;
if (max 《 delta) {
max = delta;
}
同樣,了解最短執行時間也可能很有趣且有用。在進行任何測量之前,只需聲明 min 并將其初始化為最大可能值(即 0xFFFFFFFF)。這是新代碼:
開始 = ARM_CM_DWT_CYCCNT;
// 測量
停止的代碼 = ARM_CM_DWT_CYCCNT;
delta = 停止 - 開始;
if (max 《 delta) {
max = delta;
}
if (min 》 delta) {
min = delta;
}
執行時間還取決于 CPU 是否配備高速緩存,就像某些 Cortex-M4 處理器和 Cortex-M7 一樣。如果您的系統使用指令或數據緩存,則同一段代碼的多次測量可能會不一致。您可能會考慮禁用緩存以測量最壞的情況。
為了顯示這些值,大多數調試器允許您實時顯示這些變量值。如果是這種情況,則需要在全局范圍內聲明顯示的變量以保留其值并允許實時監控。此外,不幸的是,這些值代表 CPU 時鐘周期,并且大多數調試器都不夠復雜,無法縮放變量以用于顯示目的。假設 CPU 時鐘速度為 16 MHz,顯示 70.19 微秒比顯示 1123 個周期要方便得多。實際上有一種更好的方法來顯示實時變量,它還提供了縮放值的能力,因此您可以以更易讀的形式查看它們。我將很快解釋如何做到這一點。
經過時間模塊
您當然可以將代碼片段添加到您的應用程序中,或者您可以使用我編寫的一個簡單模塊(包含在本文中)。與 elapsed_time.h 模塊一起出現在下方的“elapsed_time.c”模塊僅包含 4 個函數。
要使用:
只需#include
在使用 elapsed_time.c 中定義的其他函數之前調用 elapsed_time_init()。
通過設置 ELAPSED_TIME_MAX_SECTIONS 定義經過時間測量結構的最大數量。這對應于您要使用停止/啟動代碼包裝的不同代碼片段的數量。
調用 elapsed_time_start() 并將您要監視的代碼片段的索引傳遞給它(即 0 。. ELAPSED_TIME_MAX_SECTIONS-1)。
調用 elapsed_time_stop() 并將您在 elapsed_time_start() 調用中使用的索引傳遞給它。
如果您的調試器允許您實時監控變量(即在目標運行時),您可以顯示 elapsed_time_tbl[] 并查看您使用的相應索引的 ELAPSED_TIME 結構。
重復執行第 4 步到第 6 步,并讓您的代碼處于最壞和最好的情況下,以便 ELAPSED_TIME 結構的 .min 和 .max 字段很好地表示您正在測量的代碼片段的執行時間。
您會注意到(請參閱 elapsed_time.c)我在測量期間沒有禁用中斷,因為可能涉及 ISR,您可能想知道這如何影響感知的執行時間。
void main (void)
{
// 一些代碼
elapsed_time_init(); // 初始化模塊
// 一些代碼
}
void MyCode (void)
{
// 這里的一些代碼
elapsed_time_start(0); // 開始測量代碼片段 #0
// 正在測量的代碼
elapsed_time_stop(0); // 停止和
// 一些其他代碼
}
當然,最小和最大執行時間取決于您進行測量的頻率以及代碼是否分別受制于其最佳和最差條件。
elapsed_time_tbl[] 中的字段可以使用 Silicon Labs 的 Micrium uC/Probe 顯示。事實上,uC/Probe 可以顯示每個字段并縮放每個值,以便可以將 CPU 時鐘周期轉換為微秒,這更加友好。與大多數 Cortex-M MCU 內置的 CoreSight 調試端口連接時,uC/Probe 不需要對您的代碼進行任何檢測。
附帶說明一下,不需要顯示起始字段,因為它僅用于記錄測量開始時 DWT 循環計數器的值。但是,開始字段可用于顯示活動。換句話說,當您看到此值發生變化時,您就會知道正在進行測量。
使用 uC/Probe 的示例顯示
我將 elapsed_time.c 模塊與 uC/Probe 結合使用,并測量了四個代碼片段的執行時間。
圖 1 顯示了使用 IAR 的 LiveWatch(左)和 uC/Probe 的 Tree View 控件(右)的原始形式的值。請注意,屏幕截圖是在不同時間拍攝的。elapsed_time_tbl[] 是一個數組,用于存儲不同代碼片段的測量值。
圖 1,IAR 和 uC/Probe 的樹形視圖控件。
您還可以將最小/最大/當前值分配給儀表和數字指示器,如圖 2 所示。在這里,這些值以微秒為單位顯示,因為我應用了 0.0125 的縮放因子,CPU 以 80 MHz 運行。我還決定只顯示最大執行時間。左側的按鈕用于重置統計信息,從而強制重新計算最小值和最大值。
【圖2 | 使用 uC/Probe 的儀表之一顯示最大執行時間。]
uC/Probe 非常強大的功能之一是能夠與 Microsoft 的 Excel 交互,從而在電子表格上顯示值(實時),如圖 3 所示。
【圖3 | 使用 Excel 顯示實時數據。]
概括
作為嵌入式開發人員,我們有很多工具可以用來測試和驗證我們的設計。我已經演示了使用 Cortex-M 處理器的眾多功能之一是多么容易,即 DWT 循環計數器。
Micrium 的 uC/Probe 提供了許多功能,允許您使用儀表、儀表、數字指示器、Excel 界面或圖形/繪圖來監控應用程序中的許多變量。憑借其內置的示波器功能,一旦滿足觸發條件,您還可以捕獲多達七個附加變量的值。
隨意使用或改進 elapsed_time.* 模塊。不要猶豫,向我發送反饋。我考慮添加的另一個功能是在最大執行時間超過閾值時調用的回調函數。如果您想在這種情況發生時立即收到通知(打開 LED、發出警報等),這可能很有用。事實上,您甚至可以設置一個斷點,以防您想查看是什么條件導致超過閾值。
審核編輯:郭婷
-
處理器
+關注
關注
68文章
19824瀏覽量
233695 -
led
+關注
關注
242文章
23740瀏覽量
671317 -
計數器
+關注
關注
32文章
2287瀏覽量
96078
發布評論請先 登錄
Analog Devices Inc. MAX32675C超低功耗Arm? Cortex?-M4F MCU數據手冊

PPEC新品發布丨圖形化編程數字電源專用 ARM Cortex-M4 MCU

MSPM0G1505 80MHz Arm? Cortex-M0?+ MCU數據手冊

MSPM0G3106 80MHz Arm? Cortex-M0?+ MCU數據手冊

MSPM0G3107-Q1 汽車級 80MHz Arm? Cortex-M0?+ MCU技術手冊

MSPM0C1103 24MHz Arm? Cortex-M0?+ MCU數據手冊

MSPM0G3505-Q1 汽車級 80MHz Arm? Cortex-M0?+ MCU數據手冊

MSPM0C1104-Q1 汽車級 24MHz Arm? Cortex-M0?+ MCU數據手冊

MAX32675C超低功耗Arm? Cortex?-M4F MCU

【RA-Eco-RA2E1-48PIN-V1.0開發板試用】原創測量代碼運行時間
實際項目開發中為何選擇ARM? Cortex?-M4 內核的HK32MCU?

評論