📄 msched.c
字号:
// This file is part of MANTIS OS, Operating System// See http://mantis.cs.colorado.edu///// Copyright (C) 2003,2004,2005 University of Colorado, Boulder//// This program is free software; you can redistribute it and/or// modify it under the terms of the mos license (see file LICENSE)#include "plat_dep.h"#include "mos.h"#include "msched.h"#include "tlist.h"#include "mem.h"#include "led.h" //TODO: remove when done debugging#include "clock.h"#include "context_switch.h" //provides asm for saving/switching/restoring stack#include "plat_clock.h"#include "mutex.h"#ifdef DEBUG_MEMORY // from msched.h#include <avr/pgmspace.h>#include "printf.h"const char sThreadRow[] ARCH_PROGMEM = "thread %d: sp %x, stack %x, size %d, unused %d, state %C, priority %C\n";const char sThreadTot[] ARCH_PROGMEM = "%C threads, %d stack allocated, %d stack used\n";const char sStackOver[] ARCH_PROGMEM = "stack %C has overflowed\n";const char sStackFull[] ARCH_PROGMEM = "stack %C looks full\n";#endifstatic uint8_t running; // flag for the state of the scheduleruint16_t last_sleep_time; // time the processor was sleeping (msecs)static thread_t *wakeup_front;static thread_t threads[MAX_THREADS]; // Our array of system thread data structsthread_t *_current_thread; // The currently running threadstatic thread_t *sleep_thread; // The currently sleeping threadstatic tlist_t readyQ[NUM_PRIORITIES]; // Ready Queue with prioritiesstatic tlist_t sleepQ; // Sleep Queueextern uint8_t _end; //linker-provided symbol for end of memory/* WARNING: You can not use local variables in the dispatcher because they mess with the calling functions stack. So we use these */ static uint8_t d_index;/** @brief Heart of the scheduler. * * Saves the current thread's context and then restores a new * thread. This function implements both context switching and * the actual scheduling algorithm. * NOTE: the naked attribute tells the compiler not to add any * prologue or epilogue code. */void __attribute__((naked)) dispatcher(void);void idle_loop(void);void start_wrapper(void);//static void mos_thread_wakeup_sleep (void);static inline void mos_thread_wakeup(uint16_t sleep_time);static inline void mos_thread_wakeup_noints(uint16_t sleep_time, handle_t int_handle);void mos_single_threaded(void){ //TIMSK &= ~(1 << TSLICE_INT); //turn off time slice DISABLE_TSLICE_TIMER(); //TSLICE_OCR = 0xffff; //SET_TSLICE_OCR_VALUE(0xffff);}void mos_multi_threaded(void){ //uint16_t sleep_time = 0; //handle_t int_handle = mos_disable_ints (); //sleep_time += (10 * TSLICE_CNT) / (CLOCK_SPEED_1024 / 100); //sleep_time += (10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100); //TSLICE_OCR = TIMESLICE_20_MS; //SET_TSLICE_OCR_VALUE(TIMESLICE_20_MS); //TSLICE_CNT = 0; //SET_TSLICE_TIMER_VALUE(0); //TIMSK |= (1 << TSLICE_INT); //turn on time slice ENABLE_TSLICE_TIMER(); //mos_enable_ints(int_handle); //mos_thread_wakeup(sleep_time);}void sched_init(void){ uint8_t i; DISABLE_INTS(); // Init the memory system for our stacks. mem_init(&_end, HEAP_TOP - ((int)&_end)); // init the thread table for(i = 0; i < MAX_THREADS; i++) { threads[i].sp = 0; threads[i].state = EMPTY; threads[i].suspend_state = SUSPEND_STATE_SLEEP; threads[i].priority = 0; threads[i].next = NULL; } // Allocate space for the kernel thread, which is currently running. _current_thread = &threads[0]; _current_thread->state = RUNNING; _current_thread->priority = PRIORITY_IDLE; #ifdef DEBUG_MEMORY // For debugging, fill these fields in for the kernel thread, too. _current_thread->stack = (uint8_t *)HEAP_TOP; _current_thread->stackSize = IDLE_STACK_SIZE; // Flag the block for debugging, normally memory manager does this. // Be careful not to write over our current stack. stackval_t *j = (stackval_t *)HEAP_TOP; stackval_t *sp; GET_SP(sp); for(; j < sp; j++) *j = 0xEF;#endif //Init the ready queue for(i = 0; i < NUM_PRIORITIES; i++) mos_tlist_init(&readyQ[i]);}void mos_sched_start(void){ running = TRUE; //initialize the hardware timers which control //sleeping and timeslicing kernel_timer_init(); sleep_timer_init(); // Once interrupts are enabled here, the sheduler will start to schedule // threads and do time slicing. ENABLE_INTS(); //Start executing the first thread dispatcher(); // On to the idle loop idle_loop();}void mos_thread_suspend_state(uint8_t state){ if(state >= SUSPEND_STATE_MAX) state = SUSPEND_STATE_IDLE; handle_t int_handle = mos_disable_ints(); _current_thread->next = NULL; _current_thread->state = BLOCKED; // Update the thread state // store the old suspend state in the sleep time, // which we aren't using _current_thread->st = (uint32_t)_current_thread->suspend_state; _current_thread->suspend_state = state; mos_thread_wakeup_noints((10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100), int_handle);}void mos_thread_suspend_noints(handle_t int_handle){ _current_thread->next = NULL; _current_thread->state = BLOCKED; // Update the thread state // store the old suspend state in the sleep time, // which we aren't using _current_thread->st = (uint32_t)_current_thread->suspend_state; _current_thread->suspend_state = SUSPEND_STATE_IDLE; mos_thread_wakeup_noints((10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100), int_handle);}void mos_thread_resume(thread_t *thread){ handle_t int_handle; int_handle = mos_disable_ints(); // put given thread onto the ready Queue if it was blocked. if(thread->state == BLOCKED) { mos_tlist_add(&readyQ[thread->priority], thread); thread->state = READY; thread->suspend_state = (uint8_t)thread->st; } mos_thread_wakeup_noints((10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100), int_handle);}void mos_thread_resume_noints(thread_t *thread, handle_t int_handle){ // put given thread onto the ready Queue if it was blocked. if(thread->state == BLOCKED) { mos_tlist_add(&readyQ[thread->priority], thread); thread->state = READY; thread->suspend_state = (uint8_t)thread->st; } mos_thread_wakeup_noints((10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100), int_handle);}void mos_thread_resume_noints_nodispatch(thread_t *thread, handle_t int_handle){ // put given thread onto the ready Queue if it was blocked. if(thread->state == BLOCKED) { mos_tlist_add(&readyQ[thread->priority], thread); thread->state = READY; thread->suspend_state = (uint8_t)thread->st; } }void mos_thread_sleep(uint32_t sleeptime){ handle_t int_handle; if(sleeptime < 40) sleeptime = 40; int_handle = mos_disable_ints(); _current_thread->state = SLEEPING; // Update the thread state _current_thread->st = sleeptime; // Update the sleeptime mos_tlist_ordadd(&sleepQ, _current_thread); mos_thread_wakeup_noints((10 * TSLICE_TIMER_VALUE) / (CLOCK_SPEED_1024 / 100), int_handle);}/** @brief wake up a thread along with decrementing the sleep time * this function wakes sleeping threads if necessary, otherwise just * decrements the sleep time of the head of the list. * @param down_time ammount of time passed since last sleeping */static inline void mos_thread_wakeup(uint16_t sleep_time){ //Adjust the sleepQ with the times that has already expired. mos_tlist_adjustst(&sleepQ, sleep_time); //get the first thread in the sleep queue wakeup_front = mos_tlist_head(&sleepQ); //loop through the sleep queue while((wakeup_front != NULL) && (wakeup_front->st == 0)) { //remove the thread if sleep time is 0 sleep_thread = mos_tlist_remove(&sleepQ); sleep_thread->state = READY; // Update the thread state //add thread to ready queue mos_tlist_add(&readyQ[sleep_thread->priority], sleep_thread); wakeup_front = mos_tlist_head(&sleepQ); } dispatcher(); // do the context switch.}static inline void mos_thread_wakeup_noints(uint16_t sleep_time, handle_t int_handle){ //Adjust the sleepQ with the times that has already expired. mos_tlist_adjustst(&sleepQ, sleep_time); //get the first thread in the sleep queue wakeup_front = mos_tlist_head(&sleepQ); //loop through the sleep queue while((wakeup_front != NULL) && (wakeup_front->st == 0)) { //remove the thread if sleep time is 0 sleep_thread = mos_tlist_remove(&sleepQ); sleep_thread->state = READY; // Update the thread state //add thread to ready queue mos_tlist_add(&readyQ[sleep_thread->priority], sleep_thread); wakeup_front = mos_tlist_head(&sleepQ); } mos_enable_ints(int_handle); dispatcher(); // do the context switch.}/** @brief Real dispatcher function * * Doxygen doesn't seem to like gcc attributes */void __attribute__((naked)) dispatcher(void){ // Prologue: Saves the current state. // All registers go onto current stack and then the stack is saved running = FALSE; PUSH_THREAD_STACK(); //stop timeslicing during the context switch, but enable interrupts to //service any pending io interrupts //ENABLE_INTS(); //asm volatile("nop\n"); //DISABLE_INTS(); // Only change the thread's state if it was running, not blocked if(_current_thread->state == RUNNING) { _current_thread->state = READY; mos_tlist_add(&readyQ[_current_thread->priority], _current_thread); } // Now bring up the new thread // idle thread's existence guarantees we'll get something for(d_index = 0; d_index < NUM_PRIORITIES; d_index++){ _current_thread = mos_tlist_remove(&readyQ[d_index]); if(_current_thread != NULL) break; } _current_thread->state = RUNNING; //ENABLE_INTS(); //asm volatile("nop\n"); //DISABLE_INTS(); POP_THREAD_STACK(); running = TRUE; // return must be explicit because naked functions don't have one asm volatile("ret\n");}/** @brief set timer0 and go to sleep for x ammount of ms * @param time ammount of time to sleep the kernel for in ms */static inline void kernel_sleep(uint8_t time){ SET_SLEEP_TIMER_VALUE(0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -