aboutsummaryrefslogtreecommitdiff
path: root/kernel/sched
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2014-01-24 20:10:45 +0100
committerJakob Odersky <jodersky@gmail.com>2014-01-24 20:10:45 +0100
commit6cdf34b0e87bc915de39a6d5817980a825a720da (patch)
tree1b8710106c0201ecad5e199099b4f084795b96ea /kernel/sched
downloadmux-6cdf34b0e87bc915de39a6d5817980a825a720da.tar.gz
mux-6cdf34b0e87bc915de39a6d5817980a825a720da.tar.bz2
mux-6cdf34b0e87bc915de39a6d5817980a825a720da.zip
initial commit
Diffstat (limited to 'kernel/sched')
-rw-r--r--kernel/sched/idle.c18
-rw-r--r--kernel/sched/include/sched/idle.h6
-rw-r--r--kernel/sched/include/sched/sched.h117
-rw-r--r--kernel/sched/mcu/atmega2560/context.c96
-rw-r--r--kernel/sched/mcu/atmega2560/include/mcu/sched/context.h116
-rw-r--r--kernel/sched/sched.c54
6 files changed, 407 insertions, 0 deletions
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
new file mode 100644
index 0000000..18ccdd5
--- /dev/null
+++ b/kernel/sched/idle.c
@@ -0,0 +1,18 @@
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+#include <avr/power.h>
+#include "sched/idle.h"
+#include "tshield/tshield.h"
+
+void idle_entry(char args) {
+ while(1) {
+ tshield_led(TSHIELD_LED_IDLE,1);
+ set_sleep_mode(SLEEP_MODE_IDLE);
+ cli();
+ sleep_enable();
+ sei();
+ sleep_cpu();
+ sleep_disable();
+ };
+}
+
diff --git a/kernel/sched/include/sched/idle.h b/kernel/sched/include/sched/idle.h
new file mode 100644
index 0000000..3b6e40a
--- /dev/null
+++ b/kernel/sched/include/sched/idle.h
@@ -0,0 +1,6 @@
+#ifndef IDLE_H
+#define IDLE_H
+
+void idle_entry(char args);
+
+#endif \ No newline at end of file
diff --git a/kernel/sched/include/sched/sched.h b/kernel/sched/include/sched/sched.h
new file mode 100644
index 0000000..d63b78c
--- /dev/null
+++ b/kernel/sched/include/sched/sched.h
@@ -0,0 +1,117 @@
+#ifndef SCHED_H
+#define SCHED_H
+
+#include "collection/list.h"
+#include "mcu/sched/context.h"
+
+/** Proposed default stack size, you may use this definition for declaring tasks. */
+#define STACK_SIZE 64
+
+/** Stack size to be allocated for the idle task. */
+#define IDLE_STACK_SIZE 64
+
+/**
+ * Task control block, contains runtime
+ * information of tasks.
+ */
+struct tcb_t {
+ /** Stack pointer of this task. (must be first in structure) */
+ char* volatile sp;
+
+ /** Lowest address of this task's memory (inclusive). */
+ char* mem_low;
+
+ /** Highest address of this task's memory (inclusive). */
+ char* mem_high;
+
+ /** Entry function of this task. */
+ void (*entry)(char);
+
+ /** Current wait queue that this task is in. */
+ struct list_head q;
+};
+
+/**
+ * Utility for declaring a task with statically allocated memory.
+ * Note: for a task to be scheduled, it must first be spawned (see spawn()).
+*/
+#define DECLARE_TASK(name, stack_size, entry_function) \
+ static char volatile declared_stack_##name[stack_size]; \
+ static struct tcb_t name = { \
+ .sp = 0, \
+ .mem_low = declared_stack_##name, \
+ .mem_high = declared_stack_##name + stack_size - 1, \
+ .entry = entry_function, \
+ .q = {} \
+ };
+
+/**
+ * Points to currently executing task. If no scheduling has been enabled,
+ * this points to NULL
+ */
+extern struct tcb_t* volatile current;
+
+/**
+ * Queue that contains all tasks that are ready to be run, awaiting their
+ * turn from the scheduler.
+ */
+extern struct list_head ready;
+
+/**
+ * Stack pointer for operations performed out of task context, including any
+ * calls made after SAVE_CONTEXT().
+ */
+extern char* volatile kstack;
+
+
+/**
+ * Makes the current task sleep on a specific queue.
+ * This moves the current task to the given queue's tail.
+ */
+static inline void sleep_on(struct list_head* queue) {
+ list_move_tail(&current->q, queue);
+}
+
+/**
+ * Wakes all tasks waiting in the given queue.
+ * This moves all tasks contained in the queue to the ready queue.
+ */
+static inline void wake_all(struct list_head* queue) {
+ list_splice_init(queue, ready.prev);
+}
+
+/**
+ * Initializes the scheduler by setting up kstack, initializing the idle task
+ * and selecting the first task to run.
+ */
+void sched_init();
+
+/**
+ * Ticks the scheduler.
+ */
+inline void sched_tick() {
+ schedule(); //in a round-robin scheduler, scheduling is called after every tick
+}
+
+/**
+ * Enters the scheduler, setting current to the next runnable task.
+ */
+void schedule();
+
+/**
+ * Initializes a given task and adds it to the ready queue.
+ */
+void spawn(struct tcb_t* const tcb, char args);
+
+/**
+ * Voluntarily yields control of the CPU to the scheduler.
+ */
+void yield() __attribute__ ( ( naked ) );
+
+void freeze() __attribute__ ( ( naked ) );
+
+
+#define ENTER_CRITICAL() cli()
+#define EXIT_CRITICAL() sei()
+
+#endif
diff --git a/kernel/sched/mcu/atmega2560/context.c b/kernel/sched/mcu/atmega2560/context.c
new file mode 100644
index 0000000..7a88b33
--- /dev/null
+++ b/kernel/sched/mcu/atmega2560/context.c
@@ -0,0 +1,96 @@
+#include "mcu/sched/context.h"
+
+char* init_stack(const char* const mem_low, const char* const mem_high, void (*entry)(char), char args) {
+ char* sp = (char*) mem_high;
+ unsigned long address = (unsigned long) entry;
+
+
+ *sp = (char) 0x1;
+ sp--;
+ *sp = (char) 0x2;
+ sp--;
+ *sp = (char) 0x1;
+ sp--;
+ *sp = (char) 0x2;
+ sp--;
+
+ *sp = (char) ( address & (unsigned short) 0x00ff );
+ sp--;
+
+ *sp = (char) ( (address >> 8) & ( unsigned short ) 0x00ff );
+ sp--;
+
+ *sp = (char) ( (address >> 16) & ( unsigned short ) 0x00ff );
+ sp--;
+
+ *sp = (char) 0x00; //r0
+ sp--;
+ *sp = (char) 0x80; //SREG, enable interrupts when task starts
+ sp--;
+ *sp = ( char ) 0x00; //r1
+ sp--;
+ *sp = ( char ) 0x00; //r2
+ sp--;
+ *sp = ( char ) 0x00; //r3
+ sp--;
+ *sp = ( char ) 0x00; //r4
+ sp--;
+ *sp = ( char ) 0x00; //r5
+ sp--;
+ *sp = ( char ) 0x00; //r6
+ sp--;
+ *sp = ( char ) 0x00; //r7
+ sp--;
+ *sp = ( char ) 0x00; //r8
+ sp--;
+ *sp = ( char ) 0x00; //r9
+ sp--;
+ *sp = ( char ) 0x00; //r10
+ sp--;
+ *sp = ( char ) 0x00; //r11
+ sp--;
+ *sp = ( char ) 0x00; //r12
+ sp--;
+ *sp = ( char ) 0x00; //r13
+ sp--;
+ *sp = ( char ) 0x00; //r14
+ sp--;
+ *sp = ( char ) 0x00; //r15
+ sp--;
+ *sp = ( char ) 0x00; //r16
+ sp--;
+ *sp = ( char ) 0x00; //r17
+ sp--;
+ *sp = ( char ) 0x00; //r18
+ sp--;
+ *sp = ( char ) 0x00; //r19
+ sp--;
+ *sp = ( char ) 0x00; //r20
+ sp--;
+ *sp = ( char ) 0x00; //r21
+ sp--;
+ *sp = ( char ) 0x00; //r22
+ sp--;
+ *sp = ( char ) 0x00; //r23
+ sp--;
+
+ *sp = (char) ( args ); //place first argument in register 24
+ sp--;
+
+ *sp = ( char ) 0x00; //r25
+ sp--;
+ *sp = ( char ) 0x00; //r26
+ sp--;
+ *sp = ( char ) 0x00; //r27
+ sp--;
+ *sp = ( char ) 0x00; //r28
+ sp--;
+ *sp = ( char ) 0x00; //r29
+ sp--;
+ *sp = ( char ) 0x00; //r30
+ sp--;
+ *sp = ( char ) 0x00; //r31
+ sp--;
+
+ return sp;
+}
diff --git a/kernel/sched/mcu/atmega2560/include/mcu/sched/context.h b/kernel/sched/mcu/atmega2560/include/mcu/sched/context.h
new file mode 100644
index 0000000..9ff8d42
--- /dev/null
+++ b/kernel/sched/mcu/atmega2560/include/mcu/sched/context.h
@@ -0,0 +1,116 @@
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include <avr/interrupt.h>
+
+#define SAVE_CONTEXT() \
+ asm volatile ( \
+ "push r0 \n\t" \
+ "in r0, __SREG__ \n\t" \
+ "cli \n\t" \
+ "push r0 \n\t" \
+ "push r1 \n\t" \
+ "clr r1 \n\t" \
+ "push r2 \n\t" \
+ "push r3 \n\t" \
+ "push r4 \n\t" \
+ "push r5 \n\t" \
+ "push r6 \n\t" \
+ "push r7 \n\t" \
+ "push r8 \n\t" \
+ "push r9 \n\t" \
+ "push r10 \n\t" \
+ "push r11 \n\t" \
+ "push r12 \n\t" \
+ "push r13 \n\t" \
+ "push r14 \n\t" \
+ "push r15 \n\t" \
+ "push r16 \n\t" \
+ "push r17 \n\t" \
+ "push r18 \n\t" \
+ "push r19 \n\t" \
+ "push r20 \n\t" \
+ "push r21 \n\t" \
+ "push r22 \n\t" \
+ "push r23 \n\t" \
+ "push r24 \n\t" \
+ "push r25 \n\t" \
+ "push r26 \n\t" \
+ "push r27 \n\t" \
+ "push r28 \n\t" \
+ "push r29 \n\t" \
+ "push r30 \n\t" \
+ "push r31 \n\t" \
+ "lds r26, current \n\t" \
+ "lds r27, current +1 \n\t" \
+ "in r0, __SP_L__ \n\t" \
+ "st x+, r0 \n\t" \
+ "in r0, __SP_H__ \n\t" \
+ "st x+, r0 \n\t" \
+ "lds r26, kstack \n\t" \
+ "lds r27, kstack + 1 \n\t" \
+ "ld r28, x+ \n\t" \
+ "out __SP_L__, r28 \n\t" \
+ "ld r29, x+ \n\t" \
+ "out __SP_H__, r29 \n\t" \
+ )
+
+#define RESTORE_CONTEXT() \
+ asm volatile ( \
+ "lds r26, kstack \n\t" \
+ "lds r27, kstack +1 \n\t" \
+ "in r0, __SP_L__ \n\t" \
+ "st x+, r0 \n\t" \
+ "in r0, __SP_H__ \n\t" \
+ "st x+, r0 \n\t" \
+ "lds r26, current \n\t" \
+ "lds r27, current + 1 \n\t" \
+ "ld r28, x+ \n\t" \
+ "out __SP_L__, r28 \n\t" \
+ "ld r29, x+ \n\t" \
+ "out __SP_H__, r29 \n\t" \
+ "pop r31 \n\t" \
+ "pop r30 \n\t" \
+ "pop r29 \n\t" \
+ "pop r28 \n\t" \
+ "pop r27 \n\t" \
+ "pop r26 \n\t" \
+ "pop r25 \n\t" \
+ "pop r24 \n\t" \
+ "pop r23 \n\t" \
+ "pop r22 \n\t" \
+ "pop r21 \n\t" \
+ "pop r20 \n\t" \
+ "pop r19 \n\t" \
+ "pop r18 \n\t" \
+ "pop r17 \n\t" \
+ "pop r16 \n\t" \
+ "pop r15 \n\t" \
+ "pop r14 \n\t" \
+ "pop r13 \n\t" \
+ "pop r12 \n\t" \
+ "pop r11 \n\t" \
+ "pop r10 \n\t" \
+ "pop r9 \n\t" \
+ "pop r8 \n\t" \
+ "pop r7 \n\t" \
+ "pop r6 \n\t" \
+ "pop r5 \n\t" \
+ "pop r4 \n\t" \
+ "pop r3 \n\t" \
+ "pop r2 \n\t" \
+ "pop r1 \n\t" \
+ "pop r0 \n\t" \
+ "out __SREG__, r0 \n\t" \
+ "pop r0 \n\t" \
+ )
+
+char* init_stack(const char* const mem_low, const char* const mem_high, void (*entry)(char), char args);
+
+static inline void init_kstack(char **kstack) {
+ *kstack = (char*) SP;
+}
+
+#define RETURN() asm volatile("ret");
+
+#endif
diff --git a/kernel/sched/sched.c b/kernel/sched/sched.c
new file mode 100644
index 0000000..8406b8d
--- /dev/null
+++ b/kernel/sched/sched.c
@@ -0,0 +1,54 @@
+#include "sched/sched.h"
+#include "sched/idle.h"
+#include "mcu/sched/context.h"
+
+struct tcb_t* volatile current = 0;
+DECLARE_TASK(idle, IDLE_STACK_SIZE, idle_entry);
+
+struct list_head ready = LIST_HEAD_INIT(ready);
+char* volatile kstack;
+
+static void init_idle() {
+ idle.sp = init_stack(idle.mem_low, idle.mem_high, idle.entry, 0);
+ INIT_LIST_HEAD(&idle.q);
+}
+
+void sched_init() {
+ init_kstack((char **) &kstack);
+ init_idle();
+ schedule();
+ sei();
+ RESTORE_CONTEXT();
+ RETURN();
+}
+
+void schedule() {
+ if(!list_empty(&ready)) {
+ current = list_entry(ready.next, struct tcb_t, q);
+ list_move_tail(&current->q, &ready);
+ } else {
+ current = &idle;
+ }
+}
+
+void spawn(struct tcb_t* const tcb, char args) {
+ tcb->sp = init_stack(tcb->mem_low, tcb->mem_high, tcb->entry, args);
+ INIT_LIST_HEAD(&tcb->q);
+ list_add_tail(&tcb->q, &ready);
+}
+
+void yield(void) {
+ SAVE_CONTEXT();
+ schedule();
+ RESTORE_CONTEXT();
+ asm volatile ( "ret" );
+}
+
+void freeze() {
+ SAVE_CONTEXT();
+ list_del_init(&current->q);
+ schedule();
+ RESTORE_CONTEXT();
+ asm volatile ( "ret" );
+}
+