A force de suivre les vidéos de Sieur Rancune j'ai fini par craquer pour une poignée de bluepill. Parce qu'il est conseillé sur tous les interweb d'utiliser arm-none-eabi-gcc pour programmer ces charmantes bestioles j'ai décidé d'utiliser clang/llvm livré de base.
Si allumer une led constitue le 'Hello World!' de l'électronique, j'ai découvert le niveau -1 en lisant cette série de billets: placer une valeur particulière dans un registre.
Le fichier core.S requiert 3 modifications mineures:
-
adapter le type de cpu
-
modifier la casse de la fonction reset_handler
-
renommer une variable
$ fetch -o - https://raw.githubusercontent.com/WRansohoff/STM32F0_minimal/master/core.S | sed -e 's/cortex-m0/cortex-m3/' -e 's/reset_handler/Reset_Handler/g' -e 's/_estack/__end_stack/' > core.S $ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -c core.S -o core.o
Pour que mon binaire final soit adapté à ma carte, je dois récupérer un 'linker script':
$ fetch https://github.com/STM32-base/STM32-base/archive/refs/heads/master.zip
$ tar xf master.zip STM32-base-master/linker
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf
C'est pour se conformer à ce script qu'il faut modifier le fichier core.S.
Avant d'envoyer le fichier sur la carte, il faut le convertir:
$ objcopy -O binary main.elf main.bin
$ du -Ah main.bin
384M main.bin
Là je crois qu'on dépasse les bornes des limites: ça fait beaucoup de méga pour placer une valeur dans un registre. Si je regarde mon fichier elf:
$ readelf -l main.elf | grep 0x
Entry point 0x8000009
LOAD 0x010000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x10000
LOAD 0x020000 0x20000000 0x20000000 0x00600 0x00600 RW 0x10000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
ARM_EXIDX 0x01001c 0x0800001c 0x0800001c 0x00000 0x00000 R 0x4
Je peux me passer de GNU_STACK avec l'option -z nognustack:
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -z nognustack -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf
$ readelf -l main.elf | grep 0x
Entry point 0x8000009
LOAD 0x010000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x10000
LOAD 0x020000 0x20000000 0x20000000 0x00600 0x00600 RW 0x10000
ARM_EXIDX 0x01001c 0x0800001c 0x0800001c 0x00000 0x00000 R 0x4
Mais cela ne corrige pas le problème. J'ai fini par comprendre qu'une section n'avait pas le bon type:
$ readelf -S main.elf.OK | grep ._user_heap_stack
[ 6] ._user_heap_stack NOBITS 20000000 020000 000600 00 WA 0 0 1
$ readelf -S main.elf.KO | grep ._user_heap_stack
[ 7] ._user_heap_stack PROGBITS 20000000 020000 000600 00 WA 0 0 1
La correction la plus simple que j'ai trouvé est de passer cette section en 'NOLOAD':
$ grep ._user_heap_stack STM32-base-master/linker/common.ld
._user_heap_stack (NOLOAD) : {
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -z nognustack -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf
$ readelf -S main.elf | grep ._user_heap_stack
[ 8] ._user_heap_stack NOBITS 20000000 020000 000600 00 WA 0 0 1
$ objcopy -O binary main.elf main.bin
$ du -Ah main.bin
512B main.bin
C'est déjà plus raisonnable.
Il est temps de programmer la carte. J'utilise donc un adaptateur usb st link v2 et devel/openocd avec la configuration suivante:
$ cat openocd.cfg
source [find interface/stlink.cfg]
transport select hla_swd
source [ find target/stm32f1x.cfg]
reset_config none separate
Je connecte la bluepill à l'adaptateur, je branche l'adaptateur et paf:
$ openocd -f openocd.cfg -c "init"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : clock speed 1000 kHz
Info : STLINK V2J17S4 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.137032
Warn : UNEXPECTED idcode: 0x2ba01477
Error: expected 1 of 1: 0x1ba01477
Pour qu'openocd daigne utiliser mon adaptateur je dois lui dire qu'il est "de confiance" (kofkof):
$ cat openocd.cfg
set CPUTAPID 0x2ba01477
source [find interface/stlink.cfg]
transport select hla_swd
source [ find target/stm32f1x.cfg]
reset_config none separate
$ openocd -f openocd.cfg -c "init"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : clock speed 1000 kHz
Info : STLINK V2J17S4 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.138581
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
On va facilement trouver des exemples d'utilisation conjointe d'openocd et de gdb. Pas de bol pour moi, le debugger llvm n'est pas compatible, d'après https://stackoverflow.com/questions/36287351/how-to-setup-lldb-with-openocd-and-jtag-board:
"from what we were researching, it is not possible to debug remote (bare-metal!) targets with lldb without writing extra code."
Et autant dire que l'extra code en python, je passe.
Je vais donc utiliser l'interface "telnet" d'openocd:
$ echo 'reset halt' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset halt
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x1fffe61c msp: 0x20000180
$ echo 'stm32f1x mass_erase 0' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> stm32f1x mass_erase 0
device id = 0x20036410
flash size = 25616kbytes
stm32x mass erase complete
$ md5 main.bin
MD5 (main.bin) = 09c6d874e631d40370b70a7bcc120f41
$ echo 'flash write_image erase main.bin 0x8000000' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> flash write_image erase main.bin 0x8000000
auto erase enabled
wrote 1024 bytes from file main.bin in 0.087373s (11.445 KiB/s)
$ echo 'reset run' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset run
$ echo 'halt' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> halt
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0x00000003 pc: 0x012fff1e msp: 0xe59efff8
$ echo 'reg' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reg
===== arm v7m registers
(0) r0 (/32): 0x008fec14
(1) r1 (/32): 0x00000000
(2) r2 (/32): 0x00000000
(3) r3 (/32): 0x00000000
(4) r4 (/32): 0x00000000
(5) r5 (/32): 0x00000000
(6) r6 (/32): 0x00000000
(7) r7 (/32): 0xdeadbeef
(8) r8 (/32): 0x00000000
(9) r9 (/32): 0x00000000
(10) r10 (/32): 0x00000000
(11) r11 (/32): 0x00000000
(12) r12 (/32): 0x00000000
(13) sp (/32): 0x20005000
(14) lr (/32): 0xffffffff
(15) pc (/32): 0x08000012
(16) xPSR (/32): 0x01000000
(17) msp (/32): 0x20005000
(18) psp (/32): 0x00000000
(20) primask (/1): 0x00
(21) basepri (/8): 0x00
(22) faultmask (/1): 0x00
(23) control (/3): 0x00
===== Cortex-M DWT registers
Pour être sûr de mon coup, j'essaie avec les valeurs suivantes:
- OxCAFECAFE: MD5 (main.bin) = 45e3b36afd8ebd902675c26e804c21b5
- OxBABEBABE: MD5 (main.bin) = 4f03cd6b4a29ca80dc28fc49401cb1c5
- Ox12345678: MD5 (main.bin) = 908fc5196f85ade8321d1d951d8035f7
A moi les leds qui clignottent !
Commentaires: #15