組み込みプログラムが終了するとどうなりますか?


29

実行が最終returnステートメントに達すると、組み込みプロセッサで何が起こるかすべてがそのままフリーズします。電力消費など、1つの長い永遠のNOPが空にありますか?またはNOPは継続的に実行されますか、それともプロセッサは完全にシャットダウンしますか?

私が尋ねる理由の一部は、実行を終了する前にプロセッサの電源を切る必要があるのか​​、それとも前に電源を切った場合にどのように実行を終了するのか疑問に思っていますか?


22
それはあなたの信念による。生まれ変わると言う人もいます。
Telaclavo

9
ミサイル用ですか?
リーKowalkowski

6
一部のシステムは、HCF(Halt and Catch Fire)命令をサポートしています。:)
ステファンポールノアック

1
自己破壊ルーチン

回答:


41

これは私の父がいつも私に尋ねていた質問です。「すべての指示を実行し、最後に停止しないのはなぜですか?

病理学的な例を見てみましょう。次のコードは、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が自身の電力を完全にカットできるようにする外部回路を編成することもできます。この質問を参照してください:ラッチオン/オフトグルスイッチとして瞬間的なプッシュボタンを使用します


20

コンパイルされたコードの場合、コンパイラに依存します。私が使用するRowley CrossWorks gcc ARMコンパイラは、無限ループを持つcrt0.sファイル内のコードにジャンプします。16ビットdsPICおよびPIC24デバイス用のMicrochip C30コンパイラ(これもgccに基づいています)は、プロセッサをリセットします。

もちろん、ほとんどの組み込みソフトウェアは、そのように終了することはなく、ループ内でコードを継続的に実行します。


13

ここには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のように。


1
「スリープモードから復帰できます(通常の使用方法です)。ロックされたCPUでもNMIハンドラーを実行できます(Cortex-Mの場合)。<-の素晴らしい部分のように聞こえます本や映画のプロット。:)
マークアレン

「bl main」は「lr」に次の命令のアドレス(「bx lr」)をロードしますが、ロードしませんか?「bx lr」の実行時に「lr」に他の何かが含まれることを期待する理由はありますか?
-supercat

@supercat:もちろんです。回答を編集してエラーを削除し、少し展開しました。これを考えると、彼らがこのループを実装する方法はかなり奇妙です。彼らは簡単にできたでしょうloop: b loop。彼らは実際にリターンをするつもりだったのに、貯金を忘れたのかしらlr
ソーン

好奇心が強いです。多くのARMコードは、エントリ時に保持したのと同じ値を保持するLRで終了すると予想しますが、それが保証されていることはわかりません。このような保証はあまり有用ではありませんが、それを維持するには、r14を他のレジスタにコピーしてから他のルーチンを呼び出すルーチンに命令を追加する必要があります。戻り時にlrが「不明」と見なされる場合、保存されたコピーを保持しているレジスタを「bx」することができます。ただし、指定されたコードでは非常に奇妙な動作が発生します。
supercat

実際、葉以外の関数はlrを節約すると期待されています。これらは通常、プロローグのスタックにlrをプッシュし、保存された値をpcにポップして戻ります。これは、たとえばCまたはC ++ main()が行うことですが、問題のライブラリの開発者は明らかにReset_Handlerでこのようなことをしませんでした。
ソーン

9

について尋ねるときreturn、あなたは高すぎるレベルを考えています。Cコードはマシンコードに変換されます。そのため、プロセッサが盲目的にメモリから命令を引き出して実行することを考えた場合、どちらが「最終」であるかはわかりませんreturn。したがって、プロセッサには固有の終わりはありませんが、代わりにエンドケースを処理するのはプログラマ次第です。レオンが彼の答えで指摘しているように、コンパイラはデフォルトの動作をプログラムしましたが、多くの場合、プログラマーは独自のシャットダウンシーケンスを必要とする場合がありますで再起動します)。

多くのマイクロプロセッサには停止命令があり、これにより周辺機関に影響を与えることなくプロセッサが停止します。他のプロセッサは、単に同じアドレスに繰り返しジャンプするだけで「停止」に依存する場合があります。選択肢はいくつかありますが、メモリが命令であることが意図されていない場合でも、プロセッサが単純に命令を読み続けるため、プログラマ次第です。


7

