在MCU on Eclipse網站上看到Erich Styger在8月2日發的博文,一篇關於在Amazon FreeRTOS V10中使用運行時統計信息的文章,本人覺得很有啟發,特將其翻譯過來以備參考。原文網址:https://mcuoneclipse.com/2018/08/02/tutorial ...
在MCU on Eclipse網站上看到Erich Styger在8月2日發的博文,一篇關於在Amazon FreeRTOS V10中使用運行時統計信息的文章,本人覺得很有啟發,特將其翻譯過來以備參考。原文網址:https://mcuoneclipse.com/2018/08/02/tutorial-using-runtime-statistics-with-amazon-freertos-v10/
FreeRTOS包含一個很好的功能,可以向我提供有關每個任務在系統上運行的時間的信息:
本教程解釋了FreeRTOS運行時統計功能以及如何打開和使用它。
♣軟體和工具
在本文中,我使用以下內容:
- MCUXpresso IDE 10.2.1
- FRDM-K64F板
- 來自MCUXpresso SDK 2.4.0的Amazon FreeRTOS V10.0.1
但是當然可以使用任何其他工具/ IDE或FreeRTOS版本(FreeRTOS至少應該是9.0.0或更高版本)。
使用以下步驟,還可以使用FreeRTOS任務運行時信息收集來更新現有項目。
♣怎麼運行的
FreeRTOS使用用戶/應用程式特定的計時器來測量任務執行時間。為此,RTOS中的每個任務描述符都有一個累積計數器,用於添加為該任務花費的計時器滴答。當任務獲得CPU時間時,當前計時器滴答計數被記憶,當RTOS切換出該任務時,則記憶當前計時器滴答計數。然後將對應於任務執行時間的增量時間添加到任務執行時間計數器。
我需要配置FreeRTOS,並將以下巨集設置為1以執行運行時分析:
1 #define configGENERATE_RUN_TIME_STATS 1
另外,需要提供以下兩個巨集:
1 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 2 portGET_RUN_TIME_COUNTER_VALUE()
RTOS使用它來配置運行時計數器計時器並獲取計時器值。
運行時計數器在每個任務描述符中存儲為32位值,這意味著對於每個任務,我對系統的RAM要求將增加4個位元組:
假設計數器周期為10 kHz(0.1 ms),這意味著變數將在大約5天後溢出。
此外,RTOS在task.c中維護額外的全局變數:
1 #if ( configGENERATE_RUN_TIME_STATS == 1 ) 2 3 PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ 4 5 PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ 6 7 #endif
第一個變數用於記住任務切換的時間,第二個變數是系統的總運行時間。這是在任務切換時發生的事情(內核函數vTaskSwitchContext):
1 /* Add the amount of time the task has been running to theaccumulated time so far. The time the task started running wasstored in ulTaskSwitchedInTime. Note that there is no overflowprotection here so count values are only valid until the timeroverflows. The guard against negative values is to protectagainst suspect run time stat counter implementations - whichare provided by the application, not the kernel. */ 2 3 if( ulTotalRunTime > ulTaskSwitchedInTime ) 4 5 { 6 7 pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); 8 9 } 10 11 else 12 13 { 14 15 mtCOVERAGE_TEST_MARKER(); 16 17 } 18 19 ulTaskSwitchedInTime = ulTotalRunTime;
通常,周期性定時器中斷用於計算執行時間,並且定時器頻率應該是嘀嗒中斷頻率的大約10倍(比如說“Hello”到“奈奎斯特 - 香農”採樣定理)。這意味著如果我的滴答中斷是1 kHz,我的運行時分析定時器頻率應該是10 kHz。
運行時統計信息通常帶有兩個數字:
- 絕對(時間)數字
- 百分比
下麵是一個文本任務列表,其中包含右側的運行時信息:
TCB Static Handle Name State Prio Stack Beg Stack End Size Stack Top Unused Runtime
1 no (0) 0x20000450 Shell Running (1,1) 0x20000440 0x20000060 1000 B 0x200001EC ( 600 B) 392 B 0x00000000 ( <1%)
7 no (0) 0x20001E68 IDLE Ready (0,0) 0x20001E58 0x20001CD0 400 B 0x20001DFC ( 96 B) 312 B 0x00007C35 ( 91%)
2 no (0) 0x20000740 Refl Blocked (4,4) 0x20000730 0x20000510 552 B 0x200006BC ( 120 B) 384 B 0x00000C6E ( 9%)
6 no (0) 0x20001C68 Main Blocked (1,1) 0x20001C58 0x20001A08 600 B 0x20001BDC ( 128 B) 356 B 0x00000000 ( <1%)
3 no (0) 0x20001378 Radio Blocked (3,3) 0x20001368 0x20000F88 1000 B 0x200012F4 ( 120 B) 680 B 0x00000001 ( <1%)
4 no (0) 0x20001658 Sumo Blocked (2,2) 0x20001648 0x20001458 504 B 0x200015C4 ( 136 B) 360 B 0x00000000 ( <1%)
5 no (0) 0x20001948 Drive Blocked (3,3) 0x20001938 0x20001748 504 B 0x200018B4 ( 136 B) 264 B 0x00000000 ( <1%)
絕對數字是運行時間計時器滴答數(TCB中的ulRunTimeCounter)以及此計數器相對於總運行時間的百分比(task.c中的ulTotalRunTime)。
對於IDLE任務,它顯示了這一點:
TCB Static Handle Name State Prio Stack Beg Stack End Size Stack Top Unused Runtime
7 no (0) 0x20001E68 IDLE Ready (0,0) 0x20001E58 0x20001CD0 400 B 0x20001DFC ( 96 B) 312 B 0x00007C35 ( 91%)
0x7C35是定時器計數器(在本例中使用0.1 ms定時器,因此它意味著IDLE任務運行約3秒(0x7C35 / 10 => 3179 ms)並使用91%的運行時間。
問題可能是:中斷花費的時間是多少?答案是RTOS不知道中斷,它只知道任務使用了多少運行時間計時器。或者換句話說:FreeRTOS運行時計數器顯示的運行時* includes*中斷的時間。
♣教程:使用FreeRTOS進行運行時分析
在下一節中,我將展示如何使用FreeRTOS啟用運行時分析。基本步驟是:
- 創建一個新項目(如果尚未存在)
- 更新FreeRTOSConfig.h
- 初始化和配置計時器
- 添加鉤子/回調到應用程式
1、創建項目
創建一個基於你的目標板的項目:
確保包含FreeRTOS:
2、添加FreeRTOS任務
接下來添加一個任務,例如:
1 #include "FreeRTOS.h" 2 3 #include "task.h" 4 5 6 7 static void MyTask(void *pvParameters) { 8 9 for(;;) { 10 11 vTaskDelay(pdMS_TO_TICKS(100)); 12 13 } 14 15 }
在main()內部,創建一個任務並啟動調度程式:
1 /* create task */ 2 3 if (xTaskCreate(MyTask, "MyTask", 500/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+1, NULL) != pdPASS) { 4 5 for(;;){} /* error */ 6 7 } 8 9 vTaskStartScheduler(); /* start RTOS */ 10 11 for(;;) { 12 13 /* should not get here */ 14 15 }
構建和調試該項目,只是為了確保一切正常。
要在Debug視圖中顯示FreeRTOS線程,請參閱https://mcuoneclipse.com/2018/06/29/show-freertos-threads-in-eclipse-debug-view-with-segger-j-link-and-nxp-s32-design-studio/
但是FreeRTOS任務列表(使用Menu FreeRTOS>任務列表來顯示該視圖)不顯示任何運行時信息:
這是我們將在接下來的步驟中添加的內容。
3、跟蹤和運行時統計信息
在FreeRTOSConfig.h中,確保將以下定義設置為1(打開):
1 #define configGENERATE_RUN_TIME_STATS 1 /* 1: generate runtime statistics; 0: no runtime statistics */ 2 3 #define configUSE_TRACE_FACILITY 1 /* 1: include additional structure members and functions to assist with execution visualization and tracing, 0: no runtime stats/trace */
該configUSE_TRACE_FACILITY需要使用RTOS有在任務描述當前存儲的運行時間計數器的附加數據元素,在configGENERATE_RUN_TIME_STATS自動關上,以創紀錄的任務執行時間的功能。
4、配置定時器
接下來,我們必須設置一個計時器來測量任務執行時間。該計時器的運行速度至少比RTOS Tick計時器快10倍。
在我們的示例中,滴答率為1 kHz:
1 #define configTICK_RATE_HZ ((TickType_t)1000)
這意味著我們的運行時間應至少以10 kHz運行。
要配置這樣的計時器,我可以使用MCUXpresso配置外設工具:
在外設工具中,我們選擇了FTM0定時器(我們也可以使用任何其他定時器)。
定時器配置為10 kHz:
我們將使用定時器中斷來增加一個計數器,所以不要忘記打開中斷:
然後單擊按鈕以更新項目源:
切換回開發人員視角。
5、定時器ISR
接下來,我們將定時器中斷代碼添加到應用程式:
1 #include "fsl_ftm.h" 2 3 static uint32_t RTOS_RunTimeCounter; /* runtime counter, used for configGENERATE_RUNTIME_STATS */ 4 5 6 7 void FTM0_IRQHandler(void) { 8 9 /* Clear interrupt flag.*/ 10 11 FTM_ClearStatusFlags(FTM0, kFTM_TimeOverflowFlag); 12 13 RTOS_RunTimeCounter++; /* increment runtime counter */ 14 15 }
6、添加定時器驅動
該項目尚未編譯,因為必要的驅動程式尚未成為項目的一部分。要添加它們,請使用“管理SDK組件”按鈕:
然後檢查ftm驅動程式並按OK,將額外的驅動程式源添加到項目中。
7、向FreeRTOS添加定時器:用於運行時統計的FreeRTOS定時器巨集
將以下行添加到FreeRTOSConfig.h:
1 extern void RTOS_AppConfigureTimerForRuntimeStats(void); 2 3 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() 4 5 extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(void); 6 7 #define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR()
這告訴FreeRTOS它將用於初始化定時器的功能以及獲取定時器值的功能。
8、FreeRTOS Callback for Timer
現在我們需要添加我們配置FreeRTOS使用的兩個回調。
1 void RTOS_AppConfigureTimerForRuntimeStats(void) { 2 3 RTOS_RunTimeCounter = 0; 4 5 EnableIRQ(FTM0_IRQn); 6 7 } 8 9 10 11 uint32_t RTOS_AppGetRuntimeCounterValueFromISR(void) { 12 13 return RTOS_RunTimeCounter; 14 15 }
9、正在運行...。
構建和調試您的應用程式。如果您現在停止應用程式並檢查任務列表,它現在顯示運行時信息:
10、沒有Eclipse?沒問題!
上面我使用了FreeRTOS的Eclipse Task List視圖,這是NXP為他們的基於Eclipse的IDE(MCUXpresso IDE,S32DS for ARM和Kinetis Design Studio)所做的事情。但是可以直接從應用程式顯示該信息,例如在終端LCD顯示器上。McuOnEclipse上的FreeRTOS 包含一個使用它的shell /終端介面。
下麵的代碼片段顯示瞭如何為每個任務列印信息:
1 #if configGENERATE_RUN_TIME_STATS 2 3 ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE(); /* get total time passed in system */ 4 5 ulTotalTime /= 100UL; /* For percentage calculations. */ 6 7 #endif 8 9 ... 10 11 #if configGENERATE_RUN_TIME_STATS && configUSE_TRACE_FACILITY 12 13 /* runtime */ 14 15 UTIL1_strcpy(tmpBuf, sizeof(tmpBuf), (unsigned char*)"0x"); 16 17 UTIL1_strcatNum32Hex(tmpBuf, sizeof(tmpBuf), taskStatus.ulRunTimeCounter); 18 19 if (ulTotalTime>0) { /* to avoid division by zero */ 20 21 /* What percentage of the total run time has the task used? 22 23 This will always be rounded down to the nearest integer. 24 25 ulTotalRunTime has already been divided by 100. */ 26 27 ulStatsAsPercentage = taskStatus.ulRunTimeCounter/ulTotalTime; 28 29 if (ulStatsAsPercentage>0) { 30 31 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)" ("); 32 33 UTIL1_strcatNum16uFormatted(tmpBuf, sizeof(tmpBuf), ulStatsAsPercentage, ' ', 3); 34 35 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)"%)"); 36 37 } else { 38 39 /* If the percentage is zero here then the task has consumed less than 1% of the total run time. */ 40 41 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)" ( <1%)"); 42 43 } 44 45 } 46 47 buf[0] = '\0'; 48 49 UTIL1_strcatPad(buf, sizeof(buf), tmpBuf, ' ', PAD_STAT_TASK_RUNTIME); 50 51 CLS1_SendStr(buf, io->stdOut); 52 53 #endif 54 55 CLS1_SendStr((unsigned char*)"\r\n", io->stdOut);
♣概要
FreeRTOS運行時統計是一個非常有用的功能:它顯示每個任務使用了多少時間,包括其中斷時間。我需要的只是設置一些FreeRTOS配置巨集並設置周期性定時器中斷。當然,這並不是免費提供額外的定時器中斷以及該功能所需的RAM和FLASH,但如果需要,它可以很容易地關閉以供最終版本使用。
♣鏈接
- GitHub上的示例項目:https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K64F/FRDM-K64F_SDK_FreeRTOS
- 使用FreeRTOS進行性能和運行時分析
- MCUXpresso IDE網頁:http://www.nxp.com/mcuxpresso/ide
- MCUXpresso IDE社區:http://www.nxp.com/mcuxpresso/ide/forum
- 在Eclipse中更好的FreeRTOS調試
- McuOnEclipse庫項目:https://github.com/ErichStyger/McuOnEclipseLibrary/tree/master/lib/FreeRTOS/Source
- ARM Cortex-M迴圈計數器:https://mcuoneclipse.com/2017/01/30/cycle-counting-on-arm-cortex-m-with-dwt/
- 更好的FreeRTOS調試:https://mcuoneclipse.com/2017/03/18/better-freertos-debugging-in-eclipse/
- FreeRTOS RuntimeStats:https://www.freertos.org/rtos-run-time-stats.html
歡迎關註: