Stephen Smith's Blog

Musings on Machine Learning…

Posts Tagged ‘gnu make

Raspberry Pi Assembly Programming

with 12 comments

Introduction

Most University Computer Science programs now do most of their instruction in quite high level languages like Java which don’t require you to need to know much about the underlying computer architecture. I think this tends to create quite large gaps in understanding which can lead to quite bad program design mistakes. Learning to program C puts you closer to the metal since you need to know more about memory and low level storage, but nothing really teaches you how the underlying computer works than doing some Assembler Language Programming, The Raspbian operating system comes with the GNU Assembler pre-installed, so you have everything you need to try Assembler programming right out of the gate. Learning a bit of Assembler teaches you how the Raspberry Pi’s ARM processor works, how a modern RISC architecture processes instructions and how work is divided by the ARM CPU and the various co-processors which are included on the same chip.

A Bit of History

The ARM processor was originally developed to be a low cost processor for the British educational computer, the Acorn. The developers of the ARM felt they had something and negotiated a split into a separate company selling CPU designs to hardware manufacturers. Their first big sale was to Apple to provide a CPU for Apple’s first PDA the Newton. Their first big success was the inclusion of their chip design in Apple’s iPods. Along the way many chip makers like TI which had given up competing on CPUs built single chip computers around the ARM. These ended up being included in about every cell phone including those from Nokia and Apple. Nowadays pretty up every Android phone is also built around ARM designs.

ARM Assembler Instructions

There have been a lot of ARM designs from early simple 16 bit processors up through 32 bit processors to the current 64bit designs. In this article I’m just going to consider the ARM processor in the Raspberry Pi, this processor is a 64 bit processor, but Raspbian is still a 32 bit operating system, so I’ll just be talking about the 32 bit processing used here (and I’ll ignore the 16 bit “thumb” instructions for now).

