Why does bring-up order matter?
A new board has two unknowns at once: the hardware may be wrong and the firmware may be wrong. Bring-up is the discipline of removing one unknown at a time, so that when something fails you know which side to suspect.
The sequence below works from the bottom up, from power and clocks to peripherals and finally to application logic. Skipping a stage usually saves little and costs a lot, because a problem in a lower layer shows up as a confusing failure in a higher one.
What should you verify before loading any code?
Before connecting a probe, inspect the board visually and check each power rail for a short to ground with a multimeter. Power the board from a current-limited bench supply and watch the current draw as it comes up. Measure each rail and confirm the voltage, the ripple and the order in which the rails appear.
Then check the reset line and the clock. Confirm that reset is released at the expected moment and that the crystal or oscillator runs at the right frequency, using a low-capacitance probe because probe loading can disturb a crystal circuit. A rail slightly out of tolerance or a clock that never starts will otherwise masquerade as a firmware fault.
How do you connect the debug probe and confirm the chip is alive?
Connect the probe over SWD or JTAG and make sure its reference voltage matches the I/O voltage of the target. The first goal is simply to read the device identification and halt the core, which proves that power, reset and the debug wiring all work. If the probe cannot see the chip, check the header pinout, the reset connection and whether the device is in a low-power state or has debug access disabled.
Once the core halts, read a few memory locations and write then read back a RAM location. These small tests confirm that the part you expect is mounted, that the debug interface is stable and that you can reload code reliably.
First milestone: blink and UART hello
Aim for the smallest visible result: toggle an LED or print a message over UART. This one milestone exercises the startup code, the linker script, the clock setup and a GPIO or serial peripheral, so success tells you a great deal. Keep the program tiny and avoid extra libraries or an operating system until it works.
If the board has no LED, toggle a spare pin and watch it on an oscilloscope; the period also gives a rough check of the real core clock. Garbled UART output is often the first sign that the actual clock differs from the configured one.
Memory map, startup code and clock tree
Confirm that the linker script matches the real memory map of the device: flash and RAM start addresses and sizes, stack placement and any special regions. Check that the vector table sits where the core expects it, and that startup code initialises data, clears uninitialised variables and sets the stack pointer before calling main. Mismatches here cause crashes that look random.
Then configure the clock tree deliberately. Select the clock source, set multipliers and dividers within the datasheet limits, and set flash wait states where the device requires them for the final frequency. Bring the clock up in stages, starting from the internal oscillator, and verify each result by measuring an output pin or timing a loop.
In what order should you bring up peripherals?
Work through peripherals from the simplest to the most dependent, and finish each one before starting the next. Each step should give a pass or fail result you can measure, not just code that compiles.
- GPIO: toggle outputs and read inputs, including buttons and any enable or reset lines the firmware controls.
- UART: establish a reliable debug console and confirm the baud rate with an oscilloscope or logic analyzer.
- I2C and SPI: read a device identification register from each sensor or memory before writing any driver logic.
- ADC: measure known voltages, check the reference and noise, and compare the readings with a multimeter.
- Timers: verify the system tick and any PWM or capture channel against a known period.
- USB and Ethernet: leave these until last, since they depend on correct clocks, a clean physical interface and a working protocol stack.
Which instruments help at each stage?
A multimeter and oscilloscope handle power, clocks and signal quality. A logic analyzer with a protocol decoder for UART, I2C and SPI shows exactly what the firmware sent and how the device answered, which separates a driver bug from a wiring fault. Reach for the oscilloscope when you suspect analogue problems such as slow edges, ringing or a rail dipping during a transmission.
| Stage | Verify | Typical tool |
|---|---|---|
| Power-up | Rails present, in the right order and within tolerance; no shorts; sensible current draw | Multimeter, bench supply, oscilloscope |
| Reset and clock | Reset released cleanly; oscillator running at the expected frequency | Oscilloscope with low-capacitance probe, frequency counter |
| Debug link | Probe reads the chip ID, core halts, RAM read and write works | SWD or JTAG probe and debugger |
| First code | LED or UART message appears; measured clock matches the configuration | Debugger, serial terminal, oscilloscope |
| Startup and memory | Vector table, stack and data initialisation match the linker map | Debugger, map file, disassembly |
| Digital buses | Correct framing, addresses, timing and acknowledge or chip-select behaviour | Logic analyzer with protocol decoder |
| Analogue inputs | Readings match known voltages; noise is acceptable | Multimeter, oscilloscope, stable reference source |
| Fault and recovery | Watchdog resets, fault handlers capture state, update and rollback work | Debugger, serial log, test scripts |
Bootloader, watchdog and fault handling
Plan the update path early. A bootloader that verifies an image and falls back to the previous one if the new image fails to start keeps field units recoverable after a bad update. Decide where the bootloader lives, how images are validated and how the device reports which version it is running.
Enable a watchdog once the application is stable enough to service it deliberately, rather than from a timer interrupt that hides a hung main loop. Install fault handlers early too: on a Cortex-M core, a hard fault handler that captures the stacked registers and the fault status registers shows which instruction faulted and why. Typical causes include a bad pointer, a stack overflow or a peripheral access before its clock is enabled on devices that require it.
Bare-metal or RTOS, and what hardware learns from bring-up
Bare-metal firmware with a simple main loop and interrupts is often enough when the device has a few tasks and modest timing needs. An RTOS such as FreeRTOS or Zephyr starts to pay off with several concurrent activities, blocking communication stacks or timing relationships that are awkward to manage by hand. Adopt it after the hardware is proven, so you are not debugging a scheduler and a board at the same time.
Bring-up is also the first real test of the schematic and layout, so keep a log of every hardware anomaly. Missing pull-ups, swapped pins, wrong strap states and noisy rails found now can often be fixed with a rework wire and recorded for the next board revision. Send those findings back to the hardware designer promptly.
This guide is general educational information. Requirements vary by project, fabricator and applicable standards, so confirm specifics with your manufacturer and test lab.