(多核DSP快速入門)SYS/BIOS入門
SYS/BIOS是一個可擴展的實時內核(或者說是操作系統),其提供了許多模塊化的APIs(應用程序接口),支持搶占式多線程,硬件抽象,實時分析和配置工具,其設計目的是為了最大限度地減少對內存和CPU的要求。其擁有很多實時嵌入式操作系統的功能,如任務的調度,任務間的同步和通信,內存管理,實時時鍾管理,中斷服務管理等。有了它,用戶可以編寫復雜的多線程程序,並且會占用更少的CPU和內存資源。
SYS/BIOS的早期版本是DSP/BIOS,更名的原因,是因為SYS/BIOS不僅可以用於DSP,而且也可以嵌入到ARM等其他SoC中去。SYS/BIOS是一個可用於實時調度、同步,主機和目標機通信,以及實時分析系統上的一個可裁減實時內核,它提供了搶占式的多任務調度,對硬件的及時反應,實時分析和配置工具等。同時也提供標准的API接口,易於使用。它是TI的eXpressDSP實時軟件技術的的一個關鍵部分。
CCS中集成安裝了SYS/BIOS,能夠大大方便用戶編寫多任務應用程序。另一方面,SYS/BIOS可以在XDCtools中使用配置技術,極大地方便了SYS/BIOS的開發流程。
本節主要是通過一些簡單的SYS/BIOS的例子來介紹相關APIs組件的作用,這里主要包括了時鍾Clock模塊、任務Tsk模塊、軟件中斷Swi模塊、信號量Sem模塊及日志Log模塊五類簡單模塊的使用。
一、新建SYS/BIOS項目
(1)在項目模板中選擇SYS/BIOS項目中的Hello Example模板,點擊Next

(2)在RTSC(XDCtools的別稱)配置頁中選中需要的SYS/BIOS,XDCtools及其他組件的版本,Target保持默認,不需修改,如果Platform沒有自動填充,選擇與設備適用的平台。Build-profile決定程序鏈接的庫,推薦使用release,即使仍然處於創建和調試階段,點擊完成創建項目。

Tips:當我們開始使用SYS/BIOS時,是不能指定自己添加的鏈接命令文件.cmd。因為.cmd會在構建工程的時候由SYS/BIOS自動創建並使用。
(3)點擊編譯
(4)導入target文件后,點擊調試,運行得到結果

二、SYS/BIOS的模塊與配置
(1)SYS/BIOS可以用文本編輯器或者是圖像配置編輯器XGCONF來編輯,雙擊打開.cfg文件如下

單擊System Overview,可以顯示程序當前使用的主模塊(帶綠色小箭頭的)
(2)各種APIs模塊的添加這里有兩種方法,一種是直接雙擊主模板進入,然后勾選Add

另一種方法是在Available Products中右擊選中的模塊,選擇Use

(3)各個API模塊的作用
CLK:片內定時器模塊,主要控制片內定時器並提供高精度的32位實時邏輯時鍾,它能控制中斷的速度,使之最快達到單指令周期時間。
HST:主機輸入/輸出模塊,管理主機通過對象,它允許應用程序在目標系統和主機之間交流數據,主機通道通過靜態配置為輸入或輸出。
HWI:硬件中斷模塊,提供對硬件中斷服務例程的支持,可在配置文件中指定當硬件中斷發生時需要啟動的函數
IDL:休眠功能模塊,管理休眠函數在目標系統程序沒有更高優先權的函數運行時啟動
LOG:日志模塊,管理LOG對象,LOG對象在目標系統程序執行時實時捕捉事件,開發者可以使用系統日志或定義自己的日志,並在CCS中利用它實時瀏覽信息。
MEM:存儲器模塊允許指定存放目標程序的代碼和數據所需的存儲器段
PIP:數據通道模塊管理數據通道,它用來緩存輸入和輸出數據流,這此數據通道提供一致性的軟件數據結構,可以使用它們驅動DSP和其他實時外圍設備之間的I/O通道
PRD:周期函數模塊,管理周期對象,它觸發應用程序的周期性執行。
RTDX:實時數據交換允許數據在主機和目標系統之間實時交換,在主機上使用自動OLE的客戶都可對數據進行實時顯示和分析。
STS:統計模塊,管理統計累積器,在程序運行時,它存儲關鍵統計數據並能通過CCS瀏覽這此統計數據
SWI:軟件中斷模塊管理軟件中斷。
TRC:跟蹤模塊,管理一套跟蹤控制比特,它們通過事件日志和統計累積器控制程序信息的實時捕捉。
三、在項目中導入LOG模塊
LOG模塊實際上是一個實現打印信息的API
(1)添加LOG模塊

