mirror of
				https://github.com/FreeRTOS/FreeRTOS-Kernel.git
				synced 2025-11-03 18:49:02 +01:00 
			
		
		
		
	GNU as makes unrecognized sections loadable and writable by default, but LLVM's assembler requires specifying flags explicitly. Without them, the linker generates "has non-ABS relocation" errors when trying to link the resulting object files.
		
			
				
	
	
		
			499 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			499 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
 | 
						|
 * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: MIT
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
						|
 * this software and associated documentation files (the "Software"), to deal in
 | 
						|
 * the Software without restriction, including without limitation the rights to
 | 
						|
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
						|
 * the Software, and to permit persons to whom the Software is furnished to do so,
 | 
						|
 * subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice shall be included in all
 | 
						|
 * copies or substantial portions of the Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
						|
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
						|
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
						|
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
						|
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
						|
 *
 | 
						|
 * https://www.FreeRTOS.org
 | 
						|
 * https://github.com/FreeRTOS
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
    .arm
 | 
						|
    .syntax unified
 | 
						|
    .section privileged_functions, "ax"
 | 
						|
 | 
						|
#define FREERTOS_ASSEMBLY
 | 
						|
    #include "portmacro_asm.h"
 | 
						|
    #include "mpu_syscall_numbers.h"
 | 
						|
#undef FREERTOS_ASSEMBLY
 | 
						|
 | 
						|
    /* External FreeRTOS-Kernel variables. */
 | 
						|
    .extern pxCurrentTCB
 | 
						|
    .extern uxSystemCallImplementations
 | 
						|
    .extern ulPortInterruptNesting
 | 
						|
    .extern ulPortYieldRequired
 | 
						|
 | 
						|
    /* External Llnker script variables. */
 | 
						|
    .extern __syscalls_flash_start__
 | 
						|
    .extern __syscalls_flash_end__
 | 
						|
 | 
						|
    /* External FreeRTOS-Kernel functions. */
 | 
						|
    .extern vTaskSwitchContext
 | 
						|
    .extern vApplicationIRQHandler
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/* Save the context of a FreeRTOS Task. */
 | 
						|
.macro portSAVE_CONTEXT
 | 
						|
    DSB
 | 
						|
    ISB
 | 
						|
    /* Push R0 and LR to the stack for current mode. */
 | 
						|
    PUSH    { R0, LR }
 | 
						|
 | 
						|
    LDR     LR, =pxCurrentTCB   /* LR = &( pxCurrentTCB ). */
 | 
						|
    LDR     LR, [LR]            /* LR = pxCurrentTCB. */
 | 
						|
    LDR     LR, [LR]            /* LR = pxTopOfStack i.e. the address where to store the task context. */
 | 
						|
 | 
						|
    LDR     R0, =ulCriticalNesting  /* R0 = &( ulCriticalNesting ). */
 | 
						|
    LDR     R0, [R0]                /* R0 = ulCriticalNesting. */
 | 
						|
    STM     LR!, { R0 }             /* Store ulCriticalNesting. ! increments LR after storing. */
 | 
						|
 | 
						|
#if ( portENABLE_FPU == 1 )
 | 
						|
    VMRS    R0, FPSCR       /* R0 = FPSCR. */
 | 
						|
    STM     LR!, { R0 }     /* Store FPSCR. */
 | 
						|
    VSTM    LR!, { D0-D15 } /* Store D0-D15. */
 | 
						|
#endif /* ( portENABLE_FPU == 1 ) */
 | 
						|
 | 
						|
    POP     { R0 }  /* Restore R0 to pre-exception value. */
 | 
						|
    /* STM (user registers) - In a PL1 mode other than System mode, STM (user
 | 
						|
     * registers) instruction stores multiple User mode registers to
 | 
						|
     * consecutive memory locations using an address from a base register. The
 | 
						|
     * processor reads the base register value normally, using the current mode
 | 
						|
     * to determine the correct Banked version of the register. This instruction
 | 
						|
     * cannot writeback to the base register.
 | 
						|
     *
 | 
						|
     * The following can be derived from the above description:
 | 
						|
     * - The macro portSAVE_CONTEXT MUST be called from a PL1 mode other than
 | 
						|
     *   the System mode.
 | 
						|
     * - Base register LR of the current mode will be used which contains the
 | 
						|
     *   location to store the context.
 | 
						|
     * - It will store R0-R14 of User mode i.e. pre-exception SP(R13) and LR(R14)
 | 
						|
     *   will be stored. */
 | 
						|
    STM     LR, { R0-R14 }^
 | 
						|
    ADD     LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */
 | 
						|
 | 
						|
    POP     { R0 }          /* Pre-exception PC is in R0. */
 | 
						|
    MRS     R1, SPSR        /* R1 = Pre-exception CPSR. */
 | 
						|
    STM     LR!, { R0-R1 }  /* Store pre-exception PC and CPSR. */
 | 
						|
 | 
						|
