Skip to content
Permalink
Browse files

arch: arc: implement z_arch_switch to replace swap

* here use new style z_arch_switch,i.e. CONFIG_USE_SWITCH
to replace old swap mechnism.

* it's also required by SMP support

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
  • Loading branch information...
vonhust authored and nashif committed Jun 13, 2019
1 parent ffe25df commit 890b9ebab73b0a5c443c370fdb106eb68d07f853
@@ -30,6 +30,8 @@ config CPU_ARCV2
bool
select ARCH_HAS_STACK_PROTECTION if ARC_HAS_STACK_CHECKING || ARC_MPU
select ARCH_HAS_USERSPACE if ARC_MPU
select USE_SWITCH
select USE_SWITCH_SUPPORTED
default y
help
This option signifies the use of a CPU of the ARCv2 family.
@@ -14,7 +14,7 @@ zephyr_library_sources(
timestamp.c
isr_wrapper.S
regular_irq.S
swap.S
switch.S
sys_fatal_error_handler.c
prep_c.c
reset.S
@@ -249,27 +249,13 @@ _firq_reschedule:

.balign 4
_firq_return_from_coop:

ld_s r3, [r2, _thread_offset_to_intlock_key]
st 0, [r2, _thread_offset_to_intlock_key]

/* pc into ilink */
pop_s r0
mov ilink, r0

pop_s r0 /* status32 into r0 */
/*
* There are only two interrupt lock states: locked and unlocked. When
* entering z_swap(), they are always locked, so the IE bit is unset in
* status32. If the incoming thread had them locked recursively, it
* means that the IE bit should stay unset. The only time the bit
* has to change is if they were not locked recursively.
*/
and.f r3, r3, (1 << 4)
or.nz r0, r0, _ARC_V2_STATUS32_IE
sr r0, [_ARC_V2_STATUS32_P0]

ld_s r0, [r2, _thread_offset_to_return_value]
rtie

.balign 4
@@ -130,7 +130,7 @@ _exc_return:
kflag r3
/* pretend lowest priority interrupt happened to use common handler */
lr r3, [_ARC_V2_AUX_IRQ_ACT]
or r3,r3,(1<<(CONFIG_NUM_IRQ_PRIO_LEVELS-1)) /* use lowest */
or r3,r3,(1 << (CONFIG_NUM_IRQ_PRIO_LEVELS - 1)) /* use lowest */
sr r3, [_ARC_V2_AUX_IRQ_ACT]

/* Assumption: r2 has current thread */
@@ -62,7 +62,7 @@ Context switch explanation:
The context switch code is spread in these files:
isr_wrapper.s, swap.s, swap_macros.s, fast_irq.s, regular_irq.s
isr_wrapper.s, switch.s, swap_macros.s, fast_irq.s, regular_irq.s
IRQ stack frame layout:
@@ -92,7 +92,7 @@ The context switch code adopts this standard so that it is easier to follow:
transition from outgoing thread to incoming thread
Not loading _kernel into r0 allows loading _kernel without stomping on
the parameter in r0 in z_swap().
the parameter in r0 in z_arch_switch().
ARCv2 processors have two kinds of interrupts: fast (FIRQ) and regular. The
@@ -174,7 +174,7 @@ From coop:
o to coop
Restore interrupt lock level and do a normal function call return.
Do a normal function call return.
o to any irq
@@ -192,9 +192,8 @@ From FIRQ:
o to coop
The address of the returning instruction from z_swap() is loaded in ilink and
the saved status32 in status32_p0, taking care to adjust the interrupt lock
state desired in status32_p0. The return value is put in r0.
The address of the returning instruction from z_arch_switch() is loaded
in ilink and the saved status32 in status32_p0.
o to any irq
@@ -207,7 +206,7 @@ From RIRQ:
The interrupt return mechanism in the processor expects a stack frame, but
the outgoing context did not create one. A fake one is created here, with
only the relevant values filled in: pc, status32 and the return value in r0.
only the relevant values filled in: pc, status32.
There is a discrepancy between the ABI from the ARCv2 docs, including the
way the processor pushes GPRs in pairs in the IRQ stack frame, and the ABI
@@ -26,9 +26,7 @@
#include <kernel_structs.h>
#include <kernel_offsets.h>

GEN_OFFSET_SYM(_thread_arch_t, intlock_key);
GEN_OFFSET_SYM(_thread_arch_t, relinquish_cause);
GEN_OFFSET_SYM(_thread_arch_t, return_value);
#ifdef CONFIG_ARC_STACK_CHECKING
GEN_OFFSET_SYM(_thread_arch_t, k_stack_base);
GEN_OFFSET_SYM(_thread_arch_t, k_stack_top);
@@ -193,27 +193,9 @@ _rirq_common_interrupt_swap:
.balign 4
_rirq_return_from_coop:

/*
* status32, sec_stat (when CONFIG_ARC_HAS_SECURE is enabled) and pc
* (blink) are already on the stack in the right order
*/
ld_s r0, [sp, ___isf_t_status32_OFFSET - ___isf_t_pc_OFFSET]

/* update status32.ie (explanation in firq_exit:_firq_return_from_coop) */

ld_s r3, [r2, _thread_offset_to_intlock_key]
st 0, [r2, _thread_offset_to_intlock_key]
cmp r3, 0
or.ne r0, r0, _ARC_V2_STATUS32_IE

st_s r0, [sp, ___isf_t_status32_OFFSET - ___isf_t_pc_OFFSET]

/* carve fake stack */
sub sp, sp, ___isf_t_pc_OFFSET

/* update return value on stack */
ld_s r0, [r2, _thread_offset_to_return_value]
st_s r0, [sp, ___isf_t_r0_OFFSET]

/* reset zero-overhead loops */
st 0, [sp, ___isf_t_lp_end_OFFSET]
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2015 Wind River Systems, Inc.
* Copyright (c) 2019 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -21,79 +21,63 @@
#include <v2/irq.h>
#include <swap_macros.h>

GTEXT(__swap)
GDATA(_k_neg_eagain)
GDATA(_kernel)
GTEXT(z_arch_switch)

/**
*
* @brief Initiate a cooperative context switch
*
* The __swap() routine is invoked by various kernel services to effect
* a cooperative context switch. Prior to invoking __swap(), the caller
* disables interrupts via irq_lock() and the return 'key' is passed as a
* parameter to __swap(). The key is in fact the value stored in the register
* operand of a CLRI instruction.
*
* It stores the intlock key parameter into current->intlock_key.
* The z_arch_switch routine is invoked by various kernel services to effect
* a cooperative context switch. Prior to invoking z_arch_switch, the caller
* disables interrupts via irq_lock()
* Given that __swap() is called to effect a cooperative context switch,
* Given that z_arch_switch() is called to effect a cooperative context switch,
* the caller-saved integer registers are saved on the stack by the function
* call preamble to __swap(). This creates a custom stack frame that will be
* popped when returning from __swap(), but is not suitable for handling a
* return from an exception. Thus, the fact that the thread is pending because
* of a cooperative call to __swap() has to be recorded via the _CAUSE_COOP code
* in the relinquish_cause of the thread's k_thread structure. The
* _IrqExit()/_FirqExit() code will take care of doing the right thing to
* restore the thread status.
* call preamble to z_arch_switch. This creates a custom stack frame that will
* be popped when returning from z_arch_switch, but is not suitable for handling
* a return from an exception. Thus, the fact that the thread is pending because
* of a cooperative call to z_arch_switch() has to be recorded via the
* _CAUSE_COOP code in the relinquish_cause of the thread's k_thread structure.
* The _rirq_exit()/_firq_exit() code will take care of doing the right thing
* to restore the thread status.
*
* When __swap() is invoked, we know the decision to perform a context switch or
* not has already been taken and a context switch must happen.
* When z_arch_switch() is invoked, we know the decision to perform a context
* switch or not has already been taken and a context switch must happen.
*
* @return may contain a return value setup by a call to
* z_set_thread_return_value()
*
* C function prototype:
*
* unsigned int __swap (unsigned int key);
* void z_arch_switch(void *switch_to, void **switched_from);
*
*/

SECTION_FUNC(TEXT, __swap)
SECTION_FUNC(TEXT, z_arch_switch)

#ifdef CONFIG_EXECUTION_BENCHMARKING
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_save_callee_saved_regs
push_s r31
push_s r0
push_s r1
push_s blink

bl read_timer_start_of_swap

pop_s r31
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_load_callee_saved_regs
st sp, [r2, _thread_offset_to_sp]
pop_s blink
pop_s r1
pop_s r0
#endif
/*
* r0 = new_thread->switch_handle = switch_to thread,
* r1 = &old_thread->switch_handle = &switch_from thread
*/

/* interrupts are locked, interrupt key is in r0 */

mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
ld_s r2, [r1]
/*
* r2 may be dummy_thread in z_cstart, dummy_thread->switch_handle
* must be 0
*/
breq r2, 0, _switch_to_target_thread

/* save intlock key */
st_s r0, [r2, _thread_offset_to_intlock_key]
st _CAUSE_COOP, [r2, _thread_offset_to_relinquish_cause]

/*
* Carve space for the return value. Setting it to a default of
* -EAGAIN eliminates the need for the timeout code to set it.
* If another value is ever needed, it can be modified with
* z_set_thread_return_value().
*/
ld r3, [_k_neg_eagain]
st_s r3, [r2, _thread_offset_to_return_value]

/*
* Save status32 and blink on the stack before the callee-saved registers.
* This is the same layout as the start of an IRQ stack frame.
@@ -106,57 +90,53 @@ SECTION_FUNC(TEXT, __swap)
push_s r3
#endif

push_s blink

_save_callee_saved_regs

#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking here, as sp will be changed to target
* thread'sp
*/
#ifdef CONFIG_ARC_HAS_SECURE
bclr r3, r3, _ARC_V2_SEC_STAT_SSC_BIT
/* sflag r3 */
/* sflag instruction is not supported in current ARC GNU */
.long 0x00ff302f
#else
/* disable stack checking during swap */
bclr r3, r3, _ARC_V2_STATUS32_SC_BIT
kflag r3
#endif
#endif

push_s blink

_save_callee_saved_regs
_switch_to_target_thread:

/* get the cached thread to run */
ld_s r2, [r1, _kernel_offset_to_ready_q_cache]
mov r2, r0

/* entering here, r2 contains the new current thread */
#ifdef CONFIG_ARC_STACK_CHECKING
_load_stack_check_regs
#endif
/* XXX - can be moved to delay slot of _CAUSE_RIRQ ? */
st_s r2, [r1, _kernel_offset_to_current]

_load_callee_saved_regs

#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
push_s r2
mov r0, r2
bl configure_mpu_thread
pop_s r2
#endif

ld_s r3, [r2, _thread_offset_to_relinquish_cause]

breq r3, _CAUSE_RIRQ, _swap_return_from_rirq
breq r3, _CAUSE_RIRQ, _switch_return_from_rirq
nop
breq r3, _CAUSE_FIRQ, _swap_return_from_firq
breq r3, _CAUSE_FIRQ, _switch_return_from_firq
nop

/* fall through to _swap_return_from_coop */
/* fall through to _switch_return_from_coop */

.balign 4
_swap_return_from_coop:

ld_s r1, [r2, _thread_offset_to_intlock_key]
st 0, [r2, _thread_offset_to_intlock_key]
ld_s r0, [r2, _thread_offset_to_return_value]
_switch_return_from_coop:

lr ilink, [_ARC_V2_STATUS32]
bbit1 ilink, _ARC_V2_STATUS32_AE_BIT, _return_from_exc
@@ -168,21 +148,19 @@ _swap_return_from_coop:
/* sflag instruction is not supported in current ARC GNU */
.long 0x00ff302f
#endif
pop_s r3 /* status32 into r3 */
kflag r3 /* write status32 */

#ifdef CONFIG_EXECUTION_BENCHMARKING
b _capture_value_for_benchmarking
#endif
return_loc:

pop_s r3 /* status32 into r3 */
kflag r3 /* write status32 */

j_s.d [blink] /* always execute delay slot */
seti r1 /* delay slot */
j_s [blink]


.balign 4
_swap_return_from_rirq:
_swap_return_from_firq:
_switch_return_from_rirq:
_switch_return_from_firq:

lr r3, [_ARC_V2_STATUS32]
bbit1 r3, _ARC_V2_STATUS32_AE_BIT, _return_from_exc_irq
@@ -199,13 +177,13 @@ _swap_return_from_firq:
#endif

lr r3, [_ARC_V2_AUX_IRQ_ACT]
brne r3, 0, _swap_already_in_irq
brne r3, 0, _switch_already_in_irq

/* use lowest interrupt priority */
or r3, r3, (1<<(CONFIG_NUM_IRQ_PRIO_LEVELS-1))
or r3, r3, (1 << (CONFIG_NUM_IRQ_PRIO_LEVELS-1))
sr r3, [_ARC_V2_AUX_IRQ_ACT]

_swap_already_in_irq:
_switch_already_in_irq:
rtie

.balign 4
@@ -232,17 +210,10 @@ _return_from_exc:
#ifdef CONFIG_EXECUTION_BENCHMARKING
.balign 4
_capture_value_for_benchmarking:
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_save_callee_saved_regs
push_s blink

bl read_timer_end_of_swap

pop_s blink
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_load_callee_saved_regs
st sp, [r2, _thread_offset_to_sp]
b return_loc
#endif /* CONFIG_EXECUTION_BENCHMARKING */

0 comments on commit 890b9eb

Please sign in to comment.
You can’t perform that action at this time.