# <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 顯示的後面
終端機的畫面

每進一次 schedule() 就會顯示最新的時間
