Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2012 / uCOS / uCOSII_ebook.pdf
Скачиваний:
70
Добавлен:
10.02.2015
Размер:
1.08 Mб
Скачать

OS_STK TaskStack[TASK_STACK_SIZE];

OSTaskCreate(task, pdata, &TaskStack[0], prio);

Listing 4.7, Stack grows from LOW memory to HIGH memory

When OS_STK_GROWTH is set to 1 in OS_CPU.H, you need to pass the highest memory location of the stack to the task create function as shown in listing 4.8.

OS_STK TaskStack[TASK_STACK_SIZE];

OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);

Listing 4.8, Stack grows from HIGH memory to LOW memory

This requirement affects code portability. If you need to port your code from a processor architecture that supports a downward growing stack to an upward growing stack then you may need to do like I did in OS_CORE.C for

OSTaskIdle() and OSTaskStat(). Specifically, with the above example, your code would look as shown in listing 4.9.

OS_STK TaskStack[TASK_STACK_SIZE];

#if OS_STK_GROWTH == 0

OSTaskCreate(task, pdata, &TaskStack[0], prio); #else

OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio); #endif

Listing 4.9, Supporting stacks which grow in either direction.

The size of the stack needed by your task is application specific. When sizing the stack, however, you must account for nesting of all the functions called by your task, the number of local variables that will be allocated by all functions called by your task and, the stack require ments for all nested interrupt service routines. In addition, your stack must be able to store all CPU registers.

4.03 Stack Checking, OSTaskStkChk()

It is sometimes necessary to determine how much stack space a task actually uses. This allows you to reduce the amount of RAM needed by your application code by not over allocating stack space. µC/OS -II provides a function called OSTaskStkChk() that provides you with this valuable information.

Refer to figure 4-2 for the following discussion. Stack checking is performed on demand as opposed to continuously. Note that figure 4-2 assumes that stack grows from high memory to low memory (i.e. OS_STK_GROWTH is set to 1)

but, the discussion applies equally well to a stack growing in the opposite direction F4-2(1). µC/OS-II determines stack growth by looking at the contents of the stack itself. To perform stack checking, µC/OS-II requires that the stack get filled with zeros when the task is created F4-2(2). Also, µC/OS-II needs to know the location of the bottom-of-stack (BOS) F4-2(3) and the size of the stack you assigned to the task F4-2(4). These two values are stored in the task’s OS_TCB when the task is created.

 

LOW MEMORY

 

 

 

0

.OSTCBStkBottom

 

 

(3)

 

 

0

 

 

 

 

 

0

 

 

Free Stack Space

(2)

 

 

(6)

 

 

 

 

0

Deepest

 

 

Stack

 

 

 

 

 

 

Growth

.OSTCBStkSize

 

 

(5)

 

 

(4)

Current

 

 

 

 

 

Location of

 

 

 

Stack Pointer

 

Stack Growth

 

(7)

 

 

 

(1)

 

 

 

 

Used Stack Space

 

 

 

(8)

 

Initial TOS

 

HIGH MEMORY

Figure 4-2, Stack checking

In order for you to use µC/OS-II’s stack checking facilities, you MUST do the following:

1.Set OS_TASK_CREATE_EXT to 1 in OS_CFG.H.

2.Create a task using OSTaskCreateExt() and give the task much more space than you think it really needs.

3.Set the opt argument in OSTaskCreateExt() to OS_TASK_OPT_STK_CHK + OS_TASK_OPT_STK_CLR. Note that if your startup code clears all RAM and you never delete tasks once they are created then, you don’t need to set the OS_TASK_OPT_STK_CLR option. This will reduce the execution time of OSTaskCreateExt().

4.Call OSTaskStkChk() by specifying the priority of the task you desire to check.

OSTaskStkChk() computes the amount of free stack space by ‘walking’from the bottom of the stack and counting the number of zero entries on the stack until a non-zero value is found F4-2(5). Note that stack entries are checked using the data type of the stack (see OS_STK in OS_CPU.H). In other words, if a stack entry is 32 -bit wide then the comparison for a zero value is done using 32 bits. The amount of stack space used F4-2(8) is obtained by subtracting the number of zero value entries F4-2(6) from the stack size you specified in OSTaskCreateExt(). OSTaskStkChk() actually places the number of bytes free and the number of bytes used in a data structure of type OS_STK_DATA (see uCOS_II.H). You should note that at any given time, the stack pointer for the task being

