Linux之ARM Cortex-A7 中斷系統詳解【轉】


轉自:https://blog.csdn.net/weixin_45309916/article/details/108290225?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

Cortex-A7 中斷系統詳解

 

1、中斷是什么?

中斷系統是一個處理器重要的組成部分,中斷系統極大的提高了 CPU 的執行效率

2、STM32中斷系統和 Cortex-M(STM32)中斷系統的異同

STM32 的中斷系統主要有以下幾個關鍵點:

①、 中斷向量表。
②、 NVIC(內嵌向量中斷控制器)。
③、 中斷使能。
④、 中斷服務函數。

2.1、中斷向量表

中斷向量表是一個表,這個表里面存放的是中斷向量。中斷服務程序的入口地址或存放中斷服務程序的首地址成為中斷向量,因此中斷向量表是一系列中斷服務程序入口地址組成的表。這些中斷服務程序(函數)在中斷向量表中的位置是由半導體廠商定好的,當某個中斷被觸發以后就會自動跳轉到中斷向量表中對應的中斷服務程序(函數)入口地址處。中斷向量表在整個程序的最前面,比如 STM32F103 的中斷向量表如下所示:

  __Vectors DCD __initial_sp 		 ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash /* 省略掉其它代碼 */ DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & l5 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

上表就是 STM32F103 的中斷向量表,中斷向量表都是鏈接到代碼的最前面,比如一般 ARM 處理器都是從地址 0X00000000 開始執行指令的,那么中斷向量表就是從 0X00000000 開始存放的。上表中第 1 行的“__initial_sp”就是第一條中斷向量,存放的是棧頂指針,接下來是第 2 行復位中斷復位函數 Reset_Handler 的入口地址,依次類推,直到第 27 行的最后一個中斷服務函數 DMA2_Channel4_5_IRQHandler 的入口地址,這樣 STM32F103 的中斷向量表就建好了。

我們說 ARM 處理器都是從地址 0X00000000 開始運行的,但是我們學習 STM32 的時候代碼是下載到 0X8000000 開始的存儲區域中。因此中斷向量表是存放到 0X8000000 地址處的,而不是 0X00000000,這樣不是就出錯了嗎?為了解決這個問題, Cortex-M 架構引入了一
個新的概念——中斷向量表偏移,通過中斷向量表偏移就可以將中斷向量表存放到任意地址處,中斷向量表偏移配置在函數 SystemInit 中完成,通過向 SCB_VTOR 寄存器寫入新的中斷向量表首地址即可,代碼如下所示:

void SystemInit (void) { RCC->CR |= (uint32_t)0x00000001; /* 省略其它代碼 */ #ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; #endif } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第 8 行和第 10 行就是設置中斷向量表偏移,第 8 行是將中斷向量表設置到 RAM 中,第10 行是將中斷向量表設置到 ROM 中,基本都是將中斷向量表設置到 ROM 中,也就是地址0X8000000 處。第 10 行用到了 FALSH_BASE 和 VECT_TAB_OFFSET,這兩個都是宏,定義如下所示:

#define FLASH_BASE ((uint32_t)0x08000000) #define VECT_TAB_OFFSET 0x0 
  • 1
  • 2

因此第 10 行的代碼就是: SCB->VTOR=0X080000000,中斷向量表偏移設置完成。

STM32中斷向量表和中斷向量偏移和I.MX6U中斷向量表和中斷向量偏移的關系?

I.MX6U 所使用的 Cortex-A7 內核也有中斷向量表和中斷向量表偏移,而且其含義和 STM32 是一模一樣的!只是用到的寄存器不同而已,概念完全相同。

2.2、NVIC(內嵌向量中斷控制器)

中斷系統得有個管理機構,對於 STM32 這種 Cortex-M 內核的單片機來說這個管理機構叫做 NVIC,全稱叫做 Nested Vectored Interrupt Controller。關於 NVIC 本教程不作詳細的講解,既然 Cortex-M 內核有個中斷系統的管理機構—NVIC,那么 I.MX6U 所使用的 Cortex-A7 內核是不是也有個中斷系統管理機構?答案是肯定的,不過 Cortex-A 內核的中斷管理機構不叫做NVIC,而是叫做 GIC,全稱是 general interrupt controller。

2.3、中斷使能

要使用某個外設的中斷,肯定要先使能這個外設的中斷,以 STM32F103 的 PE2 這個 IO 為例,假如我們要使用 PE2 的輸入中斷肯定要使用如下代碼來使能對應的中斷:

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優先級 2, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子優先級 2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道 NVIC_Init(&NVIC_InitStructure); 
  • 1
  • 2
  • 3
  • 4
  • 5

上述代碼就是使能 PE2 對應的 EXTI2 中斷,同理,如果要使用 I.MX6U 的某個中斷的話也需要使能其對應的中斷。

2.4、中斷服務函數

我們使用中斷的目的就是為了使用中斷服務函數,當中斷發生以后中斷服務函數就會被調用,我們要處理的工作就可以放到中斷服務函數中去完成。同樣以 STM32F103 的 PE2 為例,其中斷服務函數如下所示:

/* 外部中斷 2 服務程序 */ void EXTI2_IRQHandler(void) { /* 中斷處理代碼 */ } 
  • 1
  • 2
  • 3
  • 4
  • 5

當PE2 引腳的中斷觸發以后就會調用其對應的中斷處理函數 EXTI2_IRQHandler,我們可以在函數 EXTI2_IRQHandler 中添加中斷處理代碼。同理, I.MX6U 也有中斷服務函數,當某個外設中斷發生以后就會調用其對應的中斷服務函數。

3、Cortex-A7 中斷系統詳解

3.1、Cortex-A7 中斷系統簡介

跟 STM32 一樣, Cortex-A7 也有中斷向量表,中斷向量表也是在代碼的最前面。 CortexA7 內核有 8 個異常中斷,這 8 個異常中斷的中斷向量表如下表所示:

向量地址 終端類型 中斷模式
0X00 復位中斷(Rest) 特權模式(SVC)
0X04 未定義指令中斷(Undefined Instruction) 未定義指令中止模式(Undef)
0X08 軟中斷(Software Interrupt,SWI) 特權模式(SVC)
0X0C 指令預取中止中斷(Prefetch Abort) 中止模式
0X10 數據訪問中止中斷(Data Abort) 中止模式
0X14 未使用(Not Used) 未使用
0X18 IRQ 中斷(IRQ Interrupt) 外部中斷模式(IRQ)
0X1C FIQ 中斷(FIQ Interrupt) 快速中斷模式(FIQ)

中斷向量表里面都是中斷服務函數的入口地址,因此一款芯片有什么中斷都是可以從中斷向量表看出來的。從上表中可以看出, Cortex-A7 一共有 8 個中斷,而且還有一個中斷向量未使用,實際只有 7 個中斷。和“示例代碼的 STM32F103 中斷向量表比起來少了很多!難道一個能跑 Linux 的芯片只有這 7 個中斷?明顯不可能的!那類似 STM32 中的EXTI9_5_IRQHandler、 TIM2_IRQHandler 這樣的中斷向量在哪里? I2C、 SPI、定時器等等的中斷怎么處理呢?這個就是 Cortex-A 和 Cotex-M 在中斷向量表這一塊的區別,對於 Cortex-M 內核來說,中斷向量表列舉出了一款芯片所有的中斷向量,包括芯片外設的所有中斷。對於 CotexA 內核來說並沒有這么做,在上表中有個 IRQ 中斷, Cortex-A 內核 CPU 的所有外部中斷都屬於這個 IQR 中斷,當任意一個外部中斷發生的時候都會觸發 IRQ 中斷。在 IRQ 中斷服務函數里面就可以讀取指定的寄存器來判斷發生的具體是什么中斷,進而根據具體的中斷做出相應的處理。這些外部中斷和 IQR 中斷的關系如圖 所示:
外部中斷和 IRQ 中斷關系

在上圖中,左側的 Software0_IRQn~PMU_IRQ2_IRQ 這些都是 I.MX6U 的中斷,他們都屬於 IRQ 中斷。當圖左側這些中斷中任意一個發生的時候 IRQ 中斷都會被觸發,所以我們需要在 IRQ 中斷服務函數中判斷究竟是左側的哪個中斷發生了,然后再做出具體的處理。

在 中斷系統表中一共有 7 個中斷,簡單介紹一下這 7 個中斷:

中斷 描述
①、復位中斷(Rest) CPU 復位以后就會進入復位中斷,我們可以在復位中斷服務函數里面做一些初始化工作,比如初始化 SP 指針、 DDR 等等。
②、未定義指令中斷(Undefined Instruction) 如果指令不能識別的話就會產生此中斷。
③、軟中斷(Software Interrupt,SWI) 由 SWI 指令引起的中斷, Linux 的系統調用會用 SWI指令來引起軟中斷,通過軟中斷來陷入到內核空間。
④、指令預取中止中斷(Prefetch Abort) 預取指令的出錯的時候會產生此中斷。
⑤、數據訪問中止中斷(Data Abort) 訪問數據出錯的時候會產生此中斷。
⑥、 IRQ 中斷(IRQ Interrupt) 外部中斷,前面已經說了,芯片內部的外設中斷都會引起此中斷的發生。
⑦、 FIQ 中斷(FIQ Interrupt) 快速中斷,如果需要快速處理中斷的話就可以使用此中。

在上面的 7 個中斷中,我們常用的就是復位中斷和 IRQ 中斷,所以我們需要編寫這兩個中
斷的中斷服務函數。

首先我們要根據表 的內容來創建中斷向量表,中斷向量表處於程序最開始的地方,比如我們前面例程的 start.S 文件最前面,中斷向量表如下:

.global _start /* 全局標號 */ _start: ldr pc, =Reset_Handler /* 復位中斷 */ ldr pc, =Undefined_Handler /* 未定義指令中斷 */ ldr pc, =SVC_Handler /* SVC(Supervisor)中斷 */ ldr pc, =PrefAbort_Handler /* 預取終止中斷 */ ldr pc, =DataAbort_Handler /* 數據終止中斷 */ ldr pc, =NotUsed_Handler /* 未使用中斷 */ ldr pc, =IRQ_Handler /* IRQ 中斷 */ ldr pc, =FIQ_Handler /* FIQ(快速中斷)未定義中斷 */ /* 復位中斷 */ Reset_Handler: /* 復位中斷具體處理過程 */ /* 未定義中斷 */ Undefined_Handler: ldr r0, =Undefined_Handler bx r0 /* SVC 中斷 */ SVC_Handler: ldr r0, =SVC_Handler bx r0 /* 預取終止中斷 */ PrefAbort_Handler: ldr r0, =PrefAbort_Handler bx r0 /* 數據終止中斷 */ DataAbort_Handler: ldr r0, =DataAbort_Handler bx r0 /* 未使用的中斷 */ NotUsed_Handler: ldr r0, =NotUsed_Handler bx r0 /* IRQ 中斷!重點!!!!! */ IRQ_Handler: /* 復位中斷具體處理過程 */ FIQ_Handler: ldr r0, =FIQ_Handler bx r0 

 

第 4 到 11 行是中斷向量表,當指定的中斷發生以后就會調用對應的中斷復位函數,比如復位中斷發生以后就會執行第 4 行代碼,也就是調用函數 Reset_Handler,函數 Reset_Handler就是復位中斷的中斷復位函數,其它的中斷同理。

第 14 到 50 行就是對應的中斷服務函數,中斷服務函數都是用匯編編寫的,我們實際需要編寫的只有復位中斷服務函數 Reset_Handler 和 IRQ 中斷服務函數 IRQ_Handler,其它的中斷本教程沒有用到,所以都是死循環。在編寫復位中斷復位函數和 IRQ 中斷服務函數之前我們還需要了解一些其它的知識,否則的話就沒法編寫。

3.2、GIC 控制器簡介

3.2.1、 GIC 控制器總覽

I.MX6U(Cortex-A)的中斷控制器叫做 GIC

GIC 是 ARM 公司給 Cortex-A/R 內核提供的一個中斷控制器,類似 Cortex-M 內核中的NVIC。目前 GIC 有 4 個版本:V1~V4, V1 是最老的版本,已經被廢棄了。 V2~V4 目前正在大量的使用。 GIC V2 是給 ARMv7-A 架構使用的,比如 Cortex-A7、 Cortex-A9、 Cortex-A15 等,V3 和 V4 是給 ARMv8-A/R 架構使用的,也就是 64 位芯片使用的。 I.MX6U 是 Cortex-A 內核的,因此我們主要講解 GIC V2。 GIC V2 最多支持 8 個核。 ARM 會根據 GIC 版本的不同研發出不同的 IP 核,那些半導體廠商直接購買對應的 IP 核即可,比如 ARM 針對 GIC V2 就開發出了 GIC400 這個中斷控制器 IP 核。當 GIC 接收到外部中斷信號以后就會報給 ARM 內核,但是ARM 內核只提供了四個信號給 GIC 來匯報中斷情況: VFIQ、 VIRQ、 FIQ 和 IRQ,他們之間的關系如圖所示:
中斷示意圖
在上圖中, GIC 接收眾多的外部中斷,然后對其進行處理,最終就只通過四個信號報給 ARM 內核,這四個信號的含義如下:

  • VFIQ:虛擬快速 FIQ。
  • VIRQ:虛擬快速 IRQ。
  • FIQ:快速中斷 IRQ。
  • IRQ:外部中斷 IRQ

本博文我們只使用 IRQ,所以相當於 GIC 最終向 ARM 內核就上報一個 IRQ信號。那么 GIC 是如何完成這個工作的呢? GICV2 的邏輯圖如圖下所示:
GICV2 總體框圖
圖中左側部分就是中斷源,中間部分就是 GIC 控制器,最右側就是中斷控制器向處理器內核發送中斷信息。我們重點要看的肯定是中間的 GIC 部分, GIC 將眾多的中斷源分為分為三類:

  • ①、 SPI(Shared Peripheral Interrupt),共享中斷,顧名思義,所有 Core 共享的中斷,這個是最常見的,那些外部中斷都屬於 SPI 中斷(注意!不是 SPI 總線那個中斷) 。比如按鍵中斷、串口中斷等等,這些中斷所有的 Core 都可以處理,不限定特定 Core。

  • ②、 PPI(Private Peripheral Interrupt),私有中斷,我們說了 GIC 是支持多核的,每個核肯定有自己獨有的中斷。這些獨有的中斷肯定是要指定的核心處理,因此這些中斷就叫做私有中斷。

  • ③、 SGI(Software-generated Interrupt),軟件中斷,由軟件觸發引起的中斷,通過向寄存器GICD_SGIR 寫入數據來觸發,系統會使用 SGI 中斷來完成多核之間的通信。

3.2.2、中斷 ID

中斷源有很多,為了區分這些不同的中斷源肯定要給他們分配一個唯一 ID,這些 ID 就是中斷 ID。每一個 CPU 最多支持 1020 個中斷 ID,中斷 ID 號為 ID0~ID1019。這 1020 個 ID 包含了 PPI、 SPI 和 SGI,那么這三類中斷是如何分配這 1020 個中斷 ID 的呢?這 1020 個 ID 分配如下:

  • ID0~ID15:這 16 個 ID 分配給 SGI。
  • ID16~ID31:這 16 個 ID 分配給 PPI。
  • ID32~ID1019:這 988 個 ID 分配給 SP。

I.MX6U 的總共使用了 128 個中斷 ID,加上前面屬於 PPI 和 SGI 的 32 個 ID, I.MX6U 的中斷源共有 128+32=160個,這 128 個中斷 ID 對應的中斷在《I.MX6ULL 參考手冊》的“3.2 Cortex A7 interrupts”小節,中斷源如表 所示:

IRQ ID 中斷源 描述
0 32 boot 用於在啟動異常的時候通知內核。
1 33 ca7_platform DAP 中斷,調試端口訪問請求中斷。
2 34 sdma SDMA 中斷。
3 35 tsc TSC(觸摸)中斷。
4 snvs_lp_wrapper 和snvs_hp_wrapper SNVS 中斷。  
…… ……
124 156 保留
125 157 保留
126 158 保留
127 159 pmu PMU 中斷

這里只列舉了一小部分並沒有給出 I.MX6U 完整的中斷源

NXP 官方 SDK中的文件 MCIMX6Y2C.h,在此文件中定義了一個枚舉類型 IRQn_Type,此枚舉類型就枚舉出了 I.MX6U 的所有中斷,代碼如下所示:

typedef enum IRQn { /* Auxiliary constants */ NotAvail_IRQn = -128, /**< Not available device specific interrupt */ /* Core interrupts */ Software0_IRQn = 0, /**< Cortex-A7 Software Generated Interrupt 0 */ Software1_IRQn = 1, /**< Cortex-A7 Software Generated Interrupt 1 */ Software2_IRQn = 2, /**< Cortex-A7 Software Generated Interrupt 2 */ Software3_IRQn = 3, /**< Cortex-A7 Software Generated Interrupt 3 */ Software4_IRQn = 4, /**< Cortex-A7 Software Generated Interrupt 4 */ Software5_IRQn = 5, /**< Cortex-A7 Software Generated Interrupt 5 */ Software6_IRQn = 6, /**< Cortex-A7 Software Generated Interrupt 6 */ Software7_IRQn = 7, /**< Cortex-A7 Software Generated Interrupt 7 */ Software8_IRQn = 8, /**< Cortex-A7 Software Generated Interrupt 8 */ Software9_IRQn = 9, /**< Cortex-A7 Software Generated Interrupt 9 */ Software10_IRQn = 10, /**< Cortex-A7 Software Generated Interrupt 10 */ Software11_IRQn = 11, /**< Cortex-A7 Software Generated Interrupt 11 */ Software12_IRQn = 12, /**< Cortex-A7 Software Generated Interrupt 12 */ Software13_IRQn = 13, /**< Cortex-A7 Software Generated Interrupt 13 */ Software14_IRQn = 14, /**< Cortex-A7 Software Generated Interrupt 14 */ Software15_IRQn = 15, /**< Cortex-A7 Software Generated Interrupt 15 */ VirtualMaintenance_IRQn = 25, /**< Cortex-A7 Virtual Maintenance Interrupt */ HypervisorTimer_IRQn = 26, /**< Cortex-A7 Hypervisor Timer Interrupt */ VirtualTimer_IRQn = 27, /**< Cortex-A7 Virtual Timer Interrupt */ LegacyFastInt_IRQn = 28, /**< Cortex-A7 Legacy nFIQ signal Interrupt */ SecurePhyTimer_IRQn = 29, /**< Cortex-A7 Secure Physical Timer Interrupt */ NonSecurePhyTimer_IRQn = 30, /**< Cortex-A7 Non-secure Physical Timer Interrupt */ LegacyIRQ_IRQn = 31, /**< Cortex-A7 Legacy nIRQ Interrupt */ /* Device specific interrupts */ IOMUXC_IRQn = 32, /**< General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition while boot. */ DAP_IRQn = 33, /**< Debug Access Port interrupt request. */ SDMA_IRQn = 34, /**< SDMA interrupt request from all channels. */ TSC_IRQn = 35, /**< TSC interrupt. */ SNVS_IRQn = 36, /**< Logic OR of SNVS_LP and SNVS_HP interrupts. */ LCDIF_IRQn = 37, /**< LCDIF sync interrupt. */ RNGB_IRQn = 38, /**< RNGB interrupt. */ CSI_IRQn = 39, /**< CMOS Sensor Interface interrupt request. */ PXP_IRQ0_IRQn = 40, /**< PXP interrupt pxp_irq_0. */ SCTR_IRQ0_IRQn = 41, /**< SCTR compare interrupt ipi_int[0]. */ SCTR_IRQ1_IRQn = 42, /**< SCTR compare interrupt ipi_int[1]. */ WDOG3_IRQn = 43, /**< WDOG3 timer reset interrupt request. */ Reserved44_IRQn = 44, /**< Reserved */ APBH_IRQn = 45, /**< DMA Logical OR of APBH DMA channels 0-3 completion and error interrupts. */ WEIM_IRQn = 46, /**< WEIM interrupt request. */ RAWNAND_BCH_IRQn = 47, /**< BCH operation complete interrupt. */ RAWNAND_GPMI_IRQn = 48, /**< GPMI operation timeout error interrupt. */ UART6_IRQn = 49, /**< UART6 interrupt request. */ PXP_IRQ1_IRQn = 50, /**< PXP interrupt pxp_irq_1. */ SNVS_Consolidated_IRQn = 51, /**< SNVS consolidated interrupt. */ SNVS_Security_IRQn = 52, /**< SNVS security interrupt. */ CSU_IRQn = 53, /**< CSU interrupt request 1. Indicates to the processor that one or more alarm inputs were asserted. */ USDHC1_IRQn = 54, /**< USDHC1 (Enhanced SDHC) interrupt request. */ USDHC2_IRQn = 55, /**< USDHC2 (Enhanced SDHC) interrupt request. */ SAI3_RX_IRQn = 56, /**< SAI3 interrupt ipi_int_sai_rx. */ SAI3_TX_IRQn = 57, /**< SAI3 interrupt ipi_int_sai_tx. */ UART1_IRQn = 58, /**< UART1 interrupt request. */ UART2_IRQn = 59, /**< UART2 interrupt request. */ UART3_IRQn = 60, /**< UART3 interrupt request. */ UART4_IRQn = 61, /**< UART4 interrupt request. */ UART5_IRQn = 62, /**< UART5 interrupt request. */ eCSPI1_IRQn = 63, /**< eCSPI1 interrupt request. */ eCSPI2_IRQn = 64, /**< eCSPI2 interrupt request. */ eCSPI3_IRQn = 65, /**< eCSPI3 interrupt request. */ eCSPI4_IRQn = 66, /**< eCSPI4 interrupt request. */ I2C4_IRQn = 67, /**< I2C4 interrupt request. */ I2C1_IRQn = 68, /**< I2C1 interrupt request. */ I2C2_IRQn = 69, /**< I2C2 interrupt request. */ I2C3_IRQn = 70, /**< I2C3 interrupt request. */ UART7_IRQn = 71, /**< UART-7 ORed interrupt. */ UART8_IRQn = 72, /**< UART-8 ORed interrupt. */ Reserved73_IRQn = 73, /**< Reserved */ USB_OTG2_IRQn = 74, /**< USBO2 USB OTG2 */ USB_OTG1_IRQn = 75, /**< USBO2 USB OTG1 */ USB_PHY1_IRQn = 76, /**< UTMI0 interrupt request. */ USB_PHY2_IRQn = 77, /**< UTMI1 interrupt request. */ DCP_IRQ_IRQn = 78, /**< DCP interrupt request dcp_irq. */ DCP_VMI_IRQ_IRQn = 79, /**< DCP interrupt request dcp_vmi_irq. */ DCP_SEC_IRQ_IRQn = 80, /**< DCP interrupt request secure_irq. */ TEMPMON_IRQn = 81, /**< Temperature Monitor Temperature Sensor (temperature greater than threshold) interrupt request. */ ASRC_IRQn = 82, /**< ASRC interrupt request. */ ESAI_IRQn = 83, /**< ESAI interrupt request. */ SPDIF_IRQn = 84, /**< SPDIF interrupt. */ Reserved85_IRQn = 85, /**< Reserved */ PMU_IRQ1_IRQn = 86, /**< Brown-out event on either the 1.1, 2.5 or 3.0 regulators. */ GPT1_IRQn = 87, /**< Logical OR of GPT1 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2, and 3 interrupt lines. */ EPIT1_IRQn = 88, /**< EPIT1 output compare interrupt. */ EPIT2_IRQn = 89, /**< EPIT2 output compare interrupt. */ GPIO1_INT7_IRQn = 90, /**< INT7 interrupt request. */ GPIO1_INT6_IRQn = 91, /**< INT6 interrupt request. */ GPIO1_INT5_IRQn = 92, /**< INT5 interrupt request. */ GPIO1_INT4_IRQn = 93, /**< INT4 interrupt request. */ GPIO1_INT3_IRQn = 94, /**< INT3 interrupt request. */ GPIO1_INT2_IRQn = 95, /**< INT2 interrupt request. */ GPIO1_INT1_IRQn = 96, /**< INT1 interrupt request. */ GPIO1_INT0_IRQn = 97, /**< INT0 interrupt request. */ GPIO1_Combined_0_15_IRQn = 98, /**< Combined interrupt indication for GPIO1 signals 0 - 15. */ GPIO1_Combined_16_31_IRQn = 99, /**< Combined interrupt indication for GPIO1 signals 16 - 31. */ GPIO2_Combined_0_15_IRQn = 100, /**< Combined interrupt indication for GPIO2 signals 0 - 15. */ GPIO2_Combined_16_31_IRQn = 101, /**< Combined interrupt indication for GPIO2 signals 16 - 31. */ GPIO3_Combined_0_15_IRQn = 102, /**< Combined interrupt indication for GPIO3 signals 0 - 15. */ GPIO3_Combined_16_31_IRQn = 103, /**< Combined interrupt indication for GPIO3 signals 16 - 31. */ GPIO4_Combined_0_15_IRQn = 104, /**< Combined interrupt indication for GPIO4 signals 0 - 15. */ GPIO4_Combined_16_31_IRQn = 105, /**< Combined interrupt indication for GPIO4 signals 16 - 31. */ GPIO5_Combined_0_15_IRQn = 106, /**< Combined interrupt indication for GPIO5 signals 0 - 15. */ GPIO5_Combined_16_31_IRQn = 107, /**< Combined interrupt indication for GPIO5 signals 16 - 31. */ Reserved108_IRQn = 108, /**< Reserved */ Reserved109_IRQn = 109, /**< Reserved */ Reserved110_IRQn = 110, /**< Reserved */ Reserved111_IRQn = 111, /**< Reserved */ WDOG1_IRQn = 112, /**< WDOG1 timer reset interrupt request. */ WDOG2_IRQn = 113, /**< WDOG2 timer reset interrupt request. */ KPP_IRQn = 114, /**< Key Pad interrupt request. */ PWM1_IRQn = 115, /**< hasRegInstance(`PWM1`)?`Cumulative interrupt line for PWM1. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */ PWM2_IRQn = 116, /**< hasRegInstance(`PWM2`)?`Cumulative interrupt line for PWM2. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */ PWM3_IRQn = 117, /**< hasRegInstance(`PWM3`)?`Cumulative interrupt line for PWM3. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */ PWM4_IRQn = 118, /**< hasRegInstance(`PWM4`)?`Cumulative interrupt line for PWM4. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */ CCM_IRQ1_IRQn = 119, /**< CCM interrupt request ipi_int_1. */ CCM_IRQ2_IRQn = 120, /**< CCM interrupt request ipi_int_2. */ GPC_IRQn = 121, /**< GPC interrupt request 1. */ Reserved122_IRQn = 122, /**< Reserved */ SRC_IRQn = 123, /**< SRC interrupt request src_ipi_int_1. */ Reserved124_IRQn = 124, /**< Reserved */ Reserved125_IRQn = 125, /**< Reserved */ CPU_PerformanceUnit_IRQn = 126, /**< Performance Unit interrupt ~ipi_pmu_irq_b. */ CPU_CTI_Trigger_IRQn = 127, /**< CTI trigger outputs interrupt ~ipi_cti_irq_b. */ SRC_Combined_IRQn = 128, /**< Combined CPU wdog interrupts (4x) out of SRC. */ SAI1_IRQn = 129, /**< SAI1 interrupt request. */ SAI2_IRQn = 130, /**< SAI2 interrupt request. */ Reserved131_IRQn = 131, /**< Reserved */ ADC1_IRQn = 132, /**< ADC1 interrupt request. */ ADC_5HC_IRQn = 133, /**< ADC_5HC interrupt request. */ Reserved134_IRQn = 134, /**< Reserved */ Reserved135_IRQn = 135, /**< Reserved */ SJC_IRQn = 136, /**< SJC interrupt from General Purpose register. */ CAAM_Job_Ring0_IRQn = 137, /**< CAAM job ring 0 interrupt ipi_caam_irq0. */ CAAM_Job_Ring1_IRQn = 138, /**< CAAM job ring 1 interrupt ipi_caam_irq1. */ QSPI_IRQn = 139, /**< QSPI1 interrupt request ipi_int_ored. */ TZASC_IRQn = 140, /**< TZASC (PL380) interrupt request. */ GPT2_IRQn = 141, /**< Logical OR of GPT2 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2 and 3 interrupt lines. */ CAN1_IRQn = 142, /**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */ CAN2_IRQn = 143, /**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */ Reserved144_IRQn = 144, /**< Reserved */ Reserved145_IRQn = 145, /**< Reserved */ PWM5_IRQn = 146, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */ PWM6_IRQn = 147, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */ PWM7_IRQn = 148, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */ PWM8_IRQn = 149, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */ ENET1_IRQn = 150, /**< ENET1 interrupt */ ENET1_1588_IRQn = 151, /**< ENET1 1588 Timer interrupt [synchronous] request. */ ENET2_IRQn = 152, /**< ENET2 interrupt */ ENET2_1588_IRQn = 153, /**< MAC 0 1588 Timer interrupt [synchronous] request. */ Reserved154_IRQn = 154, /**< Reserved */ Reserved155_IRQn = 155, /**< Reserved */ Reserved156_IRQn = 156, /**< Reserved */ Reserved157_IRQn = 157, /**< Reserved */ Reserved158_IRQn = 158, /**< Reserved */ PMU_IRQ2_IRQn = 159 /**< Brown-out event on either core, gpu or soc regulators. */ } IRQn_Type;

3.3.3、 GIC 邏輯分塊

GIC 架構分為了兩個邏輯塊: Distributor 和 CPU Interface,也就是分發器端和 CPU 接口端。這兩個邏輯塊的含義如下:

Distributor(分發器端): 從GICV2 的邏輯圖中可以看出,此邏輯塊負責處理各個中斷事件的分發問題,也就是中斷事件應該發送到哪個 CPU Interface 上去。分發器收集所有的中斷源,可以控制每個中斷的優先級,它總是將優先級最高的中斷事件發送到 CPU 接口端。分發器端要做的主要工作如下:

  • ①、全局中斷使能控制。
  • ②、控制每一個中斷的使能或者關閉。
  • ③、設置每個中斷的優先級。
  • ④、設置每個中斷的目標處理器列表。
  • ⑤、設置每個外部中斷的觸發模式:電平觸發或邊沿觸發。
  • ⑥、設置每個中斷屬於組 0 還是組 1。

CPU Interface(CPU 接口端): CPU 接口端聽名字就知道是和 CPU Core 相連接的,因此在圖中每個 CPU Core 都可以在 GIC 中找到一個與之對應的 CPU Interface。 CPU 接口端就是分發器和 CPU Core 之間的橋梁, CPU 接口端主要工作如下:
①、使能或者關閉發送到 CPU Core 的中斷請求信號。
②、應答中斷。
③、通知中斷處理完成。
④、設置優先級掩碼,通過掩碼來設置哪些中斷不需要上報給 CPU Core。
⑤、定義搶占策略。
⑥、當多個中斷到來的時候,選擇優先級最高的中斷通知給 CPU Core。

構體里面的寄存器分為了分發器端和 CPU 接口端,寄存器定義如下所示:

typedef struct { uint32_t RESERVED0[1024]; __IOM uint32_t D_CTLR; /*!< Offset: 0x1000 (R/W) Distributor Control Register */ __IM uint32_t D_TYPER; /*!< Offset: 0x1004 (R/ ) Interrupt Controller Type Register */ __IM uint32_t D_IIDR; /*!< Offset: 0x1008 (R/ ) Distributor Implementer Identification Register */ uint32_t RESERVED1[29]; __IOM uint32_t D_IGROUPR[16]; /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */ uint32_t RESERVED2[16]; __IOM uint32_t D_ISENABLER[16]; /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */ uint32_t RESERVED3[16]; __IOM uint32_t D_ICENABLER[16]; /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */ uint32_t RESERVED4[16]; __IOM uint32_t D_ISPENDR[16]; /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */ uint32_t RESERVED5[16]; __IOM uint32_t D_ICPENDR[16]; /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */ uint32_t RESERVED6[16]; __IOM uint32_t D_ISACTIVER[16]; /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */ uint32_t RESERVED7[16]; __IOM uint32_t D_ICACTIVER[16]; /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */ uint32_t RESERVED8[16]; __IOM uint8_t D_IPRIORITYR[512]; /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */ uint32_t RESERVED9[128]; __IOM uint8_t D_ITARGETSR[512]; /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */ uint32_t RESERVED10[128]; __IOM uint32_t D_ICFGR[32]; /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */ uint32_t RESERVED11[32]; __IM uint32_t D_PPISR; /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */ __IM uint32_t D_SPISR[15]; /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */ uint32_t RESERVED12[112]; __OM uint32_t D_SGIR; /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */ uint32_t RESERVED13[3]; __IOM uint8_t D_CPENDSGIR[16]; /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */ __IOM uint8_t D_SPENDSGIR[16]; /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */ uint32_t RESERVED14[40]; __IM uint32_t D_PIDR4; /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */ __IM uint32_t D_PIDR5; /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */ __IM uint32_t D_PIDR6; /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */ __IM uint32_t D_PIDR7; /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */ __IM uint32_t D_PIDR0; /*!< Offset: 0x1FE0 (R/ ) Peripheral ID0 Register */ __IM uint32_t D_PIDR1; /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */ __IM uint32_t D_PIDR2; /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */ __IM uint32_t D_PIDR3; /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */ __IM uint32_t D_CIDR0; /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */ __IM uint32_t D_CIDR1; /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */ __IM uint32_t D_CIDR2; /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */ __IM uint32_t D_CIDR3; /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */ __IOM uint32_t C_CTLR; /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */ __IOM uint32_t C_PMR; /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */ __IOM uint32_t C_BPR; /*!< Offset: 0x2008 (R/W) Binary Point Register */ __IM uint32_t C_IAR; /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */ __OM uint32_t C_EOIR; /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */ __IM uint32_t C_RPR; /*!< Offset: 0x2014 (R/ ) Running Priority Register */ __IM uint32_t C_HPPIR; /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */ __IOM uint32_t C_ABPR; /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */ __IM uint32_t C_AIAR; /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */ __OM uint32_t C_AEOIR; /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */ __IM uint32_t C_AHPPIR; /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */ uint32_t RESERVED15[41]; __IOM uint32_t C_APR0; /*!< Offset: 0x20D0 (R/W) Active Priority Register */ uint32_t RESERVED16[3]; __IOM uint32_t C_NSAPR0; /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */ uint32_t RESERVED17[6]; __IM uint32_t C_IIDR; /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */ uint32_t RESERVED18[960]; __OM uint32_t C_DIR; /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */ } GIC_Type; 

 

結構體 GIC_Type 就是 GIC 控制器,列舉出了 GIC 控制器的所有寄存器,可以通過結構體 GIC_Type 來訪問 GIC 的所有寄存器。

第 5 行是 GIC 的分發器端相關寄存器,其相對於 GIC 基地址偏移為 0X1000,因此我們獲取到 GIC 基地址以后只需要加上 0X1000 即可訪問 GIC 分發器端寄存器。

第 51 行是 GIC 的 CPU 接口端相關寄存器,其相對於 GIC 基地址的偏移為 0X2000,同樣的,獲取到 GIC 基地址以后只需要加上 0X2000 即可訪問 GIC 的 CPU 接口段寄存器。那么問題來了? GIC 控制器的寄存器基地址在哪里呢?這個就需要用到 Cortex-A 的 CP15 協處理器了

3.3、CP15 協處理器

關 於 CP15 協處理 器和其 相關寄存 器的詳細 內容 請參考下 面兩份文 檔: 《 ARMArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》 第 1469 頁“B3.17 Oranizationof the CP15 registers in a VMSA implementation”。《Cortex-A7 Technical ReferenceManua.pdf》 第55 頁“Capter 4 System Control”。

CP15 協處理器一般用於存儲系統管理,但是在中斷中也會使用到, CP15 協處理器一共有16 個 32 位寄存器。 CP15 協處理器的訪問通過如下幾個指令完成:

指令 描述
MRC 將 CP15 協處理器中的寄存器數據讀到 ARM 寄存器中。
MCR 將 ARM 寄存器的數據寫入到 CP15 協處理器寄存器中。

指令格式:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2> 
  • 1
參數 描述
cond 指令執行的條件碼,如果忽略的話就表示無條件執行。
opc1 協處理器要執行的操作碼。
Rt ARM 源寄存器,要寫入到 CP15 寄存器的數據就保存在此寄存器中。
CRn CP15 協處理器的目標寄存器。
CRm 協處理器中附加的目標寄存器或者源操作數寄存器,如果不需要附加信息就將CRm 設置為 C0,否則結果不可預測。
opc2 可選的協處理器特定操作碼,當不需要的時候要設置為 0。

MRC 的指令格式和 MCR 一樣,只不過在 MRC 指令中 Rt 就是目標寄存器,也就是從CP15 指定寄存器讀出來的數據會保存在 Rt 中。而 CRn 就是源寄存器,也就是要讀取的寫處理器寄存器。

假如我們要將 CP15 中 C0 寄存器的值讀取到 R0 寄存器中,那么就可以使用如下命令:

MRC p15, 0, r0, c0, c0, 0 
  • 1

CP15 協處理器有 16 個 32 位寄存器, c0~c15

3.3.1、c0 寄存器

CP15 協處理器有 16 個 32 位寄存器, c0~c15,在使用 MRC 或者 MCR 指令訪問這 16 個
寄存器的時候,指令中的 CRn、 opc1、 CRm 和 opc2 通過不同的搭配,其得到的寄存器含義是
不同的。比如 c0 在不同的搭配情況下含義如下圖所示:
寄存器不同搭配含義
在圖中當 MRC/MCR 指令中的 CRn=c0, opc1=0, CRm=c0, opc2=0 的時候就表示此時的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,這個也是 c0 的基本作用。對於 Cortex-A7內核來說, c0 作為 MDIR 寄存器的時候其含義如圖所示:
MIDR 寄存器結構圖
在圖中各位所代表的含義如下:

含義
bit31:24 廠商編號, 0X41, ARM。
bit23:20 內核架構的主版本號, ARM 內核版本一般使用 rnpn 來表示,比如 r0p1,其中 r0后面的 0 就是內核架構主版本號。
bit19:16 架構代碼, 0XF, ARMv7 架構。
bit15:4 內核版本號, 0XC07, Cortex-A7 MPCore 內核。
bit3:0 內核架構的次版本號, rnpn 中的 pn,比如 r0p1 中 p1 后面的 1 就是次版本號。

3.3.2、c1 寄存器

c1 寄存器同樣通過不同的配置,其代表的含義也不同,如圖所示:
在這里插入圖片描述
在圖中當 MRC/MCR 指令中的 CRn=c1, opc1=0, CRm=c0, opc2=0 的時候就表示此時的 c1 就是 SCTLR 寄存器,也就是系統控制寄存器,這個是 c1 的基本作用。 SCTLR 寄存器主要是完成控制功能的,比如使能或者禁止 MMU、 I/D Cache 等, c1 作為 SCTLR 寄存器的時候其含義如下圖所示:
在這里插入圖片描述
SCTLR 的位比較多,我們就只看本次會用到的幾個位:

描述
bit13 V , 中斷向量表基地址選擇位,為 0 的話中斷向量表基地址為 0X00000000,軟件可以使用 VBAR 來重映射此基地址,也就是中斷向量表重定位。為 1 的話中斷向量表基地址為0XFFFF0000,此基地址不能被重映射。
bit12 I, I Cache 使能位,為 0 的話關閉 I Cache,為 1 的話使能 I Cache。
bit11 Z,分支預測使能位,如果開啟 MMU 的話,此位也會使能。
bit10 SW, SWP 和 SWPB 使能位,當為 0 的話關閉 SWP 和 SWPB 指令,當為 1 的時候就使能 SWP 和 SWPB 指令。
bit9:3 未使用,保留。
bit2 C, D Cache 和緩存一致性使能位,為 0 的時候禁止 D Cache 和緩存一致性,為 1 時使能。
bit1 A,內存對齊檢查使能位,為 0 的時候關閉內存對齊檢查,為 1 的時候使能內存對齊檢查。
bit0 M, MMU 使能位,為 0 的時候禁止 MMU,為 1 的時候使能 MMU。

如果要讀寫 SCTLR 的話,就可以使用如下命令:

MRC p15, 0, <Rt>, c1, c0, 0 ;讀取 SCTLR 寄存器,數據保存到 Rt 中。 MCR p15, 0, <Rt>, c1, c0, 0 ;將 Rt 中的數據寫到 SCTLR(c1)寄存器中。 
  • 1
  • 2

3.3.3、 c12 寄存器

c12 寄存器通過不同的配置,其代表的含義也不同,如圖所示:
在這里插入圖片描述
在上圖中當 MRC/MCR 指令中的 CRn=c12, opc1=0, CRm=c0, opc2=0 的時候就表示此時 c12 為 VBAR 寄存器,也就是向量表基地址寄存器。設置中斷向量表偏移的時候就需要將新的中斷向量表基地址寫入 VBAR 中,比如在前面的例程中,代碼鏈接的起始地址為0X87800000,而中斷向量表肯定要放到最前面,也就是 0X87800000 這個地址處。所以就需要設置 VBAR 為 0X87800000,設置命令如下:

ldr r0, =0X87800000 ; r0=0X87800000 MCR p15, 0, r0, c12, c0, 0 ;將 r0 里面的數據寫入到 c12 中,即 c12=0X87800000 
  • 1
  • 2

3.3.4、c15 寄存器

c15 寄存器也可以通過不同的配置得到不同的含義
在這里插入圖片描述
在圖中,我們需要 c15 作為 CBAR 寄存器,因為 GIC 的基地址就保存在 CBAR中,我們可以通過如下命令獲取到 GIC 基地址:

MRC p15, 4, r1, c15, c0, 0 ; 獲取 GIC 基礎地址,基地址保存在 r1 中。 
  • 1

獲取到 GIC 基地址以后就可以設置 GIC 相關寄存器了,比如我們可以讀取當前中斷 ID,當前中斷 ID 保存在 GICC_IAR 中,寄存器 GICC_IAR 屬於 CPU 接口端寄存器,寄存器地址相對於 CPU 接口端起始地址的偏移為 0XC,因此獲取當前中斷 ID 的代碼如下:

MRC p15, 4, r1, c15, c0, 0 ;獲取 GIC 基地址 ADD r1, r1, #0X2000 ;GIC 基地址加 0X2000 得到 CPU 接口端寄存器起始地址 LDR r0, [r1, #0XC] ;讀取 CPU 接口端起始地址+0XC 處的寄存器值,也就是寄存器GIC_IAR 的值 
  • 1
  • 2
  • 3
  • 4

3.3.5、 總結

寄存器 作用
c0 寄存器可以獲取到處理器內核信息;
c1 寄存器可以使能或禁止 MMU、 I/D Cache 等;
c12 寄存器可以設置中斷向量偏移;
c15 寄存器可以獲取 GIC 基地址。

3.4、中斷使能

中斷使能包括兩部分,一個是 IRQ 或者 FIQ 總中斷使能,另一個就是 ID0~ID1019 這 1020個中斷源的使能。

3.4.1、IRQ 和 FIQ 總中斷使能

IRQ 和 FIQ 分別是外部中斷和快速中斷的總開關,就類似家里買的進戶總電閘,然后ID0~ID1019 這 1020 個中斷源就類似家里面的各個電器開關。要想開電視,那肯定要保證進戶總電閘是打開的,因此要想使用 I.MX6U 上的外設中斷就必須先打開 IRQ 中斷(本教程不使用FIQ)。在“6.3.2 程序狀態寄存器”小節已經講過了,寄存器 CPSR 的 I=1 禁止 IRQ,當 I=0 使能 IRQ; F=1 禁止 FIQ, F=0 使能 FIQ。我們還有更簡單的指令來完成 IRQ 或者 FIQ 的使能和禁止,圖表所示:

指令 描述
cpsid i 禁止 IRQ 中斷。
cpsie i 使能 IRQ 中斷。
cpsid f 禁止 FIQ 中斷。
cpsie f 使能 FIQ 中斷

3.4.2、 ID0~ID1019 中斷使能和禁止

GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用來完成外部中斷的使能和禁止,對於 Cortex-A7 內核來說中斷 ID 只使用了 512 個。一個 bit 控制一個中斷 ID 的使能,那么就需要 512/32=16 個 GICD_ISENABLER 寄存器來完成中斷的使能。同理,也需要 16 個GICD_ICENABLER 寄存器來完成中斷的禁止。其中 GICD_ISENABLER0 的 bit[15:0]對應ID15~0 的 SGI 中斷, GICD_ISENABLER0 的 bit[31:16]對應 ID31~16 的 PPI 中斷。剩下的GICD_ISENABLER1~GICD_ISENABLER15 就是控制 SPI 中斷的。

3.5、中斷優先級設置

3.5.1、優先級數配置

學過 STM32 都知道 Cortex-M 的中斷優先級分為搶占優先級和子優先級,兩者是可以配置的。同樣的 Cortex-A7 的中斷優先級也可以分為搶占優先級和子優先級,兩者同樣是可以配置的。 Cortex-A7 最多可以支持 256 個優先級,數字越小,優先級越高!半導體廠商自行決定選擇多少個優先級。 I.MX6U 選擇了 32 個優先級。在使用中斷的時候需要初始化 GICC_PMR 寄存器,此寄存器用來決定使用幾級優先級,寄存器結構如圖所示:
在這里插入圖片描述
GICC_PMR 寄存器只有低 8 位有效,這 8 位最多可以設置 256 個優先級,其他優先級數設置如下表所示:

bit[7:0] 優先級數
11111111 256 個優先級。
11111110 128 個優先級。
11111100 64 個優先級。
11111000 32 個優先級
11110000 16 個優先級。

I.MX6U 支持 32 個優先級,所以 GICC_PMR 要設置為 0b11111000。

3.5.2、搶占優先級和子優先級位數設置

搶占優先級和子優先級各占多少位是由寄存器 GICC_BPR 來決定的, GICC_BPR 寄存器結構如下圖所示:
在這里插入圖片描述
寄存器 GICC_BPR 只有低 3 位有效,其值不同,搶占優先級和子優先級占用的位數也不同,配置如下表所示:

Binary Point 搶占優先級域 子優先級域 描述
0 [7:1] [0] 7 級搶占優先級, 1 級子優先級。
1 [7:2] [1:0] 6 級搶占優先級, 2 級子優先級。
2 [7:3] [2:0] 5 級搶占優先級, 3 級子優先級。
3 [7:4] [3:0] 4 級搶占優先級, 4 級子優先級。
4 [7:5] [4:0] 3 級搶占優先級, 5 級子優先級。
5 [7:6] [5:0] 2 級搶占優先級, 6 級子優先級。
6 [7:7] [6:0] 1 級搶占優先級, 7 級子優先級。
7 [7:0] 0 級搶占優先級, 8 級子優先級

為了簡單起見,一般將所有的中斷優先級位都配置為搶占優先級,比如 I.MX6U 的優先級位數為 5(32 個優先級),所以可以設置 Binary point 為 2,表示 5 個優先級位全部為搶占優先級。

3.6.3、優先級設置

前面已經設置好了 I.MX6U 一共有 32 個搶占優先級,數字越小優先級越高。具體要使用某個中斷的時候就可以設置其優先級為 0~31。某個中斷 ID 的中斷優先級設置由寄存器D_IPRIORITYR 來完成,前面說了 Cortex-A7 使用了 512 個中斷 ID,每個中斷 ID 配有一個優先級寄存器,所以一共有 512 個 D_IPRIORITYR 寄存器。如果優先級個數為 32 的話,使用寄存器 D_IPRIORITYR 的 bit7:4 來設置優先級,也就是說實際的優先級要左移 3 位。比如要設置ID40 中斷的優先級為 5,示例代碼如下:

GICD_IPRIORITYR[40] = 5 << 3; 
  • 1

3.6.4、總結

優先級設置主要有三部分:

  • ①、設置寄存器 GICC_PMR,配置優先級個數,比如 I.MX6U 支持 32 級優先級。
  • ②、設置搶占優先級和子優先級位數,一般為了簡單起見,會將所有的位數都設置為搶占優先級。
  • ③、設置指定中斷 ID 的優先級,也就是設置外設優先級。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM