Hard Real-Time Control & Real-Time Linux

Dr. Dobb's Journal November 2003

A cost-effective tool for hard real-time control

By Raul Murillo-Garcia

Raul is a Ph.D. candidate and researcher in the School of Engineering Science and Design at Glasgow Caledonian University. He can be contacted at r.murillogarcia@gcal.ac.uk.

Hard real-time control requires specialized—and often expensive—hardware and software. In this article, I present Simulink-Target for Real-Time Linux (ST-RTL), a cost-effective tool for hard real-time control. ST-RTL couples MATLAB, Simulink, and Real-Time Workshop with Real-Time Linux to build a platform in which controllers run deterministically.

Real-Time Linux (RTLinux; http://www.fsmlabs.com/) is a real-time extension of the standard Linux kernel that adds a layer of emulation software between the original Linux kernel and interrupt controller. The real-time scheduler gives Linux the lowest priority, thereby letting you create higher priority real-time threads. MATLAB, from The MathWorks (http:// www.mathworks.com/products/matlab/), is a high-performance language for technical computing that integrates computation, visualization, and programming. Also from The MathWorks, Simulink (http:// www.mathworks.com/products/simulink/) is an environment for modeling, simulating, and analyzing dynamic systems. Since the system to be controlled can be represented in the form of a block diagram, Real-Time Workshop (RTW; http://www .mathworks.com/products/rtw/) translates the block diagram representation into platform-invariant C code files.

We use ST-RTL in the control-systems laboratory at Glasgow Caledonian University to control laboratory-scaled experiments, ranging from coupled tanks and inverted pendulums to DC motors and vibrations rigs. Typical users are students who design control algorithms using the Simulink interface. The subsequent code generated by RTW is then copied onto the RTLinux platform. ST-RTL, which is available electronically from DDJ (see "Resource Center," page 5) and at http://www.sesd.gcal.ac.uk/raulm/St-rtl.htm, ensures the correct execution of the implemented algorithm within this environment. This approach does not require that users have any knowledge of low-level programming or the underlying system. Consequently, they can focus on everything from modeling to final prototyping, keeping controller algorithm implementation to a minimum.

Implementation

RTW generates six model-dependent ANSI C code source files. These files contain the information required to describe and parameterized a Simulink block diagram. A second set of model-independent C source-code files is involved in the creation of a target-dependent, standalone file for running the model, data logging, and communication. Generic Real-Time (GRT) target, the default RTW target, lets you customize targets by providing an environment for simulating fixed-step models in which code validation can be performed, as well as a starting point for targeting customized hardware.

Originally, we programmed the model-independent files to execute in user space. However, to use RTLinux's facilities, applications need to compile as modules and be loaded into the kernel. Consequently, standard file input/output functions (such as fprintf() and fopen()) or network functions (socket(), connect(), and send()) are not valid. Therefore, the model-independent source files must be modified.

Since Linux's end-of-line character differs from Windows, the dos2unix utility has been run on all source files to prevent parse errors during compilation. Display functions such as printf() and fprintf() are not supported in the kernel. RTLinux supplies the rtl_printf() function, a real-time-safe function for data logging. Macros can be used to avoid having to modify every instance of those functions:

#define fprintf(stderr,text) rtl_printf(text)

#define printf(text) rtl_printf(text)

Moreover, the header file stdio.h has to be removed from files that define it.

The flow control of a GRT target model is implemented in the int_T main(int_T argc, char_T *argv[]) function declared in grt_main.c. Its three sequential phases are model initialization, model execution, and model termination. These phases have been made independent by separating them into three new functions:

int_T model_init(SimStruct *S)

int_T model_run(SimStruct *S)

int_T model_clean(SimStruct *S)

Kernel modules can include the functions init_module() and cleanup_module(). The former is invoked when the module is first loaded in the kernel, the latter is invoked when it is unloaded. The new source file krnl_main.c includes those functions, which serve as entry points to launch the model initialization and termination, respectively.

Simulink lets you define multiple sample rates in a block diagram. Each sample rate value, however, has to be an integer-multiple of the base sample rate used to run the continuous states of the block diagram. Thus, models can run in single-tasking or multitasking mode. Single-tasking models execute a single real-time thread that runs at intervals specified by the base sample rate. Multitasking can improve the efficiency of the program if the model is large and has many blocks executing at each rate. When multitasking is enabled, the Simulink macro MULTITASKING is set to 1, and NUMST is set to the number of different rates found in the model. Continuous blocks are specified by giving the value zero to the block parameter "Sample time." However, the actual sample rate is mapped to the base sample rate. In such situations, NUMST specifies a one-to-many sample rate. To account for this, the macro TID01EQ is set to 1 when continuous blocks are present in the model. Thus, the actual number of sample rates is given by:

# define NUM_RT_THREADS (NUMST - TID01EQ)

To support multitasking, ST-RTL creates as many real-time threads as sample rates defined in the macro NUM_RT_THREADS:

for (tid = 0; tid < NUM_RT_THREADS; tid++)

pthread_create(&rtThread[tid],NULL,rt_thread_func,

(void*) tid);

Once created, the tasks are made periodic with interval times users specify in the block diagram:

/* Make all the threads periodic. 1-second delay for all the threads to be set. */

for (tid = 0; tid < NUM_RT_THREADS; tid++)

pthread_make_periodic_np(rtThread[tid], gethrtime() +

NSECS_PER_SEC, tidSampleRate[tid]);

Threads with higher sample rates are given increasing priorities, thus enabling preemption as specified in the RTW manual. To guarantee real-time performance, the scheduler SCHED_FIFO is set. Each real-time task is an instance of the function void *rt_thread_func(void *arg) in krnl_main.c. This function sets parameters such as the priority and scheduler, then enters an endless while loop, in which the function model_run(S, tid) in grt_main.c is called upon with the correspondent tid. The tid value groups blocks with the same sample rate. In turn, model_run() calls the function void MdlOutputs(int_T tid) in model.c, where the actual C code representation of the Simulink diagram is defined. The specific C code that defines each block is wrapped with if statements defined by the condition of the tid value. Since each real-time task inherits the appropriate tid when (eventually) MdlOutputs(int_T tid) is executed, only the code corresponding to the sample rate (or tid) associated to the calling real-time thread will be run through. Once the model cycle is finished, the task is put to sleep until it is invoked again by the scheduler. By default, RTL threads cannot use floating-point operations because real-time threads have an integer context solely to facilitate fast context switches (as switching floating-point context takes some time). However, since RTW makes use of this type of variable, the thread needs to be told explicitly to support floating-point operations. Listing One (available electronically; see "Resource Center," page 5) is the code for the real-time threads. The new layout of the process is as follows: When the module is loaded into the kernel, init_module() calls on model_init() to initialize the model. The real-time threads are created, then scheduled to run at the appropriate intervals. Every time this thread is invoked, it calls upon model_run(), which executes a model cycle. Finally, when the module is unloaded, cleanup_module() calls upon model_clean() to terminate the application; see Figure 1.

RTLinux does not provide tools for detecting missed deadlines. However, you can periodically run a real-time thread, whose priority is the highest, to preempt and stop the real-time controller if necessary. This thread can act as a watchdog, ensuring that deadlines are not missed. The period of the watchdog thread is that of the base sample rate. Each real-time task has a unique counter that is incremented at the end of every cycle. On the other hand, the watchdog also keeps a counter for each thread, incrementing them accordingly with the thread's rate. If a deadline is missed, the corresponding counter is not increased. This mismatch is detected by the watchdog. Listing Two (also available electronically) is code for the watchdog thread, while Figure 2 illustrates the mechanism.

The CIO-DAS08/JR-AO Data-Acquisition Card

ST-RTL includes a driver for the CIO-DAS08/JR-AO data-acquisition card from Measurement Computing (http://www.measurementcomputing.com/). This card has eight digital inputs, eight digital outputs, eight analog inputs, and two analog outputs. The driver consists of four local functions (to perform the low-level access to the card) and four API functions. Two of the four API functions discern which local function is to be called, depending on the selected channel number; that is, depending on whether the channel is digital or analog. The other two API functions initialize and close the driver. The former automatically finds the I/O-base-memory address selected through the card's switch by polling a predefined set of addresses. When the base address is found, the driver requests that the kernel allocate the I/O port range used to access the card. The latter releases the range of I/O ports that are used.

The data-acquisition card is a shared device that can be accessed by multiple threads when multitasking is enabled, thus becoming vulnerable to race conditions. To protect low-level access to the data-acquisition card, two mutexes are used—one for input channels (mutex m_read) and the other for output channels (mutex m_write). When a thread requests access to the input/output ports, the correspondent mutex is locked (pthread_mutex_lock()). Only when the process is finished is the mutex unlocked (pthread_mutex_unlock()).

The ST-RTL Input and ST-RTL Output blocks make the external inputs and outputs of the data-acquisition card accessible in a Simulink block diagram. ST-RTL Input and ST-RTL Output are general-purpose S-function blocks. Figure 3 illustrates the dialog box that has been created for ST-RTL Input; ST-RTL Output shares the same characteristics.

The interface panel includes two fields:

Each block in a Simulink model is associated to a Target Language Compiler (TLC) file, which governs the way code is generated for that specific block. Thus, the TLC files strtl_in.tlc and strtl_out.tlc embed the driver function calls in the generated code. The former inlines the driver API function call das08jrao_read (channel,*value), whereas the latter inlines das08jrao_write(channel,value) in the generated source file model.c. The call-up parameters are those specified by users in the fields of the S-function block (Figure 3).

Template Makefile

During model code generation, RTW creates a makefile from a target-dependent template makefile. The makefile is then used to govern the compilation-linking process. The template makefile (rtlinux.tmf) is created for targeting RTLinux, and based on grt_unix.tmf. The modifications to grt_unix.tmf include the following:

Adding a Target to The System Target File Browser

Available RTW targets are listed in the "System Target File Browser," accessible from the menu Simulation|Parameters| Real-Time Workshop|Browse. To add the RTLinux target, the system target file rtlinux.tlc must be created, which is nearly identical to grt.tlc (the Generic Real-Time target), as the model-specific code generated during the build process remains identical. Still, I customized the first two lines:

%% SYSTLC: Simulink Target for Real Time Linux\

%% TMF: rtlinux.tmf MAKE: make_rtw EXTMODE: ext_comm

The first line adds the "Simulink Target for Real Time Linux" target to the System Target File Browser. The second line specifies the template makefile, system target file, and external mode file, used during the generation of the source files by RTW.

Conclusion

As presented here, ST-RTL users are blind as to the correct functionality of the controller, since feedback is not available. The full version of ST-RTL, however, supplies feedback by implementing a TCP/IP client-server network connection with a host machine, on which Simulink and RTW are running. Users can monitor the current state of the control experiment by observing live signals on special-purpose blocks called "Scopes," which display specific points of the Simulink diagram in the time domain on a unique window (somewhat like an oscilloscope). Signal data flows from target to host through a channel opened when the connection is established. In addition, users can modify the controller parameters on the fly through the Simulink interface. When a parameter is modified, its new value is downloaded to the target machine and updated in the next cycle. Furthermore, ST-RTL integrates an application layer control-flow algorithm to improve real-time monitoring when restrictions of the hardware-network infrastructure are present.

DDJ