checked may be pointing somewhere between the initial Top-Of-Stack (TOS) and the deepest stack growth F4 -2(7). Also, every time you call OSTaskStkChk(), you may get a different value for the amount of free space on the stack until your task has reached its deepest growth F4-2(5).

You will need to run the application long enough and under your worst case conditions to get proper numbers. Once OSTaskStkChk() provides you with t he worst case stack requirement, you can go back and set the final size of your

stack. You should accommodate for system expansion so make sure you allocate between 10 and 25% more. What you should get from stack checking is a ‘ballpark’figure; you are not looking for an exact stack usage.

The code for OSTaskStkChk() is shown in listing 4.10. The data structure OS_STK_DATA (see uCOS_II.H) is

used to hold information about the task stack. I decided to use a data structure for two reasons. First, I consider OSTaskStkChk()to be a ‘query’type function and I wanted to have all query functions work the same way–return

data about the query in a data structure. Second, passing data in a data structure is both efficient and allows me to add additional fie lds in the future without changing the API (Application Programming Interface) ofOSTaskStkChk().

For now, OS_STK_DATA only contains two fields: OSFree and OSUsed. As you can see, you invoke OSTaskStkChk() by specifying the priority of the task you desire to perform stack checking on. If you specify OS_PRIO_SELF L4.10(1) then it is assumed that you desire to know the stack information about the current task. Obviously, the task must exist L4.10(2). To perform stack checking, you must have created the task using OSTaskCreateExt() and you must have passed the option OS_TASK_OPT_STK_CHK L4.10(3). If all the proper conditions are met, OSTaskStkChk() computes the free stack space as described above by ‘walking’from

the bottom of stack until a non-zero stack entry is encountered L4.10(4). Finally, the information to store in OS_STK_DATA is computed L4.10(5). Note that the function computes the actual number of bytes free and the

number of bytes used on the stack as opposed to the number of elements. Obvio usly, the actual stack size (in bytes) can be obtained by adding these two values.

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata)

 

{

 

 

OS_TCB

*ptcb;

 

OS_STK

*pchk;

 

INT32U

free;

 

INT32U

size;

 

pdata->OSFree = 0;

 

pdata->OSUsed = 0;

 

if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {

 

return (OS_PRIO_INVALID);

 

}

 

 

OS_ENTER_CRITICAL();

 

if (prio == OS_PRIO_SELF) {

(1)

prio = OSTCBCur->OSTCBPrio;

 

}

 

 

ptcb = OSTCBPrioTbl[prio];

 

if (ptcb == (OS_TCB *)0) {

(2)

OS_EXIT_CRITICAL();

 

return (OS_TASK_NOT_EXIST);

 

}

 

 

if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0) {

(3)

OS_EXIT_CRITICAL();

 

return (OS_TASK_OPT_ERR);

 

}

 

 

free = 0;

(4)

size = ptcb->OSTCBStkSize;

 

pchk = ptcb->OSTCBStkBottom;

 

OS_EXIT_CRITICAL();

 

#if OS_STK_GROWTH == 1

 

while (*pchk++ == 0) {

 

free++;

 

}

 

 

#else

 

 

while (*pchk-- == 0) {

 

free++;

 

}

 

 

#endif

 

 

pdata->OSFree = free * sizeof(OS_STK);

(5)

pdata->OSUsed = (size - free) * sizeof(OS_STK);

 

return (OS_NO_ERR);

}

Listing 4.10, Stack checking function

4.04 Deleting a Task, OSTaskDel()

It is sometimes necessary to delete a task. Deleting a task means that the task will be returned to the DORMANT state (see Section 3.02, Task States) and does not mean that the code for the task will be deleted. The task code is simply no longer scheduled by µC/OS -II. You delete a task by callingOSTaskDel() and the code for this function is shown in

listing 4.11. Here, we start by making sure that you are not attempting to delete the idle task because this is not allowed L4.11(1). You are, however, allowed to delete the statistic task L4.11(2). OSTaskDel() then checks to

make sure you are not attempting to delete a task from within an ISR which is again not allowed L4.11(3). The caller can delete itself by specifying OS_PRIO_SELF as the argument L4.11(4). We then verify that the task to delete does

in fact exist L4.11(5). This test will obviously pass if you specified OS_PRIO_SELF. I didn’t want to create a separate case for this situation because it would have increased code size and thus execution time.

Once all conditions are satisfied, the OS_TCB is removed from all the possible µC/OS -II data structures. OSTaskDel() does this in two parts to reduce interrupt latency. First, if the task is in the ready list, it is removed

L4.11(6). We then check to see if the task is in a list waiting for a mailbox, a queue or a semaphore and if so, the task is removed from that list L4.11(7). Next, we force the delay count to zero to make sure that the tick ISR wi ll not ready this task once we re-enable interrupts L4.11(8). Finally, we set the task’s .OSTCBStat flag to OS_STAT_RDY. Note that we are not trying to make the task ready, we are simply preventing another task or an ISR from resuming this task (i.e. in case the other task or ISR called OSTaskResume() L4.11(9)). This situation could occur because we

will be re -enabling interrupts L4.11(11) so, an ISR can make a higher priority task ready which could resume the task we are trying to delete. Instead of setting the task’s .OSTCBStat flag to OS_STAT_RDY, I could have simply cleared the OS_STAT_SUSPEND bit (which would have been clearer) but, this takes slightly more processing time.

At this point, the task to delete cannot be made ready to run by another task or an ISR because, it’s been removed from the ready list, it’s not waiting for an event to occur, it’s not waiting for time to expire and cannot be resumed. In other words the task is DORMANT for all intents and purposes. Because of this, I must prevent the scheduler L4.11(10) from switching to another task because, if the current task is almost deleted then it would not be able to be rescheduled! At this point, we want to re -enable interrupts in order to reduce interrupt latency L4.11(11). We could thus service an interrupt but because we incremented OSLockNesting the ISR would return to the interrupted task. You should note that we are still not done with the deletion process because we need to unlink the OS_TCB from the TCB chain and return the OS_TCB to the free OS_TCB list.

Note also that I call a ‘dummy’function (OSDummy()) immediately after calling OS_EXIT_CRITICAL() L4.11(12). I do this because I want to make sure that the processor will execute at least one instruction with interrupts enabled. On many processors, executing an interrupt enable instruction forces the CPU to have interrupts disabled until the end of the next instruction! The Intel 80x86 and Zilog Z-80 processors actually work like this. Enabling and

immediately disabling interrupts would behave just as if I didn’t enable interrupts. This would of course increase interrupt latency. Calling OSDummy() thus ensures that I will execute a call and a return instruction before

re-disabling interrupts. You could certainly replace OSDummy() with a macro that executes a ‘no-operation’ instruction and thus slightly reduce the execution time of OSTaskDel(). I didn’t think it was worth the effort of

creating yet another macro that would require porting.

We can now continue with the deletion process of the task. After we re -disable interrupts, re -enable scheduling by decrementing the lock nesting counter L4.11(13). We then call the user definable task delete hook, OSTaskDelHook() L4.11(14). This allows user defined TCB extensions to be relinquished. Next, we decrement the task counter to indicate that there is one less task being managed by µC/OS -II. We remove the OS_TCB from the priority table by simply replacing the link to the OS_TCB of the task being deleted with a NULL pointer L4.11(15). We then remove the OS_TCB of the task to delete from the doubly -linked list of OS_TCBs that starts at OSTCBList L4.11(16). You should note that there is no need to check for the case where ptcb->OSTCBNext == 0 because we cannot delete the idle task which happens to always be at the end of the chain. The OS_TCB is returned to the free list of OS_TCBs to allow another task to be created L4.11(17). Last, but not least, the scheduler L4.11(18) is called to see if a higher priority task has been made ready to run by an ISR that would have occurred when we re-enabled interrupts at step L4.11(11).

INT8U OSTaskDel (INT8U prio)

{

OS_TCB *ptcb; OS_EVENT *pevent;

if (prio == OS_IDLE_PRIO) {

(1)

return (OS_TASK_DEL_IDLE);

 

}

(2)

if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {

return (OS_PRIO_INVALID);

 

}

 

OS_ENTER_CRITICAL();

 

if (OSIntNesting > 0) {

(3)

Соседние файлы в папке uCOS