blob: d941809eb7adf2cd5fc22f2f3aae5c99f782d5a6 [file] [log] [blame]
/**
* Copyright (c) 2015 Runtime Inc.
*
* Licensed 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.
*/
#include "os/os.h"
#include "os/os_arch.h"
#include <bsp/cmsis_nvic.h>
/* Initial program status register */
#define INITIAL_xPSR 0x01000000
/*
* Exception priorities. The higher the number, the lower the priority. A
* higher priority exception will interrupt a lower priority exception.
*/
#define PEND_SV_PRIO ((1 << __NVIC_PRIO_BITS) - 1)
#define SYSTICK_PRIO (PEND_SV_PRIO - 1)
/* Make the SVC instruction highest priority */
#define SVC_PRIO (1)
/* Stack frame structure */
struct stack_frame {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t xpsr;
};
#define SVC_ArgN(n) \
register int __r##n __asm("r"#n);
#define SVC_Arg0() \
SVC_ArgN(0) \
SVC_ArgN(1) \
SVC_ArgN(2) \
SVC_ArgN(3)
#if (defined (__CORTEX_M0)) || defined (__CORTEX_M0PLUS)
#define SVC_Call(f) \
__asm volatile \
( \
"ldr r7,="#f"\n\t" \
"mov r12,r7\n\t" \
"svc 0" \
: "=r" (__r0), "=r" (__r1), "=r" (__r2), "=r" (__r3) \
: "r" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) \
: "r7", "r12", "lr", "cc" \
);
#else
#define SVC_Call(f) \
__asm volatile \
( \
"ldr r12,="#f"\n\t" \
"svc 0" \
: "=r" (__r0), "=r" (__r1), "=r" (__r2), "=r" (__r3) \
: "r" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) \
: "r12", "lr", "cc" \
);
#endif
/* XXX: determine how we will deal with running un-privileged */
uint32_t os_flags = OS_RUN_PRIV;
void
timer_handler(void)
{
os_time_tick();
os_callout_tick();
os_sched_os_timer_exp();
os_sched(NULL, 1);
}
void
os_arch_ctx_sw(struct os_task *t)
{
os_sched_ctx_sw_hook(t);
/* Set PendSV interrupt pending bit to force context switch */
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
void
os_arch_ctx_sw_isr(struct os_task *t)
{
os_sched_ctx_sw_hook(t);
/* Set PendSV interrupt pending bit to force context switch */
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
os_sr_t
os_arch_save_sr(void)
{
uint32_t isr_ctx;
isr_ctx = __get_PRIMASK();
__disable_irq();
return (isr_ctx & 1);
}
void
os_arch_restore_sr(os_sr_t isr_ctx)
{
if (!isr_ctx) {
__enable_irq();
}
}
os_stack_t *
os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size)
{
int i;
os_stack_t *s;
struct stack_frame *sf;
/* Get stack frame pointer */
s = (os_stack_t *) ((uint8_t *) stack_top - sizeof(*sf));
/* Zero out R1-R3, R12, LR */
for (i = 9; i < 14; ++i) {
s[i] = 0;
}
/* Set registers R4 - R11 on stack. */
os_arch_init_task_stack(s);
/* Set remaining portions of stack frame */
sf = (struct stack_frame *) s;
sf->xpsr = INITIAL_xPSR;
sf->pc = (uint32_t)t->t_func;
sf->r0 = (uint32_t)t->t_arg;
return (s);
}
void
os_arch_init(void)
{
/*
* Trap on divide-by-zero.
*/
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
os_init_idle_task();
}
__attribute__((always_inline))
static inline void
svc_os_arch_init(void)
{
SVC_Arg0();
SVC_Call(os_arch_init);
}
os_error_t
os_arch_os_init(void)
{
os_error_t err;
int i;
/* Cannot be called within an ISR */
err = OS_ERR_IN_ISR;
if (__get_IPSR() == 0) {
err = OS_OK;
/* Drop priority for all interrupts */
for (i = 0; i < sizeof(NVIC->IP); i++) {
NVIC->IP[i] = 0xff;
}
/*
* Install default interrupt handler, which'll print out system
* state at the time of the interrupt, and few other regs which
* should help in trying to figure out what went wrong.
*/
NVIC_SetVector(-13, (uint32_t)os_default_irq_asm); /* Hardfault */
NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)os_default_irq_asm);
NVIC_SetVector(BusFault_IRQn, (uint32_t)os_default_irq_asm);
NVIC_SetVector(UsageFault_IRQn, (uint32_t)os_default_irq_asm);
for (i = 0; i < NVIC_NUM_VECTORS - NVIC_USER_IRQ_OFFSET; i++) {
NVIC_SetVector(i, (uint32_t)os_default_irq_asm);
}
/* Call bsp related OS initializations */
os_bsp_init();
/* Set the PendSV interrupt exception priority to the lowest priority */
NVIC_SetPriority(PendSV_IRQn, PEND_SV_PRIO);
/* Set the SVC interrupt to priority 0 (highest configurable) */
NVIC_SetPriority(SVCall_IRQn, SVC_PRIO);
/*
* Set the os environment. This will set stack pointers and, based
* on the contents of os_flags, will determine if the tasks run in
* priviliged or un-privileged mode.
*/
os_set_env();
/* Check if priviledged or not */
if ((__get_CONTROL() & 1) == 0) {
os_arch_init();
} else {
svc_os_arch_init();
}
}
return err;
}
/**
* os systick init
*
* Initializes systick for the MCU
*
* @param os_tick_usecs The number of microseconds in an os time tick
*/
static void
os_systick_init(uint32_t os_tick_usecs)
{
uint32_t reload_val;
reload_val = (((uint64_t)SystemCoreClock * os_tick_usecs) / 1000000) - 1;
/* Set the system time ticker up */
SysTick->LOAD = reload_val;
SysTick->VAL = 0;
SysTick->CTRL = 0x0007;
/* Set the system tick priority */
NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIO);
}
uint32_t
os_arch_start(void)
{
struct os_task *t;
/* Get the highest priority ready to run to set the current task */
t = os_sched_next_task();
os_sched_set_current_task(t);
/* Adjust PSP so it looks like this task just took an exception */
__set_PSP((uint32_t)t->t_stackptr + offsetof(struct stack_frame, r0));
/* Intitialize and start system clock timer */
os_systick_init(1000000 / OS_TICKS_PER_SEC);
/* Mark the OS as started, right before we run our first task */
g_os_started = 1;
/* Perform context switch */
os_arch_ctx_sw(t);
return (uint32_t)(t->t_arg);
}
__attribute__((always_inline))
static inline void svc_os_arch_start(void)
{
SVC_Arg0();
SVC_Call(os_arch_start);
}
os_error_t
os_arch_os_start(void)
{
os_error_t err;
err = OS_ERR_IN_ISR;
if (__get_IPSR() == 0) {
/*
* The following switch statement is really just a sanity check to
* insure that the os initialization routine was called prior to the
* os start routine.
*/
err = OS_OK;
switch (__get_CONTROL() & 0x03) {
/*
* These two cases are for completeness. Thread mode should be set
* to use PSP already.
*
* Fall-through intentional!
*/
case 0x00:
case 0x01:
err = OS_ERR_PRIV;
break;
case 0x02:
/* Privileged Thread mode w/SP = PSP */
if ((os_flags & 1) == 0) {
err = OS_ERR_PRIV;
}
break;
case 0x03:
/* Unpriviliged thread mode w/sp = PSP */
if (os_flags & 1) {
err = OS_ERR_PRIV;
}
break;
}
if (err == OS_OK) {
/* Always start OS through SVC call */
svc_os_arch_start();
}
}
return err;
}