Xtensa ESP32: Basically a redesign of the interrupt dispatch logic.

This commit is contained in:
Gregory Nutt 2016-12-16 15:36:52 -06:00
parent d4ad5f04d3
commit cdd8dc72a5
5 changed files with 171 additions and 135 deletions

View File

@ -390,7 +390,8 @@
#define ESP32_CPUINT_NINTERNAL 6 #define ESP32_CPUINT_NINTERNAL 6
#define ESP32_CPUINT_MAX 31 #define ESP32_NCPUINTS 32
#define ESP32_CPUINT_MAX (ESP32_NCPUINTS - 1)
#define EPS32_CPUINT_PERIPHSET 0xdffe773f #define EPS32_CPUINT_PERIPHSET 0xdffe773f
#define EPS32_CPUINT_INTERNALSET 0x200188c0 #define EPS32_CPUINT_INTERNALSET 0x200188c0

View File

@ -126,45 +126,25 @@
* a consequence of context switching. * a consequence of context switching.
*/ */
mov a12, sp /* a12 = address of save area */ mov a12, sp /* Address of save area */
._xtensa_dispatch_level&level&:
#ifdef __XTENSA_CALL0_ABI__
/* Get mask of pending, enabled interrupts at this level into a2. */ /* Get mask of pending, enabled interrupts at this level into a2. */
rsr a2, INTENABLE rsr a2, INTENABLE
rsr a3, INTERRUPT rsr a3, INTERRUPT
movi a4, \mask movi a4, \mask
and a2, a2, a3 and a2, a2, a3
and a2, a2, a4 and a2, a2, a4 /* a2 = Set of pending, enabled interrupts for this level */
beqz a2, 5f /* Nothing to do */ beqz a2, 3f /* Nothing to do */
/* If multiple bits are set then MSB has highest priority. */
extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */
/* Check for a timer interrupt.
*
* REVISIT: XT_TIMER_INTEN will be only one of the configured timers
* selected as the system periodic timer (see xtensa_timer.h). There
* is no mechanism here to detect other timer interrupts.
*/
movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */
wsr a4, INTCLEAR /* Clear sw or edge-triggered interrupt */
beq a3, a4, 4f /* If timer interrupt then skip decode */
/* Call xtensa_int_decode with, passing that address of the register save /* Call xtensa_int_decode with, passing that address of the register save
* area as a parameter (A2). * area as a parameter (A2).
*/ */
#ifdef __XTENSA_CALL0_ABI__ /* Argument 1: Set of CPU interrupt to dispatch */
mov a2, a12 /* Argument: Top of stack = register save area */ mov a3, sp /* Argument 2: Top of stack = register save area */
call0 xtensa_int_decode /* Call xtensa_int_decode */ call0 xtensa_int_decode /* Call xtensa_int_decode */
#else
mov a6, a12 /* Argument: Top of stack = register save area */
call4 xtensa_int_decode /* Call xtensa_int_decode */
#endif
/* On return from xtensa_int_decode, a2 will contain the address of the new /* On return from xtensa_int_decode, a2 will contain the address of the new
* register save area. Usually this would be the same as the current SP. * register save area. Usually this would be the same as the current SP.
@ -172,7 +152,7 @@
* register save area. This may or may not reside on a stack. * register save area. This may or may not reside on a stack.
*/ */
beq a2, a12, 3f /* If timer interrupt then keep stack */ beq a2, sp, 3f /* If no context switch then keep stack */
/* Switch stacks */ /* Switch stacks */
@ -180,53 +160,42 @@
l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */
addi sp, a2, -(4 * XCPTCONTEXT_SIZE) addi sp, a2, -(4 * XCPTCONTEXT_SIZE)
3: #else
j ._xtensa_dispatch_level&level& /* Check for more interrupts */ /* Get mask of pending, enabled interrupts at this level into a2. */
4: rsr a6, INTENABLE
rsr a2, INTERRUPT
movi a3, \mask
and a6, a6, a2
and a6, a6, a3 /* a6 = Set of pending, enabled interrupts for this level */
beqz a6, 3f /* Nothing to do */
.ifeq XT_TIMER_INTPRI - \level /* Call xtensa_int_decode with, passing that address of the register save
* area as a parameter (A2).
/* Interrupt handler for the NuttX system timer if at this level.
* We'll be reading the interrupt state again after this call
* so no need to preserve any registers except a7 (pointer to
* state save area).
*
* REVISIT: Here we explicitly assume that the INTERRUPT XT_TIMER_INTEN
* corresponds to TIMER0. That is probably that case, but not necessarily
* so. xtensa_timer.h should probably select the IRQ number as well.
*/ */
#ifdef __XTENSA_CALL0_ABI__ /* Argument 1: Set of CPU interrupt to dispatch */
movi a2, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */ mov a7, sp /* Argument 2: Top of stack = register save area */
mov a3, a12 /* Argument 2: Top of stack = register save area */ call4 xtensa_int_decode /* Call xtensa_int_decode */
call0 xtensa_irq_dispatch /* Call xtensa_int_decode */
#else /* On return from xtensa_int_decode, a2 will contain the address of the new
movi a6, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */ * register save area. Usually this would be the same as the current SP.
mov a7, a12 /* Argument 2: Top of stack = register save area */ * But in the event of a context switch, a2 will instead refer to the TCB
call4 xtensa_irq_dispatch /* Call xtensa_int_decode */ * register save area. This may or may not reside on a stack.
*/
beq a4, sp, 3f /* If no context switch then keep stack */
/* Switch stacks */
mov a12, a4 /* Switch to the save area of the new thread */
l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */
addi sp, a2, -(4 * XCPTCONTEXT_SIZE)
#endif #endif
/* On return from xtensa_irq_dispatch, A2 will contain the address of the new
* register save area. Usually this would be the same as the current SP.
* But in the event of a context switch, A2 will instead refer to the TCB
* register save area.
*/
beq a2, a12, 5f /* If timer interrupt then skip table */
/* Switch stacks */
mov a12, a2 /* Switch to the save area of the new thread */
l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */
addi sp, a2, -(4 * XCPTCONTEXT_SIZE)
.endif
j ._xtensa_dispatch_level&level& /* Check for more interrupts */
5:
/* done */
/* Done */
3:
.endm .endm
/**************************************************************************** /****************************************************************************

View File

@ -40,6 +40,7 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <debug.h> #include <debug.h>
@ -147,6 +148,17 @@
#define ESP32_MAX_PRIORITY 5 #define ESP32_MAX_PRIORITY 5
#define ESP32_PRIO_INDEX(p) ((p) - ESP32_MIN_PRIORITY) #define ESP32_PRIO_INDEX(p) ((p) - ESP32_MIN_PRIORITY)
/****************************************************************************
* Public Data
****************************************************************************/
/* Maps a CPU interrupt to the attached peripheral interrupt */
uint8_t g_cpu0_intmap[ESP32_NCPUINTS];
#ifdef CONFIG_SMP
uint8_t g_cpu1_intmap[ESP32_NCPUINTS];
#endif
/**************************************************************************** /****************************************************************************
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
@ -156,13 +168,9 @@
*/ */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static uint32_t g_intenable[CONFIG_SMP_NCPUS]; static uint32_t g_intenable[CONFIG_SMP_NCPUS];
#else #else
static uint32_t g_intenable[1]; static uint32_t g_intenable[1];
#endif #endif
/* Bitsets for free, unallocated CPU interrupts available to peripheral /* Bitsets for free, unallocated CPU interrupts available to peripheral
@ -170,11 +178,8 @@ static uint32_t g_intenable[1];
*/ */
static uint32_t g_cpu0_freeints = EPS32_CPUINT_PERIPHSET; static uint32_t g_cpu0_freeints = EPS32_CPUINT_PERIPHSET;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static uint32_t g_cpu1_freeints = EPS32_CPUINT_PERIPHSET; static uint32_t g_cpu1_freeints = EPS32_CPUINT_PERIPHSET;
#endif #endif
/* Bitsets for each interrupt priority 1-5 */ /* Bitsets for each interrupt priority 1-5 */
@ -311,6 +316,7 @@ int esp32_alloc_cpuint(uint32_t intmask)
int esp32_cpuint_initialize(void) int esp32_cpuint_initialize(void)
{ {
uintptr_t regaddr; uintptr_t regaddr;
uint8_t *intmap;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
int cpu; int cpu;
#endif #endif
@ -345,6 +351,38 @@ int esp32_cpuint_initialize(void)
putreg32(NO_CPUINT, regaddr); putreg32(NO_CPUINT, regaddr);
} }
/* Initialize CPU0-to-peripheral mapping table */
#ifdef CONFIG_SMP
if (cpu != 0)
{
intmap = g_cpu1_intmap;
}
else
#endif
{
intmap = g_cpu0_intmap;
}
/* Indiate that no peripheral interrupts are assigned to CPU interrupts */
memset(intmap, CPUINT_UNASSIGNED, ESP32_NCPUINTS);
/* Special case the 6 internal interrupts.
*
* CPU interrupt bit IRQ number
* --------------------------- ---------------------
* ESP32_CPUINT_TIMER0 6 XTENSA_IRQ_TIMER0 0
* SP32_CPUINT_SOFTWARE0 7 Not yet defined
* ESP32_CPUINT_PROFILING 11 Not yet defined
* ESP32_CPUINT_TIMER1 15 XTENSA_IRQ_TIMER1 1
* ESP32_CPUINT_TIMER2 16 XTENSA_IRQ_TIMER2 2
* ESP32_CPUINT_SOFTWARE1 29 Not yet defined
*/
intmap[ESP32_CPUINT_TIMER0] = XTENSA_IRQ_TIMER0;
intmap[ESP32_CPUINT_TIMER1] = XTENSA_IRQ_TIMER1;
intmap[ESP32_CPUINT_TIMER2] = XTENSA_IRQ_TIMER2;
return OK; return OK;
} }
@ -524,6 +562,7 @@ void esp32_free_cpuint(int cpuint)
void esp32_attach_peripheral(int cpu, int periphid, int cpuint) void esp32_attach_peripheral(int cpu, int periphid, int cpuint)
{ {
uintptr_t regaddr; uintptr_t regaddr;
uint8_t *intmap;
DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS); DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS);
DEBUGASSERT(cpuint >= 0 && cpuint <= ESP32_CPUINT_MAX); DEBUGASSERT(cpuint >= 0 && cpuint <= ESP32_CPUINT_MAX);
@ -533,13 +572,18 @@ void esp32_attach_peripheral(int cpu, int periphid, int cpuint)
if (cpu != 0) if (cpu != 0)
{ {
regaddr = DPORT_APP_MAP_REGADDR(periphid); regaddr = DPORT_APP_MAP_REGADDR(periphid);
intmap = g_cpu1_intmap;
} }
else else
#endif #endif
{ {
regaddr = DPORT_PRO_MAP_REGADDR(periphid); regaddr = DPORT_PRO_MAP_REGADDR(periphid);
intmap = g_cpu0_intmap;
} }
DEBUGASSERT(intmap[cpuint] == CPUINT_UNASSIGNED);
intmap[cpuint] = periphid + XTENSA_IRQ_FIRSTPERIPH;
putreg32(cpuint, regaddr); putreg32(cpuint, regaddr);
} }
@ -564,6 +608,7 @@ void esp32_attach_peripheral(int cpu, int periphid, int cpuint)
void esp32_detach_peripheral(int cpu, int periphid, int cpuint) void esp32_detach_peripheral(int cpu, int periphid, int cpuint)
{ {
uintptr_t regaddr; uintptr_t regaddr;
uint8_t *intmap;
DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS); DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
@ -572,12 +617,17 @@ void esp32_detach_peripheral(int cpu, int periphid, int cpuint)
if (cpu != 0) if (cpu != 0)
{ {
regaddr = DPORT_APP_MAP_REGADDR(periphid); regaddr = DPORT_APP_MAP_REGADDR(periphid);
intmap = g_cpu1_intmap;
} }
else else
#endif #endif
{ {
regaddr = DPORT_PRO_MAP_REGADDR(periphid); regaddr = DPORT_PRO_MAP_REGADDR(periphid);
intmap = g_cpu0_intmap;
} }
DEBUGASSERT(intmap[cpuint] != CPUINT_UNASSIGNED);
intmap[cpuint] = CPUINT_UNASSIGNED;
putreg32(NO_CPUINT, regaddr); putreg32(NO_CPUINT, regaddr);
} }

