DEV Community

11cookies11
11cookies11

Posted on

VSCode + Renode: Building a Modern Embedded Simulation Workflow

In traditional embedded development, we usually rely on physical development boards, hardware debuggers (ST-Link/J-Link), various drivers, and heavyweight IDEs such as IAR or Keil. This setup not only makes the environment cumbersome to configure, but also limits testing capabilities due to hardware availability, physical conditions, and debugging risks.

With the maturity of Renode (from Antmicro), we can finally:

Use VSCode + GCC for a lightweight, modern development experience

Use Renode to simulate CPUs and peripherals

Use GDB Server to debug firmware remotely

Even run Renode on a server (Raspberry Pi / Orange Pi) and access it from anywhere

Over the past few days, I built a complete workflow from VSCode → Renode → GDB, solved a series of pitfalls, and documented everything — configurations, scripts, common errors, and fixes.

📌 1. Why Renode + VSCode?

Modern embedded development is shifting away from:

❌ IAR + physical board + hardware programmer + serial cables
❌ Breakpoints and stepping limited to physical devices
❌ Reinstalling toolchains when switching MCU families

Towards:

✔ VSCode + GCC — lightweight, modern, cross-platform
✔ Renode — full simulation of STM32 / ESP32 devices, including UART, SPI, I2C, GPIO, DMA
✔ GDB Remote Debugging — platform-agnostic, can debug over network
✔ Automation-ready — suitable for CI/CD and reproducible testing

This workflow is very common in international companies because:

It dramatically improves development efficiency

Enables automated testing

Allows CI pipelines to run MCU simulations

Reduces dependency on hardware

However, it is still uncommon in domestic environments, so I created this article to serve as a practical guide.

Steps

Install Renode

Configure Renode (write .resc script)

Configure VSCode (.vscode/tasks.json / .vscode/launch.json)

Verify end-to-end debugging

Goals

Write code that interacts with Renode-simulated peripherals

Use VSCode + Renode for full-featured debugging workflows

📌 2. Installing Renode

Renode provides multiple binary distributions — choose any that works for your platform:

Repository:
https://github.com/renode/renode

My choice:

renode-latest.linux-portable-dotnet.tar.gz

Reasons:

Bundles its own .NET runtime

Does not require system-installed dotnet/mono

Runs on OrangePi / Raspberry Pi directly

Extract and launch:

./renode

📌 3. Renode Configuration

Create a minimal Renode .resc script (example for STM32F103).
This file describes the MCU configuration Renode simulates — peripherals, firmware, and debug setup.

A working version:

using sysbus

mach create "stm32f103"
machine LoadPlatformDescription @platforms/cpus/stm32f103.repl

Load ELF from OrangePi

machine LoadELF @/home/orangepi/object/renode_portable/firmware.elf

Enable UART2 analyzer

showAnalyzer sysbus.usart2

Start GDB server on port 3333 for VSCode debugging

machine StartGdbServer 3333 true

start

Save it as:

run_stm32.resc

📌 4. VSCode Debug Configuration

The key part is setting up launch.json:

{
"version": "0.2.0",
"configurations": [
{
"name": "Debug STM32F103 on Renode Remote",
"type": "cppdbg",
"request": "launch",

        "program": "${workspaceFolder}/build/firmware.elf",
        "cwd": "${workspaceFolder}",

        "miDebuggerPath": "arm-none-eabi-gdb",
        "miDebuggerServerAddress": "xxx.xxx.xxx.xxx:3333",

        "preLaunchTask": "Run Renode Remote",
        "stopAtEntry": true,

        "setupCommands": [
            { "text": "set pagination off" },
            { "text": "set confirm off" },
            { "text": "set print pretty on" }
        ],

        "postRemoteConnectCommands": [
            { "text": "monitor machine Pause" },
            { "text": "monitor sysbus LoadELF @/home/orangepi/object/renode_portable/firmware.elf" },
            { "text": "monitor cpu Reset" }
        ],

        "externalConsole": false,
        "targetArchitecture": "arm"
    }
]
Enter fullscreen mode Exit fullscreen mode

}

Core parameters:

miDebuggerPath → Local GDB executable
miDebuggerServerAddress → Remote Renode GDB endpoint

And:

"preLaunchTask": "Run Renode Remote"

VSCode automatically starts Renode on the remote board before debugging.

The post-connect sequence:

Pause simulation

Reload the ELF from the remote paths

Reset CPU and sync execution state

This mirrors what you'd normally do manually.

tasks.json

Required build + upload + execution pipeline:

{
"version": "2.0.0",
"tasks": [
{
"label": "CMake Configure",
"type": "shell",
"command": "cmake",
"args": [
"-S", "${workspaceFolder}",
"-B", "${workspaceFolder}/build",
"-DCMAKE_BUILD_TYPE=Debug"
]
},
{
"label": "CMake Build",
"type": "shell",
"command": "cmake",
"args": [
"--build", "${workspaceFolder}/build",
"--config", "Debug"
],
"dependsOn": ["CMake Configure"]
},
...
]
}

This pipeline uploads ELF and .resc to the remote machine, restarts Renode, and attaches GDB automatically.

Testing

Once configured, press F5 in VSCode:

Renode boots remotely

Firmware loads automatically

UART output and breakpoints work as expected

Top comments (0)