###### tags: `FreeRTOS 探討` # Linux 讀書會 2021/1/25 FreeRTOS 介紹 ### Task vs. process, is there really any difference? The term "task" is mostly used in the context of scheduling*, when it can refer to either a thread or a *process***, that can be scheduled to run on a processor. From the scheduler's point of view there might be little-to-no difference between a thread and a process - both represent a task that must be scheduled. Recently, the term "task" is gaining more-widespread usage, especially among .NET developers, thanks to e.g. the Task Parallel Library. Within it, tasks are units of work that can be scheduled to run on threads from a pool of worker threads. --- ### OS 中 heap 和 stack 的區別 **1.Stack** 用來儲存 Value Types (Primitives)的地方,其特性是 LIFO (後進先出),用來儲存物件的 stack 與 run-time 的 call stack 運作原理是一樣的,run-time 的 stack frame 包含了: * Parameters:函數的參數 * Return address:回傳位址,當function執行完,從哪行code繼續執行 * Local variables:區域變數 Stack frame 存活時間是規律可預測的,只存在於 function 的執行期間,一旦 function 執行完畢,系統會自動回收空間,不需要擔心 Memory Leak 在這裡發生。 **2.Heap:** 用來儲存 Reference Types,new 一個物件即是存在 Heap 裡面,由於是動態配置記憶體空間,其存活時間不規律不可預測的,即使已經執行完動態配置的 function ,物件仍可能存在 Heap 中,這邊會因程式語言有沒有 GC 功能而有所不同: * 沒有 GC :像 C++ 就需要用 delete 語法來清除物件 * 有 GC:Java 的 Garbage collector 為了防止 memory leak 會自動釋放 heap 上的記憶體空間 Stack 區段一般的狀況會從高記憶體位址往低記憶體位址成長,而 heap 剛好從對面以相反的方向成長。 --- ### Task Control Block 作業系統主要的工作都是運行和協調任務,FreeRTOS 的基本工作單元是任務,使用Task Control Block表示每個任務。 ``` typedef struct tskTaskControlBlock { volatile portSTACK_TYPE *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */ xListItem xGenericListItem; /* List item used to place the TCB in ready and blocked queues. */ xListItem xEventListItem; /* List item used to place the TCB in event lists.*/ unsigned portBASE_TYPE uxPriority; /* The priority of the task where 0 is the lowest priority. */ portSTACK_TYPE *pxStack; /* Points to the start of the stack. */ signed char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* Descriptive name given to the task when created. Facilitates debugging only. */ #if ( portSTACK_GROWTH > 0 ) portSTACK_TYPE *pxEndOfStack; /* Used for stack overflow checking on architectures where the stack grows up from low memory. */ #endif #if ( configUSE_MUTEXES == 1 ) unsigned portBASE_TYPE uxBasePriority; /* The priority last assigned to the task - used by the priority inheritance mechanism. */ #endif } tskTCB; ``` TCB 將Stack的起始位子存在**pxStack**中(也負責向上overflow檢查),當前的Stack的Top存在**pxTopOfStack**中,**pxEndOfStack**檢查Stack是否overflow。 TCB 將任務的初始優先級存在uxPriority和uxBasePriority中,創建任務時賦予優先級,也可以更改任務的優先級,如果FreeRTOS implements了"inherited" priority,則它將用於uxBasePriority記住原始優先級,同時將任務暫時提升為“繼承的”優先級。 **xGenericListItem** or **xEventListItem** 用來記錄 任務的狀態( ready to run, suspended, or blocked. ) --- ### Context Switch **Systick定時器介紹** SysTick定時器被捆綁在NVIC中,用於產生SYSTICK異常(異常號: 15)。在以前,大多作業系統需要一個硬體定時器來產生作業系統需要的滴答中斷,例如,為多個任務許以不同數目的時間片,確保沒有一個任務能霸佔系統;或者把每個定時器週期的某個時間範圍賜予特定的任務等,還有作業系統提供的各種定時功能,都與這個滴答定時器有關。因此,需要一個定時器來產生週期性的中斷,而且最好還讓使用者程式不能隨意訪問它的暫存器,以維持作業系統“心跳”的節律。 **Basic Concept** ![](https://i.imgur.com/zI7sc5y.png) 在Task要被switch out之前,需要作以下事情 1. 當SysTick進入IRQ中,PUSH Processor registers r0、r1、r2、r3、r12、LR、PC和xPSR到task的私有Stack Memory區域。 1. 如果需要Context Switch,SysTick將觸發PendSV_Handler。 1. 手動儲存r4-r11和r14至Task私有的Stack Memory區域。 1. 儲存最上層的Stack值至TCB的第一個成員pxTopOfStack。 1. 執行vTaskSwitchContext()選出下一個可能使用CPU資源的Task。 --- **PendSV exception** PendSV對OS操作非常重要,優先權可以透過程式設定,且透過SCB->ICSR中的第28位元來觸發,也就是將其設置為1就能夠觸發PendSV_Handler。 在FreeRTOS中是利用SysTick進入中斷後,由Scheduler來決定是否應該執行Context Switch,來切換到不同的任務,如果其他的中斷請求(IRQ)在SysTick之前產生,則SysTick可能會去搶佔IRQ的處理,這種情況下,FreeRTOS不應該去執行Context Switch,否則IRQ處理就會被延遲,對於IRQ設計應該是越短越好,對於延遲往往會有不可預期的事情發生,以ARM Cortex M3和ARM Cortex M4處理器來說,當Processor進入Handler Mode中,默認是不允許返回Thread Mode,如果試圖想由Handler Mode返回Thread Mode則會產生Usage Fault。 ![](https://i.imgur.com/V3fswmW.png) 在FreeRTOS設計中,為了要解決這個問題,PendSV的特性就是將Context Switch請求延遲到所有的IRQ處理完成後,此時,需要將PendSV的優先權設置為最低優先權,如果FreeRTOS需要執行Context Switch,他會設置SCB->ICSR |= (1 << 28);,並且在PendSV_Handler中執行Context Switch。 ![](https://i.imgur.com/E0ZJfRW.png) --- ### ARM Register ARM的暫存器,全部共有31個32位元的暫存器。但ARM核心同時最多只可以有18個同時執行的暫存器:這包含了16個資料暫存器和2個處理器狀態暫存器,16個資料暫存器中包含了3個專用暫存器,R13、R14和R15。 1. R0-R7:這8個暫存器是所有模式都共用的。 1. R8-R12:則會依照模式去切換Bank Register。 1. R13通常做為堆疊指標Stack Pointer,保存目前處理器模式的堆疊的堆疊頂端。(by convention) 1. R14為Link Register,保存副程式的返回位址,比如在BL指令時,會將PC的值複製到R14,作為返回(Return)的位址。(hardwired) 1. R15為PC(Program Counter),內容存放處理器要存取的下一道指令位址。(hardwired) ![](https://i.imgur.com/w0Z2pDh.png) Current Program Status Register (CPSR) 用來儲存條件旗標之外,還有中斷控制位元 I、F,可用來允許或禁止中斷,狀態位元 T 用來記錄處理器是位於正常 (ARM:指令為32位元) 或精簡 (Thumb:指令為16位元) 狀態,處理器模式 Mode 是用來記錄處理器的模式,像是 (User / System / FIQ / SVC / ABT / IRQ / Undef) 等 --- 學長建議: 1. 根據[成大資工Wiki FreeROTS ](http://wiki.csie.ncku.edu.tw/embedded/freertos)逐漸理解 任務 通訊 硬體界面 1. Arm 架構的暫存器要搞懂,可閱讀陳鍾誠書籍或網站 1. TCB 資料結構理解 1. Task function理解 補充:看code不用每行都看懂,看不懂不用糾結太久,可以記下來先往後看,後面再回頭看說不定就懂了