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 computers I/O ports.
Get Privileged 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.
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 Linux isnt 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 wont notice any additional delays. You shouldnt grant such a privilege to a program as a whole, though. If you do, youll block all the other regular-priority processes that the OS must perform. At Sigma-Delta, weve 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 wont 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. Its 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 waita 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 CPUs productivity during the delay.
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, Ive 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 resttypically 12 ms to 19 msto 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 Todor Petrov is head of ASIC tester R&D at Sigma-Delta Ltd. |


















