Embedded System Design in Device Driver Environment
Device Driver Development - Portability and Tools
- While application developers often have access to good software tools, the task of designing and implementing device drivers have continued to be time-consuming and prone to errors, largely due to a lack of adequate tools.
- Developing device drivers for a highly integrated microcontroller can be daunting, partly due to the sheer complexity of the device, but also due to some other difficulties.
- This article will give an overview of device driver design and traditional development techniques, and then discuss portability and the options available using modern tools, such as IAR MakeApp().
- Peripheral module device driversDevice drivers provide a software interface for accessing hardware from software.
- The application developer now needs a driver library that can be used by the application program to access hardware services in peripheral modules (UARTs, Timers, A/D or D/A converters, CAN or DMA controllers etc).
Peripheral module device drivers take care of:
- Initialization (e.g. setting up baud rate or timer periods)
- Run-time control (e.g. sending character strings or starting DMA transfers)
- Interrupt handling (responding to hardware events)
- Driver logic is normally implemented by modifying or testing special function register (SFR) control and status bits in a suitable order. A modern high-end microcontroller can have several thousand SFR bits, each of which must be carefully initialized and manipulated at run-time in proper sequence.
- Implementation of device drivers sometimes requires engineering tricks, such as using compiler-specific interrupt #pragmas, low-level hardware access from assembly language, advanced use of the linker command file and so forth. Unfortunately—but not surprisingly—device driver development is more inefficient and time-consuming compared to other software development.
- “Device drivers accommodate a very high information density and are dependent on many parts that can appear in a large number of combinations. This fact is the reason for the low productivity for device driver modelling, four times lower than ordinary software code.”
- Because of the close relationship to the hardware, traditional software tools have no or little support for device driver development. In fact, for the most part device drivers have been hand-crafted.
Lack of portability
- Most devices have differences in their programming model, and a device driver for one device is not likely to work with another device. Typical device differences that affect device driver implementation are:
- Feature set
- SFR control/status bit configuration
- Chip pin-out
- Interrupt structure
- Memory configuration
- As device drivers have a tight connection to the target device and the development environment, they are usually not portable. This reduces possibilities for code reuse and increase the cost, time, and efforts needed to migrate a software project from one microcontroller device to another.
Traditional development techniques
- Before writing a driver library, the hardware manuals must be studied in great detail, as both the chip internals and the electronic board design must be fully understood. This is time-consuming and usually considered to be a rather tedious task.
- When using a new microcontroller derivative that is similar to a familiar one, it might take just as long time to find out the difference between the two, as to understand a new architecture.
- Minor—but important changes can be well hidden as small-print footnotes, and it can require substantial efforts to make sure one is aware of, and can handle, any changes to the new device.
- Design and implementation requires not only programming experience but also expertise in hardware design and development tools.
- Coding is error-prone, as it can be very difficult to initialize and manipulate all configuration and status bits properly in the correct sequence. Debugging can prove to be particularly tricky as the drivers may not work without external hardware, and may need to conform to timing constraints or other resource limitations.
- They may need to run at full speed and can sometimes not be single-stepped in a debugger.
- In practice, a fairly complete hardware environment with a debug monitor or an emulator is needed, as are additional laboratory instruments such as an oscilloscope or a logic analyser. A debug simulator can seldom be used.
To summarize, hand-crafted device driver development requires the following tasks:
- Reading the hardware manual and learning the chip internals
- Understanding the electronic board design
- Designing the device driver library
- Implementing the device driver library
- Debugging the device driver library
- Test and integration.
- We are convinced that all of these items can be eliminated entirely or in part using new innovative development tools, thus decreasing time-to-market and development cost, while at the same time increasing the utilization of advanced chip features and portability.
- A typical example of increased utilization of microcontroller features is copying a memory block from one address to another. Although an unused DMA channel may be available in the processor, most programmers still write a software loop to copy bytes rather than using the faster hardware mechanism.
- By simplifying the use of peripheral modules like the DMA controller, more programmers will make better use of the hardware and deliver more competitive software solutions faster.
- Chip InitializationWhen a new electronic board is available, software must be written to handle system start-up. This is usually done by responding to a reset interrupt or
- jumping to a fixed start address. Basic initialization of stack pointer, compiler environment and bus controller settings are done during this phase. The
- CPU will most often not interface to external hardware unless some low-level configurations are made:
- Bus interface (address and data buses, chip-select signals).
- Memory configuration (DRAM refresh, wait-states, handshaking).
- Interrupt system (IRQs, interrupt priorities and masks)
- Once low-level configuration has been performed, execution normally continues in the application program’s main() function. At this point, the application logic and device drivers can start execution.