実行が最終return
ステートメントに達すると、組み込みプロセッサで何が起こるかすべてがそのままフリーズします。電力消費など、1つの長い永遠のNOPが空にありますか?またはNOPは継続的に実行されますか、それともプロセッサは完全にシャットダウンしますか?
私が尋ねる理由の一部は、実行を終了する前にプロセッサの電源を切る必要があるのか、それとも前に電源を切った場合にどのように実行を終了するのか疑問に思っていますか?
実行が最終return
ステートメントに達すると、組み込みプロセッサで何が起こるかすべてがそのままフリーズします。電力消費など、1つの長い永遠のNOPが空にありますか?またはNOPは継続的に実行されますか、それともプロセッサは完全にシャットダウンしますか?
私が尋ねる理由の一部は、実行を終了する前にプロセッサの電源を切る必要があるのか、それとも前に電源を切った場合にどのように実行を終了するのか疑問に思っていますか?
回答:
これは私の父がいつも私に尋ねていた質問です。「すべての指示を実行し、最後に停止しないのはなぜですか?」
病理学的な例を見てみましょう。次のコードは、PIC18用にMicrochip社のC18コンパイラでコンパイルされました。
void main(void)
{
}
次のアセンブラー出力を生成します。
addr opco instruction
---- ---- -----------
0000 EF63 GOTO 0xc6
0002 F000 NOP
0004 0012 RETURN 0
.
. some instructions removed for brevity
.
00C6 EE15 LFSR 0x1, 0x500
00C8 F000 NOP
00CA EE25 LFSR 0x2, 0x500
00CC F000 NOP
.
. some instructions removed for brevity
.
00D6 EC72 CALL 0xe4, 0 // Call the initialisation code
00D8 F000 NOP //
00DA EC71 CALL 0xe2, 0 // Here we call main()
00DC F000 NOP //
00DE D7FB BRA 0xd6 // Jump back to address 00D6
.
. some instructions removed for brevity
.
00E2 0012 RETURN 0 // This is main()
00E4 0012 RETURN 0 // This is the initialisation code
ご覧のとおり、main()が呼び出され、最後にreturnステートメントが含まれていますが、明示的にそこに配置しませんでした。mainが戻ると、CPUはコードの先頭に戻るための単純なGOTOである次の命令を実行します。main()は単純に何度も呼び出されます。
さて、これを言って、これは人々が通常のことをする方法ではありません。main()がそのように終了できる埋め込みコードを書いたことはありません。ほとんどの場合、私のコードは次のようになります。
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
そのため、通常はmain()を終了させません。
「OK OK」と言います。これらはすべて非常に興味深いものであり、コンパイラは最後のreturnステートメントがないことを確認します。しかし、問題を強制するとどうなりますか?アセンブラを手動でコーディングし、ジャンプを最初に戻さないとどうなりますか?
まあ、明らかにCPUは次の命令を実行し続けるだけです。これらは次のようになります。
addr opco instruction
---- ---- -----------
00E6 FFFF NOP
00E8 FFFF NOP
00EA FFFF NOP
00EB FFFF NOP
.
. some instructions removed for brevity
.
7EE8 FFFF NOP
7FFA FFFF NOP
7FFC FFFF NOP
7FFE FFFF NOP
main()の最後の命令の後の次のメモリアドレスは空です。フラッシュメモリを備えたマイクロコントローラでは、空の命令には値0xFFFFが含まれます。少なくともPICでは、そのopコードは「nop」または「no operation」として解釈されます。単に何もしません。CPUはメモリの最後までずっとこれらのnopを実行し続けます。
その後は?
最後の命令では、CPUの命令ポインターは0x7FFeです。CPUがその命令ポインターに2を追加すると、32x FLASHのPICでオーバーフローと見なされる0x8000を取得するため、0x0000に戻り、CPUはコードの先頭で命令を実行し続けます、まるでリセットされたかのように。
また、電源を切る必要性についても尋ねました。基本的には何でも好きなことができますが、それはアプリケーションによって異なります。
電源をオンにした後、1つのことだけを行う必要があるアプリケーションがあり、その後何もしない場合は、while(1)を置くだけで済みます。main()の最後で、CPUが目立った処理を行わないようにします。
アプリケーションでCPUの電源を切る必要がある場合は、CPUに応じて、おそらくさまざまなスリープモードが利用できます。ただし、CPUには再び目を覚ます習慣があるため、スリープ時間に制限がないこと、アクティブなウォッチドッグタイマーなどがないことを確認する必要があります。
CPUが終了したときに、CPUが自身の電力を完全にカットできるようにする外部回路を編成することもできます。この質問を参照してください:ラッチオン/オフトグルスイッチとして瞬間的なプッシュボタンを使用します。
ここには2つのポイントがあります。
プログラムのシャットダウンの概念は、通常、組み込み環境には存在しません。低レベルでは、CPUは可能な限り命令を実行します。「final return statement」のようなものはありません。CPUは、回復不能な障害が発生した場合、または明示的に停止した場合(スリープモード、低電力モードなど)、実行を停止する場合がありますが、スリープモードまたは回復不能な障害でさえ、通常、これ以上コードが実行されないことを保証しないことに注意してください実行されます。スリープモードからウェイクアップすることができ(通常の使用方法です)、ロックされたCPUでさえNMIハンドラーを実行できます(これはCortex-Mの場合です)。ウォッチドッグも引き続き実行されますが、有効にすると、一部のマイクロコントローラーでウォッチドッグを無効にできない場合があります。詳細はアーキテクチャによって大きく異なります。
CやC ++などの言語で書かれたファームウェアの場合、main()が終了するとどうなるかは、起動コードによって決まります。たとえば、STM32 Standard Peripheral Libraryのスタートアップコードの関連部分は次のとおりです(GNUツールチェーンの場合、コメントは私のものです)。
Reset_Handler:
/* ... */
bl main ; call main(), lr points to next instruction
bx lr ; infinite loop
このコードは、main()が戻るときに無限ループに入りますが、非自明な方法で(実質的にそれ自体へのジャンプである次の命令のアドレスをbl main
ロードlr
します)CPUを停止したり、低電力モードに入るなどの試みは行われません。アプリケーションでそのいずれかが正当に必要な場合は、自分で行う必要があります。
ARMv7-M ARM A2.3.1で指定されているように、リセット時にリンクレジスタが0xFFFFFFFFに設定され、そのアドレスへの分岐が障害をトリガーすることに注意してください。そのため、Cortex-Mの設計者は、リセットハンドラーからの復帰を異常として扱うことにしました。
ファームウェアの終了後にCPUを停止する正当な必要性について言えば、デバイスのパワーダウンによってうまく機能しないものを想像することは困難です。(CPUを「永久に」無効にした場合、デバイスにできることは電源の再投入または外部ハードウェアのリセットだけです。)DC / DCコンバーターのENABLE信号をディアサートするか、電源をオフにします。他の方法、ATX PCのように。
loop: b loop
。彼らは実際にリターンをするつもりだったのに、貯金を忘れたのかしらlr
。
について尋ねるときreturn
、あなたは高すぎるレベルを考えています。Cコードはマシンコードに変換されます。そのため、プロセッサが盲目的にメモリから命令を引き出して実行することを考えた場合、どちらが「最終」であるかはわかりませんreturn
。したがって、プロセッサには固有の終わりはありませんが、代わりにエンドケースを処理するのはプログラマ次第です。レオンが彼の答えで指摘しているように、コンパイラはデフォルトの動作をプログラムしましたが、多くの場合、プログラマーは独自のシャットダウンシーケンスを必要とする場合がありますで再起動します)。
多くのマイクロプロセッサには停止命令があり、これにより周辺機関に影響を与えることなくプロセッサが停止します。他のプロセッサは、単に同じアドレスに繰り返しジャンプするだけで「停止」に依存する場合があります。選択肢はいくつかありますが、メモリが命令であることが意図されていない場合でも、プロセッサが単純に命令を読み続けるため、プログラマ次第です。
問題は埋め込まれていません(組み込みシステムはLinuxまたはWindowsさえ実行できます)が、スタンドアロンまたはベアメタル:(コンパイルされた)アプリケーションプログラムがコンピューターで実行されている唯一のものです(それが問題であるかどうかは関係ありません)マイクロコントローラまたはマイクロプロセッサ)。
ほとんどの言語では、「メイン」が終了し、戻るOSがないときに何が起こるかは言語で定義されていません。Cの場合、startupfile(多くの場合crt0.s)の内容によって異なります。ほとんどの場合、ユーザーはスタートアップコードを提供することができる(またはする必要があります)ため、最終的な答えは次のとおりです。
実際には、3つのアプローチがあります。
特別な措置を講じないでください。mainが戻るときに何が起こるかは未定義です。
0にジャンプするか、他の手段を使用してアプリケーションを再起動します。
タイトループに入り(または割り込みを無効にして停止命令を実行)、プロセッサを永久にロックします。
適切なものはアプリケーションによって異なります。ファーエリスのグリーティングカードとブレーキ制御システム(2つの組み込みシステムを言うだけでも)はおそらく再起動するはずです。再起動の欠点は、問題が気付かれない可能性があることです。
先日、逆アセンブルされた(avr-gccでコンパイルされたC ++)コードを探していましたが、コードの最後で何が行われるかは0x0000にジャンプします。基本的にリセット/再起動を行います。
0x0000への最後のジャンプがコンパイラ/アセンブラによって省略されている場合、プログラムメモリ内のすべてのバイトは「有効な」マシンコードとして解釈され、プログラムカウンタが0x0000にロールオーバーするまでずっと実行されます。
AVRでは、00バイト(セルが空の場合のデフォルト値)はNOP = No Operationです。そのため、非常に高速で実行され、少し時間がかかるだけです。
通常、コンパイルされたmain
コードは、その後スタートアップコードとリンクされます(ツールチェーンに統合され、チップベンダーから提供され、ユーザーが作成するなど)。
その後、リンカーはすべてのアプリケーションおよびスタートアップコードをメモリセグメントに配置します。そのため、質問への回答は次の要素に依存します。
bl lr
またはb .
)で終了します。これは「プログラム終了」に似ていますが、以前に有効にした割り込みと周辺機器は引き続き動作します。main
)。呼び出しがmain
戻った後、「次はどうなるか」を単に無視します。
main
動作から復帰した後にプログラムカウンターが単純に増加する場合、リンカー(および/またはリンク中に使用されるリンカースクリプト)に依存します。main
無効/未定義の引数値で実行される他の関数/コードがあなたの後に置かれる場合、
ウォッチドッグが有効になっている場合、すべての無限ループにかかわらず、最終的にMCUがリセットされます(もちろん、リロードされない場合)。
それはマニュアルで明確に説明されていました。通常、CPUはスタックセグメントの外側にあるメモリ位置にアクセスするため、一般的な例外がスローされます。[メモリ保護例外]。
組み込みシステムとはどういう意味ですか?マイクロプロセッサまたはマイクロコントローラ?どちらの方法でも、マニュアルで定義されています。
x86 CPUでは、コマンドをACIPコントローラーに送信してコンピューターの電源を切ります。システム管理モードに入る。そのため、このコントローラーはI / Oチップであり、手動でオフにする必要はありません。
詳細については、ACPI仕様をお読みください。