System designTroubleshooting

JTAG breakpoints not hit in embedded camera firmware: fix the build

JTAG firmware breakpoints run past main() but are not hit. The cause is the build: toolchain and SDK pins. Use the rebuild sequence that makes them hit again.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01Why do JTAG breakpoints run past main() but never fire?
  2. 02Which build settings decide whether a breakpoint can fire?
  3. 03Why does the vendor's simple example break correctly and mine does not?
  4. 04What is the fastest way to prove the cause?
  5. 05Where should breakpoints go so the RTOS does not hide them?
  6. 06How does an SDK version mismatch break debugging?
  7. 07Could the debug probe or its driver be the cause instead?
  8. 08Which cause is most likely, in order?
  9. 09When should you not be writing camera firmware at all?
  10. 10Questions

You set a breakpoint after main(), press run, and the firmware streams video straight past it. Connection works, download works, and the target halts at main() — so the probe link is fine. This failure is common when you develop embedded camera hardware: a USB camera board you design in-house, or a camera product built on board-level cameras. The cause is almost never the debugger. It is the build: the project you debug was produced with a legacy toolchain and SDK folders that do not match the toolchain and SDK your debug setup was validated against. This page gives the symptom signature, the three settings that decide whether a breakpoint can fire, a four-test sequence that isolates the cause in about 30 minutes, and the point at which writing camera firmware is the wrong project entirely.

Why do JTAG breakpoints run past main() but never fire?

Because the image executing on the target is not the image your debugger believes it downloaded: startup code from an old build still matches the symbols, but the code your breakpoint points at was never generated from the sources you are looking at.

The signature is consistent. The debugger connects and downloads, the target halts at main(), run-to-line inside main() works, and a plain breakpoint never does. Single-stepping tolerates the mismatch because the debugger re-syncs the CPU on every step. A breakpoint waits for the CPU to arrive at one address on its own, and the executing code never does, because it was laid out differently.

Two build variables produce that layout difference: which compiler toolchain built the image, and which SDK release supplied the headers and libraries. Low-level startup from the old build still runs and still matches your symbols; application code regenerated by a different toolchain does not.

Which build settings decide whether a breakpoint can fire?

Three project settings decide it — the compiler toolchain the project invokes, the SDK version every include and library path points at, and the debug information the build emits — and any one of them wrong means the debugger downloads code its symbols do not describe.

Where each one lives:

  • The toolchain is a selector in the project's build settings. The vendor demo and your camera project name different entries there, and that difference is the case to close first.
  • The SDK pin is the set of include and library paths. SDKs often ship example projects with one folder set per release, and the build configuration selects one of them.
  • Debug information is a build flag; keep it at the maximum level for debug builds.

Optimization is the amplifier. At full optimization the compiler reorders and removes lines, so even a matching build skips breakpoints on statements that no longer exist. Build debug configurations at low optimization and save the fast build for release.

Why does the vendor's simple example break correctly and mine does not?

The working demo and your failing project differ in exactly the variables that decide breakpoint behavior: the demo builds with the IDE's current GCC toolchain against the installed SDK, while your project calls a legacy third-party GCC distribution and links older SDK folders, on identical silicon and the same probe.

That comparison is the most useful fact in this failure. In reported cases of this exact symptom, a simple interface-to-USB streaming demo built and broke correctly on the same board and probe, while the camera project on the same machine did not. The demo's project file selected the IDE's own toolchain; the camera project selected a toolchain distribution from an earlier SDK generation and carried folder sets for two older SDK releases.

Nothing about the silicon or the probe changed between the two tests — only the build. Treat any project that behaves differently from the vendor demo on identical hardware as a build problem until the tests say otherwise.

What is the fastest way to prove the cause?

