zynq的栈监控与Xil_XXXAbortHandler问题排查

参考

UG585Address Map.csdn

stm32的栈监控与HardFault_Handler问题排查.csdn

崩溃现象

程序崩溃后会跳转到

bash 复制代码
ps7_cortexa9_0\standalone_domain\bsp\ps7_cortexa9_0\libsrc\standalone_v7_3\src\xil_exception.c

异常总览表

异常类型 默认处理函数(Xilinx BSP) CPU进入时机 典型触发场景 向量表默认绑定 关键寄存器/信息 默认行为 常见根因(重点) 架构说明
Reset(复位) 启动汇编入口 上电 / 硬复位瞬间 上电、复位按键、看门狗复位 Boot ROM / Reset Vector - 从头启动系统 硬复位、看门狗复位 ARM32/64均有,最高优先级
Undefined Instruction Xil_UndefinedExceptionHandler() 指令译码阶段(ID) 执行非法机器码、跑飞执行数据区 默认绑定该函数 UndefinedExceptionAddr 打印地址 + 死循环 PC跑飞、Flash损坏、函数指针错误、代码被破坏 ARM32/64均支持(ARM64为同步异常)
SVC / SWI(系统调用) Xil_ExceptionNullHandler()(裸机) 执行 SVC 指令瞬间 OS系统调用、特权级切换 默认空处理 - 裸机死循环 / OS接管 正常系统调用 / 误执行SVC ARM32独立 / ARM64同步异常
Prefetch / Instruction Abort Xil_PrefetchAbortHandler() 取指阶段(IF) PC跳飞、执行非法代码地址 默认绑定该函数 PrefetchAbortAddr / IFSR 打印寄存器 + 死循环 栈溢出、函数指针错误、return地址破坏、跳转非法地址、MMU禁止执行 ARM32为Prefetch Abort / ARM64为Instruction Abort
Data Abort Xil_DataAbortHandler() 访存阶段(MEM) 读写非法内存或外设地址 默认绑定该函数 DataAbortAddr / DFSR 打印地址 + 死循环 野指针、堆损坏、栈溢出、free后使用、访问未映射外设 ARM32/64均存在(ARM64为同步异常)
IRQ(普通中断) 用户注册 handler(默认NullHandler) GIC中断分发后 UART、Timer、GPIO等外设中断 未注册则NullHandler GIC CPU Interface寄存器 未注册→死循环 中断未清、未注册handler ARM32/64均有
FIQ(快速中断) 用户注册 handler(默认NullHandler) GIC高优先级通道 高实时控制、电机控制 未注册则NullHandler GIC高优先级路径 未注册→死循环 高优先级实时任务 ARM32/64均有
SError(系统错误) Xil_SErrorAbortHandler()(AArch64) 异步触发(与指令无关) ECC错误、AXI总线错误、cache一致性错误 AArch64向量表绑定 系统错误状态寄存器 打印日志 + 死循环 DDR ECC损坏、AXI bus fault、硬件一致性错误 ARM64常见;ARM32通常归入Abort类

问题1

中断专用栈被破坏了

在中断里调用 AT_println("ke %d", code); 崩溃进入 Xil_DataAbortHandler

改成 AT_println("ke"); 正常

主循环 注释 BspGetMillis 又不崩

把BspGetMillis 代码拷贝到主循环也不崩

解决

修改 链接脚本 lscript.ldx

bash 复制代码
# 修改
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
# 为
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 4000;

lscript.ld

