CoOS Demonstrator adapted to mbed Hardware.

Dependencies:   mbed

task.c

Committer:
ericebert
Date:
2010-12-03
Revision:
0:57690853989a

File content as of revision 0:57690853989a:

/**
 *******************************************************************************
 * @file       task.c
 * @version    V1.1.3    
 * @date       2010.04.26
 * @brief      task management implementation code of CooCox CoOS kernel.    
 *******************************************************************************
 * @copy
 *
 * INTERNAL FILE,DON'T PUBLIC.
 * 
 * <h2><center>&copy; COPYRIGHT 2009 CooCox </center></h2>
 *******************************************************************************
 */ 


/*---------------------------- Include ---------------------------------------*/
#include <coocox.h>

/*---------------------------- Variable Define -------------------------------*/

/*!< Table use to save TCB pointer.              */
OSTCB    TCBTbl[CFG_MAX_USER_TASKS+SYS_TASK_NUM] = {{0}};

/*!< The stack of IDLE task.                     */
OS_STK   idle_stk[CFG_IDLE_STACK_SIZE] = {0};

P_OSTCB  FreeTCB     = 0;  /*!< pointer to free TCB                        */    
P_OSTCB  TCBRdy      = 0;  /*!< Pointer to the READY list.                 */
P_OSTCB  TCBNext     = 0;  /*!< Poniter to task that next scheduled by OS  */
P_OSTCB  TCBRunning  = 0;  /*!< Pointer to TCB that current running task.  */
U64      OSCheckTime = 0;     /*!< The counter of system tick.                */

#if CFG_ORDER_LIST_SCHEDULE_EN ==0
OS_TID   PriNum;
U8       ActivePri[CFG_MAX_USER_TASKS+SYS_TASK_NUM];
U8       TaskNumPerPri[CFG_MAX_USER_TASKS+SYS_TASK_NUM];
OS_TID   RdyTaskPri[CFG_MAX_USER_TASKS+SYS_TASK_NUM] = {0};    
U32      RdyTaskPriInfo[(CFG_MAX_USER_TASKS+SYS_TASK_NUM+31)/32];
#endif


/**
 *******************************************************************************
 * @brief      Create a TCB list.      
 * @param[in]  None      
 * @param[out] None    
 * @retval     None         
 *
 * @par Description
 * @details    This function is called by CoOSInit() to initial the empty list     
 *             of OS_TCBS,supply a pointer to free TCB.
 *******************************************************************************
 */
void CreateTCBList(void)
{    
    U8  i;
    P_OSTCB ptcb1,ptcb2;
    
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    PriNum = 0;
#endif

    ptcb1 = &TCBTbl[0];                    /* Build the free TCB list            */
    ptcb2 = &TCBTbl[1];  
    for(i=0;i< (CFG_MAX_USER_TASKS+SYS_TASK_NUM-1);i++ )
    {
        ptcb1->taskID    = i;
        ptcb1->state     = TASK_DORMANT;
        ptcb1->TCBnext   = ptcb2;
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        RdyTaskPri[i]    = INVALID_ID;
        ActivePri[i]     = INVALID_ID;
#endif
        ptcb1++;
        ptcb2++;    
    }
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        ActivePri[i]     = INVALID_ID;
#endif

    ptcb1->taskID    = i;    
    ptcb1->TCBnext   = 0;
    FreeTCB = &TCBTbl[0];         /* Initialize FreeTCB as head item of list  */            
}



#if CFG_ORDER_LIST_SCHEDULE_EN ==0

/**
 *******************************************************************************
 * @brief      Get sequence number for Assign priority      
 * @param[in]  pri            Assign priority     
 * @param[out] SequenceNum    priority number 
 * @retval     TRUE           Assign priority in priority queue.    
 *             FALSE          Assign priority not in priority queue.          
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to get sequence number for Assign priority.     
 *******************************************************************************
 */
static BOOL  GetPriSeqNum(U8 pri,OS_TID* SequenceNum)
{
    OS_TID  seqNum;
    OS_TID  num,tmpNum;
    num = 0;
    seqNum = PriNum;
    while(num != seqNum)
    {
        tmpNum = num;
        num = (num+seqNum)/2;
        if(pri == ActivePri[num])
        {
            *SequenceNum = num;
            return TRUE;
        }
        else if (pri < ActivePri[num])
        {
            seqNum = num;
            num = tmpNum;
        }
        else
        {
            num++;
        }
    }
    *SequenceNum = num;
    return FALSE;        
}


/**
 *******************************************************************************
 * @brief      Get the nearest ready priority sequence number for Assign number      
 * @param[in]  seqNum         Assign sequence number     
 * @param[out] None
 * @retval     INVALID_ID     Cannot find higher ready priority.   
 *             Others         Nearest ready priority sequence number          
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to get the nearest ready priority sequence number.    
 *******************************************************************************
 */
static U8 GetRdyPriSeqNum(U8 seqNum)
{
    U32 tmp;
    U8  i,j,num;
    S8  cnt;
    i = seqNum/32;
    j = seqNum%32;

    do
    {
          tmp = RdyTaskPriInfo[i];
        if(tmp != 0)
        {
            num = j/8;
            do
            {
                if((tmp&(0xff<<(num*8))) !=0 )
                {
                    if((tmp&(0xf0<<(num*8))) !=0)
                    {
                        for(cnt=j; cnt >=(num*8+4); cnt--)    
                        {
                            if( (tmp&(1<<cnt)) !=0)
                            {
                                return (32*i+cnt);
                            }    
                        }            
                    }

                    if((j&0x4)==4)
                        j = (j|0x3) -4;
                    
                    for(cnt=j; cnt >=num*8; cnt--)    
                    {
                        if( (tmp&(1<<cnt)) !=0)
                        {
                            return (32*i+cnt);
                        }    
                    }
                }
                j = num*8 -1;
            }while((num--)!=0);
        }
        j=31;
    }while((i--)!=0);
    return INVALID_ID;                                            
}


/**
 *******************************************************************************
 * @brief      Remap the ready status of priority queue from Assign sequence number 
 * @param[in]  seqNum         Assign sequence number     
 * @param[out] None
 * @retval     None             
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to Remap the ready status for priority queue.    
 *******************************************************************************
 */
static void PrioRemap(OS_TID  seqNum)
{
    U8 i,j;
    U32 tmp;
    tmp = j = 0;
    j = seqNum/32;
    for(i=0;i<seqNum%32;i++)
    {
        tmp |= 1<<i;
    }
    tmp &= RdyTaskPriInfo[j];
    
    for(i=seqNum; i<PriNum; i++)
    {
        if((i%32==0)&&(i!=seqNum))
        {
            RdyTaskPriInfo[j++] = tmp;
            tmp = 0;
        }
        if(RdyTaskPri[i] != INVALID_ID)
        {
            tmp = tmp | (1<<(i%32));
        }
    }
    RdyTaskPriInfo[j++] = tmp;
}


/**
 *******************************************************************************
 * @brief      Get the ready status for assign sequence number 
 * @param[in]  seqNum      Assign sequence number     
 * @param[out] None
 * @retval     TRUE        This priority has ready task   
 *             FALSE       This priority doesn't have ready task           
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to get the ready status for assign sequence number.    
 *******************************************************************************
 */
static BOOL GetPrioSeqNumStatus(U8 seqNum)
{
    if( (RdyTaskPriInfo[seqNum/32] & (1<<(seqNum%32))) == 0)
    {
        return FALSE;
    }
    return TRUE;
}


/**
 *******************************************************************************
 * @brief      Set the ready status for assign sequence number 
 * @param[in]  seqNum      Assign sequence number
 * @param[in]  isRdy       Ready statues for assign sequence number      
 * @param[out] None
 * @retval     None          
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to set the ready status for assign sequence number.    
 *******************************************************************************
 */
static void SetPrioSeqNumStatus(U8 seqNum, BOOL isRdy)
{
    U32 tmp;
    tmp = RdyTaskPriInfo[seqNum/32];
    tmp    &= ~(1<<(seqNum%32));
    tmp |= isRdy<<(seqNum%32);
    RdyTaskPriInfo[seqNum/32] = tmp;
}


/**
 *******************************************************************************
 * @brief      Active priority in queue 
 * @param[in]  pri       Task priority
 * @param[in]  None     
 * @param[out] None
 * @retval     None          
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to active priority in queue, if this priority had been in activation,
 *             increate the task num for this priority.    
 *******************************************************************************
 */
void ActiveTaskPri(U8 pri)
{
    OS_TID  seqNum,num;
    if(GetPriSeqNum(pri,&seqNum) == FALSE)
    {
        for(num=PriNum;num>seqNum;num--)
        {
            ActivePri[num]     = ActivePri[num-1];
            TaskNumPerPri[num] = TaskNumPerPri[num-1];
            RdyTaskPri[num]    = RdyTaskPri[num-1];
        }
        ActivePri[seqNum]     = pri;
        TaskNumPerPri[seqNum] = 1;
        RdyTaskPri[seqNum]    = INVALID_ID;
        PriNum++;
        PrioRemap(seqNum);
    }
    else
    {
         TaskNumPerPri[seqNum]++;
    }
}



/**
 *******************************************************************************
 * @brief      Delete priority in queue 
 * @param[in]  pri       Task priority
 * @param[in]  None     
 * @param[out] None
 * @retval     None          
 *                    
 * @par Description
 * @details    This function is called in Binary-Scheduling Algorithm 
 *             to decrease the task num for this priority, if the num goto 0,
 *             remove the priority for queue.
 *******************************************************************************
 */
void DeleteTaskPri(U8 pri)
{
    OS_TID  seqNum,num;

    GetPriSeqNum(pri,&seqNum);
    TaskNumPerPri[seqNum]--;
    if(TaskNumPerPri[seqNum]==0)
    {
        for(num=seqNum; num<(PriNum-1); num++)
        {
            ActivePri[num]     = ActivePri[num+1];
            TaskNumPerPri[num] = TaskNumPerPri[num+1];
            RdyTaskPri[num]    = RdyTaskPri[num+1];
        }
        PriNum--;
        PrioRemap(seqNum);
    }
}

#endif


/**
 *******************************************************************************
 * @brief      Insert a task to the ready list       
 * @param[in]  tcbInsert    A pointer to task will be inserted.
 * @param[out] None  
 * @retval     None     
 *
 * @par Description
 * @details   This function is called to insert a task to the READY list. 
 *******************************************************************************
 */
void InsertToTCBRdyList(P_OSTCB tcbInsert)
{
    P_OSTCB ptcbNext,ptcb;
    U8  prio;
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    U8  seqNum;
    U8  RdyTaskSeqNum;
#endif
    
    prio = tcbInsert->prio;             /* Get PRI of inserted task           */
    tcbInsert->state     = TASK_READY;  /* Set task as TASK_READY             */

#if CFG_ROBIN_EN >0
    ptcb = TCBRunning;
    /* Set schedule time for the same PRI task as TCBRunning.                 */
    if(prio == ptcb->prio)  /* Is PRI of inserted task equal to running task? */
    {
        if(ptcb != tcbInsert) /* Yes,is inserted task equal to running task?  */
        {
            if(ptcb != 0)            /* No,TCBRunning == 0?             */
            {                           /* N0,OSCheckTime < OSTickCnt?        */
                if(OSCheckTime < OSTickCnt)     
                {                       /* Yes,set OSCheckTime for task robin */
                    OSCheckTime = OSTickCnt + ptcb->timeSlice;    
                }            
            }            
        }
    }
#endif


#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    GetPriSeqNum(prio,&seqNum);
    if(GetPrioSeqNumStatus(seqNum) == TRUE)
    {
        ptcb = &TCBTbl[RdyTaskPri[seqNum]];
        RdyTaskPri[seqNum] = tcbInsert->taskID;
    }
    else
    {
        RdyTaskPri[seqNum] = tcbInsert->taskID;
        RdyTaskSeqNum = GetRdyPriSeqNum(seqNum);
        SetPrioSeqNumStatus(seqNum, 1);
        if(RdyTaskSeqNum == INVALID_ID)
        {
            ptcb = TCBRdy;
            TaskSchedReq = TRUE;
            if(ptcb == 0)
            {
                TCBRdy   = tcbInsert;    
            }
            else
            {
                tcbInsert->TCBnext = ptcb;  /* Yes,set tcbInsert as head item of list */
                ptcb->TCBprev = tcbInsert;
                TCBRdy         = tcbInsert;
            }
            return;
        }
        else
        {
            ptcb = &TCBTbl[RdyTaskPri[RdyTaskSeqNum]];    
        }
    }

    ptcbNext = ptcb->TCBnext;
    tcbInsert->TCBnext = ptcbNext;    /* Set link for list                  */
    ptcb->TCBnext      = tcbInsert;
    tcbInsert->TCBprev = ptcb;
    if(ptcbNext != 0)
    {
        ptcbNext->TCBprev  = tcbInsert;
    }


#else
    ptcb = TCBRdy;
    if (ptcb == 0)                   /* Is ready list 0?                */
    {
        TaskSchedReq = TRUE;
        TCBRdy = tcbInsert;         /* Yse,set tcbInsert as head item of list */
    }
    else if (prio < ptcb->prio)/* Is PRI of inserted task higher than TCBRdy? */
    {
        TaskSchedReq = TRUE;
        tcbInsert->TCBnext = ptcb;  /* Yes,set tcbInsert as head item of list */
        ptcb->TCBprev  = tcbInsert;
        TCBRdy         = tcbInsert;
    }
    else                                /* No,find correct place              */
    {                                    
        ptcbNext = ptcb->TCBnext;       /* Get next item                      */
        while(ptcbNext != 0)         /* Is last item in ready list?        */
        {                               /* No,find correct place              */
            if(prio < ptcbNext->prio)   /* Is correct place?                  */
                break;                  /* Yes,break circulation              */
            ptcb     = ptcbNext;        /* Save current item                  */
            ptcbNext = ptcbNext->TCBnext; /* Get next item                    */
        }
        tcbInsert->TCBnext = ptcbNext;  /* Set link for list                  */
        ptcb->TCBnext      = tcbInsert;
        tcbInsert->TCBprev = ptcb;
        if(ptcbNext != 0)
        {
            ptcbNext->TCBprev  = tcbInsert;
        }        
    }
#endif
}



/**
 *******************************************************************************
 * @brief      Remove a task from the READY list       
 * @param[in]  ptcb     A pointer to task which be removed.     
 * @param[out] None                  
 * @retval     None         
 *
 * @par Description
 * @details    This function is called to remove a task from the READY list.
 *******************************************************************************
 */
void RemoveFromTCBRdyList(P_OSTCB ptcb)
{

#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    U8 prio;
    U8 seqNum;
    BOOL isChange;
    isChange = FALSE;
    prio = ptcb->prio;
    GetPriSeqNum(prio,&seqNum);
#endif

    /* Is there only one item in READY list?                                  */
    if((ptcb->TCBnext == 0) && (ptcb->TCBprev == 0) )
    {
        TCBRdy = 0;                  /* Yes,set READY list as 0         */
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        isChange = TRUE;
#endif
    }
    else if(ptcb->TCBprev == 0)      /* Is the first item in READY list?   */
    {   
        /* Yes,remove task from the list,and reset the head of READY list     */
        TCBRdy = ptcb->TCBnext;            
        ptcb->TCBnext   = 0;
        TCBRdy->TCBprev = 0;
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        if(TCBRdy->prio != prio)
            isChange = TRUE;
        
#endif
    }
    else if( ptcb->TCBnext == 0)     /* Is the last item in READY list?    */
    {                                   /* Yes,remove task from list          */
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        if(ptcb->TCBprev->prio != prio)
            isChange = TRUE;
        else 
            RdyTaskPri[seqNum] = ptcb->TCBprev->taskID;
#endif
        ptcb->TCBprev->TCBnext = 0;  
        ptcb->TCBprev          = 0;
    }
    else                                /* No, remove task from list          */
    {    
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        if((ptcb->TCBprev->prio != prio) && (ptcb->TCBnext->prio != prio))
            isChange = TRUE;
        else if((ptcb->TCBprev->prio == prio) && (ptcb->TCBnext->prio != prio))
            RdyTaskPri[seqNum] = ptcb->TCBprev->taskID;
#endif                                
        ptcb->TCBprev->TCBnext = ptcb->TCBnext;
        ptcb->TCBnext->TCBprev = ptcb->TCBprev;
        ptcb->TCBnext = 0;
        ptcb->TCBprev = 0;
    }
#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        if(isChange == TRUE)
        {
            RdyTaskPri[seqNum] = INVALID_ID;
            SetPrioSeqNumStatus(seqNum, 0);
        }
#endif
}


#if CFG_MUTEX_EN > 0
#define CFG_PRIORITY_SET_EN       (1)
#endif
#if CFG_PRIORITY_SET_EN >0
/**
 *******************************************************************************
 * @brief      Change task priority       
 * @param[in]  taskID     Specify task id.
 * @param[in]  priority   New priority.     
 * @param[out] None           
 * @retval     E_OK              Change priority successful.
 * @retval     E_INVALID_ID      Invalid id,change priority fail.
 * @retval     E_PROTECTED_TASK  Can't change idle task priority.         
 *
 * @par Description
 * @details    This function is called to change priority for a specify task.     
 *******************************************************************************
 */
StatusType CoSetPriority(OS_TID taskID,U8 priority)
{            
    P_OSTCB ptcb;
#if CFG_MUTEX_EN >0
    U8 prio;
    P_MUTEX    pMutex;
#endif
#if CFG_EVENT_EN >0
    P_ECB pecb;
#endif

    if(taskID == 0)                     /* Is idle task?                      */
    {                                             
        return E_PROTECTED_TASK;        /* Yes,error return                   */
    }   
    
#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
    {
        return E_INVALID_ID;
    }
#endif
    ptcb = &TCBTbl[taskID];             /* Get TCB of task ID                 */
#if CFG_PAR_CHECKOUT_EN >0    
    if(ptcb->state == TASK_DORMANT)
    {
        return E_INVALID_ID;
    }
    if(priority > CFG_LOWEST_PRIO)
    {
        return E_INVALID_ID;
    }
#endif

    if(ptcb->prio != priority)          /* Is PRI equal to original PRI?      */
    {                                   /* No                                 */
#if CFG_MUTEX_EN >0
        if(ptcb->mutexID != INVALID_ID)
        {
            pMutex = &MutexTbl[ptcb->mutexID];
            if(pMutex->taskID == ptcb->taskID)  /* Task hold mutex?               */
            {
                 pMutex->originalPrio= priority;/* Yes,change original PRI in mutex*/
                 if(ptcb->prio < priority)     /* Is task priority higher than set?*/
                 {
                     return E_OK;                /* Yes,do nothing,return OK       */
                 }
            }        
         }

#endif    

#if CFG_ORDER_LIST_SCHEDULE_EN ==0
        DeleteTaskPri(ptcb->prio);
        ActiveTaskPri(priority);    
#endif    

        ptcb->prio = priority;              /* Change task PRI                */
        if(ptcb->state == TASK_READY)       /* Is task in READY list?         */
        {
            OsSchedLock();                  /* Yes,reorder task in READY list */
            RemoveFromTCBRdyList(ptcb);
            InsertToTCBRdyList(ptcb);    
            OsSchedUnlock();
        }
        else if(ptcb->state == TASK_RUNNING)/* Is task running?               */
        {
            if(ptcb->prio > TCBRdy->prio)   /* Yes,Is PRI higher than TCBRdy? */
            {
                OsSchedLock();              /* Yes,reorder task in READY list */
                TaskSchedReq = TRUE;
                OsSchedUnlock();
            }
        }
        else
        {                                   /* No,task in WAITING list        */
#if CFG_MUTEX_EN >0
            if(ptcb->mutexID != INVALID_ID) /* Is task in mutex WAITING list? */
            {
                /* Yes,reset the highest PRI in the list */
                OsSchedLock(); 
                pMutex = &MutexTbl[ptcb->mutexID];
                ptcb = pMutex->waittingList;  
                prio = pMutex->originalPrio; 
                pMutex->hipriTaskID = pMutex->taskID;
                while(ptcb != 0)
                {
                    if(ptcb->prio < prio)
                    {
                        prio = ptcb->prio;
                        pMutex->hipriTaskID = ptcb->taskID;
                    }
                    ptcb = ptcb->TCBnext;            
                }
                OsSchedUnlock();
                if(pMutex->originalPrio != prio)
                {
                    CoSetPriority(pMutex->taskID,prio);    
                }    
            }
#endif

#if CFG_EVENT_EN >0
            ptcb = &TCBTbl[taskID];
            if(ptcb->eventID != INVALID_ID) /* Is task in event WAITING list? */
            {                                    
                pecb = &EventTbl[ptcb->eventID];
                
                /* Yes,is event sort type as preemptive PRI?                  */
                if(pecb->eventSortType == EVENT_SORT_TYPE_PRIO)
                {      
                    /* Yes,reorder task in the list                           */
                    RemoveEventWaittingList(ptcb);
                    EventTaskToWait(pecb,ptcb);
                }    
            }
#endif
        }
    }
    return E_OK;
}
#endif

/**
 *******************************************************************************
 * @brief      Schedule function      
 * @param[in]  None      
 * @param[out] None       
 * @retval     None     
 *
 * @par Description
 * @details    This function is called by every where need to switch context,
 *             It is schedule function of OS kernel.
 *******************************************************************************
 */
void Schedule(void)
{
    U8  RunPrio,RdyPrio;
    P_OSTCB pRdyTcb,pCurTcb;
   
    
    pCurTcb = TCBRunning;    
    pRdyTcb = TCBRdy;

    if((pRdyTcb==0) || (pCurTcb != TCBNext) || (OSSchedLock >1) || (OSIntNesting >0))
    {
        return;
    }
    
    TaskSchedReq = FALSE;
    RunPrio = pCurTcb->prio;
    RdyPrio = pRdyTcb->prio;

    /* Is Running task status was changed? */
    if(pCurTcb->state != TASK_RUNNING)    
    {
        TCBNext        = pRdyTcb;   /* Yes,set TCBNext and reorder READY list */
        pRdyTcb->state = TASK_RUNNING;
        RemoveFromTCBRdyList(pRdyTcb);
    }

    else if(RdyPrio < RunPrio )     /* Is higher PRI task coming in?          */
    {
        TCBNext        = pRdyTcb;   /* Yes,set TCBNext and reorder READY list */
        InsertToTCBRdyList(pCurTcb);
        RemoveFromTCBRdyList(pRdyTcb);
        pRdyTcb->state = TASK_RUNNING;
    }
    
#if CFG_ROBIN_EN >0                 /* Is time for robinning                  */                            
    else if((RunPrio == RdyPrio) && (OSCheckTime == OSTickCnt))
    {
        TCBNext        = pRdyTcb;   /* Yes,set TCBNext and reorder READY list */
        InsertToTCBRdyList(pCurTcb);
        RemoveFromTCBRdyList(pRdyTcb);
        pRdyTcb->state = TASK_RUNNING;
    }
#endif
    else
    {                                    
        return;    
    }
    
#if CFG_ROBIN_EN >0
    if(TCBNext->prio == TCBRdy->prio)  /* Reset OSCheckTime for task robinnig */
        OSCheckTime = OSTickCnt + TCBNext->timeSlice;
#endif
    
  
#if CFG_STK_CHECKOUT_EN > 0                       /* Is stack overflow?       */
    if((pCurTcb->stkPtr < pCurTcb->stack)||(*(U32*)(pCurTcb->stack) != MAGIC_WORD))       
    {                                    
        CoStkOverflowHook(pCurTcb->taskID);       /* Yes,call handler         */        
    }   
#endif
     
    SwitchContext();                              /* Call task context switch */
}


/**
 *******************************************************************************
 * @brief      Assign a TCB to task being created                         
 * @param[in]  None     
 * @param[out] None     
 *      
 * @retval     XXXX                             
 *
 * @par Description
 * @details    This function is called to assign a task control block for task 
 *              being created.
 *******************************************************************************
 */
static P_OSTCB AssignTCB(void)
{
    P_OSTCB    ptcb;
    
    OsSchedLock();                      /* Lock schedule                      */
    if(FreeTCB == 0)                 /* Is there no free TCB               */
    {
        OsSchedUnlock();                /* Yes,unlock schedule                */
        return 0;                    /* Error return                       */
    }    
    ptcb    = FreeTCB;          /* Yes,assgin free TCB for this task  */    
    /* Set next item as the head of free TCB list                     */
    FreeTCB = FreeTCB->TCBnext; 
    OsSchedUnlock();
    return ptcb;
}


/**
 *******************************************************************************
 * @brief      Create a task       
 * @param[in]  task       Task code entry.
 * @param[in]  argv       The parameter passed to task.
 * @param[in]  parameter  Task priority + stack size + time slice + isWaitting.
 * @param[in]  stk        Pointer to stack top of task.
 * @param[out] None   
 * @retval     E_CREATE_FAIL    Fail to create a task .
 * @retval     others           Valid task id.                 
 *
 * @par Description
 * @details    This function is called by application to create a task,return a id 
 *             to mark this task.
 *******************************************************************************
 */
OS_TID CreateTask(FUNCPtr task,void *argv,U32 parameter,OS_STK *stk)
{
    OS_STK* stkTopPtr;
    P_OSTCB ptcb;
    U8      prio;
#if CFG_ROBIN_EN >0    
    U16     timeSlice;
#endif
   
#if CFG_STK_CHECKOUT_EN >0              /* Check validity of parameter        */
    U16 sktSz;
    sktSz = (parameter&0xfff00)>>8;    
#endif
    prio = parameter&0xff;

#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(task == 0)
    {
        return E_CREATE_FAIL;
    }
    if(stk == 0)
    {
        return E_CREATE_FAIL;
    }
    if(prio > CFG_LOWEST_PRIO)
    {
        return E_CREATE_FAIL;        
    }
#if CFG_STK_CHECKOUT_EN >0
    if(sktSz < 20)
    {
        return E_CREATE_FAIL;        
    }
#endif      // CFG_STK_CHECKOUT_EN
#endif      // CFG_PAR_CHECKOUT_EN

#if CFG_TASK_SCHEDULE_EN == 0
    if(TCBRunning != 0)
         return E_CREATE_FAIL;    
#endif   

    stkTopPtr = InitTaskContext(task,argv,stk);   /* Initialize task context. */
    
    ptcb = AssignTCB();                 /* Get free TCB to use                */
    
    if(ptcb == 0)                    /* Is free TCB equal to 0?         */
    {
        return E_CREATE_FAIL;           /* Yes,error return                   */
    }
    
    ptcb->stkPtr = stkTopPtr;           /* Initialize TCB as user set         */
    ptcb->prio   = prio;
#if CFG_STK_CHECKOUT_EN >0
    ptcb->stack = stk+1 - sktSz; /* Set bottom stack for stack overflow check */
    *(U32*)(ptcb->stack) = MAGIC_WORD;
#endif    

#if CFG_TASK_WAITTING_EN >0
    ptcb->delayTick    = INVALID_VALUE;    
#endif         

#if CFG_TASK_SCHEDULE_EN == 0
    ptcb->taskFuc = task;
    ptcb->taskStk = stk;
#endif     
    ptcb->TCBnext = 0;               /* Initialize TCB link in READY list  */
    ptcb->TCBprev = 0;

#if CFG_ROBIN_EN >0                        /* Set task time slice for task robin */
    timeSlice = (parameter&0x7fff0000)>>20; 
    if(timeSlice == 0)
    {
        timeSlice = CFG_TIME_SLICE;
    }
    ptcb->timeSlice = timeSlice;
#endif

#if CFG_FLAG_EN > 0
    ptcb->pnode = 0;                 /* Initialize task as no flag waiting */
#endif

#if CFG_EVENT_EN > 0
    ptcb->eventID  = INVALID_ID;          /* Initialize task as no event waiting*/
    ptcb->pmail    = 0;
    ptcb->waitNext = 0;
    ptcb->waitPrev = 0;
#endif

#if CFG_MUTEX_EN > 0
    /* Initialize task as no mutex holding or waiting                         */
    ptcb->mutexID = INVALID_ID; 
#endif 

#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    ActiveTaskPri(prio);    
#endif    

    if((parameter>>31) == 0)            /* Is task in waitting state?         */
    {                                    /* No,set it into ready list          */
        OsSchedLock();                  /* Lock schedule                      */
        InsertToTCBRdyList(ptcb);       /* Insert into the READY list         */
        OsSchedUnlock();                /* Unlock schedule                    */
    }
    else
    {                                    /* Yes,Set task status as TASK_WAITING*/
        ptcb->state   = TASK_WAITING;    
    }
    return ptcb->taskID;                /* Return task ID                     */
}


/**
 *******************************************************************************
 * @brief      Delete Task     
 * @param[in]  taskID      Task ID 
 * @param[out] None  
 * @retval     E_INVALID_ID      Invalid task ID.     
 * @retval     E_PROTECTED_TASK  Protected task in OS.     
 * @retval     E_OK              Delete successful.    
 *
 * @par Description
 * @details    This function is called to delete assign task.     
 *******************************************************************************
 */
StatusType CoDelTask(OS_TID taskID)
{
    P_OSTCB ptcb;

#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
    {
        return E_INVALID_ID;
    }
#endif
    ptcb = &TCBTbl[taskID];
#if CFG_PAR_CHECKOUT_EN >0 
    if(ptcb->state == TASK_DORMANT)
    {
        return E_INVALID_ID;
    }
#endif
    if(taskID == 0)                     /* Is idle task?                      */
    {                                             
        return E_PROTECTED_TASK;        /* Yes,error return                   */
    }    
    
    if(ptcb->state == TASK_RUNNING)     /* Is task running?                   */
    {
        if(OSSchedLock != 0)            /* Yes,is OS lock?                    */
        {
            return E_OS_IN_LOCK;        /* Yes,error return                   */
        }    
    }
        
#if CFG_MUTEX_EN >0                     /* Do task hold mutex?                */
    if(ptcb->mutexID != INVALID_ID)
    {
        if(MutexTbl[ptcb->mutexID].taskID == ptcb->taskID)
        {                               /* Yes,leave the mutex                */
            CoLeaveMutexSection(ptcb->mutexID);
        }
    }
    
#endif    

    OsSchedLock();                      /* Lock schedule                      */
    
    if(ptcb->state == TASK_READY)       /* Is task in READY list?             */
    {
        RemoveFromTCBRdyList(ptcb);     /* Yes,remove task from the READY list*/
    }

#if CFG_TASK_WAITTING_EN > 0 
    else if(ptcb->state == TASK_WAITING)/* Is task in the WAITING list?       */
    {
        /* Yes,Is task in delay list? */
        if(ptcb->delayTick != INVALID_VALUE)                     
        {
            RemoveDelayList(ptcb);      /* Yes,remove task from READY list    */
        }

#if CFG_EVENT_EN > 0
        if(ptcb->eventID != INVALID_ID) /* Is task in event waiting list?     */
        {        
            /* Yes,remove task from event waiting list                        */
            RemoveEventWaittingList(ptcb);    
        }
#endif

#if CFG_FLAG_EN > 0
        if(ptcb->pnode != 0)         /* Is task in flag waiting list?      */
        {
            /* Yes,remove task from flag waiting list                         */
            RemoveLinkNode((P_FLAG_NODE)ptcb->pnode);    
        }
#endif

#if CFG_MUTEX_EN >0
        if(ptcb->mutexID != INVALID_ID) /* Is task in mutex waiting list?     */
        {
            RemoveMutexList(ptcb);  /* Yes,remove task from mutex waiting list*/
        }
#endif
      }
#endif
    ptcb->state   = TASK_DORMANT;       /* Release TCB                        */
    TaskSchedReq  = TRUE;    

#if CFG_ORDER_LIST_SCHEDULE_EN ==0
    DeleteTaskPri(ptcb->prio);    
#endif    

#if CFG_TASK_SCHEDULE_EN >0
    ptcb->TCBnext = FreeTCB;
    FreeTCB       = ptcb;
#endif
    OsSchedUnlock();                    /* Unlock schedule                    */
    return E_OK;                        /* return OK                          */
}


/**
 *******************************************************************************
 * @brief      Exit Task       
 * @param[in]  None 
 * @param[out] None  
 * @retval     None             
 *
 * @par Description
 * @details    This function is called to exit current task.     
 *******************************************************************************
 */
void CoExitTask(void)
{
    CoDelTask(TCBRunning->taskID);      /* Call task delete function          */
}


#if CFG_TASK_SCHEDULE_EN ==0
/**
 *******************************************************************************
 * @brief      Activate Task       
 * @param[in]  taskID      Task ID 
 * @param[in]  argv        Task argv 
 * @param[out] None  
 * @retval     E_INVALID_ID      Invalid task ID.      
 * @retval     E_OK              Activate task successful.             
 *
 * @par Description
 * @details    This function is called to activate current task.     
 *******************************************************************************
 */
StatusType CoActivateTask(OS_TID taskID,void *argv)
{
    P_OSTCB ptcb;
    OS_STK* stkTopPtr;
#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
    {
        return E_INVALID_ID;
    }
#endif
    ptcb = &TCBTbl[taskID];
#if CFG_PAR_CHECKOUT_EN >0
    if(ptcb->stkPtr == 0)
        return E_INVALID_ID;
#endif
    if(ptcb->state != TASK_DORMANT)    
        return E_OK;


                                        /* Initialize task context. */
    stkTopPtr = InitTaskContext(ptcb->taskFuc,argv,ptcb->taskStk);   
        
    ptcb->stkPtr = stkTopPtr;           /* Initialize TCB as user set         */
    OsSchedLock();                      /* Lock schedule                      */
    InsertToTCBRdyList(ptcb);           /* Insert into the READY list         */
    OsSchedUnlock();                    /* Unlock schedule                    */
    return E_OK;
}
#endif


/**
 *******************************************************************************
 * @brief      Get current task id      
 * @param[in]  None
 * @param[out] None
 * @retval     ID of the current task.             
 *
 * @par Description
 * @details    This function is called to get current task id.     
 *******************************************************************************
 */
OS_TID CoGetCurTaskID(void)
{
    return (TCBRunning->taskID);        /* Return running task ID             */
}

#if CFG_TASK_SUSPEND_EN >0
/**
 *******************************************************************************
 * @brief      Suspend Task      
 * @param[in]  taskID    ID of task that want to suspend.
 * @param[out] None  
 * @retval     E_OK                  Task suspend successful. 
 * @retval     E_INVALID_ID          Invalid event ID. 
 * @retval     E_PROTECTED_TASK      Can't suspend idle task. 
 * @retval     E_ALREADY_IN_WAITING  Task now in waiting state.
      
 *
 * @par Description
 * @details    This function is called to exit current task.     
 *******************************************************************************
 */
StatusType CoSuspendTask(OS_TID taskID)
{
    P_OSTCB ptcb;

    if(taskID == 0)                     /* Is idle task?                      */
    {                                             
        return E_PROTECTED_TASK;        /* Yes,error return                   */
    }   
#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
    {
        return E_INVALID_ID;
    }
#endif
    ptcb = &TCBTbl[taskID];
#if CFG_PAR_CHECKOUT_EN >0  
    if(ptcb->state == TASK_DORMANT)
    {
        return E_INVALID_ID;
    }
#endif
    if(OSSchedLock != 0)
    {
        return E_OS_IN_LOCK;
    }
    if(ptcb->state == TASK_WAITING)     /* Is task in WAITING list?           */
    {
        return E_ALREADY_IN_WAITING;    /* Yes,error return                   */
    }
    
    OsSchedLock();    
    if(ptcb != TCBRunning)              /* Is runing task?                    */
    {
        RemoveFromTCBRdyList(ptcb);     /* No,Remove task from READY list     */
    }
    else
    {
        TaskSchedReq = TRUE;
    }

    ptcb->state = TASK_WAITING;            /* Set task status as TASK_WAITING    */
    OsSchedUnlock();                    /* Call task schedule                 */
    return E_OK;                        /* Return OK                          */
}


/**
 *******************************************************************************
 * @brief      Awake Task     
 * @param[in]  taskID      ID of task that will been awaked.
 * @param[out] None  
 * @retval     E_OK                 Task awake successful. 
 * @retval     E_INVALID_ID         Invalid task ID.
 * @retval     E_TASK_NOT_WAITING   Task now not in waiting state.
 * @retval     E_TASK_WAIT_OTHER    Task now waiting other awake event.
 * @retval     E_PROTECTED_TASK     Idle task mustn't be awaked.      
 *
 * @par Description
 * @details    This function is called to awake current task.     
 *******************************************************************************
 */
StatusType CoAwakeTask(OS_TID taskID)
{
    P_OSTCB ptcb;
    
     if(taskID == 0)                     /* Is idle task?                      */
    {                                             
        return E_PROTECTED_TASK;        /* Yes,error return                   */
    } 
#if CFG_PAR_CHECKOUT_EN >0              /* Check validity of parameter        */
    if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
    {
        return E_INVALID_ID;
    }
#endif
    ptcb = &TCBTbl[taskID];
#if CFG_PAR_CHECKOUT_EN >0  
    if(ptcb->state == TASK_DORMANT)
    {
        return E_INVALID_ID;
    }
#endif
    
    if(ptcb->state != TASK_WAITING)     /* Is task in WAITING list            */
    {
        return E_TASK_NOT_WAITING;      /* No,error return                    */
    }    

#if CFG_TASK_WAITTING_EN > 0
    if(ptcb->delayTick != INVALID_VALUE)/* Is task in READY list              */
    {
        return E_TASK_WAIT_OTHER;       /* Yes,error return                   */
    }

#if CFG_FLAG_EN > 0
    if(ptcb->pnode != 0)             /* Is task in flag waiting list       */
    {
        return E_TASK_WAIT_OTHER;       /* Yes,error return                   */
    }
#endif

#if CFG_EVENT_EN>0
    if(ptcb->eventID != INVALID_ID)     /* Is task in event waiting list      */
    {
        return E_TASK_WAIT_OTHER;       /* Yes,error return                   */
    }
#endif    

#if CFG_MUTEX_EN > 0
    if(ptcb->mutexID != INVALID_ID)     /* Is task in mutex waiting list      */
    {
        return E_TASK_WAIT_OTHER;       /* Yes,error return                   */
    }
#endif

#endif      //CFG_TASK_WAITTING_EN

    /* All no,so WAITING state was set by CoSuspendTask()                     */
    OsSchedLock();                      /* Lock schedule                      */
    InsertToTCBRdyList(ptcb);           /* Insert the task into the READY list*/
    OsSchedUnlock();                    /* Unlock schedule                    */
    return E_OK;                        /* return OK                          */
}
#endif