This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

undef handler

Hi all,

If the processor tries to go back from the test() function to the main() function, the program counter gets always the wrong address - so that an undef handler occurs.... (processor is searching for an programm code in the sdram...). The whole programm is stored in an external nor flash device (0x10000000).

void test(void)
{
   //do some stuff...
   return;
}

void main(void)
{
   test();
}


The user stack size is 0x800. That's a short part of the disassembly where the jump to the sdram occurs (the next step after 0x100012D4).

According to the map file, the function test() is using the memory space until 0x1000012D8 and the next function starts at 0x100001304. I'm not sure about the space within these two points? All these values are also stored in the nor flash device...


   522: return;
0x100012C8  E7980185  LDR       R0,[R8,R5,LSL #3]
0x100012CC  E3C00001  BIC       R0,R0,#0x00000001
0x100012D0  E7880185  STR       R0,[R8,R5,LSL #3]
   523: }
0x100012D4  E8BD8FF8  LDMIA     R13!,{R3-R11,PC}
0x100012D8  544F5250  DD        0x544F5250
0x100012DC  0050495F  DD        0x0050495F
0x100012E0  0A50490A  DD        0x0A50490A
0x100012E4  00000000  DD        0x00000000
0x100012E8  606E6F64  DD        0x606E6F64
0x100012EC  6E6B2074  DD        0x6E6B2074
0x100012F0  7420776F  DD        0x7420776F
0x100012F4  20736968  DD        0x20736968
0x100012F8  6B636170  DD        0x6B636170
0x100012FC  000A7465  DD        0x000A7465
0x10001300  68746520  DD        0x68746520
0x10001304  00000020  DD        0x00000020
    39:                 pbuf->hdr->flag = used;



best regards
Howard

Parents
  • I think there is something wrong that the processor doesn't add the new stack variables at the end of the actual stack location.

    It could be stack pointer corruption then. If at some point in the program the stack pointer register is not updated properly (or updated improperly) then all subsequent stack operations would reference wrong memory locations.
    I don't think it's possible to corrupt the stack pointer by means of the C language only. It would involve pieces written in assembly, or improper handling of CPU mode switching, improper handling of interrupts etc...

Reply
  • I think there is something wrong that the processor doesn't add the new stack variables at the end of the actual stack location.

    It could be stack pointer corruption then. If at some point in the program the stack pointer register is not updated properly (or updated improperly) then all subsequent stack operations would reference wrong memory locations.
    I don't think it's possible to corrupt the stack pointer by means of the C language only. It would involve pieces written in assembly, or improper handling of CPU mode switching, improper handling of interrupts etc...

Children
  • I'm using the startup file from Atmel instead of the one from Keil, because it was easier to add some instructions to boot from an external flash device.

    ;------------------------------------------------------------------------------
    ; After a reset, execution starts here, the mode is ARM, supervisor
    ; with interrupts disabled.
    ; Initializes the chip and branches to the main() function.
    ;------------------------------------------------------------------------------
    
                    AREA  cstartup, CODE
                    ENTRY        ; Entry point for the application
    
    
    ; Reset Handler
    
            EXPORT  resetHandler
            IMPORT  |Image$$Fixed_region$$Limit|
            IMPORT  |Image$$Relocate_region$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Limit|
            IMPORT  |Image$$ARM_LIB_STACK$$Base|
            IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
                    ; Perform low-level initialization of the chip using LowLevelInit()
                    IMPORT  LowLevelInit
    
    resetHandler
    
    
            ; Set pc to actual code location (i.e. not in remap zone)
                LDR     pc, =label
    label
    
    
                    ; Set up temporary stack (Top of the SRAM)
                    LDR     r0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
            MOV     sp, r0
                    ; Call Low level init
                LDR     r0, =LowLevelInit
            MOV     lr, pc
            BX      r0
    
    
    ;Initialize the Relocate_region segment
                    LDR     r0, = |Image$$Fixed_region$$Limit|
                    LDR     r1, = |Image$$Relocate_region$$Base|
                    LDR     r3, = |Image$$Relocate_region$$ZI$$Base|
    
                CMP     r0, r1
            BEQ     %1
    
    
            ; Copy init data
    0       CMP     r1, r3
            LDRCC   r2, [r0], #4
            STRCC   r2, [r1], #4
            BCC     %0
    
    1       LDR     r1, =|Image$$Relocate_region$$ZI$$Limit|
            MOV     r2, #0
    2       CMP     r3, r1
            STRCC   r2, [r3], #4
            BCC     %2
    
    
    ; Cache Setup ------------------------------------------------------------------
    
    ; Constants
    ICACHE_ENABLE   EQU     (1<<12)         ; Instruction Cache Enable Bit
    
                    MRC     p15, 0, R0, c1, c0, 0   ; Enable Instruction Cache
                    ORR     R0, R0, #ICACHE_ENABLE
                    MCR     p15, 0, R0, c1, c0, 0
    
    
    
    ; Setup Stack for each mode
    
            LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
    
    
    ;  Enter Undefined Instruction Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_UND:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #UND_Stack_Size
    
    ;  Enter Abort Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_ABT:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #ABT_Stack_Size
    
    ;  Enter FIQ Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_FIQ:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #FIQ_Stack_Size
    
    
    
    ;  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
            SUB     R0, SP, #IRQ_Stack_Size
    
    
    ; Supervisor mode (interrupts enabled)
            MSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT
    ;        MOV     SP, R4
                    MOV     SP, R0
    ;        SUB     R4, SP, #SVC_Stack_Size
            SUB     R0, SP, #SVC_Stack_Size
    
    
    ;  Enter User Mode and set its Stack Pointer
            MSR     CPSR_c, #ARM_MODE_USR
            MOV     SP, R0
            SUB     SL, SP, #USR_Stack_Size
    
    
    ; Enter the C code
    
            IMPORT  __main
            LDR     R0, =__main
            BX      R0
    
            END
    

    I've added four of the instruction modes (undefined, abort mode and FIQ mode as well as the user mode).

    Which parts of the startup file will affect the stack pointer?

    best regards
    Howard

  • here is the first part of the startup file:

    ; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
    
    ARM_MODE_USR        EQU     0x10
    ARM_MODE_FIQ        EQU     0x11
    ARM_MODE_IRQ        EQU     0x12
    ARM_MODE_SVC        EQU     0x13
    ARM_MODE_ABT        EQU     0x17
    ARM_MODE_UND        EQU     0x1B
    ARM_MODE_SYS        EQU     0x1F
    
    I_BIT               EQU     0x80            ; when I bit is set, IRQ is disabled
    F_BIT               EQU     0x40            ; when F bit is set, FIQ is disabled
    
    AT91C_BASE_AIC      EQU     0xFFFFF000
    AIC_IVR             EQU     0x100
    AIC_EOICR           EQU     0x130
    
    UND_Stack_Size      EQU     0x00000000
    SVC_Stack_Size      EQU     0x00000008
    ABT_Stack_Size      EQU     0x00000000
    FIQ_Stack_Size      EQU     0x00000000
    IRQ_Stack_Size      EQU     0x00000100
    USR_Stack_Size      EQU     0x00000800
    
            PRESERVE8
    
    ; Area Definition and Entry Point
    ; Startup Code must be linked first at Address at which it expects to run.
    
            AREA    VECTOR, CODE
            ARM
    
    ; Exception Vectors
    
    Vectors
                                    LDR     pc,=resetHandler
                                    LDR     PC,Undef_Addr
                    LDR     PC,SWI_Addr
                    LDR     PC,PAbt_Addr
                    LDR     PC,DAbt_Addr
                                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_IVR
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_FVR
    
    Reset_Addr      DCD     resetHandler
    Undef_Addr      DCD     undefVector
    SWI_Addr        DCD     swiVector
    PAbt_Addr       DCD     prefetchAbortVector
    DAbt_Addr       DCD     dataAbortVector
                    DCD     0               ; Reserved Address
    IRQ_Addr        DCD     irqVector
    FIQ_Addr        DCD     fiqVector
    
    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
    
    ;------------------------------------------------------------------------------
    ; Handles incoming interrupt requests by branching to the corresponding
    ; handler, as defined in the AIC. Supports interrupt nesting.
    ;------------------------------------------------------------------------------
    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 | 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}^
    
    ;------------------------------------------------------------------------------
    ; After a reset, execution starts here, the mode is ARM, supervisor
    ; with interrupts disabled.
    ; Initializes the chip and branches to the main() function.
    ;------------------------------------------------------------------------------
    
                    AREA  cstartup, CODE
                    ENTRY        ; Entry point for the application
    
    
    ; Reset Handler
    
            EXPORT  resetHandler
            IMPORT  |Image$$Fixed_region$$Limit|
            IMPORT  |Image$$Relocate_region$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Limit|
            IMPORT  |Image$$ARM_LIB_STACK$$Base|
            IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
                    ; Perform low-level initialization of the chip using LowLevelInit()
                    IMPORT  LowLevelInit
    
    resetHandler
    

    and that will be my scatter-loading file:

    Load_region 0x10000000 0x4000000 {
    
    
    Fixed_region 0x10000000 { *.o (VECTOR, +First) *(cstartup) .ANY (+RO) }
    ; Relocate_region +0 { Relocate_region 0x300000 0x10000 { ether.o (+RW +ZI) }
    SDRAM 0x20000000 0x1000000 { .ANY (+RW +ZI) }
    ARM_LIB_HEAP 0x312000 EMPTY 0x1000 { }
    ARM_LIB_STACK 0x314000 EMPTY -0x1000 { } }

    Howard

  • SVC_Stack_Size EQU 0x00000008
    ...
    ; Branch to interrupt handler in Supervisor mode */

    Apparently, interrupt handlers are run in Supervisor mode, and only 8 bytes are allocated for Supervisor mode stack. If you have any interrupt handlers at all, 8 bytes would not be enough.
    The straightforward solution is to increase Supervisor stack size. Another solution is to run the interrupt handlers in User or System mode (shares stack with main program, saves memory.)
    I could be missing something, of course...

  • thank you for the hint. I was always looking at the last definition in the startup file

    MSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT
    


    where the interrupts are disabled in the supervisor mode. I've one question about the supervisor mode: in which mode will be the lowlevelinit function called in the startup file?

    The straightforward solution is to increase Supervisor stack size. Another solution is to run the interrupt handlers in User or System mode (shares stack with main program, saves memory.)
    

    The definition of the user mode (at the end of the startup file)

    MSR     CPSR_c, #ARM_MODE_USR
    


    will allow all kind of interrupts (in the user mode). I'm starting the main function in the user mode, when a interrupt occurs the irqhandler in the startup file will be executed and will change the original settings of the supervisor mode (interrupts disabled)? And all the values of the interupt (+ jump back addr) will be stored in the SVC_Stack_Size?

    ; 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 | I_BIT
    


    Could you explain me the above part a little bit?

    So if I will replace the line above with

    ; Branch to interrupt handler in Supervisor mode */
    MSR     CPSR_c, #ARM_MODE_USR
    


    the interupts will be executed in the user mode?

    best regards
    Howard

  • I've changed the size of the supervisor stack to 0x100 and the same error occurs.

    The stack size, which the processor use, starts from 0x314000 and ends at 0x313E40.

    The interrupt method is very small - only checking the status registers (emac receive, transmit) and setting the value of a volatile global variable, which inidicates that a new ethernet frame received.

    best regards
    Howard

  • Too many questions, and all of them are answered in the ARM Architecture Reference Manual.
    I have no knowledge of the AIC, so I'm afraid my advice could be completely wrong. All I know is that with the startup file you presented, you'll definitely overflow the Supervisor mode stack. That could explain all your troubles.


  • All I know is that with the startup file you presented, you'll definitely overflow the Supervisor mode stack. That could explain all your troubles.

    Could you please tell me which configuration I have to made to avoid supervisor mode stack overflow?

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

    Howard

  • This is exactly what I would try first. Of course, the problem could be caused by something completely unrelated to this...

  • Hi Howard,

    ; Exception Vectors
    
    Vectors
                    LDR     pc,=resetHandler
                    LDR     PC,Undef_Addr
                    LDR     PC,SWI_Addr
                    LDR     PC,PAbt_Addr
                    LDR     PC,DAbt_Addr
                    Where is the Reserved Vector?
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_IVR
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_FVR
    
    Reset_Addr      DCD     resetHandler
    Undef_Addr      DCD     undefVector
    SWI_Addr        DCD     swiVector
    PAbt_Addr       DCD     prefetchAbortVector
    DAbt_Addr       DCD     dataAbortVector
                    DCD     0               ; Reserved Address
    IRQ_Addr        DCD     irqVector
    FIQ_Addr        DCD     fiqVector
    

    Did you delete anything here?

    You should have 8 exception vectors here, but I only see 7.

  • Hi Howard,

    Mmmmm........

    For me, I choose to use the startup file from KEIL, because I am using KEIL's toolchain. And, I prefer not to change anything in the startup file. It is danger for me.

  • You're definitely right, but the Keil startup file fails if you try to boot from an external flash device. It only works when the programm starts from the internal ram or the external sdram.

    The initialization of the processor is a little bit different depending on the source to boot.

    The reserve vector should be available in the startup file above. The only thing which is different - comparing the map file using Keil startup and atmel startup is that the dataAbortVector get 8byte instead of 4byte as all the other vectors.

    VECTOR                                   0x10000000   Section      168  copy of board_cstartup_keil.o(VECTOR)
        undefVector                              0x1000003c   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        swiVector                                0x10000040   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        prefetchAbortVector                      0x10000044   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        dataAbortVector                          0x10000048   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        irqVector                                0x10000050   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        fiqVector                                0x10000054   ARM Code       0  copy of board_cstartup_keil.o(VECTOR)
        !!!main                                  0x100000a8   Section        8  __main.o(!!!main)
    

    best regards
    Howard

  • Hi Howard,

    Are you using an AT91SAM9263, and booting from the NOR-Flash?

    Vectors
                    LDR     pc,=resetHandler
                    LDR     PC,Undef_Addr
                    LDR     PC,SWI_Addr
                    LDR     PC,PAbt_Addr
                    LDR     PC,DAbt_Addr
                    NOP
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_IVR
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_FVR
    

    Please add an "NOP" there, and give it a try.

    (It seems some solutions for AT91SAM9263 are made by GNU toolchain.)

  • Are you using an AT91SAM9263, and booting from the NOR-Flash?

    You are right - are you also using one of the atmel controllers?

    Please add an "NOP" there, and give it a try.

    The problem changed; now the processor is able to call the main function from the test2function() once; if I try the same code a second time, it fails.... the test2function is always called if there was an interrupt. So maybe there is something wrong with my interrupt handler in the startup file....

    (It seems some solutions for AT91SAM9263 are made by GNU toolchain.)

    Right, atmel use the GNU toolchain to compile their boards.

    best regards
    Howard

  • I'm not alone with that problem...

    I will try to use the keil startup file together with my lowlevelinit function. So that the only difference between the original keil file and my startup file will be shown below.

    ; Reset Handler
    
                    EXPORT  Reset_Handler
                   IMPORT  LowLevelInit
    Reset_Handler
    
    
                    ; Set pc to actual code location (i.e. not in remap zone)
                LDR     pc, =label
                    NOP
    label
    
    
                    ; Set up temporary stack (Top of the SRAM)
    
                    LDR     r0, = Stack_Top                         ; get top of the stack
            MOV     sp, r0                                          ; store top of the stack in register0
                    ; Call Low level init
                    BL      LowLevelInit
                    NOP
    
    
    
    ; Cache Setup ------------------------------------------------------------------
    
                    IF      ICACHE_SETUP != 0
                    MRC     p15, 0, R0, c1, c0, 0   ; Enable Instruction Cache
                    ORR     R0, R0, #ICACHE_ENABLE
                    MCR     p15, 0, R0, c1, c0, 0
                    ENDIF
    
    
    
    
    
    ; Setup Stack for each mode ----------------------------------------------------
    
                    LDR     R0, =Stack_Top
    

    best regards
    Howard