ARMv8-A架构Raspberry Pi 3B定时器与中断启用故障排查
Raspberry Pi 3B(QEMU)定时器中断启用问题排查
我正在QEMU上模拟Raspberry Pi 3B编写嵌入式操作系统,目前卡在定时器与中断的启用环节。已查阅BCM2836/BCM2837 datasheet,尝试向GIC基地址兼64位定时器控制寄存器0x40000000写入0x300,配置了预分频值并启用中断与路由,但读取定时器低32位地址0x4000001C和高32位地址0x40000020时无数据返回。
GDB调试已确认代码运行,进入汇编循环等待中断(已配置有效中断向量表),当前运行在EL3模式,但始终未收到定时器中断。请问启用arm_timer中断还缺少哪些步骤?
相关汇编代码
/* ************************* ** BCM2837 Timer Setup ** ************************* */ .equ TimerAddress, 0x40000000 .equ TimerCtrlVal, 0x300 .equ TimerPrescalerAddr, 0x40000008 .equ TimerPrescalerVal, (1 << 32) .equ TimerRWLSBAddr, 0x4000001C .equ TimerRWMSBAddr, 0x40000020 .equ CoreTimerIntAddr, 0x40000040 .equ CoreTimerIntVal, 0x00000003 .equ Core0Mailbox0, 0x400000C0 .equ AxiCounter, 0x4000002C .equ LocalTimerCtrlAddr, 0x40000034 .equ LocalTimerCtrlVal, 0x3F000000 .equ LocalIntRoutingAddr, 0x40000024 .equ LocalIntRoutingVal, 0x00000000 .equ LocalTimerClearAddr, 0x40000038 .equ LocalTimerClearVal, 0xC0000000 .global _enable_timer _enable_timer: ldr x0, =TimerAddress ldr x1, =TimerCtrlVal str w1, [x0] ldr x0, =TimerPrescalerAddr ldr x1, =TimerPrescalerVal str w1, [x0] ldr x0, =CoreTimerIntAddr ldr x1, =CoreTimerIntVal str w1, [x0] ldr x0, =LocalTimerCtrlAddr ldr x1, =LocalTimerCtrlVal str w1, [x0] ldr x0, =LocalIntRoutingAddr ldr x1, =LocalIntRoutingVal str w1, [x0] ldr x0, =LocalTimerClearAddr ldr x1, =LocalTimerClearVal str w1, [x0] ret
关键缺失步骤与修复方案
1. EL3模式下的中断权限配置
ARMv8 EL3模式默认屏蔽外部中断,需调整系统控制寄存器和状态寄存器:
- 配置
SCR_EL3:打开外部中断路由(EA=1),根据目标中断处理级别设置IRQ/FIQ位(若要在EL1处理,需将IRQ/FIQ置0)。 - 清除
DAIF寄存器的IRQ/FIQ屏蔽位,确保中断能被CPU响应。
示例汇编代码:
// 配置SCR_EL3允许外部中断路由 mrs x0, SCR_EL3 orr x0, x0, #(1 << 3) // EA=1:允许外部中断到低级别EL bic x0, x0, #(1 << 1) // IRQ=0:IRQ中断路由到当前EL以下级别 bic x0, x0, #(1 << 2) // FIQ=0:FIQ中断路由到当前EL以下级别 msr SCR_EL3, x0 // 清除DAIF寄存器的IRQ/FIQ屏蔽 mrs x0, DAIF bic x0, x0, #0x3 // 清除I(bit0)和F(bit1)位,允许IRQ/FIQ触发 msr DAIF, x0
2. 定时器预分频配置错误
你设置的TimerPrescalerVal=(1 << 32)是无效的:该寄存器为32位,写入后实际值为0,预分频未生效。预分频值范围是1-0xFFF,若要分频为2,直接写入1即可(预分频计数器递减到0后重载,值为N则实际分频系数为N+1)。
修正后的预分频配置:
.equ TimerPrescalerVal, 0x00000001 // 分频系数为2
3. GIC全局控制器未初始化
仅配置本地定时器寄存器不足以触发中断,需完成GIC的基础配置:
- 启用GIC分配器(GICD):设置
GICD_CTLR的EnableGrp0和EnableGrp1位为1。 - 启用GIC CPU接口(GICC):设置
GICC_CTLR的对应组使能位为1。 - 启用本地定时器对应的PPI中断(中断号29):设置
GICD_ISENABLER1的第29位为1(因为PPI中断号是16-31,对应ISENABLER1的bit13)。
4. 本地定时器启动逻辑错误
代码中混淆了本地定时器的加载寄存器和控制寄存器:
- BCM2836本地定时器的加载寄存器是0x40000034,控制寄存器是0x40000030(位0为定时器使能,位1为中断使能)。
- 正确流程:先写入加载值,再设置控制寄存器启动定时器并启用中断。
修正示例:
.equ LocalTimerLoadAddr, 0x40000034 .equ LocalTimerCtrlAddr, 0x40000030 .equ LocalTimerCtrlVal, 0x3 // bit0=1(定时器使能),bit1=1(中断使能) // 写入加载值 ldr x0, =LocalTimerLoadAddr ldr x1, =0x3F000000 // 你的原加载值 str w1, [x0] // 启动定时器并启用中断 ldr x0, =LocalTimerCtrlAddr ldr x1, =LocalTimerCtrlVal str w1, [x0]
5. 定时器寄存器读取顺序
BCM2836的64位定时器需先读低32位(0x4000001C),再读高32位(0x40000020),高32位的值会在读取低32位时锁定,单独读高32位无法获取正确的更新值。
内容的提问来源于stack exchange,提问作者Timer Hunter ARMv8
相关产品推荐
相关产品推荐

