说明:vectors.S 不存放中断向量表 ,向量表在 startup_gcc.S 的 __Vectors 段;本文件实现 异常/中断入口、上下文保存、软件分发 ISR 等底层逻辑。二者通过 mtvec / mtvt 在启动时关联。
1. 在启动链中的位置
136:140:bsp/artinchip/sys/d13x/startup_gcc.S
la a0, Default_Handler
ori a0, a0, 3
csrw mtvec, a0
la a0, __Vectors
csrw mtvt, a0
| CSR | 指向 | 作用 |
|---|---|---|
mtvec |
Default_Handler(低 2 位 = 3) |
异常统一入口;模式 3 表示与 CLIC/向量扩展配合 |
mtvt |
__Vectors |
中断向量表:IRQn → 处理函数地址 |
sp 初值 |
g_top_irqstack |
中断专用栈顶 |
__Vectors 中 0--15 多为 Default_Handler,7 为 Default_IRQHandler;16 起外设 IRQ 多为 Default_IRQHandler(见 startup_gcc.S)。
整体路径可概括为:
异常 mtvec
IRQ mtvt 表项
是
否
是 中断
否 同步异常
硬件 trap/IRQ
入口
Default_Handler
Default_IRQHandler
mcause低10位==24?
NMI_Handler → g_nmivector
trap
mcause最高位=1?
保存寄存器 → trap_c
g_irqvector 分发 ISR
2. BSS:栈与辅助变量(第 13--39 行)
13:39:bsp/artinchip/sys/d13x/vectors.S
.section .bss
...
g_base_irqstack:
.space AIC_INTERRUPTSTACK_SIZE
g_top_irqstack:
...
g_trap_sp:
.long 0
...
#ifdef KERNEL_BAREMETAL
g_base_normalstack / g_top_normalstack
#endif
irq_nested_level:
.long 0
| 符号 | 含义 |
|---|---|
g_base_irqstack ~ g_top_irqstack |
4KB(AIC_INTERRUPTSTACK_SIZE)中断栈;Reset_Handler 把 sp 设为 g_top_irqstack |
g_trap_sp |
异常上下文保存区指针(trap 里用 g_trap_sp - 132 作帧) |
g_*_normalstack |
仅 baremetal:普通任务栈 |
irq_nested_level |
嵌套计数(本文件未改,可能供他处使用) |
3. Default_IRQHandler:统一外设中断入口(第 47--163 行)
所有指向 Default_IRQHandler 的向量表项都会进入这里,再 按 mcause 软件查表 调用具体 ISR。
3.1 T-Head 硬件压栈:ipush / ipop
47:52:bsp/artinchip/sys/d13x/vectors.S
Default_IRQHandler:
#ifdef __riscv_xthead
ipush
#else
.long 0x0040000b
#endif
- 有 T-Head 扩展:用
ipush/ipop由硬件保存/恢复部分上下文。 - 否则嵌入自定义指令编码
0x0040000b/0x0050000b(与 E907 工具链约定一致)。
3.2 可选:栈回溯帧、FPU
_ENABLE_BACK_TRACE_STACK_+_NO_OMIT_FRAME_POINT_:在栈上建小帧,保存s0、mepc,便于 C 侧回溯。ARCH_RISCV_FPU:保存/恢复ft0--ft11、fa0--fa7共 20 个浮点寄存器(调用约定中的 caller-saved 浮点部分)。
3.3 RT-Thread / FreeRTOS 钩子
87:93:bsp/artinchip/sys/d13x/vectors.S
#if defined(KERNEL_RTTHREAD)
la t0, rt_interrupt_enter
jalr t0
#elif defined(KERNEL_FREERTOS)
la t0, aicos_irq_enter
进入 ISR 前通知内核"进入中断",退出时调用 rt_interrupt_leave / aicos_irq_exit,用于 中断嵌套计数、调度器临界区 等。
3.4 核心:软件向量分发
95:119:bsp/artinchip/sys/d13x/vectors.S
csrr t1, mcause
andi t1, t1, 0x3FF
slli t1, t1, 2
la t0, g_irqvector
add t0, t0, t1
lw t2, (t0)
mv a0, t1
srli a0, a0, 2 /* a0 = irq_num */
la t0, g_irqdata
add t0, t0, t1
lw a1, (t0) /* a1 = irq_data */
...
jalr t2 /* handler(irq_num, irq_data) */
逻辑:
mcause[9:0]→ IRQ 编号(与aic_soc.h中IRQn一致,如CORET_IRQn=7、DMA_IRQn=32)。g_irqvector[irq_num]→ 函数指针(aic_drv_irq.c里drv_irq_register填充)。g_irqdata[irq_num]→ 第二个参数。g_irqcnt[irq_num]++→ 统计中断次数(drv_irq_show等可用)。
默认初始化(drv_irq_vectors_init)把 0...MAX_IRQn-1 设为 Default_Handler,CORET_IRQn 设为 SysTick_Handler;未注册的外设 IRQ 若仍进 Default_IRQHandler,会调到 Default_Handler → trap → 打印异常。
与 硬件向量表 的关系:表项地址固定为 Default_IRQHandler,真正驱动 ISR 在运行时通过 g_irqvector 切换 ,无需改 __Vectors。
4. trap:异常 vs 中断分流(第 173--321 行)
173:321:bsp/artinchip/sys/d13x/vectors.S
trap:
addi sp, sp, -4
sw t0, 0x0(sp)
csrr t0, mcause
blt t0, x0, .Lirq /* MSB=1 → 中断 */
...
/* 同步异常:保存 x1-x31、mepc、mstatus 到 132 字节帧 */
la a5, trap_c
jalr a5
...
j . /* 异常后死循环 */
.Lirq:
...
j Default_IRQHandler
RISC-V 约定:mcause 最高位为 1 表示中断,为 0 表示同步异常。
- 中断 :恢复
t0、sp后跳到Default_IRQHandler(与向量表路径汇合)。 - 同步异常 (非法指令、访存错误、ecall 等):
- 在
g_trap_sp - 132(或AIC_BACKTRACE_DEBUG时用当前sp)保存 GPR 帧; - 故意不保存 x5 (
t0),最后把临时保存的t0写入帧偏移 16; - 调用 C 函数
trap_c(regs)(trap_c.c打印mcause/mtval/mepc等); - 若开启
AIC_BACKTRACE_DEBUG,还会print_stack/print_back_trace; - 最后
j .挂死(正常异常路径不会返回)。
- 在
g_trap_sp 在链接脚本或别处应指向一块 ≥132 字节的缓冲区;异常时复用该静态区域,避免在未知栈上写爆。
5. Default_Handler:mtvec 入口 + NMI(第 327--436 行)
327:339:bsp/artinchip/sys/d13x/vectors.S
Default_Handler:
...
csrr t0, mcause
andi t0, t0, 0x3FF
li t1, 24
beq t0, t1, .NMI_Handler
...
j trap
- 先到
Default_Handler的 trap,多数是 未向量化的异常 或向量表里填的Default_Handler。 mcause & 0x3FF == 24:芯片上的 NMI 异常码 (与aic_soc.h里枚举名NMI_EXPn = -2的"逻辑编号"不同,硬件编码为 24)。- NMI 路径:保存 callee-saved + 浮点(可选)→
jalr g_nmivector(默认TIM4_NMIHandler)→mret返回。 - 非 NMI:
j trap走同步异常/中断判断。
向量表里未单独实现的外设 handler,通过宏弱符号别名到 Default_Handler:
444:448:bsp/artinchip/sys/d13x/vectors.S
.macro def_irq_handler handler_name
.weak \handler_name
.globl \handler_name
.set \handler_name, Default_Handler
.endm
驱动可定义强符号 DMA_IRQHandler 等覆盖;当前 d13x 主要靠 drv_irq_register + g_irqvector,而不是 per-IRQ 汇编符号。
6. Baremetal 的 PendSV_Handler(第 453--461 行)
453:459:bsp/artinchip/sys/d13x/vectors.S
#ifdef KERNEL_BAREMETAL
PendSV_Handler:
j trap
#endif
startup_gcc.S 向量 3 指向 PendSV_Handler;baremetal 下直接进 trap,用于软件 PendSV/上下文切换类异常(无 RT-Thread 时简化处理)。
7. 与 aic_drv_irq.c / trap_c.c 的配合
| 组件 | 职责 |
|---|---|
drv_irq_vectors_init() |
填充 g_irqvector/g_nmivector |
drv_irq_register() |
注册 void handler(uint32_t irq, void *data) |
Default_IRQHandler |
所有 CLIC 外设 IRQ 的汇编入口 + 分发 |
trap_c() |
打印寄存器、可选回调、死循环 |
SysTick:g_irqvector[CORET_IRQn] = SysTick_Handler,仍经 Default_IRQHandler → mcause==7 → SysTick_Handler。
8. 设计要点小结
- 两级模型 :硬件
mtvt表项多为Default_IRQHandler;运行时 用g_irqvector[]做第二级分发,便于统一统计和 GPIO 扩展 IRQ(MAX_IRQ_ENTRY可大于MAX_IRQn)。 - 异常与中断分离 :
mtvec→Default_Handler;中断在trap里根据mcause最高位再并入Default_IRQHandler。 - NMI 单独快速路径 :异常码 24,不经过
trap_c,直接g_nmivector+mret。 - 栈与扩展 :独立中断栈 + T-Head
ipush/ipop;FPU/回溯为可编译选项。 - 未使用宏 :
MSTATUS_PRV1 0x3880在本文件中未引用,可能为历史遗留或其它文件使用。
9. 调试时可关注的点
- 外设中断不进驱动:查
drv_irq_enable、g_irqvector[irq]是否仍为Default_Handler。 - 进中断就 "CPU Exception":可能同步异常或 NMI/未注册 IRQ 落到
trap/Default_Handler。 mcause与aic_soc.h的IRQn应对齐低 10 位;CORET_IRQn=7对应 SysTick。- 异常后挂死:
trap末尾j .与trap_c里while(1)属预期(需修根因后复位)。