Archived: How to Make an iBus as Part of the MHDDK

NI does not actively maintain this document.

This content provides support for older products and technology, so you may notice outdated links or obsolete information about operating systems or other relevant products.


This tutorial explains how to use the Measurement Hardware Driver Development Kit (MHDDK) on an operating system that does not have an operating system interface bus (iBus) implementation.



In order to use the Chip Objects in the Measurement Hardware Driver Development Kit (MHDDK) on an operating system, you must have an iBus for that operating system. This document covers the basics of an iBus and the considerations for creating one for your target operating system.



What is an iBus?

An iBus is an OS abstraction layer. It provides a simple interface to the PCI/PXI bus for the example programs in the MHDDK. It provides simple functions for register I/O and, if supported, DMA buffer operations.

User-Defined iBus Functions for OS Support

These are the two functions you will need to define to make the iBus work on your target OS.  Almost all of the system dependent code for the RLP examples is in osiUserCode.cpp. To port an iBus to a new OS:

  1. Redefine acquireBoard(…) and releaseBoard(…).
  2. Use void *osSpecific to point to any OS specific data that will need to be shared between acquireBoard(…) and releaseBoard(…).

iBus* acquireBoard(tChar* brdLocation);
This function creates the iBus and maps the Base Address Ranges (BARs) for the selected PCI device into memory.  It can store system-specific data by using void *osSpecific in the iBus. This is a convenient way to share data with releaseBoard(…), such as OS specific structures, pointers, or file numbers.

On most systems, acquireBoard(…) will:

  1. Create a new iBus.
  2. Find the selected PCI device using the VXI-formatted resource string: “PXI<bus>::<device>::INSTR”.
  3. Store the PCI device's physical memory BARs in the new iBus.
  4. Map the PCI device's BARs into memory.
  5. Store the addresses of the memory mapped BARs in the iBus.

void releaseBoard(iBus* &bus);
This function deletes the iBus created by acquireBoard(…).

On most systems, releaseBoard(…) will:

  1. Unmap the PCI device's BARs.
  2. If necessary, tell the OS that the program is finished accessing the PCI device.
  3. Delete the iBus.

User-Defined iBus Functions for DMA Support

These are the two functions you will need to define to make the iBus support DMA on your target OS:

tDMAMemory* iBus::allocDMA (u32 size);
This function requests a block of DMA memory and maps it into memory. Typically, the tDMAMemory class is subclassed to hold OS-specific information so that data can be shared with freeDMA(…).

On most systems, allocDMA(…) will:

  1. Allocate memory from the kernel.
  2. Map the memory.
  3. Create, construct, and return a tDMAMemory object.

void iBus::freeDMA (tDMAMemory *mem);
This function deletes the tDMAMemory object created by allocDMA(…).

On most systems, freeDMA(…) will:

  1. Unmap the DMA memory.
  2. Free the memory from the kernel.

Predefined iBus Functions

These are the iBus functions used by Chip Objects and the RLP examples. They are already defined and should not need to be modified to port iBus to a new OS. This section is only an overview—the functions are located in osiBus.h and osiBus.cpp.

u32 iBus::get(u32 attribute, u32 occurrence);
get(…) allows a program to get attributes of the PCI device. In the RLP examples, the only attribute needed is the physical addresses of the BARs, which is used for initializing the MITE on some devices.

tAddressSpace iBus::createAddressSpace(tBusWindowType windowType);
createAddressSpace(…) returns an address space which can read and write to the PCI device.  createAddressSpace(…) can return an address space for any of the BARs which were initialized by acquireBoard(…).

void iBus::destroyAddressSpace(tAddressSpace &addressSpace);
destroyAddressSpace(…) releases the system resources retained by createAddressSpace(…), if any.

Accessing Registers Through a tAddressSpace

Use the tAddressSpace functions read8/16/32 and write8/16/32 to access PCI registers through an iBus.

write8( ), write16( ), write32( );
Write 8, 16, or 32 bits. This function is inlined and is usually compiled away to a pointer dereference.

read8( ), read16( ), read32( );
Read 8, 16, or 32 bits. This function is inlined and is usually compiled away to a pointer dereference.

This example is from the MITE initialization in the RLP examples:

iBus* bus = NULL;
tAddressSpace miteSpace;
u32 physicalBar1 = 0;
u32 address = 0xC0;
u32 value = 0;

bus = acquireBoard("PXI::4::2::INSTR");
miteSpace = bus->createAddressSpace(kPCI_BAR0);
physicalBar1 = bus->get(kBusAddressPhysical, kPCI_BAR1);

value = (physicalBar1 & 0xFFFFFF00L) | 0x80;
miteSpace.write32(address, value);


Why Use an iBus

  1. iBus is easy to port to a new OS.  There are only two system specific functions in the iBus that you need to port: acquireBoard(…) and releaseBoard(…). For DMA support, you need to port allocDMA(...) and freeDMA(...) as well.
  2. iBus works well with Chip Objects, and Chip Objects require an iBus to operate.
  3. Register reads and writes using a tAddressSpace and iBus are inlined on most compilers, which produces fast executing code.

Was this information helpful?