MSP430, gcc, CCE, and task yields in ISRs

plinder13 wrote on Tuesday, July 31, 2007:

I have a general question about the MSP430 GCC port.  The ISRs calls the taskYIELD() macro when there is a need for a context switch.  Eventually in the macro, portRESTORE_CONTEXT() is called which provides the neccessary assembly instructions to restore the context for the next task.  However, from what I can tell, it fails to clean up the stack as modified by the entrance into the ISR.  For the Tick ISR this seems to be acceptable because the ISR is given a “naked” atrribute, and hence, I assume the gcc compiler does not modify the stack upon entry to this function.  However, the serial ISRs are not declared as “naked”, so won’t this cause a memory leak (as the stack manipulation peformed by the compiler entering this ISR is not cleaned up)?

I am actually not using gcc but the CCE Pro version offered by TI so I can’t look at the assembly that gcc creates upon an non-naked ISR entry.  I believe I have everything ported except for this ISR issue.

For those who are interested, some simple things I have done to port gcc to CCE:
1.  Use the #pragma FUNC_NEVER_RETURNS() to make an ISR "naked"
2.  Put a space between the " and the first assembly mneumonic of each asm() instruction
3.  Use mov.w R1,0(R12) in place of mov.w R1,@R12
4.  Define inline as nothing "#define inline" (taken from Westmoreland port)
5.  removed "volatile" keyword from all asm() instructions
6.  Added "asm(" .global pxCurrentTCB");" underneath pxCurrentTCB declaration as the compiler was not adding this because there was not statement in C (other than some asm() statements) that used this variable.
7.  Added TIMERA0_ISR(prvTickISR); for Tick ISR
8.  Added an "#include "portmacro.h"" to portable.h - a better fix would be to create a symbol for the CCE build and place this include inside of an #ifdef
9.  I have not yet found that I needed any special compiler or linker settings - the default has worked thus far.

sotd wrote on Tuesday, July 31, 2007:

I would guess from other ports that entering the ISR pushes registers onto the stack of the task that was interrupted by the ISR.  When this task next runs, it will continue from the point at which the yield occurred, which means it will then run the code that pops said registers from its stack before returning to the task code itself.

jwestmoreland wrote on Tuesday, July 31, 2007:

This is the behavior of most modern compilers when dealing with ISR’s.  Some type of minimal context switch (speaking of RISC)
save/restore is done - with CISC-like machines something similar takes place.

My advice is to get things running with the default behavior of the tools first, then look at the assembler entry/exit code on
the ISR’s and do any assembly fix-up that may be appropriate.  Sounds like the CCE Pro tools won’t let you do this so that’s
a bit of a shame.  I don’t trust any toolset that doesn’t let me look at 100% of assembly in a debugger - no reason to
put up with that today.  Better to buy IAR or Keil or Rowley or another tool-set to send a message to the vendor.

If you’re having any type of stack problem - it’ll show up rather quickly in the MSP430 since you don’t have a very big area of
RAM.  Also check to make sure the port isn’t resetting; then happily starting again - making you think everything is OK.

Some debuggers - like IAR - have a logging feature you can turn on to log if it hits the reset vector; for instance - or start-
up code - whichever is easier to monitor.  A memory leak in the form of a stack bug usually rears its ugly head rather quickly
with the MSP430.  It’d be nice to have a stack probe for the MSP430 - IAR has one in their debugger - but it’s tied directly
to the tools - since FreeRTOS defines its own stack - the IAR stack probe always complains.  I think this can be fixed in the
debugger customizations - but all it does it monitor if the stack is in an allocated area.  You can set advanced breakpoints
on the MSP430 to look at when, for instance, the Stack Pointer (SP) exceeds the max RAM address - which is a rather easy one
to implement to check for a corrupt SP.  You can also set one for the min RAM address as well.  A corrupt SP is usually what
will occur (eventually) if there is a stack problem.  Since the MSP430 has such a limited area of RAM - it’s a lot easier to
find than looking for one on a Pentium P4 for instance with a 4GB SDRAM space.

John W.

plinder13 wrote on Tuesday, July 31, 2007:

Thank you for your replies, you are both right.

As far as using taskYIELD() in interupts, I have been able to verify that the stack eventually unwinds as SOTD said.

