The current GPIO API

Currently the GPIO API implementation is Raspberry Pi specific, as delivering a generic GPIO API that can be used across the hardware RTEMS supports demands more community input and care. The API design is based on the cpukit/libi2c API, which is the RTEMS API for the I2C and SPI communication buses, while trying to keep multiio in mind. Right now it is just an header file that defines the data structures and function prototypes, leaving the implementation to the Raspberry Pi BSP. This header must then by included in an application, which then calls the needed directives to operate the GPIO hardware.
The first directive to call is the rtems_gpio_initialize, which receives the total amount of GPIO pins on the target hardware. This function sets a flag indicating that the API has been initialized so further calls can silently exit the function. It then proceds to allocate memory to an array of structs (rtems_gpio_pin) which will contain info for every pin, and sets each of them as not being used. The access to a pin is done through the array index. For instance, to get info on the first pin on the board (pin 1), the corresponding array index will be 0. This numbering conversion is done on the directives and not the application, so the application uses the board pin number.

At this point the application must setup the GPIO pins it wants to use. This requires a pin number to identify the pin and a pin type or function, such as:

  • digital output;
  • digital input;
  • one of 6 alternative functions, which are hardware specific and managed by the BSP implementation of the API. The API only provides these generic types that can be used by a BSP to use some specific non-standard function to a GPIO pin, such as configuring a GPIO pin to behave as a general purpose clock, or as a receiver for an UART interface;
  • not used.

To select a pin to be used two directives can be called:

rtems_gpio_select_pin -> receives a pin number (its number on the board hardware) and the pin function. The function takes this information to get the pin select memory address and proceds to select the specified function to that pin on the hardware, while updating the GPIO pin array with the information that the said pin is no longer available to selection because it is now in use.

rtems_gpio_setup_input_mode -> this function receives an array of structures (rtems_gpio_configuration) that define a number of pins and their corresponding function, and calls rtems_gpio_select_pin for each of them. This allows a BSP to define specific GPIO configurations that are useful to that target than can then be easily used by an application.

