Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Embedded Systems

A BSP Porting Library for Embedded Peripherals


Jul00: A BSP Porting Library for Embedded Peripherals

Tom is an engineer for Motorola and can be contacted at [email protected] .mot.com.


Many real-time operating systems are designed to include a Board Support Package (BSP), which comprises interfaces known to the kernel so that the OS can access available system-level services. For example, BSPs might contain a driver for a serial device to support a console for the OS, and a timer driver to provide a system heartbeat. The whole idea behind BSPs is to isolate the RTOS from idiosyncrasies of the board on which it runs by abstracting device characteristics into a generic interface.

BSPs are provided by both OS and processor/board vendors. OS vendors generally supply a minimal BSP, primarily to get a system heartbeat and an interactive interface for downloading and debugging. Consequently, what you'll usually find in a BSP are drivers for a relatively high-resolution clock (as opposed to a time-of-day clock, functionality that the OS itself provides by dividing down the hi-res clock) and a serial connection.

When the processor folks provide a BSP, it is usually because they want to provide access to more hardware functionality (such as peripheral devices that are not ordinarily supported by an RTOS vendor) that could be useful to application developers -- pulse-width modulators, melody generators, interrupt controllers, virtual I/O ports, and even cache controllers. Basically, it's a way to make possibly arcane but interesting hardware elements usable by a larger population and hopefully reduce the amount of angst involved in programming and debugging those elements.

BSPs define the board-level interfaces from the top down, in that board designers must encapsulate device functionality inside a known set of routines. In this article, I'll describe an interface for one BSP that is built on top of the M-CORE Peripherals Library from Motorola (the company I work for). This BSP package operates from the bottom up, abstracting devices into classes that can then be used in standard BSP routines to streamline BSP porting from one board to another. I designed this particular BSP and wrote some of the code for it. BSPs like this one will eventually be available on the M-CORE Development Tools CD-ROM, which is bundled with M-CORE eval boards.

