chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

STM32F401CC用にMicroPythonをgdbでデバッグする->なんとかREPLが動いた

背景:
STEVAL-DRONE01上にSTM32F401CCが搭載されていて、普通にCとかRUSTとかで開発するにはサイズの問題が出ないと思うが、センサのデータ処理や姿勢制御アルゴリズムについて試行錯誤したいのでSTM32F401CC上でMicroPythonを動かしたいと思っている。が、マイコンに内蔵されているFlashMemoryが256KBのため、普通にビルドしたMicroPythonではサイズが大きすぎて搭載できない。だから、、他の省リソースマイコン用のMicroPythonをベースにSTM32F401CC用のMicroPythonをカスタマイズして動かそうとしている。。が、、動かない。そこで、ST-LINK経由でOpenOCD + GDBデバッグ
以下のコマンドでOpenOCDとgdbを起動

./bin-x64/openocd.exe -f ./scripts/interface/stlink-v2.cfg  -f ./scripts/target/stm32f4x.cfg
/usr/local/GNUToolsARMEmbedded/4.8_2013q4/bin/arm-none-eabi-gdb.exe ./build-NUCLEO_L073RZ_KAI2/firmware.elf

gdbのセットアップとロード

target remote localhost:3333
monitor reset halt
load
hbreak stm32_main
cont

stm32_mainで一旦止めて、Cソース行の単位でステップ実行

320         MICROPY_BOARD_STARTUP();
(gdb) n
320         MICROPY_BOARD_STARTUP();
(gdb) n
367         HAL_InitTick(TICK_INT_PRIORITY);
(gdb) n
370         SystemClock_Config();
(gdb) n

Breakpoint 4, stm32_main (reset_mode=0) at main.c:312
312         SCB->VTOR = MICROPY_HW_VTOR;
(gdb) n
316         SCB->CCR |= SCB_CCR_STKALIGN_Msk;
(gdb) n
320         MICROPY_BOARD_STARTUP();
(gdb) n
367         HAL_InitTick(TICK_INT_PRIORITY);
(gdb) n
370         SystemClock_Config();
(gdb) n

Breakpoint 4, stm32_main (reset_mode=0) at main.c:312
312         SCB->VTOR = MICROPY_HW_VTOR;
(gdb)

スタートアップの所でグルグル回っているようだ。ステップ実行で調べると、FLASH->ACRの値を変更しようとしてハードフォールトが発生している。多分番地が正しく設定されていないからでは?と推測

