How to make the STM32 debugger aware of FreeRTOS tasks?

I am setting a new stm32cube project with FreeRTOS, based on library files generated by the Cube IDE. It’s at a point where it builds, runs simple tasks, and works with the debugger, but, the debugger is not task aware and shows only the current thread. Any idea how to make the debugger task aware?

Entire project:

Debugger screenshot:

platformio.ini

[env:weact_mini_h750vbtx]
build_type = debug
debug_tool = stlink
upload_protocol = stlink
debug_build_flags = -O0 -ggdb3 -g3
platform = ststm32
board = weact_mini_h750vbtx
framework = stm32cube
lib_archive = no
build_flags =
  -mthumb 
  -mfpu=fpv4-sp-d16 
  -mfloat-abi=softfp
  -D debug
  -D USE_HAL_DRIVER

It’s OpenOCD’s duty in this case to create thread-awareness. For that, the -rtos auto (or -rtos FreeRTOS) option must be given in the OpenOCD config file.

Unfortunately it doesn’t seem to to be possible to just give OpenOCD some commandline option to turn on FreeRTOS support.

With

You should have the file C:\Users\<user>\.platformio\packages\tool-openocd\scripts\target\stm32h7x.cfg, where, line 86 says

$_CHIPNAME.cpu0 configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

at the end of that line append -rtos auto.

Note that OpenOCD in the debug console may still complain about

Error: FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around

in which case you need to follow this and create the needed variable while making sure it’s not optimized away. I e.g. achieved this by alternatively by doing

volatile int uxTopUsedPriority;

//in main():
uxTopUsedPriority = configMAX_PRIORITIES - 1;

Thanks @maxgerhardt. I applied the changes as I understood them, but now the debugger doesn’t break upon start and is non responsive. Below are the details. Any suggestions?

Full repository:

platformio.ini

[env:weact_mini_h750vbtx]
build_type = debug
debug_tool = stlink
upload_protocol = stlink
debug_build_flags = -O0 -ggdb3 -g3
platform = ststm32
board = weact_mini_h750vbtx
framework = stm32cube
lib_archive = no
build_flags =
  -mthumb 
  -mfpu=fpv4-sp-d16 
  -mfloat-abi=softfp
  -D debug
  -D USE_HAL_DRIVER

Pop up message that I get when the debugger starts.

Unable to get thread information: No symbol "undefined" in current context. (from thread-select undefined)

Debugger log

Loading section .isr_vector, size 0x298 lma 0x8000000
Loading section .text, size 0xc750 lma 0x80002a0
Loading section .rodata, size 0xa0 lma 0x800c9f0
Loading section .ARM, size 0x8 lma 0x800ca90
Loading section .init_array, size 0x4 lma 0x800ca98
Loading section .fini_array, size 0x4 lma 0x800ca9c
Loading section .data, size 0x168 lma 0x800caa0
Info : Padding image section 0 at 0x08000298 with 8 bytes
Info : Padding image section 1 at 0x0800cc08 with 24 bytes (bank write end alignment)
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08006ffc msp: 0x24080000
Start address 0x8006ffc, load size 52224
Transfer rate: 33 KB/sec, 5222 bytes/write.
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08006ffc msp: 0x24080000
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08006ffc msp: 0x24080000
Temporary breakpoint 1 at 0x800c77a: file src\main.cpp, line 93.
PlatformIO: Initialization completed
[Switching to thread 1 (Thread 603980176)]
[New Thread 603980176]
PlatformIO: Resume the execution to `debug_init_break = tbreak main`
PlatformIO: More configuration options -> https://bit.ly/pio-debug
Note: automatically using hardware breakpoints for read-only addresses.
[New Remote target]
[Switching to Thread 1]

main.cpp


#include "main.h"

#include "FreeRTOS.h"
#include "cmsis_os.h"
#include "gpio.h"
#include "task.h"
#include "usart.h"
#include "usb_device.h"

void SystemClock_Config(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
   */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
   */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
  }

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
  }

  /** Initializes the RCC Oscillators according to the specified parameters
   * in the RCC_OscInitTypeDef structure.
   */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 5;
  RCC_OscInitStruct.PLL.PLLN = 192;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
   */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
                                RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 |
                                RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
    Error_Handler();
  }
}

// static osThreadId_t defaultTaskHandle;

// extern void MX_USB_DEVICE_Init(void);

void defaultTask(void *argument) {
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
  // const int f_cpu = F_CPU;
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  for (;;) {
    // osDelay(1);
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
    vTaskDelay(100);
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    vTaskDelay(100);
  }
}

// TODO: This file is main.cpp. Should this variable be declared 
// C extern? 
volatile int uxTopUsedPriority;


int main(void) {
  uxTopUsedPriority = configMAX_PRIORITIES - 1;

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

  osKernelInitialize();

  // MX_FREERTOS_Init();
  // osThreadAttr_t defaultTask_attributes;
  // defaultTask_attributes.name = "defaultTask";
  // defaultTask_attributes.stack_size = 512 * 4;
  // defaultTask_attributes.priority = (osPriority_t)osPriorityNormal;

  // osThreadNew(defaultTask, NULL, &defaultTask_attributes);

  TaskHandle_t xHandle = NULL;
  xTaskCreate(defaultTask, "T1", 1000 / sizeof(StackType_t), nullptr, 10,
              &xHandle);
  xTaskCreate(defaultTask, "T2", 1000 / sizeof(StackType_t), nullptr, 10,
              &xHandle);

  vTaskStartScheduler();

  // osKernelStart();
  // We should never get here as control is now taken by the scheduler infinite
  // loop.
  while (1) {
    vTaskDelay(10);

    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
  }
}

void Error_Handler(void) {
  __disable_irq();
  while (1) {
  }
}

stm32h7x.cfg:

# script for stm32h7x family

#
# stm32h7 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 stm32h7x
}

if { [info exists DUAL_BANK] } {
	set $_CHIPNAME.DUAL_BANK $DUAL_BANK
	unset DUAL_BANK
} else {
	set $_CHIPNAME.DUAL_BANK 0
}

if { [info exists DUAL_CORE] } {
	set $_CHIPNAME.DUAL_CORE $DUAL_CORE
	unset DUAL_CORE
} else {
	set $_CHIPNAME.DUAL_CORE 0
}

# Issue a warning when hla is used, and fallback to single core configuration
if { [set $_CHIPNAME.DUAL_CORE] && [using_hla] } {
	echo "Warning : hla does not support multicore debugging"
	set $_CHIPNAME.DUAL_CORE 0
}

if { [info exists USE_CTI] } {
	set $_CHIPNAME.USE_CTI $USE_CTI
	unset USE_CTI
} else {
	set $_CHIPNAME.USE_CTI 0
}

# Issue a warning when DUAL_CORE=0 and USE_CTI=1, and fallback to USE_CTI=0
if { ![set $_CHIPNAME.DUAL_CORE] && [set $_CHIPNAME.USE_CTI] } {
	echo "Warning : could not use CTI with a single core device, CTI is disabled"
	set $_CHIPNAME.USE_CTI 0
}

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] } {
	  set _CPUTAPID 0x6ba00477
   } {
      set _CPUTAPID 0x6ba02477
   }
}

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
}

if {![using_hla]} {
	# STM32H7 provides an APB-AP at access port 2, which allows the access to
	# the debug and trace features on the system APB System Debug Bus (APB-D).
	target create $_CHIPNAME.ap2 mem_ap -dap $_CHIPNAME.dap -ap-num 2
	swo  create $_CHIPNAME.swo  -dap $_CHIPNAME.dap -ap-num 2 -baseaddr 0xE00E3000
	tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 2 -baseaddr 0xE00F5000
}

target create $_CHIPNAME.cpu0 cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -ap-num 0

$_CHIPNAME.cpu0 configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -rtos auto

flash bank $_CHIPNAME.bank1.cpu0 stm32h7x 0x08000000 0 0 0 $_CHIPNAME.cpu0

if {[set $_CHIPNAME.DUAL_BANK]} {
	flash bank $_CHIPNAME.bank2.cpu0 stm32h7x 0x08100000 0 0 0 $_CHIPNAME.cpu0
}

if {[set $_CHIPNAME.DUAL_CORE]} {
	target create $_CHIPNAME.cpu1 cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -ap-num 3

	$_CHIPNAME.cpu1 configure -work-area-phys 0x38000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

	flash bank $_CHIPNAME.bank1.cpu1 stm32h7x 0x08000000 0 0 0 $_CHIPNAME.cpu1

	if {[set $_CHIPNAME.DUAL_BANK]} {
		flash bank $_CHIPNAME.bank2.cpu1 stm32h7x 0x08100000 0 0 0 $_CHIPNAME.cpu1
	}
}

# Make sure that cpu0 is selected
targets $_CHIPNAME.cpu0

if { [info exists QUADSPI] && $QUADSPI } {
   set a [llength [flash list]]
   set _QSPINAME $_CHIPNAME.qspi
   flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_CHIPNAME.cpu0 0x52005000
} else {
   if { [info exists OCTOSPI1] && $OCTOSPI1 } {
      set a [llength [flash list]]
      set _OCTOSPINAME1 $_CHIPNAME.octospi1
      flash bank $_OCTOSPINAME1 stmqspi 0x90000000 0 0 0 $_CHIPNAME.cpu0 0x52005000
   }
   if { [info exists OCTOSPI2] && $OCTOSPI2 } {
      set b [llength [flash list]]
      set _OCTOSPINAME2 $_CHIPNAME.octospi2
      flash bank $_OCTOSPINAME2 stmqspi 0x70000000 0 0 0 $_CHIPNAME.cpu0 0x5200A000
   }
}

# Clock after reset is HSI at 64 MHz, no need of PLL
adapter speed 1800

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

# use hardware reset
#
# The STM32H7 does not support connect_assert_srst mode because the AXI is
# unavailable while SRST is asserted, and that is used to access the DBGMCU
# component at 0x5C001000 in the examine-end event handler.
#
# It is possible to access the DBGMCU component at 0xE00E1000 via AP2 instead
# of the default AP0, and that works with SRST asserted; however, nonzero AP
# usage does not work with HLA, so is not done by default. That change could be
# made in a local configuration file if connect_assert_srst mode is needed for
# a specific application and a non-HLA adapter is in use.
reset_config srst_nogate

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

	if {[set $_CHIPNAME.DUAL_CORE]} {
		$_CHIPNAME.cpu1 cortex_m reset_config sysresetreq
	}

   # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal
   # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3
   # makes the data access cacheable. This allows reading and writing data in the
   # CPU cache from the debugger, which is far more useful than going straight to
   # RAM when operating on typical variables, and is generally no worse when
   # operating on special memory locations.
   $_CHIPNAME.dap apcsw 0x08000000 0x08000000
}

$_CHIPNAME.cpu0 configure -event examine-end {
	# Enable D3 and D1 DBG clocks
	# DBGMCU_CR |= D3DBGCKEN | D1DBGCKEN
	stm32h7x_dbgmcu_mmw 0x004 0x00600000 0

	# Enable debug during low power modes (uses more power)
	# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP D1 Domain
	stm32h7x_dbgmcu_mmw 0x004 0x00000007 0
	# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP D2 Domain
	stm32h7x_dbgmcu_mmw 0x004 0x00000038 0

	# Stop watchdog counters during halt
	# DBGMCU_APB3FZ1 |= WWDG1
	stm32h7x_dbgmcu_mmw 0x034 0x00000040 0
	# DBGMCU_APB1LFZ1 |= WWDG2
	stm32h7x_dbgmcu_mmw 0x03C 0x00000800 0
	# DBGMCU_APB4FZ1 |= WDGLSD1 | WDGLSD2
	stm32h7x_dbgmcu_mmw 0x054 0x000C0000 0

	# Enable clock for tracing
	# DBGMCU_CR |= TRACECLKEN
	stm32h7x_dbgmcu_mmw 0x004 0x00100000 0

	# RM0399 (id 0x450) M7+M4 with SWO Funnel
	# RM0433 (id 0x450) M7 with SWO Funnel
	# RM0455 (id 0x480) M7 without SWO Funnel
	# RM0468 (id 0x483) M7 without SWO Funnel
	# Enable CM7 and CM4 slave ports in SWO trace Funnel
	# Works ok also on devices single core and without SWO funnel
	# Hack, use stm32h7x_dbgmcu_mmw with big offset to control SWTF
	# SWTF_CTRL |= ENS0 | ENS1
	stm32h7x_dbgmcu_mmw 0x3000 0x00000003 0
}

$_CHIPNAME.cpu0 configure -event reset-init {
	# Clock after reset is HSI at 64 MHz, no need of PLL
	adapter speed 4000
}

# get _CHIPNAME from current target
proc stm32h7x_get_chipname {} {
	set t [target current]
	set sep [string last "." $t]
	if {$sep == -1} {
		return $t
	}
	return [string range $t 0 [expr {$sep - 1}]]
}

if {[set $_CHIPNAME.DUAL_CORE]} {
	$_CHIPNAME.cpu1 configure -event examine-end {
		set _CHIPNAME [stm32h7x_get_chipname]
		global $_CHIPNAME.USE_CTI

		# Stop watchdog counters during halt
		# DBGMCU_APB3FZ2 |= WWDG1
		stm32h7x_dbgmcu_mmw 0x038 0x00000040 0
		# DBGMCU_APB1LFZ2 |= WWDG2
		stm32h7x_dbgmcu_mmw 0x040 0x00000800 0
		# DBGMCU_APB4FZ2 |= WDGLSD1 | WDGLSD2
		stm32h7x_dbgmcu_mmw 0x058 0x000C0000 0

		if {[set $_CHIPNAME.USE_CTI]} {
			stm32h7x_cti_start
		}
	}
}

# like mrw, but with target selection
proc stm32h7x_mrw {used_target reg} {
	set value ""
	$used_target mem2array value 32 $reg 1
	return $value(0)
}

# like mmw, but with target selection
proc stm32h7x_mmw {used_target reg setbits clearbits} {
	set old [stm32h7x_mrw $used_target $reg]
	set new [expr {($old & ~$clearbits) | $setbits}]
	$used_target mww $reg $new
}

# mmw for dbgmcu component registers, it accepts the register offset from dbgmcu base
# this procedure will use the mem_ap on AP2 whenever possible
proc stm32h7x_dbgmcu_mmw {reg_offset setbits clearbits} {
	# use $_CHIPNAME.ap2 if possible, and use the proper dbgmcu base address
	if {![using_hla]} {
		set _CHIPNAME [stm32h7x_get_chipname]
		set used_target $_CHIPNAME.ap2
		set reg_addr [expr {0xE00E1000 + $reg_offset}]
	} {
		set used_target [target current]
		set reg_addr [expr {0x5C001000 + $reg_offset}]
	}

	stm32h7x_mmw $used_target $reg_addr $setbits $clearbits
}

if {[set $_CHIPNAME.USE_CTI]} {
	# create CTI instances for both cores
	cti create $_CHIPNAME.cti0 -dap $_CHIPNAME.dap -ap-num 0 -baseaddr 0xE0043000
	cti create $_CHIPNAME.cti1 -dap $_CHIPNAME.dap -ap-num 3 -baseaddr 0xE0043000

	$_CHIPNAME.cpu0 configure -event halted { stm32h7x_cti_prepare_restart_all }
	$_CHIPNAME.cpu1 configure -event halted { stm32h7x_cti_prepare_restart_all }

	$_CHIPNAME.cpu0 configure -event debug-halted { stm32h7x_cti_prepare_restart_all }
	$_CHIPNAME.cpu1 configure -event debug-halted { stm32h7x_cti_prepare_restart_all }

	proc stm32h7x_cti_start {} {
		set _CHIPNAME [stm32h7x_get_chipname]

		# Configure Cores' CTIs to halt each other
		# TRIGIN0 (DBGTRIGGER) and TRIGOUT0 (EDBGRQ) at CTM_CHANNEL_0
		$_CHIPNAME.cti0 write INEN0 0x1
		$_CHIPNAME.cti0 write OUTEN0 0x1
		$_CHIPNAME.cti1 write INEN0 0x1
		$_CHIPNAME.cti1 write OUTEN0 0x1

		# enable CTIs
		$_CHIPNAME.cti0 enable on
		$_CHIPNAME.cti1 enable on
	}

	proc stm32h7x_cti_stop {} {
		set _CHIPNAME [stm32h7x_get_chipname]

		$_CHIPNAME.cti0 enable off
		$_CHIPNAME.cti1 enable off
	}

	proc stm32h7x_cti_prepare_restart_all {} {
		stm32h7x_cti_prepare_restart cti0
		stm32h7x_cti_prepare_restart cti1
	}

	proc stm32h7x_cti_prepare_restart {cti} {
		set _CHIPNAME [stm32h7x_get_chipname]

		# Acknowlodge EDBGRQ at TRIGOUT0
		$_CHIPNAME.$cti write INACK 0x01
		$_CHIPNAME.$cti write INACK 0x00
	}
}

… also tried ‘FreeRTOS’ instead of ‘auto’ and got similar results and this debug log:

Processing weact_mini_h750vbtx (platform: ststm32; board: weact_mini_h750vbtx; framework: stm32cube)
--------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/weact_mini_h750vbtx.html
PLATFORM: ST STM32 (15.6.0) > WeAct Studio MiniSTM32H750VBTX
HARDWARE: STM32H750VBT6 480MHz, 128KB RAM, 512KB Flash
DEBUG: Current (stlink) External (blackmagic, cmsis-dap, jlink, stlink)
PACKAGES: 
 - framework-stm32cubeh7 @ 1.9.0 
 - tool-ldscripts-ststm32 @ 0.2.0 
 - toolchain-gccarmnoneeabi @ 1.70201.0 (7.2.1)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 59 compatible libraries
Scanning dependencies...
Dependency Graph
|-- autogen_middlewares
|-- autogen_core
|-- autogen_usb
Building in debug mode
Linking .pio\build\weact_mini_h750vbtx\firmware.elf
Checking size .pio\build\weact_mini_h750vbtx\firmware.elf

Instead of this can you try the originally linked way? Maybe this somehow happenss to later and it reads a wrong value at the wrong time…

Thanks @maxgerhardt, I am still struggle with it.

  1. What do you mean by ‘the originally linked way’? Without uxTopUsedPriority ?

  2. Is there a way to increase the verbosity of the debugger log?

  3. Do you happen to have an STM32 ‘pill’ board such as SMT32F401? If so, I can set up a project that you can replicated.

  4. With Cube-IDE, I can debug threads with no problems with ST-LINK(ST_LINK GDB) but not ST-LINK(OpenOCD) is broken. Does it make sense to try to the same with platformio? that is, using ST-LINK GDB server?

Thanks
Zapta

^-- this is the originally linked way.

Thanks @maxgerhardt, it’s working now. It required three changes outlined below. One of them, is updating the openocd binaries from 0.11.0+dev (2021-10-16-21:19) to 0.12.0-00017-gb153daa14 (2023-02-03-14:58) (these versions are on Windows). Will it be possible to upgrade the openocd version that platformio installs? I got my binaries from an installation of ST Cube IDE on widnows.

For the record, here are the three changes:

1. Include const int uxTopUsedPriority in the link.

Its name should be non C++ mangled and should have an actual address, not 0x0000000 which means it was not optimized out.

This is how it should look in the linker map file:

 .rodata.uxTopUsedPriority
                0x0800d9ac        0x4 .pio/build/weact_mini_h750vbtx/src/main.o
                0x0800d9ac                uxTopUsedPriority

I achieved that by having this in my main.cpp (should be simpler in a .c):

extern "C" {
  extern const int uxTopUsedPriority;
 __attribute__((used)) const int uxTopUsedPriority = configMAX_PRIORITIES - 1;
}

int main(void) {
  printf("%p\n", &uxTopUsedPriority);

  ...
}

2. Append ‘-rtos auto’ to the relevant openocd configuration script.

in my case it’s the file

~/.platformio/packages/tool-openocd/scripts/target/stm32h7x.cfg

And the line after the change is

$_CHIPNAME.cpu0 configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -rtos auto

3. Replace the openocd binaries with newer ones.

To do this deleted all the files in the opencd bin directory and place instead the binaries of the newer version.

Open OCD bin directory:

~/.platformio/packages/tool-openocd/bin

The old files in my case (windowz)

-rwxr-xr-x 1 user 197121  397027 Oct 16  2021 libftdi1.dll
-rwxr-xr-x 1 user 197121  209885 Oct 16  2021 libusb-1.0.dll
-rwxr-xr-x 1 user 197121 4075008 Oct 16  2021 openocd.exe

The new files in my case (window):

-rwxr-xr-x 1 user 197121 1102386 May 31 11:10 libgcc_s_sjlj-1.dll
-rwxr-xr-x 1 user 197121  328493 May 31 11:10 libhidapi-0.dll
-rwxr-xr-x 1 user 197121 1159378 May 31 11:10 libjaylink-0.dll
-rwxr-xr-x 1 user 197121 1058321 May 31 11:10 libusb-1.0.dll
-rwxr-xr-x 1 user 197121  530510 May 31 11:10 libwinpthread-1.dll
-rwxr-xr-x 1 user 197121 5083837 May 31 11:10 openocd.exe

The binaries files I used are here temp_public/openocd_bin_windows at main · zapta/temp_public · GitHub . They were copied from a windows install of ST Cube IDE 1.12.1.

EDIT: Upgrading the openocd binaries result in these (seems to be harmless) warnings. Would be nice to upgradethe scripts as well.

Warn : DEPRECATED! use 'read_memory' not 'mem2array'

EDIT2: This also preserved the variable in the map and works:

extern "C" {
  extern  const int uxTopUsedPriority;
  __attribute__((section(".rodata"))) const  int uxTopUsedPriority = configMAX_PRIORITIES - 1;
}

In the map:

 .rodata        0x0800b500        0xf .pio/build/weact_mini_h750vbtx/src/main.o
                0x0800b508                uxTopUsedPriority

For the record, stumbled upon this in the ocd repository. Haven’t tried it yet.

// SPDX-License-Identifier: GPL-2.0-or-later

/*
 * Since at least FreeRTOS V7.5.3 uxTopUsedPriority is no longer
 * present in the kernel, so it has to be supplied by other means for
 * OpenOCD's threads awareness.
 *
 * Add this file to your project, and, if you're using --gc-sections,
 * ``--undefined=uxTopUsedPriority'' (or
 * ``-Wl,--undefined=uxTopUsedPriority'' when using gcc for final
 * linking) to your LDFLAGS; same with all the other symbols you need.
 */

#include "FreeRTOS.h"

#ifdef __GNUC__
#define USED __attribute__((used))
#else
#define USED
#endif

const int USED uxTopUsedPriority = configMAX_PRIORITIES - 1;