IoT何をいまさら(94) ATSAMD51、ArmのWFI命令使ってみた

Joseph Halfmoon

前回『初回が0なのはプログラムの仕様<バグともいう>です。』など勝手なことを書いて「逃げて」しまいました、すみません。今回はバグ修正版? ArmのWFI(Wait for Interrupt)命令を使って、割り込みが発生するまで「低消費電力で」待ちを入れたいと思います。まあこれとても根本的な対策ではないのでありますが。

ここしばらく、Arm Cortex M4Fコア搭載のMicrochip社ATSAMD51マイコンを「ハードに近いところで直接」プログラムしております。前回は「内蔵ペリフェラルの発生する割り込みの受け」をやってみました。割り込み源としてはTRNG(真性乱数ジェネレータ)を流用させていただきましたが、「つつけば割り込み発生してくれる単なる割り込み源」としての利用であります。もったいない。

前回、実験に使用したソースコード全文を掲載させていただいておりますので、今回は差分のみです。

WFI命令とC言語からの呼び出し

使用しておりますのがArm Cortex M4Fです。そのWFI(Wait for Interrupt)命令については、Arm社の以下のマニュアルを参照していただくのが筋ってもんかと思います。

ARMv7-M Architecture Reference Manual

上記リンク先からPDFファイルをダウンロードしなければならず、手元にダウンロード済のバージョンでは合計858ページあるドキュメントの505ページにWFI命令が掲載されております。といって私は全部読んでおりません。すみません。拾い読みです。x86系に比べればArm、そのなかでもCortex-M、はまだ「薄い」のだと思いますが、全然読み切れる気がしません。

意味はその名の通り、割り込み待ち(割り込みでなくてもRESETや例外でも)の命令です。待ち時間は一応無駄に電気を使わない状態になる、とのお約束です。実際に何と何が止まるのかはまた別な話。

C言語から実際にこの命令を使う場合、直接インラインアセンブラのお世話になる必要はありません。マクロで定義されております。ただし、お家流に2種類あり、ツールチェーンに armcc を使っている場合と、gcc を使っている場合で微妙に異なります。

  • armccの場合は、__WFI
  • gccの場合は、__WFI()

どちらもアンダーバー2個ですぞ。当方 Wio Terminal上でのArduino環境でのビルドには、VS Code + PlatformIOを使わせていただいとります。PlatformIOが自動でインストールしてくれるのは、gccのツールチェーンですので後者の記法であります。

WFIを使った前回コードの修正

前回のコードの問題は、ちゃんと割り込みが発生したか否かを確認せずに値を印字していることです。前回の動作状況を推測すると、”INT:~”でプリントしようとしている時点では実はまだ割り込みハンドラは完了しておらず、次の”if (pollReadTRNG~”文のところではハンドラが完了していた、という絶妙なタイミングのようでした。

「とりあえず」__WFI()で割り込み待ちを行ってみると、前回のように初回が0になるという「バグ」は改善したように見えます。が、成り行きでたまたまうまく行っているだけですな。

void loop() {
  uint32_t dat;
  Serial.println("---LOOP---");
  digitalWrite(GUIDE, 1);
  startTRNG();
  __WFI();
  digitalWrite(GUIDE, 0);
  Serial.printf("INT: 0x%08x\r\n",dataTRNG);
  if (pollReadTRNG(&dat)) { // If no interrupt happened,
    Serial.printf("POLL: 0x%08x\r\n",dat);
  }
  delay(2000);
}

だいたい__WFI()って、何かイベントがあれば復帰してくるので、TRNG割り込みに限らず、タイマ割り込みでもなんでも下に落ちてくるはずです。今回は他の割り込みが発生するような設定になってないので動いているように見えるだけ。本当はWFIから抜けてきたら、お目当ての割り込みが本当に完了したのか、確認してから値を読み取るべきでしょう。勿論、まだ未完了なら再びWFIに戻る、と。基本そういうループの中に置くべき命令なんだと思います。ただね、TRNGは決まった短いサイクル数で必ず割り込み発生してくるので、チェックしている間に割り込みは終ってしまうだろうけれど。

動作タイミングの観察

上記のコードを見ていただくと、GUIDEと名付けた端子(実際にはA1/D1端子)にstartTRNG()する直前に1,WFIから戻ってきた直後に0を出力しています。この部分も前回からの差分(出力設定などは省略。)オシロで観察するとこんな感じ。非常に安定してとれています。

TRNG_WFI_TIMINGハイ期間、ほぼほぼ2μ秒。このケースでは実際にWFIで待っているのはごく短い時間であることが分かります。まあわざわざWFIで待たなくても前々回のようなポーリングで十分(どうせフラグチェックするし)かと。

まあね、塵も積もれば山となるなるで、無駄にソフトウエアループで空回りして待つよりかは消費電力いくらかマシですかね。どうなんだろ。

IoT何をいまさら(93) ATSAMD51、内蔵ペリフェラルの割り込み受け へ戻る