-
Notifications
You must be signed in to change notification settings - Fork 1
/
cmcm.c
171 lines (137 loc) · 3.75 KB
/
cmcm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <string.h>
#include "cmcm.h"
typedef struct {
void *sp;
uint8_t flags;
} cmcm_task_t;
#define CMCM_TASK_INUSE (1 << 0)
#define CMCM_TASK_SLEEPING (1 << 1)
// interrupt control and state register
#define ICSR (*(volatile uint32_t *) 0xE000ED04)
static cmcm_task_t tasks[CMCM_MAX_NUM_TASKS];
static uint8_t __attribute__((aligned(8))) stack_space[CMCM_STACK_SIZE * CMCM_MAX_NUM_TASKS];
// the first context switch (from MSP to PSP) will increment this
static int current_task = -1;
typedef struct {
// we explicity push these registers
struct {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
} sw_frame;
// these registers are pushed by the hardware
struct {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
void *lr;
void *pc;
uint32_t psr;
} hw_frame;
} cmcm_stack_frame_t;
static void cmcm_destroy_task(void) {
tasks[current_task].flags = 0;
cmcm_yield();
while (1);
}
void cmcm_create_task(void (*handler)(void)) {
// find first available slot
int index;
for (index = 0; tasks[index].flags & CMCM_TASK_INUSE && index < CMCM_MAX_NUM_TASKS; index++);
if (index >= CMCM_MAX_NUM_TASKS) {
return;
}
void *stack = stack_space + (index * CMCM_STACK_SIZE);
memset(stack, 0, CMCM_STACK_SIZE);
// set sp to the start of the stack (highest address)
tasks[index].sp = ((uint8_t *) stack) + CMCM_STACK_SIZE;
// initialize the start of the stack as if it had been
// pushed via a context switch
tasks[index].sp -= sizeof(cmcm_stack_frame_t);
cmcm_stack_frame_t *frame = (cmcm_stack_frame_t *)tasks[index].sp;
frame->hw_frame.lr = cmcm_destroy_task;
frame->hw_frame.pc = handler;
frame->hw_frame.psr = 0x21000000; // default PSR value
tasks[index].flags |= CMCM_TASK_INUSE;
}
int cmcm_current_task(void) {
return current_task;
}
void cmcm_delay(uint32_t ticks) {
uint32_t start = cmcm_tick_get();
while (1) {
if (cmcm_tick_since(start) >= ticks) {
// enough time has passed
break;
}
cmcm_yield();
}
}
static void *cmcm_push_context(void) {
void *psp;
// copies registers to the PSP stack
// additional registers are pushed in hardware
__asm__("MRS %0, psp\n"
"STMDB %0!, {r4-r11}\n"
"MSR psp, %0\n"
: "=r" (psp));
return psp;
}
static void cmcm_pop_context(void *psp) {
// loads registers with contents of the PSP stack
// additional registers are popped in hardware
__asm__("LDMFD %0!, {r4-r11}\n"
"MSR psp, %0\n"
: : "r" (psp));
}
void cmcm_context_switch() {
// the first context switch will be called from the MSP
// in that case we do not need to save the context
// since we never return to MSP
if (current_task != -1) {
tasks[current_task].sp = cmcm_push_context();
}
// find next running task
uint8_t running;
do {
current_task++;
if (current_task >= CMCM_MAX_NUM_TASKS) {
current_task = 0;
}
uint8_t flags = tasks[current_task].flags;
running = (flags & CMCM_TASK_INUSE) & !(flags & CMCM_TASK_SLEEPING);
} while (!running);
cmcm_pop_context(tasks[current_task].sp);
__asm__("bx %0" : : "r" (0xFFFFFFFD));
}
void cmcm_yield(void) {
// manually trigger pend_sv
ICSR |= (1 << 28);
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
void cmcm_sleep() {
tasks[current_task].flags |= CMCM_TASK_SLEEPING;
cmcm_yield();
}
void cmcm_wake(int task_id) {
// critial section, could be called from any task
cmcm_disable_interrupts();
tasks[task_id].flags &= ~CMCM_TASK_SLEEPING;
cmcm_enable_interrupts();
}
void cmcm_disable_interrupts(void) {
__asm__("CPSID i");
}
void cmcm_enable_interrupts(void) {
__asm__("CPSIE i");
}