Week #7 (Apr 7) :: ARM and RTOS: Part II
重大事項宣達
- 分組報告: (課堂採用 round-robin 排程,time slice 為 2 小時)
Lab 39: FreeRTOS 作業分析
- Adrian Huang 的 共筆 與 GitHub
- 建立每一Task所需要的資料結構 (使用heap_ww.c):
- XBlockLink:此結構用來記錄每一個區塊 (Block) 大小、下一個未被佔用的區塊大小與下一個未被佔用的區塊實體記憶體位址,其大小為 12 個位元組。由於使用 ARM Cortex-M3 架構,其對齊位元組為 8 個位元組。所以經過調整,一律使用 16 個位元組配置記憶體。
- TCB (Task Control Block):大小為 80 個位元組
- Stack:呼叫 xTaskCreate() 時,usStackDepth 設定 128,所以 stack 大小為 512 bytes (128 * 4)
- 每建立一個 task,需佔用 624 位元組。且記憶體大小為 17K,當剩下的記憶體空間低於 624 位元組時,就無法再建立 task
- task 建立/刪除後,一直在追蹤記憶體的變化,意外發現 vPortFree() 有個 bug,請見重現和修正的方式
- 建立每一Task所需要的資料結構 (使用heap_ww.c):
- 曾柏翔的 共筆
- 針對優先序高的task執行太久而佔據 CPU,提出的改進方法:在 task 中加入
vTaskDelayUntil(&xLastWakeTime, xFrequency);
,當執行到一段時間後,就會主動釋出執行時間給別的 task 使用,從而避免因為一個 task 執行太久而造成 shell 來不及回應的問題
- 針對優先序高的task執行太久而佔據 CPU,提出的改進方法:在 task 中加入
Lab 40: Scheduling 作業分析
- 沈宗穎的 共筆
- Gary Gu 的 共筆
4 組 system register 可用來設定 SysTick
- SysTick Control and status : enable , clock source, poll ,interrupt (address : 0xE000E010)
- SysTick reload value : value to load Current Value register when 0 is reached (address : 0xE000E014)
- SysTick current value : the current value of the count down (address : 0xE000E018).
- SysTick Calibration Value : might contain the number of ticks to generate a 10ms interval and other information, depending on the implementation (address : 0xE000E01C)
若要設定 SysTick,我們需要載入 SysTick event 發生間隔的數值到 reload value register,當 SysTick Control and Status Register 的 COUNTFLAG bit 內含值從 1 到 0 時,SysTick 會被啟動,所以會需要 n + 1 個 clock tick,如果 period 為 100 的話,那就是要輸入 99,reload value register 所支援的數值空間為 1 ~ 0x00FFFFFF
若想透過 SysTick 在固定的時間內產生一個事件,比如說 1ms,那可透過另外一個 Calibration register 的數值來 scale 給定的 reload register 值,Calibration register 是唯讀的暫存器,其數值表示 10 ms (SysTick 的 reload 週期) 的整數倍
若要讓 SysTick 產生中斷的話,要去設定 TICKINT (bit 1) 為 HIGH,然後透過 NVIC 去設定適當的中斷,另外還要設置 CLKSOURCE (bit 2) 為以下:
- 1 for core clock
- 0 for external reference clock
這次實驗就是透過讀取 current reload register 的數值,來達到精準的時間單位。系統載入階段,在記憶體初始化時,會做 memory mapping,定義的在檔案
CMSIS/CoreSupport/core_cm3.h
中關於 USART2 這個 IRQ 的 handler,可以到
$(CMSIS_PLAT_SRC)/startup/gcc_ride7/startup_stm32f10x_md.s
中定義的 vector table 找到USART2_IRQHandler
,但是這邊存放的不是處理的程式碼,而是真正 handler 的位址,ARM 核心會依據這個位址跳到真正處理的 handler 的位置進行中斷處理。在處理前,必須要先設定這個中斷,透過NVIC
- hsuedw 的 共筆 與 GitHub
vTaskSwitchContext()
中,執行 switch out 前的那一刻記錄在previous_systick_current
中,執行完畢之際,利用traceTASK_SWITCHED_IN()
來記錄 task 被 switch in 的時間,而traceTASK_SWITCHED_IN()
是trace_task_switch()
的 wrapper。這樣的做法是,呼叫了trace_task_switch()
後,才記錄 task 被 switch in 時間- 這樣的作法不夠精確,因為這樣含有呼叫
trace_task_switch()
與呼叫get_current()
的時間。嚴重錯估 context switch 成本
- 吳哲綱的 共筆 與 GitHub
- 使用的測試 task 如下:
.. code-block:: prettyprint
void cpu_run_tester_task(void *pvParameters) {
while (1) {
int i, j = 0;
for (i = 0; i < 1000000; i++)
j += i * j + i;
vTaskDelay(50 / portTICK_RATE_MS);
}
}
vTaskDelay
設為 50ms,但在一個 task 測試下,delay 可能是 40~55ms 之間的任意值,推測可能是 idle 時 SysTick 每 10ms reload 一次,task delay 在 10ms 以下即可被排入 ready在兩個相同 pirority 的 task 執行時,平均約 10ms 作一次 context switch (即 SysTick forced switch)
若將圖放大,可以看到 task1/2 之間的 context switch 大約耗費 0.005ms
-
- 計算出來的時間是「從Ready Task List挑選下一個被執行的 Task」。詳情task.c: vTaskSwitchContext()。這並非發生Context Switch時,所花費的全部時間。因為發生 Context Switch 時,需要三個步驟 (參考port.c: xPortPendSVHandler()):
- 儲存目前 Task 狀態 (將暫存器 r4-r11 儲存至該 Task 的堆疊,並將 top of stack 位址儲存至 tskTCB 結構成員
pxTopOfStack
)。 - 從 Ready Task List 挑選下一個被執行的 Task,並將被挑中 Task 位址指定給
pxCurrentTCB
(這就是函式vTaskSwitchContext()
所花費的時間) - 回復被挑中 Task 之狀態 (將上次儲存於 tskTCB結構成員
pxTopOfStack
的內容值取出來,並依據此內容值將上次儲存堆疊的 r4-r11 內容值回存至暫存器 r4-r11)
- 儲存目前 Task 狀態 (將暫存器 r4-r11 儲存至該 Task 的堆疊,並將 top of stack 位址儲存至 tskTCB 結構成員
- 計算出來的時間是「從Ready Task List挑選下一個被執行的 Task」。詳情task.c: vTaskSwitchContext()。這並非發生Context Switch時,所花費的全部時間。因為發生 Context Switch 時,需要三個步驟 (參考port.c: xPortPendSVHandler()):
延伸閱讀: Cortex M3 SysTick Explained
教材
- STM32 程式開發流程:以 GNU Toolchain 為例
- mini-arm-os,詳細分析和操作提示,請見 Lab 42