組込みソフトウェアコーディングTips

volatile 指定

H/W のレジスタなど、ソースコード上ではいじらなくても値が変化する可能性のある変数があります。そのような変数は、volatile 指定をしておく必要があります。

volatile 指定をしないと、コンパイラの最適化により、最初の値評価以外のコードが消されてしまう可能性があります。その結果、絶対に抜けることのない無限ループとなってしまいます。

例として、以下のようなコードは、ソースコード上 while ループ内で timer_reg と expired の値が変化することはないので、コンパイラは無駄な処理と判断して削除します。

しかし、実際には H/W が timer_reg の値を更新していくので、while ループを回すことに意味があります。そこで、volatile 指定を行い、コンパイラにそのことを教えてあげます。

long* timer_reg;
long expired = 1000;

while (*timer_reg != expired) {}

組込みソフトウェアのコーディングで、レジスタ変数と while ループを使う場合ば、頭の片隅に入れておくと良いです。

Cache 操作

メモリアクセスの高速化のために、CPU などはキャッシュを備えています。全てのアクセスがキャッシュ経由で行なわれるのなら、あまり意識はしなくて良いのですが、キャッシュを経由せずに直接メモリ本体にアクセスする H/W もあります。その際に、キャッシュ上のデータと、メモリ本体にあるデータが食い違う可能性があります。

キャッシュ上のデータをメモリ本体に反映しているか。CPU で操作したあと、そのデータを別の領域へ DMA コントローラでコピーする場合、事前にキャッシュ上のデータをメモリ本体へ Write Back しておく必要があります。

キャッシュ上のデータがメモリ本体の内容を破壊しないか。DMA コントローラでコピーした先の領域に対応するキャッシュデータがある場合、せっかくコピーしたデータが、古いキャッシュデータで上書きされてしまいます。それを回避するために、事前にキャッシュ上のデータを無効化しておく必要があります。

アラインメント

各 H/W によって、メモリアクセスで利用できるアラインメントが異なります。その制約に沿わないアドレスを渡すと、動作は不正となり、デバッグに苦労します。

また、構造体を定義する時も、CPU のアラインメントを意識して、パディングを考慮しておきます。

4byte, 32byte, 4KB など、アラインメントは、H/W によって様々です。

リンカスクリプトのシンボル定義

リンカスクリプトには、シンボルを定義して、Cコード側から参照させることが出来ます。この際、リンカスクリプトに書けるのは、シンボル名とシンボルのアドレスになります。

例えば以下のようなシンボルがあった場合、

SAMPLE_AREA=0x02000000
SAMPLE_AREA_SIZE=0x1000

Cコードでは、次のように値を取得できます。

extern unsigned long SAMPLE_AREA;
extern unsigned long SAMPLE_AREA_SIZE;
printf("addr = %x, size= %x\n", &SAMPLE_AREA, (unsigned long)&SAMPLE_AREA_SIZE);

注意する点として、リンカスクリプトに定義できるのは、あくまでシンボルのアドレスのみです。シンボルが示すメモリ空間に格納される実値を定義できるわけでは、ありません。故に、以下のような場合は、何が返るかわかりません。

printf("value = %x\n", *(unsigned long*)&SAMPLE_AREA_SIZE);

履歴

  • 2015/01/28 bokupi リンカスクリプトのシンボル定義を追加
  • 2015/01/08 bokupi 新規作成

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: Thu, 29 Jan 2015 01:39:35 JST (910d)