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_MAX 31
#define ESP32_NCPUINTS 32
#define ESP32_CPUINT_MAX (ESP32_NCPUINTS - 1)
#define EPS32_CPUINT_PERIPHSET 0xdffe773f
#define EPS32_CPUINT_INTERNALSET 0x200188c0

View File

@ -126,45 +126,25 @@
* a consequence of context switching.
*/
mov a12, sp /* a12 = address of save area */
._xtensa_dispatch_level&level&:
mov a12, sp /* Address of save area */
#ifdef __XTENSA_CALL0_ABI__
/* Get mask of pending, enabled interrupts at this level into a2. */
rsr a2, INTENABLE
rsr a3, INTERRUPT
movi a4, \mask
and a2, a2, a3
and a2, a2, a4
beqz a2, 5f /* 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 */
and a2, a2, a4 /* a2 = Set of pending, enabled interrupts for this level */
beqz a2, 3f /* Nothing to do */
/* Call xtensa_int_decode with, passing that address of the register save
* area as a parameter (A2).
*/
#ifdef __XTENSA_CALL0_ABI__
mov a2, a12 /* Argument: Top of stack = register save area */
/* Argument 1: Set of CPU interrupt to dispatch */
mov a3, sp /* Argument 2: Top of stack = register save area */
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
* 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.
*/
beq a2, a12, 3f /* If timer interrupt then keep stack */
beq a2, sp, 3f /* If no context switch then keep stack */
/* Switch stacks */
@ -180,53 +160,42 @@
l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */
addi sp, a2, -(4 * XCPTCONTEXT_SIZE)
3:
j ._xtensa_dispatch_level&level& /* Check for more interrupts */
#else
/* 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
/* 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.
/* Call xtensa_int_decode with, passing that address of the register save
* area as a parameter (A2).
*/
#ifdef __XTENSA_CALL0_ABI__
movi a2, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */
mov a3, a12 /* Argument 2: Top of stack = register save area */
call0 xtensa_irq_dispatch /* Call xtensa_int_decode */
#else
movi a6, XTENSA_IRQ_TIMER0 /* Argument 1: Timer0 IRQ number */
mov a7, a12 /* Argument 2: Top of stack = register save area */
call4 xtensa_irq_dispatch /* Call xtensa_int_decode */
/* Argument 1: Set of CPU interrupt to dispatch */
mov a7, sp /* Argument 2: Top of stack = register save area */
call4 xtensa_int_decode /* Call xtensa_int_decode */
/* 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.
* But in the event of a context switch, a2 will instead refer to the TCB
* 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
/* 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
/****************************************************************************

View File

@ -40,6 +40,7 @@
#include <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
@ -147,6 +148,17 @@
#define ESP32_MAX_PRIORITY 5
#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
****************************************************************************/
@ -156,13 +168,9 @@
*/
#ifdef CONFIG_SMP
static uint32_t g_intenable[CONFIG_SMP_NCPUS];
#else
static uint32_t g_intenable[1];
#endif
/* 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;
#ifdef CONFIG_SMP
static uint32_t g_cpu1_freeints = EPS32_CPUINT_PERIPHSET;
#endif
/* Bitsets for each interrupt priority 1-5 */
@ -311,6 +316,7 @@ int esp32_alloc_cpuint(uint32_t intmask)
int esp32_cpuint_initialize(void)
{
uintptr_t regaddr;
uint8_t *intmap;
#ifdef CONFIG_SMP
int cpu;
#endif
@ -345,6 +351,38 @@ int esp32_cpuint_initialize(void)
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;
}
@ -524,6 +562,7 @@ void esp32_free_cpuint(int cpuint)
void esp32_attach_peripheral(int cpu, int periphid, int cpuint)
{
uintptr_t regaddr;
uint8_t *intmap;
DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS);
DEBUGASSERT(cpuint >= 0 && cpuint <= ESP32_CPUINT_MAX);
@ -533,13 +572,18 @@ void esp32_attach_peripheral(int cpu, int periphid, int cpuint)
if (cpu != 0)
{
regaddr = DPORT_APP_MAP_REGADDR(periphid);
intmap = g_cpu1_intmap;
}
else
#endif
{
regaddr = DPORT_PRO_MAP_REGADDR(periphid);
intmap = g_cpu0_intmap;
}
DEBUGASSERT(intmap[cpuint] == CPUINT_UNASSIGNED);
intmap[cpuint] = periphid + XTENSA_IRQ_FIRSTPERIPH;
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)
{
uintptr_t regaddr;
uint8_t *intmap;
DEBUGASSERT(periphid >= 0 && periphid < ESP32_NPERIPHERALS);
#ifdef CONFIG_SMP
@ -572,12 +617,17 @@ void esp32_detach_peripheral(int cpu, int periphid, int cpuint)
if (cpu != 0)
{
regaddr = DPORT_APP_MAP_REGADDR(periphid);
intmap = g_cpu1_intmap;
}
else
#endif
{
regaddr = DPORT_PRO_MAP_REGADDR(periphid);
intmap = g_cpu0_intmap;
}
DEBUGASSERT(intmap[cpuint] != CPUINT_UNASSIGNED);
intmap[cpuint] = CPUINT_UNASSIGNED;
putreg32(NO_CPUINT, regaddr);
}

