點擊藍字 ╳ 關注我們
開源項目 OpenHarmony是每個人的 OpenHarmony

蔣衛峰
深圳開鴻數字產業發展有限公司
OS內核開發工程師
一、前言
OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)采用多內核架構,支持
Linux內核的標準系統、
LiteOS-A的小型系統、LiteOS-M的輕量系統。
其中LiteOS-A要求設備具備一定的處理能力,對比LiteOS-M,LiteOS-A支持以下特性:
(1)MMU支持:通過MMU支持內核態和用戶態分離,支持虛擬單元;
(2)支持獨立進程:調度對象分別為進程、線程;
(3)支持文件系統:包括虛擬文件和塊設備等;
(4)支持更復雜的IPC:包括LiteIPC等;
(5)支持多核調度:支持雙核
MCU,支持雙核調度;
(6)支持POSIX
接口:為APP開發提供更多幫助。
LiteOS-A內核特性都是建立在
CPU硬件的基礎上,而中斷
控制器在支持LiteOS-A內核的CPU中發揮著巨大的作用:它管理和控制可屏蔽中斷并對可屏蔽中斷進行優先權判定,減少CPU的負載,使得CPU更加專注于計算。
在
嵌入式領域,
ARM公司提供的芯片目前是市場的主流,OpenHarmony LiteOS-M內核目前支持的ARM公司的Cortex-M系列的芯片,而LiteOS-A內核支持的則是ARM公司功能更強大的Cortex-A/R系列的芯片,GIC是ARM公司給Cortex-A/R系列芯片提供的一個中斷控制器,在移植OpenHarmony LiteOS-A內核到特定板子的實踐中,我們遇到了很多GIC中斷控制器相關的技術問題,所以需要深入了解ARM體系架構下GIC中斷控制器的原理和使用方法,特此總結并共享給各位網友。
二、GIC控制器概述
1、GIC簡介
GIC是ARM公司給Cortex-A/R核提供的一個中斷控制器,類似Cortex-M中的NVIC。目前GIC有4個版本:V1~V4:V1是最老的版本,已經被廢棄了;V2~V4目前正在被大量地使用。GIC V2是給ARMv7-A架構使用的,比如Cortex-A5,Cortex-A7、Cortex-A9、Cortex-A15等,V3和V4是給ARMv8-A/R架構使用的,也就是64位芯片使用的。
GIC V2最多支持8個核。ARM會根據GIC版本的不同研發出不同的IP核,
半導體廠商直接購買對應的IP核即可,比如ARM針對GIC V2就開發出了GIC400中斷控制器IP核。注意,具體產品是GIC400,設計規范是V2。當GIC接收到外部中斷
信號以后匯報給ARM內核,但是ARM內核只提供四個信號給GIC來匯報中斷情況:VFIQ、VIRQ、FIQ和IRQ,他們之間的關系如圖所示:

?
在圖中,GIC接收眾多的外部中斷,并對其進行處理,最終只通過四個信號報給 ARM 內核,這四個信號的含義如下:
●VFIQ: 虛擬快速 FIQ
●VIRQ: 虛擬外部 IRQ
●FIQ: 快速中斷 IRQ
●IRQ: 外部中斷 IRQ
2、GIC整體實現
下圖左側部分是中斷源,中間部分是GIC控制器,最右側是中斷控制器向
處理器內核發送中斷信息。我們重點要看的是中間的 GIC 部分,GIC 將眾多的中斷源分為三類:
①SPI(Shared Peripheral Interrupt),共享中斷,即所有Core共享的中斷,外部中斷都屬于SPI中斷。比如按鍵中斷、串口中斷等,這些中斷所有的Core都可以處理,不限定特定Core。
在圖中,每個SPI設計是共享的,在SMP系統中每個SPI都需要連接一個線到各個CPU中。
②P
PI(Private Peripheral Interrupt),私有中斷,GIC是支持多核的,每個核有自己獨有的中斷,需要指定的核心處理。
在圖中,每個CPU都有屬于自己的PPIs,對應關系是1對1。
③SGI(Software-genera
ted Interrupt),軟件中斷,由軟件觸發引起的中斷,通過向
寄存器GICD_SGIR寫入數據來觸發,系統使用SGI中斷來完成多核之間的
通信。
在圖中,SGI是軟件(軟件運行在CPU上)觸發,所以SGI中斷源頭是各個CPU上的應用,另外SGI和PPI一樣,每個CPU都有屬于自己的SGI,所以必須指定CPU。

?
3、中斷 ID
中斷源有很多,為了區分不同的中斷源要給它們分配唯一ID,也就是中斷ID。每一個CPU最多支持1020個中斷ID,中斷ID號為ID0~ID1019。1020 個ID包含了PPI、SPI和SGI,那么這三類中斷是如何分配這 1020 個中斷 ID 的呢?分配如下:
●ID0~ID15:這16個ID分配給SGI
●ID16~ID31:這16個ID分配給PP
●ID32~ID1019:這988個ID分配給SPI,像GPIO中斷、串口中斷等這些外部中斷
至于具體到某個ID對應哪個中斷,那就由半導體廠商根據實際情況去定義。比如 I.MX6U 的總共使用了128個中斷ID,加上前面屬于PPI和SGI的32個ID,I.MX6U的中斷源共有128+32=160個。

?
4、GIC邏輯模塊
GIC架構分為了兩個邏輯塊:Distributor和CPU Interface,也就是分發器端和CPU接口端。
Distributor(分發器端):參考GIC整體實現的圖,此邏輯塊負責處理各個中斷事件的分發問題,確定中斷事件應該發送到哪個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
三、GIC400原理
1、整體框架圖
GIC-400實現了以下的中斷類型:
●16個軟件產生的中斷(SGI)
●每個處理器有6個外部私有外設中斷(PPI)
●每個處理器有1個內部PP
●可配置的共享外設中斷(SPI)的數量
GIC-400的BD如下圖所示,GIC-400從中斷輸入信號中檢測PPI和SPI。每個處理器的每個PPI中斷ID都有一個信號。每個SPI中斷ID只有一個輸入信號,與SoC中處理器的數量無關。SGI沒有輸入信號,在GIC-400中使用AXI
編程接口生成。

?
?
2、輸入信號
?
上圖中左邊就是來自外設的interrupt source輸入信號。分成兩種類型,分別是PPI(Private Peripheral Interrupt)和SPI(Shared Peripheral Interrupt)。PPI中斷信號是CPU私有的,每個CPU都有其特定的PPI信號線。而SPI是所有CPU之間共享的。通過寄存器GICD_TYPER可以配置SPI的個數(最多480個)。GIC-400支持多少個SPI中斷,其輸入信號線就有多少個SPI interrupt request signal。同樣的,通過寄存器GICD_TYPER也可以配置CPU interface的個數(最多8個),GIC-400支持多少個CPU interface,其輸入信號線就提供多少組PPI中斷信號線。一組PPI中斷信號線包括6個實際的signal:
?
(1)nLEGACYIRQ信號線。對應interrupt ID 31,在bypass mode下(這里的bypass是指bypass GIC functionality,直接連接到某個processor上),nLEGACYIRQ可以直接連到對應CPU的nIRQCPU信號線上。在這樣的設置下,該CPU不參與其他屬于該CPU的PPI以及SPI中斷的響應,而是特別為這一根中斷線服務。
(2)nCNTPNSIRQ信號線。來自Non-secure physical timer的中斷事件,對應interrupt ID 30。
(3)nCNTPSIRQ信號線。來自secure physical timer的中斷事件,對應interrupt ID 29。
(4)nLEGACYFIQ信號線。對應interrupt ID 28。概念與nLEGACYIRQ信號線相同。
(5)nCNTVIRQ信號線。對應interrupt ID 27。Virtual Timer Event,和虛擬化相關,這里不予描述。
(6)nCNTHPIRQ信號線。對應interrupt ID 26。Hypervisor Timer Event,和虛擬化相關,這里不予描述。
?
3、輸出信號
所謂輸出信號,就是GIC和各個CPU直接的接口,這些接口包括:
(a)觸發CPU中斷的信號。nIRQCPU和nFIQCPU信號線,主要用來觸發ARM CPU進入IRQ mode和FIQ mode。
(b)Wake up信號。nFIQOUT和nIRQOUT信號線,協助ARM CPU的
電源管理模塊,用來喚醒CPU。
(c)AXI slave interface signals。AXI(
Advanced eXtensible Interface)是一種總線協議,屬于AMBA規范的一部分。通過這些信號線,ARM CPU可以和GIC硬件block進行通信(例如寄存器訪問)。
4、觸發路徑
下面總結下SGI、PPI、SPI三種類型中斷的觸發路徑:
SGI(ID0-ID15):是由CPU內部軟件觸發,所以從CPU CORE-->CPU interface-->Distributor-->CPU Interface-->CPU CORE。
PPI(ID16-ID31):是由外部器件觸發,從Peripheral-->Distributor-->CPU interface-->CPU CORE。
SPI(ID32-ID1019):也是外部器件觸發,從Peripheral-->Distributor-->CPU interface-->CPU CORE。
5、中斷狀態遷移圖
對于每一個中斷而言,有以下4個狀態:
in
active:中斷處于無效狀態。
pending:中斷處于有效狀態,但是cpu沒有響應該中斷。
active:cpu在響應該中斷。
active and pending:cpu在響應該中斷,但是該中斷源又發送中斷過來。
PS:對于電平觸發的中斷而言,一個中斷的處理階段也會有active and pending的階段,這是比較特殊的。

