Author Topic: STLINK debugger configures some 32F4 target registers e.g. SWO trace macrocell  (Read 3106 times)

0 Members and 5 Guests are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
This also refers a bit
https://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 going into the 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. this



produces 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 this

Code: [Select]

/*
 * 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 [in]     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[0U].u32 == 0UL)
    {
      __NOP();
    }
    ITM->PORT[0U].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 code

Code: [Select]
// Enable debug clocks in RCC
void SWO_Init(void)
{
    // Enable DBGMCU clock (usually already enabled)
    // Enable trace and debug blocks
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
   
    // Get current system clock frequency
    uint32_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) - 1
    uint32_t swo_freq = 2000000; // 2 MHz
    uint32_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 formatter
    TPI->FFCR = 0x00;
   
    // Unlock ITM (Instrumentation Trace Macrocell)
    ITM->LAR = 0xC5ACCE55;
   
    // Enable ITM
    ITM->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 100kHz ;) Why 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 this



so the debugger actually 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 execute after the debugger has configured it, so now Cube needs to be configured to match your software config.

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 here
https://community.st.com/t5/stm32-mcus-boards-and-hardware/how-to-enable-swo-with-st-link-utility-on-stm32f407g-disc1/td-p/228896
https://gist.github.com/mofosyne/178ad947fdff0f357eb0e03a42bcef5c

But 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 here
https://community.st.com/t5/stm32-mcus-boards-and-hardware/how-to-enable-swo-with-st-link-utility-on-stm32f407g-disc1/td-p/228896
suggests 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.
« Last Edit: September 30, 2025, 01:08:53 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 
The following users thanked this post: thm_w

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12470
  • Country: us
    • Personal site
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.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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 here
https://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#M27761
so 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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12470
  • Country: us
    • Personal site
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[0].u32 = 11; ITM->PORT[1].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.
« Last Edit: September 30, 2025, 03:17:22 pm by ataradov »
Alex
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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 machines :)

The 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 see continuous data 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 ticked



BTW 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...
« Last Edit: September 30, 2025, 05:39:34 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17795
  • Country: fr
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
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
Well, bloody hell, it looks like it is this



Must be disabled!
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12470
  • Country: us
    • Personal site
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.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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 this
https://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 this



With both versions of the button, hovering over the button says Start Trace ;) Stupid 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.
« Last Edit: October 01, 2025, 06:56:35 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
And I see exactly the same behaviour if talking to the STLINK V3 MINIE, via OpenOCD.

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 this



then it comes up always ready and you don't need to fiddle about with the above red button!
« Last Edit: October 01, 2025, 03:33:47 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12470
  • Country: us
    • Personal site
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.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
Just tried it with the STLINK V3 MINIE



which 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 cycles



BTW, 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.
« Last Edit: October 01, 2025, 03:48:04 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12470
  • Country: us
    • Personal site
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.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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 is



and 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: [Select]
// todo
uint32_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 [heavily] in one's code (yes, even just read it) if you want profiling to work. This function kills the profiling

Code: [Select]

// 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 sec
volatile uint32_t start_time;

do
{
start_time = DWT->CYCCNT;
while((DWT->CYCCNT-start_time) < max_count) ; // this counts milliseconds
delay--;
} while (delay>0);

}

I have this code to set up CYCCNT and enable tracing (very old code)

Code: [Select]
// 1. Enable trace
CoreDebug->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 DWT
DWT->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 problem

Code: [Select]
__attribute__((noinline))
void B_hang_around(uint32_t delay)
{
  delay *= (SystemCoreClock/4100L);

  asm volatile (
    "1: subs %[delay], %[delay], #1 \n"
    "   nop \n"
    "   bne 1b \n"
    : [delay] "+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.
« Last Edit: October 02, 2025, 07:16:17 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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 CYCCNT

Code: [Select]
// 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 zero
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

EDIT1: switching to the OpenOCD debugger 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 the debugger is power cycled, and then you can do it again ;)

There 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.

« Last Edit: October 03, 2025, 10:01:55 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline aae30

  • Contributor
  • Posts: 18
  • Country: fi
Did i read before that you're using some old version perhaps ? Could be, it is fixed in some current version.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6016
  • Country: gb
  • Doing electronics since the 1960s...
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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->