Fast GPIO toggle on BeagleBone Black using a userspace driver

Home / blog / Fast GPIO toggle on BeagleBone Black using a userspace driver

Fast GPIO toggle on BeagleBone Black using a userspace driver

If you ever had to manipulate a GPIO from userspace on a Linux system, you’ve probably came up with the idea of using the sysfs to do it. This is a popular approach; in fact, our customers often ask about it, there are many posts on the web suggesting to do so and moreover there’s a well-known Node.js onoff module with its backend on sysfs.

In this post we will try to demystify sysfs usage, and explain why it is NOT intended to control efficiently GPIOs from userspace. Doing so will make your application perform poorly (and might kill a puppie too).

First, we will analyze the drawback of the sysfs aproach, and then we will provide a user-friendly, open-source alternative to the problem: our own userspace driver for GPIO banks on the BeagleBone Black boards (AM335x chips).

The sources for this userspace driver can be found here. Don’t hesitate to take a look at it! The README is well documented, and there are some examples that you can easily compile and run on your BeagleBone Black (or on any other AM335x-based board).

Of course, the driver is hardware specific, but extending it to work on other SoCs shouldn’t be too hard.

About sysfs

Sysfs is a virtual filesystem that describes the devices known to the system from various viewpoints and allows to export kernel data structures, their attributes, and the linkages between them to userspace. For more info about the sysfs, go to the docs!

In the case of GPIO devices, you may see a tree like this, which represents the gpio banks present in your hardware. In the case of the BeagleBone Black, there’re 4 banks (32 lines each):

Several device’s attributes are exported in the form of regular files in the filesystem. Sysfs forwards file I/O operations to methods defined for the attributes, providing a means to read and write kernel attributes from userspace.

GPIO sysfs interface

GPIO kernel driver exposes a sysfs interface that allow userspace to manage attributes like: direction (in/out), value (read/write).

Pretty simple, uh? The sysfs interface is great for a simple and quick GPIO manipulation.
However, simplicity comes with a price. Accesing GPIOs in this way is very expensive, adding a lot of overhead to the application. Talk is cheap, so let’s run some measurements and see how it actually performs…

Sysfs limitations

When a userspace program needs to interact with hardware using a kernel driver, a context switch will occur to jump between modes. This is not for free, it requires considerable processor time, and under some circumstances we cannot afford that.

Fast GPIO toggle using sysfs + bash is a perfect example to understand that and to see how this affects performance.

Sysfs toggle from bash script

Let’s toggle the GPIO line with this simple bash script.

sysfs_bash_measurement

As it can be seen in the above measurement, a frequency of 8.25kHz is achieved. Lower enough, let’s see if we can do better than that.

Sysfs toggle from C program

You may have noticed that the above example isn’t quite fair. Bash is running a new process on each echo, thus extra processor time is wasted and GPIO frequency is reduced.

To speed up things, you could just run a C (or whatever your favourite language is) program keeping the file descriptor opened. (The example is simplified: in a real program you should check on function’s return values and stuff like that.)

sysfs_c_measurement

As you may see, now GPIO frequency is around 172.66kHz. Probably still too low, right? Note that each write syscall to access the file descriptor for the value attribute results on a context switch, wasting a lot of time.

If you consider that your embedded system will be running lots of process concurrently, sharing valuable processor time, it would be a mistake to use the sysfs to acomplish such a simple task. With this in mind, let’s take a deeper look and propose a different approach.

Memory mapping GPIO banks registers

Using a userspace driver is going to be a lot faster than going through the kernel, since it avoids the costly context switches. This means writing directly from userspace program into the GPIO banks.

How do we do it? /dev/mem is the answer. The /dev/mem device is used to access areas of physical memory from userspace. This feature needs be enabled in the kernel, so you have to make sure CONFIG_DEVMEM option is turned on.

Using /dev/mem we can have GPIO chip banks mmap’ped directly to the program’s virtual memory. Thus, there will be no need to call syscalls, and so no need for expensive context switching into kernel space. Our program will have access to the GPIO controller configuration, and also will be able to set the GPIOs values.

AM335x GPIO chip registers notes

AM335x chips has 4 GPIO banks at addresses:
[0] = 0x44E07000
[1] = 0x4804C000
[2] = 0x481AC000
[3] = 0x481AE000

Each bank groups several registers to manipulate a total of 32 GPIO lines. The registers we’re interested in are listed below. Note that each register is 32bit long and each bit represents a different line.

OFFSET_OUTPUT_ENABLE 0x134
The GPIO_OE register is used to enable the pins output capabilities (0 for output, 1 for input).

OFFSET_DATAIN 0x138
The GPIO_DATAIN register is used to register the data that is read from the GPIO pins. Read-only register.

OFFSET_CLEAR 0x190
Writing a 1 to a bit in the GPIO_CLEARDATAOUT register clears to 0 the corresponding bit in the GPIO_DATAOUT register; writing a 0 has no effect.

OFFSET_SET 0x194
Writing a 1 to a bit in the GPIO_SETDATAOUT register sets to 1 the corresponding bit in the GPIO_DATAOUT register; writing a 0 has no effect.

AM335x GPIO Userspace driver

The userspace driver provides a simple and clear API to directly manipulate GPIOs banks on AM335x chips. The sources can be found at https://bitbucket.org/vanguardiasur/gpiolib.

Here’s an example of a userspace program using gpiolib, which simply toggles GPIO 1_12 (bank 1, pin 12).
(Again, this is just a simplified example adapted to the scope of this post. Real examples can be found on the repository listed above).

cycle_measurement

Measuring the GPIO signal with a logic analyzer, a maximum frequency of 3 MHz is achieved. Much better!

The unavoidable scheduler

If you take a look at the entire signal measurement, one thing you may notice is that there’re small gaps of time where signal is kept high or low. This is caused by the scheduler. Although we’ve managed to avoid context switching on every GPIO toggle, context switching occurs as Linux is a multithreading preemptive OS and the kernel will make us yield the processor.

This is a general example of how do tasks work on a multithreading system. If your embedded system must address certain time-constraints, perhaps you may be considering a RTOS solution instead.

To get a picture, build and run program gpiolib/utils/cycle with the highest real time priority:

scheduler_gap

In the above signal, it can be seen that every ~1sec timeslice, there’s a 50ms gap.

Final notes

We have seen that, although tempting, sysfs causes poor performance. Using a different approach, a userspace driver is demonstrated outperforms against all other cases analyzed.

The GPIOLIB was developed by the VanguardiaSur team. We encourage everyone to use it and modify it as well. Any questions and feedback are welcome!

Showing 2 comments
  • Alexandre Belloni
    Reply

    Well, isn’t the 50ms gap caused by sched_rt_runtime_us? Try setting it to -1 and it will probably improve things (and lockup your system ;))

    • Ariel D'Alessandro
      Reply

      Hey Alexandre, sorry for the delay.

      Yes, indeed. Thanks for your comment, perhaps the scheduler part is too vague, just wanted to broadly explain the reason of that gap without getting into much details.

      In case someone wants to inspect a little bit more on this, it’s pretty well documented in kernel source:
      http://lxr.free-electrons.com/source/Documentation/scheduler/sched-rt-group.txt#L80

Leave a Reply to Ariel D'Alessandro Cancel reply