(2)LOG模塊的使用
LOG模塊定義了許多比如Log_error、Log_info、Log_warning、Log_print等之類函數,這些函數的用法同printf函數的用法很相似,這些函數都可以在<xdc/runtime/Log.h>找到,其實際上是將printf的有用法分成許多不同的類(如錯誤信息、提示信息、警告信息等),LOG模塊打印的內容可以在Tools/RTA/PrintfLogs中看到。
Tips:LOG中定義了許多如下的函數,i比如Log_info1函數后面的數字代表函數接的變量數目
如Log_info1("%d",s1)、Log_info2("%d, %d", s1, s2)

四、在項目中導入TSK任務模塊
TSK任務模塊是操作系統中最基本的模塊,其實際上反映了多線程搶占,每個任務單獨是一個線程,各個線程(任務)具有各自的優先級
(1)新建任務,首先選擇Use TSK,確定TSK模擬是否導入

然后創建新任務New Task

這里我們創建兩個任務task0、task1,分別對應其函數func_tsk0、func_tsk1。其優先級都為1

(2)編寫任務函數

a). 這里我們在主函數中BIOS_start()函數,說明任務開始執行了
b). 任務執行完后調用BIOS_exit(0)退出
c). Task_yield()是個優先級調度函數,其作用就是如果有相同優先級的任務,則調度到同優先級的其它任務執行!
d). Log_info1()的作用是打印日志信息
e). while和count循環的目標是讓任務執行較長的時間,而不是只執行一次就退出了。
f). 最后別忘了在增加相關頭文件和函數的聲明!

(3)編譯調試,運行查看結果(這里我們只需要選擇單核運行就可以了)
從下面的分析,我們可以看到兩個任務是相互依次運行的,每個任務運行一次后,其優先級就會降低,此時就切換到下一個任務

五、在項目中導入Swi軟件中斷模塊
前面我們已經知道了不同任務有不同優先級,而軟件中斷具有比任何任務都高的優先級,而其中硬件中斷(HWI)又比軟件中斷(SWI)優先級更高。
(1)添加軟件中斷Swi,這里方法同前面添加TSK的方法不一樣,似乎不能用圖形界面方式去添加(我沒有成功=_=||)
這里我選擇通過直接在源代碼中添加相關代碼:
a). 首先需要添加一個全局的Swi句柄:Swi_Handle swi0;
b). 初始化Swi參數:
Swi_Params swiParams;
Swi_Params_init(&swiParams);
swiParams.priority = 2; // 軟件中斷的優先級
swiParams.trigger = 2; // 軟件中斷的計數器
c). 創建軟件中斷:
swi0 = Swi_create(func_swi0, &swiParams, NULL); // swi0是中斷名,func_swi0是中斷函數
d). 編寫中斷函數:
void func_swi0(void)
{
static Int count = 0;
Log_info1("Swi0 is doing %d\n",count);
count++;
}
e). 更改軟件中斷計數器trigger,要觸發軟件中斷,首先需要讓trigger的計數為0,這里我們可以在任務函數內增加一個trigger自減的函數,任務函數執行兩次后,將會觸發軟件中斷。
void func_tsk0(void)
{
Int count = 0;
while(count<10){
Log_info1("Task0 is doing %d\n",count);
Swi_dec(swi0);
Task_yield();
count++;
}
BIOS_exit(0);
}
(2)一些Swi APIs 函數
Swi_inc Swi.trigger自增函數,每次增加1
Swi_dec Swi.trigger自減函數,每次減少1
Swi_or 將trigger的值與模板求或操作
Swi_and 將trigger的值與模板求和操作
(3)編譯調試,運行查看結果(這里我們只需要選擇單核運行就可以了)
從下面的分析,我們可以看到兩個任務的每次都會使得軟件中斷計數trigger減1(通過Swi_dec函數),直到trigger的值減少到0時,執行軟件中斷,中斷后,trigger恢復到原來的值,這里的trigger初始值為2,所以執行兩次任務后就會觸發一次軟件中斷。

