最近有个项目,用的SAM9G45平台,遇到一个问题,就是运行一段时间‘’死机‘’问题,现象就是下发协议没有反应。这个问题解决耗费了好长时间,现记录如下,希望能够帮助需要的人吧。

首先分析是程序真的死掉了,还是逻辑进入了死循环跳不出来。在心跳PIT中断里面加入灯闪烁,测试发现灯不闪了,说明是程序死掉了。接着分析看看程序死到哪里了?首先看ARM9手册,查看异常,如下:

我猜想,是否进入某个异常了,没有退出,导致PIT中断不能进入。看下启动文件:

Vectors         LDR     pc,=resetHandler
undefVector  b      undefVector             ; Undefined instruction
swiVectorb       swiVector               ; Software interrupt
prefetchAbortVectorb       prefetchAbortVector     ; Prefetch abort
dataAbortVectorb       dataAbortVector         ; Data abort
reservedVectorb       reservedVector          ; Reserved for future use
irqVectorb       irqHandler              ; Interrupt
fiqVector; Fast interrupt;------------------------------------------------------------------------------
; Handles a fast interrupt request by branching to the address defined in the
; AIC.
;------------------------------------------------------------------------------
fiqHandlerb       fiqHandler

看到没有除了IRQ,其它都是死循环,再看下系统外设中断怎么设置的:

    /* Initialize AIC****************/AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF;AT91C_BASE_AIC->AIC_SVR[0] = (unsigned int) defaultFiqHandler;for (i = 1; i < 31; i++) {AT91C_BASE_AIC->AIC_SVR[i] = (unsigned int) defaultIrqHandler;}AT91C_BASE_AIC->AIC_SPU = (unsigned int) defaultSpuriousHandler;// Unstack nested interruptsfor (i = 0; i < 8 ; i++) {AT91C_BASE_AIC->AIC_EOICR = 0;}

再看:

void defaultSpuriousHandler( void )
{while (1);
}//------------------------------------------------------------------------------
/// Default handler for fast interrupt requests. Infinite loop.
//------------------------------------------------------------------------------
void defaultFiqHandler( void )
{while (1);
}//------------------------------------------------------------------------------
/// Default handler for standard interrupt requests. Infinite loop.
//------------------------------------------------------------------------------
void defaultIrqHandler( void )
{while (1);
}

看到没有,默认所有的中断和外设都是死循环,修改如下:

Vectors         LDR     pc,=resetHandler
undefVector  b      undefHandler            ; Undefined instruction
swiVectorb       swiHandler              ; Software interrupt
prefetchAbortVectorb       prefetchAbortHandler    ; Prefetch abort
dataAbortVectorb       dataAbortHandler        ; Data abort
reservedVectorb       reservedVector          ; Reserved for future use
irqVectorb       irqHandler              ; Interrupt
fiqVectorb       fiqHandler              ; Fast interrupt

处理函数添加打印标志,例如:

void defaultFiqHandler( void )
{led_on();while (1){printf("defaultFiqHandler\r\n");delayms(20);led_on();delayms(20);led_off();}
}

上电测试,发现进入了swiVector,查阅文档发现这个是软中断,通过SWI命令进入。难道代码里面有这部分操作,查看整个C代码,并没有发现有这个操作,然后通过反汇编查看asm文件,全部查找,也没有发现SWI指令,这可奇了怪了,为什么会进入这个中断呢?

另外一个同事通过不停的打LOG,最后定位到某个固定函数不能return,也就是说return没有执行,就触发SWI中断了。至此我们算是找到了死机的地方。可是为什么呢?我怀疑是栈溢出了,于是加大了栈,效果一样。于是我在SWI里面加了一点打印栈的东西,

        MOV      r0, spMRS     r1, SPSRMSR     CPSR_c, #ARM_MODE_IRQ :OR: I_BIT :OR: F_BITMOV        r2, spMOV     r3, lr        MSR     CPSR_c, #ARM_MODE_SVC :OR: I_BIT :OR: F_BIT        ;´ò¿ªIRQ,¹Ø±ÕFIQBL      defaultSwiHandler

汇编里面用R0~R3传递参数;

