A Cortex-M board reaching main() feels like progress.
But in bring-up, it is only evidence for a few things. It does not prove your clocks are configured, your UART baud rate is correct, your fault handlers are useful, or your linker script is doing exactly what you think.
Here is the bring-up question I like to ask:
Before I move to the next layer, what evidence do I have?
Reaching main() proves less than we want
It probably proves:
- The initial stack pointer was readable
- The reset vector was fetched
- Some startup code executed
- The debugger can observe the target
It may not prove:
-
.datawas copied correctly -
.bsswas cleared - The vector table is where exceptions expect it
- The clock tree matches your assumptions
- The first UART byte will have the baud rate you think it has
My favorite bring-up milestone: first trustworthy UART byte
For me, the first useful milestone is not just blinking an LED. It is sending a raw UART byte that I can verify on a scope or logic analyzer.
A repeated 0x55 pattern tells a very honest story.
If the bit timing is wrong, something below the driver layer is still unproven.
The bring-up loop
My practical loop is simple:
- Expect
- Observe
- Decide
- Record
That sounds boring, but it prevents a lot of bring-up pain.
A question for other firmware engineers
What is your first “real” sign of life on a new Cortex-M board?
- Reaching
main()? - GPIO toggle?
- MCO clock output?
- First UART byte?
- First interrupt?
- Something else?
I’d love to compare notes.
I also put together a free two-page Cortex-M bring-up checklist here:
https://embeddedville.com/resources/cortex-m-firmware-bring-up-checklist
If you’re learning embedded systems, preparing for firmware roles, or working through real bring-up/debugging projects, I am also building EmbeddedVille here:
https://www.skool.com/embeddedville-7477/about
Top comments (1)
Drop your answer in the comments: what is your first bring-up milestone that you actually trust?