Assembler programming on the Raspberry Pi

Flash

Now that you have seen a simple assembler program, it's time for a more sophisticated project. The good old flash program is a good candidate; it simply flashes a single LED. The positive contact of the LED is connected to GPIO21 (BCM notation) with a 1kilohm series resistor. The connection is routed out on header pin 40, which is handy because you have a GND on header pin 39 right next door. This pin is connected to the negative terminal on the LED.

Of course, the program presents several challenges to those used to programming with high-level languages. The GPIOs of the Raspberry Pi support extremely versatile use, which means it is not easy to address them correctly in an assembler program. Forty-one registers, each with a length of 32 bits, control the 54 GPIO pins of the chip installed on the Pi. (Not all of these GPIOs are accessible from the header; some of them are used internally by the Pi.) However, this still leaves you with a huge number of options for controlling the individual GPIOs. A detailed description of how the GPIOs work can be found in the BCM2835 peripherals data sheet [2] (pages 89-109).

Now that you know how to address the GPIOs, another problem raises its head. The Raspberry Pi's operating system prevents direct access to hardware resources. If you try to access the hardware directly, you will see a Segmentation Fault error message, which indicates that a memory protection violation occurred but gives you no additional clues as to where exactly the error occurred. Fortunately, the Raspberry Pi OS developers have provided a way to access the GPIOs without directly accessing the corresponding memory addresses. The operating system offers a driver to a special character file, /dev/mem, that is a mirror of main memory. A good description of this can be found on the Sonoma State University website [3].

The first block of the program in Listing 2 contains the definition of various constants with assigned values, which offers several advantages: On the one hand, it lets you use meaningful names in the program, and on the other hand, it lets you to load the registers with 32-bit values.

Listing 2

flash.s