void defaultSwiHandler(uint32_t *pwPC,uint32_t  hwSPSR,uint32_t *pwSPIRQ,uint32_t  hwIRQLR)
{//uint32_t* cur_sp = 0, *cur_lr = 0, *cur_pc = 0;uint16_t     i=0;uint32_t    *p=0;led_on();while (1){printf("START\r\n");extern unsigned char   MainUsbOperFlag;printf("defaultSwiHandler SP=%X\r\n",(uint32_t)pwPC);printf("defaultSwiHandler SPSR=%X\r\n",(uint32_t)hwSPSR);printf("defaultSwiHandler IrqSP=%X\r\n",(uint32_t)pwSPIRQ);printf("defaultSwiHandler IrqLr=%X\r\n",(uint32_t)hwIRQLR);printf("defaultSwiHandler MainUsbOperFlag=%u\r\n",MainUsbOperFlag);p=(uint32_t*)0x00310000;for(i=0;i<2048;i++){printf("defaultSwiHandler =%X\r\n",*p);p--;}printf("END\r\n");//printf("RSTC_RSR =%u\r\n",AT91C_BASE_RSTC->RSTC_RSR);delayms(60);led_on();delayms(60);led_off();}
}

打印发现栈里面有一段全是0xFFFFFFFF,难道PC指针因为这个?试了如下语句:

(*((void(*)())(0xFFFFFFFF)))();

果然触发了SWI中断。这样还是不确定是不是这个问题,于是我把正常栈打印出来:

添加如下语句:

    {static uint16_t     i=0;i++;if(i>100){__asm {SWI 0}}}

发现打印出来的栈并没有一段0xFFFFFFFF。

首先还是考虑栈溢出,于是我给栈加了标签:

; 给栈打印标签LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit| ;ÔÚmapÎļþÀïÃ棬±íʾһ¸öµØÖ·MOV     R1,#2048
STATIC1SUB     R0,#4MOV     R2,#165STR     R2,[R0]SUB     R1,#1CMP     R1,#0BNE     STATIC1; Enter the C codeIMPORT  __mainLDR     R0, =__mainBX      R0
loop4B       loop4                END

发现栈并没有越界,那只能考虑是某个指针飞了。于是添加下面的宏:

#define my_pi(_Name,__X) do{                                                          \if((__X)>=0x30E000){                                      \while(1){                                             \delayms(1000);                                    \printf("NAME=%d  %X\n\r",_Name,(uint32_t)(__X));  \}                                                     \}                                                         \}while(0)

注:

1、地址需要根据栈里面的0xFFFFFFFF处地址调整。

然后在不能反回的函数里面把用到的指针地址全部打出来,果然发现有问题。函数嵌套调用,上层函数把局部变量的地址传给子函数,子函数获得的地址有时候是错的。在局部变量前加static,在测试,问题解决。至此我想吐血。。。。。

再说下官方给的启动文件一个bug,就是原来的栈设置是这样:

; Setup Stack for each modeLDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|;  Enter IRQ Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BITMOV     SP, R0SUB     R4, SP, #IRQ_Stack_Size; Supervisor mode (interrupts enabled) MSR     CPSR_c, #ARM_MODE_SVC :OR: F_BITMOV     SP, R4         ; Enter the C codeIMPORT  __mainLDR     R0, =__mainBX      R0
loop4B       loop4                END

但是,我追了下,发现ARM_MODE_SVC栈在经过__main,进入main时候,被修改了,在SRAM最高处,也就是说ARM_MODE_SVC只能设置在最高地址处,不明白为什么这样。

原因找到,如下:

If you use a scatter file to tailor stack and heap placement, the linker includes a version of the library
heap and stack setup code using the linker defined symbols, ARM_LIB_*, for these region names.
Alternatively you can create your own implementation.

Load_region 0x300000 0x10000 {    Fixed_region 0x300000 {*.o (VECTOR, +First).ANY (+RO)}Relocate_region +0  {*(cstartup +First).ANY (+RW +ZI)}ScatterAssert((ImageLength(Fixed_region) + ImageLength(Relocate_region)) <  0xE000);    ARM_LIB_HEAP 0x30F000 EMPTY 0x800 {
;    };    ARM_LIB_STACK 0x310000 EMPTY -0x800 {
;    }ARM_LIB_HEAP 0x30E000 EMPTY 0x800 {}ARM_LIB_STACK 0x310000 EMPTY -0x1800 {}
}
    __main_main_stk0x003000b4:    e59fd00c    ....    LDR      sp,__lit__00000000 ; [0x3000c8] = 0x310000

(这个是汇编文件,__main首先就是修改SP操作,不知道__lit__00000000可不可以修改)

于是我修改了栈设置,把ARM_MODE_SVC设置在最高处:

; Setup Stack for each modeLDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit| ;ÔÚmapÎļþÀïÃ棬±íʾһ¸öµØÖ·;SUB     R0,R0,  #SVC_Stack_SizeMSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°ËλMOV     SP, R0;  Enter ABT Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_ABT:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°ËλSUB     R4, R0, #SVC_Stack_SizeMOV     SP, R4;  Enter UND Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_UND:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°ËλSUB     R4, R4, #ABT_Stack_SizeMOV     SP, R4;  Enter FIQ Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_FIQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°ËλSUB     R4, R4, #UND_Stack_SizeMOV     SP, R4;  Enter IRQ Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°ËλSUB     R4, R4, #FIQ_Stack_SizeMOV     SP, R4; Supervisor mode (interrupts enabled) ;MSR     CPSR_c, #ARM_MODE_SYS :OR:I_BIT:OR: F_BIT;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ;SUB     R4, R4, #IRQ_Stack_Size;MOV     SP, R4          MSR     CPSR_c, #ARM_MODE_SVC :OR: F_BIT        ;´ò¿ªIRQ,¹Ø±ÕFIQ

好了至此完毕。

说句题外话,为什么官网启动代码错误的,也能跑呢,首先看下iqr汇编:

irqHandler;  Save interrupt context on the stack to allow nesting */SUB     lr, lr, #4STMFD   sp!, {lr}MRS     lr, SPSRSTMFD   sp!, {r0,r1,lr}; Write in the IVR to support Protect Mode */LDR     lr, =AT91C_BASE_AICLDR     r0, [r14, #AIC_IVR]STR     lr, [r14, #AIC_IVR]; Branch to interrupt handler in Supervisor mode */MSR     CPSR_c, #ARM_MODE_SVCSTMFD   sp!, {r1-r4, r12, lr}MOV     lr, pcBX      r0LDMIA   sp!, {r1-r4, r12, lr}MSR     CPSR_c, #ARM_MODE_IRQ :OR: I_BIT; Acknowledge interrupt */LDR     lr, =AT91C_BASE_AICSTR     lr, [r14, #AIC_EOICR]; Restore interrupt context and branch back to calling codeLDMIA   sp!, {r0,r1,lr}MSR     SPSR_cxsf, lrLDMIA   sp!, {pc}^

在IRQ模式下只用了4个字,我追了下栈,发现main里面,在进如while(1)时候,有入栈操作,但是不会反回,因此占用前四个字,没有影响,我入栈15个寄存器,直接跑飞。

    i.mainmain0x00306564:    e92d400e    .@-.    PUSH     {r1-r3,lr}0x00306568:    e3a000c0    ....    MOV      r0,#0xc0

(查看这段汇编,main刚进入,就有刚好4个字节的入栈操作,哎,就是这个恰好隐藏了一个很深的Bug)

再补充一个调试过程当中遇到的奇怪问题,就是刚开始用官方的启动代码调试,中断优先级只能设置为一样,如果不一样,就会死机。原因如下:

首先看栈设置:

; Setup Stack for each modeLDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|;  Enter IRQ Mode and set its Stack PointerMSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BITMOV     SP, R0SUB     R4, SP, #IRQ_Stack_Size; Supervisor mode (interrupts enabled) MSR     CPSR_c, #ARM_MODE_SVC | F_BITMOV     SP, R4         ; Enter the C codeIMPORT  __mainLDR     R0, =__mainBX      R0
loop4B       loop4                END

首先设置的IRQ中断栈,在

|Image$$ARM_LIB_STACK$$ZI$$Limit|

处,查看MAP文件,在SRAM最高地址处。然后是一个

IRQ_Stack_Size的偏移,设置SVC栈。通过上面的分析可知,这里SVC栈被__main修改为SRAM最高地址处。也就是和

IRQ栈重合。那么再看IRQ中断怎么处理:

MSR     CPSR_c, #ARM_MODE_SVC

看到没有在进入具体中断处理函数之前,把模式改为SVC,同时打开了IRQ和FIQ,也就是说在中断处理函数里面是可以进行中断嵌套的。那么这个时候,如果有中断就会有问题了,前面讲的很清楚了。

哎,至此完毕,睡觉,心好累。。。。。。

2019.01.14

看了很多资料,说ARM9不支持非对齐访问,于是我写了如下程序:

    while(1){volatile static uint8_t *pi=(uint8_t *)&spid;volatile static uint32_t  j=0;printf("pi= %X",(uint32_t)pi);j=*((uint32_t*)pi);printf("j= %X",(uint32_t)j);pi++;delayms(1000);}

发现程序能够正常打印,并没有进入异常,因此说明ARM9支持地址非对齐访问。

