Como usar o GDB (Gnu Debugger) e o OpenOCD para depuração de microcontroladores - a partir do terminal?

A maneira padrão (de baixo custo) de programar microcontroladores ARM é usar o Eclipse com uma cadeia de ferramentas complexa conectada a ele. Definitivamente, o Eclipse tem seus méritos, mas eu gostaria de me sentir independente desse IDE. Gostaria de descobrir o que acontece nos bastidores quando construo (compile - link - flash) meu software e quando executo uma sessão de depuração. Para obter um entendimento mais profundo, seria maravilhoso executar todo o procedimento na linha de comando.

Nota: Estou usando o Windows 10. de 64 bits, mas a maioria das coisas explicadas aqui também se aplica aos sistemas Linux. Por favor, abra todos os terminais de comando com direitos de administrador. Isso pode economizar muitos problemas.

1. Construindo o software

A primeira 'missão' é cumprida. Agora sou capaz de compilar e vincular meu software a um binário.bin e um.elf imagem através da linha de comando. A chave do sucesso foi descobrir onde o Eclipse coloca seus arquivos de criação para um projeto específico. Depois de saber onde eles estão, tudo o que você precisa fazer é abrir um terminal de comando e digite oGNU make comando.

Você não precisa mais do Eclipse para isso! Especialmente se você puder ler (e entender) o makefile e ajustá-lo às suas necessidades quando o projeto avançar.

Observe que encontrei as ferramentas GNU (compilador, vinculador, utilitário de criação, GDB, ...) na seguinte pasta, depois de instalar o SW4STM32 (System Workbench for STM32):

C:\Ac6\SystemWorkbench\plugins\fr.ac6.mcu.externaltools.arm-none.win32_1.7.0.201602121829\tools\compiler\

Em seguida, criei uma nova pasta no meu disco rígido e copiei todas essas ferramentas GNU nele:

C:\Apps\AC6GCC
           |-> arm-none-eabi
           |-> bin
           '-> lib

E adiciono essas entradas à "variável do caminho do ambiente":

 - C:\Apps\AC6GCC\bin
 - C:\Apps\AC6GCC\lib\gcc\arm-none-eabi\5.2.1

Huray, agora eu tenho todas as ferramentas GNU em funcionamento no meu sistema! Eu coloquei o seguintebuild.bat na mesma pasta que o arquivomakefile:

@echo off
echo.
echo."--------------------------------"
echo."-           BUILD              -"
echo."--------------------------------"
echo.

make -j8 -f makefile all

echo.

A execução deste arquivo bat deve fazer o trabalho! Se tudo correr bem, você recebe um.bin e um.elf arquivo binário como resultado da compilação.

2. Piscando e depurando o firmware

O passo seguinte natural é atualizar o firmware para o chip e iniciar uma sessão de depuração. No Eclipse, é apenas um 'clique em um botão' - pelo menos se o Eclipse estiver configurado corretamente para o seu microcontrolador. Mas o que acontece nos bastidores? Eu li (parte de) a Dissertação de Mestrado de Dominic Rath - desenvolvedor do OpenOCD. Você pode encontrá-lo aqui:http://openocd.net/ . Isto é o que eu aprendi:

O Eclipse inicia o software OpenOCD quando você clica no ícone 'debug'. O Eclipse também fornece alguns arquivos de configuração para o OpenOCD - como o OpenOCD sabe como se conectar ao seu microcontrolador. 'Como conectar' não é uma coisa trivial. O OpenOCD precisa encontrar o driver USB adequado para conectar ao adaptador JTAG (por exemplo, STLink). O adaptador JTAG e o driver USB geralmente são fornecidos pelo fabricante do seu chip (por exemplo, STMicroelectronics). O Eclipse também entrega um arquivo de configuração ao OpenOCD que descreve as especificações do microcontrolador. Quando o OpenOCD souber tudo isso, ele poderá estabelecer uma conexão JTAG confiável com o dispositivo de destino.

O OpenOCD inicia dois servidores. O primeiro é um servidor Telnet na porta TCP 4444. Ele fornece acesso ao OpenOCD CLI (Command Line Interface). Um cliente Telnet pode conectar e enviar comandos para o OpenOCD. Esses comandos podem ser um simples 'stop', 'run', 'set breakpoint', ...

Esses comandos podem ser suficientes para depurar seu microcontrolador, mas muitas pessoas já estavam familiarizadas com o Gnu Debugger (GDB). É por isso que o OpenOCD também inicia um servidor GDB na porta TCP 3333. Um cliente GDB pode se conectar a essa porta e começar a depurar o microcontrolador!

O Gnu Debugger é um software de linha de comando. Muitas pessoas preferem uma interface visual. É exatamente isso que o Eclipse faz. O Eclipse inicia um cliente GDB que se conecta ao OpenOCD - mas tudo está oculto para o usuário. O Eclipse fornece uma interface gráfica que interage com o cliente GDB nos bastidores.

Eu fiz uma figura para explicar todas essas coisas:

>> Iniciando o OpenOCD

Consegui iniciar o OpenOCD a partir da linha de comando. Eu vou explicar como

Primeiro, verifique se o seu programador STLink-V2 JTAG está instalado corretamente. Você pode testar a instalação com a "ferramenta STLink Utility" da STMicroelectronics. Tem uma interface gráfica agradável e você simplesmente clica no botão conectar.Em seguida, baixe o software OpenOCD executável neste site:http://gnutoolchains.com/arm-eabi/openocd/ . Instale-o e coloque-o em uma pasta no seu disco rígido, como "C: \ Apps \".

Abra um terminal de comando e inicie o OpenOCD. Você precisará fornecer ao OpenOCD alguns arquivos de configuração, para que ele saiba onde procurar seu microcontrolador. Normalmente, você precisa fornecer um arquivo de configuração que descreve o programador JTAG e um arquivo de configuração que define seu microcontrolador. Passe esses arquivos para o OpenOCD com o-f argumento na linha de comando. Você também precisará conceder ao OpenOCD acesso aoscripts pasta, passando-o com o-s argumento. É assim que inicio o OpenOCD no meu computador com a linha de comando:

> "C:\Apps\OpenOCD-0.9.0-Win32\bin\openocd" -f "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts\interface\stlink-v2.cfg" -f "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts\target\stm32f7x.cfg" -s "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts"

Se você iniciou o OpenOCD corretamente (com os argumentos corretos), ele será iniciado com a seguinte mensagem:

Open On-Chip Debugger 0.9.0 (2015-08-15-12:41)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v24 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.231496
Info : stm32f7x.cpu: hardware has 8 breakpoints, 4 watchpoints
Info : accepting 'gdb' connection on tcp/3333
Info : flash size probed value 1024

Observe que a janela do seu terminal está bloqueada. Você não pode mais digitar comandos. Mas isso é normal. O OpenOCD está sendo executado em segundo plano e bloqueia o terminal. Agora você tem duas opções para interagir com o OpenOCD: você inicia uma sessão Telnet em outro terminal e faz logon na porta TCPlocalhost:4444, para que você possa dar comandos ao OpenOCD e receber feedback. Ou você inicia uma sessão do cliente GDB e a conecta à porta TCPlocalhost:3333.

>> Iniciando uma sessão Telnet para interagir com o OpenOCD

É assim que você inicia uma sessão Telnet para interagir com o programa OpenOCD em execução:

> dism /online /Enable-Feature /FeatureName:TelnetClient

> telnet 127.0.0.1 4444

Se funcionar bem, você receberá a seguinte mensagem no seu terminal:

Open On-Chip Debugger
> ..

E você está pronto para enviar comandos ao OpenOCD! Mas agora vou mudar para a sessão GDB, já que essa é a maneira mais conveniente de interagir com o OpenOCD.

>> Iniciando uma sessão do cliente GDB para interagir com o OpenOCD

Abra outra janela do terminal e digite o seguinte comando:

> "C:\Apps\AC6GCC\bin\arm-none-eabi-gdb.exe"

Este comando simplesmente inicia oarm-none-eabi-gdb.exe Cliente GDB. Se tudo der certo, o GDB será iniciado com a seguinte mensagem:

    GNU gdb (GNU Tools for ARM Embedded Processors) 7.10.1.20151217-cvs
    Copyright (C) 2015 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
    For help, type "help".
    Type "apropos word" to search for commands related to "word".
    (gdb)..

Agora conecte esse cliente GDB ao servidor GDB dentro do OpenOCD:

    (gdb) target remote localhost:3333

Agora você está conectado ao OpenOCD! É bom saber: se você deseja usar um comando OpenOCD nativo (como faria em uma sessão Telnet), basta preceder o comando com a palavra-chavemonitor. Dessa forma, o servidor GDB dentro do OpenOCD não manipulará o comando, mas passará para o deamon nativo do OpenOCD.

Então, agora é hora de redefinir o chip, apagá-lo e interrompê-lo:

    (gdb) monitor reset halt
       target state: halted
       target halted due to debug-request, current mode: Thread
       xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
    (gdb) monitor halt

    (gdb) monitor flash erase_address 0x08000000 0x00100000
       erased address 0x08000000 (length 1048576) in 8.899024s (115.069 KiB/s)
    (gdb) monitor reset halt
       target state: halted
       target halted due to debug-request, current mode: Thread
       xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
    (gdb) monitor halt

O chip está pronto para receber algumas instruções de nós. Primeiro, diremos ao chip que suas seções de flash de 0 a 7 (todas as seções de flash do meu chip de 1Mb) não devem ser protegidas:

    (gdb) monitor flash protect 0 0 7 off

    (gdb) monitor flash info 0
       #0 : stm32f7x at 0x08000000, size 0x00100000, buswidth 0, chipwidth 0
            #  0: 0x00000000 (0x8000 32kB) not protected
            #  1: 0x00008000 (0x8000 32kB) not protected
            #  2: 0x00010000 (0x8000 32kB) not protected
            #  3: 0x00018000 (0x8000 32kB) not protected
            #  4: 0x00020000 (0x20000 128kB) not protected
            #  5: 0x00040000 (0x40000 256kB) not protected
            #  6: 0x00080000 (0x40000 256kB) not protected
            #  7: 0x000c0000 (0x40000 256kB) not protected

Em seguida, paro o chip novamente. Só pra ter certeza..

    (gdb) monitor halt

Finalmente eu entrego o binário.elf arquivo para GDB:

    (gdb) file C:\\..\\myProgram.elf
       A program is being debugged already.
       Are you sure you want to change the file? (y or n) y
       Reading symbols from C:\..\myProgram.elf ...done.

Agora é o momento da verdade. Peço ao GDB para carregar esse binário no chip. Dedos cruzados:

    (gdb) load
       Loading section .isr_vector, size 0x1c8 lma 0x8000000
       Loading section .text, size 0x39e0 lma 0x80001c8
       Loading section .rodata, size 0x34 lma 0x8003ba8
       Loading section .init_array, size 0x4 lma 0x8003bdc
       Loading section .fini_array, size 0x4 lma 0x8003be0
       Loading section .data, size 0x38 lma 0x8003be4
       Error finishing flash operation

Infelizmente, não foi bem sucedido. Recebo a seguinte mensagem no OpenOCD:

    Error: error waiting for target flash write algorithm
    Error: error writing to flash at address 0x08000000 at offset 0x00000000

EDIT: problema de hardware corrigido.

Aparentemente, foi um problema de hardware. Eu nunca pensei que meu chip estivesse com defeito, pois o carregamento do binário no chip com a ferramenta STLink Utility funcionou sem problemas. Somente o OpenOCD estava reclamando e dando erros. Então, naturalmente, culpei o OpenOCD - e não o próprio chip. Veja minha resposta abaixo para mais detalhes.

EDIT: Maneira alternativa elegante de piscar o chip - usando makefile!

Como o problema foi resolvido, agora vou me concentrar em uma maneira alternativa de executar o flash e a depuração do chip. Eu acredito que isso é realmente interessante para a comunidade!

