diff --git a/cpu/arm/common/sys/mtarch.c b/cpu/arm/common/sys/mtarch.c new file mode 100644 index 000000000..b5acf2bb1 --- /dev/null +++ b/cpu/arm/common/sys/mtarch.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2016, Benoît Thébaudeau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \addtogroup arm-cm-mtarch + * @{ + * + * \file + * Implmentation of the ARM Cortex-M support for Contiki multi-threading. + */ +#include CMSIS_DEV_HDR +#include "sys/mt.h" + +#include + +#define EXC_RETURN_PROCESS_THREAD_BASIC_FRAME 0xfffffffd + +/* Check whether EXC_RETURN[3:0] in LR indicates a preempted process thread. */ +#if __ARM_ARCH == 7 +#define PREEMPTED_PROCESS_THREAD() \ + "and r0, lr, #0xf\n\t" \ + "cmp r0, #0xd\n\t" +#elif __ARM_ARCH == 6 +#define PREEMPTED_PROCESS_THREAD() \ + "mov r0, lr\n\t" \ + "movs r1, #0xf\n\t" \ + "and r0, r1\n\t" \ + "cmp r0, #0xd\n\t" +#else +#error Unsupported ARM architecture +#endif +/*----------------------------------------------------------------------------*/ +/** + * \brief SVCall system handler + * + * This exception handler executes the action requested by the corresponding + * \c svc instruction, which is a task switch from the main Contiki thread to an + * mt thread or the other way around. + */ +__attribute__ ((__naked__)) +void +svcall_handler(void) +{ + /* This is a controlled system handler, so do not use ENERGEST_TYPE_IRQ. */ + + /* + * Decide whether to switch to the main thread or to a process thread, + * depending on the type of the thread preempted by SVCall. + */ + __asm__ (PREEMPTED_PROCESS_THREAD() +#if __ARM_ARCH == 7 + "it eq\n\t" +#endif + "beq switch_to_main_thread\n\t" + + /* + * - Retrieve from the main stack the PSP passed to SVCall through R0. Note + * that it cannot be retrieved directly from R0 on exception entry because + * this register may have been overwritten by other exceptions on SVCall + * entry. + * - Save the main thread context to the main stack. + * - Restore the process thread context from the process stack. + * - Return to Thread mode, resuming the process thread. + */ +#if __ARM_ARCH == 7 + "ldr r0, [sp]\n\t" + "push {r4-r11, lr}\n\t" + "add r1, r0, #9 * 4\n\t" + "msr psp, r1\n\t" + "ldmia r0, {r4-r11, pc}"); +#elif __ARM_ARCH == 6 + "mov r0, r8\n\t" + "mov r1, r9\n\t" + "mov r2, r10\n\t" + "mov r3, r11\n\t" + "push {r0-r7, lr}\n\t" + "ldr r0, [sp, #9 * 4]\n\t" + "ldmia r0!, {r4-r7}\n\t" + "mov r8, r4\n\t" + "mov r9, r5\n\t" + "mov r10, r6\n\t" + "mov r11, r7\n\t" + "ldmia r0!, {r3-r7}\n\t" + "msr psp, r0\n\t" + "bx r3"); +#endif +} +/*----------------------------------------------------------------------------*/ +/** + * \brief PendSV system handler + * + * This exception handler executes following a call to mtarch_pstart() from + * another exception handler. It performs a task switch to the main Contiki + * thread if it is not already running. + */ +__attribute__ ((__naked__)) +void +pendsv_handler(void) +{ + /* This is a controlled system handler, so do not use ENERGEST_TYPE_IRQ. */ + + /* + * Return without doing anything if PendSV has not preempted a process thread. + * This can occur either because PendSV has preempted the main thread, in + * which case there is nothing to do, or because mtarch_pstart() has been + * called from an exception handler without having called mt_init() first, in + * which case PendSV may have preempted an exception handler and nothing must + * be done because mt is not active. + */ + __asm__ ( PREEMPTED_PROCESS_THREAD() +#if __ARM_ARCH == 7 + "it ne\n\t" + "bxne lr\n" +#elif __ARM_ARCH == 6 + "beq switch_to_main_thread\n\t" + "bx lr\n" +#endif + + /* + * - Save the process thread context to the process stack. + * - Place into the main stack the updated PSP that SVCall must return through + * R0. + * - Restore the main thread context from the main stack. + * - Return to Thread mode, resuming the main thread. + */ + "switch_to_main_thread:\n\t" + "mrs r0, psp\n\t" +#if __ARM_ARCH == 7 + "stmdb r0!, {r4-r11, lr}\n\t" + "str r0, [sp, #9 * 4]\n\t" + "pop {r4-r11, pc}"); +#elif __ARM_ARCH == 6 + "mov r3, lr\n\t" + "sub r0, #5 * 4\n\t" + "stmia r0!, {r3-r7}\n\t" + "mov r4, r8\n\t" + "mov r5, r9\n\t" + "sub r0, #9 * 4\n\t" + "mov r6, r10\n\t" + "mov r7, r11\n\t" + "stmia r0!, {r4-r7}\n\t" + "pop {r4-r7}\n\t" + "sub r0, #4 * 4\n\t" + "mov r8, r4\n\t" + "mov r9, r5\n\t" + "str r0, [sp, #5 * 4]\n\t" + "mov r10, r6\n\t" + "mov r11, r7\n\t" + "pop {r4-r7, pc}"); +#endif +} +/*----------------------------------------------------------------------------*/ +void +mtarch_init(void) +{ + SCB->CCR = (SCB->CCR +#ifdef SCB_CCR_NONBASETHRDENA_Msk + /* + * Make sure that any attempt to enter Thread mode with exceptions + * active faults. + * + * Only SVCall and PendSV are allowed to forcibly enter Thread + * mode, and they are configured with the same, lowest exception + * priority, so no other exceptions may be active. + */ + & ~SCB_CCR_NONBASETHRDENA_Msk +#endif + /* + * Force 8-byte stack pointer alignment on exception entry in order + * to be able to use AAPCS-conforming functions as exception + * handlers. + */ + ) | SCB_CCR_STKALIGN_Msk; + + /* + * Configure SVCall and PendSV with the same, lowest exception priority. + * + * This makes sure that they cannot preempt each other, and that the processor + * executes them after having handled all other exceptions. If both are + * pending at the same time, then SVCall takes precedence because of its lower + * exception number. In addition, the associated exception handlers do not + * have to check whether they are returning to Thread mode, because they + * cannot preempt any other exception. + */ + NVIC_SetPriority(SVCall_IRQn, (1 << __NVIC_PRIO_BITS) - 1); + NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1); + + /* + * Force the preceding configurations to take effect before further + * operations. + */ + __DSB(); + __ISB(); +} +/*----------------------------------------------------------------------------*/ +void +mtarch_start(struct mtarch_thread *thread, + void (*function)(void *data), void *data) +{ + struct mtarch_thread_context *context = &thread->start_stack.context; + + /* + * Initialize the thread context with the appropriate values to call + * function() with data and to make function() return to mt_exit() without + * having to call it explicitly. + */ + context->exc_return = EXC_RETURN_PROCESS_THREAD_BASIC_FRAME; + context->r0 = (uint32_t)data; + context->lr = (uint32_t)mt_exit; + context->pc = (uint32_t)function; + context->xpsr = xPSR_T_Msk; + thread->psp = (uint32_t)context; +} +/*----------------------------------------------------------------------------*/ +void +mtarch_exec(struct mtarch_thread *thread) +{ + /* Pass the PSP to SVCall, and get the updated PSP as its return value. */ + register uint32_t psp __asm__ ("r0") = thread->psp; + __asm__ volatile ("svc #0" + : "+r" (psp) + :: "memory"); + thread->psp = psp; +} +/*----------------------------------------------------------------------------*/ +__attribute__ ((__naked__)) +void +mtarch_yield(void) +{ + /* Invoke SVCall. */ + __asm__ ("svc #0\n\t" + "bx lr"); +} +/*----------------------------------------------------------------------------*/ +void +mtarch_stop(struct mtarch_thread *thread) +{ +} +/*----------------------------------------------------------------------------*/ +void +mtarch_pstart(void) +{ + /* Trigger PendSV. */ + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} +/*----------------------------------------------------------------------------*/ +void +mtarch_pstop(void) +{ +} +/*----------------------------------------------------------------------------*/ +void +mtarch_remove(void) +{ +} +/*----------------------------------------------------------------------------*/ + +/** @} */ diff --git a/cpu/arm/common/sys/mtarch.h b/cpu/arm/common/sys/mtarch.h new file mode 100644 index 000000000..fd6a0d0ca --- /dev/null +++ b/cpu/arm/common/sys/mtarch.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016, Benoît Thébaudeau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \addtogroup arm + * @{ + * + * \defgroup arm-cm-mtarch ARM Cortex-M support for Contiki multi-threading + * + * All the Cortex-M devices supported by CMSIS-CORE are supported. + * + * An exception handler can decide to make the main Contiki thread preempt any + * running mt thread by calling mtarch_pstart() (e.g. to perform urgent + * operations that have been triggered by some event or that had been + * scheduled). If the running thread is already the main Contiki thread, then + * nothing happens. The corresponding task switch takes place when leaving + * Handler mode. The main Contiki thread then resumes after the call to + * mt_exec() that yielded to the preempted mt thread. + * @{ + * + * \file + * Header file for the ARM Cortex-M support for Contiki multi-threading. + */ +#ifndef MTARCH_H_ +#define MTARCH_H_ + +#include "contiki-conf.h" +#include "sys/cc.h" + +#include + +#ifndef MTARCH_CONF_STACKSIZE +/** Thread stack size configuration, expressed as a number of 32-bit words. */ +#define MTARCH_CONF_STACKSIZE 256 +#endif +/** Actual stack size, with minimum size and alignment requirements enforced. */ +#define MTARCH_STACKSIZE ((MAX(MTARCH_CONF_STACKSIZE, \ + sizeof(struct mtarch_thread_context) / \ + sizeof(uint32_t)) + 1) & ~1) + +/** + * Structure of a saved thread context. + * + * xpsr..r0 are managed by the processor (except in mtarch_start()), + * while the other register values are handled by the software. + */ +struct mtarch_thread_context { +#if __ARM_ARCH == 7 + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; +#endif + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t exc_return; +#if __ARM_ARCH == 6 + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; +#endif + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t xpsr; +}; + +struct mtarch_thread { + uint32_t psp; + union { + struct { + uint32_t free[MTARCH_STACKSIZE - + sizeof(struct mtarch_thread_context) / sizeof(uint32_t)]; + struct mtarch_thread_context context; + } start_stack; + uint32_t stack[MTARCH_STACKSIZE]; + } CC_ALIGN(8); +}; + +#endif /* MTARCH_H_ */ + +/** + * @} + * @} + */