ARM is a RISC processor which means the idea is that it executes very simple instructions very quickly. The idea is to keep the main processor simple to reduce complexity and power usage. Each instruction is 32 bits long, so the processor doesn’t need to think about how much to increment the program counter for each instruction. Interestingly nearly all the instructions are in the same format, where you can control whether it sets the status bits, can test the status bits on whether to do anything and can have four register parameters (or 2 registers and an immediate constant). One of the registers can also have a shift operation applied. So how is all this packed into on 32 bit instruction? There are 16 registers in the ARM CPU (these include the program counter, link return register and the stack pointer. There is also the status register that can’t be used as a general purpose register. This means it takes 4 bits to specify a register, so specifying 4 registers takes 16 bits out of the instruction.

Below are the formats for the main types of ARM instructions.

Now we break out the data processing instructions in more detail since these comprise quite a large set of instructions and are the ones we use the most.

Although RISC means a small set of simple instructions, we see that by cleverly using every bit in those 32 bits for an instruction that we can pack quite a bit of information.

Since the ARM is a 32-Bit processor meaning among other things that it can address a 32-Bit address space this does lead to some interesting questions:

  1. The processor is 32-Bits but immediate constants are 16-Bits. How do we load arbitrary 32-Bit quantities? Actually the immediate instruction is 12-Bits of data and 4 Bits of a shift amount. So we can load 12 Bits and then shift it into position. If this works great. If not we have to be tricky by loading and adding two quantities.
  2. Since the total instruction is 32-Bits how do we load a 32-Bit memory address? The answer is that we can’t. However we can use the same tricks indicated in number 1. Plus you can use the program counter. You can add an immediate constant to the program counter. The assembler often does this when assembling load instructions. Note that since the ARM is pipelined the program counter tends to be a couple of instructions ahead of the current one, so this has to properly be taken into account.
  3. Why is there the ability to conditionally execute all the data processing instructions? Since the ARM processor is pipelines, doing a branch instruction is quite expensive since the pipeline needs to be discarded and then reloaded at the new execution point. Having individual instructions conditionally execute can save quite a few branch instructions leading to faster execution. (As long as your compiler is good at generating RISC type code or you are hand coding in Assembler).
  4. The ARM processor has a multiply instruction, but no divide instruction? This seems strange and unbalanced. Multiply (and multiply and add) are recent additions to the ARM processor. Divide is still considered too complicated and slow, plus is it really used that much? You can do divisions on either the floating point coprocessor or the Neon SIMD coprocessor.

A Very Small Example

Assembler listings tend to be quite long, so as a minimal set let’s start with a program that just exits when run returning 17 to the command line (you can see this if you type echo $@ after running it). Basically we have an assembler directive to define the entry point as a global. We move the immediate value 17 to register R0. Shift it left by 2 bits (Linux expects this). Move 1 to register R7 which is the Linux command code to exit the program and then call service zero which is the Linux operating system. All calls to Linux use this pattern. Set some parameters in registers and then call “svc 0” to do the call. You can open files, read user input, write to the console, all sorts of things this way.

 

.global _start              @ Provide program starting address to linker
_start: mov     R0, #17     @ Use 17 for test example
        lsl     R0, #2      @ Shift R0 left by 2 bits (ie multiply by 4)
        mov     R7, #1      @ Service command code 1 terminates this program
        svc     0           @ Linux command to terminate program

 

Here is a simple makefile to compile and link the preceding program. If you save the above as model.s and then the makefile as makefile then you can compile the program by typing “make” and run it by typing “./model”. as is the GNU-Assembler and ld is the linker/loader.

 

model: model.o
     ld -o model model.o

model.o: model.s
     as -g -o model.o model.s

clean:
     rm model model.o

 

Note that make is very particular about whitespace. It must be a true “tab” character before the commands to execute. If Google Docs or WordPress changes this, then you will need to change it back. Unfortunately word processors have a bad habit of introducing syntax errors to program listings by changing whitespace, quotes and underlines to typographic characters that compilers (and assemblers) don’t recognize.

Summary

Although these days you can do most things with C or a higher level language, Assembler still has its place in certain applications that require performance or detailed hardware interaction. For instance perhaps tuning the numpy Python library to use the Neon coprocessor for faster operation of vector type operations. I do still think that every programmer should spend some time playing with Assembler language so they understand better how the underlying processor and architecture they use day to day really works. The Raspberry Pi offers and excellent environment to do this with the good GNU Macro Assembler, the modern ARM RISC architecture and the various on-chip co-processors.

Just the Assembler introductory material has gotten fairly long, so we won’t get to an assembler version of our flashing LED program. But perhaps in a future article.

 

Written by smist08

January 4, 2018 at 10:45 pm

C Programming on the Raspberry Pi

with 7 comments

Introduction

I blogged on programming Fortran a few articles ago, this was really a tangent. I installed the Code::Blocks IDE to do some C programming, but when I saw the Fortran support I took a bit of a side trip. In this article I want to get back to C programming on the Raspberry Pi. I’ve been a C programmer for much of my professional career starting at DREA and then later on various flavours of Unix at Epic Data and then doing Windows programming at a number of companies including Computer Associates/Accpac International/Sage. I’ve done a lot of programming in the object oriented extensions to C including C++, Java, C# and Objective-C. But I think at heart I still have a soft spot for C and enjoy the fun things you can do with pointers. I think there are a lot of productivity benefits to languages like Java and C# which take away the most dangerous features of C (like memory pointers and memory allocation/deallocation), I still find C to be very efficient and often a quick way to get things done. Admittedly I do all my programming these days in Python, but sometimes Python is frustratingly slow and then I miss C.

In this article we’ll re-implement in C our flashing LED program that I introduced here. We’ve now run the same program in Python, Scratch, Fortran and C. The Raspberry Pi is a great learning environment. If you want to learn programming on very inexpensive equipement, the Raspberry Pi is really excellent.

GCC and C

The Gnu Compiler Collection (GCC) is the main C compiler for Linux development and runs on many other platforms. GCC supports many programming languages now, but C is its original and main language. The GCC C Compiler implements the 2011 C language standard C11 along with a large collection of extensions. You can define compiler flags to enforce strict compliance to the standards to improve portability, but I tend to rely on GCC portability instead. A number of the extensions are very handy like being able to define the loop variable inside a for statement which is a feature from C++.

Code::Blocks

Although you don’t need an IDE to do C development, you can just edit the source files in any text editor then use GNU Make to do the build. Then run the GNU Debugger separately to do any debugging. But generally it’s a bit easier to use an IDE especially for debugging. Code::Blocks is a popular IDE that runs on the Raspberry Pi (and many other things), it’s fairly light weight (certainly compared to Eclipse, XCode or Visual Studio) and has all the standard IDE features programmers expect.

A number of people recommend programming on a more powerful computer (like a good Mac or Windows laptop) and then just transferring the resulting executable to the Pi to run and test. For big projects this might have some productivity benefits, but I find the Pi is up to the task and can quite happily develop directly on the Raspberry Pi.

Accessing the GPIO

From C you have quite a few alternatives in how to access the GPIO. You can open /dev/mem and get a direct memory mapping to the hardware registers. This requires running as root, but it is more direct. I’m going to go the same route as I did for the Fortran program and use the easier file access from the Raspbian GPIO device driver. This one works pretty well and doesn’t require root access to use. A good web page with all the ways to access the GPIO from various languages including C is here. If you want to see how to access GPIO hardware registers directly, go for it. Like the Fortran program I needed a delay after opening the main devices file and before accessing the separate file created for the GPIO pin. There seems to be a bit of a race condition here that needs to be avoided. Otherwise a fairly simple C program and accessing the GPIO is pretty standard as accessing Linux devices go.

C Code

Here is the C code for my little flashing lights program. Three source files main.c and then gpio.h and gpio.c for the gpio access routines.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include “gpio.h”

int main()
{
   int i;
int sleepTime = 200000;

   if (-1 == GPIOExport(17) ||
        -1 == GPIOExport(27) ||
        -1 == GPIOExport(22))
   return(1);

   // Necessary sleep to allow the pin
   // files to be created.
usleep(10000);

   /*
    * Set GPIO directions
*/
if (-1 == GPIODirection(17, OUT) ||
        -1 == GPIODirection(27, OUT) ||
        -1 == GPIODirection(22, OUT))
       return(2);

   for ( i = 0; i < 10; i++ )
   {
        if (-1 == GPIOWrite(17, HIGH))
             return(3);
       usleep(sleepTime);
if (-1 == GPIOWrite(17, LOW))
            return(3);
       if (-1 == GPIOWrite(27, HIGH))
            return(3);
       usleep(sleepTime);
if (-1 == GPIOWrite(27, LOW))
            return(3);
       if (-1 == GPIOWrite(22, HIGH))
            return(3);
       usleep(sleepTime);
if (-1 == GPIOWrite(22, LOW))
            return(3);
   }
return( 0 );
}

// gpio.h

#define IN  0
#define OUT 1

#define LOW  0
#define HIGH 1

extern int GPIOExport(int pin);
extern int GPIOUnexport(int pin);
extern int GPIODirection(int pin, int dir);
extern int GPIORead(int pin);
extern int GPIOWrite(int pin, int value);

/* gpio.c
 *
* Raspberry Pi GPIO example using sysfs interface.
* Guillermo A. Amaral B. <g@maral.me>
*
* This file blinks GPIO 4 (P1-07) while reading GPIO 24 (P1_18).
*/

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include “gpio.h”

int GPIOExport(int pin)
{
#define BUFFER_MAX 3
    char buffer[BUFFER_MAX];
    ssize_t bytes_written;
    int fd;

    fd = open(“/sys/class/gpio/export”, O_WRONLY);
    if (-1 == fd) {
         fprintf(stderr, “Failed to open export for writing!\n”);
         return(-1);
    }
    bytes_written = snprintf(buffer, BUFFER_MAX, “%d”, pin);
    write(fd, buffer, bytes_written);
    close(fd);
    return(0);
}

int GPIOUnexport(int pin)
{
    char buffer[BUFFER_MAX];
    ssize_t bytes_written;
    int fd;

    fd = open(“/sys/class/gpio/unexport”, O_WRONLY);
    if (-1 == fd) {
         fprintf(stderr, “Failed to open unexport for writing!\n”);
        return(-1);
     }

     bytes_written = snprintf(buffer, BUFFER_MAX, “%d”, pin);
     write(fd, buffer, bytes_written);
    close(fd);
    return(0);
}

int GPIODirection(int pin, int dir)
{
static const char s_directions_str[]  = “in\0out”;

#define DIRECTION_MAX 35
    char path[DIRECTION_MAX];
     int fd;

   snprintf(path, DIRECTION_MAX, “/sys/class/gpio/gpio%d/direction”, pin);
    fd = open(path, O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, “Failed to open gpio direction for writing!\n”);
        return(-1);
    }

    if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) {
         fprintf(stderr, “Failed to set direction!\n”);
         return(-1);
    }
    close(fd);
    return(0);
}

