Main Page   Modules   Data Structures   File List   Globals  

task.c

Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2002, 2003, Oliver Tscherwitschke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007 
00008 1. Redistributions of source code must retain the above copyright notice,
00009    this list of conditions and the following disclaimer.
00010 2. Redistributions in binary form must reproduce the above copyright notice,
00011    this list of conditions and the following disclaimer in the documentation
00012    and/or other materials provided with the distribution.
00013 3. The name of the author may not be used to endorse or promote products
00014    derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
00017 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00018 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00019 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00020 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00021 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
00022 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00023 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00025 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 */
00027 
00035 #include <avr/interrupt.h>
00036 #include <avr/io.h>
00037 #include <string.h>
00038 #include <avr/pgmspace.h>
00039 
00040 #include "otos_cfg.h"
00041 #include "otos_def.h"
00042 #include "types.h"
00043 #include "memory.h"
00044 #include "hardware.h"
00045 #include "error.h"
00046 #include "time.h"
00047 #include "task.h"
00048 
00049 
00050 
00051 /*
00052  * global variables
00053  */
00054 
00055 uint8_t     msgErrOutOfMem[] PROGMEM = "otOS ERR: out of mem";
00056 
00057 /* Note: All global variables are automatically initialized to 0 by the compiler */
00058 
00060 OtosTask*   volatile g_pReadyQueue;
00061 
00063 OtosTask*   volatile g_pBlockedQueue;
00064 
00066 OtosTask*   volatile g_pRunningTask;
00067 
00069 // uint8_t     g_NumberOfTasks;
00070 
00072 uint8_t     volatile g_performDispatch;
00073 
00075 uint8_t     volatile g_timeSliceTicks;
00076 
00078 uint8_t     volatile g_timeSliceCounter;
00079 
00080 /* variables used for profiling */
00081 #ifdef OTOS_PROFILING
00082 
00083 uint32_t    g_idleEnterTicks;
00084 uint32_t    g_idleTotalTicks;
00085 uint8_t     g_idleActive;
00086 
00087 #endif
00088 
00089 
00113 OtosTask* otosCreateTask(void (*pFunc)(void*), void* pArg, uint8_t prio, uint16_t stacksize, uint8_t name)
00114 {
00115     OtosTask*           pTask;
00116     TaskStackLayout*    pStack;
00117     uint8_t*            pMem;
00118     
00119 
00120     /* allocate memory for stack */
00121     pMem = otosAllocate(stacksize);
00122     if (pMem == NULL)       /* error: out of memory */
00123         return NULL;
00124 
00125     /* allocate memory for task structure */
00126     pTask = otosAllocate(sizeof (OtosTask));
00127     if (pTask == NULL)      /* error: out of memory */
00128         return NULL;
00129 
00130 #ifdef OTOS_DEBUG_STACK
00131     /* fill stack with magic pattern */
00132     memset(pMem, 0x42, stacksize);
00133 
00134     /* store stacksize */
00135     pTask->stacksize = stacksize;
00136 #endif
00137 
00138 #ifdef OTOS_STACK_CHECK
00139     /* mark stack begin to be able to detect if it overflows */
00140     pTask->pStack = (uint16_t*) pMem;
00141     *(pTask->pStack) = STACK_CHECK;
00142 #endif
00143 
00144     /* set stack pointer to last word in stack minus size of TaskStackLayout */
00145     pStack = (TaskStackLayout*) (pMem + stacksize - sizeof (TaskStackLayout));
00146 //    (void*) pStack += (stacksize - sizeof (TaskStackLayout));
00147 
00148     /* fill stack frame according to GCCs function calling conventions */
00149     pStack->pcHi = ((uint16_t) pFunc) >> 8;
00150     pStack->pcLo = ((uint16_t) pFunc) & 0xff;
00151 /*    pStack->r1 = 0;   */
00152     pStack->r24 = ((uint16_t) pArg) & 0xff;             /* argument */
00153     pStack->r25 = ((uint16_t) pArg) >> 8;
00154 #ifdef OTOS_BANKING
00155     pStack->bank = 0;                                   /* the default bank is 0 for every task */
00156 #endif
00157 
00158     cli();
00159 
00160     /* set task control block */
00161     pTask->sp = (uint16_t) pStack - 1;
00162     pTask->events = 0;
00163     pTask->waitEvents = 0;
00164     pTask->pWaitMsgQueue = NULL;
00165     pTask->pWaitMutex = NULL;
00166     pTask->sleepTicks = 0;
00167     pTask->priority = prio;
00168     pTask->name = name;
00169 
00170     /* insert task in ready queue */
00171     otosInsertTaskInQueue(&g_pReadyQueue, pTask, TASK_READY);
00172 
00173     /* update global task variables */
00174 //    ++g_NumberOfTasks;
00175 
00176     /* call scheduler. The new task will be started if it has
00177      * higher priority than the running task */
00178     otosScheduler(SCHED_NORM);
00179 
00180     return pTask;
00181 }
00182 
00183 
00190 OtosTask* otosGetRunningTask(void)
00191 {
00192     return g_pRunningTask;
00193 }
00194 
00195 
00204 uint8_t otosSetPriority(uint8_t prio)
00205 {
00206     uint8_t   oldPrio;
00207 
00208 
00209     cli();
00210     oldPrio = g_pRunningTask->priority;
00211     g_pRunningTask->priority = prio;
00212     otosScheduler(SCHED_NORM);
00213 
00214     return oldPrio;
00215 }
00216 
00217 
00218 #ifdef OTOS_PROFILING
00219 
00228 uint8_t otosGetIdleRate(void)
00229 {
00230     return 100 * g_idleTotalTicks / otosGetTicks();
00231 }
00232 
00233 #endif
00234 
00235 
00236 
00257 OtosTask* otosCreateMainTask(uint8_t prio, uint16_t stacksize)
00258 {
00259     OtosTask*   pTask;
00260     uint8_t*    pSpNew;
00261     uint16_t    spOld;
00262     uint8_t*    pMem;
00263 
00264 
00265     /* allocate memory for stack */
00266     pMem = otosAllocate(stacksize);
00267     if (pMem == NULL)                   /* fatal error, stop system */
00268         otosError_P(msgErrOutOfMem);
00269 
00270     /* allocate memory for task structure */
00271     pTask = otosAllocate(sizeof (OtosTask));
00272     if (pTask == NULL)                  /* fatal error, stop system */
00273         otosError_P(msgErrOutOfMem);
00274 
00275 #ifdef OTOS_DEBUG_STACK
00276     /* fill stack with magic pattern */
00277     memset(pMem, 0x42, stacksize);
00278 
00279     /* store stacksize */
00280     pTask->stacksize = stacksize;
00281 #endif
00282 
00283 #ifdef OTOS_STACK_CHECK
00284     /* mark stack begin to be able to detect if it overflows */
00285     pTask->pStack = (uint16_t*) pMem;
00286     *(pTask->pStack) = STACK_CHECK;
00287 #endif
00288 
00289     /* get current stack pointer */
00290     spOld = GET_SP();
00291 
00292     /* new stack pointer points to last word in stack minus offset of current stack pointer */
00293     pSpNew = pMem + stacksize - 1  - RAMEND + spOld;
00294 
00295     cli();
00296 
00297     /*
00298      * now copy stack to new location
00299      * source:  spOld + 1
00300      * dest:    pSpNew + 1
00301      * length:  RAMEND - spOld
00302      */
00303     memcpy(pSpNew + 1, (uint8_t*) spOld + 1, RAMEND - spOld);
00304 
00305     /* move stack pointer to new stack */
00306     __asm__ volatile(
00307         "out %1, %A0"   "\n\t"
00308         "out %2, %B0"   "\n\t"
00309         :
00310         : "r" (pSpNew), "I" (_SFR_IO_ADDR(SPL)), "I" (_SFR_IO_ADDR(SPH))
00311     );
00312 
00313 
00314     /* set task control block */
00315     pTask->state = TASK_RUNNING;
00316     pTask->events = 0;
00317     pTask->waitEvents = 0;
00318     pTask->pWaitMsgQueue = NULL;
00319     pTask->pWaitMutex = NULL;
00320     pTask->sleepTicks = 0;
00321     pTask->priority = prio;
00322     pTask->name = NAME_MAIN;
00323 
00324     /* set global variables */
00325     g_pRunningTask = pTask;                          /* pointer to the current task */
00326 //    ++g_NumberOfTasks;
00327 
00328     sei();
00329 
00330     return pTask;
00331 }
00332 
00333 
00344 void otosDispatch(OtosTask* pOldTask, OtosTask* pNewTask)
00345 {
00346 
00347     /* save context of the old (running) task */
00348     TASK_CONTEXT_SAVE(pOldTask);
00349 
00350     /* restore context of the new task */
00351     TASK_CONTEXT_RESTORE(pNewTask);
00352 
00353     /* enable interrupts again */
00354     sei();
00355 }
00356 
00357 
00368 void otosScheduler(uint8_t operation)
00369 {
00370     OtosTask* pTmp;
00371 #ifdef OTOS_STACK_CHECK
00372     static uint8_t   errorMsg[] = "otOS ERR: task x stack overflow";
00373 #endif
00374 
00375 
00376     cli();
00377 
00378     g_performDispatch = FALSE;                  /* clear flag */
00379 
00380 #ifdef OTOS_STACK_CHECK
00381     /* test if stack overflowed */
00382     if (*(g_pRunningTask->pStack) != STACK_CHECK)
00383     {
00384         errorMsg[15] = g_pRunningTask->name;    /* replace the x with the task's name */
00385         otosError(errorMsg);
00386     }
00387 #endif
00388 
00389     if (operation == SCHED_NORM)                /* running task is moved to ready queue */
00390     {
00391         /* if ready queue is empty or if there is no task in ready queue
00392            with higher or equal prio than running tasks prio, return */
00393         if ((g_pReadyQueue == NULL) || (g_pReadyQueue->priority < g_pRunningTask->priority))
00394         {
00395             sei();
00396             return;                             /* no task switch */
00397         }
00398     }
00399 
00400 
00401     /* remember running task */
00402     pTmp = g_pRunningTask;
00403 
00404     /* select new running task and delete it from ready queue */
00405     g_pRunningTask = g_pReadyQueue;
00406     g_pReadyQueue = g_pReadyQueue->pNext;
00407     g_pRunningTask->state = TASK_RUNNING;
00408 
00409     if (operation == SCHED_NORM)                /* running task is moved to ready queue */
00410         /* insert old runnning task into ready queue */
00411         otosInsertTaskInQueue(&g_pReadyQueue, pTmp, TASK_READY);
00412 
00413     else /* if (operation == SCHED_BLOCK) */    /* running task is moved to blocked queue */
00414         /* insert old runnning task into blocked queue */
00415         otosInsertTaskInQueue(&g_pBlockedQueue, pTmp, TASK_BLOCKED);
00416 
00417 /* profiling */
00418 #ifdef OTOS_PROFILING
00419     if (!g_idleActive)                          /* idle task not running at the moment */
00420     {
00421         if (g_pRunningTask->priority == 0)      /* switch to idle task (only task with prio 0) */
00422         {
00423             /* remember idle task entering time */
00424             g_idleEnterTicks = otosGetTicks();
00425             g_idleActive = TRUE;
00426         }
00427     }
00428     else                                        /* idle task is currently running */
00429     {
00430 /*        if (g_pRunningTask->priority != 0) */ /* switch to other task */
00431         {
00432             /* remember idle task leaving time */
00433             g_idleActive = FALSE;
00434             g_idleTotalTicks += otosGetTickDiff(g_idleEnterTicks);
00435         }
00436     }
00437 #endif  /* OTOS_PROFILING */
00438 
00439     /* switch to new task */
00440     otosDispatch(pTmp, g_pRunningTask);
00441 }
00442 
00443 
00456 void otosWakeup(OtosTask* pTask)
00457 {
00458     OtosTask* pTmp;
00459     OtosTask* volatile* pTmpPrev;
00460 
00461 
00462     /* point to first entry in blocked queue */
00463     pTmp = g_pBlockedQueue;             /* points to first OtosTask in queue */
00464     pTmpPrev = &g_pBlockedQueue;        /* points to first OtosTask* */
00465 
00466     /* find task in queue */
00467     while ((pTmp != pTask) && (pTmp != NULL))
00468     {
00469         pTmpPrev = &pTmp->pNext;
00470         pTmp = pTmp->pNext;
00471     }
00472 
00473     if (pTmp == NULL)                   /* fatal error, stop system */
00474         otosError_P(PSTR("otOS ERR: task not found"));
00475 
00476     /* now remove task from blocked queue*/
00477     *pTmpPrev = pTask->pNext;
00478 
00479     /* insert task in ready queue */
00480     otosInsertTaskInQueue(&g_pReadyQueue, pTask, TASK_READY);
00481 }
00482 
00499 void otosInsertTaskInQueue(OtosTask* volatile* pQueue, OtosTask* pTask, uint8_t state)
00500 {
00501     OtosTask* pTmp;
00502     OtosTask* volatile* pTmpPrev;
00503 
00504 
00505     /* point to first entry */
00506     pTmp = *pQueue;                 /* points to first OtosTask in pQueue */
00507     pTmpPrev = pQueue;              /* points to first OtosTask* */
00508 
00509     /* find first task with lower prio than task */
00510     while ((pTmp != NULL) && (pTmp->priority >= pTask->priority))
00511     {
00512         pTmpPrev = &pTmp->pNext;
00513         pTmp = pTmp->pNext;
00514     }
00515 
00516     /* insert task between pTmpPrev and pTmp */
00517     pTask->pNext = pTmp;
00518     *pTmpPrev = pTask;
00519 
00520     pTask->state = state;
00521 }
00522 
00533 uint8_t otosSetPreemtive(uint8_t timeSlice)
00534 {
00535     uint8_t oldSetting;
00536     uint8_t iStat;    
00537 
00538 
00539     oldSetting = g_timeSliceTicks;
00540 
00541     iStat = SREG;           /* save cpu status register         */
00542     cli();
00543     g_timeSliceCounter = timeSlice;
00544     g_timeSliceTicks = timeSlice;
00545     SREG = iStat;           /* restore status register (i-bit)  */
00546     
00547     return oldSetting;
00548 }
00549 
00560 uint8_t otosGetPreemtive(void)
00561 {   
00562     return g_timeSliceTicks;
00563 }
00564 
00575 OTOS_TASK_FUNCTION(idle, pArg)
00576 {
00577     while (1)
00578     {
00579         otosScheduler(SCHED_NORM);
00580     }
00581 }
00582 
00583 
00594 int main(void)
00595 {
00596     /* basic hardware init needed for otOS */
00597     otosInitHardware();
00598 
00599 #ifdef OTOS_BANKING
00600     /* set ram bank 0 */
00601     otosSetRamBank(0);
00602 #endif
00603 
00604     /* force linker to include time.c (we need the timer ISR) */
00605     otosGetTicks();
00606 
00607     /* make the calling function the main task */
00608     otosCreateMainTask(PRIO_MAIN, STACK_MAIN);
00609 
00610     /* create idle task */
00611     otosCreateTask(idle, NULL, PRIO_IDLE, STACK_IDLE, NAME_IDLE);
00612 
00613 
00614     otosMain();                     /* user entry point */
00615     while (1);                      /* never ever return */
00616 }
00617 
00621 #ifdef OTOS_DEBUG_TASK_QUEUES
00622 #include "dev/uart.h"
00623 
00624 void otos_dump_queues(void)
00625 {
00626     OtosTask* pTask;
00627 
00628 
00629     cli();
00630 
00631     otosPrint_P(PSTR("\nRunning Task:  "));
00632     otosPutchar(g_pRunningTask->name);
00633     otosPutchar('\n');
00634 
00635     otosPrint_P(PSTR("Ready Queue:   "));
00636     pTask = g_pReadyQueue;
00637     while (pTask != NULL)
00638     {
00639         otosPutchar(pTask->name);
00640         otosPutchar(' ');
00641         pTask = pTask->pNext;
00642     }
00643     otosPutchar('\n');
00644 
00645     otosPrint_P(PSTR("Blocked Queue: "));
00646     pTask = g_pBlockedQueue;
00647     while (pTask != NULL)
00648     {
00649         otosPutchar(pTask->name);
00650         otosPutchar(' ');
00651         pTask = pTask->pNext;
00652     }
00653     otosPutchar('\n');
00654 
00655     sei();
00656 
00657 
00658 }
00659 #endif
00660 
00661 
00662 #ifdef OTOS_DEBUG_STACK
00663 #include "dev/uart.h"
00664 
00665 static unsigned char convertBuff[9];
00666 
00667 unsigned char hexTab[] PROGMEM = "0123456789abcdef";
00668 
00669 
00671 // ByteToHex(): convert an unsigned char to a hex string
00673 unsigned char* byteToHex(unsigned char ucValue)
00674 {
00675     convertBuff[0] = PRG_RDB(&hexTab[ucValue >> 4]);
00676     convertBuff[1] = PRG_RDB(&hexTab[ucValue & 0x0f]);
00677     convertBuff[2] = '\0';
00678 
00679     return convertBuff;
00680 }
00681 
00683 // ShortToHex(): convert an unsigned short to a hex string
00685 unsigned char* shortToHex(unsigned short usiValue)
00686 {
00687     convertBuff[0] = PRG_RDB(&hexTab[usiValue >> 12]);
00688     convertBuff[1] = PRG_RDB(&hexTab[(usiValue >> 8) & 0x0f]);
00689     convertBuff[2] = PRG_RDB(&hexTab[(usiValue >> 4) & 0x0f]);
00690     convertBuff[3] = PRG_RDB(&hexTab[usiValue & 0x0f]);
00691     convertBuff[4] = '\0';
00692 
00693     return convertBuff;
00694 }
00695 
00699 void otos_dump_task_stack(OtosTask* pTask)
00700 {
00701     uint8_t*  addr;
00702     uint16_t  i;
00703 
00704 
00705     addr = (uint8_t*) pTask->pStack;
00706 
00707     for (i = 0; i < pTask->stacksize; ++i)
00708     {
00709         otosPrint(shortToHex(pTask->stacksize - 1 - i));
00710         otosPrint(": ");
00711         otosPrint(byteToHex(*addr));
00712         otosPrint("\n");
00713         ++addr;
00714     }
00715     otosPrint("\n");
00716 }
00717 
00718 void otos_dump_stack(void)
00719 {
00720     OtosTask* pTask;
00721 
00722 
00723     cli();
00724 
00725     otosPrint_P(PSTR("\nRunning Task:\n"));
00726     otosPutchar(g_pRunningTask->name);
00727     otosPutchar('\n');
00728     otos_dump_task_stack(g_pRunningTask);
00729 
00730     otosPrint_P(PSTR("Ready Queue:\n"));
00731     pTask = g_pReadyQueue;
00732     while (pTask != NULL)
00733     {
00734         otosPutchar(pTask->name);
00735         otosPutchar('\n');
00736         otos_dump_task_stack(pTask);
00737         pTask = pTask->pNext;
00738     }
00739     otosPutchar('\n');
00740 
00741     otosPrint_P(PSTR("Blocked Queue:\n"));
00742     pTask = g_pBlockedQueue;
00743     while (pTask != NULL)
00744     {
00745         otosPutchar(pTask->name);
00746         otosPutchar('\n');
00747         otos_dump_task_stack(pTask);
00748         pTask = pTask->pNext;
00749     }
00750     otosPutchar('\n');
00751 
00752     sei();
00753 
00754 
00755 }
00756 
00757 
00758 #endif
00759 

Generated on Sat Jan 25 18:41:43 2003 for otOS by doxygen1.3-rc2