新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

ARM の EABI とか newlib とか

あまりまとまっていないけど最近の成果をメモ。わすれちゃうからね。

XilinxSDK 用の gccgdb だけ再コンパイルしようとしたけどだめだった。もう全部コンパイルしないといけないみたい。結構めんどくさい。とはいえ、シェルプロがあるようなのでそれを一発動かせばよさそうであるのも事実。この SDK 用の gcc では g++ が使える。特に arm-xilinx-eabi- の方はどうしているのだろうとちょっと調べてみた。普通と言えば普通なんだけど、newlib を使っている。newlib を使うと、open や write などの Unixシステムコールが用意されているものとして libc を作ってくる。あとはこれらのシステムコールをシミュレーションすれば(用意すれば)c++ はできあがる。実際に、SDK では普通に c++ を選択できる。Eclispe での設定は次のようになっている。

-Wl,--start-group,-lxil,-lgcc,-lc,-lstdc++,--end-group
  • lstdc++ が指定されているが、 -nostdlib は指定されていない。つまり crt0 なんかがくっつく。crt0 はほとんど何もしていない。だから本来は crt0 なんかくつけないように -nostdlib を使用するべきなのだ。がそうはなっていない。理由は恐らく _init の存在。なぜか _init は crtbegin の中にあったりする。この _init はシステムの初期化なので本来は OS が持っていなければならないはずだ。そこが手抜きされている。っていうかなにリンクされているかわからないからほんとはすべて自分でコントロールすべきだよ。

まぁそんなこんなで newlib が要求するシステムコールは bsp 内の ps7_cortexa9_0/libsrc/standalone_v5_3/src にある。write なんてかなり手抜き。まぁいいのか。

リンカーで特徴的なのは .ctors とか .dtors で(コンストラクタとデストラクタかな)、さらに初期化ルーチンの preinit_array とinit_array 。C だけならあまりこの辺は気にしなくてよいのだけど、c++ を使うなら、この preinit_array と init_array に登録された関数をちゃんと呼ばないといけない。newlib では misc の init.c で __libc_init_array を呼べばよい。実際、SDK の main の前でよばれている。(xil-crt0.S)。もー crt0.S を用意するなら -nostdlib を設定すればいいのに、、、この __libc_init_array から _init が呼ばれているが、これは main に入る前の _init なんで(くりかえすけど) OS が用意するもの。SDK は用意しないで gcc の crt をリンクすることで解決している。まぁ弊害はないからいいのか?

c++ でめんどくさいのはグローバル変数のオブジェクト。これは gcc のバージョンにもよるが、4.9.1 だと

__static_initialization_and_destruction_0(int, int)

という関数が出来た。init_array 各関数をコールすることで呼ばれる。そして、このとき cxa_atexit 経由でデストラクタが登録される。いま検索したら

-fno-use-cxa-atexit

というオプションをつけると登録されないらしい。なんだよ~~~。意味がなくなりつつあるけど、続けて書くよ。

つまり終了処理の時にデストラクタを呼ぶ。終了処理は exit の時だけではなく、動的ライブラリのアンロードの時も呼ばれる。動的ライブラリを使っていないのに動的ライブラリの事を気にしなくてはいけなくなるのよ(あーでも -fno-use-cxa-atexit を使うと解決するみたいよ)。

動的ライブラリは各ライブラリに handle が割り当てられて、本体も仮想的に動的ライブラリ扱いされるので、main に dso_handle が必要になる。そして、デストラクタ登録のために cxa_atexit が呼ばれる。これらは newlib に用意されているから本来あまり気にしなくてよい。cxa_atexit などの必要な関数というのは EABI で決められているらしい。

あと、もう一つ、gcc は void *_impure_ptr; も用意しなければならない。これは再入可能な環境を保持していくもののようで、 newlib は reentrant な環境も用意していているので、newlib を使うとばっちりそろっている。

ということで、SDK ちょっとへんだけど普通に g++ が使えるという話。めでたしめでたし。

tkernel みたいな

tkernel みたいなのはちょっとめんどくさくなってくる。どういうわけか malloc を持っている。まぁそもそも libc とはなじまないから -nostdlibc だよね。最初は -nostdlibc を削除して SDK のように -lc をつかった。はい。できました。でも、これはよくなくて、crtX.o とか atexit とか、あまり tkernel の OS の事を考えてないものがくっつく。malloc なんてリンク順でかろうじて tkernel のが呼ばれる。危ない。

だから、この手の RTOS はやっぱり -nostdlibc で自前で crtX.o や libc をつくらないといけない。

_impure_ptr は newlib にその構造体が定義されていて、それを指すポインタになっている。 ./libsupc++/vterminate.cc から参照されている。システムでどのように reent の構造を用意するかということみたいなので、とりあえず、変数だけを用意しておく。(おい!! > 自分)

なんとなくだが、libstdc++ のコンパイルに失敗している気がするぞ。たしかに configure に --with-newlib がある。

usb_modeswitch と FS01BU

FS01BU は普通に USB Host につなぐと 1c9e:98ff で Mass Storage と認識される。usb-modeswitch を使うとモードを変えることが出来る。usb_modeswitch の本家からusb-modeswitch-data をとってくると 1c9e:98ff のデータがある。それを使う。

