RSS

Archive for June, 2023

VSCode Debugging with NuttX and Raspberry Pi PicoW

Wednesday, June 14th, 2023

VS Code Halted in NuttX __start

In the previous post, we managed to get GDB working with NuttX running on the Raspberry Pi PicoW. In this post we will look at using VSCode to debug NuttX.

For a large part of this post it was case of following Shawn Hymels guide Raspberry Pi Pico and RP2040 – C/C++ Part 2 Debugging with VS Code. This is an excellent guide and I recommend using it as a companion to this post.

We will start with the assumption that you have followed the previous post in this series and have a working NuttX build for the Raspberry Pi PicoW. We will also assume that you have a working VSCode installation with the Cortex-Debug extension installed.

Configuration Files

We will need to create (or modify) three configuration files to allow VSCode to debug NuttX.

  • launch.json
  • tasks.json
  • settings.json

In the first article of this series we created the directory NuttX-PicoW to hold the apps and nuttx folders holding out NuttX code. This directory is the project directory or in VS Code parlance, the workspaceFolder. The workspaceFolder should also contain the Raspberry Pi PicoW SDK and the Raspberry Pi specific version of openocd.

We now add a .vscode directory to the workspaceFolder. This directory will hold the three configuration files listed above.

Start by opening VS Code and opening the workspaceFolder. This should show the four folders already in the workspaceFolder. Now create a .vscode directory in the workspaceFolder if it does not already exist.

setting.json

Only one entry is required in the settings.json file and this is the location of the openocd executable. If you have followed these posts so far this will be in the openocd/src folder. Create a setting.json file in the .vscode folder and add the following to the file.

{
    "cortex-debug.openocdPath": "${workspaceFolder}/openocd/src/openocd"
}

Note that the name of the executable may vary depending upon your operating system this post is being written from a MacOS perspective.

tasks.json

The tasks.json file holds the entry that will be used to build NuttX prior to deployment. In Shawns document the projects being worked on use the cmake system. We need to modify this to build NuttX using make. We want VS Code to use the command make -C ${workspaceFolder}/nuttx -j to build NuttX. The build task below will create a task Build NuttX to do just this:

{
    "version": "2.0.0",
    "tasks": [
        {
          "label": "Build NuttX",
          "type": "cppbuild",
          "command": "make",
          "args": [
            "-C",
            "${workspaceFolder}/nuttx",
            "-j"
          ]
        }
      ]  
}

launch.json

Of the three files we are creating, the launch.json file is the most complex. Much of the file remains the same as that presented by Shawn but there are some differences. The file used here looks like this:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Pico Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}/nuttx/nuttx.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "gdbPath" : "arm-none-eabi-gdb",
            "device": "RP2040",
            "configFiles": [
                "interface/cmsis-dap.cfg",
                "target/rp2040.cfg"
            ],
            "svdFile": "${workspaceFolder}/pico-sdk/src/rp2040/hardware_regs/rp2040.svd",
            "runToEntryPoint": "__start",
            "searchDir": ["${workspaceFolder}/openocd/tcl"],
            "openOCDLaunchCommands": ["adapter speed 5000"],
            "preLaunchTask": "Build NuttX"
        }
    ]
}

The following items are the ones that need to be changed:

“executable”: “${workspaceFolder}/nuttx/nuttx.elf”

This is the path to the executable that will be created by the build system. In this cse it is the NuttX ELF file. This may need to be changed to “executable”: “${workspaceFolder}/nuttx/nuttx” depending upon the output of the build system, this may simply be nuttx.

“configFiles”

The recent versions of the picoprobe software use a different interface to talk to the picoprobe. For versions 1.01 and above of the picoprobe software this interface changed from picoprobe.cfg to cmsis-dap.cfg.

“svdFile”: “${workspaceFolder}/pico-sdk/src/rp2040/hardware_regs/rp2040.svd”

We have a version of the Pico SDK specifically for our build requirements and so for this reason the location of the file is changed to reference the workspaceFolder rather than the global PICO_SDK_PATH environment variable.

