root / trunk / code / projects / scheduler / scheduler.c @ 1984
History | View | Annotate | Download (4 KB)
1 |
/**
|
---|---|
2 |
* @file scheduler.c
|
3 |
* @brief Scheduler
|
4 |
*
|
5 |
* Implementation of functions for scheduler
|
6 |
* Currently relies on avr-gcc's interrupt call convention.
|
7 |
*
|
8 |
* @author Colony Project, CMU Robotics Club
|
9 |
* Based on avrOS and 18348 Lab9 code
|
10 |
**/
|
11 |
|
12 |
#include <avr/interrupt.h> |
13 |
#include <util/delay.h> |
14 |
#include "scheduler.h" |
15 |
|
16 |
#define DEBUG
|
17 |
|
18 |
#ifdef DEBUG
|
19 |
#include <serial.h> |
20 |
#endif
|
21 |
|
22 |
static uint8_t STACK[MAXTASKS + 1][STACKSIZE]; |
23 |
static uint8_t nactive_tasks = 1; |
24 |
static uint8_t current_task = 0; //Default to main. |
25 |
|
26 |
//Internal functions
|
27 |
static void task_terminate(void); |
28 |
|
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; |
35 |
|
36 |
//Define some PCB's
|
37 |
typedef struct PCB_t |
38 |
{ |
39 |
void (*exec)(void); |
40 |
uint8_t* sp; |
41 |
uint16_t period; //Interval in clock cycles.
|
42 |
uint32_t next; |
43 |
char running;
|
44 |
} PCB_t; |
45 |
|
46 |
PCB_t PCB[MAXTASKS + 1];
|
47 |
|
48 |
//Keep track of global time.
|
49 |
static uint32_t current_time = 0; |
50 |
|
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 |
sei(); |
72 |
} |
73 |
|
74 |
static void task_terminate(void) { |
75 |
PCB[current_task].running = 0;
|
76 |
yield(); |
77 |
} |
78 |
|
79 |
void yield() {
|
80 |
//TODO Actually implement.
|
81 |
for(;;){}
|
82 |
} |
83 |
|
84 |
int register_task(void (*exec)(void), uint16_t period) |
85 |
{ |
86 |
if(nactive_tasks >= MAXTASKS) {
|
87 |
return -1; |
88 |
} |
89 |
|
90 |
PCB[nactive_tasks].exec = exec; |
91 |
PCB[nactive_tasks].period = period; |
92 |
|
93 |
cli(); |
94 |
PCB[nactive_tasks].next = current_time + period; |
95 |
sei(); |
96 |
|
97 |
PCB[nactive_tasks].running = 0;
|
98 |
|
99 |
//Don't need to initialize SP, it will get done later.
|
100 |
nactive_tasks++; |
101 |
return nactive_tasks - 1; |
102 |
} |
103 |
|
104 |
//I need a timer to do the rest. Thinking about stealing it from rtc.
|
105 |
SIGNAL(TIMER3_COMPA_vect) { |
106 |
static uint8_t task_i = 1; |
107 |
volatile uint8_t* sp;
|
108 |
|
109 |
uint8_t i; |
110 |
|
111 |
//Store sp.
|
112 |
sp = (uint8_t*)&(PCB[current_task].sp); |
113 |
|
114 |
asm volatile( \ |
115 |
"in r0, __SP_L__ \n" \
|
116 |
"st %a0+, r0 \n" \
|
117 |
"in r0, __SP_H__ \n" \
|
118 |
"st %a0, r0 \n" : : "e" (sp)); |
119 |
|
120 |
//Keep track of the number of 1/16 second's that have passed.
|
121 |
current_time++; |
122 |
|
123 |
/******** scheduler ********/
|
124 |
|
125 |
//Loop over registered tasks, like in round robin order.
|
126 |
for(i = nactive_tasks - 1; i > 0; i--) { |
127 |
//Launch a new task if it is due to run.
|
128 |
if( !PCB[task_i].running
|
129 |
&& PCB[task_i].next <= current_time) { |
130 |
|
131 |
current_task = task_i; |
132 |
PCB[current_task].next += PCB[current_task].period; |
133 |
PCB[current_task].running = 1;
|
134 |
|
135 |
task_i++; |
136 |
if(task_i >= nactive_tasks)
|
137 |
task_i = 1;
|
138 |
|
139 |
/***** create launch stack ***/
|
140 |
|
141 |
sp = (void*)((uint16_t)&STACK[current_task][STACKSIZE - 1]); |
142 |
|
143 |
//Put task terminate and the task to execute on the stack.
|
144 |
*(sp--) = (uint8_t)(uint16_t)*task_terminate; |
145 |
*(sp--) = (uint8_t)((uint16_t)*task_terminate >> 8);
|
146 |
|
147 |
*(sp--) = (uint8_t)(uint16_t)PCB[current_task].exec; |
148 |
*(sp--) = (uint8_t)((uint16_t)PCB[current_task].exec >> 8);
|
149 |
|
150 |
//The current state of the registers is what the tasks will
|
151 |
//see on entry (shouldn't matter).
|
152 |
|
153 |
asm volatile( \ |
154 |
"out __SP_L__, %A0 \n" \
|
155 |
"out __SP_H__, %B0 \n" : : "d" (sp)); |
156 |
|
157 |
/* start process and enable interrupts */
|
158 |
asm volatile("reti \n"); |
159 |
break;
|
160 |
} |
161 |
|
162 |
//Continue an old task.
|
163 |
if(PCB[task_i].running == 1) { |
164 |
current_task = task_i; |
165 |
task_i++; |
166 |
if(task_i >= nactive_tasks)
|
167 |
task_i = 1;
|
168 |
break;
|
169 |
} |
170 |
|
171 |
task_i++; |
172 |
if(task_i >= nactive_tasks)
|
173 |
task_i = 1;
|
174 |
} |
175 |
|
176 |
//If no task was selected to run, go to main
|
177 |
if(i == 0) |
178 |
current_task = 0;
|
179 |
|
180 |
//Reset the stack pointer.
|
181 |
sp = PCB[current_task].sp; |
182 |
asm volatile( \ |
183 |
"out __SP_L__, %A0 \n" \
|
184 |
"out __SP_H__, %B0 \n" : : "d" (sp)); |
185 |
|
186 |
//Set the running task.*/
|
187 |
PCB[current_task].running = 1;
|
188 |
|
189 |
//avr-gcc inserts all of the necessary pops and the reti here.
|
190 |
} |
191 |
|