.endm
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/* Restore the context of a FreeRTOS Task. */
 | 
						|
.macro portRESTORE_CONTEXT
 | 
						|
    /* Load the pointer to the current task's Task Control Block (TCB). */
 | 
						|
    LDR     LR, =pxCurrentTCB   /* LR = &( pxCurrentTCB ). */
 | 
						|
    LDR     LR, [LR]            /* LR = pxCurrentTCB. */
 | 
						|
    ADD     R1, LR, #0x4        /* R1 now points to the xMPUSettings in TCB. */
 | 
						|
    LDR     LR, [LR]            /* LR = pxTopOfStack i.e. the address where to restore the task context from. */
 | 
						|
 | 
						|
    /* When creating a loop label in a macro it has to be a numeric label.
 | 
						|
     * for( R5 = portFIRST_CONFIGURABLE_REGION ; R5 <= portNUM_CONFIGURABLE_REGIONS ; R5++ ) */
 | 
						|
    MOV     R5, #portFIRST_CONFIGURABLE_REGION
 | 
						|
    123:
 | 
						|
        LDMIA   R1!, { R2-R4 }  /* R2 = ulRegionSize, R3 = ulRegionAttribute, R4 = ulRegionBaseAddress. */
 | 
						|
 | 
						|
        MCR     p15, #0, R5, c6, c2, #0 /* MPU Region Number Register. */
 | 
						|
        MCR     p15, #0, R4, c6, c1, #0 /* MPU Region Base Address Register. */
 | 
						|
        MCR     p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */
 | 
						|
        MCR     p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */
 | 
						|
 | 
						|
        ADD     R5, R5, #1
 | 
						|
        CMP     R5, #portNUM_CONFIGURABLE_REGIONS
 | 
						|
        BLE     123b
 | 
						|
 | 
						|
    LDR     R1, =ulCriticalNesting /* R1 = &( ulCriticalNesting ). */
 | 
						|
    LDM     LR!, { R2 }            /* R2 = Stored ulCriticalNesting. */
 | 
						|
    STR     R2, [R1]               /* Restore ulCriticalNesting. */
 | 
						|
 | 
						|
#if ( portENABLE_FPU == 1 )
 | 
						|
    LDM     LR!, { R1 }     /* R1 = Stored FPSCR.  */
 | 
						|
    VMSR    FPSCR, R1       /* Restore FPSCR. */
 | 
						|
    VLDM   LR!, { D0-D15 }  /* Restore D0-D15. */
 | 
						|
#endif /* portENABLE_FPU*/
 | 
						|
 | 
						|
    /* LDM (User registers) - In a PL1 mode other than System mode, LDM (User
 | 
						|
     * registers) loads multiple User mode registers from consecutive memory
 | 
						|
     * locations using an address from a base register. The registers loaded
 | 
						|
     * cannot include the PC. The processor reads the base register value
 | 
						|
     * normally, using the current mode to determine the correct Banked version
 | 
						|
     * of the register. This instruction cannot writeback to the base register.
 | 
						|
     *
 | 
						|
     *  The following can be derived from the above description:
 | 
						|
     * - The macro portRESTORE_CONTEXT MUST be called from a PL1 mode other than
 | 
						|
     *   the System mode.
 | 
						|
     * - Base register LR of the current mode will be used which contains the
 | 
						|
     *   location to restore the context from.
 | 
						|
     * - It will restore R0-R14 of User mode i.e. SP(R13) and LR(R14) of User
 | 
						|
     *   mode will be restored.
 | 
						|
     */
 | 
						|
    LDM     LR, { R0-R14 }^
 | 
						|
    ADD     LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */
 | 
						|
 | 
						|
    RFE     LR  /* Restore PC and CPSR from the context. */
 | 
						|
 | 
						|
