Firmware

Embedded Firmware Bring-Up: A Checklist for New Hardware

By Hire PCB Designer engineering team · Updated · 8 min read

Bring up new hardware from the bottom up: verify power rails and clocks, connect the debug probe and read the chip ID, reach a blink or UART hello, check the memory map and clock tree, then add peripherals one at a time. Add a bootloader, watchdog and fault handlers, and use a scope or logic analyzer to separate hardware faults from firmware bugs.

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.

  1. GPIO: toggle outputs and read inputs, including buttons and any enable or reset lines the firmware controls.
  2. UART: establish a reliable debug console and confirm the baud rate with an oscilloscope or logic analyzer.
  3. I2C and SPI: read a device identification register from each sensor or memory before writing any driver logic.
  4. ADC: measure known voltages, check the reference and noise, and compare the readings with a multimeter.
  5. Timers: verify the system tick and any PWM or capture channel against a known period.
  6. 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.

Bring-up stage vs. what to verify vs. tool
StageVerifyTypical tool
Power-upRails present, in the right order and within tolerance; no shorts; sensible current drawMultimeter, bench supply, oscilloscope
Reset and clockReset released cleanly; oscillator running at the expected frequencyOscilloscope with low-capacitance probe, frequency counter
Debug linkProbe reads the chip ID, core halts, RAM read and write worksSWD or JTAG probe and debugger
First codeLED or UART message appears; measured clock matches the configurationDebugger, serial terminal, oscilloscope
Startup and memoryVector table, stack and data initialisation match the linker mapDebugger, map file, disassembly
Digital busesCorrect framing, addresses, timing and acknowledge or chip-select behaviourLogic analyzer with protocol decoder
Analogue inputsReadings match known voltages; noise is acceptableMultimeter, oscilloscope, stable reference source
Fault and recoveryWatchdog resets, fault handlers capture state, update and rollback workDebugger, 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.

Frequently asked questions

What should I do if the debugger cannot connect to a new board?

Check the basics in order: target power and reference voltage at the probe, the reset line, and the pin mapping of the SWD or JTAG header. Try a lower debug clock speed and connecting while holding reset. If nothing responds, probe the debug lines with an oscilloscope to see whether the target answers, and inspect the solder joints of the chip.

Why does my firmware work on the development board but not on my custom board?

Differences are usually in hardware the dev board hid: a different crystal or load capacitance, missing pull-ups, different strap or boot pin states, or a power rail that sags. Compare the clock configuration with the actual oscillator, check boot pins, and measure rails under load. Do not assume the firmware is at fault until these have been ruled out.

How much test code should I keep after bring-up?

Keep it. Small self-test routines for each peripheral, a serial command console and a record of expected measurements become the basis of production test and later regression checks. Keep them behind a build option so they do not bloat the release image, and document the expected results alongside the code.

Is it safe to power a new board straight from a USB port or battery?

A current-limited bench supply is safer for a first power-up because you can set a ceiling and watch the draw. A USB port or battery may deliver enough current to damage the board if there is a short. Start with the limit low, raise it gradually, and stop immediately if the current or any component temperature looks wrong.

Can I start firmware work before the hardware arrives?

Partly. You can write and test portable logic on a host computer, use a development board with the same or a similar microcontroller, and prepare drivers from datasheets. Board-specific details such as pin assignments, clock sources and external devices can only be confirmed on the real hardware, so keep them in a small, isolated configuration layer.

Have a hardware project in mind?

Send us your requirements through the contact form. We review the scope and reply with questions or a proposed approach.