一、GICv2 簡介
GIC(Generic Interrupt Controller)是 ARM 架構中用于管理中斷的控制器,GICv2 是其一個重要版本,廣泛應用于 ARM Cortex - A 系列處理器。GIC - 400 是 GICv2 架構下的一款具體的中斷控制器芯片,通過配置其相關寄存器,可實現中斷的分發、優先級管理等功能。
二、關鍵寄存器作用及配置方法
(一)分發器(Distributor)寄存器
分發器負責接收所有中斷源的中斷請求,并將其分發到相應的 CPU 接口。以下是幾個關鍵的分發器寄存器:
- GICD_CTLR(控制寄存器)
- 作用:用于控制分發器的啟用和禁用。
- 配置方法:將其設置為 0 時關閉分發器,設置為 1 時打開分發器。
代碼示例:
volatile uint32_t *dist_base = (volatile uint32_t *)DIST_BASE_ADDR;
dist_base[GICD_CTLR / 4] = 0x0; // 關閉分發器
dist_base[GICD_CTLR / 4] = 0x1; // 打開分發器
- GICD_TYPER(類型寄存器)
- 作用:用于獲取 GIC 支持的最大中斷數。
- 選擇方法:此寄存器用于獲取全局信息,不依賴特定中斷號進行選擇。
- 配置方法:讀取該寄存器的值,通過計算得到最大中斷數。
- 代碼示例:
uint32_t typer = dist_base[GICD_TYPER / 4]; uint32_t it_lines = (typer & 0x1F) + 1; uint32_t max_irq = 32 * it_lines;
- GICD_ISENABLER(中斷設置使能寄存器基址)和 GICD_ICENABLER(中斷清除使能寄存器基址)
- 作用:分別用于使能和禁用中斷源。
- 選擇方法:每個寄存器組控制 32 個中斷,根據中斷號
irq_num計算所在寄存器組偏移irq_num / 32,以及在寄存器中的位偏移irq_num % 32。 - 配置方法:向相應的寄存器位寫入 1 來使能或禁用特定的中斷。
- 代碼示例:
// 禁用所有中斷源 for (uint32_t i = 0; i < max_irq / 32; i++) { dist_base[(GICD_ICENABLER / 4) + i] = 0xFFFFFFFF; } // 使能特定中斷 volatile uint32_t *isenabler = (volatile uint32_t *)(DIST_BASE_ADDR + GICD_ISENABLER); isenabler[irq_num / 32] = 1 << (irq_num % 32);
- GICD_ICFGR(配置寄存器基址)
- 作用:用于配置中斷的觸發模式,如電平觸發或邊沿觸發。
- 選擇方法:每個寄存器控制 16 個中斷,根據中斷號
irq_num計算所在寄存器索引irq_num / 16,以及在寄存器中的位偏移(irq_num % 16) * 2。 - 配置方法:通過設置相應的位來選擇觸發模式。
- 代碼示例:
-
volatile uint32_t *icfgr = (volatile uint32_t *)(DIST_BASE_ADDR + GICD_ICFGR); uint32_t icfgr_idx = irq_num / 16; uint32_t icfgr_bit = (irq_num % 16) * 2; icfgr[icfgr_idx] = (icfgr[icfgr_idx] & ~(0x3 << icfgr_bit)) | (0x2 << icfgr_bit); // 配置為邊沿觸發
- GICD_ITARGETSR(處理器目標寄存器基址)
- 作用:用于指定中斷的目標 CPU。
- 選擇方法:每個中斷對應 4 位,根據中斷號
irq_num計算所在字節偏移irq_num & ~0x3,以及在字節中的偏移irq_num % 4。 - 配置方法:設置相應的位來選擇目標 CPU。
- 代碼示例:
volatile uint8_t *itargetsr = (volatile uint8_t *)(DIST_BASE_ADDR + GICD_ITARGETSR); itargetsr += (irq_num & ~0x3); itargetsr[irq_num % 4] = 0x01; // 配置中斷路由到 CPU0
- GICD_IPRIORITYR(優先級寄存器基址)
- 作用:用于設置中斷的優先級。
- 選擇方法:每個中斷對應一個字節,直接根據中斷號
irq_num定位寄存器偏移。 - 配置方法:向相應的寄存器寫入優先級值。
- 代碼示例:
volatile uint8_t *ipriority = (volatile uint8_t *)(DIST_BASE_ADDR + GICD_IPRIORITYR); ipriority[irq_num] = 0x80; // 設置中斷優先級
- GICD_ISPENDR(中斷 Set - Pending 寄存器組基址)
- 作用:用于檢查中斷是否處于掛起狀態。
- 選擇方法:每個寄存器組控制 32 個中斷,根據中斷號
irq_num計算所在寄存器組偏移irq_num / 32,以及在寄存器中的位偏移irq_num % 32。 - 配置方法:讀取相應的位來判斷中斷是否掛起。
- 代碼示例:
int is_irq_pending(uint32_t irq_num) { volatile uint32_t *dist_base = (volatile uint32_t *)DIST_BASE_ADDR; uint32_t reg_offset = GICD_ISPENDR + (irq_num / 32) * 4; uint32_t bit_offset = irq_num % 32; uint32_t pending_reg = dist_base[reg_offset / 4]; return (pending_reg & (1 << bit_offset)) != 0; }
- GICD_SGIR(軟件生成中斷寄存器)
- 作用:用于軟件生成中斷。
- 選擇方法:此寄存器用于全局軟件中斷生成,不依賴特定中斷號選擇,但需指定中斷號
sgi_num和目標 CPUtarget_cpu。 - 配置方法:寫入相應的值來指定中斷號和目標 CPU。
代碼示例:
void trigger_sgi(int sgi_num, int target_cpu)
{
volatile uint32_t *dist_base = (volatile uint32_t *)DIST_BASE_ADDR;
uint32_t sgi_value = (target_cpu << 16) | (sgi_num & 0xF);
dist_base[GICD_SGIR / 4] = sgi_value;
}
(二)CPU 接口(CPU Interface)寄存器
CPU 接口負責將中斷請求傳遞給 CPU 核心,并處理 CPU 對中斷的響應。以下是幾個關鍵的 CPU 接口寄存器:
- GICC_CTLR(控制寄存器)
- 作用:用于控制 CPU 接口的啟用和禁用。
- 選擇方法:此寄存器控制整個 CPU 接口,不依賴特定中斷號進行選擇。
- 配置方法:設置為 1 時使能 CPU 接口。
代碼示例:
volatile uint32_t *cpu_base = (volatile uint32_t *)GICC_BASE_ADDR;
cpu_base[GICC_CTLR / 4] = 0x1; // 使能 CPU 接口
2.GICC_PMR(優先級掩碼寄存器)
-
- 作用:用于設置允許的最低中斷優先級。
- 選擇方法:此寄存器為全局配置,不依賴特定中斷號進行選擇。
- 配置方法:寫入相應的優先級值。
代碼示例:
cpu_base[GICC_PMR / 4] = 0xFF; // 允許所有優先級
- GICC_IAR(中斷確認寄存器)
- 作用:用于獲取當前正在處理的中斷號。
- 選擇方法:此寄存器返回當前中斷信息,不依賴特定中斷號進行選擇。
- 配置方法:讀取該寄存器的值。
代碼示例:
uint32_t znic_interrupt_dispatcher(void)
{
return ((volatile uint32_t *)GICC_BASE_ADDR)[GICC_IAR / 4] & 0x3FF;
}
- GICC_EOIR(中斷結束寄存器)
- 作用:用于通知 GIC 中斷處理已完成。
- 選擇方法:根據當前處理的中斷號寫入該寄存器。
- 配置方法:寫入相應的中斷號。
- 代碼示例:
volatile uint32_t *cpu_base = (volatile uint32_t *)GICC_BASE_ADDR; cpu_base[GICC_EOIR / 4] = 149; // 確認中斷