1. 程式人生 > >學習uboot前奏之hardware-IRQ[s3c2440]

學習uboot前奏之hardware-IRQ[s3c2440]

中斷體系結構

ARM體系結構的CPU有7種工作模式

1.使用者模式(usr): ARM處理器正常的程式執行狀態
2.快速中斷模式(fiq):用於高速資料輸出或者通道處理
3.中斷模式(irq):用於通用的中斷處理
4.管理模式(svc):作業系統使用的保護模式
5.資料訪問終止模式(abt):當資料或者指令預取終止時進入該模式,可用於虛擬儲存及保護模式
6.系統模式(sys):執行具有特權的作業系統任務
7.未定義指令中止模式(und):當未定義的指令執行時進入該模式,可用於支援硬體協處理器的軟體模擬

以上各個模式可以通過軟體切換或者發生各類中斷、異常時CPU自動進入相應的模式。
除使用者模式外,其他6種模式都屬於特權模式。大多數程式運行於使用者模式,進入特權
模式是為了處理中斷、異常,或者訪問被保護的系統資源。

另外,ARM體系的CPU有一下兩種工作狀態

  • ARM狀態:此時處理器執行32位的字對齊的ARM指令
  • Thumb狀態:此時處理器執行16位的、半字對齊的Thumb指令

CPU一上電就處於ARM狀態。

S3C2440的ARM920T有31個通用的32位暫存器和6個程式狀態暫存器,這些暫存器分為7組,進入相應的模式就使用相應的暫存器器組,也就是說有些暫存器在不同的工作模式下有不同的副本。分組情況如下:
這裡寫圖片描述

圖中R0~R15可以直接訪問,這些暫存器除了R15外都是通用暫存器。另外,R13-R15的情況比較特殊。R13又被稱為棧指標暫存器,通常用於儲存棧指標。R14又被稱為程式連線暫存器或者連線暫存器,當執行BL子程式呼叫指令時,R14中得到R15(程式計數器PC)的備份。而當發生中斷或者異常的時候,對應的R14_svc、R14_irq、R14_fiq、R14_und中儲存R15返回值。

每種工作模式處理R0~R15共16個暫存器外,還有第17個暫存器CPSR,即“當前程式狀態暫存器”(Current Program Status Register)。CPSR中一些位被用於標識各種狀態,一些位用於標識當前處於什麼工作模式。
這裡寫圖片描述

CPSR中各位的意義:
T位:置位時,CPU處於Thumb狀態,否則處於ARM狀態
中斷禁止位:I位和F位屬於中斷禁止位,它們被置位時,IRQ中斷、FIQ中斷分別被禁止。
工作模式位:表明當前處於什麼工作模式,可以編寫這些位使CPU進入指定的工作模式。

除CPSR外,還有快速中斷模式、中斷模式、管理模式、資料訪問終止模式和未定義指令中止模式等5中工作模式和一個暫存器—-SPSR,即程式狀態儲存暫存器“Saved Process Status Register”。當切換進入這些工作模式時,在SPSR中儲存前一個工作模式的CPSR值,這樣,當返回到前一個工作模式時,可以將SPSR的值恢復到CPSR中。

綜上所述,當一個異常發生時,將切換進入相應的異常模式,這是ARM920T CPU核自動完成下面的事情。

  1. 在異常工作模式的連線暫存器R14中儲存前一個工作模式的下一條,即將執行的指令的地址,對於ARM狀態,這個值是當前PC值加4或者加8
  2. 將CPSR的值複製到異常模式的SPSR.
  3. 將CPSR的工作模式位設為這個異常對應的工作模式。
  4. 令PC的值等於這個異常模式在異常向量的地址,即跳轉去執行異常向量表中的相應指令。

相反地,從異常工作模式退出回到之前的工作模式,需要通過軟體完成如下事情。

  1. 前面進入異常工作模式時,連線暫存器中儲存了前一個工作模式的一個指令地址,將它減去一個適當的值(參考下表)後賦值給PC暫存器
  2. 將SPSR的值複製會CPSR

這裡寫圖片描述

CPU執行過程中,通過中斷的方式知道外設發生的某些不可預期的事情:當某事件發生時,硬體會設定某個暫存器;CPU在每執行一個指令時,通過硬體檢視這個暫存器,如果關注的事情發生了,則中斷當前程式流程,跳轉到一個固定的地址處理這個事件。

無論何種CPU中斷的處理過程是類似的

  • 中斷控制器彙集各類外設發生的中斷訊號,然後告訴CPU
  • CPU儲存當前程式的執行環境,呼叫中斷服務程式(ISR)來處理這些中斷
  • 在ISR中通過讀取中斷控制器、外設的相關暫存器來識別這是哪個中斷,並進行相應處理
  • 清除中斷:通過讀寫中斷控制器和外設的相關暫存器來實現
  • 最後恢復被中斷程式的執行環境,繼續執行

S3C2440A 中的中斷控制器接受來自60 箇中斷源的請求。提供這些中斷源的是內部外設,如DMA 控制器、UART、IIC 等等。在這些中斷源中,UARTn、AC97 和EINTn 中斷對於中斷控制器而言是“或”關係。當從內部外設和外部中斷請求引腳收到多箇中斷請求時,中斷控制器在仲裁步驟後請求ARM920T 核心的FIQ
或IRQ。仲裁步驟由硬體優先順序邏輯決定並且寫入結果到幫助使用者通告是各種中斷源中的哪個中斷髮生了的中斷掛起暫存器中。

這裡寫圖片描述

  • 對於不帶sub暫存器的中斷源,被觸發後,SRCPND暫存器中相應位被置1,如果此中斷沒有被INTMASK暫存器遮蔽的話,它將進一步被處理。
  • 對於帶有sub暫存器的請求源,被觸發後,SUBSRCPND暫存器中相應位也被置1,如果此中斷沒有被INTSUBMASK暫存器遮蔽的話,它在SRCPND相應位也會置1,後面的處理過程就和上面的過程型別了。
  • 如果被觸發的中斷中有快速中斷(FIQ),則CPU進入快速中斷模式進行處理。
  • 對於一般中斷IRQ,可能同時又幾個中斷被觸發,未被INTMASK暫存器的中斷遮蔽的中斷經過比較後,選出優先順序最高的中斷,此中斷在INTPND暫存器中的相應位被置1,然後CPU進入中斷模式(IRQ Mode)進行處理。中斷服務程式可以通過讀取INTPND暫存器或者INTOFFSET暫存器來確定中斷源。

終上所述,使用中斷步驟如下。

  • 設定好中斷模式和快速中斷模式下的棧:當發生中斷IRQ時,CPU進入中斷模式,這時使用中斷模式下的棧。當發生快速中斷FIQ時,CPU進入快速中斷模式,這時使用快速中斷模式下的棧
  • 準備好中斷處理函式
    1. 在異常向量表中設定好當進入中斷模式或快速中斷模式時的跳轉函式,它們的異常向量地址分別為0x00000018,0x0000001c 。
    2. 中斷服務程式(ISR),IRQ、FIQ的跳轉函式,最終將呼叫具體中斷的服務函式。對於IRQ,讀取INTPND暫存器或INTOFFSET暫存器的值來確定中斷源,然後分別處理。
    3. 清除中斷:如果不清除中斷,則CPU會誤以為中斷又發生了一次。清除中斷時,從源頭開始:首先,需要的話,操作具體外設清除中斷訊號;其次,清除SUBSRCPND(用到的話)、SRCPND暫存器中的相應位(往相應位寫1即可);最後,清除INTPND暫存器中相應位,最簡單的方法就是INTPND=INTPND
  • 進入、退出中斷模式或者快速中斷模式時,需要儲存、恢復被中斷程式的執行環境

     1. 對於IRQ,進入和退出的程式碼如下
    
        sub lr, lr, #4              @計算返回地址
        stmdb  sp!, {r0-r12,lr}     @儲存使用到的暫存器
        ... ...                     @處理中斷
        ldmia  sp!, {r0-r12,pc}^    @中斷返回
                            @^表示將spsr的值賦值給cpsr
     2. 對於FIQ,進入和退出的程式碼如下:
        sub lr, lr, #4              @計算返回地址
        stmdb  sp!, {r0-r12,lr}     @儲存使用到的暫存器
        ... ...                     @處理中斷
        ldmia  sp!, {r0-r12,pc}^    @中斷返回
                            @^表示將spsr的值賦值給cpsr
  • 根據具體中斷,設定相關外設。比如對於GPIO中斷,需要將相應引腳的功能設為“外部中斷”、設定中斷觸發條件
  • 對於有sub暫存器的請求源的中斷,還需要將INTSUBMASK相應位置0
  • 確定使用此中斷的方式:FIQ和IRQ
  • 設定CPSR暫存器中的I-bit或F-bit為,使能IRQ

下面以程式碼來說明如果使用中斷

@******************************************************************************
@ File:head.S
@ 功能:初始化,設定中斷模式、管理模式的棧,設定好中斷處理函式
@******************************************************************************       

.extern     main
.text 
.global _start 
_start:
@******************************************************************************       
@ 中斷向量,本程式中,除Reset和HandleIRQ外,其它異常都沒有使用
@******************************************************************************       
    b   Reset

@ 0x04: 未定義指令中止模式的向量地址
HandleUndef:
    b   HandleUndef 

@ 0x08: 管理模式的向量地址,通過SWI指令進入此模式
HandleSWI:
    b   HandleSWI

@ 0x0c: 指令預取終止導致的異常的向量地址
HandlePrefetchAbort:
    b   HandlePrefetchAbort

@ 0x10: 資料訪問終止導致的異常的向量地址
HandleDataAbort:
    b   HandleDataAbort

@ 0x14: 保留
HandleNotUsed:
    b   HandleNotUsed

@ 0x18: 中斷模式的向量地址
    b   HandleIRQ

@ 0x1c: 快中斷模式的向量地址
HandleFIQ:
    b   HandleFIQ

