10

私は組み込みシステムドメインで働いています。Cファイルから始めて、マイクロコントローラー(一般的にuCは主観的である必要はありません)からコードがどのように実行されるかを知りたいです。また、スタートアップコードやオブジェクトファイルなどを知りたいのですが、上記に関するオンラインドキュメントは見つかりませんでした。可能であれば、それらを最初から説明するリンクを提供してください。よろしくお願いします

4

7 に答える 7

45

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

低レベルの組み込みソフトウェアは、次のように分類できます。

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

これが良いスタートを提供することを願っています。他にご不明な点がございましたら、お気軽にコメントをお寄せください。

于 2009-09-02T11:24:19.313 に答える
5

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

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

秘訣は、コードが適切な場所にあることを確認することです。

コンパイルプロセスは通常、Cをマシンコード(オブジェクトファイル)に変換するという点で汎用コンピューターに似ています。そこから、そのコードを次のコードにリンクする必要があります。

  • 多くの場合アセンブラにあるシステム起動コード。
  • ランタイムライブラリ(C RTLの必要なビットを含む)。

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

組み込みシステムのリンカも通常ははるかに単純で、再配置可能なバイナリではなく固定位置のコードを出力します。これを使用して、起動コードが(たとえば)0xf000になるようにします。

組み込みシステムでは、通常、実行可能コードを最初から存在させて、EPROM(またはEEPROM、フラッシュ、または電源切断時にコンテンツを維持するその他のデバイス)に書き込むことができます。

もちろん、私の最後の試みは8051と68302プロセッサであったことを覚えておいてください。最近の「組み込み」システムは、あらゆる種類の素晴らしいハードウェアを備えた本格的なLinuxボックスである可能性があります。その場合、汎用と組み込みの間に実際の違いはありません。

しかし、私はそれを疑っています。カスタムオペレーティングシステムやアプリケーションコードを必要とする、真剣に低スペックのハードウェアが依然として必要です。

SPJエンベデッドテクノロジーズは、8051開発環境のダウンロード可能な評価を持っており、あなたが望むものに見えます。最大2Kのサイズのプログラムを作成できますが、プロセス全体(リンクのコンパイル、ターゲットハードウェアにダンプするためのHEXまたはBINファイルの生成、オンチップのものや外部デバイスへのアクセスを提供するシミュレーターでさえ)を通過するようです。 )。

非評価製品の価格は200ユーロですが、ちょっとした遊びだけなら、評価をダウンロードするだけです。2Kの制限を除けば、完全な製品です。

于 2009-09-02T10:52:26.567 に答える
3

sybreonが「ステップ2」と呼んでいるものにあなたが最も興味を持っているという印象を受けます。そこでは多くのことが起こる可能性があり、プラットフォームによって大きく異なります。通常、このようなものは、ブートローダー、ボードサポートパッケージ、Cランタイム(CRT)、および存在する場合はOSの組み合わせによって処理されます。

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

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

また、一部のターゲットはフラッシュからアプリを実行しますが、他のターゲットは実行可能ファイルの.textをフラッシュからRAMにコピーします。(RAMはほとんどのターゲットでフラッシュよりも高速/幅が広いため、これは通常、速度とフットプリントのトレードオフです。)

于 2009-09-02T16:56:12.273 に答える
2

わかりました、これを試してみます...

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

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

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

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

スタックと呼ばれるメモリ領域もあります。これは、一部のマイクロコントローラー(8051など)のメモリ内の単なる領域です。他では、それは特別な保護を持つことができます。スタックの「最上位」にあるメモリ位置を記録するスタックポインタと呼ばれるレジスタがあります。アキュムレータからスタックに何かを「プッシュ」すると、「トップ」メモリアドレスがインクリメントされ、アキュムレータからのデータが前のアドレスに配置されます。スタックからデータを取得またはポップするときは、その逆が行われ、スタックポインターがデクリメントされ、スタックからのデータがアキュムレーターに入れられます。

今、私はまた、命令がどのように「実行される」かについて、ある種の釉薬をかけました。さて、これはあなたがデジタルロジックに取り掛かるときです-VHDLタイプのもの。マルチプレクサとデコーダ、真理値表など。それがデザインの本質です。したがって、メモリ位置の内容をアキュムレータに「移動」する場合は、アドレス指定ロジックを理解し、アキュムレータレジスタをクリアし、メモリ位置のデータと一緒にクリアする必要があります。 VHDLまたは任意のデジタルロジック方式で個別の部分(アドレス指定、ハーフアダーなど)を実行した場合、何が必要かがわかる場合があります。

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

于 2009-09-03T01:54:12.813 に答える
2

リンクhttps://automotivetechis.wordpress.com/を参照できます。

次のシーケンスは、コントローラー命令の実行シーケンスの概要を示しています。

1)プログラムの実行にプライマリメモリを割り当てます。

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

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

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

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

6)開始ルーチンにジャンプします。これは、main()の引数をスタックからコピーし、main()にジャンプします。

于 2016-05-24T18:43:41.280 に答える
1

私はAVRマイクロコントローラーの経験がありますが、これはすべてのマイクロコントローラーでほぼ同じだと思います。

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

スタートアップコード(コンパイラが生成する場合)は、「通常の」コンピュータのスタートアップコードと同じ方法で追加されます。つまり、main()コードの前(および場合によってはその後)にいくつかのコードが追加されます。

もう1つの違いはリンクです。マイクロコントローラーには動的リンクを処理するOSがないため、everythigは静的にリンクする必要があります。

于 2009-09-02T11:00:07.787 に答える
1

JimLynchによる非常に詳細なGNUARMチュートリアルを見ることができます。

于 2009-09-02T19:49:26.527 に答える