Вы находитесь на странице: 1из 27

3/25/2004 Progressive Resources LLC, Indianapolis, IN http://www.prllc.com Version 3.

PR_RTX

A Real-Time Executive

For

The Atmel AVR Microcontrollers

Progressive Resources, LLC


4105 Vincennes Road
Indianapolis, IN 46268

©2002 Progressive Resources LLC


1
Progressive Resources LLC
Real-Time eXecutive, PR_RTX

Table of Contents

Updates in version 3.0 ............................................................................................................................................................................3


Description ...............................................................................................................................................................................................3
Overall Scheme .......................................................................................................................................................................................3
Implementation........................................................................................................................................................................................4
Task Control Block. ...........................................................................................................................................................................4
Memory Considerations.........................................................................................................................................................................8
Task Switching Tables .......................................................................................................................................................................8
Task System and Data Stacks...........................................................................................................................................................8
Stack Considerations..............................................................................................................................................................................8
Stack Allocation:...............................................................................................................................................................................10
Timing Considerations.........................................................................................................................................................................11
PR_RTX function Calls .......................................................................................................................................................................12
PR_Wait_Ticks(ticks)......................................................................................................................................................................12
PR_Wait_Semaphore(ticks)............................................................................................................................................................12
PR_End_Task (Pr_active or PR_inactive) ...................................................................................................................................12
PR_Stop_Task(Task_number)........................................................................................................................................................12
PR_Enable_Task(Task_number, PR_continue or PR_restart) .................................................................................................13
PR_Query_Task(PR_Task_Number) ............................................................................................................................................13
PR_Send_Semaphore(Task_number, Semaphore) .....................................................................................................................13
PR_Query_Semaphore()..................................................................................................................................................................14
Example PR_RTX Application ..........................................................................................................................................................14
PR_RTX with multi-file applications................................................................................................................................................21
Alternate Task-Switching Time Base................................................................................................................................................22
Interrupts.................................................................................................................................................................................................23
External RAM Use................................................................................................................................................................................24
PR_RT_eXaminer.................................................................................................................................................................................24
Software License...................................................................................................................................................................................27
Operating License.................................................................................................................................................................................27
Liability Disclaimer..............................................................................................................................................................................27

©2002 Progressive Resources LLC


2
Progressive Resources LLC
Real-Time eXecutive, PR_RTX

Progressive Resources LLC


Real-Time eXecutive, PR_RTX
Updates in version 3.0
Version 3.0 represents a shift in philosophy for PR_RTX. BE sure to read the documentation thoroughly especially
concerning the follow points:
1. The automatic processor identification has been dropped. The purpose of the processor ID was to determine the
high point of the internal RAM (0x60 or 0x100) and the prescaler division ratios to use in setting up the timer tick
times automatically. Two #defines have been added to the RTX control block to accomplish the same result
without the need for a new version with each new processor introduced.
2. The stack memory allocation scheme has been changed (see Memory Considerations) to use all of the available
RAM memory unless the user specifies a stack size with a task.
3. All RAM (internal and external) is automatically used as space for the stacks if it has been used by the compiler.
i.e. external RAM that is being used by the processor for its stacks will be used by PR_RTX as well.
4. The RT eXaminer tool is included to allow users to examine the stack usage.
These changes will require a couple of additions to the PR_RTX Control block of existing code. The advantages are
that the RTX now works with new processors without revision, uses external RAM if present, and by default (unless the
user specifies otherwise) allocates the largest stacks possible to the tasks following the memory allocation scheme
presented in Stack Considerations.

Description
This is a lean, mean, task-switching machine for the Atmel AVR processors. All of the control
structure is created and held in memory for the tasks. No dynamic allocation/de-allocation is
accomplished. This means that active or inactive tasks still take up RAM space (i.e. inactive tasks
do not release their RAM for other tasks although tasks can be enabled or disabled during program
execution). While not memory-efficient, this scheme allows for very fast task switching.

Overall Scheme
PR_RTX functions by creating separate data and system stacks for each task. Task switching is
accomplished by using a table of stack pointers (for both system and data stacks). The default
source for task-switching ticks is Timer 0 (see Alternate Task-Switching Time Base, below). When
timer 0 (or an alternate time base) times out (creating a ‘tick’), or when the active task calls for a
task (context) switch, the stack pointers for the active task are saved and the stack pointers for the
next task are loaded into the appropriate registers. The task switching function then executes a
return as if from an interrupt function and the next task then runs.

The PR_RTX functions in two modes set by PR_Op_Mode in the PR_RTX Task Control Block.
Setting this definition to 1 produces a strict round-robin form of task switching. In round-robin
mode it is important that each task give up the processor whenever possible via the PR_End_Task
function or one of the PR_Wait… functions. The default task has a few restrictions when returning
control back to PR_RTX. In round robin mode, PR_End_Task (PR_Active) may be called from the
default task. PR_Wait….. functions however, are not allowed and are locked out by PR_RTX.
This is done to assure that there is always one 'ready' task able to be run. Releasing the processor
back to PR_RTX whenever possible, is very important to maximize throughput. In round-robin
mode, the ‘tick’ time is really the maximum amount of time that the processor can be held by a
single task.

Setting PR_Op_Mode to 2 selects priority mode of task switching. In priority mode, task 0 (the
highest priority task) will be given the chance to execute once per tick for time-critical tasks and the

©2002 Progressive Resources LLC


3
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
remaining tasks will then execute in priority order from task 1 down to the default task. As each
task gives up the processor, the next task in priority order will be run if it is ‘ready’ and if not the
following tasks will be given the chance to run, always in priority order. If the default task (the
lowest priority) gets control of the processor in this mode, it will retain control (PR_End_Task,
PR_Wait_Ticks, etc. are disabled) until the tick occurs and task 0 is again offered a chance to run
and the whole process starts over. Higher priority tasks cannot pre-empt running tasks of a lower
priority, however, higher priority processes may be handled on an interrupt basis or the tick time
may be shortened to allow the higher priority tasks to execute more often, simulating a preemptive
system.

Note that it is possible to use the PR_Wait… functions with task 0 in priority mode. This would be
used where the tick rate is to happen faster that the higher priority task is run. Priority mode only
guarantees that task 0 has the chance to run once per tick.

Implementation
The RTOS is essentially implemented by inserting a source file and a task control block into the
code produced by the CodeVisionAVR wizard, the ImageCraft IDE, or written by a programmer (a
complete example is presented in a later section). The source file is PR_RTX_Control.c. This file
contains all of the control structure and initializations to get the RTX running. It also contains the
actual switcher code. It is important that this file be located just below the processor include
directive (i.e. #include <mega16.h> or #include <iom16v.h>) so it gets first chance to claim space
in the memory.

PR_RTX automatically calculates the correct timing values for PR_RTX using information supplied
by the user. See the information on the Task Control Block, below.

The only other PR_RTX implementation steps necessary are to add ‘PR_RTX()’ call block. For
CodeVisionAVR users, it is added just above the main while(1) loop (or above the ‘#asm(“sei”)
code if it is present). For ImageCraftAVR users, it must be added just below the ‘init_devices()’
call in main(). ICC users will also need to comment out or delete the ‘SEI()’ call in the init_devices
function.

If you are using CodeVisionAVR, you must also set the project data stack size to 20 (under Project |
Configure | C Compiler set Data Stack Size to 20). Any CVAVR warnings about the stack being
too small may be ignored because the stacks will be reset by PR_RTX to the values needed. Refer
to 'Stack Considerations' for further information about how CVAVR uses memory.

ICC users need to add the ASM Include Path to the project options for each new project. The path
will be the same as the include path for the C include files.

Task Control Block. The PR_RTX task control block consists of the necessary #defines and the
#include for PR_RTX. The items that must be defined are defined below the sample control block
below:

// PR_RTX Control Block ***************************************


//Compiler definition CVAVR = CodeVisionAVR, ICC = ImageCraft

©2002 Progressive Resources LLC


4
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
#define CVAVR

// System crystal frequency [Hz]


#define PR_Xtal 14745600L

// Task switch time [microseconds]


#define PR_Switch_Time 5000

// Operating mode 1 = round robin


// 2 = priority
#define PR_op_mode 1

// Set low point in RAM for the task stacks.


#define PR_Stack_Ram_Low 0x60

// Define the stack sizes for the default task.


#define PR_default_task_data_stack 40
#define PR_default_task_system_stack 80

// Define the maximum number of tasks for the project.


#define PR_Max_Tasks 10

// Define the prescaler divisor type. See docs for details.


#define PR_Divisor_Type_1

// Define eXaminer use if necessary


// #define PR_RT_eXaminer

// Define assembly code include file if using ICC.


// Use the correct processor number in the aiom___.s file name.
// asm (".include aiom128.s");

#include "PR_RTX_Control.c"
// End of PR_RTX Control Block ********************************

Control block definitions:


// PR_RTX Control Block ***************************************
//Compiler definition CVAVR = CodeVisionAVR, ICC = ImageCraft
#define CVAVR
This define is used to tell the RTX which compiler is being used. RTX treats some
functions differently and provides different code depending upon the compiler being used.
// System crystal frequency [Hz]
#define PR_Xtal 14745600L
This define provides the system clock information that RTX uses to calculate the
appropriate timing constants for the requested 'tick' time (below). For users of CVAVR,
version 1.23.4 or later, the ‘#define PR_Xtal’ line is optional for your projects. If it is not
included in the PR_RTX control block, the _MCU_CLOCK_FREQUENCY_ macro tag
from CVAVR will be used to calculate all the timing, etc.
// Task switch time [microseconds]
#define PR_Switch_Time 5000
This define provides the basic system 'tick' time for task switching. It is used for timing
all waits as well as controlling task switching. This define is not required if using an
alternate task switching time base. See Alternate Task-Switching Time Base.
// Operating mode 1 = round robin

©2002 Progressive Resources LLC


5
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
// 2 = priority
#define PR_op_mode 1
This define sets the operating mode as detailed in Overall Scheme, above.
// Set low point in RAM for the task stacks.
#define PR_Stack_Ram_Low 0x90
This define sets the low point in RAM that may be used by the RTX for task stacks. For
more details, see Memory Considerations and Stack Considerations.
// Define the stack sizes for the default task.
#define PR_default_task_data_stack 40
#define PR_default_task_system_stack 80
These defines are optional and allow the user to set the data and system stack size for the
default task. The default task is often used for slow, task- intensive, operations and may,
therefore need to have its stacks adjusted independently. Leaving one or both of these
defines out, will cause the default task to be assigned the default stack sizes in the same
manner as the other tasks.
// Define the maximum number of tasks for the project.
#define PR_Max_Tasks 10
This define sets the maximum number of tasks that are to be handled by the RTX. This
number may exceed the actual number of defined tasks in order to make it easier to add
tasks as a project progresses. This number is used to allocate RAM space for the task
stacks and so it is usually a good plan to try and make this number match the planned
number of tasks in the project. Changing it later (especially to add tasks) will reduce the
stack sizes for each task and may, therefore cause some unexpected (and unpleasant)
problems.(refer to author's note under Stack Considerations).
// Define the prescaler divisor type.
#define PR_Divisor_Type_1
Atmel chose to use several different divisor chains for their processors. For the RTX to
correctly calculate the 'tick' time set up and control Timer0, it needs to know which divisor
scheme is appropriate. The table below shows the divisor schemes, their types, and the
processors that use each. If a new processor is produced, check the divisor scheme and
select the divisor type as appropriate.
#define Division Ratios Processors
8515, mega8515, 8535,
mega8535, mega8,
mega16, mega103,
PR_Divisor_Type_1 1, 8, 64, 256, 1024
mega161, mega163,
mega169, mega32,
mega323
1, 8, 32, 64, 128, 256,
PR_Divisor_Type_2 mega64, mega128
1024
1, 8, 16, 32, 64, 256,
PR_Divisor_Type_3 mega162
1024

// Define eXaminer use if necessary


// #define PR_RT_eXaminer
This define is required for the use of the eXaminer tool. If the eXaminer tool is not
needed, this define can be commented out.
// Define assembly code include file if using ICC.
// Use the correct processor number in the aiom___.s file name.

©2002 Progressive Resources LLC


6
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
// asm (".include aiom128.s");
ImageCraft requires that the assembly code definitions be explicitly included. This #define
is not required for CVAVR. Change the number as required to fit the processor in use.
#include <PR_RTX_Control.c>
This line includes the RTX code as a part of the project.
// End of PR_RTX Control Block ********************************

The final line (#include <PR_RTX_Control.c>) assumes that the RTX control files are located in
the compiler ‘inc’ or ‘include’ folder and is the usual way to implement PR_RTX. This makes the
RTX control files available to all of your projects. If you would rather place the file into the folder
with your source files, then use ‘#include “PR_RTX_Control.c” ’ so the compiler looks in the right
place for the file.

The tasks to be performed are entered into the source code below the task control block using this
format for each task:

void task_name (void)


{
// enter your task_name start-up code here
while (1)
{
// enter your task_name code here
}
}

The tasks may be named as you like, but the tasks must be followed by a list of calls to
PR_Create_Task which includes task numbers, task names, task system and data stack sizes and
information on whether the task should be active at start-up or not. The task numbers included in
the PR_Create_Task calls are used for reference when referring to the task during system calls and
they determine the order of switching in Round-Robin mode and the priority for the priority for the
other modes. The task numbers must start at 0 and no numbers may be skipped up to the highest
task number. It is sensible, but not required that the task numbers be sequential and kept in order
starting at 0 unless you are using the RT eXaminer which requires sequential task numbers.

Placing a 0 in either of the stack size parameters will cause the default stack sizes to be allocated by
PR_RTX. See Stack Considerations.

Set the ‘start’ parameter to ‘1’ if the task is to be run at its first opportunity or to ‘0’ if the task is to
start dormant and then be enabled by another task. The format for the task initialization function is:

void PR_Init_Tasks(void)
{ // Task number, Task name, data stack size, system stack size start
PR_Create_Task( 0, first_task, 0, 0, 1);
PR_Create_Task( 1, george_the_task, 4, 10, 1);

// Continue until there is one 'Create' call for each task. Actual spacing is unimportant.
}

©2002 Progressive Resources LLC


7
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Remembering the task numbers can be inconvenient, and so it is often useful (but not required) to
create definitio ns of task numbers that make sense to use in the PR_RTX function calls. Do not use
the same names as the function names. Example:
#define firsttask 0
#define georgetask 1

Memory Considerations
Since there is no provision to allocate or de-allocate data memory in PR_RTX, data memory use
may become a concern. I.e. how many tasks will run without running out of data memory? And
the answer is really totally dependent on the amount of RAM that is available.

Task Switching Tables. Data memory is used by PR_RTX to store the task switching tables.
PR_RTX uses the defined maximum number of tasks (PR_Max_Tasks from the RTX control block)
to create the stack pointer tables used during task switching. These tables use 7 bytes of RAM per
task. In addition, PR_RTX uses 13 bytes of overhead and 20 bytes for the start- up data stack
required by the compiler. So for a Mega16 set to do 20 tasks, the PR_RTX tables and overhead
consume:

20 tasks * 7 bytes/task + 13 bytes overhead = 153 bytes total

Task System and Data Stacks. PR_RTX allows the programmer to set the system stack size and
data stack size for each task either by accepting the default stack sizes or by specifying the stack
size for each task. In the case where the default stack sizes are accepted, PR_RTX calculates the
most memory that it can allow per task and allocates the stacks to each task.

Stack Considerations
Understanding how the stacks are arranged and allocated is the first step to determine how to select
the stack sizes to assure that the project will run as desired.
The first requirement is for the programmer to understand how
each of the compilers and PR_RTX use the available memory.

The figure to the left shows the memory maps produced by


each of the compilers when compiling a program. In each
case the stacks shown grow downward through memory. The
major difference lies in the placement of the data stack (below
the global variables for CVAVR and above the global
variables for ICC). The fact that CVAVR puts the data stack
below the variables accounts for the need for the programmer
to configure the project to use minimum space for the data
stack (Project | Configure | C Compiler | Data Stack Size ).
Setting the value to 20 will allow the project to compile but
will likely produce a warning about the data stack size being too small. This warning may be
ignored.

©2002 Progressive Resources LLC


8
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
PR_RTX re-configures the memory as shown in the figure on the following page.

The original system stack as set by the compiler is retained and is used as the system stack during
the PR_RTX initialization and set-up process. After set-up, this stack becomes the system stack for
the default task.

During the PR_RTX set- up process, the space directly below the Default system stack space is used
as the data stack space for the set-up process. This is accomplished by moving the data stack from
the location specified by the compiler to this new location. Unfortunately any variables that the
compiler places on the stack will have their initial values lost during set-up due to the moving of the
data stack. Variables that are stored on the stack by the compiler will need to have their initial
values set after PR_RTX initializes.

After PR_RTX initialization is complete, the space immediately below the default task system stack
space becomes the data stack for Task 0. Immediately below that is the system stack for Task 0.
Stacks are assigned in this order until each defined task has both a data stack and a system stack.
The data stack for the default task is placed below the last task's system stack. This is shown
graphically below.

The space shown as unused is any space below the #define


PR_Stack_Ram_Low and the top of the global variables. Setting
this define as low as possible will minimize or eliminate any
wasted memory space.

In the case of CVAVR only, the relic data stack space is below the
global variables. This space is unused (wasted) reinforcing the
need to configure the project data stack as small as possible.

Understanding the memory PR_RTX memory allocation plan


allows the programmer to wisely allocate the stacks in order to
maximize the opportunity to avoid stack problems.

PR_RTX usage may be mapped for analysis and debugging using


the PR_RT_eXaminer tool.

©2002 Progressive Resources LLC


9
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Stack Allocation: Three entries in the RTX Control Block affect how the memory is allocated.
These are:
#define PR_Stack_Ram_Low 0x70
&
#define PR_default_task_data_stack 40
#define PR_default_task_system_stack 80

The PR_Stack_Ram_Low definition sets the lowest point in memory that is available to the RTX for
use as stacks. It should be set such that sufficient space is left above the start of RAM (either 0x60
or 0x100 depending on your processor) to contain the global variables and to allow for any stack
space not being allocated by PR_RTX. I.e. in a Mega128 processor project that had 0x150 bytes of
global variables, had allocated a 200 byte data stack to one task, and had set the CVAVR data stack
space to 0x20 bytes, the define would be:
#define PR_Stack_Ram_Low 0x338
The 0x338 came from:
0x100 Start of RAM
0x150 Global variable space
200 Allocated stack space
0x20 CVAVR data stack
0x338 Pre-allocated RAM space

In other words, you must determine the amount of RAM you want to set aside and the balance will
be used for the task stacks by PR_RTX. The actual amount of RAM allocated to the global
variables is reported by the compiler at the time of compilation. So, the order is usually:
1. Check the spec to determine the low address of available RAM (0x60 or 0x100).
2. Check the Project|Configure|C Compiler and set the amount of space allocated to the data
stack (CVAVR only).
3. Add up the stack space that is pre-allocated in PR_Init_Task ..
4. Compile the project to get the amount of memory allocated to global variables.
5. Add the results of 1, 2, 3, and 4 to get the value for PR_Ram_Stack_Low.

The PR_default_task_data_stack and PR_default_task_system_stack defines allow you to control


the amount of space in these two stacks. Since the default task is not initialized elsewhere, this is
the only way to control the sizes of these two stacks. These defines are optional and, if left out, will
cause RTX to allocate the default task stacks in the same manne r as the other tasks.

Keeping in mind that conserving memory space is worthless if you are not going to use it for
something else, PR_RTX divides up the remaining RAM as follows:

Starting with the available RAM space, PR_RTX divides the amount of RAM by the actual number
of tasks specified in the PR_RTX control block to get the amount of RAM available per task. Then
the available RAM per task is allocated 1/3 to the task system stack and 2/3 to the task data stack.
Experience has shown these to be good values for most projects.

Author's note: It may seem like a hassle to do all of the math yourself when the RTX could handle it.
However, if the RTX did all the allocation automatically, you would end up in the situation where
each time you added some stack bytes to one task, and then it would automatically reduce all the
others and cause you no end of mysterious problems. So this seems to be the best compromise. If I

©2002 Progressive Resources LLC


10
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
were you, I would set aside a hundred bytes more than you need in the area prescribed by
PR_Ram_Stack_Low so you can add some bytes where needed without reducing other stacks.

So the answer to the question of 'how many tasks will the xxx processor handle?' is really answered
by saying that as long as there is sufficient stack space, the processor can handle the tasks.

Timing Considerations
PR_RTX uses an algorithm to figure the timer0 prescaler that selects the smallest possible prescaler
division number. Usually this will produce the most accurate timing for the tick. Three possible
concerns remain: 1) is that really the most accurate timing possible, 2) how close does the actual
timing fall to the requested tick time, and 3) can PR_RTX provide the timing I have requested (what
limits are there to the timing calculations)? Caution: Cases exist, particularly with high speed
clocks and fast tick rates, in which PR_RTX will not correctly calculate the tick rate. If you suspect
an errant tick rate, always place an extra task in the project that creates a visible event (toggles a
port pin?) once per tick and check the tick rate using an oscilloscope or similar device. If the tick
rate is not right, see #1 below for the method to manually set the timing.

1. Selecting the most accurate timing sometimes invo lves juggling the division ratio and the
timer reload number to get the best accuracy. If the programmer wishes to set the timer0 tick
time him or herself, simply insert the following two lines into the PR_RTX control block and all
of the PR_RTX automatic calculations will be skipped:

PR_TCCR0_Set = x; //’x’ is the division ration selector to be loaded into TCCR0


PR_Reload_Set = y; //’y’ is the value to be reloaded into TCNT0 to set the tick length

2. PR_RTX truncates the crystal frequency to the nearest 100 Hz for its calculations. This is
necessary to keep the calculation number sizes within the limits of the compiler (see #3 below).
Normally the difference is negligible, but might be significant in critical timing situations. The
actua l time of each tick can be calculated as follows:
Actual tick time = (System crystal freq to 100 Hz/System crystal actual) * PR_RTX tick time

Example: A PR_RTX-based system uses a 3.689599 MHz crystal and is set for a 5000 usec.
Tick. The actual tick time is:
Actual tick time = (3.6895/3.689599) * 5000 us = 4999.86 us.

3. The calculation of the timer constants is limited by the math limits of the compiler. The
largest number that the compiler can handle is an unsigned long int. Hence, the system crystal
frequency/100 times the tick time (in microseconds) must be less than 232 or 4.295E9. If this
situation occurs, an appropriate error message will usually appear and the programmer will need
to manually specify the TCCR0 setup and the timer0 reload number as in #1 above.

Example: A PR_RTX-based system using a 7.6083 MHz crystal and asking for a 6000
microsecond tick time would make the limit check as follows:
(7608300/100) * 6000 = 456.5E6

©2002 Progressive Resources LLC


11
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Since the result is below 4.295E9, PR_RTX will correctly calculate the tick constants, but as
should be evident, some of the faster speeds and longer tick times will definitely exceed the
math abilities of the compiler.

PR_RTX function Calls


The functions below are provided by PR_RTX and are included in PR_RTX_Control.c. They are
typically implemented as macro function calls.

PR_Wait_Ticks(ticks)
This function allows a task to put itself to sleep for a specific number of timer ticks (‘ticks’ is an
unsigned char variable and so the maximum wait time is 255 ‘ticks’). When the correct number of
ticks has passed it will again be marked as ‘ready’ to execute at its next opportunity in the switching
scheme. Executing this macro will cause an immediate task switch to occur to the next ‘ready’ task.
The waiting condition can be overridden by calling PR_Enable_Task from another task.

Example:
PR_Wait_Ticks(10); //This call removes the task from the active task list for 10 ticks.

PR_Wait_Semaphore(ticks)
This function is much like PR_Wait_ticks except that the task puts itself into a waiting state while
waiting for a semaphore from another task. It removes the current task from the active task list until
a semaphore is received. It also may set a maximum time for task to await the semaphore. Setting
‘ticks’ to 0 will disable the time-out feature. After the task again becomes active it can determine
whether the readying source was a semaphore or a time out by use of PR_Query_Semaphore() as
shown below:

Example:
PR_Wait_Semaphore(ticks); //This call removes the current task from the active task list until a semaphore is received or
// until ‘ticks’ ticks have elapsed, whichever occurs first.

PR_End_Task (Pr_active or PR_inactive)


A task may end itself and immediately return control to the task switcher. Setting the parameter to
‘PR_active’ will allow the task to again be run as soon as its turn comes around continuing from
wherever it was ‘ended’. Setting it to ‘PR_inactive’ will cause the task to be permanently un-
readied (set to dormant) until another task re-readies it through the use of PR_Enable(). If the task
is re-readied thru PR_Enable_Task, it may either pick up from the point at which it was disabled or
it can completely restart – see PR_Enable_Task, below.

Example:
PR_End_Task (PR_active); //This call returns control to the task switcher until this task’s turn comes around again.
PR_End_Task (PR_inactive); //This call returns control to the task switcher and sets the task to dormant.

PR_Stop_Task(Task_number)
This function is called by one task to disable another if necessary. The disabled task is not lost and
may re-enabled via the PR_Enable_Task() function shown below. If the task is re-readied thru
PR_Enable_Task, it may either pick up from the point at which it was disabled or it can completely
restart – see PR_Enable_Task, below.

©2002 Progressive Resources LLC


12
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Example:
PR_Stop_Task(Task_number); //This call removes the denoted task from the active task list.
//This task will continue from the point at which it was stopped.

PR_Enable_Task(Task_number, PR_continue or PR_restart)


This function will cause a task to be re-enabled to return it to the active task list. Calling this
function will only cause the task to be returned to the active state. I.e. the task will be executed
when its turn in the round-robin or priority switch list occurs. This function will also override and
make ready tasks that are waiting for semaphores or for time-outs. CAUTION: ‘Enabling’ a task
that is not dormant can cause unexpected results. This occurs because any waiting conditions
(time out or semaphore) are cancelled when the task is enabled.

Setting the second parameter to ‘PR_continue’ will cause the task to be re-enabled to run form the
point at which it was ‘ended’ or ‘stopped’ by calls to the functions above. Setting the second
parameter to ‘PR_restart’ will cause the task to start running from its beginning (i.e. re- initialize the
task) when its turn to run comes around. CAUTION: Restarting a task that is running will cause
the task to start over at its beginning.

Example:
PR_Enable_Task(Task_number, PR_continue); //This call readies the denoted task to cancel a ‘stop’ or ‘end’ function.
//The task will start from the point at which it was ‘stopped’ or ‘ended’.
PR_Enable_Task(Task_number, PR_restart); //This call readies the denoted task to cancel a ‘stop’ or ‘end’ function.
//The task will re-initialize and start from its beginning.

PR_Query_Task(PR_Task_Number)
This queries the status of another task. It allows a task to determine if the task is ready, inactive,
awaiting a semaphore or waiting for time to pass.

Example:
//This queries the status of the #8 task to see if it is dormant.
if (PR_Query_Task(8)== PR_task_dormant) ……. //find out if task 8 is inactive
-or-
x = PR_Query_Task(8); //get the current status of task 8 into x

The possible results of PR_Query_Task() are:


PR_task_dormant Task is inactive.
PR_task_ready Task is ready to run.
PR_task_wait_semaphore Task is awaiting a semaphore.
PR_task_wait_time Task is awaiting time out.

PR_Send_Semaphore(Task_number, Semaphore)
This task sends a semaphore to another task. If the task is awaiting a semaphore, it will be
immediately set to the ‘ready’ state for execution at its next opportunity. If the task is not awaiting
a semaphore, this function will have no effect. The semaphore sent may be a value from 0 to 15.
The receiving task can interrogate the semaphore process to get this va lue and make decisions based
on it (See PR_Query_Semaphore(), below).

©2002 Progressive Resources LLC


13
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Example:
PR_Send_Semaphore(Task_number, Semaphore); //This sends a semaphore to ‘task_number’.

PR_Query_Semaphore()
This queries the result of being awakened by a semaphore. It allows a task to determine if it was
awakened by a time-out or by a semaphore and, if so, to retrieve the semaphore.

Example:
//This queries the semaphore sent to the current task..
if (PR_Query_Semaphore() == 0x10)……. //checks for an awakening by a time out
-or-
x = PR_Query_Semaphore(); //returns the value of the semaphore that was sent

The possible results returned are ‘PR_task_timed_out’ (0x10), meaning that the task was awakened
by a time-out while waiting for a semaphore, or a value from 0 through 15 (0xf) that is the value of
the semaphore sent.

Example PR_RTX Application


The example application program is designed to blink two LED’s in an asynchronous fashion. Each
of the LED’s blinking is controlled by a serial command word. The example hardware consists of a
Mega163 with a green LED tied to bit 0 of Port A and a red LED tied to bit 1 of Port A. In both
cases the LED’s are connected so that a TTL low on the port pin will illuminate the LED. This
example is included with PR_RTX in a folder appropriate to the compiler being used (the examples
are cvavr_ex.prj and icc_ex.prj which are in their respective folders with source file(s)).

A serial command word is used to enable or disable the blinking of each LED. The least significant
two bits control the LED’s. If bit 0 of the command word is low, the red LED is off and not
blinking, if it is high then the red LED is blinking at a 500ms rate. Bit 1 controls the green LED in
a similar manner except that it blinks at a 200ms rate.

Whenever a control word is received, a statement is printed via the serial port that states the value of
the control word.

This entire program could be written using a timer interrupt and some conditional statements.
However, the programmer would need to figure out the delay times, and make sure the interaction
between the blinkers and the control system were clear of glitches. With PR_RTX, the timing
issues and interactions are all handled by PR_RTX making the project much simpler and quicker to
develop.

The first step in using PR_RTX for this project is to divide the project into tasks. One possible way
to divide the tasks is as follows:
One task is responsible to monitor the serial port for command words and to respond with the
printed value of the command word.
One task is responsible to determine which LED(s) should be blinking and set them accordingly.
One task is responsible to blink the green LED.
One task is responsible to blink the red LED.

©2002 Progressive Resources LLC


14
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Having divided the tasks, only two system decisions need to be made: the tick rate for the system
and the mode of operation. The tick rate is pretty open, but a good rule of thumb is to set it at 1/10
of the timing rate needed. If the timing is to time 200ms events (the shortest time in this example),
the maximum tick rate ought to be 20ms or less. The maximum number of ticks a task can wait is
255 ticks. In this example, the longest time is 500ms and so the shortest tick that can be
accommodated is 2ms. Somewhere between 2ms and 20ms is workable. For the example 5ms was
chosen.

The only other decision is which mode (priority or round robin) to use. None of the tasks is
particularly more critical than any other; so round robin mode is the likely choice.

Now the tasks need to be created. The LED blinker tasks are very simple with PR_RTX handling
the timing and interface functions. They look like this:

void blnkred (void)


{
unsigned char x = 255; //start slow blink
PORTC |= 0x2; //start with light on
while (1)
{
PORTC ^= 0x02; //change red LED state
PR_Wait_Ticks(x); //wait 500 ms
x = 10; //change to fast blink after first blink
}
}

void blnkgrn (void)


{
while (1)
{
PORTC ^= 0x01; //change green LED state
PR_Wait_Ticks(40); //wait 200 ms
}
}

In each case, the state of the LED is toggled using an exclusive OR construct each time the task
runs. After toggling the LED state, the task uses a PR_RTX function call (PR_Wait_Ticks(x)) to
put itself to sleep until the LED state needs to be changed again. In the case of the red LED, either
255 or 10 are passed to PR_Wait_Ticks to create a long or short delay respectively until the next
toggle (5ms tick * # ticks delay). Similarly, the green LED blinker task wait 40 ticks for a total
delay of 200ms. PR_RTX is handling the timing making these tasks very simple.

The next task to consider might be the default task. The default task is responsible to monitor the
serial port (UART) for a new command character and cause the new command to be carried out.
The default task is placed in the while(1) loop of the main program and looks like this:

// The main program's while(1) loop becomes the default (lowest priority) task.
while (1)
{
if (UCSRA & 0x80)
{
flag = UDR & 0x03; // Mask unused bits of the control character
printf("\n\r\n\rBlink Control Character is %X. \n\r", flag);

©2002 Progressive Resources LLC


15
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
PR_Send_Semaphore (Control,flag); // Send blink control info
}
PR_End_Task(PR_active);
//task releases processor time if in round-robin mode.
}

The default task monitors the RXC bit in the UCSRA UART status register to determine when a
serial character is received. After receipt the unused bits of the command byte are masked off and
the result stored in the global variable flag. The task uses printf() to display the message about the
control bits and then sends a semaphore (via the PR_RTX function PR_Send_Semaphore)
containing the control bits to the blink control task.

After checking for a received character, the default task releases control to the next task via the
PR_End_Task() PR_RTX function call. In this case the call passes the parameter PR_active so that
the task is left in the ready state for its next opportunity to execute. Actually calling PR_End_Task
even with the parameter set to PR_inactive will not permanently disable the default task in round
robin mode. PR_RTX does not allow such an illegal call.

The only task left is the blink control task that controls which LED is blinking and which is not. It
is as follows:

void blnkcontrol (void)


{

unsigned char sema;


while(1)
{
PR_Wait_Semaphore(0); // Await a control instruction
sema = PR_Query_Semaphore(); //Get semaphore contents
if (!(sema & 0x1))
{
PR_Stop_Task(0); //stop 0
PORTC |= 0x2;
}
else
{
PR_Enable_Task(0, PR_restart); //restart 0 at the
//long blink
}
if (!(sema & 0x2))
{
PR_Stop_Task(1); //stop 1
PORTC |= 0x1;
}
else
{
PR_Enable_Task(1, PR_continue); //start 1 where it left off
}
}
}

The control task waits to receive a semaphore and then uses the data passed by the semaphore to
determine how to set the LED blinker tasks. This task uses the PR_Stop_Task() PR_RTX function
call to stop the blinker task for the LED(s) that are not supposed to be blinking and
PR_Enable_Task() to enable the blinker task for the LED(s) that are supposed to be blinking. The

©2002 Progressive Resources LLC


16
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
task also sets the port bit high when a task is disabled to assure that the LED is off when it is not
blinking.

Now look at the entire program to see how simply PR_RTX is used with this program. The bolded
areas are those added by the programmer beyond what is set up by the CodeVisionAVR Code
Wizard. (Note: This is similar to, but not the same as the example provided with PR_RTX for each
compiler.)

/*********************************************
This program was produced by the
CodeWizardAVR V1.23.4 Standard
Automatic Program Generator
© Copyright 1998-2002 HP InfoTech s.r.l.
http://www.hpinfotech.ro
e-mail:office@hpinfotech.ro , hpinfotech@xnet.ro

Project :
Version :
Date : 7/14/2002
Author : Progressive Resources LLC
Company : 4105 VINCENNES Rd., Indianapolis
Comments:
PORTC is used for LEDs on MegaAVR-Dev board
available from http://www.prllc.com

Chip type : ATmega163


Program type : Application
Clock frequency : 6.000000 MHz
Memory model : Small
Internal SRAM size : 1024
External SRAM size : 0
Data Stack size : 256
*********************************************/

#include <mega163.h>
#include <stdio.h>

// PR_RTX Control Block ***************************************


//Compiler definition CVAVR = CodeVisionAVR, ICC = ImageCraft
#define CVAVR

// System crystal frequency [Hz]


#define PR_Xtal 14745600L A
// Task switch time [microseconds]
#define PR_Switch_Time 5000

// Operating mode 1 = round robin


// 2 = priority
#define PR_op_mode 1

// Set low point in RAM for the task stacks.


#define PR_Stack_Ram_Low 0x60

// Define the stack sizes for the default task. These


// is an optional defines. If either is not present, the default
// task gets the same default stack sizes as the other tasks.
#define PR_default_task_data_stack 40
#define PR_default_task_system_stack 80

©2002 Progressive Resources LLC


17
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
// Define the maximum number of tasks for the project.
#define PR_Max_Tasks 10

// Define the prescaler divisor type. See docs for details.


#define PR_Divisor_Type_1

// Define assembly code include file if using ICC.


// Use the correct processor number in the aiom___.s file name.
// asm (".include aiom128.s");

#include "PR_RTX_Control.c"
// End of PR_RTX Control Block ********************************

// Declare your global variables here


unsigned char flag;

void blnkred (void)


{
unsigned char x = 255; //start slow blink
PORTC |= 0x2; //start with light on
while (1)
{
PORTC ^= 0x02; //change red LED state
PR_Wait_Ticks(x); //wait 500 ms
x = 10; //change to fast blink after first blink
}
}

void blnkgrn (void)


{
while (1)
{
PORTC ^= 0x01; //change green LED state
PR_Wait_Ticks(40); //wait 200 ms
}
} B
void blnkcontrol (void)
{

unsigned char sema;


while(1)
{
PR_Wait_Semaphore(0); // Await a control instruction
sema = PR_Query_Semaphore(); //Get semaphore contents
if (!(sema & 0x1))
{
PR_Stop_Task(0); //stop 0
PORTC |= 0x2;
}
else
{
PR_Enable_Task(0, PR_restart); //restart 0 at the
//long blink
}
if (!(sema & 0x2))
{
PR_Stop_Task(1); //stop 1
PORTC |= 0x1;
}
else
{
PR_Enable_Task(1, PR_continue); //start 1 where it left off

©2002 Progressive Resources LLC


18
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
}
}
}

#define RedLed 0 /*assign a name to the red blinker task*/


#define GreenLed 1 /*assign a name to the green blinker task*/ C
#define Control 2 /*assign a name to the blinker control task*/

void PR_Init_Tasks(void)
{
PR_Create_Task(0,blnkred,0,0,1); //start task 0
PR_Create_Task(2,blnkcontrol,0,0,1); //start task 2 D
PR_Create_Task(1,blnkgrn,0,0,1); //start task 1
}

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization


// Port A
PORTA=0x00;
DDRA=0x03;

// Port B
PORTB=0x00;
DDRB=0x00;

// Port C
PORTC=0x00;
DDRC=0x03;

// Port D
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Output Compare
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Output Compare
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceller: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock

©2002 Progressive Resources LLC


19
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
// Clock value: Timer 2 Stopped
// Mode: Output Compare
// OC2 output: Disconnected
TCCR2=0x00;
ASSR=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization


// INT0: Off
// INT1: Off
GIMSK=0x00;
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization


TIMSK=0x00;

// UART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// UART Receiver: On
// UART Transmitter: On
// UART Baud rate: 9600
UCSRA=0x00;
UCSRB=0x18;
UBRR=0x26;
UBRRHI=0x00;

// Analog Comparator initialization


// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// RTX call block ****************************************


PR_RTX(); //Start RTOS D
// End of RTX call Block *********************************

// The main program's while(1) loop becomes the default (lowest priority) task.
while (1)
{
if (UCSRA & 0x80) E
{
flag = UDR & 0x03; // Mask unused bits of the control character
printf("\n\r\n\rBlink Control Character is %X. \n\r", flag);
PR_Send_Semaphore (Control,flag); // Send blink control info
}
PR_End_Task(PR_active);
//task releases processor time if in round-robin mode.
}
}

An explanation of the areas added to use PR_RTX:

A. This is the task control block that defines the crystal frequency, the tick rate, the default task
system stack size, and the operating mode. It also contains the #include to include
PR_RTX.

B. These are the tasks.

©2002 Progressive Resources LLC


20
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
C. This is a table of #defines used to give easy-to-remember tags to the tasks so you don’t have
to remember the numbers. These were included in the program, but not used. As an
example, the call to disable the red LED blinker was:
PR_Stop_Task(0); //stop 0

Using the tag, it could have been written as:


PR_Stop_Task(RedLed); //stop 0

The second form is much easier when coding larger tasks since you do not need to
remember the task’s numbers.

D. This is the call to PR_RTX. PR_RTX sets up the control tables and otherwise starts the
switching process before turning control over to the default task in the while(1) just below
the call to PR_RTX.

E. The default task.

PR_RTX with multi-file applications.


In many instances, it is prudent or convenient to split a program into multiple source files. PR_RTX supports this as
follows:

1. Create the main file as shown in the example above.


2. Copy the PR_RTX_Macros_defs.h into the same directory as the PR_RTX_Control.c file.
3. Each additional task source file must contain the PR_RTX control block just as the main file does except that the
‘#include’ for PR_RTX must be changed to:
#include <PR_RTX_Macros_defs.h>

So, for the example above, the control block in each additional task source file would look like:
// PR_RTX Control Block ***************************************
//Compiler definition CVAVR = CodeVisionAVR, ICC = ImageCraft
#define CVAVR

// System crystal frequency [Hz]


#define PR_Xtal 14745600L

// Task switch time [microseconds]


#define PR_Switch_Time 5000

// Operating mode 1 = round robin


// 2 = priority
#define PR_op_mode 1

// Set low point in RAM for the task stacks.


#define PR_Stack_Ram_Low 0x60

// Define the stack sizes for the default task. These


// is an optional defines. If either is not present, the default
// task gets the same default stack sizes as the other tasks.
#define PR_default_task_data_stack 40
#define PR_default_task_system_stack 80

// Define the maximum number of tasks for the project.


#define PR_Max_Tasks 10

// Define the prescaler divisor type. See docs for details.

©2002 Progressive Resources LLC


21
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
#define PR_Divisor_Type_1

// Define assembly code include file if using ICC.


// Use the correct processor number in the aiom___.s file name.
// asm (".include aiom128.s");

#include <PR_RTX_Macros_defs.h>
// End of PR_RTX Control Block ********************************

3. Add a prototype for each external function (those in additional source files) to the main
project file. i.e.:
extern void blink3 (void);
4. Include a call to PR_Create_Task in PR_Init for each task defined in the external modules.

That is all it takes. An additional example using multiple files is included in the example folder for
your compiler. It is the same name as the example above with ‘_mf’ added to the name
(cavar_mf.prj or icc_mf.prj).

When working with multi- file programs it is a good ides to put the PR_RTX control block (without
the #include for PR_RTX_Control.c or for PR_RTX_Macros_defs.h) and then #include the control
block file into each module. This avoids the problem of changing multiple files when you need to
make a change to the PR_RTX control block. The multi- file example shows the use of a header file
for the control block.

Alternate Task-Switching Time Base


Occasionally it may be useful to use a time base other than Timer0 to control the task switching.
Examples might be using the ADC interrupt to switch tasks when Timer0 is needed for other duties.
Or possible switching tasks based on an external interrupt in cases such as engine control where
tasks switching may need to occur a rate determined by the RPM via the cam signal. In any of these
cases it is possible to control the task switcher via another source provided the user is willing to
assume somewhat more of the programming duties to accomplish this. I.e. some of the automated
features relative to setting up the tick time, etc. will be lost and these duties will fall to the
programmer.

An important concept to remember in setting an alternate time base up is that the alternate time base
tick only controls the ‘tick’ time. That is, it controls when the highest priority task is run in priority
mode or it controls the maximum run time for a task in round-robin mode. No matter which time
base is controlling the tick, as the tasks release the processor, the next task is run without need for a
‘tick’ to occur.

To use another source for switching, insert the following into the PR_RTX Control Block as the
first statement:

#define _ALTERNATE_TIME_BASE_

Putting the #define _ALTERNATE_TIME_BASE_ into the PR_RTX removes the need for the
#defines for crystal frequency and tick time. These may be left out or included, but if included will
have no effect.

©2002 Progressive Resources LLC


22
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
The #define _ALTERNATE_TIME_BASE_ statement allows you, then, to put the task switch
control line (shown below) into the function of your choice. It is best to put this call to the task
manager as the last line in the function. Otherwise, the task switch will occur before the function
completes its work.

PR_Task_Mgr();

Whenever this function is called, the task switching will occur. In priority mode, the switch will
always be to task 0 or to the highest priority ‘ready’ task whereas in round-robin mode the switch
will occur to the next ‘ready’ task in sequence.

Timing considerations when using an alternate time base. When using an alternate time base, you
will need to control how fast the task switcher switches tasks. The task switcher takes around 40 us
to switch in a system with a 4 MHz system clock (and proportionally higher or lower with other
system clock rates). You will need to determine the ‘tick rate’ yourself to use in calling the PR task
manager (the ‘PR_Switch_Time’ statement in the PR_RTX Control Block is meaningless with an
alternate time base).

Assume, for sake of example that you want to do the task switching with the ADC interrupt. The
ADC interrupt occurs every 60 to 210 us depending on the ADC prescaler and system clock. This
may be much faster than you want the task switching to occur. Use the following code with a
global variable to limit the speed of switcher:

unsigned char rtc3; //switcher control

In the ADC ISR function:

if (rtc3++ == 10)
{
rtc3 = 0; //reset the task switch counter
PR_Task_Mgr(); // call the task switcher as the last
// executable line in the function
}

This will cause the task switch to occur every 10 times the ADC interrupt occurs and thereby set the
task switching (or ‘tick’) speed to whatever rate is desired by the programmer.

An additional example using an alternate timebase is included in the example folder for your
compiler. It is the same name as the example above with ‘_atb’ added to the name ( i.e.
cavar_atb.prj or icc_atb.prj). The example uses Timer2 as the alternate timebase.

Interrupts
Interrupts may be used in programs based on PR_RTX. These interrupts will function
independently of PR_RTX and, in general, cause no problems for PR_RTX.

©2002 Progressive Resources LLC


23
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
The only problem is that, with only one level of interrupt, the task switcher cannot pre-empt the
interrupt. Hence some timing might be affected by an interrupt holding the processor when a task
switch time occurs. The task switch will occur as soon as the interrupt releases the processor and
timing will correct itself as long as the interrupt does not hold the processor longer than one ‘tick’
time.

In general, it is a good idea to keep the interrupts somewhat separated from the tasks through the
use of global variables. However some PR_RTX services such as PR_Send_Semaphore() are
available to be used in interrupt service routine. In general those which do not cause a task switch
are OK to use with interrupts whereas those services such as PR_Wait_Ticks() which cause a task
switch are not.

External RAM Use


External RAM (for those processors that support it) may be used with PR_RTX. PR_RTX looks at
where the compiler put the system stack and works from that point downward in assigning task
stack space. So if the original project uses the external space, PR_RTX will use it as well.

PR_RT_eXaminer
PR_RT_eXaminer is a debugging and analysis tool for use with PR_RTX.

Essentially the eXaminer is a facility to print out a map of the stack usage for all tasks as well as a
count of the number of times that each task is being executed. The eXaminer is enabled by putting
the following define into the RTX Control Block of the source program for a PR_RTX project:
#define PR_RT_eXaminer

When the project is compiled the extra code for the eXaminer will be inserted. Then it will be
necessary for the programmer to determine a trigger event. In the example below, a logic low on
PINA.0 is used as the trigger event:
if (!PINA.0) PR_eX_report(); //call eXaminer report

As shown in the example, the trigger event calls a function called PR_eX_report(). This halts the
program and prints out a summary of the stacks usage and the number of times each task has been
executed as shown below:
Task Data Stack System Stack Times
Start End Size Start End Size eXecuted
0 0xffad 0xff83 0x002b 0xeea9 0xee8d 0x001d 1101
1 0xe625 0xe5e0 0x0046 0xd521 0xd507 0x001b 264
2 0xcc9f 0xcc77 0x0029 0xbb9b 0xbb81 0x001b 3
Default 0xb319 0xb2f1 0x0029 0xfffd 0xffde 0x0020 2537471

As can be seen, the report shows the size and location of the stacks for each task and the number of
times that each task has executed when the trigger event occurred.

The eXaminer works by filling the available RAM with 0xff prior to starting the program. Then
when the trigger event calls for the report, the eXaminer starts at the bottom of each system and
data stack and works its up down through memory until a non-0xff value is found. If the end point
is too low, a 'Stack Overflow!' message appears in the table of results. Using this scheme means
that it is not possible to determine how much overlap occurs, only that an overflow did occur.

©2002 Progressive Resources LLC


24
Progressive Resources LLC
Real-Time eXecutive, PR_RTX

By default, the eXaminer provides results via a special non- interrupt based putchar() meaning that
the results will normally be sent via the default serial port. It is the programmer's job to initialize
the serial port as desired.

The eXaminer's output may be re-directed to the other serial port (for processors with 2 serial ports)
by putting the following #define into the PR_RTX control block:
#define USE_UART 1

The eXaminer's output may be re-directed as desired by putting the following #define into the
PR_RTX control block (the SPI bus for example):
#define PR_RT_eXaminer_output spi

To direct the eXaminer’s output to another function, replace spi with whatever the name of the
alternate output function is called. In the same manner, the output could also be directed to a spot in
memory that could later be examined using an ICE or similar tool. This choice would require the
programmer to supply the output function.

The output may be directed to any function desired provided that the function does not allow for
nested interrupts. The eXaminer turns off the interrupts to stop the program operation and if the
output function re-enabled the interrupts, some parts of the overall project will continue to operate,
and the output function may produce unexpected results.

These caveats must be observed when using the eXaminer:


1. The tasks must be listed in task number order in PR_Init_Tasks(). That is the first task listed
must be task 0 and the rest of the tasks must be in order. This allows the stacks to be assigned in
order so that they can be analyzed.
2. The 'times executed' is kept in an 'unsigned long' variable, so if the executed times exceeds
this, you will get a false number.
3. The RT eXaminer adds (8*PR_Max_Tasks)+11 additional bytes to the global variable space.
These bytes are accounted for automatically, but they do have the effect of reducing the
available RAM by that amount. This will shrink the data and system stacks slightly (Divide
(8*PR_Max_Tasks)+13 by PR_Max_Tasks to find out how space is consumed by the PR
eXaminer.
4. The Default Task Stack is analyzed as if it were at the bottom of the RAM space even if
there are less than PR_Max_Tasks tasks defined if it's size is defined in the PR_RTX control
block. Otherwise it is compared to PR_Stack_Ram_Low and may appear huge if less than
PR_Max_Tasks tasks are defined.

Generally speaking, the PR_RT eXaminer is a preventative tool. It usually will not work once your
program has crashed due to stack problems. It is best used to keep an eye on the stacks as you build
up an application. In this mode, the RT eXaminer can alert you to tasks that are getting close to
stack problems.

Or it can be used when you add some code and it crashes by removing the offending code and
checking the stacks, you can see what adjustments may be necessary to support the new code.

©2002 Progressive Resources LLC


25
Progressive Resources LLC
Real-Time eXecutive, PR_RTX
Lastly it can provide a way to balance stacks in the final product. i.e. use the RT eXaminer to check
the stack usage of your completed project. If some tasks are close to the stack limits and some have
more space. Reallocate the stack space. This will go a long way towards assuring that those 'once in
a great while' errors are not occurring because some stack that is close to its limit overflows due to a
'once in a great while condition'.

The Mega 128 CVAVR example included with PR_RTX includes sample use of the eXaminer.

©2002 Progressive Resources LLC


26
Progressive Resources LLC
Real-Time eXecutive, PR_RTX

Software License
The use of Progressive Resources LLC Real- Time Executive (PRRTX) indicates
your understanding and acceptance of the following terms and conditions.
This license shall supersede any verbal or prior verbal or written, statement
or agreement to the contrary. If you do not understand or accept these terms,
or your local regulations prohibit "after sale" license agreements or limited
disclaimers, you must cease and desist using this product immediately.
This product is © Copyright 2002 by Progressive Resources LLC, all rights
reserved. International copyright laws, international treaties and all other
applicable national or international laws protect this product. This software
product and documentation may not, in whole or in part, be copied, photocopied,
translated, or reduced to any electronic medium or machine readable form, without
prior consent in writing, from Progressive Resources LLC and according to all
applicable laws. The sole owner of this product is Progressive Resources LLC.

Operating License
You have the non-exclusive right to use any enclosed product but have no right
to distribute it as a source code product without the express written permission
of Progressive Resources LLC. Use over a "local area network" (within the same
locale) is permitted provided that only a single person, on a single computer
uses the product at a time. Use over a "wide area network" (outside the same
locale) is strictly prohibited under any and all circumstances.

Liability Disclaimer
This product and/or license is provided as is, without any representation or
warranty of any kind, either express or implied, including without limitation
any representations or endorsements regarding the use of, the results of, or
performance of the product, Its appropriateness, accuracy, reliability, or
correctness. The user and/or licensee assume the entire risk as to the use of
this product. Progressive Resources LLC does not assume liability for the use
of this product beyond the original purchase price of the software. In no event
will Progressive Resources LLC be liable for additional direct or indirect
damages including any lost profits, lost savings, or other incidental or
consequential damages arising from any defects, or the use or inability to
use these products, even if Progressive Resources LLC have been advised of
the possibility of such damages.

Progressive Resources LLC


4105 Vincennes Road
Indianapolis, IN 46268 http://www.prllc.com

©2002 Progressive Resources LLC


27

Вам также может понравиться