2026-02-24 7:03 AM
Have been fighting ST-LINK_gdbserver over the last few days and am hoping that someone can shed some light.
I'm using JetBrains CLion with the ST-LINK gdb server. I have this configured in the "Debug Servers" setting. I want to get my ITM / printf calls to appear in my "MCU Console" window in CLion.
So I have my SWO Enabled, I specify my clock (400Mhz / STM32H743), my divider (1000, should give 400kbaud data coming out), and my TCP port for the SWO data to appear on (59999). I've overridden my `_write` function to push to the ITM_SendChar function.
I press debug, JetBrains compiles, runs the ST-LINK_gdbserver, passes in the correct flags, "-z 59999 -a 400000000 -b 1000 ", and the GDB then connects to the GDB port (61234 - default).
However I don't see any SWO output via ST-LINK_gdbserver.
If I run the STM32_Programmer_CLI in a separate terminal window with "./STM32_Programmer_CLI -c port=swd -swv freq=400 portnumber=0", it successfully connects to the MCU, configures the ITM on the Cortex core, and I can see the output fine. This at least rules out a hardware issue as I know my connections are good, and that the SWO trace is connected up as I'm expecting.
However this doesn't then allow me to run the gdbserver for me to step through the code properly.
It seems that ST-LINK_gdbserver, even if you pass the flags to enable SWO, it sets up the ST-Link device (I've got a ST-LINk/V2 ISOL) to read the Trace pin at the right baud rate, but then doesn't setup the MCU to enable the ITM module in the Cortex core. I've put a logic analyser on the pin and can't see any output from that pin when in this mode.
So, things I've tried:
1) Force-enabling the output in code. I manually set the ITM to output data from the code, rather than relying on the debugger to do so. This means that essentially I could just now pipe this pin into a USB serial adapter and use it there, but ideally I want to use the ST-Link device as it should be able to support this!
*(__IO uint32_t *) (0x5C001004) |= 0x00700000; // DBGMCU_CR D3DBGCKEN D1DBGCKEN TRACECLKEN
*(__IO uint32_t *) (0x5C004FB0) = 0xC5ACCE55; // SWTF_LAR
*(__IO uint32_t *) (0x5C014FB0) = 0xC5ACCE55; // TPIU_LAR
*(__IO uint32_t *) (0x5C003FB0) = 0xC5ACCE55; // SWO_LAR
// SWO current output divisor register
// value = (CPU Freq/sw speed )-1
// Check RM 0433 for this.
*(__IO uint32_t *) (0x5C003010) = (1000 - 1); // SWO_CODR =
*(__IO uint32_t *) (0x5C0030F0) = 0x00000002; // SWO_SPPR = NRZ
//Enable ITM input of SWO trace funnel
*(__IO uint32_t *) (0x5C004000) |= 0x00000001; // SWTF_CTRL
// Enable the ITM module
ITM->TCR = (ITM_TCR_ITMENA_Msk);
// Enable the trace register
ITM->TER = 0x1;
2) Running STM32_Programmer_CLI first. This then sets up the ITM variables internally on the STM32 part (with a divisor of 200, not 1000, so it gets 2Mbaud). I can then disconnect this and then use the ST-LINK_gdbserver and I now get some garbage data coming through (as CLion configures the ST-Link to expect 400kbaud). I can then update my config in CLion to say that it should be using a divisor of 200, and now I see actual data coming through. But this is only because STM32_Programmer_CLI has set up the MCU, not ST-LINK_gdbserver.
3) Running the ST-LINK_gdbserver from CLion for gdb to work, but then use wrapper script around the ST-LINK_gdbserver call to swap the TCP port it's using for SWV output. So the gdbserver actually is run with a port of 59998. However if I do "netcat localhost 59998", it connects to that port fine, ST-LINK_gdbserver logs that it's got a new connection, but it doesn't send any data across that connection. The gdbserver log shows:
[0.027] pollAndNotifyRun(): Stm32Device, pollAndNotify running...
[0.027] updateState(): SwvSrv state change: 0 -> 1
[0.027] WaitConnection(): Waiting for connection on port 59998...
--
[12.770] WaitConnection(): Accepted connection on port 59998...
[12.771] openConnectionInt(): SWV collect poll delay set to 17066µs for baudrate 400000Hz (buffer size 2048b)
[12.771] updateState(): SwvSrv state change: 1 -> 2
I can't find any documentation on what "SwvSrv state" is though.
If I don't do that port swap (where CLion shows the data after the trick with STM32_Programmer_CLI), the logfile output shows this "SwvSrv state" go to 4.
tail: /tmp/stgdb.log: file truncated
--
[0.029] pollAndNotifyRun(): Stm32Device, pollAndNotify running...
[0.029] updateState(): SwvSrv state change: 0 -> 1
[0.029] WaitConnection(): Waiting for connection on port 59999...
--
[0.491] WaitConnection(): Accepted connection on port 59999...
[0.491] openConnectionInt(): SWV collect poll delay set to 17066µs for baudrate 400000Hz (buffer size 2048b)
[0.491] updateState(): SwvSrv state change: 1 -> 2
[1.075] WaitConnection(): Accepted connection on port 61234...
--
[1.282] closeDevice(): Stm32Device, closeDevice() entry
[1.282] updateState(): SwvSrv state change: 2 -> 4
[1.282] handleTargetDeviceEvent(): GDB session, device event: 6
--
[11.099] pollAndNotifyRun(): Stm32Device, pollAndNotify running...
[11.099] openConnectionInt(): SWV collect poll delay set to 17066µs for baudrate 400000Hz (buffer size 2048b)
[11.099] updateState(): SwvSrv state change: 4 -> 2
[11.099] handleTargetDeviceEvent(): GDB session, device event: 7
--
[11.101] read(): <12> Rx: $c#63
[11.107] updateState(): SwvSrv state change: 2 -> 3
[11.107] handleTargetDeviceEvent(): GDB session, device event: 4
--
So I'm almost at the point of giving up with ST-Link and just using OpenOCD instead. What's the trick to setting ST-LINK_gdbserver to actually setup the MCU correctly?
I'd love to try using the STM32CubeMonitor as well, but that's got a separate bug with TCP not working on Ubuntu so that's annoyingly a no-go to view the data as well.
2026-02-24 9:50 AM
Using a logic analyser on the SWDIO / SWCLK lines, I can see that the STM32_Programmer_CLI is writing to the SWO configuration registers:
| 0x5c004fb0 | 0xc5acce55 |
| 0x5c004000 | 0x00000303 |
| 0x5c003fb0 | 0xc5acce55 |
| 0x5c003004 | 0x00000001 |
| 0x5c003010 | 0x000000c7 |
| 0x5c0030f0 | 0x00000002 |
| 0x5c003304 | 0x00000000 |
| 0xe0000fb0 | 0xc5acce55 |
| 0xe0000e80 | 0x0001000d |
But the ST-LINK_gdbserver doesn't write any of those registers.