NuttX RTOS Porting Guide

Last Updated: March 3, 2011



Table of Contents

1.0 Introduction

Overview This document provides and overview of the NuttX build and configuration logic and provides hints for the incorporation of new processor/board architectures into the build.

See also arch/README.txt and configs/README.txt.

2.0 Directory Structure

Directory Structure. The general directly layout for NuttX is very similar to the directory structure of the Linux kernel -- at least at the most superficial layers. At the top level is the main makefile and a series of sub-directories identified below and discussed in the following paragraphs:

Configuration Files. The NuttX configuration consists of:

2.1 Documentation

General documentation for the NuttX OS resides in this directory.

2.2 arch

2.2.1 Subdirectory Structure

This directory contains several sub-directories, each containing architecture-specific logic. The task of porting NuttX to a new processor consists of add a new subdirectory under arch/ containing logic specific to the new architecture. The complete board port in is defined by the architecture-specific code in this directory (plus the board-specific configurations in the config/ subdirectory). Each architecture must provide a subdirectory, <arch-name> under arch/ with the following characteristics:

2.2.2 Summary of Files

2.2.3 Supported Architectures

Architecture- and Chip-Specific Directories. All processor architecture-specific directories are maintained in sub-directories of the arch/ directory. Different chips or SoC's may implement the same processor core. Chip-specific logic can be found in sub-directories under the architecture directory. Current architecture/chip directories are summarized below:

Deprecated Architecture Directories. The following architecture directories are deprecated. They have been replaced by the logic in arm/arm and will deleted when arch/arm is fully verified.

Other ports for the for the TI TMS320DM270 and for MIPS are in various states of progress

2.3 binfmt

The binfmt/ subdirectory contains logic for loading binaries in the file system into memory in a form that can be used to execute them.

2.4 configs

The configs/ subdirectory contains configuration data for each board. These board-specific configurations plus the architecture-specific configurations in the arch/ subdirectory complete define a customized port of NuttX.

2.3.1 Subdirectory Structure

The configs directory contains board specific configuration files. Each board must provide a subdirectory <board-name> under configs/ with the following characteristics:

2.3.2 Summary of Files

2.3.2.1 Board Specific Logic

2.3.2.2 Board Specific Configuration Sub-Directories

The configs/<board-name>/ sub-directory holds all of the files that are necessary to configure NuttX for the particular board. A board may have various different configurations using the common source files. Each board configuration is described by three files: Make.defs, defconfig, and setenv.sh. Typically, each set of configuration files is retained in a separate configuration sub-directory (<config1-dir>, <config2-dir>, .. in the above diagram). The procedure for configuring NuttX is described below, This paragraph will describe the contents of these configuration files.

2.3.3 Supported Boards

All of the specific boards supported by NuttX are identified below. These are the specific <board-name>'s that may be used to configure NuttX as described below.

* A customized version of the buildroot is available to build these toolchains under Linux or Cygwin.

2.5 drivers

This directory holds architecture-independent device drivers.

2.6 examples

Example and test programs to build against.

2.7 fs

This directory contains the NuttX file system. This file system is described below.

2.8 graphics

This directory contains files for graphics/video support under NuttX.

2.9 include

This directory holds NuttX header files. Standard header files file retained in can be included in the normal fashion:

Directory structure:

2.10 lib

This directory holds a collection of standard libc-like functions with custom interfaces into NuttX.

2.11 libxx

This directory holds a tiny, minimal standard std C++ that can be used to build some, simple C++ applications in NuttX.

2.12 mm

This is the NuttX memory manager.

2.13 net

This directory contains the implementation of the socket APIs. The subdirectory, uip contains the uIP port.

2.14 netutils

This directory contains most of the network applications. Some of these are original with NuttX (like tftpc and dhcpd) and others were leveraged from the uIP-1.0 apps directory. As the uIP apps/README says, these applications "are not all heavily tested."

2.15 sched

The files forming core of the NuttX RTOS reside here.

2.16 tools

This directory holds a collection of tools and scripts to simplify configuring, building and maintaining NuttX.

2.17 Makefile

The top-level Makefile in the ${TOPDIR} directory contains all of the top-level control logic to build NuttX. Use of this Makefile to build NuttX is described below.

3.0 Configuring and Building

3.1 Configuring NuttX

Manual Configuration. Configuring NuttX requires only copying the board-specific configuration files into the top level directory which appears in the make files as the make variable, ${TOPDIR}. This could be done manually as follows:

Where <board-name> is the name of one of the sub-directories of the NuttX configs/ directory. This sub-directory name corresponds to one of the supported boards identified above. And <config-dir> is the optional, specific configuration directory for the board.

Automated Configuration. There is a script that automates these steps. The following steps will accomplish the same configuration:

Additional Configuration Steps. The remainder of configuration steps will be performed by ${TOPDIR}/Makefile the first time the system is built as described below.

3.2 Building NuttX

Building NuttX. Once NuttX has been configured as described above, it may be built as follows:

The ${TOPDIR} directory holds:

That directory also holds:

The setenv.sh contains Linux/Cygwin environmental settings that are needed for the build. The specific environmental definitions are unique for each board but should include, as a minimum, updates to the PATH variable to include the full path to the architecture-specific toolchain identified in Make.defs. The setenv.sh only needs to be source'ed at the beginning of a session. The system can be re-made subsequently by just typing make.

First Time Make. Additional configuration actions will be taken the first time that system is built. These additional steps include:

4.0 Architecture APIs

The file include/nuttx/arch.h identifies by prototype all of the APIs that must be provided by the architecture specific logic. The internal OS APIs that architecture-specific logic must interface with also also identified in include/nuttx/arch.h or in other header files.

4.1 APIs Exported by Architecture-Specific Logic to NuttX

4.1.1 up_initialize()

Prototype: void up_initialize(void);

Description. up_initialize() will be called once during OS initialization after the basic OS services have been initialized. The architecture specific details of initializing the OS will be handled here. Such things as setting up interrupt service routines, starting the clock, and registering device drivers are some of the things that are different for each processor and hardware platform.

up_initialize() is called after the OS initialized but before the init process has been started and before the libraries have been initialized. OS services and driver services are available.

4.1.2 up_idle()

Prototype: void up_idle(void);

Description. up_idle() is the logic that will be executed when their is no other ready-to-run task. This is processor idle time and will continue until some interrupt occurs to cause a context switch from the idle task.

Processing in this state may be processor-specific. e.g., this is where power management operations might be performed.

4.1.3 up_initial_state()

Prototype: void up_initial_state(FAR _TCB *tcb);

Description. A new thread is being started and a new TCB has been created. This function is called to initialize the processor specific portions of the new TCB.

This function must setup the initial architecture registers and/or stack so that execution will begin at tcb->start on the next context switch.

4.1.4 up_create_stack()

Prototype: STATUS up_create_stack(FAR _TCB *tcb, size_t stack_size);

Description. Allocate a stack for a new thread and setup up stack-related information in the TCB.

The following TCB fields must be initialized:

This API is NOT required if CONFIG_CUSTOM_STACK is defined.

Inputs:

4.1.5 up_use_stack()

Prototype: STATUS up_use_stack(FAR _TCB *tcb, FAR void *stack, size_t stack_size);

Description. Setup up stack-related information in the TCB using pre-allocated stack memory.

The following TCB fields must be initialized:

This API is NOT required if CONFIG_CUSTOM_STACK is defined.

Inputs:

4.1.6 up_release_stack()

Prototype: void up_release_stack(FAR _TCB *dtcb);

Description. A task has been stopped. Free all stack related resources retained int the defunct TCB.

This API is NOT required if CONFIG_CUSTOM_STACK is defined.

4.1.7 up_unblock_task()

Prototype: void up_unblock_task(FAR _TCB *tcb);

Description. A task is currently in an inactive task list but has been prepped to execute. Move the TCB to the ready-to-run list, restore its context, and start execution.

This function is called only from the NuttX scheduling logic. Interrupts will always be disabled when this function is called.

Inputs:

4.1.8 up_block_task()

Prototype: void up_block_task(FAR _TCB *tcb, tstate_t task_state);

Description. The currently executing task at the head of the ready to run list must be stopped. Save its context and move it to the inactive list specified by task_state. This function is called only from the NuttX scheduling logic. Interrupts will always be disabled when this function is called.

Inputs:

4.1.9 up_release_pending()

Prototype: void up_release_pending(void);

Description. When tasks become ready-to-run but cannot run because pre-emption is disabled, they are placed into a pending task list. This function releases and makes ready-to-run all of the tasks that have collected in the pending task list. This can cause a context switch if a new task is placed at the head of the ready to run list.

This function is called only from the NuttX scheduling logic when pre-emption is re-enabled. Interrupts will always be disabled when this function is called.

4.1.10 up_reprioritize_rtr()

Prototype: void up_reprioritize_rtr(FAR _TCB *tcb, uint8_t priority);

Description. Called when the priority of a running or ready-to-run task changes and the reprioritization will cause a context switch. Two cases:

  1. The priority of the currently running task drops and the next task in the ready to run list has priority.
  2. An idle, ready to run task's priority has been raised above the the priority of the current, running task and it now has the priority.

This function is called only from the NuttX scheduling logic. Interrupts will always be disabled when this function is called.

Inputs:

4.1.11 _exit()

Prototype: void _exit(int status) noreturn_function;

Description. This function causes the currently executing task to cease to exist. This is a special case of task_delete().

Unlike other UP APIs, this function may be called directly from user programs in various states. The implementation of this function should disable interrupts before performing scheduling operations.

4.1.12 up_assert()

Prototype:
void up_assert(FAR const uint8_t *filename, int linenum);
void up_assert_code(FAR const uint8_t *filename, int linenum, int error_code);

Description. Assertions may be handled in an architecture-specific way.

4.1.13 up_schedule_sigaction()

Prototype: void up_schedule_sigaction(FAR _TCB *tcb, sig_deliver_t sigdeliver);

Description. This function is called by the OS when one or more signal handling actions have been queued for execution. The architecture specific code must configure things so that the 'sigdeliver' callback is executed on the thread specified by 'tcb' as soon as possible.

This function may be called from interrupt handling logic.

This operation should not cause the task to be unblocked nor should it cause any immediate execution of sigdeliver. Typically, a few cases need to be considered:

  1. This function may be called from an interrupt handler During interrupt processing, all xcptcontext structures should be valid for all tasks. That structure should be modified to invoke sigdeliver() either on return from (this) interrupt or on some subsequent context switch to the recipient task.
  2. If not in an interrupt handler and the tcb is NOT the currently executing task, then again just modify the saved xcptcontext structure for the recipient task so it will invoke sigdeliver when that task is later resumed.
  3. If not in an interrupt handler and the tcb IS the currently executing task -- just call the signal handler now.

This API is NOT required if CONFIG_DISABLE_SIGNALS is defined.

4.1.14 up_allocate_heap()

Prototype: void up_allocate_heap(FAR void **heap_start, size_t *heap_size);

Description. The heap may be statically allocated by defining CONFIG_HEAP_BASE and CONFIG_HEAP_SIZE. If these are not defined, then this function will be called to dynamically set aside the heap region.

This API is NOT required if CONFIG_HEAP_BASE is defined.

4.1.15 up_interrupt_context()

Prototype: bool up_interrupt_context(void)

Description. Return true if we are currently executing in the interrupt handler context.

4.1.16 up_disable_irq()

Prototype:

Description. Disable the IRQ specified by 'irq' On many architectures, there are three levels of interrupt enabling: (1) at the global level, (2) at the level of the interrupt controller, and (3) at the device level. In order to receive interrupts, they must be enabled at all three levels.

This function implements enabling of the device specified by 'irq' at the interrupt controller level if supported by the architecture (irqsave() supports the global level, the device level is hardware specific).

If the architecture does not support up_disable_irq, CONFIG_ARCH_NOINTC should be defined in the NuttX configuration file. Since this API cannot be supported on all architectures, it should be avoided in common implementations where possible.

4.1.17 up_enable_irq()

Prototype:

Description. This function implements disabling of the device specified by 'irq' at the interrupt controller level if supported by the architecture (irqrestore() supports the global level, the device level is hardware specific).

If the architecture does not support up_disable_irq, CONFIG_ARCH_NOINTC should be defined in the NuttX configuration file. Since this API cannot be supported on all architectures, it should be avoided in common implementations where possible.

4.1.18 up_prioritize_irq()

Prototype:

Description. Set the priority of an IRQ.

If the architecture supports up_enable_irq, CONFIG_ARCH_IRQPRIO should be defined in the NuttX configuration file. Since this API cannot be supported on all architectures, it should be avoided in common implementations where possible.

4.1.19 up_putc()

Prototype: int up_putc(int ch);

Description. This is a debug interface exported by the architecture-specific logic. Output one character on the console

4.2 APIs Exported by NuttX to Architecture-Specific Logic

These are standard interfaces that are exported by the OS for use by the architecture specific logic.

4.2.1 os_start()

To be provided

4.2.2 OS List Management APIs

To be provided

4.2.3 sched_process_timer()

Prototype: void sched_process_timer(void);

Description. This function handles system timer events. The timer interrupt logic itself is implemented in the architecture specific code, but must call the following OS function periodically -- the calling interval must be MSEC_PER_TICK.

4.2.4 irq_dispatch()

Prototype: void irq_dispatch(int irq, FAR void *context);

Description. This function must be called from the architecture- specific logic in order to display an interrupt to the appropriate, registered handling logic.

4.3 On-Demand Paging

The NuttX On-Demand Paging feature permits embedded MCUs with some limited RAM space to execute large programs from some non-random access media. If the platform meets certiain requirements, then NuttX can provide on-demand paging: It can copy .text from the large program in non-volatile media into RAM as needed to execute a huge program from the small RAM. Design and porting issues for this feature are discussed in a sepate document. Please see the NuttX Demand Paging design document for further information.

4.4 LED Support

A board architecture may or may not have LEDs. If the board does have LEDs, then most architectures provide similar LED support that is enabled when CONFIG_ARCH_LEDS is selected in the NuttX configuration file. This LED support is part of architecture-specific logic and is not managed by the core NuttX logic. However, the support provided by each architecture is sufficiently similar that it can be documented here.

4.3.1 Header Files

LED-related definitions are provided in two header files:

4.3.2 LED Definitions

The implementation of LED support is very specific to a board architecture. Some boards have several LEDS, others have only one or two. Some have none. Others LED matrices and show alphanumeric data, etc. The NuttX logic does not refer to specific LEDS, rather, it refers to an event to be shown on the LEDS in whatever manner is appropriate for the board; the way that this event is presented depends upon the hardware available on the board.

The model used by NuttX is that the board can show 8 events defined as follows in <board-name>/include/board.h:

The specific value assigned to each pre-processor variable can be whatever makes the implementation easiest for the board logic. The meaning associated with each definition is as follows:

4.3.3 Common LED interfaces

The <arch-name>/src/common/up_internal.h probably has definitions like:

Where:

5.0 NuttX File System

Overview. NuttX includes an optional, scalable file system. This file-system may be omitted altogether; NuttX does not depend on the presence of any file system.

Pseudo Root File System. Or, a simple in-memory, pseudo file system can be enabled. This simple file system can be enabled setting the CONFIG_NFILE_DESCRIPTORS option to a non-zero value (see Appendix A). This is an in-memory file system because it does not require any storage medium or block driver support. Rather, file system contents are generated on-the-fly as referenced via standard file system operations (open, close, read, write, etc.). In this sense, the file system is pseudo file system (in the same sense that the Linux /proc file system is also referred to as a pseudo file system).

Any user supplied data or logic can be accessed via the pseudo-file system. Built in support is provided for character and block drivers in the /dev pseudo file system directory.

Mounted File Systems The simple in-memory file system can be extended my mounting block devices that provide access to true file systems backed up via some mass storage device. NuttX supports the standard mount() command that allows a block driver to be bound to a mountpoint within the pseudo file system and to a file system. At present, NuttX supports only the VFAT file system.

Comparison to Linux From a programming perspective, the NuttX file system appears very similar to a Linux file system. However, there is a fundamental difference: The NuttX root file system is a pseudo file system and true file systems may be mounted in the pseudo file system. In the typical Linux installation by comparison, the Linux root file system is a true file system and pseudo file systems may be mounted in the true, root file system. The approach selected by NuttX is intended to support greater scalability from the very tiny platform to the moderate platform.

6.0 NuttX Device Drivers

NuttX supports a variety of device drivers including:

These different device driver types are discussed in the following paragraphs. Note: device driver support requires that the in-memory, pseudo file system is enabled by setting the CONFIG_NFILE_DESCRIPTORS in the NuttX configuration file to a non-zero value.

6.1 Character Device Drivers

Character device drivers have these properties:

6.2 Block Device Drivers

Block device drivers have these properties:

6.3 Specialized Device Drivers

6.3.1 Ethernet Device Drivers

6.3.2 SPI Device Drivers

6.3.3 I2C Device Drivers

6.3.4 Serial Device Drivers

6.3.5 Frame Buffer Drivers

6.3.6 LCD Drivers

6.3.7 Memory Technology Device Drivers

6.3.8 SDIO Device Drivers

6.3.9 USB Host-Side Drivers

6.3.10 USB Device-Side Drivers

Appendix A: NuttX Configuration Settings

The following variables are recognized by the build (you may also include architecture-specific settings).

Architecture selection

The following configuration items select the architecture, chip, and board configuration for the build.

Some architectures require a description of the RAM configuration:

Build Options

General build options:

Building application code:

Two-pass Build Options. If the 2 pass build option is selected, then these options configure the make system build a extra link object. This link object is assumed to be an incremental (relative) link object, but could be a static library (archive) (some modification to this Makefile would be required if CONFIG_PASS1_OBJECT is an archive). Pass 1 1ncremental (relative) link objects should be put into the processor-specific source directory where other link objects will be created - ff the pass1 obect is an archive, it could go anywhere.

When the two pass build option is enabled, the following also apply:

General OS setup

OS setup related to on-demand paging:

If CONFIG_PAGING is selected, then you will probabaly need CONFIG_BUILD_2PASS to correctly position the code and the following configuration options also apply:

The following can be used to disable categories of APIs supported by the OS. If the compiler supports weak functions, then it should not be necessary to disable functions unless you want to restrict usage of those APIs.

There are certain dependency relationships in these features.

Miscellaneous libc settings

Allow for architecture optimized implementations

The architecture can provide optimized versions of the following to improve system performance.

Sizes of configurable things (0 disables)

File Systems

Device Drivers

SPI driver

SPI-based MMC/SD driver

SDIO-based MMC/SD driver

RiT P14201 OLED driver

Nokia 6100 Configuration Settings:

ENC28J60 Ethernet Driver Configuration Settings

Network Support

TCP/IP and UDP support via uIP

UIP Network Utilities

THTTPD

USB Device-Side Support

USB Device Controller Driver

USB Serial Device Class Driver

USB Storage Device Configuration

USB Host-Side Support

USB Host Controller Driver

USB Host HID Class Driver

Requires CONFIG_USBHOST=y, CONFIG_USBHOST_INT_DISABLE=n, CONFIG_NFILE_DESCRIPTORS > 0, CONFIG_SCHED_WORKQUEUE=y, and CONFIG_DISABLE_SIGNALS=n.

USB Host HID Mass Storage Class Driver

Requires CONFIG_USBHOST=y, CONFIG_USBHOST_BULK_DISABLE=n, CONFIG_NFILE_DESCRIPTORS > 0, and CONFIG_SCHED_WORKQUEUE=y.

Graphics related configuration settings

NX configuration setting

NX Multi-user only options

Stack and heap information

Appendix B: Trademarks

  • ARM, ARM7 ARM7TDMI, ARM9, ARM920T, ARM926EJS, Cortex-M3 are trademarks of Advanced RISC Machines, Limited.
  • Cygwin is a trademark of Red Hat, Incorporated.
  • Linux is a registered trademark of Linus Torvalds.
  • Eagle-100 is a trademark of Micromint USA, LLC.
  • LPC2148 is a trademark of NXP Semiconductors.
  • TI is a trade name of Texas Instruments Incorporated.
  • UNIX is a registered trademark of The Open Group.
  • VxWorks is a registered trademark of Wind River Systems, Incorporated.
  • ZDS, ZNEO, Z16F, Z80, and Zilog are a registered trademark of Zilog, Inc.
  • NOTE: NuttX is not licensed to use the POSIX trademark. NuttX uses the POSIX standard as a development guideline only.