Cortex-A7 IO 輸入中斷系統分析

Cortex-A7中斷向量表是在代碼的最前面,Cortex-A7 內核有 8 個異常中斷,這 8 個異常中斷的中斷向量表如表所示:

Cortex-A7 IO 輸入中斷系統分析

Cortex-A7 中斷向量表

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

Cortex-A7 IO 輸入中斷系統分析

外部中斷和 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 文件最前面,中斷向量表如下:

1 .global _start /* 全局標號 */

2

3 _start:

4 ldr pc, =Reset_Handler /* 復位中斷 */

5 ldr pc, =Undefined_Handler /* 未定義指令中斷 */

6 ldr pc, =SVC_Handler /* SVC(Supervisor)中斷 */

7 ldr pc, =PrefAbort_Handler /* 預取終止中斷 */

8 ldr pc, =DataAbort_Handler /* 數據終止中斷 */

9 ldr pc, =NotUsed_Handler /* 未使用中斷 */

10 ldr pc, =IRQ_Handler /* IRQ 中斷 */

11 ldr pc, =FIQ_Handler /* FIQ(快速中斷)未定義中斷 */

12

13 /* 復位中斷 */

14 Reset_Handler:


15 /* 復位中斷具體處理過程 */

16

17 /* 未定義中斷 */

18 Undefined_Handler:

19 ldr r0, =Undefined_Handler

20 bx r0

21

22 /* SVC 中斷 */

23 SVC_Handler:

24 ldr r0, =SVC_Handler

25 bx r0

26

27 /* 預取終止中斷 */

28 PrefAbort_Handler:

29 ldr r0, =PrefAbort_Handler

30 bx r0 31

32 /* 數據終止中斷 */

33 DataAbort_Handler:

34 ldr r0, =DataAbort_Handler

35 bx r0

36

37 /* 未使用的中斷 */

38 NotUsed_Handler:

39

40 ldr r0, =NotUsed_Handler

41 bx r0 42

43 /* IRQ 中斷!重點!!!!! */

44 IRQ_Handler:

45 /* 復位中斷具體處理過程 */

46

47 /* FIQ 中斷 */

48 FIQ_Handler:

49 ldr r0, =FIQ_Handler

50 bx r0

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

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

I.MX6U(Cortex-A)的中斷控制器叫做 GIC,關於GIC 的詳細內容請參考文檔《ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf》。

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,他們之間的關係如圖所示:

Cortex-A7 IO 輸入中斷系統分析

中斷示意圖

在圖中,GIC 接收眾多的外部中斷,然後對齊進行處理,最終就只通過四個信號報給ARM 內核,這四個信號的含義如下:

VFIQ:虛擬快速 FIQ。

VIRQ:虛擬快速 IRQ。

FIQ:快速中斷 IRQ。

IRQ:外部中斷 IRQ。

VFIQ 和 VIRQ 是針對虛擬化的,我們討論虛擬化,剩下的就是 FIQ 和 IRQ 了,我們前面都講了很多次了。本教程我們只使用 IRQ,所以相當於 GIC 最終向 ARM 內核就上報一個 IRQ信號。那麼GIC 是如何完成這個工作的呢?GICV2 的邏輯圖如圖所示:


Cortex-A7 IO 輸入中斷系統分析

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 中斷來完成多核之間的通信。

中斷源有很多,為了區分這些不同的中斷源肯定要給他們分配一個唯一 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 分配給 SPI,像 GPIO 中斷、串口中斷等這些外部中斷 ,至於具體到某個 ID 對應哪個中斷那就由半導體廠商根據實際情況去定義了。比如 I.MX6U 的總共使用了 128 箇中斷 ID,加上前面屬於 PPI 和 SGI 的 32 個 ID,I.MX6U 的中斷源共有 128+32=160個,這 128 箇中斷 ID 對應的中斷在《I.MX6ULL 參考手冊》的“3.2 Cortex A7 interrupts”小節,中斷源如表所示:

Cortex-A7 IO 輸入中斷系統分析

I.MX6U 中斷源

限於篇幅原因,表中並沒有給出 I.MX6U 完整的中斷源,完整的中斷源自行查閱《I.MX6ULL 參考手冊》的 3.2 小節。打開裸機例程“9_int”,我們前面移植了 NXP 官方 SDK中的文件 MCIMX6Y2C.h,在此文件中定義了一個枚舉類型 IRQn_Type,此枚舉類型就枚舉出了 I.MX6U 的所有中斷,代碼如下所示:

1 #define NUMBER_OF_INT_VECTORS 160 /* 中斷源 160 個,SGI+PPI+SPI*/

2

3 typedef enum IRQn {

4 /* Auxiliary constants */

5 NotAvail_IRQn = -128,

6

7 /* Core interrupts */

8 Software0_IRQn = 0,

9 Software1_IRQn = 1,

10 Software2_IRQn = 2,

11 Software3_IRQn = 3,

12 Software4_IRQn = 4,

13 Software5_IRQn = 5,

14 Software6_IRQn = 6,

15 Software7_IRQn = 7,

16 Software8_IRQn = 8,

17 Software9_IRQn = 9,

18 Software10_IRQn = 10,

19 Software11_IRQn = 11,

20 Software12_IRQn = 12,

21 Software13_IRQn = 13,

22 Software14_IRQn = 14,

23 Software15_IRQn = 15,

24 VirtualMaintenance_IRQn = 25,

25 HypervisorTimer_IRQn = 26,


26 VirtualTimer_IRQn = 27,

27 LegacyFastInt_IRQn = 28,

28 SecurePhyTimer_IRQn = 29,

29 NonSecurePhyTimer_IRQn = 30,

30 LegacyIRQ_IRQn = 31, 31

32 /* Device specific interrupts */

33 IOMUXC_IRQn = 32,

34 DAP_IRQn = 33,

35 SDMA_IRQn = 34,

36 TSC_IRQn = 35,

37 SNVS_IRQn = 36,

…… ...... ......

151 ENET2_1588_IRQn = 153,

152 Reserved154_IRQn = 154,

153 Reserved155_IRQn = 155,

154 Reserved156_IRQn = 156,

155 Reserved157_IRQn = 157,

156 Reserved158_IRQn = 158,

157 PMU_IRQ2_IRQn = 159 158
} IRQn_Type;

3、GIC 邏輯分塊

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

Distributor(分發器端):從圖中可以看出,此邏輯塊負責處理各個中斷事件的分發問題,也就是中斷事件應該發送到哪個 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。

文件 core_ca7.h 定義了GIC 結構體,此結構體裡面的寄存器分為了分發器端和CPU 接口端,寄存器定義如下所示:

/*

* GIC 寄存器描述結構體,

* GIC 分為分發器端和 CPU 接口端

*/

