Things to Think About When Doing Low-Level Embedded Systems Development.


Building a Mental Model

The list is presented as questions I ask myself when thinking about a firmware system. The questions are without answers, as the purpose is to query and elaborate a mental model. I’m assuming the list is incomplete.

The ISA and ABI:

  • What registers does my ISA have, and how they are used by the compiler?
  • What is the calling convention? How much stack space is consumed by an ISR or a function call?
  • Can I at least quickly guess what any assembler mnemonic might mean?

The Stack:

  • What is the stack? Where is it? Who put it there? What register points to it?
  • How much stack memory do I have? Is it shared between interrupt context and task context?
  • What am I doing to avoid stack overflow? What am I storing on the stack?
  • What happens when I overflow the stack, how does my software recover?


  • Where/how are globals initialized? What about function local statics?
  • Do I need to have a heap? What might need to be allocated/de-allocated at runtime?


  • Where is the data stored? What is tightly coupled memory? What is cache? What’s the difference and what does my platform use?
  • What is memory alignment? What is the width of my platform’s bus?
  • Where is the code stored? Is random access to code penalty-free?

Interrupts/Critical Sections (The hardware):

  • How long does it take to enter/exit an ISR? What is tail chaining and does my platform benefit from it?
  • How do my critical sections work, do they block all interrupts, block a priority level, or a mask?
  • How long does a critical section take to enter and exit? What is my longest timed critical section?

Interrupts/Critical Sections (The software):

  • How am I prioritizing my interrupts? What is the most time-critical ISR?
  • Are there interrupts that are too time-critical to be blocked by a critical section?
  • What is interrupt nesting? What is the maximum nesting level of my interrupts? Do I have enough stack if they all nest?
  • Why should I not do too much work in a high-priority interrupt handler or critical section?
  • How do I delegate work from a high-priority interrupt to a lower-priority context?


  • How do MMIO registers and system registers work? What’s the difference?
  • What are some gotcha’s with MMIOs, due to the fact they aren’t really memory?
  • Why should I use volatileto access MMIO?
  • Why should I be careful of the size of the memory bus used to access MMIO?
  • Why does reading a 64-bit timer register over a 32-bit bus occasionally have unexpected results?
  • Why might writing a set of 32-bit registers via a uint8_t* pointer lead to strange results?
  • What registers can I read-write-modify safely? Why do some registers have redundant views? (e.g. set register, clear register)
  • What are the modes, read-only, write-only, write-once, trigger-on-write, etc?

Execution privilege:

  • What are the different execution levels/privilege levels of my ISA? What permissions does each level have?
  • e.g Cortex-M ARMv6: Supervisor and task with just a different stack.
  • e.g Cortex-M ARMv7: Supervisor and task with different privileges.
  • e.g Cortex-R ARMv8 : (Hypervisor, Kernel, Task)
  • How do you transition between them? What level does the startup code exit to?
  • Does my RTOS use them? Do I need to call into a higher privilege to access some features?
  • Does my RTOS have different function calls depending on my context?

What About RTOSs? What about XYZ?

I specifically haven't included things like RTOS task creation, synchronization, etc. These are really just specialized application libraries — and in that sense similar to general application programming. For the same reason, I haven’t touched on all sorts of application support libraries (IoT libraries, DSP libraries, control libraries, etc).


As I write topics for this blog, the above list includes many things I’d like to cover, and some are covered by the articles I’ve already written. I find the intersection of hardware and software interesting, and that is where many “gotcha’s” for embedded software development live.

Application-Specific Questions

These are some topics that are not general enough to make it to the list above. I’ve placed them here as a reminder to myself that I can use them to refresh my mental model.

Low power/clock gating.

There are applications that require very low power consumption, it’s the hardware that consumes the power and needs to be turned off — but often it’s the firmware that decides what hardware needs to be active at any given time.

  • What is a clock, and why does the CPU need one? What does it mean to “gate” a clock?
  • What is and why is there a WFI/WFE instruction?
  • What power mode do I go into during WFI? What platform-dependent register controls the clocks? What clocks keep running?
  • Do I disable interrupts during clock gating control and WFI? What do you mean WFI is woken while interrupts are disabled? How can that even work?
  • If my CPU clock is disabled, do I wake up? (e.g. is there a special GPIO or comms port that can do asynchronous wake-up?)
  • How long does it take to exit a low power mode? Does an oscillator need to power up?
  • What blocks are powered off at any time? What happens if I write to a powered-off MMIO?
  • What happens to my timers (RTOS tick, real-time counter, etc) when I turn off clocks?

Analog Interfaces.

Analog hardware is a separate domain to digital hardware, with different designers who have completely different ideas on how things work.

  • What is analog hardware? Why is it different from digital hardware?
  • Flash memory is an analog circuit!? Did I need to enable some high voltage line and wait a bit before I write to flash?
  • Pin IO is an analog circuit!? What is a pull-up, pull-down, drive strength, floating pins, (pseudo) open-drain/collector?
  • Does everything in analog-land get controlled by digital hardware or my firmware?
  • How does everything in analog-land synchronize to the same clock as digital-land?
  • Does the block have another power source? Reference voltage/current? How long do I wait after a block has been enabled until it can be used?
  • What is a setup time? What is a hold time?
  • What is the default state of a comparator? Does initialization trigger an edge and interrupt that I’m not expecting? What other corner cases can occur on the boundary?



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store