NuttX on an emulated ESP32 using QEMU
This article is part of the “First Steps with ESP32 and NuttX” series. A series whose objective is to present an overview of the NuttX Operating System and to provide instructions for using NuttX on ESP32.
If you are new to the NuttX universe, reading the article where Sara Monteiro explains the process of preparing the development environment and describes the necessary steps to compile Apache NuttX and upload the firmware to the Flash memory of the ESP32 is recommended.
In this tutorial, as the official QEMU repository does not support ESP32, we will compile the Espressif’s fork of QEMU, configure NuttX to work with the emulator, and compile an example for demonstration purposes. It is worth noting that, although there is a version of QEMU with ESP32 support developed by Espressif, the company does not officially support QEMU.
What is QEMU?
QEMU (from Quick EMUlator) is an open source software created by Fabrice Bellard for creating virtualization and emulation environments.
In emulations, its main objective is to run operating systems and applications developed for another hardware platform. For example, running software written for the ARM platform on an x86-based computer.
In virtualizations, QEMU emulates devices and privileged instructions using kernel modules (KQEMU or KVM) and the virtual machine environment provided by the operating system. It is typically used to run the Windows operating system on computers using Linux.
For more information, visit the official project page.
Why use QEMU?
One of the main advantages of using QEMU is the possibility of testing software being developed without having to purchase the necessary hardware to run it.
In addition, other advantages include the debugging of problems without the need for a JTAG/Serial converter and the possibility of emulating a system with more memory than the corresponding physical module.
Installing QEMU
Prerequisites
For this guide, we will assume that the Apache NuttX development environment is already configured as described in the previous article. Also, make sure that the system and the nuttx and nuttx-apps repositories are up to date.
sudo apt update
sudo apt upgrade
cd ~/nuttxspace/nuttx
git pull origin master
cd ~/nuttxspace/apps
git pull origin master
First, we need to install some packages needed for the QEMU compilation:
sudo apt install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build libgcrypt-dev python3
Now we can proceed with downloading the fork of the QEMU application. In this tutorial, we will clone the repository into the esp-qemu folder inside the user’s home directory:
git clone https://github.com/espressif/qemu ~/esp-qemu
With these steps done, we have everything we need to compile QEMU with support for the Xtensa architecture.
Compiling QEMU
With the files prepared, we can now configure the compilation system to select the necessary functionalities for the ESP32 to work (if you want to know more about the available configuration options, run ./configure -h inside the esp-qemu folder):
cd ~/esp-qemu
./configure --target-list=xtensa-softmmu \
--enable-gcrypt \
--enable-debug --enable-sanitizers \
--disable-strip --disable-user \
--disable-capstone --disable-vnc \
--disable-sdl --disable-gtk
When the configuration process is completed, we can compile the application to generate the QEMU executable with ESP32 support:
ninja -C build
After completing this step, the executable qemu-system-xtensa will be available in the build folder. With QEMU installed, we now need to configure our NuttX system to work with QEMU.
NuttX Settings
The following step is to configure our application to support QEMU. As the emulator requires the memory partitions to be in a single file, we need to enable the configuration option for merging the binary files into a single one.
First, ensure that there are no settings applied to the NuttX system:
cd ~/nuttxspace/nuttx
make distclean
Then, we can select an application to be compiled. We will use the NuttShell application as an example to be emulated:
./tools/configure.sh esp32-devkitc:nsh
To be able to execute NuttX on the QEMU emulator, we need to change some configuration options. To do that, we need to access the configuration menu:
make menuconfig
Within the menu, navigate to Board Selection and select the ESP32 binary image for QEMU option. Save and exit.
We can now generate the NuttX system image with the NuttShell application to be executed on the emulator:
make ESPTOOL_BINDIR=../esp-bins -j
If the steps were followed correctly, the message “Generated: nuttx.merged.bin (QEMU compatible)” should be displayed at the end of the compilation process.
Running the NuttX System
To run our application, we need to go back to the folder where our QEMU executable is located and run it with the correct settings for ESP32:
cd ~/esp-qemu/build
./qemu-system-xtensa -nographic \
-machine esp32 \
-drive file=~/nuttxspace/nuttx/nuttx.merged.bin,if=mtd,format=raw
If the whole process was successful, the application will be executed on an emulated ESP32 by QEMU in the terminal:
Debug using GDB
We can also run the emulator with a GDB server so we can debug the application while being executed. For that, we first need to install the GDB for ESP32:
sudo apt install python2.7 libpython2.7-dev
wget -qO- https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v12.1_20221002/xtensa-esp-elf-gdb-12.1_20221002-x86_64-linux-gnu.tar.gz | tar -xvz
sudo mv xtensa-esp-elf-gdb/ /opt/xtensa/
echo "export PATH=\$PATH:/opt/xtensa/xtensa-esp-elf-gdb/bin" >> ~/.bashrc
echo "export QEMU_XTENSA_CORE_REGS_ONLY=1" >> ~/.bashrc
source ~/.bashrc
Now we can run QEMU waiting for a GDB connection:
cd ~/esp-qemu/build
./qemu-system-xtensa -nographic -s -S \
-machine esp32 \
-drive file=~/nuttxspace/nuttx/nuttx.merged.bin,if=mtd,format=raw
The emulation will not be started until GDB is attached to QEMU. To do so, use the following command in another terminal window to run the debugger:
xtensa-esp32-elf-gdb ~/nuttxspace/nuttx/nuttx \
-ex "target remote :1234" \
-ex "monitor system_reset" \
-ex "tb app_main"
With GDB connected, you can now add breakpoints and/or watches for debugging the application. To continue the emulation, just run the command continue or c.
For a more in-depth debugging, don’t forget to enable the desired debug options in NuttX’s configuration menu.
Final Thoughts
In this tutorial you have learned how to install and use QEMU, run the NuttX system on an emulated ESP32 and use debugging tools on your application.
To learn more about the limitations of emulating an ESP32 using QEMU or have any questions, visit the fork’s wiki.
References
QEMU Official website: https://www.qemu.org/
Espressif’s QEMU Wiki: https://github.com/espressif/qemu/wiki
ESP-IDF Documentation: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-tools.html
NuttX Documentation: https://nuttx.apache.org/docs/latest/index.html