Log In   |  Register Free Newsletter Subscription
Global TMW:
Skip navigation
Zibb
Subscribe to Test & Measurement World
RSS
Reprints/License
Print
Email
Average Rating:
  • (0)
    Rate this:
  • 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 2:00:00 AM

    (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.
    Average Rating:
  • (0)
    Rate this:
  • RSS
    Reprints/License
    Print
    Email
    Talkback
    Similar Content from T&MW
    Reed Business Information Resource Center

    Featured Company


    Related Resources

    Advertisement

    Related Microsite Content

    Related Links

    • No Related Content Available

    More Content
    • Blogs
    • Webcasts

    Sorry, no blogs are active for this topic.

    » VIEW ALL BLOGS RSS

    EDN's Designing with LEDs
    Advertisement
    TMW Video - www.tmworld.com/video/
    NEWSLETTERS
    Test Industry News
    Automotive, Aerospace & Defense
    Communications Test
    Design, Test & Yield
    Machine-Vision & Inspection
    Instrumentation



    Please read our Privacy Policy

    About Us   |   Advertising Info   |   Site Map   |   Contact Us   |   FREE Subscription   |   Editorial Calendar
    © 2010 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