The M-CORE architecture (http://motorola .com/SPS/MCORE/) is a 32-bit RISC design targeted for embedded applications requiring reduced system power consumption. M-CORE-based microcontrollers are particularly suited to applications in battery-operated, portable products or highly integrated components functioning in extreme temperature environments. A wide array of peripheral devices can surround M-CORE-equipped microcontrollers, including timers, serial interfaces, A/D converters, network controllers, and even coprocessors -- all on a single piece of silicon.

Low-Level Device Drivers

Fundamental to the BSP porting library are low-level device drivers that underlie all BSP operations. In the M-CORE Peripherals Library, a device driver is an API that provides a particular level of accessibility or service to a device (for more information, see "Low-Level APIs for Embedded Systems," by Tom Cunningham and Chad Peckham, DDJ, March 1999). At the lowest level, the API maps directly to device register control, giving the programmer a symbolic interface to the peripheral hardware. At higher levels, a driver may support more sophisticated operations such as queuing, buffering, error handling, or other application-specific processing.

The M-CORE Peripherals Library takes a device-centric approach to driver design, rather than a host-centric model that depends on a specific protocol for device interaction. The library supports a uniform interface to a wide range of peripherals built around the M-CORE processor (see M-CORE MMC2001 Device Driver Reference Manual, MMC2001DDRM/D, Motorola, 1999). Devices supported include several flavors of asynchronous serial devices (UART, SCI), serial peripheral interfaces (SPI, ISPI, QSPI), timers, interrupt controllers, and keypads. The API definition can vary from device to device, but in general all drivers have entry points for initialization (DEVICE_Init), enabling and disabling the device (DEVICE_ControlOperation), and enabling and disabling of interrupts (DEVICE_ControlInterrupt). If the device performs I/O, there are transmit (DEVICE_Transmit) and receive (DEVICE_Receive) routines. In addition, devices are accessible at the register level using DEVICE_GetRegister and DEVICE_ SetRegister functions, or directly using pointer notation to a structure defined on top of the memory-mapped device register block.

RTOS Integration

Because the low-level drivers exist at a point just above the device registers, the uniform interface definitions dovetail nicely with the general device types supported by an RTOS. Operating systems have well-defined interfaces to categories of devices such as terminals, printers, and disks. Within these interfaces, it is the driver writer's responsibility to program the device to fulfill any needed function. The M-CORE device-driver API maps well to basic BSP functionality, so that little, if any, direct register access is required within the OS driver code.

For example, many operating systems have a POSIX-reminiscent set of kernel device entry points (init, open, close, read, write, and control). These entries may vary from OS to OS, with slightly different naming conventions and parameter lists. Low-level driver functions correspond closely to the OS entries, making it easier and quicker to integrate a new board with the operating system; see Figure 1.

Some operating systems abstract away interactions with certain types of devices, distilling the interface to a set of minimal functions or callbacks. Wind River's SIO interface for VxWorks illustrates this capability. The SIO driver implements a registration function for higher level software to provide function pointers for rudimentary serial operations such as reading or writing a character. The device interrupt service routine calls these functions as characters are received by the serial device, or as room becomes available in the transmit FIFO. The Integrated Systems DISI interface for PSOS works in a similar manner. Low-level drivers are fully functional in this environment because the operational granularity is fine enough to accommodate the most basic I/O services.

Despite the adaptability of low-level drivers to varied and possibly austere BSP requirements, there remains a dependency between the device and associated driver routines. The low-level API reflects the characteristics of the device to which it is targeted, and this means that BSP glue code between the OS and the drivers must change when the board or underlying devices change. The BSP porting library addresses this issue by raising the abstraction bar one more level.

Consider an analogous situation at the RTOS application level. The Motorola Interface to Real-Time Operating Systems (MIRTOS) is a specification for a vendor-neutral RTOS API used by many of the equipment groups within Motorola. It specifies fundamental objects such as tasks, mailboxes, queues, and semaphores, and implements these on top of a variety of commercial operating systems. One goal of MIRTOS is to codify the interface elements in terms of macros or other zero-overhead mechanisms to reduce run-time latencies. In this way, you can write application code to the MIRTOS specification and benchmark it against any operating system for which a MIRTOS interface has been defined. The BSP porting library works similarly by abstracting device types so that BSP code written for the porting library does not require modification for subsequent boards and devices.

Abstract Device Types

Just as MIRTOS maps a variety of RTOS system calls into a single generic function, the BSP porting library relies on device abstraction to obtain generality and portability. Among the myriad Motorola peripherals there are broad device classes to which any particular peripheral can be assigned. For example, the MMC2001 UART and the MMC2080 SCI would fit into the category of asynchronous serial devices. The MMC2001 ISPI, MMC2080 SPI, and MMC2103 QSPI belong in the category of synchronous serial devices. This categorization is applicable to many other types of devices such as timers, interrupt controllers, and even bus interfaces; see Figure 2.

The BSP porting library goes a step further in device abstraction for certain classes of peripherals. RTOS integration does not put undue demands on particular device interfaces; that is, operating systems tend not to exercise idiosyncratic characteristics of a peripheral. To leverage this tendency, synchronous and asynchronous serial devices can be represented by a single serial API. Similarly, periodic devices such as periodic interval timers and pulse-width modulators can be rolled into one API group.

Another aspect of the BSP library beyond porting and maintenance issues concerns broadening the scope and availability of board-level devices within the RTOS arena. BSPs have traditionally only supported the minimal hardware needed to get the operating system running on a target board. But many available controllers have an array of programmable peripherals on chip, and board-resident devices can extend and enhance controller functionality. If the interface to various devices is abstracted enough to provide a straightforward avenue to RTOS integration, then the BSP can be extended to support access to these devices by application programs.

One instance of an abstraction suitable for BSP extension is the handling of general-purpose I/O (GPIO) pins. In hardware, these pins can either be controlled from a central module or included as part of another functional unit. A useful abstraction for GPIO is David B. Stewart's notion of logical ports (see "An I/O Device Driver Model and Framework for Embedded Systems," by David Stewart, Proceedings of the Workshop on Middleware for Distributed Real-Time Systems, December 1997).

Using this paradigm, the hardware association of the various pins is hidden by the BSP driver, and pins can be grouped and accessed in arbitrary ways via the library interface. As a representative case, if an application wanted to use the MMC2001 UART control and data pins in conjunction with the ISPI output pin, these pins could be logically combined and manipulated as a virtual output device through the BSP driver.

BSP Porting Library

The BSP porting library is implemented as a set of C++ classes that correspond to the classes and categories described in Table 1. For a given device abstraction, there is an interface definition that maps to a specific device class. The device class then maps to a low-level driver. The interface definition consists of virtual functions that are overridden within the device class. These virtual functions are invoked inside BSP routines to manipulate a class of device. When a driver is instantiated for a particular device, the specific device routines are called through the virtual interface.

The Serial virtual base class (available electronically; see "Resource Center," page 5) illustrates the combining of disparate devices into a uniform driver interface. The Serial.Init() virtual function takes the fundamental and usual parameters to initialize a serial device. For a synchronous device, baudRate may be interpreted as a divide ratio and dataBits as the streaming packet size. Other device settings such as FIFO thresholds, flow control, clock sources, and bit polarity are defaulted in the Init() routine, although they can be controlled through the Serial.Config() member function. The role parameter of Init() is used to specify a master or slave function if the serial device is synchronous. Serial.ControlOperation() handles enabling or disabling the device and its submodules. The submodule parameters are ignored if they are not relevant for the device. Serial.ControlInterrupt() enables or disables individual interrupt sources on the device. Serial.Transmit() and Serial .Receive() send and receive data and return status information (parity error, break detect, or FIFO overflow). With synchronous devices, Transmit() simply loads data values into the device-data register or queue, and the master initiates the transfer with Serial.ControlOperation(enable). After a synchronous transfer, Receive() unloads data values from the data register or queue.

The Periodic virtual base class can support counters, timers, and pulse-width modulators. The Periodic.Init() member function takes an input clock divider ratio, counter modulus, and pulse period if applicable. Periodic.ControlOperation() enables or disables the device. PeriodicControlInterrupt() enables or disables periodic interrupts. The Periodic.Config() virtual function is used to set specific timer or counter characteristics such as bit polarity, counter reset, or doze mode operation for the device; see Table 2.

The Interrupt class covers interrupt controller functionality. The Interrupt .Init() function is used to set the processor vector base register and establish a vector table if required. The Interrupt.ControlInterrupt() member function takes a source mask and mode to enable or disable several interrupt sources at once. Interrupt.Config() may be used to set interrupt priorities or fast/normal attributes.

The Port class is a realization of the logical ports discussed earlier. Port.Init() accepts a port definition structure that provides addresses, masks, and data direction for the pins comprising the logical device. Port.Read() reads from an input port; Port.Write() writes to an output port. The Port.Config() member allows for port configuration such as changing port direction or modifying the port mask.

Table 3 shows a sample mapping between the interface levels of the BSP porting library. There are often 1:1 linkages between virtual function, device class function, and low-level driver for a given device. In some cases, however, the driver class must resort to a combination of driver functions, register access routines, or pointer notations to preserve a uniform device abstraction. Also, whereas all of the BSP library functions support a Config() member function, configuration capability need not be all-inclusive as low-level driver routines can and should be called to support very special-purpose device setup and control.

Performance Issues

In embedded environments, performance and low overhead are overriding concerns. It may seem puzzling, therefore, to introduce C++ and its perceived overheads into the domain of device drivers where performance is paramount. The design of the BSP library has been careful to avoid features of the language that can lead to slow code or code bloat. But the cost of retaining the virtual interface was worth the price given the design goals for the library. Rather than rolling our own flavor of function indirection, we were willing to rely on compiler technology to give us the best vtable implementation.

You could argue that library interface definitions could be written at the device- class level, removing the vtable and one layer of procedure call overhead. However, this scenario would not work for systems with more than one device of a given type. For example, a board with two different types of UART would have a conflict using a generic device-class interface and direct object file linkage. The C++ vtable alleviates this problem.

C Language Interfacing

Many available RTOSs do not support a C++ interface, at least not at the system level where device drivers reside. The BSP porting library has a C language interface that maps the abstract member functions onto C language equivalents. An accessor function allocates and returns a handle to the class instance, which is then used in subsequent calls. Of course, the C language interface introduces another level of procedure call, which is not free. It may be worth investigating and exploiting processor ABI characteristics to provide the C interface without the accompanying call overhead.

Conclusion

The BSP porting library seeks to reduce or eliminate maintenance of BSP glue code between an RTOS kernel and low-level device drivers. It does this by abstracting classes of devices and codifying these abstractions as C++ virtual base classes. BSP interface logic can then be written in terms of these virtual base classes, with transparent access to the underlying low-level drivers. An added benefit to the BSP porting library is the potential for greater board and chip-level device accessibility through the operating system. If device interfaces are relatively straightforward and uniform, a BSP writer can readily adapt the device to the I/O subsystem of an RTOS, making more board and device functionality available to you.

DDJ


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.