/* * FreeRTOS Kernel * 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 * */ /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" /*-----------------------------------------------------------*/ /* Count of the critical section nesting depth. */ uint32_t ulCriticalNesting = 9999; /*-----------------------------------------------------------*/ /* Registers required to configure the RTI. */ #define portRTI_GCTRL_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC00 ) ) #define portRTI_TBCTRL_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC04 ) ) #define portRTI_COMPCTRL_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC0C ) ) #define portRTI_CNT0_FRC0_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC10 ) ) #define portRTI_CNT0_UC0_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC14 ) ) #define portRTI_CNT0_CPUC0_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC18 ) ) #define portRTI_CNT0_COMP0_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC50 ) ) #define portRTI_CNT0_UDCP0_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC54 ) ) #define portRTI_SETINTENA_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC80 ) ) #define portRTI_CLEARINTENA_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC84 ) ) #define portRTI_INTFLAG_REG ( * ( ( volatile uint32_t * ) 0xFFFFFC88 ) ) /* Constants required to set up the initial stack of each task. */ #define portINITIAL_SPSR ( ( StackType_t ) 0x1F ) #define portINITIAL_FPSCR ( ( StackType_t ) 0x00 ) #define portINSTRUCTION_SIZE ( ( StackType_t ) 0x04 ) #define portTHUMB_MODE_BIT ( ( StackType_t ) 0x20 ) /* The number of words on the stack frame between the saved Top Of Stack and R0 (in which the parameters are passed. */ #define portSPACE_BETWEEN_TOS_AND_PARAMETERS ( 12 ) /*-----------------------------------------------------------*/ /* vPortStartFirstSTask() is defined in portASM.asm */ extern void vPortStartFirstTask( void ); /*-----------------------------------------------------------*/ /* Saved as part of the task context. Set to pdFALSE if the task does not require an FPU context. */ uint32_t ulTaskHasFPUContext = 0; /*-----------------------------------------------------------*/ /* * See header file for description. */ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) { StackType_t *pxOriginalTOS; pxOriginalTOS = pxTopOfStack; #if __TI_VFP_SUPPORT__ { /* Ensure the stack is correctly aligned on exit. */ pxTopOfStack--; } #endif /* Setup the initial stack of the task. The stack is set exactly as expected by the portRESTORE_CONTEXT() macro. */ /* First on the stack is the return address - which is the start of the as the task has not executed yet. The offset is added to make the return address appear as it would within an IRQ ISR. */ *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE; pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x00000000; /* R14 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */ pxTopOfStack--; #ifdef portPRELOAD_TASK_REGISTERS { *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */ pxTopOfStack--; } #else { pxTopOfStack -= portSPACE_BETWEEN_TOS_AND_PARAMETERS; } #endif /* Function parameters are passed in R0. */ *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ pxTopOfStack--; /* Set the status register for system mode, with interrupts enabled. */ *pxTopOfStack = ( StackType_t ) ( ( _get_CPSR() & ~0xFF ) | portINITIAL_SPSR ); if( ( ( uint32_t ) pxCode & 0x01UL ) != 0x00 ) { /* The task will start in thumb mode. */ *pxTopOfStack |= portTHUMB_MODE_BIT; } #ifdef __TI_VFP_SUPPORT__ { pxTopOfStack--; /* The last thing on the stack is the tasks ulUsingFPU value, which by default is set to indicate that the stack frame does not include FPU registers. */ *pxTopOfStack = pdFALSE; } #endif return pxTopOfStack; } /*-----------------------------------------------------------*/ static void prvSetupTimerInterrupt(void) { /* Disable timer 0. */ portRTI_GCTRL_REG &= 0xFFFFFFFEUL; /* Use the internal counter. */ portRTI_TBCTRL_REG = 0x00000000U; /* COMPSEL0 will use the RTIFRC0 counter. */ portRTI_COMPCTRL_REG = 0x00000000U; /* Initialise the counter and the prescale counter registers. */ portRTI_CNT0_UC0_REG = 0x00000000U; portRTI_CNT0_FRC0_REG = 0x00000000U; /* Set Prescalar for RTI clock. */ portRTI_CNT0_CPUC0_REG = 0x00000001U; portRTI_CNT0_COMP0_REG = ( configCPU_CLOCK_HZ / 2 ) / configTICK_RATE_HZ; portRTI_CNT0_UDCP0_REG = ( configCPU_CLOCK_HZ / 2 ) / configTICK_RATE_HZ; /* Clear interrupts. */ portRTI_INTFLAG_REG = 0x0007000FU; portRTI_CLEARINTENA_REG = 0x00070F0FU; /* Enable the compare 0 interrupt. */ portRTI_SETINTENA_REG = 0x00000001U; portRTI_GCTRL_REG |= 0x00000001U; } /*-----------------------------------------------------------*/ /* * See header file for description. */ BaseType_t xPortStartScheduler(void) { /* Start the timer that generates the tick ISR. */ prvSetupTimerInterrupt(); /* Reset the critical section nesting count read to execute the first task. */ ulCriticalNesting = 0; /* Start the first task. This is done from portASM.asm as ARM mode must be used. */ vPortStartFirstTask(); /* Should not get here! */ return pdFAIL; } /*-----------------------------------------------------------*/ /* * See header file for description. */ void vPortEndScheduler(void) { /* Not implemented in ports where there is nothing to return to. Artificially force an assert. */ configASSERT( ulCriticalNesting == 1000UL ); } /*-----------------------------------------------------------*/ #if configUSE_PREEMPTION == 0 /* The cooperative scheduler requires a normal IRQ service routine to * simply increment the system tick. */ __interrupt void vPortNonPreemptiveTick( void ) { /* clear clock interrupt flag */ portRTI_INTFLAG_REG = 0x00000001; /* Increment the tick count - this may make a delaying task ready to run - but a context switch is not performed. */ xTaskIncrementTick(); } #else /* ************************************************************************** * The preemptive scheduler ISR is written in assembler and can be found * in the portASM.asm file. This will only get used if portUSE_PREEMPTION * is set to 1 in portmacro.h ************************************************************************** */ void vPortPreemptiveTick( void ); #endif /*-----------------------------------------------------------*/ /* * Disable interrupts, and keep a count of the nesting depth. */ void vPortEnterCritical( void ) { /* Disable interrupts as per portDISABLE_INTERRUPTS(); */ portDISABLE_INTERRUPTS(); /* Now interrupts are disabled ulCriticalNesting can be accessed directly. Increment ulCriticalNesting to keep a count of how many times portENTER_CRITICAL() has been called. */ ulCriticalNesting++; } /*-----------------------------------------------------------*/ /* * Decrement the critical nesting count, and if it has reached zero, re-enable * interrupts. */ void vPortExitCritical( void ) { if( ulCriticalNesting > 0 ) { /* Decrement the nesting count as we are leaving a critical section. */ ulCriticalNesting--; /* If the nesting level has reached zero then interrupts should be re-enabled. */ if( ulCriticalNesting == 0 ) { /* Enable interrupts as per portENABLE_INTERRUPTS(). */ portENABLE_INTERRUPTS(); } } } /*-----------------------------------------------------------*/ #if __TI_VFP_SUPPORT__ void vPortTaskUsesFPU( void ) { extern void vPortInitialiseFPSCR( void ); /* A task is registering the fact that it needs an FPU context. Set the FPU flag (saved as part of the task context. */ ulTaskHasFPUContext = pdTRUE; /* Initialise the floating point status register. */ vPortInitialiseFPSCR(); } #endif /* __TI_VFP_SUPPORT__ */ /*-----------------------------------------------------------*/