2009-09-02 18 views
8

私は組み込みシステムドメインで作業しています。私はマイクロコードからどのようにコードが実行されるのかを知りたいのですが(Cは一般的に主観的である必要はありません)、Cファイルから始まります。また、私は起動コード、オブジェクトファイルなどのようなものを知りたいと思います。私は上記のものに関するオンライン文書を見つけることができませんでした。可能であれば、最初からそれらのことを説明するリンクを提供してください。事前にお手数をおかけします。組み込みシステムでのコード実行

+0

マイクロコントローラのタイプを示すのに役立ちます。 –

+0

8051コントローラで作業中です。私はオペコードがどのようにフェッチされ、アセンブリ言語で実行されるかについて少し知っています)。複数のCファイルを持つプロジェクトがどのようにしてuC上で実行されているかを知りたがっています。 – inquisitive

+2

Cファイルは実行されません! :-)これらはオブジェクトファイルにコンパイルされ、フラッシュまたはRAMにロードされてそこから実行される最終実行可能イメージにリンクされます。 –

答えて

36

私はマイクロプロセッサアーキテクトであり、ソフトウェアのレベルを非常に低く抑える機会がありました。基本的に、低レベルの埋め込みは、ハードウェア固有のレベルでのみ一般的なPCプログラミングと非常に異なります。

低レベル組込みソフトウェアは、以下に分けることができる:

  1. Reset vector - これは通常アセンブリで書かれています。これはスタートアップ時に実行される最初のもので、ハードウェア固有のコードと見なすことができます。通常、レジスタなどを設定することによってプロセッサをあらかじめ定義された定常状態に設定するなどの単純な機能を実行します。その後、起動コードにジャンプします。最も基本的なリセットベクトルは、スタートアップコードに直接ジャンプするだけです。
  2. スタートアップコード - これは最初に実行されるソフトウェア固有のコードです。その仕事は、基本的にC言語のコードを実行できるようにソフトウェア環境を設定することです。たとえば、Cコードでは、スタックとヒープとして定義されたメモリ領域があると想定しています。これらは通常、ハードウェアではなくソフトウェア構成です。したがって、このスタートアップコードはスタックポインタとヒープポインタなどを定義します。これは通常 'c-runtime'の下にグループ化されています。 C++コードでは、コンストラクタも呼び出されます。ルーチンの最後に、main()が実行されます。 編集:初期化が必要な変数、およびクリアが必要なメモリの特定の部分がここで実行されます。基本的には、物事を「既知の状態」に移行させるために必要なものすべて。
  3. アプリケーションコード - これはmain()関数から始まる実際のCアプリケーションです。ご覧のように、実際には多くのことがフードの下にあり、最初の主な機能が呼び出される前に起きています。このコードは通常、利用可能な良いhardware abstraction layerがある場合、ハードウェアに依存しないものとして記述することができます。アプリケーションコードは、多くのライブラリ関数を必ず使用します。これらのライブラリは、通常、組み込みシステムで静的にリンクされています。
  4. ライブラリ - これらは、プリミティブなC関数を提供する標準のCライブラリです。ソフトウェア浮動小数点サポートのようなものを実装するプロセッサ固有のライブラリもあります。 I/Oデバイスにアクセスするためのハードウェア固有のライブラリやstdin/stdoutなどのライブラリもあります。いくつかの一般的なCライブラリはNewlibuClibcです。
  5. Interrupt/Exceptionハンドラ - これらは、ハードウェアまたはプロセッサの状態が変化した結果、通常のコード実行中にランダムに実行されるルーチンです。これらのルーチンは、実際に呼び出されるハードウェアにサービスを提供するために、最小限のソフトウェアオーバーヘッドで実行する必要があるため、通常はアセンブリで記述されます。

これは良いスタートを提供します。他の質問がある場合は、コメントを残しておきましょう。

+0

ターゲットに強打!ありがとうsybreon。今、メモリ割り当てはどうですか?私のuCにフラッシュメモリがあると仮定すると、プログラムで使用される静的変数と大域変数は、システムスタートアップコード、スタック内のローカル変数(RAM)、フラッシュメモリ(ROM) )。実際の実行は、フラッシュから各命令を実行することによって行われます。私は正しい? – inquisitive

+0

@ Guru_newbie: "フラッシュから各命令を実行することで実際の実行が行われます。"一部のプロセッサはフラッシュから直接コードを実行し、一部のプロセッサはコードから直接実行します。私は8051がフラッシュからコードを実行すると信じています。 PCのようなハイエンド(32ビット)の組み込みプロセッサは、アプリケーションコードをRAMにコピーし、RAMから実行します。 – simon

+0

@sybreon:ステップ2では、スタティック変数をRAMに設定し、初期化データもコピーします。 – simon

5

一般的に、汎用コンピュータよりもはるかに低いレベルで作業しています。

すべてのレジスタをクリアし、プログラムカウンタを0xf000に設定するなど、各CPUの電源投入時に特定の動作が発生します(ここの内容はすべて質問のとおりです)。

あなたのコードが正しい場所に確実に存在するようにすることが肝要です。

コンパイルプロセスは、通常、C言語を機械コード(オブジェクトファイル)に変換するという点で、汎用コンピュータに似ています。そこから、そのコードを:

  • あなたのシステムのスタートアップコード、しばしばアセンブラでリンクする必要があります。
  • 任意のランタイムライブラリ(C RTLの必須ビットを含む)。

システム起動コードは通常、ハードウェアを初期化して、Cコードが機能するように環境を設定します。組み込みシステムのランタイムライブラリでは、浮動小数点サポートやprintfなどの大きな巨大なものがオプションでコードの拡張を抑えることができます。

組み込みシステムのリンカは、通常、リロケータブルバイナリではなく固定場所コードを出力する方がずっと簡単です。起動コードが(たとえば)0xf000になるようにするために使用します。

組み込みシステムでは、一般に実行可能コードを最初からそこに置いて、EPROM(またはEEPROMまたはフラッシュまたはパワーダウン時にコンテンツを保持する他のデバイス)に書き込むことができます。

私の最後の努力は、8051と68302プロセッサを使用していたことに注意してください。今日の「組み込み」システムは、まさにあらゆる種類のすばらしいハードウェアを備えた本格的なLinuxボックスです。その場合、汎用と組み込みの間には大きな違いはありません。

しかし、私はそれを疑う。カスタムオペレーティングシステムやアプリケーションコードを必要とする非常に低仕様のハードウェアが必要です。

SPJ Embedded Technologiesは、自分の望むものと思われる8051開発環境のdownloadable evaluationを持っています。最大2Kのプログラムを作成することができますが、それはプロセス全体を処理するように見えます(リンクのコンパイル、ターゲットハードウェアにダンプするためのHEXまたはBINファイルの生成、オンチップのものや外部デバイスへのアクセスを可能にするシミュレータ)。

評価対象外の製品の価格は200ユーロですが、ほんの少しの遊びが必要な場合は、評価版をダウンロードするだけです.2Kの制限以外は完全な製品です。

+0

クイック返信ありがとう、pax。可能であれば、上のプロセスを説明するのに良いリンクを提供することができますか(実際のuCとは何か) – inquisitive

1

私はAVRマイクロコントローラでの経験を持っている、しかし、私は、これはほとんど同じ、それらのすべてのためになると思う。

コンパイルは通常のCコードと同じラインに沿って行きます。これはオブジェクトファイルにコンパイルされますが、これらはリンクされていますが、ELFやPEのような複雑な形式を出力するのではなく、ヘッダーなしでuCのメモリ内の固定アドレスに出力されます。

起動コード(コンパイラが生成する場合)は、通常のコンピュータの起動コードと同じ方法で追加されます。main()コードの前にコードが追加されています。

別の違いは、リンクされていることです。マイクロコントローラには動的リンクを処理するOSがないため、すべて静的にリンクする必要があります。

+0

ありがとうキューブ。実行可能ファイルがホストPCに作成され、uCの不揮発性メモリに格納されることを理解しました。その後実際のターゲットでどのように実際の実行が開始されるのかを知りたいと思います。どのようなオンラインドキュメンテーションか、事例研究が望ましい。 – inquisitive

+0

ELF/PEについてはあまり正確ではありません。組み込みシステムのリンカの多くはELFを出力しますが、その内部のバイナリコードは位置に依存しない固定アドレスです。したがって、Flashにロードするために、16進ファイル(Motorola S-recordまたはIntel Hex)またはストレートバイナリダンプ(開始アドレスを知っていると仮定して)を生成することが可能です。 –

2

私は、sybreonが "ステップ2"と呼んでいるものに最も関心がある印象を受けます。多くはそこで発生することができ、プラットフォームによって大きく異なります。通常、このようなものは、ブートローダー、ボードサポートパッケージ、Cランタイム(CRT)、そしてもしあなたが持っているなら、OSのいくつかの組み合わせによって処理されます。

通常、リセットベクタの後に、何らかの種類のブートローダがフラッシュから実行されます。このブートローダーは、ハードウェアをセットアップし、アプリのCRTにジャンプするだけでなく、フラッシュでも使用できます。この場合、CRTはおそらく.bssをクリアし、.dataをRAMにコピーします。他のシステムでは、ブートローダはELFのようなコード化されたファイルからアプリをスキャッタロードすることができ、CRTは他のシステムランタイムのもの(ヒープなど)。このすべては、CRTがアプリケーションのmain()を呼び出す前に発生します。

アプリケーションが静的にリンクされている場合、リンカディレクティブは.data/.bssとスタックが初期化されるアドレスを指定します。これらの値は、CRTにリンクされているか、ELFにコード化されています。動的にリンクされた環境では、アプリケーションの読み込みは通常、OSが指定するメモリに関係なくELFを再ターゲットするOSによって処理されます。

また、一部のターゲットはフラッシュからアプリケーションを実行するものもあれば、実行可能な.textをフラッシュからRAMにコピーするものもあります。

1

ジムリンチによって非常に詳細なGNU ARM Tutorialを見ることができます。

+0

ちょうど注意:ARMは、より複雑な組み込みシステムの一つです。スタートアップコードは、より小さいuCに比べて特に複雑です。 AVR。 –

2

よろしくお願いいたします。

ファーストオフのアーキテクチャ。フォンノイマン対ハーバード。ハーバードアーキテクチャは、コードとデータのために別々のメモリを持っています。フォンノイマンはしません。ハーバードは多くのマイクロコントローラで使用されており、私がよく知っているものです。

基本的なハーバードアーキテクチャから始めて、プログラムメモリがあります。マイクロコントローラが最初に起動すると、メモリ位置0の命令が実行されます。通常、これはメインコードが始まるコマンドに対処するJUMPです。

ここで、私が命令を言うとき、私はオペコードを意味します。オペコードは、バイナリデータ(通常は8または16ビット)にエンコードされた命令です。いくつかのアーキテクチャでは、各オペコードは特定のものを意味するようにハードコードされています。その他の場合、各ビットは重要です(ビット1はチェックキャリーを意味し、ビット2はチェックゼロフラグなどを意味します)。そのため、オペコードのためのオペコードとそれからパラメータがあります。 JUMP命令は、オペコードと、コードがジャンプする8または16または32ビットのメモリアドレスです。つまり、制御はそのアドレスの命令に移されます。これは、実行されるべき次の命令のアドレスを含む特別なレジスタを操作することによってこれを達成する。したがって、メモリ位置0x0050にJUMPすると、そのレジスタの内容が0x0050に置き換えられます。次のクロックサイクルで、プロセッサはレジスタを読んでメモリアドレスを探し出し、そこで命令を実行する。

命令を実行すると、マシンの状態が変化します。最後のコマンドが何をしたのかに関する情報を記録する一般的なステータスレジスタがあります(例えば、追加の場合、必要な実行があった場合、そのためのビットなどがあります)。命令の結果が配置される「アキュムレータ」レジスタがあります。命令のパラメータは、いくつかの汎用レジスタ、アキュムレータ、またはメモリアドレス(データORプログラム)のいずれかに入ることができます。異なるオペコードは、特定の場所のデータに対してのみ実行できます。たとえば、2つの汎用レジスタのデータを加算して結果をアキュムレータに表示することはできますが、2つのデータメモリの場所からデータを取得し、結果を別のデータメモリの場所に表示することはできません。必要なデータを汎用レジスタに移動し、追加して、結果を必要なメモリ位置に移動する必要があります。そういうわけで、アセンブリは難しいと考えられています。アーキテクチャが設計するのと同じ数のステータスレジスタがあります。より複雑なアーキテクチャでは、より複雑なコマンドを可能にするために、より多くのことがあります。より単純なものはできません。

また、スタックと呼ばれるメモリ領域もあります。これは、8051のようないくつかのマイクロコントローラのメモリ内の領域です。それ以外の場合は特別な保護を受けることができます。スタックの「トップ」がどのメモリ位置にあるかを記録するスタックポインタと呼ばれるレジスタがあります。アキュムレータからスタックに何かを押し込むと、「トップ」メモリアドレスがインクリメントされ、アキュムレータからのデータが元のアドレスに格納されます。スタックからデータを取得またはポップすると、その逆が実行され、スタックポインタがデクリメントされ、スタックからのデータがアキュムレータに格納されます。

今、私は命令が「実行される」方法についても賞賛しています。さて、これはあなたがデジタルロジック、つまりVHDLタイプのものになるときです。マルチプレクサとデコーダ、真理値表など。それはデザインの本当の根っからの砂です。したがって、メモリ位置の内容をアキュムレータに '移動'させたい場合は、アドレッシングロジックを把握し、アキュムレータレジスタをクリアし、メモリ位置のデータなどと一緒に配置しなければなりません。 VHDLやデジタルロジックのような別々の部分(アドレッシング、ハーフアダーなど)を実行していれば、必要なことが分かっているはずです。

これはCとどのように関連していますか?さて、コンパイラはC命令を受け取り、要求された操作を実行する一連のオペコードに変換します。そのすべては基本的には16進数のデータです - プログラムメモリのある点に置かれる1と0。これはコンパイラ/リンカのディレクティブで行われ、どのコードがどのメモリの場所に使用されているかを示します。チップ上のフラッシュメモリに書き込まれ、チップが再起動すると、プログラムメモリ内のコードの開始アドレスに0x0000とJUMPをコードメモリに格納し、次にオペコードでプラグインを開始します。

+0

リセット時に、プロセッサは、0x0000の位置にある場合とそうでない場合がある再起動ベクトルで実行を開始します。再起動ベクトルの位置については、特定のプロセッサのデータシートを参照する必要があります。 – tkyle

0

https://automotivetechis.wordpress.com/のリンクを参照してください。

以下の配列は、コントローラ命令実行の順序概観:

1)は、プログラムの実行のための主要なメモリを割り当て。

2)セカンダリからプライマリメモリにアドレス空間をコピーします。

3).textセクションと.dataセクションを実行可能ファイルからプライマリメモリにコピーします。

4)プログラムの引数(コマンドライン引数など)をスタックにコピーします。

5)レジスタを初期化する:スタックの先頭を指すようにesp(スタックポインタ)を設定し、残りをクリアします。

6)main()の引数をスタックからコピーしてmain()にジャンプします。

関連する問題