STLINK debugger configures some 32F4 target registers e.g. SWO trace macrocell - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: September 30, 2025, 11:09:28 am
content: This also refers a bithttps://www.eevblog.com/forum/microcontrollers/10-pin-debug-connector-and-stlink-v3-isol-not-reliable/I have been investigating why most (not all) STLINK V3 debuggers are persistently unreliable, in the SWO feature, which they call SWV ITM debug. It is a high speed UART which comes out on the SWO pin of the SWD interface.On a scope, I see the data coming out on the SWO pin goingintothe debugger, so it is apparently nothing to do with the source code. The first mystery is that in all the ST example code there is no config for this feature. So initially I thought they just rely on power-up register values.As regards Cube IDE config for this, the debugger config (under Project Properties Debug etc) looks like this, and one can control the SWO speed so e.g. thisproduces 1MHz. One can check this with a scope.But still Cube IDE shows no data, even though the scope shows the bits 1us wide. So I wondered if the UART has the right config...This led me to wondering whether there is a specific init to be done in the CPU. No STM code I have seen configures the SWO output. The output function is just thisCode:/** Copied here from core_cm4.h.\brief ITM Send Character\details Transmits a character via the ITM channel 0, and\li Just returns when no debugger is connected that has booked the output.\li Is blocking when a debugger is connected, but the previous character sent has not been transmitted.\param  ch Character to transmit.*/static void ITM_SendChar_2 (uint32_t ch){if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */{while (ITM->PORT.u32 == 0UL){__NOP();}ITM->PORT.u8 = (uint8_t)ch;}return;}So they are enabling the output for every char. And this is old code, used by many.So I wondered whether there is something deeper going on which is producing the data in a format which the debugger+Cube cannot parse. Googling around, one finds this codeCode:// Enable debug clocks in RCCvoid SWO_Init(void){// Enable DBGMCU clock (usually already enabled)// Enable trace and debug blocksCoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;// Get current system clock frequencyuint32_t sysclk = SystemCoreClock;// Configure SWO prescaler for desired baud rate// For example, for 2 MHz SWO clock from 168 MHz system clock:// Prescaler = (SYSCLK / SWO_FREQ) - 1uint32_t swo_freq = 2000000; // 2 MHzuint32_t prescaler = (sysclk / swo_freq) - 1;// Configure TPIU (Trace Port Interface Unit)TPI->ACPR = prescaler;// Set protocol to NRZ (UART-like)TPI->SPPR = 2; // 2 = NRZ/UART mode// Disable formatterTPI->FFCR = 0x00;// Unlock ITM (Instrumentation Trace Macrocell)ITM->LAR = 0xC5ACCE55;// Enable ITMITM->TCR = (1 << ITM_TCR_ITMENA_Pos) | // Enable ITM(1 << ITM_TCR_SYNCENA_Pos) | // Enable sync packets(1 << ITM_TCR_TraceBusID_Pos); // Trace bus ID = 1// Enable stimulus ports (channels 0-31)ITM->TER = 0xFFFFFFFF; // Enable all 32 channels// Configure DWT (Data Watchpoint and Trace)DWT->CTRL = 0;}which does not help, perhaps because the SWO clock is now 100kHzWhy 100kHz? No idea. Probably because the trace macrocell runs off the APB1 and not the 168MHz clock. But looking up the trace macrocell registers in the RM, I see thisso thedebuggeractually sets up all this, by writing into the relevant CPU registers! So no wonder that ST do not provide SWO setup code in their code examples...As to what data the debugger is then looking for in the way of data format, I can't find this but since there is no clock signal coming out, I would expect SWO to be straight NRZ like RS232.This whole thing is a mess. Fairly obviously, if you include any code to configure this SWO feature, that will executeafterthe debugger has configured it, so now Cube needs to be configured to match yoursoftwareconfig.Now I am seeing something else: continuous data on SWO. A packet of data every 95us. No idea where that is coming from. The data, if NRZ, is inverted i.e. normally 1, which is what a UART inside a CPU normally emits. And this remains after I have reverted back to the previous code. Given that this trace macrocell is set up by the debugger, probably that is where the problem is.Obviously I've tried different STLINKs; both the V3 ISOL in a plastic box and the tiny 40x15mm PCB version with a USB-C connector. SWV ITM debugs are dead on all of these.A quick google seems to agree with my above hypothesis:Yes, STLINK debuggers configure STM32F4 target registers, including the ITM (Instruction Trace Macrocell) for Serial Wire Output (SWO) trace, by writing to ITM/TPIU registers through the SWD or JTAG interface. This allows developers to use software like STM32CubeIDE or Keil MDK to enable SWO output, view debug information from the Instrumentation Trace Macrocell, and visualize it in a terminal.This topic also relates to an old question: can you use the SWO debug output with a target that does not have a debugger attached? Obviously this will not work. In such a case you must include the code to set it all up. Like herehttps://community.st.com/t5/stm32-mcus-boards-and-hardware/how-to-enable-swo-with-st-link-utility-on-stm32f407g-disc1/td-p/228896https://gist.github.com/mofosyne/178ad947fdff0f357eb0e03a42bcef5cBut the last one above is still using a debugger of some sort because he is sending out OpenOCD commands to it.However, the post by Nikita91 herehttps://community.st.com/t5/stm32-mcus-boards-and-hardware/how-to-enable-swo-with-st-link-utility-on-stm32f407g-disc1/td-p/228896suggests that it will still not work until enabled by the debugger! That code looks closest to something which might work though.Also he is missing the code to set PB3 to an output. That code appears in the mofosyne code above.

author: ataradov
author_rank: Super Contributor
timestamp: September 30, 2025, 02:21:06 pm
content: This is expected behavior. Debuggers will configure relevant debug peripherals whoever is appropriate for the current settings. Debuggers can't expect your code to configure the hardware correctly. They are used to debug potentially buggy code. There are tools that are just trace receivers. They will not configure anything and will expect you to provide configuration details.In some device those components will not even work unless debugger presence is detected.

author: peter-h
author_rank: Super Contributor
timestamp: September 30, 2025, 02:55:51 pm
content: OK that makes sense.It was my efforts to make SWV ITM work reliably with Cube IDE which led me to investigate standalone UART output mode.I got some traction herehttps://community.st.com/t5/stm32-mcus-boards-and-hardware/32f417-how-to-use-swv-itm-swo-debug-output-without-cube-ide-or/m-p/843611#M27761so we will see if anybody replies. In particular SWO may not be a simple UART but a 16-byte packager interface as suggested in some doc.

author: ataradov
author_rank: Super Contributor
timestamp: September 30, 2025, 03:13:51 pm
content: SWO may use UART or NRZ for the encoding. But it does not just send the raw data bytes like printf() on a regular UART would. ITM (which outputs SWO) is a debug data sink. It takes debug data from multiple sources and serializes it.In an UART mode you can use a regular UART to receive the stream, but you need to parse it to extract the data.It is impossible to get just the byte stream. You can do something like "ITM->PORT.u32 = 11; ITM->PORT.u32 = 22;" and you can meaningfully understand that 11 came from port 0 and 22 came from port 1. To do this a more involved encoding is required.

