Skip to content

Commit 3bb45d6

Browse files
kwagyemanclaude
andcommitted
docs: firmware-development chapter audit; sidebar chevron CSS fix
Page-by-page accuracy pass over the firmware chapter: - index/setup: shorter chapter title; VS Code framed as easiest path; SDK table lists gdbrunner alone (Segger software is not bundled). - building: drop OPENMV1 row and nonexistent .dfu outputs; mpy-cross rebuilds only on MicroPython updates; AE3 HE outputs listed like HP with TOC spelled out; recursive Docker clone; FB_ALLOC_STATS removed. - flashing: normalized bootloader-entry language (IDE mention once on the index, per-device manual entry kept, automation mentions removed); AE3 table refactored to the stm32 style; erase content centralized on erasing.rst with uniform seealso footers; recovery seealso added per family; Nicla Wi-Fi/BT blob commands; :leave only on final transfers; new AE3 Everspin MRAM initialization section (special builds; -a 7 recovery key from flash_recovery_key.py). - debugging: OPENMV2 J-Link row; WSL bridge reduced to the J-Link Remote Server GUI flow (port shown in its window); OpenOCD section and external-flash caveats dropped; gdbrunner section rewritten SDK-first (make debug entry point, board-config debugger args, STLINK backend, MPS2_AN500 QEMU target, SDK CubeProgrammer path). - recovery: IDE-automation note merged into the intro; N6 recovery as BOOT--VCC jumper steps; AE3 section trimmed to recovery-mode / recovery-switch wording with a manual-first procedure and a use-the-IDE fallback note. - custom.css: pad globaltoc links so expander chevrons stop overlapping section titles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 61b9fe9 commit 3bb45d6

12 files changed

Lines changed: 254 additions & 297 deletions

File tree

docs/openmvcam/tutorial/production/firmware/building.rst

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Compiling
88
---------
99

1010
First build **mpy-cross**, the host tool that compiles the frozen ``.py``
11-
modules into bytecode (do this once, and again whenever you switch boards
12-
or update MicroPython)::
11+
modules into bytecode (do this once, and again whenever you update
12+
MicroPython)::
1313

1414
make -j$(nproc) -C lib/micropython/mpy-cross
1515

@@ -44,11 +44,6 @@ and their silicon:
4444
- Port
4545
- Core
4646
* - OpenMV Cam M4
47-
- ``OPENMV1``
48-
- STM32F407
49-
- stm32
50-
- Cortex-M4
51-
* - OpenMV Cam M4 (V2)
5247
- ``OPENMV2``
5348
- STM32F427
5449
- stm32
@@ -134,18 +129,18 @@ Everything for a board lands in ``build/<TARGET>/bin/``. For
134129
Firmware* and by ``dfu-util``
135130
* - ``firmware.elf``
136131
- Firmware with debug symbols -- the file you point the debugger at
137-
* - ``firmware.dfu``
138-
- Firmware as a DFU image
139-
* - ``bootloader.bin`` / ``.elf`` / ``.dfu``
132+
* - ``bootloader.bin`` / ``.elf``
140133
- The bootloader (only on boards with a bootloader enabled)
141-
* - ``openmv.bin`` / ``openmv.dfu``
134+
* - ``openmv.bin``
142135
- Combined bootloader + firmware image
143136
* - ``romfs<n>.img``
144137
- Read-only ROM filesystem image flashed alongside the firmware
145138

146139
The Alif AE3 is dual-core, so it produces ``firmware_M55_HP.elf`` /
147140
``firmware_M55_HP.bin`` (the high-performance core) and a separate
148-
``firmware_M55_HE.*`` (the high-efficiency core) plus a TOC image.
141+
``firmware_M55_HE.elf`` / ``firmware_M55_HE.bin`` (the high-efficiency
142+
core) plus a table-of-contents (TOC) image that tells the boot ROM
143+
where each core's image lives.
149144

150145
Cleaning and rebuilding
151146
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -156,8 +151,8 @@ board's build::
156151
make TARGET=<TARGET> clean
157152

158153
There is no ``distclean``; ``clean`` always needs a ``TARGET``.
159-
``mpy-cross`` is shared across boards -- if you switch targets or update
160-
MicroPython, rebuild it too::
154+
``mpy-cross`` is shared across boards -- if you update MicroPython,
155+
rebuild it too::
161156

162157
make -C lib/micropython/mpy-cross clean
163158
make -j$(nproc) -C lib/micropython/mpy-cross
@@ -172,7 +167,7 @@ Building in Docker (no host toolchain)
172167
If you would rather not install anything on the host (or you are on a
173168
platform without a native build), use the Docker path::
174169

175-
git clone https://github.com/openmv/openmv.git --depth=50
170+
git clone --recursive https://github.com/openmv/openmv.git
176171
cd openmv/docker
177172
make TARGET=<TARGET>
178173

@@ -235,11 +230,6 @@ The variables a firmware developer will use:
235230
- ``STACK_PROTECTOR=1`` adds ``-fstack-protector-all`` -- stack
236231
canaries that trap stack-buffer overflows. Useful when chasing
237232
memory corruption.
238-
* - ``FB_ALLOC_STATS``
239-
- ``0``
240-
- ``FB_ALLOC_STATS=1`` instruments the frame-buffer allocator so you
241-
can see vision-buffer allocation behavior. Useful for
242-
out-of-memory debugging in the image pipeline.
243233
* - ``DEBUGGER``
244234
- ``JLINK``
245235
- Which debugger the ``make debug`` / ``make deploy`` targets use.

docs/openmvcam/tutorial/production/firmware/debugging.rst

Lines changed: 59 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ The hardware: J-Link over SWD
3333

3434
Connect a Segger J-Link to the camera's **SWD** pins (SWDIO, SWCLK, GND,
3535
and target VCC for reference; the camera is powered over USB as usual).
36-
A J-Link EDU / Base / Pro all work. The SWD pads / header location
37-
differs per camera -- check that board's pinout diagram and schematic in
38-
the OpenMV hardware documentation for which pins to wire (some boards
39-
expose SWD on the I/O header, others on dedicated test pads). Install
36+
A J-Link EDU / Base / Pro all work. Where the debug pins surface
37+
differs per camera -- many boards have a dedicated JTAG/SWD connector,
38+
others expose SWD on the I/O header or on test pads -- so check that
39+
board's pinout diagram and schematic in the OpenMV hardware
40+
documentation for which pins to wire. Install
4041
the **J-Link Software and Documentation Pack** from `segger.com
4142
<https://www.segger.com/downloads/jlink/>`__ on the machine the probe is
4243
physically plugged into. Keep it reasonably current -- older J-Link
@@ -52,12 +53,15 @@ right flash loader and memory map:
5253
* - Camera (``TARGET``)
5354
- MCU
5455
- J-Link ``device``
55-
* - ``OPENMV4`` / ``OPENMV4P`` / ``OPENMVPT``
56-
- STM32H743
57-
- ``STM32H743VI``
56+
* - ``OPENMV2``
57+
- STM32F427
58+
- ``STM32F427VG``
5859
* - ``OPENMV3``
5960
- STM32F765
6061
- ``STM32F765VI``
62+
* - ``OPENMV4`` / ``OPENMV4P`` / ``OPENMVPT``
63+
- STM32H743
64+
- ``STM32H743VI``
6165
* - ``OPENMV_N6``
6266
- STM32N657
6367
- ``STM32N657L0``
@@ -71,15 +75,6 @@ right flash loader and memory map:
7175
- STM32H747
7276
- ``STM32H747XI``
7377

74-
.. note::
75-
76-
The STM32N6, MIMXRT1062, and Alif AE3 boot from **external** flash. A
77-
J-Link *flash-load* of those needs the correct external loader,
78-
which is less straightforward; it is usually easier to **attach** to
79-
the already-running firmware (``"request": "attach"`` below) than to
80-
have the debugger program flash. The STM32H7 cameras (H7 / H7 Plus /
81-
Pure Thermal) have internal flash and ``launch`` works directly.
82-
8378
VS Code Cortex-Debug setup
8479
--------------------------
8580

@@ -150,28 +145,25 @@ the ``..._CM55_HP_View.svd`` for the AE3 HP core).
150145
Windows: the WSL ↔ Windows J-Link bridge
151146
----------------------------------------
152147

153-
WSL 2 cannot see the J-Link's USB device directly, so the split is: the
154-
**J-Link GDB server runs on Windows** (where the probe is plugged in)
155-
and **VS Code + gdb run in WSL** and connect to it over TCP.
148+
WSL 2 cannot see the J-Link's USB device directly, so the split is:
149+
**Windows serves the probe** (where it is plugged in) and **VS Code +
150+
gdb run in WSL** and reach it over TCP.
156151

157152
#. **On Windows**, install the Segger J-Link pack and plug the J-Link
158153
into a Windows USB port.
159154

160-
#. **On Windows**, start the J-Link GDB server, using your board's
161-
device name from the table above::
162-
163-
"C:\Program Files\SEGGER\JLink\JLinkGDBServerCL.exe" -select USB -device STM32H743VI -endian little -if SWD -speed 4000 -port 2331
164-
165-
Allow it through the Windows firewall when prompted (inbound TCP
166-
2331). The server window **displays the IP address** it is listening
167-
on -- note it.
155+
#. **On Windows**, start the **J-Link Remote Server** (it ships with
156+
the J-Link pack): launch it with the J-Link attached and click
157+
**OK**. Allow it through the Windows firewall when prompted. The
158+
window shows the **IP address** it is serving the probe on -- note
159+
it.
168160

169161
#. **In WSL**, build ``DEBUG=1`` and make sure ``arm-none-eabi-gdb`` is
170162
reachable (set ``armToolchainPath`` as above).
171163

172-
#. **In WSL VS Code**, use a ``launch.json`` that connects to the
173-
already-running server instead of starting one. Either keep
174-
``servertype: "jlink"`` and add ``serverpath`` + ``ipAddress``::
164+
#. **In WSL VS Code**, keep ``servertype: "jlink"`` -- the GDB server
165+
runs in WSL and reaches the probe through the Remote Server -- and
166+
add ``serverpath`` + ``ipAddress``::
175167

176168
{
177169
"name": "OpenMV J-Link (Windows host)",
@@ -188,39 +180,8 @@ and **VS Code + gdb run in WSL** and connect to it over TCP.
188180
"armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin"
189181
}
190182

191-
Set ``ipAddress`` to whatever the Windows J-Link server window
192-
shows. Or use the cleaner ``external`` form, which starts no server
193-
and just points gdb at the bridge::
194-
195-
{
196-
"name": "OpenMV J-Link (external)",
197-
"type": "cortex-debug",
198-
"request": "attach",
199-
"cwd": "${workspaceFolder}",
200-
"executable": "${workspaceFolder}/build/OPENMV4/bin/firmware.elf",
201-
"servertype": "external",
202-
"gdbTarget": "host.docker.internal:2331",
203-
"armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin",
204-
"gdbPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin/arm-none-eabi-gdb"
205-
}
206-
207-
Point the config at the Windows host (see below) on the port the
208-
server uses (``2331`` by default). Windows Firewall must allow
209-
inbound TCP on that port for ``JLinkGDBServerCL.exe``.
210-
211-
Reaching the Windows host from WSL depends on the WSL networking mode:
212-
213-
* **NAT mode (the default).** The Windows host is the WSL virtual
214-
network's gateway. Get its address in WSL with ``ip route show
215-
default`` and use that as the ``ipAddress`` / ``gdbTarget`` host; the
216-
LAN IP the J-Link server window prints also works if both ends share
217-
a network. ``host.docker.internal`` resolves to the Windows host
218-
**only if Docker Desktop's WSL integration is installed** -- do not
219-
rely on it otherwise.
220-
* **Mirrored mode** (``networkingMode=mirrored`` in
221-
``%UserProfile%\.wslconfig`` on recent Windows builds). WSL shares
222-
the Windows network stack, so the J-Link server is reachable as plain
223-
``localhost`` and no IP discovery is needed.
183+
Set ``ipAddress`` to the address the Remote Server window shows.
184+
That is the whole bridge.
224185

225186
.. tip::
226187

@@ -246,28 +207,38 @@ Reaching the Windows host from WSL depends on the WSL networking mode:
246207

247208
Use ``"request": "attach"`` to debug the firmware *as it is already
248209
running* without resetting or reflashing it -- ideal for catching a
249-
hang in the field, and the reliable choice for the external-flash
250-
boards (N6 / RT1062 / AE3). Use ``"request": "launch"`` to reset,
251-
flash the ELF, and start fresh at ``runToEntryPoint``.
210+
hang in the field. Use ``"request": "launch"`` to reset, flash the
211+
ELF, and start fresh at ``runToEntryPoint``.
252212

253213
Command-line debugging with gdbrunner
254214
-------------------------------------
255215

256216
Setting up a GDB session against an embedded target by hand is a
257-
five-step dance: start the J-Link / ST-Link / OpenOCD GDB server in
217+
five-step dance: start the J-Link / ST-Link GDB server in
258218
one window with the right device, port, and interface flags; wait
259219
for it to print *Waiting for GDB connection*; run
260220
``arm-none-eabi-gdb`` in a second window; type ``target remote
261221
localhost:<port>``; point gdb at the ELF. When the gdb session ends,
262222
remember to kill the server window. `gdbrunner
263223
<https://github.com/openmv/gdbrunner>`__ is a small CLI that collapses
264-
all of that into one foreground command::
224+
all of that into one foreground command. It ships in the OpenMV SDK's
225+
Python environment, so there is nothing to install; the usual entry
226+
point is the firmware repository's ``make debug`` target::
227+
228+
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
229+
230+
This runs gdbrunner with the debugger arguments from the board's
231+
configuration -- the J-Link device name and, where needed, the
232+
ST-Link external flash loader -- with the SDK's ``arm-none-eabi-gdb``
233+
already on ``PATH``. The default backend is J-Link; ``make
234+
DEBUGGER=STLINK debug`` works with an ST-Link probe instead.
265235

266-
pip install gdbrunner
236+
gdbrunner can also be invoked directly (outside the SDK, ``pip
237+
install gdbrunner``)::
267238

268239
gdbrunner jlink --device STM32H743VI build/OPENMV4/bin/firmware.elf
269240
gdbrunner stlink build/OPENMV4/bin/firmware.elf
270-
gdbrunner qemu --machine mps2-an500 build/OPENMV4/bin/firmware.elf
241+
gdbrunner qemu --machine mps2-an500 build/MPS2_AN500/bin/firmware.elf
271242

272243
The first positional argument picks the server backend (``jlink``,
273244
``stlink``, ``qemu``); the rest are forwarded to that backend, with
@@ -276,15 +247,6 @@ the full per-backend flag list; each backend's argument table is
276247
JSON-driven (``src/gdbrunner/backends.json``), so adding a new server
277248
is a config edit rather than code.
278249

279-
The firmware repository's ``make debug`` target is a thin wrapper
280-
around gdbrunner that fills in the current ``TARGET``'s J-Link
281-
arguments automatically::
282-
283-
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
284-
285-
This is the fastest way to drop into command-line gdb on whichever
286-
board ``TARGET`` selects.
287-
288250
What gdbrunner does for command-line work:
289251

290252
* **One process, clean lifecycle.** Server starts, gdb attaches when
@@ -293,14 +255,17 @@ What gdbrunner does for command-line work:
293255
to manage.
294256
* **STM32CubeProgrammer auto-discovery.** The ``stlink`` backend
295257
searches the usual install locations (``~/STM32CubeProgrammer/``,
296-
``/opt/st/``, the STM32CubeIDE plugin tree) for
297-
``ST-LINK_gdbserver``, so the long ``--cube-prog`` path doesn't
298-
have to be typed every time.
258+
``/opt/st/``, the STM32CubeIDE plugin tree) for the
259+
STM32CubeProgrammer tools, so the long ``--cube-prog`` path doesn't
260+
have to be typed every time. The SDK bundles its own copy at
261+
``~/openmv-sdk-<version>/stcubeprog/bin`` -- point ``--cube-prog``
262+
there if no system install exists.
299263
* **Per-project gdbinit honoured.** A ``.gdbinit`` in the current
300264
directory is loaded with ``-ix`` -- overriding the user-wide
301265
``~/.gdbinit`` -- so per-project gdb scripting (pretty-printers,
302266
board-specific macros, breakpoint sets) drops in by being present
303-
in the working directory.
267+
in the working directory. ``make debug`` runs from the repository
268+
root, so a ``.gdbinit`` there applies.
304269
* **Dry run.** ``--dryrun`` prints the server command without
305270
running it, useful for adapting the invocation to a wrapper
306271
script, copying it into an IDE launcher config, or just checking
@@ -309,10 +274,13 @@ What gdbrunner does for command-line work:
309274
stdout / stderr visible. The default suppresses it (so gdb's UI
310275
stays clean); flip the flag when the server itself is what's
311276
misbehaving.
312-
* **QEMU backend.** ``qemu-system-arm`` running ``mps2-an500`` /
313-
``mps3-an547`` debugs the same ELF without a board plugged in --
314-
code paths that don't touch cam-specific peripherals are
315-
steppable on a flight.
277+
* **QEMU backend.** ``qemu-system-arm`` debugs a firmware build with
278+
no board plugged in. The ``MPS2_AN500`` target selects this backend
279+
in its board configuration, so ``make TARGET=MPS2_AN500 DEBUG=1
280+
debug`` builds for QEMU's ``mps2-an500`` machine and steps the
281+
platform-independent code -- everything that doesn't touch
282+
cam-specific peripherals -- on a flight. (``qemu-system-arm`` is a
283+
host install, not part of the SDK.)
316284

317285
For source-level stepping with breakpoint gutters and a peripheral
318286
register view, the VS Code Cortex-Debug setup above is the better
@@ -361,33 +329,10 @@ Once a session is running (the processor halted at ``main``):
361329
This is the right tool when a breakpoint would change the timing you
362330
are trying to observe.
363331

364-
* **Disconnecting** -- *Stop* on a ``launch`` / ``jlink`` session halts
365-
the target; *Disconnect* on an ``attach`` / ``external`` session
366-
leaves the camera running. Power-cycle the camera to return it to
367-
normal operation afterwards.
368-
369-
OpenOCD alternative (ST-Link)
370-
-----------------------------
371-
372-
If you have an ST-Link instead of a J-Link, the STM32 cameras can be
373-
debugged with OpenOCD. The firmware repository ships OpenOCD configs
374-
under ``lib/micropython/ports/stm32/boards/``:
375-
376-
.. list-table::
377-
:header-rows: 1
378-
:widths: 38 62
379-
380-
* - OpenOCD config
381-
- Cameras
382-
* - ``openocd_stm32h7_dual_bank.cfg``
383-
- STM32H7 -- OpenMV Cam H7, H7 Plus, Pure Thermal; Arduino
384-
Portenta H7, Giga, Nicla Vision
385-
* - ``openocd_stm32f7.cfg``
386-
- STM32F7 -- OpenMV Cam M7
387-
388-
Use ``servertype: "openocd"`` with ``configFiles`` pointing at the
389-
appropriate ``.cfg``. There are no in-repo OpenOCD configs for the N6,
390-
RT1062, or AE3 -- use J-Link for those.
332+
* **Disconnecting** -- *Stop* on a ``launch`` session halts the
333+
target; *Disconnect* on an ``attach`` session leaves the camera
334+
running. Power-cycle the camera to return it to normal operation
335+
afterwards.
391336

392337
Debugging pitfalls
393338
------------------
@@ -400,9 +345,6 @@ Debugging pitfalls
400345
``device`` name; use the exact string from the table.
401346
* **Breakpoints silently not hit** -- too many hardware breakpoints on
402347
flash-resident code; reduce them.
403-
* **N6 / RT1062 / AE3 won't flash from the debugger** -- they boot
404-
from external flash; use ``"request": "attach"`` against running
405-
firmware instead of ``launch``.
406348
* **Source paths don't match (Docker-built ELF)** -- build with the
407349
Docker ``build-firmware-dev`` target (same absolute path inside and
408350
outside the container) or set gdb ``set substitute-path``.

0 commit comments

Comments
 (0)