uc/os軟件中斷與硬件中斷處理流程分析
===================================================================================
=====================軟件中斷===================================================================================
SWI(software interrupt)軟件中斷,由用戶定義的中斷指令.可以用於用戶模式下的程序調用特權操作指令.在實時操作系統中可以通過該機制實現系統調用.
一個 SWI 所做的一切就是把模式改變成超級用戶並設置 PC 來執行在地址 &08 處的下一個指令!
編程異常通常叫做軟中斷.軟中斷是通訊進程之間用來模擬硬中斷的一種信號通訊方式。中斷源發中斷請求或軟中斷信號後,CPU或接收進程在適當的時機自動進行中斷處理或完成軟中斷信號對應的功能.
軟中斷是軟件實現的中斷,也就是程序運行時其他程序對它的中斷;而硬中斷是硬件實現的中斷,是程序運行時設備對它的中斷。
1.軟中斷髮生的時間是由程序控制的,而硬中斷髮生的時間是隨機的
2.軟中斷是由程序調用發生的,而硬中斷是由外設引發的
3.硬件中斷處理程序要確保它能快速地完成它的任務,這樣程序執行時纔不會等待較長時間
2. 軟件中斷使用的始末
2.1 SWI指令
SWI{cond} immed_24
其中,immed_24位24位立即數
2.2 ADS編譯器中軟中斷的規定
ADS編譯器規定,用戶可使用關鍵字__swi作爲前綴來聲明一個利用軟中斷的調用,其格式如下:
__swi(功能號) 返回值類型名稱(參數列表);
其中關鍵字__swi後面的括號中的字段叫做軟中斷的功能編號。系統是根據這個編號在軟中斷服務程序戶中來確定應執行的程序段的。用戶可以在用戶程序中像調用一個普通函數那樣實現軟中斷的調用。只是該“函數”沒有普通函數那樣明顯的函數實現體。至於軟件中斷具體的函數實體後面會講到。
利用軟中斷服務程序可以規避由於ARM處在不同工作模式時所造成的訪問限制(如資源的訪問限制)。
2.3 在Uc/OS-II中軟中斷來實現的函數的聲明
函數註冊軟中斷號
__swi(0x00) void OS_TASK_SW(void); /* 任務級任務切換函數 */
__swi(0x01) void __OSStartHighRdy(void); /* 運行優先級最高的任務 */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開中斷 */
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統服務函數入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index); /* 獲取自定義服務函數入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */
__swi(0x80) void ChangeToSYSMode(void); /* 任務切換到系統模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務切換到用戶模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任務代碼是ARM代碼 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任務代碼是THUMB */
這些函數不是通常意義上的函數,只是可以讓用戶在應用程序中使用這些“函數名”去引發一個攜帶功能號的軟中斷SWI,即ADS編譯器在編譯這些函數時,把他們編譯成SWI指令的二進制代碼。
注:軟中斷功能號爲0x00、0x01的函數使用匯編語言寫的,而其他的函數是用c語言寫的。具體代碼見下面。
注:遵照ATPCS函數調用標準,一個SWI調用允許帶1~4個字型參數和1~4個字型返回值,觸發SWI調用時四個參數依次保存在R0~R3中,返回值也存於R0~R3內。所以在軟中斷服務程序中應函數所需要的參數按順序保存到R0~R3中。
2.4 軟中斷具體實現始末
以兩個具體的函數調用來說明軟中斷具體的實現。一個是任務級切換函數void OS_TASK_SW(void),一個是任務模式切換函數void ChangeToSYSMode(void).
2.4.1. 從啓動代碼中開始
硬件平臺爲ARM7內核。當有軟中斷髮生(即調用2.2中某一個函數時)時,系統首先自動調轉到0x0008處執行。
1、第一級中斷向量
AREA Init,CODE,READONLY
ENTRY
b ResetHandler ;for debug
b HandlerUndef ;handlerUndef
b HandlerSWI ;SWI interrupt handler
b HandlerPabort ;handlerPAbort
b HandlerDabort ;handlerDAbort
b . ;handlerReserved
b HandlerIRQ
b HandlerFIQ
2、宏定義(2440init.s)
//HANDLER爲宏定義名,當調用該宏定義時候,都進入到該函數,相當於函數名爲HandlerLanble,參數爲HandleLable
//該標號前面有$,即表示有調用該宏的時候,替換HANDLER的前後。
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel ;標號,如果前面有“b HandlerIRQ”且宏展開有“HandlerIRQ HANDLER HandleIRQ”
;該處立即表示HandlerIRQ,立即跳轉到此處
sub sp,sp,#4 ;減少sp(用於存放轉跳地址)
stmfd sp!,{r0} ;把工作寄存器壓入棧
ldr r0,=$HandleLabel ;將HandleXXX的址址放入r0
ldr r0,[r0] ;把HandleXXX所指向的內容(也就是中斷程序的入口)放入r0
;如果前面有“b HandlerIRQ”且宏展開有“HandlerIRQ HANDLER HandleIRQ”,立即跳轉到HandleIRQ處(爲具體該中斷處理函數)。
str r0,[sp,#4] ;把中斷服務程序(ISR)壓入棧
ldmfd sp!,{r0,pc} ;用出棧的方式恢復r0的原值和爲pc設定新值(也就完成了到ISR的轉跳)
MEND
3、宏展開
繼續找HandlerSWI。
HandlerSWI HANDLER HandleSWI ;//當有“b HandlerSWI”,立即跳轉到HandleSWI
4、中斷入口函數地址(opthion.h)
#define _RAM_STARTADDRESS 0x30000000
#define _ISR_STARTADDRESS 0x33ffff00
5、中斷處理中不同的函數入口地址(OS_CPU.S)
// Exception vector
#define pISR_RESET (*(unsigned *)(_ISR_STARTADDRESS+0x0))
#define pISR_UNDEF (*(unsigned *)(_ISR_STARTADDRESS+0x4))
#define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x8))
#define pISR_PABORT (*(unsigned *)(_ISR_STARTADDRESS+0xc))
#define pISR_DABORT (*(unsigned *)(_ISR_STARTADDRESS+0x10))
#define pISR_RESERVED (*(unsigned *)(_ISR_STARTADDRESS+0x14))
#define pISR_IRQ (*(unsigned *)(_ISR_STARTADDRESS+0x18))
#define pISR_FIQ (*(unsigned *)(_ISR_STARTADDRESS+0x1c))
// Interrupt vector
#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1 (*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2 (*(unsigned *)(_ISR_STARTADDRESS+0x28))
#define pISR_EINT3 (*(unsigned *)(_ISR_STARTADDRESS+0x2c))
#define pISR_EINT4_7 (*(unsigned *)(_ISR_STARTADDRESS+0x30))
#define pISR_EINT8_23 (*(unsigned *)(_ISR_STARTADDRESS+0x34))
#define pISR_CAM (*(unsigned *)(_ISR_STARTADDRESS+0x38)) // Added for 2440.
#define pISR_BAT_FLT (*(unsigned *)(_ISR_STARTADDRESS+0x3c))
#define pISR_TICK (*(unsigned *)(_ISR_STARTADDRESS+0x40))
#define pISR_WDT_AC97 (*(unsigned *)(_ISR_STARTADDRESS+0x44)) //Changed to pISR_WDT_AC97 for 2440A
#define pISR_TIMER0 (*(unsigned *)(_ISR_STARTADDRESS+0x48))
#define pISR_TIMER1 (*(unsigned *)(_ISR_STARTADDRESS+0x4c))
#define pISR_TIMER2 (*(unsigned *)(_ISR_STARTADDRESS+0x50))
#define pISR_TIMER3 (*(unsigned *)(_ISR_STARTADDRESS+0x54))
#define pISR_TIMER4 (*(unsigned *)(_ISR_STARTADDRESS+0x58))
#define pISR_UART2 (*(unsigned *)(_ISR_STARTADDRESS+0x5c))
#define pISR_LCD (*(unsigned *)(_ISR_STARTADDRESS+0x60))
#define pISR_DMA0 (*(unsigned *)(_ISR_STARTADDRESS+0x64))
#define pISR_DMA1 (*(unsigned *)(_ISR_STARTADDRESS+0x68))
#define pISR_DMA2 (*(unsigned *)(_ISR_STARTADDRESS+0x6c))
#define pISR_DMA3 (*(unsigned *)(_ISR_STARTADDRESS+0x70))
#define pISR_SDI (*(unsigned *)(_ISR_STARTADDRESS+0x74))
#define pISR_SPI0 (*(unsigned *)(_ISR_STARTADDRESS+0x78))
#define pISR_UART1 (*(unsigned *)(_ISR_STARTADDRESS+0x7c))
#define pISR_NFCON (*(unsigned *)(_ISR_STARTADDRESS+0x80)) // Added for 2440.
#define pISR_USBD (*(unsigned *)(_ISR_STARTADDRESS+0x84))
#define pISR_USBH (*(unsigned *)(_ISR_STARTADDRESS+0x88))
#define pISR_IIC (*(unsigned *)(_ISR_STARTADDRESS+0x8c))
#define pISR_UART0 (*(unsigned *)(_ISR_STARTADDRESS+0x90))
#define pISR_SPI1 (*(unsigned *)(_ISR_STARTADDRESS+0x94))
#define pISR_RTC (*(unsigned *)(_ISR_STARTADDRESS+0x98))
#define pISR_ADC (*(unsigned *)(_ISR_STARTADDRESS+0x9c))
6、內存第二級中斷向量(2440INIT.S)
再找HandleSWI。
AREA RamData, DATA, READWRITE ; 在內存中分配一數據段
^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00 ,"^"相當於MAP,定義一個結構化內存表的首地址
HandleReset # 4 ;"#"相當於FIELD,結構化內存表的數據域,4個字節,“#”僅僅是定義結構,並不分配內存單元
HandleUndef # 4
HandleSWI # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ # 4
HandleFIQ # 4
;Don''t use the label 'IntVectorTable',
;The value of IntVectorTable is different with the address you think it may be.
;IntVectorTable
;@0x33FF_FF20
HandleEINT0 # 4
HandleEINT1 # 4
HandleEINT2 # 4
HandleEINT3 # 4
HandleEINT4_7 # 4
HandleEINT8_23 # 4
HandleCAM # 4 ; Added for 2440.
HandleBATFLT # 4
HandleTICK # 4
HandleWDT # 4
HandleTIMER0 # 4
HandleTIMER1 # 4
HandleTIMER2 # 4
HandleTIMER3 # 4
HandleTIMER4 # 4
HandleUART2 # 4
;@0x33FF_FF60
HandleLCD # 4
HandleDMA0 # 4
HandleDMA1 # 4
HandleDMA2 # 4
HandleDMA3 # 4
HandleMMC # 4
HandleSPI0 # 4
HandleUART1 # 4
HandleNFCON # 4 ; Added for 2440.
HandleUSBD # 4
HandleUSBH # 4
HandleIIC # 4
HandleUART0 # 4
HandleSPI1 # 4
HandleRTC # 4
HandleADC # 4
;@0x33FF_FFA0
END
現在我們知道軟中斷的服務程序跑到了內存中去了,我們可以編寫相應的代碼來實現不同功能號下的不同功能。
7、在文件OS_CPU_A.S中編寫軟中斷服務程序
HandleSWI
LDR sp, StackSvc;
STMFD sp!, {r0-r3, r12, lr};// STMDB,保護現場
MOV r1, sp ;// 若SWI調用帶參,將R1指向第二個參數
;// 遵照ATPCS標準,第一個參數存於R0中
MRS r3, spsr
TST r3, #0x20 ;//檢查時ARM還是THUMB指令
STMFD sp!, {r0} ;// spsr入棧
LDRNEH r0, [lr,# -2] ;// THUMB指令時執行,獲取SWI指令碼
BICNE r0, r0, #0xFF00 ;// 獲取SWI number
;// r0 now contains SWI number
;// r1 now contains pointer to stacked registers
LDREQ r0, r0, #0xFF000000;//ro中存放着軟中斷的功能號
CMP r0, #1;
LDRLO pc, =OSIntCtxSw ;//LDRLO=LDRCC r0=0時跳轉到彙編語言處理
LDREQ pc, =__OSStartHighRdy ;//r0=1時跳轉到彙編語言處理
BL SWI_Exception ;// r0>1時跳轉,調用C編寫的SWI處理函數
LDMFD sp!, {r0-r3, r12, pc}^ ;// 恢復現場
我們可以看出當調用2.2中某一個函數並且函數具有兩個參數時,函數的第一個參數存放在r0中,第二個參數存放在r1中。
8、在文件OS_CPU_C.C中編寫軟中斷服務程序
爲了使uC/OS-II的一些底層系統函數在調用時與處理器工作模式無關,所以在移植時採用軟中斷來實現.除了功能號01、02的軟中斷函數是用匯編語言,其他的軟中斷函數都是用C語言編寫的.
void SWI_Exception( int SWI_Num,//軟中斷功能號
int *Regs //爲指向堆棧中保存寄存器的值的指針
)
{
OS_TCB *ptch;
Switch(SWI_Num)// 具體代碼間參考文獻[1]P214
{
case 0x02://宏OS_ENTER_CRITICAL()的代碼
case 0x03://宏OS_EXIT_CRITICAL()的代碼
case 0x80://宏ChangeToSYSMode ()的代碼
case 0x81://宏ChangeToUSRMode ()的代碼
case 0x82://宏TaskIsARM ()的代碼
case 0x83://宏TaskIsTHUMB ()的代碼
default:break;
}
}
===================================================================================
=============================================硬件中斷
===================================================================================
1、初始化HandleIRQ地址,系統復位定義
; Setup IRQ handler,//因爲IRQ的不同處理就是通過這個程序段實現的,具體的看IsrIRQ程序
ldr r0,=HandleIRQ ;This routine is needed
;ldr r1,=IsrIRQ ;if there isn''t 'subs pc,lr,#4' at 0x18, 0x1c
ldr r1, =OS_CPU_IRQ_ISR ;modify by txf, for ucos ,//HandleIRQ的(中斷處理函數)指向具體中斷處理函數
str r1,[r0]
2、有硬件中斷後執行(2440init.s)
b HandlerIRQ
3、根據宏展開(2440init.s)
HandlerIRQ HANDLER HandleIRQ
執行宏定義中,跳轉到HandleIRQ,而HandleIRQ指向了OS_CPU_IRQ_ISR彙編函數
4、調用OS_CPU_IRQ_ISR(os_cpu_a.s)
OS_CPU_IRQ_ISR
;0.//保存IRQ模式堆棧地址和部分寄存器值
STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
;----------------------------------------------------------------------------
; R1--SP
; R2--PC
; R3--SPSR
;------------------------------------------------------------------------
MOV R1, SP
ADD SP, SP, #12 ;Adjust IRQ stack pointer
SUB R2, LR, #4 ;Adjust PC for return address to task
MRS R3, SPSR ; Copy SPSR (Task CPSR)
;1.// 保存中斷前任務的寄存器(需要切換到中斷前任務的運行模式SVC模式),壓棧後,task棧存儲: CPSR,R0->R12,LR,PC;
MSR CPSR_cxsf, #SVCMODE|NOINT ;Change to SVC mode
; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK
STMFD SP!, {R2} ; Push task''s PC
STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4
LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQ stack
STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVC stack
STMFD SP!, {R0} ; Push Task''s R0 to SVC stack
STMFD SP!, {R3} ; Push task''s CPSR
LDR R0,=OSIntNesting ;OSIntNesting++
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
CMP R1,#1 ;if(OSIntNesting==1){ ;//第一次中斷
BNE %F1
;2.//保存中斷前任務的SP到OSTCBCur中;
LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
LDR R5,[R4]
STR SP,[R5] ;}
;3.//切換到中斷模式
1
MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
LDR R0, =INTOFFSET ;//得到中斷偏移量值
LDR R0, [R0]
;4.//處理中斷函數
LDR R1, IRQIsrVect ;//中斷0向量的入口地址
MOV LR, PC ;//保存PC值 ; Save LR befor jump to the C function we need return back
LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler(); 跳轉到具體的中斷入口地址(看下面5、6)
;5.//退出中斷,切換到SVC模式
MSR CPSR_c,#SVCMODE|NOINT ;Change to SVC mode
BL OSIntExit ;Call OSIntExit;//如果退出了所有中斷,則調用任務切換
;6.//恢復堆棧,並中斷處理完返回
LDMFD SP!,{R4} ;POP the task''s CPSR
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context
5、分配一個內存,指向第一個中斷入口地址(os_cpu_a.s)
IRQIsrVect DCD HandleEINT0 ;//分配一個內存單元,並等於HandleEINT0.即由IRQIsrVect
6、函數關聯入口地址(中斷初始化)
pISR_TIMER0= (uint32) OSTickISR;
pISR_TIEMR0在軟件中斷中已見定義指針,這裏初始化具體指向函數
7、處理中斷
OSTickISR
MOV R5,LR
MOV R1, #1
MOV R1, R1, LSL #10 ; Timer0 Source Pending Reg.
LDR R0, =SRCPND
LDR R2, [R0]
ORR R1, R1,R2
STR R1, [R0]
LDR R0, =INTPND
LDR R1, [R0]
STR R1, [R0]
;----------------------------------------------------------------------------------
; OSTimeTick();
;----------------------------------------------------------------------------------
BL OSTimeTick ;//調用具體中斷的數據處理
MOV PC, R5 ; Return