Getting Started with STM32F4 Emulation using Renode

Phan Cuong
6 min readAug 3, 2024

--

Photo by Axel Richter on Unsplash

You’re curious about embedded systems but prefer to avoid dealing with hardware, or maybe you’ve even damaged hardware in the past. There’s a solution for this: emulation.

An emulator isn’t a physical device like a hardware board, but it allows you to run your firmware and interact with data over UART, CAN, or SPI. This capability is more than enough to develop real firmware!

In this post, we’ll guide you through setting up the Renode emulator and running firmware for the STM32F4. Additionally, we’ll demonstrate how to debug your firmware using VS Code

What is Renode?

Renode is an open-source Emulator for embedded platforms. Today, it supports x86 (Intel Quark), Cortex-A (NVIDIA Tegra), Cortex-M, SPARC (Leon), and RISC-V based platforms.

Renode enables you to run your production firmware on virtual cores, peripherals, and even simulate sensors and actuators. Its robust networking features and multi-system emulation capabilities make it perfect for testing environments with multiple interacting devices.

By using Renode, you can start development even before your physical hardware is ready, conduct firmware testing without setting up extensive hardware, and speed up development cycles by avoiding time-consuming flash loading.

Built on the Mono framework, Renode offers cross-platform support, making it versatile for various development environments.

Installing Renode:

The Renode project can be installed on Windows, MacOS and Linux. I am using v1.15.1 release available on Github

Please note this post was written on Windows, and all the examples below were tested only on this platform. After installing the renode_1.15.1.msi file, you can run first example to validate the Renode installation.

  • Open “Command Prompt”, then run command “renode
  • A Renode terminal window will appear. Load the example with “start @scripts/single-node/stm32f4_discovery.resc”
  • A second terminal window should open, with below output

Running our firmware in Renode

Our firmware will print “Hello World” via UART log after being flashed to the STM32F4 Discovery board, as the emulator supports this out of the box.

Building our firmware:

Before starting , ensure that Git, CMake and GCC Arm compiler are installed. Installation links are provided at the end of this post.

The next step is to build the firmware for Renode by opening the “command prompt” and running commands below.

$ git clone https://github.com/PhanCuong91/data.git
$ cd data/renode
$ .\build.bat
# Final result is ./build/src/STM32F4Template.elf that was generated.
# start Renode
$ renode

Running our firmware with simple command

The following commands need to be executed in Renode terminal window.

mach create
machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl
# you have to copy location of .\build\src\STM32F4Template.elf
# sysbus LoadELF -> load the elf
sysbus LoadELF "C:\working\data\renode\build\src\STM32F4Template.elf"
# open a terminal to display UART data
showAnalyzer sysbus.usart2
# run our simulation
start

Note: Ensure you provide the correct path to the ELF file.

And voila, we now have Hello World! string in the analyzer!

Automating the setup with a .resc script

