GenAI for Firmware: Where LLM Code Generation Fails — and Where It Helps

GenAI for Firmware: Where LLM Code Generation Fails — and Where It Helps

 

Quick Overview

Problem: Function-level-correct LLM output passes review and unit tests, then fails as firmware — because firmware is judged on deterministic system behavior and a traceable process, not correctness at the function boundary.

Common failure points: Unprotected shared state across interrupt contexts, DMA cache-coherence and buffer-lifetime bugs, missed real-time deadlines from hidden blocking or priority inversion, vendor-SDK misuse against undocumented constraints, and generated code with no traceability to requirements or verification.

Where it appears: Safety- and timing-critical firmware — automotive ECUs, industrial controllers, railway and medical devices — and any MCU program piloting AI coding assistants.

Engineering focus: System-level verification, interrupt/DMA discipline, worst-case timing validation, MISRA plus static analysis, and traceability under the applicable standard (ISO 26262, IEC 61508, IEC 62304, or DO-178C).

 

Prroduction Failure Scenario

The driver compiled on the first try and passed its unit tests. In review it looked like something an experienced engineer would have written — a clean SPI-to-DMA path for a sensor front end, idiomatic HAL calls, sensible naming.

On the bench it ran without a fault. Then, under sustained load on a cache-enabled Cortex-M7 target, data started coming back corrupted — not a crash, just wrong values, intermittently, on one board and not another.

The generated code had assumed a coherent, sequential memory model. It set up the DMA transfer and read the buffer back with no cache maintenance, and in one path let the buffer be reused before the transfer completed. On a core with a data cache and a DMA engine writing straight to SRAM, that is a correctness problem no compiler warns about and no function-level test catches.

Then came the harder question. In review, someone asked where the function came from — which requirement it traced to, who had reasoned about its failure modes, and how the safety case should account for it. For the generated module, there was no documented answer. The code was plausible; it was neither verified system behavior nor traceable to anything. That is the boundary this article is about.

Wrong Assumption

The assumption behind most GenAI-in-firmware disappointment is reasonable on its face: if the code compiles, passes its unit tests, and reads like idiomatic C or Rust, it will behave the same inside the system and can enter the product like any hand-written code. That skips what firmware is actually evaluated on. A function can be correct in isolation while the firmware is wrong across interrupts, DMA transactions, memory and cache state, and timing budgets — and, in regulated products, while its implementation units have no traceable link to approved requirements and verification evidence. An LLM generates the most probable continuation of a prompt; it does not model the peripheral state machine, the cache, the scheduler, or the safety argument. So it produces code that looks right and can carry structural defects that surface only at system scale or in an audit.

Why It Fails

Concurrency across execution contexts. On a microcontroller an interrupt can preempt the main loop or an RTOS task almost anywhere, so data shared between an ISR and a thread has to be handled deliberately. The volatile qualifier only stops the compiler from caching a value in a register — it provides no atomicity, memory ordering, or synchronization, and treating it as thread-safe is a classic error. Correctness comes from the right mechanism for the case: atomic operations, critical sections (architecture-specific interrupt masking, e.g. PRIMASK/BASEPRI on Cortex-M), RTOS synchronization primitives, and a clear ownership and concurrency design. GenAI can omit or misplace these, producing code that compiles, runs in a debug session, and corrupts a shared value under real preemption — the class of defect disciplined MCU firmware engineering is built to prevent rather than debug after launch.

DMA and memory reality. A DMA engine moves data between a peripheral and memory independently of the CPU. Three constraints decide whether that works: the buffer must stay valid for the whole transfer, it must be aligned and placed correctly, and on cache-enabled cores the CPU’s cached view has to be reconciled with what DMA wrote — clean before a memory-to-peripheral transfer, invalidate after a peripheral-to-memory transfer, or place the buffer in a non-cacheable region. LLMs generate code that assumes a flat, coherent, sequential model. That holds on a core with no data cache and breaks on one that has it enabled — which is why the same generated driver passes on one part and fails on another.

