コンパイラは何をするか

C言語を勉強すると、コンパイラは何をしてリンカは何をして、みたいなことを学ぶけれど、組み込みシステム向けC言語の場合に特に注意しないといけないコンパイラの動作があります。それが最適化です。最適化というと、読んで字のごとく、最適な状態に変更すること、無駄をなくして効率よく動作するようにすること、です。例えばこんなコードを書いたとします。

int a, b, c;
a = 100;
b = 200;
c = a * b;
printf("%d", c);

このコードを最適化するとどうなるでしょう?この場合、どうみても途中の計算は無駄ですので、例えばこんな感じになると思います。あくまでイメージするための例えです。

printf("%d", 20000);

計算しないといけないことがわかっているなら最初から計算結果を計算してしまえばいい、ということですね。

では、普通にパソコン上で書く純粋なソフトウェアと組み込みシステムでのソフトウェアは何が違うのか? 答えはハードウェアの存在です。もちろん普通のソフトウェアもハードディスクやSSDに保存されていて、メモリに読み込まれて動作して、というようなハードウェアは関係ありますが、組み込みシステムではもっとハードウェアがいろいろ出てきます。

組み込みシステムで登場するハードウェア

  • メモリマップドIO
  • ペリフェラル
  • DMA

一般的にはこれらのハードウェアと仲良くしながらソフトウェアを作っていくことでしょう。初めての組み込み開発でみんながやる通称Lチカ(LEDをチカチカ点滅させる)では、メモリのようにアドレスを割り当てられたIOポートを制御して、HighやLowを出力することでLEDをつけたり消したりするわけです。

while(1){
  led = 1;
  led = 0;
}

例えばこんなことをしてLEDを点滅させます。やや極端な例ではありますが、あくまで例です。このコードでまったくハードウェアを意識しない場合、ソフトウェアとしては結局のところ何もしないコードです。ledという番地に代入した値は誰も使っていません。ソフトウェアとして最適化した場合、何もしない、というのが最適な状態です。つまりLEDも点滅しなくなってしまいます。

volatile でメモリアクセスを確実に

最適化が邪魔をして自分の作りたいソフトウェアが作れないなら、コンパイルオプションで最適化をやめてしまう、というのもひとつの方法ではありますが、最適化の恩恵も受けたいです。そんなときに使うのが volatile です。変数を宣言するときにこれをつけることで、その変数はかならずメモリアクセスを伴うようにコードを生成してくれます。

volatile int i;

かならずメモリアクセスをともないますので、例えばLEDを点滅したり、UARTから受信した値を1byteずつ読み出したり、やりたい動作が確実に発生するようになるのです。高級な開発環境を使っているとこういうあたりは誰かが処理してくれて隠されてしまっているかもしれませんが、たくさんあるファイルのどこかには volatile がたくさん使われているファイルが存在することでしょう。

コメント