UNIT – 6 INTRODUCTION TO RTOS Tasks -...
Transcript of UNIT – 6 INTRODUCTION TO RTOS Tasks -...
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 */ {
whil e (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 nable calculation
!! Do more intermi nable cal 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 sTask is busy
calculating
while vButtonTask
is blocked.
User presses button;
RTOS switches
microprocessor to
vButtonTas k;
vButtonTask
does everything it
needs to do to
respond to the
vButtonTask finishes its
work and blocks
again; RTOS
switches
vlevelsTask button. nucroprocessor
is ready.
I
back to
vButtonTask
\
/ vLevelsTask.
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 a button
i - !! ID of button pressed;
printf ("\nTIME: %08ld LEVEL: %08ld". tankdata[i].lTimeUpdated,
tankdata[i].lTankLevel);
}
}
I* "Levels Task" */
void vCalculateTankLevel s (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 1 oca1 ;
}
� 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",
tankdata[i].lTimeUpdated,
tankdata[i].lTankLevel): ReleaseSemaphore ();
}
}
/* "Levels Task" */
voi d vCalculateTanklevels (void)
{
I* Low priority */
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 i n report. */
static int iLinesTota l ;
/*Count of l i nes printed so far. */
static int ilinesPrinted;
/* Semaphore to wait for report to fini sh. */
static OS_EVENT *semP ri nter:
void vPrinterTask<voi d) (
BYTE by Error; Int
wMsg;
I* Place for an error return. *I
I* Initi alize 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-- ili n esTota l )
I* The report is done. Release the semaphore. *I
OSSemPost (semPrinter):
else
I* Print the next line. *I
vHardwarePrinterOutputline (a chPrint[iLinesPri nted++]);
}
� 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
message in its qu
eue and unblocks;
RTOS switches to
Task B.
Task C takes a
semaphore that
it
s hares with Task A .
Task A
Task A tries to
take the sema
phore that
Task C already has taken.
Task B goes on
running and running
and running, never
givingTask C a chance
to release the
semap hore.Task A is bloc
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 (voi d)
(
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_MSG + 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 of events. *I
AMXIO amxidTrigger:
I* Constants for use in the group. *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 wi ll 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-
. _1_ uBufS1zeT
uBufCount
Figure 7.11 Using Memory Management Functions
static char a_lines[MAX_LINES][MAX_LINE_LENGTH]; 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 */
#define LINE POOL 1
#define MAX_LINE_LENGTH 40
#define MAX_LINES 80
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