This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430FR5964: How does GCC structure call frames?

Part Number: MSP430FR5964

I'm trying to chase a problem which I think is caused by stack corruption within an ISR - the ISR never returns to non-ISR mode as expected.

To try and get to the bottom of this, I've been trying to understand the call frame generated on ISR entry, but I'm struggling. From what I can glean the call frame is composed of the PC and SR when the ISR is triggered, any ISR local variables the compiler decides to store on the stack, and the values of a set of registers that need to be recovered on RETI, but the actual extent is totally determined by what the compiler determines it needs. Is there any way other than the disassembled code to see the extent of the call frame, and exactly what will be pushed and popped? I have found slaa140 which gives a somewhat conceptual overview of how call frames are structured - is there anything more available anywhere? 

Code Composer is able to generate a call hierarchy without (presumably) access to the underlying stack operations. This makes me think there is some metadata buried in the call frames that I am missing (maybe some sort of frame pointer?). Just how does CCS walk the stack to produce the call hierarchy?

The code is all generated by GCC. Thanks for any insights or pointers.

Andrew

  • The compiler does put some comments into the generated code, which you can see if you tell it to stop after producing the assembly source. But you have it right. The hardware pushes the PC and SR in response to the interrupt. The compiler then does what it needs to do to preserve state before executing code. A simple example:

           .section        __interrupt_vector_45,"ax",@progbits
            .word   Timer_A
                    .section        .lowtext
            .type   Timer_A, @function
    Timer_A:
    .LFB2:
            .loc 1 73 2
    ; start of function
    ; attributes: interrupt wakeup 
    ; framesize_regs:     0
    ; framesize_locals:   0
    ; framesize_outgoing: 0
    ; framesize:          0
    ; elim ap -> fp       4
    ; elim fp -> sp       0
    ; saved regs:(none)
            ; start of prologue
            ; end of prologue
            .loc 1 74 4
            .loc 1 74 8 is_stmt 0
            ADD.W   #1, &tick
            .loc 1 75 2
            ; start of epilogue
            BIC.W   #240, 0(SP)
            RETI
    .LFE2:
            .size   Timer_A, .-Timer_A
    

    ISRs are usually simple enough so that large stack frames aren't needed.

    If your ISR is corrupting the stack, then the most likely cause is a pointer with a bad address. An off by one error indexing a string is my usual mistake.

  • Thanks for the hint of trapping the generated assembly - hadn't thought of that, and hopefully gives a bit better idea of the compiler intent than the raw disassembly. I'll have a play and let you know.

    Any idea of how CCS backtracks through the stack? There doesn't seem enough information there to do this, which makes me think I have to be missing something!

    Andrew

  • When debugging you want to pass the "-g" option to gcc so that it will include debugging information for gdb.

  • Hi David,

    Thanks for the hint on capturing the assembly listing. The annotations in there tell me exactly what I want and need to know about the frame extent. I'm just in the throes of retaining these as an artifact of the builds, as its pretty hard to decode an interrupt frame without them.

    I ended up falling over the immediate problem, so that is resolved. The reported frame size also gave me a pretty unequivocal nudge that this particular isr is doing way too much at interrupt level, so that's next on my to do.

    Thanks again - Andrew

**Attention** This is a public forum