Linux 中斷子系統:GIC 中斷控制器
GIC 是 ARM 公司給 Cortex-A/R 內核提供的一個中斷控制器,類似 Cortex-M 內核(STM32)中的 NVIC。
- GIC:Generic Interrupt Controller,通用中斷控制器。
- NVIC:Nested Vectored Interrupt Controller,嵌套中斷向量控制器。
目前 GIC 有 4 個版本:V1~V4,V1 是最老的版本,已經被廢棄了。V2~V4 目前正在大量的使用。GIC V2 是給 ARMv7-A 架構使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等, V3 和 V4 是給 ARMv8-A/R 架構使用的,也就是 64 位芯片使用的。
我們使用的是 IP 核,也就是 gic400、gic500、gic600。支持對應的架構。
gic400,支持 GIC-v2 架構。
gic500,支持 GIC-v3 架構。
gic600,支持 GIC-v3 和 GIC-v4 架構。
GIC-v2
GIC V2 最多支持 8 個核。ARM 會根據 GIC 版本的不同研發出不同的 IP 核,半導體廠商直接購買對應的 IP 核即可,比如 ARM 針對 GIC V2 就開發出 了 gic400 這個中斷控制器 IP 核。
當 GIC 接收到外部中斷信號以后就會報給 ARM 內核,但是 ARM 內核只提供了四個信號給 GIC 來匯報中斷情況:VFIQ、VIRQ、FIQ 和 IRQ:
VFIQ:虛擬快速 IRQ。
VIRQ:虛擬 IRQ。
FIQ:快速 IRQ。
IRQ::Interrupt ReQuest。
VFIQ 和 VIRQ 是針對虛擬化的,我們不討論虛擬化中斷,剩下的就是 FIQ 和 IRQ 了。一般我們只使用 IRQ,所以相當于 GIC 最終向 ARM 內核就上報一個 IRQ 信號。
下圖來源于ARM官方手冊,是 GIC-v2 的框圖:
左側部分就是中斷源,中間部分就是 GIC 控制器,最右側就是中斷控制器向 處理器內核發送中斷信息。我們重點要看的肯定是中間的 GIC 部分,GIC 將眾多的中斷源分為 分為三類:
①、SPI(Shared Peripheral Interrupt),共享外設中斷,該中斷來自于外設,所有 Core 共享的中斷。比如按鍵中斷、串口中斷等等,這些中斷所有的 Core 都可以處理,不限定特定 Core。
②、PPI(Private Peripheral Interrupt),私有外設中斷,該終端來自于外設,被特定的核處理。GIC 是支持多核的,每個核有自己獨有的中斷。
③、SGI(Software-generated Interrupt),軟中斷,由軟件觸發引起的中斷,通過向寄存器 GICD_SGIR 寫入數據來觸發,系統會使用 SGI 中斷來完成多核之間的通信。
中斷源有很多,為了區分這些不同的中斷源肯定要給他們分配一個唯一 ID,這些 ID 就是中斷 ID。GIC-v2中每一個 CPU 最多支持 1020 個中斷 ID,中斷 ID 號為 ID0~ID1019。這 1020 個 ID 包 含了 PPI、SPI 和 SGI。這 1020 個 ID 分 配如下:
ID0~ID15:這 16 個 ID 分配給 SGI。每個CPU核都有自己的16個。
ID16~ID31:這 16 個 ID 分配給 PPI。每個CPU核都有自己的16個。
ID32~ID1019:這 988 個 ID 分配給 SPI,像 GPIO 中斷、串口中斷等這些外部中斷 ,至于具體到某個 ID 對應哪個中斷那就由半導體廠商根據實際情況去定義了。
GIC-v2 架構分為了兩個邏輯塊: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。
GIC-v2 支持 bypass 功能,當左上角 CFGSDISABLE 信號為高,外部來的 IRQ 和FIQ 不經過 GIC 仲裁,直連 CPU core 的 IRQ 和 FIQ 引腳。此場景可能用在啟動階段,一般不用。
右上角有 GICD_ 、GICC_ 、GICV_ 、GICH_ 系列寄存器,因為不討論虛擬中斷,所以我們一般只關心 GICD_ 、GICC_ 開頭的寄存器, GICD_ 代表 Distributor 分配器的寄存器, GICC_ 代表 CPU interface 的寄存器。
有一點需要說明:不管 GIC 如何對中斷進行分類,對 CPU core 來講,只分為 IRQ、FIQ、VIRQ、VFIQ,一般所有的外部中斷對CPU core來講都屬于IRQ:
即便在 GIC 內部分為了 SPI、SGI、PPI,但是最后都會到 CPU interface,CPU interface 再給 CPU core ,CPU core 只認為有四種中斷類型,普通都是 IRQ。
GIC-v3
GIC-v3 架構有改變,中斷號也變多了,不過還是向后兼容 GIC-v2 的。
GIC-v3支持超多核,以 xxx.xxx.xxx.xxx 命名,不止8核,GIC-v2 只支持 8 核,命名為 0-7 。
GIC-v3將 CPU interface 從GIC側移到了CPU側,因為處理中斷會頻繁訪問 CPU interface 的寄存器,移到 CPU 側加快訪問速度,中斷處理就會加快。
GIC-v3 的架構變化如下:以前 SPI、PPI、SGI 都歸 Distributor(分發器端) 管,現在只有 SPI 歸 Distributor管,PPI、SGI、LPI 都歸 Redistributor 管,作用還是一樣的。
寄存器分布,不同東西的寄存器開頭不一樣:
GIC-v 3的中斷號規定如下,來源于ARM官方文檔。
最主要的區別就是增加了 LPI 這個中斷類型,是基于消息的中斷。
一般 IRQ 和 FIQ 都會有一個物理線,會給 CPU 核一個物理信號,代表中斷到來。LPI 不一樣,它是基于消息的機制,寫寄存器就會發一個消息中斷,是 ARM 在為未來布局,以后會出一些 server 的產品,獨享中斷號。
GIC-v3 邏輯圖總結如下:
GIC -v2 架構寄存器:
來源于 GIC-v2 手冊最后幾頁:
這里的 alias 別名很有意思,說明了這個寄存器是干嘛的:
GIC -v3的寄存器不一樣,是 ICC_ 、ICV_ 、ICH_ 系列寄存器。