// open source · embedded · devtools

Debug your AVR firmware
without touching any hardware

$ bin/protosim app.elf --profile --callgraph --max-steps 1000000

I built a cycle-accurate AVR simulator that gives you named breakpoints, variable watches, full profiling, and code coverage — from a single command line, zero firmware changes required.

Embedded development has a dirty secret: the feedback loop is brutal. You write code, flash it, attach a logic analyser, squint at waveforms, flash again. Repeat. For most projects, 80% of debugging time is fighting the hardware, not the logic.

So I built protosim — a wrapper around simavr that turns AVR firmware simulation into something you'd actually want to use as a daily driver.

// what makes it different

Most AVR simulators are either too primitive (just a PC counter and cycle ticks) or too heavyweight (full IDE integration, 30-step setup). protosim sits in the sweet spot: a single binary, no firmware modifications, and a capability set that rivals hardware debuggers.

→ breakpoints
Named symbol breakpoints

Break on -b uart_init or wildcard -b *handler*. No address arithmetic.

→ watches
SRAM variable watches

Print any variable's value at every breakpoint. The AVR calling convention means r24/r25 tell you the function argument.

→ profiling
Cycle-exact flat profile

Not sampled. Not approximated. Every avr_run() call reports the exact cycles consumed by that one instruction.

→ callgraph
Dynamic call graph

Per-edge call counts, total cycles, and a hot path marker — detected via opcode pattern matching across all five AVR CALL/RET variants.

// real output, real numbers

Here's what a profiling run looks like against a demo firmware — no post-processing, no special build flags:

$ bin/protosim demo.elf --profile --callgraph --max-steps 1000000

[PROF] Flat Performance Profile (cycle-exact)
[PROF] ─────────────────────────────────────────────
[PROF] Rank Function Self Cycles Self % Calls
[PROF] ──── ────────────── ─────────── ────── ─────
[PROF] 1 uart_tx 1645845 98.92% 92 ◄ HOT
[PROF] 2 compute 16330 0.98% 71
[PROF] 3 main 1002 0.06% 1
[PROF] 4 uart_send 248 0.01% 1
[PROF] 5 uart_init 15 0.00% 1

[COV] unused_handler 0 → 0.0% ← NEVER REACHED

[DBG] Simulated time: 103.987 ms @ 16000000 Hz

The hot path jumps out immediately. uart_tx is eating 98.9% of runtime — not because the algorithm is wrong, but because UART is inherently slow. That's the kind of insight that takes hours to find with a logic analyser and seconds with a profiler.

"The feedback loop for embedded code shouldn't cost you 20 minutes every iteration. A simulator with real observability changes what's possible."

// designed for LLMs too

One thing I deliberately designed for: LLM-friendly debugging. The SKILL.md in the repo tells an AI assistant exactly how to drive protosim — which flags to use, how to interpret register dumps, how to set up deterministic UART tests. The profiling output format is human-readable but also completely parseable.

Headless UART testing is especially useful here. Instead of async PTY/TCP gymnastics, you can do this:

$ bin/protosim app.elf --uart0-in "hello\n" --uart0-out out.txt --exit-on-uart "SUCCESS" --max-steps 10000000

That's a complete self-verifying firmware test. No test harness, no Arduino board, no USB cable. CI-friendly by default.

// works on windows too

simavr is deeply Linux-native. Getting it to build on Windows without patching the source was genuinely tricky — the solution was a single compatibility header injected at compile time via gcc -include. Four shims: closesocket() vs close(), a CLOCK_MONOTONIC_RAW override, an ssize_t typedef, and an inline basename(). MSYS2 UCRT64 handles everything else. The result: protosim.exe with full feature parity.

// try it

The repo is at github.com/jklarenbeek/protosim. The README has worked examples with real output — not toy demos. If you're doing AVR development, or just curious about how cycle-accurate simulation works under the hood, take a look.

Feedback welcome. Especially curious what features embedded folks would find most useful — coverage-gated CI? GDB integration? EEPROM simulation? Open an issue.

#EmbeddedSystems #Arduino #AVR #OpenSource #DevTools #Firmware #SoftwareTesting #CProgramming #LLM #CI
JK
Joham Embedded systems · language design · tooling