2014-03-26 18:13:43 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Freie Universität Berlin
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
|
|
* Public License. See the file LICENSE in the top level directory for more
|
|
|
|
* details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2014-06-06 12:01:14 +02:00
|
|
|
* @ingroup cpu_cortex-m3
|
2014-03-26 18:13:43 +01:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file thread_arch.c
|
|
|
|
* @brief Implementation of the kernel's architecture dependent thread interface
|
|
|
|
*
|
|
|
|
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
|
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
2014-06-06 12:01:14 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
|
2014-03-26 18:13:43 +01:00
|
|
|
#include "arch/thread_arch.h"
|
|
|
|
#include "sched.h"
|
|
|
|
#include "irq.h"
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "kernel_internal.h"
|
|
|
|
|
|
|
|
|
2014-04-17 19:36:12 +02:00
|
|
|
/**
|
|
|
|
* @name noticeable marker marking the beginning of a stack segment
|
|
|
|
*
|
|
|
|
* This marker is used e.g. by *thread_arch_start_threading* to identify the stacks start.
|
|
|
|
*/
|
|
|
|
#define STACK_MARKER (0x77777777)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @name ARM Cortex-M specific exception return value, that triggers the return to the task mode
|
|
|
|
* stack pointer
|
|
|
|
*/
|
|
|
|
#define EXCEPT_RET_TASK_MODE (0xfffffffd)
|
|
|
|
|
|
|
|
|
2014-03-26 18:13:43 +01:00
|
|
|
static void context_save(void);
|
2014-06-05 21:01:35 +02:00
|
|
|
static void context_restore(void) NORETURN;
|
|
|
|
static void enter_thread_mode(void) NORETURN;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/**
|
2014-06-06 12:01:14 +02:00
|
|
|
* Cortex-M3 knows stacks and handles register backups, so use different stack frame layout
|
2014-03-26 18:13:43 +01:00
|
|
|
*
|
2014-04-17 19:36:12 +02:00
|
|
|
* Layout without floating point registers:
|
2014-03-26 18:13:43 +01:00
|
|
|
* --------------------------------------
|
|
|
|
* | R0 | R1 | R2 | R3 | LR | PC | xPSR |
|
|
|
|
* --------------------------------------
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
char *thread_arch_stack_init(void (*task_func)(void), void *stack_start, int stack_size)
|
|
|
|
{
|
2014-06-06 12:01:14 +02:00
|
|
|
uint32_t *stk;
|
|
|
|
stk = (uint32_t *)(stack_start + stack_size);
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* marker */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t)STACK_MARKER;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* FIXME xPSR */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t)0x01000200;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* program counter */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t)task_func;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* link register, jumped to when thread exits */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t)sched_task_exit;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* r12 */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t) 0;
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
/* r0 - r3 */
|
|
|
|
for (int i = 3; i >= 0; i--) {
|
|
|
|
stk--;
|
|
|
|
*stk = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* r11 - r4 */
|
|
|
|
for (int i = 11; i >= 4; i--) {
|
|
|
|
stk--;
|
|
|
|
*stk = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lr means exception return code */
|
|
|
|
stk--;
|
2014-06-06 12:01:14 +02:00
|
|
|
*stk = (uint32_t)EXCEPT_RET_TASK_MODE; /* return to task-mode main stack pointer */
|
2014-03-26 18:13:43 +01:00
|
|
|
|
|
|
|
return (char*) stk;
|
|
|
|
}
|
|
|
|
|
|
|
|
void thread_arch_stack_print(void)
|
|
|
|
{
|
|
|
|
/* TODO */
|
|
|
|
}
|
|
|
|
|
|
|
|
void thread_arch_start_threading(void)
|
|
|
|
{
|
|
|
|
sched_run();
|
|
|
|
enableIRQ();
|
|
|
|
enter_thread_mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the MCU into Thread-Mode and load the initial task from the stack and run it
|
|
|
|
*/
|
2014-06-05 21:01:35 +02:00
|
|
|
void NORETURN enter_thread_mode(void)
|
2014-03-26 18:13:43 +01:00
|
|
|
{
|
|
|
|
/* switch to user mode use PSP instead of MSP in ISR Mode*/
|
|
|
|
CONTROL_Type mode;
|
|
|
|
mode.w = __get_CONTROL();
|
|
|
|
mode.b.SPSEL = 1; /* select PSP */
|
|
|
|
mode.b.nPRIV = 0; /* privilege */
|
|
|
|
__set_CONTROL(mode.w);
|
|
|
|
|
|
|
|
/* load pdc->stackpointer in r0 */
|
2014-04-10 22:28:35 +02:00
|
|
|
asm("ldr r0, =sched_active_thread" ); /* r0 = &sched_active_thread */
|
|
|
|
asm("ldr r0, [r0]" ); /* r0 = *r0 = sched_active_thread */
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("ldr sp, [r0]" ); /* sp = r0 restore stack pointer*/
|
|
|
|
asm("pop {r4}" ); /* skip exception return */
|
|
|
|
asm("pop {r4-r11}" );
|
|
|
|
asm("pop {r0-r3,r12,lr}" ); /* get registers from stack */
|
|
|
|
asm("pop {r4}" ); /* get PC */
|
|
|
|
asm("pop {r5}" ); /* discard the xPSR entry */
|
|
|
|
asm("mov pc, r4" ); /* load PC */
|
2014-06-05 21:01:35 +02:00
|
|
|
|
|
|
|
UNREACHABLE();
|
2014-03-26 18:13:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void thread_arch_yield(void)
|
|
|
|
{
|
|
|
|
if (irq_arch_in()) {
|
|
|
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; /* set PendSV Bit */
|
2014-04-17 19:36:12 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("svc 0x01\n"); /* trigger SVC interrupt, which will trigger the pendSV (needed?) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief SVC interrupt handler (to be discussed if this is really needed)
|
|
|
|
*/
|
2014-04-17 19:36:12 +02:00
|
|
|
__attribute__((naked)) void isr_svc(void)
|
2014-03-26 18:13:43 +01:00
|
|
|
{
|
2014-04-17 19:36:12 +02:00
|
|
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; /* trigger the pendsv interrupt */
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("bx LR" ); /* load exception return value to PC causes end of exception*/
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:36:12 +02:00
|
|
|
__attribute__((naked)) void isr_pendsv(void)
|
2014-03-26 18:13:43 +01:00
|
|
|
{
|
|
|
|
context_save();
|
|
|
|
sched_run();
|
|
|
|
context_restore();
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:36:12 +02:00
|
|
|
__attribute__((always_inline)) static __INLINE void context_save(void)
|
2014-03-26 18:13:43 +01:00
|
|
|
{
|
|
|
|
/* {r0-r3,r12,LR,PC,xPSR} are saved automatically on exception entry */
|
|
|
|
|
|
|
|
/* save unsaved registers */
|
|
|
|
asm("mrs r0, psp" ); /* get stack pointer from user mode */
|
|
|
|
asm("stmdb r0!,{r4-r11}" ); /* save regs */
|
|
|
|
asm("stmdb r0!,{lr}" ); /* exception return value */
|
2014-04-17 19:36:12 +02:00
|
|
|
/* asm("vstmdb sp!, {s16-s31}" ); */ /* TODO save FPU registers if needed */
|
2014-04-10 22:28:35 +02:00
|
|
|
asm("ldr r1, =sched_active_thread" ); /* load address of current tcb */
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("ldr r1, [r1]" ); /* dereference pdc */
|
|
|
|
asm("str r0, [r1]" ); /* write r0 to pdc->sp means current threads stack pointer */
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:36:12 +02:00
|
|
|
__attribute__((always_inline)) static __INLINE void context_restore(void)
|
2014-03-26 18:13:43 +01:00
|
|
|
{
|
2014-04-10 22:28:35 +02:00
|
|
|
asm("ldr r0, =sched_active_thread" ); /* load address of current TCB */
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("ldr r0, [r0]" ); /* dereference TCB */
|
|
|
|
asm("ldr r1, [r0]" ); /* load tcb->sp to register 1 */
|
|
|
|
asm("ldmia r1!, {r0}" ); /* restore exception return value from stack */
|
2014-04-17 19:36:12 +02:00
|
|
|
/* asm("pop {s16-s31}" ); */ /* TODO load FPU register if needed depends on r0 exret */
|
2014-03-26 18:13:43 +01:00
|
|
|
asm("ldmia r1!, {r4-r11}" ); /* restore other registers */
|
|
|
|
asm("msr psp, r1" ); /* restore PSP register (user mode SP)*/
|
|
|
|
asm("bx r0" ); /* load exception return value to PC causes end of exception*/
|
|
|
|
|
|
|
|
/* {r0-r3,r12,LR,PC,xPSR} are restored automatically on exception return */
|
2014-06-05 21:01:35 +02:00
|
|
|
|
|
|
|
UNREACHABLE();
|
2014-03-26 18:13:43 +01:00
|
|
|
}
|