問題は埋め込まれていません(組み込みシステムはLinuxまたはWindowsさえ実行できます)が、スタンドアロンまたはベアメタル:(コンパイルされた)アプリケーションプログラムがコンピューターで実行されている唯一のものです(それが問題であるかどうかは関係ありません)マイクロコントローラまたはマイクロプロセッサ)。

ほとんどの言語では、「メイン」が終了し、戻るOSがないときに何が起こるかは言語で定義されていません。Cの場合、startupfile(多くの場合crt0.s)の内容によって異なります。ほとんどの場合、ユーザーはスタートアップコードを提供することができる(またはする必要があります)ため、最終的な答えは次のとおりです。

実際には、3つのアプローチがあります。

  • 特別な措置を講じないでください。mainが戻るときに何が起こるかは未定義です。

  • 0にジャンプするか、他の手段を使用してアプリケーションを再起動します。

  • タイトループに入り(または割り込みを無効にして停止命令を実行)、プロセッサを永久にロックします。

適切なものはアプリケーションによって異なります。ファーエリスのグリーティングカードとブレーキ制御システム(2つの組み込みシステムを言うだけでも)はおそらく再起動するはずです。再起動の欠点は、問題が気付かれない可能性があることです。


5

先日、逆アセンブルされた(avr-gccでコンパイルされたC ++)コードを探していましたが、コードの最後で何が行われるかは0x0000にジャンプします。基本的にリセット/再起動を行います。

0x0000への最後のジャンプがコンパイラ/アセンブラによって省略されている場合、プログラムメモリ内のすべてのバイトは「有効な」マシンコードとして解釈され、プログラムカウンタが0x0000にロールオーバーするまでずっと実行されます。

AVRでは、00バイト(セルが空の場合のデフォルト値)はNOP = No Operationです。そのため、非常に高速で実行され、少し時間がかかるだけです。


1

通常、コンパイルされたmainコードは、その後スタートアップコードとリンクされます(ツールチェーンに統合され、チップベンダーから提供され、ユーザーが作成するなど)。

その後、リンカーはすべてのアプリケーションおよびスタートアップコードをメモリセグメントに配置します。そのため、質問への回答は次の要素に依存します。

  • 空のループ(bl lrまたはb .)で終了します。これは「プログラム終了」に似ていますが、以前に有効にした割り込みと周辺機器は引き続き動作します。
  • プログラムの先頭にジャンプして終了します(スタートアップを完全に再実行するか、jsutに移動しますmain)。
  • 呼び出しがmain戻った後、「次はどうなるか」を単に無視します。

    1. 3番目の箇条書きでは、main動作から復帰した後にプログラムカウンターが単純に増加する場合、リンカー(および/またはリンク中に使用されるリンカースクリプト)に依存します。
  • main無効/未定義の引数値で実行される他の関数/コードがあなたの後に置かれる場合、

  • 次のメモリが不正な命令例外で始まる場合、MCUが生成され、MCUは最終的にリセットされます(例外がリセットを生成する場合)。

ウォッチドッグが有効になっている場合、すべての無限ループにかかわらず、最終的にMCUがリセットされます(もちろん、リロードされない場合)。


-1

組み込みデバイスを停止する最良の方法は、NOP命令で永久に待機することです。

2番目の方法は、デバイス自体を使用してデバイスを閉じることです。あなたがあなたの指示でリレーを制御することができた場合、あなたは自分の組み込み機器に電力を供給しているスイッチを開くことができハァッあなたの組み込みデバイスが無消費電力でなくなっています。


それは本当に質問に答えません。
マットヤング14

-4

それはマニュアルで明確に説明されていました。通常、CPUはスタックセグメントの外側にあるメモリ位置にアクセスするため、一般的な例外がスローされます。[メモリ保護例外]。

組み込みシステムとはどういう意味ですか?マイクロプロセッサまたはマイクロコントローラ?どちらの方法でも、マニュアルで定義されています。

x86 CPUでは、コマンドをACIPコントローラーに送信してコンピューターの電源を切ります。システム管理モードに入る。そのため、このコントローラーはI / Oチップであり、手動でオフにする必要はありません。

詳細については、ACPI仕様をお読みください。


3
-1:TSは特定のCPUに言及していなかったので、あまり想定しないでください。さまざまなシステムがこのケースを非常に異なる方法で処理します。
Wouter van Ooijen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.