arch: arc: add nested interrupt support

* add nested interrupt support for interrupts
   + use a varibale exc_nest_count to trace nest interrupt and exception
   + regular interrupts can be nested by regular interrupts and fast
interrupts
   + fast interrupt's priority is the highest, cannot be nested
* remove the firq stack and exception stack
   + remove the coressponding kconfig option
   + all interrupts (normal and fast) and exceptions will be handled
     in the same stack (_interrupt stack)
   + the pros are, smaller memory footprint (no firq stack), simpler
     stack management, simpler codes, etc.. The cons are, possible
     10-15 instructions overhead for the case where fast irq nests
     regular irq
* add the case of ARC in test/kernel/gen_isr_table

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Wayne Ren 2017-07-11 10:39:54 +08:00 committed by Anas Nashif
parent 072ef10baf
commit f8d061faf7
13 changed files with 158 additions and 247 deletions

View file

@ -103,15 +103,6 @@ config RGF_NUM_BANKS
register bank, the fast interrupt handler must save
and restore general purpose registers.
config FIRQ_STACK_SIZE
int
prompt "Size of stack for FIRQs (in bytes)"
depends on CPU_ARCV2
default 1024
help
FIRQs and regular IRQs have different stacks so that a FIRQ can start
running without doing stack switching in software.
config ARC_STACK_CHECKING
bool "Enable Stack Checking"
depends on CPU_ARCV2

View file

@ -21,27 +21,13 @@
GTEXT(_firq_enter)
GTEXT(_firq_exit)
GTEXT(_firq_stack_setup)
GTEXT(_firq_stack_suspend)
GTEXT(_firq_stack_resume)
#if CONFIG_RGF_NUM_BANKS != 1
GDATA(_firq_stack)
GDATA(_saved_firq_stack)
SECTION_VAR(NOINIT, _firq_stack)
.space CONFIG_FIRQ_STACK_SIZE
#else
GDATA(saved_r0)
#endif
.macro _firq_return
GDATA(exc_nest_count)
#if CONFIG_RGF_NUM_BANKS == 1
b _firq_no_reschedule
GDATA(saved_r0)
#else
rtie
GDATA(saved_sp)
#endif
.endm
/**
*
@ -66,7 +52,6 @@ GDATA(saved_r0)
*/
SECTION_FUNC(TEXT, _firq_enter)
/*
* ATTENTION:
* If CONFIG_RGF_NUM_BANKS>1, firq uses a 2nd register bank so GPRs do
@ -74,7 +59,6 @@ SECTION_FUNC(TEXT, _firq_enter)
* If CONFIG_RGF_NUM_BANKS==1, firq must use the stack to save registers.
* This has already been done by _isr_wrapper.
*/
#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking */
lr r2, [_ARC_V2_STATUS32]
@ -95,8 +79,41 @@ SECTION_FUNC(TEXT, _firq_enter)
#endif
#endif
ld r1, [exc_nest_count]
add r0, r1, 1
st r0, [exc_nest_count]
cmp r1, 0
bgt.d firq_nest
mov r0, sp
mov r1, _kernel
ld sp, [r1, _kernel_offset_to_irq_stack]
#if CONFIG_RGF_NUM_BANKS != 1
b firq_nest_1
firq_nest:
mov r1, ilink
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
kflag r0
st sp, [saved_sp]
lr ilink, [_ARC_V2_STATUS32]
or ilink, ilink, _ARC_V2_STATUS32_RB(1)
kflag ilink
mov r0, sp
ld sp, [saved_sp]
mov ilink, r1
firq_nest_1:
#else
firq_nest:
#endif
push_s r0
j @_isr_demux
/**
*
* @brief Work to be done exiting a FIRQ
@ -106,6 +123,8 @@ SECTION_FUNC(TEXT, _firq_enter)
SECTION_FUNC(TEXT, _firq_exit)
pop sp
#if CONFIG_RGF_NUM_BANKS != 1
#ifndef CONFIG_FIRQ_NO_LPCC
/* restore lp_count, lp_start, lp_end from r23-r25 */
@ -114,29 +133,20 @@ SECTION_FUNC(TEXT, _firq_exit)
sr r25, [_ARC_V2_LP_END]
#endif
#endif
/* check if we're a nested interrupt: if so, let the interrupted
* interrupt handle the reschedule */
mov r1, exc_nest_count
ld r0, [r1]
sub r0, r0, 1
cmp r0, 0
bne.d _firq_no_reschedule
st r0, [r1]
#ifdef CONFIG_PREEMPT_ENABLED
mov_s r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
#if CONFIG_NUM_IRQ_PRIO_LEVELS > 1
/* check if we're a nested interrupt: if so, let the interrupted
* interrupt handle the reschedule */
lr r3, [_ARC_V2_AUX_IRQ_ACT]
/* the OS on ARCv2 always runs in kernel mode, so assume bit31 [U] in
* AUX_IRQ_ACT is always 0: if the contents of AUX_IRQ_ACT is not 1, it
* means that another bit is set so an interrupt was interrupted.
*/
breq r3, 1, _firq_check_for_swap
_firq_return
#endif
.balign 4
_firq_check_for_swap:
/*
* Non-preemptible thread ? Do not schedule (see explanation of
* preempt field in kernel_struct.h).
@ -180,7 +190,6 @@ _firq_no_reschedule:
sr r0, [_ARC_V2_LP_START]
pop_s r0
mov lp_count,r0
ld r0,[saved_r0]
#ifdef CONFIG_CODE_DENSITY
pop_s r0
sr r0, [_ARC_V2_EI_BASE]
@ -189,6 +198,7 @@ _firq_no_reschedule:
pop_s r0
sr r0, [_ARC_V2_JLI_BASE]
#endif
ld r0,[saved_r0]
add sp,sp,8 /* don't need ilink & status32_po from stack */
#endif
rtie
@ -295,84 +305,3 @@ _firq_return_from_firq:
rtie
#endif /* CONFIG_PREEMPT_ENABLED */
/**
*
* @brief Install the FIRQ stack in register bank 1 if CONFIG_RGF_NUM_BANK!=1
*
* @return N/A
*/
SECTION_FUNC(TEXT, _firq_stack_setup)
#if CONFIG_RGF_NUM_BANKS != 1
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
or r0, r0, _ARC_V2_STATUS32_RB(1)
kflag r0
mov sp, _firq_stack
add sp, sp, CONFIG_FIRQ_STACK_SIZE
/*
* We have to reload r0 here, because it is bank1 r0 which contains
* garbage, not bank0 r0 containing the previous value of status32.
*/
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
kflag r0
#endif
j_s [blink]
/**
*
* @brief Save the FIRQ context if CONFIG_RGF_NUM_BANK!=1
*
* @return N/A
*/
SECTION_FUNC(TEXT, _firq_stack_suspend)
#if CONFIG_RGF_NUM_BANKS != 1
/* Switch to bank 1 */
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
or r0, r0, _ARC_V2_STATUS32_RB(1)
kflag r0
st sp, [_saved_firq_stack]
/* Switch back to bank 0 */
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
kflag r0
#endif
j_s [blink]
/**
*
* @brief Restore the FIRQ context if CONFIG_RGF_NUM_BANK!=1
*
* @return N/A
*/
SECTION_FUNC(TEXT, _firq_stack_resume)
#if CONFIG_RGF_NUM_BANKS != 1
/* Switch to bank 1 */
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
or r0, r0, _ARC_V2_STATUS32_RB(1)
kflag r0
ld sp, [_saved_firq_stack]
/* Switch back to bank 0 */
lr r0, [_ARC_V2_STATUS32]
and r0, r0, ~_ARC_V2_STATUS32_RB(7)
kflag r0
#endif
j_s [blink]