“runToEntryPoint”: “__start”

This entry replaces the “runToMain”: true entry. Unlike convention C/C++ applications, NuttX does not have a main method. Having the runToMain entry generates an error stating that main cannot be found and stops at the first executable statement in our code, namely in __start. Replacing runToMain with runToEntryPoint achieves the same thing but does not generate the error.

Doing this also removes the need to have the postRestartCommands specified.

“searchDir”: [“${workspaceFolder}/openocd/tcl”]

This entry is used by openocd to look for the interface and target configuration files specified in the configFiles entry.

“openOCDLaunchCommands”: [“adapter speed 5000”]

These commands are executed by openocd when it starts. The change to the adapter speed is required and is documented in Getting Started with Raspberry Pi Pico documentation from the Raspberry Pi Foundation.

“preLaunchTask”: “Build NuttX”

The final entry tells Cortex-Debug how to build NuttX. It references the task we previously defined in the tasks.json file.

Testing

Testing this should simply be a case of saving all of the files above and pressing F5 in VS Code. If the changes have been successful then VS Code will first try to build NuttX in a Terminal window and it will then deploy NuttX to the Pico board and start the debugger. VS Code should look something like this (click on image for full view):

Debugging PicoW with VSCode

Debugging PicoW with VSCode

The inclusion of the SVD file allows us to examine the PicoW peripheral registers as well as the core registers:

PicoW Peripheral Registers in VSCode

PicoW Peripheral Registers in VSCode

Pico Cortex Registers in VSCode

Pico Cortex Registers in VSCode

Conclusion

GDB is a great debugger but it is often more convenient to use an IDE to debug your code. VS Code with the Cortex-Debug extension allow visual debugging of NuttX with a few nice additional features thrown in:

  • Easily viewed call stacks for both cores.
  • The SVD file allows the peripheral registers to be viewed through VS Code

We should also note that the use of VS Code resolved the issues noted at the end of the previous post as Cortex-Debug is able to deploy a binary using the picoprobe without resorting to the UF2 method of deployment. This results in a seamless build, deploy and debug process.

We have two debug options and it is now down to personal preferences as to which one to use.

Debugging NuttX on the Raspberry Pi Pico

Sunday, June 11th, 2023

GDB showing thread information

At some point in the development lifecycle we will hit some unexpected application behaviour. At this point we reach for the debugger to allow us to connect to the application an interrogate the state and hopefully we will be able to determine the cause of the problem. Working with NuttX is no different from any other application. The only complication we have is connecting to application running on the PicoW requires a debug adapter. Luckily, the Raspberry Pi Foundation has an affordable solution to this problem.

Environment

The getting started guide for the Pico has instructions on how to set up the environment for Linux, Mac and Windows. This post will be following the guide for MacOS adding some hardware to allow for a stable connection between the debug probe and the PicoW.

There are two options for the debug probe:

  • The Raspberry Pi debug probe
  • Use a Pico programmed as a debug probe

I have a number of Pico and PicoW boards and so the later option, using a Pico programmed as a debug probe, is going to be the most cost effective and this is the route we will look at here.

Hardware

For the purpose of discussion we will use the following terms to describe the two boards:

  • Picoprobe – Pico that is programmed to be a debug probe
  • Target – PicoW that is running the application we wish to debug

The getting stared guide shows how two Pico boards should be connected to provide both debugging and serial console output through a UART on the target board. This shows the two boards on a breadboard setup (image is taken from the Raspberry Pi Pico getting started guide under CC-BY-ND license).

Picoprobe and Pico Fritzing

Picoprobe and Pico Fritzing

The same setup can be reproduced on protoboard. This will make the setup a little more robust and permanent. Breaking out the soldering iron and the parts bin yielded the following:

Pico on Protoboard

Pico on Protoboard

