RTOS - context switch - example 2
收錄於 : 關於Ameba的一百篇
續前篇 : Jserv 的 IoT OS
mini-arm-os 的 Hackpad
我們來用 Arduino 做 mini-arm-os 的 context-switch 實驗.
原本的程式 code 在 :
https://github.com/jserv/mini-arm-os/tree/master/02-ContextSwitch-1
因為想直接用 Arduino IDE 來做實驗, 但目前我這包 IDE 包了一個 RTX.
首先我們先來把 RTX 呼叫的地方移除.
( 可參考 RTOS 如何植入 )
1. variant.cpp
在 Ameba 這包, ROM code boot up, reset_handler 會做相關的 硬體設定,
最後呼叫到 _AppStart(), 我把這個放在 variant.cpp.
以 Mac 為例, variant.cpp 位置在
/Users/<使用者> /Library/Arduino15/packages/njiot/hardware/ameba/1.2.1/variants/arduino_ameba/
其他 Arduino platform 也類似.
我們把這 function 改成 只執行
__libc_init_array();
main();
如下:
以 Mac 為例, main.cpp 位置在
/Users/<使用者> /Library/Arduino15/packages/njiot/hardware/ameba/1.2.1/cores/arduino/
續前篇 : Jserv 的 IoT OS
mini-arm-os 的 Hackpad
我們來用 Arduino 做 mini-arm-os 的 context-switch 實驗.
原本的程式 code 在 :
https://github.com/jserv/mini-arm-os/tree/master/02-ContextSwitch-1
因為想直接用 Arduino IDE 來做實驗, 但目前我這包 IDE 包了一個 RTX.
首先我們先來把 RTX 呼叫的地方移除.
( 可參考 RTOS 如何植入 )
1. variant.cpp
在 Ameba 這包, ROM code boot up, reset_handler 會做相關的 硬體設定,
最後呼叫到 _AppStart(), 我把這個放在 variant.cpp.
以 Mac 為例, variant.cpp 位置在
/Users/<使用者> /Library/Arduino15/packages/njiot/hardware/ameba/1.2.1/variants/arduino_ameba/
其他 Arduino platform 也類似.
我們把這 function 改成 只執行
__libc_init_array();
main();
如下:
// The Main App entry point
void _AppStart(void)
{
#if 1
__libc_init_array();
main();
#else
VectorTableSettingForOS((void*)SVC_Handler,
(void*)PendSV_Handler,
(void*)SysTick_Handler);
__asm (
"ldr r0, =SystemInit\n"
"blx r0\n"
"ldr r0, =_start\n"
"bx r0\n"
);
#endif
}
2. main.cpp
main() 裡有一個 osThreadYield(), 這是 CMSIS-RTOS 的 API.
我們可以先把它 mark 起來 //osThreadYield()
這樣就恢復成原本 Arduino 沒有 OS 的狀況.
------
來做 02 Context-switch 實驗.
Arduino 程式 code 可以寫成 :
__attribute__((naked)) void activate(unsigned int *pstack)
{
asm volatile (
"mrs ip, psr \n\t"
"push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr} \n\t"
"msr psp, r0 \n\t"
"mov r0, #3 \n\t"
"msr control, r0 \n\t"
"pop {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t"
"bx lr \n\t"
);
}
void usertask(void)
{
Serial.println("User Task #1");
while (1);
}
void setup() {
unsigned int usertask_stack[256];
unsigned int *usertask_stack_start = usertask_stack +256-16;
usertask_stack_start[8] = (unsigned int) &usertask;
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// prints title with ending line break
Serial.println("OS Starting...");
activate(usertask_stack_start);
while(1);
}
void loop() {
}
-----
分析:
1. activate function 其實應該用參數寫法.
( 可參考之前 arduino 寫 assembly )
不過這邊為方便, 原封不動把 原本 assembly function 包進來, 寫成 gcc inline assembly.
在 ARM 系統, 第一個 function 參數, 會放在 r0.
2. uart 相關設定, 被 arduino 簡化成 Serial 操作
執行結果 :
會印出 :
OS Starting...
User Task #1
表示 activate function 有正確的把 lr load 進去, bx 跳到 該 function.
---
說明: 可參考上述的 hackpad, 或
hardware push 搭配 pendSV
- CM3 有 main stack pointer (MSP) 和 process stack pointer (PSP)
MSP handler mode / thread mode , privileged / un-privileged
PSP 用在一般程式 (thread mode)(un-privileged)
- MRS : 把值從 特殊暫存器 (Special) 放到 一般暫存器 (Regular)
- MSR : 把值從 一般暫存器 (Regular) 放到 特殊暫存器 (Special)
---
Table 2.10. CONTROL register bit assignments
用 inline assembly 讀取 register, 我們可以參考 core_cm3.c 的寫法
uint32_t __get_CONTROL(void)
{
uint32_t result=0;
asm volatile ("MRS %0, control" : "=r" (result) );
return(result);
}
事實上在 core_cmFunc.h 已經有 inline function 定義,
如果把 function 搬進來, compiler 會說有重複定義的錯誤.
我們可以直接印出來看
可以把底下的 code , 加在呼叫 activate(usertask_stack_start); 前,
以及 usertask() function 內
value = __get_CONTROL();
Serial.print("Control = 0x");
Serial.println(value, HEX);
結果 :
OS Starting...
Control = 0x0
Control = 0x3
User Task #1
所以我們知道 一開始他是在 privileged mode, 使用 MSP.
在 activate() 中, 我們把 usertask_stack_start 設到 psp
並將 control 設成 unprivileged / 使用 PSP
"msr psp, r0"
"mov r0, #3 \n\t"
"msr control, r0 \n\t"
- PUSH/POP
register list 會由小到大 對應到 低記憶體位置 - 高記憶體位置, 如果故意 register 不照順序排, compiler 會有 warning. 但還是會由小到大來做.
control register 設定到 user thread 後, 就無法再透過設定 control register 回到 privileged mode. 而得靠 interrupt 進到 exception handler.
在 unprivileged mode 時, 無法讀取 MSP / PSP register.
留言
張貼留言