¿Cómo imprimo mensajes de depuración en la consola gdb con la placa de descubrimiento STM32 usando GDB, OpenOCD y arm-none-eabi-gcc?

Estoy programando una placa de descubrimiento STM32 Cortex M0 (32F0308DISCOVERY) usando OpenOCD, arm-none-eabi-gcc y gdb. Me preguntaba si hay alguna forma sencilla de registrar mensajes de depuración a través de SWD. He leído sobre la opción de semialojamiento, pero esto parece requerir la extracción de newlib u otras bibliotecas grandes. (Solo hay flash de 64k disponible). ¿Hay alguna forma más liviana de registrar texto a través de SWD, o usar el UART es la única opción práctica?

Te sugiero que pruebes la opción de semihosting. A modo de indicación, las bibliotecas que ofrece CooCox (un entorno gratuito de Windows Cortex-M) para M3/4 son bastante minimalistas, su transferencia de un solo byte es de 17 instrucciones de ensamblaje. Una reconstrucción de un proyecto antiguo (STM32F4) con su semihospedaje y -O0 agregó 48 bytes al tamaño del código.
Es posible que no tenga su enlazador eliminando el código no utilizado. En cuanto a las alternativas, el repositorio github de texane para controlar las herramientas stlink tiene un esquema de envío de correo simple, aunque aún no lo he probado.

Respuestas (1)

Gracias por los consejos, markt y chris-stratton. La opción de semihosting resultó ser bastante sencilla. Logré encontrar la fuente de un par de rutinas de registro simples que pueden enviar mensajes a la consola OpenOCD. Los publicaré aquí ya que (i) requirieron algunas modificaciones para funcionar y (ii) creo que esta información no es muy fácil de encontrar para las personas que recién comienzan.

Primero, el código D aquí se adapta fácilmente para proporcionar la siguiente función C:

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");
}

Ejemplo de llamar a send_command para escribir una cadena en la consola de OpenOCD:

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);

En segundo lugar, la función putChar dada en los comentarios aquí funciona bien, excepto que tuve que agregar un '#' antes de 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"
    );
}

Para ver el resultado de estas funciones, primero inicio OpenOCD, luego me conecto usando arm-none-eabi-gdb de la siguiente manera:

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

Tenga en cuenta que los mensajes aparecen en la salida estándar del proceso OpenOCD, no en la consola GDB.

Hay un error, el tamaño de() debe ser strlen().
Gracias usuario107642. De hecho, es posible usar sizeof aquí si 's' es una matriz en lugar de un puntero, así que lo modifiqué de esa manera.
¡Gran respuesta! También puedes escribir putchartan simple comovoid putchar(char c) { send_command(3,&c); }
El "sizeof" contará el \0 final de la cadena, mientras que strlen no lo hará. Si openocd solo imprime en stdout y una ventana de terminal xterm, eso probablemente no hará una diferencia notable ya que la terminal probablemente lo ignorará. Pero si termina poniendo cosas en un archivo, creo que se sorprenderá al encontrar esos ceros ahí. ¿O el protocolo especifica que necesita enviar cadenas con el terminador final?
Ah, buen punto user242579. He agregado un '-1' para tener en cuenta el \0 final.