The flying leads are needed to connect to the SWD debug headers on the PicoW as it was not possible to connect these through to the protoboard. The black (GND) and red (5V) wires provide power to the target board. This means that we only have one connector to the system, namely to the Picoprobe but we must take care not to exceed the power capabilities of the USB connection.

The white (SWCLK) and blue (SWDIO) flying leads provide the debug connection between the two boards. As with the permanent connections above, the black lead is a GND connection.

The blue and yellow leads connected permanently to the protoboard connect the UART from the target board through to the picoprobe. The picoprobe will use the USB connection to present the host computer with a connection through to the UART on the target board.

Software

The first piece of software needed is the Picoprobe software itself. There are instruction on how to build this but I found the simplest thing to do was to download the latest release from the Picoprobe GitHub repository. At the time of writing this is version 1.0.2. The Picoprobe software is deployed to the Pico being used as a Picoprobe in the usual way, reset the Pico whilst hold the BootSel button and then drag and drop the firmware file onto the drive present to the host computer.

The next thing we need is a version of openocd that can talk to the Picoprobe. The machine I am using is used to develop software for multiple boards and so the preferable solution is to build the Raspberry Pi Pico version of openocd for this setup and then reference this locally rather than install the software globally. This is the same approach taken for the various development environment I use to prevent them interfering with each other.

Following the guide (for Mac) we need to execute the following commands:

cd ~/pico
git clone https://github.com/raspberrypi/openocd.git --branch picoprobe --depth=1 $ cd openocd
export PATH="/usr/local/opt/texinfo/bin:$PATH" 1
./bootstrap
./configure --enable-picoprobe --disable-werror 2
make -j4

Check the latest version of the guide for instructions for your OS.

Now to try openocd… This is where I hit a problem caused by having out of date documentation. Following the latest documentation we need to execute the following command:

src/openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -s tcl

I was following I hit two errors due to the out of date documentation:

  • Error: Can’t find a picoprobe device! Please check device connections and permissions.
  • Error: CMSIS-DAP command CMD_DAP_SWJ_CLOCK failed.

Both of these errors were corrected by using the command above to connect to the debug probe. Moral of the story, make sure you are using the latest information. If everything goes well then we should be rewarded with something similar to the following output:

Open On-Chip Debugger 0.11.0-g4f2ae61 (2023-06-10-18:20)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 5000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E66058388356A232
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections

If we have got here then we have the the debug probe programmed and we have a copy of openocd that can communicate with the picoprobe.

Reconfiguring NuttX

In previous posts we used UART over USB to communicate with the host computer. The picoprobe gives us another option, using the UART on the target board which is routed through the picoprobe. We must reconfigure NuttX in order to take advantage of the UART redirection. The configuration we want is raspberrypi-pico-w:nsh, so following the first post in this series and making the change to the configuration we need to execute the following:

make distclean
./tools/configure.sh -l raspberrypi-PicoW:nsh
make -j

At the end of this process we should have a new nuttx.uf2 file configured to use the UART port on the target board to communicate with the NuttX shell. This UF2 file can be deployed using the usual deployment process (Bootsel and reset followed by drag and dropping the file) to the target board.

At this point we should have two Pico boards programmed, one to be the picoprobe and one to be the target board (with NuttX). Now we can connect the boards (in my case using the protoboard shown above) and verify that NuttX is active and communicating through the UART.

Plugging the Pico (picoprobe) and the PicoW (target board) into the protoboard and then connecting the host computer to the picoprobe through USB presents the computer with a new serial port, usbmodem14302. Connecting to this port and hitting enter shows the nsh prompt. Asking for help shows the expected output.

