HOME :: JOB LISTINGS :: DEMOS :: ARCHIVES :: MEDIA KIT :: SUBSCRIBE


SPONSORED WHITE PAPER

Delivering a Consistent Software Development Environment on a Changing Processor Platform
by Stefano Zammattio

Introduction

Altera has developed a configurable 32-bit soft processor core, called Nios®II that has been designed for implementation in FPGA. Many features of the processor core can be configured to allow developers to trade off processor size and performance to match their application requirements. To enhance productivity the processor configuration is done using tool called SOPC builder. This tool also allows the developer to select and integrate processor peripherals from a library of components supplied by Altera, a third party IP provider or IP modules that the developer has integrated into the SOPC builder library. The properties of these components, including where they reside in the processor memory map and the interrupt used, can be easily changed using the SOPC Builder graphical user interface (GUI). As the tool is GUI driven it is very easy and quick to generate custom processor based systems. Once built systems can be edited at any time using the same graphical interface, making it easy for the hardware developer to modify and adapt the system until the required target for performance, size and features are met.

In order to accelerate the development process many developers choose to begin software development as early as possible. With a fixed processor the software environment is rigidly defined and the software engineer can plan and implement large parts of the code before the hardware is ready. If an off-the-shelf development board similar to the system being developed is available this can be used to partially test the functionality of the code; alternatively developers can create simulation modules and test on a model of the system being designed. This can save much development time but it can also result in design errors that only emerge when the software is run for the first time on the real system; sometimes these issues can be very difficult to fix as the developers are usually debugging the hardware and software simultaneously.

In a FPGA based system the hardware is flexible and it is more likely that an off-the shelf board can adapted and used to test parts of the system in real time. The flexibility of the hardware and the processor is a major benefit to the hardware developer as the system can be quickly re-designed at any time to address problems, performance issues or last minute additions to the specification.

Software issues

This ability to suddenly make changes to the hardware can create a major problem for the software developer. If the hardware designer does not communicate every change accurately this may cause the software to fail and the software developer to spend many frustrating hours identifying the cause. Also, if the software developer has not abstracted the hardware implementation in the application code, every hardware change has to be tediously implemented in every relevant part of the code. Hardware parameters that can vary include:

Memory map - location of peripherals and types of memory
Peripheral register functionality – modification to change peripheral functionality
Interrupt level used by peripheral
Addition of new or multiple peripherals to the system
Addition of processor cache or changes to cache features
Addition/modification of custom instructions

In order to avoid these issues and enhance productivity Altera has created a development environment that incorporates an automated build system and a defined infrastructure for the management of peripheral device drivers. The Nios ® II software development environment is based on the Eclipse project IDE; this IDE drives a set of GNU based tools that do the actual compiling, linking and debugging. A set of tools that run under Cygwin (1) and the Eclipse IDE plug-in capability have been used to create the automated build environment and enable the IDE to run under both Microsoft Windows and Linux.

The Nios II Software Development Environment

The Nios II IDE offers all of the features expected of a professional software project development environment (project manager, code editor, debugger, etc.) but it also offers additional features to enhance productivity with FPGA based system development. These include:

Import of all relevant hardware parameters into the software environment
Generation of an ANSI ‘C’ library that is customised to the system hardware
Automatic inclusion and configuration of peripheral device drivers
ANSI ‘C’ and UNIX style function support for standard device classes
• A pre-defined framework for creating peripheral drivers that are abstracted from hardware dependencies

When the hardware system is created the SOPC builder tool records all of the system parameters in a plain text format file (*.ptf); this file contains information on the configuration of each individual component and how it has been connected within the system. When the software developer creates a new project the IDE requests the location of the system .ptf file. The information in this file is then used to create a software development environment that is tailored to match the processor and system configuration.

A software header file called system.h is created by parsing the .ptf file. System.h contains all the relevant hardware information for the processor, peripherals and system configuration in the form of clearly specified #define statements for each part of the system, for example #defines are created for the base address of each peripheral component, the location of different types of memory, etc. This means that system.h can be used throughout the software system to abstract all of the hardware details away from the application developer. For example the IDE builds an ANSI ‘C’ library, based on the newlib source code (2), that has been tailored to match the processor configuration; this is very important for features like system startup, interrupt handlers and cache management code.

Figure 1. Overview of the functionality of the Nios II IDE

