From d3d9a8bc280900ffac9b61a3a1bc8532fa9aa8c6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 26 Oct 2016 21:09:55 +0800 Subject: [PATCH 1/5] Most code written. Interrupts still do not fire yet. --- components/esp32/crosscore_int.c | 107 +++++++++++++ components/esp32/include/esp_crosscore_int.h | 8 + .../freertos/include/freertos/portable.h | 8 + components/freertos/port.c | 8 + components/freertos/tasks.c | 147 ++++++++++++------ 5 files changed, 233 insertions(+), 45 deletions(-) create mode 100644 components/esp32/crosscore_int.c create mode 100644 components/esp32/include/esp_crosscore_int.h diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c new file mode 100644 index 0000000000..95f2762f23 --- /dev/null +++ b/components/esp32/crosscore_int.c @@ -0,0 +1,107 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr.h" + +#include "rom/ets_sys.h" +#include "rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + + +#define REASON_YIELD (1<<0) + +static portMUX_TYPE reasonSpinlock = portMUX_INITIALIZER_UNLOCKED; +static volatile uint32_t reason[ portNUM_PROCESSORS ]; + + +/* +ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but +the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. +*/ +static void esp_crosscore_isr(void *arg) { + volatile uint32_t myReasonVal; +#if 0 + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *myReason=arg; +#else + //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... + volatile uint32_t *myReason=&reason[xPortGetCoreID()]; +#endif + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 1); + } + //Grab the reason and clear it. + portENTER_CRITICAL(&reasonSpinlock); + myReasonVal=*myReason; + *myReason=0; + portEXIT_CRITICAL(&reasonSpinlock); + + //Check what we need to do. + if (myReasonVal&REASON_YIELD) { + portYIELD_FROM_ISR(); + } + + ets_printf("recv yield\n"); +} + +//Initialize the crosscore interrupt on this core. Call this once +//on each active core. +void esp_crosscore_int_init() { + portENTER_CRITICAL(&reasonSpinlock); + reason[xPortGetCoreID()]=0; + portEXIT_CRITICAL(&reasonSpinlock); + ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); + if (xPortGetCoreID()==0) { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + } else { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + } + xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); + ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); + +} + +void esp_crosscore_int_send_yield(int coreId) { + ets_printf("send yield\n"); + assert(coreIduxPriority ] ), &( ( pxTCB )->xGenericListItem ) ) /*-----------------------------------------------------------*/ + +#define tskCAN_RUN_HERE( cpuid ) ( cpuid==xPortGetCoreID() || cpuid==tskNO_AFFINITY ) + /* * Several functions take an TaskHandle_t parameter that can optionally be NULL, * where NULL is used to indicate that the handle of the currently executing @@ -581,6 +584,35 @@ static void prvResetNextTaskUnblockTime( void ); /*-----------------------------------------------------------*/ +/* + * This routine tries to send an interrupt to another core if needed to make it execute a task + * of higher priority. We try to figure out if needed first by inspecting the pxTCB of the + * other CPU first. Specifically for Xtensa, we can do this because pxTCB is an atomic pointer. It + * is possible that it is inaccurate because the other CPU just did a task switch, but in that case + * at most a superfluous interrupt is generated. +*/ +static void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) +{ + BaseType_t i; + if (xCoreID != tskNO_AFFINITY) { + if ( pxCurrentTCB[ xCoreID ]->uxPriority < uxPriority ) { + vPortYieldOtherCore( xCoreID ); + } + } + else + { + /* The task has no affinity. See if we can find a CPU to put it on.*/ + for (i=0; iuxPriority < uxPriority) + { + vPortYieldOtherCore( i ); + break; + } + } + } +} + + BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { BaseType_t xReturn; @@ -753,7 +785,10 @@ BaseType_t i; the other processor will keep running the task it's working on, and only switch to the newer task on a timer interrupt. */ //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) + if( xCoreID != xPortGetCoreID() ) { + taskYIELD_OTHER_CORE(xCoreID, uxPriority); + } + else if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } @@ -834,7 +869,7 @@ BaseType_t i; after which it is not possible to yield away from this task - hence xYieldPending is used to latch that a context switch is required. */ - portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portYIELD_WITHIN_API(); } else @@ -1188,10 +1223,14 @@ BaseType_t i; /* The priority of a task other than the currently running task is being raised. Is the priority being raised above that of the running task? */ - if( uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, uxNewPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1470,13 +1509,17 @@ BaseType_t i; prvAddTaskToReadyList( pxTCB ); /* We may have just resumed a higher priority task. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* This yield may not cause the task just resumed to run, but will leave the lists in the correct state for the next yield. */ taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex); } + else if( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1521,7 +1564,14 @@ BaseType_t i; { /* Ready lists can be accessed so move the task from the suspended list to the ready list directly. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + if ( pxTCB->xCoreID == xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); + } + else if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } @@ -1529,9 +1579,6 @@ BaseType_t i; { mtCOVERAGE_TEST_MARKER(); } - - ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); - prvAddTaskToReadyList( pxTCB ); } else { @@ -1732,11 +1779,16 @@ BaseType_t xAlreadyYielded = pdFALSE; /* If the moved task has a priority higher than the current task then a yield must be performed. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - xYieldPending = pdTRUE; + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; break; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1753,7 +1805,7 @@ BaseType_t xAlreadyYielded = pdFALSE; { if( xTaskIncrementTick() != pdFALSE ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { @@ -1767,7 +1819,7 @@ BaseType_t xAlreadyYielded = pdFALSE; mtCOVERAGE_TEST_MARKER(); } - if( xYieldPending == pdTRUE ) + if( xYieldPending[ xPortGetCoreID() ] == pdTRUE ) { #if( configUSE_PREEMPTION != 0 ) { @@ -2135,7 +2187,7 @@ BaseType_t xSwitchRequired = pdFALSE; #if ( configUSE_PREEMPTION == 1 ) { - if( xYieldPending != pdFALSE ) + if( xYieldPending [ xPortGetCoreID() ] != pdFALSE ) { xSwitchRequired = pdTRUE; } @@ -2251,11 +2303,11 @@ void vTaskSwitchContext( void ) { /* The scheduler is currently suspended - do not allow a context switch. */ - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { - xYieldPending = pdFALSE; + xYieldPending[ xPortGetCoreID() ] = pdFALSE; traceTASK_SWITCHED_OUT(); #if ( configGENERATE_RUN_TIME_STATS == 1 ) @@ -2610,16 +2662,16 @@ BaseType_t xReturn; taskEXIT_CRITICAL(&xTaskQueueMutex); } - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* Return true if the task removed from the event list has a higher - priority than the calling task. This allows the calling task to know if - it should force a context switch now. */ + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2670,17 +2722,16 @@ BaseType_t xReturn; ( void ) uxListRemove( &( pxUnblockedTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* Return true if the task removed from the event list has - a higher priority than the calling task. This allows - the calling task to know if it should force a context - switch now. */ + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2751,7 +2802,7 @@ BaseType_t xReturn; void vTaskMissedYield( void ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } /*-----------------------------------------------------------*/ @@ -2921,7 +2972,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) /* A task was made ready while the scheduler was suspended. */ eReturn = eAbortSleep; } - else if( xYieldPending != pdFALSE ) + else if( xYieldPending[ xPortGetCoreID() ] != pdFALSE ) { /* A yield was pended while the scheduler was suspended. */ eReturn = eAbortSleep; @@ -3597,12 +3648,6 @@ TCB_t *pxTCB; #endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ -/* -ToDo: Mutexes haven't been tested or adapted to multicore at all. - -In fact, nothing below this line has/is. -*/ - #if ( configUSE_MUTEXES == 1 ) void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) @@ -4434,12 +4479,16 @@ TickType_t uxReturn; /* The task should not have been on an event list. */ configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ portYIELD_WITHIN_API(); } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE(pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4530,7 +4579,7 @@ TickType_t uxReturn; vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); } - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4539,6 +4588,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4593,8 +4646,8 @@ TickType_t uxReturn; this task pending until the scheduler is resumed. */ vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); } - - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4603,6 +4656,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); From c6477ff10d983aa8dcb152be2bc673352d5bd69c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 12:37:19 +0800 Subject: [PATCH 2/5] Fix int clear, actually call int init code --- components/esp32/cpu_start.c | 3 +++ components/esp32/crosscore_int.c | 4 ++-- components/esp32/lib | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 5c7a411c8e..0c94f39e30 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -41,6 +41,7 @@ #include "esp_event.h" #include "esp_spi_flash.h" #include "esp_ipc.h" +#include "esp_crosscore_int.h" #include "esp_log.h" #include "trax.h" @@ -146,6 +147,7 @@ void start_cpu0_default(void) uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); ets_setup_syscalls(); do_global_ctors(); + esp_crosscore_int_init(); esp_ipc_init(); spi_flash_init(); xTaskCreatePinnedToCore(&main_task, "main", @@ -165,6 +167,7 @@ void start_cpu1_default(void) while (port_xSchedulerRunning[0] == 0) { ; } + esp_crosscore_int_init(); ESP_LOGI(TAG, "Starting scheduler on APP CPU."); xPortStartScheduler(); } diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 95f2762f23..a98b13583c 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -57,7 +57,7 @@ static void esp_crosscore_isr(void *arg) { if (xPortGetCoreID()==0) { WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); } else { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 1); + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); } //Grab the reason and clear it. portENTER_CRITICAL(&reasonSpinlock); @@ -77,6 +77,7 @@ static void esp_crosscore_isr(void *arg) { //on each active core. void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); + ets_printf("init cpu %d\n", xPortGetCoreID()); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); @@ -87,7 +88,6 @@ void esp_crosscore_int_init() { } xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); - } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/lib b/components/esp32/lib index a1e5f8b953..b9561aa5db 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1e5f8b953c7934677ba7a6ed0a6dd2da0e6bd0f +Subproject commit b9561aa5db15443d11f8bb5aefdfc5da540d8f2d From 3371083c1611fb7a5b0f389739599146af51047c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 16:07:47 +0800 Subject: [PATCH 3/5] Add checks for tasks woken up on other CPUs where needed, make xYieldPending and xPendingReadyList per-processor, add configurable ISR stack size to Kconfig, in general fix the entire wake-up-task-on-other-cpu-by-interrupt implementation --- components/esp32/crosscore_int.c | 90 +++++++++---------- components/esp32/include/esp_crosscore_int.h | 27 ++++++ components/freertos/Kconfig | 9 ++ .../include/freertos/FreeRTOSConfig.h | 2 +- components/freertos/tasks.c | 70 ++++++++------- 5 files changed, 117 insertions(+), 81 deletions(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index a98b13583c..56fe6fe9a7 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,64 +44,60 @@ ToDo: There is a small chance the CPU already has yielded when this ISR is servi the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ static void esp_crosscore_isr(void *arg) { - volatile uint32_t myReasonVal; + volatile uint32_t myReasonVal; #if 0 - //A pointer to the correct reason array item is passed to this ISR. - volatile uint32_t *myReason=arg; + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *myReason=arg; #else - //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... - volatile uint32_t *myReason=&reason[xPortGetCoreID()]; + //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... + volatile uint32_t *myReason=&reason[xPortGetCoreID()]; #endif - //Clear the interrupt first. - if (xPortGetCoreID()==0) { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); - } else { - WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); - } - //Grab the reason and clear it. - portENTER_CRITICAL(&reasonSpinlock); - myReasonVal=*myReason; - *myReason=0; - portEXIT_CRITICAL(&reasonSpinlock); + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); + } + //Grab the reason and clear it. + portENTER_CRITICAL(&reasonSpinlock); + myReasonVal=*myReason; + *myReason=0; + portEXIT_CRITICAL(&reasonSpinlock); - //Check what we need to do. - if (myReasonVal&REASON_YIELD) { - portYIELD_FROM_ISR(); - } - - ets_printf("recv yield\n"); + //Check what we need to do. + if (myReasonVal&REASON_YIELD) { + portYIELD_FROM_ISR(); + } } //Initialize the crosscore interrupt on this core. Call this once //on each active core. void esp_crosscore_int_init() { - portENTER_CRITICAL(&reasonSpinlock); - ets_printf("init cpu %d\n", xPortGetCoreID()); - reason[xPortGetCoreID()]=0; - portEXIT_CRITICAL(&reasonSpinlock); - ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); - if (xPortGetCoreID()==0) { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); - } else { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); - } - xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); - ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); + portENTER_CRITICAL(&reasonSpinlock); + reason[xPortGetCoreID()]=0; + portEXIT_CRITICAL(&reasonSpinlock); + ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); + if (xPortGetCoreID()==0) { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + } else { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + } + xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); + ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); } void esp_crosscore_int_send_yield(int coreId) { - ets_printf("send yield\n"); - assert(coreIduxPriority < uxPriority ) + No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. + */ + if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } + else if( xCoreID != xPortGetCoreID() ) { + taskYIELD_OTHER_CORE(xCoreID, uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1452,7 +1450,7 @@ BaseType_t i; if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) { /* Has the task already been resumed from within an ISR? */ - if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + if( listIS_CONTAINED_WITHIN( &xPendingReadyList[ xPortGetCoreID() ], &( pxTCB->xEventListItem ) ) == pdFALSE ) { /* Is it in the suspended list because it is in the Suspended state, or because is is blocked with no timeout? */ @@ -1544,7 +1542,6 @@ BaseType_t i; #if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) -/* ToDo: Make this multicore-compatible. */ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) { BaseType_t xYieldRequired = pdFALSE; @@ -1567,14 +1564,14 @@ BaseType_t i; ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxTCB ); - if ( pxTCB->xCoreID == xPortGetCoreID() ) - { - taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); - } - else if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE( pxTCB->xCoreID ) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1585,7 +1582,7 @@ BaseType_t i; /* The delayed or ready lists cannot be accessed so the task is held in the pending ready list until the scheduler is unsuspended. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } } else @@ -1770,9 +1767,9 @@ BaseType_t xAlreadyYielded = pdFALSE; { /* Move any readied tasks from the pending list into the appropriate ready list. */ - while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + while( listLIST_IS_EMPTY( &xPendingReadyList[ xPortGetCoreID() ] ) == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList[ xPortGetCoreID() ] ) ); ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxTCB ); @@ -1785,10 +1782,6 @@ BaseType_t xAlreadyYielded = pdFALSE; xYieldPending[xPortGetCoreID()] = pdTRUE; break; } - else if ( pxTCB->xCoreID != xPortGetCoreID() ) - { - taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); - } else { mtCOVERAGE_TEST_MARKER(); @@ -2658,15 +2651,20 @@ BaseType_t xReturn; /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ taskENTER_CRITICAL(&xTaskQueueMutex); - vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxUnblockedTCB->xEventListItem ) ); taskEXIT_CRITICAL(&xTaskQueueMutex); } if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* We can schedule the awoken task on this CPU. */ - xYieldPending[xPortGetCoreID()] = pdTRUE; + /* Return true if the task removed from the event list has a higher + priority than the calling task. This allows the calling task to know if + it should force a context switch now. */ xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) { @@ -2724,9 +2722,15 @@ BaseType_t xReturn; if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - /* We can schedule the awoken task on this CPU. */ - xYieldPending[xPortGetCoreID()] = pdTRUE; + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) { @@ -2967,7 +2971,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) eSleepModeStatus eReturn = eStandardSleep; taskENTER_CRITICAL(&xTaskQueueMutex); - if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + if( listCURRENT_LIST_LENGTH( &xPendingReadyList[ xPortGetCoreID() ] ) != 0 ) { /* A task was made ready while the scheduler was suspended. */ eReturn = eAbortSleep; @@ -3210,7 +3214,7 @@ UBaseType_t uxPriority; vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); - vListInitialise( &xPendingReadyList ); + vListInitialise( &xPendingReadyList[ xPortGetCoreID() ] ); #if ( INCLUDE_vTaskDelete == 1 ) { @@ -4576,7 +4580,7 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) @@ -4644,7 +4648,7 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) From 68f39c1ed90bc948ad46af74869d26f5bef9806c Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 27 Oct 2016 16:50:28 +0800 Subject: [PATCH 4/5] Only init crosscore when FreeRTOS runs in multicore mode, add warnings that cross_int calls are private. --- components/esp32/cpu_start.c | 2 ++ components/esp32/crosscore_int.c | 4 ++-- components/esp32/include/esp_crosscore_int.h | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 0c94f39e30..bfc751d453 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -147,7 +147,9 @@ void start_cpu0_default(void) uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); ets_setup_syscalls(); do_global_ctors(); +#if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); +#endif esp_ipc_init(); spi_flash_init(); xTaskCreatePinnedToCore(&main_task, "main", diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 56fe6fe9a7..78287d405e 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -49,8 +49,8 @@ static void esp_crosscore_isr(void *arg) { //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; #else - //Does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... + //The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... this is a valid but slightly less optimal replacement. volatile uint32_t *myReason=&reason[xPortGetCoreID()]; #endif //Clear the interrupt first. diff --git a/components/esp32/include/esp_crosscore_int.h b/components/esp32/include/esp_crosscore_int.h index ca5f3901ee..0e4b2b8385 100644 --- a/components/esp32/include/esp_crosscore_int.h +++ b/components/esp32/include/esp_crosscore_int.h @@ -19,6 +19,10 @@ * Initialize the crosscore interrupt system for this CPU. * This needs to be called once on every CPU that is used * by FreeRTOS. + * + * If multicore FreeRTOS support is enabled, this will be + * called automatically by the startup code and should not + * be called manually. */ void esp_crosscore_int_init(); @@ -28,6 +32,9 @@ void esp_crosscore_int_init(); * currently running task in favour of a higher-priority task * that presumably just woke up. * + * This is used internally by FreeRTOS in multicore mode + * and should not be called by the user. + * * @param coreID Core that should do the yielding */ void esp_crosscore_int_send_yield(int coreId); From 90b787636a49fd2f36c6c30e2a57c06303afb20e Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 31 Oct 2016 11:00:27 +0800 Subject: [PATCH 5/5] Remove redundant volatile keyword --- components/esp32/crosscore_int.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 78287d405e..60a45da402 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,7 +44,7 @@ ToDo: There is a small chance the CPU already has yielded when this ISR is servi the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ static void esp_crosscore_isr(void *arg) { - volatile uint32_t myReasonVal; + uint32_t myReasonVal; #if 0 //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg;