Skip to content

Target Identification

Unknown board on your bench. No markings, faded markings, or you just want to confirm what the debug probe actually sees. Here is the systematic approach using mcjtag.

Before connecting anything:

  • Read chip markings — top marking, date codes, package type. A QFP-48 with “STM32F103C8T6” on it saves you all the steps below.
  • Count debug header pins — 10-pin 1.27 mm pitch is ARM SWD (Cortex Debug Connector), 20-pin 2.54 mm is legacy ARM JTAG, 14-pin is STDC14 (ST’s compact debug + trace connector).
  • Note board markings — silk-screened part numbers, FCC IDs, and URLs can all lead to datasheets.

Even with legible markings, always verify electronically. Remarked chips (counterfeit or mislabeled) exist in the wild, especially on cheap dev boards from marketplace sellers.

SWD needs only two signal wires (SWDIO + SWCLK), which means fewer things to get wrong compared to JTAG’s four. If the target is any ARM Cortex-M, SWD is the fastest path to a response.

Use target/cortex_m.cfg as a generic config. It works for any Cortex-M without chip-specific knowledge — you lose flash programming support, but all register and memory reads work fine.

source [find interface/cmsis-dap.cfg]
transport select swd
adapter speed 1000
source [find target/cortex_m.cfg]

Start OpenOCD with this config:

start_openocd(config="/path/to/generic-swd.cfg")

If SWD fails with “no targets found” or similar, the target might not be ARM, might not support SWD, or the wiring is wrong. Try JTAG next (swap to transport select jtag and wire TDI/TDO/TMS/TCK). See Hardware Setup for wiring details.

probe_diagnostics()

The diagnostics tool reads the CPUID register at 0xE000ED00. This is an ARM-defined register present on every Cortex-M core. The diagnostics output includes the raw CPUID value and a decoded summary.

BitsFieldDescription
31:24Implementer0x41 = ARM
23:20VariantMajor revision
19:16Architecture0xC = ARMv6-M, 0xF = ARMv7-M/v8-M
15:4Part NumberCore type identifier
3:0RevisionMinor revision
Part NumberCore
0xC20Cortex-M0
0xC21Cortex-M1
0xC23Cortex-M3
0xC24Cortex-M4
0xC27Cortex-M7
0xC60Cortex-M0+
0xD20Cortex-M23
0xD21Cortex-M33

Example: CPUID = 0x410FC241 breaks down as:

  • Implementer = 0x41 (ARM)
  • Variant = 0x0
  • Architecture = 0xF (ARMv7-M)
  • Part Number = 0xC24 (Cortex-M4)
  • Revision = 0x1

This tells you the core type but not the chip vendor or specific part number. For that, continue to the next steps.

jtag_scan()

If connected via JTAG, this enumerates all TAPs on the scan chain. Each TAP reports an IDCODE. The IDCODE contains a manufacturer ID in bits 11:1, defined by the JEDEC JEP106 standard.

Manufacturer ID (bits 11:1)Vendor
0x020STMicroelectronics
0x015NXP (Freescale)
0x244Nordic Semiconductor
0x108Raspberry Pi
0x00BGigaDevice
0x049Microchip (Atmel)
0x097Texas Instruments
0x23BARM (debug components)

SWD reports a DPIDR instead of an IDCODE. The probe_diagnostics() output includes this value. The DPIDR has a different bit layout, but the lower bits still contain a manufacturer code.

Many Cortex-M devices share the same DPIDR — 0x0BC11477 is ARM’s standard CoreSight SWD-DP identifier. The DPIDR identifies the debug port, not the chip. You need CPUID plus vendor-specific registers to pinpoint the exact part.

read_memory("0x08000000", 8, 32)

The first two words of flash reveal critical information about the target:

  • Word 0 (Initial SP) — Points to the top of SRAM. If it reads 0x20005000, the chip has 20 KB of SRAM (0x20000000 to 0x20004FFF). This immediately narrows down the chip family.
  • Word 1 (Reset Vector) — The entry point address. Confirms code is present and where execution begins.

If 0x08000000 returns a bus fault, the chip maps flash elsewhere. Try these:

Flash BaseChip Family
0x08000000STM32, GD32
0x00000000NXP LPC, Nordic nRF, RP2040
0x00400000Microchip SAM
0x10000000RP2040 (XIP flash)

A valid vector table has:

  • Word 0 pointing into SRAM (0x2xxxxxxx)
  • Word 1 pointing into flash with bit 0 set (Thumb mode — all Cortex-M code runs in Thumb state, so the reset vector always has the LSB set)

If both words read 0xFFFFFFFF, the flash is blank or erased. If word 0 does not point into SRAM, this may not be a vector table at all — try a different flash base address.

See Memory Layout — The Vector Table for the full vector table structure and what each entry means.

Once you know the vendor (from IDCODE manufacturer bits or an educated guess based on the board), check vendor-specific identification registers to get the exact part number.

read_memory("0xE0042000", 1, 32)

This reads DBGMCU_IDCODE, the debug MCU identification register:

BitsFieldDescription
31:16REV_IDSilicon revision identifier
11:0DEV_IDDevice identifier
DEV_IDChip
0x410STM32F103 (medium density)
0x412STM32F103 (low density)
0x414STM32F103 (high density)
0x411STM32F2xx
0x413STM32F405/407
0x419STM32F42x/43x
0x449STM32F74x/75x
0x450STM32H743/753

Most STM32 chips expose their flash size at a fixed address:

read_memory("0x1FFFF7E0", 1, 16)

Returns flash size in KB as a 16-bit value. For example, 0x0040 = 64 KB, 0x0080 = 128 KB, 0x0100 = 256 KB.

Note: Some STM32 families (F4, H7) move this register to 0x1FFF7A22 or 0x1FF1E880. Check the reference manual for your specific family.

read_memory("0x10000100", 4, 32)

Reads FICR (Factory Information Configuration Register). The four words starting at this address contain:

  • Word 0 (0x10000100): Part number (e.g., 0x00052832 = nRF52832)
  • Word 1 (0x10000104): Variant (e.g., 0x41414230 = “AAB0”)
  • Word 2 (0x10000108): Package type
  • Word 3 (0x1000010C): RAM size in KB

GD32 is register-compatible with STM32 at the debug interface level. The DBGMCU_IDCODE lives at the same address (0xE0042000), but DEV_ID values are different. Cross-reference with GigaDevice datasheets.

Common GD32 identifiers:

DEV_IDChip
0x410GD32F103 (yes, same as STM32F103 — check the JTAG IDCODE manufacturer bits to distinguish)
0x414GD32F303
read_memory("0x41002018", 1, 32)

Reads the Device Identification register (DSU DID) on SAM D/E/C series. Contains processor, family, series, die, and revision fields.

Once you know the exact chip, load an SVD file for complete peripheral decoding:

svd_inspect(svd_path="/path/to/STM32F103xx.svd")

With SVD loaded, every peripheral register on the chip becomes inspectable by name with decoded bitfields. No more manual address lookups.

SVD files can be obtained from:

  • cmsis-svd-data — Community-maintained repository on GitHub with SVD files for most vendors. See the SVD Register Decoding guide for download instructions.
  • Vendor IDE installations — STM32CubeIDE, Keil, IAR all bundle SVD files.
  • Keil pack files.pack files are ZIP archives containing .svd files. Rename to .zip and extract.

Summary of the identification workflow:

Data PointTool CallWhat It Tells You
JTAG IDCODEjtag_scan()Manufacturer (JEDEC code)
DPIDRprobe_diagnostics()Debug port type
CPUID (0xE000ED00)probe_diagnostics()ARM core type (M0, M3, M4, M7…)
Vector tableread_memory("0x08000000", 8, 32)SRAM size, code base, firmware presence
DBGMCU_IDCODEread_memory("0xE0042000", 1, 32)Exact STM32/GD32 part number
Flash sizeread_memory("0x1FFFF7E0", 1, 16)Flash capacity in KB
FICRread_memory("0x10000100", 4, 32)nRF part number and variant
SVD decodesvd_inspect()Full peripheral register map

Some scenarios where this workflow hits a wall:

  • Readout protection enabled — The chip may have RDP/APPROTECT set, which blocks all debug access. probe_diagnostics() will show a connection but memory reads return faults. This is by design — the firmware author locked the chip.
  • Non-ARM targets — Everything above assumes ARM Cortex-M. RISC-V, 8051, and other architectures have completely different identification mechanisms. jtag_scan() still works for JTAG IDCODE, but CPUID and the vendor-specific registers do not apply.
  • Custom or obscure silicon — Some chips have undocumented or nonstandard debug interfaces. If jtag_scan() returns an IDCODE with a manufacturer ID not in the JEDEC tables, you are in datasheet-hunting territory.
  • Damaged or dead target — If nothing responds at all, verify wiring, power, and adapter speed before concluding the chip is dead. Drop adapter speed to 100 kHz and try again. See Troubleshooting for the full diagnostic checklist.