You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
6.4 KiB
C

/****************************************************************************
* MInimal Real-time Operating System (MIROS)
* version 0.23.2 (matching lesson 23 step 2)
*
* This software is a teaching aid to illustrate the concepts underlying
* a Real-Time Operating System (RTOS). The main goal of the software is
* simplicity and clear presentation of the concepts, but without dealing
* with various corner cases, portability, or error handling. For these
* reasons, the software is generally NOT intended or recommended for use
* in commercial applications.
*
* Copyright (C) 2018 Miro Samek. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Contact Information:
* https://www.state-machine.com
****************************************************************************/
#include <stdint.h>
#include "miros.h" /* OS API */
OSThread * volatile OS_curr; /* the currently runnig thread */
OSThread * volatile OS_next; /* the next thread to run */
#define OS_INT_DISABLE() __asm volatile ("cpsid i")
#define OS_INT_ENABLE() __asm volatile ("cpsie i")
#define SCB_ICSR (*(uint32_t volatile *)0xE000ED04)
#define SCB_SYSPRI3 (*(uint32_t volatile *)0xE000ED20)
#define OS_CONTEXT_SWITCH() (SCB_ICSR = (uint32_t)(1U << 28))
#if (OS_SCHED_TYPE == 0) /* manual scheduler? */
void OS_sched(void) {
/* set the next thread OS_next manually */
/* ... */
if (OS_curr != OS_next) {
OS_CONTEXT_SWITCH();
}
}
#elif (OS_SCHED_TYPE == 1) /* round-robin scheduler? */
static OSThread *l_threadFirst;
static OSThread *l_threadLast;
void OS_sched(void) {
/* set next thread QS_next as the next in the list */
OS_next = OS_next->next;
if (OS_curr != OS_next) {
OS_CONTEXT_SWITCH();
}
}
#else /* unsupported scheduler */
#error "Scheduler type not supported in this MIROS version."
#endif
void OS_init(void) {
OS_INT_DISABLE();
/* SCB_SYSPRI3[16:23]: PendSV set to the lowest priority 0xFF */
SCB_SYSPRI3 |= (0xFFU << 16);
}
void OS_run() {
OS_curr = (OSThread *)0;
#if OS_SCHED_TYPE == 0
OS_next = (OSThread *)0;
#elif OS_SCHED_TYPE == 1
OS_next = l_threadFirst;
#endif
OS_sched();
OS_INT_ENABLE();
}
static void OS_threadRet(void) {
while (1) {
}
}
void OSThread_start(
OSThread *me,
OSThreadHandler threadHandler,
void *stkSto, uint32_t stkSize)
{
/* round down the stack top to the 8-byte boundary
* NOTE: ARM Cortex-M stack grows down from hi -> low memory
*/
uint32_t *sp =
(uint32_t *)((((uint32_t)stkSto + stkSize) / 8) * 8);
uint32_t *stk_limit;
/* synthesize the ARM Cortex-M exception stack frame...*/
*(--sp) = (1U << 24); /* xPSR (just the THUMB bit) */
*(--sp) = (uint32_t)threadHandler; /* PC (the thread routine) */
*(--sp) = (uint32_t)&OS_threadRet; /* LR (return from thread) */
*(--sp) = 0x0000000CU; /* R12 */
*(--sp) = 0x00000003U; /* R3 */
*(--sp) = 0x00000002U; /* R2 */
*(--sp) = 0x00000001U; /* R1 */
*(--sp) = (uint32_t)me; /* R0 (argument to the thread routine */
*(--sp) = 0x0000000BU; /* R11 */
*(--sp) = 0x0000000AU; /* R10 */
*(--sp) = 0x00000009U; /* R9 */
*(--sp) = 0x00000008U; /* R8 */
*(--sp) = 0x00000007U; /* R7 */
*(--sp) = 0x00000006U; /* R6 */
*(--sp) = 0x00000005U; /* R5 */
*(--sp) = 0x00000004U; /* R4 */
/* save the top of the stack in the thread's attibute */
me->sp = sp;
/* pre-fill the unused part of the stack with 0xDEADBEEF */
stk_limit = (uint32_t *)(((((uint32_t)stkSto - 1U) >> 3) + 1U) << 3);
for (sp = sp - 1U; sp >= stk_limit; --sp) {
*sp = 0xDEADBEEFU;
}
if (l_threadFirst == (OSThread *)0) {
l_threadFirst = me;
l_threadLast = me;
}
/* insert the thread into a circular linked list... */
me->next = l_threadLast;
l_threadLast = me;
l_threadFirst->next = l_threadLast;
}
/*****************************************************************************
* The PendSV exception handler is used for handling context switch and is
* the recommended context switch mechansism for ARM Cortex-M CPUs.
*
* The PendSV exception should have the lowest interrupt priority in the
* system (0xFF, see OS_init). All other exceptions and interrupts should
* have higher priority (lower numerical value of priority).
*
* Due to tail-chaining and its lowest priority, the PendSV exception will be
* entered very efficiently immediately after the exit from the *last* nested
* interrupt (or exception).
*****************************************************************************/
__attribute__ ((naked))
void PendSV_Handler(void) {
__asm volatile (
" LDR r1,=OS_curr \n" /* r1 := &OS_curr */
" LDR r2,=OS_next \n" /* r2 := &OS_next */
" CPSID i \n" /* disable interrupts (set PRIMASK) */
" LDR r3,[r1] \n" /* r3 := OS_curr */
" CMP r3,#0 \n" /* (r3 == 0)? */
" BEQ PendSV_restore \n" /* branch if (r3 == 0) */
" MRS r0,PSP \n" /* r0 := Process Stack Pointer */
" STMDB r0!,{r4-r11} \n" /* save r4-r11 on top of the stack */
" STR r0,[r3, #0] \n" /* OS_curr.sp := sp */
"PendSV_restore: \n"
" LDR r3,[r2] \n" /* r3 := OS_next */
" LDR r0,[r3, #0] \n" /* r0 := OS_next.sp */
" STR r3,[r1] \n" /* QS_curr := OS_next */
" CPSIE i \n" /* enable interrupts (clear PRIMASK) */
" LDMIA r0!,{r4-r11} \n" /* restore r4-r11 */
" MSR PSP,r0 \n" /* Process Stack Pointer := r0 */
" ORR lr,lr,#4 \n" /* make sure PSP is used on return */
" BX lr \n" /* return to the next thread */
);
}