GROUND TRUTH · EMBEDDED SYSTEMS

Embedded Engineers Are Expected to Ask What the Hardware Is Doing

Software people ask what the code should do. Embedded engineers also have to ask what the silicon is doing while it runs.

A correct-looking function can still fail if you never asked what the hardware was doing.

A software engineer debugging a desktop application can usually treat the hardware underneath as irrelevant: the operating system abstracts away the processor, the memory, the disk, and the code can be reasoned about almost entirely in terms of logic. An embedded engineer does not get that abstraction, or gets only a thin slice of it. The code they write is directly responsible for configuring physical registers, reading real voltages off a sensor, and timing signals on an actual wire. This is why an embedded engineer's instinct, when something behaves unexpectedly, is rarely "let me re-read my code" alone. It usually starts with "let me find out what the hardware did," because the two are not guaranteed to match, and assuming they do is one of the most common sources of embedded bugs.

The code and the hardware can disagree without any error message

Consider a function that reads a temperature sensor over an I2C bus and returns a value. If the code is logically correct, it sends the right commands, reads the right number of bytes, converts them with the right formula, it can still return a wrong answer if the sensor was not ready when the read happened, if the bus experienced electrical noise that corrupted a bit, if the sensor's power supply had not stabilized yet, or if a timing requirement in the sensor's own datasheet was not respected between issuing a command and reading the result. None of these show up as a compiler error or even necessarily as a software exception: the function runs, returns a number, and the number is wrong, or right only some of the time. An engineer who only checks the code for logic errors can spend hours confused about an intermittent bug that has nothing to do with the code's logic at all.

Where the instinct to check goes vs where the bug often is

What people thinkWhat it actually is
The function returned the wrong value, so the logic in the function is wrongThe logic can be correct and the value still wrong, if the hardware was not in the state the code assumed.
If the datasheet doesn't explicitly forbid it, the timing should be fineMany timing requirements are stated as minimums and maximums in the datasheet and are easy to violate without any obvious symptom until it fails.
A working prototype on the bench proves the design is solidA bench prototype often has better power quality, shorter wires and less electrical noise than a production unit: some hardware issues only appear at scale.

A datasheet is not optional reading

A component's datasheet describes, in specific and often unforgiving detail, exactly how that piece of hardware is meant to be used: supply-voltage ranges, timing requirements between signals, register bit layouts, startup sequences, electrical limits that cannot be exceeded without damaging the part. A software engineer coming from a desktop background can be surprised by how much of embedded work is reading and correctly interpreting these documents, because there is no equivalent step when calling a library function on a desktop. You trust the function's contract and move on. An embedded engineer configuring a peripheral has to read the datasheet's register map, understand what each bit controls, and often has to respect timing diagrams that specify, in nanoseconds or microseconds, how long to wait between one operation and the next. Skipping this and guessing at a configuration based on an example found online is a common source of code that "mostly works," which in embedded systems is a dangerous category: mostly working means it will eventually fail, usually somewhere less convenient than the lab bench.

The oscilloscope answers questions the debugger cannot

A software debugger can show what the program believes is happening, which line is executing, what a variable currently holds. It cannot show what is happening on a physical pin, because that information exists outside the program entirely, in the real electrical signal on a wire. An oscilloscope or logic analyzer connected to that pin can show whether a signal is toggling when the code believes it is, whether its timing matches what the datasheet requires, whether there is noise or ringing on the line that a purely software view would never reveal. Experienced embedded engineers develop the habit of reaching for these tools relatively early when something behaves strangely, not as a last resort, because a surprising number of embedded bugs turn out to live in the physical signal rather than the code that generated it, and no amount of staring at source code will reveal a problem that only exists on the wire.

Asking about the hardware is asking what state it is in

The habit of "ask what the hardware is doing" is really a habit of asking what state a physical system is in before trusting an assumption about it. Has this peripheral finished its power-on sequence yet? Is this bus free, or is another device holding it? Did that previous command complete before this one was issued, or did the code just assume it did because no error was returned? A desktop program rarely has to ask these questions because the operating system and a much more forgiving hardware environment handle most of them invisibly. Embedded code frequently has to ask them explicitly, because there is no layer underneath quietly making sure the hardware is ready: the embedded engineer's code often is that layer, for whatever software runs above it.

The habit generalizes well beyond any one bug

Once an engineer has been burned once by trusting a piece of hardware without verifying it, the underlying habit tends to spread into everyday work well beyond debugging a specific failure. It shows up as double-checking a new sensor's actual output on a scope before writing any code against it, rather than trusting the datasheet's typical values blindly. It shows up as being skeptical of a peripheral's documented default configuration and verifying it explicitly in code rather than assuming the reset state matches what the datasheet describes as default, because errata sheets, the lists of known deviations a chip has from its own datasheet, exist precisely because real silicon does not always match its documentation perfectly. This generalized skepticism toward unverified assumptions about hardware, built from specific painful experiences, is a large part of what experience means in embedded engineering, more than any single fact learned along the way.

Simulators and emulators are useful, but they are not the hardware

Many embedded teams develop and test a meaningful amount of code on a simulator or an emulated target before it ever touches the real board, and this is valuable. It is faster to iterate on, easier to debug with familiar desktop tools, and catches a large share of logic errors early. The risk is treating a simulator pass as equivalent to a hardware pass, when a simulator typically models the processor's instruction behavior far more faithfully than it models the analog and electrical reality around it: exact peripheral timing quirks, power-rail behavior under load, electrical noise, manufacturing variation between individual chips of the same part number. Code that behaves perfectly in simulation can still fail on real hardware because the simulator never modeled the specific timing edge case or electrical condition that the physical chip exhibits. Experienced embedded engineers treat a simulator as a fast first filter, not a substitute for eventually validating behavior on the actual target hardware under realistic conditions.

The cost of a bug found late is not the same across environments

A bug caught on a desktop during development typically costs the time it takes to fix and retest, often minutes. A bug caught after an embedded device has shipped can cost dramatically more, sometimes involving a physical recall, a field update requiring specialized equipment or technician visits, or in safety-relevant products, a formal corrective action process. This asymmetry is part of why embedded teams invest so heavily in testing on real hardware, in code review specifically focused on the categories of bug that are hard to catch after shipping, and in defensive design choices, watchdog timers, redundant checks, conservative margins on timing and memory, that a desktop-only mindset would likely see as unnecessary caution. The caution is calibrated to the actual cost of being wrong, which is much higher once a device is out of the lab.

A habit worth building before your first embedded role

  • When a value looks wrong, ask whether the hardware was in the expected state before assuming the logic is at fault.
  • Read the relevant section of a component's datasheet before writing code that configures it, not after something goes wrong.
  • Get comfortable with the idea that a bug can be intermittent because it depends on physical timing, not just logical conditions.
  • If you have access to an oscilloscope or logic analyzer, practice using one on a simple signal before you need it for something you cannot explain any other way.
  • Treat "it worked once" as weaker evidence than it feels like, hardware timing issues often only fail some of the time.

References and further reading

Background reading on the topic of this article. HireSetu is not affiliated with these organisations.

HireSetu
Career Intelligence Platform