第一部分:GIC的功能和組成
1. GIC要解決的根本問題
在一個復雜的片上系統(SoC)中,有非常多的硬件模塊(如定時器、串口、按鍵、DMA等),它們都需要在完成任務或遇到特定事件時通知CPU。同時,系統里可能有多個CPU核心。因此,必須有一個統一的、專門的硬件來管理這些中斷請求。它需要解決:
- 來源管理:管理成百上千個中斷源。
- 優先級仲裁:當多個中斷同時發生時,決定哪個更重要,應該先被處理。
- 中斷路由:決定將一個中斷發送給哪個CPU核心去處理。
- 狀態同步:確保CPU和中斷控制器之間的狀態是一致的,比如一個中斷正在被處理時,就不應該再次打擾CPU。
GIC (Generic Interrupt Controller) 就是 ARM 定義的用來解決以上所有問題的標準硬件模塊。
2. GIC的核心組件
根據文檔,GIC 主要由兩個功能模塊組成,它們的職責劃分非常清晰:
-
分發器 (Distributor)
- 職責:作為整個系統的中斷“管理中心”和“仲裁中心”。
- 具體工作:
- 接收所有中斷:系統中所有外設的中斷信號線都連接到 Distributor。
- 配置中斷屬性:軟件通過 Distributor 的寄存器來配置每個中斷的屬性,包括:
- 優先級 (Priority):定義中斷的重要程度。
- 目標CPU (Target):指定這個中斷可以發送給哪些CPU核心。
- 觸發方式 (Configuration):是電平觸發還是邊沿觸發。
- 使能/禁用 (Enable/Disable):控制一個中斷是否被允許。
- 仲裁:在所有已觸發且被使能的中斷中,找出優先級最高的那一個。
- 轉發:將這個最高優先級的中斷,轉發給其目標CPU對應的“CPU接口”。
-
CPU接口 (CPU Interface)
- 職責:作為 Distributor 和單個CPU核心之間的“信使”。每個CPU核心都有一個自己專屬的 CPU Interface。
- 具體工作:
- 接收中斷:從 Distributor 接收已經仲裁過的、最高優先級的中斷。
- 優先級過濾 (Masking):CPU Interface 內部可以設置一個優先級閾值。如果接收到的中斷的優先級不夠高(低于這個閾值 ),它就不會去打擾CPU。
- 通知CPU:如果中斷通過了優先級過濾,CPU Interface 會向其對應的CPU核心發出一個物理的中斷信號(IRQ 或 FIQ)。
- 提供中斷信息:當CPU響應中斷后,軟件可以通過讀取 CPU Interface 的寄存器來獲知當前中斷的ID號。
- 處理完成通信:當軟件處理完中斷后,通過寫入 CPU Interface 的寄存器來通知 GIC。
Distributor 負責多對多(多個中斷源到多個CPU)的管理和決策;
CPU Interface 負責一對一(Distributor 到單個CPU)的通信和執行。
好的,我們完全按照要求,用更直接、無修飾的語言來重新講解第二部分。我們將嚴格遵循硬件和軟件的交互行為,來描述一個中斷從產生到處理完成的全過程。
第二部分:從中斷觸發到CPU處理,發生了什么?
第1步:中斷源觸發與Pending狀態的設置
- 物理層:外部設備(如GPIO)的信號線電平發生變化,該信號被送入GIC Distributor。
- GIC Distributor的行為:
- Distributor硬件邏輯檢測到輸入信號。
- 硬件查詢該中斷ID對應的使能寄存器(
GICD_ISENABLERn
)。 - 若該中斷ID被使能,Distributor在內部狀態寄存器中,將該中斷ID的標志位設置為 Pending (掛起)。
- Pending狀態的定義:表示一個有效的中斷請求已經由GIC記錄,正在等待GIC的后續處理。
第2步:優先級仲裁
- 內部事件:Distributor的仲裁邏輯被觸發。這可能在有新的中斷變為Pending狀態時發生。
- GIC Distributor的行為:
- 硬件邏輯掃描所有處于Pending狀態的中斷ID。
- 對于每一個Pending的中斷ID,硬件會讀取其對應的優先級寄存器(
GICD_IPRIORITYRn
)中的值。 - 硬件比較所有這些優先級值,并選擇值最小(即優先級最高)的中斷ID作為本次處理的目標。
- 仲裁結果:Distributor確定了當前應該被處理的、優先級最高的中斷請求。
第3步:向CPU接口轉發與優先級過濾
- 事件:Distributor已選出最高優先級的中斷ID。
- GIC Distributor的行為:
- 硬件讀取該中斷ID的目標處理器寄存器(
GICD_ITARGETSRn
),確定目標CPU。 - Distributor通過內部總線,將這個最高優先級的中斷ID及其優先級信息,發送給目標CPU的專屬CPU Interface。
- 硬件讀取該中斷ID的目標處理器寄存器(
- GIC CPU Interface的行為:
- CPU Interface接收到來自Distributor的中斷請求。
- 硬件執行優先級過濾:它將收到的中斷的優先級,與CPU Interface內部的優先級屏蔽寄存器(
GICC_PMR
)的值,以及當前正在處理的中斷的優先級組(如果有的話)進行比較。 - 只有當新中斷的優先級嚴格高于過濾閾值時,該中斷才被接受。
- 若中斷被接受,CPU Interface會驅動連接到CPU核心的物理IRQ(或FIQ)信號線,使其變為有效電平。
第4步:CPU響應與中斷確認 (Acknowledge)
-
事件:CPU核心檢測到其IRQ信號線變為有效。
-
CPU硬件的行為:
- 若CPU的中斷屏蔽位(CPSR寄存器中的I/F位)未置位,CPU將暫停當前指令的執行。
- 硬件自動將程序計數器(PC)和相關寄存器壓入當前模式的堆棧。
- 硬件強制將PC設置為中斷向量表中預定義的IRQ異常向量地址。
-
軟件(異常處理程序)的行為:
- 位于異常向量地址的程序開始執行。其首要任務是執行對CPU Interface的
GICC_IAR
(Interrupt Acknowledge Register) 的讀操作。
- 位于異常向量地址的程序開始執行。其首要任務是執行對CPU Interface的
-
讀
GICC_IAR
的直接后果:- 對軟件而言:該讀操作返回一個32位值,其中包含了觸發本次中斷的硬件中斷ID。
- 對GIC硬件而言:GIC檢測到對
GICC_IAR
的讀訪問,會原子地更新該中斷ID的狀態:清除其Pending標志位,并設置其Active標志位。
-
Active狀態的定義:表示該中斷已被CPU獲知,并且其對應的服務程序即將或正在執行。GIC將不會再次向CPU轉發同一個處于Active狀態的中斷請求。
- 并發情況:若一個中斷在Active期間再次被外設觸發,它的狀態將變為Active and Pending。
第5步:中斷服務程序執行
- 事件:軟件已從
GICC_IAR
獲取了硬件中斷ID。 - 軟件(操作系統)的行為:
- 軟件使用該硬件中斷ID作為索引,在中斷分派表中查找對應的中斷服務程序(ISR)的地址。
- 調用該ISR。
- ISR執行與中斷源設備相關的特定操作。
第6步:中斷處理完成與中斷結束 (End of Interrupt)
-
事件:中斷服務程序(ISR)已執行完畢。
-
軟件(異常處理程序)的行為:
- 在從異常處理流程返回前,軟件必須執行對CPU Interface的
GICC_EOIR
(End of Interrupt Register) 的寫操作,寫入的值是第4步中獲取的那個硬件中斷ID。
- 在從異常處理流程返回前,軟件必須執行對CPU Interface的
-
寫
GICC_EOIR
的直接后果:- 對GIC硬件而言:GIC檢測到對
GICC_EOIR
的寫訪問,會清除該中斷ID的Active標志位。 - 狀態轉移:
- 如果該中斷狀態之前是單純的
Active
,現在將變為Inactive
(非活動)。 - 如果該中斷狀態之前是
Active and Pending
,現在Active
位被清除后,狀態將變回Pending
。GIC的仲裁邏輯會立刻將這個新的Pending
中斷納入下一輪仲裁。
- 如果該中斷狀態之前是單純的
- 對GIC硬件而言:GIC檢測到對
-
CPU恢復:軟件執行異常返回指令(如
SUBS PC, LR, #4
),CPU從堆棧中恢復之前保存的上下文,并從被中斷的指令處繼續執行。
省流版本
1.中斷發生,Distributor檢測到輸入信號,提取中斷ID號,使能寄存器(GICD_ISENABLERn
),將該中斷狀態寄存器設置為掛起Pending
2.Distributor找出當前觸發的優先級最高的中斷
3.Distributor提取當前最高優先級中斷ID的目標CPU,轉發給CPU專屬的CPU Interface
4.CPU Interface接收到來自Distributor中斷請求,將其優先級與優先級屏蔽寄存器的值比較,符合條件則:驅動連接到CPU核心的物理IRQ(或FIQ)信號線,使其變為有效電平
5.CPU核心檢測到其IRQ信號線變為有效,且CPU的中斷屏蔽位未置位:
停止當前指令執行,壓棧:將程序計數器(PC)和相關寄存器壓入當前模式的堆棧;
6.強制將PC設置為中斷向量表中預定義的IRQ異常向量地址;
7.執行對CPU Interface的 GICC_IAR (Interrupt Acknowledge Register) 的讀操作
原子地更新該中斷ID的狀態:清除其Pending標志位,并設置其Active標志位
8.執行對應對應中斷,完成后執行對CPU Interface的 GICC_EOIR (End of Interrupt Register) 的寫操作
9.軟件執行異常返回指令(如SUBS PC, LR, #4),CPU從堆棧中恢復之前保存的上下文,并從被中斷的指令處繼續執行
第三部分:上文提到的中斷寄存器專講
我們繼續按照觸發順序講解這些寄存器
這樣更能清晰一些
第1步:了解GIC硬件能力 (可選,但良好驅動的實踐)
在配置之前,軟件可以先讀取一些只讀寄存器,了解當前GIC的規格。
**GICD_TYPER 了解GIC一些基礎信息
(Interrupt Controller Type Register)**
作用: 讀取此寄存器,可以知道:
這個GIC最多支持多少個中斷 (ITLinesNumber)、
實現了多少個CPU接口 (CPUNumber)、
是否支持安全擴展 (SecurityExtn) 等。
這讓驅動可以動態適應不同的硬件。
位域 | 名 | 讀寫 | 描述 |
---|---|---|---|
15:11 | LSPI | R | 如果GIC實現了安全擴展,則此字段的值是已實現的可鎖定SPI的最大數量,范圍為0(0b00000)到31(0b11111)。 如果此字段為0b00000,則GIC不會實現配置鎖定。 如果GIC沒有實現安全擴展,則保留該字段。 |
10 | SecurityExtn | R | 表示GIC是否實施安全擴展: 0未實施安全擴展; 1實施了安全擴展 |
7:5 | CPUNumber | R | 表示已實現的CPU interfaces的數量。 已實現的CPU interfaces數量比該字段的值大1。 例如,如果此字段為0b011,則有四個CPU interfaces。 |
4:0 | ITLinesNumber | R | 表示GIC支持的最大中斷數。 如果ITLinesNumber = N,則最大中斷數為32*(N+1)。 中斷ID的范圍是0到(ID的數量– 1)。 例如:0b00011最多128條中斷線,中斷ID 0-127。 中斷的最大數量為1020(0b11111)。 無論此字段定義的中斷ID的范圍如何,都將中斷ID 1020-1023保留用于特殊目的 |
第2步:對每一個“計劃使用”的中斷進行單獨配置
1.GICD_ICFGRn :設置中斷的觸發方式
(Interrupt Configuration Registers)
置該中斷是電平觸發 (Level-sensitive) 還是邊沿觸發 (Edge-triggered)。這個必須根據外設的硬件手冊來設置,否則中斷會行為異常
這里兩位代表一個中斷,一個GICD_ICFGRn 寄存器能配置16個中斷
已知硬件中斷號m怎么知道GICD_ICFGRn 的n是多少?用m整除16即可得到n
然后是哪個字段?(兩位等于一個字段)
假設中斷號34,34/16=2,余2,所以F=2,所以寄存器位是【22,22+1】
也就是【4,5】
Int_config[1] (高位,即 bit[5]):0 = 電平觸發, 1 = 邊沿觸發。
Int_config[0] (低位,即 bit[4]):保留位,通常寫0。
2.Distributor (分組 , GICD) GICD_IGROUPRn
(GICD_ Interrupt GROUP Registers)
一位即可表示是否為安全中斷,一個寄存器能給32個中斷置位
某一位
置位 0(通常是安全中斷,FIQ)
置位 1(通常是非安全中斷,IRQ)的中斷分發
非安全中斷:給主操作系統(如Linux)用的常規中斷
- 是什么?
- 處理普通任務的中斷。
- 誰產生?
- 普通的、非敏感的硬件設備。比如:網卡、普通按鍵、串口、觸摸屏。
- 誰處理?
- 主操作系統,比如你手機上的Android系統或電腦上的Linux/Windows系統。
- 有什么用?
- 讓主操作系統知道硬件發生了事。比如網卡收到數據了,Linux內核就去處理這個數據。
- 在GIC里怎么設置?
- 通過 GICD_IGROUPRn 寄存器,把這個中斷對應的位設置為 1。
安全中斷 (Secure Interrupt / Group 0 Interrupt)
安全中斷是給一個獨立的安全系統用的特殊中斷,主操作系統(如Linux)不能處理它,也通常不知道它的存在
- 是什么?
- 處理高度敏感和機密任務的中斷。
- 誰產生?
- 受保護的、敏感的硬件設備。比如:指紋傳感器、加密芯片、安全定時器。
- 誰處理?
- 一個獨立于主操作系統之外的、微型的安全系統(常被稱為TEE,Trusted Execution Environment)。這個安全系統和Linux是隔離的。
- 有什么用?
- 處理那些絕對不能讓主操作系統(比如Linux)接觸到的數據。比如,指紋識別過程中的數據,必須由安全系統處理,以防被Linux上的惡意軟件竊取。
- 在GIC里怎么設置?
- 通過 GICD_IGROUPRn 寄存器,把這個中斷對應的位設置為 0。
3.優先級 (Priority) - GICD_IPRIORITYRn
因為設置了256個優先級,所以描述一個中斷的優先級需要8位
一個寄存器32位可以設置四個中斷的優先級
目標CPU (Targeting) - GICD_ITARGETSRn (主要用于SPI)
這個是GICV2版本下,采用位圖方式(每一位代表一個目標CPU),最多只支持8核CPU。
第3步:使能/禁用中斷
1.GICD_ISENABLERn (Set-Enable) / GICD_ICENABLERn (Clear-Enable)
(中斷分發)
( Interrupt Set-Enable Registers)
每一位控制一個中斷的轉發,如果沒有
向 GICD_ISENABLERn 的 bit 位寫1來使能中斷。
向 GICD_ICENABLERn 的 bit 位寫1來禁用中斷。
“轉發” = 傳遞中斷信號給CPU核心
中斷首先由GIC接收。但GIC是否會將該中斷傳遞(轉發)給CPU核心,取決于以下條件
1.中斷是否使能(由2.ICD_ISENABLERn/GICD_ICENABLERn控制)。 中斷的優先級、目標CPU核心等其他配置是否滿足。
第4步:配置CPU接口
1.優先級屏蔽:GICC_PMR
Interrupt Priority Mask Register
作用: 設置當前CPU的優先級“門檻”。CPU將只處理優先級高于此寄存器值的中斷(即優先級數值小于PMR值)。
32位寄存器,只有前八位有效,因為前面的也是八位優先級
2.優先級分組-GICC_BPR
Binary Point Register
GICC_BPR 寄存器里那3位 [2:0] 的值 (0到7),就定義了拆分的“分割點”。
這個分割點 BPR_Value 的含義是:8位優先級中,有多少位屬于子優先級。
8位優先級: [ bit7, bit6, bit5, bit4, bit3, bit2, bit1, bit0 ]
第4步:打開總開關 (最后一步)
GICD_CTLR (Distributor Control Register)
作用: 分發器總開關。
把它理解為整個GIC分發器的“總電源開關”就對了。
在你把所有中斷都精心配置好(設置好優先級、目標CPU、觸發方式等)之后,最后一步就是打開這個總開關,GIC才能正式開始工作。
-
位
[0]
-EnableGrp0
(使能組0)- 作用:控制 Group 0 中斷的轉發。
0
:關閉。所有屬于Group 0的中斷,即使已經觸發(處于pending狀態),也會被GIC分發器攔住,不會被發送給任何CPU接口。就像閘門A關閉了。1
:打開。GIC分發器會根據優先級規則,將處于pending狀態的Group 0中斷正常轉發給CPU接口。就像閘門A打開了。
-
位
[1]
-EnableGrp1
(使能組1)- 作用:控制 Group 1 中斷的轉發。
0
:關閉。所有屬于Group 1的中斷都會被攔住,不會被發送出去。就像閘門B關閉了。1
:打開。GIC分發器會根據優先級規則,將處于pending狀態的Group 1中斷正常轉發給CPU接口。就像閘門B打開了。
GICC_CTLR (CPU Interface Control Register)
作用: 當前CPU接口的總開關。
GICC_CTLR 就是每個CPU核心自己的“中斷接收器開關”。
主要功能:它到底控制了什么?
它主要控制兩件大事:
- 全局開關:決定當前這個 CPU 核心是否要開始處理中斷。
- 高級行為配置:微調一些中斷處理的細節,比如如何結束中斷、如何處理被屏蔽的信號等。
位域講解 (Bit Fields)
我們直接按功能塊來講解最重要的幾個位,這比按數字順序更容易理解。
1. 最重要的全局開關
-
EnableGrp0
(位 0)- 功能:Group 0 (安全中斷, 通常是 FIQ) 的總開關。
- 設置為 1: 允許當前 CPU 核心接收并處理來自 GIC 的 Group 0 中斷。
- 設置為 0: 屏蔽當前 CPU 核心的所有 Group 0 中斷。即使 GIC 已經把中斷信號發過來了,到這里也會被擋住。
-
EnableGrp1
(位 1)- 功能:Group 1 (非安全中斷, 通常是 IRQ) 的總開關。
- 設置為 1: 允許當前 CPU 核心接收并處理來自 GIC 的 Group 1 中斷。
- 設置為 0: 屏蔽當前 CPU 核心的所有 Group 1 中斷。
使用要點:在系統初始化時,必須根據你需要處理的中斷類型,將這兩位或其中一位設置為 1。這是讓中斷系統工作的最基本前提。
2. 中斷結束行為的配置
EOImode
(位 9)- 功能:控制**“中斷結束”** (
GICC_EOIR
寄存器) 的行為模式。 - 背景:當你向
GICC_EOIR
寫入一個值表示中斷處理完畢時,GIC 硬件內部會做兩件事:- 優先級降低 (Priority Drop):允許其他中斷可以搶占。
- 中斷去激活 (Deactivation):將中斷狀態從“Active”移除。
EOImode = 0
(默認模式):寫一次GICC_EOIR
,硬件自動完成上面兩件事。簡單直接。EOImode = 1
(分離模式):寫一次GICC_EOIR
,硬件只做第一件事(降低優先級)。你需要再向另一個寄存器GICC_DIR
寫入相同的值,才能完成第二件事(去激活)。這是一種性能優化手段,用于復雜的嵌套中斷,普通應用用不到。
- 功能:控制**“中斷結束”** (
3. 中斷信號傳遞的微調
-
FIQEn
(位 3)- 功能:決定 GIC 的 CPU 接口是否要將 Group 0 的中斷信號物理地傳遞給 CPU 的 FIQ 引腳。
- 設置為 1: 正常傳遞,觸發 FIQ 異常。
- 設置為 0: 不傳遞。等于在軟件層面掐斷了 FIQ 信號線。
-
CBPR
(位 4) - Control Bypass and Priority Register- 功能:控制 Group 0 和 Group 1 中斷的優先級處理方式。
CBPR = 0
: Group 0 和 Group 1 的中斷共享同一個優先級寄存器 (GICC_PMR
)。CBPR = 1
: Group 0 和 Group 1 使用各自獨立的優先級處理邏輯。這允許你為安全中斷和非安全中斷設置不同的優先級屏蔽策略。