一、GIC通用中斷控制器
1.GIC通用中斷控制器
? ? ? ? GIC 是 ARM 公司給 Cortex-A/R 內核提供的一個中斷控制器,GIC接收眾多外部中斷,然后對其進行處理,最終通過VFIQ、VIRQ、FIQ 和 IRQ給內核;這四個 信號的含義如下: VFIQ:虛擬快速 FIQ。 VIRQ:虛擬 IRQ。 FIQ:快速中斷 IRQ。 IRQ:中斷 IRQ。
2.GIC中斷分類
SPI(Shared Peripheral Interrupt),共享中斷, (注意!不是 SPI 總線那個中斷),這類中斷泛指所有的 外設中斷;
PPI(Private Peripheral Interrupt),私有中斷,我們說了 GIC 是支持多核的,每個核肯定有自己獨有 的中斷。這些獨有的中斷肯定是要指定的核心處理,因此這些中斷就叫做私有中斷;
SGI(Software-generated Interrupt),軟件中斷,由軟件觸發引起的中斷,通過向寄存器GICD_SGIR 寫入數據來觸發,系統會使用 SGI 中斷來完成多核之間的通信。?從圖中可以明顯看出,Distributor(分發器)可以分發所有共享中斷給8個內核,但是私有中斷和 軟件中斷只出現自己獨自的內核中。
中斷ID:中斷源有很多,為了區分這些不同的中斷源肯定要給他們分配一個唯一 ID,這些 ID 就 是中斷 ID。每一個 CPU 最多支持 1020 個中斷 ID,中斷 ID 號為 ID0~ID1019。
ID0~ID15:這 16 個 ID 分配給 SGI; ID16~ID31:這 16 個 ID 分配給 PPI;ID32~ID1019:這 988 個 ID 分配給 SPI。
3.GIC的組成
? ?由分發器 (1個)、CPU接口(幾核就幾個)
(1)分發器
功能:
- 全局中斷使能控制;
- 控制每一個中斷的使能或者關閉;
- 設置每個中斷的優先級;
- 設置每個中斷的目標處理器列表;
- 設置每個外部中斷的觸發模式:電平觸發或邊沿觸發;
- 設置每個中 斷屬于組 0 還是組 1;
(2)CPU接口
功能:
- 使能或者關閉發送到 CPU Core 的中斷請求信號;
- 應答中斷;
- 通知中斷處理完成;
- 設置優先級掩碼,通過掩碼來設 置哪些中斷不需要上報給 CPU Core;
- 定義搶占策略;
- 當多個中斷到來的時候,選擇優先級最高的 中斷通知給 CPU Core;
4.協處理器
總共由16個,cp0~cp15;其中最常使用的cp15
(1)作用
- 獲取GIC的基地址(CBAR)
- MMU的配置(使能/禁用;SCTLR)
- cache的配置
- 監控系統性能
- 配置中斷控制器(優先級、分組、使能/禁用,VBAR:設置中斷向量表基地址)
- 訪問寄存器(mrc讀、mcr寫)
- 獲取或結束中斷(IAR、EOIR)
(2)讀寫訪問指令
以MRC指令為例,從CP15的某個寄存器讀取數據到ARM寄存器的指令格式為:MRC{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>cond:指令執行的條件碼,就是之前我們使用過的指令條件,eq,lt什么的。如果忽略的話就表示無條件執行;p15:表示要讀取的是CP15當中的某個寄存器;opc1:協處理器要執行的操作碼1,其實就是一個數,要做什么將來查表;Rt:ARM 目標寄存器,讀出來的數據放到哪個ARM寄存器里。CP15CRn:CP15 協處理器的目標寄存器,就是你要讀取CP15的哪個寄存器(C0~C15);CRm:協處理器中附加的目標寄存器或者源操作數寄存器,如果不需要附加信息就將CRm 設置 為 C0,否則結果不可預測。opc2:可選的協處理器特定操作碼2,使用時查表。MCR與MRC類似的,只是Rt的作用變成了要寫入CP15某個寄存器的值。
?mrc p15 0, r0, c0, c0, 0
?????????簡單總結一下,通過 MIDR 寄存器可以獲取到處理器內核信息;通過 SCTLR 寄存器可以使能或禁止 MMU、I/D Cache 等;通過 VBAR 寄存器可以設置中斷向量偏移;通過CBAR 寄存器可以獲取 GIC 基地址。
二、外部中斷
1.設置GIC
(1)讀取SCTLR,將V位置0(軟件可以通過 VBAR來重新映射這個基地址)I位置1(I cache使能)
(2)通過GIC查詢當前中斷ID;先獲取GIC基地址(CBAR);對其進行偏移(IAR),獲得中斷ID,然后進入中斷處理函數
2.中斷服務函數 ? ? ??
(1)中斷初始化;重新定位異常向量表的位置到0x87800000;并且調用GIC_Init函數
(2)對于GPIO1->ICR2(觸發方式)、GPIO1->IMR(該中斷使能)在中斷源初始化進行配置
(3)中斷服務函數
??
注意:先要在相對應的中斷源的初始化函數里面注冊在中斷向量數組中;
例子:注冊完中斷之后,中斷發生就會調用中斷服務函數
內斂函數:INLINE,定義被放在頭文件中
三、OCP原則(開閉原則)??
對代碼擴展是開放的,對代碼的修改是關閉的。
四、啟動代碼
.global _start
//異常向量表
//位于內存起始位置,當特定異常發生時,處理器會自動跳轉到對應的地址上
_start:ldr pc, = _reset_handlerldr pc, = _undefined_handlerldr pc, = _svc_handlerldr pc, = _prefetch_handlerldr pc, = _data_abort_handlerldr pc, = _not_user_handlerldr pc, = _irq_handlerldr pc, = _fiq_handler
//異常處理程序 大多是簡單的無限循環
_undefined_handler:ldr pc, = _undefined_handler
_svc_handler:ldr pc, = _svc_handler
_prefetch_handler:ldr pc, = _prefetch_handler
_data_abort_handler:ldr pc, = _data_abort_handler
_not_user_handler:ldr pc, = _not_user_handler
_fiq_handler:ldr pc, = _fiq_handler//IRQ中斷處理程序_irq_handler:subs lr,lr,#4 //調整返回地址stmfd sp!,{r0-r12,lr} //保存寄存器//讀取中斷控制器狀態mrc p15,4,r1,c15,c0,0 //獲取外設基地址add r1,r1,#0x2000 //偏移到中斷控制器ldr r0,[r1,#0x0C] //讀取中斷狀態stmfd sp!,{r0,r1} //保存臨時寄存器//切換到系統模式處理中斷cps #0x1F //切換到系統模式stmfd sp!,{lr} //保存系統模式LRbl system_interrupt_handler //調用C中斷處理函數ldmfd sp!,{lr} //恢復LR模式cps #0x12 //切換回IRQ模式//清除中斷ldmfd sp!,{r0,r1} //恢復臨時寄存器str r0,[r1,#0x10] //寫EQIR寄存器ldmfd sp!,{r0-r12,pc}^ //恢復寄存器并返回//啟動代碼執行程序
_reset_handler:cpsid i //禁用中斷//配置CP15系統控制寄存器mrc p15,0,r0,c1,c0,0 //讀取控制寄存器bic r0,r0,#(1<<13) //清除V位(異常向量表位置)orr r0,r0,#(1<<12) //設置I位(啟用指令緩存)mcr p15,0,r0,c1,c0,0 //寫回控制寄存器// 設置IRQ模式棧指針cps #0x12 // 切換到IRQ模式ldr sp, =0x82000000 // 設置IRQ棧cps #0x1F // 切換到系統模式ldr sp, =0x84000000 // 設置系統棧cpsie i // 啟用中斷bl _init_bss // 初始化BSS段b main // 跳轉到主程序b finished // 永遠不會執行(冗余)
//初始化BSS段(清零)
_init_bss:ldr r0,= __bss_start // BSS段起始地址ldr r1,= __bss_end // BSS段結束地址
loop:mov r2,#0 // 清零值str r2,[r0] // 存儲0到當前地址add r0,r0,#4 // 移動到下一個字cmp r0,r1 // 比較是否到達結束blt loop // 如果未結束則循環bx lrfinished:b finished