カスタマーサポートセンター

FAQ~よくある質問~ | FAQマスタ詳細表示

FAQ詳細情報

ID 10810924
FAQカテゴリ(大) コンパイラ
最終更新日 2016-01-02

質問内容

TN59350: AVR用 IAR Embedded Workbench で安全なプログラミングをする

回答内容

ターゲット: AVR
コンポーネント: コンパイラ
公開: 2003/08/08 10:06

 

概要
このテクニカルノートは AVR用 IAR Embedded Workbench で安全なプログラミングをする方法を説明します。

 

同時にアクセスされる変数を保護します。
複数のスレッドからアクセスされる変数は、適切にマークし十分に保護しなくてはなりません。唯一の例外は読み取り専用の変数変数です。

変数を正しくマークするには、volatile キーワードを使用します。これは変数が他のスレッドから変更されることをコンパイラに通知します。コンパイラは、変数の最適化 (例えば、変数をレジスタに置くなど) を行わずすぐに書き込みます。ソース コードで指定されている倍の数だけ変数へアクセスするので注意してください。

変数へのアクセスシーケンス中に割り込みを入れられません。これは、割り込みコードの中で __monitor キーワードを使用します。これは、書き込みと読み出しの両方行う必要があり、そうしないと一部だけ更新された変数を読んでしまうかも知れません。

これはすべてのサイズのすべての変数にも当てはまります。バイトサイズの変数へのアクセスはアトミック操作とすることができますが、これは保証されず、コンパイラの出力を常にチェックしなければなりません。__monitor キーワードを使用したアトミック操作でアクセスシーケンスを保証するほうが安全です。

 

可能ならインラインアセンブラを使用せず、組み込み(intrinsics)関数を使用します。
正確なタイミングと特別な命令シーケンスを必要とする AVR デバイスのメカニズムがあります。そのようなメカニズムは通常組み込み関数の形式で利用できます。使用可能な組み込み関数は、inc\inavr.h ヘッダー ファイルで宣言されます。

組み込み関数は通常の関数呼び出しのように見えますが、実際はコンパイラが認識しているビルトイン関数です。ソースファイル内の関数呼び出しのように見えますが、通常コンパイラがインライン命令のシーケンスを生成します。

インラインアセンブラーではなく、組み込み関数を使用する利点は、コンパイラが、今起こっていることを理解し、レジスタの割り当てと変数に正しくインタフェースすることです。コンパイラは、インラインアセンブラーシーケンスでは不可能な、このようなシーケンスを最適化する方法を知ることができます。最終的に、コード上で目的とするシーケンスを完全に統合し、コンパイラはコードを完全に最適することができます。

 

可能な場合は、インラインアセンブラーではなく、アセンブラー関数を使用します。
インラインアセンブラは、C コードから生成された周辺のコードとの良く定義された インタフェースがありません。このため、インラインアセンブラーコードは壊れやすく、将来コンパイラをアップグレードする場合もメンテナンス問題になるでしょう。コンパイラは、関数とインライン アセンブラーコードを一緒に最適化することを避け、アセンブラコードが壊れにくくなるようにします。

インラインアセンブラは避けるのがベストです。多くの場合、特別なアセンブラコード(上を参照) を書かずにすむ組み込み関数を使用することができます。

もし適切な組み込み関数がない場合、Cから呼び出される、アセンブラモジュールにアセンブラコードを書くのが最良です。これにはいくつかの利点があります:

1. 関数を呼び出すメカニズムは良く定義されていて将来のコンパイラリリースでも変更されなにでしょう。__version_1 メカニズムは通常の用途に適しています。これは壊れやすいコードの問題を排除します。
2. コードは読みやすくなります。
3. 最適化を行うこともできます。

しかし、関数の呼び出しはオーバーヘッドを増やすのでしょうか?答えは、場合によります。もちろん、CALL と RET 命令の形と、レジスタを作業レジスタとしてみなす (関数コールにより破壊される) オーバヘッドがあります。

一方、コンパイラは、作業レジスタかインラインアセンブラ命令により破壊されることを前提にします。これは CALL と RET のオーバヘッドになります。多くの場合、最適化オフにすることのオーバーヘッドは、簡単にCALLとRET 命令のオーバーヘッドを上回ります。

 

EEPROM書き込みメカニズムを保護します。
eeprom 変数に書き込む機構は、リエントラントではないので、保護する必要があります。これは、eeprom への書き込みを、 (たとえばメインと割り込みのコード) 2 つのスレッドからの書き込みから保護するために __monitor キーワードを使用して、 eeprom 書き込みメカニズムを使用するコードのシーケンスが、中断しないことを保証する必要があることを意味します。

 

ワード SFR 書き込みの保護
ワード SFRs は AVR のテンポラリバイトを使用してアクセスします。このようなテンポラリバイトは1つだけです。ワード SFRを2つのスレッド (たとえばメインと割り込みのコード) からアクセスする時は、割り込みからワードアクセスを保護しなくてはなりません。さもないと、再現頻度の低い微妙なバグを抱えることになります。割り込み処理プログラムの内部で割り込みをオンにする習慣がある時は、ワード SFRへのアクセスをこのような割り込みからも保護しなくてはなりません。

 

ロックされたレジスタをプッシュしてはいけません。
レジスタをロックしてグローバル変数に代入する時は、アプリケーションでこの目的のためにロックされていることを認識してください。アセンブラコードでこのレジスタを使用し、レジスタを貸している間にこの変数を単にプッシュすることができると考えてはいけません。

理由は、割り込みから、ロックされているレジスタ変数にアクセスする可能性があることです。レジスタに変数を置く目的は、たぶん処理スピードを上げたいからでずか、割り込みからそれにアクセスする可能性があることを覚えておいてください。

また、サードパーティのアセンブラコードをインポートする時、このコードがこのレジスタを使用すると、スタックに保存してあっても、割り込みにより無意味になるかも知れません。

結論は、常にロックレジスタを手放しておくことです !

 

全ての製品の名前は、それぞれの所有者の商標または登録商標です。

参考資料URL