> usb_modeswitch -v 0x1c9e -p 0x98ff -c usb_modeswitch.d/1c93:98ff

これで vendor 固有のやりとりをして 1c9e:6801 に変更してくれる。1c9e:6801 になったら、今度は、modprobe で usb_serial を”無理やり" わりあてる。そのまえに 1c9e:6801 のエンドポイントを簡単に見てみる。(lsusb -v で詳しく見れるはず)。lsusb -v の出力ではないけど、自分で解析した結果は次の通り。

inteface:00 ff ff ff
   81: bulk
   01: bulk
inteface:01 ff ff ff   
   82: interrupt
   83: bulk
   02: bulk
inteface:02 ff ff ff
   84: bulk
   03: bulk
inteface:03 08 06 50
   04: bulk
   85: bulk

みたいな感じ。ff ff ff は USB のクラス等なのだが未定義ということになる。08 06 50 は Mass Storage。そこで、modprobe で usb serial を指定すると、よくわからないものすべてを usb serial に割り当てるようだ。kernel のソースを読むと vendor id と product id しかみていない。つまり、順に未定義の inteface すべてに usb serial を割り当てる。結果として ttyUSB0, ttyUSB1, ttyUSB2 ができる。素性のわかっている 08 06 50 には usb serial は割り当てないようだ。

usb serial の general に vendor id と product id 以上の割り当てポリシーに関する解像度はないので、すべてが usb serial になってしまう。ttyUSB2 は AT コマンドを受け付けるようなので (cu -l /dev/ttyUSB2 で確認可能。ATE1 でecho。ATI で情報が得られる)
、適当に遊べる。atd は使えなかった(そういうものじゃないらしい)。

じゃ、あとの interface 0 と 1 をアサインした ttyUSB0 と ttyUSB1 の役割は何なのだろうという疑問がわく。82 が interrupt なので、データの送受信は 82 02 でやるのかな?

ということで、ubuntu を upgrade するとうまくいくという記述をちらほら見かけますが、これは恐らく、usb_modeswitch のデータが更新されたためですね。

FS01BUからSIMカードが外せなかった、、、

検索すると
FS01BUからSIMカードが外せなかったので、分解して取り出す – まつぼ x Web
がひかかった。ここまでわかれば分解しなくても外せそう。ということで、ノートを切って隙間に差し込むと nanosim のひっかかりがなくなり、ちゃんと外せました。分解しなくても大丈夫。

CPS のメモ

CPS で APPLY が出てくることろで、割り込みをチェックして、割り込みルーチンに飛ばす。そして、その中でレジスタの切り替えとコンテキストの切り替えをすれば、VM 上でうまくマルチタスクが出来そう。割り込み性能が落ちる。がそれはそれ、そもそも VM で CPU をシミュレーションしているからなのであって、最終的に VM じゃなくて FPGA 上で走るリアルなVM(なにいっているんだか)にすれば解決する、、、、きがするぞ。

vm と割り込み

VM をつくっているひとは割り込みについてどう実装しているのだろう?実際の CPU の動きは割り込みがあがると別のアドレスにジャンプしたりするわけだが、VM ではどう実装すればよいのだ?単純にはどっかのフラグを"毎回"見て必要があればジャンプすればよい。しかし、これでは効率が悪すぎるだろう。折角(おれおれVMを)スレッデッドにした意味がなくなるのではないか?
Qemu は確か、ブロックごとにわけて、ブロック処理毎に割り込みを見ていたと思う。
タスク切り替えやエクセプションも問題になりそう。

vivado で突然ダウンロードケーブル経由の作業が出来なくなる

なんかの拍子に Vivado で Platform Cable USB II 経由で作業が出来なくなった。SDK もだめ。最初の理由はわからない。単純にケーブルの抜き差しか電源の off/on で大丈夫だったのかもしれない。
落ち着いて対処すればよかったのだが、慌てていろんなことをしてしまった。決定的だったのが、Jungo の windriver の更新。MS 経由だから安全だろうと思って最新の 11.5 にしてしまった。これがだめだった。恐らく、Platform Cable USB II のドライバと整合性がなくなった。
今考えるとデバイスマネジャーで元に戻すだけでよかった気もする。

対処方法は、

  • デバイスマネージャーで Jungo の windriver を削除
  • /Windows/System32/drivers/windrvr6.sys を確認
  • 存在するなら削除
  • 再起動(念のため)
  • デバイスマネージャで windriver がないことと drivers の下に windrvr6.sys がないことを確認
  • cmd.exe を管理者権限で起動
  • Vivado の /Xilinx/Vivado/2015.4/data/xicom/cable_drivers/nt64 へ移動
  • wdreg でインストール
wdreg -inf windrvr6.inf install

これで直った。正規の方法ではないので試すなら自己責任ということで。

ついでに書くと、混乱した原因の一つは Atmel の環境とのバッティング。今考えると atmel をインストールしていたから 11.5 がインストールされてしまったんだろう。
ここでも混乱して DriverMax とかいう妙なものまでインストールしてしまった(今は削除した)。この DriverMax は atmel のドライバの名称を使って流布されていたりするので気を付けないといけない。

とにかく慌てちゃダメ。