SAM9G45死机问题

た 入场券 2022-03-27 03:56 248阅读 0赞

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

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

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1aGVueW91eXV5b3V5dQ_size_16_color_FFFFFF_t_70

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

  1. Vectors
  2. LDR pc,=resetHandler
  3. undefVector
  4. b undefVector ; Undefined instruction
  5. swiVector
  6. b swiVector ; Software interrupt
  7. prefetchAbortVector
  8. b prefetchAbortVector ; Prefetch abort
  9. dataAbortVector
  10. b dataAbortVector ; Data abort
  11. reservedVector
  12. b reservedVector ; Reserved for future use
  13. irqVector
  14. b irqHandler ; Interrupt
  15. fiqVector
  16. ; Fast interrupt
  17. ;------------------------------------------------------------------------------
  18. ; Handles a fast interrupt request by branching to the address defined in the
  19. ; AIC.
  20. ;------------------------------------------------------------------------------
  21. fiqHandler
  22. b fiqHandler

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

  1. /* Initialize AIC
  2. ****************/
  3. AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF;
  4. AT91C_BASE_AIC->AIC_SVR[0] = (unsigned int) defaultFiqHandler;
  5. for (i = 1; i < 31; i++) {
  6. AT91C_BASE_AIC->AIC_SVR[i] = (unsigned int) defaultIrqHandler;
  7. }
  8. AT91C_BASE_AIC->AIC_SPU = (unsigned int) defaultSpuriousHandler;
  9. // Unstack nested interrupts
  10. for (i = 0; i < 8 ; i++) {
  11. AT91C_BASE_AIC->AIC_EOICR = 0;
  12. }

再看:

  1. void defaultSpuriousHandler( void )
  2. {
  3. while (1);
  4. }
  5. //------------------------------------------------------------------------------
  6. /// Default handler for fast interrupt requests. Infinite loop.
  7. //------------------------------------------------------------------------------
  8. void defaultFiqHandler( void )
  9. {
  10. while (1);
  11. }
  12. //------------------------------------------------------------------------------
  13. /// Default handler for standard interrupt requests. Infinite loop.
  14. //------------------------------------------------------------------------------
  15. void defaultIrqHandler( void )
  16. {
  17. while (1);
  18. }

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

  1. Vectors
  2. LDR pc,=resetHandler
  3. undefVector
  4. b undefHandler ; Undefined instruction
  5. swiVector
  6. b swiHandler ; Software interrupt
  7. prefetchAbortVector
  8. b prefetchAbortHandler ; Prefetch abort
  9. dataAbortVector
  10. b dataAbortHandler ; Data abort
  11. reservedVector
  12. b reservedVector ; Reserved for future use
  13. irqVector
  14. b irqHandler ; Interrupt
  15. fiqVector
  16. b fiqHandler ; Fast interrupt

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

  1. void defaultFiqHandler( void )
  2. {
  3. led_on();
  4. while (1){
  5. printf("defaultFiqHandler\r\n");
  6. delayms(20);
  7. led_on();
  8. delayms(20);
  9. led_off();
  10. }
  11. }

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

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

  1. MOV r0, sp
  2. MRS r1, SPSR
  3. MSR CPSR_c, #ARM_MODE_IRQ :OR: I_BIT :OR: F_BIT
  4. MOV r2, sp
  5. MOV r3, lr
  6. MSR CPSR_c, #ARM_MODE_SVC :OR: I_BIT :OR: F_BIT ;´ò¿ªIRQ,¹Ø±ÕFIQ
  7. BL defaultSwiHandler

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

  1. void defaultSwiHandler(uint32_t *pwPC,uint32_t hwSPSR,uint32_t *pwSPIRQ,uint32_t hwIRQLR)
  2. {
  3. //uint32_t* cur_sp = 0, *cur_lr = 0, *cur_pc = 0;
  4. uint16_t i=0;
  5. uint32_t *p=0;
  6. led_on();
  7. while (1){
  8. printf("START\r\n");
  9. extern unsigned char MainUsbOperFlag;
  10. printf("defaultSwiHandler SP=%X\r\n",(uint32_t)pwPC);
  11. printf("defaultSwiHandler SPSR=%X\r\n",(uint32_t)hwSPSR);
  12. printf("defaultSwiHandler IrqSP=%X\r\n",(uint32_t)pwSPIRQ);
  13. printf("defaultSwiHandler IrqLr=%X\r\n",(uint32_t)hwIRQLR);
  14. printf("defaultSwiHandler MainUsbOperFlag=%u\r\n",MainUsbOperFlag);
  15. p=(uint32_t*)0x00310000;
  16. for(i=0;i<2048;i++){
  17. printf("defaultSwiHandler =%X\r\n",*p);
  18. p--;
  19. }
  20. printf("END\r\n");
  21. //printf("RSTC_RSR =%u\r\n",AT91C_BASE_RSTC->RSTC_RSR);
  22. delayms(60);
  23. led_on();
  24. delayms(60);
  25. led_off();
  26. }
  27. }

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