分组 功能 解释
栈/堆配置 _STACK_SIZE 0x2000 主栈大小 System/User 模式栈大小(8KB)
_HEAP_SIZE 0x2000 堆大小 malloc/new 动态内存
_ABORT_STACK_SIZE 1024 Abort栈 Data Abort / Prefetch Abort
_SUPERVISOR_STACK_SIZE 2048 SVC栈 Supervisor模式
_IRQ_STACK_SIZE 4000 IRQ栈 中断模式
_FIQ_STACK_SIZE 1024 FIQ栈 快速中断模式
_UNDEF_STACK_SIZE 1024 Undefined栈 未定义指令异常
MEMORY内存区域 ps7_ddr_0 0x00100000~0x3FFFFFFF DDR区域 主程序运行内存
ps7_qspi_linear_0 0xFC000000~0xFCFFFFFF QSPI线性映射 Flash线性访问
ps7_ram_0 0x00000000~0x0002FFFF OCM低地址 On-Chip Memory
ps7_ram_1 0xFFFF0000~0xFFFFFE00 OCM高地址 高地址映射OCM
程序入口 ENTRY(_vector_table) _vector_table 程序入口 CPU复位后跳转地址
代码段 .text > ps7_ddr_0 代码段 函数机器码
.vectors 异常向量 中断入口 ARM异常向量表
.boot Boot代码 启动代码 reset/startup
.init 初始化代码 CRT初始化 main前执行
.fini 结束代码 程序退出清理 main后执行
只读数据 .rodata const数据 只读常量 字符串/查表
.rodata1 扩展rodata 编译器扩展 次级只读区
.sdata2 小只读数据 GP优化 小常量快速访问
.sbss2 小只读BSS GP优化 小未初始化数据
数据段 .data 已初始化变量 全局静态变量 启动时复制
.data1 扩展data 编译器扩展 次级data
.sdata 小数据区 GP优化 小变量快速访问
.sbss 小BSS区 GP优化 小未初始化变量
.bss 未初始化变量 零初始化区 启动时清零
COMMON 公共变量 旧式全局变量 GCC兼容
TLS线程局部存储 .tdata TLS初始化数据 线程局部变量 Thread Local
.tbss TLS未初始化数据 TLS BSS Thread Local
C++初始化 .ctors 构造函数表 全局对象构造 main前执行
.dtors 析构函数表 全局对象析构 main后执行
.preinit_array 预初始化数组 libc前初始化 极少使用
.init_array 初始化数组 GCC现代构造机制 替代ctors
.fini_array 析构数组 GCC现代析构机制 替代dtors
异常/调试 .eh_frame 异常展开表 C++异常 stack unwind
.eh_framehdr 异常头 unwind索引 backtrace辅助
.gcc_except_table GCC异常表 throw/catch C++异常
.ARM.exidx ARM异常索引 ARM unwind backtrace
.ARM.extab ARM unwind表 ARM异常处理 unwind数据
MMU相关 .mmu_tbl MMU页表 一级页表 Cortex-A9 MMU
ALIGN(16384) 16KB对齐 页表对齐 ARM硬件要求
地址符号 __rodata_start 地址符号 rodata开始 边界标记
__rodata_end 地址符号 rodata结束 边界标记
__data_start 地址符号 data开始 数据复制起点
__data_end 地址符号 data结束 数据复制终点
__bss_start 地址符号 bss开始 清零起点
__bss_end 地址符号 bss结束 清零终点
SDA小数据 _SDA_BASE_ sdata中点 SDA基址 GP小数据寻址
_SDA2_BASE_ sdata2中点 SDA2基址 小只读寻址
Heap区域 .heap heap段 动态内存 malloc/new
_heap_start heap起点 heap开始 libc使用
_heap_end heap终点 heap结束 heap边界
HeapBase heap基址 heap起始 调试/库
HeapLimit heap结束 heap上限 heap边界
Stack区域 .stack stack段 CPU栈 所有模式栈
_stack_end 栈底 主栈开始 向上分配
_stack 主栈顶 SP初值 User/System
__stack 主栈符号 SP引用 BSP使用
__irq_stack IRQ栈顶 IRQ SP IRQ模式
__supervisor_stack SVC栈顶 SVC SP Supervisor模式
__abort_stack Abort栈顶 Abort SP Data Abort
__fiq_stack FIQ栈顶 FIQ SP Fast IRQ
__undef_stack Undefined栈顶 Undefined SP Undefined模式
程序结束 _end 最终地址 程序结束 heap边界参考
GNU LD语法 KEEP() 强制保留 防止gc删除 向量表常用
ALIGN(x) 地址对齐 cache/MMU要求 硬件对齐
NOLOAD 不装载 不生成bin BSS/Stack
SORT() section排序 ctor顺序 GCC使用
EXCLUDE_FILE() 排除文件 避免重复 ctor/dtor
> section映射 指定MEMORY 放入内存区

脚本工具

