--- title: Week #7 (Apr 7) :: ARM and RTOS: Part II toc: no ... 重大事項宣達 ------------------- * 分組報告: (課堂採用 round-robin 排程,time slice 為 2 小時) - `FreeRTOS`_, `rtenv+`_ - `FreeRTOS (MMU)`_, `RT-Thread`_ - `Linux`_, `Xenomai`_, `uClinux`_ - `Xvisor`_ `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執行太久而佔據 CPU,提出的改進方法:在 task 中加入 ``vTaskDelayUntil(&xLastWakeTime, xFrequency);``,當執行到一段時間後,就會主動釋出執行時間給別的 task 使用,從而避免因為一個 task 執行太久而造成 shell 來不及回應的問題 `Lab 40`_: Scheduling 作業分析 ----------------------------------------------------------------------- * 沈宗穎的 `共筆`_ + `USART interrupt handler 運作動畫`_ * 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); } } .. image:: /embedded/2015q1w7/ctx.png + ``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 * 延伸閱讀: Cortex M3 SysTick Explained - `Part I`_, `Part II`_