Run four tests in order and let their outcomes decide: rebuild the vendor demo unmodified, break on its worker-thread entry, switch your project to the demo's toolchain and installed SDK, then rebuild clean — the whole sequence fits in about 30 minutes on a mid-size firmware project, with each clean rebuild taking 2 to 5 minutes.

  1. Build the vendor's streaming demo exactly as shipped, with the toolchain its own project file selects. A clean build here also proves your SDK installation is complete.
  2. Download it, run, and set one breakpoint in the demo's worker-thread entry function. If it fires, the probe, probe driver, GDB server, IDE and SDK are all good — stop suspecting them.
  3. Switch your camera project to the demo's toolchain, repoint every include and library path at the installed SDK tree, delete every intermediate object, and rebuild clean. An incremental rebuild preserves objects from the old toolchain and preserves the failure with them.
  4. Download, run, and set breakpoints in your thread entry and main loop. If they fire now, the toolchain and SDK pins were the cause. If they still do not, the code may genuinely never reach them — fix placement next.

Keep the demo project as your reference build and rerun its breakpoint test whenever you update the IDE or the SDK. A clean rebuild of camera firmware takes only a few minutes, so this sequence costs little to run after every tool update.

Where should breakpoints go so the RTOS does not hide them?

Set your first breakpoints where execution reliably passes once the scheduler starts: at the entry function of each application worker thread and on a statement inside its main loop. Breakpoints only in main() prove startup ran and say nothing about the threads doing the work.

Camera firmware does no inspection work in main(). The SDK initializes the interface, then starts threads, and the threads run the sensor pipeline and the USB or GigE traffic. The SDK also calls an application-define callback before the RTOS starts, so a breakpoint there proves the image launched and nothing more.

If the loop-body breakpoint fires but an earlier one does not, the earlier code is not executed — a logic finding, not a debugger fault. Read it as a branch your code never takes, and check the condition that guards it.

How does an SDK version mismatch break debugging?

Camera SDKs ship example projects pinned to the release they were tested with; building against folders from an older release while your tools and documentation are newer mixes libraries and headers into one image that links, runs, and still does not match your debugger's picture of the code.

The fix is to make one version true everywhere: select the folder set that matches the installed SDK, or install the release the project expects — never mix. The same discipline repeats across embedded stacks. A smart camera needs its application SDK matched to the device firmware before deployed code behaves, which is the subject of deploying custom code to a smart camera: match SDK and firmware versions, and containerized host builds drift the same way until you pin one library version everywhere, as in fixing a Docker build SDK version mismatch.

Could the debug probe or its driver be the cause instead?

Rarely — probe and driver faults announce themselves as failed connection, download or halting, while your case halts at main() and streams video past a dead breakpoint, which means the probe link is healthy and the build is the remaining suspect.

Keep the probe software current anyway: the driver and the GDB server come from the probe maker's own download page, and old stacks fail in unrelated ways. In the published reports both were already at the latest release, and the breakpoints stayed dead until the build changed — the update alone did not fix them.

One probe-side exception exists. On some microcontroller families, enabling cache breaks stepping and breakpoints outright; the signature is debugging that dies right after the code turns caches on. If you enabled cache recently, retest with it disabled before touching anything else.

None of this applies on the host side. A vision controller runs your capture application under a full operating system, so its failures are library and driver mismatches, not image mismatches — see fixing a DLL that will not load on a vision controller.

Which cause is most likely, in order?

Rank the causes by what you observe, not by instinct: a stale toolchain pin first, an SDK folder pin second, breakpoint placement third, probe software fourth, and cache-related debug failures last — one test in the table separates each neighboring pair.

Order Cause Signature Test that separates it Fix
1 Legacy toolchain pin Demo breaks, your project does not, same board and probe Rebuild the demo unmodified; break in its thread entry Switch your project to the demo's toolchain; rebuild clean
2 SDK folder pin Include and library paths point at an older release than installed Compare every path against the installed SDK tree Repoint all paths; rebuild clean
3 Breakpoint placement Nothing fires, not even inside a loop that runs every frame Set one breakpoint on the frame-complete statement Move breakpoints to thread entry and loop body
4 Probe software Connection, download or halting is itself unreliable Update driver and GDB server from the maker's download page Retest; it will not fix a build mismatch
5 Cache enabled Debug dies immediately after the firmware turns caches on Retest with caches disabled Keep caches off in debug builds

Prevention is a record, not a ritual. Save with every firmware build the toolchain name and version, the SDK release every path points at, the build flags, and the probe driver and GDB server versions that validated it, and keep the linker map file with the released binary. The embedded firmware build-evidence and supply-chain mapping guide shows how to structure that record so a repeat of this symptom is minutes of comparison, not days. Pin hardware revisions the same way when you reorder boards, as in checking board-level camera hardware revision compatibility.

When should you not be writing camera firmware at all?

If your product needs no custom sensor timing, no custom interface and no unusual power or size budget, use a finished camera module with maintained firmware — then this entire failure class disappears, because you debug your inspection application and never rebuild the camera's code.

You write camera firmware when the camera is your product. For an inspection task, a finished module removes this failure class: modules that speak USB3 Vision, GigE Vision or UVC run maintained firmware and connect through standard drivers. Two places to start are running a UVC USB camera on an embedded board without rebuilding drivers and board level camera selection for embedded vision machines.

Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
$690.00View part
Board-level camerasHikrobot MV-CB016-10GM-M-W 1.6 MP GigE board-level camera, IMX296, Mono, M12 lens mount, WTB GigE portMV-CB016-10GM-M-W
Sensor format
1/2.9"
Lens mount
M12
$559.00View part
Board-level camerasMiniature industrial camera, 1.3 MP, GigE, Colour/Mono (LS-BCM-0061)LS-BCM-0061
Sensor format
1/2.7"
Lens mount
M12
$209.00View part

Each of these modules ships firmware you never rebuild, so your debugging happens in the inspection application on the host instead. The decisions, case by case: vendor demo breaks and your project does not — rebuild on the demo's toolchain against the installed SDK, one clean rebuild closes it. Nothing breaks anywhere, including the demo — update the probe driver and GDB server, then check placement. Debug dies when caches switch on — keep caches off in debug builds. Inspection task with no custom sensor needs — use the module and never rebuild camera firmware.

Pick the host and the module together: the camera finder filters modules by sensor, interface and mount, the camera configurator walks the pairing, and a fanless controller like the one below runs the application. More build-side and integration fixes are collected on the systems topic page.

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$1,679.00View partBuild a system with it

If the four tests leave your breakpoints dead, describe the demo-versus-project comparison to our applications team through the contact page — free expert support comes with every order.

Questions

Do hardware and software breakpoints fail differently when the build is stale?

Both fail, and for the same root reason. A hardware breakpoint fires only when execution reaches the exact address held in a comparator inside the core; a software breakpoint overwrites the instruction at an address taken from the debugger's image. If the running firmware was laid out from different sources, neither address is ever executed. Making the build match the executing code fixes both types at once.

How many hardware breakpoints does an embedded camera controller have?

Common small cores provide between two and eight instruction comparators, set by the core family rather than by you. Camera firmware runs several threads on top of that budget, so spend the comparators where execution is guaranteed: the application thread entry and a statement in each main loop. Use run-to-line and single-stepping everywhere else.

Can I diagnose this failure without a debug probe, using logs only?

Partly. Log output or a USB serial print from the first lines of startup shows how far execution gets, and a GPIO pulse at the thread entry shows the scheduler started. Logs cannot show where the CPU sits when nothing responds — that needs the probe. Use logs to confirm the symptom and the rebuild sequence to remove the cause.

Does the same fix apply to debugging applications on a vision controller?

No. A vision controller runs a full operating system on a PC-class processor, so there is no downloaded image to mismatch. Failures there come from run-time libraries, driver versions and the OS the SDK was built for. Match the SDK to the controller's OS and architecture first, then debug your application code with normal host tools.

What should I record with every firmware build so this stays fixed?

Record four things: the toolchain name and version, the SDK release every include and library path points at, the build configuration and flags, and the probe driver and GDB server versions used to validate the build. Keep the linker map file with each released binary. With those records, a repeat of the symptom is a minutes-long comparison instead of days of guessing.