Statistics
| Revision:

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

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