[自作キーボード]QMKで2MB以上のフラッシュを持つRP2040ボード用のファームウェアを作成する

目次

Abstract

  • QMK, RP2040環境で2MB以上のファームウェアを作成
  • フラッシュサイズの変更はリンカスクリプト付近をいじる

Introduction

前回記事までで、フォント、画像、動画などをQMKファームウェアに取り込めるようになりました。が、それらを取り込むとファームウェアのサイズが巨大になってきました。特に動画とかを入れるとRaspberry Pi Picoのフラッシュサイズの2MBを超えてしまうケースなどもあったので、もう少しフラッシュの容量に余裕のあるボードを使用できるようにしたいと思います。

使用するボード

RP2040で扱えるフラッシュの最大サイズ16MBで、Raspberry Pi Picoと同様のピンアサイン、type-cと良いことづくめだったので以下を選定。

  • Pico Boot
    • 入手元
      • Aliexpress
      • 3~4[USD]程度
    • Spec
      • Flash 16MB
      • ピンアサインとかはRaspberry Pi Picoとほぼ同じです
      • 詳細は上記の販売元ページ参照
    • Other

Method

前回記事のAppendixに記載したように、QMKでRP2040を指定してファームウェアのコンパイルした場合、ファームウェアのサイズが2MBを超える場合にコンパイルエラーになってしまいます。

この状態だと、せっかくPico Bootを使ってもそもそも2MBのファームウェアしか作成できず16MB中2MBしか使えないことになり、Raspberry Pi Pico使ってるのと同じ状況です。そのため、まずはこのコンパイルエラーに対応してきたいと思います。

↓コンパイル時にファームウェアのサイズが上限を超えたときのエラー

(venv) user@user:~/qmk$ qmk compile -kb cepst/rp2040test -km default
~省略~
Linking: .build/cepst_rp2040test_default.elf
 [ERRORS]
 |
 | /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: .build/cepst_rp2040test_default.elf section `.rodata' will not fit in region `flash1'
 | /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: region `flash1' overflowed by 1555592 bytes
 | collect2: error: ld returned 1 exit status
 |
gmake[1]: *** [builddefs/common_rules.mk:268: .build/cepst_rp2040test_default.elf] Error 1
Make finished with errors
gmake: *** [Makefile:392: cepst/rp2040test:default] Error 1

1. コンパイルエラーを読む

/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: .build/cepst_rp2040test_default.elf section `.rodata' will not fit in region `flash1'
/usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: region `flash1' overflowed by 1555592 bytes

↑のエラーを読むと、ldコマンドのとこですし内容的にリンカスクリプトでflash1に割り当てている領域に対して、今回ldしたとき結果のサイズが超過していることが問題っぽい。なので、リンカスクリプト側をいじってflash1に2MB以上の割当をしてあげれば良いはず。

qmk firmwareのソースでどこにリンカスクリプトのソースが有るのかは知らないので、とりあえずgithubのqmk_firmwareリポジトリをflash1という名前で検索してみると拡張子.ldのリンカスクリプトと思われるファイルが多数ヒットした。名前にRP2040が入っているので、おそらく以下がRP2040用のリンカスクリプトと思われる。

qmk_firmware/platforms/chibios/boards/common/ld /RP2040_FLASH_TIMECRIT.ld

2. リンカスクリプトを読む

↑で見つかったRP2040_FLASH_TIMECRIT.ldを見てみると以下のように、各種の領域に対するメモリ割り当て構成が記載されています。 flash1のところを見ると、len = DEFINED(FLASH_LEN) ? FLASH_LEN : 2048kとなっており、FLASH_LENのシンボル定義があればその値を、シンボル定義が無い(デフォルトの)場合は2048kを割り当てるという処理になっています。ここを作った人はRP2040の場合はFlashが外付けでサイズ変更可能ということで、ReferenceとなるPicoの2048kから変更可能に設計してくれているようです。

↓RP2040_FLASH_TIMECRIT.ld

~省略~
/*
 * RP2040 memory setup.
 */
MEMORY
{
    flash0 (rx) : org = 0x00000000, len = 16k   /* ROM                  */
    flash1 (rx) : org = 0x10000000, len = DEFINED(FLASH_LEN) ? FLASH_LEN : 2048k /* XIP */
    flash2 (rx) : org = 0x00000000, len = 0
    flash3 (rx) : org = 0x00000000, len = 0
    flash4 (rx) : org = 0x00000000, len = 0
    flash5 (rx) : org = 0x00000000, len = 0
    flash6 (rx) : org = 0x00000000, len = 0
    flash7 (rx) : org = 0x00000000, len = 0
    ram0   (wx) : org = 0x20000000, len = 256k  /* SRAM0 striped        */
    ram1   (wx) : org = 0x00000000, len = 256k  /* SRAM0 non striped    */
    ram2   (wx) : org = 0x00000000, len = 0
    ram3   (wx) : org = 0x00000000, len = 0
    ram4   (wx) : org = 0x20040000, len = 4k    /* SRAM4                */
    ram5   (wx) : org = 0x20041000, len = 4k    /* SRAM5                */
    ram6   (wx) : org = 0x00000000, len = 0
    ram7   (wx) : org = 0x20041f00, len = 256   /* SRAM5 boot           */
}
~省略~

3. flash1の容量を16MBにする

変更する方法は3種類ほどありそうです。

  • ①上記のqmk_firmware/platforms/chibios/boards/common/ld/RP2040_FLASH_TIMECRIT.ldのflash1のlenを直接書き換える
    • len = 16384kみたいに
  • ②自身のキーボードフォルダ内にリンカスクリプトを作成して、コンパイル時にそちらを読み込ませるようにする
  • ③Makefileからリンカスクリプトに対してFLASH_LENの定義を渡す

ただ変更したいだけなら、①が手っ取り早いですが他の種類のRP2040ボード使うときに毎回変更する必要があり面倒。
②の方法でもありですが、今回はflash1の容量以外に変更したい箇所も無いので少し大げさ。

ということで、③の手段を取ります

MakefileからリンカスクリプトにFLASH_LENのシンボル定義を渡す

自身のrules.mkを開いて以下を追記する。
 ※16384kのところは使いたいフラッシュサイズに変更する。今回使うPicoBootは16MBなので16384k。

↓rules.mk

LDFLAGS += -Xlinker --defsym=FLASH_LEN=16384k

追加したオプションの中身を簡単に説明すると、-Xlinkerが後に続く一文をリンカに渡すオプション。--defsymはldコマンドのオプションでコマンドラインからシンボルを定義してあげるオプション。組み合わせることでMakefile側からリンカスクリプト側にFLASH_LENシンボルの定義をできる

4. コンパイルしてみる

rules.mkに上記の変更してコンパイルしてみる。 最初に出たコンパイルエラーは発生しなくなり、下記のように2MB以上のファームウェアが作成できました。

↓生成されたファームウェア。約7MBとなっている

-rw-r--r--   1 user user 7428096 Oct 23 22:40 cepst_rp2040test_default.uf2

Result

上記の手順で作成した2MB超えのファームウェアをPico-bootに書き込んでみました。 長めで高品質な動画も再生できるようになりました。

↓約7MBのファームウェアを書き込み(8sec, 10fps, pal256の動画を埋め込み)

↓約16MBのファームウェアを書き込み(20sec, 10fps, pal256の動画を埋め込み)

Conclusion

QMK, RP2040環境で2MB以上のファームウェアを作成できるようになりました。これでファームウェアサイズの容量的には余裕を持って色々作れそうです。

次回はLCDの描画とキーボードの操作の連携について書く予定。

Appendix

自分のキーボードフォルダ側にリンカスクリプトをもたせる

QMKの下記のmakeファイルを見ると、リンカスクリプトは<keyboard_dir>/ld/に置いても良いとの記載がある

platforms/chibios/platform.mk

  # Linker script to use
  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
  #   or <keyboard_dir>/ld/
  STARTUPLD_CONTRIB = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/ld
  MCU_LDSCRIPT ?= RP2040_FLASH_TIMECRIT
  LDFLAGS += -L $(STARTUPLD_CONTRIB)

実際に下記のようにldフォルダを作成して、qmk_firmware/platforms/chibios/boards/common/ld/からコピーしたRP2040用のリンカスクリプトを配置して、FLASH_LENを書き換えたところ、こちらで定義した値が読み込まれるようになった

  • home\user\qmk\qmk_firmware\keyboards\cepst\
    • rp2040test\
      • ld\
        • RP2040_FLASH_TIMECRIT.ld
        • RP2040_rules_data_with_timecrit.ld

リンカスクリプトをいじることはあまり無いですが、一応こういう形でキーボード毎にリンカスクリプトを書けるというのは覚えておこう。。