author: peter-h
author_rank: Super Contributor
timestamp: September 30, 2025, 03:27:46 pm
content: In that case I need to sink some time into working out why, currently, SWV ITM is dead as a dodo on my two development machinesThe target code, at least up to the point where I do some debug strings, has not changed in years.I've tried different STLINK V3 debuggers. Just occassionally it works for a bit.BTW is there a reasonable explanation for why I seecontinuousdata on SWO, on a scope?This is the other Cube IDE config. I don't think comparator 0 does anything but most people have it tickedBTW after doing the Cube config in post #1, one has to exit Cube and restart it before the /168 clock prescaler etc shows up correctly above. One of many gotchas...

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 30, 2025, 06:32:23 pm
content: The registers for outputting from SWO are typically configured via SWD by the debug tool (there's also a way of doing it using OpenOCD with specific commands), and in your case, this looks correctly executed since you can see a data stream out of the SWO pin with the bit rate you configured. So, all looks fine on this front and there's nothing more to do on the MCU side.I have used SWO as a simple "UART" but using OpenOCD to configure ITM.But this article deals with it. Let me know if it's enough/correct info in your case.https://community.st.com/t5/stm32-mcus/using-the-itm-console-for-printf-redirects-and-lwip-debug/ta-p/723472

author: peter-h
author_rank: Super Contributor
timestamp: September 30, 2025, 07:13:24 pm
content: Well, bloody hell, it looks like it is thisMust be disabled!

author: ataradov
author_rank: Super Contributor
timestamp: September 30, 2025, 07:19:10 pm
content: If you don't want extra data in the trace, then also disable the "Comparator 0" or you will get events every time address 0 is accessed.But all those things are kind of the point of the trace. If you just need UART - use UART.

author: peter-h
author_rank: Super Contributor
timestamp: September 30, 2025, 07:47:45 pm
content: This is weird. Some years ago I used PC Sampling to get a graph of where the CPU spends its time.I think I must have used it to get the data for thishttps://www.eevblog.com/forum/microcontrollers/freertos-where-should-the-cpu-be-spending-most-of-its-time/But now it breaks it. May be something in the clock config e.g. I am running SWV ITM at just 1MHz. But nothing I tried makes any difference.It looks like SWV ITM, and SWV Statistical Profiling, are mutually exclusive. For the former you must disable PC Sampling, for the latter you (obviously) need it. Jesus... But I now find statistical profiling is extremely flakey. It works but only over a very small address range; just the bottom 32k or so. Never mind.Another issue, which I found a long time ago, is that this button must appear like this, and then you click on it, and only then the SWV ITM works.When it is ready to receive the SWV data, it must look like thisWith both versions of the button, hovering over the button says Start TraceStupid software...This used to be pretty reliable in older versions of Cube IDE. I am now using the relatively old 1.14.1.EDIT: above now verified across 3 machines, win7-64 and win10. PC Sampling must be disabled for SWV ITM console to work, and the red button operation as described above.

author: peter-h
author_rank: Super Contributor
timestamp: October 01, 2025, 02:18:57 pm
content: And I see exactly the same behaviour if talking to the STLINK V3 MINIE, viaOpenOCD.And statistical profiling is dud. I can sometimes get some data, but only from the boot block (bottom 32k). That data looks correct, but the behaviour looks like the data collection is limited to the bottom 32k of the address space.This stuff is so weird.I found something useful: if you "detach" the SWV ITM Data Console window, so it looks like thisthen it comes up always ready and you don't need to fiddle about with the above red button!

author: ataradov
author_rank: Super Contributor
timestamp: October 01, 2025, 03:13:12 pm
content: It is likely that your sampling rate is too high and interface speed is too low. Trace data sinks will prioritize channels when they can't fit all the data.

author: peter-h
author_rank: Super Contributor
timestamp: October 01, 2025, 03:40:16 pm
content: Just tried it with the STLINK V3 MINIEwhich must be the fastest debugger ST do.Setting both the SWD speed to auto, and the SWO speed to auto (comes up at 12MHz, 12x faster than my usual setting), makes no difference. Enabling PC Sampling totally blocks the SWV ITM debugs.Sampling rate is the slowest available: 16384 cyclesBTW, one has to exit and restart Cube IDE after each change to the debug settings page, for it to be reflected in the config page of which the above is a small bit.

author: ataradov
author_rank: Super Contributor
timestamp: October 01, 2025, 03:47:35 pm
content: That's strange, but not surprising. Tracing tools are not that great even in the best case. Even really expensive ETM probes from Segger fail to give sufficient debug information when something goes wrong.

author: peter-h
author_rank: Super Contributor
timestamp: October 01, 2025, 03:52:19 pm
content: The statistical profiling function is almost useless no matter what I do. It did work a few years ago...Now, when I pause the target after some mins, all I see isand those functions are in the 32k boot block. This is after the CPU has run billions of instructions from the main FLASH. It is as if there was something in my main() (which is located just above base+32k) which blocks the data collection. That is very possibly where changes have been made since a few years ago, but there is so much stuff...So I put in 10M calls to some useless function which waggles a GPIO, and put this in various places in the startup code, to see where this function appears (or not) in the stats IOW at which point is the debug functionality getting disabled.Code:// todouint32_t fred=0;while (fred<10000000L){LED_On(LED6);LED_Off(LED6);fred++;}I was suspecting some Port B init disabled PB3 (the SWO pin) but putting the above loop (as a void function call) well above any port init, but it doesn't seem to be that. Instead, this delay function seems to be causing it:It looks like one cannot read CYCCNT  in one's code (yes, even just read it) if you want profiling to work. This function kills the profilingCode:// Delay for ms. Uses CPU clock counter CYCNT.// Uses two loops to prevent due to delay*SystemCoreClock being too big for uint32_t.// Max delay is uint32_t ms.// This is a precise delay. It uses special code to deal with uint32_t overflow.// DO NOT USE THIS before CYCCNT has been enabled!// If this function is called before the PLL is set up to wind up the CPU to 168MHz, the// delay will be 168/16 longer than the ms value, because the CPU starts up at 16MHz.void hang_around(uint32_t delay){volatile uint32_t max_count = SystemCoreClock/1000L; // 168M = 1 secvolatile uint32_t start_time;do{start_time = DWT->CYCCNT;while((DWT->CYCCNT-start_time) < max_count) ;// this counts millisecondsdelay--;} while (delay>0);}I have this code to set up CYCCNT and enable tracing (very old code)Code:// 1. Enable traceCoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT = 0;// start counter at zero// 2. Enable DWT cycle counter (for PC sampling)DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;// 3. Enable PC sampling in DWTDWT->CTRL |= DWT_CTRL_PCSAMPLENA_Msk;and tried putting it after the point where I think the tracing is getting disabled, to re-enable it, but it doesn't work.Replacing that delay function with this function fixes the problemCode:__attribute__((noinline))void B_hang_around(uint32_t delay){delay *= (SystemCoreClock/4100L);asm volatile ("1: subs %, %, #1 \n"" nop \n"" bne 1b \n":  "+l"(delay));}But not completely. I am now seeing data from FLASH above the boot block but very little. The problem seems to be that the data acquisition stops at about 4095 samples, which is nowhere near enough. But I see no config for this. Even setting the Cube IDE log buffer to 200MB makes no difference.

author: peter-h
author_rank: Super Contributor
timestamp: October 02, 2025, 01:06:21 pm
content: Unless someone comes up with a way to lift the ~4096 packet limit, I will give up on this. I have a working solution for SWV ITM debugs...FWIW I have reverted to this code for enabling CYCCNTCode:// Enable CYCCNT as a free running counter, for various timeouts where we// don't want to rely on interrupts.// Runs at SystemCoreClock Hz AFTER this is set up.CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT = 0;// start it at zeroDWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;EDIT1: switching to theOpenOCDdebugger mode (still using STLINK V3) briefly worked for both profiling and the SWO debug.Bloody hell! Well, for about an hour, then it all broke. I am testing concurrently on two machines, too... I've not been able to get anything working in the OpenOCD mode since. It appears that maybe the PC needs a reboot too, to recover the USB port into some working mode, or the debugger needs a power cycle. 100% definitely Cube IDE needs a restart after any change in the config (either of the two screens). This stuff is really shitty. Maybe the €1000+ Segger boxes are better.So I went back to STLINK GDB Server mode, to get SWV debugs coming out, because those are sometimes really needed. One could use a UART in a dumb polled mode for those, actually, but SWV can run a lot faster, and doesn't need a separate means of data reception, and I am already using all four pin-accessible UARTSs under interrupts.The OpenOCD mode, even with same speed settings, is much more reliable than the GDB Server mode, for SWV ITM debugs which crash with the latter after some minutes of continuous output. That may explain why I only ever got a little bit of statistical profiling data with GDB Server.One funny thing I noticed during when the profiling worked: some 40% of the time was spent in a function called _build_type() and the name was not recognised by Cube (normally you can flick on the names) whose base address was 0x1. This is nonsense; the code starts at 0x8000000. I would expect that much time to be spent in the RTOS Idle thread, as was found previously.EDIT2:Using a freshly rebooted win10 laptop I managed to use OpenOCD mode and grabbed this profiling, before it stopped:You can see the weird 0x1 address line 1. No idea what it means. The idle thread uses a WFI to reduce power, in vApplicationIdleHook. That may be confusing the data collection.Further testing confirms the thing stops working after one capture session, until thedebuggeris power cycled, and then you can do it againThere are issues with statistical profiling reliability. Testing suggests that with PC Sampling enabled, Cube leaks memory at around 100k bytes per second. It starts at around 700MB and when it reaches 1500MB it hangs. So if one just wants SWV ITM debugs, it is better to turn off PC Sampling. There seems to be no way to run Cube for hours with PC Sampling enabled.

author: aae30
author_rank: Contributor
timestamp: October 03, 2025, 11:32:18 pm
content: Did i read before that you're using some old version perhaps ? Could be, it is fixed in some current version.

author: peter-h
author_rank: Super Contributor
timestamp: October 04, 2025, 08:17:26 am
content: I am using 1.14.1.I tested later versions when they came out but AFAICT nothing was ever fixed, and 1.15.0 and 1.16.0 broke the debugger interface.ST don't really fix the code. They update mostly new device support.