Guidelines for Developing a Nios II HAL Device Driver

ID 683146
Date 6/12/2015
Public
Document Table of Contents

1.7. Staging the HAL Device Driver Development

The following sections describe the existing my_uart_driver source code, particularly the device access descriptors used to manipulate the peripheral. my_uart_driver is based on the Altera Avalon UART device driver, with all of the names changed to represent the “my” flavored device, as an illustration of how you can incorporate your own device driver. All of the function and macro names (except for the *_INIT() and *_INSTANCE() macros) in the Altera Avalon UART device driver have had the “altera_avalon” portion of the name replaced with “my”. For example, ALTERA_AVALON_UART_STATUS_REG() has become MY_UART_STATUS_REG().

The two macros for _INSTANCE() and _INIT() are exceptions, because their names must match the hardware component name. As a result, the my_uart_driver device driver has definitions for ALTERA_AVALON_UART_INIT() and ALTERA_AVALON_UART_INSTANCE(). These _INIT() and _INSTANCE() macros must be defined in a header file that also matches the device name, which in this case is altera_avalon_uart.h. This restriction is necessary for the automatic construction of alt_sys_init.c, a generated C source file that handles of component instance initialization.

This example shows you how to write a software device driver that fits the HAL structure, either for manipulation of your own new device, or to override the functionality of the provided software device driver for an Altera component or other third party device.

The file bit_bang_uart.c demonstrates how to write source code. The source code development progresses toward a complete device driver. Source code development starts from direct access of the peripheral's registers and goes on to validating the proper functioning of the Altera_Avalon_UART hardware. bit_bang_uart.c is the first piece of software to communicate with the Altera Avalon UART hardware.

To develop the source code that accesses a new device, perform the following steps:

  1. Use IOWR() macros with hard-coded address values in main() to write values directly to the memory-mapped UART registers. IOWR() macros are the most direct way to access the UART hardware. Direct hardware access is useful for validating proper functioning of the component instance, while minimizing the potential for any software coding errors to interfere with hardware validation.

    For more information about HAL device driver access macros, refer to the "Accessing Hardware" section of the "Developing Device Drivers for the Hardware Abstraction Layer" chapter in the Nios II Software Developer's Handbook.

  2. After developing some direct peripheral manipulation code for your custom component, modeled after bit_bang_uart.c, write custom device access macros.
  3. Using the custom device access macros from the previous step, develop and test polled routines for the init(), read(), and write() functions.
  4. Write the ISRs for interrupt driven mode. An ISR is an interrupt-driven software routine, responding to a hardware interrupt that the peripheral generates when it requires servicing. An interrupt-driven device driver is much more efficient than a polled device driver, which wastes processor clock cycles by repeatedly querying the peripheral to determine if there is work to be performed. An ISR allows the Nios II processor to do other work while the peripheral is idle, or while it is operating autonomously and does not require servicing by the Nios II processor. Call alt_ic_isr_install() from main() to install the ISRs.
  5. After you have tested the ISR and polled routines from main(), create and test the INIT and INSTANCE macros. alt_sys_init.c invokes these initialization macros to initialize both the software device driver and the hardware driver. The INIT macro needs to initialize an alt_dev structure for the software device driver with the tested functions for reading and writing to the UART hardware device. The INSTANCE macro declares a structure for each component instance to hold component instance-specific information, such as the baud rate and the transmit and receive memory buffers. At this point, you move the alt_ic_isr_install() calls from function main() to the device’s initialization code, as described in the next section.

For more information about the alt_dev structure, refer to "Character-Mode Device Drivers" in the "Developing Device Drivers for the Hardware Abstraction Layer" chapter of the Nios II Software Developer's Handbook.