Global TMW:
Login  |  Register          Free Newsletter Subscription
Subscribe
Email
Print
Reprint
Learn RSS

Get Direct I/O Control With Linux

You don't need drivers, and you can control delay times.

Todor Petrov, Sigma-Delta Ltd., Sofia, Bulgaria -- Test & Measurement World, 4/15/1999

(This article assumes a knowledge of Linux and C programming.)

When our company needed to change to a new operating system (OS), we chose Linux over Windows NT. Unlike NT, Linux provides direct I/O capability, which lets us better control the operations of our ASIC testers. Linux has several functions built into its kernel that you can use to get precise control over your computer’s I/O ports.

14t2fg1a.gif (14222 bytes)
Figure 1. Linux lets you program the high and low times of an I/O port. Here, the program keeps a bit high for 1.1 ms and low for 1.0 ms.

Get Privileged
Although Linux provides direct access to the PC’s I/O ports, your program must have the proper privileges to use the ports.1 The code example in Listing 1 works only if your program has super user privileges. Therefore, the executing user (the person that starts the program) must either have root privileges or be a member of a group that has root privileges over the program file.

In addition to having the proper permission, you must give the program permission to control the I/O ports. Design the program to call the ioperm() function before it requires access to any I/O port.

In Listing 1, the unistd.h header file declares the ioperm() function and the Linux kernel defines the function. The syntax is ioperm(start_port, port_ num, on_off), where start_port is the first port number and port_num is the number of consecutive ports for which you need access. For the last argument, a value of 1 enables the ports while a value of 0 disables them. For example, ioperm(0x330, 8, 1) gives you access to ports 0x300 through 0x337. We used these eight ports to control our ATE. The ioperm() function can give you access to ports 0x000 hex through 0x3ff hex only.

For controlling ports at higher addresses, use the iopl() function. Be careful when using this function, because invoking it with the third argument (shown in the ioperm() function above) gives a program access to all I/O ports. Listing 1 shows an example of how to write to a printer port, which is at address 0x378 hex.

Listing 1

#include
#include
#include
    ...
if (ioperm (0x378, 1, 1))       
// get access to LPT1
{
puts (“LPT1 access denied!”);   
exit (1);
}
    ...
outb (0, 0x378);                

     // clear LPT1

Once you have access to a port, you need to transfer data to and from it. The simplest routines are outb(value, port), which will write a byte to a port, and inb(port), which will read a byte from a port. Linux has outw and inw functions that read and write 16-bit words, and outl and inl that handle 32-bit words. You declare these routines using the asm/io.h header file.

When you write code to control I/O ports, be sure to compile the I/O-port source code with an optimization switch (-O or -O2) in the C compiler. Those switches let the compiler make in-line substitutions rather than library calls for these particular routines.

Precise Delays
After gaining access to the I/O ports, we had to overcome some delay-timing issues to get the ATE to run properly. If you’ve ever written a test program for an IC or other electronic device, then you know that the transient and setup delays take a significant portion of the device’s test time.

Linux isn’t a real-time operating system, and this can create problems if your system uses device drivers and has critical timing requirements. The good news is that Linux lets you program delays of hundreds of microseconds, which provide more precision than you can attain under Windows, giving you better control over I/O timing.

While you often must program some delays into your test application, you must also accept some increases in those delays caused by the OS. If the OS switches tasks during such a programmed delay, the delay often becomes desired. If the task has real-time priority, however, it can stay in the focus of the Linux task scheduler. When that happens, you won’t notice any additional delays. You shouldn’t grant such a privilege to a program as a whole, though. If you do, you’ll block all the other regular-priority processes that the OS must perform.

At Sigma-Delta, we’ve found that our ASIC test programs usually need delays from hundreds of microseconds to several seconds. For delays of multiple seconds, the Linux sleep() function works well. For delays of at least tens of milliseconds, usleep() should work too. These functions give the CPU access to other processes during the delay and won’t waste CPU time. When you need delays of 2 ms or less, you need a different approach.

Versions 2.0.x or later of the Linux kernel have a precise delay function. It’s called nanosleep() and it puts a task to sleep for a few microseconds or more. For delays less than 2 ms, if the process is set to real-time scheduling, this routine uses a busy wait—a loop of CPU instructions. Otherwise, the CPU “sleeps,” just like it does when you use the usleep() function. The faster the processor, the more precise the delay routine because the clock cycles provide smaller time increments. The code in Listing 2 implements precise delays from several hundreds of microseconds up to 70 min.