View file

@ -33,18 +33,7 @@ GTEXT(__ev_div_zero)
GTEXT(__ev_dc_error)
GTEXT(__ev_maligned)
SECTION_VAR(BSS, saved_stack_pointer)
.balign 4
.word 0
#if CONFIG_RGF_NUM_BANKS == 1
GDATA(_exception_stack)
SECTION_VAR(NOINIT, _exception_stack)
.space 512
/* note: QUARK_SE_C1000_SS can't afford 512B */
#else
GDATA(_firq_stack)
#endif
GDATA(exc_nest_count)
/*
* @brief Fault handler installed in the fault and reserved vectors
@ -82,15 +71,6 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned)
pop_s r2
#endif
st sp, [saved_stack_pointer]
#if CONFIG_RGF_NUM_BANKS == 1
mov_s sp, _exception_stack
add sp, sp, 512
#else
mov_s sp, _firq_stack
add sp, sp, CONFIG_FIRQ_STACK_SIZE
#endif
/* save caller saved registers */
_create_irq_stack_frame
@ -99,13 +79,32 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned)
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
ld r1, [exc_nest_count]
add r0, r1, 1
st r0, [exc_nest_count]
cmp r1, 0
bgt.d exc_nest_handle
mov r0, sp
mov r1, _kernel
ld sp, [r1, _kernel_offset_to_irq_stack]
exc_nest_handle:
push_s r0
jl _Fault
pop sp
mov r1, exc_nest_count
ld r0, [r1]
sub r0, r0, 1
st r0, [r1]
/* if _Fault returns, restore the registers */
_pop_irq_stack_frame
/* now restore the stack */
ld sp,[saved_stack_pointer]
rtie
#ifdef CONFIG_IRQ_OFFLOAD
@ -136,26 +135,41 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_trap)
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
ld r1, [exc_nest_count]
add r0, r1, 1
st r0, [exc_nest_count]
cmp r1, 0
bgt.d trap_nest_handle
mov r0, sp
mov r1, _kernel
ld sp, [r1, _kernel_offset_to_irq_stack]
trap_nest_handle:
push_s r0
jl _irq_do_offload
mov_s r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
pop sp
/* check if we're a nested interrupt: if so, let the
* interrupted interrupt handle the reschedule
*/
lr r3, [_ARC_V2_AUX_IRQ_ACT]
/* the OS on ARCv2 always runs in kernel mode, so assume bit31 [U] in
* AUX_IRQ_ACT is always 0: if the contents of AUX_IRQ_ACT is 0, it
* means trap was taken from outside an interrupt handler.
* But if it was inside, let that handler do the swap.
*/
breq r3, 0, _trap_check_for_swap
mov r1, exc_nest_count
ld r0, [r1]
sub r0, r0, 1
cmp r0, 0
beq.d _trap_check_for_swap
st r0, [r1]
_trap_return:
_pop_irq_stack_frame
rtie
.balign 4
_trap_check_for_swap:
mov_s r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
/*
* Non-preemptible thread ? Do not schedule (see explanation of
* preempt field in kernel_struct.h).

View file

@ -23,13 +23,24 @@
GTEXT(_isr_wrapper)
GTEXT(_isr_demux)
GDATA(exc_nest_count)
SECTION_VAR(BSS, exc_nest_count)
.balign 4
.word 0
#if CONFIG_RGF_NUM_BANKS == 1
GDATA(saved_r0)
SECTION_VAR(BSS, saved_r0)
.balign 4
.word 0
#else
GDATA(saved_sp)
SECTION_VAR(BSS, saved_sp)
.balign 4
.word 0
#endif
#if defined(CONFIG_SYS_POWER_MANAGEMENT)
@ -231,6 +242,14 @@ SECTION_FUNC(TEXT, _isr_wrapper)
push_s r0
mov r0,ilink
push_s r0
#ifdef CONFIG_CODE_DENSITY
lr r0, [_ARC_V2_JLI_BASE]
push_s r0
lr r0, [_ARC_V2_LDI_BASE]
push_s r0
lr r0, [_ARC_V2_EI_BASE]
push_s r0
#endif
mov r0,lp_count
push_s r0
lr r0, [_ARC_V2_LP_START]
@ -326,6 +345,7 @@ _skip_sys_power_save_idle_exit:
SECTION_FUNC(TEXT, _isr_demux)
push_s r3
/* cannot be done before this point because we must be able to run C */
/* r0 is available to be stomped here, and exit_tickless_idle uses it */
exit_tickless_idle
@ -333,6 +353,12 @@ SECTION_FUNC(TEXT, _isr_demux)
log_sleep_k_event
lr r0, [_ARC_V2_ICAUSE]
/* handle software triggered interrupt */
lr r3, [_ARC_V2_AUX_IRQ_HINT]
brne r3, r0, irq_hint_handled
sr 0, [_ARC_V2_AUX_IRQ_HINT]
irq_hint_handled:
sub r0, r0, 16
mov r1, _sw_isr_table