01 .globl main
02 .equ GPIOVAL, 0x200000 // Register value for the GPIO 21(BCM)
03 .equ GPFSEL2, 0x08     // Offset address for setting the GPIO mode
04 .equ GPIO_OUTPUT,0x08  // Define GPIO as an output
05 .equ GPFSET0, 0x1c     // Offset register set
06 .equ GPFCLR0, 0x28     // Offset register clear
07 .equ TIME, 0x8000000   // Wait value
08 main:
09 ldr r0,=gpiomem
10 ldr r1,=0x101002 // Open for reading and writing
11 mov r7, #5
12 svc #0
13 mov r4, r0
14 mov r0, #0
15 mov r1, #4096
16 mov r2, #3
17 mov r3, #1
18 mov r5, #0
19 mov r7, #192
20 svc #0
21 // r0 Contains the base address of the mapped GPIO memory area
22 ldr r1, =GPIO_OUTPUT // GPIO21
23 str r1, [r0,#GPFSEL2] // set as output
24 ldr r2, =TIME    // Wait in r2
25 ldr r1, =GPIOVAL // Register value in r1 for GPIO21
26 // Infinite loop
27 loop:
28   str r1, [r0,#GPFSET0] // Switch LED on
29   mov r10, #0           // Set r10 to 0
30   wait_on:              // Increment r10 to TIME
31     add r10, r10, #1
32     cmp r10, r2
33     bne wait_on
34   str r1, [r0,#GPFCLR0] // Switch LED off
35   mov r10, #0           // Set r10 to 0
36   wait_off: // Increment r10 to TIME
37     add r10, r10, #1
38     cmp r10, r2
39     bne wait_off
40 b loop
41
42 .data
43
44 gpiomem: .asciz "/dev/gpiomem"

The mov command can only move values up to a certain size directly to registers. The next large block of instructions opens the /dev/gpiomem device and saves the base address of the mapped memory in register r0.

Supervisor calls are used in the svc block; put simply, these are something like calls to existing operating system programs. (The "Supervisor Calls" box provides additional information.) Initially, it is important that you have the option of accessing the GPIO from the address in r0.

Supervisor Calls

Supervisor calls (syscalls) are functions provided by the operating system to perform certain tasks. Each syscall has its own number with which it is called. On the Raspberry Pi, the numbers with the matching names can be output on the terminal with the

cat /usr/include/arm-linux-gnueabihf/asm/unistd-common.h

command. You need the svc #0 command to execute a syscall in assembler, but you can also execute syscalls from high-level languages. When doing so, the number of the syscall must be in the r7 register. Depending on the syscall, the registers r0 to r6 contain the associated parameters. The return value of the call always ends up in register r0. You can access the documentation for the individual syscalls in a terminal window by typing:

man 2 <name>

The 2 here indicates that you only want to search section 2 of the documentation.

The flash program starts in line 22 by first setting the mode for GPIO21 to output. It then loads the wait value into register r2 (line 24), and line 25 stores the bit combination for switching GPIO21 on and off in register r1.

The GPIO registers work in a fairly simple way, and each of them has a specific task (set GPIO, clear GPIO, enable pullup, etc.). Each single bit in the registers corresponds to a GPIO pin. If you set the bit corresponding to GPIO21 in register GPFCLR0 (register for clearing GPIOs), the GPIO drops off to 0, which is why the program loads the combination for GPIO21 into register r1.

Now all you need to do later is alternately move the r0 register to the GPFSET0 and GPFCLR0 GPIO registers (lines 28 and 34). The command means: Store the contents of r1 at the memory address that results from adding the contents of r0 to the constant from GPFSET0 and GPFCLR0, respectively. The two wait loops increment the value in r0 until it reaches the value of TIME (r2).

This procedure of creating a wait is not very smart, because one CPU core is counting continuously. You can use the top command to look at the CPU usage when the program is running. A CPU time-optimized program would use timers and interrupts, but it would be considerably more complicated in that case.

Finally, line 40 contains an unconditional jump command that jumps back to the loop label, thus running the program for all eternity.

Where To Go Next

Now that you are knee-deep in assembler programming, you might want to look into the subject in detail. I would recommend a tutorial, such as the one you can find on the Think in Geek website [4]. It explains the basics from A to Z, with many useful tips.

You can easily enter simple examples, like the ones from this article, in an editor such as Nano. However, if you are working on more complex projects, you will want a more powerful editor to make your life easier.

Several possible ways to upload files to the Raspberry Pi are at your disposal. I mounted the Raspberry Pi over SSH in the Ubuntu file manager. This simple approach usually works fine on a LAN. With more complex projects it doesn't make much sense to build all the files manually. Even the old-fashioned make will save you time and overhead.

Before you start to implement a function, always have a look at the list of syscalls. In many cases you will find something suitable. When using syscalls, you can assume that they do not contain any errors, which is worth its weight in gold, especially in assembler programming.

Conclusions

In this article I was only able to scratch the surface of assembler programming, and many details remain open. Getting started with this programming language is not difficult, and the individual commands are not complicated. Once you have looked into the CPU architecture, the meaning of assembler code is fairly easy to understand.

The tricky part begins as soon as you start using assembler to solve problems that are typically tackled in high-level languages. Even a small program will quickly grow to a few hundred commands. The advantages are the minimal code footprint and maximum execution speed, if programmed correctly.

The Author

Martin Mohr has experienced the complete development of modern computer technology. After finishing university, he mainly developed Java applications. The Raspberry Pi woke his old passion for electronics.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • ARM64 Assembly and GPIO

    Reading, writing, and arithmetic with the Raspberry Pi in ARM64 assembly language.

  • GPIO on Linux Devices

    The general purpose input/output interface is not just for small-board computers anymore: You can use GPIO on your Linux desktop or laptop, too, through the USB port.

  • Kitchen Timer

    A simple kitchen helper with two timers assists budding chefs in coping with dishes that are unlikely to be ready at the same time.

  • Go on the Rasp Pi

    We show you how to create a Go web app that controls Raspberry Pi I/O.

  • Bash Web Server

    With one line of Bash code, you can create a Bash web server for quickly viewing the output from Bash scripts and commands.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News