--- title: rtenv-plus categories: embedded, arm, stm32, stm32f429 toc: no ... `HackPad 共筆`_ 組員 ---- 楊震 / <`Omar002`_> 丁士宸 / <`Stanley Ding`_> 程政罡 / <`marktwtn`_> 李昆憶 / <`LanKuDot`_> 鄭聖文 / <`Shengwen`_> 作業系統架構 ----------------------- Context Switch =============== **activate** - 功能:將 kernel state push 到 Main stack 中,再將目前正在執行的 process state pop 到 register 中。 .. code-block:: c activate: 41 /* save kernel state */ 42 mrs ip, psr 43 push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr} 44 45 /* switch to process stack pointer */ 46 msr psp, r0 47 mov r0, #3 48 msr control, r0 49 50 /* load user state */ 51 pop {r4, r5, r6, r7, r8, r9, r10, r11, lr} 52 pop {r7} 53 54 bx lr - 指令介紹:``mrs Rd, PSR`` 及 ``msr PSR, Rd``[1]_:Rd 為 general-purpose registers,PSR 可以為 psr、cpsr、apsr、msp、psp 等。 ``mrs Rd, PSR`` 可以將 PSR 的值寫到 Rd,而 ``msr PSR, Rd`` 則是將 Rd 值寫到 PSR 裡。 - 運作: - L42,43:將 ``psr``(program status register) 的值保存到 ``ip`` (r12) 裡,然後一同 push 到 main stack 裡。 - L46:將 ``r0`` 所帶的值寫入到 ``psp`` (process stack pointer),注意呼叫 activate 所放的參數就是該 task 之 task_control_block 中 stack 的 address。 - L47,48:將 ``control`` register 的值設為 3,藉此可以將 stack pointer 轉為指向 process stack (使 sp 值為 psp)。所以藉由 ``sp`` 可以存取其 stack 的內容。 - L51:將 ``user_thread_stack`` 的 register 依序 pop 到 r4~r11 及lr,也是為何 ``user_thread_stack`` 的前9個 register 設計為 r4~r10、fp、_lr。 - L52:再將 ``user_thread_stack`` 的 ``_r7`` pop 到 ``r7``,達成傳遞 syscall 的功能。 - 所以除了 r0~r3 及 ip、sp、pc、cpsr 之外,都被換成 user-mode 的 register 了。 init_task ========== - 功能:將系統初始函式 ``first()`` 的位址放置到 process stack 的 lr 位置。藉由 ``activate`` 置換 process state 上來,可讓程式執行 ``first()``。 - 運作: .. code-block:: c /* 傳入的參數為:欲執行 first() 的 task 的 stack位址 以及 first() 的位址 */ unsigned int *init_task(unsigned int *stack, void (*start)()) { /* 由於 stack 的設計為 full descendent stack, * 所以 stack pointer 一開始必須指向最高位址。 * 觀察 user_thread_stack 的設計:r4 是最低位址,處在 stack 的底部 * 而預期將 first() 的位址存到 _lr 中,所以必須 push 9個 word */ stack += STACK_SIZE - 9; /* 利用 pointer arithmetic,可以將 first() 的位址存到 _lr 中: * user_thread_stack -> |r4 |r5 |r6 |r7 |r8 |r9 |r10|fp |_lr|... * stack -> |[0]|[1]|[2]|[3]|[4]|[5]|[6]|[7]|[8]|... */ stack[8] = (unsigned int)start; /* 回傳新的 sp 給該 task */ return stack; } fork 原理 ========== **第一次進到 while loop** - 進到 ``activate()`` 後,藉由 pop user state 到 register,將預先存好的 ``first()`` 的位址存到 ``lr`` 中。此時,被 push 進到 main stack 的資訊中含有離開 ``activate()`` 後繼續執行的指令位址。 :: user_thread_stack -> |r4 |r5 |r6 |r7 |r8 |r9 |r10|fp |_lr|... V 依 pop 順序到 register -> |R4 |R5 |R6 |R7 |R8 |R9 |R10|R11| LR|... - 利用 ``bx lr`` 使得程式轉往執行 ``first()``。進到 ``first()`` 後,程式執行第一行的 ``fork()``。 - 在 syscall 中,會觸發 svc exception,程式轉往執行 ``SVC_Handler()``,同時會 processor 會將 xPSR、PC、LR、R12、R3、R2、R1、R0[2]_依序 push 到目前的 stack 中 ( 也就是 process stack ),被 push 到 process stack 的資訊中含有離開 ``fork()`` 後繼續執行的指令位址。 :: user_thread_stack -> ...|_r7|r0|r1|r2|r3|ip |lr|pc|xpsr|stack... 低位址 高位址 依 push 順序到 stack -> .......|R0|R1|R2|R3|R12|LR|PC|xPSR| ( _r7 存的是在 syscall 中被暫存的 R7 值 ) - ``SVC_Handler()`` 中會將目前的 user state ( push 到 process stack ) 與 kernel state ( 從 main stack pop 出來 ) 作交換,此時 ``LR`` 擁有的位址為離開 activate() 後要執行的指令位址。所以離開 ``SVC_Handler()`` 後,程式會轉往執行 while loop,而在 ``SVC_Handler()`` 中,也將在 ``fork()`` 中存入 R7 的值 push 到 process stack 中了。所以接著會判定要進行 fork 動作。 硬體驅動原理 ============= * `USB OTG`_ 效能表現 ======== 參考資料 ----------- .. [1] `mrs指令`_ 、 `msr指令`_ .. [2] `Cortex-M3 Exception`_