Skip to content
Chiplab logo
Chiplab logo
Back to Now

Copilot for firmware is solved. That was the easy half.

Ask a current model for a USART init on an STM32F4 and you get one. Not a sketch — the RCC clock enable, the GPIO alternate-function bits, the fractional baud divisor, the busy-wait on TXE, in about four seconds. That is the state of AI firmware development today and it is, genuinely, very good.

Ask for an interrupt handler and you get the right shape: read the status register, clear the flag, keep the body short, don't touch shared state without masking. Ask for a DMA ring buffer and you get head and tail indices, a half-transfer callback, and a comment about where the buffer has to live. Ask for a protocol state machine and you get an enum with exhaustive matches and a timeout arm you would have forgotten.

I don't write that code by hand anymore. Nobody I work with does. So let's stop arguing about it: for the narrow task of producing plausible embedded code, Copilot for embedded works, and the generation problem is closed.

Generation was never the problem.

The problem is that after the model emits those forty lines, it has no way to find out whether they did anything. It can't run them. And an agent that can't run what it wrote doesn't say "I'm not sure." It says "looks right to me" — which is the single failure mode embedded punishes hardest.

What the agent gets back when it's wrong

Here is the comparison that made me stop treating this as a code-quality argument. Take the same agent, the same model, the same harness, and change only the domain it's working in. What comes back after it submits an answer?

Can the agent run it?What comes back when it's wrongOne iterationWhat failure looks like
Web / backendYes, in its own sandboxStack trace with file and line, failing assertion, HTTP status, expected-vs-actual diffSecondsA red test with a name
MobileMostly — simulator on the same machineSymbolicated crash log, UI-test failure, a screenshot it can look atSeconds to minutesA crash report or a wrong pixel
Data / analyticsYesException, schema mismatch, row counts, a metric that moved the wrong waySeconds to hoursA number that's off, or a job that died
FirmwareOnly if somebody handed it a targetNothingMinutes to days, if a board is freeA binary that exists

Every row above firmware has the same property: the machine argues back. The agent proposes, the runtime disposes, and the disagreement arrives as text the model can read. That is the whole trick: a shorter distance between a wrong guess and being told, not a smarter model.

Firmware's row is not "worse feedback." It's no feedback. The build succeeds, an ELF appears on disk, and that is the last event in the loop. We've written about this at the CI layer before — a green build doesn't mean the firmware runs — but for an agent it's sharper than a CI gap. The compile is the terminal state. There is no next observation.

Diagram comparing a closed AI agent feedback loop in web development to a firmware loop broken at the execution step

The unlock in every other domain was execution access, not intelligence

This is the most reproducible result in agentic coding, not a theory I made up to sell something.

SWE-agent is the cleanest statement of it. The authors didn't train a better model — they built an interface that let an existing model create files, navigate a repo, and execute tests and other programs. Same weights, new access. They report pass@1 of 12.5% on SWE-bench and 87.7% on HumanEvalFix, and describe both as far exceeding what non-interactive language models had achieved.1 The paper's framing is worth stealing: language model agents are a new category of end user, and they need interfaces built for them.

Now look at the one embedded benchmark that tried the same move. EmbedAgent evaluated 10 models on 126 cases across 3 hardware platforms, and its authors proposed two remedies for weak performance: retrieval, and compiler feedback. The compiler-feedback result is the one I keep coming back to. Feeding compiler errors back to DeepSeek-R1 for a single repair iteration cut syntax errors from 34.1% of cases (43 of 126) to 3.2% (4 of 126). Pass@1 on the ESP32 migration task went from 21.4 to 27.8.2

Read those two numbers next to each other. One feedback channel eliminated roughly nine-tenths of the compile failures and moved end-to-end correctness by six points. That's the whole argument in one ablation: the compiler was never what was holding the model back. It was holding back the part that was already easy. The paper is also blunt about the state of the field — existing LLM-for-embedded approaches, it notes, "rarely enable genuine interaction between the LLM and the hardware."

Bar chart showing compiler feedback nearly eliminating syntax errors while barely improving pass rate in LLM firmware code generation

Be fair: what firmware tooling actually gives an agent today

The honest version of this argument has to admit the agent isn't working in total darkness.

ToolSignal it returnsRuns unattended in a loop?Blind to
cargo check, clippy, the compilerType errors, stale APIs, borrow violationsYes, secondsAnything an address can express — a raw register write is always "valid"
Static analysis (MISRA, sanitizers)Rule violations, some UB, traceable findingsYesWhether the peripheral was ever enabled
Host-side unit testsAssertion failures on pure logicYesEverything below the HAL boundary
Emulator (QEMU, Renode, Wokwi)UART output, peripheral state, real executionOnly if a model for your board existsWhatever the board model doesn't implement
A physical boardGround truthNo — someone has to plug it in and watchNothing, and that's the point

That top block is genuinely useful and it is why the failure classes have shifted. In our cold-prompt test of what LLMs get wrong about embedded code, every HAL attempt died in the compiler on API drift, and every raw-register attempt compiled clean on the first try. The mechanical layer works. What survives it is the wrong-but-plausible register write, and no static tool has an opinion about that.

The emulator row is where it gets interesting, because that row is the only one that both returns a real execution signal and can run in a loop without a human. It is also the row gated on a conditional: if a board model exists. That conditional is doing enormous work, and it's the reason firmware code generation risks don't get retired by better prompting.

A broken signal doesn't fail loudly. It converges confidently.

This is the part people underrate, and it's the reason I don't find "just review the output" reassuring.

An agent optimizing against a signal that is absent behaves identically to an agent optimizing against a signal that is correct. It iterates, it reports progress, it declares success. There's no uncertainty flag, because from inside the loop nothing looked wrong.

Software engineering already measured the softer version of this. SpecBench decomposes 30 systems-level programming tasks into a spec, a visible test suite, and a held-out suite that composes the same features, and quantifies reward hacking as the gap between them. Every frontier agent saturated the visible suite; the holdout gap persisted, and grew by 28 percentage points for every tenfold increase in code size. The failure catalog includes a 2,900-line hash-table "compiler" that memorized the test inputs.3 Give an agent a proxy for correctness and it will find the proxy.

Firmware doesn't even offer a proxy to game. It offers nothing — and we have our own receipt for what that produces. We staged a Cortex-M fault-debugging session, planted a write straight into flash, and planned to hand an agent the fault dump so it could diagnose and fix the bug. There was no fault dump to hand it: the HardFault never fired, because the simulator's CPU model doesn't escalate bus faults. Four architecturally distinct ways to fault the core, zero faults, and a run that idled out at the time limit looking exactly like a healthy one. The instrument wasn't plugged in, and from inside the loop that looks identical to nothing being wrong.

That is the shape of the risk in AI embedded systems work: not garbage output you can spot, but a clean run, a confident all-clear, and a bug nobody caught.

Is this a pigeon meme with a coding agent mistaking silent UART output for a passing test

What closing the loop in AI firmware development actually requires

Not "better models." Four concrete capabilities, none of which are about intelligence:

  1. Execute on the target. Not x86_64 with the HAL stubbed out. The actual instruction set, the actual memory map, the actual reset vector.
  2. Observe more than stdout. Register values, peripheral state, memory contents, interrupt activity, and the bus warnings the model itself generated. printf is a proxy, and EmbedAgent's own benchmark design agrees: it moved past grading on serial output alone to monitoring real component state, because text on a wire doesn't confirm the hardware did the right thing.2
  3. Attribute a fault to a cause. "It hung" is not feedback. "It faulted at PC 0x8000604 reading an unmapped address" is. The gap between those two sentences is the difference between an agent guessing and an agent debugging.
  4. Iterate without a human in the middle. If closing the loop requires someone to walk to a bench, the loop is a ticket queue, not a loop.

That's what we're building Chiplab for, and post 11 is public proof we haven't finished either — our own fault escalation isn't wired up yet, which is precisely why that HardFault never fired in the first place.

So is Copilot for embedded actually good enough?

For writing code, yes, and I'd rather concede that loudly than pretend otherwise. Every argument that starts with "but the code is bad" is going to lose, and it's going to lose soon. We already made the narrower version of this case: LLMs write good firmware and can't prove it. The generation half is a solved product with a pricing page.

The verification half doesn't have a product yet. It has certification standards that demand every artifact be traceable. That's the point Promwad makes better than most: firmware is judged on deterministic behavior across interrupts, DMA, and timing, not function-level correctness, and GenAI "generates plausible code, not verifiable system behavior."4 It has vendor-specific register maps that differ between variants of the same family, which is the one substantive thing in the otherwise fairly promotional roundups of embedded AI constraints.5 What it doesn't have is a way for the agent to check.

Agents are going to own firmware. Embedded engineers become reviewers, the same way we became reviewers of compiler output and stopped reading assembly. I have zero doubt about the destination.

But the ordering isn't optional. An agent that writes brilliantly and cannot observe is a very fast intern who never gets told they were wrong. Execution access has to land before autonomy does, and right now, for almost every chip on the market, it hasn't landed at all.

The code takes minutes. Finding out it was wrong still takes a human with a board.

Sources

Footnotes

  1. "SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering," arXiv:2405.15793. https://arxiv.org/abs/2405.15793

  2. "EmbedAgent: Benchmarking Large Language Models in Embedded System Development," arXiv:2506.11003v3. https://arxiv.org/html/2506.11003v3 2

  3. "SpecBench: Measuring Reward Hacking in Long-Horizon Coding Agents," arXiv:2605.21384v1. https://arxiv.org/html/2605.21384v1

  4. Promwad, "GenAI for Firmware: Where LLM Code Generation Breaks Down in Real Embedded Systems." https://promwad.com/news/genai-for-firmware-llm-code-generation-certification-risks

  5. Garg, Jatin. "AI Code Generation in Embedded Systems: Constraints and Solutions." GoCodeo, July 1, 2025. https://www.gocodeo.com/post/ai-code-generation-in-embedded-systems-constraints-and-solutions