ac6-training, un département d'Ac6 SAS
EN
EnglishFrench
go-up

ac6 ac6-training

Letting an LLM write embedded code: what works and what breaks

A language model produces code that looks correct. On a server the gap shows up quickly: the test fails, the exception surfaces. On a microcontroller an invented register raises no error, it configures something else, and the defect appears three weeks later on one board in ten.

That asymmetry is what makes AI assistance different in embedded work, not some supposed inability of the models.

What genuinely works

Code transformation. Converting a state machine from one form to another, moving from one HAL API to another, systematically adding return-value checks across three hundred call sites. The model excels when the source of truth is in the context and the task is mechanical.

Writing test benches. Generating edge cases for a frame parsing function, producing a bench that covers overflows, zero lengths, invalid CRCs. It is tedious work with no hardware traps, and the model is good at it.

Driver skeletons. A Zephyr or Linux driver structure with its init tables, macros and registration is highly codified. The model produces a sound starting point to fill in.

Explaining existing code. Working out why a legacy function pokes a register a certain way, provided you supply the component documentation.

What breaks, and why

Invented registers and bit fields. This is the dominant failure mode. The model saw thousands of STM32 drivers during training; it produces a register name consistent with that corpus but absent from your exact part number. The compiler cannot catch it if the name comes from a generic header, and the write lands in a reserved register.

Timing assumptions. A delay computed "roughly", an unbounded wait loop, a timeout expressed in iterations rather than microseconds. The code works on the bench and fails cold, or after a clock frequency change.

Concurrency problems. A model rarely produces the required memory barriers, forgets volatile on a variable shared with an interrupt, and proposes critical sections that are too wide or too narrow. This is a domain where the mistake is invisible to a quick review.

Memory management under constraint. Dynamic allocation where there must be none, stack buffers sized without regard for a 2 KB stack, recursion.

Coding rules. MISRA C, no malloc after initialisation, in-house conventions: a model respects them only if reminded explicitly, every time.

Context changes everything

The difference between a useless assistant and a productive one is not the model, it is what you give it.

Supply the component header, not the part number. Pasting the relevant register definitions, or the reference manual excerpt, eliminates the whole invented-register class. The model stops guessing because it is reading.

Supply an example of your existing code. A driver already written in the project beats a page of style guidance: the model imitates what it sees.

State the constraints explicitly. "No dynamic allocation, 2 KB stack maximum, callable from interrupt context, MISRA C 2012 rule 21.3 compliant" produces a very different result from "write me a driver".

That is exactly what structured context mechanisms such as the Model Context Protocol formalise, giving the model controlled access to datasheets and project sources rather than to its training memory alone.

The validation loop, non-negotiable

Model-produced code must pass the same filters as code from a capable intern with no hardware experience.

  1. Compile with all warnings, -Wall -Wextra -Werror. A good share of the inventions dies here.
  2. Static analysis, cppcheck or a MISRA tool. It catches buffer overflows and dubious conversions that review lets through.
  3. Targeted review on three points: is every register access checked against the manual, is every variable shared with an interrupt volatile, does every wait loop have a bound.
  4. Test on target, not only in simulation.

Point 3 is the one that requires an engineer. The other three automate.

A simple rule for deciding

Give the model what you can verify faster than you could write it. A test bench, a mechanical conversion, a skeleton: verification is quick and the gain is real.

Do not give it what costs more to verify than to write. A clock initialisation sequence, a critical interrupt handler, a synchronisation protocol between cores: reading line by line against the reference manual takes longer than writing it yourself, and yields less confidence.

References

Going further

These reflexes are built by practising on real code, with datasheets in context and a validation loop in place. That is the subject of our AI-Assisted Embedded Development course.