1 typedef struct 2 {

3 /* 分發器端寄存器 */

4 uint32_t RESERVED0[1024];

5 IOM uint32_t D_CTLR; /* Offset: 0x1000 (R/W) */

6 IM uint32_t D_TYPER; /* Offset: 0x1004 (R/ ) */

7 IM uint32_t D_IIDR; /* Offset: 0x1008 (R/ ) */

8 uint32_t RESERVED1[29];

9 IOM uint32_t D_IGROUPR[16]; /* Offset: 0x1080 - 0x0BC (R/W) */

10 uint32_t RESERVED2[16];

11 IOM uint32_t D_ISENABLER[16];/* Offset: 0x1100 - 0x13C (R/W) */

12 uint32_t RESERVED3[16];

13 IOM uint32_t D_ICENABLER[16];/* Offset: 0x1180 - 0x1BC (R/W) */

14 uint32_t RESERVED4[16];

15 IOM uint32_t D_ISPENDR[16]; /* Offset: 0x1200 - 0x23C (R/W) */

16 uint32_t RESERVED5[16];

17 IOM uint32_t D_ICPENDR[16]; /* Offset: 0x1280 - 0x2BC (R/W) */

18 uint32_t RESERVED6[16];


19 IOM uint32_t D_ISACTIVER[16];/* Offset: 0x1300 - 0x33C (R/W) */

20 uint32_t RESERVED7[16];

21 IOM uint32_t D_ICACTIVER[16];/* Offset: 0x1380 - 0x3BC (R/W) */

22 uint32_t RESERVED8[16];

23 IOM uint8_t D_IPRIORITYR[512];/* Offset: 0x1400 - 0x5FC (R/W) */

24 uint32_t RESERVED9[128];

25 IOM uint8_t D_ITARGETSR[512];/* Offset: 0x1800 - 0x9FC (R/W) */

26 uint32_t RESERVED10[128];

27 IOM uint32_t D_ICFGR[32]; /* Offset: 0x1C00 - 0xC7C (R/W) */

28 uint32_t RESERVED11[32];

29 IM uint32_t D_PPISR; /* Offset: 0x1D00 (R/ ) */

30 IM uint32_t D_SPISR[15]; /* Offset: 0x1D04 - 0xD3C (R/ ) */

31 uint32_t RESERVED12[112];

32 OM uint32_t D_SGIR; /* Offset: 0x1F00 ( /W) */

33 uint32_t RESERVED13[3];

34 IOM uint8_t D_CPENDSGIR[16];/* Offset: 0x1F10 - 0xF1C (R/W) */

35 IOM uint8_t D_SPENDSGIR[16];/* Offset: 0x1F20 - 0xF2C (R/W) */

36 uint32_t RESERVED14[40];

37 IM uint32_t D_PIDR4; /* Offset: 0x1FD0 (R/ ) */

38 IM uint32_t D_PIDR5; /* Offset: 0x1FD4 (R/ ) */

39 IM uint32_t D_PIDR6; /* Offset: 0x1FD8 (R/ ) */

40 IM uint32_t D_PIDR7; /* Offset: 0x1FDC (R/ ) */

41 IM uint32_t D_PIDR0; /* Offset: 0x1FE0 (R/ ) */

42 IM uint32_t D_PIDR1; /* Offset: 0x1FE4 (R/ ) */

43 IM uint32_t D_PIDR2; /* Offset: 0x1FE8 (R/ ) */


44 IM uint32_t D_PIDR3; /* Offset: 0x1FEC (R/ ) */

45 IM uint32_t D_CIDR0; /* Offset: 0x1FF0 (R/ ) */

46 IM uint32_t D_CIDR1; /* Offset: 0x1FF4 (R/ ) */

47 IM uint32_t D_CIDR2; /* Offset: 0x1FF8 (R/ ) */

48 IM uint32_t D_CIDR3; /* Offset: 0x1FFC (R/ ) */ 49

50 /* CPU 接口端寄存器 */

51 IOM uint32_t C_CTLR; /* Offset: 0x2000 (R/W) */

52 IOM uint32_t C_PMR; /* Offset: 0x2004 (R/W) */

53 IOM uint32_t C_BPR; /* Offset: 0x2008 (R/W) */

54 IM uint32_t C_IAR; /* Offset: 0x200C (R/ ) */

55 OM uint32_t C_EOIR; /* Offset: 0x2010 ( /W) */

56 IM uint32_t C_RPR; /* Offset: 0x2014 (R/ ) */

57 IM uint32_t C_HPPIR; /* Offset: 0x2018 (R/ ) */

58 IOM uint32_t C_ABPR; /* Offset: 0x201C (R/W) */

59 IM uint32_t C_AIAR; /* Offset: 0x2020 (R/ ) */

60 OM uint32_t C_AEOIR; /* Offset: 0x2024 ( /W) */

61 IM uint32_t C_AHPPIR; /* Offset: 0x2028 (R/ ) */

62 uint32_t RESERVED15[41];

63 IOM uint32_t C_APR0; /* Offset: 0x20D0 (R/W) */

64 uint32_t RESERVED16[3];

65 IOM uint32_t C_NSAPR0; /* Offset: 0x20E0 (R/W) */

66 uint32_t RESERVED17[6];

67 IM uint32_t C_IIDR; /* Offset: 0x20FC (R/ ) */

68 uint32_t RESERVED18[960];

69 OM uint32_t C_DIR; /* Offset: 0x3000 ( /W) */


70 } 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 協處理器了。關於CP15 協處理器和其相關寄存器的詳細內容請參考下面兩份文檔:

《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》第 1469 頁“B3.17 Oranization of 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 協處理器寄存器中。

MRC 就是讀CP15 寄存器,MCR 就是寫 CP15 寄存器,MCR 指令格式如下:

MCR{cond} p15, <opc1>, , , , <opc2> /<opc2>/<opc1>

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

CP15 協處理器有 16 個 32 位寄存器,c0~c15,本章來看一下c0、c1、c12 和 c15 這四個寄存器,因為我們本章實驗要用到這四個寄存器,其他的寄存器大家參考上面的兩個文檔即可。

1、c0 寄存器

