- 論壇徽章:
- 0
|
第四章 MIPS 異常和中斷處理
MIPS 異常和中斷處理(Exception and Interrupt handling)
任何一個(gè)CPU都要提供一個(gè)詳細(xì)的異常和中斷處理機(jī)制。一個(gè)軟件系統(tǒng),如操作系統(tǒng),就是一個(gè)時(shí)序邏輯系統(tǒng),通過時(shí)鐘,外部事件來驅(qū)動(dòng)整個(gè)預(yù)先定義好的邏輯行為。這也是為什么當(dāng)寫一個(gè)操作系統(tǒng)時(shí)如何定義時(shí)間的計(jì)算是非常重要的原因。
大家都非常清楚UNIX提供了一整套系統(tǒng)調(diào)用(System Call)。系統(tǒng)調(diào)用其實(shí)就是一段EXCEPTION處理程序。
我們可能要問:為什么CPU要提供Excpetion 和 Interrupt Handling呢?
*處理illegal behavior, 例如,TLB Fault, or, we say, the Page fault; Cache Error;
*
Provide an approach for accessing priviledged resources, for example,
CP0 registers. As we know, for user level tasks/processes, they are
running with the User Mode priviledge and are prohibilited to
directly control CPO. CPU need provide a mechanism for them to trap to
kernel mode and then safely manipulate resources that are only available
when CPU runs in kernel mode.
* Provide handling for
external/internal interrupts. For instance, the timer interrupts and
watch dog exceptions. Those two interrupt/exceptions are very important
for an embedded system applicances.
Now let's get back to how MIPS supports its exception and interrupt handling.
For simplicty, all information below will be based on R7K CPU, which is derived from the R4k family.
*
The first thing for understanding MIPS exception handling is: MIPS
adopts **Precise Exceptions** mechanisms. What that means? Here is the
explaination from the book of "See MIPS Run": "In a precise-exception
CPU, on any exception we get pointed at one instruction(the exception
victim). All instructions preceding the exception victim in execution sequence are complete; any work done on the victim and on any subsequent instructions (BNN
NOTE: pipeline effects) has no side effects that the software need
worry about. The software that handles exceptions can ignore all the
timing effects of the CPU's implementations"
上面的意思其實(shí)很簡(jiǎn)單:在發(fā)生EXCEPTION之前的一切計(jì)算行為會(huì)**FINISH**。在發(fā)生EXCEPTION之后的一切計(jì)算行為將不需考慮。
對(duì)絕大多數(shù)情況而言,如你要寫一個(gè)系統(tǒng)調(diào)用(System Call),你只要記。 MIPS已經(jīng)把syscall這條指令的地址壓在了EPC寄存器里。換句話說,在MIPS里,compard to the PowerPC CPU srr1 register, 你需要**explicitely** refill the EPC register by EPC<-----EPC+4, before you use the eret中斷返回。只有這樣,你才能從系統(tǒng)調(diào)用中正確返回。
異常/中斷向量(Exception/Interrupt Vector)
MIPS 的Exception/Interrupt Vector的organizaion is not as good as PowerPC CPUs.
For
PPC, every detailed exception cause is directed to a unqiue vector
address. MIPS is otherwise. Below is a recap of MIPS exception/interrupt
vectors. (We herein only talk about running MIPS CPU in the 32 bit mode ) Reset, NMI 0x8000 0000 TLB refill 0x8000 0000 Cache
Error 0xA000 00100 (BNN: Why goes to 0xAxxxxx? A question to readers.
Please think about the difference between Kseg0 and kseg1) All other exceptions 0x8000 0180
How MIPS acts when taking an exception?
1. It sets up the EPC to point to the restart location. 2. CPU changes into kernel mode and disables the interrupts (BNN: MIPS does this by setting EXL bit of SR register) 3.
Set up the Cause register to indicate which is wrong. So that software
can tell the reason for the exception. If it is for address exception,
for example, TLB miss and so on, the BadVaddr register is also set. 4. CPU starts fetching instructions from the exception entry point and then goes to the exception handler.
Returning from exceptions
Up
to MIPS III, we use the eret instruciton to return to the original
location before falling into the exception. Note that eret behavior is:
clear the SR[EXL] bit and returns control to the adress stored in EPC.
An important bit in SR for interrupt handling
SR[IE]: This bit is used to enable/disable interrupts,, including the timer interrupts. Of couse, when the SR[EXL] bit is set, this bit has no effects.
K0 and K1 registers:
These
two registers are mostly used by kernel as a temporary buffer to hold
some values if necessary. So that you don't have to find some
pre-defined memories for that purpose.
One thing we should
be careful is : When you are allowing the nested exception/interrupt
handling, you need take care of these two registers' values as they will be over-written, for example.
I
don't encouarge people to use the AT register too often, even though
you can use the .set noat directive. I have found a bug in mips-gcc,
which will use the AT register anyway, even after we use the .set noat.
In other wrods, using AT is dangeous somhow if you are not quite
familire with the register convention/usage
流水線(Pipeline) and Interrupt Taken
我們知道,MIPS是一個(gè)RISC技術(shù)處理器。在某一個(gè)時(shí)刻,在流水線上,同時(shí)有若干個(gè)指令被處理在不同的階段(stage)上.
MIPS處理器一般采用5級(jí)流水結(jié)構(gòu)。
IF RD ALU MEM WB
那么我們要問:當(dāng)一個(gè)Interrupt發(fā)生時(shí),CPU到底該 如何handle?答案是這樣的:
“On an interrupt in a typical MIPS CPU, the last instruction to be completed before interrupt processing
starts will be the one that has just finished its MEM stage when the
interrupt is detected. The exception victim will be the one that has
just finished its ALU stage..."
對(duì)上述的理解是這樣的:CPU 會(huì)**完成**那條已**finish** MEM stage的指令。然后將exception victim定位在下一條(following)指令上。要注意的是:我們是在談Interrupt, not the exception. 在MIPS中,這是有區(qū)別的。
下面介紹幾個(gè)重要的SR(Status Register)與Exception和中斷有關(guān)的位。
* SR[EXL] Exception
Level; set by the processor when any exception other than Reset, Soft
Reset, NMI, or Cache Error exception are taken. 0: normal 1: exception
When EXL is set: - Interrupts are disabled. 換句話說,這時(shí)SR[IE]位是不管用了,相當(dāng)于所有的中斷都被MASK了。 - TLB refill exceptions will use the general exception vector instead of the TLB refill vector. - EPC is not updated if another exception is taken. 這一點(diǎn)要注意。如果我們想支持nesting exceptions, 我們要在exception hander中clear EXL bit.當(dāng)然要先保存EPC的值。另外要注意的:MIPS當(dāng)陷入Exception/Interrupt時(shí),并不改變SR[UX],SR[KX]或SR[SX]的值。SR[EXL]為1自動(dòng)的將CPU mode運(yùn)行在KERNEL模式下。這一點(diǎn)要注意。
* SR[ERL] Error Level; set by the processor when Reset, Soft Reset, NMI, or Cache Error exception are taken. 0: normal 1: error When ERL is set: - Interrupts are disabled. - The ERET instruction uses the return address held in ErrorEPC instead of EPC. -
Kuseg and xkuseg are treated as unmapped and uncached regions.This
allows main memory to be accessed in the presence of cache errors. 這時(shí)刻,我們可以說,MIPS CPU只有在這個(gè)時(shí)刻才是一種**實(shí)模式(real mode)**.
* SR[IE] Interrupt Enable 0: disable interrupts 1: enable interrupts。請(qǐng)記。寒(dāng)SR[EXL]或SR[ERL]被SET時(shí), SR[IE]是無效的。
* Exception/Interrupt優(yōu)先級(jí)。
Reset (highest priority) Soft Reset Nonmaskable Interrupt (NMI) Address error --Instruction fetch TLB refill--Instruction fetch TLB invalid--Instruction fetch Cache error --Instruction fetch Bus error --Instruction fetch Watch - Instruction Fetch Integer
overflow, Trap, System Call, Breakpoint, Reserved Instruction,
Coprocessor Unus-able, or Floating-Point Exception Address error--Data
access TLB refill --Data access TLB invalid --Data access TLB modified--Data write Cache error --Data access Watch - Data access Virtual Coherency - Data access Bus error -- Data access Interrupt (lowest priority)
大家請(qǐng)注意,所謂的優(yōu)先級(jí)是指:當(dāng)在某個(gè)時(shí)刻,同時(shí)多個(gè)Exception或Interrupt出現(xiàn)時(shí),CPU將會(huì)按照上述的優(yōu)先級(jí)來TAKE。如果CPU目前在,for example,TLB refill處理handler中,這時(shí),出現(xiàn)了Bus Error的信號(hào),CPU不會(huì)拒絕。當(dāng)然,在這次的處理中,EPC的值不會(huì)被更新,如果EXL是SET的話。
Nesting Exceptions
在有的情況下,我們希望在Exception或中斷中,系統(tǒng)可以繼續(xù)接送exception或中斷。 這需要我們小心如下事情:
*進(jìn)入處理程序后,我們要設(shè)置CPU模式為KERNEL MODE然后重新clear SR[EXL],從而支持EPC會(huì)被更新,如果出現(xiàn)新的Exception/Interrupt的話。
* EPC 和SR的值
EPC和SR寄存器是兩個(gè)全局的。任何一個(gè)Exception/Interrupt發(fā)生時(shí),CPU硬件都會(huì) 將其value over-write.所以,對(duì)于支持Nesting Exceptoins的系統(tǒng),要妥善保存EPC和SR寄 存器的VALUE。
* EPC 和SR的值 EPC和SR寄存器是兩個(gè)全局的。任何一個(gè)Exception/Interrupt發(fā)生時(shí),CPU硬件都會(huì) 將其value over-write.所以,對(duì)于支持Nesting Exceptoins的系統(tǒng),要妥善保存EPC和SR寄 存器的VALUE。SR[IE]是一個(gè)很重要的BIT來處理 在處理Nesting Exception時(shí),值得注意的,或容易犯錯(cuò)的一點(diǎn)是(我在這上面吃過苦頭): 一定要注意:在做 restore context時(shí),要避免重入問題。比如,但要用eret返回時(shí), 我們要set up the EPC value. 在此之前,一定要先disable interrupt. 否 則,EPC value 可能被沖掉。
下面是一段codes of mine for illustrating the exception return.
restore_context /* Retrieve the SR value */ mfc0 t0,C0_SR /* Fill in a delay slot instruction */ nop /* Clear the SR[IE] to disable any interrupts */ li t1,~SR_IE and t0,t0,t1 mtc0 t0,C0_SR nop /* We can then safely restore the EPC value * from the stack */ ld t1,R_EPC(sp) mtc0 t1,C0_EPC nop lhu k1, /* restore old interrupt imask */ or t0,t0,k1 /*
We reset the EXL bit before returning from the exception/interrupt the
eret instruction will automatically clear the EXL then. 一定要理解 我為什么要在前面clear EXL.如果不得話。就不能支持nesting exceptions. 為什么,希望讀者能思考并回答。并且,在清EXL之前,我們一定要先把CPU模式變?yōu)?/span>KERNEL MODE。 */ ori t0,t0,SR_EXL /* restore mask and exl bit */ mtc0 t0,C0_SR nop ori t0,t0,SR_IE /* re-set ie bit */ ori t0,t0,SR_IMASK7 mtc0 t0,C0_SR nop /*恢復(fù)CPU模式 */ ori t0, t0,SR_USERMODE mtc0, t0, C0_SR
eret /*eret將ATOMIC的將EXL清零。所以要注意,如果你在處理程序中改變了CPU得模式,例如,一定要確保,在重新設(shè)置EXL位后,恢復(fù)CPU的original
mode. Otherwise,for example, a task/process will run in kernel mode.
That would be totally mess up your system software.*/
In
summary, exception/interrupt handling is very critical for any os
kernel. For a kernel engineer, you should be very clear with the
exception mechanisms of your target CPU provides. Otherwise, it would
cost you bunches of time for bug fixes.
Again, the best way
is to read the CPU specification slowly and clearly. There is no any
better approach there. No genius, but hard worker, always.
|
|