GIC(Generic Interrupt Controller)是ARM公司提供的一個通用的中斷控制器,其architecture specification目前有四個版本,V1~V4(V2最多支援8個ARM core,V3/V4支援更多的ARM core,主要用于ARM64伺服器系統結構)。目前在ARM官方網站隻能下載下傳到Version 2的GIC architecture specification,是以,本文主要描述符合V2規範的GIC硬體及其驅動。
具體GIC硬體的實作形态有兩種,一種是在ARM vensor研發自己的SOC的時候,會向ARM公司購買GIC的IP,這些IP包括的型号有:PL390,GIC-400,GIC-500。其中GIC-500最多支援128個 cpu core,它要求ARM core必須是ARMV8指令集的(例如Cortex-A57),符合GIC architecture specification version 3。另外一種形态是ARM vensor直接購買ARM公司的Cortex A9或者A15的IP,Cortex A9或者A15中會包括了GIC的實作,當然,這些實作也是符合GIC V2的規格。
1. ARM中斷控制器
1.1 ARM支援中斷類型ARM GIC-v2支援三種類型的中斷:
SGI:軟體觸發中斷(Software Generated Interrupt),通常用于多核間通訊,最多支援16個SGI中斷,硬體中斷号從ID0~ID15。SGI通常在Linux核心中被用作 IPI 中斷(inter-processor interrupts),并會送達到系統指定的CPU上。
PPI:私有外設中斷(Private Peripheral Interrupt),是每個CPU私有的中斷。最多支援16個PPI中斷,硬體中斷号從ID16~ID31。PPI通常會送達到指定的CPU上,應用場景有CPU本地時鐘。
SPI:公用外設中斷(Shared Peripheral Interrupt),最多可以支援988個外設中斷,硬體中斷号從ID32~ID1019。
1.2 GIC檢測中斷流程
GIC主要由兩部分組成,分别是仲裁單元(Distributor)和CPU接口(Interface)子產品
從圖中可以看出, GIC 可以清晰的劃分成兩個部分:仲裁( Distributor )和 CPU 接口(CPU Interface)。CPU interface有兩種,一種就是和普通processor接口,另外一種是和虛拟機接口的。Virtual CPU interface在本文中不會較長的描述。分發器其實應該叫彙聚器,在 IC 的後端設計中, layout 會把各個子產品引過來的中斷線(就是上面說的三種中斷)混接到 GIC 上,然後把混聚合的中斷接到 CPU 的 IRQ 和 FIQ 線上,這樣 CPU 就有觸覺了。
其中 Distribute 隻有一套,故基位址也隻有一個,而 Interface 有多套,因為每個 CPU 都對應一套 interface 。
其中各個子中斷使能,設定觸發方式,優先級排序,分發到哪個 CPU 上這些功能由 Distribute 負責,總的中斷的使能,狀态的維護由 Interface 負責。
更多linux核心視訊教程文檔資料免費領取背景私信【核心】自行擷取.
Distributor
Distributor的主要的作用是檢測各個interrupt source的狀态,控制各個interrupt source的行為,分發各個interrupt source産生的中斷事件分發到指定的一個或者多個CPU interface上。雖然Distributor可以管理多個interrupt source,但是它總是把優先級最高的那個interrupt請求送往CPU interface。Distributor對中斷的控制包括:
(1)中斷enable或者disable的控制。Distributor對中斷的控制分成兩個級别。一個是全局中斷的控制(GIC_DIST_CTRL)。一旦disable了全局的中斷,那麼任何的interrupt source産生的interrupt event都不會被傳遞到CPU interface。另外一個級别是對針對各個interrupt source進行控制(GIC_DIST_ENABLE_CLEAR),disable某一個interrupt source會導緻該interrupt event不會分發到CPU interface,但不影響其他interrupt source産生interrupt event的分發。
(2)控制将目前優先級最高的中斷事件分發到一個或者一組CPU interface。當一個中斷事件分發到多個CPU interface的時候,GIC的内部邏輯應該保證隻assert 一個CPU。
(3)優先級控制。
(4)interrupt屬性設定。例如是level-sensitive還是edge-triggered
(5)interrupt group的設定
Distributor可以管理若幹個interrupt source,這些interrupt source用ID來辨別,我們稱之interrupt ID。
CPU interface
CPU interface這個block主要用于和process進行接口。該block的主要功能包括:
(a)enable或者disable CPU interface向連接配接的CPU assert中斷事件。對于ARM,CPU interface block和CPU之間的中斷信号線是nIRQCPU和nFIQCPU。如果disable了中斷,那麼即便是Distributor分發了一個中斷事件到CPU interface,但是也不會assert指定的nIRQ或者nFIQ通知processor。
(b)ackowledging中斷。processor會向CPU interface block應答中斷,中斷一旦被應答,Distributor就會把該中斷的狀态從pending狀态修改成active。如果沒有後續pending的中斷,那麼CPU interface就會deassert nIRQCPU和nFIQCPU信号線。如果在這個過程中又産生了新的中斷,那麼Distributor就會把該中斷的狀态從pending狀态修改成pending and active。這時候,CPU interface仍然會保持nIRQ或者nFIQ信号的asserted狀态,也就是向processor signal下一個中斷。
(c)中斷處理完畢的通知。當interrupt handler處理完了一個中斷的時候,會向寫CPU interface的寄存器進而通知GIC CPU已經處理完該中斷。做這個動作一方面是通知Distributor将中斷狀态修改為deactive,另外一方面,可以允許其他的pending的interrupt向CPU interface送出。
(d)設定priority mask。通過priority mask,可以mask掉一些優先級比較低的中斷,這些中斷不會通知到CPU。
(e)設定preemption的政策
(f)在多個中斷事件同時到來的時候,選擇一個優先級最高的通知processor
GIC仲裁單元:為每一個中斷維護一個狀态機,分别是:inactive、pending、active and pending、active。
下面是來自IHI0048B GIC-V2規格書3.2.4 Interrupt handling state machine截圖:
GIC檢測中斷流程如下:
(1) 當GIC檢測到一個中斷發生時,會将該中斷标記為pending狀态(A1)。
(2) 對處于pending狀态的中斷,仲裁單元回确定目标CPU,将中斷請求發送到這個CPU上。
(3) 對于每個CPU,仲裁單元會從衆多pending狀态的中斷中選擇一個優先級最高的中斷,發送到目标CPU的CPU Interface子產品上。
(4) CPU Interface會決定這個中斷是否可以發送給CPU。如果該終端優先級滿足要求,GIC會發生一個中斷信号給該CPU。
(5) 當一個CPU進入中斷異常後,會去讀取GICC_IAR 寄存器來響應該中斷(一般是Linux核心的中斷處理程式來讀寄存器)。寄存器會傳回硬體中斷号(hardware interrupt ID),對于SGI中斷來說是傳回源CPU的ID。
當GIC感覺到軟體讀取了該寄存器後,又分為如下情況:
- 如果該中斷源是pending狀态,那麼轉改将變成active。**(C) **
- 如果該中斷又重新産生,那麼pending狀态變成active and pending。(D)
-
如果該中斷是active狀态,現在變成active and pending。(A2)
(6) 當處理器完成中斷服務,必須發送一個完成信号EOI(End Of Interrupt)給GIC控制器。軟體寫GICC_EOIR寄存器,狀态變成inactive。(E1)
補充:
(7) 對于level triggered類型中斷來說,當觸發電平消失,狀态從active and pending變成active。(B2)
常用路徑是A1->D->B2->E1。
1.2.1 GIC中斷搶占
GIC中斷控制器支援中斷優先級搶占,一個高優先級中斷可以搶占一個低優先級且處于active狀态的中斷,即GIC仲裁單元會記錄和比較目前優先級最高的pending狀态,然後去搶占目前中斷,并且發送這個最高優先級的中斷請求給CPU,CPU應答了高優先級中斷,暫停低優先級中斷服務,進而去處理高優先級中斷。
GIC會将pending狀态優先級最高的中斷請求發送給CPU。
1.2.2 Linux對中斷搶占處理
從GIC角度看,GIC會發送高優先級中斷請求給CPU。
但是目前CPU處于關中斷狀态,需要等低優先級中斷處理完畢,直到發送EOI給GIC。
然後CPU才會響應pending狀态中優先級最高的中斷進行處理。
是以Linux下:
- 高優先級中斷無法搶占正在執行的低優先級中斷。
2.同處于pending狀态的中斷,優先響應高優先級中斷進行處理。
1.3 GIC中斷時序
首先給出前提條件:
- N和M用來辨別兩個外設中斷,N的優先級大于M
- 兩個中斷都是SPI類型,level trigger,active-high
- 兩個中斷被配置為去同一個CPU
- 都被配置成 group 0,通過FIQ觸發中斷
(1) T1時刻:GIC的總裁單元檢測到中斷M的電平變化。
(2) T2時刻:仲裁單元設定中斷M的狀态為pending。
(3) T17時刻:CPU Interface子產品會拉低nFIQCPU[n]信号。在中斷M的狀态變成pending後,大概需要15個時鐘周期後會拉低nFIQCPU[n]信号來向CPU報告中斷請求(assertion)。仲裁單元需要這些時間來計算哪個是pending狀态下優先級最高的中斷。
(4) T42時刻:仲裁單元檢測到另外一個優先級更高的中斷N。
(5) T43時刻:仲裁單元用中斷N替換中斷M為目前pending狀态下優先級最高的中斷,并設定中斷N為pending狀态。
(6) T58時刻:經過tph個時鐘後,CPU Interface拉低你FIOCPU[n]信号來通知CPU。因為此信号在T17時刻已經被拉低,CPU Interface子產品會更新GICC_IAR寄存器的Interrupt ID域,該域的值變成中斷N的硬體中斷号。
(7) T61~T131時刻:Linux對中斷N的服務程式---------------------中斷服務程式處理段,從GICC_IAR開始到GICC_EOIR結束。
- T61時刻:CPU(Linux中斷服務例程)讀取GICC_IAR寄存器,即軟體響應了中斷N。這時仲裁單元把中斷N的狀态從pending變成active and pending。
- T64時刻:在中斷N被Linux相應3個時鐘内,CPU Interface子產品完成對nFIQCPU[n]信号的deasserts,即拉高nFIQCPU[n]信号。
- T126時刻:外設也deassert了該中斷N。
- T128時刻:仲裁單元移出了中斷N的pending狀态。
- T131時刻:Linux服務程式把中斷N的硬體ID号寫入GICC_EOIR寄存器來完成中斷N的全部處理過程。
(8) T146時刻:在向GICC_EOIR寄存器寫入中斷N中斷号後的tph個時鐘後,仲裁單元會選擇下一個最高優先級中斷,即中斷M,發送中斷請求給CPU Interface子產品。CPU Interface會拉低nFIQCPU[n]信号來向CPU報告外設M的中斷請求。
(9) T211時刻:Linux中斷服務程式讀取GICC_IAR寄存器來響應中斷,仲裁單元設定中斷M的狀态為active and pending。
(10) T214時刻:在CPU響應中斷後的3個時鐘内,CPU Interface子產品拉高nFIOCPU[n]信号來完成deassert動作。
1.4 GIC中 寄存器描述
1.4.1 Distributor
一種實作是:
偏移 | 名稱 | 可讀寫 | 複位後 | 作用 |
0x000 | GICD_CTLR | RW | 0x00000000[c] | Distributor Control Register寫入1使能控制器,必備 |
0x004 | GICD_TYPER | RO | Configuration-dependent[d] | Interrupt Controller Type Register其中可以檢視中斷線的總數 |
0x008 | GICD_IIDR | RO | 0x0200143B | Distributor Implementer Identification Register, GICD_IIDR存了一些版本資訊 |
0x080-0x0BC | GICD_IGROUPRn | RW | 0x00000000 | Interrupt Group Registers[e]一個位圖,控制中斷屬于A組還是B組 |
0x100 | GICD_ISENABLERn | RW[f] | SGIs and PPIs:0x0000FFFF | Interrupt Set-Enable Registers一個位圖,用于使能各個中斷,寫入1使能。有用 |
0x104-0x13C | SPIs:0x00000000 | |||
0x180 | GICD_ICENABLERn | RW[f] | 0x0000FFFF | Interrupt Clear-Enable Registers和上一個寄存器類似,作用相反,寫入1禁止。 |
0x184-0x1BC | 0x00000000 | |||
0x200-0x23C | GICD_ISPENDRn | RW | 0x00000000 | Interrupt Set-Pending Registerspend位圖,寫入1可以進入pend狀态 |
0x280-0x2BC | GICD_ICPENDRn | RW | 0x00000000 | Interrupt Clear-Pending Registers同上,寫入1效果相反,阻止pend狀态 |
0x300-0x33C | GICD_ISACTIVERn | RW | 0x00000000 | Interrupt Set-Active Registers位圖,寫入1可以激活中斷 |
0x380-0x3BC | GICD_ICACTIVERn | RW | 0x00000000 | Interrupt Clear-Active Registers寫入1反激活中斷 |
0x400-0x5FC | GICD_IPRIORITYRn | RW | 0x00000000 | Interrupt Priority Registers存着各個中斷的優先級,每8位算一個 |
0x800-0x81C | GICD_ITARGETSRn | RO[h] | - | Interrupt Processor Targets Registers[i]某個中斷應該發往哪個處理器進行處理 |
0x820-0x9FC | RW | 0x00000000 | ||
0xC00 | GICD_ICFGRn | RO | SGIs:0xAAAAAAAA | Interrupt Configuration Registers, GICD_ICFGRn配置中斷是低電平觸發還是下降沿觸發 |
0xC04 | RO | PPIs:0x55540000 | ||
0xC08-0xC7C | RW[j] | SPIs:0x55555555 | ||
0xD00 | GICD_PPISR | RO | 0x00000000 | Private Peripheral Interrupt Status Register, GICD_PPISR一般沒用 |
0xD04-0xD3C | GICD_SPISRn | RO | 0x00000000 | Shared Peripheral Interrupt Status Registers, GICD_SPISRn沒用 |
0xF00 | GICD_SGIR | WO | - | Software Generated Interrupt Register控制軟中斷 |
0xF10-0xF1C | GICD_CPENDSGIRn | RW | 0x00000000 | SGI Clear-Pending Registers軟中斷的pend位 |
0xF20-0xF2C | GICD_SPENDSGIRn | RW | 0x00000000 | SGI Set-Pending Registers同上,不過寫入1時停止pend |
0xFD0 | GICD_PIDR4 | RO | 0x00000004 | Peripheral ID 4 Register |
0xFD4 | GICD_PIDR5 | RO | 0x00000000 | Peripheral ID 5 Register |
0xFD8 | GICD_PIDR6 | RO | 0x00000000 | Peripheral ID 6 Register |
0xFDC | GICD_PIDR7 | RO | 0x00000000 | Peripheral ID 7 Register |
0xFE0 | GICD_PIDR0 | RO | 0x00000090 | Peripheral ID 0 Register |
0xFE4 | GICD_PIDR1 | RO | 0x000000B4 | Peripheral ID 1 Register |
0xFE8 | GICD_PIDR2 | RO | 0x0000002B | Peripheral ID 2 Register |
0xFEC | GICD_PIDR3 | RO | 0x00000000 | Peripheral ID 3 Register |
0xFF0 | GICD_CIDR0 | RO | 0x0000000D | Component ID 0 Register |
0xFF4 | GICD_CIDR1 | RO | 0x000000F0 | Component ID 1 Register |
0xFF8 | GICD_CIDR2 | RO | 0x00000005 | Component ID 2 Register |
0xFFC | GICD_CIDR3 | RO | 0x000000B1 | Component ID 3 Register |
1.4.2 CPU Interface
Offset | Name | Type | Reset | Description[a] |
0x0000 | GICC_CTLR | RW | 0x00000000 | CPU Interface Control Register 使能位。寫入1使能 |
0x0004 | GICC_PMR | RW | 0x00000000 | Interrupt Priority Mask Register限制中斷最低優先級,高于此值無法中斷,最好寫大一點 |
0x0008 | GICC_BPR | RW | 0x00000002 | Binary Point RegisterThe minimum value of the Binary Point Register depends on which security-banked copy is considered:0x2Secure copy0x3Non-secure copy優先級分組 |
0x000C | GICC_IAR | RO | 0x000003FF | Interrupt Acknowledge Register隻讀,中斷id |
0x0010 | GICC_EOIR | WO | - | End of Interrupt Register寫入以告知cpu已經處理完中斷 |
0x0014 | GICC_RPR | RO | 0x000000FF | Running Priority Register目前中斷優先級 |
0x0018 | GICC_HPPIR | RO | 0x000003FF | Highest Priority Pending Interrupt Register最高優先級中斷号及其pend值 |
0x001C | GICC_ABPR | RW | 0x00000003 | Aliased Binary Point RegisterThe minimum value of the Aliased Binary Point Register is0x3.别名寄存器 |
0x0020 | GICC_AIAR | RO | 0x000003FF | Aliased Interrupt Acknowledge Register别名寄存器 |
0x0024 | GICC_AEOIR | WO | - | Aliased End of Interrupt Register别名寄存器 |
0x0028 | GICC_AHPPIR | RO | 0x000003FF | Aliased Highest Priority Pending Interrupt Register别名寄存器 |
0x00D0 | GICC_APR0 | RW | 0x00000000 | Active Priority Register用于儲存和恢複 |
0x00E0 | GICC_NSAPR0 | RW | 0x00000000 | Non-Secure Active Priority Register用于儲存和恢複 |
0x00FC | GICC_IIDR | RO | 0x0202143B | CPU Interface Identification Register, GICC_IIDR存着版本資訊 |
0x1000 | GICC_DIR | WO | - | Deactivate Interrupt Register |