From 9db18ddc06bfe8661b48f1213b092fcbd2c97401 Mon Sep 17 00:00:00 2001 From: Thomas Doerfler Date: Tue, 15 Dec 2009 15:14:12 +0000 Subject: add support for ARM11, reimplement nested interrupts --- cpukit/score/cpu/arm/ChangeLog | 8 + cpukit/score/cpu/arm/arm_exc_interrupt.S | 265 ++++++++++++++----------------- cpukit/score/cpu/arm/rtems/score/arm.h | 3 + 3 files changed, 127 insertions(+), 149 deletions(-) diff --git a/cpukit/score/cpu/arm/ChangeLog b/cpukit/score/cpu/arm/ChangeLog index d6fcc1980c..4c81e5cb1a 100644 --- a/cpukit/score/cpu/arm/ChangeLog +++ b/cpukit/score/cpu/arm/ChangeLog @@ -1,3 +1,11 @@ +2009-12-15 Sebastian Huber + + * rtems/score/arm.h: Recognize ARMv5TEJ. + * arm_exc_interrupt.S: The previous implementation was broken. In + case of a nested interrupt the link register of the INT mode was not + properly restored. This lead to a major rewrite. Interrupt + processing is now done in SVC mode. + 2009-10-01 Joel Sherrill * rtems/score/arm.h: Recognize ARMv7A. diff --git a/cpukit/score/cpu/arm/arm_exc_interrupt.S b/cpukit/score/cpu/arm/arm_exc_interrupt.S index 7cfd56ff00..738c248141 100644 --- a/cpukit/score/cpu/arm/arm_exc_interrupt.S +++ b/cpukit/score/cpu/arm/arm_exc_interrupt.S @@ -18,114 +18,98 @@ */ /* - * These two non-volatile registers contain the program status for INT and SVC - * mode. It is important that they are directly accessible in THUMB - * instruction mode. + * The upper EXCHANGE_SIZE bytes of the INT stack area are used for data + * exchange between INT and SVC mode. Below of this is the actual INT stack. + * The exchange area is only accessed if INT is disabled. */ -#define MODE_INT r4 -#define MODE_SVC r5 -/* - * These three non-volatile registers are used to exchange information between - * INT and SVC mode. - */ -#define SCRATCH_0 r6 -#define SCRATCH_1 r7 -#define SCRATCH_2 r8 +#define EXCHANGE_LR r4 +#define EXCHANGE_SPSR r5 +#define EXCHANGE_CPSR r6 +#define EXCHANGE_INT_SP r7 -/* List of scratch registers. They will be saved and restored in INT mode. */ -#define SCRATCH_LIST {SCRATCH_0, SCRATCH_1, SCRATCH_2} +#define EXCHANGE_LIST {EXCHANGE_LR, EXCHANGE_SPSR, EXCHANGE_CPSR, EXCHANGE_INT_SP} +#define EXCHANGE_SIZE 16 -/* - * List of all volatile registers (r0, r1, r2, r3, r12 and lr), registers - * containing the interrupt context (return address in SCRATCH_0 and saved - * program status in SCRATCH_1) and non-volatile registers used for modes - * (MODE_INT and MODE_SVC). They will be saved and restored in SVC mode. - */ -#define TASK_CONTEXT_LIST \ - {r0, r1, r2, r3, MODE_INT, MODE_SVC, SCRATCH_0, SCRATCH_1, r12, lr} +#define CONTEXT_LIST {r0, r1, r2, r3, EXCHANGE_LR, EXCHANGE_SPSR, r12} +#define CONTEXT_SIZE 28 -/* - * List of all volatile registers (r0, r1, r2, r3, r12 and lr) and the saved - * program status (SCRATCH_0 register). They will be saved and restored in INT - * mode. - */ -#define INTERRUPT_CONTEXT_LIST \ - {r0, r1, r2, r3, SCRATCH_0, r12, lr} - -.extern _ISR_Thread_dispatch .extern _ISR_Nest_level +.extern _ISR_Signals_to_thread_executing +.extern _ISR_Thread_dispatch .extern _Thread_Dispatch_disable_level + .extern bsp_interrupt_dispatch +.macro SWITCH_FROM_THUMB_TO_ARM +#ifdef __thumb__ +.align 2 + bx pc .arm -.globl arm_exc_interrupt -arm_exc_interrupt: +#endif /* __thumb__ */ +.endm - /* Save scratch registers on INT stack */ - stmdb sp!, SCRATCH_LIST +.macro SWITCH_FROM_ARM_TO_THUMB REG +#ifdef __thumb__ + add \REG, pc, #1 + bx \REG +.thumb +#endif /* __thumb__ */ +.endm - /* Increment interrupt nest level */ - ldr SCRATCH_0, =_ISR_Nest_level - ldr SCRATCH_1, [SCRATCH_0] - add SCRATCH_2, SCRATCH_1, #1 - str SCRATCH_2, [SCRATCH_0] +.arm +.globl arm_exc_interrupt +arm_exc_interrupt: - /* Branch for nested interrupts */ - cmp SCRATCH_1, #0 - bne nested_interrupt_context_save + /* Save exchange registers to exchange area */ + stmdb sp, EXCHANGE_LIST - /* Move interrupt context and CPSR to scratch registers */ - mov SCRATCH_0, lr - mrs SCRATCH_1, spsr - mrs SCRATCH_2, cpsr + /* Set exchange registers */ + mov EXCHANGE_LR, lr + mrs EXCHANGE_SPSR, spsr + mrs EXCHANGE_CPSR, cpsr + sub EXCHANGE_INT_SP, sp, #EXCHANGE_SIZE /* Switch to SVC mode */ - orr SCRATCH_2, SCRATCH_2, #0x1 - msr cpsr_c, SCRATCH_2 + orr EXCHANGE_CPSR, EXCHANGE_CPSR, #0x1 + msr cpsr, EXCHANGE_CPSR - /* Save context on SVC stack */ - stmdb sp!, TASK_CONTEXT_LIST - - /* Save SVC mode program status to non-volatile register */ - mov MODE_SVC, SCRATCH_2 + /* + * Save context. We save the LR separately because it has to be + * restored in SVC mode. The other registers can be restored in INT + * mode. + */ + stmdb sp!, CONTEXT_LIST + stmdb sp!, {lr} - /* Save INT mode program status to non-volatile register */ - bic MODE_INT, MODE_SVC, #0x1 - - /* Switch to INT mode */ - msr cpsr_c, MODE_INT + /* Remember INT stack pointer */ + mov r1, EXCHANGE_INT_SP - /* Restore scratch registers from INT stack */ - ldmia sp!, SCRATCH_LIST + /* Restore exchange registers from exchange area */ + ldmia r1, EXCHANGE_LIST - /* - * At this point the INT stack is in the exception entry state and - * contains no data for us. The context is saved on the SVC stack. We - * can easily switch modes via the registers MODE_INT and MODE_SVC - * which are valid through subroutine calls. We can use all volatile - * registers in both modes. Note that this comment describes the non - * nested interrupt entry. For a nested interrupt things are - * different, since we can save everything on the INT stack and there - * is no need to switch modes. - */ + /* Get interrupt nest level */ + ldr r0, =_ISR_Nest_level + ldr r2, [r0] -task_context_save_done: + /* Switch stack if necessary and save original stack pointer */ + mov r3, sp + cmp r2, #0 + moveq sp, r1 + stmdb sp!, {r3} /* Switch to THUMB instructions if necessary */ -#ifdef __thumb__ - add r0, pc, #1 - bx r0 -.thumb -#endif /* __thumb__ */ + SWITCH_FROM_ARM_TO_THUMB r1 - /* Increment thread dispatch disable level */ + /* Increment interrupt nest and thread dispatch disable level */ ldr r1, =_Thread_Dispatch_disable_level ldr r3, [r1] - add r3, r3, #1 + add r2, #1 + add r3, #1 + str r2, [r0] str r3, [r1] - /* Call BSP dependent interrrupt dispatcher */ + /* Call BSP dependent interrupt dispatcher */ bl bsp_interrupt_dispatch /* Decrement interrupt nest and thread dispatch disable level */ @@ -133,97 +117,80 @@ task_context_save_done: ldr r1, =_Thread_Dispatch_disable_level ldr r2, [r0] ldr r3, [r1] - sub r2, r2, #1 - sub r3, r3, #1 + sub r2, #1 + sub r3, #1 str r2, [r0] str r3, [r1] - /* Switch to ARM instructions if necessary */ -#ifdef __thumb__ -.align 2 - bx pc -.arm -#endif /* __thumb__ */ - - /* Branch if we have a nested interrupt */ - cmp r2, #0 - bne nested_interrupt_return + /* Restore stack pointer */ + SWITCH_FROM_THUMB_TO_ARM + ldr sp, [sp] + SWITCH_FROM_ARM_TO_THUMB r0 - /* Branch if thread dispatching is disabled */ + /* Check thread dispatch disable level */ cmp r3, #0 bne thread_dispatch_done - - /* - * Switch to SVC mode. It is important to call the thread dispatcher - * in SVC mode since overwise the INT stack may need to store an - * arbitrary number of contexts and it may lead to an invalid order of - * stack operations. - */ - msr cpsr_c, MODE_SVC - /* Call thread dispatcher */ -#ifdef __thumb__ - ldr r0, =_ISR_Thread_dispatch - mov lr, pc - bx r0 -.thumb - bx pc - nop -.arm -#else - bl _ISR_Thread_dispatch -#endif + /* Check context switch necessary */ + ldr r0, =_Context_Switch_necessary + ldrb r1, [r0] + ldr r0, =_ISR_Signals_to_thread_executing + cmp r1, #0 + bne do_thread_dispatch - /* Switch to INT mode */ - msr cpsr_c, MODE_INT + /* Check ISR signals to thread executing */ + ldrb r1, [r0] + cmp r1, #0 + beq thread_dispatch_done -thread_dispatch_done: - - /* Save scratch registers on INT stack */ - stmdb sp!, SCRATCH_LIST - - /* Switch to SVC mode */ - msr cpsr_c, MODE_SVC - - /* Move INT mode program status to scratch register */ - mov SCRATCH_2, MODE_INT + /* This aligns thread_dispatch_done on a 4 byte boundary */ +#ifdef __thumb__ + nop +#endif /* __thumb__ */ - /* Restore context from SVC stack */ - ldmia sp!, TASK_CONTEXT_LIST +do_thread_dispatch: - /* Switch to INT mode */ - msr cpsr_c, SCRATCH_2 + /* Clear ISR signals to thread executing */ + strb r3, [r0] - /* Restore interrupt context */ - mov lr, SCRATCH_0 - msr spsr, SCRATCH_1 + /* Thread dispatch */ + bl _Thread_Dispatch - /* Restore scratch registers from INT stack */ - ldmia sp!, SCRATCH_LIST +thread_dispatch_done: - /* Return from interrupt */ - subs pc, lr, #4 + /* Switch to ARM instructions if necessary */ + SWITCH_FROM_THUMB_TO_ARM -nested_interrupt_context_save: + /* Restore link register */ + ldmia sp!, {lr} - /* Move saved program status register to scratch register */ - mrs SCRATCH_0, spsr + /* + * XXX: Remember and restore stack pointer. The data on the stack is + * still in use. So the stack is now in an inconsistent state. The + * FIQ handler implementation must not use this area. + */ + mov r0, sp + add sp, #CONTEXT_SIZE - /* Save context on INT stack */ - stmdb sp!, INTERRUPT_CONTEXT_LIST + /* Get INT mode program status register */ + mrs r1, cpsr + bic r1, r1, #0x1 - b task_context_save_done + /* Switch to INT mode */ + msr cpsr, r1 -nested_interrupt_return: + /* Save EXCHANGE_LR and EXCHANGE_SPSR registers to exchange area */ + stmdb sp!, {EXCHANGE_LR, EXCHANGE_SPSR} - /* Restore context from INT stack */ - ldmia sp!, INTERRUPT_CONTEXT_LIST + /* Restore context */ + ldmia r0, CONTEXT_LIST - /* Restore saved program status register */ - msr spsr, SCRATCH_0 + /* Set return address and program status */ + mov lr, EXCHANGE_LR + msr spsr, EXCHANGE_SPSR - /* Restore scratch registers from INT stack */ - ldmia sp!, SCRATCH_LIST + /* Restore EXCHANGE_LR and EXCHANGE_SPSR registers from exchange area */ + ldmia sp!, {EXCHANGE_LR, EXCHANGE_SPSR} /* Return from interrupt */ subs pc, lr, #4 diff --git a/cpukit/score/cpu/arm/rtems/score/arm.h b/cpukit/score/cpu/arm/rtems/score/arm.h index 54da4a7e7b..119f235015 100644 --- a/cpukit/score/cpu/arm/rtems/score/arm.h +++ b/cpukit/score/cpu/arm/rtems/score/arm.h @@ -50,6 +50,9 @@ extern "C" { #elif defined(__ARM_ARCH_5TE__) # define CPU_MODEL_NAME "ARMv5TE" +#elif defined(__ARM_ARCH_5TEJ__) +# define CPU_MODEL_NAME "ARMv5TEJ" + #elif defined(__ARM_ARCH_6J__) # define CPU_MODEL_NAME "ARMv6J" -- cgit v1.2.3