In order to clearly delineate the application and hardware specific system code the Nios II IDE creates two sub-projects, an application project and a system library project. The application project is used by the developer to add and manage the files used to create the application. The system library contains all of the hardware specific functions to abstract away all of the hardware details from the developer, hence this is known as the Hardware Abstraction Layer (HAL) system library. This library provides a consistent, hosted C/C++ runtime environment, regardless of the underlying hardware features of the system and cleanly separates the application code from the hardware dependent code.

With a complete definition of the hardware, the IDE is able to allow the developer to easily choose from the available memory resources to locate the stack, heap, and memory sections generated by the compiler, as shown in Figure 2. The developer can also select which timers to use for the system timer functions and which peripherals to use for stdin, stdout and stderr. The code used to drive these peripherals uses the system.h file and the HAL system library drivers to ensure correct functionality regardless of hardware settings. The IDE also creates a project make file, linker script and include path that will build the ‘C’ library, application code and drivers for every peripheral included in the hardware system. Source code for peripheral drivers is automatically included within each SOPC Builder component and can be accessed directly by the Nios II IDE by the inclusion of the component directory paths into the build system.

Figure 2. Setting the HAL system library properties.

System.h

The system.h file contains #defines that pass all of the processor, peripheral and system configuration parameters to the software environment. This means that the HAL system library, or any code written by the developer, can create processor or system configuration specific software without adding hard constants to the code. Information on e ach individual peripheral in system.h includes #defines for:

• A symbolic name for the peripheral (allocated by system designer in SOPC Builder)
• A generic type (or Class) name for the peripheral (defined by peripheral designer)
• The hardware configuration of the peripheral (depends of peripheral type)
• The peripheral base address (allocated by system designer)
• The IRQ priority (if any, allocated by system designer)

/*
* uart_0 configuration
*
*/

#define UART_0_NAME "/dev/uart_0"
#define UART_0_TYPE "altera_avalon_uart"
#define UART_0_BASE 0x00100020
#define UART_0_SPAN 32
#define UART_0_IRQ 1
#define UART_0_BAUD 115200
#define UART_0_DATA_BITS 8
#define UART_0_FIXED_BAUD 1
#define UART_0_PARITY 'N'
#define UART_0_STOP_BITS 1
#define UART_0_USE_CTS_RTS 0
#define UART_0_USE_EOP_REGISTER 0
#define UART_0_SIM_TRUE_BAUD 0
#define UART_0_SIM_CHAR_STREAM ""
#define UART_0_FREQ 50000000

/*
* processor configuration
*
*/

#define NIOS2_CPU_IMPLEMENTATION "fast"

#define NIOS2_ICACHE_SIZE 4096
#define NIOS2_DCACHE_SIZE 2048
#define NIOS2_ICACHE_LINE_SIZE 32
#define NIOS2_ICACHE_LINE_SIZE_LOG2 5
#define NIOS2_DCACHE_LINE_SIZE 4
#define NIOS2_DCACHE_LINE_SIZE_LOG2 2
#define NIOS2_FLUSHDA_SUPPORTED
#define NIOS2_EXCEPTION_ADDR 0x00000020
#define NIOS2_RESET_ADDR 0x00000000
#define NIOS2_HAS_DEBUG_STUB

#define NIOS2_CPU_ID_SIZE 1
#define NIOS2_CPU_ID_VALUE 0

Figure 3. Example of #defines for a peripheral and processor in system.h

Using system.h to create the peripheral drivers and system library code means that a custom set of drivers and ‘C’ libraries can be generated every time the system configuration changes. All changes made by the hardware engineer are contained in the .ptf and therefore propagate automatically into system.h and the HAL system library.

The Hardware Abstraction Layer (HAL)

The HAL system library is a lightweight runtime environment that provides a simple device driver interface for programs to communicate with the underlying hardware. The HAL application program interface (API) is integrated with the ANSI C standard library and allows access to peripheral devices and files using familiar ‘C’ library functions, such as printf(), fopen(), fwrite(), etc. The HAL device driver implementation also provides a consistent interface to the peripherals in the system and a clear distinction between application and peripheral device driver software. This promotes reusable application code that is resistant to changes in the underlying hardware and provides a framework for new peripheral drivers so that they are consistent with existing drivers.

Figure 4. The HAL system library abstracts hardware from the application developer

The HAL system library provides the following services:

Integration with the newlib ANSI C standard library - provides the familiar C standard library functions
Peripheral device drivers - provides access to each peripheral device in the system
The HAL AP I - provides a consistent, standard UNIX style interface to HAL services, such as peripheral device access, interrupt handling, and alarm facilities
System initialization - performs initialization tasks for the processor and the runtime environment before main()
Device initialization - instantiates and initializes each device in the system before main()

The HAL API (UNIX-style) functions can ease the task of porting existing code to run under the HAL environment. The HAL primarily uses these functions to provide the system interface for the ANSI ‘C’ standard library, for example, the ‘C’ functions defined in stdio.h that require peripheral device access.

  • _exit()
  • close()
  • fstat()
  • getpid()
  • gettimeofday()
  • ioctl()
  • isatty()
  • kill()
  • lseek()
  • open()
  • read()
  • sbrk()
  • settimeofday()
  • stat()
  • usleep()
  • wait()
  • write()

Figure 5. List of the HAL API UNIX style functions

Applications can interact with system hardware either through the ‘C’ standard library, or through the HAL system library API. The system.h file contains a list of the peripherals instantiated in the hardware and this is used by the build system to include all of the relevant peripheral drivers into the make file, linker script and include paths. Each instance of a peripheral is referenced by its symbolic name derived from system.h (and the SOPC Builder hardware design). This means that for multiple instances of a peripheral, the same driver function calls are used; the application code simply calls the relevant ANSI C or HAL API function and passes the symbolic name of the individual peripheral. The system library HAL peripheral device driver code can then use the symbolic name to reference the correct hardware information.

The HAL provides generic device models for classes of peripherals commonly found in embedded systems, such as timers, Ethernet MAC/PHY chips, and character based I/O peripherals. These generic device models allow the developer to write programs using familiar functions instead of having to learn a custom set of calls for every peripheral; this reduces the application developer’s learning curve and enhances productivity.

The HAL provides models for the following classes of devices:

Character-mode device s - hardware peripherals that send and/or receive characters, for example a UART.
Timer devices - hardware peripherals that count clock ticks and can generate periodic interrupt requests, these are used to deliver system and timestamp timer functionality support within the HAL API.
File subsystems - provide a mechanism for accessing files stored within physical device(s). Depending on the internal implementation, the file subsystem driver may access the underlying device(s) directly or use a separate device driver. For example, a flash file subsystem driver can access flash using the HAL API for flash memory devices
Ethernet devices - provide access to an Ethernet connection for the lightweight IP protocol stack
DMA devices - peripherals that perform bulk data transactions from a data source to a destination. Sources and destinations can be memory or another device, such as an Ethernet connection
Flash memory devices – non-volatile memory devices that use a special programming protocol to store data

The HAL system library defines a set of functions that are used to initialize and access each class of device; this means that application developers do not have to use low-level routines to initialise and communicate with the hardware for these classes of peripherals. For more information on specific classes please see the Nios II Software Development handbook (3).

Developing a HAL Device Driver

Peripheral device driver code can be added to the project in many ways (for example explicitly adding source files to the HAL system library project) but the HAL system library offers the opportunity to utilise the automatic integration of software files into the HAL library, the abstraction and maintenance of hardware parameters and the abstraction of peripheral driver functions using ANSI ‘C’ or HAL API function calls. Driver developers can leverage some or all of these features as required by their project.

To develop a HAL driver that abstracts functions to ANSI ‘C’ or HAL API function calls the developer needs to select the appropriate driver class, provide the set of functions required and integrate them into the HAL system library. New HAL driver functions and their parameters are required to follow the conventions and infrastructure defined by the HAL system library. This means that the driver development task is pre-defined and well documented; also the resulting driver functions follow familiar conventions and do not require the application developer to worry about hardware specific details.

When creating a new system peripheral component in SOPC Builder the hardware developer imports the HDL source into the SOPC Builder Component library. In order to add software support a set of files can also be integrated into the SOPC builder component; these files are imported into a component directory and grouped into sub directories according to their functionality. This directory structure is leveraged by the IDE build system when it creates the include paths for the system project.

Figure 6. Importing peripheral driver software files into a SOPC Builder Component

Hardware Components integrated into the SOPC Builder library have the following directories:

<Component Type>\HDL - imported HDL files for the component
<Component Type>\inc - header file(s) that defines the components hardware interfaces
<Component Type>\HAL\inc - header file(s) that define the peripheral device driver functions
<Component Type>\HAL\src - driver source code and make file fragment to build the driver.

Driver code in these directories will be automatically integrated into the system library build path and can be used directly by the application code when the header files that define the device driver functions (HAL/inc/*.h) have been #included in the application code. This feature can be used to support source code file integration for driver types that do not map to a supported HAL class.

For full integration with the HAL it is required that a header file exists in the <Component Type>\inc directory; for clarity this file can be named in using this name convention <Component_Type> _regs.h , it should define:

register access macros that provide a read and/or write for each register within the component,
register address accessor macros that return the physical address for each register within a component.
• Bit-field masks and offsets that provide access to individual bit-fields within a register.

When writing the device driver functions only the macros defined in the * _regs.hfile should be used to pass the register addresses to functions and to access registers themselves. Hard-coded constants should be avoided because they make the software susceptible to changes in the underlying hardware. Use of these conventions ensures that the software will always use the correct values found in system.h (as these are derived directly from the hardware built using SOPC Builder).

When the IDE creates the software project it creates a function called alt_sys_init() that is used to initialise all HAL based devices before main(). The IDE build system will add the HAL initialisation macros for every instance of each HAL based peripheral into the alt_sys_init() function. If this automatic initialisation of the peripheral driver is to be added to a new HAL driver the initialisation macros should be provided in a header file in the <Component_Type>\HAL\inc directory. The HAL convention is to give the file the name <Component_Type>.h. The definition of the initialisation macros should follow this format:

<Component_Type>_INSTANCE (Component_name, Component_name)
<Component_Type>_INIT(Component_name, Component_name)

The <Component_Type>_INSTANCE macro performs any per-device static memory allocation required by the driver. Each HAL class of device driver requires a structure containing hardware implementation details and pointers to driver functions that are abstracted by the ANSI ‘C’ and HAL API standard functions. When compiled this macro is combined with the hardware information from system.h to create the HAL resources required to support this instance of the device.

The <Component_Type>_INIT macro performs runtime initialization of the device. When the IDE creates the software project it creates a function called alt_sys_init() that is used to initialise all HAL based devices before main(). When a peripheral is integrated with the HAL the build system will add the initialisation macro for every instance of each peripheral into the alt_sys_init() function.

Both macros take the capitalized and lower case symbolic name of the device instance as input arguments; this name is that given to the instance of the component in SOPC Builder at system generation time. This is an example of how the HAL drivers abstract the hardware information and use the information in the system.h file. With this infrastructure in place the HAL system library automatically instantiates and initialises the device driver before calling main().

ALTERA_AVALON_JTAG_UART_INSTANCE( JTAG_UART_0, jtag_uart_0 );
ALTERA_AVALON_UART_INSTANCE( UART_0, uart_0 );

/*
* Initialise the devices
*
*/

void alt_sys_init( void )
{
______ALTERA_AVALON_JTAG_UART_INIT( JTAG_UART_0, jtag_uart_0 );
______ALTERA_AVALON_UART_INIT( UART_0, uart_0 );
}

Figure 7. Code fragment from alt_sys_init.c showing two drivers being initialised.

Source code for peripheral driver functions are placed in the <Component Type>\HAL\src directory. In addition a make file fragment, component.mk , is required to list the source files and their build settings that should be included in the system library build. The Nios II (4) (5) IDE automatically includes the component.mk file into the top-level make file when compiling system library projects and application projects.

Following these simple rules allows peripheral drivers to be fully integrated into the HAL API and benefit from automatic initialisation, integration and abstraction from hardware settings.

All SOPC Builder (6) components can provide a HAL device driver, however, if the driver supplied with a component is inappropriate for the application it can be over-ridden by supplying another instance in the system library project directory of the Nios II IDE. The Nios II IDE always searches the system library project directory first so any files provided in this folder get built into the project instead of the versions supplied by default.

If the peripheral does match one of the HAL generic device model classes the device driver interface will be specific to the hardware and separate from the HAL API. Incorporating the driver source files into the correct directories will include the files into the automated build system of the IDE but application developers will be required to explicitly initialise peripherals and include driver source headers ( \HAL\inc) manually into the project.

Verification of hardware system information

In order to work with an FPGA based platform the software developer needs a .sof file, that is used to configure the FPGA device with the processor system, and a matching .ptf file. However, if the .sof and .ptf do not match the software support package and the hardware will not match and there are likely to be issues with running the software. In order to avoid this situation Altera provide a System ID peripheral.

The system ID peripheral is a simple read-only device that provides SOPC Builder systems with a unique identifier. The peripheral has two registers that contain values that are set when the hardware is built:

ID - a unique 32-bit value that is based on the contents of the system, any system change will alter this number.
Timestamp - a unique 32-bit value that is based on the system generation time.

Before downloading a program to run or debug or after a reset, the Nios II IDE reads and compares the ID and Timestamp values from the system.h file and the actual hardware image configured in the FPGA by the .sof. If the values do not match the IDE reports the error to the user before taking any further action; it is still possible to run the code on the hardware but it may not function correctly. This ensures that the hardware and software support package remain in synch and that the developer is aware if they are not.

Dealing with custom instructions

Custom instructions can be used to greatly accelerate the application specific performance of the processor and with SOPC builder it is very easy to add these to the Nios II processor. Without a major re-design of the compiler it is not possible to make the compiler add custom instructions to the code; however dealing with in-line assembly and calling custom instructions by opcode is not ideal.

In order to make the custom instructions more accessible and to allow the syntax to communicate more meaning system.h contains macro definitions that abstract the instruction opcode and call the custom instruction. This makes use of the custom instruction call as easy as calling an ordinary ‘C’ function and allows the use of the symbolic instruction name instead of the opcode number. This makes it easier for software developers to use and understand the custom instruction and avoid changing the code if the hardware developer changes the opcode used by the custom instruction.

/*
* custom instruction macros
*
*/

#define ALT_CI_CRC_N 0x00000000
#define ALT_CI_CRC(A,B) __builtin_custom_inii(ALT_CI_CRC_N,(A),(B))
#define ALT_CI_BSWAP_N 0x00000001
#define ALT_CI_BSWAP(A) __builtin_custom_ini(ALT_CI_BSWAP_N,(A))

Figure 8. Example #defines to support custom instructions

Conclusion

With increasing pressure on developers to deliver in shorter timescales FPGA devices can offer the hardware developer a way to deliver optimised embedded systems in less time, however a rapidly evolving hardware platform can create development issues for the software developer. By enabling the communication of hardware information into the software environment and creating a toolset that automatically creates a custom system support package of drivers and ‘C’ libraries that are abstracted from the hardware by familiar API calls and symbolic names, the Nios II IDE eliminates these issues and promotes rapid software development.

by Stefano Zammattio, Senior Product Marketing Engineer, Altera Europe

March 30, 2006

About the author: Stefano J. Zammattio is a member of the Technical Product Marketing team at Altera. Stefano is responsible for embedded products in the European region, and his focus is on Altera's Nios II processor and SOPC builder products. Stefano has been has been involved in the computing and electronics industry since 1987 and holds a Bachelor of Science in Physics and a Masters of Science in Medical Electronics.

References

1. Cygwin
http://www.cygwin.com

2. Newlib ‘C’ library
http://sources.redhat.com/ml/newlib/

3. Nios II Software Development handbook
http://www.altera.com/literature/lit-nio2.jsp

4. Nios II Embedded Processors
http://www.altera.com/products/ip/processors/nios2/ni2-index.html

5. Nios II integrated development environment (IDE)
http://www.altera.com/products/software/products/nios2/emb-nios2_ide.html

6. SOPC Builder
http://www.altera.com/products/software/products/sopc/sop-index.html

ALTERA CORPORATION
101 Innovation Drive
San Jose , CA 95134
(408) 544-7000
http://www.altera.com

 

Copyright © 2006 Altera Corporation. All rights reserved. Altera, The Programmable Solutions Company, the stylized Altera logo, specific device designations, and all other words and logos that are identified as trademarks and/or service marks are, unless noted otherwise, the trademarks and service marks of Altera Corporation in the U.S. and other countries. All other product or service names are the property of their respective holders. Altera products are protected under numerous U.S. and foreign patents and pending applications, maskwork rights, and copyrights. Altera warrants performance of its semiconductor products to current specifications in accordance with Altera's standard warranty, but reserves the right to make changes to any products and services at any time without notice. Altera assumes no responsibility or liability arising out of the application or use of any information, product, or service described herein except as expressly agreed to in writing by Altera Corporation. Altera customers are advised to obtain the latest version of device specifications before relying on any published information and before placing orders for products or services.

[back to top]

Comments on this article? Send them to comments@fpgajournal.com

 
All material on this site copyright © 2006 techfocus media, inc. All rights reserved.
FPGA and Structured ASIC Journal
Privacy Statement