# <center>作業系統工程 12-1作業<br><font size=5>611121212 李奕承</font></center> 建立標頭檔和相關巨集 ```clike #ifndef __HWTIMER_H_ #define __HWTIMER_H_ //每次發生時間中斷的間隔 #define TIMER_INTERVAL 10000000 //存取 CLINT 暫存器的基址 #define CLINT_BASE 0x2000000L //存取 CLINT 中時鐘暫存器的地址 #define CLINT_MTIME (CLINT_BASE + 0xBFF8) //存取 CLINT 中用來比較時間的暫存器的地址 #define CLINT_MTIMECMP(hartid) (CLINT_BASE + 0x4000 + 8 * (hartid)) #endif ``` 在總巨集 os.h 中引用 ```clike #include"hwtimer.h" ``` 確保 riscv.h 中有以下幾個巨集 ```clike //讀取當前 hart 的編號 static inline reg_t r_mhartid() { reg_t x; asm volatile("csrr %0, mhartid" : "=r" (x)); return x; } //寫入 mie 暫存器 static inline void w_mie(reg_t x) { asm volatile("csrw mie, %0" : : "r" (x)); } //寫入 mstatus 暫存器 static inline void w_mstatus(reg_t x) { asm volatile("csrw mstatus, %0" : : "r" (x)); } ``` 接著編寫hwtimer.c ```clike #include"os.h" //將比較暫存器 CLINT_MTIMECMP 設置為下次發生中斷的時間 void timer_load(int interval) { int id = r_mhartid(); *(uint64_t *)CLINT_MTIMECMP(id) = *(uint64_t *)CLINT_MTIME + interval; } //初始化下次發生時間中斷的時間,打開時間中斷,打開全域中斷 void timer_init(void) { timer_load(TIMER_INTERVAL); w_mie(r_mie() | MIE_MTIE); w_mstatus(r_mstatus() | MSTATUS_MIE); } //宣告紀錄時間的參數 int seconds = 0; int mintues = 0; int hours = 0; //時間進位的邏輯 void tick() { seconds++; if(seconds == 60) { mintues++; seconds = 0; if(mintues == 60) { hours++; mintues = 0; if(hours == 24) { hours = 0; } } } } //顯示時間的函數 void clock() { if(hours < 10) {printf("0%d : ", hours);}else{printf("%d : ", hours);} if(mintues < 10) {printf("0%d : ", mintues);}else{printf("%d : ", mintues);} if(seconds < 10) {printf("0%d ", seconds);}else{printf("%d ", seconds);} printf("\n"); } //時間中斷處理函數,這裡每一次都加1秒,並且更新下次要發生的時間 void timer_handle(void) { tick(); timer_load(TIMER_INTERVAL); } ``` 將其他 .c 檔會用到的函數放到 os.h 中公開,這裡我加在 #include"hwtimer.h" 這行後面 ```c #include"hwtimer.h" void timer_init(void); void timer_handle(void); void clock(void); void timer_load(int interval); ``` 將 timer_handle() 放入中斷處理函數中 ```clike reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc = epc; reg_t cause_code = cause & 0xfff; if(cause & 0x80000000) { switch (cause_code) { case 3: uart_puts("software interruption!\n"); break; case 7: //這裡 timer_handle(); break; case 11: external_interrupt_handler(); break; default: uart_puts("unknown async exception!\n"); break; } } else { printf("Sync exceptions!, code = %d\n", cause_code); uart_puts("OOPS! What can I do!\n"); uart_puts("next time, this task will start at pc where exeception next \n"); ((pcb_t *)current)->error = 1; return return_pc += 4; } return return_pc; } ``` 在你想顯示時間的 task中 加入clock() 函數,這裡我加在 schedule() 中的 PCB 顯示的後面 終端機的畫面 ![](https://i.imgur.com/uhOVs3W.png) 每進一次 schedule() 就會顯示最新的時間 ![](https://i.imgur.com/MHT4yvg.png)