14
\$\begingroup\$

I'm programming an STM32 Cortex M0 discovery board (32F0308DISCOVERY) using OpenOCD, arm-none-eabi-gcc, and gdb. I was wondering if there is any straightforward way to log debug messages over SWD. I've read about the semihosting option, but this seems to require pulling in newlib or other big libraries. (There's only 64k flash available.) Is there any more lightweight way to log text over SWD, or is using the UART the only practical option?

\$\endgroup\$
  • 1
    \$\begingroup\$ I suggest you try the semihosting option. By way of indication, the libraries offered by CooCox (a free Windows Cortex-M environment) for M3/4 are pretty minimalist, their single-byte transfer is 17 assembly instructions. A rebuild of an old (STM32F4) project with their semihosting and -O0 added 48 bytes to the code size. \$\endgroup\$ – markt Jan 16 '15 at 3:32
  • \$\begingroup\$ You may not have your linker stripping unused code. As for alternatives, texane's github repo for driving stlink tools has a simple mailboxing scheme, though I have't tried it out yet. \$\endgroup\$ – Chris Stratton Jan 16 '15 at 4:04
14
\$\begingroup\$

Thanks for the pointers, markt and chris-stratton. The semihosting option turned out to be quite straightforward. I managed to find the source for a couple of simple logging routines that can send messages to the OpenOCD console. I'll post them here since (i) they required some modification to work and (ii) I think this info is not super easy to find for people who are just starting out.

First, the D code here is easily adapted to provide the following C function:

void send_command(int command, void *message)
{
   asm("mov r0, %[cmd];"
       "mov r1, %[msg];"
       "bkpt #0xAB"
         :
         : [cmd] "r" (command), [msg] "r" (message)
         : "r0", "r1", "memory");
}

Example of calling send_command to write a string to the OpenOCD console:

const char s[] = "Hello world\n";
uint32_t m[] = { 2/*stderr*/, (uint32_t)s, sizeof(s)/sizeof(char) - 1 };
send_command(0x05/* some interrupt ID */, m);

Second, the putChar function given in the comments here works fine, except that I had to add a '#' before 0x03:

void put_char(char c)
{
    asm (
    "mov r0, #0x03\n"   /* SYS_WRITEC */
    "mov r1, %[msg]\n"
    "bkpt #0xAB\n"
    :
    : [msg] "r" (&c)
    : "r0", "r1"
    );
}

To look at the output from these functions, I first start up OpenOCD, then connect using arm-none-eabi-gdb as follows:

target remote localhost:3333
monitor arm semihosting enable
monitor reset halt
load code.elf
continue

Note that the messages appear on the stdout of the OpenOCD process, not at the GDB console.

\$\endgroup\$
  • 1
    \$\begingroup\$ There's a bug, the sizeof() should be strlen(). \$\endgroup\$ – user107642 Apr 21 '16 at 19:33
  • 1
    \$\begingroup\$ Thanks user107642. In fact it is possible to use sizeof here if 's' is an array rather than a pointer, so I've modified it that way. \$\endgroup\$ – foldl May 2 '16 at 10:18
  • \$\begingroup\$ Great answer! You could also write putchar as simple as void putchar(char c) { send_command(3,&c); } \$\endgroup\$ – mvds Dec 10 '16 at 12:18
  • 1
    \$\begingroup\$ The "sizeof" will count the trailing \0 of the string, while strlen will not. If openocd just prints to stdout and an xterm terminal window, that probably won't make a noticable difference as the terminal will probably ignore it. But if you end up putting things in a file, I think you'll be surprised to find those zeroes in there. Or does the protocol specify that you need to send strings with the trailing terminator? \$\endgroup\$ – user242579 Oct 8 '17 at 17:00
  • \$\begingroup\$ Ah, good point user242579. I've added a '-1' to take into account the trailing \0. \$\endgroup\$ – foldl Dec 4 '17 at 14:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.