alunz wrote on Tuesday, February 15, 2005:
Following is some code that is offered as a Proof of Concept of a ISR only stack. (The code includes a sample ISR which demonstrates how to use it.) The code is targeted for a MSP430F149 and is obviously not portable in the slightest; it is offered with the usual disclaimers (if it breaks your application/computer - you get to keep the pieces).
Having said that, it does seem to work, but has not been tested thoroughly. Specifically; whether it works with nested interrupts has yet to be tested.
The other limitations of this are:
- It is NOT suitable for wrapping the timer ISR for pre-emptive scheduling (the reason why is left as an exercise for the reader). Some different code will have to be produced to support that.
- Not sure if the approach is all that portable.
- The wrapper macro is does not qualify as elegant at all. There are probably smart ways to determine main’s top of stack, and the address range for allocated stack space. But have yet to figure out how to do that.
- The whole wrapper macro approach was adopted as there does not seem to be a generic way to determine a functions register / stack usage. This approach work, but does have the overhead of an extra function call (5 cycles).
Next up is to figure out a version which will support the timer tick for pre-emptive scheduling. Am open to any comments / suggestions?
aLUNZ
/*-----------------------------------------------------------*/
/*
* ISR stack shenanigan routines
*/
volatile unsigned portSHORT usIsrToYield = 0;
// The maximum value for SP when in normal application space:
// 0x09c0 == 2496
#define MAX_STACK_ADDR "2496"
// The top of the ISR stack:
// 0x0a00 == 2560
#define ISR_STACK_SAVED "2560"
#define INTERRUPT_FUNCTION(irq, fcn)
interrupt ( (irq) ) fcn ## _wrapper ( void ) __attribute__ ( ( naked ) );
interrupt ( (irq) ) fcn ## _wrapper ( void )
{
asm volatile (
"cmp #" MAX_STACK_ADDR ", r1 \n\t"
"jc $+10 \n\t"
"push r15 \n\t"
"mov.w r1, r15 \n\t"
"mov.w #" ISR_STACK_SAVED ", r1 \n\t"
"push r15 \n\t"
"push r14 \n\t"
"push r13 \n\t"
"push r12 \n\t"
"call #" #fcn " \n\t"
"pop r12 \n\t"
"pop r13 \n\t"
"pop r14 \n\t"
"pop r15 \n\t"
"cmp #" ISR_STACK_SAVED ", r1 \n\t"
"jnc $+16 \n\t"
"mov.w r15, r1 \n\t"
"pop r15 \n\t"
"cmp #0, &usIsrToYield \n\t"
"jz $+6 \n\t"
"call #vPortYield \n\t"
"reti \n\t"
);
}
#define INTERRUPT_TASK_YIELD ( usIsrToYield = 1 )
/*-----------------------------------------------------------*/
/* declare the routines that service the interrupt, wrapping them
* in the ISR stack handler.
*/
static void vRxIsr( void );
static void vRxIsr( void );
INTERRUPT_FUNCTION(UART0RX_VECTOR, vRxISR);
INTERRUPT_FUNCTION(UART0TX_VECTOR, vTxISR);
/*-----------------------------------------------------------*/
/*
* UART RX interrupt service routine.
*/
static void vRxISR( void )
{
signed portCHAR cChar;
/* Get the character from the UART and post it on the queue of Rxed
characters. */
cChar = U0RXBUF;
if( cQueueSendFromISR( xRxedChars, &cChar, pdFALSE ) )
{
/*If the post causes a task to wake force a context switch
as the woken task may have a higher priority than the task we have
interrupted. */
INTERRUPT_TASK_YIELD;
}
}