Stephen Smith's Blog

Musings on Machine Learning…

Assembly Language on the Raspberry Pi Pico

with 3 comments


Introduction

The Raspberry Pi Pico is the Raspberry Foundation’s first entry into the domain of Arduino style microcontrollers. The board contains Raspberry’s own designed SoC (System on a Chip) containing a dual core ARM Cortex-M0+ CPU along with memory and a collection of I/O circuitry. There are no keyboard, mouse or monitor ports on the board, only a micro-USB to connect to a host computer, a number of GPIO pins and three debug pins. This SoC is called the RP2040 and is licensed to other companies to use in their own boards. Raspberry supports programming this board in either C/C++ or MicroPython. The C/C++ SDK also supports Assembly Language programming to some degree and this article is a look at my first attempt to write an Assembly Language program for this board. I ran into a few problems and still have a few things to figure out and we’ll explain those in the article. We’ll write an Assembly Language version of the program we wrote in C last time to flash three connected LEDs.

ARM Cortex-M0+ Assembly Language

I blogged about 32-bit ARM Assembly Language here, and then presented the flashing LED Assembly Language program for the Raspberry Pi here. Further I wrote a whole book on 32-bit ARM Assembly Language Programming: “Raspberry Pi Assembly Language Programming”. These are all oriented to ARM’s full A-series processors which include floating point units (FPU), vector processors, virtual memory support and much more. The ARM M-series processors are a subset of these, designed to be low cost, use little memory and be very power efficient. The ARM M-series processors only contain what are called the ARM “thumb” instructions. Normally, on an A-series processor, each instruction takes 32-bits, but for some applications this uses too much memory, so ARM came up with “thumb” instructions where if the processor is operating in “thumb” mode then each instruction is only 16-bits in length, thus only using half the memory. The original set of “thumb” instructions was too limited, so ARM added a way to run some 32-bit instructions in with the 16-bit instructions and that makes the modern “thumb” instructions set used by the M-series processors. One consequence of using the “thumb” instructions is that registers R8 to R12 are not accessible and hence not implemented on the chip, thus saving circuitry. The registers you do have are all 32-bit and the Raspberry RP2040 has special multiplication and division circuitry to perform these operations quickly.

Code

This program uses the C/C++ SDK to access the GPIO pins, this means this Assembly Language program is quite similar to last week’s C program. To call a routine in Assembly, you put the first parameter in R0, the second in R1 and then call Branch with Link (BL). BL places the address of the next instruction into the LR register, so the called return returns by branching to the address contained in the LR register. When calling functions there is a convention on who has to save which register on the stack, but we don’t use any register over the function calls, so we don’t need to do this. This program is set up as an infinite loop, since there is nothing for the main routine to return to and if it does return the processor halts.

Assembly Language code:

@
@ Assembler program to flash three LEDs connected to the
@ Raspberry Pi Pico GPIO port using the Pico SDK.
@
@

.EQU LED_PIN1, 18
.EQU LED_PIN2, 19
.EQU LED_PIN3, 20
.EQU GPIO_OUT, 1
.EQU sleep_time, 200

.global main_asm             @ Provide program starting address to linker
main_asm:

MOV R0, #LED_PIN1
BL gpio_init
MOV R0, #LED_PIN1
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
MOV R0, #LED_PIN2
BL gpio_init
MOV R0, #LED_PIN2
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
MOV R0, #LED_PIN3
BL gpio_init
MOV R0, #LED_PIN3
MOV R1, #GPIO_OUT
BL link_gpio_set_dir
loop:   MOV R0, #LED_PIN1
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN1
MOV R1, #0
BL link_gpio_put
MOV R0, #LED_PIN2
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN2
MOV R1, #0
BL link_gpio_put
MOV R0, #LED_PIN3
MOV R1, #1
BL link_gpio_put
LDR R0, =sleep_time
BL sleep_ms
MOV R0, #LED_PIN3
MOV R1, #0
BL link_gpio_put
B       loop

.data

      .align  4 @ necessary alignment

I didn’t intend to include any C code, but I ran into a couple of problems that require it. One is that a large number of SDK functions are inline C functions which means they can’t be called from outside of C. In our case two functions gpio_set_dir and gpio_put are inline and required wrapping. The other problem is that if the main program is Assembly Language then the code to initialize the board doesn’t seem to be called. I think this is a matter of setting the correct CMake options, but I haven’t had a chance to figure it out yet. For now we have main in the C code and then call the Assembly Language main routine.

C code:

#include “hardware/gpio.h”

void link_gpio_set_dir(int pin, int dir)
{
gpio_set_dir(pin, dir);
}

void link_gpio_put(int pin, int value)
{
gpio_put(pin, value);
}

void main()
{
main_asm();
}

The Raspberry Pi Pico SDK uses the CMake system to manage builds. The SDK provides a large set of build rules. You run CMake and then it creates a makefile that compiles your program.

CMake file:

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(test_project C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

include_directories(${CMAKE_SOURCE_DIR})

add_executable(flashledsasm
  mainmem.S
  sdklink.c
)

pico_enable_stdio_uart(flashledsasm 1)
pico_add_extra_outputs(flashledsasm)
target_link_libraries(flashledsasm pico_stdlib)

Still To-Do

The program works, but there are a few things I’m not happy about. The Raspberry Pi Pico SDK is pretty new, so there aren’t a lot of answers on StackOverflow yet. The good thing is that it is all open source, so it is just a matter of time to figure out the code. Here is what I’ll be working on:

  1. How to have main be in Assembly Language and have the board properly initialized. Match the C startup sequence.
  2. Figure out the details of the GPIO registers and have Assembly Language versions of the inline C code that accesses these. They are similar to those on the full Raspberry Pi, but different.
  3. How to get constants from the C include file, on first try this didn’t work and gave syntax errors, but the SDK says they should be usable from Assembly Language. They might need a couple of fixes.

Summary

I planned to write a 100% Assembly Language program, but didn’t quite make it. At least the program works, showing you can include Assembly Language in your RP2040 projects. The support to build using the GCC macro assembler is all there and besides some interactions with the SDK all seems to work well. Of course the Raspberry Pi Pico SDK is pretty new so there will be a lot of updates and there are still a number of undocumented holes to investigate.

Written by smist08

April 16, 2021 at 9:43 am

3 Responses

Subscribe to comments with RSS.

  1. Very informative. Thank you!

    jlgreer1

    April 16, 2021 at 10:38 am

  2. […] last week’s article, I presented my first Assembly Language program on the Raspberry Pi Pico. The program worked, but […]

  3. […] Last week, I introduced my first Assembly Language program for the Raspberry Pi Pico. This was a version of my flashing LED program that I implemented in a number of programming languages for the regular Raspberry Pi. In the original article, I required three routines written in C to make things work. Yesterday, I showed how to remove one of these C routines, namely to have the main routine written in Assembly Language. Today, I’ll show how to remove the two remaining C routines, which were wrappers for two SDK routines which are implemented as inline C functions and as a consequence only usable from C code. […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: