opensbi下的riscv64裸機(jī)編程2(中斷與異常)
1.本文說(shuō)明
2.riscv特權(quán)模式下的異常
2.1 CSPs
2.2 異常開關(guān)的寄存器
2.3 與中斷相關(guān)的指令
3.中斷測(cè)試
3.1 設(shè)置中斷向量表
3.2 開啟中斷設(shè)置
3.3 初始化timer
3.4 開啟中斷
3.5 中斷處理
4.測(cè)試及校驗(yàn)
5.總結(jié)
1.本文說(shuō)明
任何時(shí)候,中斷和異常的產(chǎn)生都是十分值得關(guān)注的,這些將破壞程序原有的執(zhí)行邏輯。按照芯片的設(shè)計(jì)來(lái)說(shuō),中斷和異常大致上可以分為三類異常(Exception)、陷入(Trap)、外部中斷(Interrupt)。
異常(Exception)
在一條指令執(zhí)行的過程中發(fā)生了錯(cuò)誤,可以通過異常處理函數(shù)進(jìn)行處理,最常見的異常包括無(wú)效的內(nèi)存地址訪問、非法指令異常、缺頁(yè)異常等等。當(dāng)發(fā)生這些異常后可以進(jìn)行處理。
陷入(Trap)
主動(dòng)的讓其進(jìn)入異常處理函數(shù),常見的是系統(tǒng)調(diào)用syscall。而在riscv上為ecall或者進(jìn)入斷點(diǎn)的ebreak。
外部中斷(Interrupt)
一般由外部事件觸發(fā),比如定時(shí)器中斷、GPIO中斷等。這些異常是不可預(yù)知的。
對(duì)于一般的中斷處理流程,進(jìn)入中斷后需要進(jìn)行上下文的保存與恢復(fù)。
2.riscv特權(quán)模式下的異常
涉及到中斷和異常,RISCV的特權(quán)模式是不能繞開的。在RISCV中,無(wú)論在任何模式發(fā)生的異常,硬件線程都會(huì)將控制權(quán)交給M-Mode的異常處理程序。然而對(duì)于類Unix的操作系統(tǒng)來(lái)說(shuō),異常都是由操作系統(tǒng)來(lái)處理。而操著系統(tǒng)運(yùn)行的模式是S-Mode,所以RISCV也可以選擇將異常重新導(dǎo)向到S-Mode,也支持異常委托機(jī)制(Machine Interrupt Delegaintion)將異常直接通過S-Mode進(jìn)行處理,這樣可以大大的增加操作系統(tǒng)的靈活性。

一般來(lái)說(shuō)M-Mode是必須實(shí)現(xiàn)的,S-Mode也一般會(huì)有,而U-Mode是選擇性擴(kuò)展的。目前的RISCV芯片中例如蜂鳥的E203與K210都只支持了RISCV架構(gòu)中的Machine Mode。
2.1 CSPs
實(shí)際上RISCV在實(shí)現(xiàn)系統(tǒng)指令集的時(shí)候,是支持多種模式的擴(kuò)展的,這一系列的指令集通過Control and Status Registers (CSRs)來(lái)進(jìn)行控制。
CSR地址是擴(kuò)展了12位,也就是可以設(shè)計(jì)最大4096個(gè)指令。

通過下面的網(wǎng)站可以看到當(dāng)前CSRs的實(shí)現(xiàn)狀態(tài)。
http://www.five-embeddev.com/quickref/csrs.html
這里只針對(duì)S-Mode下的異常處理進(jìn)行分析,M-Mode下的異常處理類似。
| Name | Number | Feature/Extensions | Description |
|---|---|---|---|
| sepc | 0x0141 | supervisor | Supervisor Exception Program Counter |
| scause | 0x0142 | supervisor | Supervisor Exception Cause |
| stval | 0x0143 | supervisor | Supervisor bad address or instruction. |
| stvec | 0x0105 | supervisor | Supervisor Trap Vector Base Address |
| sstatus | 0x0100 | supervisor | Supervisor Status |
Supervisor Exception Program Counter (sepc)
當(dāng)中斷發(fā)生時(shí),存放需要跳轉(zhuǎn)的PC值。這里需要利用stvec提供中斷向量表的基地址。

該寄存器的值是在32位下是4字節(jié)對(duì)齊的。
Supervisor Cause Register (scause)
該寄存器表示中斷發(fā)生的原因。下面的表格中表述了中斷的發(fā)生原因:
| Interrupt | Exception Code | Description |
|---|---|---|
| 1 | 0 | Reserved |
| 1 | 1 | Supervisor software interrupt |
| 1 | 2–4 | Reserved |
| 1 | 5 | Supervisor timer interrupt |
| 1 | 6–8 | Reserved |
| 1 | 9 | Supervisor external interrupt |
| 1 | 10–15 | Reserved |
| 1 | ≥16 | Designated for platform use |
| 0 | 0 | Instruction address misaligned |
| 0 | 1 | Instruction access fault |
| 0 | 2 | Illegal instruction |
| 0 | 3 | Breakpoint |
| 0 | 4 | Load address misaligned |
| 0 | 5 | Load access fault |
| 0 | 6 | Store/AMO address misaligned |
| 0 | 7 | Store/AMO access fault |
| 0 | 8 | Environment call from U-mode |
| 0 | 9 | Environment call from S-mode |
| 0 | 10–11 | Reserved |
| 0 | 12 | Instruction page fault |
| 0 | 13 | Load page fault |
| 0 | 14 | Reserved |
| 0 | 15 | Store/AMO page fault |
| 0 | 16–23 | Reserved |
| 0 | 24–31 | Designated for custom use |
| 0 | 32–47 | Reserved |
| 0 | 48–63 | Designated for custom use |
| 0 | ≥64 | Reserved |
Supervisor Trap Value (stval) Register
由于scause不足以表示異常發(fā)生的所有信息,比如發(fā)生了缺頁(yè)異常,就會(huì)將stavl設(shè)置成需要訪問但是不在內(nèi)存中的地址。以便于操作系統(tǒng)將這個(gè)地址加載進(jìn)來(lái)。
Supervisor Trap Vector Base Address Register (stvec)
設(shè)置中斷處理的基地址,同時(shí)設(shè)置模式

對(duì)于基地址的模式有如下兩種:
| Value | Name | Description |
|---|---|---|
| 0 | Direct | All exceptions set pc to BASE. |
| 1 | Vectored | Asynchronous interrupts set pc to BASE+4×cause. |
| ≥2 | — | Reserved |
Direct:顧名思義,當(dāng)異常發(fā)生的時(shí)候,每次都會(huì)跳轉(zhuǎn)到這個(gè)地址,然后通過這個(gè)地址的中斷處理程序去判斷哪種中斷。
Vectored:在這種模式下,會(huì)跳轉(zhuǎn)到BASE + 4 * cause 進(jìn)行處理流程。每種異常的cause都不一樣。
Supervisor Status Register (sstatus)
控制中斷的狀態(tài)等等,也可以控制全局中斷的時(shí)能等等。

SIE域表示全局中斷使能。當(dāng)該MIE域值為1時(shí),表示所有中斷的全局開關(guān)打開,當(dāng)MIE域的值為0時(shí)候,表示全局關(guān)閉所有中斷。
SPIE用于保存進(jìn)入異常之前MIE域的值。
2.2 異常開關(guān)的寄存器
對(duì)于S-Mode中斷的Enable與Pending,還需要關(guān)注兩個(gè)寄存器。sie與sip。
Supervisor Interrupt Enable(sie)

Supervisor Interrupt Pending(sip)

可以看到有三種類型的中斷,由芯片廠家進(jìn)行自定義設(shè)計(jì)。
Supervisor software interrupt
Supervisor timer interrupt
Supervisor external interrupt
2.3 與中斷相關(guān)的指令
CSR Read Write(csrrw)
csrrw dst, csr, src:將指定的CSR寄存器寫入dst,同時(shí)將src的值寫入CSR。
CSR Read(csrr)
csrr dst,csr:讀一個(gè)CSR寄存器到dst。
CSR Clear(csrc)
csrc(i) csr, rs1:將指定的位清零。
CSR Set(csrs)
csrs(i) csr, rs1:將指定的位置一。
3.中斷測(cè)試
由于在qemu上,中斷的產(chǎn)生可以通過定時(shí)器來(lái)發(fā)生,所以需要理解riscv上對(duì)timer的使用。timer又需要通過sbi的接口進(jìn)行訪問。
相關(guān)的代碼文件可以參考:
https://github.com/bigmagic123/riscv64_opensbi_baremetal/tree/master/03_interrupt
已經(jīng)實(shí)現(xiàn)了timer中斷的產(chǎn)生過程。
3.1 設(shè)置中斷向量表
本程序需要設(shè)置中斷向量表,前面提到過,中斷向量的跳轉(zhuǎn)有兩種模式:Direct與Vectored。Direct可以直接轉(zhuǎn)到固定的pc地址,然后由統(tǒng)一的入口進(jìn)行處理,這種比較容易實(shí)現(xiàn),所以設(shè)置為這種模式。
.globaltable_val_set table_val_set: lat0,trap_entry csrwstvec,t0 jrra
直接將trap_entry函數(shù)的入口寫到stvec的寄存器中。由于函數(shù)地址4字節(jié)對(duì)其,所以設(shè)置后模式為Direct。
3.2 開啟中斷設(shè)置
要開啟時(shí)鐘中斷,這樣才能產(chǎn)生時(shí)鐘,而根據(jù)手冊(cè),開啟時(shí)鐘中斷實(shí)際上是設(shè)置Supervisor Interrupt Enable(sie),也就是設(shè)置SIE的寄存器開啟。

