| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- /*
- * FreeRTOS Kernel V10.3.1
- * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * 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.
- *
- * http://www.FreeRTOS.org
- * http://aws.amazon.com/freertos
- *
- * 1 tab == 4 spaces!
- */
- /*-----------------------------------------------------------
- * Implementation of functions defined in portable.h for the Nuclei N/NX Processor port.
- *----------------------------------------------------------*/
- /* Scheduler includes. */
- #include <stdio.h>
- #include "FreeRTOS.h"
- #include "task.h"
- //#define ENABLE_KERNEL_DEBUG
- #ifdef ENABLE_KERNEL_DEBUG
- #define FREERTOS_PORT_DEBUG(...) printf(__VA_ARGS__)
- #else
- #define FREERTOS_PORT_DEBUG(...)
- #endif
- #ifndef configSYSTICK_CLOCK_HZ
- #define configSYSTICK_CLOCK_HZ SOC_TIMER_FREQ
- #endif
- #ifndef configKERNEL_INTERRUPT_PRIORITY
- #define configKERNEL_INTERRUPT_PRIORITY 0
- #endif
- #ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY
- #define configMAX_SYSCALL_INTERRUPT_PRIORITY 255
- #endif
- /* Constants required to check the validity of an interrupt priority. */
- #define portFIRST_USER_INTERRUPT_NUMBER ( 18 )
- #define SYSTICK_TICK_CONST (configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ)
- /* Constants required to set up the initial stack. */
- #define portINITIAL_MSTATUS ( MSTATUS_MPP | MSTATUS_MPIE | MSTATUS_FS_INITIAL | MSTATUS_VS_INITIAL)
- #define portINITIAL_EXC_RETURN ( 0xfffffffd )
- /* The systick is a 24-bit counter. */
- #define portMAX_BIT_NUMBER ( SysTimer_MTIME_Msk )
- /* A fiddle factor to estimate the number of SysTick counts that would have
- occurred while the SysTick counter is stopped during tickless idle
- calculations. */
- #define portMISSED_COUNTS_FACTOR ( 45UL )
- /* Let the user override the pre-loading of the initial LR with the address of
- prvTaskExitError() in case it messes up unwinding of the stack in the
- debugger. */
- #ifdef configTASK_RETURN_ADDRESS
- #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
- #else
- #define portTASK_RETURN_ADDRESS prvTaskExitError
- #endif
- /*
- * Setup the timer to generate the tick interrupts. The implementation in this
- * file is weak to allow application writers to change the timer used to
- * generate the tick interrupt.
- */
- void vPortSetupTimerInterrupt(void);
- /*
- * Exception handlers.
- */
- void xPortSysTickHandler(void);
- /*
- * Start first task is a separate function so it can be tested in isolation.
- */
- extern void prvPortStartFirstTask(void) __attribute__((naked));
- /*
- * Used to catch tasks that attempt to return from their implementing function.
- */
- static void prvTaskExitError(void);
- #define xPortSysTickHandler irqc_mtip_handler
- /*-----------------------------------------------------------*/
- /* Each task maintains its own interrupt status in the critical nesting
- variable. */
- static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
- /*
- * The number of SysTick increments that make up one tick period.
- */
- #if( configUSE_TICKLESS_IDLE == 1 )
- static TickType_t ulTimerCountsForOneTick = 0;
- #endif /* configUSE_TICKLESS_IDLE */
- /*
- * The maximum number of tick periods that can be suppressed is limited by the
- * 24 bit resolution of the SysTick timer.
- */
- #if( configUSE_TICKLESS_IDLE == 1 )
- static TickType_t xMaximumPossibleSuppressedTicks = 0;
- #endif /* configUSE_TICKLESS_IDLE */
- /*
- * Compensate for the CPU cycles that pass while the SysTick is stopped (low
- * power functionality only.
- */
- #if( configUSE_TICKLESS_IDLE == 1 )
- static TickType_t ulStoppedTimerCompensation = 0;
- #endif /* configUSE_TICKLESS_IDLE */
- /*
- * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure
- * FreeRTOS API functions are not called from interrupts that have been assigned
- * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY.
- */
- #if( configASSERT_DEFINED == 1 )
- #endif /* configASSERT_DEFINED */
- /*-----------------------------------------------------------*/
- /*
- * See header file for description.
- * As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in
- * a1, and pvParameters in a2. The new top of stack is passed out in a0.
- *
- * RISC-V maps registers to ABI names as follows (X1 to X31 integer registers
- * for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed).
- *
- * Register ABI Name Description Saver
- * x0 zero Hard-wired zero -
- * x1 ra Return address Caller
- * x2 sp Stack pointer Callee
- * x3 gp Global pointer -
- * x4 tp Thread pointer -
- * x5-7 t0-2 Temporaries Caller
- * x8 s0/fp Saved register/Frame pointer Callee
- * x9 s1 Saved register Callee
- * x10-11 a0-1 Function Arguments/return values Caller
- * x12-17 a2-7 Function arguments Caller
- * x18-27 s2-11 Saved registers Callee
- * x28-31 t3-6 Temporaries Caller
- *
- * The RISC-V context is saved RTOS tasks in the following stack frame,
- * where the global and thread pointers are currently assumed to be constant so
- * are not saved:
- *
- * mstatus
- * #ifndef __riscv_32e
- * rsv1
- * rsv0
- * x31
- * x30
- * x29
- * x28
- * x27
- * x26
- * x25
- * x24
- * x23
- * x22
- * x21
- * x20
- * x19
- * x18
- * x17
- * x16
- * #endif
- * x15
- * x14
- * x13
- * x12
- * x11
- * pvParameters
- * x9
- * x8
- * x7
- * x6
- * x5
- * portTASK_RETURN_ADDRESS
- * pxCode
- */
- StackType_t* pxPortInitialiseStack(StackType_t* pxTopOfStack, TaskFunction_t pxCode, void* pvParameters)
- {
- /* Simulate the stack frame as it would be created by a context switch
- interrupt. */
- /* Offset added to account for the way the MCU uses the stack on entry/exit
- of interrupts, and to ensure alignment. */
- pxTopOfStack--;
- *pxTopOfStack = portINITIAL_MSTATUS; /* MSTATUS */
- /* Save code space by skipping register initialisation. */
- #ifndef __riscv_32e
- pxTopOfStack -= 24; /* X11 - X31 + 2 reserved. */
- #else
- pxTopOfStack -= 6; /* X11 - X15. */
- #endif
- *pxTopOfStack = (StackType_t) pvParameters; /* X10/A0 */
- pxTopOfStack -= 6; /* X5 - X9 */
- *pxTopOfStack = (StackType_t) portTASK_RETURN_ADDRESS; /* RA, X1 */
- pxTopOfStack --;
- *pxTopOfStack = ((StackType_t) pxCode) ; /* PC */
- return pxTopOfStack;
- }
- /*-----------------------------------------------------------*/
- static void prvTaskExitError(void)
- {
- volatile uint32_t ulDummy = 0;
- /* A function that implements a task must not exit or attempt to return to
- its caller as there is nothing to return to. If a task wants to exit it
- should instead call vTaskDelete( NULL ).
- Artificially force an assert() to be triggered if configASSERT() is
- defined, then stop here so application writers can catch the error. */
- configASSERT(uxCriticalNesting == ~0UL);
- portDISABLE_INTERRUPTS();
- while (ulDummy == 0) {
- /* This file calls prvTaskExitError() after the scheduler has been
- started to remove a compiler warning about the function being defined
- but never called. ulDummy is used purely to quieten other warnings
- about code appearing after this function is called - making ulDummy
- volatile makes the compiler think the function could return and
- therefore not output an 'unreachable code' warning for code that appears
- after it. */
- /* Sleep and wait for interrupt */
- __WFI();
- }
- }
- /*-----------------------------------------------------------*/
- /*
- * See header file for description.
- */
- BaseType_t xPortStartScheduler(void)
- {
- __disable_irq();
- /* Start the timer that generates the tick ISR. Interrupts are disabled
- here already. */
- vPortSetupTimerInterrupt();
- /* Initialise the critical nesting count ready for the first task. */
- uxCriticalNesting = 0;
- /* Start the first task. */
- prvPortStartFirstTask();
- /* Should never get here as the tasks will now be executing! Call the task
- exit error function to prevent compiler warnings about a static function
- not being called in the case that the application writer overrides this
- functionality by defining configTASK_RETURN_ADDRESS. Call
- vTaskSwitchContext() so link time optimisation does not remove the
- symbol. */
- vTaskSwitchContext();
- prvTaskExitError();
- /* Should not get here! */
- return 0;
- }
- /*-----------------------------------------------------------*/
- void vPortEndScheduler(void)
- {
- /* Not implemented in ports where there is nothing to return to.
- Artificially force an assert. */
- configASSERT(uxCriticalNesting == 1000UL);
- }
- /*-----------------------------------------------------------*/
- void vPortEnterCritical(void)
- {
- portDISABLE_INTERRUPTS();
- uxCriticalNesting++;
- /* This is not the interrupt safe version of the enter critical function so
- assert() if it is being called from an interrupt context. Only API
- functions that end in "FromISR" can be used in an interrupt. Only assert if
- the critical nesting count is 1 to protect against recursive calls if the
- assert function also uses a critical section. */
- if (uxCriticalNesting == 1) {
- }
- }
- /*-----------------------------------------------------------*/
- void vPortExitCritical(void)
- {
- configASSERT(uxCriticalNesting);
- uxCriticalNesting--;
- if (uxCriticalNesting == 0) {
- portENABLE_INTERRUPTS();
- }
- }
- /*-----------------------------------------------------------*/
- void vPortAssert(int32_t x)
- {
- TaskHandle_t th;
- if ((x) == 0) {
- taskDISABLE_INTERRUPTS();
- #if (INCLUDE_xTaskGetCurrentTaskHandle == 1)
- th = xTaskGetCurrentTaskHandle();
- if (th) {
- printf("Assert in task %s\n", pcTaskGetName(th));
- }
- #endif
- while (1) {
- /* Sleep and wait for interrupt */
- __WFI();
- };
- }
- }
- /*-----------------------------------------------------------*/
- void xPortTaskSwitch(void)
- {
- portDISABLE_INTERRUPTS();
- /* Clear Software IRQ, A MUST */
- SysTimer_ClearSWIRQ();
- vTaskSwitchContext();
- portENABLE_INTERRUPTS();
- }
- /*-----------------------------------------------------------*/
- __INTERRUPT void xPortSysTickHandler(void)
- {
- /* The SysTick runs at the lowest interrupt priority, so when this interrupt
- executes all interrupts must be unmasked. There is therefore no need to
- save and then restore the interrupt mask value as its value is already
- known. */
- portDISABLE_INTERRUPTS();
- {
- SysTick_Reload(SYSTICK_TICK_CONST);
- /* Increment the RTOS tick. */
- if (xTaskIncrementTick() != pdFALSE) {
- /* A context switch is required. Context switching is performed in
- the SWI interrupt. Pend the SWI interrupt. */
- portYIELD();
- }
- }
- portENABLE_INTERRUPTS();
- }
- /*-----------------------------------------------------------*/
- #if( configUSE_TICKLESS_IDLE == 1 )
- __attribute__((weak)) void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
- {
- uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;
- volatile TickType_t xModifiableIdleTime, xTickCountBeforeSleep, XLastLoadValue;
- FREERTOS_PORT_DEBUG("Enter TickLess %d\n", (uint32_t)xExpectedIdleTime);
- /* Make sure the SysTick reload value does not overflow the counter. */
- if (xExpectedIdleTime > xMaximumPossibleSuppressedTicks) {
- xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
- }
- /* Stop the SysTick momentarily. The time the SysTick is stopped for
- is accounted for as best it can be, but using the tickless mode will
- inevitably result in some tiny drift of the time maintained by the
- kernel with respect to calendar time. */
- SysTimer_Stop();
- /* Calculate the reload value required to wait xExpectedIdleTime
- tick periods. -1 is used because this code will execute part way
- through one of the tick periods. */
- ulReloadValue = (ulTimerCountsForOneTick * (xExpectedIdleTime - 1UL));
- if (ulReloadValue > ulStoppedTimerCompensation) {
- ulReloadValue -= ulStoppedTimerCompensation;
- }
- /* Enter a critical section but don't use the taskENTER_CRITICAL()
- method as that will mask interrupts that should exit sleep mode. */
- __disable_irq();
- /* If a context switch is pending or a task is waiting for the scheduler
- to be unsuspended then abandon the low power entry. */
- if (eTaskConfirmSleepModeStatus() == eAbortSleep) {
- /* Restart from whatever is left in the count register to complete
- this tick period. */
- /* Restart SysTick. */
- SysTimer_Start();
- /* Reset the reload register to the value required for normal tick
- periods. */
- SysTick_Reload(ulTimerCountsForOneTick);
- /* Re-enable interrupts - see comments above the cpsid instruction()
- above. */
- __enable_irq();
- } else {
- xTickCountBeforeSleep = xTaskGetTickCount();
- /* Set the new reload value. */
- SysTick_Reload(ulReloadValue);
- /* Get System timer load value before sleep */
- XLastLoadValue = SysTimer_GetLoadValue();
- /* Restart SysTick. */
- SysTimer_Start();
- IRQC_EnableIRQ(SysTimer_IRQn);
- __RWMB();
- /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can
- set its parameter to 0 to indicate that its implementation contains
- its own wait for interrupt or wait for event instruction, and so wfi
- should not be executed again. However, the original expected idle
- time variable must remain unmodified, so a copy is taken. */
- xModifiableIdleTime = xExpectedIdleTime;
- configPRE_SLEEP_PROCESSING(xModifiableIdleTime);
- if (xModifiableIdleTime > 0) {
- __WFI();
- }
- configPOST_SLEEP_PROCESSING(xExpectedIdleTime);
- /* Re-enable interrupts to allow the interrupt that brought the MCU
- out of sleep mode to execute immediately. */
- __enable_irq();
- /* Make sure interrupt enable is executed */
- __RWMB();
- __FENCE_I();
- __NOP();
- /* Disable interrupts again because the clock is about to be stopped
- and interrupts that execute while the clock is stopped will increase
- any slippage between the time maintained by the RTOS and calendar
- time. */
- __disable_irq();
- /* Disable the SysTick clock. Again,
- the time the SysTick is stopped for is accounted for as best it can
- be, but using the tickless mode will inevitably result in some tiny
- drift of the time maintained by the kernel with respect to calendar
- time*/
- IRQC_DisableIRQ(SysTimer_IRQn);
- /* Determine if SysTimer Interrupt is not yet happened,
- (in which case an interrupt other than the SysTick
- must have brought the system out of sleep mode). */
- if (SysTimer_GetLoadValue() >= (XLastLoadValue + ulReloadValue)) {
- /* As the pending tick will be processed as soon as this
- function exits, the tick value maintained by the tick is stepped
- forward by one less than the time spent waiting. */
- ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
- FREERTOS_PORT_DEBUG("TickLess - SysTimer Interrupt Entered!\n");
- } else {
- /* Something other than the tick interrupt ended the sleep.
- Work out how long the sleep lasted rounded to complete tick
- periods (not the ulReload value which accounted for part
- ticks). */
- xModifiableIdleTime = SysTimer_GetLoadValue();
- if (xModifiableIdleTime > XLastLoadValue) {
- ulCompletedSysTickDecrements = (xModifiableIdleTime - XLastLoadValue);
- } else {
- ulCompletedSysTickDecrements = (xModifiableIdleTime + portMAX_BIT_NUMBER - XLastLoadValue);
- }
- /* How many complete tick periods passed while the processor
- was waiting? */
- ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
- /* The reload value is set to whatever fraction of a single tick
- period remains. */
- SysTick_Reload(ulTimerCountsForOneTick);
- FREERTOS_PORT_DEBUG("TickLess - External Interrupt Happened!\n");
- }
- FREERTOS_PORT_DEBUG("End TickLess %d\n", (uint32_t)ulCompleteTickPeriods);
- /* Restart SysTick */
- vTaskStepTick(ulCompleteTickPeriods);
- /* Exit with interrupts enabled. */
- IRQC_EnableIRQ(SysTimer_IRQn);
- __enable_irq();
- }
- }
- #endif /* #if configUSE_TICKLESS_IDLE */
- /*-----------------------------------------------------------*/
- /*
- * Setup the systick timer to generate the tick interrupts at the required
- * frequency.
- */
- __attribute__((weak)) void vPortSetupTimerInterrupt(void)
- {
- /* Calculate the constants required to configure the tick interrupt. */
- #if( configUSE_TICKLESS_IDLE == 1 )
- {
- ulTimerCountsForOneTick = (SYSTICK_TICK_CONST);
- xMaximumPossibleSuppressedTicks = portMAX_BIT_NUMBER / ulTimerCountsForOneTick;
- ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / (configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ);
- FREERTOS_PORT_DEBUG("CountsForOneTick, SuppressedTicks and TimerCompensation: %u, %u, %u\n", \
- (uint32_t)ulTimerCountsForOneTick, (uint32_t)xMaximumPossibleSuppressedTicks, (uint32_t)ulStoppedTimerCompensation);
- }
- #endif /* configUSE_TICKLESS_IDLE */
- TickType_t ticks = SYSTICK_TICK_CONST;
- /* Make SWI and SysTick the lowest priority interrupts. */
- /* Stop and clear the SysTimer. SysTimer as Vector Interrupt */
- SysTick_Config(ticks);
- IRQC_EnableIRQ(SysTimer_IRQn);
- /* Set as Vector Interrupt */
- IRQC_EnableIRQ(SysTimerSW_IRQn);
- }
- /*-----------------------------------------------------------*/
- /*-----------------------------------------------------------*/
- #if( configASSERT_DEFINED == 1 )
- void vPortValidateInterruptPriority(void)
- {
- }
- #endif /* configASSERT_DEFINED */
|