Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / scheduler / scheduler.c @ 1573

History | View | Annotate | Download (4.71 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 1573 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
#endif //DEBUG
21
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
        char running;
42
        uint16_t period; //Interval in clock cycles.
43
        uint32_t next;
44
} 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
#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
82 1544 justin
        PCB[current_task].running = 0;
83
        yield();
84
}
85
86
void yield() {
87
        //TODO Actually implement.
88
        for(;;){}
89
}
90
91
int register_task(void (*exec)(void), uint16_t period)
92
{
93
        if(nactive_tasks >= MAXTASKS) {
94
                return -1;
95
        }
96
97
        PCB[nactive_tasks].exec = exec;
98
        PCB[nactive_tasks].period = period;
99 1573 justin
        PCB[nactive_tasks].next = current_time + period;
100 1544 justin
        PCB[nactive_tasks].running = 0;
101
102
        //Don't need to initialize SP, it will get done later.
103
        nactive_tasks++;
104
        return nactive_tasks - 1;
105
}
106
107 1573 justin
//I need a timer to do the rest.  Thinking about stealing it from rtc.
108
SIGNAL(TIMER3_COMPA_vect) {
109
        static uint8_t task_i = 1;
110
        volatile uint8_t** sploc;
111
        volatile uint8_t* sp;
112 1544 justin
113 1573 justin
        uint8_t i;
114 1544 justin
115 1573 justin
#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 1544 justin
141
        //Loop over registered tasks, like in round robin order.
142 1573 justin
        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) {
147 1544 justin
148 1573 justin
                        task_i++;
149
150 1544 justin
                        current_task = task_i;
151
                        PCB[task_i].next += PCB[task_i].period;
152 1573 justin
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");
180 1545 justin
                        break;
181
                }
182 1573 justin
183
                //Continue an old task.
184
                if(PCB[task_i].running) {
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 1544 justin
193 1546 justin
                        task_i++;
194 1544 justin
                        break;
195
                }
196
197 1573 justin
                //Loop back to 1 if necessary.
198 1544 justin
                task_i++;
199 1573 justin
                if(task_i >= nactive_tasks)
200 1545 justin
                        task_i = 1;
201 1544 justin
        }
202 1545 justin
203 1573 justin
        //If no task was selected to run, go to main
204 1545 justin
        if(i == 0) {
205 1573 justin
#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
214 1545 justin
                current_task = 0;
215
        }
216 1573 justin
217
        //Set the running task.*/
218
        PCB[current_task].running = 1;
219 1544 justin
220 1573 justin
        //avr-gcc inserts all of the necessary pops and the reti here.
221 1544 justin
}