mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 04:24:33 +00:00
modify the 05-Preemptive.md
This commit is contained in:
@@ -138,12 +138,12 @@ trap_vector:
|
||||
# trap_handler will return the return address via a0.
|
||||
csrw mepc, a0
|
||||
|
||||
# restore context(registers).
|
||||
# load context(registers).
|
||||
csrr t6, mscratch
|
||||
reg_load t6
|
||||
|
||||
la a1, os_kernel # mepc = sys_kernel
|
||||
csrw mepc, a1 # mret : will jump to sys_kernel
|
||||
# return to whatever we were doing before trap.
|
||||
# jump to sys_kernel
|
||||
la a1, os_kernel # a1 = os_kernel
|
||||
csrw mepc, a1 # mepc = sys_kernel
|
||||
mret
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "timer.h"
|
||||
|
||||
extern void os_kernel();
|
||||
|
||||
// a scratch area per CPU for machine-mode timer interrupts.
|
||||
reg_t timer_scratch[NCPU][5];
|
||||
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@@ -1,5 +1,8 @@
|
||||
mini-riscv-os is written by:
|
||||
Chung-Chen Chen <ccckmit@gmail.com>
|
||||
|
||||
Other contributors:
|
||||
Ian Chen <ychen.desl@gmail.com>
|
||||
|
||||
Copyrighted by:
|
||||
National Quemoy University, Taiwan
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
# 05-Preemptive -- RISC-V 的嵌入式作業系統
|
||||
|
||||
[lib.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/lib.c
|
||||
|
||||
[os.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
|
||||
|
||||
[timer.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/timer.c
|
||||
|
||||
[sys.s]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/sys.s
|
||||
|
||||
[task.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/task.c
|
||||
|
||||
[user.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/user.c
|
||||
[lib.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/lib.c
|
||||
[os.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
|
||||
[timer.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/timer.c
|
||||
[sys.s]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/sys.s
|
||||
[task.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/task.c
|
||||
[user.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/user.c
|
||||
|
||||
專案 -- https://github.com/ccc-c/mini-riscv-os/tree/master/05-Preemptive
|
||||
|
||||
@@ -115,7 +110,7 @@ void lib_delay(volatile int count)
|
||||
|
||||
## 作業系統 [os.c]
|
||||
|
||||
* https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
|
||||
- https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
|
||||
|
||||
作業系統 os.c 一開始會呼叫 user_init() ,讓使用者建立 task (在本範例中會在 [user.c] 裏建立 user_task0 與 user_task1。
|
||||
|
||||
@@ -178,16 +173,102 @@ int os_main(void)
|
||||
}
|
||||
```
|
||||
|
||||
05-Preemptive 的時間中斷原理和上一章相同,都是在 [timer.c] 的 timer_init() 中設定好第一次的時間中斷。
|
||||
在 05-Preemptive 的中斷機制中,我們修改了中斷向量表:
|
||||
|
||||
```c=
|
||||
.globl trap_vector
|
||||
# the trap vector base address must always be aligned on a 4-byte boundary
|
||||
.align 4
|
||||
trap_vector:
|
||||
# save context(registers).
|
||||
csrrw t6, mscratch, t6 # swap t6 and mscratch
|
||||
reg_save t6
|
||||
csrw mscratch, t6
|
||||
# call the C trap handler in trap.c
|
||||
csrr a0, mepc
|
||||
csrr a1, mcause
|
||||
call trap_handler
|
||||
|
||||
# trap_handler will return the return address via a0.
|
||||
csrw mepc, a0
|
||||
|
||||
# load context(registers).
|
||||
csrr t6, mscratch
|
||||
reg_load t6
|
||||
|
||||
# jump to sys_kernel
|
||||
la a1, os_kernel # a1 = os_kernel
|
||||
csrw mepc, a1 # mepc = sys_kernel
|
||||
mret
|
||||
```
|
||||
|
||||
當中斷發生時,中斷向量表 `trap_vector()` 會呼叫 `trap_handler()` :
|
||||
|
||||
```c=
|
||||
reg_t trap_handler(reg_t epc, reg_t cause)
|
||||
{
|
||||
reg_t return_pc = epc;
|
||||
reg_t cause_code = cause & 0xfff;
|
||||
|
||||
if (cause & 0x80000000)
|
||||
{
|
||||
/* Asynchronous trap - interrupt */
|
||||
switch (cause_code)
|
||||
{
|
||||
case 3:
|
||||
lib_puts("software interruption!\n");
|
||||
break;
|
||||
case 7:
|
||||
lib_puts("timer interruption!\n");
|
||||
timer_handler();
|
||||
break;
|
||||
case 11:
|
||||
lib_puts("external interruption!\n");
|
||||
break;
|
||||
default:
|
||||
lib_puts("unknown async exception!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Synchronous trap - exception */
|
||||
lib_puts("Sync exceptions!\n");
|
||||
while (1)
|
||||
{
|
||||
/* code */
|
||||
}
|
||||
}
|
||||
return return_pc;
|
||||
}
|
||||
```
|
||||
|
||||
跳到 `trap_handler()` 之後,它會針對不同類型的中斷呼叫不同的 handler ,所以我們可以將它視為一個中斷的派發任務中繼站:
|
||||
|
||||
```
|
||||
+----------------+
|
||||
| soft_handler() |
|
||||
+-------+----------------+
|
||||
|
|
||||
+----------------+-------+-----------------+
|
||||
| trap_handler() | | timer_handler() |
|
||||
+----------------+ +-----------------+
|
||||
|
|
||||
+-------+-----------------+
|
||||
| exter_handler() |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
`trap_handler` 可以根據不同的中斷類型,將中斷處理交給不同的 handler ,這樣子做就可以大大的提高作業系統的擴充性。
|
||||
|
||||
```cpp
|
||||
#include "timer.h"
|
||||
|
||||
extern void os_kernel();
|
||||
|
||||
// a scratch area per CPU for machine-mode timer interrupts.
|
||||
reg_t timer_scratch[NCPU][5];
|
||||
|
||||
#define interval 20000000 // cycles; about 2 second in qemu.
|
||||
|
||||
void timer_init()
|
||||
{
|
||||
// each CPU has a separate source of timer interrupts.
|
||||
@@ -195,8 +276,8 @@ void timer_init()
|
||||
|
||||
// ask the CLINT for a timer interrupt.
|
||||
// int interval = 1000000; // cycles; about 1/10th second in qemu.
|
||||
int interval = 20000000; // cycles; about 2 second in qemu.
|
||||
*(reg_t*)CLINT_MTIMECMP(id) = *(reg_t*)CLINT_MTIME + interval;
|
||||
|
||||
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
|
||||
|
||||
// prepare information in scratch[] for timervec.
|
||||
// scratch[0..2] : space for timervec to save registers.
|
||||
@@ -207,48 +288,33 @@ void timer_init()
|
||||
scratch[4] = interval;
|
||||
w_mscratch((reg_t)scratch);
|
||||
|
||||
// set the machine-mode trap handler.
|
||||
w_mtvec((reg_t)sys_timer);
|
||||
|
||||
// enable machine-mode interrupts.
|
||||
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
||||
|
||||
// enable machine-mode timer interrupts.
|
||||
w_mie(r_mie() | MIE_MTIE);
|
||||
}
|
||||
|
||||
static int timer_count = 0;
|
||||
|
||||
void timer_handler() {
|
||||
void timer_handler()
|
||||
{
|
||||
lib_printf("timer_handler: %d\n", ++timer_count);
|
||||
os_kernel();
|
||||
int id = r_mhartid();
|
||||
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
但不同的是,[sys.s] 裏的 sys_timer 會呼叫上面 [timer.c] 裏的 timer_handler(),其中包含了 `os_kernel()` 這個函數,該函數會呼叫 [task.c] 裏的 task_os(), task_os() 會呼叫 [sys.s] 裏的 sys_switch 去切換回 kernel,於是作業系統就透過時間中斷將控制權強制取回來了。
|
||||
看到 [timer.c] 裏的 `timer_handler()`,它會將 `MTIMECMP` 做 reset 的動作,等到 `timer_handler()` 執行完畢,中斷向量表 `trap_vector()` 會將 mepc 指向 `os_kernel()` ,做到任務切換的功能。
|
||||
|
||||
```s
|
||||
# ...
|
||||
sys_switch:
|
||||
ctx_save a0 # a0 => struct context *old
|
||||
ctx_load a1 # a1 => struct context *new
|
||||
ret # pc=ra; swtch to new task (new->ra)
|
||||
# ...
|
||||
sys_kernel:
|
||||
addi sp, sp, -128 # alloc stack space
|
||||
reg_save sp # save all registers
|
||||
call timer_handler # call timer_handler in timer.c
|
||||
reg_load sp # restore all registers
|
||||
addi sp, sp, 128 # restore stack pointer
|
||||
jr a7 # jump to a7=mepc , return to timer break point
|
||||
# ...
|
||||
sys_timer:
|
||||
# ...
|
||||
csrr a7, mepc # a7 = mepc, for sys_kernel jump back to interrupted point
|
||||
la a1, sys_kernel # mepc = sys_kernel
|
||||
csrw mepc, a1 # mret : will jump to sys_kernel
|
||||
# ...
|
||||
最後,記得在 Kernel 開機時導入 trap 以及 timer 的初始化動作:
|
||||
|
||||
```c=
|
||||
void os_start()
|
||||
{
|
||||
lib_puts("OS start\n");
|
||||
user_init();
|
||||
trap_init();
|
||||
timer_init(); // start timer interrupt ...
|
||||
}
|
||||
```
|
||||
|
||||
透過時間中斷強制取回控制權,我們就不用擔心有惡霸行程把持 CPU 不放,系統也就不會被惡霸卡住而整個癱瘓了,這就是現代作業系統中最重要的《行程管理機制》。
|
||||
@@ -259,29 +325,25 @@ sys_timer:
|
||||
|
||||
還好,這些事情已經有人做好了,您可以透過學習 xv6-riscv 這個由 MIT 所設計的教學型作業系統,進一步了解這些較複雜的機制,xv6-riscv 的原始碼總共有八千多行,雖然不算太少,但是比起那些動則數百萬行到數千萬行的 Linux / Windows 而言,xv6-riscv 算是非常精簡的系統了。
|
||||
|
||||
* https://github.com/mit-pdos/xv6-riscv
|
||||
- https://github.com/mit-pdos/xv6-riscv
|
||||
|
||||
然而 xv6-riscv 原本只能在 linux 下編譯執行,但是我把其中的 mkfs/mkfs.c 修改了一下,就能在 windows + git bash 這樣和 mini-riscv-os 一樣的環境下編譯執行了。
|
||||
|
||||
您可以從下列網址中取得 windows 版的 xv6-riscv 原始碼,然後編譯執行看看,應該可以站在 mini-riscv-os 的基礎上,進一步透過 xv6-riscv 學習更進階的作業系統設計原理。
|
||||
|
||||
* https://github.com/ccc-c/xv6-riscv-win
|
||||
- https://github.com/ccc-c/xv6-riscv-win
|
||||
|
||||
以下提供更多關於 RISC-V 的學習資源,以方便大家在學習 RISC-V 作業系統設計時,不需再經過太多的摸索。
|
||||
|
||||
* [RISC-V 手册 - 一本开源指令集的指南 (PDF)](http://crva.ict.ac.cn/documents/RISC-V-Reader-Chinese-v2p1.pdf)
|
||||
* [The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture (PDF)](https://riscv.org//wp-content/uploads/2019/12/riscv-spec-20191213.pdf)
|
||||
* [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md)
|
||||
* https://github.com/riscv/riscv-opcodes
|
||||
* https://github.com/riscv/riscv-opcodes/blob/master/opcodes-rv32i
|
||||
* [SiFive Interrupt Cookbook (SiFive 的 RISC-V 中斷手冊)](https://gitlab.com/ccc109/sp/-/blob/master/10-riscv/mybook/riscv-interrupt/sifive-interrupt-cookbook-zh.md)
|
||||
* [SiFive Interrupt Cookbook -- Version 1.0 (PDF)](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf)
|
||||
* 進階: [proposal for a RISC-V Core-Local Interrupt Controller (CLIC)](https://github.com/riscv/riscv-fast-interrupt/blob/master/clic.adoc)
|
||||
- [RISC-V 手册 - 一本开源指令集的指南 (PDF)](http://crva.ict.ac.cn/documents/RISC-V-Reader-Chinese-v2p1.pdf)
|
||||
- [The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture (PDF)](https://riscv.org//wp-content/uploads/2019/12/riscv-spec-20191213.pdf)
|
||||
- [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md)
|
||||
- https://github.com/riscv/riscv-opcodes
|
||||
- https://github.com/riscv/riscv-opcodes/blob/master/opcodes-rv32i
|
||||
- [SiFive Interrupt Cookbook (SiFive 的 RISC-V 中斷手冊)](https://gitlab.com/ccc109/sp/-/blob/master/10-riscv/mybook/riscv-interrupt/sifive-interrupt-cookbook-zh.md)
|
||||
- [SiFive Interrupt Cookbook -- Version 1.0 (PDF)](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf)
|
||||
- 進階: [proposal for a RISC-V Core-Local Interrupt Controller (CLIC)](https://github.com/riscv/riscv-fast-interrupt/blob/master/clic.adoc)
|
||||
|
||||
希望這份 mini-riscv-os 教材能幫助讀者在學習 RISC-V OS 設計上節省一些寶貴的時間!
|
||||
|
||||
陳鍾誠 2020/11/15 於金門大學
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user