ESP32: Add inter-cpu interrupts

This commit is contained in:
Gregory Nutt 2016-10-31 08:29:28 -06:00
parent 63d5ab5b67
commit a787a99071
11 changed files with 307 additions and 179 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 */
}

View File

@ -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 */

View File

@ -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

View File

@ -1,103 +0,0 @@
/****************************************************************************
* arch/xtensa/src/esp32/esp32_cpu_interrupt.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <arch/irq.h>
#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 */

View File

@ -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 */

View File

@ -0,0 +1,199 @@
/****************************************************************************
* arch/xtensa/src/esp32/esp32_intercpu_interrupt.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <arch/irq.h>
#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 */

View File

@ -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 <gnutt@nuttx.org>>
@ -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 */

View File

@ -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);

View File

@ -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
==============