mirror of
				https://github.com/FreeRTOS/FreeRTOS-Kernel.git
				synced 2025-11-04 11:09:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
 | 
						|
 * Copyright (C) 2021 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
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
.file "portASM.S"
 | 
						|
#include "FreeRTOSConfig.h"
 | 
						|
#include "ISR_Support.h"
 | 
						|
 | 
						|
    .extern pxCurrentTCB
 | 
						|
    .extern vTaskSwitchContext
 | 
						|
    .extern vPortCentralInterruptHandler
 | 
						|
    .extern xTaskIncrementTick
 | 
						|
    .extern vPortAPICErrorHandler
 | 
						|
    .extern pucPortTaskFPUContextBuffer
 | 
						|
    .extern ulPortYieldPending
 | 
						|
 | 
						|
    .global vPortStartFirstTask
 | 
						|
    .global vPortCentralInterruptWrapper
 | 
						|
    .global vPortAPICErrorHandlerWrapper
 | 
						|
    .global vPortTimerHandler
 | 
						|
    .global vPortYieldCall
 | 
						|
    .global vPortAPICSpuriousHandler
 | 
						|
 | 
						|
    .text
 | 
						|
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.align 4
 | 
						|
.func vPortYieldCall
 | 
						|
vPortYieldCall:
 | 
						|
    /* Save general purpose registers. */
 | 
						|
    pusha
 | 
						|
 | 
						|
    .if configSUPPORT_FPU == 1
 | 
						|
 | 
						|
        /* If the task has a buffer allocated to save the FPU context then save
 | 
						|
        the FPU context now. */
 | 
						|
        movl    pucPortTaskFPUContextBuffer, %eax
 | 
						|
        test    %eax, %eax
 | 
						|
        je      1f
 | 
						|
        fnsave  ( %eax )
 | 
						|
        fwait
 | 
						|
 | 
						|
        1:
 | 
						|
 | 
						|
        /* Save the address of the FPU context, if any. */
 | 
						|
        push    pucPortTaskFPUContextBuffer
 | 
						|
 | 
						|
    .endif /* configSUPPORT_FPU */
 | 
						|
 | 
						|
    /* Find the TCB. */
 | 
						|
    movl    pxCurrentTCB, %eax
 | 
						|
 | 
						|
    /* Stack location is first item in the TCB. */
 | 
						|
    movl    %esp, (%eax)
 | 
						|
 | 
						|
    call vTaskSwitchContext
 | 
						|
 | 
						|
    /* Find the location of pxCurrentTCB again - a callee saved register could
 | 
						|
    be used in place of eax to prevent this second load, but that then relies
 | 
						|
    on the compiler and other asm code. */
 | 
						|
    movl    pxCurrentTCB, %eax
 | 
						|
    movl    (%eax), %esp
 | 
						|
 | 
						|
    .if configSUPPORT_FPU == 1
 | 
						|
 | 
						|
        /* Restore address of task's FPU context buffer. */
 | 
						|
        pop     pucPortTaskFPUContextBuffer
 | 
						|
 | 
						|
        /* If the task has a buffer allocated in which its FPU context is saved,
 | 
						|
        then restore it now. */
 | 
						|
        movl    pucPortTaskFPUContextBuffer, %eax
 | 
						|
        test    %eax, %eax
 | 
						|
        je      1f
 | 
						|
        frstor  ( %eax )
 | 
						|
        1:
 | 
						|
    .endif
 | 
						|
 | 
						|
    popa
 | 
						|
    iret
 | 
						|
 | 
						|
.endfunc
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.align 4
 | 
						|
.func vPortStartFirstTask
 | 
						|
vPortStartFirstTask:
 | 
						|
 | 
						|
    /* Find the TCB. */
 | 
						|
    movl    pxCurrentTCB, %eax
 | 
						|
 | 
						|
    /* Stack location is first item in the TCB. */
 | 
						|
    movl    (%eax), %esp
 | 
						|
 | 
						|
    /* Restore FPU context flag. */
 | 
						|
    .if configSUPPORT_FPU == 1
 | 
						|
 | 
						|
        pop     pucPortTaskFPUContextBuffer
 | 
						|
 | 
						|
    .endif /* configSUPPORT_FPU */
 | 
						|
 | 
						|
    /* Restore general purpose registers. */
 | 
						|
    popa
 | 
						|
    iret
 | 
						|
.endfunc
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.align 4
 | 
						|
.func vPortAPICErrorHandlerWrapper
 | 
						|
vPortAPICErrorHandlerWrapper:
 | 
						|
    pusha
 | 
						|
    call    vPortAPICErrorHandler
 | 
						|
    popa
 | 
						|
    /* EOI. */
 | 
						|
    movl    $0x00, (0xFEE000B0)
 | 
						|
    iret
 | 
						|
.endfunc
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.align 4
 | 
						|
.func vPortTimerHandler
 | 
						|
vPortTimerHandler:
 | 
						|
 | 
						|
    /* Save general purpose registers. */
 | 
						|
    pusha
 | 
						|
 | 
						|
    /* Interrupts are not nested, so save the rest of the task context. */
 | 
						|
    .if configSUPPORT_FPU == 1
 | 
						|
 | 
						|
        /* If the task has a buffer allocated to save the FPU context then save the
 | 
						|
        FPU context now. */
 | 
						|
        movl    pucPortTaskFPUContextBuffer, %eax
 | 
						|
        test    %eax, %eax
 | 
						|
        je      1f
 | 
						|
        fnsave  ( %eax ) /* Save FLOP context into ucTempFPUBuffer array. */
 | 
						|
        fwait
 | 
						|
 | 
						|
        1:
 | 
						|
        /* Save the address of the FPU context, if any. */
 | 
						|
        push    pucPortTaskFPUContextBuffer
 | 
						|
 | 
						|
    .endif /* configSUPPORT_FPU */
 | 
						|
 | 
						|
    /* Find the TCB. */
 | 
						|
    movl    pxCurrentTCB, %eax
 | 
						|
 | 
						|
    /* Stack location is first item in the TCB. */
 | 
						|
    movl    %esp, (%eax)
 | 
						|
 | 
						|
    /* Switch stacks. */
 | 
						|
    movl    ulTopOfSystemStack, %esp
 | 
						|
    movl    %esp, %ebp
 | 
						|
 | 
						|
    /* Increment nesting count. */
 | 
						|
    add     $1, ulInterruptNesting
 | 
						|
 | 
						|
    call    xTaskIncrementTick
 | 
						|
 | 
						|
    sti
 | 
						|
 | 
						|
    /* Is a switch to another task required? */
 | 
						|
    test    %eax, %eax
 | 
						|
    je      _skip_context_switch
 | 
						|
    cli
 | 
						|
    call    vTaskSwitchContext
 | 
						|
 | 
						|
_skip_context_switch:
 | 
						|
    cli
 | 
						|
 | 
						|
    /* Decrement the variable used to determine if a switch to a system
 | 
						|
    stack is necessary. */
 | 
						|
    sub     $1, ulInterruptNesting
 | 
						|
 | 
						|
    /* Stack location is first item in the TCB. */
 | 
						|
    movl    pxCurrentTCB, %eax
 | 
						|
    movl    (%eax), %esp
 | 
						|
 | 
						|
    .if configSUPPORT_FPU == 1
 | 
						|
 | 
						|
        /* Restore address of task's FPU context buffer. */
 | 
						|
        pop     pucPortTaskFPUContextBuffer
 | 
						|
 | 
						|
        /* If the task has a buffer allocated in which its FPU context is saved,
 | 
						|
        then restore it now. */
 | 
						|
        movl    pucPortTaskFPUContextBuffer, %eax
 | 
						|
        test    %eax, %eax
 | 
						|
        je      1f
 | 
						|
        frstor  ( %eax )
 | 
						|
        1:
 | 
						|
    .endif
 | 
						|
 | 
						|
    popa
 | 
						|
 | 
						|
    /* EOI. */
 | 
						|
    movl    $0x00, (0xFEE000B0)
 | 
						|
    iret
 | 
						|
 | 
						|
.endfunc
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.if configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1
 | 
						|
 | 
						|
    .align 4
 | 
						|
    .func vPortCentralInterruptWrapper
 | 
						|
    vPortCentralInterruptWrapper:
 | 
						|
 | 
						|
        portFREERTOS_INTERRUPT_ENTRY
 | 
						|
 | 
						|
        movl $0xFEE00170, %eax          /* Highest In Service Register (ISR) long word. */
 | 
						|
        movl $8, %ecx                   /* Loop counter. */
 | 
						|
 | 
						|
    next_isr_long_word:
 | 
						|
        test %ecx, %ecx                 /* Loop counter reached 0? */
 | 
						|
        je wrapper_epilogue             /* Looked at all ISR registers without finding a bit set. */
 | 
						|
        sub $1, %ecx                    /* Sub 1 from loop counter. */
 | 
						|
        movl (%eax), %ebx               /* Load next ISR long word. */
 | 
						|
        sub $0x10, %eax                 /* Point to next ISR long word in case no bits are set in the current long word. */
 | 
						|
        test %ebx, %ebx                 /* Are there any bits set? */
 | 
						|
        je next_isr_long_word           /* Look at next ISR long word if no bits were set. */
 | 
						|
        sti
 | 
						|
        bsr %ebx, %ebx                  /* A bit was set, which one? */
 | 
						|
        movl $32, %eax                  /* Destination operand for following multiplication. */
 | 
						|
        mul %ecx                        /* Calculate base vector for current register, 32 vectors per register. */
 | 
						|
        add %ebx, %eax                  /* Add bit offset into register to get final vector number. */
 | 
						|
        push %eax                       /* Vector number is function parameter. */
 | 
						|
        call vPortCentralInterruptHandler
 | 
						|
        pop %eax                        /* Remove parameter. */
 | 
						|
 | 
						|
    wrapper_epilogue:
 | 
						|
        portFREERTOS_INTERRUPT_EXIT
 | 
						|
 | 
						|
    .endfunc
 | 
						|
 | 
						|
.endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
.align 4
 | 
						|
.func vPortAPISpuriousHandler
 | 
						|
vPortAPICSpuriousHandler:
 | 
						|
    iret
 | 
						|
 | 
						|
.endfunc
 | 
						|
 | 
						|
.end
 |