BlackfinプロセッサのIDLE命令は、プロセッサを低消費電力状態に移行させます。割り込み待ちにつかうと便利に見えるこの命令には思わぬ落とし穴があります。以下、この命令を使う上での注意点などを説明します。
IDLE命令は割り込み待ちには使えません。これはとても意外なことです。実際、IDLE命令の実行によってアイドル状態になったBlackfinコアは、割り込みが入るとアイドル状態から実行状態に戻ります。では、何が問題なのでしょうか。
問題はIDLE命令の実行前にあります。IDLE命令は、自身の実行中に発生した割り込みを正しく処理できます。つまり、実行状態に戻り、割り込みベクトルが示すアドレスへ分岐することが出来ます。ところが、その割り込みがIDLE命令の前に発生すると、こまったことがおきます。割り込み待ち状態ということは、割り込みが来たからにはそれを処理して次の処理に進むべきですが、IDLE命令はそれを判定できず、アイドル状態に突入してしまうのです。結果的に、割り込みが来たにもかかわらず、プロセッサコアはその割り込みを処理してさらに割り込み待ちに入ってしまいます。
この問題は、じつはBlackfinの前のすべてのアナログ・デバイセズのプロセッサに存在します。それが問題化しなかったのは、ほとんどの信号処理アプリケーションが単純だったからでしょう。小規模の信号処理アプリケーションでは、メインループは初期化が終了したら、単純に割り込み待ちループを実行するだけになります。
while (1) idle();
このようなループは割り込みから帰ってきても処理を行いません。単に次の割り込みを待つだけです。したがって、IDLE命令の前で割り込みがおきようが、アイドル状態でおきようが、違いはないのです。
ところが、RTOSを使うとなると話が変わります。RTOSを使う場合、割り込みの終了に伴ってタスク状態が変わることがありますから、割り込みが終了したら必ず何らかのチェックが必要です。したがって、IDLE命令実行前に割り込みが発生したら、IDLEを実行せずに次に進むべきなのです。ちょっと紙と鉛筆で試してみるとわかりますが、そのようなプログラムは書けません。たとえIDLE命令の直前に割り込みがあったかどうかチェックしても、チェック中に割り込みがきたらアウトです。
では、どうすればいいのでしょう。答えは単純で、割り込みを使わずにウェイクアップを使えばいいのです。ADSP-BF533のSIC_IWRは、ペリフェラルの割り込み要求信号をウェイクアップ信号に変換するレジスタです。これは対応するSIC_IMASKと同じビットマップになっており、SIC_IMASKが、割り込み要求をコアの割り込み入力に渡すためのゲートとして働くのに対して、SIC_IWRはコアのウェイクアップ入力に渡すためのゲートとして働きます。
ウェイクアップ入力は、IDLE命令だけが認識する信号です。この信号は割り込みをおこさず、ただ、アイドル状態にあるコアを実行状態に戻すだけの役割を持っています。そして、IDLE命令実行前にウェイクアップ信号が発生すると、IDLE命令実行と同時にコアは実行状態にもどります。これを利用すれば、安全に割り込みを待つことが出来ます。
具体的には、いったんすべての割り込みを禁止し、IDLE命令でウェイクアップを待ちます。ウェイクアップ信号が発生したら、割り込みを許可して受け付けます。
cli R0; idle; sti R0;
まとめると、IDLE命令で割り込みを待ってはならず、かならずウェイクアップを待つようにします。
IDLE命令実行時にクロックが停止するかどうかは、IDLE命令の管理外です。ADSP-BF533の場合、これはPLL_CTLレジスタの設定によります。STOP_CLKが1のとき、IDLE命令の実行によりCCLKが停止します(スリープモード)。STOP_CLKが0ならば、CCLKは停止しません(アクティブ/フル・オン)。
PLL_CTLのPWDNビットも同様に、IDLE命令の実行によって効果があらわれます。
ADSP-BF535は、ADSP-BF533とコアの構造が異なるため、IDLE命令の使い方に注意が必要です。
第一に、idle命令の直後にSSYNC命令が必要です。先の命令列は次のようになります。
cli R0; idle; ssync; // ADSP-BF535に必要 sti R0;
第二に、ADSP-BF535のIDLE命令はスーバーバイザーモードでしか使えません。ADSP-BF533はユーザーモードでも使えます。ただし、ADSP-BF533でもCLI/STIは特権命令ですので、事実上IDLE命令も特権命令あつかいです。この件は気にする必要はありません。