/raspberry-pi

Raspberry Pi distribution of Alpha

Primary LanguageCOtherNOASSERTION

Farjump Logo

Alpha Alpha Logo

GDB offers the best embedded software development experience by allowing you to remotely load, debug and test your programs and hardware/software interfaces. It feels like native programming thanks to a similar smooth "Edit-Compile-Run" cycle.

Alpha is a system-level GDB server (aka "gdbstub" and "gdb stubs") allowing you to dynamically debug anything running in your hardware target, from bare metal software to OS-backed programs, including their threads, the underlying drivers, etc. All with a single GDB session and without any JTAG probe.

Hello World Example

This repository contains the freemium distribution of Alpha for any version of the Raspberry Pi.

Table of Contents

Generated by gh-md-toc

Use Cases

Bare Metal Programming

Bare metal programming is ideal for benchmarking, high-performance programs & low-level prototyping.

Alpha provides a modern & convenient bare metal programming environment:

  • A ready-to-use hardware state thanks to more advanced hardware initializations than what firmwares or bootloaders usually do, including the floating point unit, caches and a convenient address space. Note that only the 32-bit mode of the ARM architecture is supported and that other modes are part of our business-level plan. Please, contact us for more details.

  • The ability to run standard C programs bare metal. C library functions involving syscalls (malloc, printf, etc.) are delegated to GDB by Alpha.

Benchmarking

The hardest part of benchmarking is understanding the results. And this is the main reason for running benchmarks bare metal:

  1. Being alone running on the hardware to:

    • Avoid interferences of concurrent or parallel programs.
    • Be able to easily reach the best or worst cases.
  2. Being at the closest possible level to the hardware/software interface to:

    • Understand perfectly what the hardware is doing.
    • Get rid of too high-level abstractions possibly hiding implementation details.

Writing drivers

The Raspberry Pi has a nice set of I/O interfaces (SPI, I2C, GPIO, USB, etc.) which makes it a good candidate to write the low-level parts of your drivers.

You can easily write, debug and test your I/O peripheral driver just its hardware documentation and GDB. You can then integrate it in any OS driver API such as Linux, FreeRTOS, etc. Note that is a very good practice to be able to easily test a driver.

Simply plug your I/O peripheral and get started:

  1. You can start exploring the hardware/software interface through GDB memory read and write commands.

  2. Focus on what really matters thanks a ready-to-use execution environment incluing supervisor-level execution privileges.

High performance

The highest levels of performance and responsiveness can be easily reached bare metal. Being alone on the machine, right above the hardware/software interface, allows to avoid time-consuming calls and to reach the fastest response times and throughputs.

Learning by doing

Learning embedded software usually comes at a price you can now avoid with this GDB-centric approach. No extra JTAG probe, no complex firmware & bootloader dependency and no complex tools. Here, it's a simple matter of powering on the Raspberry Pi and launching GDB to load a program and run/debug/test it using the same tools (GCC) and file formats (ELF) as usual native programming. GDB is the most famous debugging tool which is much more than a simple debugger.

Moreover, Alpha can catch common programming errors such as memory access violations to stop the execution exactly where the mistake happened:

(gdb) monitor gdb/catch
   RST : no  : Reset Exception
   UND : no  : Undefined Instruction Exception
   SWI : no  : Software Interrupt Exception
 PABRT : no  : Prefetch Abort Exception
 DABRT : no  : Data Abort Exception
   IRQ : no  : IRQ (interrupt) Exception
  FIQR : no  : FIQ (fast interrupt) Exception
(gdb) monitor gdb/catch DABRT yes
 DABRT : yes : Data Abort Exception

So have fun and enjoy exploring the GPU, enabling another core, communicating through USB, or whatever comes to your mind.

Operating System Debugging

OS-aware debugging is part of our business-level plan and is disabled in this version. Please, contact us for more details.

Multi-core Debugging

Multi-core debugging is part of our business-level plan and is disabled in this version. Please, contact us for more details.

A convenient programming environment

A bare metal C library

Running bare metal does not mean at all having to write everything from scratch and in assembly. This repository shows program examples written in C and involving calls to the C library, such as printf() and scanf().

The provided C library is the newlib (because it can be directly integrated into GCC), which relies on the POSIX syscalls. Some of them

  • the most useful ones when prototyping, benchmarking & testing - can be handled by GDB (see below), while others are implemented bare metal and others not at all.

List of delegated syscalls

Syscalls are delegated to GDB by Alpha through its remote protocol. Their number and their arguments are limited but it is enough to print convenience logs, write benchmark results into CSV files, etc.

  • open
  • close
  • read
  • write
  • lseek
  • rename
  • unlink
  • stat/fstat
  • gettimeofday
  • isatty
  • system

GDB File I/O

List of implemented syscalls

Syscalls that are implemented and can be used bare metal:

  • brk: requires a global symbol _end as beginning of the free area. We provide it through our linker script sdk/link.ld as the end of the program.

A stack

The stack address is configured in the linker script through the __stack symbol. Its maximum size is thus the configured address minus the next non-free memory region.

An address space

Alpha maps a useful address space including the RAM and the memory-mapped I/Os:

RPi Address Space

An extended GDB server

Exiting GDB resets the SoC

Resetting the hardware is a good practice to avoid side-effects and make things repeatable. Correctly leaving GDB sends a kill command to the target, which is translated by Alpha into a SoC reset, so you don't have to bother turning it off and on again to restart from scratch.

Stopping the execution

Asynchronously interrupt the Raspberry Pi while it is executing your program using the command interrupt or the SIGINT signal through the ctrlc keystroke.

For example:

(gdb) continue
Continuing.
^C
Program received signal SIGSTOP, Stopped (signal).
0x0000921c in __ieee754_sqrt ()
(gdb) # The execution is stopped
(gdb) continue &
Continuing.
(gdb) interrupt
Program received signal SIGSTOP, Stopped (signal).
0x0000921c in __ieee754_sqrt ()
(gdb) # The execution is stopped

This feature requires external interrupts which must be unmasked (enabled) to allow them. Here is an example using GDB's built-in scripting:

(gdb) print /x $cpsr &= ~(1 << 7)
$1 = 0x6000015f

The raytracer example below uses it to be able to interrupt endless loop and quit GDB.

Alpha-specific commands

Alpha provides extra GDB commands accessible through the monitor command:

(gdb) monitor help
help [COMMAND]
    Print help of all or help of COMMAND in parameter

version
    Print version of Alpha Target

mr8 ADDRESS [COUNT]
    Read COUNT or 1 8bit word at ADDRESS

mr16 ADDRESS [COUNT]
    Read COUNT or 1 16bit word at ADDRESS

mr32 ADDRESS [COUNT]
    Read COUNT or 1 32bit word at ADDRESS

mw8 ADDRESS VALUE
    Write the 8bit word VALUE at ADDRESS

mw16 ADDRESS VALUE
    Write the 16bit word VALUE at ADDRESS

mw32 ADDRESS VALUE
    Write the 32bit word VALUE at ADDRESS

fill32 ADDRESS COUNT VALUE
    Fill at ADDRESS COUNT 32bit word with VALUE

gdb/wcet [yes|no]
    Print or set Alpha WCET mode

gdb/catch
    Print the list of exceptions that can be caught by Alpha

gdb/catch EXCEPTION [yes|no]
    Print or set/unset the catching of EXCEPTION

Installation

GDB remote link with the Raspberry Pi

Alpha

Installing Alpha into your Raspberry Pi is very easy. You simply need to copy boot/{Alpha.bin, config.txt} into the boot partition of your Raspberry Pi' SD card. Alpha is then started by the Raspberry Pi's bootloader from its SD card according to config.txt directives (load & start address).

The script scripts/install-rpi-boot.sh creates a new SD card from scratch by:

  1. Downloading the officially distributed firmware and bootloader into boot/.
  2. Formatting the SD card in FAT32 (without partitioning it).
  3. Copying every files in boot/ into the SD card.
$ ./scripts/install-rpi-boot.sh /dev/<your SD card>
[+] Downloading the Raspberry Pi's firmware version 1.20161215
######################################################################## 100.0%
######################################################################## 100.0%
[+] Temporarily mounting `/dev/<your SD card>` into `/tmp/rpi-sdcard-mountpoint`
[+] Installing the RPi firmware and the Alpha debugger
'boot/bootcode.bin' -> '/tmp/rpi-sdcard-mountpoint/bootcode.bin'
'boot/start.elf' -> '/tmp/rpi-sdcard-mountpoint/start.elf'
'boot/Alpha.bin' -> '/tmp/rpi-sdcard-mountpoint/Alpha.bin'
'boot/config.txt' -> '/tmp/rpi-sdcard-mountpoint/config.txt'
[+] Checking the integrity
/tmp/rpi-sdcard-mountpoint/bootcode.bin: OK
/tmp/rpi-sdcard-mountpoint/start.elf: OK
/tmp/rpi-sdcard-mountpoint/Alpha.bin: OK
[+] Un-mounting `/tmp/rpi-sdcard-mountpoint`
[+] Your SD card is ready!
[+] You can now insert it into the RPi and use Alpha through the RPI's Mini-UART

Wiring

Using GDB in client/server mode requires a link between your workstation and your Raspberry Pi. For portability reasons, we chose the Raspberry Pi's Mini-UART. You can connect it to your workstation using a USB-UART TTL 3.3V (not 5V) converter.

Here are some random converter references:

Using a TTL Cable

Using a converter

Examples

Two bare metal programs are provided as examples: a hello world including bare metal calls to printf() and scanf(), and a raytracer using the GPU.

Note that we use docker to produce our development environments and build a Debian image with the expected tools, including the GCC toolchain for the ARM architecture. The Makefile command make shell builds the docker image according to sdk/Dockerfile. It is up to you to use it or use instead your own setup (and you can find the list of required dependencies in the sdk/Dockerfile).

$ make shell
docker build -f sdk/Dockerfile --build-arg uid=$(id -u) .
Sending build context to Docker daemon 3.843 MB
Step 1 : FROM debian:stretch
...
Successfully built 730a81db9233
user@3979cd200f4b:/home/user/farjump/raspberry-pi$ # Let's get started

GDB is then used to remotely load the program. The file run.gdb is a helper GDB script:

  1. Connecting to the target through the TTY interface of the TTL cable.
  2. Loading the ELF executable.
  3. Starting running the program until entering the main() function.

You need to replace the serial interface /dev/ttyUSB0 with yours (usually /dev/ttyUSB*, /dev/ttyACM*, /dev/cu.* ... according to your OS).

$ arm-none-eabi-gdb -x run.gdb <your ELF program>

Hello World

Compiling

$ make hello.elf
arm-none-eabi-gcc -specs=sdk/Alpha.specs -mfloat-abi=hard -mfpu=vfp -march=armv6zk -mtune=arm1176jzf-s -g3 -ggdb -Wl,-Tsdk/link.ld -Lsdk -Wl,-umalloc -Wl,-Map,hello.map -o hello.elf  src/hello-world/HelloWorld.c

Running

user@f76db25a61c1:/home/user/farjump/raspberry-pi$ arm-none-eabi-gdb -x run.gdb hello.elf
Reading symbols from hello.elf...done.
0x07f10570 in ?? ()
Loading section .entry, size 0x14f lma 0x8000
Loading section .text, size 0x11ed4 lma 0x8150
Loading section .init, size 0x18 lma 0x1a024
Loading section .fini, size 0x18 lma 0x1a03c
Loading section .rodata, size 0x50c lma 0x1a058
Loading section .ARM.exidx, size 0x8 lma 0x1a564
Loading section .eh_frame, size 0x4 lma 0x1a56c
Loading section .init_array, size 0x8 lma 0x2a570
Loading section .fini_array, size 0x4 lma 0x2a578
Loading section .jcr, size 0x4 lma 0x2a57c
Loading section .data, size 0x9ac lma 0x2a580
Start address 0x820c, load size 77607
Transfer rate: 10 KB/sec, 892 bytes/write.
Temporary breakpoint 1 at 0x832c: file src/hello-world/HelloWorld.c, line 7.

Temporary breakpoint 1, main () at src/hello-world/HelloWorld.c:7
7         printf("Enter a string: \e[?25h");

(gdb) # We have reached the main() function, have fun now ;)
(gdb) continue
Continuing.

Enter a string: Farjumper
RPi says "Hello Farjumper!"

Program received signal SIGTRAP, Trace/breakpoint trap.
_exit (rc=0) at SYSFILEIO/MAKEFILE/../SOURCE/SYSFILEIO_EXIT.c:11
11      SYSFILEIO/MAKEFILE/../SOURCE/SYSFILEIO_EXIT.c: No such file or directory.

(gdb) quit
user@f76db25a61c1:/home/user/farjump/raspberry-pi$ # The RPi resets.

Raytracer

Compiling

$ make raytracer.elf
arm-none-eabi-gcc -specs=sdk/Alpha.specs -mfloat-abi=hard -mfpu=vfp -march=armv6zk -mtune=arm1176jzf-s -g3 -ggdb -Wl,-Tsdk/link.ld -Lsdk -Wl,-umalloc -Wl,-Map,raytracer.map -o raytracer.elf -Og src/raytracer/main.c src/raytracer/Raytracing.c src/raytracer/VC.c src/raytracer/VC_aligned_buffer.S -lm

Running

If you want to watch the video output, plug first a screen to your RPi's HDMI port ;)

user@f76db25a61c1:/home/user/farjump/raspberry-pi$ arm-none-eabi-gdb -x run.gdb raytracer.elf
Reading symbols from raytracer.elf...done.
0x07f10570 in ?? ()
Loading section .entry, size 0x14f lma 0x8000
Loading section .text, size 0x1890 lma 0x8150
Loading section .init, size 0x18 lma 0x99e0
Loading section .fini, size 0x18 lma 0x99f8
Loading section .rodata, size 0xc lma 0x9a10
Loading section .ARM.exidx, size 0x8 lma 0x9a1c
Loading section .eh_frame, size 0x4 lma 0x9a24
Loading section .init_array, size 0x8 lma 0x19a28
Loading section .fini_array, size 0x4 lma 0x19a30
Loading section .jcr, size 0x4 lma 0x19a34
Loading section .data, size 0x590 lma 0x19a38
Start address 0x820c, load size 8135
Transfer rate: 10 KB/sec, 451 bytes/write.
Temporary breakpoint 1 at 0x8320: file src/raytracer/main.c, line 221.

Temporary breakpoint 1, main () at src/raytracer/main.c:221
221     {

(gdb) # Enable external interrupts to be able to stop the execution later
(gdb) print /x $cpsr &= ~(1 << 7)
$1 = 0x6000015f

(gdb) continue
Continuing.
^C
Program received signal SIGSTOP, Stopped (signal).
0x0000921c in __ieee754_sqrt ()

(gdb) quit
user@f76db25a61c1:/home/user/farjump/raspberry-pi$ # The RPi resets.

Support

Support is provided through this repository's issue board. Feel free to also contact us.

Licensing

See LICENSE for the full license text.