comuのbootloader


mruby/cをCH32V203用にカスタマイズしているが、現状では幾つかの課題があった。 その最も重要なものは、USBからはmrbcだけを書き換えることが出来無いことである。 CH32V203はUSBにハードウェアで対応でき、内蔵のブートローダーを立ち上げると、USBからflashを書き換えることができる。 しかし、その仕様ではflashの先頭から書き換えるようになっている。 一方、CH32V203用のmruby/cでは、flashの先頭にはmruby/cを動かすためのコードがあり、その後ろにユーザー用のmrbcコードを配置するようにしている。 mruby/cを使う際には、このmrbcの部分だけを書き換えることが出来れば、便利である。 SWDで接続している場合には、minichlinkを使って、アドレスを指定すればmrbcのみを書き換えることができる。 しかし、USB接続の場合にはその仕様のためにこれが出来無いのである。

ch32funのUSBDのコードが最近修正されたことに先日のブログで触れたが、いろいろと調べていたら、その修正を提案した人が作っているcomuというプロジェクトのことを知った。 comuというのは、CH32V203を使ってUSB-Aポートに収まるような小型のマイコンボードであり、それ用のプログラムなどが開発されている。 その中では、CH32V203用のブートローダーが公開されていて、これを応用するとmrbcのみの書き換えができるのではと思い、試してみた。

comuのブートローダーは、flashの先頭の2kの領域に書き込まれて、USBを介してminichlinkを用いてflashへの書き込みなどを可能にする。 ユーザーのコードはアドレスが2k以降の領域に書き込むことになるので、それだけ使えるflashの容量は減るが、USB用のbootloaderで2kというのは、かなり小さい部類に入る。 CH32V203用のmruby/cのメインのコードは、幸い62kより僅かに小さいぐらいの大きさなので、comuのブートローダーを丁度組み込める。

comuのブートローダーはch32funでコンパイルして、wchispで書き込めば良い。 すると、起動直後の5秒間はそのブートローダーが動き、その間はminichlinkからのアクセスが可能となるが、何も無ければそのままユーザーコードに移行する。 ユーザー用のプログラムの例は、comuのexampleにあるが、2k以降に配置するという指定のためにcomu.ldを使うようになっている。 mrubyのコードは、拡張flashを使うように修正したcomu.ldを組み込んでコンパイルて、minichlinkを使って2k以降と拡張flashにアドレスを指定して書き込めばよいはずである。 ところが、なかなかうまく行かずに、かなり苦労した。

comuの例にあるblinkやprintfは、使っているボード用に少し修正してコンパイルして書き込んだら、うまく動いた。 ちなみに、printfはUSBを使うものなので、ブートローダーの後でも、ユーザーコードでUSBを使えることを示している。 しかし、mruby/cを使おうとすると、全く動かない。 ブートローダーが使った機能やメモリの初期化が完全では無いために動かないのかと予想して、ブートローダーのソースを改造したりもしたが、それらはうまく行かなかった。 書き込みがすぐに終るので、書き込みがうまく行っていないかも知れないと思って、プログラムを書き込んだ後でその領域を読み出して、書き込んだファイルと内容を比較すると、256バイト目以降が一致していないことが判明した。 flashに書き込む際には、その直前にflashの消去が行われる。 消去がうまく行って書き込みが出来ていない場合には書き込めていない部分は規則的なデータになるはずだが、消去がうまく行かずに書き込みは出来ている場合にはその部分のデータは不規則になる。 また、flashの消去はch32v203では256バイト単位で行われるらしく、症状は消去が出来ていないことを示していた。 そこで、wchispでflash全体を消去してから、wchispでブートローダーを書き込んで、minichlinkでmruby/cのコードを書き込んで、その内容を読み出すと、データは一致して、うまく書き込めていることが分かり、mrbのコードが動き始めた。 flashの消去には或る程度の時間がかかるが、消去する領域が大きくなると、消去が終わる前に書き込みが始まり、データが化けてしまっていたのであろう。 書き込むプログラムが小さい時には、消去もすぐに終わるので、問題は起きないのである。 確実なのは、bootloaderとメインのコードを、サイズを調整して結合して、それをwchispで書き込む方法である。

私の目的としては、上記のようにしてmruby/cを一度書き込めば、その後はmrbを書き換えるだけであり、mrbは小さいことが多いので、それほど問題にならないかも知れない。 今のところ、mrbの書き込みで失敗は無いように思われる。 しかし、非常に気持が悪いので、どのように変更すれば良いかを検証してみた。 まず、boot_usercode_addressなどの変数はch32funのbootloaderで使われていたが、comuではユーザーコードへの移行の仕方が異っているために、使われていないようだったので、それらを取り除いたが、これは機能には影響しないはずである。 ブートローダーでは、minichlinkから送られてきたデータをメモリ上にコピーして、それをプログラムとして実行するということが行われている。 メモリ上のコードに実行を移す前後に、USBがそれぞれnakとackを返すように設定するルーチンを加えて、実行が終わるまではUSBから新たなデータが入って来ないようにしても、あまりうまく行かない。 また、minichlinkに待ち時間を入れたりもしたが、変化は見られない。 何が起きているのだろう。 comuの作者がこのバグに気が付いて、改善してくれるのを待つことにしよう。

これで、このブートローダーを使って、当初の課題であったmrbcのみの書き換えができる仕組みが一応出来上がった。 mrbの書き換えがうまく行かない症状が出る可能性が残っているので、注意して使わないといけないが。