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.
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.
Break on -b uart_init or wildcard -b *handler*. No address arithmetic.
Print any variable's value at every breakpoint. The AVR calling convention means r24/r25 tell you the function argument.
Not sampled. Not approximated. Every avr_run() call reports the exact cycles consumed by that one instruction.
Per-edge call counts, total cycles, and a hot path marker — detected via opcode pattern matching across all five AVR CALL/RET variants.
Here's what a profiling run looks like against a demo firmware — no post-processing, no special build flags:
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."
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:
That's a complete self-verifying firmware test. No test harness, no Arduino board, no USB cable. CI-friendly by default.
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.
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.