?
下面是官方提供中斷遷移的條件和場景說明,要注意的是文中either表示轉移條件滿足其一即可:
?


?
四、GIC400寄存器
1、GIC-400寄存器地圖
所有GIC-400寄存器都有短名稱。在這些名稱中,前三個字符是GIC,第四個字符表示 GIC-400 的功能塊:
●GICD_ 分配器
●GICC_ 中央處理器接口
●GICH_ 虛擬接口控制塊
●GICV_ 虛擬CPU接口。
GIC-400寄存器是內存映射的,下表列出了地址范圍:

?
內存映射的含義:對于ARM體系架構而言,IO和內存是統一編制,寄存器會占用內存的地址范圍。至于GIC-400寄存器首地址映射在哪個內存地址上,這是硬件廠商設計,并通過CP15協處理器來獲取。
?
2、具體寄存器
下面解釋幾個經典的寄存器,更多的寄存器請參考ARM出品的官方文檔。
(1)GICD_CTRL寄存器

?
寄存器GICD_CTRL各個位的含義如下:-------------------------------------GICD_CTRL_reserved_0:保留。-------------------------------------EnableGrp1:Global enable for forwarding pending Group 1 interrupts from the Distributor to the CPU interfaces:-------------------------------------EnableGrp0:Global enable for forwarding pending Group 0 interrupts from the Distributor to the CPU interfaces:-------------------------------------個人見解:第一,EnableGrp1和EnableGrp0是全局開關,第二,這是Distributor側的開關,至于CPU interfaces側繼續參考其它寄存器。-------------------------------------
(2)2 GICD_TYPER

?
-------------------------------------GICD_TYPER_reserved_0:個人見解:保留字段,不使用-------------------------------------LSPI:If the GIC implements the Security Extensions, the value of this field is the maximum numberof implemented lockable SPIs, from 0 (0b00000) to 31 (0b11111), see Configuration lockdown on page 4-82. If this field is 0b00000 then the GIC does not implement configuration lockdown. If the GIC does not implement the Security Extensions, this field is reserved.個人見解:在不使能ARM安全體系的功能前提下,此處保留,安全體系的功能暫時不使用。-------------------------------------SecurityExtn:Indicates whether the GIC implements the Security Extensions.0 Security Extensions not implemented.1 Security Extensions implemented.個人見解:ARM安全體系的功能,使能后,0組和1組含義不一樣,具體需要另外篇幅來闡述-------------------------------------GICD_TYPER_reserved_1:-------------------------------------CPUNumber:Indicates the number of implemented CPU interfaces. The number of implemented CPUinterfaces is one more than the value of this field, for example if this field is 0b011, thereare four CPU interfaces. If the GIC implements the Virtualization Extensions, this is also the number of virtual CPU interfaces.個人見解:the number of implemented CPU interfaces = CPUNumber+1,域值是從0開始,含義卻是從1開始。-------------------------------------ITLinesNumber:Indicates the maximum number of interrupts that the GIC supports. If ITLinesNumber=N, the maximum number of interrupts is 32(N+1). The interrupt ID range is from 0 to (number of IDs - 1).For example: 0b00011 Up to 128 interrupt lines, interrupt IDs 0-127.The maximum number of interrupts is 1020 (0b11111). See the text in this section for more information. Regardless of the range of interrupt IDs defined by this field, interrupt IDs 1020-1023 are reserved for special purposes.個人見解:最大的中斷數量是32*(ITLinesNumber +1)-------------------------------------
(3)GICD_IDDR