(gdb) n
109         FLASH->ACR |= FLASH_ACR_LATENCY;
(gdb) n
HardFault_Handler () at stm32_it.c:213
213         __asm volatile (

FLASHのが多分レジスタ用構造体の開始番地で->ACRでオフセットの番地を計算しているのだろうと推測。FLASHはマクロか何かで置換されているのか、シンボル不明になっている。

104     void SystemClock_Config(void) {
105         // Enable power control peripheral
106         __HAL_RCC_PWR_CLK_ENABLE();
107
108         // Set flash latency to 1 because SYSCLK > 16MHz
(gdb) l
109         FLASH->ACR |= FLASH_ACR_LATENCY;
110
111         // Enable the 16MHz internal oscillator
112         RCC->CR |= RCC_CR_HSION;
113         while (!(RCC->CR & RCC_CR_HSIRDY)) {
114         }
115
116         // Use HSI16 and the PLL to get a 32MHz SYSCLK
117         RCC->CFGR = 1 << RCC_CFGR_PLLDIV_Pos | 1 << RCC_CFGR_PLLMUL_Pos;
118         RCC->CR |= RCC_CR_PLLON;
(gdb) p/x FLASH
No symbol "FLASH" in current context.

(gdb) disas $pc,+16
Dump of assembler code from 0x8023912 to 0x8023922:
=> 0x08023912 <SystemClock_Config+14>:  ldr     r2, [pc, #88]   ; (0x802396c <SystemClock_Config+104>)
   0x08023914 <SystemClock_Config+16>:  ldr     r1, [r2, #0]
   0x08023916 <SystemClock_Config+18>:  movs    r0, #1
   0x08023918 <SystemClock_Config+20>:  orrs    r1, r0
   0x0802391a <SystemClock_Config+22>:  str     r1, [r2, #0]
   0x0802391c <SystemClock_Config+24>:  ldr     r2, [r3, #0]
   0x0802391e <SystemClock_Config+26>:  orrs    r2, r0
   0x08023920 <SystemClock_Config+28>:  str     r2, [r3, #0]
End of assembler dump.
(gdb)

アセンブルを見てもイマイチどれがどう対応しているのか不明。だから、、nextiで機械語単位でステップをかけて落ちた所の操作対象レジスタが誤った番地だろうと推測。

アドレス: 0x08023914を実行した時点でハードフォールトになったので、この命令でしくじっていると判断。

(gdb) nexti
0x08023914      109         FLASH->ACR |= FLASH_ACR_LATENCY;
   0x08023912 <SystemClock_Config+14>:  16 4a   ldr     r2, [pc, #88]   ; (0x802396c <SystemClock_Conf
ig+104>)
=> 0x08023914 <SystemClock_Config+16>:  11 68   ldr     r1, [r2, #0]
   0x08023916 <SystemClock_Config+18>:  01 20   movs    r0, #1
   0x08023918 <SystemClock_Config+20>:  01 43   orrs    r1, r0
   0x0802391a <SystemClock_Config+22>:  11 60   str     r1, [r2, #0]
(gdb) i r
r0             0x0      0
r1             0x0      0
r2             0x40022000       1073881088
r3             0x40021000       1073876992
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            0xe000ed00       -536810240
sp             0x2000ffb8       0x2000ffb8
lr             0x8022c8f        134360207
pc             0x8023914        0x8023914 <SystemClock_Config+16>
xPSR           0x1000000        16777216

(gdb) nexti
HardFault_Handler () at stm32_it.c:213
213         __asm volatile (
=> 0x08022fd8 <HardFault_Handler+0>:    70 46   mov     r0, lr
   0x08022fda <HardFault_Handler+2>:    c0 08   lsrs    r0, r0, #3
   0x08022fdc <HardFault_Handler+4>:    ef f3 08 80     mrs     r0, MSP
   0x08022fe0 <HardFault_Handler+8>:    01 d3   bcc.n   0x8022fe6 <HardFault_Handler+14>
   0x08022fe2 <HardFault_Handler+10>:   ef f3 09 80     mrs     r0, PSP
   0x08022fe6 <HardFault_Handler+14>:   85 e7   b.n     0x8022ef4 <HardFault_C_Handler>

推測だが、間接アドレッシングで、r2で示される番地にr1の値を入れると。r2の値が0x40022000 なので、
この値がFLASHレジスタのベースアドレスと解釈してビルドされているのだろう。多分。

=> 0x08023914 <SystemClock_Config+16>:  11 68   ldr     r1, [r2, #0]

STM32L073のFLASHレジスタの番地と、STM32F401のレジスタの番地が違ってるのではなかろうか。。

STM32L0x3 の仕様書を確認すると、0x4002_2000からFLASH Registerの領域で、オフセット0の番地が、FLASH_ACRとなっている。だから、STM32L073としての動作は正しい。一方、STM32F401CCの場合、FLASH InterfaceRegisterの領域は、0x4002 3C00 である。だから、間違った領域に値を書こうとしている。ちなみに、0x4002_2000はAHB用のレジスタ空間だけど、該当の番地にはレジスタが存在しない。だからアクセス異常になってリセットがかかったのだろう。これらのアドレス解決はソースを見ないと確実には分からないが、build用のマイコンシリーズ定義に従いifdefで切り替えられていると思う。マイコンシリーズ定義は、STM32L073を指定する、l0 (エルゼロ)を設定しているので、レジスタ群の番地がSTM32L073として解決されていると思う。悩みポイントとしては、マイコンシリーズ定義を本来のf0(エフゼロ)にしてしまうと、高機能関数群が一度に入ってくるので、サイズが大きくなってしまう。切り離しもむつかしい(中途半端に切り離すとシンボル未定義エラーになる)

やりたいこと:
サイズが大きくなるようなモリモリの機能はいらないから、レジスタマップだけはF401に合わせたい。だけど、レジスタマップと機能はペアで実装されるものだから、切り離して実装するのは無理だろう。。多分。

今回問題になっている、SystemClock_Config (file:powercontrolboot.c)のソースを確認すると、STM32L0のifdefが存在し、やはりマイコン種別として間違ったルーチンが呼び出されいる。STM32F4のマクロが有効になった状態でビルドされるようにしないと、正しい初期化等ができない。

#elif defined(STM32L0)

void SystemClock_Config(void) {
    // Enable power control peripheral
    __HAL_RCC_PWR_CLK_ENABLE();

    // Set flash latency to 1 because SYSCLK > 16MHz
    FLASH->ACR |= FLASH_ACR_LATENCY;
    // Set flash latency to 1 because SYSCLK > 24MHz
    FLASH->ACR = (FLASH->ACR & ~0x7) | 0x1;

マイコンシリーズの異なるSTM32L072RZをベースに、STM32F101CCに持っていくアプローチはif defが異なりすぎてなかなかまともにビルドできないので、STM32F401REから機能を削ってサイズを減らすアプローチに変更した。
この場合、エラーが出たら、機能を有効化すればよいので、ビルドエラーは容易に回避可能であるが、やはり、F4シリーズの場合ベースのビルドでは機能モリモリでサイズが大きくなる傾向が強く、ifdefで機能を削る程度ではあまり効果が得られない。ということで、、機能を付けたり減らしたりという程度ではなかなか小サイズのMicroPythonを作り出すのは難しいことが分かった。(MicroPython自体、機能を自由に選択してマイコンFlashサイズにフィットさせるような考えでは作られていないので)

$ make BOARD=NUCLEO_F401CC V=1 DEBUG=1
     *略*
arm-none-eabi-ld: build-NUCLEO_F401CC/firmware.elf section `.text' will not fit in region `FLASH_TEXT'
arm-none-eabi-ld: region `FLASH_TEXT' overflowed by 150800 bytes
arm-none-eabi-ld: build-NUCLEO_F401CC/modpyb.o:(.rodata.pyb_module_globals_table+0x7c): undefined reference to `pyb_flash_type'
make: *** [Makefile:714: build-NUCLEO_F401CC/firmware.elf] Error 1
$ make BOARD=NUCLEO_F401CC V=1

arm-none-eabi-size build-NUCLEO_F401CC/firmware.elf
   text    data     bss     dec     hex filename
 254968      12   37140  292120   47518 build-NUCLEO_F401CC/firmware.elf
GEN build-NUCLEO_F401CC/firmware0.bin
arm-none-eabi-objcopy -O binary -j .isr_vector build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware0.bin
GEN build-NUCLEO_F401CC/firmware1.bin
arm-none-eabi-objcopy -O binary -j .text -j .data -j .ARM build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware1.bin
GEN build-NUCLEO_F401CC/firmware.dfu
python3 ../../tools/dfu.py -D 0x0483:0xDF11 -b 0x08000000:build-NUCLEO_F401CC/firmware0.bin -b 0x08020000:build-NUCLEO_F401CC/firmware1.bin build-NUCLEO_F401CC/firmware.dfu
GEN build-NUCLEO_F401CC/firmware.hex
arm-none-eabi-objcopy -O ihex build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware.hex

ST-Link Utilityでファームは焼けた

21:23:51 : ST-LINK SN : 18470000000000000
21:23:51 : V2J29S7
21:23:51 : Connected via SWD.
21:23:51 : SWD Frequency = 4,0 MHz.
21:23:51 : Connection mode : Normal.
21:23:51 : Debug in Low Power mode enabled.
21:23:52 : Device ID:0x423 
21:23:52 : Device flash Size : 256KBytes
21:23:52 : Device family :STM32F401xB/C
21:24:01 : [firmware.hex] opened successfully.
                  Address Ranges [0x08000000 0x080039D0] [0x08005000 0x0803FA34] 
21:24:01 : [firmware.hex] checksum : 0x017CD635 
21:24:15 : Memory programmed in 7s and 47ms.

焼くには焼いたが、、動かしてみるとやっぱりおかしい。デバッグ情報付いていないので詳細は分からないが、再起動を繰り返している印象。またどこかでアドレスの不一致か、あるいは、Flashメモリの使い方のまずさか?により異常が発生しているようだ。アセンブラコードを頼りにトレースするのか!?

最低、シンボルは見えるようなので、、おおよそどこらへんにいるかはわかりそうだ。

(gdb) disas $pc,+32
Dump of assembler code from 0x8026110 to 0x8026130:
=> 0x08026110 <stm32_main+12>:  str     r3, [r4, #8]
   0x08026112 <stm32_main+14>:  ldr     r3, [r4, #20]
   0x08026114 <stm32_main+16>:  orr.w   r3, r3, #512    ; 0x200
   0x08026118 <stm32_main+20>:  sub     sp, #32
   0x0802611a <stm32_main+22>:  str     r3, [r4, #20]
   0x0802611c <stm32_main+24>:  mov     r6, r0
   0x0802611e <stm32_main+26>:  bl      0x8026c08 <powerctrl_check_enter_bootloader>
   0x08026122 <stm32_main+30>:  ldr     r3, [pc, #504]  ; (0x802631c <stm32_main+536>)
   0x08026124 <stm32_main+32>:  ldr     r2, [r3, #0]
   0x08026126 <stm32_main+34>:  orr.w   r2, r2, #512    ; 0x200
   0x0802612a <stm32_main+38>:  str     r2, [r3, #0]
   0x0802612c <stm32_main+40>:  ldr     r2, [r3, #0]
   0x0802612e <stm32_main+42>:  orr.w   r2, r2, #1024   ; 0x400
End of assembler dump.
(gdb) where
#0  0x08026110 in stm32_main ()
#1  0x0802c8b2 in .bss_zero_entry ()
#2  0x0802c8b2 in .bss_zero_entry ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

機械語単位でステップ実行してみると、init_flash_fsで異常が発生して再起動しているようだ。Flash上のファイルシステムが正しく初期化できないのだろう。

0x0802627e in stm32_main ()
=> 0x0802627e <stm32_main+378>: ff f7 c7 fe     bl      0x8026010 <init_flash_fs>
(gdb)

Breakpoint 1, 0x08026110 in stm32_main ()
=> 0x08026110 <stm32_main+12>:  a3 60   str     r3, [r4, #8]
(gdb)

init_flash_fsにHardware Breadpointを設定して再実行、

(gdb) i b
Num     Type           Disp Enb Address    What
1       hw breakpoint  keep y   0x08026110 <stm32_main+12>
        breakpoint already hit 7 times
(gdb) disable 1
(gdb) hb init_flash_fs
Hardware assisted breakpoint 2 at 0x8026010
(gdb) cont
Continuing.

Breakpoint 2, 0x08026010 in init_flash_fs ()
=> 0x08026010 <init_flash_fs+0>:        03 28   cmp     r0, #3
(gdb) disas $pc,+32
Dump of assembler code from 0x8026010 to 0x8026030:
=> 0x08026010 <init_flash_fs+0>:        cmp     r0, #3
   0x08026012 <init_flash_fs+2>:        push    {r4, lr}
   0x08026014 <init_flash_fs+4>:        mov     r4, r0
   0x08026016 <init_flash_fs+6>:        bne.n   0x802601c <init_flash_fs+12>
   0x08026018 <init_flash_fs+8>:        bl      0x8026f10 <factory_reset_create_filesystem>
   0x0802601c <init_flash_fs+12>:       ldr     r0, [pc, #52]   ; (0x8026054 <init_flash_fs+68>)
   0x0802601e <init_flash_fs+14>:       movw    r1, #1906       ; 0x772
   0x08026022 <init_flash_fs+18>:       bl      0x801e384 <mp_vfs_mount_and_chdir_protected>
   0x08026026 <init_flash_fs+22>:       cmn.w   r0, #19
   0x0802602a <init_flash_fs+26>:       bne.n   0x802604c <init_flash_fs+60>
   0x0802602c <init_flash_fs+28>:       cmp     r4, #3
   0x0802602e <init_flash_fs+30>:       bne.n   0x802603a <init_flash_fs+42>
End of assembler dump.
(gdb) nexti
0x08026012 in init_flash_fs ()
=> 0x08026012 <init_flash_fs+2>:        10 b5   push    {r4, lr}

最後の手段として、、FLASHを無効化にするオプションを有効にして再度ビルド、サイズもきちきち収まって、REPLが動いた。

MPY: soft reboot
MicroPython v1.16-243-g8c4ba575f-dirty on 2021-08-29; NUCLEO-F401CC with STM32F401CC
Type "help()" for more information.
>>> import gc
>>> gc.mem_free()
46000

>>> help('modules')
__main__          micropython       uhashlib       uselect
_onewire          pyb                     uio                ustruct
_uasyncio         uarray                ujson             usys
builtins            ubinascii            umachine       utime
cmath             ucollections       uos                  uzlib
gc                   uctypes              urandom
math              uerrno                ure
Plus any modules on the filesystem
>>>

こうやってみると、_uasyncio、_onewire、uhashlib、ujson、ure、uselect、uzlib等もいらないと思うけど、、
これらが切りはせるのかは不明

Flashが無いことになっているので、、uos.mkdir()などを実行するとエラーになる。ハングしないのでよく作りこまれていますね。。

>>> uos.mkdir('/hoge')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 19] ENODEV

GPIOの番号とかずれてるかもしれないので、I2CやSPIを動かすにはしばらくかかるかもしれませんが、インタープリタマイコン操作できるのは楽だ~。特に使い慣れないマイコンの場合、周辺I/O等の試行錯誤が繰り返しできるので理解もはかどるはず。

忘れないように最終設定を記録
file:boards/NUCLEO_F401CC/mpconfigboard.h
(UARTのPINを修正、機能無効化を定義、それ以外は、STM32F401REのまま。だから、、Pin配置が多分違っていてSPI, I2C等は動かないと思われる)

#define MICROPY_HW_BOARD_NAME       "NUCLEO-F401CC"
#define MICROPY_HW_MCU_NAME         "STM32F401CC"

#define MICROPY_EMIT_THUMB          (0)
#define MICROPY_EMIT_INLINE_THUMB   (0)
#define MICROPY_OPT_COMPUTED_GOTO   (0)
#define MICROPY_PY_GENERATOR_PEND_THROW (0)
#define MICROPY_PY_MACHINE_BITSTREAM (0)

#define MICROPY_PY_FRAMEBUF         (0)
#define MICROPY_PY_USOCKET          (0)
#define MICROPY_PY_NETWORK          (0)
#define MICROPY_PY_STM              (0)
#define MICROPY_PY_PYB_LEGACY       (0)
#define MICROPY_PY_UHEAPQ           (0)
#define MICROPY_PY_UTIMEQ           (0)

//# trial (8/29 22:09)
//#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1)
#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)

#define MICROPY_HW_HAS_SWITCH       (1) 
#define MICROPY_HW_HAS_FLASH        (0)     // trial (8/29 22:43)
#define MICROPY_HW_HAS_LCD          (0)    // trial (8/29 22:43)
#define MICROPY_HW_ENABLE_RTC       (1)
#define MICROPY_HW_ENABLE_SDCARD    (0)     // trial (8/29 22:43)
#define MICROPY_HW_ENABLE_SERVO     (0)    // trial (8/29 22:43)
#define MICROPY_HW_ENABLE_USB       (0)    // trial (8/29 22:43)
#define MICROPY_HW_ENABLE_DAC       (0)    // trial (8/29 22:43)


// HSE is 8MHz, HSI is 16MHz CPU freq set to 84MHz
// Default source for the clock is HSI.
// For revisions of the board greater than C-01, HSE can be used as a
// clock source by removing the #define MICROPY_HW_CLK_USE_HSE line
#define MICROPY_HW_CLK_USE_HSI (1)

#if MICROPY_HW_CLK_USE_HSI
#define MICROPY_HW_CLK_PLLM (16)
#else
#define MICROPY_HW_CLK_PLLM (8)
#endif
#define MICROPY_HW_CLK_PLLN (336)
#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV4)
#define MICROPY_HW_CLK_PLLQ (7)

// UART config
#define MICROPY_HW_UART1_TX     (pin_A9)
#define MICROPY_HW_UART1_RX     (pin_A10)
#define MICROPY_HW_UART2_TX     (pin_A2)
#define MICROPY_HW_UART2_RX     (pin_A3)
// 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_1
#define MICROPY_HW_UART_REPL_BAUD   115200

// I2C buses
#define MICROPY_HW_I2C1_SCL (pin_B8)        // Arduino D15, pin 3 on CN10
#define MICROPY_HW_I2C1_SDA (pin_B9)        //         D14, pin 5 on CN10
#define MICROPY_HW_I2C2_SCL (pin_B10)       // Arduino D6,  pin 25 on CN10
#define MICROPY_HW_I2C2_SDA (pin_B3)        // Arduino D3,  pin 31 on CN10
#define MICROPY_HW_I2C3_SCL (pin_A8)        // Arduino D7,  pin 23 on CN10
#define MICROPY_HW_I2C3_SDA (pin_C9)        //              pin  1 on CN10

// SPI buses
#define MICROPY_HW_SPI1_NSS     (pin_A15)   //              pin 17 on CN7
#define MICROPY_HW_SPI1_SCK     (pin_A5)    // Arduino D13, pin 11 on CN10
#define MICROPY_HW_SPI1_MISO    (pin_A6)    // Arduino D12, pin 13 on CN10
#define MICROPY_HW_SPI1_MOSI    (pin_A7)    // Arduino D11, pin 15 on CN10

#define MICROPY_HW_SPI2_NSS     (pin_B12)   //              pin 16 on CN10
#define MICROPY_HW_SPI2_SCK     (pin_B13)   //              pin 30 on CN10
#define MICROPY_HW_SPI2_MISO    (pin_B14)   //              pin 28 on CN10
#define MICROPY_HW_SPI2_MOSI    (pin_B15)   //              pin 26 on CN10

#define MICROPY_HW_SPI3_NSS     (pin_A4)    // Arduino A2,  pin 32 on CN7
#define MICROPY_HW_SPI3_SCK     (pin_B3)    // Arduino D3,  pin 31 on CN10
#define MICROPY_HW_SPI3_MISO    (pin_B4)    // Arduino D5,  pin 27 on CN10
#define MICROPY_HW_SPI3_MOSI    (pin_B5)    // Arduino D4,  pin 29 on CN10

// USRSW is pulled low. Pressing the button makes the input go high.
#define MICROPY_HW_USRSW_PIN        (pin_C13)
#define MICROPY_HW_USRSW_PULL       (GPIO_NOPULL)
#define MICROPY_HW_USRSW_EXTI_MODE  (GPIO_MODE_IT_FALLING)
#define MICROPY_HW_USRSW_PRESSED    (0)

// LEDs
#define MICROPY_HW_LED1             (pin_A5) // Green LD2 LED on Nucleo
#define MICROPY_HW_LED_ON(pin)      (mp_hal_pin_high(pin))
#define MICROPY_HW_LED_OFF(pin)     (mp_hal_pin_low(pin))

boards/NUCLEO_F401CC/mpconfigboard.mk

MCU_SERIES = f4
CMSIS_MCU = STM32F401xE
AF_FILE = boards/stm32f401_af.csv
LD_FILES = boards/stm32f401cc.ld boards/common_ifs.ld
TEXT0_ADDR = 0x08000000
TEXT1_ADDR = 0x08020000

# trial (8/29 22:09)

# MicroPython settings
MICROPY_VFS_FAT = 0

# Don't include default frozen modules because MCU is tight on flash space
FROZEN_MANIFEST ?=

boards/stm32f401cc.ld

/*
    GNU linker script for STM32F401CC
*/

/* Specify the memory areas */
MEMORY
{
    FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 256K /* entire flash */
    FLASH_ISR (rx)  : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */
    FLASH_FS (rx)   : ORIGIN = 0x08004000, LENGTH = 4K /* sectors 1 16K but use 4K*/
    FLASH_TEXT (rx) : ORIGIN = 0x08005000, LENGTH = 236K /* 12K +  sectors 2,3 are 16K sectors 4 is 64K 5 are 128K */
    RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 64K
}

/* produce a link error if there is not this amount of RAM for these sections */
_minimum_stack_size = 2K;
_minimum_heap_size = 16K; /* tunable */

/* Define the stack.  The stack is full descending so begins just above last byte
   of RAM.  Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K;

/* RAM extents for the garbage collector */
_ram_start = ORIGIN(RAM);
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
_heap_start = _ebss; /* heap starts just after statically allocated memory */
_heap_end = _sstack;
$ pwd
 ....  ports/stm32/boards/NUCLEO_F401CC

$ ls
mpconfigboard.h  mpconfigboard.mk  pins.csv  stm32f4xx_hal_conf.h

pins.csv , stm32fxx_hal_conf.hは、../NUCLEO_F401RE/からコピー

さらに不要なライブラリを外した。起動時の標準ライブラリは以下

>>> help('modules')
__main__          math                   uctypes           urandom
_onewire          micropython       uerrno            uselect
builtins            pyb                      uio                  ustruct
cmath              uarray                  umachine       usys
gc                    ucollections         uos                  utime
Plus any modules on the filesystem

現在のファームのサイズ、sizeコマンドによると、223776B (219KB)、割り当てた領域が236KBなので、ほとんど余裕がない。きちきちで収まっている。

$ /usr/local/GNUToolsARMEmbedded/4.8_2013q4/bin/arm-none-eabi-size.exe firmware.elf
   text    data     bss     dec     hex filename
 203584      12   20180  223776   36a20 firmware.elf

STEVAL-DRONE01の制御基板にはLEDが2つ付いているのだが、、Lチカも以下の記述で実行可能。楽だ。

>>> import pyb
>>> led1 = pyb.LED(1)
>>> led1.toggle()
>>> led1.toggle()

■追記
MicroPythonを使って長いけど、普段はビルドされたESP32用ファームを使ってるだけなので、内部構造とか全く分かっていなかった。こうやってデバッグしていると、分からないながらも少しずつ知識が増える。だから。。?? 課題が発生するとそれを解決しようとあれこれ調べるので勉強になる。