Cannot Use cube programmer SWV with ITM
Hi, I am using the STM32N6 nucleo board to print some application data using the ITM stimulus port 0 over the SWO pin.
I am using the dev boot mode.
My stlink version is V3J17M11, which is up to date, and my cube programmer version is 2.23.0, which is up to date.
Problem statement:
My program works in the Cube IDE, and it shows the output in the Serial Wire Viewer tab.
Problem 1: When I try to use it with the Cube Programmer, I click the start button, and it says, Serial wire viewer configuration failed. ( elaborated below)
Problem 2: When I try to use the J-Link with SWD in the Segger Serial Wire Viewer, the J-Link viewer configuration window just resets my MCU, and the code vanishes.
I’ll share my main.c file below.
Explanation
Problem 1:
I tried a lot of things and read about the Access ports. My system clock is 32MHz, and the TPIU clock is 4MHz. The cube programmer’s SWV asks for the system clock, which I assume is the core clock, so 32MHz, but I have also tried 4, 8, 16, and 64 options. Reset type is a software reset.
The Frequency I set usually is 8000Khz but I set it to 1000. I mainly used the Hot plug option cuz any other option would reset the MCU.
I have also tried different access ports. I read about them in the ref manual, so I think my access port is 1, as it accesses the AHB bus to which the TPIU is attached.
Access port 0: it does not work
Access port 1: It shows the error, and the target hangs.
I increased the verbosity level, and it showed me the log “ STLink Write to (0xE0040004) returned: 0x00000020, 2nd try failed“.
To me, it is strange why it would read 0xE0040004. There are two TPIU addresses mentioned in the reference manual, one is under Table 878. Trace subsystem ROM table, that is 0xE008 4000, which is where the actual TPIU is located. The other TPIU address is in Table 871. Processor ROM table that is 0xE004 0000, but here is a note saying that “The TPIU is included in this table by default, but bit 0 is reset to indicate that it is not present.”
I came to know this when my MCU wanted a hard fault handler from the instruction, TPI->ACPR = 400; where in the CMSIS file, its base is defined as “ #define TPI_BASE (0xE0040000UL) /* !< TPI Base Address */ ”
HOW I THINK I SHOULD USE THE ACCESS PROT 1
In the ref manual, we have three APs in Figure 1078. AHB access is present at the AP1. This is mentioned before Table 878 in the RM: “These components are accessible by the debugger and the Cortex-M55 processor via the system AHB-AP and its associated EPPB debug bus.”
Problem 2:
I did not spend much time with the J-Link and Segger serial wire viewer. If anyone knows any tips to make it work pls share. I hope that once the code works with the Cube Programmer, the J-Link will also work with the Segger serial wire viewer.
My Question
When we are doing such debug stuff, what things do we need to configure in the code and what does the debugger does itseld. There is a lot of confusion about this; sometimes it is seen that the code enables the ITM, TPIU, and also sets the SWO and ITM prescaler itself. And sometimes it just puts the data in the stimulus register, and it does work?
Why I cant access the debug port in the flash boot mode? It is very inconvenient; the debugger resets the MCUs, and suddenly, we have no firmware in the MCU.
Why did it try to read the 0xE004 0000 since no TPIU is present there, and also why does the CMSIS file have this address as the TPIU base?
Which access port should I use?
Is this issue related to the trust zone or the debug authentication? If yes, how is the same code working in the Cube IDE?
My main.c
There are several lines that I have commented out, but the current state of code is the actual program that works in cube IDE and Cube. I also hope work in Cube Programmer software.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//#define DEMCR *((volatile uint32_t*) 0xE000EDFCU )
//#define TPIU_base (volatile)(*0xE0084000)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */
uint8_t ITM_send_string(char* str, uint8_t port);
uint8_t itm_send_char(uint8_t ch, uint8_t port);
void itm_init(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
SCB_DisableDCache();
SCB_DisableICache();
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
HAL_Init();
/* USER CODE BEGIN Init */
//DBGMCU->CR |=0x00300000;
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
//CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//TPI->SPPR = 2; // NRZ
TPI->ACPR = 400; // setting 16 bit prescaler register; 4000/400 = 10khz
//ITM->LAR = 0xC5ACCE55;
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
uint8_t myport = 0;
itm_init();
const char myString[] = "Hello form STM \n";
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_8);
HAL_Delay(300);
uint8_t ret = ITM_send_string(myString, myport);
ret++;
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN CLK 1 */
/* USER CODE END CLK 1 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the System Power Supply
*/
if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY) != HAL_OK)
{
Error_Handler();
}
/* Enable HSI */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Get current CPU/System buses clocks configuration and if necessary switch
to intermediate HSI clock to ensure target clock can be set
*/
HAL_RCC_GetClockConfig(&RCC_ClkInitStruct);
if ((RCC_ClkInitStruct.CPUCLKSource == RCC_CPUCLKSOURCE_IC1) ||
(RCC_ClkInitStruct.SYSCLKSource == RCC_SYSCLKSOURCE_IC2_IC6_IC11))
{
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_CPUCLK | RCC_CLOCKTYPE_SYSCLK);
RCC_ClkInitStruct.CPUCLKSource = RCC_CPUCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_NONE;
RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL1.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL1.PLLM = 1;
RCC_OscInitStruct.PLL1.PLLN = 25;
RCC_OscInitStruct.PLL1.PLLFractional = 0;
RCC_OscInitStruct.PLL1.PLLP1 = 1;
RCC_OscInitStruct.PLL1.PLLP2 = 1;
RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_CPUCLK|RCC_CLOCKTYPE_HCLK
|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
|RCC_CLOCKTYPE_PCLK2|RCC_CLOCKTYPE_PCLK5
|RCC_CLOCKTYPE_PCLK4;
RCC_ClkInitStruct.CPUCLKSource = RCC_CPUCLKSOURCE_IC1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;
RCC_ClkInitStruct.APB5CLKDivider = RCC_APB5_DIV1;
RCC_ClkInitStruct.IC1Selection.ClockSelection = RCC_ICCLKSOURCE_PLL1;
RCC_ClkInitStruct.IC1Selection.ClockDivider = 50;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOO_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOO, GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_10|GPIO_PIN_8, GPIO_PIN_RESET);
/*Configure GPIO pin : PO1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOO, &GPIO_InitStruct);
/*Configure GPIO pins : PG10 PG8 */
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
// configuring the pb5 for the swo pin
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF0_TRACE;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/**
* @brief This function uses the api itm_send_char to send the characters in the strings.
* This function expects that str variable is an actual string, i.e terminates at \0 charactor.
* @param str String to be send, terminated by \0
* @param port port to be used.
* @return uint8_t return 0 in case of success.
*/
uint8_t ITM_send_string(char* str, uint8_t port)
{
int i = 0;
while( str[i] != '\0')
{
// a new valid charactor
if( ! itm_send_char(str[i], port) )
{
// successfully sent a charactor
i++;
}
else {
{
// error ouccered
return 1;
}
}
}
// end of the string reached
return 0;
}
/**
* @brief Sends a character on to the specified port.
*
* @param ch charactor to be sent
* @param port number of port from 0~31
* @return uint8_t returns
*/
uint8_t itm_send_char(uint8_t ch, uint8_t port)
{
if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */
((ITM->TER & 1UL ) != 0UL) && /* ITM Port #0 enabled */
(port<31) ) /* port number do not exceed the limit */
{
// all good , wait for the fifo to be empty and send charactor.
while (ITM->PORT[port].u32 == 0UL)
{
__NOP();
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_8);
HAL_Delay(50);
}
ITM->PORT[port].u8 = (uint8_t)ch;
}
else
{
return 1;
}
return 0;
}
/**
* @brief This function initializes the ITM port 0 to 7
* It sets their access to unprovilidged and enable itm and first 7 ports
*
*/
void itm_init()
{
// enabling ITM
ITM->TCR |= 1;
// enabling port 0~7
ITM->TER = 0x7F;
// enabling port 0~7 Unprovilidged access
ITM->TPR = 0x00;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