nsh> help
help usage:  help [-v] [<cmd>]

    .         cat       df        free      mount     rmdir     truncate  
    [         cd        dmesg     help      mv        set       uname     
    ?         cp        echo      hexdump   printf    sleep     umount    
    alias     cmp       env       kill      ps        source    unset     
    unalias   dirname   exec      ls        pwd       test      uptime    
    basename  date      exit      mkdir     reboot    time      usleep    
    break     dd        false     mkrd      rm        true      xd        

Builtin Apps:
    nsh  sh   
nsh>

So we have a good UART connection and openocd can connect to the picoprobe.

Now we need to connect the debugger to openocd.

Debugging

To debug the board we will start with GDB, specifically arm-none-eabi-gdb-py as this has Python scripting enabled. We can use the scripting engine to automate some tasks later.

First thing to do is add a .gdbinit file to the directory we will be executing GDB from, in this case we will use the nuttx directory. The following commands should setup GDB and openocd ready for debugging. The nuttx ELF file will be loaded for us so by the time the GDB prompt is show we will have the symbol file loaded. Our .gdbinit file should look something like this:

set history save on
set history size unlimited
set history remove-duplicates unlimited
set history filename ~/.gdb_history

set output-radix 16
set mem inaccessible-by-default off

set remote hardware-breakpoint-limit 4
set remote hardware-watchpoint-limit 2

set confirm off

file nuttx
add-symbol-file -readnow nuttx

target extended-remote :3333
mon gdb_breakpoint_override hard

To debug we will need two terminal sessions open. In the first session run openocd as described above. In the second session start GDB (simply enter the command arm-none-eabi-gdb-py in the terminal session) making sure that you are in the directory containing the nuttx file and the .gdbinit file. If you are successful then you will see something like this:

GNU gdb (GDB) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin22.3.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
add symbol table from file "../nuttx/nuttx"

We can now start to debug the application on the board. A few simple tests, firstly, reset the board with the GDB command mon reset halt. We should see something like this:

target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ea msp: 0x20041f00
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ea msp: 0x20041f00

We can now set a breakpoint, say in nsh_main (b nsh_main) and run the application to the breakpoint (c). If we then ask for information about the threads (info thre) we should see something like this:

  Id   Target Id                                           Frame
* 1    Thread 1 (Name: rp2040.core0, state: breakpoint)    nsh_main (argc=0x1, argv=0x20003170) at nsh_main.c:58
  2    Thread 2 (Name: rp2040.core1, state: debug-request) 0x000000ea in ?? ()

Conclusion

Getting debugging up and running took a little more effort than I expected but was not expensive due to the ability to use a Pico to debug a Pico. There is some excellent debugger documentation on the NuttX web site. This suggests that openocd should support NuttX however I was not able to get this working with the Raspberry Pi release of openocd. It was a question of having Pico support or NuttX support. I’m going with the Pico support for the minute.

I was hoping to use the picoprobe board to deploy the application meaning that we only have one connection between the host computer and the development hardware. A little more research is going to be necessary to see if this can be done.

Adding SMP to NuttX on the Raspberry Pi PicoW

Tuesday, June 6th, 2023

ps Command Results without SMP Enabled

So far in this series we have configured NuttX to build C++ applications on the PicoW and we have created our own custom application and successfully deployed this to the board.

In this post we will look at configuring the system to use both cores on the RP2040 processor using the configuration we have developed over the previous posts. NuttX includes a SMP configuration already, raspberrypi-pico-w:smp, this uses UART0 for communication rather than USB hence we will look at add this configuration on top of the USB configuration.

Default Configuration

It appears that the default configuration for NuttX running on the PicoW is to run the processor as a single core processor. Running the ps in the NuttX shell yields the following output:

   PID GROUP PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK           STACK COMMAND
    0     0   0 FIFO     Kthread N-- Ready              0000000000000000 001000 Idle Task
    1     1 100 RR       Task    --- Running            0000000000000000 002000 nsh_main   

From the output we see that there are only two tasks running on the system. From this we can deduce that only one of the cores is enabled. If both cores were running then we should see two Idle Tasks running, one on each core plus the nsh_main task. This is confirmed in the SMP Documentation where we find the statement:

Each CPU will have its own IDLE task.

Now we have this confirmed, let’s move on to reconfiguring the system.

Enabling SMP

According to the documentation we need set three options through the configuration system in order to turn on SMP:

  • CONFIG_SMP
  • CONFIG_SMP_NCPUS
  • CONFIG_IDLETHREAD_STACKSIZE

Starting the configuration system with the make menuconfig we are presented with the top level options screen:

NuttX configuration system

NuttX configuration system

This poses the question “How do I find CONFIG_SMP and the rest of the options?”. Luckily, kconfig has a search option, pressing the / key presents the user with a search dialog box:

Search dialog here.

Typing SMP and pressing return shows a number of results, some are relevant to use, some are not. The longer the search term the more accurate the results will be. In our case, searching for SMP yields a number of results that are irrelevant. However, the top result looks like it is the one for us:

SMP Search results image Here.

The results give us a fair amount of information about the configuration options. If we look at the top entry for SMP (this is actually the option that we need to turn on) the system is giving us the following information:

  • The option name (SMP) which will translate to CONFIG_SMP in the various include files
  • The location of the option, in this case RTOS Features -> Tasks and Scheduling
  • In which file (and the line number) the option is defined, sched/Kconfig, line 271
  • Any other options that this option depends upon.
  • Any options that will be turned on as a result of selecting this option

You will also note a number in brackets at the side of Tasks and Scheduling. This is the short cut key, in this case 1 and pressing 1 will take us to the appropriate place in the configuration system.

RTOS Features options.

Selecting Tasks and Scheduling by pressing enter we are presented with the following:

Tasks and scheduling, no SMP.

Notice that there is no option to turn on SMP. Confused, so was I the first time I saw this. Pressing the ESC key a few times should take us back to the search results. Reading these more carefully we note the following:

Depends on: ARCH_HAVE_MULTICPU [=y] && ARCH_HAVE_TESTSET [=y] && ARCH_INTERRUPTSTACK [=0]!=0

So we have to ensure that three conditions are met in order to enable SMP:

  • HAVE_MULTICPU is set to turned on, this looks to be the case (ARCH_HAVE_MULTICPU [=y])
  • HAVE_TESTSET is turned on, again this looks to be the case (ARCH_HAVE_TESTSET [=y])
  • INTERRUPTSTACK has a non-zero value, this appears to be false (ARCH_INTERRUPTSTACK [=0]!=0)

It appears that we have not met all of the conditions to activate SMP. We need to look at defining a size for the interrupt stack. So using the search system again but this time for ARCH_INTERRUPTSTACK we get the following results:

Interrupt stack search results

Pressing the 1 key takes us directory to the entry we need to change. Setting the value to 2048 should be a reasonable starting point. We can always adjust this later if we find it is causing problems.

Exiting this menu and the search for ARCH_INTERRUPTSTACK we can search for SMP again. Pressing the 1 key in the search results presents us with a different view, this time we are taken to the SMP option and we can now turn this on. If we do this the system will add some new entries to the menu, one of which defines the number of CPUs available and this is set to 4. We will need to change this value to 2.

SMP Configured image.

We are now ready to build the system.

Build and Deploy NuttX

We should now be ready to build and deploy the system to the PicoW. Executing the following command will build NuttX and create the UF2 file ready for deployment:

make clean
make -j

At the end of the build process we can deploy the UF2 file to the board by putting the board in bootloader mode and dropping the file to the drive that is shown on the host computer. Se the first article in this series for more information on how to do this.

Time to connect our serial terminal to the PicoW and run the ps command once again:

SMP Enabled ps output.

As you ca see from the screen shot above, we now have two ide tasks as expected and the ps command has some additional information, notably the CPU that each task is allocated to.

Conclusion

Adding SMP to the system was not as simple as it first sounded. The main lesson from this was to check the dependencies in the search results. The requirement for an interrupt stack was not discussed in the SMP documentation and this caused some confusion initially. The help in the configuration system came to our rescue and the situation was easily recoverable.

If you have been following the series we are now at the point where we have think about some serious development. We now have space for our own application code, C++ is enabled and we are able to use both cores on the RP2040 microcontroller.

At this point the series will slow down a little as some of the features now need some in depth investigation most notable:

  • Using SMP correctly in NuttX
  • Interprocess communication between tasks running on different cores
  • RP2040 PIO and NuttX

Adding a User Application to NuttX

Tuesday, June 6th, 2023

Enable SSEM User Application

So far we have seen how to deploy NuttX to a Raspberry Pi PicoW and enable C++ in NuttX. Next up we will create our own application and deploy it to the PicoW.

Creating a New Application

The build system for NuttX applications uses a a makefile that is written in such a way that all we have to do to add a new application is simply create a directory containing some template files. The easiest way to do this is to use an existing application as a template. So let us start by making a copy of the helloxx application. This can be found in the apps repository in the examples directory. So we will start by making a recursive copy of this directory and renaming it ssem.

Looking inside the ssem directory we find a number of files:

helloxx_main.cxx
Make.defs
Kconfig
Makefile

Time to start editing the files.

helloxx_main.cxx

We will start by renaming the file to represent the application name, so we will rename this to ssem_main.cxx. Now editing the file we change all occurrences of helloxx_main to ssem_main. This will allow us to verify that we have indeed deployed the application.

Makefile

Make a similar change to the Makefile by changing all occurrences of helloxx to ssem. Make changes to the comments to reflect that this application is a new application. Our Makefile should now look something like this:

include $(APPDIR)/Make.defs

# SSEM Application

MAINSRC = ssem_main.cxx

# ssem built-in application info

PROGNAME = ssem
PRIORITY = SCHED_PRIORITY_DEFAULT
STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE)
MODULE = $(CONFIG_EXAMPLES_SSEM)

include $(APPDIR)/Application.mk

Make.defs

The Make.defs file conditionally adds the new application to the list of configured applications and ensures that the file will be built. This file should be edited to look something like this:

ifneq ($(CONFIG_EXAMPLES_SSEM),)
CONFIGURED_APPS += $(APPDIR)/examples/ssem
endif

Kconfig

The Kconfig file allows us to define and change application parameters, something we may look at later. For the moment it simply defines the application name and title and tells the configuration system how to turn compilation on or off. Making changes similar to the above we will have a file that contains something like this:

config EXAMPLES_SSEM
tristate "SSEM example"
default n
depends on HAVE_CXX
---help---
    Enable the SSEM application

Build the SSEM Application

Now we have set up the application we can start to reconfigure the build system and deploy the application. First thing to do is to clean the current build environment in order to make NuttX aware of the presence of the application. Issuing the command make clean will remove the previous build components.

Now we can configure the environment by issuing the make menuconfig command. This will present the configuration menuing system we saw in the previous post. So now head over to the following menu items and disable the Hello, world example and enable the SSEM example.

  • Main menu -> Application Configuration -> “Hello, World!” C++ example
  • Main menu -> Application Configuration -> SSEM example

We should now be ready to build the system, so execute the command make -j to build the system. The build system should show output similar to the following if the changes to the system have been made correctly:

