00001 /* 00002 * Femto OS v 0.91 - Copyright (C) 2008-2009 Ruud Vlaming 00003 * 00004 * This file is part of the Femto OS distribution. 00005 * 00006 * This program is free software: you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation, version 3 of the License. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * Please note that, due to the GPLv3 license, for application of this 00019 * work and/or combined work in embedded systems special obligations apply. 00020 * If these are not to you liking, please know the Femto OS is dual 00021 * licensed. A commercial license and support are available. 00022 * See http://www.femtoos.org/ for details. 00023 */ 00024 00025 #include "femtoos_core.h" 00026 #include "femtoos_shared.h" 00027 00028 00029 /* ========================================================================= */ 00030 /* FEMTO OS INTERNAL FUNCTIONS ============================================ */ 00031 /* ========================================================================= */ 00032 00033 /* 00034 * Body definitions. All switching methods are called via an assembler 00035 * redirect, making it possible to switch stack, save context and preserve 00036 * all registers (including the parameters) while doing so. Without this 00037 * trick gcc may push the parameter registers or construct a frame pointer 00038 * first thereby destroying other registers. Notice functions never return, 00039 * but are not 'noreturn'. They are naked or bikini. 00040 */ 00041 00049 #if (includeTaskDelayFromNow == cfgTrue) 00050 static void privDelayFromNowBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00051 #endif 00052 00062 #if (includeTaskDelayFromWake == cfgTrue) 00063 static void privDelayFromWakeBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00064 #endif 00065 00071 #if (includeTaskRecreate == cfgTrue) 00072 static void privRecreateBody(Tuint08 uiTaskNumber) __attribute__((used)) defSysReduceProEpilogue; 00073 #endif 00074 00080 #if (includeTaskRestart == cfgTrue) 00081 static void privRestartBody(Tuint08 uiRestartMode, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00082 #endif 00083 00089 #if (includeTaskYield == cfgTrue) 00090 static void privYieldBody(void) __attribute__((used)) defSysReduceProEpilogue; 00091 #endif 00092 00098 #if (includeTaskTerminate == cfgTrue) 00099 static void privTerminateBody(Tuint08 uiTaskNumber) __attribute__((used)) defSysReduceProEpilogue; 00100 #endif 00101 00107 #if (includeTaskSuspend == cfgTrue) 00108 static void privSuspendBody(Tuint08 uiSuspendMode) __attribute__((used)) defSysReduceProEpilogue; 00109 #endif 00110 00116 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleep == cfgTrue) 00117 static void privSleepBody(void) __attribute__((used)) defSysReduceProEpilogue; 00118 #endif 00119 00125 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue) 00126 static void privSleepAllBody(void) __attribute__((used)) defSysReduceProEpilogue; 00127 #endif 00128 00134 #if (cfgUseSynchronization != cfgSyncNon) && (includeTaskWaitForTasks == cfgTrue) 00135 #if (cfgUseTimeout == cfgTrue) 00136 static void privWaitForTasksBody(Tuint08 uiSlot, Tuint08 uiNumberOfTasks, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00137 #else 00138 static void privWaitForTasksBody(Tuint08 uiSlot, Tuint08 uiNumberOfTasks) __attribute__((used)) defSysReduceProEpilogue; 00139 #endif 00140 #endif 00141 00147 #if (cfgUseSynchronization != cfgSyncNon) && ( (includeTaskQueu == cfgTrue) || (includeTaskMutex == cfgTrue) ) 00148 #if (cfgUseTimeout == cfgTrue) 00149 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 00150 static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeLeftFilling, Tsint08 siFreeRightFilling, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00151 #else 00152 static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeRightFilling, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00153 #endif 00154 #else 00155 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 00156 static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeLeftFilling, Tsint08 siFreeRightFilling) __attribute__((used)) defSysReduceProEpilogue; 00157 #else 00158 static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeRightFilling) __attribute__((used)) defSysReduceProEpilogue; 00159 #endif 00160 #endif 00161 #endif 00162 00168 #if (cfgUseSynchronization != cfgSyncNon) && ( (includeTaskQueu == cfgTrue) || (includeTaskMutex == cfgTrue) ) 00169 static void privSyncReleaseBody(Tuint08 uiSlotSlot) __attribute__((used)) defSysReduceProEpilogue; 00170 #endif 00171 00177 #if (cfgUseFileSystem == cfgTrue) && (includeTaskFileAccess == cfgTrue) 00178 #if (cfgUseTimeout == cfgTrue) 00179 #if (cfgUseFileSystemConcurrentRead == cfgTrue) 00180 static void privFileOpenBody(Tbool bReadOnly, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00181 #else 00182 static void privFileOpenBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00183 #endif 00184 #else 00185 #if (cfgUseFileSystemConcurrentRead == cfgTrue) 00186 static void privFileOpenBody(Tbool bReadOnly) __attribute__((used)) defSysReduceProEpilogue; 00187 #else 00188 static void privFileOpenBody(void) __attribute__((used)) defSysReduceProEpilogue; 00189 #endif 00190 #endif 00191 #endif 00192 00198 #if (cfgUseFileSystem == cfgTrue) && (includeTaskFileAccess == cfgTrue) 00199 static void privFileCloseBody(void) __attribute__((used)) defSysReduceProEpilogue; 00200 #endif 00201 00207 #if (cfgUseFileSystem == cfgTrue) 00208 static void privWaitForFsAccessBody(void) __attribute__((used)) defSysReduceProEpilogue; 00209 #endif 00210 00216 #if (cfgUseEvents == cfgTrue) && (includeTaskWaitForEvents == cfgTrue) 00217 #if (cfgUseTimeout == cfgTrue) 00218 static void privWaitForEventBody(Tuint08 uiEvent, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue; 00219 #else 00220 static void privWaitForEventBody(Tuint08 uiEvent) __attribute__((used)) defSysReduceProEpilogue; 00221 #endif 00222 #endif 00223 00231 #if (defCheckReportingError == cfgTrue) 00232 #if (cfgCheckAlwaysFatal == cfgTrue) 00233 static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo) __attribute__ (( noreturn )); 00234 #else 00235 static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo); 00236 #endif 00237 #endif 00238 00245 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 00246 static void privCheckOsStackLevel(void); 00247 #endif 00248 00255 #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse) 00256 static void privCheckOsStackRegion(void); 00257 #endif 00258 00264 #ifdef portInitContext 00265 static Taddress privInitContext(Taddress pTaskStart, Taddress pStackTop, Tuint08 uiRegisterCount, Tuint08 uiInterruptStart); 00266 #endif 00267 00276 static TtaskControlBlock * privTcbList(Tuint08 uiTaskNumber) __attribute__ ((pure, noinline)); 00277 00284 static Tuint08 privTaskNumber(Tuint08 uiTaskNumber) __attribute__ ((pure, noinline, unused)); 00285 00293 #if (cfgCheckTaskStack == cfgTrue) && (StackSafety > 0) 00294 static void privTestStackRegion(void) __attribute__ (( noinline)); 00295 #endif 00296 00305 #if (cfgUseLowPowerSleep == cfgTrue) 00306 static void privWakeupFromLowPower(void) __attribute__ ((noinline)); 00307 #endif 00308 00319 #if (defRegisterUseConstant == cfgFalse) 00320 static Tuint08 privRegisterCount(Tuint08 uiRegisterUse) __attribute__ ((const)); 00321 #endif 00322 00323 /*DISCUSSION 00324 * Some methods use quite a bit of stack, i.e. they start by saving 00325 * say 10 registers on the context. This implies not only 10 more bytes 00326 * of ram use, but also 40 extra flash bytes just to store and retrieve 00327 * the bytes. This must be weighed against inlining those methods. 00328 * Less stack, more flash, but maybe not that much more. It really depends 00329 * which resource is limited. 00330 * Also that discussion cannot be made per function, since you cannot simply 00331 * add the stack use. Functions could be inlined if needed by adding 00332 * __attribute__ ( ( always_inline ) ); 00333 * to their definitions. 00334 */ 00335 00344 static void privTaskInit(Tuint08 uiTaskNumber, Tuint08 uiInitControl); 00345 00354 static void privEnterOS(Tuint08 uiAction) defSysReduceProEpilogue; 00355 00363 static void privIncrementTick(void); 00364 00371 static Tuint08 privSwitchContext(void); 00372 00380 static Tselect privSelectTask(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd) __attribute__ (( always_inline )); 00381 00388 #if (cfgUseHierarchicalRoundRobin == cfgTrue) 00389 static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiPriority, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) __attribute__ (( always_inline )); 00390 #else 00391 static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) __attribute__ (( always_inline )); 00392 #endif 00393 00401 #if (cfgUseLowPowerSleep == cfgTrue) 00402 static void privEnterSleep(Tuint08 uiTickMinDelay); 00403 #endif 00404 00412 static void privEnterIdle(void) defSysReduceProEpilogue; 00413 00421 static void privEnterTask(void) defSysReduceProEpilogue; 00422 00429 #if (cfgUseLoadMonitor == cfgTrue) 00430 static void privCopyLoad(void); 00431 #endif 00432 00439 #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue) 00440 static void privTraceWatermarks(void); 00441 #endif 00442 00449 #if (defUseDelay == cfgTrue) 00450 static void privWakeupFromDelay(Tuint08 uiTaskNumber, TtaskControlBlock * taskTCB); 00451 #endif 00452 00459 #if (defUseDelay == cfgTrue) 00460 #if (includeTaskDelayFromWake == cfgTrue) 00461 #define privDelayCalcFromNow(DT) privDelayCalc(DT,true) 00462 #define privDelayCalcFromWake(DT) privDelayCalc(DT,false) 00463 static void privDelayCalc(Tuint16 uiDelayTime, Tbool bFromNow) defConditionalInline; 00464 #else 00465 static void privDelayCalcFromNow(Tuint16 delayTime) defConditionalInline; 00466 #endif 00467 #endif 00468 00474 #if (cfgUseSynchronization != cfgSyncNon) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue )) 00475 static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB); 00476 #endif 00477 00484 #if (cfgUseSynchronization != cfgSyncNon) 00485 static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot); 00486 #endif 00487 00493 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 00494 static Tuint08 privGetQueuSize(Tuint08 uiQueuNumber) __attribute__ ((always_inline, const)); 00495 #endif 00496 00497 00507 #if (cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue) 00508 static void privUnblockTask(Tuint08 uiControlTaskNumber); 00509 #endif 00510 00518 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue) 00519 static void privLiftLocksOnSlot(Tuint08 uiSlot); 00520 #endif 00521 00529 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue) 00530 static void privRestoreInitialPriority(Tuint08 uiTaskNumber); 00531 #endif 00532 00540 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 00541 static Tuint08 privQueuTest(Tuint08 uiSlot, Tsint08 siFreeFilling); 00542 #endif 00543 00552 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue)) 00553 static void privReleaseSyncBlockingTasks(void) defConditionalInline; 00554 #endif 00555 00564 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue)) 00565 static Tuint08 privFreeLockAbsent(Tuint08 uiSlot); 00566 #endif 00567 00576 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 00577 static Tbool privSizeFitsQueu(Tuint08 uiSlot, Tsint08 siFreeFilling); 00578 #endif 00579 00587 #if (cfgUseFileSystem == cfgTrue) 00588 static void privReleaseFileBlocks(void); 00589 #endif 00590 00598 #if (cfgUseFileSystem == cfgTrue) 00599 static Taddress privFileLocation(Tuint08 uiFileNumber, Tuint08 uiOffset); 00600 #endif 00601 00608 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue) 00609 static void privPutAllTasksToSleep(void); 00610 #endif 00611 00620 #if (cfgUseFileSystem == cfgTrue) 00621 static void privPrepareFileClose(Tuint08 uiTaskNumber); 00622 #endif 00623 00632 #if (cfgUseFileSystem == cfgTrue) && (cfgCheckMethodUse == cfgTrue) 00633 static void privCheckFileSpecsWriting(Tuint08 uiFileNumber, Tuint08 uiOffset, Tuint08 uiSize, Tuint08 uiCallId); 00634 #endif 00635 00644 #if (cfgUseFileSystem == cfgTrue) && (cfgCheckMethodUse == cfgTrue) 00645 static void privCheckFileSpecsReading(Tuint08 uiFileNumber, Tuint08 uiOffset, Tuint08 uiSize, Tuint08 uiCallId); 00646 #endif 00647 00654 #if (cfgUseFileSystem == cfgTrue) && ( ((cfgUseFileSystemMaintainFAT == cfgTrue) && (includeTaskFileAppendByte == cfgTrue)) || (cfgCheckMethodUse == cfgTrue) ) 00655 static Tuint08 privFileSpace(Tuint08 uiFileNumber); 00656 #endif 00657 00664 #if (cfgCheckMethodUse == cfgTrue) 00665 #if (defCapabilitiesFull == cfgFalse) 00666 static void privCheckCapabilities(Tuint08 uiCallId, Tuint08 uiTaskCaps) __attribute__ ((unused)); 00667 #else 00668 #define privCheckCapabilities(X,Y) 00669 #endif 00670 #endif 00671 00672 00673 /* ========================================================================= */ 00674 /* FEMTO OS CORE IMPLEMENTATION ============================================ */ 00675 /* ========================================================================= */ 00676 00677 00678 #if (defCheckReportingError == cfgTrue) 00679 00680 static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo) 00681 { /* We may arrive here from OS space, isr or task space. In the latter case is important we do 00682 * not get tick interrupts (only possible when coming from a task), since reporting an error 00683 * is usually a critical situation. But since we want the user to see the error it is best 00684 * we stop all interrupts. 00685 * Note that for tasks this method should not return to the point of calling but invoke a 00686 * context switch instead. When coming from an isr, we cannot stop that particular isr so 00687 * we should treat that situation as fatal. Only when coming here from the OS itself, we 00688 * may return if that was specifically asked for, or we must switch to a new task (usually 00689 * can a switching method was called from the task). Only when the possibility exists we 00690 * return to the point of origin we need to keep the stack. 00691 * The only question remains, how do we decide where the error occurred. On course we 00692 * can ask the uiOsStatus. If we come from genXXX that is the only way to find out. But, 00693 * if we come for example from privInitOS, this information has not yet been updated, 00694 * or if we come from a switching call, we are not really inside OS space, but we are 00695 * handling a call from the task in OS space. Therefore we have one bit controlling 00696 * if we should use the uiOsState "as-is" or if we should use the Os State forced. 00697 * 00698 * Then, with the resulting state being (idle, isr, task, os) 00699 * idle => issue an internal error, this should not happen. TODO: not implemented yet. 00700 * isr => make error fatal, renew stack, never return 00701 * => we should not arrive here from a sleeping state. 00702 * task => if fatal error, renew stack, never return 00703 * => if non-fatal error, renew stack, terminate task, issue a switch-task 00704 * => for shared tasks, reset the share state 00705 * os => if fatal error, renew stack, never return 00706 * => if non-fatal error return to place from which the call originated. 00707 * Of course, when cfgCheckAlwaysFatal is activated the error is ... always fatal, 00708 * so we can ignore this hocus-pocus. 00709 * A switch task is a context switch without the without the context-save and os 00710 * initialization operations. 00711 * 00712 * One extra note. Errors reporting an incorrect task number must be fatal since this 00713 * routine assumes that, when the error is not fatal, the task number represents the 00714 * failing task. 00715 */ 00716 privDisableGlobalInterrupts(); 00717 /* Strip the error type information from the message */ 00718 Tuint08 uiBareMessage = (uiMessage & errMessageGetMask); 00719 /* Errors are traced first, to be sure that tracing is done in case the portError holds the system for ever. */ 00720 privTrace(traceErrorBase | uiBareMessage); 00721 /* First check if we have fatal errors per default */ 00722 #if (cfgCheckAlwaysFatal == cfgTrue) 00723 /* If so, the matter is simple. All errors are fatal and we never return. */ 00724 const Tbool bFatal = true; 00725 const Tbool bReturn = false; 00726 #else 00727 /* uiWorkStatus represents the status we will use upon which we decide what to do. */ 00728 Tuint08 uiWorkStatus; 00729 /* See if we must use the uiOsStatus as a basis (standard) or the given by the uiCallId. */ 00730 if ((uiMessage & errOsStateGetMask) == errOsStateAsIs) 00731 { /* Extract the upper two bits containing the status info from the standard status. */ 00732 uiWorkStatus = uiOsStatus & defContextGetMask; } 00733 else 00734 { /* Use the StateOs as forced status (i.e. assume we are in the OS state) */ 00735 uiWorkStatus = defContextStateOs; } 00736 /* We first calculate when we have a fatal error. That is the case is the error in itself is 00737 * fatal, or if we arrive here from an isr. */ 00738 const Tbool bFatal = (uiBareMessage >= errFatalError) || (uiWorkStatus == defContextStateIsr); 00739 /* We may normally return only in one special case. Of course the error may not be fatal, and we must come 00740 * here from the OS itself, (or we must be told we come from the OS itself) */ 00741 const Tbool bReturn = !bFatal && (uiWorkStatus == defContextStateOs); 00742 #endif 00743 /* Strip the error type information from the call Id */ 00744 Tuint08 uiBareCallId = (uiCallId & errCallIdGetMask); 00745 /* If we do not return we reset the stack to prevent stack overflow when handling the error code. 00746 * We don't need the preserve stack anymore. Note, that we should note have a stack frame present. 00747 * Pushed variables are not important when we no not return. */ 00748 if (!bReturn) { privSetStack(&xOS.StackOS[OSstackInit]); } 00749 /* We disable tick interrupts, needed to be deactivated in case we should return. We do that here, after a possible 00750 * stack transplant since this call may make use of the stack. It must be done before global interrupts are 00751 * activated again. */ 00752 privDisableTickInterrupts(); 00753 /* Now determine the task number. */ 00754 Tuint08 uiTaskNumber; 00755 /* Test if we must use the given task number by uiInfo or the current one. This flag is used because 00756 * from a lot of places its much sorter to set the flag than to calculate the current task number. 00757 * Do not use the privTaskNumber() method, for we do not want to introduce stack use here. */ 00758 if ((uiMessage & errTaskStateGetMask) == errTaskStateCurrent) 00759 { /* Deduce the current task number from the Status */ 00760 uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift; 00761 /* Reconstruct the uiInfo byte, that will be send to the error method later on */ 00762 uiInfo = (uiInfo & errTaskNumberSetMask) | (uiTaskNumber << errTaskNumberShift); } 00763 else 00764 { /* Read the task number from the given byte, probably the current task is not the one giving problems */ 00765 uiTaskNumber = (uiInfo & errTaskNumberGetMask) >> errTaskNumberShift ; } 00766 /* Now, show the error, if we had a fatal error it makes no sense to continue operations, 00767 * so the only thing we can do is show the error again*/ 00768 do { portShowError(uiBareMessage,uiBareCallId,uiInfo); } while (bFatal); 00769 /* Now, if we return, we must be sure we have enough OS stack space. Since error handling can be 00770 * stack hungry, let us check that here. This may invoke an other error. But since we know 00771 * that, if it occurs, it will be fatal, there cannot be a loop. */ 00772 #if (cfgCheckOsStack == cfgTrue) 00773 privCheckOsStackLevel(); 00774 #endif 00775 /* In case we had a normal error we want to continue, but since it is reasonable to assume that it 00776 * may have taken some time, the user has probably stop the timer, if he did not misused the timer 00777 * to make some leds blink. In any case its best to restart the timer. We have to live with the 00778 * fact that real time and timer ticks are out of sync anyway. Note that the user may use the timer, 00779 * but may not activate the interrupt. */ 00780 portSetupTimerInterrupt(); 00781 /* We return from this call with a setup timer, and with tick interrupts disabled. The global interrupts 00782 * are still disabled. If we have no os protection we must re-enable global interrupts again. */ 00783 #if (cfgIntOsProtected == cfgFalse) 00784 privEnableGlobalInterrupts(); 00785 #endif 00786 /* All errors not being fatal at least must stop the current or requested task and put it in error mode. 00787 * (This cannot be an isr anymore, and all non fatal Os errors are about some task.) */ 00788 TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber); 00789 /* Note that the task is terminated, but it's locks are not removed, since this is an error 00790 * condition, it is unclear what the result would be, so that this cannot be compared with 00791 * a normal terminate, in which case the user can take precautions for released blocks etc. */ 00792 taskTCB->uiTaskStatus = defBaseTerminatedTask; 00793 /* In case we may have a shared task, we must check if this was one and thus if we must 00794 * reset the ShareStatus. */ 00795 #if (defUseSharedStack == cfgTrue) 00796 /* In case all tasks are shared, we know that we must reset. */ 00797 #if (defAllSharedStack == cfgFalse) 00798 /* If not, we need the initial status of the task */ 00799 #if (defInitialStatusConstant == cfgTrue) 00800 /* if we are lucky that is a constant. */ 00801 Tuint08 uiInitialStatus = defInitialStatusFixed; 00802 #else 00803 /* if not, that must be read from flash. */ 00804 Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus); 00805 #endif 00806 /* Test if this task is a shared task. */ 00807 if ((uiInitialStatus & defInitialSharedGetMask) == defInitialSharePresent) 00808 #endif 00809 { /* If so, we must identify this shared task as shared. */ 00810 taskTCB->pcStackLevel = defStackEmpty; 00811 /* and then we know, since is was running, we must reset the share state. */ 00812 uiOsStatus = ((uiOsStatus & defShareStateSetMask) | defShareStateAbsent); } 00813 #endif 00814 /* If we are at a task, or at some operations within the OS that handle a specific task, 00815 * we can leave this method by asking for a task switch. This is not a normal return. 00816 * Note that in this case the AuxRegBit (if used) can be set. Here, that is not a problem 00817 * for there are no switching interrupts that can take place. */ 00818 if (!bReturn) { privEnterOS(defActionTaskStateSwitch); } 00819 /* Otherwise we are done, and return to the OS operations that generated the error. */ } 00820 00821 #endif 00822 00823 00824 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 00825 00826 static void privCheckOsStackLevel(void) 00827 { /* We are going to check if we overflowed the Os Stack. We should only arrive here if we are 00828 * in OS space, i.e. if we are sure the stack pointer actually should be inside the OS stack. */ 00829 Taddress pStack; 00830 Tuint16 uiOsStackSize; 00831 /* Obtain the current stack pointer. */ 00832 privGetStack(pStack); 00833 /* Calculate the Stacksize, we have to distinguish between the different stack types. Note we 00834 * assumed the stack pointer points to the byte that will be written next, i.e. the stack pointer 00835 * is post decrement. */ 00836 #if (cfgSysStackGrowthUp == cfgTrue) 00837 uiOsStackSize = ((Tuint16) pStack - (Tuint16) &xOS.StackOS[0]); 00838 #else 00839 uiOsStackSize = ((Tuint16) &xOS.StackOS[(StackSizeOS)-1] - (Tuint16) pStack); 00840 #endif 00841 /* Now, we do not incorporate the StackSafety parameter into our calculation. That parameter is 00842 * used for task stacks only. And, besides that, the call to this function, i.e.. privCheckOsStack 00843 * costs two bytes extra anyway */ 00844 #if (cfgCheckWatermarks == cfgTrue) 00845 if (uiOsStackMax<uiOsStackSize) uiOsStackMax=uiOsStackSize; 00846 #endif 00847 #if (cfgCheckOsStack == cfgTrue) 00848 if (uiOsStackSize>(StackSizeOS)) { privShowError((fatOsStackOverflowed | errTaskStateNon), callIdSystem ,errNoInfo); } 00849 #endif 00850 } 00851 00852 #endif 00853 00854 00855 #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse) 00856 00857 static void privCheckOsStackRegion(void) 00858 { /* We are going to check which part of the stack space is used. Note, this routine is not capable 00859 * of detecting a stack overflow. In that case, it will simple see that the whole of the space 00860 * has been used. */ 00861 Taddress pStack; 00862 /* Start with the largest detectable stack size. */ 00863 Tuint08 uiOsStackSize = StackSizeOS; 00864 /* If the whole stack already has been used, there is nothing to check */ 00865 if (uiOsStackMax < uiOsStackSize) 00866 { /* Calculate the Stack size, we have to distinguish between the different stack types. */ 00867 #if (cfgSysStackGrowthUp == cfgTrue) 00868 /* Find the beginning of the stack. */ 00869 pStack = &xOS.StackOS[(StackSizeOS)-1]; 00870 /* loop downwards until we find the first used byte, or until we reach a previous boundary whatever comes first. */ 00871 while ((*(pStack--) == defStackInitByte) && ((uiOsStackSize--)>uiOsStackMax)); 00872 #else 00873 /* Find the beginning of the stack. */ 00874 pStack = &xOS.StackOS[0]; 00875 /* loop upwards until we find the first used byte, or until we reach a previous boundary whatever comes first. */ 00876 while ((*(pStack++) == defStackInitByte) && ((uiOsStackSize--)>uiOsStackMax)); 00877 #endif 00878 /* Load the new boundary. */ 00879 uiOsStackMax=uiOsStackSize; } } 00880 00881 #endif 00882 00883 00884 00885 #if (cfgCheckMethodUse == cfgTrue) && (defCapabilitiesFull == cfgFalse) 00886 00887 static void privCheckCapabilities(Tuint08 uiCallId, Tuint08 uiTaskCaps) 00888 { /* Use this function to see if the task is capable of the required functions. 00889 * The function is not needed if all tasks have full capabilities, for there is nothing 00890 * to check in that case. Note this function can only check the capabilities of the 00891 * current function. If we happen to be in isr space (genXXXX functions) the current 00892 * function has no meaning, and the check is omitted. */ 00893 Tuint08 uiTaskNumber; 00894 /* The current task has no meaning inside an isr, so there is nothing to check in that case */ 00895 if ((uiOsStatus & defContextGetMask) == defContextStateIsr) { return; } 00896 /* If no, extract the current task number. */ 00897 uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift; 00898 /* Get the capabilities of the task*/ 00899 Tuint08 uiDefinedCaps = portFlashReadByte(Tuint08,uiCapabilities[uiTaskNumber]); 00900 /* Check if the capabilities match, with ~ all non defined caps turn high, if it matches with a 00901 * required cap, this is an error. */ 00902 Tuint08 uiViolation = (uiTaskCaps & ~uiDefinedCaps); 00903 /* Thus if the value equals zero all required capabilities are present, otherwise some are absent. */ 00904 if (uiViolation != 0) 00905 { /* Determine the highest violation (binary log) */ 00906 Tuint08 uiInfo = 8; 00907 /* Check if this is the violating bit */ 00908 while ( (uiViolation & 0x80) == 0 ) 00909 { /* If not it could be bit 6, so we decrease the counter one ... */ 00910 uiInfo--; 00911 /* and shift the 6th bit to the 7th */ 00912 uiViolation <<= 1; } 00913 /* Report an error and stop the task if the requested capabilities are absent. The ControlTaskNumber 00914 * also contains the realm in which we currently are. */ 00915 privShowError((errInsufficientCapabilities | errTaskStateInfo | errOsStateAsIs), uiCallId, (uiInfo << errInfoNumberShift) | (uiTaskNumber << errTaskNumberShift)); } } 00916 00917 #endif 00918 00919 00920 #ifdef portInitContext 00921 00922 static Taddress privInitContext(Taddress pTaskStart, Taddress pStackTop, Tuint08 uiRegisterCount, Tuint08 uiInterruptStart) 00923 { /* Arrive here to set up the initial stack. Of course this is machine dependent, but not very 00924 * strongly. For 8 bits cpu's it is fairly general. Just place the return address, the registers 00925 * and the status. Here the values are just cleaned. If needed we can add a portInitConext call 00926 * on the basis of an option. */ 00927 Tuint16 uiStartAddress = (Tuint16) pTaskStart; 00928 /* At the bottom of the artificial stack the start address of each task is defined. This is a pointer 00929 * to your Loop code. */ 00930 *(pStackTop--) = (Tuint08) uiStartAddress; 00931 *(pStackTop--) = (Tuint08) (uiStartAddress >> 8); 00932 /* If we have a program counter of more than 16 bit, call instructions push three bytes 00933 * into the stack. We must take that into account. Unfortunately, i believe, gcc is not 00934 * able to handle pointer larger as 64K words in the current setting. Thus this will 00935 * effectively be zero, thus although you need something like 00936 * *(pStackTop--) = (Tuint08) (pTaskStart >> 16); 00937 * we will use: */ 00938 #if (defThreeByteAddress == cfgTrue) 00939 *(pStackTop--) = 0; 00940 #endif 00941 /* We rely upon register cleaning done by the Femto OS, or the precleaning done by privTaskInit(). */ 00942 pStackTop -= uiRegisterCount; 00943 /* The way the status register is setup depends on the state of the interrupts. These can be specified per task 00944 * or (easier, and shorter) in general. Note that the optimization is not needed in a strict sense, the variable 00945 * uiInterruptStart constrains the interrupt start information, even if this is constant for all tasks. But, of course, 00946 * the lower part generates shorter code. */ 00947 #if (defInterruptStartConstant == cfgFalse) 00948 /* If we specify the CPU status register per task, we need to prepare them per task. uiInterruptStart contains 00949 * the information about which interrupts must be activated. The other bits of the status register cannot be 00950 * set individually (there is no need in general) */ 00951 Tuint08 uiInitCPUStatusRegister = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc); 00952 /* Test if global interrupts must be activated at the start, if so set the specified bit. */ 00953 #if (cfgIntGlobalOnly == cfgTrue) 00954 /* In case we have a mapping from tick interrupts on global interrupts, we require both interrupts 00955 * to be activated before activating the global interrupt. */ 00956 if ((uiInterruptStart & ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) == ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); } 00957 #else 00958 /* Otherwise we just test the global interrupt setting. */ 00959 if ((uiInterruptStart & (cfgGlobSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); } 00960 #endif 00961 /* Test if tick interrupts must be activated at the start, if so set the specified bit. */ 00962 #if (cfgIntTickTrack == cfgTrue) 00963 if ((uiInterruptStart & (cfgTickSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitTickInterruptLoc); } 00964 #endif 00965 /* The status register is put at the end of the stack. (See portSaveContext for the reason why) */ 00966 *(pStackTop--) = uiInitCPUStatusRegister; 00967 #else 00968 /* Set up the status register with the initial interrupt states: global set and tick set */ 00969 #if (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet) 00970 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc); 00971 /* Set up the status register with the initial interrupt states: global set and tick clear */ 00972 #elif (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear) 00973 #if (cfgIntGlobalOnly == cfgTrue) 00974 /* In case we have a mapping from tick interrupts on global interrupts, global interrupts cannot be activated if tick interrupts are not activated */ 00975 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00976 #else 00977 /* Default situation. */ 00978 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00979 #endif 00980 /* Set up the status register with the initial interrupt states: global clear and tick set */ 00981 #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet) 00982 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc); 00983 /* Set up the status register with the initial interrupt states: global clear and tick clear */ 00984 #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear) 00985 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00986 #elif (defNumberOfTasks == 0) 00987 /* without tasks there is nothing to set. */ 00988 #else 00989 /* well, we never come here. */ 00990 #error "Parameter 'defInterruptStartFixed' is misspeld or incorrect (You should not arrive here)." 00991 #endif 00992 #endif 00993 /* Done, let the caller know where the stack ended. */ 00994 return pStackTop; } 00995 #endif 00996 00997 00998 static TtaskControlBlock * privTcbList(Tuint08 uiTaskNumber) 00999 { /* This method may not be called with defCurrentTaskNumber any longer. Unfortunately we 01000 * cannot check if we violate that, because that may corrupt stack in privInitOs(). 01001 * The location of the task control block is stored in flash, be careful, use the correct 01002 * instructions to retrieve it. */ 01003 return portFlashReadWord(TtaskControlBlock *,pxTCBlist[uiTaskNumber]); } 01004 01005 01006 static Tuint08 privTaskNumber(Tuint08 uiTaskNumber) 01007 { /* Check if we are interested in the current task */ 01008 if ((uiTaskNumber & defCurrentTaskMask) == defCurrentTaskNumber) 01009 { /* If yes, replace the uiTaskNumber with the current task number. */ 01010 uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift; } 01011 return uiTaskNumber; } 01012 01013 01014 #if (defRegisterUseConstant == cfgFalse) 01015 01016 static Tuint08 privRegisterCount(Tuint08 uiRegisterUse) 01017 { /* We want to count the number of register this uiRegisterUse byte represent. Each set bit 01018 * counts for four registers. */ 01019 /* uiCount accumulates the number of set bits */ 01020 Tuint08 uiCount = 0; 01021 /* Loop through all bits of the uiRegisterUse byte. Start with 'do' for the most optimal compiler result. */ 01022 do 01023 { /* Increase the counter if the bit is set by four since every bit represents four registers. */ 01024 if ((uiRegisterUse & 0x01) == 0x01) { uiCount+=4; } 01025 /* Shift to the next bit */ 01026 uiRegisterUse >>= 1; } 01027 while (uiRegisterUse); 01028 /* When uiRegisterUse equals zero there are no bits left. We are done. */ 01029 return uiCount; } 01030 01031 #endif 01032 01033 01034 static void privTaskInit(Tuint08 uiTaskNumber, Tuint08 uiInitControl) 01035 { /* Tasks are initialized in this routine. Note the fields: 01036 * uxDelay, siQueuLock, uiLoadCollect, uiLoadTotal, uiStackMax, uiRegisterUse 01037 * are not cleared when we re-initialize the task. Only about uxDelay there could 01038 * be debate since the would indicate the last wake time. But what is a last wake 01039 * time in this case. We could set it to the current time, but have not done so. */ 01040 /* Report that we are initializing this task. */ 01041 privTrace(traceTaskInit | uiTaskNumber); 01042 TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber); 01043 /* In case we might return here due to a watchdog bark or a restart or so. some old connections 01044 * must be closed when present. */ 01045 #if (defReUseTaskInit == cfgTrue) 01046 /* And if asked for it. */ 01047 if ((uiInitControl & defInitLockGetMask) == defInitLockRelease) 01048 { /* In case we use synchronization and the watchdog or want to be able to manually restart a task 01049 * it may be that the task that has to be re-initiated is holding some locks. These must be released properly 01050 * We need only to release locks on other tasks that are held by this task. Since waits cannot be non-blocking 01051 * it can safely be ignored here. Hmm, some other task may call restart on a blocking task on wait. So it seems 01052 * better to always clean when synchronization is used. */ 01053 #if (cfgUseSynchronization != cfgSyncNon) 01054 /* we only need to check if the task contains a slot. */ 01055 if (uiTaskNumber < defNumberOfTasksWithSlot) 01056 { /* We may only enter here if the particular task indeed has a slot stack, otherwise there is nothing clean 01057 * If we made use of priority lifting, we do not need to do anything. We can simply clean the slot stack. */ 01058 privCleanSlotStack((TtaskExtendedControlBlock *) taskTCB ); 01059 /* and any task that may run freely now will be release by the call below. Please note that is not possible 01060 * that is call releases the present task, since all its slots have been wiped. */ 01061 #if ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue)) 01062 privReleaseSyncBlockingTasks(); 01063 #endif 01064 } 01065 #endif 01066 /* At this point we must check if we have some kind of lock in the file system and we need 01067 * to release this and other tasks. Only include code if this is possible at all. */ 01068 #if (cfgUseFileSystem == cfgTrue) 01069 /* We can simply try to close a file. If no lock is hold and no blocks are present this method returns 01070 * without any harm done. Of course it is more overhead, but we assume this part of the code is rarely executed 01071 * and therefore we do not want to invest the bytes. There are quite a number of situations in which the task 01072 * can be caught when killed. All these situations are handled by the method. */ 01073 privPrepareFileClose(uiTaskNumber); 01074 #endif 01075 } 01076 #endif 01077 /* Read the priority the task will start with is contained in the lowest nibble this number, the start state 01078 * information is contained in the highest nibble. the interrupt state in both. While the handling for the 01079 * priority is simple, simply replace it or not, depending on the defInitStatusPrio, the start state 01080 * requires a more complex handling. Globally this routine provides the following services to the caller 01081 * (1) Load the start state from flash, this is the default action 01082 * (2) Load the start state from the uiInitControl, use defInitStatusCopyDo 01083 * (3) Load the start state from the uiInitControl, but allow for a fall back scenario for the default state 01084 * (4) Make sure that the start state is 'shared' instead of 'running' for shared tasks. 01085 * Apart from these facilities, it also provide a facility to set the shared state bit in 01086 * the OS status. If the task was running before, but is not running afterwards, this but must 01087 * be reset and vice versa. 01088 * First read the initial status from flash, or use a constant value. */ 01089 #if (defInitialStatusConstant == cfgTrue) 01090 Tuint08 uiInitialStatus = defInitialStatusFixed; 01091 #else 01092 Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus); 01093 #endif 01094 /* We can call this with a predefined state or with a default state, in which case we want to restart 01095 * like we started at the beginning. */ 01096 #if (defUseSharedStack == cfgTrue) || (includeTaskRestart == cfgTrue) 01097 /* Since the bit pattern of the default start state equals the one of the shared state, and since we 01098 * know we can never explicitly set the state to be shared from the outside (this is always a modified 01099 * running state) We must first test if we have a default state. */ 01100 if ((uiInitControl & defInitialStartGetMask) == defRestartDefault ) 01101 { /* We do not need to copy the start information from the uiInitControl if the caller asked for a default 01102 * If it is not equal, we leave leave the copy bit alone. Most of the time however, there will not 01103 * be a request for a copy, so this does nothing.*/ 01104 uiInitControl = (uiInitControl & defInitStatusCopySetMask) | defInitStatusCopyDont; } 01105 #endif 01106 /* If we have a suspend request, this will be honored at this place. This may override the previous 01107 * situation. Tasks cannot ignore a suspend request at this moment. Suspend requests are only possible 01108 * when genSuspend() is present: */ 01109 #if (includeGenSuspend == cfgTrue) 01110 /* test if the request has been made */ 01111 if ((taskTCB->defSusField & defSusGetMask) == defSusRequest) 01112 { /* if so, we must set the StatusCopy and the suspended restart: */ 01113 uiInitControl = (uiInitControl & (defInitStatusCopySetMask & defInitialStartSetMask)) | (defRestartSuspended | defInitStatusCopyDo); 01114 /* We do not need to clear the SusRequest separately since that is done below in 01115 * the clearance of the TaskMonitor. */ } 01116 #endif 01117 /* If we were running, and make use of a shared stack, we must inform the OS that we stopped a running 01118 * shared state (unless we continue to run, but that is not the default, and not possible in this routine) 01119 * This is however only needed if we are not actively are creating a context .*/ 01120 /* If we make use of shared tasks we must inform the OS about the situation. */ 01121 #if (defUseSharedStack == cfgTrue) 01122 /* And only if the current task is indeed a shared task */ 01123 #if (defAllSharedStack == cfgFalse) 01124 /* If all tasks are shared this test is always true, so skip it. */ 01125 if ((uiInitialStatus & defInitialSharedGetMask) == defInitialSharePresent) 01126 #endif 01127 { /* Retrieving the uiOsStatus is a costly operation, so lets work via a register 01128 * gcc optimization does not recognize this. */ 01129 Tuint08 uiStatusCopy = uiOsStatus; 01130 /* If so, we must test if we want to activate a shared task, if so we must create a context (see below) */ 01131 if ((uiInitControl & defInitSharedGetMask) == defInitSharedActive) 01132 { /* and set the shared bit of the OS status */ 01133 uiStatusCopy = ((uiStatusCopy & defShareStateSetMask) | defShareStateRunning); } 01134 else 01135 { /* Task that are not rescheduled right now need no context */ 01136 uiInitControl = (uiInitControl & defInitContextSetMask) | defInitContextKeep; 01137 /* Shared tasks that are not scheduled for running may not be in the shared mode, 01138 * but be put to sleep or be suspended from the outside. The only way to recognize 01139 * such tasks is at the stack level being zero. Other way around, if the stack 01140 * level does not equal zero, it must have been running, or been put to sleep/suspend 01141 * block while running, and is holding the share state. Since we are to passivy this 01142 * state, we clear the bit. */ 01143 if (taskTCB->pcStackLevel != defStackEmpty) 01144 { /* If so, we are stopping the only running shared task, and must inform the OS 01145 * so it can select a new one. */ 01146 uiStatusCopy = ((uiStatusCopy & defShareStateSetMask) | defShareStateAbsent); 01147 /* and make sure that the stack is empty so this is recognized */ 01148 taskTCB->pcStackLevel = defStackEmpty; } 01149 /* This is the place to correct the situation where we have asked for a running state 01150 * but we are going to get into a shared state, since a shared tasks can only be 01151 * in the running state when we have a valid context, ie.e on defInitContextRenew. 01152 * We need only to check the first bit of the state, since if it is set the second 01153 * must be set too, or if it is not, it is already in the shared state (what is a 01154 * defRestartDefault actually. */ 01155 if ((uiInitControl & defBaseStopStateGetMask) == defBaseStopStateGo ) 01156 { /* So we have requested for a defRestartRunning, change it to defRestartShared. */ 01157 uiInitControl = (uiInitControl & defBaseModeSetMask) | defBaseModeShared; } } 01158 /* Dont forget to put the copy back */ 01159 uiOsStatus = uiStatusCopy; } 01160 /* If we are putting a task to sleep, and that task happens to be a shared task, the sleep 01161 * instruction must be interpreted as a restart. However, this is only the case for shared 01162 * tasks. Therefore we have a special option to indicate this situation, which is only 01163 * uses for this special case. For regular tasks we want to leave this routine after 01164 * the specialties for shared tasks have been skipped. 01165 * Test if we have a low power sleep possibility */ 01166 #if (cfgUseLowPowerSleep == cfgTrue) 01167 /* If all tasks are shared, we do not test for the defInitialSharePresent thus we also 01168 * not have an alternative branch. We simply always have to test if we want to leave. */ 01169 #if (defAllSharedStack == cfgFalse) 01170 /* otherwise we will only perform the test below in case we are certain we have 01171 * a regular task. */ 01172 else 01173 #endif 01174 { /* if we have a regular task, but we wanted to process shared tasks only we are done. */ 01175 if ((uiInitControl & defInitProcessGetMask) == defInitProcessSharedOnly ) { return; } } 01176 #endif 01177 #endif 01178 /* The defInitStatus indicates if we want to prepare a new TaskStatus. This is needed upon first use, as it 01179 * contains the initial run state, its priority etc. The structure of the information is not identical to 01180 * the uiTaskStatus. The priority and start state are on the same location, but on the location if the lock bit, 01181 * the delay bit and the dress bit other information is stored. This must be corrected. For shared tasks, the 01182 * task starts in the shared mode. Upon first pass thats OK (no shared running, but if a running task is 01183 * modified to shared extra measures might be needed. In any case, a new or renewed task, is not blocked, 01184 * delayed of dominant, therefore we set those bits. */ 01185 Tuint08 uiIntialFilteredStatus = (uiInitialStatus & (defBaseBlockStateSetMask & defBaseDelayStateSetMask & defBaseDressSetMask) ) | (defBaseBlockStateFree | defBaseDelayStateWake | defBaseDressDone); 01186 /* Depending on the fact if we are able to return here we can directly set its initial state, or 01187 * we must be more careful. In case there is the possibility to return here: */ 01188 #if (defReUseTaskInit == cfgTrue) 01189 /* See what must be kept from the original status, and what must be read from flash. uiInitControl contains 01190 * a bit mask for those pieces we want to retain from the original task status. The default is to 01191 * read everything from flash except for the priority information. The start state may be overwritten 01192 * later on. */ 01193 if ((uiInitControl & defInitStatusPrioGetMask) == defInitStatusPrioKeep) 01194 { /* If we indeed must replace the priority, do so in the uiIntialFilteredStatus */ 01195 uiIntialFilteredStatus = (uiIntialFilteredStatus & defInitialPrioritySetMask) | (taskTCB->uiTaskStatus & defInitialPriorityGetMask); } 01196 /* In three situations we may ask for a copy of the start state from the uiInitControl parameter */ 01197 #if (defUseSharedStack == cfgTrue) || (includeTaskRestart == cfgTrue) || ((cfgUseTaskWatchdog == cfgTrue) && (includeGenSuspend == cfgTrue)) 01198 /* If we have a request for copying the start state from the control (this request may be from outside, 01199 * but is not valid for the a start state being defRestartDefault, in which case the copyDo was cleared 01200 * above */ 01201 if ((uiInitControl & defInitStatusCopyGetMask) == defInitStatusCopyDo) 01202 { /* Replace the first start bits by the bits given in the control parameter. */ 01203 uiIntialFilteredStatus = (uiIntialFilteredStatus & defBaseRestartSetMask) | (uiInitControl & defBaseRestartGetMask); } 01204 #endif 01205 #endif 01206 /* Set the newly prepared task status. */ 01207 taskTCB->uiTaskStatus = uiIntialFilteredStatus; 01208 /* Fields uiTaskMonitor is initialized. Usually the init bytes are 0x00. If we 01209 * have no watchdog and no TaskRestart/TaskRecreate we can only arrive here one time. If the init fields are 01210 * 0x00 so we may rely upon the cleaning of the .bss section to reset these fields. 01211 * In other cases however we must explicitly clean/initialize those fields. */ 01212 #if (defUseTaskMonitor == cfgTrue) && ((defReUseTaskInit == cfgTrue) || (defTaskMonitorInit != 0x00)) 01213 taskTCB->uiTaskMonitor = defTaskMonitorInit; 01214 #endif 01215 /* Fields uiTasksLevels is initialized. Usually the init bytes are 0x00. If we 01216 * have no watchdog and no TaskRestart/TaskRecreate we can only arrive here one time. If the init fields are 01217 * 0x00 so we may rely upon the cleaning of the .bss section to reset these fields. 01218 * In other cases however we must explicitly clean/initialize those fields. */ 01219 #if (defUseTaskLevels == cfgTrue) && ((defReUseTaskInit == cfgTrue) || (defTaskLevelsInit != 0x00)) 01220 taskTCB->uiTaskLevels = defTaskLevelsInit; 01221 #endif 01222 /* If we keep track of the watermarks we renew them when we renew the priority */ 01223 #if (defReUseTaskInit == cfgTrue) && (cfgCheckWatermarks == cfgTrue) 01224 /* ... Initializing the task may (requested through the call) require resetting those levels. */ 01225 if ((uiInitControl & defInitStatusPrioGetMask) == defInitStatusPrioRenew) 01226 { /* If so, clean the StackMax level and the use of the registers. */ 01227 taskTCB->uiStackMax = 0; 01228 taskTCB->uiRegisterUse = 0; } 01229 #endif 01230 /* We make a new context if this is a one time call, ... */ 01231 #if (defReUseTaskInit == cfgTrue) 01232 /* ... or if we specially ask for it ... */ 01233 if ((uiInitControl & defInitContextGetMask) == defInitContextRenew) 01234 #endif 01235 { /* Report that we are making the context of this task. */ 01236 privTrace(traceCreateContext); 01237 /* pcStackOffset is a pointer to the beginning of the stack of this task, it is located in flash. */ 01238 Taddress pcStackOffset = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Taddress,pcStackOffset); 01239 /* We need the place where the task starts. This is placed on the context as return address. It is located in flash */ 01240 Taddress pTask = portFlashReadWord(Taddress,pxLooplist[uiTaskNumber]); 01241 /* Register compression requires information on which registers must be saved. Every bit stands for 01242 * four bits to save. If we make use of Register Compression with variable registers per task we must 01243 * load the information from flash. otherwise we just may use the constant here. 01244 * Here we count how may registers will be saved on the stack. For each register one byte will be reserved. 01245 * If all RegisterUse parameters are equal, we can calculate the number at compile time. */ 01246 #if (defRegisterUseConstant == cfgTrue) 01247 /* The constant defRegisterCount holds the number of registers used.*/ 01248 Tuint08 uiRegCount = defRegisterCount; 01249 #else 01250 /* The register use must be read from flash. */ 01251 Tuint08 uiRegisterUse = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiRegisterUse); 01252 /* We have to calculate the number dynamically */ 01253 Tuint08 uiRegCount = privRegisterCount(uiRegisterUse); 01254 #endif 01255 /* We need the StackSize if we must clean the stack before use */ 01256 #if (defStackClean == cfgTrue) 01257 /* Get the reserved stack space from flash, or use the constant if all stack sizes are identical Note that, if we 01258 * don't check, we don;t need the uiStackSize information. It is simply assumed there is enough. */ 01259 #if (defStackSizeConstant == cfgTrue) 01260 Tstack uiStackSize = defStackSizeFixed; 01261 #else 01262 Tstack uiStackSize = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tstack,uiStackSize); 01263 #endif 01264 #endif 01265 /* Clean the stack and set up the context. The method portInitContext makes room the return address. 01266 * the device status register and all registers used. */ 01267 #if (cfgSysStackGrowthUp == cfgTrue) 01268 /* First fill the whole stack with the defStackInitByte if needed */ 01269 #if (defStackClean == cfgTrue) 01270 Taddress pStackTop = pcStackOffset; 01271 while (uiStackSize--) { *(pStackTop++) = defStackInitByte; } 01272 #endif 01273 /* Subsequently define the context. */ 01274 taskTCB->pcStackLevel = (Tstack) ((Tuint16) portInitContext(pTask, pcStackOffset, uiRegCount, uiInitialStatus) - (Tuint16) pcStackOffset); 01275 #else 01276 /* First fill the whole stack with the defStackInitByte if needed */ 01277 #if (defStackClean == cfgTrue) 01278 Taddress pStackTop = pcStackOffset; 01279 while (uiStackSize--) { *(pStackTop--) = defStackInitByte; } 01280 #endif 01281 /* Subsequently define the context. */ 01282 taskTCB->pcStackLevel = (Tstack) ((Tuint16) pcStackOffset - (Tuint16) portInitContext(pTask, pcStackOffset, uiRegCount, uiInitialStatus)); 01283 #endif 01284 } } 01285 01286 01287 static void privEnterOS(Tuint08 uiAction) 01288 { /* This is the method to call to start the OS functions. Its is typically called from yield, 01289 * delay etc, but also from the switching api functions after the completed their task. The 01290 * action parameter states the intention of the caller. It contains whether or not we should 01291 * switch the task, and if a tick occurred. 01292 * Report that we are starting OS specific actions */ 01293 privTrace(traceOsStart); 01294 /* Now is the time to permanently set the status of the system to OS. */ 01295 uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateOs); 01296 /* First we check if no event has taken place. */ 01297 #if (cfgUseEvents == cfgTrue) 01298 /* Handling events must take place in an protected environment. */ 01299 #if (cfgIntOsProtected == cfgFalse) 01300 privDisableGlobalInterrupts(); 01301 #endif 01302 /* Check if there are any event set */ 01303 if (portEventRegister != defAllEventsReset) 01304 { /* OK, we have to handle the events and possibly deblock some tasks. If we invert the Event 01305 * register, we have '1' on all places that must be left alone, and '0' on those places where 01306 * the bits may be reset. */ 01307 Tuint08 uiInvEvent = ~portEventRegister; 01308 /* Don't forget to reset the event register */ 01309 portEventRegister = defAllEventsReset; 01310 /* Loop through all tasks possibly waiting on an event. */ 01311 Tuint08 uiLoopTask; 01312 for (uiLoopTask=defTaskNumberEventBegin; uiLoopTask<defTaskNumberEventEnd; uiLoopTask++) 01313 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 01314 /* Test if the task is really waiting on events */ 01315 if (loopTCB->uiTaskEvents != defAllEventsReset) 01316 { /* reset all events that are fired */ 01317 loopTCB->uiTaskEvents &= uiInvEvent; 01318 /* Check if there are any events left. */ 01319 if (loopTCB->uiTaskEvents == defAllEventsReset) 01320 { /* If not, unblock this task. */ 01321 privUnblockTask(uiLoopTask | defParaLockStateUnlock | defParaRetStateTrue); 01322 /* If we have unblocked a task, we make sure we switch tasks asap */ 01323 /* TODO v0.90: If we are returning a full byte, it must not be possible to 01324 * switch the task since there will be only one return value register. */ 01325 uiAction |= defActionTaskStateSwitch; } } } } 01326 /* Done, restore interrupts if needed */ 01327 #if (cfgIntOsProtected == cfgFalse) 01328 privEnableGlobalInterrupts(); 01329 #endif 01330 #endif 01331 /* If we do not want to switch, we are quickly done. Note that time measurement is also 01332 * skipped, so, we may not skip this to often, often being a full round of the subticktimer. 01333 * Normally however, this is a one time action, because a regular entry over here due to 01334 * an interrupt will not skip this section. */ 01335 if ((uiAction & defActionTaskGetMask) == defActionTaskStateSwitch) 01336 { /* If we make use of a file system we must check if there is a task waiting for a burn lock 01337 * to finish (this can only be one task at most. */ 01338 #if (cfgUseFileSystem == cfgTrue) 01339 /* Look if we have a burn lock active, if so there must be a write lock too, so we do not need to check that. */ 01340 if ((uiFsStatus & defFsBurnBlockGetMask) == defFsBurnBlockActive) 01341 { /* If so we test if the file system is ready for a new byte to be written */ 01342 if (portFSWriteReady()) 01343 { /* Tell we may burn again. In this case we must clean the burn lock in the uiFsStatus, we do so below. */ 01344 privTrace(traceBurnFree); 01345 /* Check if we are just waiting for a FS release. In this situation there is no task 01346 * actually writing, but one of the write tasks was somehow terminated, and the system 01347 * must wait with the release of new tasks until the burn block is gone. */ 01348 if ((uiFsStatus & defFsReadBlockGetMask) == defFsReadBlockReleaseRequest) 01349 { /* Clear everything in the status register, except a possible request for sleep. */ 01350 uiFsStatus = (uiFsStatus & defFsFreeSetMask) | defFsFree; 01351 /* There could be other tasks waiting to be released and perform file operations. Always, directly 01352 * after a task completed its file operations, it must call for a release of new tasks. There is 01353 * no automatic dispatch mechanism to activate the tasks independently. */ 01354 privReleaseFileBlocks(); } 01355 else 01356 { /* We still have to clear the burn lock */ 01357 uiFsStatus = ((uiFsStatus & defFsBurnBlockSetMask) | defFsBurnBlockClear); 01358 /* In this case we are in a simple file write operation, so extract the task 01359 * that was waiting for burning. The Burn lock can not be active if no task is waiting, 01360 * which is essential since we have no task number indicating a 'free' state. We could check if that 01361 * task found has a write lock, but that should be the case */ 01362 #if (defUseFsOnMultipleTasks == cfgTrue) 01363 /* In the case of multiple tasks, the task that is currently writing is in the status */ 01364 Tuint08 uiLockTaskNumber = (uiFsStatus & defFsWriteNumberGetMask) >> defFsTaskNumberShift; 01365 #else 01366 /* otherwise it is a fixed number determined by the preprocessor. */ 01367 Tuint08 uiLockTaskNumber = defUseFsSingleTaskNumber; 01368 #endif 01369 /* we must unblock the task, note that 01370 * this kind of block (burn block) cannot timeout, and thus the function does not return a value 01371 * to indicate this, we must leave the return register as it was. Naturally we keep the lock, for 01372 * there could be more bytes that must be written. It is not possible for a terminated task 01373 * to arrive here, since the taskKill routine will always try to close a lock on the file. */ 01374 privUnblockTask(uiLockTaskNumber | defParaLockStateKeep | defParaRetStateNon); } } } 01375 #endif 01376 /* Well, let us first determine what kind of tick engine 01377 * we are running on. Since both types require quite a different approach the code is 01378 * completely separated. */ 01379 #if (cfgUseEquidistantTicks == cfgTrue) 01380 /* We are running equidistant ticks. Thus the timer interrupt is set to a fixed value and 01381 * can only be read, but not changed. Also, the timer is of the type interrupt and clear, thereby 01382 * guaranteeing the most regular tick intervals. But if we are in a full cooperative mode 01383 * i.e. all tick interrupts masked either by disabling tick interrupts itself, of by disabling 01384 * global interrupts, we do not see ticks-interrupts any more and have to check manually. */ 01385 #if (cfgIntManualTicks == cfgTrue) 01386 /* The check method returns true if a timer interrupt was due. The interrupt was also 01387 * cleaned and therefore we ask for a increase of the tick counter here. */ 01388 if (portCheckTimer()) { uiAction |= defActionTickStateTick; } 01389 #endif 01390 /* If we are checking timing issues or want to keep track of the load of the tasks we 01391 * must read the value timer. */ 01392 #if ( (cfgCheckTiming == cfgTrue) || (cfgUseLoadMonitor == cfgTrue) ) 01393 Tuint08 uiOsStartTime = portReadTimer(); 01394 #endif 01395 /* If we want to monitor the load, some calculations are needed. Please note that this facility 01396 * is only available when cfgIntOsProtected == true. Otherwise there is simply no way get the 01397 * data to the os, since during this calculation it may be interrupted also. */ 01398 #if (cfgUseLoadMonitor == cfgTrue) 01399 Tuint08 uiTaskTime; 01400 /* We want to calculate how much time the last task took. The subbyte contains the 01401 * last saved value of portReadTimer.*/ 01402 Tuint08 uiSubByte = uxTickCount.SubByte; 01403 /* It is possible that we had an interrupt since we saved the the subByte. We assume, 01404 * that in that case uiSubByte>uiOsStartTime. This may not be true if we had a very 01405 * long interrupt but that cannot be checked. We cannot check one full round of the timer, 01406 * since we do not want to spend bytes to keep track of that. 01407 * If, however the timer has an unhandled interrupt, we expect the portReadTimer() function 01408 * to return a timer value with cfgSysSubTicksPerFullTick added to the actual value. This 01409 * is 'easy' to implement. */ 01410 if (uiSubByte<=uiOsStartTime) 01411 /* If we have a very slow timer, we could arrive in the same subtick here, thus the equality 01412 * should result in the addition of zero subticks instead of cfgSysSubTicksPerFullTick. */ 01413 { uiTaskTime = uiOsStartTime - uiSubByte; } 01414 /* So here we past the cfgSysSubTicksPerFullTick barrier and must correct for the situation. */ 01415 else 01416 { uiTaskTime = cfgSysSubTicksPerFullTick - uiSubByte + uiOsStartTime; } 01417 /* If we have other interrupts at hand, the system may have spend some time there as well. We should 01418 * correct the uiTaskTime accordingly. Note that we cannot distinguish between different interrupts 01419 * of succeeding interrupts. */ 01420 #if (cfgIntUserDefined == cfgTrue) 01421 /* uiIsrLoadTemp holds the time spend in isr */ 01422 Tuint08 uiIsrTime = uiIsrLoadTemp; 01423 /* This time however was erroneously incorporated in the task time, so we must correct for that error, 01424 * again, we assume no multiple tick interrupts in between. The test in between it to make sure the 01425 * value is indeed deducible, which may not be the case in rare cases */ 01426 if (uiTaskTime > uiIsrTime) { uiTaskTime -= uiIsrTime; } 01427 /* Add it to the LoadCollect */ 01428 uiIsrLoadCollect += uiIsrTime; 01429 /* We have read out the value of uiIsrLoadTemp and must reset it for the next interrupt. */ 01430 uiIsrLoadTemp = 0; 01431 #endif 01432 /* Which task was running lately? */ 01433 TtaskControlBlock * oldTCB = privTcbList(privTaskNumber(defCurrentTaskNumber)); 01434 /* OK, this was the last task that did run, but ... did it run the very last slice or was it the 01435 * somewhat longer ago and did the idle time run after that? We can see the difference by looking 01436 * at uiAction variable. We did run a task (or isr) if we did not run the idle state. If we run the 01437 * idle state we must have come from privTickYield, the RunState and running mode. We only count the time if the task is running and the state 01438 * is still runnable (may be delayed). Of course we miss the time of those tasks which just went 01439 * blocking. Since there is no other way to find out, we have to accept this (small) error. */ 01440 if ((uiAction & defActionRunGetMask) == defActionRunStateTask) 01441 { /* The RunState is still set, so indeed the calculated time was due to the running of that task */ 01442 oldTCB->uiLoadCollect += uiTaskTime; } 01443 else 01444 { /* Now the last task was the idle task, so add the time to the idle collect. Btw we could 01445 * also have been sleeping that difference cannot be made. However, it does not matter, since the 01446 * time from wakeup until now could count for idle as well. The sleep time itself is lost.*/ 01447 uiIdleLoadCollect += uiTaskTime; } 01448 #endif 01449 /* Here we arrive at the first instruction when all checks are off. We increment the tick counter 01450 * if a timer interrupt has taken place. This is indicated the the uiAction parameter. Most times 01451 * we arrive here it is namely due to a yield of delay or so, and we can skip that step. */ 01452 if ((uiAction & defActionTickStateTick) == defActionTickStateTick) { privIncrementTick(); } 01453 /* DISCUSSION 01454 * Sometimes the GCC compiler uses a frame pointer when there is not real need. This costs a lot 01455 * of flash. With the measure below we try to convince gcc that a particular register is free. 01456 * This seems portable since it is a request the compiler may ignore. 01457 * See the explanation at the option for more information. */ 01458 #if (cfgSysFramePointerCounterMeasures == cfgTrue) 01459 register volatile Tuint08 uiAssignmentStatus asm ("r6"); 01460 #else 01461 Tuint08 uiAssignmentStatus; 01462 #endif 01463 /* The two most important steps of the scheduler are, incrementing the tick counter, and 01464 * determining which task has to run next. The former is done above, and this action possible 01465 * waked some tasks, the latter below. The switch context results in a new task to run, or 01466 * the idle task that may or, or the sleep mode that the device must be put in. */ 01467 uiAssignmentStatus = privSwitchContext(); 01468 /* First, we check if we are put to sleep, which is done in OS space btw. If we are to sleep, 01469 * we are not allowed to prepare a subbyte for timing measurement, since that variable is used 01470 * to pass the number of allowable tick blocks to sleep.*/ 01471 #if (cfgUseLowPowerSleep == cfgTrue) 01472 if ((uiAssignmentStatus & defAssignmentSleep) == defAssignmentSleep) 01473 { /* We indeed have to go to low power sleep. Enter the sleep mode, we do not return from this call. */ 01474 privEnterSleep(uxTickCount.SubByte); } 01475 #endif 01476 /* If we arrive here, we are left with two possibilities, either we go run a task or go to the 01477 * idle state. First however, we measure how long the OS took, since most tasks of the OS are 01478 * done by this time. */ 01479 #if ((cfgCheckTiming == cfgTrue) || (cfgUseLoadMonitor == cfgTrue)) 01480 Tuint08 uiOsTime; 01481 /* Read the timer again. */ 01482 Tuint08 uiOsStopTime = portReadTimer(); 01483 /* From the moment we filled uiOsStartTime until now, that is the time the 01484 * OS took to run. This may not even be one subtick, so the we must test for equality too. (i.e.. 01485 * both times being equal probably means not even one subtick, contrary to a full round of the 01486 * subtick timer */ 01487 if (uiOsStartTime <= uiOsStopTime) 01488 { uiOsTime = uiOsStopTime - uiOsStartTime; } 01489 else 01490 { /* Again, in this case the timer interrupt fired, but is not handled yet. Hmmm, I am not 01491 * even sure id this situation can happen, since this should be a hanging interrupt right? 01492 * Well anyway, it seems save to leave the calculation in anyway */ 01493 uiOsTime = cfgSysSubTicksPerFullTick - uiOsStartTime + uiOsStopTime; } 01494 /* If you already used half of the tick here, there is probably not enough time to complete 01495 * the following task. Let us calculate that situation. */ 01496 #if (cfgCheckTiming == cfgTrue) 01497 /* Since this may incidentally happen, and that does not cause problems 01498 * we calculate the continuous 4 sample average. Depending on how large that number can be 01499 * we need a 8 or 16 bit variable. */ 01500 #if (cfgSysSubTicksPerFullTick >= 64) 01501 Tuint16 uiOsLocalTimeAverage = ((Tuint16)uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2); 01502 #else 01503 Tuint08 uiOsLocalTimeAverage = (uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2); 01504 #endif 01505 /* The average value however should not exceed the a one byte value */ 01506 uiOsTimeAverage = (Tuint08) (uiOsLocalTimeAverage >> 2); 01507 /* If we have tracing activated, we report the actual timing and the averaged timing. 01508 * when they are close to the border*/ 01509 #if (cfgCheckTrace == cfgTrue) 01510 if (uiOsTimeAverage > (3*cfgSysSubTicksPerFullTick/8)) 01511 { privTrace(traceOsTime); 01512 privTrace(uiOsTime); 01513 privTrace(uiOsTimeAverage); } 01514 #endif 01515 /* If this value exceeds have the tick time, this should be reported. */ 01516 if (uiOsTimeAverage >= (cfgSysSubTicksPerFullTick / 2)) 01517 { /* This is a fatal error since it makes no sense to continue as this issue 01518 * will rise again and again. */ 01519 privShowError((fatOsTickRateTooHigh | errTaskStateNon), callIdSystem, errNoInfo ); } 01520 #endif 01521 #if (cfgUseLoadMonitor == cfgTrue) 01522 /* Having calculated the time spend in the OS, add it to the Collected times. */ 01523 uiOsLoadCollect += uiOsTime; 01524 /* In order to make the next time measurement possible save the last value read 01525 * from the timer in the Subbyte. That will be used to calculate the time spend in 01526 * a particular task. Of course we make a small error because the next few instructions 01527 * are not really part of the task. However, the same applies to the instructions 01528 * after the context save so we assume (hope?) these errors cancel out. */ 01529 uxTickCount.SubByte = uiOsStopTime; 01530 #endif 01531 #endif 01532 /* Now check if we must perform an idle task */ 01533 if ((uiAssignmentStatus & defAssignmentIdle) == defAssignmentIdle) 01534 { /* if so, call for the EnterIdle, from which we do not return */ 01535 privEnterIdle(); } 01536 /* Here we are done, the next thing is to enter the privEnterTask, in order to run 01537 * the task chosen */ 01538 #else 01539 /* We are running in the true time slice mode. Good choice, now you are sure that no tasks 01540 * are being starved if more are running full swing in one priority. OK, so far for the 01541 * advertisements. The timer interrupt is variable in this mode, and reset every time a task, 01542 * or the os starts. Note that the interrupts may be (and are) irregular. Ticks, however, 01543 * are on average, in pace with the system clock. */ 01544 Tuint08 uiTaskTime; 01545 /* Determining how long the task took is a snap, since the timer was reset just before 01546 * the task started. And, we must also specify how long we want to wait before the next 01547 * timer interrupt. Now that does not matter much here, since (tick) interrupts are disabled 01548 * anyhow. */ 01549 uiTaskTime = portReadAndResetTimer(defSubTicksMax); 01550 /* We assume the timer did not overflow. This may happen if you choose cfgSysSubTicksPerFullTick 01551 * too high. But you can easily check this, see below. In this timer model the subbyte collects 01552 * the subticks, from which the tick counter is driven. */ 01553 uxTickCount.SubByte += uiTaskTime; 01554 /* If we keep track of the task times, the only thing to do is to add uiTaskTime to the collector */ 01555 #if (cfgUseLoadMonitor == cfgTrue) 01556 /* If we have other interrupts at hand, the system may have spend some time there as well. We should 01557 * correct the uiTaskTime accordingly. Note that we cannot distinguish between different interrupts 01558 * of succeeding interrupts. */ 01559 #if (cfgIntUserDefined == cfgTrue) 01560 /* uiIsrLoadTemp holds the time spend in isr */ 01561 Tuint08 uiIsrTime = uiIsrLoadTemp; 01562 /* This time however was erroneously incorporated in the task time, so we must correct for that error, 01563 * again, we assume no multiple tick interrupts in between. The test in between it to make sure the 01564 * value is indeed deducible, which may not be the case in rare cases */ 01565 if (uiTaskTime > uiIsrTime) { uiTaskTime -= uiIsrTime; } 01566 /* Add it to the LoadCollect */ 01567 uiIsrLoadCollect += uiIsrTime; 01568 /* We have read out the value of uiIsrLoadTemp and must reset it for the next interrupt. */ 01569 uiIsrLoadTemp = 0; 01570 #endif 01571 /* Which task was running lately? */ 01572 TtaskControlBlock * oldTCB = privTcbList(privTaskNumber(defCurrentTaskNumber)); 01573 /* OK, this was the last task that did run, but ... did it run the very last slice or was it the 01574 * somewhat longer ago and did the idle time run after that? We can see the difference by looking 01575 * at uiAction variable. We did run a task (or isr) if we did not run the idle state. If we run the 01576 * idle state we must have come from privTickYield, the RunState and running mode. We only count the time if the task is running and the state 01577 * is still runnable (may be delayed). Of course we miss the time of those tasks which just went 01578 * blocking. Since there is no other way to find out, we have to accept this (small) error. */ 01579 if ((uiAction & defActionRunGetMask) == defActionRunStateTask) 01580 { /* The RunState is still set, so indeed the calculated time was due to the running of that task */ 01581 oldTCB->uiLoadCollect += uiTaskTime; } 01582 else 01583 { /* Now the last task was the idle task, so add the time to the idle collect. Btw we could 01584 * also have been sleeping that difference cannot be made. However, it does not matter, since the 01585 * time from wakeup until now could count for idle as well. The sleep time itself is lost.*/ 01586 uiIdleLoadCollect += uiTaskTime; } 01587 #endif 01588 /* in this timing model we always call privIncrementTick() since the ticks added to the tick counter 01589 * are deduced from the value of subbyte */ 01590 privIncrementTick(); 01591 /* DISCUSSION 01592 * Sometimes the GCC compiler uses a frame pointer when there is not real need. This costs a lot 01593 * of flash. With the measure below we try to convince gcc that a particular register is free. 01594 * This seems portable since it is a request the compiler may ignore. 01595 * See the explanation at the option for more information. */ 01596 #if (cfgSysFramePointerCounterMeasures == cfgTrue) 01597 register volatile Tuint08 uiAssignmentStatus asm ("r6"); 01598 #else 01599 Tuint08 uiAssignmentStatus; 01600 #endif 01601 /* The two most important steps of the scheduler are, incrementing the tick counter, and 01602 * determining which task has to run next. The former is done above, and this action possible 01603 * waked some tasks, the latter below. The switch context results in a new task to run, or 01604 * the idle task that may or, or the sleep mode that the device must be put in. */ 01605 uiAssignmentStatus = privSwitchContext(); 01606 /* First, we check if we are put to sleep, which is done in OS space btw. If we are to sleep, 01607 * we are not allowed to prepare a subbyte for time measurement, since that variable is used 01608 * to pass the number of allowable tick blocks to sleep.*/ 01609 #if (cfgUseLowPowerSleep == cfgTrue) 01610 if ((uiAssignmentStatus & defAssignmentSleep) == defAssignmentSleep) 01611 { /* We indeed have to go to low power sleep. Enter the sleep mode, we do not return from this call. */ 01612 privEnterSleep(uxTickCount.SubByte); } 01613 #endif 01614 /* If we arrive here, we are left with two possibilities, either we go run a task or go to the 01615 * idle state, Now check if we must perform an idle task .*/ 01616 if ((uiAssignmentStatus & defAssignmentIdle) == defAssignmentIdle) 01617 { /* if so, first set the Timer with the right time for the idle task, which at the same time 01618 * returns the time spend so far in OS */ 01619 Tuint08 uiOsTime = portReadAndResetTimer(TimeSliceIdleTime); 01620 /* This is of course time spend, so must be added to the tick counter */ 01621 uxTickCount.SubByte += uiOsTime; 01622 /* If we monitor the load */ 01623 #if (cfgUseLoadMonitor == cfgTrue) 01624 /* the time spend in OS must be added to the appropriate collector */ 01625 uiOsLoadCollect += uiOsTime; 01626 #endif 01627 /* End we may enter the idle task. In this case we do not have the opportunity to check 01628 * upon the OS time. */ 01629 privEnterIdle(); } 01630 /* Arriving here means we are going to perform a regular task. So we must prepare the timers 01631 * for that.*/ 01632 Tuint08 uiTimeSlice; 01633 /* We have two options. Either we have specified the time slice for each task separately, or 01634 * we use the one fits all model. */ 01635 #if (defTimeSliceConstant == cfgFalse) 01636 /* Get the task number we choose to run next */ 01637 Tuint08 uiTaskNumber = privTaskNumber(defCurrentTaskNumber); 01638 /* Get the value of the time slice from flash */ 01639 uiTimeSlice = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiTimeSlice); 01640 #else 01641 /* In the standard model, we give all tasks the same value, */ 01642 uiTimeSlice = defTimeSliceFixed; 01643 #endif 01644 /* The time has come to determine how long the OS has run, reset the timer and set the new time slice */ 01645 Tuint08 uiOsTime = portReadAndResetTimer(uiTimeSlice); 01646 /* The time spend in the OS must be added to the extended tick counter */ 01647 uxTickCount.SubByte += uiOsTime; 01648 /* And, if we monitor the load ... */ 01649 #if (cfgUseLoadMonitor == cfgTrue) 01650 /* to the collector of the OS load time as well */ 01651 uiOsLoadCollect += uiOsTime; 01652 #endif 01653 /* Many things can go wrong with the timing, so the moment has come to check. */ 01654 #if (cfgCheckTiming == cfgTrue) 01655 /* First, check if the time spend in the OS does not exceed one tick. Although it is not an error in the 01656 * strictest sense, it can do harm to the collected subticks in the subbyte, which may overflow, since 01657 * it cannot be cleaned on this point. Since this may incidentally happen, and that does not cause 01658 * problems we calculate the continuous 4 sample average. */ 01659 #if (cfgSysSubTicksPerFullTick >= 64) 01660 Tuint16 uiOsLocalTimeAverage = ((Tuint16)uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2); 01661 #else 01662 Tuint08 uiOsLocalTimeAverage = (uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2); 01663 #endif 01664 /* The average value however should not exceed the a one byte value */ 01665 uiOsTimeAverage = (Tuint08) (uiOsLocalTimeAverage >> 2); 01666 /* If we have tracing activated, we report the actual timing and the averaged timing. 01667 * when they are close to the border*/ 01668 #if (cfgCheckTrace == cfgTrue) 01669 if (uiOsTimeAverage > (3*cfgSysSubTicksPerFullTick/8)) 01670 { privTrace(traceOsTime); 01671 privTrace(uiOsTime); 01672 privTrace(uiOsTimeAverage); } 01673 #endif 01674 /* If this value exceeds have the tick time, this should be reported. */ 01675 if (uiOsTimeAverage >= (cfgSysSubTicksPerFullTick / 2)) 01676 { /* This is a fatal error since it makes no sense to continue as this issue 01677 * will rise again and again. */ 01678 privShowError((fatOsTickRateTooHigh | errTaskStateNon), callIdSystem, errNoInfo ); } 01679 /* Second, we test if there is enough room for the current time slice, i.e. if the subtick counter will 01680 * not overflow, even if the task runs its full length. The comparison seams awkward, but makes use 01681 * of the overflow of the unsigned integers arithmetic. */ 01682 if (((Tuint08)(uiTimeSlice + uxTickCount.SubByte)) < uiTimeSlice) 01683 { /* if it does not fit report the error, we are in OS space and return automatically. */ 01684 privShowError((errTaskTakesTooLong | errTaskStateCurrent | errOsStateAsIs), callIdSystem, errCurrentTask ); 01685 /* We have a problem, since the task is put in error mode we cannot start it any more, that 01686 * would violate assumptions on running tasks. But we cannot select an other task at this point either. 01687 * The only senseable thing to do seems like launching the idle task, just for one slice. 01688 * Of course we must add the subticks already past */ 01689 uxTickCount.SubByte += portReadAndResetTimer(TimeSliceIdleTime); 01690 /* and start the idle task: */ 01691 privEnterIdle(); } 01692 #endif 01693 /* Here we are done, the next thing is to enter the privEnterTask, in order to run 01694 * the task chosen */ 01695 #endif 01696 } 01697 /* make it so */ 01698 privEnterTask(); } 01699 01700 01701 static void privIncrementTick(void) 01702 { /* This the place where the tick counter is increased. This may be one or more ticks, depending of 01703 * the timing model in use. */ 01704 #if (cfgUseEquidistantTicks == cfgFalse) 01705 /* Only in case we have true time slice model, more than one tick may be needed to add. For every 01706 * tick we fully loop the routine below. This is a little inefficient, but ensures we do not miss 01707 * a tick in the delay/wake section. If we do, we may miss to reactivate a delay task. */ 01708 while (uxTickCount.SubByte >= cfgSysSubTicksPerFullTick) 01709 #endif 01710 { /* In case of true time slicing we use the SubByte to collect all subticks, otherwise there is 01711 * no need */ 01712 #if (cfgUseEquidistantTicks == cfgFalse) 01713 uxTickCount.SubByte -= cfgSysSubTicksPerFullTick; 01714 #endif 01715 /* Every time we arrive here we must increase the tick counter by one. Or we arrived here because 01716 * subbyte overflowed the cfgSysSubTicksPerFullTick or we arrived here because we were send here 01717 * from a tick interrupt. */ 01718 ++uxTickCount.LowByte; 01719 /* Call the tick hook. Please note that at this point the tick counter is incomplete, */ 01720 #if (callAppTick00 == cfgTrue) 01721 appTick00(); 01722 #endif 01723 01724 /* if the low byte overflows ... */ 01725 if (uxTickCount.LowByte == 0 ) 01726 { /* ... Increase the high byte */ 01727 ++uxTickCount.HighByte; 01728 /* every 256 ticks we must call the tick08 hook */ 01729 #if (callAppTick08 == cfgTrue) 01730 appTick08(); 01731 #endif 01732 /* Once ever 256 ticks let us adjust the Os stack watermark */ 01733 #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse) 01734 privCheckOsStackRegion(); 01735 #endif 01736 /* If we have tracing activated we must regularly inform the outside world about 01737 * the status of the watermarks and registeruse. */ 01738 #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue) 01739 if ((uxTickCount.HighByte & 0x03) == 0) { privTraceWatermarks(); } 01740 #endif 01741 /* every 256*256 ticks we must call the appTick16 hook */ 01742 #if (callAppTick16 == cfgTrue) 01743 if (uxTickCount.HighByte == 0) { appTick16(); } 01744 #endif 01745 /* On every cross of the low byte boundary, new tasks may have entered the near wake 01746 * state, thus we must set the corresponding bit. */ 01747 uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStatePresent); 01748 /* If we make use of the watchdog facility */ 01749 #if (cfgUseTaskWatchdog == cfgTrue) 01750 /* we test if we already arrived at a watchdog decrease boundary. This test simple passes when 01751 * the HighByte ends with the number of zeros given by cfgNumWatchdogDiv */ 01752 if ( (uxTickCount.HighByte & (0xFF >> (8 - cfgNumWatchdogDiv))) == 0) 01753 { /* if so, now loop trough all tasks who might have a watchdog */ 01754 Tuint08 uiLoopTask; 01755 for (uiLoopTask=defTaskNumberWatchdogBegin; uiLoopTask<defTaskNumberWatchdogEnd; uiLoopTask++) 01756 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 01757 /* We may only decrease the watchdog state of running tasks, If a task is delayed, suspended 01758 * or otherwise blocked, it can never feed its watchdog so we should not decrease its counter. */ 01759 if (loopTCB->uiTaskStatus >= defBaseRunningTask) 01760 { /* Extract the value of the watchdog counter which can only take four states */ 01761 Tuint08 uiWatchdog = loopTCB->defDogField & defDogGetMask; 01762 /* The value of zero (defDogDead) means we do not use the watchdog, the value one (defDogBark) means 01763 * it is already barking, so it should not be decreased further rendering it inactive,*/ 01764 if (uiWatchdog > defDogBark) 01765 { /* so only on higher values, decrease its value, which may result in a barking watchdog */ 01766 loopTCB->defDogField = (loopTCB->defDogField & defDogSetMask) | (uiWatchdog - defDogDec); } } } } 01767 #endif 01768 /* If we make use of the load monitor facility */ 01769 #if (cfgUseLoadMonitor == cfgTrue) 01770 /* we test if we already arrived at a watchdog monitor boundary. This test simple passes when 01771 * the HighByte ends with the number of zeros given by cfgNumMonitorDiv, we must 01772 * copy all values from the collectors to the totals. */ 01773 if ( (uxTickCount.HighByte & (0xFF >> (8 - cfgNumMonitorDiv))) == 0) { privCopyLoad(); } 01774 #endif 01775 /* This concludes the activity on the high byte (256 ticks) boundary */ 01776 } 01777 /* Let us check the OS stack on this place, just to be sure. */ 01778 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 01779 privCheckOsStackLevel(); 01780 #endif 01781 #if (defUseDelay == cfgTrue) 01782 /* Now we must check if we must wake some delayed tasks, this only is the case when we 01783 * have near wakes. The mechanism of near wakes is made in order not to have to check all 01784 * tasks on every tick */ 01785 if ((uiOsStatus & defNearWakeStateGetMask) == defNearWakeStatePresent) 01786 { Tuint08 uiNearWakeCount = 0; 01787 Tuint08 uiLoopTask; 01788 /* we must find out which tasks are near wake, thus we must check all tasks */ 01789 for (uiLoopTask=defTaskNumberDelayBegin; uiLoopTask<defTaskNumberDelayEnd; uiLoopTask++) 01790 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 01791 /* We check if the task is delayed. Note that it need not to be running. If the task was suspended or 01792 * sleeping while delayed, this delay simply ends, but the task remains suspended/sleeping. The same 01793 * applies for killed tasks. Tasks that are blocked may have a timeout, so this must be checked as well. */ 01794 if ((loopTCB->uiTaskStatus & defBaseDelayStateGetMask) == defBaseDelayStateDelayed) 01795 { /* a task from which the high byte does not much is not near wake */ 01796 if (loopTCB->uxDelay.HighByte == uxTickCount.HighByte) 01797 { /* if it matches, we assume that the task must be waked, since we do not allow so long delays that the 01798 * end time is, after overflow, just before the current time in the same block. (The need for this has 01799 * several reasons, one of which is that this allows us to skip ticks within a tick block. It is needed 01800 * for recovery from low power sleep too. The former facility is not really used right now */ 01801 if (loopTCB->uxDelay.LowByte <= uxTickCount.LowByte) 01802 { /* We have a winner, we may give a wakeup call, but first we check synchronization */ 01803 privWakeupFromDelay(uiLoopTask,loopTCB); } 01804 else 01805 { /* arriving here means the particular task has a wake time in the near future, thus we may 01806 * increase the near wake counter. */ 01807 ++uiNearWakeCount; } } } } 01808 /* If we had no more near wakes we may clear the near wake bit. If we do not do this, it remains set, as 01809 * it was for certain when arriving here. If we clear the bit will be set again when we enter the 01810 * next tick block. */ 01811 if (uiNearWakeCount==0) { uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStateAbsent); } } 01812 #endif 01813 } } 01814 01815 01816 static Tselect privSelectTask(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd) 01817 { Tselect result; 01818 Tuint08 uiLoopTask; 01819 /* Since this is on if the deeper calls, we perform an OS stack check here when timing 01820 * is activted, (without timing activated, it is done inside privTcbList. */ 01821 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 01822 privCheckOsStackLevel(); 01823 #endif 01824 /* Tselect contains of (MaxTask,Prio) MaxTask is variable which will hold the task with the highest 01825 * priority. The Prio is* the important variable. It is set to the highest priority of a non runnable task, 01826 * the default so to say. If it is not increased, we have no runnable task, thus we must run the idle task. 01827 * By using this approach we do not need a real idle task, with stack and so. */ 01828 result.MaxStatus = defBaseRunningTask - 1; 01829 /* Now loop trough all tasks and determine which has the highest priority. Since the 01830 * priority information is just before the bit 0, which determines the run state, all 01831 * tasks with equal priority are issued round robin. Tasks with a status below defRunableTask 01832 * will never be started, they are typically the blocked, delayed suspended, error or 01833 * sleeping tasks. */ 01834 for (uiLoopTask=uiLoopStart; uiLoopTask<uiLoopEnd; uiLoopTask++) 01835 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 01836 Tuint08 uiCompareStatus = loopTCB->uiTaskStatus ^ uiFlipMask; 01837 /* The 'smaller sign' is important here, only if the priority exceeds the smallest possible 01838 * runnable task a replacement must take place. */ 01839 if (result.MaxStatus < uiCompareStatus) 01840 { /* this is the most important task so far */ 01841 result.MaxTask = uiLoopTask; 01842 /* and this is its priority */ 01843 result.MaxStatus = uiCompareStatus; } } 01844 /* Return the result. Note that MaxTask in not initialized when there where not runnable tasks. */ 01845 return result; } 01846 01847 #if (cfgUseHierarchicalRoundRobin == cfgTrue) 01848 static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiPriority, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) 01849 #else 01850 static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) 01851 #endif 01852 { Tuint08 uiLoopTask; 01853 /* In fact we need only to restore the run state of the current priority. It 01854 * is however more work to check the priority, and it does not matter that we 01855 * reset the run states of all lower priorities as well. Since it could matter in 01856 * special cases where one particular lower priority task revives higher priority 01857 * tasks, which, when done, get to the same lower priority task every time, 01858 * thereby skipping the other lower priority tasks. If this is the case choose 01859 * for hierarchical round robin. However, we must take care that only running 01860 * tasks are handled. For the blocking tasks the dressbit is used for other 01861 * purposes. */ 01862 for (uiLoopTask=uiLoopStart; uiLoopTask<uiLoopEnd; uiLoopTask++) 01863 { /* So get the task ... */ 01864 TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 01865 /* Test if may have a blocking of some kind. */ 01866 /* If so make sure the present task is not blocked. */ 01867 if ((bCheckSkip) || ((loopTCB->uiTaskStatus ^ uiFlipMask) >= defBaseNoBlocksTask)) 01868 { /* ... and set the RunState to Runable If we make use of HierarchicalRoundRobin */ 01869 #if (cfgUseHierarchicalRoundRobin == cfgTrue) 01870 /* We may only reset those tasks in the requested priority. (Note we test the shifted priority because it 01871 * is faster on this side and on the callers side.) */ 01872 if ((loopTCB->uiTaskStatus & defBasePrioGetMask) == uiPriority) 01873 #endif 01874 { loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBaseDressSetMask) | defBaseDressRunable; } } } } 01875 01876 01877 static Tuint08 privSwitchContext(void) 01878 { /* We arrive here when we may want to do a context switch. If we do not want to switch, we are 01879 * always running a task, so that is the default return state. Here we fill in in, so we can 01880 * modify it to our needs. */ 01881 Tuint08 uiContextResult = defAssignmentTask; 01882 /* ... determine which task was running */ 01883 #if (cfgUseTaskWatchdog == cfgTrue) || (includeTaskProtectSwitchTasks == cfgTrue) || (includeTaskProtectSwitchCritical == cfgTrue) 01884 Tuint08 uiOldTaskNumber = privTaskNumber(defCurrentTaskNumber); 01885 TtaskControlBlock * oldTCB = privTcbList(uiOldTaskNumber); 01886 #endif 01887 /* DISCUSSION 01888 * Where to put he watchdog check was a matter of considerable inner debate of mine. Is the 01889 * privSwitchContext the right place? In any case, we must not do it after we determined 01890 * what the next task to run will be. We could have done it in the privInitOS, probably it 01891 * results in smaller code over there. On the other side, we want to keep privInitOS as lean as 01892 * possible by itself. We could have done it in privSwitchContext since there is no need 01893 * to check broken tasks after. In any case, a call to privTaskInit eats a lot of stack so 01894 * we want to do it at a place where the stack is fresh. Anyway i decided that this place 01895 * is not that bad. */ 01896 /* If we make use of the watchdog ... */ 01897 #if (cfgUseTaskWatchdog == cfgTrue) 01898 /* check if the watchdog barked (in this way we make sure we only check tasks which 01899 * actually did run, so at least had a chance to feed the watchdog. A blocking task 01900 * therefore should not be barking. Exception is, if the task just called for some 01901 * unavailable slot, and went blocking as last action. Likewise, the task could also be 01902 * delayed as last action (the task normally is started with bark activated as last 01903 * action, since bark is only checked upon tasks that have run.) Furthermore, a 01904 * terminated task can only issue a bark if it was not stuck but running without 01905 * watchdog feeding. This situation is extremely rare, but not impossible. Such a 01906 * task should stay terminated. End of the story, we have to recheck if the task is 01907 * indeed still running. Test if the task is running and if the watchdog barks. */ 01908 if ( (oldTCB->uiTaskStatus >= defBaseRunningTask) && ((oldTCB->defDogField & defDogGetMask) == defDogBark)) 01909 { /* Report that the watchdog is barking. The task number can be deduced from the following TaskInit report. */ 01910 privTrace(traceWatchdog); 01911 /* If so get the pointer to the bark code from flash, and call it, if there is something to call. 01912 * We first call the Bark and reinitialize afterwards in order to process suspend requests, 01913 * from the bark routine. This further has the advantage that the bark routine may inspect 01914 * the malicious task, before it is erased. */ 01915 #if (callAppBark == cfgTrue) 01916 portFlashReadWord(fpBarkTask,pxBarklist[uiOldTaskNumber-defTaskNumberWatchdogBegin])(); 01917 #endif 01918 /* Re-initialize the task, (but keep its priority). The rest of the state will be restored as if 01919 * the task was created for the first time. All hold locks will be released, blocking and delays 01920 * are removed, with out exception. The task could be in block waiting for the burnbock to release. 01921 * We want to keep the status partially (priority etc) and the watermarks. Shared tasks are put 01922 * back in the pool of waiting shared tasks. */ 01923 privTaskInit(uiOldTaskNumber,(defInitContextRenew | defInitStatusCopyDont | defInitSharedPassive | defInitStatusPrioKeep | defInitLockRelease | defInitProcessAll)); 01924 /* In case we check timing we must inform the caller that we may have spend considerable time in 01925 * the watchdog routine. Since this is a one time event (or should be rare at least, is is better 01926 * if it not triggers an error. */ 01927 #if (cfgCheckTiming == cfgTrue) 01928 uiContextResult |= defAssignmentWatchdog; 01929 #endif 01930 } 01931 #endif 01932 /* ... its state to done. If we previously run the idle task, the old task is already 01933 * done, but that does not matter. Since the dress runnable bit has only the runnable meaning 01934 * when the state is indeed not blocked (running, may be delayed) we may only set it to 01935 * done in that case. In blocking situations the bit is used for different purposes. 01936 * So there tasks must be skipped. We can save some bytes if there is no possibility for 01937 * blocking states. */ 01938 /* TODO v0.90 Calls on the heap may block also */ 01939 /* If we have shared tasks in our midst, ... */ 01940 #if (defUseSharedStack == cfgTrue) 01941 Tselect uiShareSelect; 01942 /* ... we must check if one of them is actually running. */ 01943 if ((uiOsStatus & defShareStateGetMask) == defShareStateAbsent) 01944 { /* If this is not the case, we must select a new task from the shared tasks pool. This 01945 * selection is based on round robin among all shared tasks, where we always select 01946 * the one with the highest priority. This selection equals the regular task selection 01947 * scheme, with only the access bit being different. (The bit being zero on shared 01948 * tasks. By flipping this bit in the selection we get the correct shared task.*/ 01949 uiShareSelect = privSelectTask((defBaseSharedGetMask & ~defBaseSharedTask),defTaskNumberSharedBegin,defTaskNumberSharedEnd); 01950 /* Now, we must see if there is indeed one that can be scheduled. Note that shared 01951 * tasks can be delayed as regular tasks, and they are not selected as long as this 01952 * delay is not over. The MaxStatus contains the states of the selected task with (!) 01953 * the flipped bit. So it looks like a stated that is able not blocked and not delayed. 01954 * Therefore compare with the highest state that could not run, if there are unequal, 01955 * we have a truly runnable state. */ 01956 if (uiShareSelect.MaxStatus != (defBaseRunningTask - 1)) 01957 { /* OK, among the shared tasks we have a winner! This task will be promoted to running 01958 * later on, get the control block of that task. */ 01959 TtaskControlBlock * shareTCB = privTcbList(uiShareSelect.MaxTask); 01960 /* But it could be that all shared tasks have completed their round robin, We have 01961 * to test this and that can be easily seen from the dressing. Only if all shared 01962 * states are defBaseDressDone, one of them get selected. */ 01963 if ((shareTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressDone) 01964 { /* In fact we need only to restore the run state of the shared states of the current 01965 * priority. We do so if we have cfgUseHierarchicalRoundRobin activated, mostly however 01966 * it does not matter much, so e save the code. See also the discussion below. 01967 * We do have to take care that we only reset shared states however */ 01968 #if (cfgUseHierarchicalRoundRobin == cfgTrue) 01969 privMakeTasksRunable((defBaseSharedGetMask & ~defBaseSharedTask),(shareTCB->uiTaskStatus & defBasePrioGetMask),defTaskNumberSharedBegin,defTaskNumberSharedEnd,(defDenseSharedStack == cfgTrue)); 01970 #else 01971 privMakeTasksRunable((defBaseSharedGetMask & ~defBaseSharedTask),defTaskNumberSharedBegin,defTaskNumberSharedEnd,(defDenseSharedStack == cfgTrue)); 01972 #endif 01973 } 01974 /* Ok, now we must initialize the context for this new state, keeping everything else as is. The 01975 * initialization in the function taskes also case of activation of the shared state in the uiOsStatus */ 01976 privTaskInit(uiShareSelect.MaxTask, (defRestartRunning | defInitContextRenew | defInitStatusCopyDo | defInitSharedActive | defInitStatusPrioKeep | defInitLockKeep | defInitProcessAll)); } } 01977 #endif 01978 /* When all tasks are shared the dominat mode makes no sense. */ 01979 #if ((includeTaskProtectSwitchTasks == cfgTrue) || (includeTaskProtectSwitchCritical == cfgTrue)) && (defAllSharedStack == cfgFalse) 01980 /* Only if the task has been 'done' we may switch to a new task. Otherwise, we are 01981 * still running this task. Note that all tasks are set to 'done' even before after start 01982 * so there must be a special situation when this has been changed to 'runnable'. Tasks that are 01983 * blocked, sleeping or cannot be continued in any way loose their switch block. Delayed tasks 01984 * may keep it, but the idle state will be called (technically this is not a switch). So first 01985 * test if we have indeed a state that does not block and is runnable*/ 01986 if ( (oldTCB->uiTaskStatus & (defBaseNoBlocksGetMask | defBaseDressGetMask)) == (defBaseNoBlocksTask | defBaseDressRunable) ) 01987 { /* If so, see if it is delayed */ 01988 if ((oldTCB->uiTaskStatus & defBaseDelayStateGetMask) == defBaseDelayStateDelayed) 01989 { /* delayed states go idle, and will persist to do so until the wake up. */ 01990 uiContextResult |= defAssignmentIdle; } } 01991 else 01992 #endif 01993 { /* After we have handle the shared tasks we must see if there are only shared tasks. In that case 01994 * we do not need to do an other selection, and know which task is selected, namely the only runable 01995 * one. */ 01996 #if (defAllSharedStack == cfgTrue) 01997 /* The task to be executed is the one selected by the shares. */ 01998 Tselect uiExecute = uiShareSelect; 01999 #else 02000 /* If there are also standard tasks, we must make a new selection about which task to run. */ 02001 Tselect uiExecute = privSelectTask(0x00,0,defNumberOfTasks); 02002 #endif 02003 if (uiExecute.MaxStatus == (defBaseRunningTask - 1)) 02004 { /* DISCUSSION 02005 * If we arrive here, all the tasks are terminated, suspended, sleeping, blocked or delayed. 02006 * (Thus, you cannot arrive here if there are running tasks, this is important for a correct 02007 * understanding of the decisions made below.) 02008 * The first situation can never recover, the next three cannot recover on their own, thus 02009 * the delayed tasks are their only hope. Thus we may go to sleep directly, or, when the 02010 * the minimum delay is long enough, for that time at least. Interrupts can revive 02011 * locked tasks may do so through the sleep anyway. Note that we disallowed sleep in case 02012 * of any write-file system activity or coming activity. 02013 * So see if we allow for low power sleep. If there are no tasks we cannot decide to go to sleep 02014 * by looking at the tasks. In fact, we should not sleep, maybe the idle loop has something 02015 * sensible to do, otherwise we just as well switch off the power. */ 02016 #if (cfgUseLowPowerSleep == cfgTrue) && (defNumberOfTasks > 0) 02017 Tuint08 uiLoopTask; 02018 /* Set the minimum delay to 255 block ticks, this is the largest value per default. */ 02019 Tuint08 uiMinDelay = defSleepMaximum; 02020 /* If there is any fs activity we do not need to test any further, so sort this out. If any 02021 * writing is going on, we may not fall asleep. Reading is permitted (read tasks cannot block 02022 * because of reading solely, only in combination of an other task writing or an other synchronization 02023 * primitive). Since we know there are no running tasks when arriving here, we only need to check 02024 * if there is not write block present.*/ 02025 #if (cfgUseFileSystem == cfgTrue) 02026 /* If the file system is in write block, there are tasks writing to the FS and we cannot put the 02027 * system into a sleep mode. If there is a read block, this is no problem by itself. 02028 * If we have a burn block, there must be a write block too, so we do not need to check 02029 * for burning separately.*/ 02030 if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockActive) 02031 { /* Setting uiLoopTask to zero blocks failing to sleep in the test below. */ 02032 uiLoopTask = 0; } 02033 else 02034 #endif 02035 { /* Now loop through all tasks to test if a sleep is justified. */ 02036 for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++) 02037 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 02038 #if (cfgUseFileSystem == cfgTrue) 02039 /* Test first if we do not have a FS block, if so, no sleep. For we must first handle any 02040 * task that is waiting for an opportunity to start writing. Task blocking for reading 02041 * operations should not hinder falling asleep. However, there cannot be a situation where 02042 * all tasks are solely blocked on reading. One read block implies an other task blocked 02043 * on writing. And even if that where possible we should not go to sleep since we cannot 02044 * store the blocked state. Tasks actually doing reading (but blocked or delayed) are 02045 * NOT FileBlocked, thus pass here undetected. When some tasks are actually writing 02046 * we never arrive here. End of the story is that any file block should hinder sleep. 02047 * The defBaseDressGetMask is not important. */ 02048 if ( (loopTCB->uiTaskStatus & defBaseFileBlockedGetMask) == defBaseFileBlockedTask ) { break; } 02049 #endif 02050 /* We can only arrive here if all tasks (until now) are terminated, suspended, sleeping of syncblocked. 02051 * (fs blocks are already filtered out). The task could be delayed or syncblocked, the latter with or 02052 * without delay. We cannot reach this place if there is any task that is able to run. 02053 * Here three situations are important. No delay, delay with UseLowPowerOnDelay or delay without UseLowPowerOnDelay. 02054 * First if delayed tasks are not possible then, there is nothing left to do. If we come here, this task 02055 * cannot hinder going to sleep anymore. 02056 * In the second situation, where we allow a sleep on (long) delays, we must determine the shortest delay among 02057 * all, where we only take tasks into account that will be reviving without the help from outside, i.e. 02058 * standard delayed tasks, or blocked delayed tasks (not on FS). 02059 * Third, if we do not allow a sleep on (long) delays, we must determine if we really do not have any delayed 02060 * tasks, thus check if there is a delay task, if so, we may not go to sleep. If there is no delayed 02061 * task, we could only arrive here (idle) if ALL tasks are stopped, suspended, sleeping of syncblocked. 02062 * Only an external event can revive any of them. We may go to sleep. 02063 * Note that it does matter if blocked state is delayed, since a delayed blocked state may revive 02064 * earlier, but only with help from an other task or from the outside. But it will certainly be revived 02065 * when its timer expires. We thus must treat is as if it is a normal delayed task. 02066 * It must however not be a stopped delayed task, since even if the timer expires it will never restart. */ 02067 #if (cfgUseDelay == cfgTrue) 02068 /* Test if we are in a delayed situation, that is not terminated or sleeping (fs is not possible any more) 02069 * but may be syncblocked. */ 02070 if ( (loopTCB->uiTaskStatus & (defBaseStopStateGetMask | defBaseModeGetMask | defBaseDelayStateGetMask)) == ( defBaseStopStateGo | defBaseModeSync | defBaseDelayStateDelayed ) ) 02071 #if (cfgUseLowPowerOnDelay == cfgTrue) 02072 { /* Second situation, we must determine the delay. We do not care for the low byte, this 256 ticks vanish in 02073 * the rounding. One tick block must at least be the delay. The calculation is correct, even if 02074 * loopTCB->uxDelay.HighByte < uxTickCount.HighByte do to the limited size of the type. */ 02075 Tuint08 uiDelayTime = loopTCB->uxDelay.HighByte - uxTickCount.HighByte; 02076 /* If the high bytes are equal, there will be no delay, in fact that task will be waked 02077 * [Is this possible at all, it seems not so, cause these tasks should have been waked earlier on ] 02078 * Delays as long as 0xFF00 or more are forbidden. */ 02079 if (uiDelayTime == 0 ) { break; } 02080 /* The time the device may be put to sleep is of course one tick block less otherwise, we could 02081 * be to late for the task to wake, thus we subtract one. */ 02082 uiDelayTime--; 02083 /* We have an option which specifies a lower bound on the sleep time. Sleep times below that 02084 * are not thought to be useful, thus if we find such a time, we might as well wait here */ 02085 if (uiDelayTime < defSleepThreshold ) { break; } 02086 /* We want to keep track of the smallest possible sleep time so far, cause that will be reported 02087 * to the portSleep method. */ 02088 if (uiMinDelay>uiDelayTime) { uiMinDelay = uiDelayTime; } } 02089 #else 02090 { /* Third situation, a present delay must prohibit going to sleep. */ 02091 break; } 02092 #endif 02093 #endif 02094 } 02095 } 02096 /* No we must see if we did not break out of the loop. If we did not ... */ 02097 if (uiLoopTask == defNumberOfTasks) 02098 { /* ... we indeed may go to sleep, the the caller so */ 02099 uiContextResult |= defAssignmentSleep; 02100 /* and let the portSleep method know what the longest possible sleep time is, for 02101 * which no tasks will be missed (note the you may sleep longer if you want). The highest 02102 * value that can emerge from a delay is 0xFE (from 0xFExx) so that we know that a value of 02103 * defSleepMaximum (0xFF) must mean that no delay was present at all. 02104 * Now we can use the subbyte to pass this information, since if we are going to sleep, the 02105 * information will be useless anyway. The tick counter can be corrected manually, but 02106 * we cannot expect it to be accurate up to the subtick */ 02107 uxTickCount.SubByte = uiMinDelay; } 02108 else 02109 #endif 02110 { /* If you arrive here, then or you did not allow for a sleep mode, or the conditions 02111 * to go go low power sleep are just not met. This implies running the idle task, 02112 * we are done, let the caller know. */ 02113 uiContextResult |= defAssignmentIdle; } } 02114 else 02115 { /* If you arrive here, then there is a task to be run. There are two possibilities. If all tasks 02116 * run in the shared mode, then there can only one task be active, so we do not need to select any 02117 * more, the only task must be the one, otherwise the task was selected in the selection process 02118 * thereafter. */ 02119 TtaskControlBlock * runTCB = privTcbList(uiExecute.MaxTask); 02120 /* So test if we only have shared tasks. */ 02121 #if (defAllSharedStack == cfgFalse) 02122 /* If not, it could be that all tasks in the current priority have already run, 02123 * so that we selected a task in the state defRunStateDone. If this is the case 02124 * then we know vice versa that all tasks in that priority have that state, 02125 * for otherwise it would have been selected due to the higher value of the status byte. */ 02126 if ((runTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressDone) 02127 { /* In fact we need only to restore the run state of the shared states of the current 02128 * priority. We do so if we have cfgUseHierarchicalRoundRobin activated, mostly however 02129 * it does not matter much, so we save the code. 02130 * However, we must take care that only running tasks are handled. 02131 * For the blocking tasks the dressbit is used for other purposes. This is all handled 02132 * within the function privMakeTasksRunable. */ 02133 #if (cfgUseHierarchicalRoundRobin == cfgTrue) 02134 privMakeTasksRunable(0x00,(runTCB->uiTaskStatus & defBasePrioGetMask),0,defNumberOfTasks,((cfgUseSynchronization == cfgSyncNon) && (cfgUseFileSystem == cfgFalse) && (defUseSharedStack == cfgFalse))); 02135 #else 02136 privMakeTasksRunable(0x00,0,defNumberOfTasks,((cfgUseSynchronization == cfgSyncNon) && (cfgUseFileSystem == cfgFalse) && (defUseSharedStack == cfgFalse))); 02137 #endif 02138 } 02139 #endif 02140 /* The state immediately needs to be set to the done state, to prohibit indefinite running, since, 02141 * when the dress bit is set this indicates a dominant mode. */ 02142 runTCB->uiTaskStatus = (runTCB->uiTaskStatus & defBaseDressSetMask) | defBaseDressDone; 02143 /* Having selected a new state to run, let us put in the status register. From 02144 * now on, that is the current task. */ 02145 uiOsStatus = (uiOsStatus & defTaskNumberSetMask) | (uiExecute.MaxTask << defOsTaskNumberShift); } } 02146 /* Tell the caller the result, which may be one of: defAssignmentIdle or defAssignmentSleep or 02147 * (and that was the default defAssignmentTask) */ 02148 return uiContextResult; } 02149 02150 02151 #if (cfgUseLowPowerSleep == cfgTrue) 02152 02153 static void privEnterSleep(Tuint08 uiTickMinDelay) 02154 { /* If you arrive here, you obviously wanted the device to go to sleep. 02155 * Here we make the last preparations before we can do so. 02156 * The OS stack is not relevant anymore, so to allow for maximal 02157 * depth on in the portSleep function, we want to reset it. This 02158 * however cannot be done with interrupts activated, since that 02159 * would open the possibility of an interrupt on an incomplete 02160 * stack. Thus the first thing to do is to deactivate the interrupts, 02161 * which is only necessary if they are active at all */ 02162 #if (cfgIntOsProtected == cfgFalse) 02163 /* We want timer interrupts 02164 * disabled during sleep. The latter is the default situation, thus we do not 02165 * need to change anything to the tick interrupts. 02166 * If we come here, global interrupts where enabled (no OS protection) thus 02167 * we must disable them explicitly. Timer interrupts where already disabled in 02168 * the privInitOs section, thus there is no need to repeat that. */ 02169 privDisableGlobalInterrupts(); 02170 #endif 02171 /* Let the system know we are about to enter the sleep mode, this information 02172 * is needed when we wakeup and restore the system state, since we always enter 02173 * the OS with privInitOS */ 02174 uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateSleep); 02175 /* report we are going to bed */ 02176 privTrace(traceSleepStart); 02177 /* Now we may reset the stack, no fear for interrupts. */ 02178 privSetStack(&xOS.StackOS[OSstackInit]); 02179 /* Before we go to sleep, there may be some work to do, if a hook is installed */ 02180 #if (callAppEnterSleep == cfgTrue) 02181 appEnterSleep(); 02182 #endif 02183 /* If we wake from sleep, we want to be sure there is no SleepRequest left. We know that we could 02184 * never arrive here when we where writing, but it could be that the system is in read only mode. 02185 * This must be preserved. */ 02186 #if (cfgUseFileSystem == cfgTrue) 02187 uiFsStatus = (uiFsStatus & defFsSleepRequestSetMask) | defFsSleepRequestClear; 02188 #endif 02189 /* This is where it happens. Call (not jump to!) portSleep. This method 02190 * returns when the sleep is done. Note we enter the portSleep method with 02191 * global interrupts disabled, and we should return that way. In between the 02192 * interrupts may be enabled when needed. */ 02193 portSleep(uiTickMinDelay); 02194 /* After we have slept, there may be some work to do, if a hook is installed */ 02195 #if (callAppEnterSleep == cfgTrue) 02196 appExitSleep(); 02197 #endif 02198 /* If we return, make the jump here */ 02199 portJump(privTickYield); } 02200 02201 #endif 02202 02203 02204 static void privEnterIdle(void) 02205 { /* Arriving here, we want to put the device in idle mode. 02206 * Here we make the last preparations before we can do so. 02207 * The OS stack is not relevant anymore, so to allow for maximal 02208 * depth on in the portIdle function, we want to reset it. This 02209 * however cannot be done with interrupts activated, since that 02210 * would open the possibility of an interrupt on an incomplete 02211 * stack. Thus the first thing to do is to deactivate the interrupts, 02212 * which is only necessary if they are activated at all. 02213 * If we have OS protection, the interrupts are already off, and\ 02214 * the timer interrupts are permanently on. If not, we must take 02215 * care of that. */ 02216 #if (cfgIntOsProtected == cfgFalse) 02217 privDisableGlobalInterrupts(); 02218 #endif 02219 /* Timer interrupts are switch on (or must be on) in the idle mode 02220 * because we must return from idle due to a timer interrupt. 02221 * however, that is the responsibility of the portIdle implementation 02222 * Notify the OS that we enter the idle state now. This is needed at 02223 * return, cause we don't need to save any context then. */ 02224 uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateIdle); 02225 /* Say that we are ready to start the idle task. */ 02226 privTrace(traceIdleStart); 02227 /* Reset the stack, so we maximal space in portIdle. */ 02228 privSetStack(&xOS.StackOS[OSstackInit]); 02229 /* Interrupts on of course, otherwise the timer interrupt cannot work, and 02230 * we are ready with the stack switch. */ 02231 privEnableGlobalInterrupts(); 02232 /* Before we enter the idle state the there may be some work to do */ 02233 #if (callAppEnterIdle == cfgTrue) 02234 appEnterIdle(); 02235 #endif 02236 /* Done, we goto (not call!) the portIdle. Thus, we do not return here. ;-) */ 02237 portJump(portIdle); } 02238 02239 02240 #if (defNumberOfTasks == 0) 02241 02242 /* Use an empty method test purposes if there are no tasks defined, we should never come here. */ 02243 static void privEnterTask(void) { while(true); } 02244 02245 #else 02246 02247 static void privEnterTask(void) 02248 { /* Arrive here when a task need to be executed. Here we make the last preparations before 02249 * we can do so. The OS stack must be changed to the task stack. This however cannot be 02250 * done with interrupts activated, since that would open the possibility of an interrupt 02251 * on an incomplete stack. Thus the first thing to do is to deactivate the interrupts, 02252 * which is only necessary if they are activated at all. 02253 * Timer interrupts are switch on (or must be on) in the task mode. This however is done 02254 * inside the portRestore method for its state is dependent on the previous interrupt 02255 * state of the task. Therefore, we keep (default situation) tick interrupts disabled 02256 * here. If we have OS protection, the interrupts are already off. If not, we must take 02257 * care of that. */ 02258 /* Notify the OS that we enter a task state now. This is needed at 02259 * return, cause we must save the context then. */ 02260 uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateTask); 02261 /* Get the number of the task we are going to run (a call to privTaskNumber takes more bytes) */ 02262 Tuint08 uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift; 02263 /* Get the task control block of the current task also */ 02264 TtaskControlBlock * curTCB = privTcbList(uiTaskNumber); 02265 /* Say that we are ready to start the task, but we do this before we switch to the 02266 * task itself since we do not know if the privTrace action fits on the task stack. */ 02267 privTrace(traceTaskStart | uiTaskNumber); 02268 /* If we make use of register compression with constant registers, (remember the facility that saves 02269 * stack space by saving only those registers you use on the context), we must determine which 02270 * registers must be used. We need that information to calculate the space these will take 02271 * on the stack. */ 02272 #if (defRegisterUseConstant == cfgTrue) 02273 /* ... we can use the constant value */ 02274 Tuint08 uiRegisterUse = defRegisterUseCollect; 02275 #else 02276 /* otherwise we must extract it from flash. */ 02277 Tuint08 uiRegisterUse = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint16,uiRegisterUse); 02278 #endif 02279 /* We must determine the number of registers used by this task, in oder to calculate the 02280 * new stack level. Since that may require a function call, we perform that operation first.*/ 02281 #if (cfgCheckTaskStack == cfgTrue) 02282 #if (defRegisterUseConstant == cfgTrue) 02283 /* The constant defRegisterCount holds the number of registers used.*/ 02284 Tuint08 uiRegCount = defRegisterCount; 02285 #else 02286 /* We have to calculate the number dynamically */ 02287 Tuint08 uiRegCount = privRegisterCount(uiRegisterUse); 02288 #endif 02289 #endif 02290 /* Disable interrupts here, for interrupts make use of the stack, at least for the call. But this 02291 * is not allowed any more since we are about to fill the background variables.*/ 02292 #if (cfgIntOsProtected == cfgFalse) 02293 privDisableGlobalInterrupts(); 02294 #endif 02295 /* Make sure all calls are handled, for after this instruction we start filling the background 02296 * variables. The stack should not be used any more. I am not 100% sure this is sufficient, but 02297 * i think i have no other means at my disposal other than assembly, which we cannot use here. */ 02298 portBarrier(); 02299 /* If we make use of register compression with constant registers, we now fill the background 02300 * variable with the correct value. */ 02301 xOS.pxSave.uiRegisterUse = uiRegisterUse; 02302 /* If we check the use of registers we may not want to check all registers, even if they are used. Here we may 02303 * want to check for all tasks the same registers so ...*/ 02304 #if (cfgCheckRegisters == cfgTrue) 02305 #if (defRegisterCheckConstant == cfgTrue) 02306 /* ... we can use the constant value */ 02307 xOS.pxSave.uiRegisterCheck = defRegisterCheckCollect; 02308 #else 02309 /* otherwise we must extract it from flash. */ 02310 xOS.pxSave.uiRegisterCheck = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint16,uiRegisterCheck); 02311 #endif 02312 #elif (cfgCheckWatermarks == cfgTrue) 02313 /* If we collect watermarks and do not check the registers, we want to collect this 02314 * information from all registers, thus */ 02315 xOS.pxSave.uiRegisterCheck = (registersAll); 02316 #endif 02317 /* If we do some checks on the use of the task stack (which are done on portSaveContext) 02318 * we prepare that information here, and put it in the background variables */ 02319 #if (cfgCheckTaskStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02320 /* Stack size is needed on two occasions */ 02321 #if (defStackSizeConstant == cfgTrue) 02322 Tstack uiStackSize = defStackSizeFixed; 02323 #else 02324 Tstack uiStackSize = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tstack,uiStackSize); 02325 #endif 02326 #endif 02327 /* At saveContext we need to know where the stack starts, but since getting this info 02328 * may overflow the stack at that time, we get it here and save it on the OS stack which 02329 * is used as background storage for the task. */ 02330 xOS.pxSave.pcStackOffset = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Taddress,pcStackOffset); 02331 /* Some methods return a boolean to the application code. This data is put in the 02332 * correct register by the restoreContext. At this point we prepare it */ 02333 #if (cfgUseSynchronization != cfgSyncNon) && (defUseBoolReturns == cfgTrue) 02334 /* Put that information in the background variables */ 02335 xOS.pxSave.uiReturn = curTCB->defRetField & defRetGetMask ; 02336 #endif 02337 #if (cfgCheckTaskStack == cfgTrue) 02338 /* Calculate the limit for the stack size. We do that calculation also here, so that, 02339 * in the portSaveContext, we can do a simple compare. */ 02340 #if (cfgSysStackGrowthUp == cfgTrue) 02341 /* TODO: cfgSysStackGrowthUp oops, add code */ 02342 xOS.pxSave.pcStackLimit = 0; 02343 #else 02344 /* Note that the limit is calculated taking StackSavety into account. Thus, it is an error 02345 * to pass this limit, although the stack is not yet corrupted at that point. This is 02346 * intentional, so that we can stop the task rather than experiencing bizarre results, 02347 * when experimenting with the stack size. The +1 represents the status register.*/ 02348 xOS.pxSave.pcStackLimit = uiRegCount+xOS.pxSave.pcStackOffset+StackSafety-uiStackSize+1; 02349 #endif 02350 #endif 02351 /* Here we will scan the stack for bytes that differ from the initial bye. This indicates the use of 02352 * the stack also on places where no interrupt is possible (at the context the stack is used heavily, 02353 * but it may not give the absolute top if tick interrupts are disabled during deep calls. */ 02354 #if (cfgCheckWatermarks == cfgTrue) 02355 #if (cfgSysStackGrowthUp == cfgTrue) 02356 /* I don't know, probably the offset point to the first writable byte*/ 02357 Taddress pStack = xOS.pxSave.pcStackOffset + uiStackSize - 1; 02358 /* We walk downwards */ 02359 while ( (*(pStack--) == defStackInitByte) && (uiStackSize--)); 02360 #else 02361 /* The watermark measurement starts from the top of the stack (first byte to be read), 02362 * which is below the stack offset */ 02363 Taddress pStack = xOS.pxSave.pcStackOffset - uiStackSize + 1; 02364 /* the stack is write, post decrement, thus, when we walk downwards, we must read, pre-increment. 02365 * We walk maximally the stack size downwards. If we encounter a value which differs from the 02366 * initial value, we can stop. */ 02367 while ( (*(pStack++) == defStackInitByte) && (uiStackSize--)); 02368 /* The level of bytes used is at least the number of bytes below the boundary found. 02369 * We assume the stack has not overflown, first it may produce erroneous results in 02370 * the calculation, and second, watermarking is a technique to detect how much stack space is 02371 * left, not to detect stack overflow.*/ 02372 #endif 02373 /* If we happen to find a higher level with this technique opposite to the level found 02374 * due to context save, there must have been a situation of heavy stack use inside a tick 02375 * protected region of the code. Good that we noticed it! */ 02376 if ((curTCB->uiStackMax)<(uiStackSize)) { curTCB->uiStackMax = uiStackSize; } 02377 #endif 02378 /* Recall the address stack level, and put it in the background variables. Thus this 02379 * variable gets accessible from the portSave/portContext routines. */ 02380 xOS.pxSave.pcStackLevel = (Taddress) &(curTCB->pcStackLevel); 02381 /* Now, switch to the task stack */ 02382 #if (cfgSysStackGrowthUp == cfgTrue) 02383 privSetStack( (Taddress) ((Tuint16) xOS.pxSave.pcStackOffset + (Tuint16) curTCB->pcStackLevel) ); 02384 #else 02385 privSetStack( (Taddress) ((Tuint16) xOS.pxSave.pcStackOffset - (Tuint16) curTCB->pcStackLevel) ); 02386 #endif 02387 /* We need not to activate the interrupts right here, since that is done in the 02388 * portRestoreContext at the end by the 'reti'. Jump to the routine that restores 02389 * the context for the task at hand. */ 02390 portJump(portRestoreContext); } 02391 02392 #endif 02393 02394 02395 #if (defUseDelay == cfgTrue) 02396 02397 static void privWakeupFromDelay(Tuint08 uiTaskNumber, TtaskControlBlock * taskTCB) 02398 { /* Call this if you want to wake up a task due to expiration of the delay timer. 02399 * That can be or a simple delay, or a task that has a time out on a block. 02400 * taskTCB must be the tcb that corresponds to the uiTaskNumber. */ 02401 #if ((cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue)) && (cfgUseTimeout == cfgTrue) 02402 /* If the task was in blocked state, it may be released for its timeout has passed. Note that 02403 * blocked tasks without a timeout are not delayed, so they never arrive here. The same holds for terminated 02404 * tasks, so unblocking a terminated task cannot result in a suspended task. Sleeping tasks may still be unblocked. 02405 * Resuming, this are the possibilities 02406 * (1) Terminated task: never delayed, should not arrive here. 02407 * (2) Suspended task: never blocked, cannot pass the test below. 02408 * (3) Sleeping, may be blocked, is always syncblocked, thus may timeout to free-sleeping 02409 * (4) FS block: may deblock to free, we must clear the FsFields 02410 * (5) Sync block: may deblock to free, we must clear the blocking slots 02411 * (6) Simple delay, just clear the delay. 02412 * (7) Shared task, may be scheduled for execution, just clear the delay 02413 * */ 02414 if ((taskTCB->uiTaskStatus & defBaseBlockStateGetMask) == defBaseBlockStateBlocked) 02415 { /* We can savely assume we only enter this part of the code if the present task indeed 02416 * has a slotstack. If not, the task could never be blocked. so we dont check. Since 02417 * privRestoreInitialPriority and privUnblockTask assume such uiTaskSlot is present, 02418 * they may crash if the fields do not exist. */ 02419 /* If we have priority lifting .. */ 02420 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue) 02421 /* ... and if we release, its priority may be changed (we don't know for sure) in any case 02422 * it must be restored. For the moment however we only support priority lifting on sync blocked 02423 * tasks. Thus we must test if we indeed have syncblocking. We included sleeping in this task 02424 * for all sleeping tasks must come from syncblocks.*/ 02425 if ((taskTCB->uiTaskStatus & (defBaseModeGetMask | defBaseDressGetMask)) == (defBaseModeSync | defBaseDressSlot)) 02426 { privRestoreInitialPriority(uiTaskNumber); } 02427 #endif 02428 /* Release the task by unblocking and unlocking and indicate that it was release by a timeout 02429 * so the requested lock was not obtained (return false) There is no need to check if other 02430 * tasks must be released since we remove a blocking task, which cannot have consequences 02431 * for other blocked/waiting tasks.*/ 02432 privUnblockTask(uiTaskNumber | defParaLockStateUnlock | defParaRetStateFalse); } 02433 /* The unblocking above also wakes the task so we may skip it here. */ 02434 else 02435 #endif 02436 /* wake the task */ 02437 { taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & defBaseDelayStateSetMask) | defBaseDelayStateWake; } } 02438 02439 #endif 02440 02441 02442 #if (cfgUseLoadMonitor == cfgTrue) 02443 02444 static void privCopyLoad(void) 02445 { /* Arrive here if we want to monitor the load of all tasks, the Os and the Isr. Every now 02446 * and then we copy the collected subticks to the totals and clear the collectors. Since 02447 * this routine is not interrupted, we get an adequate picture of the loads. */ 02448 #if (cfgCheckTrace == cfgTrue) 02449 /* If we have tracing, first sent to the trace channel load info is comming */ 02450 privTrace(traceLoadInfo); 02451 /* then send the number of bytes to follow. */ 02452 privTrace(2*(defNumberOfTasks+3)); 02453 #endif 02454 /* Copy the OS load information into the register total. */ 02455 uiOsLoadTotal = uiOsLoadCollect; 02456 #if (cfgCheckTrace == cfgTrue) 02457 /* Send the load of the OS in big endian. */ 02458 privTrace((Tbyte)(uiOsLoadTotal>>8)); 02459 privTrace((Tbyte)(uiOsLoadTotal)); 02460 #endif 02461 /* Reset the collecting register. */ 02462 uiOsLoadCollect = 0; 02463 /* Copy the idle load information into the register total. */ 02464 uiIdleLoadTotal = uiIdleLoadCollect; 02465 #if (cfgCheckTrace == cfgTrue) 02466 /* Send the load of the idle 'task' in big endian. */ 02467 privTrace((Tbyte)(uiIdleLoadTotal>>8)); 02468 privTrace((Tbyte)(uiIdleLoadTotal)); 02469 #endif 02470 /* Reset the collecting register. */ 02471 uiIdleLoadCollect = 0; 02472 /* Only if non empty interrupts we need to copy the isr load. Otherwise, these variables 02473 * are absent. */ 02474 #if (cfgIntUserDefined == cfgTrue) 02475 /* Copy the ISR load information into the register total. */ 02476 uiIsrLoadTotal = uiIsrLoadCollect; 02477 #if (cfgCheckTrace == cfgTrue) 02478 /* Send the load of the ISR in big endian. */ 02479 privTrace((Tbyte)(uiIsrLoadTotal>>8)); 02480 privTrace((Tbyte)(uiIsrLoadTotal)); 02481 #endif 02482 /* Reset the collecting register. */ 02483 uiIsrLoadCollect = 0; 02484 #else 02485 #if (cfgCheckTrace == cfgTrue) 02486 /* We even send if we have no ISR whatsoever, since we want to emit a fixed number of bytes.*/ 02487 privTrace(0); 02488 privTrace(0); 02489 #endif 02490 #endif 02491 /* Depending on the number of tasks, we move the loads, and send the info. */ 02492 Tuint08 uiLoopTask; 02493 for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++) 02494 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 02495 /* Copy the load information of this task into the register total. */ 02496 loopTCB->uiLoadTotal = loopTCB->uiLoadCollect; 02497 #if (cfgCheckTrace == cfgTrue) 02498 /* Send the load of the task in big endian. */ 02499 privTrace((Tbyte)(loopTCB->uiLoadTotal>>8)); 02500 privTrace((Tbyte)(loopTCB->uiLoadTotal)); 02501 #endif 02502 /* Reset the collecting register. */ 02503 loopTCB->uiLoadCollect = 0; } } 02504 02505 #endif 02506 02507 02508 #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue) 02509 02510 static void privTraceWatermarks(void) 02511 { /* Arrive here too send the water mark and register use information to tracing. 02512 * First send the byte indicating that Watermarks are coming, then send the 02513 * number of bytes to follow. */ 02514 privTrace(traceWatermarks); 02515 privTrace(3*defNumberOfTasks+3); 02516 /* Send the watermark info of the OS. */ 02517 privTrace(uiOsStackMax); 02518 /* See if we are using an isr */ 02519 #if (defUseIsrStack == cfgTrue) 02520 /* Send the watermark of the isr stack, and depending on the size we must send 02521 * an extra zero. */ 02522 #if ((StackSizeISR) > 0xFF) 02523 privTrace((Tbyte)(uiIsrStackMax>>8)); 02524 #else 02525 privTrace(0); 02526 #endif 02527 privTrace((Tbyte)(uiIsrStackMax)); 02528 #else 02529 /* If not send zero's */ 02530 privTrace(0); 02531 privTrace(0); 02532 #endif 02533 /* Depending on the number of tasks, we move the loads. */ 02534 Tuint08 uiLoopTask; 02535 for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++) 02536 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 02537 /* Depending on the size of the Stack we must send a zero byte or extract the first byte */ 02538 #if (defStackSizeExceedsOneByte == cfgTrue) 02539 privTrace((Tbyte)(loopTCB->uiStackMax>>8)); 02540 #else 02541 privTrace(0); 02542 #endif 02543 privTrace((Tbyte)(loopTCB->uiStackMax)); 02544 /* Send the use of the registers. */ 02545 privTrace((Tbyte)(loopTCB->uiRegisterUse)); } } 02546 02547 #endif 02548 02549 02550 #if (defUseDelay == cfgTrue) 02551 02552 #if (includeTaskDelayFromWake == cfgTrue) 02553 static void privDelayCalc(Tuint16 uiDelayTime, Tbool bFromNow) 02554 #else 02555 static void privDelayCalcFromNow(Tuint16 uiDelayTime) 02556 #endif 02557 { /* This is the delay core code. Several routines make use of it. Arrive here if you need to 02558 * to set the delay variables of the task control block. */ 02559 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02560 /* This is one of the places in the current design of the Femto OS that is somewhat deeper. 02561 * Thus it makes sense to check the OS stack here. */ 02562 privCheckOsStackLevel(); 02563 #endif 02564 TtaskControlBlock * curTCB = privTcbList(privTaskNumber(defCurrentTaskNumber)); 02565 /* There are two different forms of delay. The time measured form the current value of the 02566 * tick counter or from the last value of activation. The latter is stored in the delay 02567 * variables. If we have no need for DelayFromWake we exclude the section. */ 02568 #if (includeTaskDelayFromWake == cfgTrue) 02569 if (bFromNow) 02570 #else 02571 if (true) 02572 #endif 02573 { /* Here the new time is calculated form the current time. */ 02574 /* Now calculate the new Delaytime. This is a 16 bit operation. Note that the uiDelaytime 02575 * parameter represents the requested delay time whereas the uiDelayTime field in the 02576 * task control block represents the wakeup time of the particular task due to delay. */ 02577 uiDelayTime += uxTickCount.Full; } 02578 else 02579 { /* Here the new time is calculated from the last wake time. */ 02580 #if (cfgCheckTiming == cfgTrue) 02581 /* We can assume we do not want to set a new wake time that is in he past, although it 02582 * is not an error in absolute sense. However, we no code that protects in this case 02583 * it being interpreted as a long delay. Therefore we report an error. */ 02584 Tuint16 uiMinDelay = (uxTickCount.Full - curTCB->uxDelay.Full) ; 02585 if (uiDelayTime < uiMinDelay) 02586 /* Unfortunately we do not know which method called the function, so we cannot issue the 02587 * correct callID with certainty. Since the method declaration is already complicated enough 02588 * we issue a IdSystem error here. We do know however that it must be the current task 02589 * causing the problems, and that we are in pseuso task-space, thus we may switch at error. */ 02590 { privShowError((errTaskDelayTooShort | errTaskStateCurrent | errOsStateAsIs), callIdSystem, errCurrentTask ); } 02591 #endif 02592 uiDelayTime += curTCB->uxDelay.Full; } 02593 /* We have a new wake time, so place it in the appropriate fields. */ 02594 curTCB->uxDelay.Full = uiDelayTime ; 02595 /* It may be that this task is in the near wake, in which all tasks are that are in the 02596 * same tick block as the current time. We must check this and set the near wake bit if 02597 * needed. */ 02598 if (curTCB->uxDelay.HighByte == uxTickCount.HighByte ) 02599 { uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStatePresent); } 02600 /* Update the task status of the task, say that the current task is delayed. If we want to 02601 * allow for delays below the minimum, we must add code here to check and keep the state 02602 * from being delayed. However we assume that this situation is normally not what you 02603 * want, because the task can be 'far behind'. We cannot get here for terminated tasks. */ 02604 curTCB->uiTaskStatus = (curTCB->uiTaskStatus & defBaseDelayStateSetMask) | defBaseDelayStateDelayed; } 02605 02606 #endif 02607 02608 02609 02610 #if (cfgUseSynchronization == cfgSyncSingleSlot) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue )) 02611 02612 static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB) 02613 { taskTCB->uiTaskSlot = defSlotRightFree; } 02614 02615 #endif 02616 02617 02618 #if ((cfgUseSynchronization == cfgSyncSingleBlock) || (cfgUseSynchronization == cfgSyncDoubleBlock)) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue )) 02619 02620 static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB) 02621 { Tuint08 * pTaskSlot = (Tuint08 *) &taskTCB->uiTaskSlot; 02622 /* We need to know the total size of the stack. Note that the number of 02623 * slots is the double of the depth. */ 02624 #if (defSlotDepthConstant == cfgTrue) 02625 /* It may be a constant and than we use that */ 02626 Tuint08 uiSDC = defSlotDepthCollect; 02627 #else 02628 /* Or it may be stored in flash*/ 02629 Tuint08 uiSDC = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiControlTaskNumber & defTaskNumberGetMask],Tuint08,uiSlotDepth); 02630 #endif 02631 Tuint08 uiLoopSlot; 02632 for (uiLoopSlot=0; uiLoopSlot<uiSDC; uiLoopSlot++) { *pTaskSlot++ = defSlotFree; } } 02633 02634 #endif 02635 02636 #if (cfgUseSynchronization == cfgSyncSingleSlot) 02637 02638 static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot) 02639 { /* Use this method to add remove one slot in the slot stack, or to search a slot. 02640 * This implementation can handle one slot only, and should be selected in case 02641 * you use only one slot and no nesting is required. The size is thus known. 02642 * The search is the default. The search can be decorated with search free only 02643 * or blocks only. Per default any match is valid. If we add a slot, it will always 02644 * be put at the first position. The slot that was at that position will be placed 02645 * elsewhere. This is to ensure that, if the task blocks, we know the blocking 02646 * slot is in the first position. */ 02647 TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask); 02648 /* Just to be sure */ 02649 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02650 privCheckOsStackLevel(); 02651 #endif 02652 /* Now we must handle all cases separately. In one special case we want only to 02653 * clean the stack, it is easy, done below. The clean case is special. */ 02654 /* Load the value of the slot (left four bits are unused.) */ 02655 Tuint08 uiVal = taskTCB->uiTaskSlot; 02656 /* Searching to see if the slot stack is free of Mutexes and Queues is need only in the 02657 * case we must restore the priority after lifting. */ 02658 #if (cfgUsePriorityLifting == cfgTrue) 02659 /* We check if we want to see if the slot numbers representing Queues and Mutexes are absent */ 02660 if ((uiControlTaskNumber & defSlotOperateSQMMask) == defSlotOperateQMAbsent) 02661 { /* Test if slot represents a Queue or Mutex, if so return false, return true otherwise */ 02662 return ((uiVal < defNumberQueuBegin) || (uiVal >= defNumberMutexEnd)); } 02663 #endif 02664 /* We need the result of the equality of the requested slot and the stored slot more 02665 * often, so store this in a separate variable. Also, this is the default result value */ 02666 Tbool uiValIsSlot = (uiVal == uiSlotSlot); 02667 /* From this place on we will test the different situations. Please note that the default 02668 * result is return the value of uiValIsSlot. Thus if this is wat we want to return, we do 02669 * not need to do anything. */ 02670 /* Test if we were looking for free slots only */ 02671 if ((uiControlTaskNumber & defSlotOperateSFreeMask) == defSlotOperateSearchFree) 02672 { /* If we where, and the task was blocking, we must return false, since there 02673 * cannot be a matching slot. If the task was free we need to return the result 02674 * of uiValIsSlot, which is default. */ 02675 if ((taskTCB->uiTaskStatus & defSlotLoopBlockMask) == defSlotLoopBlockBlocked) return false; } 02676 /* Test if we want to add a slot */ 02677 if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction) 02678 { /* If we make use of method checking, this slot should not be used at this moment */ 02679 #if (cfgCheckMethodUse == cfgTrue) 02680 /* Test if it is*/ 02681 if (uiVal != defSlotRightFree) 02682 /* If so return an error. Since we are potentially not stopping the current task, we want to return after the error. */ 02683 { privShowError((errSlotIncreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); } 02684 #endif 02685 /* Report the locks */ 02686 #if (cfgCheckTrace == cfgTrue) 02687 /* First we report which task is currently running */ 02688 privTrace(traceSlotLock | (uiControlTaskNumber & defTaskNumberGetMask) ); 02689 /* Subsequently report the lock. */ 02690 privTrace(uiSlotSlot); 02691 #endif 02692 /* Set the new slot*/ 02693 taskTCB->uiTaskSlot = uiSlotSlot; 02694 /* and what we return is not of importance. */ } 02695 /* Test if we want to remove a slot */ 02696 if ((uiControlTaskNumber & defSlotOperateDecreaseMask) == defSlotOperateDecreaseAction) 02697 { /* Test if the slot matches. Now this is only needed if we make use of method checking, since it 02698 * is considered an error to remove a non existing slot. */ 02699 #if (cfgCheckMethodUse == cfgTrue) 02700 /* If it does not match */ 02701 if (!uiValIsSlot) 02702 /* report the error. Since we are potentially not stopping the current task, we want to return after the error. */ 02703 { privShowError((errSlotDecreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); } 02704 #endif 02705 /* Report the unlocks */ 02706 #if (cfgCheckTrace == cfgTrue) 02707 /* First we report which task is currently running */ 02708 privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) ); 02709 /* Subsequently report the lock */ 02710 privTrace(uiSlotSlot); 02711 #endif 02712 /* Remove the slot, even if it does not match, cause we may asume it does. */ 02713 { taskTCB->uiTaskSlot = defSlotRightFree; } 02714 /* We return with false, since this condition may only return true if an other slot of the same number 02715 * is present on the stack. This cannot be the case however, since there is only one slot present. */ 02716 return false; } 02717 /* In all remaining cases we where just searching, and since there is only one slot a simple test is enough. */ 02718 return uiValIsSlot; } 02719 02720 #endif 02721 02722 02723 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 02724 02725 static Tuint08 privGetQueuSize(Tuint08 uiQueuNumber) 02726 { /* Used to read the size of a particular queue from flash or to return the 02727 * constant if all sizes are equal. */ 02728 Tuint08 uiResult; 02729 /* So first determine if they are equal */ 02730 #if (defQueuSizeConstant == cfgTrue) 02731 /* Fixed sizes, return the constant. */ 02732 uiResult = defQueuSizeFixed; 02733 #else 02734 /* Otherwise we must look it up from a table in flash. */ 02735 uiResult = portFlashReadWord(Tuint08,uiQueuSize[uiQueuNumber]); 02736 #endif 02737 /* Done, return the size of the queue. */ 02738 return uiResult; } 02739 02740 #endif 02741 02742 02743 02744 #if (cfgUseSynchronization == cfgSyncSingleBlock) || (cfgUseSynchronization == cfgSyncDoubleBlock) 02745 02746 static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot) 02747 { /* Use this method to add remove slots in the slot stack, or to search a slot. 02748 * The latter is the default. The search can be decorated with search free only 02749 * or blocks only. Per default any match is valid. If we add a slot, it will always 02750 * be put at the first position. The slot that was at that position will be placed 02751 * elsewhere. This is to ensure that, if the task blocks, we know the blocking 02752 * slot is in the first position. Two slots can be added simultaneously, they always 02753 * take the first bytes. */ 02754 /* The default reaction is false, but this is only needed if we have double slots, since 02755 * then it is sometimes handy to have a default reaction. Normally you should not enter 02756 * this method with the free slot. */ 02757 TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask); 02758 /* Just to be sure */ 02759 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02760 privCheckOsStackLevel(); 02761 #endif 02762 /* pTaskSlot holds the pointer to the first two slots in the stack. The rest 02763 * of the stack is outside the TaskControlBlock. */ 02764 Tuint08 * pTaskSlot = (Tuint08 *) &taskTCB->uiTaskSlot; 02765 /* We need to know the total size of the stack. Note that the number of 02766 * slots is the double of the depth. */ 02767 #if (defSlotDepthConstant == cfgTrue) 02768 /* It may be a constant and than we use that */ 02769 Tuint08 uiSDC = defSlotDepthCollect; 02770 #else 02771 /* Or it may be stored in flash*/ 02772 Tuint08 uiSDC = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiControlTaskNumber & defTaskNumberGetMask],Tuint08,uiSlotDepth); 02773 #endif 02774 Tuint08 uiLoopSlot; 02775 /* This variable is needed to hold some slots later on. */ 02776 Tuint08 uiVal; 02777 /* Searching to see if the slotstack is free of Mutexes and Queues is need only in the 02778 * case we must restore the priority after lifting. */ 02779 #if (cfgUsePriorityLifting == cfgTrue) 02780 /* We check if we want to see if the slot numbers representing Queues and Mutexes are absent */ 02781 if ((uiControlTaskNumber & defSlotOperateSQMMask) == defSlotOperateQMAbsent) 02782 { /* Loop through all the slots */ 02783 for (uiLoopSlot=0; uiLoopSlot<uiSDC; uiLoopSlot++) 02784 { /* Get the first to slots (technically the left slot in the zeroth slot can never be 02785 * a wait, but checking does not harm either.*/ 02786 uiVal = *pTaskSlot++; 02787 /* Extract the left slot */ 02788 Tuint08 uiLeftSlot = (uiVal & defSlotLeftGetMask) >> defSlotLeftShift; 02789 /* Test if it represents a Queue or Mutex, if so return false */ 02790 if ((uiLeftSlot >= defNumberQueuBegin) && (uiLeftSlot < defNumberMutexEnd)) { return false;} 02791 /* Extract the right slot */ 02792 Tuint08 uiRightSlot = (uiVal & defSlotRightGetMask) >> defSlotRightShift; 02793 /* Test if it represents a Queue or Mutex, if so return false */ 02794 if ((uiRightSlot >= defNumberQueuBegin) && (uiRightSlot < defNumberMutexEnd)) { return false;} } 02795 /* If we did no encounter any Queues ore Mutexes, the stack is empty or contains only Waits, in that 02796 * case we may return true. */ 02797 return true; } 02798 #endif 02799 /* uiLoopControl will contains two bits of information. One to indicate that is 02800 * the task is free or blocked. And the second (set further below) to indicate if 02801 * the operation is to handle all slots. The latter is the default */ 02802 Tuint08 uiLoopControl = (taskTCB->uiTaskStatus & defSlotLoopBlockMask); 02803 /* If we are still here we did not search Waits only. Now if we are not searching 02804 * free slots only eigther or increase the stack (i.e. we search all or we decrease the stack, we must make 02805 * sure the loop below does not skip the first two slots in the search loop below. so override 02806 * the blocked state form the task to enforce checking the first byte. */ 02807 if (((uiControlTaskNumber & defSlotOperateSFreeMask) != defSlotOperateSearchFree) && ((uiControlTaskNumber & defSlotOperateIncreaseMask) != defSlotOperateIncreaseAction)) 02808 { uiLoopControl = defSlotLoopBlockNon; } 02809 /* The loop below contains two parts, a test for each slot position and an action. The default 02810 * is to test agains the given slot, and to replace the found value with the freeSlot (0x00) 02811 * Since searching can only be at one slot at a time, we know uiSlotSlot has first four bits zero */ 02812 Tuint08 uiTest = uiSlotSlot; 02813 Tuint08 uiFill = defSlotRightFree; 02814 /* Load the value of the fist two slots. */ 02815 uiVal = *pTaskSlot; 02816 /* However in case we want to add a slot, we must look for a free slot and substitute the 02817 * value that was placed first, which in his turn must contain the new value. Thus */ 02818 if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction) 02819 {/* Place the new slot at the first position, thereby leaving a possible second position in tact */ 02820 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 02821 /* In this case the whole byte is reserved for the new lock, which may contain two slots. */ 02822 *pTaskSlot = uiSlotSlot; 02823 #else 02824 /* In this case the left nibble can contain a free lock which must be preserved, and uiSlotSlot can only 02825 * contain one slot */ 02826 *pTaskSlot = (uiVal & defSlotRightSetMask) | uiSlotSlot; 02827 #endif 02828 /* Report the locks */ 02829 #if (cfgCheckTrace == cfgTrue) 02830 /* First we report which task is currently running */ 02831 privTrace(traceSlotLock | (uiControlTaskNumber & defTaskNumberGetMask) ); 02832 /* Report the slots */ 02833 privTrace(uiSlotSlot); 02834 #endif 02835 /* and from now on we look for empty slots */ 02836 uiTest = defSlotRightFree; 02837 /* Which must be filled with the value that was on the first place before. */ 02838 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 02839 /* For a double block both slots may need replacement */ 02840 uiFill = uiVal; 02841 #else 02842 /* For a single block only the right nibble is interesting, the left may contain a free lock */ 02843 uiFill = (uiVal & defSlotRightGetMask); 02844 #endif 02845 /* If that slot was empty before, it makes no sense to continue (although it cannot harm) 02846 * and we are done. */ 02847 if (uiFill == defSlotFree) return false; 02848 /* If we do continue we may not test the first nibble/byte since that is what we just filled, and we do 02849 * not want to risk a double block. so we simulate a block. */ 02850 uiLoopControl = defSlotLoopBlockBlocked; } 02851 /* If we do not want to add a slot, we are about to remove one, or search for one. In case 02852 * we do not want to remove one, we search for one, and indicate so in the uiLoopControl variable. 02853 * This is to make sure we leave when found, before we substitute a value. */ 02854 else if ((uiControlTaskNumber & defSlotOperateDecreaseMask) != defSlotOperateDecreaseAction) 02855 { uiLoopControl |= defSlotLoopSearchActive; } 02856 /* If we are decreasing, maybe we must inform the outside world we are doing so */ 02857 #if (cfgCheckTrace == cfgTrue) 02858 else 02859 { /* Here we are decreasing, which we want to report the unlocks, first we report which task is currently running */ 02860 privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) ); 02861 /* Report the slot(s) */ 02862 privTrace(uiSlotSlot); } 02863 #endif 02864 /* Start looping at the beginning or skip the first byte depending on the situation of the uiLoopControl. */ 02865 if ((uiLoopControl & defSlotLoopBlockMask) == defSlotLoopBlockNon) 02866 { uiLoopSlot = 0; } 02867 else 02868 { /* We must distinguish the double and single block situations */ 02869 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 02870 /* In this case we need to skip the first full byte, which contains the new slots */ 02871 uiLoopSlot = 2; 02872 pTaskSlot++; 02873 #else 02874 /* In this case we need to skip only the first nibble, which contains the new slot */ 02875 uiLoopSlot = 1; 02876 #endif 02877 } 02878 /* Now run through all slots, double the number because we check left and right nibble with the same code */ 02879 while (uiLoopSlot<2*uiSDC) 02880 { uiVal = *pTaskSlot; 02881 /* At odd passes we check the other nibble (slot) */ 02882 if ((uiLoopSlot & 0x01) == 0x01) { portSwapNibbles(uiVal); } 02883 /* Test if the right slot fulfills the test, and if so, test also if we may test this slot. The latter may 02884 * not be the case for the very first slot, when we are only searching for free slots and this task is blocking. */ 02885 if ((uiVal & defSlotRightGetMask) == uiTest ) 02886 { /* So we found a match on the right slot. If we were only searching, we may leave with the message we found a hit. */ 02887 if ((uiLoopControl & defSlotLoopSearchMask) == defSlotLoopSearchActive) return true; 02888 /* If we were replacing (for increase or decrease), do so now */ 02889 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 02890 /* We simply slide in the new value on the right, if the values ware swapped before 02891 * that does not matter */ 02892 *pTaskSlot = (uiVal & defSlotRightSetMask) | (uiFill & defSlotRightGetMask); 02893 #else 02894 /* Calculate the new value for the slot stack */ 02895 Tuint08 uiNewVal = (uiVal & defSlotRightSetMask) | uiFill; 02896 /* here we must be careful since the sequence of nibbles on the bottom of the slot stack 02897 * does matter, so we must restore if we corrupted it */ 02898 if ((uiLoopSlot & 0x01) == 0x01) { portSwapNibbles(uiNewVal); } 02899 /* Now it is safe to store. */ 02900 *pTaskSlot = uiNewVal; 02901 #endif 02902 /* If we are decreasing, it is interesting to know if there is a second slot with the same number, 02903 * so we continue otherwise */ 02904 if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction) 02905 { /* If there can be a second slot */ 02906 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 02907 /* We move to the second slot */ 02908 uiFill >>= 4; 02909 /* there may be no second slot to paste */ 02910 if (uiFill == defSlotFree) { return false; } 02911 #else 02912 /* If not we are done, the return value is of no importance */ 02913 return false; 02914 #endif 02915 } 02916 else 02917 { /* we are decreasing, so turn the operation into a search */ 02918 uiLoopControl |= defSlotLoopSearchActive; } } 02919 /* Only at odd passes we check go to the next byte in the stack */ 02920 if ((uiLoopSlot & 0x01) == 0x01) { pTaskSlot++; } 02921 uiLoopSlot++; } 02922 /* If we found nothing, return false. If we were not searching but increasing we should not arrive here, 02923 * for it indicates we could not add the slot. If we where decreasing arriving here means the there was no 02924 * slot with the given number that could be removed. If this is an error depends on the caller. */ 02925 #if (cfgCheckMethodUse == cfgTrue) 02926 /* Test if we arrived here while increasing, if so we could not add the slot for the stack is full. 02927 * Since we are potentially not stopping the current task, we want to return after the error. */ 02928 if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction) 02929 { privShowError((errSlotIncreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); } 02930 /* Test if we arrived here while decreasing and did not yet succeed in doing so. If so the slot 02931 * was not present and this is considered an error. If we where decreasing and searching, we did 02932 * not find a second slot so we should return false, this is normal operation */ 02933 if ( ((uiControlTaskNumber & defSlotOperateDecreaseMask) == defSlotOperateDecreaseAction) && 02934 ((uiLoopControl & defSlotLoopSearchMask) != defSlotLoopSearchActive) ) 02935 { /* Since we are potentially not stopping the current task, we want to return after the error. */ 02936 privShowError((errSlotDecreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); } 02937 #endif 02938 return false; } 02939 02940 #endif 02941 02942 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue)) 02943 02944 static Tuint08 privFreeLockAbsent(Tuint08 uiSlot) 02945 { /* This method is used to see if a particular slot is occupied by an other task as a free lock. 02946 * Thus it returns true when the slot is totally unused, returns true when it is only used 02947 * as blocking slots or if you call it with the free Slot. Note that it returns false if the 02948 * slot has been used in a task as free, even if that task was later blocked. */ 02949 /* Just to be sure */ 02950 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02951 privCheckOsStackLevel(); 02952 #endif 02953 if (uiSlot != defSlotFree) 02954 { Tuint08 uiLoopTask; 02955 /* Run trough all tasks that use slots */ 02956 for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++) 02957 { /* Search for the use of this slots as free slots in de the slotstack. privOperateSlotStack returns 02958 * true if the slot is reported free in this task. If so we may stop here, if not we must continue 02959 * to search. */ 02960 if ( privOperateSlotStack((uiLoopTask | defSlotOperateSearchFree ), uiSlot ) ) { return false; } } } 02961 /* If we have not found any free use of the slot, the slot is not used as such. */ 02962 return true; } 02963 02964 #endif 02965 02966 02967 #if (cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue) || (cfgUseEvents == cfgTrue) 02968 02969 static void privUnblockTask(Tuint08 uiControlTaskNumber) 02970 { /* This method unlocks / unblocks a task for you, depending on the bits in uiControlTaskNumber. 02971 * It can handle fs blocks and syncblocks. Only call this on blocking tasks, since it will 02972 * always perform a deblock, there is no test if the task is indeed blocked. Terminated and 02973 * suspended tasks are ignored. It may NOT be called on shared tasks. */ 02974 TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask); 02975 /* Just to be sure */ 02976 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02977 privCheckOsStackLevel(); 02978 #endif 02979 /* Check if the task is not terminated or suspended, since they may not be unlocked, for all the 02980 * other types (sleeping included) there is no problem */ 02981 if (taskTCB->uiTaskStatus >= defBaseSleepingTask) 02982 { /* First we see if the caller wanted to unlock the task too. */ 02983 if ((uiControlTaskNumber & defParaLockMask) == defParaLockStateUnlock) 02984 { 02985 #if (cfgUseSynchronization != cfgSyncNon) 02986 if ((taskTCB->uiTaskStatus & defBaseModeGetMask) == defBaseModeSync) 02987 { /* If we make use of events, the test above also passes for event unlocks, thus we 02988 * must explicitly test in that case that we do not have an event unlock. This could 02989 * lead to the erasure of free slots. */ 02990 #if (cfgUseEvents == cfgTrue) 02991 if ((taskTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressSlot) 02992 #endif 02993 { /* Unlocking means setting the lowest slot number to zero, the 'free lock'. Note we assume that 02994 * slot is the correct one, for we came from a blocking situation. */ 02995 #if (cfgCheckTrace == cfgTrue) 02996 /* First we report which task is currently running */ 02997 privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) ); 02998 /* Report the slots */ 02999 privTrace(taskTCB->uiTaskSlot); 03000 #endif 03001 /* Clean the lock, depending on the type */ 03002 #if (cfgUseSynchronization == cfgSyncSingleSlot) 03003 /* We may clean all of it, since the left nibble is not used */ 03004 taskTCB->uiTaskSlot = defSlotFree; 03005 #elif (cfgUseSynchronization == cfgSyncSingleBlock) 03006 /* We must be careful for the left nibble may contain a free lock */ 03007 taskTCB->uiTaskSlot = (taskTCB->uiTaskSlot & defSlotRightSetMask) | defSlotRightFree; 03008 #elif (cfgUseSynchronization == cfgSyncDoubleBlock) 03009 /* We must be sure to clean all blocking slots. */ 03010 taskTCB->uiTaskSlot = defSlotFree; 03011 #else 03012 #error "You should not arrive here." 03013 #endif 03014 } } 03015 #endif 03016 #if (cfgUseFileSystem == cfgTrue) 03017 /* Check if the task is locked on the file system. (Note: shared tasks will pass this 03018 * test, therefore it may not be called on shared tasks.) */ 03019 if ((taskTCB->uiTaskStatus & defBaseModeGetMask) == defBaseModeAccess) 03020 { /* If so, we clean the read/write modes. Clean the locks, if present. Note we only do so 03021 * when requested to unlock. This is the case in for example when a delay occurs. */ 03022 #if (defUseFsField == cfgTrue) 03023 taskTCB->defFsField = (taskTCB->defFsField & (defFsReadSetMask & defFsWriteSetMask)) | (defFsReadClear | defFsWriteClear); 03024 #endif 03025 } 03026 #endif 03027 /* The last situation, namely, the Events, do not need standard unlocking operations, for we can 03028 * only arrive here if the uiTaskEvents has already be cleared. The only exception being in case 03029 * of timeouts. Also, there is no harm in cleaning the TaskEvent if we come from an unlock due 03030 * to a slot locking. Since if the task was blocking on a slot, it could not have been blocking on 03031 * an event to happen. In that case the TaskEvent variable should already be cleared. Thus */ 03032 #if (cfgUseEvents == cfgTrue) && (cfgUseTimeout == cfgTrue) 03033 taskTCB->uiTaskEvents = defAllEventsReset; 03034 #endif 03035 } 03036 privTrace(traceTaskRelease | (uiControlTaskNumber & defTaskNumberGetMask) ); 03037 /* ... if so, unblock the task and also remove a possible timeout. If we forget this, the task will 03038 * turn into a delayed state. This is needed since we also unblock tasks which are not the current ones. 03039 * A sleeping state may be deblocked and delay-waked, but must stay sleeping. Shared tasks are not allowed. */ 03040 taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & (defBaseModeSetMask & defBaseBlockStateSetMask & defBaseDelayStateSetMask & defBaseDressSetMask)) | (defBaseModeSync | defBaseBlockStateFree | defBaseDelayStateWake | defBaseDressDone); 03041 /* If we must return a value, this can only be the case after an unblock of course. Without an unblock, there 03042 * is no task to return something to. */ 03043 #if (defUseBoolReturns == cfgTrue) 03044 /* The return value is stored in the defRetField, and consists of two bits, one for true/false, one to 03045 * indicate if there is something to return. */ 03046 taskTCB->defRetField = (taskTCB->defRetField & defRetSetMask) | ((uiControlTaskNumber & defParaRetMask) >> defParaRetToRetShift); 03047 #endif 03048 /* If we want to keep track of accurate wake times, and we have release a task, we must copy the current 03049 * time to the delay fields, since this is the place where the time of last activation is kept.*/ 03050 #if (defUseDelay == cfgTrue) && (cfgUseCorrectWakeupTimes == cfgTrue) 03051 taskTCB->uxDelay.Full = uxTickCount.Full; 03052 #endif 03053 } } 03054 03055 #endif 03056 03057 03058 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue) 03059 03060 static void privLiftLocksOnSlot(Tuint08 uiSlot) 03061 { /* Arrive here when you need to increase the priorities of several tasks in a slot 03062 * of a task about to be blocked. By increasing those priorities to the level of 03063 * the blocking slot, these tasks have a chance to finish, and not keep the current 03064 * blocking task for ever. Note that we do not level all priorities in one slot, 03065 * since due to the fact that tasks may be blocked on two slots simultaneously this 03066 * would be a very costly to sort out. This mechanism tries to keep it simple.*/ 03067 TtaskControlBlock * curTCB = privTcbList(privTaskNumber(defCurrentTaskNumber)); 03068 /* Get the priority of the current task. Note we do not need to shift the 03069 * priority since we will only compare priorities among each other, we do not 03070 * need the exact value. */ 03071 Tuint08 uiCurTaskPriority = curTCB->uiTaskStatus & defBasePrioGetMask; 03072 Tuint08 uiLoopTask; 03073 TtaskControlBlock * loopTCB; 03074 /* Run through all task possible having a slot. */ 03075 for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++) 03076 { loopTCB = privTcbList(uiLoopTask); 03077 /* Test if the task indeed holds the slot. We do not distinguish between blocked and 03078 * locked only. Solely possessing the lock is enough to rise its priority. Sometimes 03079 * this may imply we rise tasks without a real need, but it would be to costly to 03080 * analyze on which slot the blocking takes place. */ 03081 if (privOperateSlotStack((uiLoopTask | defSlotOperateSearchDefault), uiSlot)) 03082 { /* Get the priority of the looped task, again, don't shift */ 03083 Tuint08 uiLoopTaskPriority = loopTCB->uiTaskStatus & defBasePrioGetMask; 03084 /* Compare which is larger */ 03085 if (uiCurTaskPriority>uiLoopTaskPriority) 03086 { /* and if the priotity of the current task (which will be blocked and thus inactivates 03087 * was larger, lift the priority of the other task to that of the current task. The 03088 * present priority gets lost. */ 03089 loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBasePrioSetMask) | uiCurTaskPriority; } } } } 03090 03091 #endif 03092 03093 03094 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue) 03095 03096 static void privRestoreInitialPriority(Tuint08 uiTaskNumber) 03097 { /* Arrive here if we need to restore the priority of a lifted task. The priority 03098 * is reset to the original value that is stored in flash. First we test if the 03099 * task does not hold any Queue's or Mutexes any more. */ 03100 if ( privOperateSlotStack((uiTaskNumber | defSlotOperateQMAbsent ), defSlotFree ) ) 03101 { /* If so it now makes sense to restore the priority. */ 03102 TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber); 03103 /* Read the initial priority from flash, or use the constant */ 03104 #if (defInitialStatusConstant == cfgTrue) 03105 Tuint08 uiInitialStatus = defInitialStatusFixed; 03106 #else 03107 Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus); 03108 #endif 03109 /* Restore its value. */ 03110 taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & defBasePrioSetMask) | (uiInitialStatus & defBasePrioGetMask); } } 03111 03112 #endif 03113 03114 03115 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 03116 03117 /* The queue mechanism allows for several tasks writing and reading bytes. The OS 03118 * locks all tasks not able at that moment to write or read the required quantity of bytes. 03119 * Ah, and take care, the queues (and locks for that matter) are numbers from 1 and 03120 * onwards. Thus, slot 0 is forbidden, since it indicates a free slot. But queue's 03121 * are stored in arrays, thus slot 1 corresponds to queue 0 etc. Therefore you see 03122 * [uiSlot-1] everywhere. */ 03123 03124 /* DISCUSSION 03125 * Except in the beginning, the Read (pointer) and Write (pointer) are never equal. 03126 * This is because otherwise you could not see the difference between a 03127 * full and an empty queue. This would imply that one byte is effectively lost, 03128 * for it could never be retrieved. Such a waste! We must solve this 03129 * by storing one bit of information that differentiates between a full and an 03130 * empty queue, when Read and Write are equal. But where? 03131 * It seems reasonable to limit the size of queue's to 127 bytes. Now we have 03132 * two extra bits available (upper bits of Read/Write storage). Second advantage 03133 * is that we can address the whole queue, from the queue Requests. What we don't 03134 * know at this moment if this will cost a lot of code. 03135 * Now, we decided to reserve a bit to indicate that the queue is full, is 03136 * it handy to use the other for indicating an empty queue? Well, it may be, 03137 * but, since queue's are empty at startup, we want to indicate the bit 03138 * empty by zero. Hmmm, that implies a lot of code. It may be shorter to 03139 * just ask for Read==Write and !full. Ok let us see what happens. 03140 * Now we keep the write pointers in uiQueuWrite[] and the read pointers in 03141 * uiQueuRead[]. The write operation is write-post-decrement, and the read 03142 * operation likewise. The pointers wrap when they hit the bottom of the queue. 03143 * Thus we write/read downwards, the wrapping is easier detected that way. 03144 */ 03145 03146 03147 static Tuint08 privQueuTest(Tuint08 uiSlot, Tsint08 siFreeFilling) 03148 { /* If we want to know the size of a queue use this function, or if we 03149 * want to test if a certain number of bytes will fit. We use negative numbers 03150 * for writing, and positive numbers for reading. */ 03151 /* The size information of the queue is in flash, note the shift from slot numbers 03152 * to queue array numbering. */ 03153 Tuint08 uiSize = privGetQueuSize(uiSlot-1); 03154 /* Parameter FreeFilling zero is miss-used to request the size of the queue. */ 03155 if (siFreeFilling==0) { return uiSize; } 03156 /* The information about the write and read positions are stored in two arrays, the 03157 * uiQueuWrite and the uiQueuRead. Obtain the write pointer. That pointer points to 03158 * the location that has to be written. */ 03159 Tuint08 uiWrite = uiQueuWrite[uiSlot-1]; 03160 /* Extract the full bit. We assume it has been set and managed correctly, although it 03161 * only has a meaning when uiWrite==uiRead, for this is the only situation where we 03162 * have ambiguity. */ 03163 Tbool bFull = (uiWrite & defQueuFillingGetMask) == defQueuFillingStateFull; 03164 /* If the queue is full ... */ 03165 if (bFull) 03166 { if (siFreeFilling>0) 03167 { /* we can read as many bytes from the queue as it is long, */ 03168 return uiSize; } 03169 else 03170 { /* but we cannot write anything anymore to it. */ 03171 return 0; } } 03172 /* In other cases we must calculate the number, extract the read pointer. */ 03173 Tuint08 uiRead = uiQueuRead[uiSlot-1]; 03174 /* Now calculate how many readable characters there are. The situation that the queue 03175 * might be full was already resolved, so in the calculation below it is assumed the 03176 * queue is not full, i.e. uiWrite==uiRead implies it is empty. Make sure the calculation 03177 * never produced negative numbers. */ 03178 Tuint08 uiReadable = uiWrite<=uiRead ? uiRead - uiWrite : uiSize - (uiWrite - uiRead); 03179 /* Every byte that is not readable, must be writable. */ 03180 Tuint08 uiWriteable = uiSize - uiReadable; 03181 /* Now decide what to return, for positive siFreeFilling we asks for the readable characters, 03182 * for negative for the writable. */ 03183 if (siFreeFilling>0) 03184 { return uiReadable; } 03185 else 03186 { return uiWriteable; } } 03187 03188 #endif 03189 03190 03191 #if (cfgUseSynchronization != cfgSyncNon) && (defUseWaits == cfgTrue) 03192 03193 static void privReleaseWait(Tuint08 uiSlot) 03194 { /* This is called if we want to clean all locks in the tasks blocked by a wait. Cannot be 03195 * called to clean up Queues or Mutexes. */ 03196 Tuint08 uiLoopTask; 03197 /* Loop trough all tasks. */ 03198 for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++) 03199 { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask); 03200 /* Check if the task holds a blocking slot with the given number. The slot can never be left, 03201 * since waits cannot be be blocked in pairs. Furthermore, the task must be blocking, wait slots 03202 * are never solely locked. */ 03203 if ((loopTCB->uiTaskSlot & defSlotRightGetMask) == uiSlot) 03204 { /* If the slot matches unblock and unlock the task */ 03205 privUnblockTask(uiLoopTask | defParaLockStateUnlock | defParaRetStateTrue); } } } 03206 03207 #endif 03208 03209 03210 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue) 03211 03212 static Tbool privSizeFitsQueu(Tuint08 uiSlot, Tsint08 siFreeFilling) 03213 { /* Call this if you need to test if a queue on a slot may fit the filling condition. First 03214 * test if we indeed have a queue, otherwise there is noting to test. */ 03215 if ((uiSlot >= defNumberQueuBegin) || (uiSlot < defNumberQueuEnd )) 03216 { Tuint08 uiTest; 03217 /* Now we test the siFreeFilling. This method returns the number of bytes that can read from / written to 03218 * the queue, depending on the sign of siFreeFilling. If siFreeFilling==0 it returns the queue size (which 03219 * value we do not need here) The number returned is always positive. */ 03220 uiTest = privQueuTest(uiSlot,siFreeFilling); 03221 /* If we want to read we may enter if we want to read not more bytes as are available */ 03222 if (siFreeFilling>0) { return ( ((Tuint08) siFreeFilling) <= uiTest); } 03223 /* If we want to read we may enter if we want to read not more bytes as are available */ 03224 if (siFreeFilling<0) { return ( ((Tuint08) (-siFreeFilling)) <= uiTest); } } 03225 /* The default is true, i.e. you get the lock and it remains in effect if the siFreeFilling == 0 or 03226 * we have no queue at all. */ 03227 return true; } 03228 03229 #endif 03230 03231 03232 #if ((cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))) && 0 03233 03234 static void privReleaseSyncBlockingTasks(void) 03235 { /* Arrive here if you have just completed putting sum data on the queue, or used a Mutex 03236 * and are done and have released your own lock. Or call it when the task is 03237 * initilialized or terminated. It runs through all tasks, and unblocks (not unlock) 03238 * the first task that meets the conditions. This may not be the one with the highest 03239 * priority. It does not operate on waitblocks (since they cannot, and should not, 03240 * be released like this) */ 03241 Tuint08 uiLoopTask; 03242 /* Run through all tasks possibly having a slot. */ 03243 for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++) 03244 { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask); 03245 /* First check it this is a blocking task (a non blocking task does not need to 03246 * be released) Furthermore, it must be a task that is able to run, otherwise way 03247 * may release a task which is for instance suspended, and that will hold the lock 03248 * for ever. */ 03249 if ((loopTCB->uiTaskStatus & ( defBaseStopStateGetMask | defBaseModeGetMask | defBaseBlockStateGetMask | defBaseDressGetMask)) == (defBaseStopStateGo | defBaseModeSync | defBaseBlockStateBlocked | defBaseDressSlot) ) 03250 { /* If yes, extract on which slots this task is blocking */ 03251 Tuint08 uiRightSlot = (loopTCB->uiTaskSlot & defSlotRightGetMask) >> defSlotRightShift; 03252 /* Now we must test if this is not a wait-slot */ 03253 #if (defUseWaits == cfgTrue) 03254 if (uiRightSlot < defNumberWaitBegin) 03255 #endif 03256 { /* If we arive here we are dealing with a queue of mutex. Now, depending on if we are working 03257 * with doublelocks, we may need the left slot too. Waits and mutex/queues can never mix, so we 03258 * do not need to worry */ 03259 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 03260 Tuint08 uiLeftSlot = (loopTCB->uiTaskSlot & defSlotLeftGetMask) >> defSlotLeftShift; 03261 /* Now we must test if these slots are unlocked on some other task */ 03262 if (privFreeLockAbsent(uiLeftSlot) && privFreeLockAbsent(uiRightSlot)) 03263 { /* If not, we must test if the filling conditions on this task are fulfilled 03264 * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If 03265 * we do not have queues at all testing is not needed. */ 03266 #if (defUseQueus == cfgTrue) 03267 if (privSizeFitsQueu(uiLeftSlot,loopTCB->siQueuLeftLock) && privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock)) 03268 #endif 03269 #else 03270 /* Now we must test if these slots are unlocked on some other task */ 03271 if (privFreeLockAbsent(uiRightSlot)) 03272 { /* If not, we must test if the filling conditions on this task are fulfilled 03273 * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If 03274 * we do not have queues at all testing is not needed. */ 03275 #if (defUseQueus == cfgTrue) 03276 if (privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock)) 03277 #endif 03278 #endif 03279 { /* This task seems to fulfill all requirements to be released. Make it so. */ 03280 privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue); } } } } } } 03281 03282 03283 #endif 03284 03285 03286 #if ((cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))) 03287 03288 static void privReleaseSyncBlockingTasks(void) 03289 { /* Arrive here if you have just completed putting sum data on the queue, or used a Mutex 03290 * and are done and have released your own lock. Or call it when the task is 03291 * initialized or terminated. It runs through all tasks, and unblocks (not unlock) 03292 * the first task with the highest priority that meets the conditions. When having 03293 * equal priority tasks with a timeout prevail above those without. 03294 * It does not operate on wait blocks (since they cannot, and should not, 03295 * be released like this) */ 03296 /* Variable to run through all tasks. */ 03297 Tuint08 uiLoopTask; 03298 #if (cfgUsePrioritizedRelease == cfgTrue) 03299 /* Variable store the most likely candidate so far. */ 03300 Tuint08 uiSelectedTask; 03301 /* Variable store the modified priority of the most likely candidate so far. */ 03302 Tuint08 uiSelectedLevel = 0; 03303 #endif 03304 /* Run through all tasks possibly having a slot. */ 03305 for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++) 03306 { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask); 03307 /* First check it this is a blocking task (a non blocking task does not need to 03308 * be released) Furthermore, it must be a task that is able to run, otherwise way 03309 * may release a task which is for instance suspended, and that will hold the lock 03310 * for ever. */ 03311 if ((loopTCB->uiTaskStatus & ( defBaseStopStateGetMask | defBaseModeGetMask | defBaseBlockStateGetMask | defBaseDressGetMask)) == (defBaseStopStateGo | defBaseModeSync | defBaseBlockStateBlocked | defBaseDressSlot) ) 03312 { /* If yes, extract on which slots this task is blocking */ 03313 Tuint08 uiRightSlot = (loopTCB->uiTaskSlot & defSlotRightGetMask) >> defSlotRightShift; 03314 /* Now we must test if this is not a wait-slot */ 03315 #if (defUseWaits == cfgTrue) 03316 if (uiRightSlot < defNumberWaitBegin) 03317 #endif 03318 { /* If we arive here we are dealing with a queue of mutex. Now, depending on if we are working 03319 * with doublelocks, we may need the left slot too. Waits and mutex/queues can never mix, so we 03320 * do not need to worry */ 03321 #if (cfgUseSynchronization == cfgSyncDoubleBlock) 03322 Tuint08 uiLeftSlot = (loopTCB->uiTaskSlot & defSlotLeftGetMask) >> defSlotLeftShift; 03323 /* Now we must test if these slots are unlocked on some other task */ 03324 if (privFreeLockAbsent(uiLeftSlot) && privFreeLockAbsent(uiRightSlot)) 03325 { /* If not, we must test if the filling conditions on this task are fulfilled 03326 * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If 03327 * we do not have queues at all testing is not needed. */ 03328 #if (defUseQueus == cfgTrue) 03329 if (privSizeFitsQueu(uiLeftSlot,loopTCB->siQueuLeftLock) && privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock)) 03330 #endif 03331 #else 03332 /* Now we must test if these slots are unlocked on some other task */ 03333 if (privFreeLockAbsent(uiRightSlot)) 03334 { /* If not, we must test if the filling conditions on this task are fulfilled 03335 * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If 03336 * we do not have queues at all testing is not needed. */ 03337 #if (defUseQueus == cfgTrue) 03338 if (privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock)) 03339 #endif 03340 #endif 03341 { /* This task seems to fulfill all requirements to be released. */ 03342 #if (cfgUsePrioritizedRelease == cfgTrue) 03343 /* See if it is a candidate. 03344 * By swapping the nibbles we get the priority before the delay status so that we compare 03345 * first on priority and subsequently on delay. (the other bits of the pattern 110dppp1 03346 * are fixed */ 03347 Tuint08 uiCandidateLevel = loopTCB->uiTaskStatus; 03348 portSwapNibbles(uiCandidateLevel); 03349 /* So the candidate level is of the form ppp1110d. If we do not have a selected level yet, 03350 * or the new task is higher */ 03351 if (uiSelectedLevel < uiCandidateLevel) 03352 { /* Copy the new level for further comparison. */ 03353 uiSelectedLevel = uiCandidateLevel; 03354 /* Copy the task number for unblocking at the end. */ 03355 uiSelectedTask = uiLoopTask; } 03356 #else 03357 /* unblock the task. */ 03358 privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue); 03359 /* we are done. Quickly terminate this loop by increasing the loop task number. 03360 * (do NOT use return here, costs 28 bytes extra! */ 03361 uiLoopTask = defCurrentTaskNumber; 03362 #endif 03363 } } } } } 03364 #if (cfgUsePrioritizedRelease == cfgTrue) 03365 if (uiSelectedLevel != 0) { privUnblockTask(uiSelectedTask | defParaLockStateKeep | defParaRetStateTrue); } 03366 #endif 03367 } 03368 03369 #endif 03370 03371 03372 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue) 03373 03374 static void privPutAllTasksToSleep(void) 03375 { /* Call this if you want to put all tasks to sleep. It will put all running and 03376 * blocked, delayed tasks to sleep. If will check if files system operations 03377 * are busy, or if tasks are waiting for fs operations. Those tasks may run 03378 * until the fs actvities are over, and are subsequently put to sleep. It will 03379 * leave suspended and terminated tasks alone. */ 03380 Tuint08 uiLoopTask; 03381 /* Run through all tasks. */ 03382 for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++) 03383 { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask); 03384 /* Putting a task to sleep, we do not want to revive a suspended task via the 03385 * sleep. An already sleeping task requires no attention. Thus we must check if 03386 * the task we put to sleep was indeed running. */ 03387 if ((loopTCB->uiTaskStatus & defBaseStopStateGetMask) == defBaseStopStateGo ) 03388 { /* Putting a task blocked on the fs to sleep may lead to a crash at wakeup, 03389 * so putting these tasks to sleep must be postponed until later. In that case 03390 * we will return here. The reason is we have no way to specially store the kind of 03391 * block in a sleepstate, so at wakeup we cannot correctly reconstruct the state. 03392 * It is no problem if the task is actually reading from the file system 03393 * for such a task is not blocked, but writing files cannot be put 03394 * to sleep. First of all, this may lead to data loss, but, second, a sleep 03395 * instruction cannot be effectuated when a burnlock is active. Thus we must 03396 * check if any task is writing. If there are writing tasks, all tasks that 03397 * want to read are readblocked, but if there are no writing tasks, readers 03398 * cannot be blocked, for they are released immediately after the writing 03399 * has finished. Thus we come to the conclusion there is no real need to store 03400 * a read-block-sleeping state. */ 03401 #if (cfgUseFileSystem == cfgTrue) 03402 /* Thus we exclude all those tasks from sleeping if the fs writeblock is activated 03403 * which are in read or write mode. (We cannot test for writeblock itself, since 03404 * there is always one task actually writing and not blocking. */ 03405 if ( ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear) || 03406 ((loopTCB->defFsField & (defFsReadGetMask | defFsWriteGetMask)) == (defFsReadClear | defFsWriteClear)) ) 03407 #endif 03408 /* Change the status to sleeping */ 03409 { loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBaseSleepingSetMask) | defBaseSleepingTask; } } } } 03410 03411 #endif 03412 03413 03414 #if (cfgUseFileSystem == cfgTrue) 03415 03416 static void privReleaseFileBlocks(void) 03417 { /* Call this method after you are done with file operations on a task and new tasks 03418 * may be given the opportunity to be released. Usually this is after a fileClose on 03419 * a file write operation, or after all reads are finished. */ 03420 /* First check if we have more task on the file system. If not, we can only arrive 03421 * here if the current (and only!) task has completed read or write operations and 03422 * closes the access to the file system. Now, since there are no other tasks that 03423 * may want access we have little to do. */ 03424 #if (defUseFsOnMultipleTasks == cfgTrue) 03425 /* It seems we have. We have to see in which state the file system is 03426 * Check if we have a write lock. When we have a write lock there must be at least 03427 * one task actually writing, so it makes no sense to release anything. When we don't 03428 * have a write lock we may have a read lock (i.e. there are tasks actually reading, but 03429 * we still search for tasks that may want to write. If we find these, we should not 03430 * accept new reads, until the tasks wanting to write has had the opportunity to do so. */ 03431 if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear) 03432 { /* It seems we don't have a write task, so loop through all tasks and search 03433 * for write blocks. We only search in those tasks making use of the file system. */ 03434 Tuint08 uiLoopTask; 03435 for (uiLoopTask=defTaskNumberFileSystemBegin; uiLoopTask<defTaskNumberFileSystemEnd; uiLoopTask++) 03436 { /* Now we must check if this is indeed a task with a file block (it could be an other kind of block) and if this 03437 * block is in the write mode. */ 03438 if ((privTcbList(uiLoopTask)->uiTaskStatus & (defBaseFileBlockedGetMask | defBaseDressGetMask)) == (defBaseFileBlockedTask | defBaseDressWrite)) 03439 { /* All right, this task is waiting for an opportunity to write to the file system. 03440 * There are two possibilities, or we can directly deblock to a lock, or we must issue 03441 * a write request. In both cases we must activate the write block in the file system 03442 * status (If there is already a read block present, this write block works as a local 03443 * flag signaling that a task is waiting for write access. Note we can never arrive here 03444 * in the other situation where both read and write block are activated, namely a writing 03445 * task waiting for file close. */ 03446 uiFsStatus = (uiFsStatus & defFsWriteBlockSetMask) | defFsWriteBlockActive; 03447 /* If there is not single-write-multiple-read synchronizer active, read blocks are not 03448 * possible (we read under the write lock, this is always permitted. */ 03449 #if (cfgUseFileSystemConcurrentRead == cfgTrue) 03450 /* Now, if the read block is set we are done, cause the write request is already set, 03451 * nothing more can be done. In case it is not set, we may appoint access to the current 03452 * task, and give it write access. */ 03453 if (((uiFsStatus & defFsReadBlockGetMask) == defFsReadBlockClear)) 03454 #endif 03455 { /* Here we must activate the task for writing, which implies that its task number is put into 03456 * the first nibble of the FsStatus. Furthermore, we clear the burn lock (it should already be 03457 * cleared btw). */ 03458 uiFsStatus = ((uiFsStatus & (defFsBurnBlockSetMask & defFsWriteNumberSetMask)) | (defFsBurnBlockClear | (uiLoopTask << defFsTaskNumberShift) ) ); 03459 /* The task was already blocked (we tested that, and besides, every task is blocked at 03460 * file-open per default). So we must unblock, but keep the lock. */ 03461 privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue); } 03462 break; } } } 03463 /* If we have simultaneous read possibility we must do some more research. */ 03464 #if (cfgUseFileSystemConcurrentRead == cfgTrue) 03465 /* It may seem strange, but we start again by looking at the WriteBlokc bit in the 03466 * FS status. If the bit is set, there are two possibilities. First, the read bit is 03467 * still cleared, so there is write action, and no further action with respect to reading 03468 * is needed. If the read bit is set also, it must be been set before we entered this method, 03469 * and the write bit must be interpreted as write request. In that case no new read tasks 03470 * may be activated. Same result, nothing to do. */ 03471 if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear) 03472 { /* No write bit, thus there is room for a new read task. Run through all tasks. */ 03473 Tuint08 uiLoopTask; 03474 for (uiLoopTask=defTaskNumberFileSystemBegin; uiLoopTask<defTaskNumberFileSystemEnd; uiLoopTask++) 03475 { /* Now we must check if this is indeed a task with a file block (it could be an other kind of block) and if this 03476 * block is in the read mode. */ 03477 if ((privTcbList(uiLoopTask)->uiTaskStatus & (defBaseFileBlockedGetMask | defBaseDressGetMask)) == (defBaseFileBlockedTask | defBaseDressRead)) 03478 { /* This task want to perform a read action. This is certainly possible, without further restrictions 03479 * since reading may be done in parallel. */ 03480 uiFsStatus