Академический Документы
Профессиональный Документы
Культура Документы
PR_RTX
A Real-Time Executive
For
Table of Contents
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
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:
#include "PR_RTX_Control.c"
// 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:
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.
}
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:
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 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.
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.
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.
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
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:
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
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.
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.
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
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).
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.
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.
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:
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);
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:
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
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
#include <mega163.h>
#include <stdio.h>
#include "PR_RTX_Control.c"
// End of PR_RTX Control Block ********************************
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
// 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
// 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;
// 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.
}
}
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.
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.
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
#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.
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.
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:
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.
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.
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.
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.
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.
The Mega 128 CVAVR example included with PR_RTX includes sample use of the eXaminer.
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.