六、在項目中導入信號量Semaphore模塊
信號量是在多線程環境下使用的一種設施,是可以用來保證兩個或多個關鍵代碼段不被並發調用,對於多個任務來說,使用信號量可以防止多個任務同時執行。
信號量可分互斥信號量和計數信號量,互斥信號量只有兩種狀態:1和0,為1時說明可用,否則不可用。而計數信號量通過設置一個計數值,如果計數值大於0,則任務請求該信號量時是可用。
這里例子是我們給task0任務中增加一個互斥信號量,當信號量狀態為1時,task0才能繼續執行。通過軟件中斷來達到信號量歸1。
(1)添加信號量
a). 首先需要添加一個全局的信號量句柄:Semaphore_Handle sem0;
b). 創建信號量:sem0 = Semaphore_create(0, NULL, NULL);
c). 在增加互斥信號量的任務函數中增加一個等待信號量為1的函數
Semaphore_pend(sem0, BIOS_WAIT_FOREVER); // BIOS_WAIT_FOREVER表示一直等待,直到信號量為1
d). 在軟件中斷函數中增加一個解鎖信號量的函數
Semaphore_post(sem0);
e). 最后別忘了添加Semaphore模塊的頭文件
#include <ti/sysbios/knl/Semaphore.h>
f). 如果.cfg文件中沒有添加信號量模塊,記得一定要添加,否則雖然不會報錯,但信號量不會工作
(2)編譯調試,運行查看結果(這里我們只需要選擇單核運行就可以了)
從下面的分析,我們可以看到只有當軟件中斷執行后,此時信號量才解鎖,task0才能執行,而任務執行兩次,才能觸發一次軟件中斷。

七、在項目中導入時鍾Clock模塊
Clocks模塊主要提供周期性執行函數,我們這里新建一個周期性執行函數,其每四個周期執行一次。
(1)添加信號量
a). 首先初始化時鍾參數:
Clock_Params clkParams;
Clock_Params_init(&clkParams);
clkParams.period = 5; // 函數執行周期
clkParams.startFlag = TRUE; // True說明時鍾立即開始計時
b). 創建時鍾:Clock_create(func_clk, 5, &clkParams, NULL); //創建時鍾,func_clk是周期執行的函數,這里5是開始執行的延時。
c). 編寫周期執行的時鍾函數
void func_clk(UArg arg0)
{
UInt32 time;
time = Clock_getTicks(); // 這里是定時器的節拍器
System_printf("System time in clk0Fxn = %lu\n", (ULong)time);
if(time>20)
BIOS_exit(0);
}
d). 因為任務的執行時間非常快,所以需要先把任務內的退出BIOS命令先刪除下,否則當任務完成后,時鍾函數還沒執行
void func_tsk1(void)
{
Int count = 0;
while(1){
//while(count<10){
Log_info1("Task1 is doing %d\n",count);
Swi_dec(swi0);
Task_yield();
count++;
}
//BIOS_exit(0);
}
e). 最后別忘了添加時鍾模塊的頭文件
#include <ti/sysbios/knl/Clock.h>
f). 如果.cfg文件中沒有添加時鍾Clock模塊,記得一定要添加,否則雖然不會報錯,但時鍾模塊不會工作
(2)編譯調試,運行查看結果(這里我們只需要選擇單核運行就可以了)
從下面的分析,我們可以看到只有當周期函數func_clk每隔5個周期開始執行,開始執行時間為5。

項目代碼下載: