diff --git a/.github/workflows/kernel-demos.yml b/.github/workflows/kernel-demos.yml index 7d83d748211..bae32c96625 100644 --- a/.github/workflows/kernel-demos.yml +++ b/.github/workflows/kernel-demos.yml @@ -111,6 +111,13 @@ jobs: cmake -S . -B build cmake --build build + - name: Build CMake SMP Example Demo + shell: bash + working-directory: examples/cmake_example + run: | + cmake -S . -B build -DFREERTOS_SMP_EXAMPLE=1 + cmake --build build + MSP430-GCC: name: GNU MSP430 Toolchain runs-on: ubuntu-latest diff --git a/examples/cmake_example/CMakeLists.txt b/examples/cmake_example/CMakeLists.txt index 85329678de7..f8a4e2d6340 100644 --- a/examples/cmake_example/CMakeLists.txt +++ b/examples/cmake_example/CMakeLists.txt @@ -7,10 +7,19 @@ set(FREERTOS_KERNEL_PATH "../../") # Add the freertos_config for FreeRTOS-Kernel add_library(freertos_config INTERFACE) -target_include_directories(freertos_config - INTERFACE - ../sample_configuration -) +if (DEFINED FREERTOS_SMP_EXAMPLE AND FREERTOS_SMP_EXAMPLE STREQUAL "1") + message(STATUS "Build FreeRTOS SMP example") + target_include_directories(freertos_config + INTERFACE + "../sample_configuration/smp" + ) +else() + message(STATUS "Build FreeRTOS example") + target_include_directories(freertos_config + INTERFACE + "../sample_configuration" + ) +endif() # Select the heap port. values between 1-4 will pick a heap. set(FREERTOS_HEAP "4" CACHE STRING "" FORCE) diff --git a/examples/sample_configuration/smp/FreeRTOSConfig.h b/examples/sample_configuration/smp/FreeRTOSConfig.h new file mode 100644 index 00000000000..f3b68a444c1 --- /dev/null +++ b/examples/sample_configuration/smp/FreeRTOSConfig.h @@ -0,0 +1,65 @@ +/* + * 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 + * + */ + +/******************************************************************************* + * This file provides an example FreeRTOSConfig.h header file, inclusive of an + * abbreviated explanation of each configuration item. Online and reference + * documentation provides more information. + * https://www.freertos.org/a00110.html + * + * Constant values enclosed in square brackets ('[' and ']') must be completed + * before this file will build. + * + * Use the FreeRTOSConfig.h supplied with the RTOS port in use rather than this + * generic file, if one is available. + ******************************************************************************/ + +#ifndef __FREERTOS_CONFIG_SMP_H__ +#define __FREERTOS_CONFIG_SMP_H__ + +#include "../FreeRTOSConfig.h" + +/******************************************************************************/ +/* Scheduling behaviour related definitions. **********************************/ +/******************************************************************************/ + +/* Set configNUMBER_OF_CORES to greater than 1 to enable running one instance of + * FreeRTOS kernel to schedule tasks across multiple identical processor cores. */ +#define configNUMBER_OF_CORES 2 + +/******************************************************************************/ +/* Hook and callback function related definitions. ****************************/ +/******************************************************************************/ + +/* Set the following configUSE_* constants to 1 to include the named hook + * functionality in the build. Set to 0 to exclude the hook functionality from the + * build. The application writer is responsible for providing the hook function + * for any set to 1. See https://www.freertos.org/a00016.html */ +#define configUSE_PASSIVE_IDLE_HOOK 0 + +#endif /* __FREERTOS_CONFIG_SMP_H__ */ diff --git a/examples/sample_configuration/smp/readme.md b/examples/sample_configuration/smp/readme.md new file mode 100644 index 00000000000..8dc02bd562a --- /dev/null +++ b/examples/sample_configuration/smp/readme.md @@ -0,0 +1,10 @@ +# Configuration support for FreeRTOS SMP + +## Overview +The FreeRTOSConfig.h provided in this folder is a sample configuration that will +assist you in preparing the configuration to enable SMP support in the FreeRTOS +Kernel for your application. + +Based on single core sample configuration file, this configuration file is created +with minimal configuration change. More SMP scheduler configurations can be found +in [Symmetric Multiprocessing (SMP) with FreeRTOS](https://freertos.org/symmetric-multiprocessing-introduction.html) diff --git a/portable/template/port.c b/portable/template/port.c index 4011eac154e..d4eb56eacb6 100644 --- a/portable/template/port.c +++ b/portable/template/port.c @@ -25,8 +25,18 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, void vPortYield( void ) { /* Save the current Context */ + /* Switch to the highest priority task that is ready to run. */ - vTaskSwitchContext(); + #if ( configNUMBER_OF_CORES == 1 ) + { + vTaskSwitchContext(); + } + #else + { + vTaskSwitchContext( portGET_CORE_ID() ); + } + #endif + /* Start executing the task we have just switched to. */ } @@ -35,12 +45,34 @@ static void prvTickISR( void ) /* Interrupts must have been enabled for the ISR to fire, so we have to * save the context with interrupts enabled. */ - /* Maintain the tick count. */ - if( xTaskIncrementTick() != pdFALSE ) + #if ( configNUMBER_OF_CORES == 1 ) { - /* Switch to the highest priority task that is ready to run. */ - vTaskSwitchContext(); + /* Maintain the tick count. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* Switch to the highest priority task that is ready to run. */ + vTaskSwitchContext(); + } + } + #else + { + UBaseType_t ulPreviousMask; + + /* Tasks or ISRs running on other cores may still in critical section in + * multiple cores environment. Incrementing tick needs to performed in + * critical section. */ + ulPreviousMask = taskENTER_CRITICAL_FROM_ISR(); + + /* Maintain the tick count. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* Switch to the highest priority task that is ready to run. */ + vTaskSwitchContext( portGET_CORE_ID() ); + } + + taskEXIT_CRITICAL_FROM_ISR( ulPreviousMask ); } + #endif /* if ( configNUMBER_OF_CORES == 1 ) */ /* start executing the new task */ } diff --git a/portable/template/portmacro.h b/portable/template/portmacro.h index 4e4aa934d43..1990532081c 100644 --- a/portable/template/portmacro.h +++ b/portable/template/portmacro.h @@ -64,26 +64,42 @@ typedef unsigned char UBaseType_t; /*-----------------------------------------------------------*/ #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) \ - { \ + do { \ uxTopPriority = 0; \ - } \ - while( 0 ) + } while( 0 ) #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ -#define portDISABLE_INTERRUPTS() \ - { /* Disable the interrupts */ \ - } -#define portENABLE_INTERRUPTS() \ - { /* Enable the interrupts */ \ - } +/* Disable the interrupts */ +#define portDISABLE_INTERRUPTS() do {} while( 0 ) -#define portENTER_CRITICAL() \ - { /* preserve current interrupt state and then disable interrupts */ \ - } -#define portEXIT_CRITICAL() \ - { /* restore previously preserved interrupt state */ \ - } +/* Enable the interrupts */ +#define portENABLE_INTERRUPTS() do {} while( 0 ) + +#if ( configNUMBER_OF_CORES == 1 ) +/* preserve current interrupt state and then disable interrupts */ + #define portENTER_CRITICAL() do {} while( 0 ) + +/* restore previously preserved interrupt state */ + #define portEXIT_CRITICAL() do {} while( 0 ) +#else + +/* The port can maintain the critical nesting count in TCB or maintain the critical + * nesting count in the port. */ + #define portCRITICAL_NESTING_IN_TCB 1 + +/* vTaskEnterCritical and vTaskExitCritical should be used in the implementation + * of portENTER/EXIT_CRITICAL if the number of cores is more than 1 in the system. */ + #define portENTER_CRITICAL vTaskEnterCritical + #define portEXIT_CRITICAL vTaskExitCritical + +/* vTaskEnterCriticalFromISR and vTaskExitCriticalFromISR should be used in the + * implementation of portENTER/EXIT_CRITICAL_FROM_ISR if the number of cores is + * more than 1 in the system. */ + #define portENTER_CRITICAL_FROM_ISR vTaskEnterCriticalFromISR + #define portEXIT_CRITICAL_FROM_ISR vTaskExitCriticalFromISR + +#endif /* if ( configNUMBER_OF_CORES == 1 ) */ extern void vPortYield( void ); #define portYIELD() vPortYield() @@ -92,4 +108,35 @@ extern void vPortYield( void ); #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters ) #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters ) +#if ( configNUMBER_OF_CORES > 1 ) + /* Return the core ID on which the code is running. */ + #define portGET_CORE_ID() 0 + +/* Set the interrupt mask. */ + #define portSET_INTERRUPT_MASK() 0 + +/* Clear the interrupt mask. */ + #define portCLEAR_INTERRUPT_MASK( x ) ( ( void ) ( x ) ) + +/* Request the core ID x to yield. */ + #define portYIELD_CORE( x ) do {} while( 0 ) + +/* Acquire the TASK lock. TASK lock is a recursive lock. + * It should be able to be locked by the same core multiple times. */ + #define portGET_TASK_LOCK() do {} while( 0 ) + +/* Release the TASK lock. If a TASK lock is locked by the same core multiple times, + * it should be released as many times as it is locked. */ + #define portRELEASE_TASK_LOCK() do {} while( 0 ) + +/* Acquire the ISR lock. ISR lock is a recursive lock. + * It should be able to be locked by the same core multiple times. */ + #define portGET_ISR_LOCK() do {} while( 0 ) + +/* Release the ISR lock. If a ISR lock is locked by the same core multiple times, \ + * it should be released as many times as it is locked. */ + #define portRELEASE_ISR_LOCK() do {} while( 0 ) + +#endif /* if ( configNUMBER_OF_CORES > 1 ) */ + #endif /* PORTMACRO_H */