D13xRISC-V中断与异常处理机制解析vectors.S

说明: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_Handlersp 设为 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_:在栈上建小帧,保存 s0mepc,便于 C 侧回溯。
  • ARCH_RISCV_FPU:保存/恢复 ft0--ft11fa0--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) */

逻辑:

  1. mcause[9:0] → IRQ 编号(与 aic_soc.hIRQn 一致,如 CORET_IRQn=7DMA_IRQn=32)。
  2. g_irqvector[irq_num] → 函数指针(aic_drv_irq.cdrv_irq_register 填充)。
  3. g_irqdata[irq_num] → 第二个参数。
  4. g_irqcnt[irq_num]++ → 统计中断次数(drv_irq_show 等可用)。

默认初始化(drv_irq_vectors_init)把 0...MAX_IRQn-1 设为 Default_HandlerCORET_IRQn 设为 SysTick_Handler;未注册的外设 IRQ 若仍进 Default_IRQHandler,会调到 Default_Handlertrap → 打印异常。

硬件向量表 的关系:表项地址固定为 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 表示同步异常。

  • 中断 :恢复 t0sp 后跳到 Default_IRQHandler(与向量表路径汇合)。
  • 同步异常 (非法指令、访存错误、ecall 等):
    • g_trap_sp - 132(或 AIC_BACKTRACE_DEBUG 时用当前 sp)保存 GPR 帧;
    • 故意不保存 x5t0),最后把临时保存的 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_IRQHandlermcause==7SysTick_Handler


8. 设计要点小结

  1. 两级模型 :硬件 mtvt 表项多为 Default_IRQHandler运行时g_irqvector[] 做第二级分发,便于统一统计和 GPIO 扩展 IRQ(MAX_IRQ_ENTRY 可大于 MAX_IRQn)。
  2. 异常与中断分离mtvecDefault_Handler;中断在 trap 里根据 mcause 最高位再并入 Default_IRQHandler
  3. NMI 单独快速路径 :异常码 24,不经过 trap_c,直接 g_nmivector + mret
  4. 栈与扩展 :独立中断栈 + T-Head ipush/ipop;FPU/回溯为可编译选项。
  5. 未使用宏MSTATUS_PRV1 0x3880 在本文件中未引用,可能为历史遗留或其它文件使用。

9. 调试时可关注的点

  • 外设中断不进驱动:查 drv_irq_enableg_irqvector[irq] 是否仍为 Default_Handler
  • 进中断就 "CPU Exception":可能同步异常或 NMI/未注册 IRQ 落到 trap/Default_Handler
  • mcauseaic_soc.hIRQn 应对齐低 10 位;CORET_IRQn=7 对应 SysTick。
  • 异常后挂死:trap 末尾 j .trap_cwhile(1) 属预期(需修根因后复位)。
相关推荐
xzl044 小时前
D13x:Reset_Handler到entry解析
artinchip
xzl042 天前
PBP 运行时:pbp_cfg.bin 解析全流程
artinchip
xzl044 天前
D13x开发实战:从QEMU到真机全流程
artinchip