Reset:                  
    ldr sp, =4096           @ 設定棧指標,以下都是C函式,呼叫前需要設好棧
    bl  disable_watch_dog   @ 關閉WATCHDOG,否則CPU會不斷重啟

    msr cpsr_c, #0xd2       @ 進入中斷模式
    ldr sp, =3072           @ 設定中斷模式棧指標

    msr cpsr_c, #0xd3       @ 進入管理模式
    ldr sp, =4096           @ 設定管理模式棧指標,
                            @ 其實復位之後,CPU就處於管理模式,
                            @ 前面的“ldr sp, =4096”完成同樣的功能,此句可省略

    bl  init_led            @ 初始化LED的GPIO管腳
    bl  init_irq            @ 呼叫中斷初始化函式,在init.c中
    msr cpsr_c, #0x5f       @ 設定I-bit=0,開IRQ中斷

    ldr lr, =halt_loop      @ 設定返回地址
    ldr pc, =main           @ 呼叫main函式
halt_loop:
    b   halt_loop

HandleIRQ:
    sub lr, lr, #4                  @ 計算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 儲存使用到的暫存器
                                    @ 注意,此時的sp是中斷模式的sp
                                    @ 初始值是上面設定的3072

    ldr lr, =int_return             @ 設定呼叫ISR即EINT_Handle函式後的返回地址  
    ldr pc, =EINT_Handle            @ 呼叫中斷服務函式,在interrupt.c中
int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中斷返回, ^表示將spsr的值複製到cpsr

在上面程式開始的部分,分佈了7條指令,地址分別為0x00,0x04,…….,0x1c,這些地址上的指令被稱為“異常向量”,當發生各類異常或者中斷時,CPU進入相應的工作模式,並跳轉去執行對應的“異常向量”。比如當復位時,CPU進入系統模式,並跳轉到0x00地址開始執行;發生中斷時,CPU進入中斷模式,並跳轉到0x18地址開始執行。

本程式中,只有復位和中斷對應的異常向量有作用,其他沒有實際作用。

0x00地址處的指令為 “b Reset”,直接跳轉到標號為Reset開始的程式碼。

Reset:                  
    ldr sp, =4096           @ 設定棧指標,以下都是C函式,呼叫前需要設好棧
    bl  disable_watch_dog   @ 關閉WATCHDOG,否則CPU會不斷重啟

    msr cpsr_c, #0xd2       @ 進入中斷模式
    ldr sp, =3072           @ 設定中斷模式棧指標

    msr cpsr_c, #0xd3       @ 進入管理模式
    ldr sp, =4096           @ 設定管理模式棧指標,
                            @ 其實復位之後,CPU就處於管理模式,
                            @ 前面的“ldr sp, =4096”完成同樣的功能,此句可省略

    bl  init_led            @ 初始化LED的GPIO管腳
    bl  init_irq            @ 呼叫中斷初始化函式,在init.c中
    msr cpsr_c, #0x5f       @ 設定I-bit=0,開IRQ中斷

    ldr lr, =halt_loop      @ 設定返回地址
    ldr pc, =main           @ 呼叫main函式
halt_loop:
    b   halt_loop

這裡先說下msr cpsr_c, #0x5f中的 cpsr_c,CPSR暫存器共32位被分為4個域如下:

CPSR

所以cpsr_c是對應操作CPSR暫存器的低8位,設定完成中斷模式的棧指標以後,讓我們來看下發生中斷時候的處理流程:

HandleIRQ:
    sub lr, lr, #4                  @ 計算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 儲存使用到的暫存器
                                    @ 注意,此時的sp是中斷模式的sp
                                    @ 初始值是上面設定的3072

    ldr lr, =int_return             @ 設定呼叫ISR即EINT_Handle函式後的返回地址  
    ldr pc, =EINT_Handle            @ 呼叫中斷服務函式,在interrupt.cint_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中斷返回, ^表示將spsr的值複製到cpsr

上面的程式首先計算返回地址,然後將現場環境r0-r12以及lr儲存到中斷模式的棧中,然後進入中斷ISR中,最後返回。

#include "s3c24xx.h"

void EINT_Handle()
{
    unsigned long oft = INTOFFSET;/*讀取中斷偏移位*/
    unsigned long val;

    switch( oft )/*根據中斷偏移判斷是一箇中斷*/
    {
        // S2被按下
        case 0: 
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄滅
            GPFDAT &= ~(1<<4);      // LED1點亮
            break;
        }

        // S3被按下
        case 2:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄滅
            GPFDAT &= ~(1<<5);      // LED2點亮
            break;
        }

        // K4被按下
        case 5:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄滅
            GPFDAT &= ~(1<<6);      // LED4點亮                
            break;
        }

        default:
            break;
    }

    //中斷處理了以後,要清除這個中斷標誌位,以後重複處理
    if( oft == 5 ) 
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<<oft;
    INTPND = 1<<oft;
}

以上就是中斷部分的內容,這篇內容拖了太久,很多內容我自己都記不清除了,以後不能這樣了