Real-time behavior. Firmware runs under latency budgets where a missed deadline is a system fault. Generated code introduces hidden blocking calls, unbounded loops, and abstraction layers that inflate worst-case execution time; it also creates lock patterns where a high-priority task waits on a resource held by a lower-priority one, and if priority inheritance or a priority-ceiling protocol is absent or misconfigured, that inversion pushes worst-case latency past the deadline. Little of this appears at compile time or in unit tests — it shows up under real workload. Which OS and scheduling model a product uses is itself a design decision, covered in the 2026 RTOS landscape for safety and MISRA alignment.

Vendor SDK and undocumented constraints. Peripheral drivers and middleware carry initialization-order requirements, silicon errata, and side effects that are often not in the headers or even the reference manual. Unless the current, controlled documentation is explicitly supplied to it, an LLM has no reliable access to these — and even then its output must be verified, since retrieval-augmented setups and tool access improve results without removing the review step. It generates API sequences that are syntactically valid and operationally wrong: a clock enabled after the peripheral it feeds, an errata workaround skipped, a callback assumed reentrant when it is not.

No inherent traceability. Even correct generated code does not originate from a requirement in a traceable way — it is a statistical continuation of a prompt. Certified processes need bidirectional traceability between requirements, architecture/design, implementation units, and verification evidence, plus control of unintended and dead code. Generated output can enter that chain, but only once it is verified and linked like any other code — the point developed in how embedded integration accelerates certification under ISO 26262 and IEC 62443.

Hidden System Complexity

requirement → architecture → design → code (hand-written or generated) → compiler / optimizer → linker & memory map → MCU peripherals (NVIC, DMA, cache, MPU) → real-time schedule → field behavior → safety case & audit

A defect seen in the field usually originates several layers up. A missing cache-maintenance step is invisible in the C source, silent through the compiler, and only becomes wrong at the DMA-plus-cache layer — and then only on parts that actually have a cache enabled. Fix the symptom without tracing the chain and you ship a different intermittent fault.

Over all of this sits the certification axis. It is not a stage in the pipeline, but every element has to participate in bidirectional traceability — requirements to design to implementation units to verification evidence — the discipline behind functional safety under IEC 61508 and ISO 26262 in automotive, and the part of firmware a code generator sits outside of.

Failure Patterns

Scenario 1. A GenAI-generated SPI-to-DMA sensor driver passes its unit tests and runs clean on a Cortex-M4 evaluation board. Ported to a cache-enabled Cortex-M7 production part, it corrupts a small, steady fraction of transfers under load, because the code reads the DMA target buffer with no cache invalidation and the CPU serves stale cache lines. The debug build, with the cache configured differently, never reproduces it.

Scenario 2. A generated routine updates a status structure shared between a UART receive ISR and the main loop with no critical section and only a volatile qualifier. It works for weeks, then under sustained traffic drops or duplicates a field — a non-atomic read-modify-write interrupted at the wrong instruction. It looks like intermittent data loss uncorrelated with anything the application does.

Scenario 3. A control loop assembled from generated blocks meets its deadline in isolation. Under full RTOS load, a logging call the model inserted takes a mutex also held by a lower-priority task; with no priority-inheritance protocol configured, the resulting inversion pushes worst-case latency past the deadline intermittently. It passes every bench test and violates timing only when every task is active at once.

Where GenAI Genuinely Helps in Firmware

The same properties that make GenAI risky in the real-time and safety path make it useful wherever its output is cheap to verify: boilerplate around vendor SDKs, peripheral-init scaffolding, refactoring and navigation of long-lived codebases, documentation drawn from code, and unit-test and mock templates. Used as an untrusted draft an engineer reviews and owns, it compresses the repetitive work without touching the guarantees. Where those boundaries sit is set out in LLM-aided design for hardware and embedded engineering and in trust boundaries for LLMs in embedded design.
 