View File

@ -42,6 +42,25 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <arch/irq.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CPUINT_UNASSIGNED 0xff /* No peripheral assigned to this CPU interrupt */
/****************************************************************************
* Public Data
****************************************************************************/
/* Maps a CPU interrupt to the attached peripheral interrupt */
extern uint8_t g_cpu0_intmap[ESP32_NCPUINTS];
#ifdef CONFIG_SMP
extern uint8_t g_cpu1_intmap[ESP32_NCPUINTS];
#endif
/**************************************************************************** /****************************************************************************
* Public Function Prototypes * Public Function Prototypes
****************************************************************************/ ****************************************************************************/

View File

@ -39,29 +39,32 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <nuttx/sched.h> #include <stdint.h>
#include <arch/chip/irq.h> #include <assert.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include "chip/esp32_dport.h"
#include "xtensa.h" #include "xtensa.h"
#include "esp32_cpuint.h"
/**************************************************************************** /****************************************************************************
* Private Data * Private Functions
****************************************************************************/ ****************************************************************************/
static const uint8_t g_baseirq[3] = /****************************************************************************
{ * Name: xtensa_intclear
ESP32_IRQ_SREG0, ****************************************************************************/
ESP32_IRQ_SREG1,
ESP32_IRQ_SREG2
};
static const uint8_t g_nirqs[3] = static inline void xtensa_intclear(uint32_t mask)
{ {
ESP32_NIRQS_SREG0, __asm__ __volatile__
ESP32_NIRQS_SREG1, (
ESP32_NIRQS_SREG2 "movi a2, 0\n"
}; "wsr %0, INTCLEAR\n"
: "=r"(mask) : :
);
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
@ -75,80 +78,74 @@ static const uint8_t g_nirqs[3] =
* handling to the registered interrupt handler via xtensa_irq_dispatch(). * handling to the registered interrupt handler via xtensa_irq_dispatch().
* *
* Input Parameters: * Input Parameters:
* cpuints - Set of pending interrupts valid for this level
* regs - Saves processor state on the stack * regs - Saves processor state on the stack
* *
* Returned Value: * Returned Value:
* Normally the same vale as regs is returned. But, in the event of an * Normally the same value as regs is returned. But, in the event of an
* interrupt level context switch, the returned value will, instead point * interrupt level context switch, the returned value will, instead point
* to the saved processor state in the TCB of the newly started task. * to the saved processor state in the TCB of the newly started task.
* *
****************************************************************************/ ****************************************************************************/
uint32_t *xtensa_int_decode(uint32_t *regs) uint32_t *xtensa_int_decode(uint32_t cpuints, uint32_t *regs)
{ {
uintptr_t regaddr; uint8_t *intmap;
uint32_t regval;
uint32_t mask; uint32_t mask;
int regndx;
int bit; int bit;
int baseirq;
int nirqs;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
int cpu; int cpu;
#endif
/* Select PRO or APP interrupt status registers */ #ifdef CONFIG_SMP
/* Select PRO or APP CPU interrupt mapping table */
cpu = up_cpu_index(); cpu = up_cpu_index();
if (cpu == 0) if (cpu != 0)
{ {
regaddr = DPORT_PRO_INTR_STATUS_0_REG; intmap = g_cpu1_intmap;
} }
else else
#endif #endif
{ {
regaddr = DPORT_APP_INTR_STATUS_0_REG; intmap = g_cpu0_intmap;
} }
/* Process each pending interrupt in each of the three interrupt status /* Skip over zero bits, eight at a time */
* registers.
*/
for (regndx = 0; regndx < 3; regndx++) for (bit = 0, mask = 0xff;
bit < ESP32_NCPUINTS && (cpuints & mask) == 0;
bit += 8, mask <<= 8);
/* Process each pending CPU interrupt */
for (; bit < ESP32_NCPUINTS && cpuints != 0; bit++)
{ {
/* Fetch the next register status register */
regval = getreg32(regaddr);
regaddr += sizeof(uint32_t);
/* Set up the search */
baseirq = g_baseirq[regndx];
nirqs = g_nirqs[regndx];
/* Decode and dispatch each pending bit in the interrupt status
* register.
*/
for (bit = 0; regval != 0 && bit < nirqs; bit++)
{
/* Check if this interrupt is pending */
mask = (1 << bit); mask = (1 << bit);
if ((regval & mask) != 0) if ((cpuints & mask) != 0)
{ {
/* Yes.. Dispatch the interrupt. Note that regs may be /* Extract the IRQ number from the mapping table */
* altered in the case of an interrupt level context switch.
uint8_t irq = intmap[bit];
DEBUGASSERT(irq != CPUINT_UNASSIGNED);
/* Clear software or edge-triggered interrupt */
xtensa_intclear(mask);
/* Dispatch the CPU interrupt.
*
* NOTE that regs may be altered in the case of an interrupt
* level context switch.
*/ */
regs = xtensa_irq_dispatch(baseirq + bit, regs); regs = xtensa_irq_dispatch((int)irq, regs);
/* Clear this bit in the sampled status register so that /* Clear the bit in the pending interrupt so that perhaps
* perhaps we can exit this loop sooner. * we can exit the look early.
*/ */
regval &= ~mask; cpuints &= ~mask;
}
} }
} }