chakokuのブログ(rev4)

日々のごった煮ブログです

STM32F401CC用にMicroPythonをビルドするがサイズがでかくてFlashに乗らない

先人の情報を参考に、普通にNUCLEO_F401REとしてビルドしてみる。使った環境は、WSL上のUbuntu

$ uname -a
Linux DESKTOP-TRNV8F8 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

sudo apt-get install libffi-dev gcc-arm-none-eabi

cd ~/lang/up/stm32/src
git clone https://github.com/micropython/micropython

cd micropython
make -C mpy-cross
cd ports/stm32
make BOARD=NUCLEO_F401RE

なぜか分からないがエラーになった。HAL_Driverが無いと怒られているような。。

LINK build-NUCLEO_F401RE/firmware.elf
arm-none-eabi-ld: cannot find build-NUCLEO_F401RE/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.o: No such file or directory
arm-none-eabi-ld: cannot find build-NUCLEO_F401RE/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.o: No such file or directory
arm-none-eabi-ld: cannot find build-NUCLEO_F401RE/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.o: No such file or directory
arm-none-eabi-ld: cannot find build-NUCLEO_F401RE/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.o: No such file or directory
略

同じコマンドをもう一度実行したらHALのライブラリ?をコンパイルして、ビルドまで終わった。

$ make BOARD=NUCLEO_F401RE
*略*
LINK build-NUCLEO_F401RE/firmware.elf
   text    data     bss     dec     hex filename
 301872      12   37408  339292   52d5c build-NUCLEO_F401RE/firmware.elf
GEN build-NUCLEO_F401RE/firmware0.bin
GEN build-NUCLEO_F401RE/firmware1.bin
GEN build-NUCLEO_F401RE/firmware.dfu
GEN build-NUCLEO_F401RE/firmware.hex

本来なら、ST-LINK等で、dfuかbinを焼くのだろう。。が、、UARTのポートが違ってるので修正が必要。

boards/NUCLEO_F401CCのディレクトリを作って、mpconfigboard.hを修正

$ diff ../NUCLEO_F401RE/mpconfigboard.h ./mpconfigboard.h
23a24,25
> #define MICROPY_HW_UART1_TX     (pin_A9)
> #define MICROPY_HW_UART1_RX     (pin_A10)
26,30c28,29
< #define MICROPY_HW_UART6_TX     (pin_C6)
< #define MICROPY_HW_UART6_RX     (pin_C7)
< // UART 2 connects to the STM32F103 (STLINK) on the Nucleo board
< // and this is exposed as a USB Serial port.
< #define MICROPY_HW_UART_REPL        PYB_UART_2
---
>
> #define MICROPY_HW_UART_REPL        PYB_UART_1

いけるかどうかわからないけど、以下でビルドしてみる

$ make BOARD=NUCLEO_F401CC
$ make BOARD=NUCLEO_F401CC

  *略*
LINK build-NUCLEO_F401CC/firmware.elf
   text    data     bss     dec     hex filename
 301900      12   37408  339320   52d78 build-NUCLEO_F401CC/firmware.elf
GEN build-NUCLEO_F401CC/firmware0.bin
GEN build-NUCLEO_F401CC/firmware1.bin
GEN build-NUCLEO_F401CC/firmware.dfu
GEN build-NUCLEO_F401CC/firmware.hex

エラー出ずにビルドできた。次はST-Linkを使って上記ファームを焼く

かつて、ST-Linkを使ってgdbとか動かしていたが、細かいことを忘れたので、ST-Link Utilityを使ってみる。
http://tri-s.world.coocan.jp/tri_S_Reference/ST_LINK/HowToUse_ST_LINK.pdf
STSW-LINK004|デザイン/サポート|STM32, STM8ファミリはSTの32bit/8bit汎用マイクロコントローラ製品


ST-Link Utilityを使って ST-Link経由でSTVAL-DRONE01上のSTM32F401CCにファームを焼こうとしたら、サイズがでかすぎると怒られた

20:43:48 : ST-LINK SN : 1847000000000000000
20:43:48 : V2J29S7
20:43:48 : Connected via SWD.
20:43:48 : SWD Frequency = 4,0 MHz.
20:43:48 : Connection mode : Normal.
20:43:48 : Debug in Low Power mode enabled.
20:43:48 : Device ID:0x423 
20:43:48 : Device flash Size : 256KBytes
20:43:48 : Device family :STM32F401xB/C
20:46:51 : [firmware.hex] opened successfully.
                  Address Ranges [0x08000000 0x080039D0] [0x08020000 0x08066188] 
20:46:51 : [firmware.hex] checksum : 0x01BD7F18 
20:47:43 : File size is bigger than the flash memory size.

Flashサイズが256KBなのだが、MicroPythonファームが294KBであった。はみ出ている。
機能削減して減らすか、最適化コンパイルして減らせられないものだろうか。。

ポーティングしているボードにはリソースの小さいのもあり、STM32L073RZT6の場合、flashメモリが192KBらしい。
NUCLEO_L073RZ/mpconfigboard.h
この場合、以下の機能が外されている。

#define MICROPY_EMIT_THUMB          (0)
#define MICROPY_EMIT_INLINE_THUMB   (0)
#define MICROPY_OPT_COMPUTED_GOTO   (0)
#define MICROPY_PY_BUILTINS_COMPLEX (0)
#define MICROPY_PY_GENERATOR_PEND_THROW (0)
#define MICROPY_PY_MATH             (0)
#define MICROPY_PY_FRAMEBUF         (0)
#define MICROPY_PY_USOCKET          (0)
#define MICROPY_PY_NETWORK          (0)
#define MICROPY_PY_ONEWIRE          (0)
#define MICROPY_PY_STM              (0)
#define MICROPY_PY_PYB_LEGACY       (0)
#define MICROPY_PY_UHEAPQ           (0)
#define MICROPY_PY_UTIMEQ           (0)
#define MICROPY_PY_MACHINE_BITSTREAM (0)
#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
#define MICROPY_HW_ENABLE_ADC       (0)

上記を参考にmpconfigboard.hを定義すればサイズが縮小化されるのでは?と期待

サイズ確認のため普通にビルドしてみる

make BOARD=NUCLEO_L073RZ

以下の通り、201KBに収まっているように思える。

$ make BOARD=NUCLEO_L073RZ
  *略*
LINK build-NUCLEO_L073RZ/firmware.elf
   text    data     bss     dec     hex filename
 194784      68   11748  206600   32708 build-NUCLEO_L073RZ/firmware.elf
GEN build-NUCLEO_L073RZ/firmware.bin
GEN build-NUCLEO_L073RZ/firmware.dfu
GEN build-NUCLEO_L073RZ/firmware.hex

このファームだとエラーなく焼くことができた。(が、MicroPythonのREPL用のUARTポートが違っているのでこのままではPCとつながらない)

そのままdisable定義をCopyするだけだとビルド中にundefineシンボルとかでエラーになる。ソースは変更したくないので、エラーになった場合はenableに戻しながらコンパイルを通した。結果、以下のサイズにしかならず、これではサイズがでかいのでファームが焼けない。

$ make BOARD=NUCLEO_F401CC

LINK build-NUCLEO_F401CC/firmware.elf
   text    data     bss     dec     hex filename
 256088       8   37140  293236   47974 build-NUCLEO_F401CC/firmware.elf
GEN build-NUCLEO_F401CC/firmware0.bin
GEN build-NUCLEO_F401CC/firmware1.bin
GEN build-NUCLEO_F401CC/firmware.dfu
GEN build-NUCLEO_F401CC/firmware.hex

というわけで、build-NUCLEO_L073RZをベースにUARTのポートだけを書き換えてビルドしてみる
しかし、このやり方はSTM32のマイコンシリーズの系統を無視したビルドで、本来は以下のように定義されているL072/L073シリーズ用のピンマッピング情報やライブラリを無理にF401に適用している。だから??ピンアサイン(ピン多重化された機能の定義)とか無茶苦茶になっている可能性がある。

MCU_SERIES = l0
CMSIS_MCU = STM32L073xx
AF_FILE = boards/stm32l072_af.csv
LD_FILES = boards/stm32l072xz.ld boards/common_basic.ld

REPL用UARTの定義だけ修正してビルドした。先ほどのビルドと同様にFlashに乗りそうなサイズに収まった。

$ make BOARD=NUCLEO_L073RZ_KAI

LINK build-NUCLEO_L073RZ_KAI/firmware.elf
   text    data     bss     dec     hex filename
 194784      68   11748  206600   32708 build-NUCLEO_L073RZ_KAI/firmware.elf
GEN build-NUCLEO_L073RZ_KAI/firmware.bin
GEN build-NUCLEO_L073RZ_KAI/firmware.dfu
GEN build-NUCLEO_L073RZ_KAI/firmware.hex

フラッシュは焼けた。あとは、、MicroPythonが正しく動いているかどうか。ST-Link + OpenOCDの組み合わせでデバッグもできるので、あまりビビる必要はないけど。。
コネクタP7からREPLのシリアルが出ているはずなのだが、応答がない。というわけで、、デバッグ
昔の記事と手元のメモを読み返しながら、、openocdを起動、ST-Link経由でターゲットと接続

$ ./bin-x64/openocd.exe -f ./scripts/interface/stlink-v2.cfg  -f ./scripts/target/stm32f4x.cfg
Open On-Chip Debugger 0.10.0
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 u
se 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results mi
ght differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
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 v29 API v2 SWIM v7 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.152424
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
$ ./arm-none-eabi-gdb.exe

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x00000000 in ?? ()

(gdb) monitor reset halt
Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz
adapter speed: 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0802296c msp: 0x20004ff8

(gdb) info register
r0             0x0      0
r1             0x0      0
r2             0x0      0
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x0      0x0
lr             0x0      0
pc             0x0      0x0
xPSR           0x0      0

読み取れる値が0ばっかりなのだが、、ターゲットとの接続エラーになっている??
OpenOCD側のログ

target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0802296c msp: 0x20004ff8

gdbからもう一度haltをしてみる

(gdb) monitor reset halt
Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz
adapter speed: 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0802296c msp: 0x20004ff8

(gdb) x/32w 0
 0x0:    0x020004ff8      0x0802296d      0x0801dc01      0x0801dbf1
0x10:   0x00000000      0x00000000      0x00000000      0x00000000
0x20:   0x00000000      0x00000000      0x00000000      0x0801dc03
0x30:   0x00000000      0x00000000      0x0801df01      0x0801df31
0x40:   0x08022871      0x0801dc05      0x0801dc11      0x08022871
0x50:   0x08022871      0x0801dc65      0x0801dc75      0x0801dc85
0x60:   0x08022871      0x0801facd      0x0801fae1      0x0801fb01
0x70:   0x08022871      0x08022871      0x0801dcd3      0x0801dc97


(gdb) info register
r0             0x20004ff8       536891384
r1             0x0      0
r2             0x0      0
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x20004ff8       0x20004ff8
lr             0xffffffff       -1
pc             0x8022972        0x8022972
xPSR           0x1000000        16777216

(gdb) x/32w $pc
0x8022972:      0x4a0c490c      0xe0034b0d      0x31046808      0x32046010
0x8022982:      0xd3f9429a      0x49092000      0xe0014a0a      0x31046008
0x8022992:      0xd3fb4291      0xff6df7ff      0xf7fa4620      0x4ff8ff7a
0x80229a2:      0xf8e02000      0x00000802      0x00442000      0x00442000
0x80229b2:      0x06282000      0x60042000      0x60866045      0x464160c7
0x80229c2:      0x46496101      0x46516141      0x46596181      0x466161c1
0x80229d2:      0x46696201      0x46686241      0xb4024770      0x08494671
0x80229e2:      0x56090049      0x448e0049      0x4770bc02      0xb40246c0

ぱっと見た目にはそれらしいコードを実行しようとしているように思えるのだが・・Flashをダンプ、逆アセンブルしている段階で、シンボルがないしソースもないので、これだけでは分からん。シンボル情報のロードが必要か。

(gdb) disas $pc,+32
Dump of assembler code from 0x8022972 to 0x8022992:
=> 0x08022972:  ldr     r1, [pc, #48]   ; (0x80229a4)
   0x08022974:  ldr     r2, [pc, #48]   ; (0x80229a8)
   0x08022976:  ldr     r3, [pc, #52]   ; (0x80229ac)
   0x08022978:  b.n     0x8022982
   0x0802297a:  ldr     r0, [r1, #0]
   0x0802297c:  adds    r1, #4
   0x0802297e:  str     r0, [r2, #0]
   0x08022980:  adds    r2, #4
   0x08022982:  cmp     r2, r3
   0x08022984:  bcc.n   0x802297a
   0x08022986:  movs    r0, #0
   0x08022988:  ldr     r1, [pc, #36]   ; (0x80229b0)
   0x0802298a:  ldr     r2, [pc, #40]   ; (0x80229b4)
   0x0802298c:  b.n     0x8022992
   0x0802298e:  str     r0, [r1, #0]
   0x08022990:  adds    r1, #4
End of assembler dump.

continueでしばらく実行してCtrl-Cでスタックを確認するとどうもおかしい。暴走しているのか?

(gdb) cont
Continuing.
The target is not running when halt was requested, stopping GDB.

Program received signal SIGINT, Interrupt.
0x0802297a in ?? ()
=> 0x0802297a:  00 00   movs    r0, r0
(gdb) where
#0  0x0802297a in ?? ()
#1  0xffffffff in ?? ()
#2  0xffffffff in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

ロードからもう一度やり直す(デバイスビジネス開拓団様の記事を参考)

$ /usr/local/GNUToolsARMEmbedded/4.8_2013q4/bin/arm-none-eabi-gdb.exe firmware.elf
GNU gdb (GNU Tools for ARM Embedded Processors) 7.6.0.20131129-cvs
Copyright (C) 2013 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=i586-mingw32 --target=arm-none-eabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\cygwin64\home\<user_name>\lang\up\stm32\src\micropython\ports\s
tm32\build-NUCLEO_L073RZ_KAI\firmware.elf...done.

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x0802296c in Reset_Handler ()

(gdb) monitor reset halt
Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz
adapter speed: 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0802296c msp: 0x20004ff8, semihosting

(gdb) i r
r0             0x0      0
r1             0x0      0
r2             0x0      0
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x20004ff8       0x20004ff8
lr             0xffffffff       -1
pc             0x802296c        0x802296c <Reset_Handler>
xPSR           0x1000000        16777216

(gdb) load
Loading section .isr_vector, size 0xc0 lma 0x8000000
Loading section .text, size 0x2f818 lma 0x80000c0
Loading section .ARM, size 0x8 lma 0x802f8d8
Loading section .data, size 0x44 lma 0x802f8e0
Start address 0x802296c, load size 194852
Transfer rate: 28 KB/sec, 10255 bytes/write.

(gdb) hbreak main
Function "main" not defined.
Make hw breakpoint pending on future shared library load? (y or [n]) y

Hardware assisted breakpoint 1 (main) pending.

(gdb) where
#0  0x0802296c in Reset_Handler ()

(gdb) cont
Continuing.
The target is not running when halt was requested, stopping GDB.

Program received signal SIGINT, Interrupt.
0x0802296c in Reset_Handler ()

(gdb) where
#0  0x0802296c in Reset_Handler ()
(gdb)

ビルドの情報を詳細に表示するためと、デバッグ情報を付け加えるために、以下でビルド

make BOARD=NUCLEO_L073RZ_KAI V=1 DEBUG=1

なお、、リンカオプションもL073RZのままだとメモリマップもおかしいので、configを作り直した。

■参考URL

micropythonをはじめよう(STM32F401編) - 寝台急行はまなす
ARM Mbed その5 OpenOCD+GDB | デバイスビジネス開拓団