?
-------------------------------------ProductID:個人見解:值0x020意味著GIC-400-------------------------------------GICD_IDDR_reserved_0:個人見解:reserved-------------------------------------Variant:An IMPLEMENTATION DEFINED variant number. Typically, this field is used to distinguishproduct variants, or major revisions of a product.個人見解:產品主版本號,0x2代表版本為2.0-------------------------------------Revision:An IMPLEMENTATION DEFINED revision number. Typically, this field is used to distinguish minor revisions of a product.個人見解:產品小版本號,0x1代表修訂版為r0p1。-------------------------------------Implementer:Contains the JEP106 code of the company that implemented the GIC Distributor:Bits [11:8] The JEP106 continuation code of the implementer. For an ARM implementation, this field is 0x4. Bits [7] Always 0. Bits [6:0] The JEP106 identity code of the implementer. For an ARM implementation, bits[7:0] are 0x3B.個人見解:實現CPU接口的公司代碼,0x43B代表ARM公司。-------------------------------------
(4)GICD_IGROUPn_0

?
-------------------------------------Group_Status_Bits:The GICD_IGROUPR registers provide a status bit for each interrupt supported by the GIC.Each bit controls whether the corresponding interrupt is in Group 0 or Group 1. Accessible by Secure accesses Only.For each bit:0 The corresponding interrupt is Group 0.1 The corresponding interrupt is Group 1.For interrupt ID m, when DIV and MOD are the integer division and modulo operations:a. the corresponding GICD_IGROUPRn number, n, is given by n = m DIV 32b. the offset of the required GICD_IGROUPR is (0x080 + (4*n))c. the bit number of the required group status bit in this register is m MOD 32.個人見解:① 組狀態位,對于每個位:“0”表示相應的中斷為Group 0;“1”表示相應的中斷為Group 1。② 問:對于一個中斷,如何設置它的Group?答:首先找到對應的GICD_IGROUPRn寄存器,即n是多少?還要確定使用這個寄存器里哪一位。對于interrtups ID m,如下計算:n = m DIV 32,GICD_IGROUPRn里的n就確定了;③ GICD_IGROUPRn在GIC內部的偏移地址是多少?答:0x080+(4*n),這個信息是為了定位到具體寄存器。④ 使用GICD_IPRIORITYRn中哪一位來表示?答:bit = m mod 32。⑤ GICD_IGROUPn_0只是GICD_IGROUPn寄存器組的第一個,總共有4個寄存器:GICD_IGROUPn_0到GICD_IGROUPn_3-------------------------------------
(5)GICD_ISENABLER0

?
-------------------------------------Set_Enable_Bits:The GICD_ISENABLERs provide a Set-enable bit for each interrupt supported by the GIC. For SPIs and PPIs, each bit controls the forwarding of the corresponding interrupt from the Distributor to the CPU interfaces:Reads:0 Forwarding of the corresponding interrupt is disabled.1 Forwarding of the corresponding interrupt is enabled.Writes:0 Has no effect.1 Enables the forwarding of the corresponding interrupt.After a write of 1 to a bit, a subsequent read of the bit returns the value 1. For interrupt ID m, when DIV and MOD are the integer division and modulo operations:a.the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32b.the offset of the required GICD_ISENABLER is (0x100 + (4*n))c.the bit number of the required Set-enable bit in this register is m MOD 32.個人見解:① 對于SPI和PPI類型的中斷,每一位控制對應中斷的轉發行為:從Distributor轉發到CPU interface:讀:“0”表示當前是禁止轉發的;“1”表示當前是使能轉發的;寫:“0”表示無效;“1”表示使能轉發。
② 對于一個中斷,如何找到GICD_ISENABLERn并確定相應的位?答:對于interrtups ID m,如下計算:n = m DIV 32,GICD_ISENABLERn里的n就確定了;GICD_ISENABLERn在GIC內部的偏移地址是多少?計算方法為0x100+(4*n),因為一個寄存器大小為4個字節。使用GICD_ISENABLERn中哪一位來表示?計算方法為?bit = m mod 32。
③ 這里的開關也是Distributor側的開關,GICV硬件設計思想和軟件設計思想類似。④ GICD_ISENABLER0是此類寄存器組的第一個,總共是4個。-------------------------------------
(6)GICD_ICFGR0


