← Essays /Post · 12 of 22 · Embedded Dev

AI-Written Firmware: How to Stop AI from Breaking Firmware

3-Tier Trust classifies PRs. Verification gates (spec, TDD, RAG, HIL, deterministic analyzer, more) take them to merge. Each catches a different defect class.

·6 min read · · · #llm#embedded-dev#firmware#ai-agents#code-review
AI-Written Firmware: How to Stop AI from Breaking Firmware
On this page

The Part 3 benchmark post got an immediate reply: “So what do we actually do with this?” There’s no single answer. It depends on the category. Run the EmbedEval n=3 data and LLM output falls into three buckets: safe to approve on its own, review required, and write it by hand from the start. The interesting part is that the boundary doesn’t budge no matter how many tokens you throw at it. Datasheets and board identity live outside the model’s context to begin with.

3-Tier Trust

TierCategoriesSonnetHaikuPR Rule
1. Auto-mergedevice-tree, pwm, boot, kconfig90%+90%+syntax review, then merge
2. Expert review requiredgpio, linux-driver, yocto, ota, power-mgmt, spi-i2c, ble67 to 90%45 to 83%verify error paths + side effects
3. Expert writes from scratchDMA, ISR, threading, storagebelow 55%below 55%LLM draft is reference only

The numbers come from Part 3: the n=3 measurements over 233 TCs. The threshold is simple. Both models under 60%, with n=3 stability under 80%, means Tier 3. Why 60% and 80%? That’s where the inequality “time to write from scratch ≤ time to polish an LLM draft into something that passes” stops holding. Expect to re-measure it when the next model generation ships.

The CAN logging daemon that killed an eMMC back in Part 1 lives in Tier 3 storage (flash wear leveling, NVS lifecycle). The 6 failure patterns I pulled out in Part 2 land the same way: ISR (volatile), DMA cache coherency, error-path cleanup, all crowded around Tier 3. Part 3 pinned the boundary with numbers. This part moves those numbers into a team operating rule.

Why More Tokens Don’t Move the Boundary

For ordinary software, quality tracks a log curve as you pour in more tokens. Embedded flattens out at some point. And the places where it flattens have something in common: the datasheet, the reference manual, real-time timing, and “which board am I sitting on right now.” All of it missing from the context.

In EmbedAgent (ICSE 2026), datasheet RAG pulled ESP-IDF from 29.4% to 65.1%. That’s 35.7 points back. What the number tells you is that context engineering raises the ceiling. It doesn’t remove the ceiling. Push whole datasheet pages in through RAG and facts like “my board is mask set X of the i.MX8M Mini, and SDMA channel N is wired to PHY Y” still don’t cross over on their own.

Tier 3 Is Red, Not Grey

DMA category: Sonnet 31%, Haiku 8% (Part 3 n=3). Here is one concrete case for where those numbers come from.

Allocate an i.MX8M Mini SDMA buffer with kmalloc() instead of Linux’s dma_alloc_coherent API, and the SDMA can’t see the data trapped in the A53 L1 cache. The i.MX8M Mini reference manual says it flat out: SDMA sits outside the Cortex-A53 cache coherency domain. The fix is one line.

/* SDMA on i.MX8M Mini is outside the A53 cache coherency domain.
 * Use coherent allocation instead of kmalloc.
 * dev = &pdev->dev (struct device *) from platform_driver probe.
 * dma_handle: dma_addr_t, dma_buf: void *. */
dma_buf = dma_alloc_coherent(dev, DMA_BUF_SIZE, &dma_handle, GFP_KERNEL);

There are two reasons the LLM can’t write that line. The relevant page of the reference manual is missing from its context, and the fact that “I’m on an i.MX8M Mini” sits outside the prompt in the first place. You can iterate all day and it just keeps not knowing. And to look at LLM output, see kmalloc where dma_alloc_coherent belongs, and call it broken, the reviewer already has to know non-coherent SDMA exists. A reviewer who doesn’t know the answer turns the review into a rubber stamp. The eMMC death in Part 1 was exactly this shape: a junior merged an LLM-written logging daemon, and nobody in the review chain thought to suspect write amplification.

How to Stop It

Block auto-merge with CODEOWNERS. Drop the Tier 3 directories into .github/CODEOWNERS or a branch protection rule.

# Tier 3: expert review mandatory, no LLM auto-merge
drivers/dma/            @firmware-leads
kernel/threading/       @firmware-leads
drivers/storage/        @firmware-leads

