root / trunk / code / projects / scheduler / scheduler.c @ 1574
History | View | Annotate | Download (4 KB)
1 | 1479 | jsexton | /**
|
---|---|---|---|
2 | * @file scheduler.c
|
||
3 | * @brief Scheduler
|
||
4 | *
|
||
5 | * Implementation of functions for scheduler
|
||
6 | 1573 | justin | * Currently relies on avr-gcc's interrupt call convention.
|
7 | 1479 | jsexton | *
|
8 | * @author Colony Project, CMU Robotics Club
|
||
9 | 1544 | justin | * Based on avrOS and 18348 Lab9 code
|
10 | 1479 | jsexton | **/
|
11 | 16 | bcoltin | |
12 | 1574 | justin | #include <avr/interrupt.h> |
13 | #include <util/delay.h> |
||
14 | 1479 | jsexton | #include "scheduler.h" |
15 | 1544 | justin | |
16 | 1573 | justin | #define DEBUG
|
17 | |||
18 | #ifdef DEBUG
|
||
19 | #include <serial.h> |
||
20 | 1574 | justin | #endif
|
21 | 1573 | justin | |
22 | static uint8_t STACK[MAXTASKS + 1][STACKSIZE]; |
||
23 | 1544 | justin | static uint8_t nactive_tasks = 1; |
24 | static uint8_t current_task = 0; //Default to main. |
||
25 | |||
26 | //Internal functions
|
||
27 | 1573 | justin | static void task_terminate(void); |
28 | 1544 | justin | |
29 | 1573 | justin | //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 | 1544 | justin | |
36 | 1573 | justin | //Define some PCB's
|
37 | 1544 | justin | 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 | 1574 | justin | char running;
|
44 | 1544 | justin | } PCB_t; |
45 | |||
46 | PCB_t PCB[MAXTASKS + 1];
|
||
47 | |||
48 | 1573 | justin | //Keep track of global time.
|
49 | static uint32_t current_time = 0; |
||
50 | 1544 | justin | |
51 | 1573 | justin | 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 | 1544 | justin | 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 | 1574 | justin | |
93 | cli(); |
||
94 | 1573 | justin | PCB[nactive_tasks].next = current_time + period; |
95 | 1574 | justin | sei(); |
96 | |||
97 | 1544 | justin | 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 | 1573 | justin | //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 | 1544 | justin | |
109 | 1573 | justin | uint8_t i; |
110 | 1544 | justin | |
111 | 1573 | justin | //Store sp.
|
112 | 1574 | justin | sp = (uint8_t*)&(PCB[current_task].sp); |
113 | 1573 | justin | |
114 | asm volatile( \ |
||
115 | "in r0, __SP_L__ \n" \
|
||
116 | "st %a0+, r0 \n" \
|
||
117 | "in r0, __SP_H__ \n" \
|
||
118 | 1574 | justin | "st %a0, r0 \n" : : "e" (sp)); |
119 | 1573 | justin | |
120 | //Keep track of the number of 1/16 second's that have passed.
|
||
121 | current_time++; |
||
122 | |||
123 | /******** scheduler ********/
|
||
124 | 1544 | justin | |
125 | //Loop over registered tasks, like in round robin order.
|
||
126 | 1573 | justin | 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 | 1544 | justin | |
131 | 1574 | justin | current_task = task_i; |
132 | PCB[current_task].next += PCB[current_task].period; |
||
133 | PCB[current_task].running = 1;
|
||
134 | |||
135 | 1573 | justin | task_i++; |
136 | 1574 | justin | if(task_i >= nactive_tasks)
|
137 | task_i = 1;
|
||
138 | 1573 | justin | |
139 | /***** create launch stack ***/
|
||
140 | |||
141 | 1574 | justin | sp = (void*)((uint16_t)&STACK[current_task][STACKSIZE - 1]); |
142 | 1573 | justin | |
143 | //Put task terminate and the task to execute on the stack.
|
||
144 | 1574 | justin | *(sp--) = (uint8_t)(uint16_t)*task_terminate; |
145 | *(sp--) = (uint8_t)((uint16_t)*task_terminate >> 8);
|
||
146 | 1573 | justin | |
147 | 1574 | justin | *(sp--) = (uint8_t)(uint16_t)PCB[current_task].exec; |
148 | *(sp--) = (uint8_t)((uint16_t)PCB[current_task].exec >> 8);
|
||
149 | |||
150 | 1573 | justin | //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 | 1545 | justin | break;
|
160 | } |
||
161 | 1573 | justin | |
162 | //Continue an old task.
|
||
163 | 1574 | justin | if(PCB[task_i].running == 1) { |
164 | 1573 | justin | current_task = task_i; |
165 | 1546 | justin | task_i++; |
166 | 1574 | justin | if(task_i >= nactive_tasks)
|
167 | task_i = 1;
|
||
168 | 1544 | justin | break;
|
169 | } |
||
170 | |||
171 | task_i++; |
||
172 | 1574 | justin | if(task_i >= nactive_tasks)
|
173 | 1545 | justin | task_i = 1;
|
174 | 1544 | justin | } |
175 | 1545 | justin | |
176 | 1573 | justin | //If no task was selected to run, go to main
|
177 | 1574 | justin | if(i == 0) |
178 | 1545 | justin | current_task = 0;
|
179 | 1573 | justin | |
180 | 1574 | justin | //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 | 1573 | justin | //Set the running task.*/
|
187 | PCB[current_task].running = 1;
|
||
188 | 1544 | justin | |
189 | 1573 | justin | //avr-gcc inserts all of the necessary pops and the reti here.
|
190 | 1544 | justin | } |