Create version.h
LN: platform/board to /Projects/NuttX/apps/platform/dummy
Register: ssem
Register: nsh
Register: sh
CPP:  /Projects/NuttX/nuttx/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld-> /Projects/NuttX/nuttx/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.lLD: nuttx
Generating: nuttx.uf2
tools/rp2040/elf2uf2 nuttx nuttx.uf2;

Time to deploy NuttX to the board and check to see if the application has built correctly. So copy the NuttX.uf2 file to the board and connect a serial console to the serial port. Press enter a few times until the nsh prompt appears and now type help. The new application should appear in the list of built in applications:

Builtin Apps:
    nsh   sh    ssem  

Executing the ssem application should now present something similar to the following (depending upon how you modified the helloxx_main application code):


nsh> ssem
ssem_main: Saying hello from the dynamically constructed instance
CHelloWorld::HelloWorld: Hello, World!!
ssem_main: Saying hello from the instance constructed on the stack
CHelloWorld::HelloWorld: Hello, World!!
nsh>

Conclusion

The process of creating a new application and having this included in the NuttX build system is not a difficult task, it simply means editing a few files.

One key step is the make clean step as omitting this from a system which already contains build artefacts may not include the new application in the configuration process and hence exclude the files from the build.

In the next post we will add SMP to the configuration.

