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.
Physical Inspection
Section titled “Physical Inspection”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.
Step 1: Try SWD First
Section titled “Step 1: Try SWD First”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 swdadapter speed 1000source [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.
Step 2: Read CPUID with probe_diagnostics
Section titled “Step 2: Read CPUID with probe_diagnostics”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.
CPUID register format
Section titled “CPUID register format”| Bits | Field | Description |
|---|---|---|
| 31:24 | Implementer | 0x41 = ARM |
| 23:20 | Variant | Major revision |
| 19:16 | Architecture | 0xC = ARMv6-M, 0xF = ARMv7-M/v8-M |
| 15:4 | Part Number | Core type identifier |
| 3:0 | Revision | Minor revision |
Part number lookup
Section titled “Part number lookup”| Part Number | Core |
|---|---|
0xC20 | Cortex-M0 |
0xC21 | Cortex-M1 |
0xC23 | Cortex-M3 |
0xC24 | Cortex-M4 |
0xC27 | Cortex-M7 |
0xC60 | Cortex-M0+ |
0xD20 | Cortex-M23 |
0xD21 | Cortex-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.
Step 3: Read JTAG IDCODE with jtag_scan
Section titled “Step 3: Read JTAG IDCODE with jtag_scan”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 lookup
Section titled “Manufacturer ID lookup”| Manufacturer ID (bits 11:1) | Vendor |
|---|---|
0x020 | STMicroelectronics |
0x015 | NXP (Freescale) |
0x244 | Nordic Semiconductor |
0x108 | Raspberry Pi |
0x00B | GigaDevice |
0x049 | Microchip (Atmel) |
0x097 | Texas Instruments |
0x23B | ARM (debug components) |
SWD note
Section titled “SWD note”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.
Step 4: Vector Table Analysis
Section titled “Step 4: Vector Table Analysis”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 (0x20000000to0x20004FFF). This immediately narrows down the chip family. - Word 1 (Reset Vector) — The entry point address. Confirms code is present and where execution begins.
Alternate flash base addresses
Section titled “Alternate flash base addresses”If 0x08000000 returns a bus fault, the chip maps flash elsewhere. Try these:
| Flash Base | Chip Family |
|---|---|
0x08000000 | STM32, GD32 |
0x00000000 | NXP LPC, Nordic nRF, RP2040 |
0x00400000 | Microchip SAM |
0x10000000 | RP2040 (XIP flash) |
Validating the vector table
Section titled “Validating the vector table”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.
Step 5: Vendor-Specific ID Registers
Section titled “Step 5: Vendor-Specific ID Registers”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.
STMicroelectronics (STM32)
Section titled “STMicroelectronics (STM32)”read_memory("0xE0042000", 1, 32)This reads DBGMCU_IDCODE, the debug MCU identification register:
| Bits | Field | Description |
|---|---|---|
| 31:16 | REV_ID | Silicon revision identifier |
| 11:0 | DEV_ID | Device identifier |
Common DEV_ID values
Section titled “Common DEV_ID values”| DEV_ID | Chip |
|---|---|
0x410 | STM32F103 (medium density) |
0x412 | STM32F103 (low density) |
0x414 | STM32F103 (high density) |
0x411 | STM32F2xx |
0x413 | STM32F405/407 |
0x419 | STM32F42x/43x |
0x449 | STM32F74x/75x |
0x450 | STM32H743/753 |
Flash size register
Section titled “Flash size register”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.
Nordic Semiconductor (nRF)
Section titled “Nordic Semiconductor (nRF)”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
GigaDevice (GD32)
Section titled “GigaDevice (GD32)”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_ID | Chip |
|---|---|
0x410 | GD32F103 (yes, same as STM32F103 — check the JTAG IDCODE manufacturer bits to distinguish) |
0x414 | GD32F303 |
Microchip SAM (Atmel)
Section titled “Microchip SAM (Atmel)”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.
Step 6: Load SVD for Full Decode
Section titled “Step 6: Load SVD for Full Decode”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 —
.packfiles are ZIP archives containing.svdfiles. Rename to.zipand extract.
Quick Reference
Section titled “Quick Reference”Summary of the identification workflow:
| Data Point | Tool Call | What It Tells You |
|---|---|---|
| JTAG IDCODE | jtag_scan() | Manufacturer (JEDEC code) |
| DPIDR | probe_diagnostics() | Debug port type |
CPUID (0xE000ED00) | probe_diagnostics() | ARM core type (M0, M3, M4, M7…) |
| Vector table | read_memory("0x08000000", 8, 32) | SRAM size, code base, firmware presence |
| DBGMCU_IDCODE | read_memory("0xE0042000", 1, 32) | Exact STM32/GD32 part number |
| Flash size | read_memory("0x1FFFF7E0", 1, 16) | Flash capacity in KB |
| FICR | read_memory("0x10000100", 4, 32) | nRF part number and variant |
| SVD decode | svd_inspect() | Full peripheral register map |
When Identification Fails
Section titled “When Identification Fails”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
100kHz and try again. See Troubleshooting for the full diagnostic checklist.