MCU Firmware Engineering — Bare-Metal and RTOS

The firmware failures behind GenAI-assisted code — interrupt races, DMA and cache-coherence faults, timing violations, vendor-SDK misuse, and missing traceability — are structural, not typos. Closing them takes system-level architecture, interrupt/DMA discipline, worst-case timing analysis, and a verification and traceability process, not another pass of the same generator. Promwad develops bare-metal and RTOS firmware for MCU platforms — BSP and driver development, HAL engineering, firmware porting, board bring-up, and MISRA-aligned, unit-tested code for safety- and timing-critical products.

Explore MCU Firmware Development →

Engineering Experience Across MCU and Embedded Platforms

 

A GenAI-Assisted Driver That Cleared Review and Failed on Certification Scope

A team building firmware for an industrial control product on an automotive-grade MCU used a coding assistant to speed the peripheral and driver layer. The generated code passed review quickly, and the schedule looked ahead of plan through integration.

Two problems landed later, in the same week. First, a generated ADC-over-DMA acquisition path produced intermittent bad samples on the production silicon — a cache-enabled Cortex-M7 part — because the buffer was read back without invalidation and, in one path, could be reused before the transfer completed. It had never reproduced on the earlier evaluation board. Second, the safety assessor asked for requirement-to-code and code-to-test traceability for the driver layer. For the generated modules there was none: no documented origin, no failure-mode reasoning, nothing the safety case could reference.

Neither was a code-quality issue in the narrow sense. The first was a hardware-model gap the generator could not see; the second was a process gap generated code sits outside of until it is verified and linked. The fix was a targeted rewrite of the affected drivers under the defined process — cache-correct DMA handling, buffer-lifetime guarantees, and a rebuilt traceability chain from requirements through unit tests — plus a policy that kept generated output as reviewed draft rather than trusting it on inspection.

Rework in cases like this typically runs into weeks — most of it re-establishing traceability that never existed, not fixing the DMA bug. The productivity the assistant added early is real; it tends to be spent, and then some, on the two things it cannot do: model the hardware and account for its own output in the safety case.

For a published, verifiable example of what certified firmware actually requires, see Promwad’s dual-MCU railway BMU architecture built for SIL2 readiness — an independent safety MCU, MISRA-compliant firmware with unit-test coverage, and FMEDA, a safety concept, and a traceability matrix as first-class deliverables.

GenAI-Assisted Driver That Cleared Review and Failed on Certification Scope

Solution Approach

Step 1: Treat generated code as untrusted draft inside the controlled lifecycle. GenAI output can enter the process — but as draft material that then goes through the same review, static analysis, traceability, and testing as any other code, never as a finished artifact trusted on inspection. MISRA C:2025 makes this explicit: AI-generated code must meet the same guidelines as hand-written code. Use generation freely where verification is cheap — scaffolding, documentation, test templates — and hold implementation logic in the real-time or safety path to the full process, validated by QA and test automation.

Step 2: Verify at the system level, not the function level. Review generated and hand-written code alike for what the function boundary hides: shared-state handling across ISR contexts, cache-correct and lifetime-safe DMA, worst-case timing under full load, and SDK init order against the errata. Back it with static analysis and a MISRA C:2025 (or the project’s approved MISRA baseline) check, and with hardware-in-the-loop and target-hardware stress testing — where cache, DMA, and timing faults are most realistically exposed. Where the code touches security, embedded security hardening is part of the same review.