所以只需要設(shè)置即可。
voidenable_timer_interrupt(void) { w_sie(r_sie()|SIE_STIE); }
3.3 初始化timer
對(duì)于timer的填充,其實(shí)就是設(shè)置中斷的值。當(dāng)timer達(dá)到設(shè)定的值后會(huì)產(chǎn)生中斷。
voidset_timer(uint64stime_value) { SBI_TIMER(stime_value); } //getcurrenttime uint64get_cycle() { returnr_time(); } voidclock_set_next_event() { set_timer(get_cycle()+TIMEBASE); }
函數(shù)填充了下一個(gè)tick的值。
3.4 開啟中斷
中斷的開啟通過sstatus全局的狀態(tài)寄存器設(shè)置。

通過設(shè)置SIE位就可以達(dá)到使能或者關(guān)閉中斷的作用。
voidinterrupt_enable(void) { w_sstatus(r_sstatus()|SSTATUS_SIE); }
3.5 中斷處理
中斷處理需要保存當(dāng)前的上下文寄存器(寄存器壓棧操作),然后跳轉(zhuǎn)到中斷處理函數(shù)去處理具體的中斷。當(dāng)處理完成之后返回現(xiàn)場(chǎng)(寄存器出棧)。
這里先不做這么復(fù)雜的工作,中斷產(chǎn)生后直接跳轉(zhuǎn)到中斷處理函數(shù)中,只執(zhí)行一次。
.globaltrap_entry trap_entry: csrra0,scause csrrca1,stval,zero csrra2,sepc mva3,s0 /*scause,stval,sepc,sp*/ callhandle_trap
其中a0為第一個(gè)參數(shù),保存中斷發(fā)生的原因。
a1是中斷發(fā)生的具體信息。
a2表示了中斷異常返回值。
然后進(jìn)入hande_trap。
uintptr_thandle_trap(uintptr_tscause,uintptr_tstval,uintptr_tsepc,uintptr_tsp) { tfp_printf("handle_trap%08lx:%08lx:%08lx:%08lx ",scause,stval,sepc,sp); while(1); return0; }
4.測(cè)試及校驗(yàn)
因?yàn)楣こ涛募脑黾?,所以使用了Makefile進(jìn)行工程的構(gòu)建工作。
%.o:%.c%.s $(CC)$(CFLAGS)-c$-o?$@
Makefile的語(yǔ)法規(guī)則基本
TARGET…:DEPENDENCIES… COMMAND
這里也不過多的涉及了。
輸入make后,在fw_bin目錄下執(zhí)行run.sh腳本即可。
最后可以看到中斷的原因
最高位是8,相應(yīng)的中斷描述為Supervisor timer interrupt。
5.總結(jié)
riscv的異常和中斷的處理模式在M-Mode或者S-Mode下都可以設(shè)計(jì),具體要看芯片的設(shè)計(jì)方式,如果設(shè)計(jì)在M-Mode,對(duì)于操作系統(tǒng)來(lái)說(shuō),可以通過轉(zhuǎn)發(fā)或者代理給S-Mode的操作系統(tǒng),如果S-Mode存在中斷處理,那么處于S-Mode的系統(tǒng)可以直接處理,這樣比較簡(jiǎn)潔。
責(zé)任編輯:xj
原文標(biāo)題:opensbi下的riscv64裸機(jī)編程2(中斷與異常)
文章出處:【微信公眾號(hào):嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
編程
+關(guān)注
關(guān)注
90文章
3724瀏覽量
97459 -
裸機(jī)
+關(guān)注
關(guān)注
0文章
42瀏覽量
6990 -
RISC-V
+關(guān)注
關(guān)注
49文章
2954瀏覽量
53607
原文標(biāo)題:opensbi下的riscv64裸機(jī)編程2(中斷與異常)
文章出處:【微信號(hào):Embeded_IoT,微信公眾號(hào):嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
尋找對(duì)RISCV眾核并行計(jì)算感興趣的伙伴、朋友
尋找對(duì)RISCV眾核并行計(jì)算感興趣的伙伴
在圖像上,Debian 無(wú)法為 wifi 構(gòu)建驅(qū)動(dòng)程序,怎么解決?
如何升級(jí) GLIBC 以避免錯(cuò)誤“找不到 GLIBC_2.34”?
如何在 VF2 上玩 Minecraft 的分步指南
debian-202308 映像不支持 firmware-realtek 包 riscv64嗎?
深入剖析ARM64異常處理:開發(fā)者必須掌握的底層核心邏輯
opensbi下的riscv64裸機(jī)編程:中斷與異常
評(píng)論