1. 程式人生 > 實用技巧 >Linux之ARM Cortex-A7 中斷系統詳解【轉】

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 中斷的關係如圖 所示:

在上圖中,左側的 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 的邏輯圖如圖下所示:

圖中左側部分就是中斷源,中間部分就是 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”小節,中斷源如表 所示:

IRQID中斷源描述
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 暫存器的時候其含義如圖所示:

在圖中各位所代表的含義如下:

含義
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 的優先順序,也就是設定外設優先順序。