View File

@ -42,6 +42,25 @@
#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
****************************************************************************/

View File

@ -39,29 +39,32 @@
#include <nuttx/config.h>
#include <nuttx/sched.h>
#include <arch/chip/irq.h>
#include <stdint.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include "chip/esp32_dport.h"
#include "xtensa.h"
#include "esp32_cpuint.h"
/****************************************************************************
* Private Data
* Private Functions
****************************************************************************/
static const uint8_t g_baseirq[3] =
{
ESP32_IRQ_SREG0,
ESP32_IRQ_SREG1,
ESP32_IRQ_SREG2
};
/****************************************************************************
* Name: xtensa_intclear
****************************************************************************/
static const uint8_t g_nirqs[3] =
static inline void xtensa_intclear(uint32_t mask)
{
ESP32_NIRQS_SREG0,
ESP32_NIRQS_SREG1,
ESP32_NIRQS_SREG2
};
__asm__ __volatile__
(
"movi a2, 0\n"
"wsr %0, INTCLEAR\n"
: "=r"(mask) : :
);
}
/****************************************************************************
* Public Functions
@ -75,80 +78,74 @@ static const uint8_t g_nirqs[3] =
* handling to the registered interrupt handler via xtensa_irq_dispatch().
*
* Input Parameters:
* cpuints - Set of pending interrupts valid for this level
* regs - Saves processor state on the stack
*
* 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
* 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;
uint32_t regval;
uint8_t *intmap;
uint32_t mask;
int regndx;
int bit;
int baseirq;
int nirqs;
#ifdef CONFIG_SMP
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();
if (cpu == 0)
if (cpu != 0)
{
regaddr = DPORT_PRO_INTR_STATUS_0_REG;
intmap = g_cpu1_intmap;
}
else
#endif
{
regaddr = DPORT_APP_INTR_STATUS_0_REG;
intmap = g_cpu0_intmap;
}
/* Process each pending interrupt in each of the three interrupt status
* registers.
*/
/* Skip over zero bits, eight at a time */
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);
if ((regval & mask) != 0)
if ((cpuints & mask) != 0)
{
/* Yes.. Dispatch the interrupt. Note that regs may be
* altered in the case of an interrupt level context switch.
/* Extract the IRQ number from the mapping table */
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
* perhaps we can exit this loop sooner.
/* Clear the bit in the pending interrupt so that perhaps
* we can exit the look early.
*/
regval &= ~mask;
}
cpuints &= ~mask;
}
}