2012年6月11日月曜日

GCC ARM備忘録

arm-none-eabiなARMをターゲットとしたgccに関する備忘録(Cortex-M3を想定)

  1. gcc(Cortex-M3)において、関数呼び出し後のSPの位置は必ず8byte境界にアライメントされていなければならないようだ。

    これまでは4byteでアライメントされていればよいと思っていた。しかし、可変長引数としてfloatやdoubleを渡そうとすると、その値は8byte境界にアライメントされて関数に引き渡されることが判明した。例えば、int型5つとfloat型1つを関数に渡すと仮定すると、int型の最初の4つはレジスタR0、R1、R2、R3に、5つ目のintはスタックにpushされ、次のfloatはdouble型に変換された上でスタックを1つ(=1ワード=4バイト)飛ばして、次の8byte境界となるスタック上に8バイト値(double型に変換されているので)がpushされることが分かった。
    そういえば、以前Cortex-M3関係で割り込み動作を調べているとき、ISRは常に8byteアライメントを要求していたような記憶が・・・・。もう、どこで知った情報か忘れてしまったけど。
  2. 上記のように、可変長引数を持つ関数にfloatを渡そうとすると、doubleに変換された上で8byteにアライメントされてpushされる(レジスタ渡しの場合は[R0,R1]ペアか、[R2,R3]ペアに入る)。ということで、渡すfloat型の引数より前にある引数が奇数個であった場合はレジスタなりスタックにパディングが入る可能性がある点に注意が必要だった。
    なお、va_arg()で引数を一つずつ取り出すとき、コンパイラはdouble型を取り出す場合には自動的にパディングの存在を調べてスタックをずらして読み出すので(va_argで読み出す型と順番が間違っていない限り)問題は出ないようになっているようだ。パディングの存在自体はSPの末尾3bitを調べて調整する方法をとっているようだ。なので、もし万一、関数を呼び出すときのスタックが8byteアライメントになっておらずに4バイトずれていたとしたら、上のような可変長引数を持つ関数+float/double型引数を持つ呼び出し処理においてはおかしな動作になってしまう。
  3. 余談だけれど、↑のようなことに気がついたのはたまたま使っていたFreeRTOS v4.9.2に不具合があったからだ。
    FreeRTOSのCortex-M3用portルーチンでタスクの初期化のところに不備があり、タスク関数が呼び出されたときのスタックポインタは8byteアライメントになっておらず、常に+4バイトずれていた。そのため、そのタスクから呼び出される関数はすべて+4バイトずれたスタックポインタを持っていて、通常の関数呼び出しでは問題はでないのだけれど、上のようなケースでは問題になることがあった。
    ちなみに、FreeRTOS最新版(v7.1.1)では問題のportルーチンは対策されていた(タスク起動時の初期スタック状態を初期化する際に、余分にパディングワードを挿入するようになっていた)。
ああ、やっぱり組み込みは難しいなぁ。

2012年6月4日月曜日

VisualStudioのデバッグ実行が遅い件

以前から悩んでいた件が解決した。
VisualStudioでデバッグ実行を行うと、実際にアプリが起動して動作するまでにたっぷり30秒以上は待たされる。これではTAT(Turn Around Time)が長くて効率が悪いばかりかイライラしていた。
しかも、Core i5のPCでは30秒以上もかかるのに、Pentium4のPCではスッと起動するから、なおさらイライラして怒りの矛先をVisualStudio2008とMSに向けてしまったものだ。

しかし、原因がようやく分かった!

ブレークポイントの設定が多すぎたんだ!

実際、私のデバッグスタイルでは、


  • 新しく作った関数の先頭にブレークポイントを張る
  • デバッグ実行してブレークに引っかかったら、ステップ実行で関数の動作を確認し、問題がなければ先頭のブレークポイントを削除する。もし、関数内の分岐条件などでスキップされる処理があれば、そのスキップされる箇所に新しいブレークポイント設定する。
  • いろいろな条件で動作を繰り返して、すべてのブレークポイントが消えたら、つまりすべてのコードをステップ実行して動作確認できたらデバッグ完了とりあえずひと安心。
という感じなので、どうしてもブレークポイントが多くなってしまう。

デバッグ実行が遅い件については、suoファイルを削除するという方法がこちらこちらなどで紹介されているけれど、suoファイルはブレークポイント以外にも「ユーザータスク」に入力した内容なども記録されているので安易に消したくはない。単純にブレークポイント一覧からすべて選択して削除すればいいだけだった。