Step 3: Assess the tool against its intended use, not a blanket rule. In safety-related development the question is the tool’s intended use and potential impact, and whether its output is independently verified through the approved lifecycle. If generated code is treated as untrusted draft and verified downstream, the qualification obligations differ from those on a tool whose output is trusted directly. Non-determinism and model drift genuinely make tool qualification and configuration control harder — but they do not create a universal ban. Design the workflow so the deterministic, qualifiable parts of the toolchain — compiler, static analyzer, test framework — carry the assurance weight, and keep the generator on the reviewed side. ISO 26262-8 and DO-178C/DO-330 describe these tool-confidence mechanisms; the traceability that makes any of it auditable is the discipline behind ASPICE-compliant development, and the trust-boundary question is explored in trust boundaries for LLMs in embedded design.

Real Trade-Offs

Using GenAI for boilerplate and scaffolding speeds the early, review-cheap work, but shifts effort downstream into verification — the net win is real only when review and test capacity exist to absorb it. Under-resource the review and the generator becomes a source of latent defects, not a time saver.

Generating in Rust instead of C removes memory-safety defects and data races by construction, which eliminates a real subset of the failures above. It does not remove logical races, deadlocks, missed deadlines, or DMA and cache-coherence bugs, and it offers no protection inside unsafe blocks — which embedded code needs for register and DMA access. The embedded Rust ecosystem and certified-toolchain story are also narrower than C’s, and much production silicon still ships C-first vendor SDKs.

Standardizing on a coding-assistant workflow raises throughput on new, low-criticality code, but for safety-related modules the tool-assessment and verification burden caps how far it goes — the same product often runs two workflows, one for the certified core and one for everything around it.

Leaning on generated documentation and test scaffolding is low-risk and consistently useful, because a human verifies the output against the code; leaning on generated implementation logic in the real-time or safety path is high-risk, because the cost of a missed defect is a field failure or a failed audit. The deciding factor is the cost of failure and the difficulty of verification, not the language or the task label. Which RTOS and safety posture a team actually chooses is laid out in how safety-critical teams choose between QNX, Zephyr, and embedded Linux.

Typical Firmware Engineering Tasks

Driver & DMA Correctness Review

Auditing ISR/shared-state handling, DMA buffer lifetime and cache maintenance on cache-enabled MCUs, alignment, and vendor-SDK init order against silicon errata.

Real-Time & Timing Analysis

Worst-case execution-time analysis, priority-inversion and blocking-call detection, and timing validation under full RTOS load rather than in isolation.

MISRA & Static-Analysis Gating

MISRA C:2025 (or the project’s approved baseline) conformance, static analysis in CI, and hardware-in-the-loop coverage sized to the module’s criticality.

Traceability & Safety-Process Support

Establishing requirement-to-code-to-test traceability and supporting the client’s tool-classification decisions (per ISO 26262-8 / DO-330) so generated output stays on the reviewed side of the process.

Qualifying Symptoms

  • Generated code passes unit tests and review, then produces intermittent, non-reproducible faults once integrated with real peripherals.
  • A driver works on the evaluation board and fails on the production part — usually a cache-coherence or DMA-lifetime gap exposed by a cache-enabled core.
  • Rare data corruption correlated with interrupt or bus load, pointing to unprotected shared state across an ISR boundary.
  • Timing budgets hold in isolation but are violated under full RTOS load, from hidden blocking calls or unconfigured priority inheritance.
  • A safety assessor asks for requirement-to-code traceability on generated modules, and there is no documented origin to point to.
  • There is no written boundary for where a coding assistant may operate relative to the safety-related or real-time path.

Solution Context Link

At this point the work is firmware architecture and verification, not another round with the generator: a driver-and-DMA correctness review, worst-case timing validation under real load, MISRA and static-analysis gating, and a traceability chain that treats generated output as reviewed draft. This article is about AI-assisted authoring of firmware; running AI models on the device is a different problem, handled at the edge-AI engineering layer, where model runtime, memory, and real-time behavior are co-designed with the firmware. For the broader picture of where LLMs fit across hardware and embedded flows, see LLM-aided design for hardware and embedded engineering; and where the codebase spans bare-metal, RTOS, and Linux, embedded software development is where those boundaries get drawn.
 

This class of problem concentrates in safety- and timing-critical firmware — automotive ECUs, industrial controllers, railway and medical devices — where GenAI is piloted to speed the driver and boilerplate layer and where cache, DMA, timing, and traceability are underestimated at scoping. What certified firmware actually takes is visible in Promwad’s dual-MCU railway BMU: an NXP S32K37xx main MCU with an independent S32K1xx safety MCU, MISRA-compliant firmware with unit-test coverage, and FMEDA, a safety concept, and a traceability matrix as first-class deliverables, on a SIL2 path under EN 50126 / 50128 / 50129. That is the layer a generator does not reach.

FAQ

Can GenAI generate production firmware for embedded systems?

 

Not as a final artifact. LLMs generate syntactically and often logically correct C or Rust, but firmware is judged on deterministic behavior across interrupts, DMA, cache, and timing — and, in regulated products, on a traceable process. Generated code can be a draft, a scaffold, or a reference pattern that a human reviews, links, and puts through the defined development and verification process. It cannot be flashed and trusted on the strength of a passing unit test.
 

Why does generated firmware pass tests and still fail on hardware?

 

Because unit tests check function-level correctness and firmware fails at the system level. The common cases are a DMA buffer read without cache invalidation on a cache-enabled core, shared state touched from an ISR with only a volatile qualifier and no critical section, and a timing budget blown under full RTOS load through hidden blocking or unconfigured priority inheritance. All three pass a bench test and surface as intermittent, load-correlated, hard-to-reproduce faults.
 

Where is GenAI actually useful in a firmware workflow?

 

Where correctness is cheap to verify independently: boilerplate around vendor SDKs, peripheral-init scaffolding, code navigation and refactoring of long-lived codebases, documentation extracted from code, and unit-test and mock templates. In each case, the engineer verifies the output and stays in control of the result. The value is real and concentrated outside the real-time and safety-critical logic.
 

Can GenAI be used in certified firmware — ISO 26262, IEC 61508, DO-178C?

 

Yes, within limits. Generated code can enter a controlled lifecycle as untrusted draft, provided it then passes the same review, traceability, static analysis, and verification as any other code — MISRA C:2025 states outright that AI-generated code must meet the same guidelines as hand-written code. What it cannot do is bypass those steps. Whether and how a generator needs tool qualification depends on its intended use, its potential impact, and whether its output is independently verified. Non-determinism and model drift make qualification and configuration control harder, but there is no blanket prohibition. Keep generated implementation logic out of the trusted-tool-output path and inside the reviewed, traceable one.
 

What is the main limitation of LLMs for embedded systems?

 

They model text, not hardware. An LLM does not track peripheral state machines, bus timing, cache behavior, or the scheduler, and it has no reliable access to undocumented SDK constraints or silicon errata unless the current, controlled documentation is explicitly supplied — and even then the output has to be verified. It produces the most probable code for a prompt, which is why it is strong on stable, repeatable patterns and weak exactly where embedded systems are hard: timing, concurrency, and the interaction with real hardware.
 

Related Engineering Cases

Tell Us About Your Firmware Project

Share your MCU target, where GenAI is in the workflow, the symptom you are seeing (intermittent faults, timing misses, or a traceability gap), and your safety standard. We’ll define the next architecture or verification step.

Tell us about your project

We’ll review it carefully and get back to you with the best technical approach.

All information you share stays private and secure — NDA available upon request.

Prefer direct email?
Write to info@promwad.com

Secured call with our expert in 24h
Secured call with our expert in 24h
Secured call with our expert in 24h
Plug-in model for your full-cycle R&D
Secured call with our expert in 24h
22 years of engineering expertise
Secured call with our expert in 24h
500+ projects for OEMs in EU & US
Secured call with our expert in 24h
MVP in 8–10 weeks — predictable delivery
Secured call with our expert in 24h
Featured at IBC, Embedded World, MWC