Sonnet only on Tier 2. Sonnet at 68% versus Haiku at 57%: that 11-point gap only earns its keep at Tier 2. Tier 1 runs fine on Haiku, and Tier 3 gets written from scratch by a human whatever model you bolt on. So route Sonnet calls to Tier 2 PRs only and the bill drops noticeably. Exact savings depend on your team’s Tier mix (PR counts, token volume).

Tier 2 review looks only at error paths and side effects. This is the stretch where an LLM halves your work time, and exactly two spots go uncovered. One is the error-path branches and reverse-order resource cleanup. The other is the side effects of recipe, DTS, and Kconfig changes. The second one burned us once. We changed a single Yocto recipe, it touched the kernel DTB, and after boot the SPI flash write latency jumped from 50ms to 800ms. A review that only stares at the recipe diff will never catch that.

The boundaries keep moving. Run this for about six weeks and more than half the categories get reclassified. BLE GATT climbed from Tier 2 to Tier 3 once its timing sensitivity surfaced; i2c bring-up dropped from Tier 3 to Tier 2 after we attached datasheet RAG. So this table isn’t a set-once-and-forget thing; it’s an operating artifact you re-measure every quarter.

Verification Gates, in Three Layers

Those four moves are pieces of a larger pipeline. Code that makes it all the way through Tier 3 to a merge passes verification gates that each catch a different class of defect. Three layers, roughly: Implicit Context (inject facts at generation time), Review (static checks before merge), Test (runtime verification).

1. Implicit Context. Push the facts into the LLM’s context at the moment of generation.

  • Spec-driven generation. Pin the requirements as a contract YAML first, so the LLM and the reviewer read the same contract.
  • Datasheet RAG injection. Chunk the board reference manual, errata, and SDK examples, then feed them in as generation-time context. EmbedAgent ICSE 2026’s +35.7-point recovery is the quantitative data for this layer.

2. Review. Verify the generated code statically before it merges.

  • Multi-agent review. Cross-check one model’s output with a different model and a different prompt. Don’t lean on a single model’s stability (Sonnet 87%, Haiku 73% on the Part 3 n=3 numbers).
  • Static analysis. Rule-based pattern detection. Missing volatile, ISR blocking, error-path cleanup, the 49-day wrap: the anti-patterns grep can find.
  • Deterministic analyzer. Keep board, errata, contract, and requirement as git YAML facts, and feed the same facts to both the LLM at generation time and the CI gate. Defects that static analysis misses for lack of board context get caught here. Examples: eMMC wear accumulation patterns, board-specific contract violations, board-fact checks like “this counter is 32-bit, so it wraps.” Get this layer right and even a category like SDMA drops to Tier 2.
  • Human expert review. The last gate. With the automated stages laid down ahead of it, expert time goes to system-level calls: architectural fit, novel defects, domain trade-offs.

3. Test. Verify behavior by running it.

  • TDD. Test cases before the code. It shrinks the space an LLM fills with hallucination.
  • On-board test. HIL with one real board wired into CI. It catches what QEMU can’t see: DMA cache coherency, interrupt latency, clock jitter.

Tool choices at each stage, how deep to go per layer, and the information flow between layers, I unpack all of that in the next part.

Limits

3-Tier Trust is a classification, and that’s all. Classification by itself catches nothing. It has to run alongside the verification gates. Board Profile, datasheet RAG, a HIL farm: this context engineering work is the infrastructure those gates run on.

EmbedEval is over 90% Zephyr. On ESP-IDF or STM32 HAL the Tier boundaries can shift. You have to measure the numbers for your own platform.

The last limit weighs the most. Tier classification is no help to a team without experts. With nobody to write Tier 3 from scratch, you can’t even tell whether the LLM output is any good. At that point adopting an LLM grows your risk instead of your productivity. Part 1’s dead eMMC is the proof. The Tier classification didn’t prevent the accident; it just pinned a label on after the fact.

Wrapping Up

Part 1 was the case: LLM-written code can kill an eMMC. Part 2 was the taxonomy: that failure resolves into six patterns, clustered in Tier 3. Part 3 put a number on where the boundary sits. Part 4 moves that measurement into the big-picture operating rules, an overview of the verification gate pipeline. The next part takes the gates apart one by one, by tooling, configuration, and trade-off.

The SDMA fix on i.MX8M is one line of dma_alloc_coherent(). But the judgment to write that line came out of the reference manual and the oscilloscope, not out of tokens. The whole series compresses into that one sentence.

ecro
Written by ecro

Created an LLM benchmark for firmware. EmbedEval →

Building a project-shaped agent harness for Claude Code, Cursor, and Codex. harness-maker →

Building a terminal that reads your datasheets. NeuroTerm →

Comments

Loading comments...