SAM9G45死机问题

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

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

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

Vectors         
				LDR     pc,=resetHandler 
undefVector  
			    b   	undefVector             ; Undefined instruction
swiVector
        		b       swiVector               ; Software interrupt
prefetchAbortVector
        		b       prefetchAbortVector     ; Prefetch abort
dataAbortVector
        		b       dataAbortVector         ; Data abort
reservedVector
        		b       reservedVector          ; Reserved for future use
irqVector
        b       irqHandler              ; Interrupt
fiqVector
                                        ; Fast interrupt
	
;------------------------------------------------------------------------------
; Handles a fast interrupt request by branching to the address defined in the
; AIC.
;------------------------------------------------------------------------------
fiqHandler
        b       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 interrupts
    for (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
swiVector
        		b       swiHandler              ; Software interrupt
prefetchAbortVector
        		b       prefetchAbortHandler    ; Prefetch abort
dataAbortVector
        		b       dataAbortHandler        ; Data abort
reservedVector
        		b       reservedVector          ; Reserved for future use
irqVector
                b       irqHandler              ; Interrupt
fiqVector
                b       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, sp
        MRS     r1, SPSR
        MSR     CPSR_c, #ARM_MODE_IRQ :OR: I_BIT :OR: F_BIT
        MOV		r2, sp
        MOV     r3, lr        
        MSR     CPSR_c, #ARM_MODE_SVC :OR: I_BIT :OR: F_BIT        ;´ò¿ªIRQ,¹Ø±ÕFIQ
        BL      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
STATIC1
        SUB     R0,#4
        MOV     R2,#165
        STR     R2,[R0]
        SUB     R1,#1
        CMP     R1,#0
        BNE     STATIC1

; Enter the C code

        IMPORT  __main
        LDR     R0, =__main
        BX      R0
loop4
        B       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 mode

        LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|

;  Enter IRQ Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT
        MOV     SP, R0
        SUB     R4, SP, #IRQ_Stack_Size

; Supervisor mode (interrupts enabled) 
        MSR     CPSR_c, #ARM_MODE_SVC :OR: F_BIT
        MOV     SP, R4         

; Enter the C code

        IMPORT  __main
        LDR     R0, =__main
        BX      R0
loop4
        B       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_stk
        0x003000b4:    e59fd00c    ....    LDR      sp,__lit__00000000 ; [0x3000c8] = 0x310000

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

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

; Setup Stack for each mode
        LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit| ;ÔÚmapÎļþÀïÃ棬±íʾһ¸öµØÖ·
        ;SUB     R0,R0,  #SVC_Stack_Size

        MSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
        MOV     SP, R0

;  Enter ABT Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_ABT:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
        SUB     R4, R0, #SVC_Stack_Size
        MOV     SP, R4

;  Enter UND Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_UND:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
        SUB     R4, R4, #ABT_Stack_Size
        MOV     SP, R4

;  Enter FIQ Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_FIQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
        SUB     R4, R4, #UND_Stack_Size
        MOV     SP, R4

;  Enter IRQ Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT ;±£´æµ½×´Ì¬¼Ä´æÆ÷,CPSR_c±íʾCPSRµÄµÍ°Ëλ
        SUB     R4, R4, #FIQ_Stack_Size
        MOV     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, #4
        STMFD   sp!, {lr}
        MRS     lr, SPSR
        STMFD   sp!, {r0,r1,lr}

        ; Write in the IVR to support Protect Mode */
        LDR     lr, =AT91C_BASE_AIC
        LDR     r0, [r14, #AIC_IVR]
        STR     lr, [r14, #AIC_IVR]

        ; Branch to interrupt handler in Supervisor mode */
        MSR     CPSR_c, #ARM_MODE_SVC
        STMFD   sp!, {r1-r4, r12, lr}
        MOV     lr, pc
        BX      r0
        LDMIA   sp!, {r1-r4, r12, lr}
        MSR     CPSR_c, #ARM_MODE_IRQ :OR: I_BIT

        ; Acknowledge interrupt */
        LDR     lr, =AT91C_BASE_AIC
        STR     lr, [r14, #AIC_EOICR]

        ; Restore interrupt context and branch back to calling code
        LDMIA   sp!, {r0,r1,lr}
        MSR     SPSR_cxsf, lr
        LDMIA   sp!, {pc}^

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

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

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

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

        首先看栈设置:

; Setup Stack for each mode

        LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|

;  Enter IRQ Mode and set its Stack Pointer
        MSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT
        MOV     SP, R0
        SUB     R4, SP, #IRQ_Stack_Size

; Supervisor mode (interrupts enabled) 
        MSR     CPSR_c, #ARM_MODE_SVC | F_BIT
        MOV     SP, R4         

; Enter the C code

        IMPORT  __main
        LDR     R0, =__main
        BX      R0
loop4
        B       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支持地址非对齐访问。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值