View file

@ -23,6 +23,7 @@
GTEXT(_rirq_enter)
GTEXT(_rirq_exit)
GTEXT(_rirq_common_interrupt_swap)
GDATA(exc_nest_count)
#if 0 /* TODO: when FIRQ is not present, all would be regular */
#define NUM_REGULAR_IRQ_PRIO_LEVELS CONFIG_NUM_IRQ_PRIO_LEVELS
@ -34,14 +35,6 @@ GTEXT(_rirq_common_interrupt_swap)
* TODO: Revist this if FIRQ becomes configurable.
*/
#if NUM_REGULAR_IRQ_PRIO_LEVELS > 1
#error "nested regular interrupts are not supported."
/*
* Nesting of Regularing interrupts is not yet supported.
* Set CONFIG_NUM_IRQ_PRIO_LEVELS to 2 even if SOC supports more.
*/
#endif
/**
*
@ -58,20 +51,28 @@ GTEXT(_rirq_common_interrupt_swap)
SECTION_FUNC(TEXT, _rirq_enter)
mov r1, _kernel
#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking */
lr r2, [_ARC_V2_STATUS32]
bclr r2, r2, _ARC_V2_STATUS32_SC_BIT
kflag r2
#endif
ld_s r2, [r1, _kernel_offset_to_current]
#if NUM_REGULAR_IRQ_PRIO_LEVELS == 1
st sp, [r2, _thread_offset_to_sp]
clri
ld r1, [exc_nest_count]
add r0, r1, 1
st r0, [exc_nest_count]
cmp r1, 0
bgt.d rirq_nest
mov r0, sp
mov r1, _kernel
ld sp, [r1, _kernel_offset_to_irq_stack]
#else
#error regular irq nesting is not implemented
#endif
rirq_nest:
push_s r0
seti
j _isr_demux
@ -83,6 +84,16 @@ SECTION_FUNC(TEXT, _rirq_enter)
*/
SECTION_FUNC(TEXT, _rirq_exit)
clri
pop sp
mov r1, exc_nest_count
ld r0, [r1]
sub r0, r0, 1
cmp r0, 0
bne.d _rirq_return_from_rirq
st r0, [r1]
#ifdef CONFIG_PREEMPT_ENABLED
@ -94,26 +105,6 @@ SECTION_FUNC(TEXT, _rirq_exit)
* point on until return from interrupt.
*/
clri
#if NUM_REGULAR_IRQ_PRIO_LEVELS > 1
/* check if we're a nested interrupt: if so, let the interrupted interrupt
* handle the reschedule */
lr r3, [_ARC_V2_AUX_IRQ_ACT]
ffs r0, r3
asl r0, 1, r0
/* the OS on ARCv2 always runs in kernel mode, so assume bit31 [U] in
* AUX_IRQ_ACT is always 0: if the contents of AUX_IRQ_ACT is greater
* than FFS(AUX_IRQ_ACT), it means that another bit is set so an
* interrupt was interrupted.
*/
cmp r0, r3
brgt _rirq_return_from_rirq
#endif
/*
* Non-preemptible thread ? Do not schedule (see explanation of
@ -134,8 +125,6 @@ SECTION_FUNC(TEXT, _rirq_exit)
* b) needs to load it to restore the interrupted context.
*/
ld sp, [r2, _thread_offset_to_sp]
/* check if the current thread needs to be rescheduled */
ld_s r0, [r1, _kernel_offset_to_ready_q_cache]
cmp_s r0, r2
@ -218,10 +207,6 @@ _rirq_return_from_coop:
/* fall through to rtie instruction */
.balign 4
_rirq_return_from_firq:
_rirq_return_from_rirq:
/* rtie will pop the rest from the stack */
/* fall through to rtie instruction */
@ -229,6 +214,8 @@ _rirq_return_from_rirq:
#endif /* CONFIG_PREEMPT_ENABLED */
.balign 4
_rirq_return_from_firq:
_rirq_return_from_rirq:
_rirq_no_reschedule:
rtie

