一、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; // 确认中断