--- In lpc2000@yahoogroups.com, "elef_papa" <elef_papa@y...> wrote:
> Hi,
>
> After many hours trying to figure out why the cpu kept freezing, i
> fixed it by changing the following in my startup file:
>
> was: .equ IRQ_Stack_Size, 0x00000080
> changed to: .equ IRQ_Stack_Size, 0x00001000
>
> obviously the original value was not enough for the IRQ function and
> it was probably overwriting the stack from the USR mode (im
> guessing?!? correct me if im wrong)
>
> Anyways, im using winarm (GNU GCC 4.0.0) and want to know how can i
> tell how much ram a function/IRQ needs, so i can set the .equ
> IRQ_Stack_Size without having to guess?
>
> Thanks
> Eleftherios
Hello,
My approach to this is to use the user stack, in system mode.
This way, I only ever use 12 bytes of irq stack space.
Here is some gnu 'C' code to demonstrate.
/*
*/
#define ARM_MODE_IRQ 0x12
#define ARM_MODE_SYS 0x1F
#define I_BIT 0x80
// SWITCH_TO_SYSTEM() - An inline assembler function to
// switch stacks from the IRQ stack to the USER
stack.
// Thus the interrupt handler operates in SYSTEM
mode.
//
extern inline void SWITCH_TO_SYSTEM(void)
{
// Adjust and save LR_irq in IRQ stack
__asm volatile (" sub lr, lr, #4" : : : "lr");
__asm volatile (" stmfd sp!, {lr}" : : : "sp");
// Save SPSR and r0 in IRQ stack
__asm volatile (" mrs lr, SPSR" : : : "lr");
__asm volatile (" stmfd sp!, {r0, lr}" : : : "sp");
// Enable Interrupt and Switch in SYS Mode
__asm volatile (" mrs r0, CPSR" : : : "r0");
__asm volatile (" bic r0, r0, %0" : : "i" (I_BIT) : "r0");
__asm volatile (" orr r0, r0, %0" : : "i" (ARM_MODE_SYS) : "r0");
__asm volatile (" msr CPSR_c, r0");
// Save scratch/used registers and LR in User Stack
__asm volatile (" stmfd sp!, {r1-r3, r12, lr}" : : : "sp");
}
// RETURN_FROM_SYSTEM() - Returns a SYSTEM mode handler to the IRQ
stack, and
// then to the interrupted code. This is done
by an assembly
// language inline function that switches the
stack to the IRQ
// stack, and then retruns from the interrupt
after acknowledging
// it to the interrupt controller.
extern inline void RETURN_FROM_SYSTEM(void)
{
// Restore scratch/used registers and LR from User Stack
__asm volatile (" ldmia sp!, {r1-r3, r12, lr}" : : : "sp",
"r1", "r2", "r3", "r12", "lr");
// Disable Interrupt and switch back in IRQ mode
__asm volatile (" mrs r0, CPSR" : : : "r0");
__asm volatile (" bic r0, r0, %0" : : "i" (ARM_MODE_SYS) : "r0");
__asm volatile (" orr r0, r0, %0" : : "i" (I_BIT |
ARM_MODE_IRQ) : "r0");
__asm volatile (" msr CPSR_c, r0");
// Restore SPSR_irq and r0 from IRQ stack
__asm volatile (" ldmia sp!, {r0, lr}" : : : "sp", "r0", "lr");
__asm volatile (" msr SPSR, lr");
// Restore adjusted LR_irq from IRQ stack directly in the PC
__asm volatile (" ldmia sp!, {pc}^" : : : "sp", "pc");
}
and an example of usage:
void timer0_irq_handler(void) __attribute__((naked))
{
led_on(LED_TIMER_0);
++global_timer;
// scan the inputs
scanInputs();
// Clear interrupt flag
T0IR = TIMER_IR_MR0;
led_off(LED_TIMER_0);
}
// setup timer 0 to generate 1mS interrupts
int timer0Init(void)
{
// power up the timer
PCONP |= PCONP_PCTIM0;
// initialise global millisecond counter
global_timer = 0;
// set count divisor
T0MR0 = ONE_MS;
// Interrupt and Reset on MR0
T0MCR = TIMER_MCR_RESET_MR0 | TIMER_MCR_IRQ_MR0;
// Timer1 Enable
T0TCR = TIMER_CR_ENABLE;
return setVector(TIMER0_PRIORITY, VECTOR_TIMER0, (unsigned long)
timer0_asm_irq_handler);
}
//
// Vector handling
//
int setVector(int priority, int vectorNum, unsigned long handler)
{
// make sure priority and vectorNum are in range
if (priority < 0 || priority > 15)
return 0;
if (vectorNum < 0 || vectorNum > VECTOR_MAX)
return 0;
// set handler
VICVectAddrBase[priority] = handler;
// enable it
VICVectCntlBase[priority] = VICVECCTRL_ENABLE | vectorNum;
// make sure that the IRQ is used, not the FIQ
//VICIntSelect &= ~(1 << vectorNum);
// and the interrupt
VICIntEnable = (1 << vectorNum);
return 1;
}