Você deve ter notado que eu usei os comandos cmd do Windows para executar todas as etapas necessárias. Isso pode ser automatizado em um arquivo em lotes. Mas há uma maneira mais elegante: automatizar tudo em um makefile! Mr./Mss. Othane sugeriu o seguinte makefile para seu Cortex-M? lasca. Suponho que o procedimento para um chip Cortex-M7 seja muito semelhante:

            #################################################
            #        MAKEFILE FOR BUILDING THE BINARY       #
            #        AND EVEN FLASHING THE CHIP!            #
            # Author: Othane                                #
            #################################################

    # setup compiler and flags for stm32f373 build 
    SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 


    CROSS_COMPILE ?= arm-none-eabi- 
    export CC = $(CROSS_COMPILE)gcc 
    export AS = $(CROSS_COMPILE)gcc -x assembler-with-cpp 
    export AR = $(CROSS_COMPILE)ar 
    export LD = $(CROSS_COMPILE)ld 
    export OD   = $(CROSS_COMPILE)objdump 
    export BIN  = $(CROSS_COMPILE)objcopy -O ihex 
    export SIZE = $(CROSS_COMPILE)size 
    export GDB = $(CROSS_COMPILE)gdb 


    MCU = cortex-m4 
    FPU = -mfloat-abi=hard -mfpu=fpv4-sp-d16 -D__FPU_USED=1 -D__FPU_PRESENT=1 -DARM_MATH_CM4 
    DEFS = -DUSE_STDPERIPH_DRIVER -DSTM32F37X -DRUN_FROM_FLASH=1 -DHSE_VALUE=8000000 
    OPT ?= -O0  
    MCFLAGS = -mthumb -mcpu=$(MCU) $(FPU) 


    export ASFLAGS  = $(MCFLAGS) $(OPT) -g -gdwarf-2 $(ADEFS) 
    CPFLAGS += $(MCFLAGS) $(OPT) -gdwarf-2 -Wall -Wno-attributes -fverbose-asm  
    CPFLAGS += -ffunction-sections -fdata-sections $(DEFS) 
    export CPFLAGS 
    export CFLAGS += $(CPFLAGS) 


    export LDFLAGS  = $(MCFLAGS) -nostartfiles -Wl,--cref,--gc-sections,--no-warn-mismatch $(LIBDIR) 


    HINCDIR += ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/CMSIS/Include/ \ 
        ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/CMSIS/Device/ST/STM32F37x/Include/ \ 
        ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/STM32F37x_StdPeriph_Driver/inc/ \ 
        ./ 
    export INCDIR = $(patsubst %,$(SELF_DIR)%,$(HINCDIR)) 




    # openocd variables and targets 
    OPENOCD_PATH ?= /usr/local/share/openocd/ 
    export OPENOCD_BIN = openocd 
    export OPENOCD_INTERFACE = $(OPENOCD_PATH)/scripts/interface/stlink-v2.cfg 
    export OPENOCD_TARGET = $(OPENOCD_PATH)/scripts/target/stm32f3x_stlink.cfg 


    OPENOCD_FLASH_CMDS = '' 
    OPENOCD_FLASH_CMDS += -c 'reset halt' 
    OPENOCD_FLASH_CMDS += -c 'sleep 10'  
    OPENOCD_FLASH_CMDS += -c 'stm32f1x unlock 0' 
    OPENOCD_FLASH_CMDS += -c 'flash write_image erase $(PRJ_FULL) 0 ihex' 
    OPENOCD_FLASH_CMDS += -c shutdown 
    export OPENOCD_FLASH_CMDS 


    OPENOCD_ERASE_CMDS = '' 
    OPENOCD_ERASE_CMDS += -c 'reset halt' 
    OPENOCD_ERASE_CMDS += -c 'sleep 10'  
    OPENOCD_ERASE_CMDS += -c 'sleep 10'  
    OPENOCD_ERASE_CMDS += -c 'stm32f1x mass_erase 0' 
    OPENOCD_ERASE_CMDS += -c shutdown 
    export OPENOCD_ERASE_CMDS 


    OPENOCD_RUN_CMDS = '' 
    OPENOCD_RUN_CMDS += -c 'reset halt' 
    OPENOCD_RUN_CMDS += -c 'sleep 10' 
    OPENOCD_RUN_CMDS += -c 'reset run' 
    OPENOCD_RUN_CMDS += -c 'sleep 10'  
    OPENOCD_RUN_CMDS += -c shutdown 
    export OPENOCD_RUN_CMDS 


    OPENOCD_DEBUG_CMDS = '' 
    OPENOCD_DEBUG_CMDS += -c 'halt' 
    OPENOCD_DEBUG_CMDS += -c 'sleep 10' 


    .flash: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_FLASH_CMDS) 


    .erase: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_ERASE_CMDS) 


    .run: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_RUN_CMDS) 


    .debug: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_DEBUG_CMDS) 

Caro Sr. / Sr. Othane, você poderia explicar como usar esse makefile para as seguintes etapas:

Construa o binário a partir do código fontePiscar o chip

Eu sei algumas noções básicas sobre makefiles, mas seu makefile está realmente indo bem fundo. Você parece usar alguns recursos do utilitário GNU make. Por favor, dê-nos mais algumas explicações e eu concederei o bônus ;-)

------------------------------

questionAnswers(5)

yourAnswerToTheQuestion