IMX8QXP内部M4移植rt-thread
最近研究了一段時間的rt-thread,相對來說還是算比較有追求的設計。從風格上講,與Linux類似,很多設計思想也是借鑒Linux,比如設備驅動;從設計上來說,精簡,高效易擴展,做了很多不錯的中間件。
當然最主要的工作還是集中于IMX8QXP,A核就不多說了,都是Linux的傳統內容,開發也都搞定了。對其內部的M核比較感興趣,就想著嘗試移植一下rt-thread,一來挑戰一下,二來檢驗一下學習成果。雖然之前在STM32上移植過rt-thread,不過太過easy,感覺沒什么意思。
移植前,首先看一下IMX8內部的架構圖
其中有兩個M4,左下角的稱之為SCU,用于資源及clock管理,不對外開放。上面的一個M4才是我們所能用的。
無論A核還是M4,都必須通過SCU來進行資源及clock管理。SCU的SDK不開放,只提供相關API。
內部的M4默認運行的系統為FreeRTOS,個人對其極不喜歡,也是我移植rt-thread的一個原因,O(∩_∩)O哈哈~
首先看一下FreeRTOS的SDK代碼結構:
原始SDK中提供了很多裸機example以及包含FreeRtos的demo,移植思路就從這些demo開始。
首先我們需要將rt-thread的源碼拷貝進rtos下并為其新建目錄rt-thread,同時還需要修改編譯配置文件,加入編譯過程。
將hello也拷貝一份,相關FreeRTOS重命名為rt-thread。
原始SDK使用的Cmake編譯,入口為相關的裸機demo以及rtos demo,我們選擇rtos_examples下的hello為例,移植后只要能見到“hello world”,世界從此就光明了。當然見到光明之前,必定先經歷一片漆黑。
hello下的armgcc文件夾即為編譯配置,對應Cmakelists.txt,在其中加入rt-thread相關源碼以及修改hello相關文件。
編譯問題不多說,遇到問題修復問題即可。基本都是文件路徑以及先后問題,沒什么難度。
下面看一下入口main函數,原始代碼如下:
/*!* @brief Application entry point.*/ int main(void) {/* Init board hardware. */sc_ipc_t ipc;ipc = BOARD_InitRpc();BOARD_InitPins(ipc);BOARD_BootClockRUN();BOARD_InitDebugConsole();BOARD_InitMemory();if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=pdPASS){PRINTF("Task creation failed!.\r\n");while (1);}vTaskStartScheduler();for (;;); }可見BOARD_XX開頭的5個函數已經將硬件環境準備好了,后面直接創建task即可。基于此,我們的適配工作好像很簡單。貌似只需要初始化systick即可。由于FreeRTOS systick初始化行為在其源碼中,rt-thread需要在外部進行。因此我們得自行實現systick初始化,有兩個選擇,第一SDK中已有API可調用,第二借用FreeRTOS初始化代碼。有systick中斷,必定有中斷處理好函數,rt-thread已經有標準的實現參考,通常我們放到board.c文件
void SysTick_Handler(void) {/* enter interrupt */rt_interrupt_enter();rt_tick_increase();/* leave interrupt */rt_interrupt_leave(); }建議移植時,分小步進行,先確認systick是否運行OK,然后再繼續。可在Systick_handler中打印log檢測,同時屏蔽rtos API。
一般來說systick基本不會有什么問題。
接著往下,有了systick就有任務切換,代碼位于:rtos/rt-thread/kernel/libcpu/arm/cortex-m4/context_gcc.S,也不需要變動。
rt-thread初始化過程中有一個重要的板級初始化過程rt_hw_board_init
//for rt-thread void rt_hw_board_init() {main_scu_init();/* Call components board initial (use INIT_BOARD_EXPORT()) */ #ifdef RT_USING_COMPONENTS_INITrt_components_board_init(); #endif#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)rt_system_heap_init(heap, sizeof(heap)); #endif }前面提到的5個硬件初始化函數BOARD_XX就是在這里進行
void main_scu_init(void) {/* Init board hardware. */sc_ipc_t ipc;ipc = BOARD_InitRpc();BOARD_InitPins(ipc);BOARD_BootClockRUN();BOARD_InitDebugConsole();BOARD_InitMemory();PRINTF("Hello world. main_scu_init\r\n"); }在rt-thread初始化流程之中,有一個組件的初始化過程rt_components_board_init,其相關符號位于一些特殊的段中,因此需要修改鏈接,加入一些段
有了板級初始化,有了systick,調度不用變,加入了特有段,至此應該可以正常跑起來,至少能進入main,哪怕沒有調度,至少main能夠運行。
關于main入口函數,armgcc與gnugcc入口不同,注意我使用的gnu gcc,入口為entry,在CFLAGS中指定即可-eentry
rt-rthread代碼中也有注釋說明
需要注意的幾個地方:
1. 鏈接加入特定的段
2. gnu gcc指定入口函數
3. Cmake加入新的或改動的源文件并注意編譯順序
4. Systick中斷以及PendSV中斷的優先級
5. main task的棧不可過小
6. 精簡rt-thread編譯配置,盡量最小化
7. 注意宏定義FSL_RTOS_FREE_RTOS,原始SDK中有很多此宏定義,參與編譯的 其包含的內容 需要修改為rt-thread頭文件
8. 注意rt-thread配置的棧大小:
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400;
最后,“hello world”展示圖:
總結
以上是生活随笔為你收集整理的IMX8QXP内部M4移植rt-thread的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rt-thread 自动初始化机制分析-
- 下一篇: Linux睡眠唤醒机制分析--以IMX6