?
-------------------------------------Int_Config:The GICD_ICFGRs provide a 2-bit Int_config field for each interrupt supported by the GIC.For Int_config[1], the most significant bit, bit [2F+1], the encoding is:0 Corresponding interrupt is level-sensitive.1 Corresponding interrupt is edge-triggered.Int_config[0], the least significant bit, bit [2F], reservedFor SGIs: Int_config[1] Not programmable, RAO/WI.For PPIs: Int_config[1] Not programmable, RAZ/WI.For SPIs: Int_config[1] this bit is programmable.A read of this bit always returns the correct value to indicate whether the corresponding interrupt is level-sensitive or edge-triggered.For interrupt ID m, when DIV and MOD are the integer division and modulo operations: a. the corresponding GICD_ICFGR number, n, is given by n = m DIV 16b. the offset of the required GICD_ICFGRn is (0xC00 + (4*n))c. the required Priority field in this register, F, is given by F = m MOD 16, where field 0 refers to register bits [1:0], field 1 refers to bits [3:2], up to field 15 that refers to bits [31:30].個人見解:對于每一個中斷,都有對應的2位數據用來描述:它的邊沿觸發,還是電平觸發。對于Int_config [1](即高位[2F + 1]),含義為:“0”表示相應的中斷是電平觸發;“1”表示相應的中斷是邊沿觸發。對于Int_config [0],即低位[2F],是保留位,這一位就是以后才使用。-------------------------------------
五、問題與建議
1、ARM芯片設計了FIQ和IRQ的2個處理模式,移植過程中沒有用到FIQ。
2、虛擬中斷相關的功能也沒有用到。
3、安全相關的中斷功能前期我們也沒有用到,后續在可信執行環境中會用到。
4、如果代碼對中斷保護寫得不好的話,會出現Systick發生多次中斷,中斷狀態為active &pending,在移植過程中OpenHarmony版本不支持嵌套中斷。
5、中斷處理分為邊沿觸發處理和電平觸發處理。這就給我們不同的應用功能提供了選擇,使得我們可以在不同個工作下選擇適合的模式,邊沿觸發適用于對時間要求高的,比如中斷中有計數之類的(GATE門控位置1時),而電平觸發則適合報警裝置。
6、在基于ARM的嵌入式應用系統中,存儲系統通常是通過系統控制協處理器CP15完成的。ARM處理器使用協處理器15(CP15)的寄存器來控制cache、TCM和存儲器管理。CP15包含16個32位的寄存器,其編號為0~15,具體請參考ARM官方文檔。
7、givc2將中斷分成了group0和group1,默認情況下,所有中斷都是組0中斷,并使用IRQ向連接的處理器發送信號中斷請求。個人理解如果都改為組1也是沒有問題的,但是沒有驗證。
8、GIC-400通用初始化流程:
(1)設置distributor和CPU interface寄存器組的基地址;
(2)讀取GICD_TYPER寄存器,計算當前GIC最大支持多少個中斷源;
(3)初始化distributor:
a.disable distributor;
b.設置中斷分組;
c.設置SPI中斷的路由;
d.設置SPI中斷的觸發類型;
e.disactive和disable所有中斷源;
f.enable distributor;
(4)初始化CPU Interface:
a.設置GIC_CPU_PRIMASK,設置中斷優先級mask level;
b.enable CPU interface;
六、總結
本篇文章從GIC控制器概述、GIC400原理、GIC400寄存器等方面介紹了OpenHarmony內核之基礎硬件——中斷控制器GIC400的內容,希望開發者能從中學有所獲,學以致用。關于OpenHarmony內核的內容,之前我還介紹了內核對象隊列的算法、OpenHarmony LiteOS-M內核事件的運作機制,以及內核IPC機制數據結構、OpenHarmony Liteos-A內核之iperf3移植方法,感興趣的讀者可以點擊閱讀:《OpenHarmony——內核對象隊列之算法詳解(上)》、《OpenHarmony——內核對象隊列之算法詳解(下)》、《OpenHarmony——內核對象事件之源碼詳解》、《OpenHarmony——內核IPC機制數據結構解析》、《OpenHarmony Liteos_A內核之iperf3移植心得》。
評論