To collect all the commands above into a “.resc” file, consider the following notes:

  1. $bin?=@renode-example.elf or $bin?=renode-example.elf assigns the path “<Renode installation path>/renode-example.elf” to the “bin” variable. The default Renode installation path is “C:\Program Files\Renode”.
  2. $bin?="C:\<path>\renode-example.elf" assigns the ELF file at specified location to “bin” variable.
  3. Macros can be defined using the keyword macro , and are enclosed within tripple double quote """ .
  4. machine StartGdbServer 3333 starts GDB server and binds it to port 3333, which is used for debugging.

Combining all these elements, you can create the following script to set up your “hello world” example. Be sure to adjust the directory path according to your project setup.

:name: STM32F4 Discovery Printf
:description: This script runs the usart_printf example on stm32f4 discovery

$name?="STM32F4_Discovery"
$cmm_repl?="C:\working\data\renode\add-ccm.repl"
$bin_path?="C:\working\data\renode\build\src\STM32F4Template.elf"

# Create Machine & Load config
mach create $name
machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl
machine LoadPlatformDescription $cmm_repl

# Create a terminal window showing the output of UART2
showAnalyzer sysbus.usart2

# Enable GDB
machine StartGdbServer 3333

macro reset
"""
sysbus LoadELF $bin_path
"""

runMacro $reset

Note: please replace the directory path with the location of your project folder instead using example path provided.

add-ccm.repl is needed if you encountered the issue below:

[...]
20:46:07.5783 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA3, value 0x0.
20:46:07.5783 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA4, value 0x0.
20:46:07.5783 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA5, value 0x0.
20:46:07.5795 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA6, value 0x0.
20:46:07.5799 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA7, value 0x0.
20:46:07.5800 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA8, value 0x0.
20:46:07.5800 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AA9, value 0x0.
20:46:07.5800 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAA, value 0x0.
20:46:07.5801 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAB, value 0x0.
20:46:07.5801 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAC, value 0x0.
20:46:07.5801 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAD, value 0x0.
20:46:07.5802 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAE, value 0x0.
20:46:07.5802 [WARNING] sysbus: [cpu: 0x8000926] WriteByte to non existing peripheral at 0x10152AAF, value 0x0.
[...]

Debugging firmware in VS Code

First , open VS code and navigate to the renode folder where you built “hello world” example. Next, follow the instructions shown in the following image. You ‘re all set to start debugging.

What is needed for debugging

Launch configuration

The “* .vscode/launch.json” file describes a launch configuration named “Debug application in Renode”. If needed, adjust the path to the ELF file and the GDB version to be used for debugging.

{
"version": "0.2.0",
"configurations": [
{
"name": "Debug application in Renode",
"type": "cppdbg",
"request": "launch",
"preLaunchTask": "Run Renode",
"postDebugTask": "Close Renode",
"miDebuggerServerAddress": "localhost:3333",
"cwd": "${workspaceRoot}",
"miDebuggerPath": "arm-none-eabi-gdb",
"program": "${workspaceRoot}/build/src/STM32F4Template.elf"
}
]
}

Defining tasks:

The sample *.vscode/tasks.json defines three tasks:

  1. “Build application” is a sample build task for this particular scenario. Please adjust to your needs. Mind that the generated binary (needs to be an ELF file for effective debugging) is referenced in “renode-config.resc” and “launch.json”.
  2. “Run Renode” runs after “Build application” finishes. It starts the “renode-config.resc” script and waits for the GDB server to start. It requires a proper path to the resc script.
  3. “Close Renode” is a task fired when you close the debugging session. It will shut down Renode, and it’s a matter of personal preference if you want to use it or not. You can disable it in “launch.json” by removing/commenting-out the “postDebugTask” line.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build application",
"type": "shell",
"command": "./build-debug.bat",
"problemMatcher": [
"$gcc"
],
"group": "build",
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
},
{
"label": "Run Renode",
"type": "shell",
"command": "renode",
"args": [
"${workspaceFolder}/renode-config.resc" // add the correct directory
],
"dependsOn": [
"Build application"
],
"isBackground": true,
"problemMatcher": {
"source": "Renode",
"pattern": {
"regexp": ""
},
"background": {
"activeOnStart": true,
"beginsPattern": "Renode, version .*",
"endsPattern": ".*GDB server with all CPUs started on port.*"
}
},
"group": "build",
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
},
{
"label": "Close Renode",
"command": "echo ${input:terminate}",
"type": "shell",
"problemMatcher": []
}
],
"inputs": [
{
"id": "terminate",
"type": "command",
"command": "workbench.action.tasks.terminate",
"args": "terminateAll"
}
]
}

Conclusion:

Although we’ve just begun to explore Renode’s capabilities, I hope this post has helped you start with firmware emulation.

All the code used in the post is available on Github.

Reference:

Renode tutorial: https://interrupt.memfault.com/blog/intro-to-renode
Debug with VSCode: https://renode.readthedocs.io/en/latest/debugging/vscode.html
STM32F4 template with Cmake: GitHub — ahessling/STM32F4Template: A CMake-based template project as a starting point for STM32F4 projects

Installation link:

  1. Cmake (v3.29.7): https://cmake.org/download/
  2. gcc-arm-none-eabi (v10.3–2021.10): https://developer.arm.com/downloads/-/gnu-rm
    please find the “Installation the gcc-arm toolchain — Windows” from the story, incase you got issue.
  3. Git: https://git-scm.com/download/win

--

--