int GPIORead(int pin)
{
#define VALUE_MAX 30
    char path[VALUE_MAX];
    char value_str[3];
    int fd;

    snprintf(path, VALUE_MAX, “/sys/class/gpio/gpio%d/value”, pin);
    fd = open(path, O_RDONLY);
    if (-1 == fd) {
        fprintf(stderr, “Failed to open gpio value for reading!\n”);
        return(-1);
    }

    if (-1 == read(fd, value_str, 3)) {
        fprintf(stderr, “Failed to read value!\n”);
        return(-1);
    }

    close(fd);

    return(atoi(value_str));
}

int GPIOWrite(int pin, int value)
{
static const char s_values_str[] = “01”;

    char path[VALUE_MAX];
    int fd;

    snprintf(path, VALUE_MAX, “/sys/class/gpio/gpio%d/value”, pin);
    fd = open(path, O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, “Failed to open gpio value for writing!\n”);
        return(-1);
    }

    if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) {
        fprintf(stderr, “Failed to write value!\n”);
        return(-1);
    }

    close(fd);
    return(0);
}

Summary

This was a quick little introduction to C development on the Raspberry Pi. With the GCC compiler, Code::Blocks, GNU Debugger and GNU Make you have all the tools you need for serious C development. I find the Raspberry Pi is sufficiently powerful to do quite a bit of development work with good productivity. As a learning/teaching environment its excellent. It’s really amazing what you can do with a $35 single board computer.

Written by smist08

December 23, 2017 at 10:48 pm