Use Renode to simulate Arduino nano 33 BLE programmed with Zephyr and debug via gdb
I use Renode to simulate an Arduino nano 33 BLE. In this way I am able to test applications without using real hardware, without flashing the program and without the need for hardware debuggers. I am able to run Zephyr programs, and debug these with GDB. This accelarates software development considerably.
Of course Renode can also be used to develop programs for hardware that is still to be developed, or being developed.
For bluetooth development I like to use just Zephyr and its possibility to run on qemu_x86, while simulating the bluetooth stack with Bluez, and monitoring all bluetooth traffic via btmon (see https://basaandewiel.github.io/Zephyr_bluetooth_programming/).
About Renode
Renode is a development framework which accelerates IoT and embedded systems development by letting you simulate physical hardwaresystems — including both the CPU, peripherals, sensors, environment and wired or wireless medium between nodes.
Renode promises to be able to simulate all kind of boards, like for example the Arduino nano 33 ble sense. This means that developing software can become much easier, because you do not have to flash the software to be able to test it.
This sounds very interesting, not? So let’s try it out.
- For the tests below I tried to simulate an Arduino nano 33 BLE. A rather popular device, of which I also have an hardware version.
But first we have to install Renode.
Install Renode on Linux Mint 20.3
Instructions used: https://renode.readthedocs.io/en/latest/introduction/installing.html
mkdir renode_portable
- download portable release from https://github.com/renode/renode/releases/tag/v1.12.0 (you can also download an daily build)
tar xf renode-*.linux-portable.tar.gz -C renode_portable --strip-components=2
To use it from any location enter the created directory and add it to the system path:
cd renode_portable
export PATH="`pwd`:$PATH"
Install dependencies
Mono
sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
Other dependencies
sudo apt-get install policykit-1 libgtk2.0-0 screen uml-utilities gtk-sharp2 libc6-dev gcc python3 python3-pip
Using Renode
cd <directory where you installed renode
Start Renode from command line
./renode
After some seconds a new window (renode monitor appears). In this window you can give renode commands, like
mach create #create a machine
machine LoadPlatformDescription @platforms/boards/arduino_nano_33_ble.repl #load a predefined machine, of cours our arduino nano 33 ble
sysbus LoadELF @/home/<user>/zephyrproject/zephyr/build/zephyr/zephyr.elf #load an elf file you built for this target machine
showAnalyzer sysbus.uart0
logLevel -1 #very verbose
sysbus.cpu LogFunctionNames true #shows al kind of function names, so you can track what de simuated SOC is doing
s #start simulation of the machine
sysbus.cpu PC #read Program Counter
sysbus.cpu Reset #after this command program must be loaded again
You can save these Renode commands in a kind of script file, a file with extension .repl, placed in the script directory of Renode.
This is the arduino.repl
script I used for this hello world example.
mach create
machine LoadPlatformDescription @platforms/boards/arduino_nano_33_ble.repl
sysbus LoadELF @/home/<user>/zephyrproject/zephyr/build/zephyr/zephyr.elf #load an elf file built for this target machine
showAnalyzer sysbus.uart0
logLevel -1 #very verbose
sysbus.cpu LogFunctionNames true
s #start simulation of the machine
Hello world - built with Zephyr, running in Renode
I generated a “hello world” program in Zephyr.
west build -b arduino_nano_33_ble <...>/hello_world/
NB: you have of course supply the correct path the the Zephyr program.
For instructions on how to install and use Zephyr, see one of my other blogs on https://basaandewiel.github.io/Zephyr_bluetooth_programming/
I used following simple Zephyr program:
#include <zephyr.h>
#include <sys/printk.h>
#include <device.h>
#include <drivers/gpio.h>
void main(void)
{
const struct device *dev;
dev = device_get_binding("GPIO_0");
//The parameter passed to this function, should be the "label"
//property of the device you want to work with. it is described here:
//https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/dts/howtos.html#get-a-struct-device-from-a-devicetree-node
//By searching the devicetree file (.dts), you can see the label of the gpio0 is "GPIO_0"
gpio_pin_configure(dev, 27, GPIO_OUTPUT);
while (1)
{
gpio_pin_toggle(dev, 27);
k_sleep(K_SECONDS(1));
}
}
Start Renode, and give the following commands:
mach create
machine LoadPlatformDescription @platforms/boards/arduino_nano_33_ble.repl
sysbus LoadELF @/home/<user>/zephyrproject/zephyr/build/zephyr/zephyr.elf #load an elf file build for this target machine
showAnalyzer sysbus.uart0
logLevel -1 #very verbose
sysbus.cpu LogFunctionNames true
s #start simulation of the machine
In the log window we now see a lot of messages; there are also a number of warnings (which I do not understand completely yet). The most interesting part is:
19:58:59.8914 [NOISY] machine-1/nvic: Completed IRQ 14 active -> inactive.
19:59:02.2434 [NOISY] machine-0/rtc1: Interrupt set by CC0 interruptEnable=True compareSet=True compareEventEnable=True
19:59:02.2435 [NOISY] machine-0/nvic: External IRQ 33: True
19:59:02.2436 [NOISY] machine-0/nvic: Waking up from deep sleep<==============
19:59:02.2437 [INFO] machine-0/cpu: Entering function arch_cpu_idle+0x12 (guessed) at 0x108C2
..
19:59:02.2609 [NOISY] machine-0/gpio0: Setting pin 27 to False<===========
...
9:59:02.8022 [NOISY] machine-1/nvic: Waking up from deep sleep
...
9:59:02.8044 [NOISY] machine-1/gpio0: Setting pin 27 to False<============
As you can see the trace starts at 19:58:59. The the k_sleep
command starts; it should sleep for one second, but as you can see Renode sleeps for three seconds.
I seems to be possible to adjust the timing of Renode (not yet done by me).
You can also see that Zephyr puts the SOC into deep sleep during the k_sleep
command.
You can also see that GPIO 27 is toggeld (in this case set to false).
You can also filter the number of log messages of Renode.
logLevel 3 nvic #shows only errors for nvic
logLevel 3 uart1 #shows only errors for uart1
Debug Zephyr on Renode via GDB
It is of course nice that we can run Zephyr programs on by Renode. But for developement we also want to be able to debug programs in a simple manner. So we now look into how we can debug programs running in Renode using gdb.
Working setup for Arduino nano 33 BLE
This is the most interesting part for me. Renode is emulating en Arduino nano 33 BLE, and running a Zephyr program, end this Zephyr program is being debugged via GDB!
Don’t take a break, but read on :)
- Build your Zephyr program
west build --pristine -b arduino_nano_33_ble <path>/testing/
- Start renode, and give following commands:
renode
mach create
machine LoadPlatformDescription @platforms/boards/arduino_nano_33_ble.repl
sysbus LoadELF @/home/<user>/zephyrproject/zephyr/build/zephyr/zephyr.elf
#load an elf file built for this target machine. See also https://basaandewiel.github.io/Zephyr_bluetooth_programming/logLevel -1 #very verbose
machine StartGdbServer 3333
#start gdb server at port 333
- In another terminal: Start gdb, and give following commands
~/zephyr-sdk-0.13.2/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb-no-py build/zephyr/zephyr.elf
#start gdb with correct elf filetarget remote :3333
#connect to remote target=renodeb main
#set breakpoint at main()list
monitor start
continue
#run til breakpoint
Now you can debug the program as you are used to do with gdb.
Console messages (printk)
Of course it is handy if you can use printk to print messages to the console. This does not work out of the box and took my some time to get working.
When you look at the example in Renode for the Arduino Nano 33 BLE, you see that they had to patch the Tensor Flow lite example so that console messages are working (see ../renode/scripts/complex/arduino_nano). The patched “serial”->”serial1”. So I assumed that for console messages to be working in Zephyr/Renode I had to change something in the area of serial/uarts too. This means I had to change something in the area of Device trees. So I started reading about device trees in the Zephyr documents. I ended up in making an overlay file (that is overlayed on the basis device tree for the board used, so in my case for the Arduino nano 33 BLE.
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
chosen {
zephyr,console = &uart1; //override default value of console (&uart0)
};
};
&uart1 { //baswi: override uart1
status = "okay";
current-speed = <115200>;
};
This overlay file changes the zepyr,console from uart0 to uart1, and sets uart1 as status ‘okay’ and the correct speed.
Now console messages are printed when running the program in Renode. Of course you have to give an extra command in Renode:
showAnalyzer sysbus.uart1
Working with virtual sensors (not yet working)
After the hello world example I wanted to test the behaviour when I use virtual sensors of the Arduino nano 33 BLE.
The arduino_nano_33_ble.repl file already contains
lsm9ds1_imu: Sensors.LSM9DS1_IMU @ twi0 0x6b
so I expected that I should be able to use this sensor. So I added following line in proj.conf to be able to use sensors from within Zephyr:
CONFIG_BOARD_ARDUINO_NANO_33_BLE_INIT_SENSORS=y
.
– Robot Framework
Automated testing with Robot Framework (to be investigated)
Used source: https://github.com/antmicro/renode-test-action
–
Integrate Renode with PlatformIO and Zephyr
Used source: https://www.youtube.com/watch?v=EdC3kFZxSzI
Configure your platformio.ini file with:
[env:hifive1]
platform = sifive
framework = zephyr
board = hifive1
monitor_speed = 115200
# Settings to integrate with Renode upload and debug
upload_command = renode -e "include @scripts/single-node/sifive_fe310.resc" -e "machine StartGdbServer 3333 True" -e "sysbus LoadELF @$SOURCE" -e "start"
debug_tool = custom
debug_port = localhost:3333
debug_server = renode
-e
include @scripts/single-node/sifive_fe310.resc
-e
machine StartGdbServer 3333 True
debug_extra_cmds =
monitor start
I also tried to do the same for the Arduino nano 33 BLE, but the first problem I ran into was that Visual Code with PlatformIO plugin does not support Zephyr for the nano 33 BLE board. So I cannot build Zephyr programs for the nano 33 BLE board in visual code.
So for the moment I work with Zephyr/Renode/gdb for the nano 33 BLE board as described above.
Connect Arduino IDE to Renode
You can even connect Renode to Arduino SDK, so you can “flash” from Arduino SDK to virtual machine in Renode
sudo modprobe vhci_hcd
You need to install usbip
:
apt-get install linux-tools-generic
#gives usbip not found for kernel 5.4.0-97
sudo apt install linux-tools-5.4.0-97-generic
sudo apt install linux-cloud-tools-5.4.0-97-generic
#You may also want to install one of the following packages to keep up to date:
sudo apt install linux-tools-generic
sudo apt install linux-cloud-tools-generic
In Renode:
(monitor) mach create
(machine-0) machine LoadPlatformDescription @platforms/boards/arduino_nano_33_ble.repl
#Start the USB/IP server in Renode and attach the loader to it:
(machine-0) emulation CreateUSBIPServer
(machine-0) host.usb CreateArduinoLoader sysbus.cpu 0x10000 0 "arduinoLoader"
(use 0x10000 in the case of Arduino Nano 33 BLE board), the port through which the bootloader is connected to the host.usb controller (should be changed if you have other devices already connected) and the name of the loader (arduinoLoader in this case)
In Renode:
arduinoLoader WaitForBinary 120 true #start the loader
Now a password is asked for running usbip as root. NB: if you don’t use a GUI (if you use for instance i3) then the authorization does NOT work. I had to use a GUI, like xfce).
NB: ubuntu has no default root password. To set this do: $ sudo -i
Type your own password when prompted, and then you will see # prompt. Now you can set up the root user password by typing the following command:
$passwd
Now I can build an application in Arduino IDE and “flash” it to Renode.
This seams to work for an ELF file generated via Arduino IDE, but not when generated via Zephyr. So I use the method above using the loadELF
command.