View file

@ -17,26 +17,13 @@
#include <arch/cpu.h>
GDATA(_interrupt_stack)
GDATA(_firq_stack)
GDATA(_main_stack)
/* use one of the available interrupt stacks during init */
/* FIRQ only ? */
#if CONFIG_NUM_IRQ_PRIO_LEVELS == 1
/* FIRQ, but uses _interrupt_stack ? */
#if CONFIG_RGF_NUM_BANKS == 1
#define INIT_STACK _interrupt_stack
#define INIT_STACK_SIZE CONFIG_ISR_STACK_SIZE
#else
#define INIT_STACK _firq_stack
#define INIT_STACK_SIZE CONFIG_FIRQ_STACK_SIZE
#endif
#else
#define INIT_STACK _interrupt_stack
#define INIT_STACK_SIZE CONFIG_ISR_STACK_SIZE
#endif
#define INIT_STACK _interrupt_stack
#define INIT_STACK_SIZE CONFIG_ISR_STACK_SIZE
GTEXT(__reset)
GTEXT(__start)
@ -106,13 +93,6 @@ done_cache_invalidate:
mov_s r2, CONFIG_ISR_STACK_SIZE
jl memset
#if CONFIG_RGF_NUM_BANKS != 1
mov_s r0, _firq_stack
mov_s r1, 0xaa
mov_s r2, CONFIG_FIRQ_STACK_SIZE
jl memset
#endif
#endif /* CONFIG_INIT_STACKS */
mov sp, INIT_STACK

View file