Enabling NuttX C++ Support on PicoW

Monday, June 5th, 2023

Enable C++ compiler support

In the last article we looked at getting NuttX running on the Raspberry Pi Pico. The system deployed the default applications which are written in C. In this post we will look at enabling C++ support.

The first thing to do is to follow the instructions in the previous post to get the development system downloaded and deployed. Next up we can clean any object files that may be remaining from any previous builds:

make distclean
./tools/configure.sh -l raspberrypi-pico-w:usbnsh

Now we have a clean system wew can enabled C++ compiler support. This is done through the configuration system which is accessed by executing the command:

make menuconfig

This will show the main menu for the configuration system:

NuttX configuration system

NuttX configuration system

Using the arrow and enter keys we need to navigate the menus and change the following options:

  • Main menu -> Library Routines -> Have C++ compiler
  • Main menu -> Application Configuration -> “Hello, World!” C++ example

Turning on the C++ compiler will show the following additional menu items:

Enable C++ compiler support

Enable C++ compiler support

The 12.1 release of the PicoW implementation deploys some additional applications which are not needed so we will turn these off in order to save memory. The two application to be disabled are:

  • ostest
  • getprime

These applications can be turned off using the configuration system, the menu items can be found here:

  • Application Configuration -> Testing -> OS test example
  • Application Configuration -> Testing -> getprime example

The system would be ready to be built using the make -j command. Once built, deploy the application to the board by copying the UF2 file to the board as discussed in the previous post. The board should reset automatically so connect a serial console to the board and hit enter a few times to get to the nsh prompt. Typing help at the command problem should show the built in commands plus the additional commands that have been deployed:

Builtin Apps:
    helloxx  nsh      sh

Finally we should verify that the C++ application has been built and deployed correctly by running the helloxx application. If all is well then the serial console should show the following output:

helloxx_main: Saying hello from the dynamically constructed instance
CHelloWorld::HelloWorld: Hello, World!!
helloxx_main: Saying hello from the instance constructed on the stack
CHelloWorld::HelloWorld: Hello, World!!

Conclusion

This is a small step forward getting C++ enabled ready for developing a new C++ application which we will look at in the next post.

In the next post we will add a new C++ user application to the system.

Deploying NuttX to the Raspberry Pi PicoW

Sunday, June 4th, 2023

Close up of Raspberry Pi PicoW

Currently taking a few weeks break and after a few days away from the computer I decided to do a little bit of research into the PicoW and NuttX. So time for a quick attempt at getting the system up and running.

I have for the last few years been working on the Meadow F7 board for Wilderness Labs. This board aims to provide a full .NET development environment bringing embedded development to a wider audience. There are three main technologies underpinning this board:

I spend much of my time working with NuttX and FreeRTOS so as you can gather, I’m not worried about working with C or C++. This gives a few options for working with the board:

  • Native development using the Pico SDK
  • NuttX
  • FreeRTOS

I decided to start with a look at NuttX as I am currently spending a lot of time working with this RTOS.

Let’s Follow the Guide

First thing to do is head over the the documentation for NuttX on the RP2040. The getting started guide is really good and I thought I would be able to use the copy of the Pico SDK I had already on my machine.

This is where I hit my first problem. NuttX requires a specific version of the Pico SDk and using this version would impact any other Pico development that I would undertake in the future. To overcome this I decided to take a second copy of the Pico SDK specifically for use when developing with NuttX.

Having said we would follow the guide, we are deviating already.

So first up we create a development environment for the PicoW (all instructions are for working on a Mac but should also work on Linux).

mkdir NuttX-PicoW
cd NuttX-PicoW
git clone -b 1.1.2 https://github.com/raspberrypi/pico-sdk.git
export PICO_SDK_PATH=$PWD/pico-sdk

