Académique Documents
Professionnel Documents
Culture Documents
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:
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".
------------------------------------------------------------------------------------
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
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:
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:
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
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.
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:
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
mutual exclusion
counting
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:
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
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++)
{
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.
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:
Description
Wind message queues are created and deleted with the following routines:
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.
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,
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");
}
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.
Additional Information
Refer to VxWorks User's Manual and Reference Manual.
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);
/* 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++)
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.
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:
Description
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:
}
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."
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.
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:
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:
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);
/* 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 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
Additional Information
Refer to VxWorks User's Manual and Reference Manual.
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:
Description
int sigaction(int signo, const struct sigaction *pAct, struct sigaction *pOact)
}
-----------------------------------------------------------------------------------
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.
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:
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
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.
vector,
routine,
/* routine to be called
int
parameter
*/
)
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.
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;
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
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"
#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);
}
}
}
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
So each interrupt :
- level (IRQ number)
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.
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 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.
/* for IRQ=7 */
#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();
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)
{
/* 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);}
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.
Task tau_1
Task tau_2
Task tau_3
T1 = 10C1 = 2
T2 = 10C2 = 5
T3 = 20C3 = 6
Computation time
T1 = 6 C1 = 3
T2 = 9 C2 = 2
T3 = 11C3 = 2