ADSP-2191にはIDLE命令があり、実行を停止することができます。しかしながらIDLE命令を実行しただけではシーケンサーが命令フェッチを停止するだけであり、DSP内部の無数のステートマシンには状態更新用のクロックが供給されつづけています。そのため、消費電力は大して下がりません。以下では実行すべき仕事がないときに消費電力を下げる方法を説明します。
実アプリケーションでは消費電力を大幅に減らさなければならない局面が多くあります。一方で、完全にクロックを止めるといろいろと困難があります。そのため、ADSP-2191は多くのマイクロプロセッサ同様複数の消費電力削減方法を提供しています。
コアクロックを停止した場合、DSPの実行を再開するには外部からの割り込みを使います。割り込みを認識すると停止していたクロックの供給が再開され、命令の実行も再開されます。
ところがペリフェラルクロックを停止すると頭の痛い問題が発生します。ペリフェラルクロックを止めるということはチップ内部の割り込み生成源もすべて動作を停止するということです。一般に高速ディジタル回路は同期設計ですのでペリフェラルクロックを停止すると動作を再開できなくなってしまいます。
これを打破するためにADSP-2191には非同期起動信号を作るバイパスが用意されています。プログラマブル・フラグの割り込みAに割り当てられた入力ピンはこの非同期起動信号として働きます。この信号は内部クロックなしで動作しますので、パワーダウン中のDSPの動作再開に利用できます。実はこの情報はHRには掲載されておらず、ADSP-2199xのマニュアルを漁っているときに発見しました。
タイマーも非同期起動信号の作成に使えます。この場合の動作はT_CNTHx, TCNTLxを参照してください。
パワーダウンモードへの移行にはいくつかの種類があります。ここではコア/ペリフェラル・パワーダウンについて説明します。その他のモードや詳細についてはHR 14-35, 14-47, PLLCTLを参照してください。
コア/ペリフェラル・パワーダウンはコアとペリフェラルへのクロック供給を停止しますが、PLLは止めません。そのため、パワーダウンからのすばやい復帰が可能です。一方でペリフェラルが停止するので非同期起動信号が必要になります。コア/ペリフェラル・パワーダウンに入るには次のようなコードを実行します。
無用に面倒な操作にも見えますが、ハードウェアがパワーダウン状態に入ることとIDLE命令の実行をうまく同期させているといえます。
さて、上の手順にはひとつだけ問題があります。実験で確認したところ、3と4の間には数サイクルの遅延が入ります。つまり普通に上の手順を実行するとオーバーランを起こしてしまいます。そこで安全のために10個程度のNOP命令を3のあとにおくか、IDLE命令をおくといいでしょう。IDLEをおくのはコードの性質を考えるとちょっとだけ不安ですが、実験ではうまくいきます。
簡単な実験コードを作りました。以下のプログラムはEZ-KIT Liteでパワーダウンを行うプログラムです。PF4をPF割り込みA、PF5をPF割り込みBに割り当てていますので、実行中に両スイッチを押してみてLEDの変化を見ることができます。もちろん、PF5を押しても何もおきません。
ためしに実行中パワーダウン状態に入れてVisualDSP++のほうからHALTをかけてみました。しかし"Performing reset of the DSP."というメッセージが表示されます。ペリフェラルクロックが停止しているためにJTAGスキャンもかけられないのでしょう。どうやら正しくパワーダウン状態に入っているようです。
// パワーダウンテストプログラム // ADSP-2191'sのパワーダウンシステムは復帰のための"ウェイクアップ" // 信号を必要とする。これはHRには記載されていないが、 // ADSP-2199xのHRにはそれがどのようなものかがか書かれている。 // それによると、マスクA 割り込みに割り当てられているGPIOの // 入力ピンは非同期ウェイクアップ信号として働く。 #include "def2191bit.h" #include <def2191.h> #include <sysreg.h> #include <signal.h> void intHandlerA( int ); void intHandlerB( int ); void powerdownHandler( int ); int main(void) { int pll; // GPIO を設定してEZ-KIT Lite2191 のPF4スイッチを割り込みAに // PF5スイッチを割り込みBに割り当てる sysreg_write( sysreg_IOPG, General_Purpose_IO ); io_space_write( DIR, 0x0f ); // set PF0-3 to output io_space_write( FSPR, 0x00 ); // Source polarity is positive. io_space_write( FSSR, 0x30 ); // set PF4,5 to edge sense. io_space_write( FSBER, 0x00 ); // set PF4,5 to one dege. io_space_write( MASKAS, 0x10 ); // set PF4 to PF interrupt A io_space_write( MASKBS, 0x20 ); // set PF5 to PF interrupt B // 割り込みの設定 sysreg_write( sysreg_IOPG, Interrupt_Controller_Page ); // PF_A PF_B 割り込みを別ベクトルとして割り当てる io_space_write( IPR3, 0x0BBA ); // PFA=>10, PFB=>11 // PF_A PF_B 割り込みの登録 interrupt( SIG_INT14, &intHandlerA ); interrupt( SIG_INT15, &intHandlerB ); // パワーダウンハンドラの登録 interrupt( SIG_PWRDWN, &powerdownHandler ); // 割り込み許可 enable_interrupts(); sysreg_write( sysreg_IOPG, Clock_and_System_Control_Page ); while (1){ // CORE/PERIPHERALパワーダウン状態では内蔵ペリフェラルの // クロックが停止するので、通常の割り込みは発生しない。 // そのため非同期ウェイクアップ信号である PF_A 割り込み // のみが機能する pll = io_space_read( PLLCTL ); // PLL の状態取得 // ここを変えると好きなモードにすることができる。HR 14-47 pll &= ~ PDWN; // PLLはパワーダウンしない pll |= STOPALL; // CORE/Peripheral 双方のクロック停止 io_space_write( PLLCTL, pll ); // PLLのセット asm( "idle;" ); // HRには書いていないが、これがないとオーバーランする } } int count = 0; // 割り込みAが入るとカウントアップ void intHandlerA( int sig ) { sysreg_write( sysreg_IOPG, General_Purpose_IO ); count ++; io_space_write( FLAGS, 0x0f & count ); // set singal to LED io_space_write( FLAGC, 0x0f & ~count ); // clear the 0 LED. io_space_write( FLAGC, 0x10 ); // clear interrupt } // 割り込みBが入るとカウントダウン // この系統は同期回路なので割り込みは決しておきない。 void intHandlerB( int sig ) { sysreg_write( sysreg_IOPG, General_Purpose_IO ); count --; io_space_write( FLAGS, 0x0f & count ); // set singal to LED io_space_write( FLAGC, 0x0f & ~count ); // clear the 0 LED. io_space_write( FLAGC, 0x20 ); // clear interrupt } // パワーダウン割り込みの中でidleを実行すると // 本当のパワーダウン状態になる void powerdownHandler( int sig ) { asm("idle;"); }
次は⇒シグナルツールキット