SAM9G45死机问题相关推荐

  1. Linux拷贝数据死机了,Linux系统“死机”时怎么办?

    如果问题能够再现,那么问题已经解决 80% 了.对于操作系统核心而言,如果有问题的再现方法,那么可以说是已经解决 99% 了.经常遇到的问题是系统可以正常运行一段时间,然后死机.如果不好再现问题,那么 ...

  2. android上传图片崩溃,导致安卓手机死机的照片拍摄者表示这张照片是无意之举...

    原标题:导致安卓手机死机的照片拍摄者表示这张照片是无意之举 上周,我们报道了在一些安卓手机上将某张图片设置为壁纸会导致手机崩溃,并卡在一个开启和关闭显示屏的循环中,让用户无法进行锁屏.它影响了大多数A ...

  3. svg图片怎么存手机上_一张普通的图片,是怎么让安卓手机死机的?

    关于我们日常使用的智能手机,大家可能都在网络上看过,一条简单的微信或者一个简单的表情包等就可以让我们的智能手机死机,情节较轻的重启手机解决,较为严重的直接就是数据丢失.而这背后的根本原因,很多都是相关 ...

  4. 插入u盘计算机未响应,win7系统插入u盘死机怎么办|win7插入u盘无响应的解决方法...

    ‍‍ 最近有些win7旗舰版用户遇到u盘插入计算机后会出现未响应导致系统死机,虽然等一会儿就能恢复正常,但是每次插入u盘都要卡一下决对是有问题的.遇到在win7系统插入u盘死机怎么办呢?会出现插入u盘 ...

  5. thinkphp 个别字段无法更新_香港华为手机大面积死机?只是个别手机更新出问题...

    点击箭头处"蓝色字",关注我们哦!! 导读 近日,有个别香港媒体报道称,大批香港华为手机用户在自动更新时出现手机无法关机,甚至死机的情况:出现问题的华为手机用户纷纷前往位于旺角的华 ...

  6. 升级BIOS解决DELL R730XD虚拟机死机问题

    1台新配置的DELL R730XD(配置有2个Intel E5-2640 V4的CPU.128GB内存.H730的RAID卡.12块4TB的SATA硬盘.2个495W电源),在安装VMware ESX ...

  7. 金山电子表格金山电子表格为何会死机?

    在写这篇文章之前,xxx已经写过了几篇关于改金山电子表格主题的文章,想要了解的朋友可以去翻一下之前的文章 金山电子表格为何会死机? 有的读者在留言中说,金山电子表格在大批盘算时会涌现死机现象.这是什么 ...

  8. 死机一个月后,31岁的哈勃望远镜又复活了

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来自:机器之心 这或许是人类创造的最伟大的太空设备,它即将在自己生命的第四个十年继续工 ...

  9. 死机、蓝屏、系统运行过慢

    [系统环境:] windows xp +瑞星杀毒  [基本配置:]tcl 品牌机p4 1.7256内存;  [故障描述:] 打游戏时死机.  [解决方法:] 怀疑是系统用的比较久了!从装系统无效;打开 ...

最新文章

  1. [转载]Surging 分布式微服务框架使用入门
  2. 借一个同事的经历,谈一谈程序员的成长
  3. 做程序猿的老婆应该注意的一些事情
  4. nginx php-fpm display_errors,php5-fpm的display_errors不能使用nginx
  5. 从零开始学习PYTHON3讲义(一)认识Python
  6. xcode6不显示键盘------解决方案
  7. linux内核杂记(16)-系统调用(3)
  8. 反应堆Reactor
  9. 清华王兴再抛神论:为什么教育决定着中国餐饮业质量?
  10. 二刻拍案惊奇之——国人为什么那么轻视技术
  11. [建议]添加模板功能
  12. oracle rac redo log,RAC共享online redo log和archived log的官方说明
  13. Django--ORM基本操作
  14. 胎压监测 (15 分)
  15. 从EXCEL中读出数据到数据库中
  16. SpringBoot2 Spring Cloud consul 分布式配置中心使用教程
  17. WinCE6.0 BootloaderMain源码分析之DownloadImage
  18. iOS OC和Swift相互引用,测试在swift项目
  19. VMware解决黑屏
  20. U盘图标显示成文件夹图标

热门文章

  1. 印度尼西亚通过加密货币期货交易规则
  2. [小说]魔王冢(2)意乱情迷
  3. 三十二楼层选几层最好_一般买房买几楼比较好 1一32高楼层选最佳楼层
  4. 基于STM32F103C8T6(HAL库)的HC-SR501红外人体传感及HC-SR04超声波测距
  5. 45-Jenkins-Sidebar Link插件实现添加侧边栏
  6. 深度解读BN、LN、WN、CN
  7. m1卡读写c语言,Android NFC(一) M1卡读写
  8. 神经网络之过拟合与欠拟合
  9. 洛谷 P5266 【深基17.例6】学籍管理
  10. MVG(second)读书笔记-3D摄影几何和变换