Now we need to clone the NuttX OS and applications repositories:

git clone https://github.com/apache/nuttx.git nuttx
git clone https://github.com/apache/nuttx-apps.git apps
cd nuttx

Now we have the NuttX components cloned we need to make sure we are on the current releases. This is done using git to checkout the correct branches. At the time of writing the current release is 12.1 so the following commands will checkout the correct branches:

cd apps
git checkout releases/12.1
cd ../nuttx
git checkout releases/12.1

Now we have the correct branches we need to configure the system to work with the PicoW. There are several different boards and configurations available using the RP2040, not just the pico. The following command will list all of the available board configurations available for the Pico series of boards (not just the PicoW but all of the Pico boards):

./tools/configure.sh -L | grep pico

For NuttX 12.1 this returns the following list:

raspberrypi-pico:lcd1602
raspberrypi-pico:waveshare-lcd-1.3
raspberrypi-pico:usbmsc
raspberrypi-pico:nshsram
raspberrypi-pico:nsh-flash
raspberrypi-pico:nsh
raspberrypi-pico:audiopack
raspberrypi-pico:composite
raspberrypi-pico:st7735
raspberrypi-pico:displaypack
raspberrypi-pico:spisd
raspberrypi-pico:smp
raspberrypi-pico:enc28j60
raspberrypi-pico:ssd1306
raspberrypi-pico:usbnsh
raspberrypi-pico:waveshare-lcd-1.14
raspberrypi-PicoW:lcd1602
raspberrypi-PicoW:waveshare-lcd-1.3
raspberrypi-PicoW:usbmsc
raspberrypi-PicoW:nshsram
raspberrypi-PicoW:nsh-flash
raspberrypi-PicoW:nsh
raspberrypi-PicoW:audiopack
raspberrypi-PicoW:composite
raspberrypi-PicoW:st7735
raspberrypi-PicoW:displaypack
raspberrypi-PicoW:spisd
raspberrypi-PicoW:smp
raspberrypi-PicoW:enc28j60
raspberrypi-PicoW:ssd1306
raspberrypi-PicoW:usbnsh
raspberrypi-PicoW:waveshare-lcd-1.14
raspberrypi-PicoW:telnet

The configuration that is interesting here (to get started) is the raspberrypi-PicoW:usbnsh configuration. This will redirect the console output from NuttX over USB to the host machine using serial communication. This will allow a simple terminal application to connect to the board without the need for any additional hardware. So let’s configure NuttX and build the system.

First up, clean the system and set the configuration:

make distclean
./tools/configure.sh -l raspberrypi-PicoW:usbnsh

This should clear any previous configurations (distclean) and then set the board type and specific configuration to be built.

Now we should be able to build the system.

make

You can also tell make to use as many cores on your system as possible with the command make -j. After a short time the system will compile the NuttX OS and applications and create a UF2 file that can be uploaded to the PicoW.

The board can be programmed by starting the board in bootloader mode (press and hold the BOOTSEL button while plugging in the USB cable). The Pico should now present itself as a USB drive on the host computer. Drag and drop the nuttx.uf2 file onto the Raspberry Pi Pico drive on the host computer. The file will be copied to the board and the board will restart.

Connecting to NuttX

Time to check that the board has been programmed correctly. Open your favourite terminal application and set your connection settings to 115200,n,8,1 and connect to the serial port presented by the PicoW. The default is something like usbmodem01. Connect to the PicoW and hit enter a couple of times. On doing this you should be presented with something like this:


NuttShell (NSH) NuttX-12.1.0
nsh>
nsh>

If you have got this far then you have successfully built NuttX and deployed the OS to the PicoW. Typying help in the serial console should reveal the list of deployed applications.

Conclusion

At this point we have NuttX running on the PicoW. This opens up some new channels for investigation:

  • Using both cores on NuttX
  • C++ application development

In the next post we will look at enabling C++ development in NuttX.