CP15 協處理器有 16 個 32 位寄存器,c0~c15,在使用 MRC 或者 MCR 指令訪問這 16 個寄存器的時候,指令中的CRn、opc1、CRm 和opc2

通過不同的搭配,其得到的寄存器含義是不同的。比如c0 在不同的搭配情況下含義如圖所示:


Cortex-A7 IO 輸入中斷系統分析

c0 寄存器不同搭配含義

在圖中當 MRC/MCR 指令中的CRn=c0,opc1=0,CRm=c0,opc2=0 的時候就表示此時的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,這個也是 c0 的基本作用。對於Cortex-A7內核來說,c0 作為 MDIR 寄存器的時候其含義如圖所示:

Cortex-A7 IO 輸入中斷系統分析

c0 作為 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 就是次版本號。

2、c1 寄存器

c1 寄存器同樣通過不同的配置,其代表的含義也不同,如圖所示:

Cortex-A7 IO 輸入中斷系統分析

c1 寄存器不同搭配含義

在圖中當 MRC/MCR 指令中的CRn=c1,opc1=0,CRm=c0,opc2=0 的時候就表示此時的 c1 就是 SCTLR 寄存器,也就是系統控制寄存器,這個是 c1 的基本作用。SCTLR 寄存器主要是完成控制功能的,比如使能或者禁止MMU、I/D Cache 等,c1 作為 SCTLR 寄存器的時候其含義如圖所示:

Cortex-A7 IO 輸入中斷系統分析

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, , c1, c0, 0 ;讀取 SCTLR 寄存器,數據保存到 Rt 中。

MCR p15, 0, , c1, c0, 0 ;將 Rt 中的數據寫到 SCTLR(c1)寄存器中。

2、c12 寄存器

c12 寄存器通過不同的配置,其代表的含義也不同,如圖所示:

Cortex-A7 IO 輸入中斷系統分析

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

3、c15 寄存器

c15 寄存器也可以通過不同的配置得到不同的含義,參考文檔《Cortex-A7 Technical ReferenceManua.pdf》第 68 頁“4.2.16 c15 registers”,其配置如圖所示:

Cortex-A7 IO 輸入中斷系統分析

c15 寄存器不同搭配含義

在圖中,我們需要 c15 作為 CBAR 寄存器,因為GIC 的基地址就保存在CBAR中,我們可以通過如下命令獲取到GIC 基地址:

MRC p15, 4, r1, c15, c0, 0 ; 獲取GIC 基礎地址,基地址保存在 r1 中。 獲取到 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 的值

關於CP15 協處理器就講解到這裡,簡單總結一下,通過 c0 寄存器可以獲取到處理器內核信息;通過c1 寄存器可以使能或禁止 MMU、I/D Cache 等;通過 c12 寄存器可以設置中斷向量偏移;通過c15 寄存器可以獲取 GIC 基地址。關於CP15 的其他寄存器,大家自行查閱本節前面列舉的 2 份ARM 官方資料。

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

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 的使能和禁止,圖表所示:

Cortex-A7 IO 輸入中斷系統分析

開關中斷指令

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 中斷的。

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

Cortex-A7 IO 輸入中斷系統分析

GICC_PMR 寄存器

GICC_PMR 寄存器只有低 8 位有效,這 8 位最多可以設置 256 個優先級,其他優先級數設置如表所示:

Cortex-A7 IO 輸入中斷系統分析

優先級數設置

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

2、搶佔優先級和子優先級位數設置

搶佔優先級和子優先級各佔多少位是由寄存器 GICC_BPR 來決定的,GICC_BPR 寄存器結構如圖所示:

Cortex-A7 IO 輸入中斷系統分析

GICC_BPR 寄存器結構圖

寄存器GICC_BPR 只有低3 位有效,其值不同,搶佔優先級和子優先級佔用的位數也不同,配置如表 所示:

Cortex-A7 IO 輸入中斷系統分析

GICC_BPR 配置表

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

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;

有關優先級設置的內容就講解到這裡,優先級設置主要有三部分:

①、設置寄存器 GICC_PMR,配置優先級個數,比如 I.MX6U 支持 32 級優先級。

②、設置搶佔優先級和子優先級位數,一般為了簡單起見,會將所有的位數都設置為搶佔優先級。

③、設置指定中斷 ID 的優先級,也就是設置外設優先級。


分享到:


相關文章: