|
|
DIAPM RTAI - Beginner's Guide What is a "real time system"? A real time system can be defined as a "system capable of guaranteeing timing requirements of the processes under its control". It must be fast and predictable. Fast means that it has a low latency, i.e. it responds to external, asynchronous events in a short time. The lower the latency, the better the system will respond to events which require immediate attention. Predictable means that it is able to determine task's completion time with certainty. Typically a real time system represents the computer controlling system that manages and coordinates the activities of a controlled system, that can be viewed as the environment with which the computer interacts. The interaction is bidirectional, say through various sensors (environment -> computer) and actuators (computer -> environment), and is characterized by timing correctness constraints. It is desirable that time-critical and non time-critical activities coexist in a real time system. Both are called tasks and a task with a timeliness requirement is called a real time task. Typically real time tasks have the following types of requirements and/or
constraints.
What is RTAI? RTAI means Real Time Application Interface. Strictly speaking, it is not a real time operating system, such as VXworks or QNX. It is based on the Linux kernel, providing the ability to make it fully pre-emptable. Linux is a standard time-sharing operating system which provides good average performance and highly sophisticated services. Like other OS, it offers to the applications at least the following services:
Linux suffers from a lack of real time support. To obtain a timing correctness behaviour, it is necessary to make some changes in the kernel sources, i.e. in the interrupt handling and scheduling policies. In this way, you can have a real time platform, with low latency and high predicatbility requirements, within full non real time Linux environment (access to TCP/IP, graphical display and windowing systems, file and data base systems, etc.). RTAI offers the same services of the Linux kernel core, adding the features of an industrial real time operating system. It consists basically of an interrupt dispatcher: RTAI mainly traps the peripherals interrupts and if necessary re-routes them to Linux. It is not an intrusive modification of the kernel; it uses the concept of HAL (hardware abstraction layer) to get information from Linux and to trap some fundamental functions. This HAL provides few dependencies to Linux Kernel. This leads to a simple adaptation in the Linux kernel, an easy RTAI port from version to version of Linux and an easier use of other operating systems instead of RTAI. RTAI considers Linux as a background task running when no real time activity occurs. Installing RTAI Please have a look at the README.INSTALL file in the RTAI distribution for detailed instructions how to install RTAI. Kernel Modules RTAI is very much module oriented. So to understand and be able to use RTAI is necessary to know the dynamically loadable modules for Linux. The Linux kernel design is similar to that of classic Unix systems: it uses a monolithic architecture with file systems, device drivers, and other pieces statically linked into the kernel image to be used at boot time. The use of dynamic kernel modules allows you to write portions of the kernel as separate objects that can be loaded and unloaded on a running system. A kernel module is simply an object file containing routines and/or data to load into a running kernel. When loaded, the module code resides in the kernel's address space and executes entirely within the context of the kernel. Technically, a module can be any set of routines, with the one restriction that two functions, init_module() and cleanup_module(), must be provided. The first is executed once the module is loaded, and the second, before the module is unloaded from the kernel. The main functions to use to load /unload and inspect kernel modules are contained in the modutils package. They are:
Note that to manage with kernel modules you have to be root. Once a module is loaded, it passes to form part of the operating system, hence it can use all the functions and access all variables and structures of the kernel. Similarly the global symbols created are made available or exported to other modules. If you don't want to share some symbol (variable or function), you have to declare it as static. A module is built from a "C" source. Here is the simplest example of a kernel module (simple1.c). --------- simple1.c ------------ #include This can be compiled with: gcc -c -D__KERNEL__ -DMODULE -o simple1 simple1.c Note that the kernel offers a different version of printf() called printk(); this works almost identically to the first except that it sends the output to a kernel ring buffer. At any istant you can examine the contents of the buffer using the command dmesg. If you want to set the value of the variable var at installation, include the following macro: MODULE_PARM(var,"i"); after its declaration. To avoid the gcc command line for all sources, let's use the Makefile facility. Here is an example for two sources, i.e. simple1.c and simple2.c. --------- Makefile ----------- CFLAGS = -D__KERNEL__ -DMODULE -c OBJS = simple1 simple2 CC = gcc all: clean: rm ------------------------------ If you want to add a math function, say sin(), to our simple module you have to use the linker to get the sin() function into the kernel space. The Makefile becomes: --------- Makefile ----------- CFLAGS = -D__KERNEL__ -DMODULE -c OBJS = simple1 simple_with_sin CC = gcc all: simple_with_sin.o: simple_with_sin.c -o $@ $< simple_with_sin: simple_with_sin.o ld -r -static -o $@ $< -lm clean: rm simple_with_sin.o ------------------------------ If you want to do some real work, you have to learn how to read/write to hardware registers. It's very simple.
Here is a simple code that write a value to the parallel port. ------------- simplepp.c ----------------- #include RTAI Modules. To use RTAI, you have to load the modules that implement whatever RTAI capabilities you need. According to 1.3 release, available are the following core modules:
Let's examine one by one. 1) It is the really core module and nothing about the real time services can be
done without it. 2) The real time scheduler module, which is in charge of distributing the CPU to
different tasks 3) The module that implements the fifo services for RTAI. Many applications appear to 4) The RTAI specific module that allows sharing memory among different real time tasks
and 5) The LX(Linux)RT(RealTime) module, which implements services to make available any 6x) Posix RTAI modules. rtai_pthread.o provides
hard real-time threads, where each Timers and Interrupts Correct timing and interrupt management represent the really challenge of a real time system, and hence of RTAI. But how can I get time from a PC? What is an interrupt and how can I manage it? From now on we will refer to Intel architecture (RTAI 1.3 runs on x86 machines). Timers UPs provide a specific chip to solve the problem of generating accurate time delays under software control. It is called 8254 and is a programmable interval timer/counter, that can be treated as an array of four I/O ports in the system software (from 0x40 to 0x43). Three are indipendent 16-bit counters and the fourth is a control register for mode programming. The programmer configures the 8254 to match his/her requirements (select the mode) and programs one of the counters for the desired delay. After this delay, the 8254 will interrupt the CPU. Note that the counters are fully indipendent, so each counter may operate in a different mode. Linux programs the timer with mode 2 (rate generator, periodic pace) and loads the counter0 with the macro HZ defined in /usr/src/linux/include/asm/param.h (usually 100 hz). The counter 2 is used instead for beeping frequency. RTAI provides both a periodic (mode 2 of the 8254) and a oneshot timer (mode 0), using the counter0 to load the initial count. In the oneshot mode the clock is reprogrammed every interrupt, while in the periodic one it is programmed only at beginning and then generates interrupts periodically. It is up to you which one to choose in relation to the application at hand. The periodic mode is much more efficient when you have one or many (but with a common period) tasks that take regular samples, while the onsehot mode is more flexible, because it allows for example to time several tasks with no large common divisor in their periods or to trigger off some external event. Note that in the RTAI oneshot mode the time is measured on the base of the CPU time stamp clock (TSC) and neither on the 8254 chip, which is used only to generate oneshot interrupts. This allows to reprogram the counter with only 2 I/O instructions, i.e. approximately 3 us. Since the TSC is not available on 486 machines for them RTAI uses a form of emulation of the "read time stamp clock" (rdtsc) assembler instruction based on counter2 of the 8254. So you can use RTAI also on such machines. Be warned that the oneshot timer on 486 is a performance overkill because of the need of reading the tsc, i.e. 8254 counter2 in this case, 2/3 times. MPs provides another facility to get time. In addition to 8254 chip, which is one per box, there is a LOCAL APIC per CPU. Interrupts [... not yet available]RTAI example 1 This program is developed for the RTAI-1.4 version. You can find a Makefile, one kernel module (rt_process.c) and a Linux process (scope.c). By means of the scipt Run you can run the program. The program simply generates a sine signal and displays the istant values on the screen.
------------- MAKEFILE -------------------- all: scope rt_process.o LINUX_HOME = /usr/src/linux RTAI_HOME = /home/rtai-1.4 INCLUDE = -I/include -I/include MODFLAGS = -D__KERNEL__ -DMODULE -O2 -Wall scope: scope.c gcc -o $@ $< rt_process.o: rt_process.c gcc -c -o $@ $< clean: rm -f rt_process.o scope
-------------- RT_PROCESS.C ------------------- \#include <linux/module.h> \#include <asm/io.h> \#include <math.h> \#include <rtai.h> \#include <rtai_sched.h> \#include <rtai_fifos.h>
\#define TICK_PERIOD 1000000 \#define TASK_PRIORITY 1 \#define STACK_SIZE 10000 \#define FIFO 0
static RT_TASK rt_task;
static void fun(int t) { int counter = 0; float sin_value; while (1) { sin_value = sin(2*M_PI*1*rt_get_cpu_time_ns()/1E9); rtf_put(FIFO, &counter, sizeof(counter)); rtf_put(FIFO, &sin_value, sizeof(sin_value)); counter++; rt_task_wait_period(); } } int init_module(void) { RTIME tick_period; rt_set_periodic_mode(); rt_task_init(&rt_task, fun, 1, STACK_SIZE, TASK_PRIORITY, 1, 0); rtf_create(FIFO, 8000); tick_period = start_rt_timer(nano2count(TICK_PERIOD)); rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); return 0; } void cleanup_module(void) { stop_rt_timer(); rtf_destroy(FIFO); rt_task_delete(&rt_task); return; }
-------------------- SCOPE.C ---------------------- \#include <stdio.h> \#include <unistd.h> \#include <sys/types.h> \#include <sys/mman.h> \#include <sys/stat.h> \#include <fcntl.h> \#include <signal.h>
static int end; static void endme(int dummy) { end=1; }
int main (void) { int fifo, counter; float sin_value; if ((fifo = open("/dev/rtf0", O_RDONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf0\n"); exit(1); } signal(SIGINT, endme); while (!end) { read(fifo, &counter, sizeof(counter)); read(fifo, &sin_value, sizeof(sin_value)); printf(" Counter : %d Seno : %f \n", counter, sin_value); } return 0; }
-------------------- RUN ---------------------- sync insmod /home/rtai-1.4/modules/rtai.o insmod /home/rtai-1.4/modules/rtai_sched.o insmod /home/rtai-1.4/modules/rtai_shm.o insmod /home/rtai-1.4/modules/rtai_fifos.o insmod rt_process.o ./scope rmmod rt_process rmmod rtai_shm rmmod rtai_fifos rmmod rtai_sched rmmod rtai RTAI example 2 This example is like the first one, but it uses the shared memory, instead of fifos, to communicate between Linux and the RTAI-layer. The common data space is declared in the header file parameters.h. ------------------ MAKEFILE ------------------------- all: scope rt_process.o LINUX_HOME = /usr/src/linux RTAI_HOME = /home/rtai-1.4 INCLUDE = -I/include -I/include MODFLAGS = -D__KERNEL__ -DMODULE -O2 -Wall scope: scope.c gcc -o $@ $< rt_process.o: rt_process.c parameters.h gcc -c -o $@ $< clean: rm -f rt_process.o scope
------------------- RT_PROCESS.C ---------------------- \#include <linux/module.h> \#include <asm/io.h> \#include <math.h> \#include <rtai.h> \#include <rtai_shm.h> \#include <rtai_sched.h> \#include "parameters.h"
static RT_TASK rt_task; static struct data_str *data;
static void fun(int t) { unsigned int count = 0; float seno,coseno; while (1) { data->indx_counter = count; seno = sin(2*M_PI*1*rt_get_cpu_time_ns()/1E9); coseno = cos(2*M_PI*1*rt_get_cpu_time_ns()/1E9); data->sin_value = seno; data->cos_value = coseno; count++; rt_task_wait_period(); } } int init_module(void) { RTIME tick_period; rt_set_periodic_mode(); rt_task_init(&rt_task, fun, 1, STACK_SIZE, TASK_PRIORITY, 1, 0); data = rtai_kmalloc(nam2num(SHMNAM), sizeof(struct data_str)); tick_period = start_rt_timer(nano2count(TICK_PERIOD)); rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); return 0; } void cleanup_module(void) { stop_rt_timer(); rt_task_delete(&rt_task); rtai_kfree(nam2num(SHMNAM)); return; }
---------------------- SCOPE.C ---------------------------- \#include <stdio.h> \#include <unistd.h> \#include <sys/types.h> \#include <sys/mman.h> \#include <sys/stat.h> \#include <fcntl.h> \#include <signal.h> \#include <rtai_shm.h> \#include "parameters.h"
static int end; static void endme(int dummy) { end=1; }
int main (void) { struct data_str *data; signal(SIGINT, endme); data = rtai_malloc (nam2num(SHMNAM),1); while (!end) { printf(" Counter : %d Sine : %f Cosine : %f \n", data->indx_counter, data->sin_value, data->cos_value); } rtai_free (nam2num(SHMNAM), &data); return 0; }
---------------- PARAMETERS.H ----------------------- \#define TICK_PERIOD 1000000 \#define TASK_PRIORITY 1 \#define STACK_SIZE 10000 \#define SHMNAM "MIRSHM" struct data_str { int indx_counter; float sin_value; float cos_value; };
----------------------- RUN ------------------------ sync insmod /home/rtai-1.4/modules/rtai.o insmod /home/rtai-1.4/modules/rtai_sched.o insmod /home/rtai-1.4/modules/rtai_shm.o insmod /home/rtai-1.4/modules/rtai_fifos.o insmod rt_process.o ./scope rmmod rt_process rmmod rtai_shm rmmod rtai_fifos rmmod rtai_sched rmmod rtai
© 2002 The RTAI Development Team
|