本系列參考: 學習開發(fā)一個RISC-V上的操作系統(tǒng) - 汪辰 - 2021春 整理而來,主要作為xv6操作系統(tǒng)學習的一個前置基礎。
RVOS是本課程基于RISC-V搭建的簡易操作系統(tǒng)名稱。
課程代碼和環(huán)境搭建教程參考github倉庫: https://github.com/plctlab/riscv-operating-system-mooc/blob/main/howto-run-with-ubuntu1804_zh.md
前置知識:
- RVOS環(huán)境搭建-01
- RVOS操作系統(tǒng)內存管理簡單實現(xiàn)-02
- RVOS操作系統(tǒng)協(xié)作式多任務切換實現(xiàn)-03
- RISC-V 學習篇之特權架構下的中斷異常處理
- 從零手寫操作系統(tǒng)之RVOS外設中斷實現(xiàn)-04
RISC-V 定時器中斷
定時器中斷屬于本地中斷的一類:
Core Local INTerrupt:
Core Local Interrupt(CLINT)是一個與處理器核心相關的中斷控制器,它負責處理特定核心的計時器中斷和軟件中斷。
CLINT位于RISC-V系統(tǒng)中的物理內存地址空間,它是一個全局共享的設備,被所有的處理器核心共享和訪問。CLINT的作用是為每個處理器核心提供計時器中斷和軟件中斷的控制。
CLINT通常具有以下功能和組成部分:
-
Timer Interrupts(計時器中斷):CLINT包含一個或多個計時器,用于生成定時中斷。每個計時器都與特定的處理器核心關聯(lián),當計時器計數(shù)達到預設的值時,CLINT會生成一個中斷信號,通知相應的處理器核心。
-
Software Interrupts(軟件中斷):CLINT可以接收來自處理器核心的軟件中斷請求。軟件中斷是由軟件代碼觸發(fā)的一種中斷請求,用于實現(xiàn)系統(tǒng)調用、任務切換和異常處理等功能。
-
中斷控制寄存器(Interrupt Control Registers):CLINT包含一組用于配置和控制中斷的寄存器,包括計時器設置寄存器、中斷使能寄存器、中斷優(yōu)先級寄存器等。這些寄存器用于配置中斷參數(shù)、使能或禁用中斷,并設置中斷的優(yōu)先級。
總而言之,CLINT是一個處理器核心本地的中斷控制器,它提供定時器中斷和軟件中斷的功能,并通過相關的寄存器進行配置和控制。每個處理器核心都可以訪問和配置CLINT,以實現(xiàn)對中斷的管理和處理。
RISC-V CLINT 介紹
寄存器 (Timer 部分)
全局唯一,表示即使存在多個核,也只會存在一個mtime寄存器
mtime
是RISC-V架構中的一個特殊寄存器,用于表示機器模式下的計時器值。它是Machine Timer(機器計時器)的縮寫。
mtime
寄存器通常由硬件提供,用于跟蹤系統(tǒng)運行的時間。它的值會不斷增加,可以用于測量程序的執(zhí)行時間、進行時間相關的操作和調度等。
在RISC-V中,mtime
寄存器是一個64位的寄存器,可用于測量長時間間隔,通常以時鐘周期或計時器滴答數(shù)的形式表示。它的精度和計時精度取決于硬件實現(xiàn)和操作系統(tǒng)的支持。
在操作系統(tǒng)或應用程序中,可以使用mtime
寄存器來實現(xiàn)計時器、延時函數(shù)、性能統(tǒng)計等功能。通過讀取mtime
寄存器的值,可以獲得當前的計時器數(shù)值,進而進行時間計算和處理。
需要注意的是,訪問mtime
寄存器通常需要特權級別的權限。在特權級別較低的用戶態(tài),可能無法直接讀取或寫入mtime
寄存器,需要通過系統(tǒng)調用或特權級別切換來訪問。具體的訪問權限和操作方式取決于系統(tǒng)的實現(xiàn)和配置。
mtime
寄存器的遞增原理是由硬件實現(xiàn)確定的,通常是由時鐘或計時器驅動的。在一個基于時鐘的系統(tǒng)中,系統(tǒng)時鐘會以固定的頻率進行振蕩,產生一個穩(wěn)定的時鐘信號。這個時鐘信號會被用作各種硬件模塊和功能的時序控制。
mtime
寄存器會根據(jù)系統(tǒng)時鐘信號的脈沖進行遞增。每當一個時鐘脈沖到達,mtime
寄存器的值會自動加1。這樣,隨著時鐘信號的不斷變化,mtime
寄存器的值也會不斷地增加。遞增速度取決于時鐘的頻率。如果系統(tǒng)時鐘頻率為1 MHz,那么每秒鐘
mtime
寄存器的值就會增加1000000。因此,可以根據(jù)mtime
寄存器的遞增速度來進行時間計算和測量。需要注意的是,
mtime
寄存器的遞增是硬件自動完成的,無法通過軟件或程序直接控制。程序可以通過讀取mtime
寄存器的值來獲取當前的計時器數(shù)值,但無法直接修改或控制其遞增過程。遞增過程是由硬件實現(xiàn)和時鐘信號控制的,程序只能觀察和利用其遞增的結果。
mtimecmp
寄存器是RISC-V架構中的一個定時器比較寄存器(Timer Compare Register)。它用于與mtime
寄存器進行比較,以實現(xiàn)定時器中斷的觸發(fā)。
當mtime
寄存器的值與mtimecmp
寄存器的值相等時,會觸發(fā)一個定時器中斷。這種機制允許程序根據(jù)需要設置定時器中斷的觸發(fā)時機。
具體而言,程序可以通過向mtimecmp
寄存器寫入一個比較值,來指定何時觸發(fā)定時器中斷。當mtime
寄存器的值達到或超過這個比較值時,定時器中斷被觸發(fā),執(zhí)行相應的中斷處理程序。
通過使用mtimecmp
寄存器,程序可以實現(xiàn)定時器相關的功能,如定時任務調度、時間片輪轉調度、精確延時等。它為程序提供了一種基于時間的觸發(fā)機制,使得程序能夠按照預定的時間間隔執(zhí)行特定的操作。
需要注意的是,具體的定時器中斷觸發(fā)機制和中斷處理程序的實現(xiàn)方式可能會有所不同,取決于具體的處理器和操作系統(tǒng)。程序需要根據(jù)所使用的平臺和系統(tǒng)進行相應的配置和編程。
硬件定時器初始化代碼如下:
/* 10000000 ticks per-second */
#define CLINT_TIMEBASE_FREQ 10000000
/* interval ~= 1s */
#define TIMER_INTERVAL CLINT_TIMEBASE_FREQ
void timer_init()
{
/*
* On reset, mtime is cleared to zero, but the mtimecmp registers
* are not reset. So we have to init the mtimecmp manually.
*/
//定時器模塊初始化---傳入interval間隔大約為1s
timer_load(TIMER_INTERVAL);
/* enable machine-mode timer interrupts. */
//開啟次級中斷中的定時器中斷
w_mie(r_mie() | MIE_MTIE);
/* enable machine-mode global interrupts. */
//開啟全局中斷
w_mstatus(r_mstatus() | MSTATUS_MIE);
}
/* load timer interval(in ticks) for next timer interrupt.*/
void timer_load(int interval)
{
/* each CPU has a separate source of timer interrupts. */
//獲取當前hartId
int id = r_mhartid();
//設置mtimecmp寄存器的值為mtime寄存器的值+interval
*(uint64_t*)CLINT_MTIMECMP(id) = *(uint64_t*)CLINT_MTIME + interval;
}
經過如上設置后,大約1秒后,會觸發(fā)一次時鐘中斷。
當mtime
中斷發(fā)生時,處理器核心(hart)會設置mip
寄存器的MTIP
位,表示發(fā)生了定時器中斷。
在處理定時器中斷時,通常需要在mtimecmp
寄存器中寫入新的值以清除mip.MTIP
位。具體的操作步驟如下:
- 響應定時器中斷,進入中斷處理程序。
- 在中斷處理程序中,讀取
mtime
寄存器的當前值,可以使用類似于uint64_t curr_time = r_mtime();
的方式獲取。 - 根據(jù)需要,計算下一個定時器中斷應該發(fā)生的時間,得到一個新的比較值。
- 將新的比較值寫入
mtimecmp
寄存器,以設置下一個定時器中斷的觸發(fā)時刻。 - 清除
mip
寄存器的MTIP
位,以告知處理器中斷已經處理完畢??梢允褂妙愃朴?code>w_mip(r_mip() & ~MIP_MTIP);的方式清除。
通過在中斷處理程序中更新mtimecmp
寄存器,程序可以實現(xiàn)周期性的定時器中斷,不斷觸發(fā)指定時間間隔的操作。同時,清除mip.MTIP
位可以確保處理器核心在中斷處理程序執(zhí)行完畢后正確地處理下一個定時器中斷。
需要注意的是,具體的操作方式可能因處理器和操作系統(tǒng)的不同而有所差異。因此,以上描述僅為一般情況下的操作流程,具體的實現(xiàn)方式需要參考所使用的平臺和系統(tǒng)的文檔或相關編程接口。
我們需要在trap_handler處理函數(shù)中新增對定時器中斷的處理:
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:
uart_puts("software interruption!\n");
break;
//新增對定時器中斷的處理
case 7:
uart_puts("timer interruption!\n");
timer_handler();
break;
case 11:
uart_puts("external interruption!\n");
external_interrupt_handler();
break;
default:
uart_puts("unknown async exception!\n");
break;
}
} else {
/* Synchronous trap - exception */
printf("Sync exceptions!, code = %d\n", cause_code);
panic("OOPS! What can I do!");
//return_pc += 4;
}
return return_pc;
}
- 定時器中斷具體處理函數(shù)
void timer_handler()
{
//記錄定時器中斷觸發(fā)次數(shù)
_tick++;
printf("tick: %d\n", _tick);
//設置下一次定時器中斷觸發(fā)時機
timer_load(TIMER_INTERVAL);
}
總體框架流程
硬件定時器的應用
時間管理
/* interval ~= 1s */
#define TIMER_INTERVAL CLINT_TIMEBASE_FREQ
static uint32_t _tick = 0;
測試
我們這里簡單測試一下定時器中斷運行效果:文章來源:http://www.zghlxwxcb.cn/news/detail-475881.html
void start_kernel(void)
{
uart_init();
uart_puts("Hello, RVOS!\n");
page_init();
trap_init();
plic_init();
//初始化定時器模塊
timer_init();
sched_init();
os_main();
schedule();
uart_puts("Would not go here!\n");
while (1) {}; // stop here!
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-475881.html
到了這里,關于從零手寫操作系統(tǒng)之RVOS硬件定時器-05的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!