bash 复制代码
# 设置环境变量
> $env:PATH = "D:\Xilinx\Vitis\2020.2\gnu\aarch32\nt\gcc-arm-none-eabi\bin;" + $env:PATH
# 查源码位置
> arm-none-eabi-addr2line -e zynq_wave_app.elf 0x00109264
axi_test/src/main.cpp:44
# 查符号表
arm-none-eabi-nm zynq_wave_app.elf
bash 复制代码
# wsl有各种工具更方便
> wsl
$:arm-none-eabi-nm zynq_wave_app.elf | grep heap
0012d360 B _heap
0012f360 B _heap_end
0012d360 B _heap_start
0012d310 b heap.5721
# 查看IRQ栈大小
$: arm-none-eabi-nm zynq_wave_app.elf | grep _IRQ_STACK_SIZE
00000fa0 A _IRQ_STACK_SIZE
# IRQ 栈地址 0x00131360:0x00132300 向下增长
$: arm-none-eabi-nm zynq_wave_app.elf | grep irq
0012ccd8 b _ZL12s_pl_irq_cnt
00107448 t _ZL14pl_irq_handlerPv
00107a20 t _ZL16uart_irq_handlerPvmj
00132300 B __irq_stack
00131360 B _irq_stack_end

栈监控

这里以__irq_stack 栈为例

c 复制代码
#define IRQ_STACK_TOP   ((uint32_t *)0x00132300)
#define IRQ_STACK_END   ((uint32_t *)0x00131360)
#define WATERMARK 0xA5A5A5A5

volatile uint32_t g_irq_stack_max_usage = 0;
//irq 栈填充水印
void irq_stack_fill_watermark(void)
{
    uint32_t *p = IRQ_STACK_END;

    while (p < IRQ_STACK_TOP)
    {
        *p++ = WATERMARK;
    }
}
// irq 栈最大用量写入全局变量g_irq_stack_max_usage 
void irq_stack_update_usage(void)
{
    uint32_t *p = IRQ_STACK_END;

    while (p < IRQ_STACK_TOP)
    {
        if (*p != WATERMARK)
        {
            break;
        }
        p++;
    }

    g_irq_stack_max_usage = (uint32_t)((uint8_t*)IRQ_STACK_TOP - (uint8_t*)p);
}

测试

c 复制代码
int main(){
    static uint32_t s_ms_tick=0;
    static uint32_t s_pt_tick=0;
    irq_stack_fill_watermark();
    BspInit();
    at_init(Bsp_shell_write);
	  AT_printf("ke");
    at_app_init();
    at_show_version();
    //启动PT协程
     Protothread::AllStart();
    while (1) {
        uint32_t ms= BspGetTickMs();
        BspGetMillis();
        if(ms-s_ms_tick>=10){
            s_ms_tick=ms;
            int len= BspUartRead((uint8_t *)AT_m_buf, -1);
            at_import((uint8_t *)AT_m_buf, len, ms);
        }
        if(g_bt.key_flag){
	        	g_bt.key_flag=0;
	        	irq_stack_update_usage();
	        	AT_println("IRQ stack max usage = %lu bytes\n", g_irq_stack_max_usage);
        }
    }
    return 0;
}

打印

因为 _IRQ_STACK_SIZE 配置的只有1024 ,irq栈最大用量是1248

所以发生了各种奇怪现象

bash 复制代码
[18:52:51.763]收←◆ke 268435456
IRQ stack max usage = 1248 bytes
相关推荐
Understanding_movies1 小时前
【Agent学习】Day13
学习
峥嵘life1 小时前
2026 五一赣州两日游记录:宋城夜色入梦,七鲤古意寻踪
学习
~光~~1 小时前
【AI工具使用配置记录】claude本地安装和使用
学习
LuminousCPP1 小时前
C 语言动态内存管理全解析:从基础函数到柔性数组与内存分区
c语言·经验分享·笔记·学习·柔性数组
qq_571099352 小时前
学习周报四十四
学习
d111111111d2 小时前
MQTT+STM32+ESP8266网络程序分层+韦老师
笔记·stm32·单片机·嵌入式硬件·学习·php
小宋加油啊2 小时前
学习CBOR
学习
王钧石的技术博客2 小时前
Harness Engineering学习
人工智能·学习·agent
babe小鑫2 小时前
计算机专业学习数据分析的价值
学习·数据挖掘·数据分析