20190113181809265.png

  1. (*((void(*)())(0xFFFFFFFF)))();

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

添加如下语句:

  1. {
  2. static uint16_t i=0;
  3. i++;
  4. if(i>100){
  5. __asm {
  6. SWI 0
  7. }
  8. }
  9. }

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

20190113182230686.png

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

  1. ; 给栈打印标签
  2. LDR R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit| ;ÔÚmapÎļþÀïÃ棬±íʾһ¸öµØÖ·
  3. MOV R1,#2048
  4. STATIC1
  5. SUB R0,#4
  6. MOV R2,#165
  7. STR R2,[R0]
  8. SUB R1,#1
  9. CMP R1,#0
  10. BNE STATIC1
  11. ; Enter the C code
  12. IMPORT __main
  13. LDR R0, =__main
  14. BX R0
  15. loop4
  16. B loop4
  17. END

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

  1. #define my_pi(_Name,__X) do{ \
  2. if((__X)>=0x30E000){ \
  3. while(1){ \
  4. delayms(1000); \
  5. printf("NAME=%d %X\n\r",_Name,(uint32_t)(__X)); \
  6. } \
  7. } \
  8. }while(0)

注:

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

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

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

  1. ; Setup Stack for each mode
  2. LDR R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
  3. ; Enter IRQ Mode and set its Stack Pointer
  4. MSR CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT
  5. MOV SP, R0
  6. SUB R4, SP, #IRQ_Stack_Size
  7. ; Supervisor mode (interrupts enabled)
  8. MSR CPSR_c, #ARM_MODE_SVC :OR: F_BIT
  9. MOV SP, R4
  10. ; Enter the C code
  11. IMPORT __main
  12. LDR R0, =__main
  13. BX R0
  14. loop4
  15. B loop4
  16. END

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

原因找到,如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1aGVueW91eXV5b3V5dQ_size_16_color_FFFFFF_t_70 1

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.

20190114142025376.png

  1. Load_region 0x300000 0x10000 {
  2. Fixed_region 0x300000 {
  3. *.o (VECTOR, +First)
  4. .ANY (+RO)
  5. }
  6. Relocate_region +0 {
  7. *(cstartup +First)
  8. .ANY (+RW +ZI)
  9. }
  10. ScatterAssert((ImageLength(Fixed_region) + ImageLength(Relocate_region)) < 0xE000)
  11. ; ARM_LIB_HEAP 0x30F000 EMPTY 0x800 {
  12. ; }
  13. ; ARM_LIB_STACK 0x310000 EMPTY -0x800 {
  14. ; }
  15. ARM_LIB_HEAP 0x30E000 EMPTY 0x800 {
  16. }
  17. ARM_LIB_STACK 0x310000 EMPTY -0x1800 {
  18. }
  19. }
  20. __main
  21. _main_stk
  22. 0x003000b4: e59fd00c .... LDR sp,__lit__00000000 ; [0x3000c8] = 0x310000

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

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

  1. ; Setup Stack for each mode
  2. LDR R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit| ;ÔÚmapÎļþÀïÃ棬±íʾһ¸öµØÖ·
  3. ;SUB R0,R0, #SVC_Stack_Size
  4. MSR CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  5. MOV SP, R0
  6. ; Enter ABT Mode and set its Stack Pointer
  7. MSR CPSR_c, #ARM_MODE_ABT:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  8. SUB R4, R0, #SVC_Stack_Size
  9. MOV SP, R4
  10. ; Enter UND Mode and set its Stack Pointer
  11. MSR CPSR_c, #ARM_MODE_UND:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  12. SUB R4, R4, #ABT_Stack_Size
  13. MOV SP, R4
  14. ; Enter FIQ Mode and set its Stack Pointer
  15. MSR CPSR_c, #ARM_MODE_FIQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  16. SUB R4, R4, #UND_Stack_Size
  17. MOV SP, R4
  18. ; Enter IRQ Mode and set its Stack Pointer
  19. MSR CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  20. SUB R4, R4, #FIQ_Stack_Size
  21. MOV SP, R4
  22. ; Supervisor mode (interrupts enabled)
  23. ;MSR CPSR_c, #ARM_MODE_SYS :OR:I_BIT:OR: F_BIT;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
  24. ;SUB R4, R4, #IRQ_Stack_Size
  25. ;MOV SP, R4
  26. MSR CPSR_c, #ARM_MODE_SVC :OR: F_BIT ;´ò¿ªIRQ,¹Ø±ÕFIQ

