diff --git a/arch/xtensa/include/esp32/tie.h b/arch/xtensa/include/esp32/tie.h index 8bc24b342d..cd72bfc934 100644 --- a/arch/xtensa/include/esp32/tie.h +++ b/arch/xtensa/include/esp32/tie.h @@ -75,8 +75,8 @@ /* Total save area for optional and custom state (NCP + CPn): */ -#define XCHAL_TOTAL_SA_SIZE 128 /* with 16-byte align padding */ -#define XCHAL_TOTAL_SA_ALIGN 4 /* actual minimum alignment */ +#define XCHAL_TOTAL_SA_SIZE 128 /* With 16-byte align padding */ +#define XCHAL_TOTAL_SA_ALIGN 4 /* Actual minimum alignment */ /* Detailed contents of save areas. * NOTE: caller must define the XCHAL_SA_REG macro (not defined here) diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index ad35c34f09..e6167a7f83 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -120,7 +120,8 @@ /* Interrupt codes from other CPUs: */ -#define CPU_INTCODE_PAUSE 0 +#define CPU_INTCODE_NONE 0 +#define CPU_INTCODE_PAUSE 1 /* Register access macros */ @@ -257,7 +258,7 @@ void xtensa_panic(int xptcode, uint32_t *regs) noreturn_function; /* Software interrupt handler */ #ifdef CONFIG_SMP -int xtensa_cpu_interrupt(int cpu, int intcode); +int xtensa_intercpu_interrupt(int tocpu, int intcode); void xtensa_pause_handler(void); #endif diff --git a/arch/xtensa/src/common/xtensa_assert.c b/arch/xtensa/src/common/xtensa_assert.c index 41adf94a48..4fb0ef3bc1 100644 --- a/arch/xtensa/src/common/xtensa_assert.c +++ b/arch/xtensa/src/common/xtensa_assert.c @@ -67,34 +67,6 @@ * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: xtensa_assert - ****************************************************************************/ - -static void xtensa_assert(int errorcode) noreturn_function; -static void xtensa_assert(int errorcode) -{ - /* Are we in an interrupt handler or the idle task? */ - - if (CURRENT_REGS || this_task()->pid == 0) - { - (void)up_irq_save(); - for (; ; ) - { -#ifdef CONFIG_ARCH_LEDS - board_autoled_on(LED_PANIC); - up_mdelay(250); - board_autoled_off(LED_PANIC); - up_mdelay(250); -#endif - } - } - else - { - exit(errorcode); - } -} - /**************************************************************************** * Name: assert_tracecallback ****************************************************************************/ @@ -120,6 +92,54 @@ static int assert_tracecallback(FAR struct usbtrace_s *trace, FAR void *arg) } #endif +/**************************************************************************** + * Name: xtensa_assert + ****************************************************************************/ + +static void xtensa_assert(int errorcode) noreturn_function; +static void xtensa_assert(int errorcode) +{ + /* Dump the processor state */ + + xtensa_dumpstate(); + +#ifdef CONFIG_ARCH_USBDUMP + /* Dump USB trace data */ + + (void)usbtrace_enumerate(assert_tracecallback, NULL); +#endif + +#ifdef CONFIG_BOARD_CRASHDUMP + /* Perform board-specific crash dump */ + + board_crashdump(up_getsp(), this_task(), filename, lineno); +#endif + + /* Are we in an interrupt handler or the idle task? */ + + if (CURRENT_REGS || this_task()->pid == 0) + { + /* Blink the LEDs forever */ + + (void)up_irq_save(); + for (; ; ) + { +#ifdef CONFIG_ARCH_LEDS + board_autoled_on(LED_PANIC); + up_mdelay(250); + board_autoled_off(LED_PANIC); + up_mdelay(250); +#endif + } + } + else + { + /* Assertions in other contexts only cause the thread to exit */ + + exit(errorcode); + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -144,18 +164,6 @@ void up_assert(const uint8_t *filename, int lineno) filename, lineno); #endif - xtensa_dumpstate(); - -#ifdef CONFIG_ARCH_USBDUMP - /* Dump USB trace data */ - - (void)usbtrace_enumerate(assert_tracecallback, NULL); -#endif - -#ifdef CONFIG_BOARD_CRASHDUMP - board_crashdump(up_getsp(), this_task(), filename, lineno); -#endif - xtensa_assert(EXIT_FAILURE); } @@ -179,17 +187,5 @@ void xtensa_panic(int xptcode, uint32_t *regs) _alert("Unhandled Exception %d\n", xptcode); #endif - xtensa_dumpstate(); - -#ifdef CONFIG_ARCH_USBDUMP - /* Dump USB trace data */ - - (void)usbtrace_enumerate(assert_tracecallback, NULL); -#endif - -#ifdef CONFIG_BOARD_CRASHDUMP - board_crashdump(up_getsp(), this_task(), filename, lineno); -#endif - - xtensa_assert(EXIT_FAILURE); + xtensa_assert(EXIT_FAILURE); /* Should not return */ } diff --git a/arch/xtensa/src/common/xtensa_cpupause.c b/arch/xtensa/src/common/xtensa_cpupause.c index e13e3ea3e8..46bac69bbd 100644 --- a/arch/xtensa/src/common/xtensa_cpupause.c +++ b/arch/xtensa/src/common/xtensa_cpupause.c @@ -168,7 +168,7 @@ int up_cpu_pause(int cpu) /* Execute SGI2 */ - ret = xtensa_cpu_interrupt(cpu, CPU_INTCODE_PAUSE); + ret = xtensa_intercpu_interrupt(cpu, CPU_INTCODE_PAUSE); if (ret < 0) { /* What happened? Unlock the g_cpu_wait spinlock */ diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index 2598bf3d6d..d16874c4bc 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -89,7 +89,7 @@ CHIP_CSRCS += esp32_start.c esp32_timerisr.c ifeq ($(CONFIG_SMP),y) CHIP_ASRCS = esp32_cpuindex.S -CMN_CSRCS += esp32_cpustart.c esp32_cpu_interrupt.c +CMN_CSRCS += esp32_cpustart.c esp32_intercpu_interrupt.c #CMN_CSRCS += esp32_cpuidlestack.c endif diff --git a/arch/xtensa/src/esp32/esp32_cpu_interrupt.c b/arch/xtensa/src/esp32/esp32_cpu_interrupt.c deleted file mode 100644 index 93315163ed..0000000000 --- a/arch/xtensa/src/esp32/esp32_cpu_interrupt.c +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** - * arch/xtensa/src/esp32/esp32_cpu_interrupt.c - * - * Copyright (C) 2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt > - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include - -#include - -#include "xtensa.h" - -#ifdef CONFIG_SMP - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Name: esp32_cpu_interrupt - * - * Description: - * Called to handle the CPU0/1 interrupts. - * - ****************************************************************************/ - -int esp32_cpu_interrupt(int irq, FAR void *context) -{ - uint32_t *regs = (uint32_t *)context; - int intcode; - - DEBUGASSERT(regs != NULL); - intcode = regs[REG_A2]; - - /* Dispatch the inter-CPU interrupt based on the intcode value */ - - switch (intcode) - { - case CPU_INTCODE_PAUSE: - xtensa_pause_handler(); - break; - - default: - DEBUGPANIC(); - break; - } - - return OK; -} - -/**************************************************************************** - * Name: xtensa_cpu_interrupt - * - * Description: - * Called to trigger a CPU interrupt - * - ****************************************************************************/ - -int xtensa_cpu_interrupt(int cpu, int intcode) -{ -#warning Missing logic -- How do we do this? - return -ENOSYS; -} - -#endif /* CONFIG_SMP */ diff --git a/arch/xtensa/src/esp32/esp32_cpustart.c b/arch/xtensa/src/esp32/esp32_cpustart.c index d471525a3b..6fe42912eb 100644 --- a/arch/xtensa/src/esp32/esp32_cpustart.c +++ b/arch/xtensa/src/esp32/esp32_cpustart.c @@ -52,7 +52,7 @@ #include "chip/esp32_dport.h" #include "esp32_region.h" #include "esp32_cpuint.h" -#include "esp32_cpu_interrupt.h" +#include "esp32_intercpu_interrupt.h" #ifdef CONFIG_SMP @@ -101,11 +101,11 @@ static inline void xtensa_disable_all(void) } /**************************************************************************** - * Name: xtensa_attach_cpu_interrupt + * Name: xtensa_attach_fromcpu0_interrupt ****************************************************************************/ #ifdef CONFIG_SMP -static inline void xtensa_attach_cpu_interrupt(void) +static inline void xtensa_attach_fromcpu0_interrupt(void) { int cpuint; @@ -121,7 +121,7 @@ static inline void xtensa_attach_cpu_interrupt(void) /* Attach the inter-CPU interrupt. */ - (void)irq_attach(ESP32_IRQ_CPU_CPU0, (xcpt_t)esp32_cpu_interrupt); + (void)irq_attach(ESP32_IRQ_CPU_CPU0, (xcpt_t)esp32_fromcpu0_interrupt); /* Enable the inter 0 CPU interrupts. */ @@ -183,7 +183,7 @@ int xtensa_start_handler(int irq, FAR void *context) #ifdef CONFIG_SMP /* Attach and enable the inter-CPU interrupt */ - xtensa_attach_cpu_interrupt(); + xtensa_attach_fromcpu0_interrupt(); #endif /* Detach all peripheral sources APP CPU interrupts */ diff --git a/arch/xtensa/src/esp32/esp32_intercpu_interrupt.c b/arch/xtensa/src/esp32/esp32_intercpu_interrupt.c new file mode 100644 index 0000000000..963e1d5156 --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_intercpu_interrupt.c @@ -0,0 +1,199 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_intercpu_interrupt.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt > + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "xtensa.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Single parameter passed with the inter-CPU interrupt */ + +static volatile uint8_t g_intcode[CONFIG_SMP_NCPUS]; + +/* Spinlock protects parameter array */ + +static volatile spinlock_t g_intercpu_spin[CONFIG_SMP_NCPUS] = +{ + SP_UNLOCKED, SP_UNLOCKED +}; + +/**************************************************************************** + * Private Function + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_fromcpu_interrupt + * + * Description: + * Common logic called to handle the from CPU0/1 interrupts. + * + ****************************************************************************/ + +static int esp32_fromcpu_interrupt(int fromcpu) +{ + uintptr_t regaddr; + int intcode; + int tocpu; + + DEBUGASSERT(regs != NULL); + + /* Clear the interrupt from the other CPU */ + + regaddr = (fromcpu == 0) ? DPORT_CPU_INTR_FROM_CPU_0_REG : + DPORT_CPU_INTR_FROM_CPU_1_REG; + putreg32(0, regaddr); + + /* Get the the inter-CPU interrupt code */ + + tocpu = up_cpu_index(); + intcode = g_intcode[tocpu]; + g_intcode[tocpu] = CPU_INTCODE_NONE; + + spin_unlock(&g_intercpu_spin[tocpu]); + + /* Dispatch the inter-CPU interrupt based on the intcode value */ + + switch (intcode) + { + case CPU_INTCODE_NONE: + break; + + case CPU_INTCODE_PAUSE: + xtensa_pause_handler(); + break; + + default: + DEBUGPANIC(); + break; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_fromcpu[0,1]_interrupt + * + * Description: + * Called to handle the from CPU0/1 interrupts. + * + ****************************************************************************/ + +int esp32_fromcpu0_interrupt(int irq, FAR void *context) +{ + return esp32_fromcpu_interrupt(0); +} + +int esp32_fromcpu1_interrupt(int irq, FAR void *context) +{ + return esp32_fromcpu_interrupt(1); +} + +/**************************************************************************** + * Name: xtensa_intercpu_interrupt + * + * Description: + * Called to trigger a CPU interrupt + * + ****************************************************************************/ + +int xtensa_intercpu_interrupt(int tocpu, int intcode) +{ + int fromcpu; + + DEBUGASSERT((unsigned)cpu < CONFIG_SMP_NCPUS && + (unsigned)incode <= UINT8_MAX); + + /* Disable context switching so that some other thread does not attempt to + * take the spinlock on the same CPU. + */ + + sched_lock(); + + /* Make sure that each inter-cpu event is atomic. The spinlock should + * only be locked if we just completed sending an interrupt to this + * CPU but the other CPU has not yet processed it. + */ + + spin_lock(&g_intercpu_spin[tocpu]); + + /* Save the passed parameter. The previous interrupt code should be + * CPU_INTCODE_NONE or we have overrun the other CPU. + */ + + DEBUGASSERT(g_intcode[tocpu] == CPU_INTCODE_NONE); + g_intcode[tocpu] = intcode; + + /* Interrupt the other CPU (tocpu) form this CPU. NOTE: that this logic + * fails in numerous ways if fromcpu == tocpu (for example because non- + * reentrant spinlocks are used). + */ + + fromcpu = up_cpu_index(); + DEBUGASSERT(fromcpu != tocpu); + + if (fromcpu == 0) + { + putreg32(DPORT_CPU_INTR_FROM_CPU_0, DPORT_CPU_INTR_FROM_CPU_0_REG); + } + else + { + putreg32(DPORT_CPU_INTR_FROM_CPU_1, DPORT_CPU_INTR_FROM_CPU_1_REG); + } + + sched_unlock(); + return OK; +} + +#endif /* CONFIG_SMP */ diff --git a/arch/xtensa/src/esp32/esp32_cpu_interrupt.h b/arch/xtensa/src/esp32/esp32_intercpu_interrupt.h similarity index 84% rename from arch/xtensa/src/esp32/esp32_cpu_interrupt.h rename to arch/xtensa/src/esp32/esp32_intercpu_interrupt.h index d46f162b2b..5b2c7eeae7 100644 --- a/arch/xtensa/src/esp32/esp32_cpu_interrupt.h +++ b/arch/xtensa/src/esp32/esp32_intercpu_interrupt.h @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/xtensa/src/esp32/esp32_cpu_interrupt.h + * arch/xtensa/src/esp32/esp32_intercpu_interrupt.h * * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt > @@ -33,8 +33,8 @@ * ****************************************************************************/ -#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_CPU_INTERRUPT_H -#define __ARCH_XTENSA_SRC_ESP32_ESP32_CPU_INTERRUPT_H +#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_INTERCPU_INTERRUPT_H +#define __ARCH_XTENSA_SRC_ESP32_ESP32_INTERCPU_INTERRUPT_H /**************************************************************************** * Included Files @@ -49,14 +49,15 @@ ****************************************************************************/ /**************************************************************************** - * Name: esp32_cpu_interrupt + * Name: esp32_fromcpu[0,1]_interrupt * * Description: - * Called to handle the CPU0-4 interrupts. + * Called to handle the from CPU0/1 interrupts. * ****************************************************************************/ -int esp32_cpu_interrupt(int irq, FAR void *context); +int esp32_fromcpu0_interrupt(int irq, FAR void *context); +int esp32_fromcpu1_interrupt(int irq, FAR void *context); #endif /* CONFIG_SMP */ -#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_CPU_INTERRUPT_H */ +#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_INTERCPU_INTERRUPT_H */ diff --git a/arch/xtensa/src/esp32/esp32_irq.c b/arch/xtensa/src/esp32/esp32_irq.c index 13792b0c48..fae4687d5a 100644 --- a/arch/xtensa/src/esp32/esp32_irq.c +++ b/arch/xtensa/src/esp32/esp32_irq.c @@ -48,7 +48,7 @@ #include "xtensa.h" #include "esp32_cpuint.h" -#include "esp32_cpu_interrupt.h" +#include "esp32_intercpu_interrupt.h" /**************************************************************************** * Public Data @@ -113,11 +113,11 @@ static inline void xtensa_disable_all(void) } /**************************************************************************** - * Name: xtensa_attach_cpu_interrupt + * Name: xtensa_attach_fromcpu1_interrupt ****************************************************************************/ #ifdef CONFIG_SMP -static inline void xtensa_attach_cpu_interrupt(void) +static inline void xtensa_attach_fromcpu1_interrupt(void) { int cpuint; @@ -133,7 +133,7 @@ static inline void xtensa_attach_cpu_interrupt(void) /* Attach the inter-CPU interrupt. */ - (void)irq_attach(ESP32_IRQ_CPU_CPU1, (xcpt_t)esp32_cpu_interrupt); + (void)irq_attach(ESP32_IRQ_CPU_CPU1, (xcpt_t)esp32_fromcpu1_interrupt); /* Enable the inter 0 CPU interrupt. */ @@ -175,7 +175,7 @@ void xtensa_irq_initialize(void) #ifdef CONFIG_SMP /* Attach and enable the inter-CPU interrupt */ - xtensa_attach_cpu_interrupt(); + xtensa_attach_fromcpu1_interrupt(); #endif esp32_irq_dump("initial", NR_IRQS); diff --git a/configs/esp32-core/README.txt b/configs/esp32-core/README.txt index f823864307..1f2d6e2be5 100644 --- a/configs/esp32-core/README.txt +++ b/configs/esp32-core/README.txt @@ -21,6 +21,7 @@ Contents o ESP32 Toolchain o Serial Console o Buttons and LEDs + o SMP o Configurations STATUS @@ -100,6 +101,39 @@ Buttons and LEDs There are several on-board LEDs for that indicate the presence of power and USB activity. None of these are available for use by sofware. +SMP +=== + + The ESP32 has 2 CPUs. Support is included for testing an SMP configuration. + That configuration is still not yet ready for usage but can be enabled with + the following configuration settings: + + RTOS Features -> Tasks and Scheduling + CONFIG_SPINLOCK=y + CONFIG_SMP=y + CONFIG_SMP_NCPUS=2 + CONFIG_SMP_IDLETHREAD_STACKSIZE=2048 + + Open Issues: + + 1. Currently all device interrupts are handled on the PRO CPU only. Critical + sections will attempt to disable interrupts but will now disable interrupts + only on the current CPU (which may not be CPU0). Perhaps that should be a + spinlock to prohibit execution of interrupts on CPU0 when other CPUs are in + a critical section? + + 2. Cache Issues. I have not though about this yet, but certainly caching is + an issue in an SMP system: + + - Cache coherency. Are there separate caches for each CPU? Or a single + shared cache? If the are separate then keep the caches coherent will + be an issue. + - Caching MAY interfere with spinlocks as they are currently implemented. + Waiting on a cached copy of the spinlock may result in a hang or a + failure to wait. + + 3. Assertions. On a fatal assertions, other CPUs need to be stopped. + Configurations ==============