kopia lustrzana https://github.com/OpenRTX/OpenRTX
2164 wiersze
90 KiB
C
2164 wiersze
90 KiB
C
/*
|
|
*********************************************************************************************************
|
|
* uC/OS-III
|
|
* The Real-Time Kernel
|
|
*
|
|
* Copyright 2009-2020 Silicon Laboratories Inc. www.silabs.com
|
|
*
|
|
* SPDX-License-Identifier: APACHE-2.0
|
|
*
|
|
* This software is subject to an open source license and is distributed by
|
|
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
|
|
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* CORE FUNCTIONS
|
|
*
|
|
* File : os_core.c
|
|
* Version : V3.08.00
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define MICRIUM_SOURCE
|
|
#include "os.h"
|
|
|
|
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
|
|
const CPU_CHAR *os_core__c = "$Id: $";
|
|
#endif
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INITIALIZATION
|
|
*
|
|
* Description: This function is used to initialize the internals of uC/OS-III and MUST be called prior to
|
|
* creating any uC/OS-III object and, prior to calling OSStart().
|
|
*
|
|
* Arguments : p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* OS_ERR_NONE Initialization was successful
|
|
* Other Other OS_ERR_xxx depending on the sub-functions called by OSInit().
|
|
* Returns : none
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSInit (OS_ERR *p_err)
|
|
{
|
|
#if (OS_CFG_ISR_STK_SIZE > 0u)
|
|
CPU_STK *p_stk;
|
|
CPU_STK_SIZE size;
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
OSInitHook(); /* Call port specific initialization code */
|
|
|
|
OSIntNestingCtr = 0u; /* Clear the interrupt nesting counter */
|
|
|
|
OSRunning = OS_STATE_OS_STOPPED; /* Indicate that multitasking has not started */
|
|
|
|
OSSchedLockNestingCtr = 0u; /* Clear the scheduling lock counter */
|
|
|
|
OSTCBCurPtr = (OS_TCB *)0; /* Initialize OS_TCB pointers to a known state */
|
|
OSTCBHighRdyPtr = (OS_TCB *)0;
|
|
|
|
OSPrioCur = 0u; /* Initialize priority variables to a known state */
|
|
OSPrioHighRdy = 0u;
|
|
|
|
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
|
|
OSSchedLockTimeBegin = 0u;
|
|
OSSchedLockTimeMax = 0u;
|
|
OSSchedLockTimeMaxCur = 0u;
|
|
#endif
|
|
|
|
#ifdef OS_SAFETY_CRITICAL_IEC61508
|
|
OSSafetyCriticalStartFlag = OS_FALSE;
|
|
#endif
|
|
|
|
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
|
|
OSSchedRoundRobinEn = OS_FALSE;
|
|
OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
|
|
#endif
|
|
|
|
#if (OS_CFG_ISR_STK_SIZE > 0u)
|
|
p_stk = OSCfg_ISRStkBasePtr; /* Clear exception stack for stack checking. */
|
|
if (p_stk != (CPU_STK *)0) {
|
|
size = OSCfg_ISRStkSize;
|
|
while (size > 0u) {
|
|
size--;
|
|
*p_stk = 0u;
|
|
p_stk++;
|
|
}
|
|
}
|
|
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u) /* Initialize Redzoned ISR stack */
|
|
OS_TaskStkRedzoneInit(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
|
|
#endif
|
|
#endif
|
|
|
|
#if (OS_CFG_APP_HOOKS_EN > 0u) /* Clear application hook pointers */
|
|
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
|
|
OS_AppRedzoneHitHookPtr = (OS_APP_HOOK_TCB )0;
|
|
#endif
|
|
OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0;
|
|
OS_AppTaskDelHookPtr = (OS_APP_HOOK_TCB )0;
|
|
OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB )0;
|
|
|
|
OS_AppIdleTaskHookPtr = (OS_APP_HOOK_VOID)0;
|
|
OS_AppStatTaskHookPtr = (OS_APP_HOOK_VOID)0;
|
|
OS_AppTaskSwHookPtr = (OS_APP_HOOK_VOID)0;
|
|
OS_AppTimeTickHookPtr = (OS_APP_HOOK_VOID)0;
|
|
#endif
|
|
|
|
#if (OS_CFG_TASK_REG_TBL_SIZE > 0u)
|
|
OSTaskRegNextAvailID = 0u;
|
|
#endif
|
|
|
|
OS_PrioInit(); /* Initialize the priority bitmap table */
|
|
|
|
OS_RdyListInit(); /* Initialize the Ready List */
|
|
|
|
|
|
#if (OS_CFG_FLAG_EN > 0u) /* Initialize the Event Flag module */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSFlagDbgListPtr = (OS_FLAG_GRP *)0;
|
|
OSFlagQty = 0u;
|
|
#endif
|
|
#endif
|
|
|
|
#if (OS_CFG_MEM_EN > 0u) /* Initialize the Memory Manager module */
|
|
OS_MemInit(p_err);
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_MSG_EN > 0u) /* Initialize the free list of OS_MSGs */
|
|
OS_MsgPoolInit(p_err);
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_MUTEX_EN > 0u) /* Initialize the Mutex Manager module */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSMutexDbgListPtr = (OS_MUTEX *)0;
|
|
OSMutexQty = 0u;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_Q_EN > 0u) /* Initialize the Message Queue Manager module */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSQDbgListPtr = (OS_Q *)0;
|
|
OSQQty = 0u;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_SEM_EN > 0u) /* Initialize the Semaphore Manager module */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSSemDbgListPtr = (OS_SEM *)0;
|
|
OSSemQty = 0u;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
|
|
OS_TLS_Init(p_err); /* Initialize Task Local Storage, before creating tasks */
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
OS_TaskInit(p_err); /* Initialize the task manager */
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
OS_IdleTaskInit(p_err); /* Initialize the Idle Task */
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
OS_TickInit(p_err);
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_STAT_TASK_EN > 0u) /* Initialize the Statistic Task */
|
|
OS_StatTaskInit(p_err);
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_TMR_EN > 0u) /* Initialize the Timer Manager module */
|
|
OS_TmrInit(p_err);
|
|
if (*p_err != OS_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OS_Dbg_Init();
|
|
#endif
|
|
|
|
|
|
OSCfg_Init();
|
|
|
|
OSInitialized = OS_TRUE; /* Kernel is initialized */
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* ENTER ISR
|
|
*
|
|
* Description: This function is used to notify uC/OS-III that you are about to service an interrupt service routine
|
|
* (ISR). This allows uC/OS-III to keep track of interrupt nesting and thus only perform rescheduling at
|
|
* the last nested ISR.
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function MUST be called with interrupts already disabled
|
|
*
|
|
* 2) Your ISR can directly increment 'OSIntNestingCtr' without calling this function because OSIntNestingCtr has
|
|
* been declared 'global', the port is actually considered part of the OS and thus is allowed to access
|
|
* uC/OS-III variables.
|
|
*
|
|
* 3) You MUST still call OSIntExit() even though you increment 'OSIntNestingCtr' directly.
|
|
*
|
|
* 4) You MUST invoke OSIntEnter() and OSIntExit() in pair. In other words, for every call to OSIntEnter()
|
|
* (or direct increment to OSIntNestingCtr) at the beginning of the ISR you MUST have a call to OSIntExit()
|
|
* at the end of the ISR.
|
|
*
|
|
* 5) You are allowed to nest interrupts up to 250 levels deep.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSIntEnter (void)
|
|
{
|
|
OS_TRACE_ISR_ENTER();
|
|
|
|
if (OSRunning != OS_STATE_OS_RUNNING) { /* Is OS running? */
|
|
return; /* No */
|
|
}
|
|
|
|
if (OSIntNestingCtr >= 250u) { /* Have we nested past 250 levels? */
|
|
return; /* Yes */
|
|
}
|
|
|
|
OSIntNestingCtr++; /* Increment ISR nesting level */
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* EXIT ISR
|
|
*
|
|
* Description: This function is used to notify uC/OS-III that you have completed servicing an ISR. When the last nested
|
|
* ISR has completed, uC/OS-III will call the scheduler to determine whether a new, high-priority task, is
|
|
* ready to run.
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) You MUST invoke OSIntEnter() and OSIntExit() in pair. In other words, for every call to OSIntEnter()
|
|
* (or direct increment to OSIntNestingCtr) at the beginning of the ISR you MUST have a call to OSIntExit()
|
|
* at the end of the ISR.
|
|
*
|
|
* 2) Rescheduling is prevented when the scheduler is locked (see OSSchedLock())
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSIntExit (void)
|
|
{
|
|
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
|
|
CPU_BOOLEAN stk_status;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
|
|
if (OSRunning != OS_STATE_OS_RUNNING) { /* Has the OS started? */
|
|
OS_TRACE_ISR_EXIT();
|
|
return; /* No */
|
|
}
|
|
|
|
CPU_INT_DIS();
|
|
if (OSIntNestingCtr == 0u) { /* Prevent OSIntNestingCtr from wrapping */
|
|
OS_TRACE_ISR_EXIT();
|
|
CPU_INT_EN();
|
|
return;
|
|
}
|
|
OSIntNestingCtr--;
|
|
if (OSIntNestingCtr > 0u) { /* ISRs still nested? */
|
|
OS_TRACE_ISR_EXIT();
|
|
CPU_INT_EN(); /* Yes */
|
|
return;
|
|
}
|
|
|
|
if (OSSchedLockNestingCtr > 0u) { /* Scheduler still locked? */
|
|
OS_TRACE_ISR_EXIT();
|
|
CPU_INT_EN(); /* Yes */
|
|
return;
|
|
}
|
|
|
|
/* Verify ISR Stack */
|
|
#if (OS_CFG_ISR_STK_SIZE > 0u)
|
|
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
|
|
stk_status = OS_TaskStkRedzoneChk(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
|
|
if (stk_status != OS_TRUE) {
|
|
OSRedzoneHitHook((OS_TCB *)0);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
OSPrioHighRdy = OS_PrioGetHighest(); /* Find highest priority */
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* Get highest priority task ready-to-run */
|
|
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
|
|
/* Yes */
|
|
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
|
|
stk_status = OSTaskStkRedzoneChk((OS_TCB *)0);
|
|
if (stk_status != OS_TRUE) {
|
|
OSRedzoneHitHook(OSTCBCurPtr);
|
|
}
|
|
#endif
|
|
OS_TRACE_ISR_EXIT();
|
|
CPU_INT_EN();
|
|
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); /* Do this here because we don't execute OSIntCtxSw(). */
|
|
return;
|
|
}
|
|
#else
|
|
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { /* Are we returning to idle? */
|
|
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* No ... get highest priority task ready-to-run */
|
|
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
|
|
/* Yes */
|
|
OS_TRACE_ISR_EXIT();
|
|
CPU_INT_EN();
|
|
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); /* Do this here because we don't execute OSIntCtxSw(). */
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if (OS_CFG_TASK_PROFILE_EN > 0u)
|
|
OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches for this new task */
|
|
#endif
|
|
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
|
|
OSTaskCtxSwCtr++; /* Keep track of the total number of ctx switches */
|
|
#endif
|
|
|
|
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
|
|
OS_TLS_TaskSw();
|
|
#endif
|
|
|
|
OS_TRACE_ISR_EXIT_TO_SCHEDULER();
|
|
|
|
OSIntCtxSw(); /* Perform interrupt level ctx switch */
|
|
|
|
CPU_INT_EN();
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INDICATE THAT IT'S NO LONGER SAFE TO CREATE OBJECTS
|
|
*
|
|
* Description: This function is called by the application code to indicate that all initialization has been completed
|
|
* and that kernel objects are no longer allowed to be created.
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : none
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#ifdef OS_SAFETY_CRITICAL_IEC61508
|
|
void OSSafetyCriticalStart (void)
|
|
{
|
|
OSSafetyCriticalStartFlag = OS_TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* SCHEDULER
|
|
*
|
|
* Description: This function is called by other uC/OS-III services to determine whether a new, high priority task has
|
|
* been made ready to run. This function is invoked by TASK level code and is not used to reschedule tasks
|
|
* from ISRs (see OSIntExit() for ISR rescheduling).
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) Rescheduling is prevented when the scheduler is locked (see OSSchedLock())
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSSched (void)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u) /* Can't schedule when the kernel is stopped. */
|
|
if (OSRunning != OS_STATE_OS_RUNNING) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (OSIntNestingCtr > 0u) { /* ISRs still nested? */
|
|
return; /* Yes ... only schedule when no nested ISRs */
|
|
}
|
|
|
|
if (OSSchedLockNestingCtr > 0u) { /* Scheduler locked? */
|
|
return; /* Yes */
|
|
}
|
|
|
|
CPU_INT_DIS();
|
|
OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority ready */
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* Get highest priority task ready-to-run */
|
|
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
|
|
CPU_INT_EN(); /* Yes */
|
|
return;
|
|
}
|
|
#else
|
|
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { /* Are we returning to idle? */
|
|
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* No ... get highest priority task ready-to-run */
|
|
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
|
|
CPU_INT_EN(); /* Yes */
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
OS_TRACE_TASK_PREEMPT(OSTCBCurPtr);
|
|
|
|
#if (OS_CFG_TASK_PROFILE_EN > 0u)
|
|
OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches to this task */
|
|
#endif
|
|
|
|
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
|
|
OSTaskCtxSwCtr++; /* Increment context switch counter */
|
|
#endif
|
|
|
|
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
|
|
OS_TLS_TaskSw();
|
|
#endif
|
|
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
OS_TASK_SW(); /* Perform a task level context switch */
|
|
CPU_INT_EN();
|
|
#else
|
|
if ((OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u))) {
|
|
OS_TASK_SW(); /* Perform a task level context switch */
|
|
CPU_INT_EN();
|
|
} else {
|
|
OSTCBHighRdyPtr = OSTCBCurPtr;
|
|
CPU_INT_EN();
|
|
for (;;) {
|
|
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
|
|
CPU_CRITICAL_ENTER();
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSIdleTaskCtr++;
|
|
#endif
|
|
#if (OS_CFG_STAT_TASK_EN > 0u)
|
|
OSStatTaskCtr++;
|
|
#endif
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
#if (OS_CFG_APP_HOOKS_EN > 0u)
|
|
OSIdleTaskHook(); /* Call user definable HOOK */
|
|
#endif
|
|
if ((*((volatile OS_PRIO *)&OSPrioHighRdy) != (OS_CFG_PRIO_MAX - 1u))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS_TASK_SW_SYNC
|
|
OS_TASK_SW_SYNC();
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* PREVENT SCHEDULING
|
|
*
|
|
* Description: This function is used to prevent rescheduling from taking place. This allows your application to prevent
|
|
* context switches until you are ready to permit context switching.
|
|
*
|
|
* Arguments : p_err is a pointer to a variable that will receive an error code:
|
|
*
|
|
* OS_ERR_NONE The scheduler is locked
|
|
* OS_ERR_LOCK_NESTING_OVF If you attempted to nest call to this function > 250 levels
|
|
* OS_ERR_OS_NOT_RUNNING If uC/OS-III is not running yet
|
|
* OS_ERR_SCHED_LOCK_ISR If you called this function from an ISR
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) You MUST invoke OSSchedLock() and OSSchedUnlock() in pair. In other words, for every
|
|
* call to OSSchedLock() you MUST have a call to OSSchedUnlock().
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSSchedLock (OS_ERR *p_err)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
|
|
if (OSIntNestingCtr > 0u) { /* Not allowed to call from an ISR */
|
|
*p_err = OS_ERR_SCHED_LOCK_ISR;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (OSRunning != OS_STATE_OS_RUNNING) { /* Make sure multitasking is running */
|
|
*p_err = OS_ERR_OS_NOT_RUNNING;
|
|
return;
|
|
}
|
|
|
|
if (OSSchedLockNestingCtr >= 250u) { /* Prevent OSSchedLockNestingCtr overflowing */
|
|
*p_err = OS_ERR_LOCK_NESTING_OVF;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
OSSchedLockNestingCtr++; /* Increment lock nesting level */
|
|
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
|
|
OS_SchedLockTimeMeasStart();
|
|
#endif
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = OS_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* ENABLE SCHEDULING
|
|
*
|
|
* Description: This function is used to re-allow rescheduling.
|
|
*
|
|
* Arguments : p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* OS_ERR_NONE The scheduler has been enabled
|
|
* OS_ERR_OS_NOT_RUNNING If uC/OS-III is not running yet
|
|
* OS_ERR_SCHED_LOCKED The scheduler is still locked, still nested
|
|
* OS_ERR_SCHED_NOT_LOCKED The scheduler was not locked
|
|
* OS_ERR_SCHED_UNLOCK_ISR If you called this function from an ISR
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) You MUST invoke OSSchedLock() and OSSchedUnlock() in pair. In other words, for every call to
|
|
* OSSchedLock() you MUST have a call to OSSchedUnlock().
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSSchedUnlock (OS_ERR *p_err)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
|
|
if (OSIntNestingCtr > 0u) { /* Not allowed to call from an ISR */
|
|
*p_err = OS_ERR_SCHED_UNLOCK_ISR;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (OSRunning != OS_STATE_OS_RUNNING) { /* Make sure multitasking is running */
|
|
*p_err = OS_ERR_OS_NOT_RUNNING;
|
|
return;
|
|
}
|
|
|
|
if (OSSchedLockNestingCtr == 0u) { /* See if the scheduler is locked */
|
|
*p_err = OS_ERR_SCHED_NOT_LOCKED;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
OSSchedLockNestingCtr--; /* Decrement lock nesting level */
|
|
if (OSSchedLockNestingCtr > 0u) {
|
|
CPU_CRITICAL_EXIT(); /* Scheduler is still locked */
|
|
*p_err = OS_ERR_SCHED_LOCKED;
|
|
return;
|
|
}
|
|
|
|
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
|
|
OS_SchedLockTimeMeasStop();
|
|
#endif
|
|
|
|
CPU_CRITICAL_EXIT(); /* Scheduler should be re-enabled */
|
|
OSSched(); /* Run the scheduler */
|
|
*p_err = OS_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* CONFIGURE ROUND-ROBIN SCHEDULING PARAMETERS
|
|
*
|
|
* Description: This function is called to change the round-robin scheduling parameters.
|
|
*
|
|
* Arguments : en determines whether round-robin will be enabled (when OS_TRUE) or not (when OS_FALSE)
|
|
*
|
|
* dflt_time_quanta default number of ticks between time slices. 0 means OSCfg_TickRate_Hz / 10.
|
|
*
|
|
* p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* OS_ERR_NONE The call was successful
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : none
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
|
|
void OSSchedRoundRobinCfg (CPU_BOOLEAN en,
|
|
OS_TICK dflt_time_quanta,
|
|
OS_ERR *p_err)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (en == 0u) {
|
|
OSSchedRoundRobinEn = OS_FALSE;
|
|
} else {
|
|
OSSchedRoundRobinEn = OS_TRUE;
|
|
}
|
|
|
|
if (dflt_time_quanta > 0u) {
|
|
OSSchedRoundRobinDfltTimeQuanta = dflt_time_quanta;
|
|
} else {
|
|
OSSchedRoundRobinDfltTimeQuanta = (OS_TICK)(OSCfg_TickRate_Hz / 10u);
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = OS_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* YIELD CPU WHEN TASK NO LONGER NEEDS THE TIME SLICE
|
|
*
|
|
* Description: This function is called to give up the CPU when a task is done executing before its time slice expires.
|
|
*
|
|
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* OS_ERR_NONE The call was successful
|
|
* OS_ERR_ROUND_ROBIN_1 Only 1 task at this priority, nothing to yield to
|
|
* OS_ERR_ROUND_ROBIN_DISABLED Round Robin is not enabled
|
|
* OS_ERR_SCHED_LOCKED The scheduler has been locked
|
|
* OS_ERR_YIELD_ISR Can't be called from an ISR
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function MUST be called from a task.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
|
|
void OSSchedRoundRobinYield (OS_ERR *p_err)
|
|
{
|
|
OS_RDY_LIST *p_rdy_list;
|
|
OS_TCB *p_tcb;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
|
|
if (OSIntNestingCtr > 0u) { /* Can't call this function from an ISR */
|
|
*p_err = OS_ERR_YIELD_ISR;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (OSSchedLockNestingCtr > 0u) { /* Can't yield if the scheduler is locked */
|
|
*p_err = OS_ERR_SCHED_LOCKED;
|
|
return;
|
|
}
|
|
|
|
if (OSSchedRoundRobinEn != OS_TRUE) { /* Make sure round-robin has been enabled */
|
|
*p_err = OS_ERR_ROUND_ROBIN_DISABLED;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_rdy_list = &OSRdyList[OSPrioCur]; /* Can't yield if it's the only task at that priority */
|
|
if (p_rdy_list->HeadPtr == p_rdy_list->TailPtr) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = OS_ERR_ROUND_ROBIN_1;
|
|
return;
|
|
}
|
|
|
|
OS_RdyListMoveHeadToTail(p_rdy_list); /* Move current OS_TCB to the end of the list */
|
|
p_tcb = p_rdy_list->HeadPtr; /* Point to new OS_TCB at head of the list */
|
|
if (p_tcb->TimeQuanta == 0u) { /* See if we need to use the default time slice */
|
|
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
|
|
} else {
|
|
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time */
|
|
}
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
OSSched(); /* Run new task */
|
|
*p_err = OS_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* START MULTITASKING
|
|
*
|
|
* Description: This function is used to start the multitasking process which lets uC/OS-III manage the task that you
|
|
* created. Before you can call OSStart(), you MUST have called OSInit() and you MUST have created at least
|
|
* one application task.
|
|
*
|
|
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* OS_ERR_FATAL_RETURN OS was running and OSStart() returned
|
|
* OS_ERR_OS_NOT_INIT OS is not initialized, OSStart() has no effect
|
|
* OS_ERR_OS_NO_APP_TASK No application task created, OSStart() has no effect
|
|
* OS_ERR_OS_RUNNING OS is already running, OSStart() has no effect
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) OSStartHighRdy() MUST:
|
|
* a) Call OSTaskSwHook() then,
|
|
* b) Load the context of the task pointed to by OSTCBHighRdyPtr.
|
|
* c) Execute the task.
|
|
*
|
|
* 2) OSStart() is not supposed to return. If it does, that would be considered a fatal error.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OSStart (OS_ERR *p_err)
|
|
{
|
|
OS_OBJ_QTY kernel_task_cnt;
|
|
|
|
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (OSInitialized != OS_TRUE) {
|
|
*p_err = OS_ERR_OS_NOT_INIT;
|
|
return;
|
|
}
|
|
|
|
kernel_task_cnt = 0u; /* Calculate the number of kernel tasks */
|
|
#if (OS_CFG_STAT_TASK_EN > 0u)
|
|
kernel_task_cnt++;
|
|
#endif
|
|
#if (OS_CFG_TMR_EN > 0u)
|
|
kernel_task_cnt++;
|
|
#endif
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
kernel_task_cnt++;
|
|
#endif
|
|
|
|
if (OSTaskQty <= kernel_task_cnt) { /* No application task created */
|
|
*p_err = OS_ERR_OS_NO_APP_TASK;
|
|
return;
|
|
}
|
|
|
|
if (OSRunning == OS_STATE_OS_STOPPED) {
|
|
OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority */
|
|
OSPrioCur = OSPrioHighRdy;
|
|
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
|
|
OSTCBCurPtr = OSTCBHighRdyPtr;
|
|
OSRunning = OS_STATE_OS_RUNNING;
|
|
OSStartHighRdy(); /* Execute target specific code to start task */
|
|
*p_err = OS_ERR_FATAL_RETURN; /* OSStart() is not supposed to return */
|
|
} else {
|
|
*p_err = OS_ERR_OS_RUNNING; /* OS is already running */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* GET VERSION
|
|
*
|
|
* Description: This function is used to return the version number of uC/OS-III. The returned value corresponds to
|
|
* uC/OS-III's version number multiplied by 10000. In other words, version 3.01.02 would be returned as 30102.
|
|
*
|
|
* Arguments : p_err is a pointer to a variable that will receive an error code. However, OSVersion() set this
|
|
* variable to
|
|
*
|
|
* OS_ERR_NONE
|
|
*
|
|
* Returns : The version number of uC/OS-III multiplied by 10000.
|
|
*
|
|
* Note(s) : none
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT16U OSVersion (OS_ERR *p_err)
|
|
{
|
|
#ifdef OS_SAFETY_CRITICAL
|
|
if (p_err == (OS_ERR *)0) {
|
|
OS_SAFETY_CRITICAL_EXCEPTION();
|
|
return (0u);
|
|
}
|
|
#endif
|
|
|
|
*p_err = OS_ERR_NONE;
|
|
return (OS_VERSION);
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* IDLE TASK
|
|
*
|
|
* Description: This task is internal to uC/OS-III and executes whenever no other higher priority tasks executes because
|
|
* they are ALL waiting for event(s) to occur.
|
|
*
|
|
* Arguments : p_arg is an argument passed to the task when the task is created.
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
*
|
|
* 2) OSIdleTaskHook() is called after the critical section to ensure that interrupts will be enabled for at
|
|
* least a few instructions. On some processors (ex. Philips XA), enabling and then disabling interrupts
|
|
* doesn't allow the processor enough time to have interrupts enabled before they were disabled again.
|
|
* uC/OS-III would thus never recognize interrupts.
|
|
*
|
|
* 3) This hook has been added to allow you to do such things as STOP the CPU to conserve power.
|
|
************************************************************************************************************************
|
|
*/
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
void OS_IdleTask (void *p_arg)
|
|
{
|
|
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
|
|
CPU_SR_ALLOC();
|
|
#endif
|
|
|
|
|
|
(void)p_arg; /* Prevent compiler warning for not using 'p_arg' */
|
|
|
|
for (;;) {
|
|
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
|
|
CPU_CRITICAL_ENTER();
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSIdleTaskCtr++;
|
|
#endif
|
|
#if (OS_CFG_STAT_TASK_EN > 0u)
|
|
OSStatTaskCtr++;
|
|
#endif
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
#if (OS_CFG_APP_HOOKS_EN > 0u)
|
|
OSIdleTaskHook(); /* Call user definable HOOK */
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INITIALIZE THE IDLE TASK
|
|
*
|
|
* Description: This function initializes the idle task
|
|
*
|
|
* Arguments : p_err is a pointer to a variable that will contain an error code returned by this function.
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
void OS_IdleTaskInit (OS_ERR *p_err)
|
|
{
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OSIdleTaskCtr = 0u;
|
|
#endif
|
|
/* --------------- CREATE THE IDLE TASK --------------- */
|
|
OSTaskCreate(&OSIdleTaskTCB,
|
|
#if (OS_CFG_DBG_EN == 0u)
|
|
(CPU_CHAR *)0,
|
|
#else
|
|
(CPU_CHAR *)"uC/OS-III Idle Task",
|
|
#endif
|
|
OS_IdleTask,
|
|
(void *)0,
|
|
(OS_PRIO )(OS_CFG_PRIO_MAX - 1u),
|
|
OSCfg_IdleTaskStkBasePtr,
|
|
OSCfg_IdleTaskStkLimit,
|
|
OSCfg_IdleTaskStkSize,
|
|
0u,
|
|
0u,
|
|
(void *)0,
|
|
(OS_OPT_TASK_STK_CHK | (OS_OPT)(OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS)),
|
|
p_err);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* BLOCK A TASK PENDING ON EVENT
|
|
*
|
|
* Description: This function is called to place a task in the blocked state waiting for an event to occur. This function
|
|
* exists because it is common to a number of OSxxxPend() services.
|
|
*
|
|
* Arguments : p_obj is a pointer to the object to pend on. If there are no object used to pend on then
|
|
* ----- the caller must pass a NULL pointer.
|
|
*
|
|
* p_tcb is the task that will be blocked.
|
|
*
|
|
* pending_on Specifies what the task will be pending on:
|
|
*
|
|
* OS_TASK_PEND_ON_FLAG
|
|
* OS_TASK_PEND_ON_TASK_Q <- No object (pending for a message sent to the task)
|
|
* OS_TASK_PEND_ON_MUTEX
|
|
* OS_TASK_PEND_ON_COND
|
|
* OS_TASK_PEND_ON_Q
|
|
* OS_TASK_PEND_ON_SEM
|
|
* OS_TASK_PEND_ON_TASK_SEM <- No object (pending on a signal sent to the task)
|
|
*
|
|
* timeout Is the amount of time the task will wait for the event to occur.
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_Pend (OS_PEND_OBJ *p_obj,
|
|
OS_TCB *p_tcb,
|
|
OS_STATE pending_on,
|
|
OS_TICK timeout)
|
|
{
|
|
OS_PEND_LIST *p_pend_list;
|
|
|
|
|
|
p_tcb->PendOn = pending_on; /* Resource not available, wait until it is */
|
|
p_tcb->PendStatus = OS_STATUS_PEND_OK;
|
|
|
|
OS_TaskBlock(p_tcb, /* Block the task and add it to the tick list if needed */
|
|
timeout);
|
|
|
|
if (p_obj != (OS_PEND_OBJ *)0) { /* Add the current task to the pend list ... */
|
|
p_pend_list = &p_obj->PendList; /* ... if there is an object to pend on */
|
|
p_tcb->PendObjPtr = p_obj; /* Save the pointer to the object pending on */
|
|
OS_PendListInsertPrio(p_pend_list, /* Insert in the pend list in priority order */
|
|
p_tcb);
|
|
|
|
} else {
|
|
p_tcb->PendObjPtr = (OS_PEND_OBJ *)0; /* If no object being pended on, clear the pend object */
|
|
}
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OS_PendDbgNameAdd(p_obj,
|
|
p_tcb);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* CANCEL PENDING
|
|
*
|
|
* Description: This function is called by the OSxxxPendAbort() and OSxxxDel() functions to cancel pending on an event.
|
|
*
|
|
* Arguments : p_tcb Is a pointer to the OS_TCB of the task that we'll abort the pend for
|
|
* -----
|
|
*
|
|
* ts Is a timestamp as to when the pend was cancelled
|
|
*
|
|
* reason Indicates how the task was readied:
|
|
*
|
|
* OS_STATUS_PEND_DEL Object pended on was deleted.
|
|
* OS_STATUS_PEND_ABORT Pend was aborted.
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_PendAbort (OS_TCB *p_tcb,
|
|
CPU_TS ts,
|
|
OS_STATUS reason)
|
|
{
|
|
#if (OS_CFG_TS_EN == 0u)
|
|
(void)ts; /* Prevent compiler warning for not using 'ts' */
|
|
#endif
|
|
|
|
switch (p_tcb->TaskState) {
|
|
case OS_TASK_STATE_PEND:
|
|
case OS_TASK_STATE_PEND_TIMEOUT:
|
|
#if (OS_MSG_EN > 0u)
|
|
p_tcb->MsgPtr = (void *)0;
|
|
p_tcb->MsgSize = 0u;
|
|
#endif
|
|
#if (OS_CFG_TS_EN > 0u)
|
|
p_tcb->TS = ts;
|
|
#endif
|
|
OS_PendListRemove(p_tcb); /* Remove task from the pend list */
|
|
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
|
|
OS_TickListRemove(p_tcb); /* Cancel the timeout */
|
|
}
|
|
#endif
|
|
OS_RdyListInsert(p_tcb); /* Insert the task in the ready list */
|
|
p_tcb->TaskState = OS_TASK_STATE_RDY; /* Task will be ready */
|
|
p_tcb->PendStatus = reason; /* Indicate how the task became ready */
|
|
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
|
|
break;
|
|
|
|
case OS_TASK_STATE_PEND_SUSPENDED:
|
|
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
|
|
#if (OS_MSG_EN > 0u)
|
|
p_tcb->MsgPtr = (void *)0;
|
|
p_tcb->MsgSize = 0u;
|
|
#endif
|
|
#if (OS_CFG_TS_EN > 0u)
|
|
p_tcb->TS = ts;
|
|
#endif
|
|
OS_PendListRemove(p_tcb); /* Remove task from the pend list */
|
|
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
|
|
OS_TickListRemove(p_tcb); /* Cancel the timeout */
|
|
}
|
|
#endif
|
|
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; /* Task needs to remain suspended */
|
|
p_tcb->PendStatus = reason; /* Indicate how the task became ready */
|
|
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
|
|
break;
|
|
|
|
case OS_TASK_STATE_RDY: /* Cannot cancel a pend when a task is in these states. */
|
|
case OS_TASK_STATE_DLY:
|
|
case OS_TASK_STATE_SUSPENDED:
|
|
case OS_TASK_STATE_DLY_SUSPENDED:
|
|
default:
|
|
/* Default case. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* ADD/REMOVE DEBUG NAMES TO PENDED OBJECT AND OS_TCB
|
|
*
|
|
* Description: These functions are used to add pointers to ASCII 'names' of objects so they can easily be displayed
|
|
* using a kernel aware tool.
|
|
*
|
|
* Arguments : p_obj is a pointer to the object being pended on
|
|
*
|
|
* p_tcb is a pointer to the OS_TCB of the task pending on the object
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) These functions are INTERNAL to uC/OS-III and your application must not call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
void OS_PendDbgNameAdd (OS_PEND_OBJ *p_obj,
|
|
OS_TCB *p_tcb)
|
|
{
|
|
OS_PEND_LIST *p_pend_list;
|
|
OS_TCB *p_tcb1;
|
|
|
|
|
|
if (p_obj != (OS_PEND_OBJ *)0) {
|
|
p_tcb->DbgNamePtr = p_obj->NamePtr; /* Task pending on this object ... save name in TCB */
|
|
p_pend_list = &p_obj->PendList; /* Find name of HP task pending on this object ... */
|
|
p_tcb1 = p_pend_list->HeadPtr;
|
|
p_obj->DbgNamePtr = p_tcb1->NamePtr; /* ... Save in object */
|
|
} else {
|
|
switch (p_tcb->PendOn) {
|
|
case OS_TASK_PEND_ON_TASK_Q:
|
|
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)"Task Q");
|
|
break;
|
|
|
|
case OS_TASK_PEND_ON_TASK_SEM:
|
|
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)"Task Sem");
|
|
break;
|
|
|
|
default:
|
|
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" ");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void OS_PendDbgNameRemove (OS_PEND_OBJ *p_obj,
|
|
OS_TCB *p_tcb)
|
|
{
|
|
OS_PEND_LIST *p_pend_list;
|
|
OS_TCB *p_tcb1;
|
|
|
|
|
|
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* Remove name of object pended on for readied task */
|
|
|
|
if (p_obj != (OS_PEND_OBJ *)0) {
|
|
p_pend_list = &p_obj->PendList;
|
|
p_tcb1 = p_pend_list->HeadPtr;
|
|
if (p_tcb1 != (OS_TCB *)0) { /* Find name of HP task pending on this object ... */
|
|
p_obj->DbgNamePtr = p_tcb1->NamePtr; /* ... Save in object */
|
|
} else {
|
|
p_obj->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* Or no other task is pending on object */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* CHANGE THE PRIORITY OF A TASK WAITING IN A PEND LIST
|
|
*
|
|
* Description: This function is called to change the position of a task waiting in a pend list. The strategy used is to
|
|
* remove the task from the pend list and add it again using its changed priority.
|
|
*
|
|
* Arguments : p_tcb is a pointer to the TCB of the task to move
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
*
|
|
* 2) It's assumed that the TCB contains the NEW priority in its .Prio field.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_PendListChangePrio (OS_TCB *p_tcb)
|
|
{
|
|
OS_PEND_LIST *p_pend_list;
|
|
OS_PEND_OBJ *p_obj;
|
|
|
|
p_obj = p_tcb->PendObjPtr; /* Get pointer to pend list */
|
|
p_pend_list = &p_obj->PendList;
|
|
|
|
if (p_pend_list->HeadPtr->PendNextPtr != (OS_TCB *)0) { /* Only move if multiple entries in the list */
|
|
OS_PendListRemove(p_tcb); /* Remove entry from current position */
|
|
p_tcb->PendObjPtr = p_obj;
|
|
OS_PendListInsertPrio(p_pend_list, /* INSERT it back in the list */
|
|
p_tcb);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INITIALIZE A WAIT LIST
|
|
*
|
|
* Description: This function is called to initialize the fields of an OS_PEND_LIST.
|
|
*
|
|
* Arguments : p_pend_list is a pointer to an OS_PEND_LIST
|
|
* -----------
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application must not call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_PendListInit (OS_PEND_LIST *p_pend_list)
|
|
{
|
|
p_pend_list->HeadPtr = (OS_TCB *)0;
|
|
p_pend_list->TailPtr = (OS_TCB *)0;
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_pend_list->NbrEntries = 0u;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INSERT A TASK BASED ON IT'S PRIORITY IN A PEND LIST
|
|
*
|
|
* Description: This function is called to place an OS_TCB entry in a linked list based on its priority. The
|
|
* highest priority being placed at the head of the list. The TCB is assumed to contain the priority
|
|
* of the task in its .Prio field.
|
|
*
|
|
* CASE 0: Insert in an empty list.
|
|
*
|
|
* OS_PEND_LIST
|
|
* +---------------+
|
|
* | TailPtr |-> 0
|
|
* +---------------+
|
|
* | HeadPtr |-> 0
|
|
* +---------------+
|
|
* | NbrEntries=0 |
|
|
* +---------------+
|
|
*
|
|
*
|
|
*
|
|
* CASE 1: Insert BEFORE or AFTER an OS_TCB
|
|
*
|
|
* OS_PEND_LIST
|
|
* +--------------+ OS_TCB
|
|
* | TailPtr |--+---> +--------------+
|
|
* +--------------+ | | PendNextPtr |->0
|
|
* | HeadPtr |--/ +--------------+
|
|
* +--------------+ 0<-| PendPrevPtr |
|
|
* | NbrEntries=1 | +--------------+
|
|
* +--------------+ | |
|
|
* +--------------+
|
|
* | |
|
|
* +--------------+
|
|
*
|
|
*
|
|
* OS_PEND_LIST
|
|
* +--------------+
|
|
* | TailPtr |---------------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +--------------+ +--------------+ +-> +--------------+
|
|
* +--------------+ | PendNextPtr |<------| PendNextPtr | ...... | PendNextPtr |->0
|
|
* | NbrEntries=N | +--------------+ +--------------+ +--------------+
|
|
* +--------------+ 0<-| PendPrevPtr |<------| PendPrevPtr | ...... | PendPrevPtr |
|
|
* +--------------+ +--------------+ +--------------+
|
|
* | | | | | |
|
|
* +--------------+ +--------------+ +--------------+
|
|
* | | | | | |
|
|
* +--------------+ +--------------+ +--------------+
|
|
*
|
|
*
|
|
* Arguments : p_pend_list is a pointer to the OS_PEND_LIST where the OS_TCB entry will be inserted
|
|
* -----------
|
|
*
|
|
* p_tcb is the OS_TCB to insert in the list
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_PendListInsertPrio (OS_PEND_LIST *p_pend_list,
|
|
OS_TCB *p_tcb)
|
|
{
|
|
OS_PRIO prio;
|
|
OS_TCB *p_tcb_next;
|
|
|
|
|
|
prio = p_tcb->Prio; /* Obtain the priority of the task to insert */
|
|
|
|
if (p_pend_list->HeadPtr == (OS_TCB *)0) { /* CASE 0: Insert when there are no entries */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_pend_list->NbrEntries = 1u; /* This is the first entry */
|
|
#endif
|
|
p_tcb->PendNextPtr = (OS_TCB *)0; /* No other OS_TCBs in the list */
|
|
p_tcb->PendPrevPtr = (OS_TCB *)0;
|
|
p_pend_list->HeadPtr = p_tcb;
|
|
p_pend_list->TailPtr = p_tcb;
|
|
} else {
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_pend_list->NbrEntries++; /* CASE 1: One more OS_TCBs in the list */
|
|
#endif
|
|
p_tcb_next = p_pend_list->HeadPtr;
|
|
while (p_tcb_next != (OS_TCB *)0) { /* Find the position where to insert */
|
|
if (prio < p_tcb_next->Prio) {
|
|
break; /* Found! ... insert BEFORE current */
|
|
} else {
|
|
p_tcb_next = p_tcb_next->PendNextPtr; /* Not Found, follow the list */
|
|
}
|
|
}
|
|
if (p_tcb_next == (OS_TCB *)0) { /* TCB to insert is lowest in priority */
|
|
p_tcb->PendNextPtr = (OS_TCB *)0; /* ... insert at the tail. */
|
|
p_tcb->PendPrevPtr = p_pend_list->TailPtr;
|
|
p_tcb->PendPrevPtr->PendNextPtr = p_tcb;
|
|
p_pend_list->TailPtr = p_tcb;
|
|
} else {
|
|
if (p_tcb_next->PendPrevPtr == (OS_TCB *)0) { /* Is new TCB highest priority? */
|
|
p_tcb->PendNextPtr = p_tcb_next; /* Yes, insert as new Head of list */
|
|
p_tcb->PendPrevPtr = (OS_TCB *)0;
|
|
p_tcb_next->PendPrevPtr = p_tcb;
|
|
p_pend_list->HeadPtr = p_tcb;
|
|
} else { /* No, insert in between two entries */
|
|
p_tcb->PendNextPtr = p_tcb_next;
|
|
p_tcb->PendPrevPtr = p_tcb_next->PendPrevPtr;
|
|
p_tcb->PendPrevPtr->PendNextPtr = p_tcb;
|
|
p_tcb_next->PendPrevPtr = p_tcb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* REMOVE TASK FROM A PEND LIST KNOWING ONLY WHICH TCB TO REMOVE
|
|
*
|
|
* Description: This function is called to remove a task from a pend list knowing the TCB of the task to remove.
|
|
*
|
|
* CASE 0: OS_PEND_LIST list is empty, nothing to do.
|
|
*
|
|
* CASE 1: Only 1 OS_TCB in the list.
|
|
*
|
|
* OS_PEND_LIST
|
|
* +--------------+ OS_TCB
|
|
* | TailPtr |--+---> +--------------+
|
|
* +--------------+ | | PendNextPtr |->0
|
|
* | HeadPtr |--/ +--------------+
|
|
* +--------------+ 0<-| PendPrevPtr |
|
|
* | NbrEntries=1 | +--------------+
|
|
* +--------------+ | |
|
|
* +--------------+
|
|
* | |
|
|
* +--------------+
|
|
*
|
|
* CASE N: Two or more OS_TCBs in the list.
|
|
*
|
|
* OS_PEND_LIST
|
|
* +--------------+
|
|
* | TailPtr |---------------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +--------------+ +--------------+ +-> +--------------+
|
|
* +--------------+ | PendNextPtr |<------| PendNextPtr | ...... | PendNextPtr |->0
|
|
* | NbrEntries=N | +--------------+ +--------------+ +--------------+
|
|
* +--------------+ 0<-| PendPrevPtr |<------| PendPrevPtr | ...... | PendPrevPtr |
|
|
* +--------------+ +--------------+ +--------------+
|
|
* | | | | | |
|
|
* +--------------+ +--------------+ +--------------+
|
|
* | | | | | |
|
|
* +--------------+ +--------------+ +--------------+
|
|
*
|
|
*
|
|
* Arguments : p_tcb is a pointer to the TCB of the task to remove from the pend list
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_PendListRemove (OS_TCB *p_tcb)
|
|
{
|
|
OS_PEND_LIST *p_pend_list;
|
|
OS_TCB *p_next;
|
|
OS_TCB *p_prev;
|
|
|
|
|
|
if (p_tcb->PendObjPtr != (OS_PEND_OBJ *)0) { /* Only remove if object has a pend list. */
|
|
p_pend_list = &p_tcb->PendObjPtr->PendList; /* Get pointer to pend list */
|
|
|
|
/* Remove TCB from the pend list. */
|
|
if (p_pend_list->HeadPtr->PendNextPtr == (OS_TCB *)0) {
|
|
p_pend_list->HeadPtr = (OS_TCB *)0; /* Only one entry in the pend list */
|
|
p_pend_list->TailPtr = (OS_TCB *)0;
|
|
} else if (p_tcb->PendPrevPtr == (OS_TCB *)0) { /* See if entry is at the head of the list */
|
|
p_next = p_tcb->PendNextPtr; /* Yes */
|
|
p_next->PendPrevPtr = (OS_TCB *)0;
|
|
p_pend_list->HeadPtr = p_next;
|
|
|
|
} else if (p_tcb->PendNextPtr == (OS_TCB *)0) { /* See if entry is at the tail of the list */
|
|
p_prev = p_tcb->PendPrevPtr; /* Yes */
|
|
p_prev->PendNextPtr = (OS_TCB *)0;
|
|
p_pend_list->TailPtr = p_prev;
|
|
|
|
} else {
|
|
p_prev = p_tcb->PendPrevPtr; /* Remove from inside the list */
|
|
p_next = p_tcb->PendNextPtr;
|
|
p_prev->PendNextPtr = p_next;
|
|
p_next->PendPrevPtr = p_prev;
|
|
}
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_pend_list->NbrEntries--; /* One less entry in the list */
|
|
#endif
|
|
p_tcb->PendNextPtr = (OS_TCB *)0;
|
|
p_tcb->PendPrevPtr = (OS_TCB *)0;
|
|
p_tcb->PendObjPtr = (OS_PEND_OBJ *)0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* POST TO A TASK
|
|
*
|
|
* Description: This function is called to post to a task. This function exist because it is common to a number of
|
|
* OSxxxPost() services.
|
|
*
|
|
* Arguments : p_obj Is a pointer to the object being posted to or NULL pointer if there is no object
|
|
* -----
|
|
*
|
|
* p_tcb Is a pointer to the OS_TCB that will receive the 'post'
|
|
* -----
|
|
*
|
|
* p_void If we are posting a message to a task, this is the message that the task will receive
|
|
*
|
|
* msg_size If we are posting a message to a task, this is the size of the message
|
|
*
|
|
* ts The timestamp as to when the post occurred
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_Post (OS_PEND_OBJ *p_obj,
|
|
OS_TCB *p_tcb,
|
|
void *p_void,
|
|
OS_MSG_SIZE msg_size,
|
|
CPU_TS ts)
|
|
{
|
|
#if (OS_CFG_TS_EN == 0u)
|
|
(void)ts; /* Prevent compiler warning for not using 'ts' */
|
|
#endif
|
|
#if (OS_MSG_EN == 0u)
|
|
(void)p_void;
|
|
(void)msg_size;
|
|
#endif
|
|
|
|
switch (p_tcb->TaskState) {
|
|
case OS_TASK_STATE_RDY: /* Cannot Post a task that is ready */
|
|
case OS_TASK_STATE_DLY: /* Cannot Post a task that is delayed */
|
|
case OS_TASK_STATE_SUSPENDED: /* Cannot Post a suspended task */
|
|
case OS_TASK_STATE_DLY_SUSPENDED: /* Cannot Post a suspended task that was also dly'd */
|
|
break;
|
|
|
|
case OS_TASK_STATE_PEND:
|
|
case OS_TASK_STATE_PEND_TIMEOUT:
|
|
#if (OS_MSG_EN > 0u)
|
|
p_tcb->MsgPtr = p_void; /* Deposit message in OS_TCB of task waiting */
|
|
p_tcb->MsgSize = msg_size; /* ... assuming posting a message */
|
|
#endif
|
|
#if (OS_CFG_TS_EN > 0u)
|
|
p_tcb->TS = ts;
|
|
#endif
|
|
if (p_obj != (OS_PEND_OBJ *)0) {
|
|
OS_PendListRemove(p_tcb); /* Remove task from pend list */
|
|
}
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OS_PendDbgNameRemove(p_obj,
|
|
p_tcb);
|
|
#endif
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
|
|
OS_TickListRemove(p_tcb); /* Remove from tick list */
|
|
}
|
|
#endif
|
|
OS_RdyListInsert(p_tcb); /* Insert the task in the ready list */
|
|
p_tcb->TaskState = OS_TASK_STATE_RDY;
|
|
p_tcb->PendStatus = OS_STATUS_PEND_OK; /* Clear pend status */
|
|
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
|
|
break;
|
|
|
|
case OS_TASK_STATE_PEND_SUSPENDED:
|
|
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
|
|
#if (OS_MSG_EN > 0u)
|
|
p_tcb->MsgPtr = p_void; /* Deposit message in OS_TCB of task waiting */
|
|
p_tcb->MsgSize = msg_size; /* ... assuming posting a message */
|
|
#endif
|
|
#if (OS_CFG_TS_EN > 0u)
|
|
p_tcb->TS = ts;
|
|
#endif
|
|
if (p_obj != (OS_PEND_OBJ *)0) {
|
|
OS_PendListRemove(p_tcb); /* Remove from pend list */
|
|
}
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
OS_PendDbgNameRemove(p_obj,
|
|
p_tcb);
|
|
#endif
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
|
|
OS_TickListRemove(p_tcb); /* Cancel any timeout */
|
|
}
|
|
#endif
|
|
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
|
|
p_tcb->PendStatus = OS_STATUS_PEND_OK; /* Clear pend status */
|
|
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
|
|
break;
|
|
|
|
default:
|
|
/* Default case. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INITIALIZATION
|
|
* READY LIST INITIALIZATION
|
|
*
|
|
* Description: This function is called by OSInit() to initialize the ready list. The ready list contains a list of all
|
|
* the tasks that are ready to run. The list is actually an array of OS_RDY_LIST. An OS_RDY_LIST contains
|
|
* three fields. The number of OS_TCBs in the list (i.e. .NbrEntries), a pointer to the first OS_TCB in the
|
|
* OS_RDY_LIST (i.e. .HeadPtr) and a pointer to the last OS_TCB in the OS_RDY_LIST (i.e. .TailPtr).
|
|
*
|
|
* OS_TCBs are doubly linked in the OS_RDY_LIST and each OS_TCB points pack to the OS_RDY_LIST it belongs
|
|
* to.
|
|
*
|
|
* 'OS_RDY_LIST OSRdyTbl[OS_CFG_PRIO_MAX]' looks like this once initialized:
|
|
*
|
|
* +---------------+--------------+
|
|
* | | TailPtr |-----> 0
|
|
* [0] | NbrEntries=0 +--------------+
|
|
* | | HeadPtr |-----> 0
|
|
* +---------------+--------------+
|
|
* | | TailPtr |-----> 0
|
|
* [1] | NbrEntries=0 +--------------+
|
|
* | | HeadPtr |-----> 0
|
|
* +---------------+--------------+
|
|
* : :
|
|
* : :
|
|
* : :
|
|
* +---------------+--------------+
|
|
* | | TailPtr |-----> 0
|
|
* [OS_CFG_PRIO_MAX-1] | NbrEntries=0 +--------------+
|
|
* | | HeadPtr |-----> 0
|
|
* +---------------+--------------+
|
|
*
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListInit (void)
|
|
{
|
|
CPU_INT32U i;
|
|
OS_RDY_LIST *p_rdy_list;
|
|
|
|
|
|
|
|
for (i = 0u; i < OS_CFG_PRIO_MAX; i++) { /* Initialize the array of OS_RDY_LIST at each priority */
|
|
p_rdy_list = &OSRdyList[i];
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries = 0u;
|
|
#endif
|
|
p_rdy_list->HeadPtr = (OS_TCB *)0;
|
|
p_rdy_list->TailPtr = (OS_TCB *)0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INSERT TCB IN THE READY LIST
|
|
*
|
|
* Description: This function is called to insert a TCB in the ready list.
|
|
*
|
|
* The TCB is inserted at the tail of the list if the priority of the TCB is the same as the priority of the
|
|
* current task. The TCB is inserted at the head of the list if not.
|
|
*
|
|
* Arguments : p_tcb is a pointer to the TCB to insert into the ready list
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListInsert (OS_TCB *p_tcb)
|
|
{
|
|
OS_PrioInsert(p_tcb->Prio);
|
|
if (p_tcb->Prio == OSPrioCur) { /* Are we readying a task at the same prio? */
|
|
OS_RdyListInsertTail(p_tcb); /* Yes, insert readied task at the end of the list */
|
|
} else {
|
|
OS_RdyListInsertHead(p_tcb); /* No, insert readied task at the beginning of the list*/
|
|
}
|
|
|
|
OS_TRACE_TASK_READY(p_tcb);
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INSERT TCB AT THE BEGINNING OF A LIST
|
|
*
|
|
* Description: This function is called to place an OS_TCB at the beginning of a linked list as follows:
|
|
*
|
|
* CASE 0: Insert in an empty list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-> 0
|
|
* +--------------+
|
|
* | HeadPtr |-> 0
|
|
* +--------------+
|
|
* | NbrEntries=0 |
|
|
* +--------------+
|
|
*
|
|
*
|
|
*
|
|
* CASE 1: Insert BEFORE the current head of list
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+ OS_TCB
|
|
* | TailPtr |--+---> +------------+
|
|
* +--------------+ | | NextPtr |->0
|
|
* | HeadPtr |--/ +------------+
|
|
* +--------------+ 0<-| PrevPtr |
|
|
* | NbrEntries=1 | +------------+
|
|
* +--------------+ : :
|
|
* : :
|
|
* +------------+
|
|
*
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-----------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
|
|
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
|
|
* | NbrEntries=N | +------------+ +------------+ +------------+
|
|
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
|
|
* +------------+ +------------+ +------------+
|
|
* : : : : : :
|
|
* : : : : : :
|
|
* +------------+ +------------+ +------------+
|
|
*
|
|
*
|
|
* Arguments : p_tcb is the OS_TCB to insert in the list
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListInsertHead (OS_TCB *p_tcb)
|
|
{
|
|
OS_RDY_LIST *p_rdy_list;
|
|
OS_TCB *p_tcb2;
|
|
|
|
|
|
|
|
p_rdy_list = &OSRdyList[p_tcb->Prio];
|
|
if (p_rdy_list->HeadPtr == (OS_TCB *)0) { /* CASE 0: Insert when there are no entries */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries = 1u; /* This is the first entry */
|
|
#endif
|
|
p_tcb->NextPtr = (OS_TCB *)0; /* No other OS_TCBs in the list */
|
|
p_tcb->PrevPtr = (OS_TCB *)0;
|
|
p_rdy_list->HeadPtr = p_tcb; /* Both list pointers point to this OS_TCB */
|
|
p_rdy_list->TailPtr = p_tcb;
|
|
} else { /* CASE 1: Insert BEFORE the current head of list */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries++; /* One more OS_TCB in the list */
|
|
#endif
|
|
p_tcb->NextPtr = p_rdy_list->HeadPtr; /* Adjust new OS_TCBs links */
|
|
p_tcb->PrevPtr = (OS_TCB *)0;
|
|
p_tcb2 = p_rdy_list->HeadPtr; /* Adjust old head of list's links */
|
|
p_tcb2->PrevPtr = p_tcb;
|
|
p_rdy_list->HeadPtr = p_tcb;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* INSERT TCB AT THE END OF A LIST
|
|
*
|
|
* Description: This function is called to place an OS_TCB at the end of a linked list as follows:
|
|
*
|
|
* CASE 0: Insert in an empty list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-> 0
|
|
* +--------------+
|
|
* | HeadPtr |-> 0
|
|
* +--------------+
|
|
* | NbrEntries=0 |
|
|
* +--------------+
|
|
*
|
|
*
|
|
*
|
|
* CASE 1: Insert AFTER the current tail of list
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+ OS_TCB
|
|
* | TailPtr |--+---> +------------+
|
|
* +--------------+ | | NextPtr |->0
|
|
* | HeadPtr |--/ +------------+
|
|
* +--------------+ 0<-| PrevPtr |
|
|
* | NbrEntries=1 | +------------+
|
|
* +--------------+ : :
|
|
* : :
|
|
* +------------+
|
|
*
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-----------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
|
|
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
|
|
* | NbrEntries=N | +------------+ +------------+ +------------+
|
|
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
|
|
* +------------+ +------------+ +------------+
|
|
* : : : : : :
|
|
* : : : : : :
|
|
* +------------+ +------------+ +------------+
|
|
*
|
|
*
|
|
* Arguments : p_tcb is the OS_TCB to insert in the list
|
|
* -----
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListInsertTail (OS_TCB *p_tcb)
|
|
{
|
|
OS_RDY_LIST *p_rdy_list;
|
|
OS_TCB *p_tcb2;
|
|
|
|
|
|
|
|
p_rdy_list = &OSRdyList[p_tcb->Prio];
|
|
if (p_rdy_list->HeadPtr == (OS_TCB *)0) { /* CASE 0: Insert when there are no entries */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries = 1u; /* This is the first entry */
|
|
#endif
|
|
p_tcb->NextPtr = (OS_TCB *)0; /* No other OS_TCBs in the list */
|
|
p_tcb->PrevPtr = (OS_TCB *)0;
|
|
p_rdy_list->HeadPtr = p_tcb; /* Both list pointers point to this OS_TCB */
|
|
p_rdy_list->TailPtr = p_tcb;
|
|
} else { /* CASE 1: Insert AFTER the current tail of list */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries++; /* One more OS_TCB in the list */
|
|
#endif
|
|
p_tcb->NextPtr = (OS_TCB *)0; /* Adjust new OS_TCBs links */
|
|
p_tcb2 = p_rdy_list->TailPtr;
|
|
p_tcb->PrevPtr = p_tcb2;
|
|
p_tcb2->NextPtr = p_tcb; /* Adjust old tail of list's links */
|
|
p_rdy_list->TailPtr = p_tcb;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* MOVE TCB AT HEAD TO TAIL
|
|
*
|
|
* Description: This function is called to move the current head of a list to the tail of the list.
|
|
*
|
|
*
|
|
* CASE 0: TCB list is empty, nothing to do.
|
|
*
|
|
* CASE 1: Only 1 OS_TCB in the list, nothing to do.
|
|
*
|
|
* CASE 2: Only 2 OS_TCBs in the list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |--------------------------+
|
|
* +--------------+ OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +------------+ +-> +------------+
|
|
* +--------------+ | NextPtr |------> | NextPtr |->0
|
|
* | NbrEntries=2 | +------------+ +------------+
|
|
* +--------------+ 0<-| PrevPtr | <------| PrevPtr |
|
|
* +------------+ +------------+
|
|
* : : : :
|
|
* : : : :
|
|
* +------------+ +------------+
|
|
*
|
|
*
|
|
* CASE N: More than 2 OS_TCBs in the list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-----------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
|
|
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
|
|
* | NbrEntries=N | +------------+ +------------+ +------------+
|
|
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
|
|
* +------------+ +------------+ +------------+
|
|
* : : : : : :
|
|
* : : : : : :
|
|
* +------------+ +------------+ +------------+
|
|
*
|
|
*
|
|
* Arguments : p_rdy_list is a pointer to the OS_RDY_LIST where the OS_TCB will be inserted
|
|
* ------
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListMoveHeadToTail (OS_RDY_LIST *p_rdy_list)
|
|
{
|
|
OS_TCB *p_tcb1;
|
|
OS_TCB *p_tcb2;
|
|
OS_TCB *p_tcb3;
|
|
|
|
|
|
if (p_rdy_list->HeadPtr != p_rdy_list->TailPtr) {
|
|
if (p_rdy_list->HeadPtr->NextPtr == p_rdy_list->TailPtr) { /* SWAP the TCBs */
|
|
p_tcb1 = p_rdy_list->HeadPtr; /* Point to current head */
|
|
p_tcb2 = p_rdy_list->TailPtr; /* Point to current tail */
|
|
p_tcb1->PrevPtr = p_tcb2;
|
|
p_tcb1->NextPtr = (OS_TCB *)0;
|
|
p_tcb2->PrevPtr = (OS_TCB *)0;
|
|
p_tcb2->NextPtr = p_tcb1;
|
|
p_rdy_list->HeadPtr = p_tcb2;
|
|
p_rdy_list->TailPtr = p_tcb1;
|
|
} else {
|
|
p_tcb1 = p_rdy_list->HeadPtr; /* Point to current head */
|
|
p_tcb2 = p_rdy_list->TailPtr; /* Point to current tail */
|
|
p_tcb3 = p_tcb1->NextPtr; /* Point to new list head */
|
|
p_tcb3->PrevPtr = (OS_TCB *)0; /* Adjust back link of new list head */
|
|
p_tcb1->NextPtr = (OS_TCB *)0; /* Adjust forward link of new list tail */
|
|
p_tcb1->PrevPtr = p_tcb2; /* Adjust back link of new list tail */
|
|
p_tcb2->NextPtr = p_tcb1; /* Adjust forward link of old list tail */
|
|
p_rdy_list->HeadPtr = p_tcb3; /* Adjust new list head and tail pointers */
|
|
p_rdy_list->TailPtr = p_tcb1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* REMOVE TCB FROM LIST KNOWING ONLY WHICH OS_TCB TO REMOVE
|
|
*
|
|
* Description: This function is called to remove an OS_TCB from an OS_RDY_LIST knowing the address of the OS_TCB to
|
|
* remove.
|
|
*
|
|
*
|
|
* CASE 0: TCB list is empty, nothing to do.
|
|
*
|
|
* CASE 1: Only 1 OS_TCBs in the list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+ OS_TCB
|
|
* | TailPtr |--+---> +------------+
|
|
* +--------------+ | | NextPtr |->0
|
|
* | HeadPtr |--/ +------------+
|
|
* +--------------+ 0<-| PrevPtr |
|
|
* | NbrEntries=1 | +------------+
|
|
* +--------------+ : :
|
|
* : :
|
|
* +------------+
|
|
*
|
|
* CASE N: Two or more OS_TCBs in the list.
|
|
*
|
|
* OS_RDY_LIST
|
|
* +--------------+
|
|
* | TailPtr |-----------------------------------------------+
|
|
* +--------------+ OS_TCB OS_TCB | OS_TCB
|
|
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
|
|
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
|
|
* | NbrEntries=N | +------------+ +------------+ +------------+
|
|
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
|
|
* +------------+ +------------+ +------------+
|
|
* : : : : : :
|
|
* : : : : : :
|
|
* +------------+ +------------+ +------------+
|
|
*
|
|
*
|
|
* Arguments : p_tcb is a pointer to the OS_TCB to remove
|
|
* -----
|
|
*
|
|
* Returns : A pointer to the OS_RDY_LIST where the OS_TCB was
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_RdyListRemove (OS_TCB *p_tcb)
|
|
{
|
|
OS_RDY_LIST *p_rdy_list;
|
|
OS_TCB *p_tcb1;
|
|
OS_TCB *p_tcb2;
|
|
|
|
|
|
|
|
p_rdy_list = &OSRdyList[p_tcb->Prio];
|
|
p_tcb1 = p_tcb->PrevPtr; /* Point to next and previous OS_TCB in the list */
|
|
p_tcb2 = p_tcb->NextPtr;
|
|
if (p_tcb1 == (OS_TCB *)0) { /* Was the OS_TCB to remove at the head? */
|
|
if (p_tcb2 == (OS_TCB *)0) { /* Yes, was it the only OS_TCB? */
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries = 0u; /* Yes, no more entries */
|
|
#endif
|
|
p_rdy_list->HeadPtr = (OS_TCB *)0;
|
|
p_rdy_list->TailPtr = (OS_TCB *)0;
|
|
OS_PrioRemove(p_tcb->Prio);
|
|
} else {
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries--; /* No, one less entry */
|
|
#endif
|
|
p_tcb2->PrevPtr = (OS_TCB *)0; /* adjust back link of new list head */
|
|
p_rdy_list->HeadPtr = p_tcb2; /* adjust OS_RDY_LIST's new head */
|
|
}
|
|
} else {
|
|
#if (OS_CFG_DBG_EN > 0u)
|
|
p_rdy_list->NbrEntries--; /* No, one less entry */
|
|
#endif
|
|
p_tcb1->NextPtr = p_tcb2;
|
|
if (p_tcb2 == (OS_TCB *)0) {
|
|
p_rdy_list->TailPtr = p_tcb1; /* Removing the TCB at the tail, adj the tail ptr */
|
|
} else {
|
|
p_tcb2->PrevPtr = p_tcb1;
|
|
}
|
|
}
|
|
p_tcb->PrevPtr = (OS_TCB *)0;
|
|
p_tcb->NextPtr = (OS_TCB *)0;
|
|
|
|
OS_TRACE_TASK_SUSPENDED(p_tcb);
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* SCHEDULER LOCK TIME MEASUREMENT
|
|
*
|
|
* Description: These functions are used to measure the peak amount of time that the scheduler is locked
|
|
*
|
|
* Arguments : none
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) The are internal functions to uC/OS-III and MUST not be called by your application code.
|
|
*
|
|
* 2) It's assumed that these functions are called when interrupts are disabled.
|
|
*
|
|
* 3) We are reading the time stamp timer via OS_TS_GET() directly even if this is a 16-bit timer. The
|
|
* reason is that we don't expect to have the scheduler locked for 65536 counts even at the rate the TS
|
|
* timer is updated. In other words, locking the scheduler for longer than 65536 count would not be a
|
|
* good thing for a real-time system.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
|
|
void OS_SchedLockTimeMeasStart (void)
|
|
{
|
|
if (OSSchedLockNestingCtr == 1u) {
|
|
OSSchedLockTimeBegin = OS_TS_GET();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void OS_SchedLockTimeMeasStop (void)
|
|
{
|
|
CPU_TS_TMR delta;
|
|
|
|
|
|
if (OSSchedLockNestingCtr == 0u) { /* Make sure we fully un-nested scheduler lock */
|
|
delta = OS_TS_GET() /* Compute the delta time between begin and end */
|
|
- OSSchedLockTimeBegin;
|
|
if (OSSchedLockTimeMax < delta) { /* Detect peak value */
|
|
OSSchedLockTimeMax = delta;
|
|
}
|
|
if (OSSchedLockTimeMaxCur < delta) { /* Detect peak value (for resettable value) */
|
|
OSSchedLockTimeMaxCur = delta;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* RUN ROUND-ROBIN SCHEDULING ALGORITHM
|
|
*
|
|
* Description: This function is called on every tick to determine if a new task at the same priority needs to execute.
|
|
*
|
|
*
|
|
* Arguments : p_rdy_list is a pointer to the OS_RDY_LIST entry of the ready list at the current priority
|
|
* ----------
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
|
|
void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
|
|
{
|
|
OS_TCB *p_tcb;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
if (OSSchedRoundRobinEn != OS_TRUE) { /* Make sure round-robin has been enabled */
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_tcb = p_rdy_list->HeadPtr; /* Decrement time quanta counter */
|
|
|
|
if (p_tcb == (OS_TCB *)0) {
|
|
CPU_CRITICAL_EXIT();
|
|
return;
|
|
}
|
|
|
|
#if (OS_CFG_TASK_IDLE_EN > 0u)
|
|
if (p_tcb == &OSIdleTaskTCB) {
|
|
CPU_CRITICAL_EXIT();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (p_tcb->TimeQuantaCtr > 0u) {
|
|
p_tcb->TimeQuantaCtr--;
|
|
}
|
|
|
|
if (p_tcb->TimeQuantaCtr > 0u) { /* Task not done with its time quanta */
|
|
CPU_CRITICAL_EXIT();
|
|
return;
|
|
}
|
|
|
|
if (p_rdy_list->HeadPtr == p_rdy_list->TailPtr) { /* See if it's time to time slice current task */
|
|
CPU_CRITICAL_EXIT(); /* ... only if multiple tasks at same priority */
|
|
return;
|
|
}
|
|
|
|
if (OSSchedLockNestingCtr > 0u) { /* Can't round-robin if the scheduler is locked */
|
|
CPU_CRITICAL_EXIT();
|
|
return;
|
|
}
|
|
|
|
OS_RdyListMoveHeadToTail(p_rdy_list); /* Move current OS_TCB to the end of the list */
|
|
p_tcb = p_rdy_list->HeadPtr; /* Point to new OS_TCB at head of the list */
|
|
if (p_tcb->TimeQuanta == 0u) { /* See if we need to use the default time slice */
|
|
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
|
|
} else {
|
|
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time */
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
************************************************************************************************************************
|
|
* BLOCK A TASK
|
|
*
|
|
* Description: This function is called to remove a task from the ready list and also insert it in the timer tick list if
|
|
* the specified timeout is non-zero.
|
|
*
|
|
* Arguments : p_tcb is a pointer to the OS_TCB of the task block
|
|
* -----
|
|
*
|
|
* timeout is the desired timeout
|
|
*
|
|
* Returns : none
|
|
*
|
|
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
|
|
************************************************************************************************************************
|
|
*/
|
|
|
|
void OS_TaskBlock (OS_TCB *p_tcb,
|
|
OS_TICK timeout)
|
|
{
|
|
#if (OS_CFG_DYN_TICK_EN > 0u)
|
|
OS_TICK elapsed;
|
|
|
|
|
|
elapsed = OS_DynTickGet();
|
|
#endif
|
|
|
|
#if (OS_CFG_TICK_EN > 0u)
|
|
if (timeout > 0u) { /* Add task to tick list if timeout non zero */
|
|
#if (OS_CFG_DYN_TICK_EN > 0u)
|
|
(void)OS_TickListInsert(p_tcb, elapsed, (OSTickCtr + elapsed), timeout);
|
|
#else
|
|
(void)OS_TickListInsert(p_tcb, 0u, OSTickCtr, timeout);
|
|
#endif
|
|
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
|
|
} else {
|
|
p_tcb->TaskState = OS_TASK_STATE_PEND;
|
|
}
|
|
#else
|
|
(void)timeout;
|
|
p_tcb->TaskState = OS_TASK_STATE_PEND;
|
|
#endif
|
|
OS_RdyListRemove(p_tcb);
|
|
}
|