NUCLEO-U575 upload+debug support

Issue

The ST-Link utility does not seem to be well supported for the NUCLEO-U575ZI-Q development board. I can neither upload code nor debug my code using the stlink configuration in VS Code. This board uses the ST-Link v3 interface which I believe is currently supported by OpenOCD.

I am unable to find any discussions on this board, likely because it is relatively new on the market.

Hardware testing

I am able to upload the compiled firmware binary (compiled in VS Code using platformio) by the drag-and-drop method offered by the ST-Link debugger where the board presents itself as a removable drive - this works well.

The STM32CubeIDE platform also allows for full firmware upload and debugging without issue using the on-board ST-Link interface.

Uploading code

NOTE: Comment on MCU Reset button being pressed mid-way through uploading over ST-LINK to exit the failed upload session.

Command run

pio run --environment=my_firmware_env --target=upload --verbose
Code upload logs for "my_firmware_env" with verbose output
Building in release mode
MethodWrapper(["checkprogsize"], [".pio\build\my_firmware_env\firmware.elf"])
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   5.8% (used 15296 bytes from 262144 bytes)
Flash: [=         ]   5.8% (used 121617 bytes from 2097152 bytes)
.pio\build\my_firmware_env\firmware.elf  :

section               size        addr

.isr_vector            564   134217728

.text                98520   134218296

.rodata              22237   134316816

.ARM                     8   134339056

.init_array             28   134339064

.fini_array             12   134339092

.data                  860   536870912

.bss                 14433   536871776

.noinit                  3   536886209

._user_heap_stack     1540   536886212

.ARM.attributes         54           0

.comment               102           0

.debug_frame          5696           0

Total               144057
<lambda>(["upload"], [".pio\build\my_firmware_env\firmware.elf"])
AVAILABLE: blackmagic, cmsis-dap, jlink, mbed, stlink
CURRENT: upload_protocol = stlink
openocd -d2 -s C:\Users\Sam\Documents\code\firmware\.platformio\packages\tool-openocd/scripts -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32u5x.cfg -c "program {.pio\build\my_firmware_env\firmware.elf}  verify reset; shutdown;"
xPack OpenOCD x86_64 Open On-Chip Debugger 0.11.0+dev (2021-10-16-21:19)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
debug_level: 2

hla_swd
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : DEPRECATED target event trace-config; use TPIU events {pre,post}-{enable,disable}
Info : clock speed 500 kHz
Info : STLINK V3J10M3 (API v3) VID:PID 0483:374E
Info : Target voltage: 3.287408
Info : stm32u5x.cpu: Cortex-M33 r0p4 processor detected
Info : stm32u5x.cpu: target has 8 breakpoints, 4 watchpoints
Info : starting gdb server for stm32u5x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Unable to match requested speed 480 kHz, using 200 kHz
Info : Unable to match requested speed 480 kHz, using 200 kHz
stm32u5x.dap
Error executing event halted on target stm32u5x.cpu:
C:\Users\Sam\Documents\code\firmware\.platformio\packages\tool-openocd/scripts/target/stm32u5x.cfg:122: Error:
in procedure 'program' 
in procedure 'ocd_process_reset'
in procedure 'ocd_process_reset_inner' called at file "embedded:startup.tcl", line 788
in procedure 'ahb_ap_non_secure_access' called at file "C:\Users\Sam\Documents\code\firmware\.platformio\packages\tool-openocd/scripts/target/stm32u5x.cfg", line 158
at file "C:\Users\Sam\Documents\code\firmware\.platformio\packages\tool-openocd/scripts/target/stm32u5x.cfg", line 122
target halted due to debug-request, current mode: Thread
xPSR: 0xf9000000 pc: 0x08000e3c msp: 0x200c0000
Info : Unable to match requested speed 4000 kHz, using 3300 kHz
Info : Unable to match requested speed 4000 kHz, using 3300 kHz
** Programming Started **
Info : device idcode = 0x20016482 (STM32U57/U58xx - Rev 'unknown' : 0x2001)
Info : TZEN = 0 : TrustZone disabled by option bytes
Info : RDP level 0 (0xAA)
Info : flash size = 2048kbytes
Info : flash mode : dual-bank
Info : Padding image section 1 at 0x0801dd7c with 4 bytes (bank write end alignment)
Warn : Adding extra erase range, 0x0801dd80 .. 0x0801dfff
Error: Target not halted
Error: failed erasing sectors 0 to 14
embedded:startup.tcl:1070: Error: ** Programming Failed **
in procedure 'program' 
in procedure 'program_error' called at file "embedded:startup.tcl", line 1135
at file "embedded:startup.tcl", line 1070
*** [upload] Error 1
================================================================== [FAILED] Took 12.58 seconds ==================================================================
Environment          Status    Duration
-------------------  --------  ------------
my_firmware_env      FAILED    00:00:12.580
============================================================= 1 failed, 0 succeeded in 00:00:12.580 =============================================================

Development setup

VS Code platformio environment

[env:my_firmware_env]
platform = ststm32
board = nucleo_u575zi_q
framework = arduino
lib_ldf_mode = chain+
monitor_speed = 250000
upload_protocol = stlink
monitor_dtr = 1
build_flags =
    -D PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF
    -D PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_SCANF

Software versions

  • Windows 10 Pro 64-bit
  • PlatformIO Core, version 6.1.5

But with the default upload_protocol = stlink, it exactly uses OpenOCD to try and do the upload. See code and code.

Then set upload_protocol = mbed to make PlatformIO use the same “copy firmware.bin to virtual USB drive” method. This still doesn’t fix debugging though.

Under which settings can STM32CubeProgrammer connect to the chip? (Reset method core reset + being connect under reset, etc…)

Many thanks for your reply @maxgerhardt.

Potential solution identified

I found that modifying the provided stm32u5x.cfg file was able to solve my problem! This file is located within platformio’s version of openocd at my_platformio_project_folder\.platformio\packages\tool-openocd\scripts\target\stm32u5x.cfg

I noticed in the debug logs that the open-ocd program was having an issue with the ahb_ap_non_secure_access method/process of this config file. Searching around online, I was able to locate some commits to Gerrit for the Open-OCD project which made use of an if {[using_hla]} check within that method which are omitted in the one included with platformio’s version of OpenOCD (as per platformio version 6.1.5).

Modified + semi-working stm32u5x.cfg file
# SPDX-License-Identifier: GPL-2.0-or-later

# script for stm32u5x family

#
# stm32u5 devices support both JTAG and SWD transports.
#
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]

if { [info exists CHIPNAME] } {
	set _CHIPNAME $CHIPNAME
} else {
	set _CHIPNAME stm32u5x
}

set _ENDIAN little

# Work-area is a space in RAM used for flash programming
# By default use 64kB
if { [info exists WORKAREASIZE] } {
	set _WORKAREASIZE $WORKAREASIZE
} else {
	set _WORKAREASIZE 0x10000
}

#jtag scan chain
if { [info exists CPUTAPID] } {
	set _CPUTAPID $CPUTAPID
} else {
	if { [using_jtag] } {
		# See STM Document RM0438
		# RM0456 Rev1, Section 65.2.8 JTAG debug port - Table 661. JTAG-DP data registers
		# Corresponds to Cortex®-M33 JTAG debug port ID code
		set _CPUTAPID 0x0ba04477
	} {
		# SWD IDCODE (single drop, arm)
		set _CPUTAPID 0x0be12477
	}
}

swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu

if {[using_jtag]} {
	jtag newtap $_CHIPNAME bs -irlen 5
}

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap

# use non-secure RAM by default
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

# create sec/ns flash and otp memories (sizes will be probed)
flash bank $_CHIPNAME.flash_ns stm32l4x 0x08000000 0 0 0 $_TARGETNAME
flash bank $_CHIPNAME.flash_s  stm32l4x 0x0C000000 0 0 0 $_TARGETNAME
flash bank $_CHIPNAME.otp      stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME

# Common knowledges tells JTAG speed should be <= F_CPU/6.
# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
# the safe side.
#
# Note that there is a pretty wide band where things are
# more or less stable, see http://openocd.zylin.com/#/c/3366/
adapter speed 500

adapter srst delay 100
if {[using_jtag]} {
	jtag_ntrst_delay 100
}

reset_config srst_nogate

if {![using_hla]} {
	# if srst is not fitted use SYSRESETREQ to
	# perform a soft reset
	cortex_m reset_config sysresetreq
}

proc is_secure {} {
	# read Debug Security Control and Status Regsiter (DSCSR) and check CDS (bit 16)
	set DSCSR [mrw 0xE000EE08]
	return [expr { [expr { $DSCSR & (1 << 16) }] != 0 }]
}

proc clock_config_160_mhz {} {
	# Mcu clock is at MSI 4MHz after reset, set mcu freq at 160 MHz with PLL
	set offset [expr [is_secure] ? 0x10000000 : 0]

	# Enable voltage range 1 for frequency above 100 Mhz
	# RCC_AHB3ENR = PWREN
	mww [expr 0x46020C94 + $offset] 0x00000004
	# delay for register clock enable (read back reg)
	mrw [expr 0x56020C94 + $offset]
	# PWR_VOSR : VOS Range 1
	mww [expr 0x4602080C + $offset] 0x00030000
	# delay for register write (read back reg)
	mrw [expr 0x4602080C + $offset]
	# FLASH_ACR : 4 WS for 160 MHz HCLK
	mww [expr 0x40022000 + $offset] 0x00000004
	# RCC_PLL1CFGR => PLL1M=0000=/1, PLL1SRC=MSI 4MHz
	mww [expr 0x46020C28 + $offset] 0x00000001
	# RCC_PLL1DIVR => PLL1P=PLL1Q=PLL1R=000001=/2, PLL1N=0x4F=80
	# fVCO = 4 x 80 /1 = 320
	# SYSCLOCK = fVCO/PLL1R = 320/2 = 160 MHz
	mmw [expr 0x46020C34 + $offset] 0x0000004F 0
	# RCC_PLL1CFGR => PLL1REN=1
	mmw [expr 0x46020C28 + $offset] 0x00040000 0
	# RCC_CR |= PLL1ON
	mmw [expr 0x46020C00 + $offset] 0x01000000 0
	# while !(RCC_CR & PLL1RDY)
	while {!([mrw [expr 0x46020C00 + $offset]] & 0x02000000)} {}
	# RCC_CFGR1 |= SW_PLL
	mmw [expr 0x46020C1C + $offset] 0x00000003 0
	# while ((RCC_CFGR1 & SWS) != PLL)
	while {([mrw [expr 0x46020C1C + $offset]] & 0x0C) != 0x0C} {}
}

proc ahb_ap_non_secure_access {} {
	# in HLA mode, non-secure debugging is possible without changing the AP CSW
	if {![using_hla]} {
		# SPROT=1=Non Secure access, Priv=1
		[[target current] cget -dap] apcsw 0x4B000000 0x4F000000
	}
}

proc ahb_ap_secure_access {} {
	if {[using_hla]} {
		echo "Error: The selected transport do not support debbuging this device in secure mode"
		shutdown
	}
	# SPROT=0=Secure access, Priv=1
	[[target current] cget -dap] apcsw 0x0B000000 0x4F000000
}

$_TARGETNAME configure -event reset-init {
	# clock_config_160_mhz
	# Boost JTAG frequency
	adapter speed 4000
}

$_TARGETNAME configure -event reset-start {
	# Reset clock is MSI (4 MHz)
	adapter speed 480
}

$_TARGETNAME configure -event examine-end {
	# DBGMCU_CR |= DBG_STANDBY | DBG_STOP
	mmw 0xE0044004 0x00000006 0

	# Stop watchdog counters during halt
	# DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP
	mmw 0xE0044008 0x00001800 0
}

$_TARGETNAME configure -event halted {
	if {[is_secure]} {
		echo "CPU in Secure state"
		ahb_ap_secure_access
	} else {
		echo "CPU in Non-Secure state"
		ahb_ap_non_secure_access
	}
}

$_TARGETNAME configure -event gdb-flash-erase-start {
	# check if FLASH_OPTR.TZEN is enabled
	set FLASH_OPTR [mrw 0x40022040]
	if {[expr { [expr { $FLASH_OPTR & (1 << 31) }] == 0 }]} {
		echo "TZEN option bit disabled"
		ahb_ap_non_secure_access
		# use non secure mapping
		$_TARGETNAME configure -work-area-phys 0x20000000
	} {
		ahb_ap_secure_access
		echo "TZEN option bit enabled"
		# Use secure mapping
		$_TARGETNAME configure -work-area-phys 0x30000000
	}
}

$_TARGETNAME configure -event trace-config {
	# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
	# change this value accordingly to configure trace pins
	# assignment
	mmw 0xE0044004 0x00000020 0
}

Caveat

While I am now able to get platformio to debug my code on the NUCLEO-U575 board, it only seems to work every “other” time, meaning it will hang in startup and I must restart the debugging session for it to work the next time. This, to me, indicates some configuration element is not being set up correctly.

To answer your questions

The approach using upload_protocol = mbed works as expected.

I am able to connect using STM32CubeProgrammer using the on-board ST-Link in its default configuration - where the ST-Link is responsible for resetting the MCU using a software reset. The Hardware Reset approach also works here, while the Core Reset approach does not work.

Questions + next steps

  • Is there a cleaner way for me to include this modified configuration file until platformio’s version is known-working for this board?
  • Might you have any insight as to why this configuration still only works every “other” time?

I had the same issue with U575 and was able to get it working using the modified config file. I also found out that the upload works with newer version of openocd (xPack OpenOCD v0.11.0-5). Would it be possible to include this version of openocd in the tool-openocd package in platformIO registry @maxgerhardt ?

I’m not a PlatformIO developer, please file an issue in GitHub - platformio/platform-ststm32: ST STM32: development platform for PlatformIO

Thanks @maxgerhardt . Created a new issue: Updated version of tool-openocd package · Issue #683 · platformio/platform-ststm32 · GitHub

Thanks both!

@orvalman - +1 on tool-openocd needing updating in platformio and thank you for creating that issue :pray:

Summary & reason for close

The version of tool-openocd bundled with platformio is not up to date and causes some functionality to misbehave. @orvalman has created an issue in the platformio/platform-ststm32 which should hopefully be resolved soon.

In the meantime and from both our experiences - using configuration files based on the latest version of openocd and overwriting platformio’s ones is likely the best route to success.

Can you share instructions on how to install the newer version of OpenOCD?