@ -32,7 +32,6 @@ extern "C" {
#ifndef _ASMLANGUAGE
extern void _firq_stack_setup(void);
extern K_THREAD_STACK_DEFINE(_interrupt_stack, CONFIG_ISR_STACK_SIZE);
/*
@ -56,7 +55,6 @@ static ALWAYS_INLINE void _irq_setup(void)
_kernel.irq_stack =
K_THREAD_STACK_BUFFER(_interrupt_stack) + CONFIG_ISR_STACK_SIZE;
_firq_stack_setup();
}
#endif /* _ASMLANGUAGE */

View file

@ -13,9 +13,7 @@ config SOC
config NUM_IRQ_PRIO_LEVELS
# This processor supports 4 priority levels:
# 0 for Fast Interrupts (FIRQs) and 1-3 for Regular Interrupts (IRQs).
# TODO: But regular irq nesting is not implemented --
# so this must be 2 for now.
default 2
default 4
config NUM_IRQS
# must be > the highest interrupt number used

View file

@ -13,9 +13,7 @@ config SOC
config NUM_IRQ_PRIO_LEVELS
# This processor supports 4 priority levels:
# 0 for Fast Interrupts (FIRQs) and 1-3 for Regular Interrupts (IRQs).
# TODO: But regular irq nesting is not implemented --
# so this must be 2 for now.
default 2
default 4
config NUM_IRQS
# must be > the highest interrupt number used

View file

@ -13,9 +13,7 @@ config SOC
config NUM_IRQ_PRIO_LEVELS
# This processor supports 4 priority levels:
# 0 for Fast Interrupts (FIRQs) and 1-3 for Regular Interrupts (IRQs).
# TODO: But regular irq nesting is not implemented --
# so this must be 2 for now.
default 2
default 4
config NUM_IRQS
# must be > the highest interrupt number used

View file

@ -29,7 +29,6 @@ extern void *_VectorTable;
#include <v2/irq.h>
static u32_t _arc_v2_irq_unit_device_power_state = DEVICE_PM_ACTIVE_STATE;
u32_t _saved_firq_stack;
struct arc_v2_irq_unit_ctx {
u32_t irq_ctrl; /* Interrupt Context Saving Control Register. */
u32_t irq_vect_base; /* Interrupt Vector Base. */
@ -97,8 +96,6 @@ unsigned int _arc_v2_irq_unit_trigger_get(int irq)
}
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
extern void _firq_stack_suspend(void);
extern void _firq_stack_resume(void);
static int _arc_v2_irq_unit_suspend(struct device *dev)
{
@ -123,8 +120,6 @@ static int _arc_v2_irq_unit_suspend(struct device *dev)
ctx.irq_ctrl = _arc_v2_aux_reg_read(_ARC_V2_AUX_IRQ_CTRL);
ctx.irq_vect_base = _arc_v2_aux_reg_read(_ARC_V2_IRQ_VECT_BASE);
_firq_stack_suspend();
_arc_v2_irq_unit_device_power_state = DEVICE_PM_SUSPEND_STATE;
return 0;
@ -137,8 +132,6 @@ static int _arc_v2_irq_unit_resume(struct device *dev)
ARG_UNUSED(dev);
_firq_stack_resume();
/* Interrupts from 0 to 15 are exceptions and they are ignored
* by IRQ auxiliary registers. For that reason we skip those
* values in this loop.

View file

@ -124,9 +124,6 @@ extern void idle(void *unused1, void *unused2, void *unused3);
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_PRINTK)
extern K_THREAD_STACK_DEFINE(sys_work_q_stack,
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE);
#if defined(CONFIG_ARC) && CONFIG_RGF_NUM_BANKS != 1
extern K_THREAD_STACK_DEFINE(_firq_stack, CONFIG_FIRQ_STACK_SIZE);
#endif /* CONFIG_ARC */
void k_call_stacks_analyze(void)
@ -134,9 +131,6 @@ void k_call_stacks_analyze(void)
printk("Kernel stacks:\n");
STACK_ANALYZE("main ", _main_stack);
STACK_ANALYZE("idle ", _idle_stack);
#if defined(CONFIG_ARC) && CONFIG_RGF_NUM_BANKS != 1
STACK_ANALYZE("firq ", _firq_stack);
#endif /* CONFIG_ARC */
STACK_ANALYZE("interrupt", _interrupt_stack);
STACK_ANALYZE("workqueue", sys_work_q_stack);
}

View file

@ -59,6 +59,11 @@ void trigger_irq(int irq)
: "=r" (mip)
: "r" (1 << irq));
}
#elif defined(CONFIG_CPU_ARCV2)
void trigger_irq(int irq)
{
_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_HINT, irq);
}
#else
/* So far, Nios II does not support this */
#define NO_TRIGGER_FROM_SW