原文來自:駿的世界
ARM GIC(一) cortex-A 處理器中斷簡介
對於ARM的處理器,中斷給處理器提供了觸覺,使處理器能夠感知到外界的變化,從而實時的處理。本系列博文,是以ARM cortex-A系列處理器,來介紹ARM的soc中,中斷的處理。
ARM cortex-A系列處理器,提供了4個管腳給soc,實現外界中斷的傳遞。分別是:
- nIRQ: 物理普通中斷
- nFIQ: 物理快速中斷
- nVIRQ: 虛擬普通中斷
- nVFIQ: 虛擬快速中斷
如下圖所示:
其中虛擬中斷,是為了實現虛擬化而加入的,在這個系列中,不討論虛擬中斷,只介紹物理中斷的相關知識。
在arm的soc系統中,會有多個外設,均有可能會產生中斷發送給arm cpu,等待cpu處理。
而arm cpu對中斷,只提供了2根信號,一個nIRQ,一個是nFIQ。因此就需要有一個中斷控制器來作為中間的橋接,收集soc的所有中斷信號,然后仲裁選擇合適的中斷,再發送給CPU,等待CPU處理。
如下圖所示:
這中間的橋接器件,就是arm公司推出大名鼎鼎的GIC,general interrupt controller。
GIC其實是一個架構,版本歷經了GICv1(已棄用),GICv2,GICv3,GICv4。對於不同的GIC版本,arm公司設計了對應的GIC IP。
- GIC400,支持GICv2架構版本。
- GIC500,支持GICv3架構版本。
- GIC600,支持GICv3架構版本。
GIC的核心功能:對soc中外設的中斷源的管理,並且提供給軟件,配置以及控制這些中斷源。
當對應的中斷源有效時,GIC根據該中斷源的配置,決定是否將該中斷信號,發送給CPU。如果有多個中斷源有效,那么GIC還會進行仲裁,選擇最高優先級中斷,發送給CPU。
當CPU接受到GIC發送的中斷,通過讀取GIC的寄存器,就可以知道,中斷的來源來自於哪里,從而可以做相應的處理。
當CPU處理完中斷之后,會告訴GIC(通過訪問GIC的寄存器),該中斷處理完畢。GIC接受到該信息后,就將該中斷源取消,避免又重新發送該中斷給cpu以及允許中斷搶占。
之后,會先介紹下GICv2的相關知識,然后介紹目前主流使用的GICv3。
ARM GIC(二)中斷術語
ARM在GIC中,對於中斷,定義了如下的一些術語。
中斷狀態
對於每一個中斷而言,有以下4個狀態:
-
inactive:中斷處於無效狀態
-
pending:中斷處於有效狀態,但是cpu沒有響應該中斷
-
active:cpu在響應該中斷
-
active and pending:cpu在響應該中斷,但是該中斷源又發送中斷過來
以下是中斷狀態的轉移圖。至於圖中的轉移條件,在GIC架構文檔中,有介紹。
中斷觸發方式
中斷觸發方式,包含以下兩種方式:
- edge-triggered:邊沿觸發,當中斷源產生一個邊沿,中斷有效
- level-sensitive:電平觸發,當中斷源為指定電平,中斷有效
中斷類型
中斷類型分為以下幾類:
- PPI:(private peripheral interrupt),私有外設中斷,該中斷來源於外設,但是該中斷只對指定的core有效。
- SPI:(shared peripheral interrupt),共享外設中斷,該中斷來源於外設,但是該中斷可以對所有的core有效。
- SGI:(software-generated interrupt),軟中斷,軟件產生的中斷,用於給其他的core發送中斷信號
- virtual interrupt:虛擬中斷,用於支持虛擬機
中斷優先級
因為soc中,中斷有很多,為了方便對中斷的管理,對每個中斷,附加了中斷優先級。在中斷仲裁時,高優先級的中斷,會優於低優先級的中斷,發送給cpu處理。
當cpu在響應低優先級中斷時,如果此時來了高優先級中斷,那么高優先級中斷會搶占低優先級中斷,而被處理器響應。
中斷號
為了方便對中斷的管理,GIC為每個中斷,分配了一個中斷號,也就是interrupt ID。對於中斷號,GIC也進行了分配:
- ID0-ID15:分配給SGI,軟中斷
- ID16-ID31:分配給PPI,私有外設中斷
- ID32-ID1019分配給SPI,共享外設中斷
- 其他
在具體的arm的cpu中,對於PPI,又進行了詳細的分配。這個,就得看arm cpu的TRM了。
中斷生命周期
一個中斷,是有生命周期的。以下是流程圖:
Start=>start: Start,中斷開始
generate=>operation: generate,中斷源產生中斷,發送給GIC
deliver=>operation: deliver,GIC將中斷發送給cpu
activate=>operation: Activate,cpu響應該中斷
deactivate=>operation: Deactivate,cpu響應完中斷,告訴GIC,中斷處理完畢,GIC更新該中斷狀態
end=>end: End,中斷結束
Start->generate->deliver->activate->deactivate->end
banking
banking(不知翻譯成啥比較合適)功能,包括以下兩個:
中斷banking
對於PPI和SGI,GIC可以有多個中斷對應於同一個中斷號。比如在soc中,有多個外設的中斷,共享同一個中斷號。
寄存器banking
對於同一個GIC寄存器地址,在不同的情況下,訪問的是不同的寄存器。例如在secure和non-secure狀態下,訪問同一個GIC寄存器,其實是訪問的不同的GIC的寄存器。
具體,更多的信息,得看GIC spec以及arm spec。
ARM GIC(三) GICv2架構
ARM的cpu,特別是cortex-A系列的CPU,目前都是多core的cpu,因此對於多core的cpu的中斷管理,就不能像單core那樣簡單去管理,由此arm定義了GICv2架構,來支持多核cpu的中斷管理。
GICv2架構
GICv2,支持最大8個core。其框圖如下圖所示:
在GICv2中,GIC由兩個大模塊組成:
-
distributor:實現中斷分發,對於PPI,SGI是各個core獨有的中斷,不參與目的core的仲裁,SPI,是所有core共享的,根據配置決定中斷發往的core。最后選擇最高優先級中斷發送給cpu interface。寄存器使用 GICD_ 作為前綴。一個GIC中,只有一個GICD。
-
cpu interface:將GICD發送的中斷信息,通過IRQ,FIQ管腳,傳輸給core。寄存器使用 GICC_ 作為前綴。每一個core,有一個cpu interface。
-
virtual cpu interface:將GICD發送的虛擬中斷信息,通過VIRQ,VFIQ管腳,傳輸給core。每一個core,有一個virtual cpu interface。而在這virtual cpu interface中,又包含以下兩個組件:
-
- virtual interface control:寄存器使用 GICH_ 作為前綴
- virtual cpu interface:寄存器使用 GICV_ 作為前綴
圖中的virtual interface,是用於支持虛擬中斷,本系列不討論虛擬中斷。
GICv2支持中斷旁路模式,也就是GIC外部的FIQ,IRQ直接接到core的FIQ,IRQ上,相當於GIC是不使能的。也就是CFGSDISABLE是有效的,將GIC給無效掉。
GICv2,定義了自己的一些寄存器,這些寄存器,都是使用memory-mapped的方式去訪問的,也就是在soc中,會留有一片空間,給GIC。cpu通過訪問這部分空間,來對GIC進行操作。
寄存器,分為以下:
- GICD_*: distributor的寄存器
- GICH_*: 虛擬interface的控制寄存器
- GICV_*:虛擬interface的控制寄存器
- GICC_*: 虛擬cpu interface的寄存器
中斷分組
givc2,將中斷,分成了group0和group1。使用寄存器GICD_IGROUPRn來對每個中斷,設置組。
- group0:安全中斷,由nFIQ驅動
- group1:非安全中斷,由nIRQ驅動
中斷號
GICv2,支持最大1020個中斷。其中斷號分配如下:
中斷號 | 分配 | 中斷來源 | 寄存器 |
---|---|---|---|
ID0-ID7 | 非安全軟中斷 | 軟件 | GICD_SGIR |
ID8-ID15 | 安全軟中斷 | 軟件 | GICD_SGIR |
ID16-ID31 | 私有中斷 | 外設 | no |
ID32-ID1019 | 共享中斷 | 外設 | no |
GIC結構
GIC主要包括以下兩個組件:distributor、cpu interface。
distributor
中斷分發器,用來收集所有的中斷來源,並且為每個中斷源設置中斷優先級,中斷分組,中斷目的core。當有中斷產生時,將當前最高優先級中斷,發送給對應的cpu interface。
distributor對中斷提供以下的功能:
- 全局中斷使能
- 每個中斷的使能
- 中斷的優先級
- 中斷的分組
- 中斷的目的core
- 中斷觸發方式
- 對於SGI中斷,傳輸中斷到指定的core
- 每個中斷的狀態管理
- 提供軟件,可以修改中斷的pending狀態
cpu interface
cpu interface,將GICD發送的中斷信息,通過IRQ,FIQ管腳,發送給連接到該cpu接口的core。
cpu interface提供了一下的功能:
- 將中斷請求發送給cpu
- 對中斷進行認可(acknowledging an interrupt)
- 中斷完成識別(indicating completion of an interrupt)
- 設置中斷優先級屏蔽
- 定義中斷搶占策略
- 決定當前處於pending狀態最高優先級中斷
中斷認可
中斷認可,是指cpu響應該中斷。此時中斷狀態從pending狀態,變為active狀態。通過訪問GICC_IAR寄存器,來對中斷進行認可。
- GICC_IAR: 認可group0的中斷
- GICC_AIAR:認可group1的中斷
中斷完成
中斷完成,是指cpu處理完中斷。此時中斷狀態從active狀態,變為inactive狀態。GIC中,對中斷完成,定義了以下兩個stage:
- 優先級重置(priority drop):將當前中斷屏蔽的最高優先級進行重置,以便能夠響應低優先級中斷。group0中斷,通過寫GICC_EOIR寄存器,來實現優先級重置,group1中斷,通過寫 GICC_AEOIR 寄存器,來實現優先級重置。
- 中斷無效(interrupt deactivation):將中斷的狀態,設置為inactive狀態。通過寫 GICC_DIR 寄存器,來實現中斷無效。
這里為什么要對中斷完成,定義2個stage,其實是有考慮的。對於中斷來說,我們是希望中斷處理程序越短越好,但是有些中斷處理程序,就是比較長,在這種情況下,就會使其他中斷得到相應,從而影響實時性。
比如當前cpu在響應優先級為4的中斷A,但是這個中斷A的中斷處理程序比較長,此時如果有優先級為5的中斷B到來,(優先級越小,優先級越高)那么cpu是不會響應這個中斷的。
在軟件上,會將中斷處理程序分為兩部分,分為上半部分,和下半部分。在上半部分,完成中斷最緊急的任務,然后就可以通知GIC,降低當前的中斷處理優先級,以便其他中斷能夠得到響應。在下半部分,處理該中斷的其他事情。
在這種機制下,低優先級的中斷,不用等待高優先級的中斷,完全執行完中斷處理程序后,就可以被cpu所響應,提高實時性。
為了實現上述機制,就將中斷完成分成了2步。還是剛剛的例子,cpu在響應優先級為4的中斷A,當中斷A的上半部分完成后,通知GIC,優先級重置(drop priority),GIC將當前的最高優先級中斷重置,重置到響應中斷A之前的優先級,比如優先級6,那么此時優先級為5的中斷B,就可以被cpu響應。最后中斷A的下半部分完成后,通知GIC,將該中斷A的狀態,設置為inactive狀態,此時中斷A就真正的完成了。
當然,也可以不將中斷完成分成2步(就1步)。通過控制 GICC_CTLR寄存器的EOImode比特,來決定是否將中斷完成分成2步。
bypass功能
GICv2支持bypass功能,這樣GIC就不起作用了,core的中斷管腳,直接由soc的其他部門信號驅動。
如下圖所示,通過控制 GICC_CLTR 寄存器的一些比特位,來實現bypass功能。不過這個功能一般不使用,不然何必要在arm的soc中,加入GIC呢?
中斷處理流程
中斷處理流程,包含了以下幾步:
- GIC決定每個中斷的使能狀態,不使能的中斷,是不能發送中斷的
- 如果某個中斷的中斷源有效,GIC將該中斷的狀態設置為pending狀態,然后判斷該中斷的目標core
- 對於每一個core,GIC將當前處於pending狀態的優先級最高的中斷,發送給該core的cpu interface
- cpu interface接收GIC發送的中斷請求,判斷優先級是否滿足要求,如果滿足,就將中斷通過nFIQ或nIRQ管腳,發送給core。
- core響應該中斷,通過讀取 GICC_IAR 寄存器,來認可該中斷。讀取該寄存器,如果是軟中斷,返回源處理器ID,否則返回中斷號。
- 當core認可該中斷后,GIC將該中斷的狀態,修改為active狀態
- 當core完成該中斷后,通過寫 EOIR (end of interrupt register)來實現優先級重置,寫 GICC_DIR 寄存器,來無效該中斷
中斷使能和禁止
通過設置GICD_ISENABLERn寄存器,來使中斷使能,通過設置GICD_ICENABLERn寄存器,來使中斷禁止。
這兩個寄存器,都是bit有效的寄存器,也就是一個bit,關聯一個中斷。
比如對於GICD_ISENABLER寄存器,描述如下:
中斷pending
通過設置GICD_ISPENDRn或GICD_ICPENDRn寄存器,可以讀取和修改中斷的pending狀態。這兩個寄存器,也是bit有效的寄存器,一個bit,關聯一個中斷。
中斷active
通過設置GICD_ISACTIVERn或GICD_ICACTIVERn寄存器,可以讀取和修改中斷的active狀態。這兩個寄存器,也是bit有效的寄存器,一個bit,關聯一個中斷。
產生軟中斷
通過寫 GICD_SGIR 寄存器,來產生軟中斷。軟中斷,可以指定產生中斷,發往執行的core,也可以發送多個core。
對於軟中斷,這個是軟件產生的中斷。比如軟件,想給執行自己的core,發送一個中斷,就可以通過軟中斷來產生。或者軟件,想起其他的core,發送一個中斷,也可以通過軟中斷來產生。
寄存器如下:
TargetListFileter
對於TargetListFileter:決定distributor將軟中斷,如何發送給cpu interface。
- 0b00:按照CPUTargetList的指定,來發送軟中斷
- 0b01:按照CPUTargetList的指定,來發送軟中斷,但是不能發送給自己
- 0b10:軟中斷,只能發送給自己
CPUTargetList
描述如下:
對於多core的系統,會給每個core一個編號。GICv2支持最多8個core,因此core的編號就是0-7,剛好8個bit,可以表示。
這樣的CPUTargetList,就和8個cpu相對應。第0bit,表示core0,第7bit,表示core7。
如果想給core1,core2,core7發送軟中斷,那么此時這個位域要填入0x84。
NSATT
描述如下:
這個用來支持安全擴展,在GICv2中,將中斷進行了分組
- group0:安全中斷
- group1:非安全中斷
這個bit,用來表示發送的軟中斷,是安全中斷,還是非安全中斷。而且這個bit,只有core處於安全狀態的時候,才能寫。如果core是處於非安全狀態,那么這個bit被忽略,也就是只能發非安全的軟中斷。
SGIINTID
發送的軟中斷的中斷號。
中斷優先級
GICv2,支持最小16個,最大256個中斷優先級,如下圖所示:
如果實現的中斷優先級小於256個,那么最低的幾個bit,是為0的。
通過設置GICD_IPRIORITYRn寄存器,來設置中斷的優先級。這個寄存器是字節有效的,也就是一個字節,對應一個中斷的優先級。優先級數值越小,那么這個中斷的優先級越高。
高優先級的中斷,是可以搶占低優先級的中斷。
GIC使用例子
下圖是GIC的使用例子:
外部的中斷,連接到GIC。由distributor進行中斷分組。中斷請求,由distributor發送給cpu interface,cpu interface再發送給處理器。
對於支持安全擴展,其應用如下:
安全中斷,處於group0,非安全中斷處於group1。
GIC寄存器
GIC寄存器,分為兩部分,一部分是distributor的寄存器,另一部分是cpu interface的寄存器。
兩部分的寄存器,均是通過memory-mapped的方式來訪問。
下圖是distributor的寄存器:
下圖是cpu interface的寄存器:
總結
以上就是GICv2的介紹,更多的內容,需要查看GICv2的文檔。
GICv2比較簡單,最多只能支持8個core,超過了8個core,那么就不能使用GICv2了。不過這也不是大問題,對於手機的arm處理器來說,最多也就8個core。但是對於服務器,桌面級的arm處理器,那么就可能會超過8個core,此時GICv2就不適用了,所以ARM后面又加入GICv3,v4架構。
GICv2的寄存器,都是通過memory-mapped的方式訪問。但是中斷在一個soc系統中,是經常會產生的,那么處理器就會經常的讀取GIC的寄存器,而使用memory-mapped的方式去訪問,就會影響中斷響應速度。在之后的GICv3,v3中,就加入了使用系統寄存器來進行訪問,加快中斷處理。
GICv2只是一個GIC的架構,其實現的對應的IP是GIC400。
ARM GIC(四) GICv3架構基礎
GICv3架構是GICv2架構的升級版,增加了很多東西。變化在於以下:
- 使用屬性層次(affinity hierarchies),來對core進行標識,使GIC支持更多的core
- 將cpu interface獨立出來,用戶可以將其設計在core內部
- 增加redistributor組件,用來連接distributor和cpu interface
- 增加了LPI,使用ITS來解析
- 對於cpu interface的寄存器,增加系統寄存器訪問方式
GICv3結構
下圖是GICv3的架構。
**
**
包含了以下的組件:
- distributor:SPI中斷的管理,將中斷發送給redistributor
- redistributor:PPI,SGI,LPI中斷的管理,將中斷發送給cpu interface
- cpu interface:傳輸中斷給core
- ITS:用來解析LPI中斷
其中,cpu interface是實現在core內部的,distributor,redistributor,ITS是實現在GIC內部的。
cpu interface和GIC的redistributor通信,通過AXI-Stream協議,來實現通信。
屬性層次
GICv3的一大變化,是對core的標識。對core不在使用單一數字來表示,而是使用屬性層次來標識,和arm core,使用MPIDR_EL1系統寄存器來標識core一致。
每個core,根據屬性層次的不同,使用不同的標號來識別。如下圖所示,是一個4層結構,那么對於一個core來說,就可以用 xxx.xxx.xxx.xxx 來識別。
這種標識方式,和ARMv8架構的使用MPIDR_EL1寄存器,來標識core是一樣的。
每個core,連接一個cpu interface,而cpu interface會連接GIC中的一個redistributor。redistributor的標識和core的標識一樣。
中斷分組
GICv3,將中斷分成了2個大組,group0和group1。
-
group0:提供給EL3使用
-
group1:又分為2組,分別給安全中斷和非安全中斷使用
如下圖所示:
以下是IRQ,FIQ與組的對應關系。
中斷生命周期
中斷生命周期,如下圖所示:
-
generate:外設發起一個中斷
-
distribute:distributor對收到的中斷源進行仲裁,然后發送給對應的cpu interface
-
deliver:cpu interface將中斷發送給core
-
activate:core通過讀取 GICC_IAR 寄存器,來對中斷進行認可
-
priority drop: core通過寫 GICC_EOIR 寄存器,來實現優先級重置
-
deactivation:core通過寫 GICC_DIR 寄存器,來無效該中斷
這個中斷生命周期,和GICv2的中斷生命周期是一樣的。
中斷流程
下圖是GIC的中斷流程,中斷分成2類:
- 一類是中斷要通過distributor,比如SPI中斷
- 一類是中斷不通過distributor,比如LPI中斷
中斷要通過distributor的中斷流程
-
外設發起中斷,發送給distributor
-
distributor將該中斷,分發給合適的re-distributor
-
re-distributor將中斷信息,發送給cpu interface。
-
cpu interface產生合適的中斷異常給處理器
-
處理器接收該異常,並且軟件處理該中斷
LPI中斷的中斷流程
-
外設發起中斷,發送給ITS
-
ITS分析中斷,決定將來發送的re-distributor
-
ITS將中斷發送給合適的re-distributor
-
re-distributor將中斷信息,發送給cpu interface。
-
cpu interface產生合適的中斷異常給處理器
-
處理器接收該異常,並且軟件處理該中斷
中斷處理
中斷處理,分為邊沿觸發處理和電平觸發處理
邊沿觸發處理
外部邊沿中斷到達,中斷狀態被置為pending狀態。
軟件讀取IAR寄存器值,表示PE認可該中斷,中斷狀態被置為active狀態
軟件中斷處理完畢后,寫EOIR寄存器,表示優先級重置。過一段時間后,寫DIR寄存器,中斷狀態被置為idle狀態。
電平觸發處理
外部高電平中斷到達,中斷狀態置為pending狀態。
軟件讀取IAR寄存器,表示PE認可該中斷。但中斷依然為高,中斷狀態進入pending and active狀態。
軟件中斷處理完畢后,寫EOIR寄存器,表示優先級重置。過一段時間后,寫DIR寄存器,中斷狀態被置為idle狀態。
寄存器
GICv3中,多了很多寄存器。而且對寄存器,提供了2種訪問方式,一種是memory-mapped的訪問,一種是系統寄存器訪問:
memory-mapped訪問的寄存器:
-
GICC: cpu interface寄存器
-
GICD: distributor寄存器
-
GICH: virtual interface控制寄存器,在hypervisor模式訪問
-
GICR: redistributor寄存器
-
GICV: virtual cpu interface寄存器
-
GITS: ITS寄存器
系統寄存器訪問的寄存器:
-
ICC: 物理 cpu interface 系統寄存器
-
ICV: 虛擬 cpu interface 系統寄存器
-
ICH: 虛擬 cpu interface 控制系統寄存器
下圖是GICv3中,各個寄存器,所在的位置。
對於系統寄存器訪問方式的GIC寄存器,是實現在core內部的。而memory-mapped訪問方式的GIC寄存器,是在GIC內部的。
GICv3架構中,沒有強制,系統寄存器訪問方式的寄存器,是不能通過memory-mapped方式訪問的。也就是ICC, ICV, ICH寄存器,也是可以實現在GIC內部,通過memory-mapped方式去訪問。但是一般的實現中,是沒有這樣的實現的。
下圖是ICC的系統寄存器,和memory-mepped方式寄存器的對應關系的一部分,更多的就要查看GICv3的spec。
那么,問題來了,GICv3中,為什么選擇將cpu interface,從GIC中抽離,實現在core內部?為什么要將cpu interface的寄存器,增加系統寄存器訪問方式,實現在core的內部?這樣做,是有什么好處?
我認為,GICv3的上述安排,第一是為了軟件編寫能夠簡單,通用,第二是為了讓中斷響應能夠更快。
首先要先了解,在GIC的寄存器中,哪一些寄存器,是會頻繁被core所訪問的,哪一些寄存器,是不會頻繁被core所訪問的。毫無疑問,cpu interface的寄存器,是會頻繁被core所訪問的,因為core需要訪問cpu interface的寄存器,來認可中斷,來中斷完成,來無效中斷。而其他的寄存器,是配置中斷的,只有在core需要去配置中斷的時候,才用訪問得到。有了這個認識,那么理解之后我所講述的,就比較容易了。
在GICv2中,cpu interface的寄存器,是實現在GIC內部的,因此當core收到一個中斷時,會通過axi總線(假設memory總線是axi總線),去訪問cpu interface的寄存器。而中斷在一個soc系統中,是會頻繁的產生的,這就意味着,core會頻繁的去訪問GIC的寄存器,這樣會占用axi總線的帶寬,總而會影響中斷的實時響應。而且core通過axi總線去訪問cpu interface寄存器,延遲,也比較大。
在GICv3中,將cpu interface從GIC中抽離出來,實現在core內部,而不實現在GIC中。core對cpu interface的訪問,通過系統寄存器方式訪問,也就是使用msr,mrs訪問,那么core對cpu interface的寄存器訪問,就加速了,而且還不占用axi總線帶寬。這樣core對中斷的處理,就加速了。
cpu interface與GIC之間,是通過專用的AXI-stream總線,來傳輸信息的,這樣也不會占用AXI總線的帶寬。
GICv3架構的內容,比較多,我這邊會拆成幾個部分。
下一部分,會介紹GIC stream協議。也就是cpu interface與GIC之間的通信。
ARM GIC(五)GICv3架構-GIC stream協議
GIC stream協議,是基於AXI-stream協議。用於GIC的IRI組件(interrupt routing infrastructure),和cpu interface之間,傳輸信息。
distributor,redistributor和ITS,統稱為IRI組件。
GIC stream協議,包含以下2個接口:
- 下行AXI-stream接口:用於IRI向cpu interface傳遞信息,連接
- 上行AXI-stream接口:用於cpu interface向IRI傳遞信息
如下圖所示:
接口信號
接口信號,包含下行接口信號,和上行接口信號。無論是上行還是下行,都是基於AXI-stream協議,通過data信號,來傳輸數據,data信號的位寬,也是固定的,為16bit。
下行接口信號
下行接口信號如下表所示,接口協議是基於AXI-stream協議。
上行接口信號
上行接口信號如下表所示,接口協議是基於AXI-stream協議。
包
IRI與cpu interface通過GIC stream協議傳輸信息,傳輸的信息,是以包為單位。包,分為兩類包:
- 命令包,分為redistributor命令包,cpu interface命令包
- 響應包,分為redistributor響應包,cpu interface響應包
AXI-stream協議,每次傳輸2個字節,多次傳輸,組成一個包。不同的包,大小不是一樣的,比如有的是16個字節,有的是8個字節。包傳輸的第一個16bit數據,表示包的類型。
如果一個組件,發送命令包,那么另一個,需要回應響應包。
redistributor命令包
下圖是下行redistributor命令包。
redistributor響應包
下圖是上行redistributor響應包。
cpu interface命令包
下圖是cpu interface命令包。
cpu interface響應包
下圖是cpu interface響應包。
例子
以下是cpu interface的activate命令包,格式如下:
這個activate命令,由cpu interface發送給IRI,表示認可中斷,中斷號,由包中的INTID字域來表示。
IRI在收到cpu interface的activate命令后,會回發activate acknowledge響應包。表示,IRI接收到cpu interface的activate命令。
其格式,如下所示:
包在傳輸的過程中,先發第一個數據的低16bit數據,再發第一個數據的高16bit數據,如果還有下一個數據,按照上述流程發送。所以,命令的類型,是最先發送的。
包傳輸流程
中斷發送
如下圖,redistributor,要發送一個中斷給cpu。包的傳輸的流程,如下:
- redistributor給cpu interface發送set命令,發送中斷X請求,cpu interface接收到該命令后,如果該中斷X符合當前優先級要求,CPU interface通過IRQ/FIQ給cpu發送中斷。
- CPU響應CPU interface發送的中斷,於是去讀取ICC_IAR寄存器,表示認可該中斷,得到中斷號。之后cpu interface給redistributor發送activate響應。然后把IRQ/FIQ給取消掉。
中斷取消
CPU在讀取ICC_IAR寄存器前,redistributor取消中斷。包的傳輸流程,如下:
- redistributor給cpu interface發送set命令,cpu interface接收到該命令后,通過IRQ/FIQ給cpu發送中斷。
- redistributor給cpu interface發送clear命令,清除該中斷,cpu interface將IRQ/FIQ拉低。然后回release響應。
- cpu interface給redistributor,回clear acknowledge響應。
- 如果此時,cpu讀取IAR寄存器,CPU會獲取到一個假的中斷號。
兩個中斷
redistributor給cpu interface發送兩個中斷。包的傳輸流程,如下圖所示:
- redistributor,首先發送set x命令,發送中斷x。cpu interface接收該命令,將IRQ/FIQ拉高,向CPU發送中斷請求。
- cpu讀取ICC_IAR寄存器,認可該中斷x,開始處理該中斷x。cpu interface給redistributor回activate x響應。
- 之后,redistributor給CPU interface發送set y命令,發送中斷y。在cpu interface中,如果y的優先級符合要求,那么IRQ/FIQ會一直有效。等待CPU處理。
中斷搶占
redistributor給cpu interface發送2個中斷,第二個中斷搶占第一個中斷。包的流程如下:
- redistributor,首先發送set x命令,發送中斷x。cpu interface接收該命令,將IRQ/FIQ拉高,向CPU發送中斷請求。
- 在cpu讀取ICC_IAR寄存器之前,redistributor,又給cpu interface發送了set y命令,發送中斷y。並且y的優先級比x高。
- cpu interface給redistributor回release x響應,表示cpu interface暫時不處理中斷x,中斷x,將來重新發送。
- CPU讀取ICC_IAR寄存器,認可中斷y。cpu interface給redistributor回activate y響應。
電源斷電
GIC給CPU發送中斷,使CPU斷電。
這個,就比較復雜了。就不解析了。
電源上電
redistributor,請求將CPU和cpu interface上電。
這個也比較復雜,這里不解析。
總結
GICv3中,IRI與cpu interface之間,是通過包,來傳輸信息。傳輸的接口協議,使用AXI-stream。通過包的各種組合,來實現GIC的中斷操作與中斷管理。
之后,會介紹GICv3中,引入的一種新的中斷類型,消息中斷。
ARM GIC(六)GICv3架構-LPI
在GICv3中,引入了一種新的中斷類型。message based interrupts,消息中斷。
消息中斷
外設,不在通過專用中斷線,向GIC發送中斷,而是寫GIC的寄存器,來發送中斷。
這樣的一個好處是,可以減少中斷線的個數。
為了支持消息中斷,GICv3,增加了LPI,來支持消息中斷。並且為他分配了特別多的中斷號,從8192開始,移植到16777216。
LPI,locality-specific peripheral interrupts。spec中,用了一章,來介紹這個LPI。
LPI介紹
LPI是一種基於消息的邊沿中斷。也就是,中斷信息,不在通過中斷線,進行傳遞,而是通過memory。GIC內部,提供一個寄存器,當外設往這個地址,寫入數據時,就往GIC發送了一個中斷。
在soc系統中,外設想要發送中斷給GIC,是需要一根中斷線的。如果現在一個外設,需要增加一個中斷,那么就要增加一根中斷線,然后連接到GIC。這樣,就需要修改設計。而引入了LPI之后,當外設需要增加中斷,只需要使用LPI方式,傳輸中斷即可,不需要修改soc設計。
引入了LPI之后,GICv3中,還加入了ITS組件,interrupt translation service。ITS將接收到的LPI中斷,進行解析,然后發送到對應的redistributor,再由redistributor將中斷信息,發送給cpu interface。
以下是帶LPI的框圖。外設,通過寫 GITS_TRANSLATER 寄存器,來傳遞消息中斷。
LPI,和SPI,PPI,SGI有些差別,LPI的中斷的配置,以及中斷的狀態,是保存在memory的表中,而不是保存在GIC的寄存器中的。
- GICR_PROPBASER:保存LPI中斷配置表的基地址
- GICR_PENDBASER: 保存LPI中斷狀態表的基地址
這里,就涉及到兩個表:
LPI中斷配置表
該表,保存在memory中。基地址,由GICR_PROPBASER寄存器決定。
該寄存器描述如下:
其中的Physical_Address字段,指定了LPI中斷配置表的基地址。
對於LPI配置表,每個LPI中斷,占用1個字節,指定了該中斷的使能和中斷優先級。
當外部發送LPI中斷給redistributor,redistributor首先要查該表,也就是要訪問memory來獲取LPI中斷的配置。為了加速這過程,redistributor中可以配置cache,用來緩存LPI中斷的配置信息。
因為有了cache,所以LPI中斷的配置信息,就有了2份拷貝,一份在memory中,一份在redistributor的cache中。如果軟件修改了memory中的LPI中斷的配置信息,需要將redistributor中的cache信息給無效掉。
LPI中斷狀態表
該表,處於memory中,保存了LPI中斷的狀態,是否pending狀態。
LPI中斷的狀態,不是保存在寄存器中,而是保存在memory中的pending表中。該狀態表,由redistributor來進行更改。而該table的基地址,是由軟件來設置的。
軟件通過設置 GICR_PENDBASER 寄存器來設置。
該寄存器,設置LPI狀態表的基地址,該狀態表的memory的屬性,如shareability,cache屬性等。
每個LPI中斷,占用一個bit空間
- 0: 該LPI中斷,沒有處於pending狀態
- 1: 該LPI中斷,處於pending狀態
該狀態表,由redistributor來設置。軟件如果修改該表,會引發unpredictable行為。
LPI的實現方式
為了實現LPI,GICv3定義了以下兩種方法來實現:
- 使用ITS,將外設發送到eventID,轉換成LPI 中斷號
- forwarding方式,直接訪問redistributor的寄存器GICR_SERLPIR,直接發送LPI中斷
forwarding方式
這種方式,比較簡單,主要由下面幾個寄存器來實現:
-
GICR_SERLPIR
-
GICR_CLRLPIR
-
GICR_INVLPIR
-
GICR_INVALLR
-
GICR_SYNCR
其GIC框圖如下所示:
GICR_SERLPIR,將指定的LPI中斷,設置為pending狀態。
GICR_INVLPIR,將指定的LPI中斷,清除pending狀態。寄存器內容和GICR_SERLPIR一致。
**
**
GICR_INVLPIR,將緩存中,指定LPI的緩存給無效掉,使GIC重新從memory中載入LPI的配置。
GICR_INVALLR,將緩存中,所有LPI的緩存給無效掉,使GIC重新從memory中,載入LPI中斷的配置。
GICR_SYNCR,對redistributor的操作是否完成。
寄存器,只有第0bit是有效的。如果為0,表示當前對redistributor的操作是完成的,如果為1,那么是沒有完成的。
使用ITS方式
理解了forwarding方式,那么理解ITS方式,就要容易了。forwarding方式,是直接得到了LPI的中斷號。
但是對於ITS方式,是不知道LPI的中斷號的。需要將外設發送的DeviceID,eventID,通過一系列查表,得到LPI的中斷號以及該中斷對應的target redistributor,然后將LPI中斷,發送給對應的redistributor。
下圖是帶有ITS的GIC框圖:
外設,通過寫GITS_TRANSLATER寄存器,發起LPI中斷。寫操作,給ITS提供2個信息:
- EventID:值保存在GITS_TRANSLATER寄存器中,表示外設發送中斷的事件類型
- DeviceID:表示哪一個外設發起LPI中斷。該值的傳遞,是實現自定義,例如,可以使用AXI的user信號來傳遞。
ITS將DeviceID和eventID,通過一系列查表,得到LPI中斷號,再使用LPI中斷號查表,得到該中斷的目標cpu。
ITS將LPI中斷號,LPI中斷對應的目標cpu,發送給對應的redistributor。redistributor再將該中斷信息,發送給CPU。
ITS
ITS是一個組件,用來提供給外設,發送LPI中斷的,然后將LPI中斷,發送給redistributor。
ITS處理流程
ITS使用三類表格,實現LPI的轉換和映射:
- device table: 映射deviceID到中斷轉換表
- interrupt translation table:映射EventID到INTID。以及INTID屬於的collection組
- collection table:映射collection到redistributor
當外設往GITS_TRANSLATER寄存器中寫數據后,ITS做如下操作:
- 使用DeviceID,從設備表(device table)中選擇索引為DeviceID的表項。從該表項中,得到中斷映射表的位置
- 使用EventID,從中斷映射表中選擇索引為EventID的表項。得到中斷號,以及中斷所屬的collection號
- 使用collection號,從collection表格中,選擇索引為collection號的表項。得到redistributor的映射信息
- 根據collection表項的映射信息,將中斷信息,發送給對應的redistributor
以上是物理LPI中斷的ITS流程。虛擬LPI中斷的ITS流程與之類似。以下是處理流程圖:
ITS命令
ITS操作,會涉及到很多表,而這些表的創建,維護是通過ITS命令,來實現的。雖然這些表,是在內存中的,但是GICv3和GICv4,不支持直接訪問這些表,而是要通過ITS命令,來配置這些表。
ITS的操作,是通過命令,來控制的。外部通過發送命令給ITS,ITS然后去執行命令,每個命令,占32字節。
ITS有command隊列,命令寫在這個隊列里面。ITS會自動的按照隊列順序,一一執行。
每個命令占32個字節。
命令,存放在內存中,GITS_CBASE,保存命令的首地址。GITS_CREADR,是由ITS控制,表示下一個命令的地址。GITS_CWRITER,是下一個待寫命令的地址。軟件往GITS_CWRITER地址處,寫入命令,之后ITS就會執行這個命令。
ITS提供的命令,有很多,可以查閱GIC手冊獲取更多。
以下是CLEAR命令。
ITS table
ITS包括很多個表,這些表均處於 non-secure區域。
GITS_BASER
其中的Physical_Address字段,就指定了表的基地址所在位置。
以下是各個表的基地址,對應的寄存器。
總結
GICv3中,引入了消息中斷,並且為之,支持了LPI。分配了大量的中斷號,用於LPI。對於LPI的實現,有2種方式,一種是訪問redistributor提供的寄存器,一種是使用ITS。
不過對於手機arm cpu來說,其實是不需要LPI的,因為現有的中斷,已經符合要求,加入了LPI,讓GIC更復雜,讓軟件操作,也更復雜。但是對於服務器arm cpu,這個,就需要了,因為這個可以和PCIE相連,實現消息中斷。個人感覺,這個LPI中斷,是為arm服務器cpu,所使用的。
對於提供的GIC IP來說,比如GIC600,是有配置選項,決定,是否是否支持LPI中斷,以及是否需要ITS。
ARM GIC(七)GICv3架構-power控制
從GIC3開始,cpu interface放到了PE中,因此cpu interface和PE是同一個power domain。而屬於GIC的其他組件,如redistributor,distributor,是另外一個power domain。因此就有如下一種情況,PE和cpu interface的電源給斷掉了,而GIC的電源並沒有斷掉。此時GIC給cpu interface發送數據,cpu interface是不會響應的。
在這種情況下,GIC提供了power管理功能。
GICR_WAKER寄存器
GIC中,提供了如下的 GICR_WAKER 寄存器,來支持power功能。
其寄存器描述如下:
斷電cpu interface和PE
在cpu interface和PE要斷電之前,軟件要保證,通知redistributor,cpu interface和PE要進入low-power狀態。軟件,要往GICR_WAKER寄存器的ProcessorSleep字段,寫入1,表示PE要進入到low-power狀態。cpu interface之后將自己置為low-power狀態之后,就將ChildrenAseep字段,設置為1。
當GICR_WAKER.ChildrenAsleep為1之后,redistributor,不會在將中斷,發送給cpu interface,distributor,在中斷仲裁時,也不會考慮該PE。
喚醒cpu interface和PE
當GIC要喚醒cpu interface和PE時,也是操作這個 GICR_WAKER寄存器。
將processorsleep,寫入0,然后去喚醒該cpu,最后讀取childrenasleep,判斷PE是否喚醒成功,
此條目發表在ARM分類目錄,貼了GIC標簽。將固定鏈接加入收藏夾。
ARM GIC(八)總結
GIC,是arm為了實現復雜的中斷控制,而定義的一套架構。版本也歷經了多個變化,從最初的GICv1到現在最新的GICv4。每一個新的版本,都增加了一些新的功能。
目前最新的GIC-600 IP,支持GICv4。
不過從GICv3開始,架構就和之前的架構,變化就比較大了。
變化一:cpu interface
下圖是GICv2架構,cpu interface是實現在GIC內部,而且GIC的寄存器,都是memory-mapped方式訪問。
下圖是GICv3架構,cpu interface從GIC內部剝離,實現在PE的內部。並且將cpu interface的寄存器,提供了系統寄存器訪問方式,從而實現中斷的快速響應。
變化二:core的標識
GICv3中,對於core的標識,使用了屬性層次的方式,來進行標識,從而可以支持更多的core。
而GICv2中,支持最大8個core。
變化三:消息中斷
GICv3中,加入了LPI中斷類型,來實現消息中斷。並且提供了ITS,來實現中斷的轉換。
變化四:SGI處理
對於SGI的處理,有如下的變化。
總結
GICv3/v4,架構,比GICv2架構,增加了很多的特性,從而支持更復雜的中斷管理,支持更多的cpu。
自此,本系列博文到此就要結束了,基本上,除了虛擬中斷的相關內容,我將GIC的內容都進行了介紹。希望大家看完這系列博文,能夠對GIC有所認識。當初,自己也是看了很多的文檔,外加上代碼,才對這個理解的。
后面,如果我有去了解過虛擬中斷,會在寫一系列博文,來介紹虛擬中斷。
ARM GIC(九) GICv3的中斷分組
GICv3架構中,對中斷進行了分組。分成了以下三個組:
-
group0,用於EL3處理的中斷
-
secure group1:用於secure EL1處理的中斷
-
non-secure group1:用於non-secure的EL2和non-secure的EL1。
對於redistributor的set命令,帶有Mod和Grp參數。
Mod與Grp共同表示,中斷所屬的組。其組合如下圖所示:
對於每一組中斷,有一個系統寄存器,來控制該組中斷是否有效。
- ICC_IGRPEN0_EL1:針對group0的中斷
- ICC_IGRPEN1_EL1:針對group1的中斷,該寄存器分為non-secure和secure訪問,不同的secure下,是訪問當前secure下的寄存器
而每個中斷的分組,由以下兩個寄存器來決定:
-
GICR_IGROUPR
: interrupt group registers -
GICR_IGRPMODR
:interrupt group modifier registers 每個中斷,占寄存器中的1個bit,使用中斷號進行索引。
當GIC給cpu interface通過set命令發送中斷,cpu能夠響應該組中斷,會回發activate命令,認可該中斷。
如果cpu不能響應該組中斷,會回發release響應。如下圖所示:
GIC給cpu interface通過set命令發送中斷,中斷號為93,優先級為0x40,Mod和Grp均為1,表示non-secure的group1。
cpu interface不能響應該中斷,回發release響應。
ARM GIC(十) GICv3軟中斷
軟中斷(software generated interrupts),用來多個核之間的通信(inter-processor communication)。軟件通過寫SGI寄存器來產生。
-
軟件寫ICC_SGI1R_EL1產生對應當前secure狀態的group1軟中斷
-
軟件寫ICC_ASGI1R_EL1產生secure狀態的group1軟中斷
-
軟件寫ICC_SGI0R_EL1產生secure狀態的group0軟中斷
這三個寄存器的位域是一樣的,如下圖:
- Aff3.Aff2.Aff1:表示軟中斷目的CPU的屬性層次。
- TargetList:表示要發給哪些CPU。
- INTID:表示軟中斷號
- IRM:軟中斷發送,是按照屬性層次發送,還是發起其他所有的cpu
- RS:range selector,和TargetList結合,表示發送哪些CPU
例如,IRM為0,INTID為1,Aff3.Aff2.Aff1為0.0.0,TargetList為0xf,RS為0,就表示,軟中斷發送給屬性層次為0.0.0.[0-3]的cpu。
GICv3對軟中斷的中斷號,進行了規定,只能是0-15。
cpu interface SGI命令
軟件寫SGI寄存器后,cpu interface會通過GIC stream接口,發送SGI命令。
-
SGT:表示寫的哪一個SGI寄存器
0b00: ICC_SGI0R_EL1
0b01: ICC_SGI1R_EL1
0b10: ICC_ASGI1R_EL1
0b11: Reserved
-
NS: 當前的secure狀態,0表示secure,1表示non-secure
-
IRM: SGI寄存器的IRM bit
-
A3V: aff3是否有效,如果有效,需要發送A3數據。
-
RSV: range selector域是否有效
-
SGInum: 軟中斷中斷號
-
A3,A2,A1: 對應Aff3.Aff2.Aff1
-
TargetList: 對應SGI寄存器中的TargetList
-
Range Selecto: 對應SGI寄存器中的RS域
比如,往ICC_SGI0R_EL1寄存器寫0xffffef_ffffffff,那么cpu interface會發送如下波形:
A3V
GICv3中,使用屬性層次對CPU進行編號,屬性層次最多有4層,最高層為Aff3,這一層可以通過cpu interface系統寄存器來控制,是否使能。
ICC_CTLR_EL3和ICC_CTLR_EL1的A3V表示cpu interface是否支持Aff3,而GICD_TYPER.A3V表示GIC ip是否支持Aff3。
關於range selector的理解。
GICv3的spec對range selector的解釋如下:
GIC3中,使用屬性層次,來對CPU進行標識,這樣可以精確的將中斷發送指定的cpu。屬性層次最高有4層,為aff3.aff2.aff1.aff0。每一個屬性層次,用8bit來表示。如下圖所示:
SGI中斷,是可以同時發送給多個cpu的中斷,通過TargetList來表示要發送給哪一些CPU。而TargetList只有16個bit,也就是只能發送給16個cpu,但是Aff0最多可以表示256個cpu,那怎么發送給其他的cpu的了?
這個時候range selector就派上用場了,RS域共4個bit,可以表示16個范圍,16×16=256,剛好表示256個cpu。所以spec里面,說TargetList[n]表示的aff0的值為RS*16 + n。
比如要給65-68號CPU發送軟中斷,首先判斷這些cpu屬於的range,為5,那么給這些CPU發送軟中斷,RS為5,TargetList為0x1E。
GIC的SGI認可響應
當GIC收到cpu interface發的SGI命令,需要回SGI認可響應。
此條目發表在ARM分類目錄,貼了GIC標簽。將固定鏈接加入收藏夾。
ARM GIC(十一) GICv3架構-two secure state
GICv3中,引入了支持2種安全狀態(secure state),也就是對於中斷,根據secure狀態,分為安全中斷和非安全中斷。當然也可以只支持一種安全狀態。。
這里的2種安全狀態和1種安全狀態,主要是影響中斷分組,所使用IRQ和FIQ管腳的映射,以及GIC中的寄存器訪問。
中斷線的映射
當GIC架構,使用GICv3后,中斷的傳遞,和GICv2有所區別。
GICv3中,將cpu interface從GIC中抽離,放入到了cpu中,cpu interface通過GIC stream接口,與GIC進行通信。
當GIC要發送中斷,GIC通過GIC stream接口,給cpu interface發送中斷命令,cpu interface收到中斷命令后,根據中斷線映射配置,決定是通過IRQ還是FIQ管腳,向cpu發送中斷。
而中斷線映射配置,要根據中斷的分組以及當前cpu所處的EL以及seucre狀態,來決定。
2種安全狀態中斷線映射
當GIC支持2種安全狀態,EL3是AArch64和AArch32,映射情況不同
EL3是AArch64
當EL3是AArch64時,映射如下:
◾對於group0中斷,中斷線均映射到FIQ
◾對於group1安全中斷,secure EL1或EL0,中斷線映射到IRQ,其他EL映射到FIQ
◾對於group1非安全中斷,secure EL1或EL0以及EL3,中斷線映射到FIQ,其他EL映射到IRQ
EL3是AArch32
當EL3是AArch32時,映射如下:
◾對於group0中斷,中斷線均映射到FIQ
◾對於group1安全中斷,secure EL0和EL3,中斷線映射到IRQ,其余EL映射到FIQ
◾對於group1非安全中斷,secure EL0和EL3,中斷線映射到FIQ,其余EL映射到IRQ
1種安全狀態中斷線映射
映射如下:
◾group0中斷線,直接映射到FIQ
◾group1中斷線,直接映射到IRQ
GICD寄存器
在GICD中的GICR_CTLR寄存器的DS bit,表示是否支持2種安全模式。
該bit描述如下,如果0,表示支持2種安全狀態,為1,表示不支持。
支持2種安全模式下GICD_CTLR
在支持2種安全模式下,GICD中寄存器會進行備份成2份,一份提供給secure訪問,。一份提供給non-secure訪問。
比如對於GICD_CTLR寄存器,secure訪問,寄存器描述如下:
而如果是non-secure訪問,其寄存器描述如下:
支持1種安全模式下GICD_CTLR
在1種安全模式下,寄存器描述如下,此時不論是non-secure訪問,還是secure訪問,都訪問的同一個寄存器。
系列其他篇
ARM GIC(十二) 中斷bypass
在GICv2架構中,GIC與core之間,是直接通過irq,fiq管腳,傳遞中斷信號。但是在GICv3架構中,GIC通過GIC stream接口向cpu interface傳遞中斷信息,然后由cpu interface向core傳遞中斷信息,而且,cpu interface被設計在了core當中。
GICv3支持中斷bypass功能,以下是我畫的一個框圖:
對於core而言,中斷有2種(不考慮虛擬中斷),分別是IRQ和FIQ。
在ICC_SRE_ELx寄存器,有DFB和DIB bit,分別控制FIQ,IRQ是否bypass。
對於DIB bit的描述:
對於DFB bit的描述:
這2個bit,復位值都是0,表示系統在復位后,中斷bypass功能是開啟的,此時外部的中斷信號,是直接輸入到core中的。
當軟件,關閉中斷bypass后,此時,才是由cpu interface給core發送中斷,而忽略掉外部發送的中斷。
在GICv3架構描述中,提到了中斷bypass功能,會受到下面幾個配置的影響:
一個是,是否運行系統寄存器訪問icc寄存器,也就是ICC_CTLR.SRE bit是否為高,如果允許系統寄存器訪問icc寄存器,那么中斷bypass功能是禁止的。
一個是,ICC_SRE寄存器,由當前設計中支持的最高EL級的ICC_SRE寄存器的DFB和DIB bit來控制。
最后一個,就是中斷分組使能,也就是ICC_IGPREN0_EL1和ICC_IGRPEN1_EL1的enable bit。
如果需要開啟中斷bypass功能,那么需要將中斷分組使能給關掉。
以下是FIQ中斷bypass的偽代碼:
IRQ的處理,和FIQ是一致的。
ARM GIC(十三) 波形為例,介紹GIC600與cpu interface通信
以下以GIC600與cpu interface之間傳遞的包,來說明,他們之間是如何通信的。這里以波形進行介紹。這樣比較直觀。
downstream control命令包
GIC600發送downstream control命令包給cpu interface。命令包的數據為0。
downstream control包格式如下:
解釋如下:
從表中可以解析,GIC600告訴cpu interface做如下配置:
- VL設置為0,表示vINTID位寬是16bit
- PL設置為0,表示pINTID位寬是16bit
- RSS設置為1,表示SGI的aff0,支持0-15
- DS設置為0,表示支持兩種secure模式
cpu interface接收到該命令包后,回一個downstream control acknowledge響應包。包格式如下:
upstream control命令包,設置PMR
cpu interface給GIC600發送upstream control命令包,數據為f8。設置PMR。
該包的格式如下:
對於f8,解析如下:
cpu interface告訴GIC600,當前的ICC_PMR_EL1值設置為f8,也就是將來優先級比這個低的,GIC600就不要發送中斷給cpu interface。
GIC600收到upstream control命令包后,回upstream control acknowledge包,格式如下:
upstream control命令包,設置group enable
cpu interface給GIC600發送upstream control命令包,數據為1。設置group enable。
對於數據1,解析如下:
cpu interface告訴GIC600,將grou0的中斷使能,group1的secure和non-secure中斷不使能。
GIC600收到upstream control命令包后,回upstream control acknowledge包,格式如下:
set命令包,發送中斷
GIC600發送set命令包給cpu interface。數據為20。
set命令包格式如下:
GIC600向cpu interface發送了一個中斷:
-
Grp為0,Mod為0,表示中斷屬於group0中斷組
-
ID length為0,表示INTID位寬為16bit
-
Priority為0x48,表示中斷優先級
-
INTID為0x20,表示中斷號為32(SPI中斷從32開始)
cpu interface收到該set命令后,發現不能處理該中斷,因此發送release響應包,並且INTID指定剛剛GIC600發送的中斷號。
該release響應包的各個參數解釋如下:
GIC600收到cpu interface的release響應包后,知道之前發送的中斷,不能得到cpu interface的響應,在之后,會重新發送該中斷。
這里為什么cpu interface不能處理該中斷,而是要回release響應包。原因在於中斷優先級不符合要求。
對於ICC_PMR_EL1寄存器,GICv3 spec描述如下:
對於中斷,GIC架構,最多使用8個bit,來表示中斷優先級。當然8個bit可以全用,也可以只用一部分,所以架構提供了5種配置方式。設計可以根據自己的需要,選擇一種配置即可。
對於使用的bit不一樣,對應的中斷優先級值不一樣。比如對於使用5個bit,那么就是[7:3],共32種中斷優先級。這個選擇多少個bit,是硬件決定好了,軟件是不能更改的。
軟件可以設置這個寄存器,來表示,符合要求的優先級,可以被cpu響應。優先級值越小,表示優先級越高。
假設這個時候,軟件往這個寄存器寫了8,表示優先級高於8的,都不能被core響應。
假設設計,使用[7:3],表示中斷優先級。
此時,發送了優先級為0x48的中斷,按照[7:3]bit,進行選擇,得到中斷優先級為9,高於設置的8,因此這個中斷不能被core響應,所以cpu interface直接給GIC600回release響應。
set命令包,發送中斷
GIC600發送set命令包給cpu interface。數據為21。
GIC600向cpu interface發送了一個中斷:
- Grp為0,Mod為0,表示中斷屬於group0中斷組
- ID length為0,表示INTID位寬為16bit
- Priority為0x38,表示中斷優先級
- INTID為0x21,表示中斷號為33
cpu interface接收到set命令包,將中斷發送給core,core響應中斷,讀取icc_iar寄存器,表示對該中斷認可。中斷被core認可后,cpu interface就會給GIC600回activate命令包,表示core認可了該中斷。GIC600就可以把該中斷狀態,更新為active或者active and pending狀態。
activate就命令包格式如下:
各個域解析如下:
deactivate命令包,無效中斷
core在處理完中斷后,會寫ICC_EOIR0_EL1或者ICC_EOIR1_EL1寄存器,來無效中斷,cpu interface接受core的這個寫操作,會向GIC600發送deactivate命令包,表示core對中斷處理完畢。
deactivate命令包,格式如下;
各個域解析如下:
GIC600收到cpu interfae發送的deactivate命令后,將自己內部對該中斷維護的狀態,修改為pending 或者 inactive。
ARM GIC(十四)GICv3架構-power控制詳解
在帶有GICv3的soc架構中,其框圖如下所示:
GICv3中的redistributor與core中的cpu interface通過AXI-Stream進行通信。
connection
當core上電之后,需要將core中cpu interface與GIC中的redistributor進行connect,這樣將來GIC才可以將中斷發送給core。
connection的流程如下所示:
描述如下:
- 執行在core的程序,將GICR_WAKER.ProcessorSleep位給置低,表示要connect redistributor
- redistributor在完成connect之后,將GICR_WAKER.ChildrenAsleep位給置低,表示connect完成
- 執行在core的程序,查詢GICR_WAKER.ChildAsleep位是否為0,如果不是,表示redistributor還沒有完成connect操作,就繼續查詢
- 如果查詢到0,表示connect完成,接着做之后的初始化工作
其匯編代碼如下:
波形如下:
首先寫GICR_WAKER寄存器,將ProcessorSleep位給置低。
然后讀取GICR_WAKER寄存器,判斷ChildAsleep位是否為0。在波形中,有讀取到0,表示connect成功。
connect成功后,GIC600會給cpu interface發送downstream包,設置vINTID,pINTID,RSS,DS這幾個域。
downstream包,對於identifier為0,length為1的解釋如下:
disconnection
當core下電之后,需要將core中cpu interface與GIC中的redistributor進行disconnect,這樣將來GIC才不會將中斷發送給core。
disconnection的流程如下所示:
描述如下:
-
執行在core的程序,先將cpu interface的中斷組使能給disable
-
執行在core的程序,將GICR_WAKER.ProcessorSleep位給置高,表示要disconnect redistributor
-
redistributor給cpu interface發送 Quiesce包
-
cpu interface清掉內部所有pending的中斷
-
清除完畢后,cpu interface回發Quiesce Acknowledge包給redistibutor
-
redistributor收到cpu interface回發的響應之后,將GICR_WAKER.ChildrenAsleep位給置高,表示disconnect完成
-
執行在core的程序,查詢GICR_WAKER.ChildAsleep位是否為1,如果不是,表示redistributor還沒有完成connect操作,就繼續查詢
-
如果查詢到1,表示disconnect完成
其匯編代碼如下:
其波形如下:
首先寫GICR_WAKER寄存器,將ProcessorSleep位給置高,表示要disconect。
然后讀取GICR_WAKER寄存器,判斷ChildAsleep位是否為0。在波形中,有讀取到0,表示disconnect不成功,需要再次讀取判斷。
GIC600給cpu interface發送Quiesce包。
cpu interface收到該命令包,完成內部的操作后,回發quiesce acknowledge響應包。
至此,完成了disconnect操作,此時再讀取GICR_WAKER寄存器,ChildAsleep位就為1了。
中斷喚醒core
當core下電之后,GIC就不再會給core發送中斷。如果此時有一個中斷是喚醒core的,那么其處理流程應該如何了?
在GICv3,為每一個redistributor,提供了WakeRequest輸出信號。當GICR_WAKER的ProcessorSleep為1,此時外部有喚醒該core的中斷請求,那么WakeRequest信號會被置高。
WakeRequest信號,會被連接到SCP或者PMU,也就是下圖中的紅色連線。當SCP或者PMU接收到WakeReqeust請求,就會將對應core給上電,然后core再connect redistributor,GIC在將中斷發送給該core,core再響應中斷。