Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

armv7/8m: use pendsv to handle context switch #13606

Merged
merged 6 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions Documentation/guides/zerolatencyinterrupts.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=========================================
High Performance, Zero Latency Interrupts
=========================================
=====================================================================
High Performance: Zero Latency Interrupts, Maskable nested interrupts
=====================================================================

Generic Interrupt Handling
==========================
Expand Down Expand Up @@ -111,6 +111,62 @@ between the high priority interrupt handler and *PendSV* interrupt
handler. A detailed discussion of that custom logic is beyond the
scope of this Wiki page.

The following table shows the priority levels of the Cortex-M family:

.. code-block::

IRQ type Priority
Dataabort 0x00
High prio IRQ1 0x20 (Zero-latency interrupt)
High prio IRQ2 0x30 (Can't call OS API in ISR)
SVC 0x70
Disable IRQ 0x80
(critical-section)
Low prio IRQ 0xB0
PendSV 0xE0

As you can see, the priority levels of the zero-latency interrupts can
beyond the critical section and SVC.
But High prio IRQ can't call OS API.


Maskable Nested Interrupts
==========================

The ARM Cortex-M family supports a feature called *BASEPRI* that can be
used to disable interrupts at a priority level below a certain level.
This feature can be used to support maskable nested interrupts.

Maskable nested interrupts differ from zero-latency interrupts in
that they obey the interrupt masking mechanisms of the system.
For example, setting the BASEPRI register to a specific threshold will
block all interrupts of a lower or equal priority.
However, high-priority interrupts (such as Non-Maskable Interrupts
or zero-latency interrupts) are unaffected by these masks.

This is useful when you have a high-priority interrupt that needs to
be able to interrupt the system, but you also have lower-priority
interrupts that you want to be able to mask.

The following table shows the priority levels of the Cortex-M family:

.. code-block::

IRQ type Priority
Dataabort 0x00
SVC 0x70
Disable IRQ 0x80
(critical-section)
High prio IRQ1 0x90 (Maskable nested interrupt)
High prio IRQ2 0xA0 (Can call OS API in ISR)
Low prio IRQ 0xB0
PendSV 0xE0

As you can see, the priority levels of the maskable nested interrupts
are between the critical section and the low-priority interrupts.
And High prio IRQ can call OS API in ISR.


Nested Interrupt Handling
=========================

Expand Down
1 change: 1 addition & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ config ARCH_CORTEXM0
select ARM_THUMB
select ARCH_ARMV6M
select ARCH_HAVE_IRQPRIO
select ARCH_HAVE_IRQTRIGGER
select ARCH_HAVE_RAMVECTORS
select ARCH_HAVE_RESET
select ARCH_HAVE_HARDFAULT_DEBUG
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/include/armv7-m/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ static inline void up_irq_enable(void)
{
/* In this case, we are always retaining or lowering the priority value */

setbasepri(NVIC_SYSH_PRIORITY_MIN);
setbasepri(0);
__asm__ __volatile__ ("\tcpsie i\n");
}

Expand Down
2 changes: 1 addition & 1 deletion arch/arm/include/armv8-m/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ static inline void up_irq_enable(void)
{
/* In this case, we are always retaining or lowering the priority value */

setbasepri(NVIC_SYSH_PRIORITY_MIN);
setbasepri(0);
__asm__ __volatile__ ("\tcpsie i\n");
}

Expand Down
1 change: 1 addition & 0 deletions arch/arm/src/armv6-m/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set(SRCS
arm_svcall.c
arm_systemreset.c
arm_tcbinfo.c
arm_trigger_irq.c
arm_vectors.c)

if(CONFIG_DEBUG_FEATURES)
Expand Down
1 change: 1 addition & 0 deletions arch/arm/src/armv6-m/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ CMN_ASRCS += arm_exception.S arm_saveusercontext.S
CMN_CSRCS += arm_cpuinfo.c arm_doirq.c arm_hardfault.c arm_initialstate.c
CMN_CSRCS += arm_schedulesigaction.c arm_sigdeliver.c arm_svcall.c
CMN_CSRCS += arm_systemreset.c arm_tcbinfo.c arm_vectors.c
CMN_CSRCS += arm_trigger_irq.c

ifeq ($(CONFIG_DEBUG_FEATURES),y)
CMN_CSRCS += arm_dumpnvic.c
Expand Down
76 changes: 49 additions & 27 deletions arch/arm/src/armv6-m/arm_doirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,25 @@

#include "arm_internal.h"
#include "exc_return.h"
#include "nvic.h"

/****************************************************************************
* Public Functions
****************************************************************************/

void exception_direct(void)
{
int irq = getipsr();

arm_ack_irq(irq);
irq_dispatch(irq, NULL);

if (g_running_tasks[this_cpu()] != this_task())
{
up_trigger_irq(NVIC_IRQ_PENDSV, 0);
}
}

uint32_t *arm_doirq(int irq, uint32_t *regs)
{
struct tcb_s *tcb = this_task();
Expand All @@ -49,52 +63,60 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
PANIC();
#else

if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb->xcp.regs = regs;
up_set_current_regs(regs);
}

/* Acknowledge the interrupt */

arm_ack_irq(irq);

/* Deliver the IRQ */
/* Set current regs for crash dump */

up_set_current_regs(regs);

if (irq == NVIC_IRQ_PENDSV)
{
#ifdef CONFIG_ARCH_HIPRI_INTERRUPT
/* Dispatch the PendSV interrupt */

irq_dispatch(irq, regs);
#endif

irq_dispatch(irq, regs);
up_irq_save();
g_running_tasks[this_cpu()]->xcp.regs = regs;
}
else
{
/* Dispatch irq */

tcb->xcp.regs = regs;
GUIDINGLI marked this conversation as resolved.
Show resolved Hide resolved
irq_dispatch(irq, regs);
}

/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value different
* from the input regs, then the lower level will know that a context
* switch occurred during interrupt processing.
*/

if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb = this_task();
tcb = this_task();

if (regs != tcb->xcp.regs)
{
/* Update scheduler parameters */
/* Update scheduler parameters */

nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);

/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/

g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
}
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
#endif

/* Update the current_regs to NULL. */
/* Clear current regs */

up_set_current_regs(NULL);
}
#endif
up_set_current_regs(NULL);

board_autoled_off(LED_INIRQ);

return regs;
}
1 change: 0 additions & 1 deletion arch/arm/src/armv6-m/arm_svcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ int arm_svcall(int irq, void *context, void *arg)
uint32_t *regs = (uint32_t *)context;
uint32_t cmd;

DEBUGASSERT(regs && regs == up_current_regs());
cmd = regs[REG_R0];

/* The SVCall software interrupt is called with R0 = system call command
Expand Down
87 changes: 87 additions & 0 deletions arch/arm/src/armv6-m/arm_trigger_irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/****************************************************************************
* arch/arm/src/armv6-m/arm_trigger_irq.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/

/****************************************************************************
* Included Files
****************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <assert.h>

#include <nuttx/arch.h>
#include <arch/irq.h>

#include "arm_internal.h"
#include "nvic.h"

#ifdef CONFIG_ARCH_HAVE_IRQTRIGGER

/****************************************************************************
* Public Functions
****************************************************************************/

/****************************************************************************
* Name: up_trigger_irq
*
* Description:
* Trigger an IRQ by software.
*
****************************************************************************/

void up_trigger_irq(int irq, cpu_set_t cpuset)
{
uint32_t pend_bit = 0;

DEBUGASSERT(irq >= NVIC_IRQ_NMI && irq < NR_IRQS);

if (irq >= NVIC_IRQ_FIRST)
{
putreg32(irq - NVIC_IRQ_FIRST, ARMV6M_NVIC2_BASE);
}
else
{
switch (irq)
{
case NVIC_IRQ_PENDSV:
pend_bit = SYSCON_ICSR_PENDSVSET;
break;

case NVIC_IRQ_NMI:
pend_bit = SYSCON_ICSR_NMIPENDSET;
break;

case NVIC_IRQ_SYSTICK:
pend_bit = SYSCON_ICSR_PENDSTSET;
break;

default:
break;
}

if (pend_bit)
{
modifyreg32(ARMV6M_SYSCON_ICSR, 0, pend_bit);
}
}
}

#endif /* CONFIG_ARCH_HAVE_IRQTRIGGER */
9 changes: 7 additions & 2 deletions arch/arm/src/armv6-m/arm_vectors.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@

#include "chip.h"
#include "arm_internal.h"
#include "ram_vectors.h"
#include "nvic.h"

/****************************************************************************
* Pre-processor Definitions
Expand Down Expand Up @@ -81,6 +83,7 @@ static void start(void)
/* Common exception entrypoint */

extern void exception_common(void);
extern void exception_direct(void);

/****************************************************************************
* Public data
Expand All @@ -92,7 +95,7 @@ extern void exception_common(void);
* As all exceptions (interrupts) are routed via exception_common, we just
* need to fill this array with pointers to it.
*
* Note that the [ ... ] designated initialiser is a GCC extension.
* Note that the [ ... ] designated initializer is a GCC extension.
*/

const void * const _vectors[] locate_data(".vectors") =
Expand All @@ -107,5 +110,7 @@ const void * const _vectors[] locate_data(".vectors") =

/* Vectors 2 - n point directly at the generic handler */

[2 ... (15 + ARMV6M_PERIPHERAL_INTERRUPTS)] = exception_common
[2 ... NVIC_IRQ_PENDSV] = &exception_common,
[(NVIC_IRQ_PENDSV + 1) ... (15 + ARMV6M_PERIPHERAL_INTERRUPTS)]
= &exception_direct
};
25 changes: 25 additions & 0 deletions arch/arm/src/armv6-m/nvic.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@
* Pre-processor Definitions
****************************************************************************/

/* Exception/interrupt vector numbers ***************************************/

/* Vector 0: Reset stack
* pointer value
*/

/* Vector 1: Reset */
#define NVIC_IRQ_NMI (2) /* Vector 2: Non-Maskable Interrupt (NMI) */
#define NVIC_IRQ_HARDFAULT (3) /* Vector 3: Hard fault */
#define NVIC_IRQ_MEMFAULT (4) /* Vector 4: Memory management (MPU) */
#define NVIC_IRQ_BUSFAULT (5) /* Vector 5: Bus fault */
#define NVIC_IRQ_USAGEFAULT (6) /* Vector 6: Usage fault */
/* Vectors 7-10: Reserved */
#define NVIC_IRQ_SVCALL (11) /* Vector 11: SVC call */
#define NVIC_IRQ_DBGMONITOR (12) /* Vector 12: Debug Monitor */
/* Vector 13: Reserved */
#define NVIC_IRQ_PENDSV (14) /* Vector 14: Pendable system service request */
#define NVIC_IRQ_SYSTICK (15) /* Vector 15: System tick */

/* External interrupts (vectors >= 16).
* These definitions are chip-specific
*/

#define NVIC_IRQ_FIRST (16) /* Vector number of the first interrupt */

/* Base addresses ***********************************************************/

#define ARMV6M_SYSCON1_BASE 0xe000e008 /* 0xe000e008-0xe000e00f System Control Block */
Expand Down
Loading
Loading