summaryrefslogtreecommitdiffstats
path: root/cpukit/score/cpu/arm
diff options
context:
space:
mode:
authorThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-12-15 15:14:12 +0000
committerThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-12-15 15:14:12 +0000
commit9db18ddc06bfe8661b48f1213b092fcbd2c97401 (patch)
tree69462eb0f68ba68cddf6d089107fc9d7a0143803 /cpukit/score/cpu/arm
parent2009-12-14 Joel Sherrill <joel.sherrill@oarcorp.com> (diff)
downloadrtems-9db18ddc06bfe8661b48f1213b092fcbd2c97401.tar.bz2
add support for ARM11, reimplement nested interrupts
Diffstat (limited to 'cpukit/score/cpu/arm')
-rw-r--r--cpukit/score/cpu/arm/ChangeLog8
-rw-r--r--cpukit/score/cpu/arm/arm_exc_interrupt.S265
-rw-r--r--cpukit/score/cpu/arm/rtems/score/arm.h3
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 <sebastian.huber@embedded-brains.de>
+
+ * 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 <joel.sherrill@oarcorp.com>
* 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"