| 3 |
3 |
* @brief Scheduler
|
| 4 |
4 |
*
|
| 5 |
5 |
* Implementation of functions for scheduler
|
|
6 |
* Currently relies on avr-gcc's interrupt call convention.
|
| 6 |
7 |
*
|
| 7 |
8 |
* @author Colony Project, CMU Robotics Club
|
| 8 |
9 |
* Based on avrOS and 18348 Lab9 code
|
| 9 |
10 |
**/
|
| 10 |
11 |
|
|
12 |
#include <avr/interrupt.h>
|
|
13 |
#include <util/delay.h>
|
| 11 |
14 |
#include "scheduler.h"
|
| 12 |
|
#include "time.h"
|
| 13 |
15 |
|
| 14 |
|
static uint8_t STACK[MAXTASKS][STACKSIZE];
|
|
16 |
#define DEBUG
|
|
17 |
|
|
18 |
#ifdef DEBUG
|
|
19 |
#include <serial.h>
|
|
20 |
#endif //DEBUG
|
|
21 |
|
|
22 |
static uint8_t STACK[MAXTASKS + 1][STACKSIZE];
|
| 15 |
23 |
static uint8_t nactive_tasks = 1;
|
| 16 |
24 |
static uint8_t current_task = 0; //Default to main.
|
| 17 |
25 |
|
| 18 |
26 |
//Internal functions
|
| 19 |
|
void task_terminate(void);
|
|
27 |
static void task_terminate(void);
|
| 20 |
28 |
|
| 21 |
|
//Scheduler gets called from the interrupt.
|
| 22 |
|
void scheduler(void);
|
| 23 |
|
void create_launch_stack(uint8_t task);
|
| 24 |
|
void store_task(void);
|
| 25 |
|
void restore_task(void);
|
| 26 |
|
void restore_next_task(void);
|
|
29 |
//TODO Use a context struct instead of pushing onto the stack.
|
|
30 |
//so we don't need to worry so much about stack overflow.
|
|
31 |
typedef struct context_t {
|
|
32 |
uint8_t reg[32];
|
|
33 |
uint8_t sreg;
|
|
34 |
} context_t;
|
| 27 |
35 |
|
|
36 |
//Define some PCB's
|
| 28 |
37 |
typedef struct PCB_t
|
| 29 |
38 |
{
|
| 30 |
39 |
void (*exec)(void);
|
| ... | ... | |
| 36 |
45 |
|
| 37 |
46 |
PCB_t PCB[MAXTASKS + 1];
|
| 38 |
47 |
|
| 39 |
|
void scheduler_init() { }
|
|
48 |
//Keep track of global time.
|
|
49 |
static uint32_t current_time = 0;
|
| 40 |
50 |
|
| 41 |
|
void task_terminate(void) {
|
|
51 |
unsigned int time_now() {
|
|
52 |
return current_time;
|
|
53 |
}
|
|
54 |
|
|
55 |
void scheduler_init() {
|
|
56 |
//Set the counter to 0.
|
|
57 |
TCNT3 = 0;
|
|
58 |
|
|
59 |
//Clear timer on compare (CTC) mode.
|
|
60 |
TCCR3B |= _BV(WGM32);
|
|
61 |
|
|
62 |
//Set a prescalar of 8. 8 / (8MHz) = 1us per tick.
|
|
63 |
TCCR3B |= _BV(CS31);
|
|
64 |
|
|
65 |
//Trigger an interrupt every 16th second for now.
|
|
66 |
OCR3A = 0xF424;
|
|
67 |
|
|
68 |
//Enable Output Compare A Interrupt. (Begins immediately).
|
|
69 |
ETIMSK |= _BV(OCIE3A);
|
|
70 |
|
|
71 |
#ifdef DEBUG
|
|
72 |
usb_init();
|
|
73 |
#endif //DEBUG
|
|
74 |
|
|
75 |
sei();
|
|
76 |
}
|
|
77 |
|
|
78 |
static void task_terminate(void) {
|
|
79 |
#ifdef DEBUG
|
|
80 |
usb_puts("In task terminate!.\n\r");
|
|
81 |
#endif //DEBUG
|
| 42 |
82 |
PCB[current_task].running = 0;
|
| 43 |
83 |
yield();
|
| 44 |
84 |
}
|
| ... | ... | |
| 56 |
96 |
|
| 57 |
97 |
PCB[nactive_tasks].exec = exec;
|
| 58 |
98 |
PCB[nactive_tasks].period = period;
|
| 59 |
|
PCB[nactive_tasks].next = rtc_get() + period;
|
|
99 |
PCB[nactive_tasks].next = current_time + period;
|
| 60 |
100 |
PCB[nactive_tasks].running = 0;
|
| 61 |
101 |
|
| 62 |
102 |
//Don't need to initialize SP, it will get done later.
|
| ... | ... | |
| 64 |
104 |
return nactive_tasks - 1;
|
| 65 |
105 |
}
|
| 66 |
106 |
|
| 67 |
|
//Create a fresh launch stack.
|
| 68 |
|
void create_launch_stack(uint8_t task) {
|
| 69 |
|
uint8_t* sp = &STACK[task][STACKSIZE - 1];
|
| 70 |
|
|
| 71 |
|
//Put task terminate and the task to execute on the stack.
|
| 72 |
|
*(sp--) = (uint8_t)(uint16_t) *task_terminate;
|
| 73 |
|
*(sp--) = (uint8_t)(uint16_t) *task_terminate >> 8;
|
| 74 |
|
*(sp--) = (uint8_t)(uint16_t) *PCB[nactive_tasks].exec;
|
| 75 |
|
*(sp--) = (uint8_t)(uint16_t) *PCB[nactive_tasks].exec >> 8;
|
| 76 |
|
|
| 77 |
|
//This is going to get me in trouble,
|
| 78 |
|
//but store_task already does everything else we need
|
| 79 |
|
//(since we only care about void-void functions)
|
| 80 |
|
store_task();
|
| 81 |
|
}
|
| 82 |
|
|
| 83 |
|
void scheduler(void){
|
|
107 |
//I need a timer to do the rest. Thinking about stealing it from rtc.
|
|
108 |
SIGNAL(TIMER3_COMPA_vect) {
|
| 84 |
109 |
static uint8_t task_i = 1;
|
|
110 |
volatile uint8_t** sploc;
|
|
111 |
volatile uint8_t* sp;
|
|
112 |
|
| 85 |
113 |
uint8_t i;
|
| 86 |
|
int current_time = rtc_get();
|
| 87 |
114 |
|
|
115 |
#ifdef DEBUG
|
|
116 |
char test_buf[128];
|
|
117 |
#endif //DEBUG
|
|
118 |
|
|
119 |
//Store sp.
|
|
120 |
sploc = &(PCB[current_task].sp);
|
|
121 |
|
|
122 |
asm volatile( \
|
|
123 |
"in r0, __SP_L__ \n" \
|
|
124 |
"st %a0+, r0 \n" \
|
|
125 |
"in r0, __SP_H__ \n" \
|
|
126 |
"st %a0, r0 \n" : : "e" (sploc));
|
|
127 |
|
|
128 |
#ifdef DEBUG
|
|
129 |
sprintf(test_buf, "current_task = %d, current_time = %d, sp = %x\n\r", current_task, current_time, sploc);
|
|
130 |
usb_puts(test_buf);
|
|
131 |
#endif //DEBUG
|
|
132 |
|
|
133 |
//The current task may no longer be running when we exit.
|
|
134 |
PCB[current_task].running = 0;
|
|
135 |
|
|
136 |
//Keep track of the number of 1/16 second's that have passed.
|
|
137 |
current_time++;
|
|
138 |
|
|
139 |
/******** scheduler ********/
|
|
140 |
|
| 88 |
141 |
//Loop over registered tasks, like in round robin order.
|
| 89 |
|
for(i = nactive_tasks; i > 0; i--) {
|
| 90 |
|
if( !PCB[task_i].running
|
| 91 |
|
&& PCB[task_i].next <= current_time) {
|
|
142 |
for(i = nactive_tasks - 1; i > 0; i--) {
|
|
143 |
|
|
144 |
//Launch a new task if it is due to run.
|
|
145 |
if( !PCB[task_i].running
|
|
146 |
&& PCB[task_i].next <= current_time) {
|
| 92 |
147 |
|
|
148 |
task_i++;
|
|
149 |
|
| 93 |
150 |
current_task = task_i;
|
| 94 |
151 |
PCB[task_i].next += PCB[task_i].period;
|
| 95 |
|
create_launch_stack(task_i);
|
| 96 |
|
task_i++;
|
|
152 |
|
|
153 |
/***** create launch stack ***/
|
|
154 |
#ifdef DEBUG
|
|
155 |
usb_puts("Switching tasks..... (cross fingers)\n\r");
|
|
156 |
#endif //DEBUG
|
|
157 |
|
|
158 |
sp = &(STACK[task_i][STACKSIZE - 1]);
|
|
159 |
|
|
160 |
//Put task terminate and the task to execute on the stack.
|
|
161 |
*(sp--) = (uint8_t)(uint16_t) task_terminate;
|
|
162 |
*(sp--) = (uint8_t)(uint16_t) task_terminate >> 8;
|
|
163 |
*(sp--) = (uint8_t)(uint16_t) PCB[nactive_tasks].exec;
|
|
164 |
*(sp--) = (uint8_t)(uint16_t) PCB[nactive_tasks].exec >> 8;
|
|
165 |
|
|
166 |
//The current state of the registers is what the tasks will
|
|
167 |
//see on entry (shouldn't matter).
|
|
168 |
|
|
169 |
asm volatile( \
|
|
170 |
"out __SP_L__, %A0 \n" \
|
|
171 |
"out __SP_H__, %B0 \n" : : "d" (sp));
|
|
172 |
|
|
173 |
#ifdef DEBUG
|
|
174 |
sprintf(test_buf, "Switching tasks, sp = %x\n\r", sp);
|
|
175 |
usb_puts(test_buf);
|
|
176 |
#endif //DEBUG
|
|
177 |
|
|
178 |
/* start process and enable interrupts */
|
|
179 |
asm volatile("reti \n");
|
| 97 |
180 |
break;
|
| 98 |
181 |
}
|
| 99 |
|
|
| 100 |
|
if( PCB[task_i].running ) {
|
|
182 |
|
|
183 |
//Continue an old task.
|
|
184 |
if(PCB[task_i].running) {
|
| 101 |
185 |
current_task = task_i;
|
|
186 |
|
|
187 |
//Reset the stack pointer.
|
|
188 |
sp = PCB[current_task].sp;
|
|
189 |
asm volatile( \
|
|
190 |
"out __SP_L__, %A0 \n" \
|
|
191 |
"out __SP_H__, %B0 \n" : : "d" (sp));
|
|
192 |
|
| 102 |
193 |
task_i++;
|
| 103 |
194 |
break;
|
| 104 |
195 |
}
|
| 105 |
196 |
|
| 106 |
|
//Loop back to 0 if necessary.
|
|
197 |
//Loop back to 1 if necessary.
|
| 107 |
198 |
task_i++;
|
| 108 |
|
if(task_i >= nactive_tasks)
|
|
199 |
if(task_i >= nactive_tasks)
|
| 109 |
200 |
task_i = 1;
|
| 110 |
201 |
}
|
| 111 |
202 |
|
| 112 |
|
//If no task was selected to run,
|
|
203 |
//If no task was selected to run, go to main
|
| 113 |
204 |
if(i == 0) {
|
| 114 |
|
//Return to main.
|
|
205 |
#ifdef DEBUG
|
|
206 |
usb_puts("Resetting main.\n\r");
|
|
207 |
#endif //DEBUG
|
|
208 |
//Set the SP to the original stack.
|
|
209 |
sp = PCB[current_task].sp;
|
|
210 |
asm volatile( \
|
|
211 |
"out __SP_L__, %A0 \n" \
|
|
212 |
"out __SP_H__, %B0 \n" : : "d" (sp));
|
|
213 |
|
| 115 |
214 |
current_task = 0;
|
| 116 |
215 |
}
|
| 117 |
|
}
|
| 118 |
|
|
| 119 |
|
void store_task(void) {
|
| 120 |
|
//Store all state for this task.
|
|
216 |
|
|
217 |
//Set the running task.*/
|
|
218 |
PCB[current_task].running = 1;
|
| 121 |
219 |
|
| 122 |
|
/* store general purpose registers */
|
| 123 |
|
asm volatile( \
|
| 124 |
|
"push r31 \n" \
|
| 125 |
|
"push r30 \n" \
|
| 126 |
|
"push r29 \n" \
|
| 127 |
|
"push r28 \n" \
|
| 128 |
|
"push r27 \n" \
|
| 129 |
|
"push r26 \n" \
|
| 130 |
|
"push r25 \n" \
|
| 131 |
|
"push r24 \n" \
|
| 132 |
|
"push r23 \n" \
|
| 133 |
|
"push r22 \n" \
|
| 134 |
|
"push r21 \n" \
|
| 135 |
|
"push r20 \n" \
|
| 136 |
|
"push r19 \n" \
|
| 137 |
|
"push r18 \n" \
|
| 138 |
|
"push r17 \n" \
|
| 139 |
|
"push r16 \n" \
|
| 140 |
|
"push r15 \n" \
|
| 141 |
|
"push r14 \n" \
|
| 142 |
|
"push r13 \n" \
|
| 143 |
|
"push r12 \n" \
|
| 144 |
|
"push r11 \n" \
|
| 145 |
|
"push r10 \n" \
|
| 146 |
|
"push r9 \n" \
|
| 147 |
|
"push r8 \n" \
|
| 148 |
|
"push r7 \n" \
|
| 149 |
|
"push r6 \n" \
|
| 150 |
|
"push r5 \n" \
|
| 151 |
|
"push r4 \n" \
|
| 152 |
|
"push r3 \n" \
|
| 153 |
|
"push r2 \n" \
|
| 154 |
|
"push r1 \n" \
|
| 155 |
|
"push r0 \n");
|
| 156 |
|
|
| 157 |
|
//Store status register.
|
| 158 |
|
asm volatile( \
|
| 159 |
|
"in r0, __SREG__ \n" \
|
| 160 |
|
"push r0 \n");
|
| 161 |
|
|
| 162 |
|
//Store sp.
|
| 163 |
|
uint16_t sploc = (uint16_t)&(PCB[current_task].sp);
|
| 164 |
|
asm volatile( \
|
| 165 |
|
"in r0, __SP_L__ \n" \
|
| 166 |
|
"st Z+, r0 \n" \
|
| 167 |
|
"in r0, __SP_H__ \n" \
|
| 168 |
|
"st Z, r0 \n" : : "e" (sploc));
|
| 169 |
|
|
|
220 |
//avr-gcc inserts all of the necessary pops and the reti here.
|
| 170 |
221 |
}
|
| 171 |
222 |
|
| 172 |
|
//Figure out which task to run next, then run it.
|
| 173 |
|
void restore_task(void) {
|
| 174 |
|
|
| 175 |
|
//Again, we never return.
|
| 176 |
|
asm volatile( \
|
| 177 |
|
"pop r0 \n" \
|
| 178 |
|
"pop r0 \n");
|
| 179 |
|
|
| 180 |
|
/* restore stack pointer */
|
| 181 |
|
uint16_t sp = (uint16_t)PCB[current_task].sp;
|
| 182 |
|
asm volatile( \
|
| 183 |
|
"out __SP_L__, %A0 \n" \
|
| 184 |
|
"out __SP_H__, %B0 \n" : : "d" (sp));
|
| 185 |
|
|
| 186 |
|
/* restore status register */
|
| 187 |
|
asm volatile( \
|
| 188 |
|
"pop r0 \n" \
|
| 189 |
|
"out __SREG__, r0 \n");
|
| 190 |
|
|
| 191 |
|
/* restore general purpose registers */
|
| 192 |
|
asm volatile( \
|
| 193 |
|
"pop r0 \n" \
|
| 194 |
|
"pop r1 \n" \
|
| 195 |
|
"pop r2 \n" \
|
| 196 |
|
"pop r3 \n" \
|
| 197 |
|
"pop r4 \n" \
|
| 198 |
|
"pop r5 \n" \
|
| 199 |
|
"pop r6 \n" \
|
| 200 |
|
"pop r7 \n" \
|
| 201 |
|
"pop r8 \n" \
|
| 202 |
|
"pop r9 \n" \
|
| 203 |
|
"pop r10 \n" \
|
| 204 |
|
"pop r11 \n" \
|
| 205 |
|
"pop r12 \n" \
|
| 206 |
|
"pop r13 \n" \
|
| 207 |
|
"pop r14 \n" \
|
| 208 |
|
"pop r15 \n" \
|
| 209 |
|
"pop r16 \n" \
|
| 210 |
|
"pop r17 \n" \
|
| 211 |
|
"pop r18 \n" \
|
| 212 |
|
"pop r19 \n" \
|
| 213 |
|
"pop r20 \n" \
|
| 214 |
|
"pop r21 \n" \
|
| 215 |
|
"pop r22 \n" \
|
| 216 |
|
"pop r23 \n" \
|
| 217 |
|
"pop r24 \n" \
|
| 218 |
|
"pop r25 \n" \
|
| 219 |
|
"pop r26 \n" \
|
| 220 |
|
"pop r27 \n" \
|
| 221 |
|
"pop r28 \n" \
|
| 222 |
|
"pop r29 \n" \
|
| 223 |
|
"pop r30 \n" \
|
| 224 |
|
"pop r31 \n");
|
| 225 |
|
|
| 226 |
|
/* start process and enable interrupts */
|
| 227 |
|
//Note that either create_launch_stack or a timer interrupt
|
| 228 |
|
//already has stored the value to load into PC
|
| 229 |
|
asm volatile("reti \n");
|
| 230 |
|
}
|
| 231 |
|
|
| 232 |
|
void restore_next_task(void) {
|
| 233 |
|
//Again, we never return.
|
| 234 |
|
asm volatile( \
|
| 235 |
|
"pop r0 \n" \
|
| 236 |
|
"pop r0 \n");
|
| 237 |
|
|
| 238 |
|
PCB[current_task].running = 0;
|
| 239 |
|
scheduler();
|
| 240 |
|
PCB[current_task].running = 1;
|
| 241 |
|
restore_task();
|
| 242 |
|
}
|
| 243 |
|
|
| 244 |
|
//I need a timer to do the rest. Thinking about stealing it from rtc.
|
| 245 |
|
//ISR needs to
|
| 246 |
|
//a) store_task
|
| 247 |
|
//b) restore_next_task.
|
| 248 |
|
|
| 249 |
|
|