What had prompted my e-mail was my code ending up int "free space".  I looked further and found that I had defined a task function without giving it the "(void * pvParameters)", I had declared it "void taskfunction(void)".

The CCE tools do give full assembly, when I said I couldn’t look at the assembly I meant that I didn’t have a gcc system up and running so I couldn’t compile one of the gcc demo apps to look at the assembly produced by gcc.

I have been able to get my code mostly up and running.  I am able to suspend tasks and resume them inside of an ISR without any problem.

However, I now have a new problem.  The TIC ISR is causing a stack issue.  So far in my app, all of the created tasks are being triggered from other ISRs, ie, I can turn the TIC ISR off and my app works fine.  Upon the portRESTORECONTEXT(), the PC is getting filled with 0xCCCC - clearly a stack problem.  I am stepping through the portRESTORECONTEXT() in the TIC ISR.  I have minimum stack size at 48, my heap set at 1400 and the SP is 0x040A right before the "reti" instruction.  Surely someone has seen this before and can give me a quick answer?  In the mean time, I will be digging through trying to figure it out.

Thanks for your help.

jwestmoreland wrote on Tuesday, July 31, 2007:


Check in port.c which register(s) pass function arg information - note this is compiler dependent.

If memory serves me - I’ve seen this error occur due to improper function argument handling in port.c.  You have to point
pvParameters to the correct register in port.c - this is different for different toolsets.  Different compilers put it different
places for the MSP430 - GCC and Rowley use R15 - IAR uses R12 - also depends if its a structure pointer or long or double - but
the port settings handle this in FreeRTOS (I think, anyway…).

The 0xCCCC would make me think that something’s up with R12.  Sounds like maybe an IAR port taken over to CCE perhaps?

The SP will point to the SR info before a RETI is executed.  Immediately following this in the stack should be the correct
value for the PC - if it isn’t - stack is corrupt.  A SP issue is about the nastiest thing you can deal with in an RTOS (IMHO) -
so might as well get acquainted with what’s needed after RETI’s, RET’s, and RETA’s (if using 430X).  All of this is in the
appropriate datasheet for the MSP430.

John W.

plinder13 wrote on Wednesday, August 01, 2007:

I figured it out!

In CCE, the FUNC_NEVER_RETURNS() pragma makes a function "naked" unless it is specified with the "interrupt" keyword.  When the function is declared an "interrupt" function, the FUNC_NEVER_RETURNS is basically ignored.

The compiler was pushing R11-R15 onto the stack before calling portSAVE_CONTEXT().  Hence, when portRESTORE_CONTEXT() was being called, the "reti" instruction caused the R11 and R12 values that were pushed onto the stack upon entering the ISR to be pushed onto the SR and PC.

I have two solutions for this issue:
1.  Do not use the "interrupt" keyword on the Port Tic ISR, only use the "FUNC_NEVER_RETURNS()" pragma.  This will create a "naked" function which will return when the "reti" in portRESTORE_CONTEXT() is executed.

2.  Call portYIELD_TASK() instead of using the other macros

interrupt void prvTickISR( void )


plinder13 wrote on Wednesday, August 01, 2007:

2. Call portYIELD_TASK() instead of using the other macros:

interrupt void prvTickISR( void )

For my app, I am going with option 1 for the prvTickISR().  In all of my other ISRs, I do not make them naked as they force a context change rarely, and have events that cannot handle the extra overhead (eg, some Timer B events need to be very fast where others force a context switch), so I use portYIELD_TASK().

jwestmoreland wrote on Wednesday, August 01, 2007:


I’m glad you got this working - I have a question for you -

in your port - is portSAVE_CONTEXT() a function or a macro?

John W.

plinder13 wrote on Thursday, August 02, 2007:


I have portSAVE_CONTEXT() as a macro.  It is similar to the GCC version:

I would be happy to e-mail you my CCE port if it would be useful to you - especially since your website seems to be one of the harbors of MSP430 ports.


jwestmoreland wrote on Thursday, August 02, 2007:


Sure - why don’t you zip up your project and e-mail it to: - I have the free tools from TI - so I don’t know that I will
be able to rebuild it - but I can certainly post it and credit you (if that’s what you want) on my site.

John W.