You are on page 1of 47

UNIT – 6

INTRODUCTION TO RTOS

Tasks
Issue – Scheduler/Task signal exchange for block-unblock of tasks via function
calls
Issue – All tasks are blocked and scheduler idles forever (not desirable!)
Issue – Two or more tasks with same priority levels in Ready state
(time-slice, FIFO)
Example: scheduler switches from processor-hog vLevelsTask to
vButtonTask (on user interruption by pressing a push-button),
controlled by the main() which initializes the RTOS, sets priority
levels, and starts the RTOS
(See Fig 6.2, Fig 6.3, Fig 6.4)

Figure 6.2 Uses for Tasks

I* "Button Task" */
void vButtonTask (void) /* High priority */
{
while(TRUE)
{
!! Block until user pushes a button
!! Quick: respond to the user
}
}

I* "Levels Task" */
void vLevelsTask (void) /* Low priori y */
{
while(TRUE)
{
!! Read levels of floats in tank
!!Calculate average float level (continued )
!!Do some intermi nablecalculation
!!Do more intermi nablecal culation
!! Do yet more interminable calculation
!! Figure out which tank to do next
)
}
Figure 6.3 Microprocessor Re ponds to a Button under an RTOS

v lev e1 sTaskis busy User presses button; vButtonTask vButtonTask finishes its
calculating RTOS switches does everything it work and blocks
microprocessor to needs to do to again; RTOS
while vButtonTask
vButtonTask; respond to the switches
is blocked.
vlevelsTask button. nucroprocessor
is ready. back to
/ vLevelsTask.
\ I
vButtonTask

Time------------------------

Figure 6.4 RTOS Initialization Code

void main (void)


(
I* Initialize(but do not start)the RTOS */ InitRTOS();

/*Tell the RTOS about our tasks*/ StartTask(vRespondToButton.


HIGH_PRIORITY); StartTask(vCalculateTanklevels, LOW_PRIORITY);

I* Start the RTOS. (This function never returns.)*/ StartRTOS();


)
Tasks and Data

Each tasks has its won context - not shared, private registers, stack, etc.
In addition, several tasks share common data (via global data declaration; use of ‘extern’ in one
task to point to another ta sk that declares the shared data
Shared data caused the ‘shared-data problem’ without solutions discussed in Chp4 or use of
‘Reentrancy’ characterization of functions
(See Fig 6.5, Fig 6.6, Fig 6.7, and Fig 6.8)
Figure 6.6 Sharing Data among RTOS Tasks
struct
{
long lTankLevel:
long lTimeUpdated;
}tankdata[MAX_TANKS];

/* "Button Task" */
void vRespondToButton (void) /* High priority */
{
inti;
while(TRUE)
{
!! Block until user pushes abutton
i - !! ID of button pressed;
printf("\nTIME: %08ld LEVEL: %08ld". tankdata[i].lTimeUpdated,
tankdata[i].lTankLevel);
}
}

I* "Levels Task" */
void vCalculateTankLevels(void)
{
I* Low priority */
int i - 0; while
(TRUE) (
!1 Read levels of floats in tank i
!! Do more interminable calculation
!! Do yet more interminable calculation

I* Store the resu1t *I


tankdata[i].lTimeUpdated -!! Current time
I* Between these two instructions is a bad place for a task swi tch
*/
tankdata[i].lTankLevel -!! Result of calculation

!! Figure out which tank to do next


i - !! something new
)
}
Figure 6.7 Tasks Can Share Code
void Taskl(void)
{
vCountErrors (9):
}
void Task2(void)
{
vCountErrors (11):
}
static 1nt cErrors:
void vCountErrors Cint cNewErrors)
{
cErrors +- cNewErrors:
}
6.2 Tasks
Reentrancy – A function that works correctly regardless of the number of tasks that call it between
interrupts
Characteristics of reentrant functions –
Only access shared variable in an atomic-way, or when variable is on callee’s stack
A reentrant function calls only reentrant functions
A reentrant function uses system hardware (shared resource) atomically

Inspecting code to determine Reentrancy:


See Fig 6.9 – Where are data stored in C? Shared, non-shared, or stacked?

• See Fig 6.10 - Is it reentrant? What about variable fError? Is print!


reentrant?
• If shared variables are not protected, could they be accessed using single
assembly instructions (guaranteeing non-atomicity)?

Figure 6.9 Variable Storage

static int static_int:


int public_int;
int initialized= 4;
char *string -"Where does this string go?":
void *vPointer:

void function Cint parm, int *parm_ptr)


{
static int static_local;
i nt 1oca1;
}
6.3 Semaphores and Shared Data – A new tool for atomicity
Semaphore – a variable/lock/flag used to control access to shared
resource (to avoid shared-data problems in RTOS)
Protection at the start is via primitive function, called take, indexed by
the semaphore
Protection at the end is via a primitive function, called release, also
indexed similarly
Simple semaphores – Binary semaphores are often adequate for
shared data problems in RTOS
(See Fig 6.12 and Fig 6.13)

Figure 6.12 Semaphores Protect Data

struct
(
long lTankLevel;
long lTimeUpdated:
}tankdata[MAX_TANKS]:

/* "Button Task" */
void vRespondToButton(void)/* High priority */
(

inti:
while(TRUE)
(
Embedded System Design 06EC82
II Block until user pushes a button
1 - II Get 10 of button pressed
TakeSemaphore():
printf("\nTIME:%08ld LEVEL:%081d", I* Low priority */
tankdata[i].lTimeUpdated,
tankdata[i].lTankLevel):
ReleaseSemaphore();
}
}
/* "Levels Task" */
void vCalculateTanklevels (void)
{
int i - 0:
while(TRUE)
{

TakeSemaphore();
!!Set tankdata[i].1TimeUpdated
!! Set tankdata[i].1TankLeve7
ReleaseSemaphore();

)
}
6.3 Semaphores and Shared Data – 1

RTOS Semaphores & Initializing Semaphores


Using binary semaphores to solve the ‘tank monitoring’ problem
(See Fig 6.12 and Fig 6.13)
The nuclear reactor system: The issue of initializing the semaphore
variable in a dedicated task (not in a ‘competing’ task) before
initializing the OS – timing of tasks and priority overrides, which can
undermine the effect of the semaphores
Solution: Call OSSemInit() before OSInit()
(See Fig 6.14)
Figure 6.14 ernaphores Protect Data in the Nudear R.eactor
#define TASK_PRIORITY_READ 11
#define TASK_PRIORITY_CONTROL 12
#define STK_SIZE 1024
static unsigned int ReadStk[STK_SIZE];
static unsigned int ControlStk(STK_SIZE];

static int iTemperatures[2;]OS_EVENT


*p_semTemp;

void ma i n <void)
{
I* Initialize(but do not start> the RTOS*I
OSinit():

I* Tell the RTOS about our tasks*I


OSTaskCreate(vReadTemperatureTask. NULLP,
(void *)&ReadStk[STK_SIZE]. TASK_PRIORITY _READ):
OSTaskCreate(vControlTask. NULLP,
(void *)&ControlStk[STK_SIZE].TASK_PRIORITY_CONTROL);

I* Start the RTOS. (This function never returns.) *I


OSStart():
}
6.3 Semaphores and Shared Data – 2

Reentrancy, Semaphores, Multiple Semaphores, Device Signaling,


Fig 6.15 – a reentrant function, protecting a shared data, cErrors, in critical section
Each shared data (resource/device) requires a separate semaphore for individual
protection, allowing multiple tasks and data/resources/devices to be shared
exclusively, while allowing efficient implementation and response time
Fig 6.16 – example of a printer device signaled by a report-buffering task, via semaphore
signaling, on each print of lines constituting the formatted and buffered report

Figure 6.15 . Semaphores Make a Function Reentrant

vo1d Task1 <void)


{
vCountErrors(9);

vo1d Task2 <void)


{

vCountErrors(11);
)

static int cErrors;


static NU_SEMAPHORE semErrors;

void vCountErrors (int cNewErrors)


{
NU_Obtain_Semaphore(&semErrors, NU_SUSPEND);
cErrors +- cNewErrors; NU_Release_Semaphore
(&semErrors);
)

Figure 6.16 Usi n g a Sen'laphore as a Signaling Device

/* Place to construct report.*/


static char a_chPrint[10][21]:

/*Count of lines in report.*/


static int iLinesTotal;

/*Count of lines printed so far. */


static int ilinesPrinted;

/* Semaphore to wait for report to finish. */


static OS_EVENT *semP rinter:
void vPrinterTask<void)
(
BYTE by Error; Int I* Place for an error return.*I
wMsg;
I* Initialize the semaphore as already taken.*I
semPrinter- OSSemlnit(O);

while <TRUE)
{
I* Wait for a message telling what report to format.*I
wMsg-(int)OSQPend (QPrinterTask, WAIT_FOREVER, &byError);

!1 Format the report into a_chPrint


iLinesTotal - 1!count of lines in the report

I* Print the first line of the report*I


iLinesPrinted-0;
vHardwarePrinterOutputline (a_chPrint[ilinesPrinted++]);

I* Wait for print job to finish.*I


OSSemPend (semPrinter, WAIT_FOREVER, &byError);
}
}

void vPrinterinterrupt (void)


{
if (ilinesPrinted-- ilin esTota l)
I* The report is done. Release the semaphore. *I
OSSemPost (semPrinter):

else
I* Print the next line. *I
vHardwarePrinterOutputline(a chPrint[iLinesPrinted++]);
}

6.3 Semaphores and Shared Data – 3


Semaphore Problems – ‘Messing up’ with semaphores
The initial values of semaphores – when not set properly or at
the wrong place
The ‘symmetry’ of takes and releases – must match or correspond – each
‘take’ must have a corresponding ‘release’ somewhere in the ES application
‘Taking’ the wrong semaphore unintentionally (issue with multiple semaphores)
Holding a semaphore for too long can cause ‘waiting’ tasks’ deadline to be missed
Priorities could be ‘inverted’ and usually solved by ‘priority
inheritance/promotion’
(See Fig 6.17)
Causing the deadly embrace problem (cycles)
(See Fig 6.18)
Figure 6.17 Priority inversio n

Task A gets a
message in its
queue and
unblocks; RTOS
switches to Task A.

Task B gets a Task A tries to


message in its qu take the sema
eue and unblocks; phore that
RTOS switches to Task C already has taken.
Task B.
Task B goes on
Task C takes a running and running
semaphore that and running, never
it givingTask C a chance
s hares with Task A . to release the
semap hore.Task A is bloc
Task A
TaskB

Task C

Ti me ---------------------------------------------------- .

6.3 Semaphores and Shared Data – 4


Variants:
Binary semaphores – single resource, one-at-a time,
alternating in use (also for resources)
Counting semaphores – multiple instances of resources,
increase/decrease of integer semaphore variable
Mutex – protects data shared while dealing with priority
inversion problem
Summary – Protecting shared data in RTOS
Disabling/Enabling interrupts (for task code and interrupt
routines), faster
ing/Releasing semaphores (can’t use them in interrupt routines), slower,
affecting response times of those tasks that
need the semaphore
Disabling task switches (no effect on interrupt routines), holds
all other tasks’ response

PART – B MORE OS SERVICES

7.1 Message Queues, Mailboxes and Pipes

Basic techniques for inter-task communication and data sharing are: interrupt
enable/disable and using semaphores. E.g., the tank monitoring tasks and
serial port and printer handling tasks
Others supported by RTOS: Message Queues, Mailboxes and Pipes
Example of Message Queue: (See Fig 7.1)
Task1 and Task2 (guaranteed to be reentrant) compute
separate functions
Use services of vLogError and ErrorsTask (vLogError enqueues
errors for ErrorsTask to process)
vLogError is supported by AddToQueue function, which keeps
a queue of integers for the RTOS to interpret or map to error- type.
Using the ReadFromQueue function, the RTOS then activates
ErrorTask to handle the error if the queue is not empty – freeing
Task1 and Task2 to continue their tasks.
Functions AddToQueue and ReadFromQueue are non-
reentrant, and the RTOS switches between Task1 and Task2 in the
middle of their tasks execution are guaranteed to be ok

Figure 7.1 Simple Use of a Queue


I* RTOS queue function prototypes */
void AddToQueue(int iData);
void ReadFromQueue(int *p_iData);

void Taskl (void)


(
if (!!problem arises>
vLogError (ERROR_TYPE_X);

11 Other things that need to be done soon.


}

void Task2(void)
(

if (JJproblem arises>
vLogError(ERROR_TYPE_Y);

11 Other things that need to be done soon.

}
7.1 Message Queues, Mailboxes, and Pipes – 1
Difficulties in using Queues:
Queue initialization (like semaphore initialization) must be dedicated
to a separate task to a) guarantee correct start-up values and b)
avoid uncertainty about task priorities and order of execution which
might affect the queue’s content
Queues must be tagged (identify which queue is referenced)
Need code to manage the queue (when full and empty) if RTOS
doesn’t – block reading/writing task on empty/full, plus returning an
error code
RTOS may limit the amount of info to write/read to queue in any
single call

Figure 7.2 More Realistic Use of a Queue

/* RTOS queue function prototypes */


OS_EVENT *OSQCreate(void **ppStart, BYTE bySize);
unsigned char OSQPost(OS_EVENT *pOse, void *pvMsg);
void *OSQPend (OS_EVENT *pOse. WORD wTimeout, BYTE *pByErr);
#define WAIT_FOREVER 0
I* Our message queue */
static OS_EVENT *pOseQueue;

I* The data space for our queue. The RTOS will manage this.*/
#define SIZEOF_QUEUE 25
void *apvQueue[SIZEOF_OUEUE];

void main (void)


{
I* The queue gets initialized before the tasks are started*/
pOseQueue - OSQCreate(apvQueue, SIZEOF_QUEUE);
! ! Start Taskl
! ! Start Task2
}

void Taskl (void)


{
if (/!problem arises)
vLogError (ERROR_TYPE_X);

!! Other things that need to be done soon.


)
void Task2(void)
{

if {!!problem arises)
vlogError (ERROR_TYPE_Y);

11 Other things that need to be done soon.


}

Message Queues, Mailboxes, and Pipes


Using Pointers and Queues
Code in Fig 7.2 limits the amount of data to write to or read from the
queue
For tasks to communicate any amount of data, create a buffer and
write the pointer to the buffer to the queue. (The receiving task
reads/retrieves data from the buffer via the pointer, and frees the
buffer space.)
(See Fig 7.3)
Figure 7.3 Passing Pointers on Queues
I* Queue function prototypes *I
OS_EVENT*OSQCreate(void **ppStart, BYTE bySize);
unsigned char OSQPost <OS_EVENT*pOse, void *pvMsg);
void *OSQPend (OS_EVENT *pOse, WORD wT1meout.BYTE *pByErr):
#define WAIT_FOREVER 0

static OS_EVENT *pOseQueueTemp:

void vReadTemperaturesTask < void)


{
int *pTemperatures:

while(TRUE)
{
!!Wait until it's time to read the next temperature

I* Get a new buffer for the new set of temperatures. *I


pTemperatures- (int *) malloc(2*sizeof *pTemperatures);

pTemperatures[OJ- II read in value from hardware;


pTemperatures[l]- II read in value from hardware:

I* Add a pointer to the new temperatures to the queue *I


OSQPost(pOseQueueTemp. <void *)pTemperatures>:

void vMa1nTask (void)


(
int *pTemperatures; BYTE
byErr;

wh1le(TRUE)
{
pTemperatures
(int *)OSQPend (pOseQueueTemp, WAIT_FOREVER. &byErr);
if(pTemperatures[OJ 1- pTemperatures[l])
11 Set off howling alarm:

free(pTemperatures);
02 }
}
7.1 Message Queues, Mailboxes, and Pipes
Using Mailboxes:
Purpose is similar to queues (both supporting asynchronous task
communication)
Typical RTOS function for managing mailboxes – create, write, read,
check-mail, destroy
Variations in RTOS implementations of mailboxes
Either a single-message mailbox or multi-message mailbox (set #
entries at start)
# of messages per mailbox could be unlimited, but total # in the
system could be (with possibility of shuffling/distributing messages
among mailboxes)
Mailboxes could be prioritized
Examples: (from the RTOS – MultiTask! )
int sndmsg (unsigned int uMbid, void *p_vMsg, unsigned int uPriority);
void *rcvmsg(unsigned int uMbid, unsigned int uTimeout);
void *chkmsg(unsigned int uMbid);

Using Pipes:
Pipes are implemented as (special) files, using normal file-descriptors
RTOS can create, read from, write to, destroy pipes (typically: each
pipe has 2 ends)
Details of implementation depends on RTOS
Pipes can have varying length messages (unlike fixed length for
queues / mailboxes)
Pipes could be byte-oriented – and read/write by tasks depends on #
bytes specified
In standard C, read/write of pipes use fread/fwrite functions,
respectively
Programming queues, mailboxes, and pipes – caution!
Coding tasks to read from or write to intended ‘structure’
(RTOS can’t help on mismatch)
Interpretation and processing of message types (see code
segments on p. 182)
• Overflow of 'structure' size - could cripple the software, so need
to set size as large as possible
• Passing pointers in 'structures' provides 'unwanted'
opportunity to create shared data problem
• (See Fig 7.4)

Figure 7.4 Be Careful When You Pass Pointers on Queues

/* Queue function prototypes */


OS_EVENT *OSQCreate(void **ppStart. BYTE bySize):
unsigned char OSQPost COS_EVENT *pOse, void *pvMsg):
void *OSQPend COS_EVENT *pOse. WORD wTimeout.
BYTE *pByErr):
#define WAIT_FOREVER 0
static OS_EVENT *pOseQueueTemp:

void vReadTemperaturesTask (void)


{
int iTemperatures[2]:

while(TRUE)
{
I! Wait until it's time to read the next temperature

iTemperatures[O]- !! read fn value from hardware:


iTemperatures[l]- !! read fn value from hardware:

I* Add to the queue a pointer to the temperatures we just read */


OSQPost (pOseQueueTemp,(void*)iTemperatures):
}
}

Timer Functions
Issues:
Embedded systems track time passage, hence, need to keep time
(e.g., to save battery life, power need to be shut off automatically
after, say, X seconds; a message send-task expects an ACK after Y
seconds, it is delayed Y seconds and may retransmit; task is allowed a slice of time
after which it is blocked)
RTOS provides these timing services or functions
(See Fig 7.5 – VsWorks RTOS support for taskDelay(nticks) function in
telephone call code)

7.2 Timer Functions


Issues:
How long is delay – measured in ticks (a tick is like a single
‘heartbeat’ timer interrupt time)
(See Fig 7.6)
RTOS knowledge of time/timer and specifics of nticks or time- interval – relies on
microprocessor’s hardware timer and its
interrupt cycles (RTOS writers must know this!) OR RTOS
writers write ‘watchdog’ timers – based on non-standard timer hardware –
and corresponding software interrupts – called each time the software
timer expires
RTOS vendors provide board support packages (BSP) – of drivers for timers
and other hardware
Length of a tick – depends on the hardware timer’s design –
trade-off
Accurate timing – short tick intervals OR use dedicated timer
for purpose

7.2 Timer Functions


Other Timing Services (all based on system tick)
Waiting time or delay on message, on a semaphore (but not too tight
for high priority tasks to miss access to shared data)
Place ‘call to’ or ‘activation of’ time-critical, high priority tasks inside
timer interrupts or specialized-time-critical tasks inside the RTOS (Note: OS
task have higher priority over other embedded software tasks).
Calling a function of choice after some S nticks
Example: (See Fig 7.7) – The Timer Callback Function
Note how wdStart function is passed a function –
vSetFrequency or vTurnOnTxorRx, associated nticks, and the
parameter to the function. Also note how the vRadioControlTask
communicates with vTurnOnTxorRx and vSetFrequency using the queue
‘queueRadio’ and msgQreceive/msgQSend)

Figure 7-7 Using Tin"ler Callback Functions


/* Message queue for radio task. */
extern MSG_O_ID queueRadio;
I* Timer for turning the radio on. */
static WDDG_IO wdRadio;

static int iFrequency; /* Frequency to use. */


void vSetFrequency (inti);
void vTurnOnTxorRx (inti);
void vRadioControlTask (void)
(
#define MAX_MSG 20
char a_chMsg[MAX_M SG + 1]: /*Message sent to this task*/
enurn
[
RADIO_OFF.

RADIO_STARTING.

RADIO_TX_ON,
RAOIO_RX_ON,
} eRadioState: I* State of the radio*I
eRadioState - RADIO_OFF;

I* Create the radio timer*I


wdRadio- wdCreate();

while(TRUE)
{
I* Find out what to do next*I
msgQReceive(queueRadio.a_chMsg, MAX_MSG. WAIT_FOREVER):

I* The first character of the message tells this task what the message is.*I
switch(a _chMsg[OJ)
{
case 'T':
case 'R':
I* Someone wants to turn on the transmitter *I
if(eRadioState -- RADIO_OFF)
{
!! Turn on power to the radio hardware.

eRadioState - RADIO_STARTING:
/* Get the frequency from the message */
iFrequency-*(int *) a_chMsg[l];

II Store what needs doing when the radio is on.


/*Make the next step 12 milliseconds from now.*/
wdStart(wd Radio, 12, vSetFrequency, Cint)a _chMsg[O]);

else
J J Handle error. Can't turn radio on if not off
break;

case 'K':
/* The radio is ready. */
eRadioState- RADIO_TX_ON;
J! Do whatever we want to do with the radio
break;

case 'L':
I* The radio is ready. */
eRadioState - RAOIO_RX_ON;
!I Do whatever we want to do with the radio
break;

case 'X':
/* Someone wants to turn off the radio. */
if (eRadioState -- RADIO_TX_ON I I
eRadioState -- RADIO_RX_ON)
(
!1 Turn oFf power to the radio hardware.
eRad1oState - RADIO_OFF;
}
else
11 Handle error. Can't turn radio off if not on
break:
default:
11 Deal with the error of a bad message
break:
}
)
7.3 Events
In standard OS, an event is typically an indication which is related to time
In RTOS, an event is a boolean flag, which is set and reset by tasks/routines
for other tasks to wait on
RTOS is supposed to manage several events for the ‘waiting’ tasks. Blocked
or waiting tasks are unblocked after the event occurrence, and the event is
reset
E.g., pulling the trigger of a cordless bar-code scanner sets the flag for a
waiting task, which turns of the laser beam for scanning, to start running
(See Fig 7.8 and Fig 7.9)
Figure 7.8 Using Events

I* Handle for the trigger group ofevents. *I


AMXIO amxidT rigger:

I* Constants foruse in thegroup. *I

//define TRIGGER_MASK OxOOOl


#define TRIGGER_SET OxOOOl
//define TRIGGER_ RESET OxOOOO
//define KEY_MASK Ox0002
#define KEY _SET Ox0002
#define KEY RESET OxOOOO

Figure 7.8 (contit1ued)

void main (void)


{

I* Create an event group with


the trigger and keyboard events reset *I
ajevcre <&amxidTrigger, 0, "EVTR"):

void interrupt vTriggeriSR (void)

I* The user pulled the trigger. Set the event. *I


ajevsig (amxidTrigger,TRIGGER_MASK. TRIGGER_SET);

void interrupt vKeyiSR (void)


{
I* The user pressed a key. Set the event. *I
ajevsig (amxidTrigger. KEY_MASK. KEY_SET);

!! Figure out which key the user pressed and store that value

void vScanTask (void)


{
while (TRUE)
{
I* Wait for the user to pull the trigger. */
ajevwat (amxidTrigger. TRIGGER_MASK. TRIGGER_SET.
WAIT_FOR_ANY. WAIT_FOREVER):
/* Reset the trigger event. */
ajevsig <amxidTrigger. TRIGGER_MASK. TRIGGER_RESET);

!! Turn on the scanner hardware and look for a scan.

!! When the scan has been found. turn off the scanner.
}
}
void vRadioTask (void)
(

while (TRUE)
(
I* Wait for the user to pull the trigger or press a key. *I
ajevwat (amxidTrigger. TRIGGER_MASK I KEY_MASK.
TRIGGER_SET I KEY_SET, WAIT_FOR_ANY ,
WAIT_FOREVER):

I* Reset the key event. (The trigger event will be reset by the ScanTask.)*I
ajevsig (amxidTrigger, KEY_MASK. KEY_RESET);

!! Turn on the radio.

!! When data has been sent. turn off the radio.


}
}

7.3 Events – 1
Features of events (and comparison with semaphores, queues, mbox,
pipes):
More than one task can wait on the same event (tasks are activated
by priority)
Events can be grouped, and tasks may wait on a subset of events in a
group
Resetting events is either done by the RTOS automatically or your
embedded software
Tasks can wait on only one semaphore, queue, mbox or pipe, but on
many events simultaneously.
Semaphores are faster, but unlike queues, mboxes, and pipes, they
carry 1-bit info
Queues, mboxes, and pipes are error prone and message
posting/retrieval is compute-intensive
7.4 Memory Management
In general RTOS offer C lang equivalent of malloc and free for MM, which
are slow and unpredictable
Real time system engineers prefer the faster and more predictable
alloc/free functions for fixed size buffers. E.g., MultiTask! RTOS allocates
pools of fixed size buffers, using
getbuf() [with timed task blocking on no buffers] and reqbuf() [with
no blocking and return of NULL pointer on no buffers]
relbuf() to free buffers in a given pool (buffer pointer must be valid)
Note that most embedded sw is integrated with the RTOS (same
address space) and the ES starts the microprocessor; hence your ES
must tell the memory-pool
(See Fig 7.10 and Fig 7.11 – high priority FormatTask and low priority
OutputTask)
Figure 7.10 The i ni t _mem_pool Function in MultiTask!

p_ vMemory-

uBufCount
. _1_
uBuf S1zeT
Figure 7.11 Using Memory Management Functions

#define LINE POOL 1


static char a_lines[MAX_LINES][MAX_LINE_LENGTH];
#define MAX_LINE_LENGTH 40
#define MAX_LINES 80 void main(void)
{

init_mem_pool (LINE_POOL. a_lines.

MAX_LINES.MAX_LINE_LENGTH.TASK_POOL);
}
void vPrintFormatTask (void)
{
char *p_chline; I* Pointer to current line */

I* Format lines and send them to the vPrintOutputTask */


p_chline- getbuf(LINE_POOL, WAIT_FOREVER):
sprintf (p_chline, "INVENTORY REPORT");
sndmsg (PRINT_MBOX, p_chline.PRIORITY_NORMAL); p_chline-getbuf(LINE_POOL.
WAIT_FOREVER): sprintf(p_chline, "Date:%02/%02/%02",
iMonth, iDay, iYear% 100);
sndmsg (PRINT_MBOX. p_chline. PRIORITY_NORMAL):
p_chline-getbuf(LINE_POOL. WAIT_FOREVER);
sprintf(p_chline. "Time:%02:%02". iHour, iMinute);
sndmsg (PRINT_MBOX, p_chline. PRIORITY_NORMAL);

void vPrintOutputTask (void)


(
char *p_chline;
while(TRUE)
{
/*Wait for a line to come in. */
p_chline-rcvmsg (PRINT_MBOX, WAIT_FOREVER):

11 Do what is needed to send the line to the printer

/* Free the buffer back to the pool */


relbuf (LINE_POOL. p_chline);
}
}

7.5 Interrupt Routines in an RTOS Environment


Rules that IR’s must comply with (but not a task code)
Rule 1: an IR can’t call RTOS function that will cause it to blo ck, e.g., wait on
semaphores, reading empty queues or mailboxes, wait on events to avoid high latency
or large response time and potential deadlock
(See Fig 7.12 wh ic h do es n ’t wo r k ; and Fig 7.13 which works using queues)
Figure 7.13 Legal Uses ofRTOS Functions in Interrupt Routines

I* Queue for temperatures.*I


int iOueueTemp:

void interrupt vReadTemperatures(void)


(
int aTemperatures[2);
int iError:
I* 16-bit temperatures. */

I* Get a new set of temperatures.*/ aTemperatures[O)- 1! read in value from hardware: aTemperatures[l]-
11 read in value from hardware:

I* Add the temperatures to a queue. */


sc_qpost(iQueueTemp,
(char*)((aTemperatures[O] << 16) I aTemperatures(1]),
&iError);
}
void vMainTask (void)
(
long int lTemps; /* 32 bits:the same size as a pointer. */
int aTemperatures(2];
1nt iError:

while(TRUE)
(
lTemps -(long) sc_qpend (iQueueTemp, WAIT_FOREVER. sizeof(int). &1Error);
aTemperatures[O]- Cint)ClTemps >> 16); aTemperatures[l]-(int)(lTemps &OxOOOOffff): if
(aTemperatures[O] !-aTemperatures[l])
11 Set off howling alarm:
)
)

7.5 Interrupt Routines in an RTOS Environment – 1


Rule 2: an IR can’t call RTOS functions that will cause the RTOS to switch
other tasks (except other IR’s); breaking this rule will cause the RTOS to
switch from the IR itself to handle the task, leaving the IR code incomplete or delay
lower priority interrupts
(See Fig 7.14 should-work case; and Fig 7.15 – what really happens case)
7.5 Interrupt Routines in an RTOS Environment – 2
One solution to Rule 2 –
Let the RTOS intercept all the interrupts, aided by an RTOS function whi ch tells the
RTOS where the IRs are and the corresponding interrupt hardware
The RTOS then ‘activates’ the calling IR or the highest priority IR
Control returns to the RTOS, and the RTOS scheduler decides which task
gets the microprocessor (allowing the IR to run to completion)
(See Fig 7.16)
7.5 Interrupt Routines in an RTOS Environment

Second solution to Rule 2:


Let the IR call a function in the RTOS to inform the RTOS of an interrupt
After the IR is done, control goes back to the RTOS, where another function
calls the scheduler to schedule the next task
(See Fig 7.17)
Third solution to Rule 2:
Let RTOS maintain a separate queue of specialized, interrupt-supporting
functions which are called by the IR (on the appropriate interrupt). When
these functions complete, control goes back to that IR (similar to Fig 7.17
with queues)
Interrupt Routines in an RTOS Environment
Nested Interrupts
If a running IR is interrupted by another (higher) priority interrupt (kind of
interrupt stacking), the RTOS should unstack the IR’s to allow all IR’s to
complete before letting the scheduler switch to any task code
(See Fig 7.18)
RECOMMENDED QUESTIONS

Introduction to RTOS and More operating systems services

1. What are the three states in a task. explain it with neat block diagram
2. Describe the use of take semaphore( ) and release semaphore( ) with an
example .
3. Explain any 6 problems with semaphores.
4. Describe the use of message queues, mailbox and pipes.
5. Explain memory management in multitasking.
6. How does interrupt routines work in RTOS environment.
7. What are nested interrupts ? and how do they work?

SOLUTION FOR UNIT – 6

Q1. How does a microprocessor respond to a button under an RTOS.

Issue – Scheduler/Task signal exchange for block-unblock of tasks via function


calls
Issue – All tasks are blocked and scheduler idles forever (not
desirable!)
Issue – Two or more tasks with same priority levels in Ready state
(time-slice, FIFO)
Example: scheduler switches from processor-hog vLevelsTask to
vButtonTask (on user interruption by pressing a push-button),
controlled by the main() which initializes the RTOS, sets priority
levels, and starts the RTOS
Q2. With a diagram explain sharing data among RTOS tasks
Each tasks has its won context - not shared, private registers, stack,
etc.
In addition, several tasks share common data (via global data
declaration; use of ‘extern’ in one task to point to another task that declares the
shared data
Shared data caused the ‘shared-data problem’ without solutions
discussed in Chp4 or use of ‘Reentrancy’ characterization of functions
Q3. What is semaphore? How does it help in shared data access along with code.

Semaphore – a variable/lock/flag used to control access to shared resource (to


avoid shared-data problems in RTOS)
Protection at the start is via primitive function, called take, indexed
by the semaphore
Protection at the end is via a primitive function, called release, also
indexed similarly
Simple semaphores – Binary semaphores are often adequate for
shared data problems in RTOS
Q4. Explain the execution flowgraphs in semaphores.
Q5. What are the basic problem in semaphores.
Semaphore Problems – ‘Messing up’ with semaphores
The initial values of semaphores – when not set properly or at
the wrong place
The ‘symmetry’ of takes and releases – must match or
correspond – each ‘take’ must have a corresponding ‘release’
somewhere in the ES application
‘Taking’ the wrong semaphore unintentionally (issue with
multiple semaphores)
Holding a semaphore for too long can cause ‘waiting’ tasks’
deadline to be missed
Priorities could be ‘inverted’ and usually solved by ‘priority
inheritance/promotion’
Causing the deadly embrace problem (cycles)

Q6. Describe the use of message queues.


Basic techniques for inter-task communication and data sharing are:
interrupt enable/disable and using semaphores. E.g., the tank
monitoring tasks and serial port and printer handling tasks
Others supported by RTOS: Message Queues, Mailboxes and Pipes
Example of Message Queue:
Task1 and Task2 (guaranteed to be reentrant) compute
separate functions
Use services of vLogError and ErrorsTask (vLogError enqueues
errors for ErrorsTask to process)
vLogError is supported by AddToQueue function, which keeps
a queue of integers for the RTOS to interpret or map to error-
type. Using the ReadFromQueue function, the RTOS then
activates ErrorTask to handle the error if the queue is not
empty – freeing Task1 and Task2 to continue their tasks.
Functions AddToQueue and ReadFromQueue are non-
reentrant, and the RTOS switches between Task1 and Task2 in
the middle of their tasks execution are guaranteed to be ok
Q7.Wrtie a code for delaying a task with RTOS delay function.
Q8. What are the features of events.
Features of events (and comparison with semaphores, queues, mbox,
pipes):
More than one task can wait on the same event (tasks are activated
by priority)
Events can be grouped, and tasks may wait on a subset of events in a
group
Resetting events is either done by the RTOS automatically or your
embedded software
Tasks can wait on only one semaphore, queue, mbox or pipe, but on
many events simultaneously.
Semaphores are faster, but unlike queues, mboxes, and pipes, they
carry 1-bit info
Queues, mboxes, and pipes are error prone and message
posting/retrieval is compute-intensive