.endm
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vPortStartFirstTask( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vPortStartFirstTask
 | 
						|
.type vPortStartFirstTask, %function
 | 
						|
vPortStartFirstTask:
 | 
						|
    /* This function is called from System Mode to start the FreeRTOS-Kernel.
 | 
						|
     * As described in the portRESTORE_CONTEXT macro, portRESTORE_CONTEXT cannot
 | 
						|
     * be called from the System mode. We, therefore, switch to the Supervisor
 | 
						|
     * mode before calling portRESTORE_CONTEXT. */
 | 
						|
    CPS #SVC_MODE
 | 
						|
    portRESTORE_CONTEXT
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
.align 4
 | 
						|
.global FreeRTOS_SVC_Handler
 | 
						|
.type FreeRTOS_SVC_Handler, %function
 | 
						|
FreeRTOS_SVC_Handler:
 | 
						|
    PUSH    { R11-R12 }
 | 
						|
 | 
						|
    /* ------------------------- Caller Flash Location Check ------------------------- */
 | 
						|
 | 
						|
    LDR     R11, =__syscalls_flash_start__
 | 
						|
    LDR     R12, =__syscalls_flash_end__
 | 
						|
    CMP     LR, R11 /* If SVC instruction address is less than __syscalls_flash_start__, exit. */
 | 
						|
    BLT     svcHandlerExit
 | 
						|
    CMP     LR, R12 /* If SVC instruction address is greater than __syscalls_flash_end__, exit. */
 | 
						|
    BGT     svcHandlerExit
 | 
						|
 | 
						|
    /* ---------------------------- Get Caller SVC Number ---------------------------- */
 | 
						|
 | 
						|
    MRS     R11, SPSR               /* LR = CPSR at the time of SVC. */
 | 
						|
    TST     R11, #0x20              /* Check Thumb bit (5) in CPSR. */
 | 
						|
    LDRHNE  R11, [LR, #-0x2]        /* If Thumb, load halfword. */
 | 
						|
    BICNE   R11, R11, #0xFF00       /* And extract immidiate field (i.e. SVC number). */
 | 
						|
    LDREQ   R11, [LR, #-0x4]        /* If ARM, load word. */
 | 
						|
    BICEQ   R11, R11, #0xFF000000   /* And extract immidiate field (i.e. SVC number). */
 | 
						|
 | 
						|
    /* --------------------------------- SVC Routing --------------------------------- */
 | 
						|
 | 
						|
    /* If SVC Number < #NUM_SYSTEM_CALLS, go to svcSystemCallEnter. */
 | 
						|
    CMP     R11, #NUM_SYSTEM_CALLS
 | 
						|
    BLT     svcSystemCallEnter
 | 
						|
 | 
						|
    /* If SVC Number == #portSVC_SYSTEM_CALL_EXIT, go to svcSystemCallExit. */
 | 
						|
    CMP     R11, #portSVC_SYSTEM_CALL_EXIT
 | 
						|
    BEQ     svcSystemCallExit
 | 
						|
 | 
						|
    /* If SVC Number == #portSVC_YIELD, go to svcPortYield. */
 | 
						|
    CMP     R11, #portSVC_YIELD
 | 
						|
    BEQ     svcPortYield
 | 
						|
 | 
						|
svcHandlerExit:
 | 
						|
    POP     { R11-R12 }
 | 
						|
    MOVS    PC, LR /* Copies the SPSR into the CPSR, performing the mode swap. */
 | 
						|
 | 
						|
svcPortYield:
 | 
						|
    POP     { R11-R12 }
 | 
						|
    portSAVE_CONTEXT
 | 
						|
    BL      vTaskSwitchContext
 | 
						|
    portRESTORE_CONTEXT
 | 
						|
 | 
						|
svcSystemCallExit:
 | 
						|
    LDR     R11, =pxCurrentTCB /* R11 = &( pxCurrentTCB ). */
 | 
						|
    LDR     R11, [R11]         /* R11 = pxCurrentTCB. */
 | 
						|
    ADD     R11, R11, #portSYSTEM_CALL_INFO_OFFSET /* R11 now points to xSystemCallStackInfo in TCB. */
 | 
						|
 | 
						|
    /* Restore the user mode SP and LR. */
 | 
						|
    LDM   R11, { R13-R14 }^
 | 
						|
 | 
						|
    AND     R12, R12, #0x0      /* R12 = 0. */
 | 
						|
    STR     R12, [R11]          /* xSystemCallStackInfo.pulTaskStackPointer = NULL. */
 | 
						|
    STR     R12, [R11, #0x4]    /* xSystemCallStackInfo.pulLinkRegisterAtSystemCallEntry = NULL. */
 | 
						|
 | 
						|
    LDMDB   R11, { R12 }        /* R12 = ulTaskFlags. */
 | 
						|
 | 
						|
    TST     R12, #portTASK_IS_PRIVILEGED_FLAG
 | 
						|
    /* If the task is privileged, we can exit now. */
 | 
						|
    BNE     svcHandlerExit
 | 
						|
    /* Otherwise, we need to switch back to User mode. */
 | 
						|
    MRS     R12, SPSR
 | 
						|
    BIC     R12, R12, #0x0F
 | 
						|
    MSR     SPSR_cxsf, R12
 | 
						|
 | 
						|
    B   svcHandlerExit
 | 
						|
 | 
						|
svcSystemCallEnter:
 | 
						|
    LDR     R12, =uxSystemCallImplementations /* R12 = uxSystemCallImplementations. */
 | 
						|
    /* R12 = uxSystemCallImplementations[ R12 + ( R11 << 2 ) ].
 | 
						|
     * R12 now contains the address of the system call impl function. */
 | 
						|
    LDR     R12, [R12, R11, lsl #2]
 | 
						|
 | 
						|
    /* If R12 == NULL, exit. */
 | 
						|
    CMP     R12, #0x0
 | 
						|
    BEQ     svcHandlerExit
 | 
						|
 | 
						|
    /* It is okay to clobber LR here because we do not need to return to the
 | 
						|
     * SVC enter location anymore. LR now contains the address of the system
 | 
						|
     * call impl function. */
 | 
						|
    MOV     LR, R12
 | 
						|
 | 
						|
    LDR     R11, =pxCurrentTCB  /* R11 = &( pxCurrentTCB ). */
 | 
						|
    LDR     R11, [R11]          /* R11 = pxCurrentTCB. */
 | 
						|
    ADD     R11, R11, #portSYSTEM_CALL_INFO_OFFSET  /* R11 now points to xSystemCallStackInfo in TCB. */
 | 
						|
 | 
						|
    /* Store User mode SP and LR in xSystemCallStackInfo.pulTaskStackPointer and
 | 
						|
     * xSystemCallStackInfo.pulLinkRegisterAtSystemCallEntry. */
 | 
						|
    STM     R11, { R13-R14 }^
 | 
						|
    ADD     R11, R11, 0x8
 | 
						|
 | 
						|
    /* Load User mode SP an LR with xSystemCallStackInfo.pulSystemCallStackPointer
 | 
						|
     * and xSystemCallStackInfo.pulSystemCallExitAddress. */
 | 
						|
    LDM     R11, { R13-R14 }^
 | 
						|
 | 
						|
    /* Change to SYS_MODE for the System Call. */
 | 
						|
    MRS     R12, SPSR
 | 
						|
    ORR     R12, R12, #SYS_MODE
 | 
						|
    MSR     SPSR_cxsf, R12
 | 
						|
 | 
						|
    B       svcHandlerExit
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vPortDisableInterrupts( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vPortDisableInterrupts
 | 
						|
.type vPortDisableInterrupts, %function
 | 
						|
vPortDisableInterrupts:
 | 
						|
    CPSID    I
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vPortEnableInterrupts( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vPortEnableInterrupts
 | 
						|
.type vPortEnableInterrupts, %function
 | 
						|
vPortEnableInterrupts:
 | 
						|
    CPSIE   I
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vMPUSetRegion( uint32_t ulRegionNumber,
 | 
						|
 *                     uint32_t ulBaseAddress,
 | 
						|
 *                     uint32_t ulRegionSize,
 | 
						|
 *                     uint32_t ulRegionPermissions );
 | 
						|
 *
 | 
						|
 * According to the Procedure Call Standard for the ARM Architecture (AAPCS),
 | 
						|
 * paramters are passed in the following registers:
 | 
						|
 * R0 = ulRegionNumber.
 | 
						|
 * R1 = ulBaseAddress.
 | 
						|
 * R2 = ulRegionSize.
 | 
						|
 * R3 = ulRegionPermissions.
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vMPUSetRegion
 | 
						|
.type vMPUSetRegion, %function
 | 
						|
vMPUSetRegion:
 | 
						|
    AND     R0,  R0, #0x0F    /* R0 = R0 & 0x0F. Max possible region number is 15. */
 | 
						|
 | 
						|
    MCR     p15, #0, R0, c6, c2, #0 /* MPU Region Number Register. */
 | 
						|
    MCR     p15, #0, R1, c6, c1, #0 /* MPU Region Base Address Register. */
 | 
						|
    MCR     p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */
 | 
						|
    MCR     p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */
 | 
						|
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vMPUEnable( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vMPUEnable
 | 
						|
.type vMPUEnable, %function
 | 
						|
vMPUEnable:
 | 
						|
    PUSH    { R0 }
 | 
						|
 | 
						|
    MRC     p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */
 | 
						|
    ORR     R0,  R0, #0x1 /* R0 = R0 | 0x1. Set the M bit in SCTLR. */
 | 
						|
    DSB
 | 
						|
    MCR     p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */
 | 
						|
    ISB
 | 
						|
 | 
						|
    POP     { R0 }
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vMPUDisable( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vMPUDisable
 | 
						|
.type vMPUDisable, %function
 | 
						|
vMPUDisable:
 | 
						|
    PUSH    { R0 }
 | 
						|
 | 
						|
    MRC     p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */
 | 
						|
    BIC     R0,  R0, #1 /* R0 = R0 & ~0x1. Clear the M bit in SCTLR. */
 | 
						|
    /* Wait for all pending data accesses to complete. */
 | 
						|
    DSB
 | 
						|
    MCR     p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */
 | 
						|
    /* Flush the pipeline and prefetch buffer(s) in the processor to ensure that
 | 
						|
    *  all following instructions are fetched from cache or memory. */
 | 
						|
    ISB
 | 
						|
 | 
						|
    POP     { R0 }
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vMPUEnableBackgroundRegion( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vMPUEnableBackgroundRegion
 | 
						|
.type vMPUEnableBackgroundRegion, %function
 | 
						|
vMPUEnableBackgroundRegion:
 | 
						|
    PUSH    { R0 }
 | 
						|
 | 
						|
    MRC     p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */
 | 
						|
    ORR     R0, R0, #0x20000 /* R0 = R0 | 0x20000. Set the BR bit in SCTLR. */
 | 
						|
    MCR     p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */
 | 
						|
 | 
						|
    POP     { R0 }
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * void vMPUDisableBackgroundRegion( void );
 | 
						|
 */
 | 
						|
.align 4
 | 
						|
.global vMPUDisableBackgroundRegion
 | 
						|
.type vMPUDisableBackgroundRegion, %function
 | 
						|
vMPUDisableBackgroundRegion:
 | 
						|
    PUSH    { R0 }
 | 
						|
 | 
						|
    MRC     p15, 0, R0, c1, c0, 0 /* R0 = System Control Register (SCTLR). */
 | 
						|
    BIC     R0, R0, #0x20000 /* R0 = R0 & ~0x20000. Clear the BR bit in SCTLR. */
 | 
						|
    MCR     p15, 0, R0, c1, c0, 0 /* SCTLR = R0. */
 | 
						|
 | 
						|
    POP     { R0 }
 | 
						|
    BX      LR
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
.align 4
 | 
						|
.global FreeRTOS_IRQ_Handler
 | 
						|
.type FreeRTOS_IRQ_Handler, %function
 | 
						|
FreeRTOS_IRQ_Handler:
 | 
						|
    SUB     LR, LR, #4 /* Return to the interrupted instruction. */
 | 
						|
    SRSDB   SP!, #IRQ_MODE /* Save return state (i.e. SPSR_irq and LR_irq) to the IRQ stack. */
 | 
						|
 | 
						|
    /* Change to supervisor mode to allow reentry. It is necessary to ensure
 | 
						|
     * that a BL instruction within the interrupt handler code does not
 | 
						|
     * overwrite LR_irq. */
 | 
						|
    CPS     #SVC_MODE
 | 
						|
 | 
						|
    PUSH    { R0-R3, R12 } /* Push AAPCS callee saved registers. */
 | 
						|
 | 
						|
    /* Update interrupt nesting count. */
 | 
						|
    LDR     R0, =ulPortInterruptNesting /* R0 = &( ulPortInterruptNesting ). */
 | 
						|
    LDR     R1, [R0] /* R1 = ulPortInterruptNesting. */
 | 
						|
    ADD     R2, R1, #1 /* R2 = R1 + 1. */
 | 
						|
    STR     R2, [R0] /* Store the updated nesting count. */
 | 
						|
 | 
						|
    /* Call the application provided IRQ handler. */
 | 
						|
    PUSH    { R0-R3, LR }
 | 
						|
    BL      vApplicationIRQHandler
 | 
						|
    POP     { R0-R3, LR }
 | 
						|
 | 
						|
    /* Disable IRQs incase vApplicationIRQHandler enabled them for re-entry. */
 | 
						|
    CPSID   I
 | 
						|
    DSB
 | 
						|
    ISB
 | 
						|
 | 
						|
    /* Restore the old interrupt nesting count. R0 holds the address of
 | 
						|
     * ulPortInterruptNesting and R1 holds original value of
 | 
						|
     * ulPortInterruptNesting. */
 | 
						|
    STR     R1, [R0]
 | 
						|
 | 
						|
    /* Context switch is only performed when interrupt nesting count is 0. */
 | 
						|
    CMP     R1, #0
 | 
						|
    BNE     exit_without_switch
 | 
						|
 | 
						|
    /* Check ulPortInterruptNesting to see if the interrupt requested a context
 | 
						|
     * switch. */
 | 
						|
    LDR     R1, =ulPortYieldRequired /* R1 = &( ulPortYieldRequired ). */
 | 
						|
    LDR     R0, [R1] /* R0 = ulPortYieldRequired. */
 | 
						|
    /* If ulPortYieldRequired != 0, goto switch_before_exit. */
 | 
						|
    CMP     R0, #0
 | 
						|
    BNE     switch_before_exit
 | 
						|
 | 
						|
exit_without_switch:
 | 
						|
    POP     { R0-R3, R12 } /* Restore AAPCS callee saved registers. */
 | 
						|
    CPS     #IRQ_MODE
 | 
						|
    RFE     SP!
 | 
						|
 | 
						|
switch_before_exit:
 | 
						|
    /* A context switch is to be performed. Clear ulPortYieldRequired. R1 holds
 | 
						|
     * the address of ulPortYieldRequired. */
 | 
						|
    MOV     R0, #0
 | 
						|
    STR     R0, [R1]
 | 
						|
 | 
						|
    /* Restore AAPCS callee saved registers, SPSR_irq and LR_irq before saving
 | 
						|
     * the task context. */
 | 
						|
    POP     { R0-R3, R12 }
 | 
						|
    CPS     #IRQ_MODE
 | 
						|
    /* The contents of the IRQ stack at this point is the following:
 | 
						|
     *       +----------+
 | 
						|
     *  SP+4 | SPSR_irq |
 | 
						|
     *       +----------+
 | 
						|
     *    SP |  LR_irq  |
 | 
						|
     *       +----------+
 | 
						|
     */
 | 
						|
    LDMIB   SP!, { LR }
 | 
						|
    MSR     SPSR_cxsf, LR
 | 
						|
    LDMDB   SP, { LR }
 | 
						|
    ADD     SP, SP, 0x4
 | 
						|
    portSAVE_CONTEXT
 | 
						|
 | 
						|
    /* Call the function that selects the new task to execute. */
 | 
						|
    BLX     vTaskSwitchContext
 | 
						|
 | 
						|
    /* Restore the context of, and branch to, the task selected to execute
 | 
						|
     * next. */
 | 
						|
    portRESTORE_CONTEXT
 | 
						|
 | 
						|
/* ----------------------------------------------------------------------------------- */
 | 
						|
 | 
						|
.end
 |