The Raspberry Pi BSP defines a structure on include/gpio.h to setup a JTAG interface using GPIO pins (https://github.com/asuol/rtems/blob/GPIO_API/c/src/lib/libbsp/arm/raspberrypi/include/gpio.h).

The API also allows a pin to be disabled, using:

rtems_gpio_disable_pin -> This directive marks a pin as not used, so it can be selected to another function.

The API only provides directives to operate digital I/O pins.

Digital Output:

rtems_gpio_set -> Writes a logical 1 to an output pin

rtems_gpio_clear -> Writes a logical 0 to an output pin

The API further provides the following functions, which may not apply to the Raspberry Pi:

rtems_gpio_set_mb and rtems_gpio_clear_mb -> Sets and clears multiple output pins in a GPIO port. A GPIO port is a group of pins that can be operated as one entity, so this function allows to operate a GPIO port as such. While this is useful for targets that organize their GPIO pins in groups, the Raspberry Pi does not, so it was not implemented.

rtems_gpio_output_mode -> Defines the output pin operating mode. This can range from:

  • push-pull – Defines that the pin can both source and sink current;
  • open drain – Defines that the pin can only sink current;
  • neutral – No drive mode defined.

Because the Raspberry Pi documentation [1] does not refer any of these modes, this function was not implemented for the Raspberry Pi, but can be useful to other targets.

Digital Input:

rtems_gpio_get_val -> Reads an input pin logic value, or level.

rtems_gpio_input_mode -> Sets an input drive mode, which means to operate the internal pull resistor.

A pull resistor “pulls” the pin voltage either up (to 3.3v) to create a logical 1, or down (to 0v) to create a logical 0. If a pull resistor is not used the pin voltage can float, due to a number of random factors [2], which would cause unexpected behaviour, as logical ones and zeros could be randomly generated.

The input drive modes available on the API are:

  • Pull up – Enables the internal pull up resistor;
  • Pull down – Enables the internal pull down resistor;
  • No pull resistor – Disables the internal pull resistors. This can be the case where an external one is used.

rtems_gpio_setup_input_mode -> This function allows to set the drive mode of a number of GPIO input pins.

References:

[1] – http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

[2] – http://raspi.tv/2013/rpi-gpio-basics-6-using-inputs-and-outputs-together-with-rpi-gpio-pull-ups-and-pull-downs

 

The implementation of the above can be found on:

The GPIO API  ->https://github.com/asuol/rtems/blob/GPIO_API/cpukit/libgpio/libgpio.h

The Raspberry Pi implementation of the API -> https://github.com/asuol/rtems/blob/GPIO_API/c/src/lib/libbsp/arm/raspberrypi/gpio/libgpio.c

Advertisements

RTEMS GPIO API design (start)

This blog post will explain the logic behind the code currently at the GPIO_API github branch, and what the next steps may be.

Currently the code only focus on the discrete I/O part, but ADC and DAC pin types can be merged from multiio. The next image is a simple diagram of the data structures that hold each pin type information.

GPIO API data structs

Where:

  • DIN – Discrete/digital input
  • DOUT – Discrete/gigital output
  • ADC – Analog to digital converter
  • DAC – Digital to analog converter
  • GPIO – General Purpose I/O, which here means a pin that can be configured to behave either as a DIN or a DOUT

The API defines that each type of pin (DIN, DOUT, ADC or DAC) has a structure type with all the information that may be relevant about the pin. The GPIO box on the diagram is another structure that defines the pin physical address, the pin type and an union structure that allows to use either a DIN or DOUT pin data structure. The API would enforce a pin data structure based on the pin type field.

There would be an array for each type of pin (GPIO (which may be a DIN or DOUT), ADC or DAC), where the access to an individual pin would be made through the index. The size of these arrays would be based on a BSP defined macro, for instance RTEMS_GPIO_COUNT, which if undefined would disable the GPIO part of the library. The access to these arrays would be syncronized by the library, so that multiple drivers can operate at the same time, and each pin would have a major and minor number so that a driver cannot access pins operated by other drivers.

The advantage of the array approach is direct access to a pin, as it could be easily reached through the array index, and easy management of the diferent drivers that may be accessing the GPIO pins (ensuring that no two drivers are operating the same pin at the same time).

GPIO peripheral interfaces

All the interfaces using a GPIO configuration can go on this API, like UART, I2C, SPI, JTAG or others. For instance, the API can define a function prototype like:

int rtems_gpio_set_UART(void)

That each BSP with this interface could implement to enable UART. This would mean that the pins used for UART would be accessible only for the UART driver.

More design notes on the GPIO API should appear soon.

The current API code can be found at: https://github.com/asuol/rtems/tree/GPIO_API/cpukit/libgpio

With draft implementation on the Raspberry Pi BSP: https://github.com/asuol/rtems/tree/GPIO_API/c/src/lib/libbsp/arm/raspberrypi/gpio

And a small test case that lits a LED connected to the Raspberry Pi GPIO pin 25: https://github.com/asuol/rtems/tree/GPIO_API/testsuites/samples/LIBGPIO_TEST

Mailbox interface

The mailbox interface is a register that has several channels (“mail accounts”) for different resources on the board, so a driver sends a buffer with a request (an “email”) to one of them and gets answers. This is an abstraction layer mainly useful to communicate with the GPU since its documentation isn’t available, but can be used to get other types of information, not related with the GPU.

This interface will be needed for the Framebuffer driver, and because the memory access has to pass through the arm memory barriers are needed.

The work currently done for the mailbox and memory barriers may be seen at:

Mailbox (GITHUB branch) -> https://github.com/asuol/rtems/tree/Mailbox

Memory Barrier (GITHUB branch) -> https://github.com/asuol/rtems/tree/Memory_barrier

The mailbox code has been based on https://github.com/jncronin/rpi-boot/blob/master/mbox.c.

References:

https://github.com/raspberrypi/firmware/wiki/Mailboxes
https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface

Memory Barrier

In the newer ARM processors (ARMv6 and above) it is architecturally defined that the software must perform a Data Memory Barrier operation every time a resource is either acquired or released, to ensure a consistent memory view to all running processes.

These newer processors can optimize the execution order of instructions and data accesses, but these optimizations may create speculative accesses to memory or out-of-order execution of instructions, leading to undesirable and unintended program behavior.

In those situations we want the processor to behave as the classical ARM processors and execute instructions and access memory in program order.

There are three types of barrier instructions, considering a uni-processor environment like the Raspberry Pi:

  • Data Synchronization Barrier (DSB) – does not complete until all the previous instructions complete;
  • Data Memory Barrier (DMB) – ensures that any explicit memory access after the DMB instruction only start when all explicit memory accesses before the DMB instruction complete;
  • Instruction Synchronization Barrier (ISB) – flushes the pipeline in the processor, so all the following instructions are fetched from cache or memory once the ISB completes.

The RPi BSP must then  implement a routine or macro that provides the DMB  inline assembly code, since several peripherals may need to access the same registers in the future (to use the MailBox interface, for instance).

The memory barrier instructions can be found at:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360f/I1014942.html

References:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGIEIE.html
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html

Hello world

Hello everyone, proud to be in GSoC 2014 with the RTEMS project!

This summer I will increase the Raspberry Pi BSP support for peripherals, namely: GPIO, I2C and SPI interfaces, as well as FrameBuffer graphics support.

Expect news here very soon!