Vous êtes sur la page 1sur 44

Lab 1

Timing
Introduction
To understand and optimize the performance of a real-time system, it can be useful
to time some of the VxWorks and application functions. VxWorks provides a
number of timing facilities to help with this task.
The VxWorks execution timer can time any subroutine or group of subroutines. To
time very fast subroutines, the timer can also repeatedly execute a group of
functions until the time of a single iteration is known with reasonable certainty.

Objectives
The following are the primary objectives of this experiment:

To demonstrate how to time a single subroutine using the


VxWorks timex() routine.

Description
The timex() routine times a single execution of a specified function with up to eight
integer arguments to be passed to the function. When execution us
complete, timex() routine displays the execution time and a margin of error in
miliseconds. If the execution was so fast relative to the clock rate that the time is
meaningless(error > 50%), a warning message will appear. In such cases,
use timexN() which will repeatedly execute the function until the time of a single
iteration is known with reasonable certainty.
1. Syntax
void timex(FUNCPTR function_name, int arg1, .., int arg8)
Note: the first argument in timex() routine is a pointer to the function to be timed.
2. Example This small example has two subroutines. The first subroutine "timing"
makes a call to timex() with the function name "printit" which is the subroutine to
be timed. The arguments are all NULL, so no parameters are being passed to
"printit".
The second subroutine, "printit", which is being timed iterates 200 times while
printing its task id(using taskIdSelf()) and the increment variable "i".
------------------------------------------------------------------------------------

Embedded system Lab

T.A. Bharat Kasyap

#include "vxWorks.h" /* Always include this as the first thing in every


program */
#include "timexLib.h"
#include "stdio.h"
#define ITERATIONS 200
int printit(void);

void timing() /* Function to perform the timing */


{
FUNCPTR function_ptr = printit; /* a pointer to the function "printit" */
timex(function_ptr,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); /* Timing the
"print" function */
}
int printit(void) /* Function being timed */
{
int i;
for(i=0; i < ITERATIONS; i++) /* Printing the task id number and the
increment variable "i" */
printf("Hello, I am task %d and is i = %d\n",taskIdSelf(),i);
return 0;
}
------------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the example by executing "timing" on the WindSh.
Note: Make sure you have redirected I/O, otherwise you won't see the results of the
printf commands.

Follow On Experiment
Experiment 1. Vary the number of ITERATIONS in the
loop(300,400,500,600,700) and note the changes in execution speed.
Experiment 2. Decrease the number of the iteration to 5, note what happens(you
should get a warning message). Write a program to get the timing in this case.

Additional Information
The timings measure the execution time of the routine body, without the usual
subroutine entry and exit code. Also, the time required to set up the arguments and
call the routines is not included in the reported times. This is because the timing

Embedded system Lab

T.A. Bharat Kasyap

routines automatically calibrate themselves by timing the invocation of a null


routine, and thereafter subtracting that constant overhead.
Refer to the VxWorks Programmer's Manual and Reference Manual(timexLib).

Embedded system Lab

T.A. Bharat Kasyap

Lab 2
Multitasking
Introduction
Modern real-time systems are based on the complementary concepts of
multitasking and intertask communications. A multitasking environment allows
real-time applications to be constructed as a set of independent tasks, each with a
separate thread of execution and its own set of system resources. The intertask
communication facilities allow these tasks to synchronize and coordinate their
activities.
The VxWorks multitasking kernel, wind, uses interrupt-driven, priority- based task
scheduling. It features fast context switch time and low interrupt latency.

Objectives
The following are the primary objectives of this experiment:

To teach the student how to initiate multiple processes using Vxworks


tasking routines.

Description
Multitasking creates the appearance of many threads of execution running
concurrently when, in fact, the kernel interleaves their execution on a basis of
ascheduling algorithm. Each apparently independent program is called a task. Each
task has its own context, which is the CPU environment and system resources that
the task sees each time it is scheduled to run by the kernel.
On a context switch, a task's context is saved in the Task Control Block(TCB). A
task's context includes:

a thread of execution, that is, the task's program counter


the CPU registers and floating-point registers if necessary
a stack of dynamic variables and return addresses of function calls
I/O assignments for standard input, output, error
a delay timer
a timeslice timer
kernel control structures
signal handlers
debugging and performance monitoring values

Embedded system Lab

T.A. Bharat Kasyap

1. Task Creation and Activation


The routine taskSpawn creates the new task context, which includes allocating and
setting up the task environment to call the main routine(an ordinary subroutine)
with the specified arguments. The new task begins at the entry to the specified
routine.
The arguments to taskSpawn() are the new task's name(an ASCII string), priority,
an "options" word(also hex value), stack size(int), main routine address(also main
routine name), and 10 integer arguments to be passed to the main routine as startup
parameters.
2. Syntax
id = taskSpawn(name,priority,options,stacksize,function, arg1,..,arg10);
3. Example
This example creates ten tasks which all print their task Id once:
--------------------------------------------------------------------------#define ITERATIONS 10
void print(void);
spawn_ten() /* Subroutine to perform the spawning */
{
int i, taskId;
for(i=0; i < ITERATIONS; i++) /* Creates ten tasks */
taskId =
taskSpawn("tprint",90,0x100,2000,print,0,0,0,0,0,0,0,0,0,0);
}
void print(void)
/* Subroutine to be spawned */
{
printf("Hello, I am task %d\n",taskIdSelf()); /* Print task Id */
}
---------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the example by executing "spawn_ten" on the WindSh.
Note: Make sure you have redirected I/O, otherwise you won't see the results of the
printf commands.

Follow On Experiment
As shown in the example, the task creation process allows the passing of ten
arguments to the function.
Experiment 1. As discussed above, up to a ten integer arguments can be passed to a
task during the call to taskSpawn(). Pass a unique argument to each of the ten tasks

Embedded system Lab

T.A. Bharat Kasyap

and have them print it. For example, pass the increment variable "i" in subroutine
spawn_ten().
Experiment 2. Assign each task a unique priority. Is there any difference in output?
Has the order in which the ten tasks print changed? If not, explain why.

Additional Information
Refer to the VxWorks Programmer's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 3
Semaphore
Introduction
Semaphores permit multitasking applications to coordinate their activities. The
most obvious way for tasks to communicate is via various shared data structures.
Because all tasks in VxWorks exist in a single linear address space, shared data
structures between tasks is trivial. Global variables, linear buffers, ring buffers,
link lists, and pointers can be referenced directly by code running in different
context.
However, while shared address space simplifies the exchange of data, interlocking
access to memory is crucial to avoid contention. Many methods exist for obtaining
exclusive access to resources, and one of them is semaphores.

Objectives
The following are the primary objectives of this experiment:

To demonstrate the use of VxWorks semaphores.

Description
VxWorks semaphores are highly optimized and provide the fastest intertask
communication mechanisms in VxWorks. Semaphores are the primary means for
addressing the requirements of both mutual exclusion and task synchronization.
The are three types of Wind semaphores, optimized to address different classes of
problems:
binary

The fastest, most general purpose semaphore. Optimized for


synchronization and can also be used for mutual exclusion.

mutual exclusion

A special binary semaphore optimized for problems inherent in mutual


exclusion: priority inheritance, deletion safety and recursion.

counting

Embedded system Lab

T.A. Bharat Kasyap

Like the binary semaphore, but keeps track of the number of times the
semaphore is given. Optimized for guarding multiple instances of a
resource.

1. Semaphore Control
Wind semaphores provide a single uniform interface for semaphore control. Only
the creation routines are specific to the semaphore type:

semBCreate(int options, SEM_B_STATE initialState ): Allocate and intialize


a binary semaphore.

semMCreate(int options): Allocate and intialize a mutual exclusion


semaphore.

semCCreate(int options, int initialCount): Allocate and intialize a counting


semaphore.

semDelete(SEM_ID semId): Terminate and free a semaphore.

semTake(SEM_ID semId, int timeout): Take a semaphore.

semGive(SEM_ID semId): Give a semaphore.

semFlush(SEM_ID semId): Ublock all tasks waiting for a semaphore.

Please refer to the VxWorks Reference Manual for valid arguments in the above
routines.
2. Example: Binary Semaphore
A binary semaphore can be viewed as a flag that is available or unavailable. When
a task takes a binary semaphore, using semTake(), the outcome depends on
whether the semaphore is available or unavailable at the time of the call. If the
semaphore is available, then the semaphore becomes unavailable and then the task
continues executing immediately. If the semaphore is unavailable, the task is put
on a queue of blocked tasks and enters a state of pending on the availability of the
semaphore.
When a task gives a binary semaphore, using semGive(), the outcome depends on
whether the semaphore is available or unavailable at the time of the call.If the
semaphore is already available, giving the semaphore has no effect at all. If the
semaphore is unavailable and no task is waiting to take it, then the semaphore
becomes available. If the semaphore is unavailable and one or more tasks are
pending on its availablity, then the first task in the queue of pending tasks is
unblocked, and the semaphore is left unavailable.
In the example below, two tasks(taskOne and taskTwo), are competing to update
the value of a global variable, called "global." The objective of the program is to

Embedded system Lab

T.A. Bharat Kasyap

toggle the value of the global variable(1s and 0s). taskOne changes the value of
"global" to 1 and taskTwo changes the value back to 0.
Without the use of the semaphore, the value of "global" would be random and the
value of "global" would be corrupted.
-----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"
#include "stdio.h"
/* function prototypes */
void taskOne(void);
void taskTwo(void);
/* globals */
#define ITER 10
SEM_ID semBinary;
int global = 0;
void binary(void)
{
int taskIdOne, taskIdTwo;
/* create semaphore with semaphore available and queue tasks on FIFO basis
*/
semBinary = semBCreate(SEM_Q_FIFO, SEM_FULL);
/* Note 1: lock the semaphore for scheduling purposes */
semTake(semBinary,WAIT_FOREVER);
/* spawn the two tasks */
taskIdOne =
taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,0,0,0);
taskIdTwo =
taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,0,0,0);
}

void taskOne(void)
{
int i;
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore
*/
printf("I am taskOne and global = %d......................\n",
++global);
semGive(semBinary); /* give up semaphore */
}
}
void taskTwo(void)
{
int i;
semGive(semBinary); /* Note 2: give up semaphore(a scheduling fix) */
for (i=0; i < ITER; i++)
{

Embedded system Lab

T.A. Bharat Kasyap

semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore


*/
printf("I am taskTwo and global = %d----------------------\n", -global);
semGive(semBinary); /* give up semaphore */
}
}
------------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the examples by executing the main routine("binary",etc.) of the example
on WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of the
printf commands.

Follow On Experiment
Experiment 1. In the binary semaphore example, remove the source code
associated with comments Notes 1 and Notes 2. Compile and run the program. Is
there a difference in the output? Is so, explain the reasons why.
Experiment 2. Write a program that toggles the value of "global" between 1 and 0
using counting semaphores. Just edit the example code to make appropriate
modifications.

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 4
Massage Queues
Introduction
In VxWorks, the primary intertask communication mechanism within a single CPU
is message queues. Message queues allow a variable number of messages, each of
variable length, to be queued(FIFO or priority based). Any task can send a message
to a message queue and any task can receive a message from a message queue.
Multiple tasks can send to and receive from the same message queue. Two way
communication between two tasks generally requires two message queues, one for
each direction.

Objectives
The following are the primary objectives of this experiment:

To demonstrate the use of VxWorks message queues.

Description
Wind message queues are created and deleted with the following routines:

msgQCreate(int maxMsgs, int maxMsgLength, int options): Allocate and


initialize a message queue.

msgQDelete(MSG_Q_ID msgQId): Terminate and free a message queue.

msgQSend(MSG_Q_ID msgQId, char *Buffer, UINT nBytes, int timeout, int


priority): Send a message to a message queue.

msgQReceive(MSG_Q_ID msgQId, char *Buffer, UINT nBytes, int


timeout) Send a message to a message queue.

This library provides messages that are queued in FIFO order, with a single
exception: there are two priority levels, and messages marked as high priority are
attached to the head of the queue.
A message queue is created with msgQCreate(). Its parameters specify the
maximum number of messages that can be queued in the message queue and the
maximum length in bytes of each message.

Embedded system Lab

T.A. Bharat Kasyap

A task sends a message to a message queue with msgQSend(). If no tasks are


waiting for messages on that queue, the message is added to the queue's buffer of
messages. If any tasks are waiting for a message from that message queue, the
message is immediately delivered to the first waiting task.
A task receives a message from a message queue with msgQReceive(). If messages
are already available in the queue's buffer, the first message is immediately
dequeued and returned to the caller. If no messages are available, then the calling
task blocks and is added to a queue of tasks waiting for messages. The queue of
waiting tasks can be ordered either by task priority or FIFO, as specified when the
queue is created.

Timeouts: Both msgQSend() and msgQReceive() take timeout parameters.


The timeout parameter specifies how many ticks(clock ticks per second) to
wait for space to be available when sending a message and to wait for a
message to be available when receiving a message.

Urgent Messages: The msgQSend() function can specify the priority of a


message either as normal MSG_PRI_NORMAL or
urgent MSG_PRI_URGENT. Normal priority messages are added to the
tail of the message queue, while urgent priority messages are added to the
head of the message queue.

1. Example
-----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "msgQLib.h"
/* function prototypes */
void taskOne(void);
void taskTwo(void);
/* defines */
#define MAX_MESSAGES 100
#define MAX_MESSAGE_LENGTH 50
/* globals */
MSG_Q_ID mesgQueueId;
void message(void) /* function to create the message queue and two tasks */
{
int taskIdOne, taskIdTwo;
/* create message queue */
if ((mesgQueueId = msgQCreate(MAX_MESSAGES,MAX_MESSAGE_LENGTH,MSG_Q_FIFO))
== NULL)
printf("msgQCreate in failed\n");

/* spawn the two tasks that will use the message queue */
if((taskIdOne =
taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,

Embedded system Lab

T.A. Bharat Kasyap

0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo =
taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskTwo failed\n");
}

void taskOne(void) /* task that writes to the message queue */


{
char message[] = "Received message from taskOne";
/* send message */
if((msgQSend(mesgQueueId,message,MAX_MESSAGE_LENGTH, WAIT_FOREVER,
MSG_PRI_NORMAL)) == ERROR)
printf("msgQSend in taskOne failed\n");
}
void taskTwo(void) /* tasks that reads from the message queue */
{
char msgBuf[MAX_MESSAGE_LENGTH];
/* receive message */
if(msgQReceive(mesgQueueId,msgBuf,MAX_MESSAGE_LENGTH, WAIT_FOREVER)
== ERROR)
printf("msgQReceive in taskTwo failed\n");
else
printf("%s\n",msgBuf);
msgQDelete(mesgQueueId); /* delete message queue */
}
------------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the examples by executing the main routine("message") of the example on
WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of the
printf commands.

Follow On Experiment
Experiment 1. Modify the above program so that taskTwo can send a string
message ("Received message from taskTwo") to taskOne. Have taskOne print the
message sent from taskTwo.
Experiment 2. Are there any advantages or disadvantages to using message queues
as opposed to global variables as a message passing scheme? Explain your answer.

Embedded system Lab

T.A. Bharat Kasyap

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 5
Round Robin
Introduction
Example: Round-robin Based Scheduling
In the example below, three tasks with the same priority print their task ids and
task names on the console. Without round-robin scheduling, "taskOne" would
usurp the processor until it was finished, and then "taskTwo" and "taskThree"
would do likewise. In the event that "taskOne" was looping indefinitely, the other
tasks would never get a chance to run.
To insure that the tasks get an equal share of the CPU time, a call is made
to kernelTimeSlice(). This sets the time slice interval value to TIMESLICE.
The TIMESLICE value is the time slice interval in terms of the number of clock
ticks(which in the example and the M68040 is 60 ticks which is equivalent to one
second). The sysClkRateGet() can be used to determine the number of clock ticks
per second.
Having setup the time slice in the manner above, the three tasks are spawned.
However, here a few implementation details that should be noted:

Make sure that sched has a higher priority than the tasks it is spawning!
Unless otherwise specified, tasks have a default priority of 100. Notice that
taskOne, taskTwo, and taskThree all have priorities of 101, which makes
them lower in priority than sched.

Yow must allow enough time for the context switches to occur. Thus the
reason for -> for (j=0; j < LONG_TIME; j++);

Using printf is not ideal in the example, because it can block .This will of
course cause a task transition which will upset the nice round robin picture.
Instead use logMsg() (see vxWorks reference manual for details). The latter
won't block unless the log message queue is full.

----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "kernelLib.h"
#include "sysLib.h"
#include "logLib.h"
/* function prototypes */
void taskOne(void);
void taskTwo(void);
void taskThree(void);

Embedded system Lab

T.A. Bharat Kasyap

/* globals */
#define ITER1 100
#define ITER2 10
#define PRIORITY 101
#define TIMESLICE sysClkRateGet()
#define LONG_TIME 1000000
void sched(void) /* function to create the three tasks */
{
int taskIdOne, taskIdTwo, taskIdThree;
if(kernelTimeSlice(TIMESLICE) == OK) /* turn round-robin on */
printf("\n\n\n\n\t\t\tTIMESLICE = %d seconds\n\n\n", TIMESLICE/60);
/* spawn the three tasks */
if((taskIdOne =
taskSpawn("task1",PRIORITY,0x100,20000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo =
taskSpawn("task2",PRIORITY,0x100,20000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskTwo failed\n");
if((taskIdThree =
taskSpawn("task3",PRIORITY,0x100,20000,(FUNCPTR)taskThree,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskThree failed\n");
}
void taskOne(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0); /* log messages */
for (j=0; j < LONG_TIME; j++); /* allow time for context
switch */
}
}
void taskTwo(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0); /* log messages */
for (j=0; j < LONG_TIME; j++); /* allow time for context
switch */
}
}
void taskThree(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)

Embedded system Lab

T.A. Bharat Kasyap

logMsg("\n",0,0,0,0,0,0); /* log messages */


for (j=0; j < LONG_TIME; j++); /* allow time for context
switch */
}
}
-----------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Execute the following command on the WindSh terminal: "logFdSet 1". This
will direct the logMsg() output to the virtual console.
4. Run the examples by executing the main routine("sched") of the example on the
WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of
the logMsg() commands.

Follow On Experiment
Experiment 1. Why must the priority of "taskOne", "taskTwo", and "taskThree" be
higher than "sched"?
Experiment 2. Write the source code necessary to vary the time
of TIMESLICE(10,20, 30,40,50,60,120,180,240 and 300 clock ticks).
Experiment 3. Add a fourth task(taskFour) that has a priority of 80 that prints out
the same message as the other three tasks in the example. Describe the output from
running the program.

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 6
Task scheduling
Introduction
Task scheduling is the assignment of starting and ending times to a set of tasks,
subject to certain constraints. Constraints are typically either time constraints or
resource constraints. On a time-sharing operating system, running each active
process in turn for its share of time (its "timeslice"), thus creating the illusion that
multiple processes are running simultaneously on a single processor.
Wind task scheduling uses a priority based preemptive scheduling algorithm as
default, but it can also accommodate round-robin scheduling.

Objectives
The following are the primary objectives of this experiment:

To demonstrate the use of VxWorks preemptive priority based task


scheduling facilities.

Description

Preemptive Priority Based Scheduling


With a preemptive priority based scheduler, each task has a priority and the
kernel insures that the CPU is allocated to the highest priority task that is
ready to run. This scheduling method is preemptive in that if a task that has
a higher priority than the current task becomes ready to run, the kernel
immediately saves the current tasks's context and switches to the context of
the higher priority task.
The Wind kernel has 256 priority levels(0-255). Priority 0 is the highest and
priority 255 is the lowest. Tasks are assigned a priority when created;
however, while executing, a task can change its priority
using taskPrioritySet().

1. Example: Preemptive Priority Based Scheduling

Embedded system Lab

T.A. Bharat Kasyap

One of the arguments to taskSpawn() is the priority at which the task is to execute:
id = taskSpawn(name, priority, options, stacksize, function, arg1,.. , arg10);
By varying the priority(0-255) of the task spawned, you can affect the priority of
the task. Priority 0 is the highest and priority 255 is the lowest.The Note the
priority of a task is relative to the priorities of other tasks. In other words, the task
priority number itself has no particular significance by itself.
In addition a task's priority can be changed after its spawned using the following
routine:

taskPrioritySet(int tid, int newPriority): Change the priority of a task.

In the example below, there are three tasks with different


priorities(HIGH,MID,LOW). The result of running the program is that the task
with the highest priority, "taskThree" will run to completion first, followed by the
next highest priority task, "taskTwo", and the finally the task with the lowest
priority which is "taskOne."
----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "logLib.h"
/* function prototypes */
void taskOne(void);
void taskTwo(void);
void taskThree(void);
/* globals */
#define ITER1 100
#define ITER2 1
#define LONG_TIME 1000000
#define HIGH 100 /* high priority */
#define MID 101 /* medium priority */
#define LOW 102 /* low priority */
void sched(void) /* function to create the two tasks */
{
int taskIdOne, taskIdTwo, taskIdThree;
printf("\n\n\n\n\n");
/* spawn the three tasks */
if((taskIdOne =
taskSpawn("task1",LOW,0x100,20000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo =
taskSpawn("task2",MID,0x100,20000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskTwo failed\n");
if((taskIdThree =
taskSpawn("task3",HIGH,0x100,20000,(FUNCPTR)taskThree,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskThree failed\n");

Embedded system Lab

T.A. Bharat Kasyap

}
void taskOne(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}
void taskTwo(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}
void taskThree(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}
-----------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Execute the following command on the WindSh terminal: "logFdSet 1". This
will direct the logMsg() output to the virtual console.
4. Run the examples by executing the main routine("sched") of the example on
WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of
the logMsg() commands.

Follow On Experiment
Experiment 1. Modify the program such that the order of execution is "taskOne"
first, then "taskTwo", and then "taskThree."

Embedded system Lab

T.A. Bharat Kasyap

Experiment 2. Modify the program so that "taskOne" has the highest priority and
"taskOne" and "taskTwo" are running at the same priority. Is there a difference in
the output when compared with the output of Experiment 1 of the Follow On
Experiment in this section.

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 7
Priority inversion
Introduction
Priority inversion occurs when a higher-priority task is forced to wait an indefinite
period for the completion of a lower priority task. For example, prioHigh,
prioMedium,and prioLow are task of high, medium, and low priority,
respectively. prioLow has acquired a resource by taking its associated binary
semaphore. When prioHigh preempts prioLow and contends for the resource by
taking the same semaphore, it becomes blocked. If prioHigh would be blocked no
longer than the time it normally takes prioLow to finish with the resource, there
would be no problem, because the resource can't be preempted. However, the low
priority task is vulnerable to preemption by the medium priority
task, prioMedium, which could prevent prioLow from relinquishing the resource.
This condition could persist, blocking prioHigh for an extensive period of time.

Objectives
The following are the primary objectives of this experiment:

To demonstrate VxWorks' priority inversion avoidance mechanisms.

Description
To address the problem of priority inversion, VxWorks provides an additional
option when using mutual exclusion semaphores. This option
is SEM_INVERSION_SAFE which enables a priority inheritance algorithm. This
algorithm insures that the task that owns a resource executes at the priority of the
highest priority task blocked on that resource. When execution is complete, the
task relinquishes the resource and returns to its normal priority. Therefore, the
inheriting task is protected from preemption by an intermediate priority task. This
option must be used in conjunction with SEM_Q_PRIORITY:
semId = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
1. Example:

Embedded system Lab

T.A. Bharat Kasyap

The example below illustrates a typical situation in which priority inversion takes
place. Here is the what happens:
1. prioLow task locks the semaphore.
2. prioLow task gets preempted by prioMedium task which runs for a long time
which results in the blocking of prioLow.
3. prioHigh task preempts prioMedium task and tries to lock the semaphore
which is currently locked by prioLow.
The situation is shown in the printout from running the program:
----------------------------------------------------------------------------------Low priority task locks semaphore
Medium task running
High priority task trys to lock semaphore
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running
------------------------------------------Medium priority task exited
Low priority task unlocks semaphore
High priority task locks semaphore
High priority task unlocks semaphore
High priority task trys to lock semaphore
High priority task locks semaphore
High priority task unlocks semaphore
High priority task trys to lock semaphore
High priority task locks semaphore
High priority task unlocks semaphore
..........................................High priority task exited
Low priority task locks semaphore
Low priority task unlocks semaphore
Low priority task locks semaphore
Low priority task unlocks semaphore
..........................................Low priority task exited
-----------------------------------------------------------------------------------

Since both prioLow and prioHigh are both blocked, prioMedium runs to
completion(a very long time). By the time prioHigh runs it is likely that it has
missed its timing requirements.
Here is what the code looks like:
----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"
/* function prototypes */
void prioHigh(void);
void prioMedium(void);
void prioLow(void);

Embedded system Lab

T.A. Bharat Kasyap

/* globals */
#define ITER 3
#define HIGH 102 /* high priority */
#define MEDIUM 103 /* medium priority */
#define LOW 104 /* low priority */
#define LONG_TIME 3000000
SEM_ID semMutex;

void inversion(void) /* function to create the three tasks */


{
int i, low, medium, high;
printf("\n\n....................##RUNNING##.........................\n\n\n"
);
/* create semaphore */
semMutex = semMCreate(SEM_Q_PRIORITY); /* priority based semaphore */
/* spawn the three tasks */
if((low = taskSpawn("task1",LOW,0x100,20000,(FUNCPTR)prioLow,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioHigh failed\n");
if((medium =
taskSpawn("task2",MEDIUM,0x100,20000,(FUNCPTR)prioMedium,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioMedium failed\n");
if((high =
taskSpawn("task3",HIGH,0x100,20000,(FUNCPTR)prioHigh,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioLow failed\n");
}
void prioLow(void)
{
int i,j;
for (i=0; i < ITER; i++)
{
semTake(semMutex,WAIT_FOREVER); /* wait indefinitely for semaphore
*/
printf("Low priority task locks semaphore\n");
for (j=0; j < LONG_TIME; j++);
printf("Low priority task unlocks semaphore\n");
semGive(semMutex); /* give up semaphore */
}
printf("..........................................Low priority task
exited\n");
}
void prioMedium(void)
{
int i;
taskDelay(20);/* allow time for task with the lowest priority to seize
semaphore */
for (i=0; i < LONG_TIME*10; i++)
{
if ((i % LONG_TIME) == 0)
printf("Medium task running\n");
}
printf("------------------------------------------Medium priority task
exited\n");
}

Embedded system Lab

T.A. Bharat Kasyap

void prioHigh(void)
{
int i,j;
taskDelay(30);/* allow time for task with the lowest priority to seize
semaphore */
for (i=0; i < ITER; i++)
{
printf("High priority task trys to lock semaphore\n");
semTake(semMutex,WAIT_FOREVER); /* wait indefinitely for semaphore
*/
printf("High priority task locks semaphore\n");
for (j=0; j < LONG_TIME; j++);
printf("High priority task unlocks semaphore\n");
semGive(semMutex); /* give up semaphore */
}
printf("..........................................High priority task
exited\n");
}
-----------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the examples by executing the main routine("inversion") of the example on
WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of
the printf() commands.

Follow On Experiment
Experiment 1. Modify the program so that the problem with priority inversion is
eliminated and the printout from the program looks like the following:
Low priority task locks semaphore
Medium task running
High priority task trys to lock semaphore
Low priority task unlocks semaphore
High priority task locks semaphore
High priority task unlocks semaphore
High priority task trys to lock semaphore
High priority task locks semaphore
High priority task unlocks semaphore
High priority task trys to lock semaphore
High priority task locks semaphore
High priority task unlocks semaphore
..........................................High priority task exited
Medium task running
Medium task running
Medium task running
Medium task running
Medium task running

Embedded system Lab

T.A. Bharat Kasyap

Medium task running


Medium task running
Medium task running
Medium task running
------------------------------------------Medium priority task exited
Low priority task locks semaphore
Low priority task unlocks semaphore
Low priority task locks semaphore
Low priority task unlocks semaphore
..........................................Low priority task exited

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 8
Signal
Introduction
A signal is a software notification to a task or a process of an event. A signal
is generated when the event that causes the signal occurs. A signal
is delivered when a task or a process takes action based on that signal.
The lifetime of a signal is the interval between its generation and its delivery. A
signal that has been generated but not yet delivered is pending. There may be
considerable time between signal generation and signal delivery.
VxWorks supports a software signal facility. The signals asynchronously alter the
control flow of task. Any task can raise a signal for a particular task. The task
being signaled immediately suspends its current thread of execution and the task
specified signal handler routine is executed the next time the task is scheduled to
run. The signal handler gets invoked even if the task is blocked on some action or
event. The signal handler is a user supplied routine that is bound to a specific
signal and performs whatever actions are necessary whenever the signal is
received. Signals are most appropriate for error and exception handling, rather than
general intertask communication.
The Wind kernel has both BSD 4.3 and POSIX signal interface. The POSIX
interface provides a standardized interface which is more functional than BSD 4.3
interface. Your application should use only one interface and not mix the two.

Objectives
The following are the primary objectives of this experiment:

To demonstrate VxWorks' implementation of POSIX signal routines.

Description

Embedded system Lab

T.A. Bharat Kasyap

The signal facility provides a set of 31 distinct signals(see VxWorks Manual). A


signal can be raised by calling kill(), which is analogous to an interrupt or
hardware exception. A signal is bound to a particular signal with sigaction().
While the signal handler is running, other signals are blocked from delivery. Tasks
can block the occurence of certain signals with sigprocmask(); if a signal is
blocked when it is raised, its handler routine will be called when the signal
becomes unblocked.
Signal handlers are typically defined as:
void sigHandlerFunction(int signalNumber)
{
.............. /* signal handler code */
..............
..............
}

where signalNumber is the signal number for which sigHandlerFunction is to be


invoked for.
The sigaction function installs signal handlers for a task:

int sigaction(int signo, const struct sigaction *pAct, struct sigaction *pOact)

A data structure of type struct sigaction holds the handler information.


The sigaction call has three parameters: the signal number to be caught, a pointer
to the new handler structure(of type struct sigaction), and a pointer to the old
structure(also of type struct sigaction). If the program does not need the value of
the old handler(*pOact), pass a NULL pointer for *pOact.
To direct a specific signal to a specific task, the kill(int, int) call is made where the
first argument the task id to send signal to, and the second argument is the signal to
send to the task .
1. Example:
In the example below, the "sigGenerator" function generates the SIGINT or Ctrl-C
signal, and directs the signal to the "sigCatcher" task. When "sigCatcher" receives
the signal, it suspends its normal execution and branches to a signal hander that it
has installed(catchSIGINT function).
----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "sigLib.h"
#include "taskLib.h"
#include "stdio.h"
/* function prototypes */
void catchSIGINT(int);
void sigCatcher(void);
/* globals */
#define NO_OPTIONS 0
#define ITER1 100
#define LONG_TIME 1000000
#define HIGHPRIORITY 101
#define MIDPRIORITY 102

Embedded system Lab

T.A. Bharat Kasyap

#define LOWPRIORITY 103


int ownId;
void sigGenerator(void) /* task to generate the SIGINT signal */
{
int i, j, taskId;
STATUS taskAlive;
if((taskId =
taskSpawn("signal",MIDPRIORITY,0x100,20000,(FUNCPTR)sigCatcher,0,0,0,0,0,0,
0,
0,0,0)) == ERROR)
printf("taskSpawn sigCatcher failed\n");
ownId = taskIdSelf(); /* get sigGenerator's task id */
taskDelay(30); /* allow time to get sigCatcher to run */
for (i=0; i < ITER1; i++)
{
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
printf("+++++++++++++++++++++++++++++++SIGINT sinal
generated\n");
kill(taskId, SIGINT); /* generate signal */
/* lower sigGenerator priority to allow sigCatcher to run
*/
taskPrioritySet(ownId,LOWPRIORITY);
}
else /* sigCatcher is dead */
break;
}
printf("\n***************sigGenerator Exited***************\n");
}
void sigCatcher(void) /* task to handle the SIGINT signal */
{
struct sigaction newAction;
int i, j;
newAction.sa_handler = catchSIGINT; /* set the new handler */
sigemptyset(&newAction.sa_mask); /* no other signals blocked */
newAction.sa_flags = NO_OPTIONS;
/* no special options */
if(sigaction(SIGINT, &newAction, NULL) == -1)
printf("Could not install signal handler\n");
for (i=0; i < ITER1; i++)
{
for (j=0; j < LONG_TIME; j++);
printf("Normal processing in sigCatcher\n");
}
printf("\n+++++++++++++++sigCatcher Exited+++++++++++++++\n");
}
void catchSIGINT(int signal) /* signal handler code */
{
printf("-------------------------------SIGINT signal caught\n");
/* increase sigGenerator priority to allow sigGenerator to run */
taskPrioritySet(ownId,HIGHPRIORITY);

Embedded system Lab

T.A. Bharat Kasyap

}
-----------------------------------------------------------------------------------

Procedures
1. Copy the source code in the example and compile it.
2. Load the object file onto the target machine.
3. Run the examples by executing the main routine("sigGenerator") of the example
on WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of
the printf commands.

Follow On Experiment
Experiment 1. Modify the program above so that there is no signal handler
associated with "sigCatcher." What happens when the SIGINT signal is now
delivered to "sigCatcher?"
Experiment 2. Modify the program above so that the SIGINT signal is blocked(i.e.
"sigCatcher" is prevented from receiving the SIGINT signal).

Additional Information
Refer to VxWorks User's Manual and Reference Manual.

Embedded system Lab

T.A. Bharat Kasyap

Lab 9
Interrupt handling
Introduction
Interrupt handling is important in real-time operating systems. The system
becomes aware of external events via the interrupt mechanism and the response of
a real-time systems depends on the speed of the system's response to interrupts and
the speed of processing interrupt handlers. To achieve the best response possible
the application writer must be aware of how to take advantage of the utilities
provided by VxWorks.

Objectives
The following are the primary objectives of this experiment:

To demonstrate VxWorks' implementation of interrupt service routines.

Description
The user may write an interrupt service routine (ISR) and attach it to a particular
interrupt using the intConnect routine provided by VxWorks. What basically
happens when an interrupt to the system occurs, is at the first non-critical code
after the interrupt occured, guaranteed to be within musec by WRS, the ISR is
executed. This time span is generally knonw as interrupt latency. Because many
interrupts may occur within a short time of each other and a higher interrupt will
block lower priority interrupts, it is necessary to keep the ISR processing to a
minimum. This is the responsibility of the application writer.
The header files which relate to VxWorks interrupt management are, intLib.h - this
is the interrupt library header file; and ARCH/mc68k/ivMc68k.h. The ISR code
does not run in the normal task context. It has no task control block and all ISR's

Embedded system Lab

T.A. Bharat Kasyap

share a single stack. Because of these differences there are restrictions to the type
of routines that can be used in the ISR.
ISR's should not invoke functions which may cause ``blocking'' of the caller. For
example, semTake. malloc and free cannot be used because they call functions
which may cause blocking and thus all creation and deletion functions are
forbidden since they use malloc and free. An ISR must not perform I/O through the
VxWorks I/O system. A call to a device driver may block the system if the caller
needs to wait for the device. However, the VxWorks pipe driver has been designed
to permit writes by interrupt service code.
The best way to print out messages from an ISR is to use the function logMsg or
other functions provided by the library logLib. ISRs should not use floating point
instructions since these registers are not saved on entry to the ISR. If floating point
instructions are to be used the registers must be saved using the functions in
fppALib. However, floating point operations are time intensive and should be
avoided in ISRs.
Ideally, an ISR only contains a semGive system call. That is to say, the function of
a ISR is to cause the execution of a task to perform whatever processing is
necessary. To improve cooperation between VxWorks' ISRs and tasks, the best
mechanism is semaphores.
1. Example:
In the example below, the interruptGenerator task generates a hard
interrupt, sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL), which is
caught by interruptCatcher.
The synatx for sysBusIntGen is:
SYNOPSIS
STATUS sysBusIntGen
(
int intLevel, /* bus interrupt level to generate
int vector
/* interrupt vector to generate (0-255)
)

*/
*/

RETURNS
OK, or ERROR if intLevel is out of range or the board cannot
generate
a bus interrupt.

interruptCatcher is able to handle this hardware interrupt by installing an


interrupt handler, interruptHandler.
interruptCatcher is "attaches" to the hardware interrupt
using intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPT
R)interruptHandler,i). The INUM_TO_IVEC(INTERRUPT_LEVEL) is a
macro that converts a hardware interrupt number to a vector.
The synatx for sysBusIntGen is:
SYNOPSIS
STATUS intConnect
(
VOIDFUNCPTR *
*/
VOIDFUNCPTR
*/

Embedded system Lab

vector,

/* interrupt vector to attach to

routine,

/* routine to be called

T.A. Bharat Kasyap

int

parameter

/* parameter to be passed to routine

*/
)
DESCRIPTION
This routine connects a specified C routine to a specified interrupt
vector. The
address of routine is stored at vector so that routine is called
with parameter when the
interrupt occurs. The routine is invoked in supervisor mode at
interrupt level. A proper
C environment is established, the necessary registers saved, and the
stack set up.
The routine can be any normal C code, except that it must not invoke
certain
operating system functions that may block or perform I/O operations.
This routine simply calls intHandlerCreate( ) and intVecSet( ). The
address of the
handler returned by intHandlerCreate( ) is what actually goes in the
interrupt vector.
RETURNS
OK, or ERROR if the interrupt handler cannot be built.

The run time scenario consists of interruptCatcher running and simulating


normal processing until interruptGenerator generates a hardware interrupt. Upon
the generation of the interrupt, interruptCatcher suspends its normal processing
and branches to interruptHandler. Once the interrupt handling code has been
executed, control is passed back to interruptCatcher. This activity is repeated
multiple times.
----------------------------------------------------------------------------------/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "taskLib.h"
#include "arch/mc68k/ivMc68k.h"
#include "logLib.h"
/* function prototypes */
void interruptHandler(int);
void interruptCatcher(void);
/* globals */
#define INTERRUPT_NUM 2
#define INTERRUPT_LEVEL 65
#define ITER1 40
#define LONG_TIME 1000000
#define PRIORITY 100
#define ONE_SECOND 100
void interruptGenerator(void) /* task to generate the SIGINT signal */
{
int i, j, taskId, priority;
STATUS taskAlive;
if((taskId =
taskSpawn("interruptCatcher",PRIORITY,0x100,20000,(FUNCPTR)interruptCatcher
,0,0,0,0,0,0,0,

Embedded system Lab

T.A. Bharat Kasyap

0,0,0)) == ERROR)
logMsg("taskSpawn interruptCatcher failed\n",0,0,0,0,0,0);
for (i=0; i < ITER1; i++)
{
taskDelay(ONE_SECOND);/* suspend interruptGenerator for one second
*/
/* check to see if interruptCatcher task is alive! */
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
logMsg("++++++++++++++++++++++++++Interrupt
generated\n",0,0,0,0,0,0);
/* generate hardware interrupt 2 */
if((sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL)) == ERROR)
logMsg("Interrupt not generated\n",0,0,0,0,0,0);
}
else /* interruptCatcher is dead */
break;
}
logMsg("\n***************interruptGenerator
Exited***************\n\n\n\n",0,0,0,0,0,0);
}
void interruptCatcher(void) /* task to handle the interrupt */
{
int i, j;
STATUS connected;
/* connect the interrupt vector, INTERRUPT_LEVEL, to a specific interrupt
handler routine ,interruptHandler, and pass an argument, i */
if((connected =
intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i))
== ERROR)
logMsg("intConnect failed\n",0,0,0,0,0,0);
for (i=0; i < ITER1; i++)
{
for (j=0; j < LONG_TIME; j++);
logMsg("Normal processing in interruptCatcher\n",0,0,0,0,0,0);
}
logMsg("\n+++++++++++++++interruptCatcher
Exited+++++++++++++++\n",0,0,0,0,0,0);
}
void interruptHandler(int arg)
{
int i;

/* signal handler code */

logMsg("-------------------------------interrupt caught\n",0,0,0,0,0,0);
for (i=0; i < 5; i++)
logMsg("interrupt processing\n",0,0,0,0,0,0);
}

-----------------------------------------------------------------------------------

Procedures
Embedded system Lab

T.A. Bharat Kasyap

1. Copy the source code in the example and compile it.


2. Load the object file onto the target machine.
3. Execute the following command on the WindSh terminal: "logFdSet 1". This
will direct the logMsg() output to the virtual console.
4. Run the examples by executing the main routine("interruptGenerator") of the
example on WindSh terminal.
Note: Make sure you have redirected I/O, otherwise you won't see the results of
the logMsg() commands.

Follow On Experiment
Experiment 1. Can the printf() statement be used instead of the logMsg()? Explain
why?
Experiment 2. Modify the example program so that it can handle hardware
interrupts 1,2, and 3. Generate these interrupts in ascending order
in interruptGenerator. interruptCatcher will use three separate handlers for
each hardware interrupt respectively.

Additional Information
What interrupt vectors are ok to use
The values 0 - 63 (decimal) are pre-defined by Motorola. Don't attach a handler to
these values, though they are worth looking at to familiarize oneself with the
default handlers. For 68K the file /VX_HSP_BASE/h/iv.h (which includes
/VX_HSP_BASE/h/arch/mc68k/ivMc68k.h)
The vectors from 64 - 255 are available for use. Some are already used by the BSP
code to connect to hardware dependent interrupts. In those cases we generally use
the vectors recommended by the hardware manufacturer. The question arises; how
to determine which vectors are available for customers applications to use. One
method to determine this would be to manually check all the usages of
intConnect(). VX_HSP_BASE/src/usr/usr*.c, VX_BSP_BASE/config/all/*, and
VX_BSP_BASE/config/BSP/* are the locations to check. Admittantly, using the
above methods to determine the used vectors seems unfriendly and tedious.
An alternative method is to use a C program to determine what vectors have been
used. This can be done because VxWorks assigns a default handler to all
"unassigned" vectors. ("Unitialized Vector...") We can scroll through the vectors
and check for that handler, if it the stub does not exist, the vector is used. Here is
some example code that will work, there is a much more elaborate & portable
version on the vxWorks users group software archive that will perform this task
(and more). There is also intVecGet().
/* vectors.c - show which mc68k user interrupt vectors are being used */
#include "vxWorks.h"

Embedded system Lab

T.A. Bharat Kasyap

#include "intLib.h"
#include "stdio.h"
#include "iv.h"
extern excIntStub; /* Default handler. */
void vectorShow (void)
{
int ix;
int vecBase = (int) intVecBaseGet();
for (ix = (vecBase + 64); ix < (vecBase + 256); ix++)
{
if ( *(int *) (UINT)(INUM_TO_IVEC(ix)) != (int) &excIntStub)
{
printf ("User defined interrupt vector %d has been
used.\n", ix);
}
}
}

= Experiment #9 addon : Interrupts on Intel architecture using the parallel


port =
===========================================================================
======
Experiment 9 was written for Motorola hardware, however our lab we have
different hardware using intel compatible CPU's. Therefore you need to do
extra
work :
Follow On Experiment 0
Make the program in excercise 9 work for intel hardware and use the
parallel port to generate the interrupts. Remark : use the documentation
below to do this.
Than make Follow on experiment 1 and 2.
Hint : Keep in mind we have different hardware!

= Interrupts and Exceptions for the Intel Architecture =


=========================================================
An interrupt is usually defined as an event that alters the sequence of
instructions executed by a processor. Such events correspond to electrical
signals generated by hardware circuits both inside and outside the CPU
chip.
Interrupts are often divided into synchronous and asynchronous interrupts:
- Synchronous interrupts are produced by the CPU control unit while
executing
instructions and are called synchronous because the control unit issues
them
only after terminating the execution of an instruction.
- Asynchronous interrupts are generated by other hardware devices at
arbitrary
times with respect to the CPU clock signals.

Embedded system Lab

T.A. Bharat Kasyap

Intel microprocessor manuals designate synchronous and asynchronous


interrupts
as exceptions and interrupts, respectively. We'll adopt this
classification,
although we'll occasionally use the term "interrupt signal" to designate
both
types together (synchronous as well as asynchronous).
Interrupts are issued by interval timers and I/O devices; for instance, the
arrival of a keystroke from a user sets off an interrupt. Exceptions, on
the
other hand, are caused either by programming errors or by anomalous
conditions
that must be handled by the kernel. In the first case, the kernel handles
the
exception by delivering to the current process one of the signals familiar
to
every Unix programmer. In the second case, the kernel performs all the
steps
needed to recover from the anomalous condition, such as a Page Fault or a
request (via an int instruction) for a kernel service.

Each interrupt or exception is identified by a number ranging from 0 to


255;
Intel calls this 8- bit unsigned number a vector. The vectors of
nonmaskable
interrupts and exceptions are fixed, while those of maskable interrupts can
be
altered by programming the Interrupt Controller.

All Interrupt Requests (IRQs) issued by I/O devices give rise to maskable
interrupts. A maskable interrupt can be in two states: masked or unmasked;
a masked interrupt is ignored by the control unit as long as it remains
masked.
Intel's vector assignment :
- Nonmaskable interrupts and exceptions are assigned in the range 0-31
- physical IRQs may be assigned any vector in the range 32-238
Intel's default vector associated with IRQn is n+32. (n+0x20)
complete list :
vector number
0-19
(0x0-0x13)
20-31
(0x14-0x1f)
32-127 (0x20-0x7f)
128
(0x80)
129-238 (0x81-0xee)
239
(0xef)
240-250 (0xf0-0xfa)
251-255 (0xfb-0xff)

purpose
Nonmaskable interrupts and exceptions
Intel-reserved
External interrupts (IRQs)
Programmed exception for system calls
External interrupts (IRQs)
Local APIC timer interrupt
Reserved by Linux for future use
Interprocessor interrupts

A system table called Interrupt Descriptor Table (IDT) associates each


interrupt or exception vector with the address of the corresponding
interrupt
or exception handler.

Embedded system Lab

T.A. Bharat Kasyap

Each entry corresponds to an interrupt or an exception vector and consists


of
an 8-byte descriptor. Thus, a maximum of 256 x 8 = 2048 bytes are required
to
store the IDT.

Hardware interrupts (Asynchronous interrupts), e.g. from a parallel port,


are
directed to a so called Programmable Interrupt Controller (PIC). This PIC
then
delivers the interrupt to the CPU. The PIC gives you the ability to
enable/disable certain interrupts.
However on the processor side it is possible to disable all interrupts at
once!
Thus we can disable/enable single interrupts or all interrupts at once !
For an intel architecture a Programmable Interrupt Controller (or PIC) is
an
Intel 8259A chip that controls interrupts. Starting with the 286-based AT,
there are two PICs in a personal computer, providing a total of 15 usable
IRQs.
The higher IRQ lines on the Intel 8259A chip have a higher priority. So IRQ
2
will be processed before IRQ 4. This priority is defined on the PIC.
Some machine architectures also allow handling of interrupts in the CPU
have
different priorities, however the intel processor has no CPU interrupt
priorities.
All interrupts on a intel processor are equal.
Intel's default vector associated with IRQ number n is vector number n+32.
(n+0x20)

So each interrupt :
- level (IRQ number)

-> specified by hardware


-> determines priority in PIC
-> we cannot choose another IRQ :

fixed by hardware!
INTERRUPT_LEVEL in interrupt.c
- vector : interrupt description in Interrupt Descriptor Table
INTERRUPT_NUM in interrupt.c -> intel specified, default :
Intel's default vector associated with IRQ number n is vector
number n+32. (n+0x20)
- interrupt handler routine : written by user
Notice interrupt.c use macro INUM_TO_IVEC :
INUM_TO_IVEC(INTERRUPT_NUM) returns the pointer in the
Interrupt
Descriptor Table for vector INTERRUPT_NUM
interrupt/io API for vxworks
============================
So we need to addres intel specific hardware. So read the
"Vxworks for Pentium 5.5 architecture supplement" to find out the
specifics.

Embedded system Lab

T.A. Bharat Kasyap

we found :
- page 11,24 : sysIntEnablePIC( ) - enable a bus interrupt level (IRQ
number)
- page 10,32 : sysInByte() - input one byte from I/O space
- page 10,32 : sysOutByte() - output one byte to I/O space
Interesting vxworks libraries :
* intArchLib
architecture-dependent interrupt library
`-> intLock/intUnlock - disable/enable all interrupts
`-> intConnect( ) - connect a C routine to a hardware interrupt
* intLib
architecture-independent interrupt subroutine library
* sysLib
system-dependent library
-> generic page for sysLib :
http://www.eelab.usyd.edu.au/tornado/docs/vxworks/ref/sysLib.html#top
-> sysLib [pcPentium] - PC [34]86/Pentium/Pentium[234] system-dependent
library :
http://www.eelab.usyd.edu.au/tornado/docs/vxworks/bsp/pcPentium/sysLib.html
`-> sysIntDisablePIC( ) - disable a bus interrupt level (:IRQ nr)
`-> sysIntEnablePIC( ) - enable a bus interrupt level (:IRQ nr)
`-> sysLib [pcPentium] - PC [34]86/Pentium/Pentium[234] systemdependent library :
`-> sysBusIntGen( ) - generate a bus interrupt
extra info
sysBusIntGen( ) :
This routine generates a VMEbus interrupt for a specified level with a
specified vector. This routine has NO EFFECT , since there is no VMEbus
on a
pentium architecture!!!!
=> we cannot use sysBusIntGen on intel hardware
=> we cannot generate interrupts!!
solution : use parallel port hardware to generate interrupts

Parallel port
=============

The parallel port can trigger interrupts.


Like most devices the parallel port doesn't actually generate interrupts
before its instructed to do so.
Traditionally IBM PC systems have allocated their first two parallel ports
according to the configuration in the table below.
PORT NAME
Interrupt #
Starting I/O
Ending I/O
LPT1
IRQ 7
0x378
0x37f
LPT2
IRQ 5
0x278
0x27f
We use LPT1 : IRQ 7
io-port 0x378
: data port
io-port 0x379 : status port
io-port 0x37a : control port

The parallel port standard states that setting bit 4 of port 2 (0x37a)
(control
port) enable interrupt reporting.
In vxworks we can use sysOutByte(0x37a,0x10) to set this bit in ioport
0x37a.

Embedded system Lab

T.A. Bharat Kasyap

Once interrupts are enabled, the parallel interface generates an interrupt


whenever the electrical signal at pin 10 (the so-called ACK bit) changes
from low to high.
However the interrupts can still be blocked by the PIC controllor!
You tell the PIC controllor to send the interrupts to the CPU with :
sysIntEnablePIC(7)

/* for IRQ=7 */

The simplest way to force the interface to generate interrupts, is to


connect
pins 9 (data line 0 out) and 10 (interrupt line in) of the parallel
connector.
Pin 9 is the data line 0 out bit of the parallel port. You can set this
bit by setting bit 0 in data port (ioport) 0x378.
The parallel port has 8 data out lines for all 8 bits of ioport 0x378.
For convenience we just set all data out lines high with :
sysOutByte(0x378,(char)0xff);
and low with :
sysOutByte(0x378,(char)0x00);
So when
- enabled IRQ 7 on PIC : sysIntEnablePIC(7)
- having all data lines default high
- connected pin 9 to 10
- enable interrupt handling : sysOutByte(0x37a,0x10)
we can generate an interrupt by putting the data lines for a moment low,
and
immediately high again :
sysOutByte(0x378,(char)0x00);
sysOutByte(0x378,(char)0xff);

info about parallel port :


http://en.wikipedia.org/wiki/Parallel_port
http://www.geocities.com/nozomsite/parallel.htm

Example program : parallelport-interrupt.c


============================================
/* Set up the printer port to generate interrupts
*/
/* IMPORTANT: connect pin one of pin 2-9 (data out) to pin 10 (interrupt
in)
on the parallel port !! */
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "kernelLib.h"
#include "sysLib.h"
#include "logLib.h"
#include "intLib.h"
#include "iv.h"
#include "ioLib.h"

Embedded system Lab

T.A. Bharat Kasyap

#include "stdio.h"
/* function prototype */
void myInt( int );
void setHigh(void);
void setLow(void);
/* globals */
#define PRIORITY 101
#define PRINTER_INT_LVL 7 /* IRQ line number */
#define PRINTER_INT_NUM (0x27) /* vector number assigned to IRQ ; can be
choosen different
than default intel convention 0x20+ IRQ
nr */
#define PRINTER_INT_ENBL (0x14)
#define PRINTER_INT_DISBL (0x04)
#define PRINTER_PORT (0x378)
int PrinterIntCount;
void interrupt(void)
{
char c;
/* set default data out high */
setHigh();
PrinterIntCount = 0;
printf("\n\n\nReady to go. Hit enter when ready\n");
c = getchar();

/* INUM_TO_IVEC returns the pointer in the Interrupt Descriptor Table


for vector INUM */
intConnect(INUM_TO_IVEC( PRINTER_INT_NUM ), myInt, 0); /* set up int
vector
*/
sysOutByte(PRINTER_PORT+2, (char)PRINTER_INT_ENBL );
/* enable port
int line */
sysIntEnablePIC( PRINTER_INT_LVL );
/* turn on int
control */
/* generate interrupt by setting data out from high->low->high
remark : one of the data out lines (D0-D7) should be connect to the
interrupt in
status line (nAck) */
setLow();
setHigh();
taskDelay ( sysClkRateGet() );

/* delay one second

sysOutByte(PRINTER_PORT+2, (char)PRINTER_INT_DISBL );
interrupts */
sysIntDisablePIC( PRINTER_INT_LVL );

*/
/* turn off

}
void myInt(int val)
{
logMsg("Got Interrupt %d\n",++PrinterIntCount,0,0,0,0,0);
}
void setHigh(void)

Embedded system Lab

T.A. Bharat Kasyap

{
/* set data out lines high (1) */
sysOutByte(PRINTER_PORT,(char)0xff);
}
void setLow(void)
{
/* set data out lines low (0) */
sysOutByte(PRINTER_PORT,(char)0x00);}

Embedded system Lab

T.A. Bharat Kasyap

Lab 10
Scheduling algorithm
Introduction
The program schedulecalculate.c does calculate the running schedule
of a set of tasks using an scheduling algorithm. This algorithm
calculates each time when the schedular is run the task which should
be scheduled next.
Thus with this program can we calculate the outcoming
schedule of a set of tasks. So we can use it to experimentally verify
of a set of tasks can be scheduled. The scheduling algorithm in
schedulecalculate.c does fixed priority preemptive scheduling at each
TIMEUNIT. It schedules the task with the highest priority, even when
another task is not finished.
We also have a program schedulecalculate-non-preemptive.c
which implements a fixed priority non-pre-emptive scheduling at each
TIMEUNIT. It schedules the task with highest priority only when the
previous task is finished.

In assigning priorities for our tasks we use Rate Monotic


Scheduling (RMS):
The Deadline is set to the Period of the task and the priorities are
set So that a task with shorter period has a higher priority.
Excercises:
a)
Period
Computation time
Task tau_1
T1 = 3 C1 = 1
Task tau_2
T2 = 4 C2 = 2
Show it is schedulable with fixed priority non-preemptive scheduling.
Is it still for C2=3?

b) Period Computation time


Embedded system Lab

T.A. Bharat Kasyap

Task tau_1
Task tau_2
Task tau_3

T1 = 10C1 = 2
T2 = 10C2 = 5
T3 = 20C3 = 6

c) Verify the schedubility for both non-preemptive as preemptive fixed


priority scheduling.
Period
Task tau_1
Task tau_2
Task tau_3

Computation time
T1 = 6 C1 = 3
T2 = 9 C2 = 2
T3 = 11C3 = 2

d) Verify the schedubility for both non-preemptive as preemptive fixed


priority scheduling.
Period Computation time
Task tau_1
T1 = 7 C1 = 2
Task tau_2
T2 = 16C2 = 4
Task tau_3
T3 = 31C3 = 7
Verify the schedubility for both non-preemptive as preemptive fixed
priority scheduling.

Embedded system Lab

T.A. Bharat Kasyap

Vous aimerez peut-être aussi