好了至此完毕。

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

  1. irqHandler
  2. ; Save interrupt context on the stack to allow nesting */
  3. SUB lr, lr, #4
  4. STMFD sp!, {lr}
  5. MRS lr, SPSR
  6. STMFD sp!, {r0,r1,lr}
  7. ; Write in the IVR to support Protect Mode */
  8. LDR lr, =AT91C_BASE_AIC
  9. LDR r0, [r14, #AIC_IVR]
  10. STR lr, [r14, #AIC_IVR]
  11. ; Branch to interrupt handler in Supervisor mode */
  12. MSR CPSR_c, #ARM_MODE_SVC
  13. STMFD sp!, {r1-r4, r12, lr}
  14. MOV lr, pc
  15. BX r0
  16. LDMIA sp!, {r1-r4, r12, lr}
  17. MSR CPSR_c, #ARM_MODE_IRQ :OR: I_BIT
  18. ; Acknowledge interrupt */
  19. LDR lr, =AT91C_BASE_AIC
  20. STR lr, [r14, #AIC_EOICR]
  21. ; Restore interrupt context and branch back to calling code
  22. LDMIA sp!, {r0,r1,lr}
  23. MSR SPSR_cxsf, lr
  24. LDMIA sp!, {pc}^

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

  1. i.main
  2. main
  3. 0x00306564: e92d400e .@-. PUSH {r1-r3,lr}
  4. 0x00306568: e3a000c0 .... MOV r0,#0xc0

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

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

首先看栈设置:

  1. ; Setup Stack for each mode
  2. LDR R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
  3. ; Enter IRQ Mode and set its Stack Pointer
  4. MSR CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT
  5. MOV SP, R0
  6. SUB R4, SP, #IRQ_Stack_Size
  7. ; Supervisor mode (interrupts enabled)
  8. MSR CPSR_c, #ARM_MODE_SVC | F_BIT
  9. MOV SP, R4
  10. ; Enter the C code
  11. IMPORT __main
  12. LDR R0, =__main
  13. BX R0
  14. loop4
  15. B loop4
  16. END

首先设置的IRQ中断栈,在

  1. |Image$$ARM_LIB_STACK$$ZI$$Limit|

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

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

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

  1. MSR CPSR_c, #ARM_MODE_SVC

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

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

2019.01.14

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

  1. while(1){
  2. volatile static uint8_t *pi=(uint8_t *)&spid;
  3. volatile static uint32_t j=0;
  4. printf("pi= %X",(uint32_t)pi);
  5. j=*((uint32_t*)pi);
  6. printf("j= %X",(uint32_t)j);
  7. pi++;
  8. delayms(1000);
  9. }

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

发表评论

表情:
评论列表 (有 0 条评论,248人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SAM9G45之USB学习笔记

    最近一直在学习SAM9G45的USB功能,资料大部分都是从网上找的,还有就是官方给的库,只是把自己调试过程中遇到的问题记录下来,因为没有调试完,所以会持续更新。 问题一(其实

    相关 SAM9G45问题

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