rt-thread系統裁剪指南
- 2020 年 3 月 31 日
- 筆記
01
為什麼要進行系統裁剪
在設計嵌入式RTOS系統邏輯時,我們往往希望系統簡潔並且程式碼可控,這樣我們做方案時心裡才有底。下面我們來從原理層面和實現層面上講一下rt-thread裁剪相關的知識。
嵌入式的本質特點如下:
1.以應用為中心
2.功耗低,體積小,成本價低
3.軟體或硬體可以裁剪
在設計嵌入式產品的過程中,可以充分的考慮這些問題,所以在實現時,我們不僅僅考慮的是系統層面的問題,而更應該關注於業務邏輯上的需求。
1.應用領域
2.研發周期
3.系統複雜度
4.晶片資源情況(ROM and RAM)
5.韌體大小要求
6.啟動速度要求
從嵌入式系統架構的角度,一定需要符合高內聚,低耦合的思想。
對於裸機開發來說,系統裁剪意味著一個輪訓操作中縮減或者優化業務邏輯,多用標誌或者返回狀態來進行程式設計。
對於Linux來說,系統裁剪就是將不必要的組件或者外設和Linux中的工具去掉,減少系統功能,在uboot中也需要裁剪,合理設計必須初始化的外設,合理算出Linux kernel大小,然後進行跳轉。
對於RTOS來說,系統裁剪可以通過對功能的縮減,減少不必要的組件初始化或者ipc初始化,例如,只需要用到關鍵的ipc比如訊號,郵箱,那就不用初始化消息隊列和事件,這樣就能縮減程式碼體積,減少不必要的初始化過程。另外就是業務邏輯的設計了,裁剪的目的一方是縮減韌體體積,另外一方面也是加快系統的啟動速度,所以要充分考慮驅動初始化的流程,在RTOS設計中,多採用設備並行初始化。
02
rt-thread具有高度的可裁剪性
RT-Thread 主要採用 C 語言編寫,淺顯易懂,方便移植。它把面向對象的設計方法應用到實時系統設計中,使得程式碼風格優雅、架構清晰、系統模組化並且可裁剪性非常好。完整版的 RT-Thread 與其他很多RTOS 如 FreeRTOS、uC/OS 的主要區別之一是,它不僅僅是一個實時內核,還具備豐富的中間層組件,如下圖所示:

完整版功能雖然十分完善,不過相應的,隨著功能的增加,資源佔用也是在增加的,這對小資源的平台 來說不是十分友好。得益於 RT-Thread 的高度可裁剪性,通過對完整版的裁剪,可以十分便捷的小資源 平台上使用上 RT-Thread。可以通過 env 工具十分方便地進行裁剪,env 工具使用方法如下圖所示:

03
資源規劃
這裡的資源指的是硬體資源和軟體資源兩個方面,硬體資源從設計的時候就基本已經確定,在與軟體進行合理的規劃和設計之後,硬體方案就可以確定下來,相關引腳的分配,上電的時序,操作的合理性,以及考慮到功耗的要求,外設的情況,前期軟體開發調試,後期工廠測試,這時,整個硬體方案就可以確定下來了,軟體開發者利用這個確定的硬體資源,進行軟體層面的規劃,所以在設計軟體的時候,是完全知道硬體的資源的,包括引腳的使用,上電的邏輯,CPU的情況,可以利用的RAM或者ROM資源。這一部分是架構的工作,一個好的硬體設計方案,將使得成型的產品更加穩定可靠。所以硬體的設計也是可裁剪的,因為前期的開發調試與後期的產品成型後的工廠測試板子的設計上是不能變動的,只可以通過裁剪將其區分出來。這個不僅僅是硬體工程師需要了解,軟體工程師也需要有產品意識。
對於RT-Thread中,比較關心的是ROM資源以及RAM資源,所以在設計之初,首先評估資源是否合理去跑一個rtos。
比如,rt-thread nano版本對 RAM 與 ROM 的開銷非常小,在支援 semaphore 和 mailbox 特性,並運行兩個執行緒 (main 執行緒 + idle 執行緒) 情況下,ROM 和 RAM 依然保持著極小的尺寸,RAM 佔用約 1K 左右,ROM 佔用 4K 左右。
對於RAM 與 ROM資源比較大的場合,合理的利用每個資源也是比較好的習慣,這裡首先分析一下rt-thread記憶體的使用與裁剪。
1.堆空間
一般在具體的bsp的board.h中,都會有堆空間的描述
#define RT_HW_HEAP_BEGIN (void*)&__bss_end
#define RT_HW_HEAP_END (void*)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024)
在系統設計的時候,評估系統所需要的堆空間。
堆空間的使用,一般是動態申請記憶體或者是動態創建執行緒,創建IPC的時候會使用到。當然,如果我們不需要記憶體管理功能,那麼就可以去掉堆空間的使用,所有的執行緒通過靜態創建的方式進行,比較典型的就是rt-thread nano的使用。
2.執行緒棧空間
我們在使用的時候,一般都會給定一個棧空間去運行執行緒,所以創建執行緒的時候,攜帶了給定的最大運行棧
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
其中棧的尺寸stack_size一般都是韌體函數調用深度和可能需要的最大資源來給定,一般剛開始開發的時候,給定的都是最大值,如果要進行裁剪,有三種方法:
1.讓系統運行一段時間,通過命令行list_thread

來調整棧空間
2.在MDK中,可以查看Static Call Graph for image文件來查看棧的使用情況

通過函數調用關係,計算最大棧的深度
3.手動計算
評估局部變數和每個函數中的調用關係,這個需要手動去計算。
04
內核裁剪
rt-thread的組成就是組件+內核,我們先說一下內核裁剪部分,通過env工具可以看到如下功能:

1.執行緒間通訊機制

根據我們系統中常用的一下功能或者組件進行裁剪
2.記憶體管理

根據具體的情況選擇不同的記憶體管理策略
3.內核設備

有關console設置
05
組件裁剪
rt-thread是內核+組件的方式,使用一些組件可以幫助我們更高效的設計出具體的業務邏輯。

這部分裁剪可以根據需要進行
1.設置main執行緒的棧空間及優先順序
2.是否使用C++特性
3.shell相關的操作的配置
4.設備虛擬文件系統
5.設備驅動框架
6.POSIX介面
7.網路部分
8.工具部分
這些裁剪是在設計方案的時候選擇是否需要。
06
業務邏輯裁剪
在使用rt-thread作業系統時,往往都是利用rt-thread實現自己的業務邏輯,所以我們在編寫自己的程式碼的時候,也需要充分的理解rt-thread的設計思想。以及我們在做設計的時候,可能需要配置一些可以調整的策略方案進行多角度,多方位的嘗試,這時可以利用Kconfig與SConscript相關的腳本進行操作,本質上來說就是在程式碼中添加一些宏定義,這些配置可以通過圖形化的方式選擇或者去掉。scons腳本的使用以及env相關的操作可以通過官網文檔查閱。
需要注意的是,在設計時,我們考慮的不僅僅是這些宏定義的依賴關係,還需要從使用的角度來考慮,比如程式碼是否編譯進去,選擇後,其依賴關係是怎樣的?
舉個例子,比如攝影機可以輸出YUV格式的影像,也可以輸出RGB的影像,通過不同的暫存器配置進行,這裡我們可以將這個宏利用Kconfig變成圖形可選擇的對象,然後依賴這些宏後,在進行影像處理的時候,利用rtconfig.h所定義的宏,來選擇不同的處理影像的模式程式碼。這樣程式碼的健壯性更強,也不會去干擾其他的業務邏輯了,並且這些配置也更加的直觀。