Stephen Smith's Blog

Musings on Machine Learning…

Assembly Language on the Raspberry Pi Pico

with 12 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

12 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. […]

    • I’m having some difficulty with cmake and compiling for the pico – if you could provide a bit more detail – I have your “RPi assembly language programming book” – and have both built and compiling many examples.

      I’m no doubt missing something, but …

      Look forward to hearing from you

      Phil

      May 14, 2021 at 4:01 pm

      • It depends a lot on what your host computer is. The easiest to do this with is a Raspberry Pi 4, then there is a really good installation script that installs everything you need. Otherwise, some more details on the error would be helpful.

        smist08

        May 15, 2021 at 8:08 pm

  4. Yeap – sorry I realized the info was a bit light, I’m using a CM3 with my pico – can nice build C projects to upload no issues – all work and run on the pico.

    I’m struggling with building the bit-banging project, when attempting to make the project – I repeatedly get this error

    “/usr/lib/gcc/arm-none-eabi/7.3.1/../../../arm-none-eabi/bin/ld: CMakeFiles/flashledsasm.dir/home/pi/pico/pico-sdk/src/rp2_common/pico_standard_link/crt0.S.obj: in function `__get_current_exception’:
    (.reset+0x9c): undefined reference to `main’
    collect2: error: ld returned 1 exit status
    make[2]: *** [CMakeFiles/flashledsasm.dir/build.make:657: flashledsasm.elf] Error 1
    make[1]: *** [CMakeFiles/Makefile2:1469: CMakeFiles/flashledsasm.dir/all] Error 2
    make: *** [Makefile:84: all] Error 2”

    I have probably not set up something – any ideas

    Phil

    May 17, 2021 at 10:26 am

  5. Just an FYI I also get the same on my Pi4

    ph1ljPhil

    May 17, 2021 at 11:59 am

    • Uhm, I have triple checked that the main is called correctly in the .s file :-

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

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

      .thumb_func
      .global main @ Provide program starting address to linker

      .align 4 @ necessary alignment

      main:

      @ Init each of the three pins and set them to output
      ……

      I’m definitely missing something or perhaps my CMakeLists.txt is incorrect


      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)

      I’ll keep at it, although not exactly sure were to go

      Phil

      May 17, 2021 at 2:58 pm

      • I wonder if there are differences in various versions of the SDK. You might find and look in crt0.S and ensure it calls main and not _main or something.

        smist08

        May 17, 2021 at 4:04 pm

  6. Just an update – the first version ([Assembly Language on the Raspberry Pi Pico] includes the wrapping ‘C’ now works, I missed the configuration of project-generation detail – however the bit-banging version compiles but does not run (*.uf2 uploaded to pico – no led functionality)

    Once again I’m sure it me – how did you ‘make’ the project file ?

    Phil

    May 18, 2021 at 9:58 am

  7. RP2040 Assembly Language Programming book
    Not sure where to post this.

    On page 80, Figure 5-1 under Carry
    “For subtraction-type operations, this flag is set if the result requires a borrow”
    should be
    “this flag is cleared if the result requires a borrow”

    Reference page 70 first paragraph, and here too
    https://www.keil.com/support/man/docs/armasm/armasm_dom1359731160609.htm
    “C Set to 1 when the operation results in a carry, or when a subtraction results in no borrow, cleared to 0 otherwise.”

    Please note this is the opposite to most micros where C is set if there is a borrow in subtraction.

    Ray

    November 2, 2021 at 8:40 pm


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 )

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: