You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/05/11 18:07:58 UTC

[incubator-nuttx] branch master updated: tlsr82/tc32: optimize the irq process

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new c39d3fa9e4 tlsr82/tc32: optimize the irq process
c39d3fa9e4 is described below

commit c39d3fa9e48246d0b2385df1585f60d86068f076
Author: wangbowen6 <wa...@xiaomi.com>
AuthorDate: Wed May 11 20:23:23 2022 +0800

    tlsr82/tc32: optimize the irq process
    
    1. using armv6-m arm_irq();
    2. simplify the interrupt number get process;
    3. To improve the performance, move common exception code to ram_code.
    
    Signed-off-by: wangbowen6 <wa...@xiaomi.com>
---
 arch/arm/src/tlsr82/chip/b87/boot/cstartup_flash.S | 170 +--------------
 arch/arm/src/tlsr82/tc32/Make.defs                 |   5 +-
 arch/arm/src/tlsr82/tc32/tc32_doirq.c              | 164 ++-------------
 arch/arm/src/tlsr82/tc32/tc32_exception.S          | 231 +++++++++++++++++++++
 4 files changed, 253 insertions(+), 317 deletions(-)

diff --git a/arch/arm/src/tlsr82/chip/b87/boot/cstartup_flash.S b/arch/arm/src/tlsr82/chip/b87/boot/cstartup_flash.S
index 50656a388f..6a676565bb 100644
--- a/arch/arm/src/tlsr82/chip/b87/boot/cstartup_flash.S
+++ b/arch/arm/src/tlsr82/chip/b87/boot/cstartup_flash.S
@@ -68,11 +68,9 @@
 	.global	__LOAD_DUT
 	.global	__LOAD_FLASH
 
-	.extern	g_current_regs
-
 __start:					@ MUST,  referenced by boot.link
 
-	.extern	irq_handler
+	.extern	tc32_exception
 
 	.extern	firmwareVersion
 
@@ -388,176 +386,20 @@ IDLE_STACK:
 	@ src code for the irq proc part .
 
 __irq:
-	/* Save R0 ~ R3, these registers will be used */
+	/* Save R0 ~ R3, these registers will be used before saved */
 
 	tpush		{r0}
 	tpush		{r1}
 	tpush		{r2}
 	tpush		{r3}
 
-	/* Switch to SVC mode to get the SVC mode SP, then save context
-	 * into the interrupted task stack.
-	 */
-
-	/* Disable interrupt, because cpu will switch into SVC mode, interrupt
-	 * nested should be prevented, this is not supported by tc32
-	 * architecture.
-	 */
-
-	tloadr		r0, _REG_IRQ_EN 		@ disable irq
-	tloadrb		r1, [r0]			@ get irq state in R1
-	tmov		r2, #0				@ disable irq
-	tstorerb	r2, [r0]			@ disable irq
-
-	/* Switch to SVC mode and get the LR and SP in SVC mode */
-
-	tloadr		r0, _REG_IRQ_EN + 4		@ switch to SVC mode
-	tnop
-	tmcsr		r0				@ switch to SVC mode
-	tnop
-	tmov		r2, r14				@ get SVC mode LR in R2
-	tmov		r3, r13 			@ get SVC mode SP in R3
-	tloadr		r0, _REG_IRQ_EN + 8 		@ return to IRQ mode
-	tnop
-	tmcsr		r0				@ return to IRQ mode
-	tnop
-
-	/* R0 = the interrupted task SP after context save */
-
-	tmov		r0, #XCPTCONTEXT_SIZE
-	tsub		r0, r3, r0
-
-	/* Save IRQ_STATE, SVC mode SP and SVC mode LR as PC */
-
-	tstorer		r1, [r0, #(4 * REG_IRQ_EN)]
-	tstorer		r3, [r0, #(4 * REG_SP)]
-	tstorer		r2, [r0, #(4 * REG_PC)]
-
-	/* Get SPSR and save as CPSR */
-
-	tmrss		r1
-	tstorer		r1, [r0, #(4 * REG_CPSR)]
-
-	/* Save IRQ mode LR as LR */
-
-	tmov		r1, r14
-	tstorer		r1, [r0, #(4 * REG_LR)]
-
-	/* Pop the saved R1 ~ R3 (pushed in beginning of irq),
-	 * then save R1 ~ R7.
-	 */
-
-	tpop		{r3}
-	tpop		{r2}
-	tpop		{r1}
-	tstorer		r1, [r0, #(4 * REG_R1)]
-	tstorer		r2, [r0, #(4 * REG_R2)]
-	tstorer		r3, [r0, #(4 * REG_R3)]
-	tstorer		r4, [r0, #(4 * REG_R4)]
-	tstorer		r5, [r0, #(4 * REG_R5)]
-	tstorer		r6, [r0, #(4 * REG_R6)]
-	tstorer		r7, [r0, #(4 * REG_R7)]
-
-	/* Pop R0 and save it */
-
-	tpop		{r1}
-	tstorer		r1, [r0, #(4 * REG_R0)]
-
-	/* Save R8 ~ R12 */
-
-	tmov		r1, r8
-	tstorer		r1, [r0, #(4 * REG_R8)]
-	tmov		r1, r9
-	tstorer		r1, [r0, #(4 * REG_R9)]
-	tmov		r1, r10
-	tstorer		r1, [r0, #(4 * REG_R10)]
-	tmov		r1, r11
-	tstorer		r1, [r0, #(4 * REG_R11)]
-	tmov		r1, r12
-	tstorer		r1, [r0, #(4 * REG_R12)]
-
-	/* R0 = interrupted task SP after the context save */
-
-	tjl 		irq_handler
-
-	/* R0 = interrupted task SP after the context save (no context switch)
-	 *    = next task tcb->regs (with context switch)
-	 * Restore all the register according to R0
-	 */
-
-	/* Dsiable interrupt to protect the SVC mode */
-
-	tloadr		r2, _REG_IRQ_EN 		@ disable irq
-	tmov		r3, #0				@ disable irq
-	tstorerb	r3, [r2]			@ disable irq
-
-	/* Restore SVC mode SP (R13), SVC mode LR (R14, based saved PC)
-	 * PC is not need to retore */
-
-	tloadr		r2, [r0, #(4 * REG_SP)]
-	tloadr		r3, [r0, #(4 * REG_PC)]
-
-	tloadr		r1, _REG_IRQ_EN + 4		@ switch to SVC mode
-	tnop
-	tmcsr		r1				@ switch to SVC mode
-	tnop
-	tmov		r14, r3				@ restore SVC mode LR
-	tmov		r13, r2 			@ restore SVC mode SP
-	tloadr		r1, _REG_IRQ_EN + 8 		@ return to IRQ mode
-	tnop
-	tmcsr		r1				@ return to IRQ mode
-	tnop
-
-	/* Restore CPSR to SPSR, IRQ_STATE */
-
-	tloadr		r2, [r0, #(4 * REG_CPSR)]
-	tloadr		r3, [r0, #(4 * REG_IRQ_EN)]
-
-	tmssr		r2				@ restore CPSR to SPSR
-
-	tloadr		r1, _REG_IRQ_EN 		@ restore IRQ enable flag
-	tstorerb	r3, [r1]			@ restore IRQ enable flag
-
-	/* Restore R8 ~ R12, IRQ mode LR */
-
-	tloadr		r2, [r0, #(4 * REG_R8)]
-	tloadr		r3, [r0, #(4 * REG_R9)]
-	tloadr		r4, [r0, #(4 * REG_R10)]
-	tloadr		r5, [r0, #(4 * REG_R11)]
-	tloadr		r6, [r0, #(4 * REG_R12)]
-	tloadr		r7, [r0, #(4 * REG_LR)]
-
-	tmov		r8, r2
-	tmov		r9, r3
-	tmov		r10, r4
-	tmov		r11, r5
-	tmov		r12, r6
-	tmov		r14, r7
-
-	/* Restore R1 ~ R7 */
-
-	tloadr		r1, [r0, #(4 * REG_R1)]
-	tloadr		r2, [r0, #(4 * REG_R2)]
-	tloadr		r3, [r0, #(4 * REG_R3)]
-	tloadr		r4, [r0, #(4 * REG_R4)]
-	tloadr		r5, [r0, #(4 * REG_R5)]
-	tloadr		r6, [r0, #(4 * REG_R6)]
-	tloadr		r7, [r0, #(4 * REG_R7)]
-
-	/* Restore R0 */
-
-	tloadr		r0, [r0, #(4 * REG_R0)]
-
-	/* Return to interrupted task or next task */
+	/* Save LR, LR will change after tjl instruction */
 
 	tpush		{r14}
-	treti		{r15}
 
-	.align		4
-_REG_IRQ_EN:
-	.word		0x00800643
-	.word		0x00000093
-	.word		0x00000092
+	/* Call tc32_exception */
+
+	tjl		tc32_exception
 
 ASMEND:
 	.section	.bss
diff --git a/arch/arm/src/tlsr82/tc32/Make.defs b/arch/arm/src/tlsr82/tc32/Make.defs
index 5efb136860..bd0cc3c84a 100644
--- a/arch/arm/src/tlsr82/tc32/Make.defs
+++ b/arch/arm/src/tlsr82/tc32/Make.defs
@@ -42,11 +42,12 @@ CMN_CSRCS := $(filter-out $(TC32_CSRCS_FILTER), $(CMN_CSRCS))
 
 # Common files in arch/arm/src/armv6-m
 
-CMN_CSRCS += arm_sigdeliver.c
+CMN_CSRCS += arm_sigdeliver.c arm_doirq.c
 
 # Common files in arch/arm/src/tlsr82/tc32
 
-CMN_ASRCS += tc32_fullcontextrestore.S tc32_switchcontext.S tc32_saveusercontext.S
+CMN_ASRCS += tc32_fullcontextrestore.S tc32_switchcontext.S
+CMN_ASRCS += tc32_saveusercontext.S tc32_exception.S
 
 CMN_CSRCS += tc32_doirq.c tc32_initialstate.c tc32_schedulesigaction.c
 CMN_CSRCS += tc32_syscall.c tc32_udelay.c
diff --git a/arch/arm/src/tlsr82/tc32/tc32_doirq.c b/arch/arm/src/tlsr82/tc32/tc32_doirq.c
index bbe178da8c..e217eb9041 100644
--- a/arch/arm/src/tlsr82/tc32/tc32_doirq.c
+++ b/arch/arm/src/tlsr82/tc32/tc32_doirq.c
@@ -37,8 +37,6 @@
 
 #include "arm_internal.h"
 
-#include "group/group.h"
-
 #include "hardware/tlsr82_irq.h"
 
 /****************************************************************************
@@ -53,75 +51,10 @@
  * Private Data
  ****************************************************************************/
 
-static const uint8_t tc32_lowbit_bitmap[] =
-{
-  0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 00 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 10 */
-  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 20 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 30 */
-  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 40 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 50 */
-  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 60 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 70 */
-  7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 80 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 90 */
-  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* A0 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* B0 */
-  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* C0 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* D0 */
-  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* E0 */
-  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* F0 */
-};
-
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
 
-/****************************************************************************
- * Name: tc32_ffs
- *
- * Description:
- *   This function finds the first bit set (beginning with the least
- *   significant bit) in value and return the index of that bit.
- *   TC32 archtecture does not support clz instruction.
- *
- * Parameters:
- *   uint32_t - value
- *
- * Return Value:
- *   [1, 32] - On Success.
- *   0       - No set bit in value (value = 0).
- *
- ****************************************************************************/
-
-static inline int locate_code(".ram_code") tc32_ffs(uint32_t value)
-{
-  int ret;
-
-  if (value == 0)
-    {
-      ret = 0;
-    }
-  else if (value & 0xff)
-    {
-      ret = (int)tc32_lowbit_bitmap[value & 0xff] + 1;
-    }
-  else if (value & 0xff00)
-    {
-      ret = (int)tc32_lowbit_bitmap[(value & 0xff00) >> 8] + 9;
-    }
-  else if (value & 0xff0000)
-    {
-      ret = (int)tc32_lowbit_bitmap[(value & 0xff0000) >> 16] + 17;
-    }
-  else
-    {
-      ret = (int)tc32_lowbit_bitmap[(value & 0xff000000) >> 24] + 25;
-    }
-
-  return ret;
-}
-
 /****************************************************************************
  * Name: tc32_getirq
  *
@@ -141,22 +74,17 @@ static inline int locate_code(".ram_code") tc32_ffs(uint32_t value)
 static int locate_code(".ram_code") tc32_getirq(void)
 {
   int irq;
+  uint32_t value;
 
-  /* Only detect the enable interrupt */
-
-  irq = tc32_ffs(IRQ_SRC_REG & IRQ_MASK_REG);
+  value = IRQ_SRC_REG & IRQ_MASK_REG;
 
-  if (irq > 0 && irq <= NR_IRQS)
+  if (value == 0)
     {
-      /* Minus one to obatin the correct irq number */
-
-      irq = irq - 1;
+      irq = NR_IRQS;
     }
   else
     {
-      /* Invalid irq number */
-
-      irq = NR_IRQS;
+      irq = __builtin_ctz(value);
     }
 
   return irq;
@@ -166,82 +94,16 @@ static int locate_code(".ram_code") tc32_getirq(void)
  * Public Functions
  ****************************************************************************/
 
-/**
- * @brief     This function serves to handle the interrupt of MCU
- * @param[in] none
- * @return    none
- */
+/****************************************************************************
+ * Name: arm_ack_irq
+ *
+ * Description:
+ *   Acknowledge the IRQ
+ *
+ ****************************************************************************/
 
-uint32_t *arm_doirq(int irq, uint32_t *regs)
+void arm_ack_irq(int irq)
 {
-  board_autoled_on(LED_INIRQ);
-#ifdef CONFIG_SUPPRESS_INTERRUPTS
-  PANIC();
-#else
-  /* Current regs non-zero indicates that we are processing an interrupt;
-   * g_current_regs is also used to manage interrupt level context switches.
-   *
-   * Nested interrupts are not supported
-   */
-
-  DEBUGASSERT(CURRENT_REGS == NULL);
-  CURRENT_REGS = regs;
-
-  /* Disable further occurrences of this interrupt (until the interrupt
-   * sources have been clear by the driver).
-   */
-
-  /* Deliver the IRQ */
-
-  irq_dispatch(irq, regs);
-
-#if defined(CONFIG_ARCH_FPU) || defined(CONFIG_ARCH_ADDRENV)
-  /* Check for a context switch.  If a context switch occurred, then
-   * g_current_regs will have a different value than it did on entry.  If an
-   * interrupt level context switch has occurred, then restore the floating
-   * point state and the establish the correct address environment before
-   * returning from the interrupt.
-   */
-
-  if (regs != CURRENT_REGS)
-    {
-#ifdef CONFIG_ARCH_FPU
-      /* Restore floating point registers */
-
-      riscv_restorefpu(CURRENT_REGS);
-#endif
-
-#ifdef CONFIG_ARCH_ADDRENV
-      /* Make sure that the address environment for the previously
-       * running task is closed down gracefully (data caches dump,
-       * MMU flushed) and set up the address environment for the new
-       * thread at the head of the ready-to-run list.
-       */
-
-      group_addrenv(NULL);
-#endif
-    }
-#endif
-
-  /* If a context switch occurred while processing the interrupt then
-   * g_current_regs may have change value.  If we return any value different
-   * from the input regs, then the lower level will know that a context
-   * switch occurred during interrupt processing.
-   */
-
-  regs = (uint32_t *)CURRENT_REGS;
-
-  /* Set g_current_regs to NULL to indicate that we are no longer in an
-   * interrupt handler.
-   */
-
-  CURRENT_REGS = NULL;
-
-  /* Unmask the last interrupt (global interrupts are still disabled) */
-
-#endif
-  board_autoled_off(LED_INIRQ);
-  return regs;
 }
 
 /****************************************************************************
diff --git a/arch/arm/src/tlsr82/tc32/tc32_exception.S b/arch/arm/src/tlsr82/tc32/tc32_exception.S
new file mode 100644
index 0000000000..1c5249f9d4
--- /dev/null
+++ b/arch/arm/src/tlsr82/tc32/tc32_exception.S
@@ -0,0 +1,231 @@
+/****************************************************************************
+ * arch/arm/src/tlsr82/tc32/tc32_exception.S
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <arch/chip/irq.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Symbols
+ ****************************************************************************/
+
+	.file	"tc32_exception.S"
+
+/****************************************************************************
+ * Macros
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tc32_exception
+ *
+ * Description:
+ *   TC32 Common exception
+ *
+ ****************************************************************************/
+
+	.align	2
+	.code	16
+	.thumb_func
+	.section .ram_code,"ax"
+	.extern irq_handler
+	.global	tc32_exception
+	.type	tc32_exception, function
+tc32_exception:
+
+	/* Restore the R14 (LR) saved in __irq */
+
+	tpop		{r3}
+	tmov		r14, r3
+
+	/* Switch to SVC mode to get the SVC mode SP, then save context
+	 * into the interrupted task stack.
+	 */
+
+	/* Disable interrupt, because cpu will switch into SVC mode, interrupt
+	 * nested should be prevented, this is not supported by tc32
+	 * architecture.
+	 */
+
+	tloadr		r0, _REG_IRQ_EN 		/* disable irq */
+	tloadrb		r1, [r0]			/* get irq state in R1 */
+	tmov		r2, #0				/* disable irq */
+	tstorerb	r2, [r0]			/* disable irq */
+
+	/* Switch to SVC mode and get the LR and SP in SVC mode */
+
+	tloadr		r0, _REG_IRQ_EN + 4		/* switch to SVC mode */
+	tnop
+	tmcsr		r0				/* switch to SVC mode */
+	tnop
+	tmov		r2, r14				/* get SVC mode LR in R2 */
+	tmov		r3, r13 			/* get SVC mode SP in R3 */
+	tloadr		r0, _REG_IRQ_EN + 8 		/* return to IRQ mode */
+	tnop
+	tmcsr		r0				/* return to IRQ mode */
+	tnop
+
+	/* R0 = the interrupted task SP after context save */
+
+	tmov		r0, #XCPTCONTEXT_SIZE
+	tsub		r0, r3, r0
+
+	/* Save IRQ_STATE, SVC mode SP and SVC mode LR as PC */
+
+	tstorer		r1, [r0, #(4 * REG_IRQ_EN)]
+	tstorer		r3, [r0, #(4 * REG_SP)]
+	tstorer		r2, [r0, #(4 * REG_PC)]
+
+	/* Get SPSR and save as CPSR */
+
+	tmrss		r1
+	tstorer		r1, [r0, #(4 * REG_CPSR)]
+
+	/* Save IRQ mode LR as LR */
+
+	tmov		r1, r14
+	tstorer		r1, [r0, #(4 * REG_LR)]
+
+	/* Pop the saved R1 ~ R3 (pushed in beginning of irq),
+	 * then save R1 ~ R7.
+	 */
+
+	tpop		{r3}
+	tpop		{r2}
+	tpop		{r1}
+	tstorer		r1, [r0, #(4 * REG_R1)]
+	tstorer		r2, [r0, #(4 * REG_R2)]
+	tstorer		r3, [r0, #(4 * REG_R3)]
+	tstorer		r4, [r0, #(4 * REG_R4)]
+	tstorer		r5, [r0, #(4 * REG_R5)]
+	tstorer		r6, [r0, #(4 * REG_R6)]
+	tstorer		r7, [r0, #(4 * REG_R7)]
+
+	/* Pop R0 and save it */
+
+	tpop		{r1}
+	tstorer		r1, [r0, #(4 * REG_R0)]
+
+	/* Save R8 ~ R12 */
+
+	tmov		r1, r8
+	tstorer		r1, [r0, #(4 * REG_R8)]
+	tmov		r1, r9
+	tstorer		r1, [r0, #(4 * REG_R9)]
+	tmov		r1, r10
+	tstorer		r1, [r0, #(4 * REG_R10)]
+	tmov		r1, r11
+	tstorer		r1, [r0, #(4 * REG_R11)]
+	tmov		r1, r12
+	tstorer		r1, [r0, #(4 * REG_R12)]
+
+	/* R0 = interrupted task SP after the context save */
+
+	tjl 		irq_handler
+
+	/* R0 = interrupted task SP after the context save (no context switch)
+	 *    = next task tcb->regs (with context switch)
+	 * Restore all the register according to R0
+	 */
+
+	/* Dsiable interrupt to protect the SVC mode */
+
+	tloadr		r2, _REG_IRQ_EN 		/* disable irq */
+	tmov		r3, #0				/* disable irq */
+	tstorerb	r3, [r2]			/* disable irq */
+
+	/* Restore SVC mode SP (R13), SVC mode LR (R14, based saved PC)
+	 * PC is not need to retore */
+
+	tloadr		r2, [r0, #(4 * REG_SP)]
+	tloadr		r3, [r0, #(4 * REG_PC)]
+
+	tloadr		r1, _REG_IRQ_EN + 4		/* switch to SVC mode */
+	tnop
+	tmcsr		r1				/* switch to SVC mode */
+	tnop
+	tmov		r14, r3				/* restore SVC mode LR */
+	tmov		r13, r2 			/* restore SVC mode SP */
+	tloadr		r1, _REG_IRQ_EN + 8 		/* return to IRQ mode */
+	tnop
+	tmcsr		r1				/* return to IRQ mode */
+	tnop
+
+	/* Restore CPSR to SPSR, IRQ_STATE */
+
+	tloadr		r2, [r0, #(4 * REG_CPSR)]
+	tloadr		r3, [r0, #(4 * REG_IRQ_EN)]
+
+	tmssr		r2				/* restore CPSR to SPSR */
+
+	tloadr		r1, _REG_IRQ_EN 		/* restore IRQ enable flag */
+	tstorerb	r3, [r1]			/* restore IRQ enable flag */
+
+	/* Restore R8 ~ R12, IRQ mode LR */
+
+	tloadr		r2, [r0, #(4 * REG_R8)]
+	tloadr		r3, [r0, #(4 * REG_R9)]
+	tloadr		r4, [r0, #(4 * REG_R10)]
+	tloadr		r5, [r0, #(4 * REG_R11)]
+	tloadr		r6, [r0, #(4 * REG_R12)]
+	tloadr		r7, [r0, #(4 * REG_LR)]
+
+	tmov		r8, r2
+	tmov		r9, r3
+	tmov		r10, r4
+	tmov		r11, r5
+	tmov		r12, r6
+	tmov		r14, r7
+
+	/* Restore R1 ~ R7 */
+
+	tloadr		r1, [r0, #(4 * REG_R1)]
+	tloadr		r2, [r0, #(4 * REG_R2)]
+	tloadr		r3, [r0, #(4 * REG_R3)]
+	tloadr		r4, [r0, #(4 * REG_R4)]
+	tloadr		r5, [r0, #(4 * REG_R5)]
+	tloadr		r6, [r0, #(4 * REG_R6)]
+	tloadr		r7, [r0, #(4 * REG_R7)]
+
+	/* Restore R0 */
+
+	tloadr		r0, [r0, #(4 * REG_R0)]
+
+	/* Return to interrupted task or next task */
+
+	tpush		{r14}
+	treti		{r15}
+
+	.align		4
+_REG_IRQ_EN:
+	.word		0x00800643
+	.word		0x00000093
+	.word		0x00000092
+	.size		tc32_exception, .-tc32_exception
+	.end