Debugging Firmware#

PyMCU ships a full source-level debugger for AVR firmware. You can set breakpoints, step through Python source lines, and inspect registers and SRAM directly from VS Code — no separate debug probe or JTAG adapter required.

The debugger uses the Debug Adapter Protocol (DAP) and runs the AVR8Sharp simulator internally, so you debug at full speed on your developer machine without touching physical hardware.


Prerequisites#

What

Where

VS Code

code.visualstudio.com

PyMCU VS Code extension

Install from the VS Code Marketplace: search PyMCU

PyMCU compiler

pip install "pymcu-compiler[avr]"


Quick start#

  1. Open your PyMCU project folder in VS Code.

  2. Add a launch.json to .vscode/:

    {
      "version": "0.2.0",
      "configurations": [
        {
          "type": "pymcu-avr",
          "request": "launch",
          "name": "Debug firmware",
          "program": "${workspaceFolder}/src/main.py",
          "stopOnEntry": true
        }
      ]
    }
    
  3. Press F5 (or Run → Start Debugging).

    PyMCU automatically:

    • Compiles your project (pymcu build)

    • Starts the AVR simulator with the generated firmware

    • Opens the debug panel with registers, memory, and call stack

  4. Click the gutter next to any Python source line to add a breakpoint.


Features#

Breakpoints on Python lines#

Breakpoints map directly to the compiled AVR instructions for each source statement. Click in the editor gutter or press F9. Conditional breakpoints are not yet supported.

Step commands#

Command

Shortcut

Description

Continue

F5

Run until next breakpoint

Step Over

F10

Execute the current statement, skip into calls

Step Into

F11

Step into function calls

Step Out

Shift+F11

Run until the current function returns

Registers panel#

The Variables panel shows all 32 AVR general-purpose registers (R0–R31), the Stack Pointer (SP), and the Status Register (SREG) with individual flag bits decoded (I, T, H, S, V, N, Z, C).

Values that changed since the last stop are highlighted in yellow.

Memory view#

Open the Memory tab to inspect SRAM contents at any address. The view updates automatically after each step.

Address  00  01  02  03  04  05  06  07   ASCII
0x0100   00  00  00  00  ff  ff  00  42   ......B
0x0108   ...

Call stack#

The Call Stack panel shows Python-level frames when source mapping is available, falling back to raw AVR instruction addresses otherwise.


How it works#

VS Code extension
      │
      │  DAP (JSON over TCP)
      ▼
pymcuc-avr-debugserver        ← bundled with pymcu-avr
      │
      │  AVR8Sharp simulator API
      ▼
AVR8Sharp (in-process)        ← simulates ATmega328P cycle-accurately

The pymcuc-avr-debugserver binary ships with pymcu-avr and is launched automatically by the VS Code extension. It speaks a simple newline-delimited JSON protocol over a local TCP socket.

The line map (.linemap.json) is generated by pymcu build alongside the firmware hex file and maps each AVR instruction address back to the Python source file and line number.


Known limitations#

  • Simulation only — hardware peripherals (UART, SPI, I2C) emit/receive data in the simulator; flashed real hardware is not controllable via the debugger.

  • ATmega328P only — other targets are not yet supported.

  • Interrupts — ISRs are stepped correctly but the timing of interrupt delivery may differ slightly from real hardware.

  • @naked functions — single-stepping through bare-metal ASM blocks works at the instruction level (no source mapping for asm() lines).


Profiling instead of debugging#

For performance analysis, use pymcu profile to generate a Speedscope flamegraph instead:

pymcu profile --ms 5000 -o profile.speedscope.json
# drag profile.speedscope.json onto speedscope.app

The profiler supports RTOS firmware with multiple tasks — each task gets its own profile tab.

See CLI reference → profile for details.