The routine in Listing 2 performs a delay in increments of 1 ms. It turns the program to normal priority every millisecond so the CPU can service other concurrent processes. The normal priority becomes necessary when you require delays greater than tens of milliseconds. The disadvantage of this method is that you waste the CPU’s productivity during the delay.

Listing 2


#include
#include

void usdelay(unsigned long delay_in_us)
{
long            i;
struct timespec        sleep_data, dummy_return;
struct sched_param    normal_priority_data,
realtime_priority_data;

// Prepare the normal priority data structure:
normal_priority_data.sched_priority = 0;       
   
// Prepare the real-time priority data stucture:
    realtime_priority_data.sched_priority = 1;        

// Prepare the nanosleep data stucture for sleeping the reminder
// of the division of the desired delay time in microseconds by
// 1000. The result is formatted in nanoseconds.
sleep_data.tv_sec = 0;
sleep_data.tv_nsec = (delay_in_us % 1000) * 1000;

// Set the current process to real-time priority scheduling:
sched_setscheduler (0, SCHED_RR, &realtime_priority_data);

// Sleep the millisecond-reminder time:
nanosleep (&sleep_data, &dummy_return);

// Prepare the nanosleep data structure for sleeping in portions
// of 1 millisecond each. The sleeping time is given in
// nanoseconds.
sleep_data.tv_nsec = 1000000;

// Perform the necessary number of delays of 1ms each
for (i = delay_in_us / 1000; i; i—)
{
// Set the current process to real-time priority scheduling:
sched_setscheduler (0, SCHED_RR, &realtime_priority_data);

// Sleep for 1ms:
nanosleep (&sleep_data, &dummy_return);

// Set the current process to normal priority scheduling so
// the other processes can breath:
sched_setscheduler (0, SCHED_OTHER, &normal_priority_data);
}
}

To see how accurate a delay can be, look at the oscilloscope display in Figure 1. The waveform shows the output of an LM311 comparator driven by the I/O port. In this example, I’ve programmed a bit to stay high for 1.1 ms and low for 1 ms, respectively.

The oscilloscope measurements show 0.1-ms difference in the positive (widp) and negative (widn) pulse durations. You can split the extra 24 ms of error into two parts.

First, the communication with the ASIC tester (where our LM311 resides) takes about 11 ms. The program takes the rest—typically 12 ms to 19 ms—to switch between normal priority and real-time priority. The faster the PC, the smaller that time. We used a 166-MHz Pentium PC to produce the waveform in Figure 1. T&MW

FOOTNOTES
1. For more detailed information on user privileges under Linux, see The Linux Bible, published by Yggdrasil Computing. You can also download the result of the so-called “Linux Documentation Project” from ftp://sunsite.unc.edu/pub/Linux/docs/LDP/   which contains useful hints on Linux system administration and programming.

Todor Petrov is head of ASIC tester R&D at Sigma-Delta Ltd.

Email
Print
Reprint
Learn RSS

Talkback

We would love your feedback!

Post a comment

» VIEW ALL TALKBACK THREADS

Related Content

Related Content

 

By This Author

There are no other articles written by this author.

Sponsored Links



 
Advertisement
SPONSORED LINKS

More Content

  • Blogs
  • Podcasts

Blogs

  • Martin Rowe
    Rowe's and Columns

    August 11, 2008
    Grachanen wins NCSLI award
    At last week's NCSL International Workshop and Symposium, Chris Grachanen was awarded the NCSLI Educ...
    More
  • Martin Rowe
    Rowe's and Columns

    August 8, 2008
    Have you seen Test Ideas?
    The August print issue of T&MW features a new column called "Test Ideas." For this fir...
    More
  • » VIEW ALL BLOGS RSS

Podcasts

Advertisements





NEWSLETTERS
Click on a title below to learn more.

Test Industry News (3 Times Per Month)
Machine-Vision & Inspection (Monthly)
Communications Test (Monthly)
Design, Test & Yield (Monthly)
Automotive, Aerospace & Defense (Monthly)
Instrumentation (Monthly)
Resource Center E-Alert (Monthly)
©2008 Reed Business Information, a division of Reed Elsevier Inc. All rights reserved.
Use of this Web site is subject to its Terms of Use | Privacy Policy
Please visit these other Reed Business sites