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();

    如下:

// 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

以 Mac 為例,  main.cpp 位置在

    /Users/<使用者> /Library/Arduino15/packages/njiot/hardware/ameba/1.2.1/cores/arduino/

 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
BitsNameFunction
[31:2]-Reserved.
[1]SPSEL
Defines the currently active stack pointer: In Handler mode this bit reads as zero and ignores writes. The Cortex-M3 updates this bit automatically on exception return.
0 = MSP is the current stack pointer
1 = PSP is the current stack pointer.
[0]nPRIV
Defines the Thread mode privilege level:
0 = Privileged
1 = Unprivileged.


用 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"



   register list 會由小到大 對應到 低記憶體位置 - 高記憶體位置, 如果故意 register 不照順序排,  compiler 會有 warning. 但還是會由小到大來做. 


control register 設定到 user thread 後, 就無法再透過設定 control register 回到 privileged mode. 而得靠 interrupt 進到 exception handler. 

在 unprivileged mode 時, 無法讀取 MSP / PSP register. 
















留言

熱門文章