Comments
Description
Transcript
PDF version - ARM Information Center
RealView Compilation Tools ® バージョン 3.0 デベロッパガイド Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ RealView Compilation Tools デベロッパガイド Copyright © 2002-2006 ARM Limited. All rights reserved. リリース情報 本書には以下の変更が追加されています。 変更履歴 日付 発行 機密保持ステータス 変更 2002 年 8 月 A 非機密扱い 第 1.2 版 2003 年 1 月 B 非機密扱い 第 2.0 版 2003 年 9 月 C 非機密扱い RVDS v2.0 リリース(第 2.0.1 版) 2004 年 1 月 D 非機密扱い RVDS v2.1 リリース(第 2.1 版) 2004 年 12 月 E 非機密扱い RVDS v2.2 リリース(第 2.2 版) 2005 年 5 月 F 非機密扱い RVDS v2.2 SP1 リリース(第 2.2 版) 2006 年 3 月 G 非機密扱い RVDS 3.0 リリース(第 3.0 版) 著作権 または ™ のマークが付いた言葉およびロゴは、ARM Limited が所有する登録商標または商標で す。本書に記載されている他の製品名は、各社の所有する商標です。 ® 本書に記載されている情報の全部または一部、ならびに本書で紹介する製品は、著作権所有者の 文書による事前の許可を得ない限り、転用・複製することを禁じます。 本書に記載されている製品は、今後も継続的に開発・改良の対象となります。本書に含まれる製 品およびその利用方法についての情報は、ARM が利用者の利益のために提供するものです。した がって当社では、製品の市販性または利用の適切性を含め、暗示的・明示的に関係なく一切の責 任を負いません。 本書は、本製品の利用者をサポートすることだけを目的としています。本書に記載されている情 報の使用、情報の誤りまたは省略、あるいは本製品の誤使用によって発生したいかなる損失・損 傷についても、ARM Limited は一切責任を負いません。 機密保持ステータス 本書は非機密扱いであり、本書を使用、複製、および開示する権利は、ARM および ARM が本書 を提供した当事者との間で締結した契約の条項に基づいたライセンスの制限により異なります。 製品ステータス 本書の情報は最終版であり、開発済み製品に対応しています。 Web アドレス http://www.arm.com ii Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 目次 RealView Compilation Tools デベロッパガイド 序章 本書について ................................................................................................. vi フィードバック .............................................................................................. ix 第1章 はじめに 1.1 1.2 1.3 1.4 第2章 組み込みソフトウェアの開発 2.1 2.2 2.3 2.4 2.5 2.6 ARM DUI 0203GJ RVCT について ........................................................................................... 1-2 プログラミングに関する一般的な問題 ....................................................... 1-3 ARM プロセッサをターゲットとした開発 ................................................. 1-9 ARM アーキテクチャ v6 のサポート ........................................................ 1-14 組み込みソフトウェアの開発について ....................................................... 2-2 ターゲットシステムが存在しない場合の RealView Compilation Tools の デフォルトの動作 ....................................................................................... 2-4 ターゲットハードウェアに合わせた C ライブラリのカスタマイズ ......... 2-11 ターゲットハードウェアに合わせたイメージのメモリマップの カスタマイズ ............................................................................................ 2-14 リセットと初期化 ..................................................................................... 2-25 メモリマップに関するその他の注意事項 ................................................. 2-36 Copyright © 2002-2006 ARM Limited. All rights reserved. iii 目次 第3章 位置非依存コードとデータの記述 3.1 3.2 3.3 第4章 ARM と Thumb のインターワーク 4.1 4.2 4.3 4.4 第5章 5-2 5-4 5-5 5-7 プロセッサ例外について ............................................................................ 6-2 プロセッサ状態の判断 ................................................................................ 6-5 例外の開始と終了 ....................................................................................... 6-7 例外処理 ................................................................................................... 6-12 例外ハンドラのインストール ................................................................... 6-13 SVC ハンドラ ........................................................................................... 6-18 割り込みハンドラ ..................................................................................... 6-28 リセットハンドラ ..................................................................................... 6-38 未定義命令ハンドラ ................................................................................. 6-39 プリフェッチアボートハンドラ ............................................................... 6-40 データアボートハンドラ .......................................................................... 6-41 例外ハンドラのチェイン .......................................................................... 6-42 システムモード ........................................................................................ 6-44 デバッグ通信チャネル 7.1 7.2 7.3 7.4 7.5 iv インラインアセンブラと組み込みアセンブラの使用 ................................. アセンブリコードから C グローバル変数へのアクセス ............................ C++ からの C ヘッダファイルの使用 ........................................................ C、C++、およびアセンブリ言語間の呼び出し ......................................... プロセッサ例外処理 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 第7章 インターワークについて ............................................................................ 4-2 アセンブリ言語のインターワーク .............................................................. 4-7 C と C++ のインターワークとベニア ...................................................... 4-13 ベニアを使用したアセンブリ言語のインターワーク ............................... 4-19 C、C++、およびアセンブリ言語の混用 5.1 5.2 5.3 5.4 第6章 位置非依存 .................................................................................................. 3-2 読み出し専用の位置非依存 ........................................................................ 3-3 読み出し - 書き込みの位置非依存 .............................................................. 3-6 デバッグ通信チャネルについて ................................................................. ターゲットのデータ転送 ............................................................................ デバッグ通信のポーリング ........................................................................ 割り込み駆動型デバッグ通信 ..................................................................... Thumb 状態からのアクセス ....................................................................... Copyright © 2002-2006 ARM Limited. All rights reserved. 7-2 7-3 7-4 7-8 7-9 ARM DUI 0203GJ 序章 本章では、RealView Compilation Tools デベロッパガイド(本書)について概説します。 本書は以下のセクションから構成されています。 • 本書について(P. vi) • ARM DUI 0203GJ フィードバック(P. ix) Copyright © 2002-2006 ARM Limited. All rights reserved. v 序章 本書について 本書には、ARM® ファミリの Reduced Instruction Set Computing(RISC)プロセッサ用の コードを開発する際に発生する特定の問題に関する情報が記載されています。本書の 各章の説明、および使用するサンプルは、RealView® Compilation Tools(RVCT)の最新 リリースを使用してコードを開発していることを前提としています。 対象読者 本書は、ARM アーキテクチャベースのコードを記述しているすべての開発者を対象と しています。本書の内容は、RealView Compilation Tools v3.0 Essentials Guide で説明され ている ARM 開発ツールを熟知した経験豊富なソフトウェア開発者を対象に書かれて います。 本書の構成 本書は以下の章と付録から構成されています。 第 1 章 はじめに RVCT の概要について説明します。 第 2 章 組み込みソフトウェアの開発 RVCT を使用して組み込みアプリケーションを開発する方法について 説明します。ターゲットシステムが存在しない場合の RVCT のデフォル トの動作について、および C ライブラリとイメージのメモリマップを ターゲットシステムに合わせてカスタマイズする方法についても説明 します。 第 3 章 位置非依存コードとデータの記述 Procedure Call Standard for the ARM Architecture(AAPCS)を使用する位置 非依存コードとデータを記述する方法について説明します。 第 4 章 ARM と Thumb のインターワーク Thumb 命令セットを実装するプロセッサ用のコードを記述する際に ARM 状態と Thumb 状態を切り替える方法について説明します。 第 5 章 C、C++、およびアセンブリ言語の混用 C、C++、および ARM アセンブリ言語の混合コードを記述する方法を説 明します。また、C および C++ から ARM インラインと組み込みアセン ブラを使用する方法についても説明します。 vi Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 序章 第 6 章 プロセッサ例外処理 ARM プロセッサでサポートされている各種例外の処理方法について説 明します。 第 7 章 デバッグ通信チャネル デバッグ通信チャネル(DCC)の使用方法について説明します。 本書では、ARM ソフトウェアがデフォルトの場所にインストールされていることを前 提としています。例えば、Windows 環境では、デフォルトの場所は volume:\Program Files\ARM になります。パス名を参照する際、install_directory の部分をこの場所に読 み替えて下さい。例えば、本書では、install_directory\Documentation\... のような パス名が使用されます。ARM ソフトウェアを別の場所にインストールした場合は、 ファイルパスの見方を変える必要があります。 表記規則 本書では以下の表記規則を使用しています。 monospace コマンド、ファイル名、プログラム名、ソースコードなど、キーボード から入力可能なテキストを示しています。 monospace コマンドまたはオプションに使用可能な略語を示します。コマンド名ま たはオプション名をすべて入力する代わりに、下線部分の文字だけを入 力することができます。 monospace italic コマンドまたは関数の引数で、特定の値に置き換えることが可能なもの を示しています。 monospace bold サンプルコード以外に使用される言語キーワードを示しています。 ARM DUI 0203GJ italic 重要事項、重要用語、相互参照、引用箇所を斜体で記載しています。 bold メニュー名などのユーザインタフェース要素を太字で記載しています。 また、必要に応じて記述リスト内の重要箇所、ARM プロセッサの信号 名、重要用語、および専門用語にも太字を使用しています。 Copyright © 2002-2006 ARM Limited. All rights reserved. vii 序章 参考資料 ここでは、ARM プロセッサファミリのコード開発に関する補足情報を記載した ARM Limited および各社の出版物を紹介します。 ARM は自社出版物の定期的な更新・修正を行っています。最新の正誤表、追補表、ARM に関する FAQ については、http://www.arm.com をご覧下さい。 ARM の出版物 このマニュアルでは、ARM ファミリのプロセッサ向けアプリケーション開発に関する 一般的な情報を提供しています。他のコンポーネントの詳細については、以下の RVCT 付属マニュアルを参照して下さい。 • RealView Compilation Tools v3.0 基本操作ガイド(ARM DUI 0202J) • RealView Compilation Tools v3.0 コンパイラ / ライブラリガイド (ARM DUI 0205J) • RealView Compilation Tools v3.0 リンカ / ユーティリティガイド (ARM DUI 0206J) • RealView Compilation Tools v3.0 アセンブラガイド(ARM DUI 0204J) • RealView Development Suite 用語集(ARM DUI 0324J) base standard、ソフトウェアインタフェース、および ARM でサポートされている標準 に関する詳細については、install_directory\Documentation\Specifications\... を参 照して下さい。 特定の ARM 製品に関する情報については、以下のマニュアルを参照して下さい。 • ARM アーキテクチャリファレンスマニュアル(ARM DUI 0100J-00) • ARM Reference Peripheral Specification(ARM DDI 0062) • お使いのハードウェアデバイスの ARM データシートまたはテクニカルリファレ ンスマニュアル 他の出版物 ARM アーキテクチャに関する一般的な情報については、以下の出版物を参照して下 さい。Steve Furber, ARM System-on-Chip Architecture (2nd edition, 2000), Addison Wesley, ISBN 0-201-67519-6. viii Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 序章 フィードバック ARM Limited では、RealView Compilation Tools および本書に関するフィードバックをお 待ちしております。 RealView Compilation Tools に関するフィードバック RVCT に関して問題がある場合は、購入元にお問い合わせ下さい。このとき、迅速かつ 適切な対応をさせて頂くために、以下の情報をご用意下さい。 • お名前と会社名 • 製品のシリアル番号 • 製品のリリース情報 • プラットフォームの詳細(ハードウェアプラットフォーム、オペレーティングシ ステムの種類とバージョンなど) • 問題を再現するサイズの小さな独立したサンプルコード • 操作の目的と実際の動作に関する詳しい説明 • 使用したコマンド(コマンドラインオプションを含む) • 問題を再現できるサンプル出力 • ツールのバージョン情報(バージョン番号、日付を含む) 本書に関するフィードバック 本 書 に 関 す る ご 意 見 に つ き ま し て は、以 下 の 内 容 を 記 載 し た 電 子 メ ー ル を [email protected] までお送り下さい。 • • • • マニュアル名 文書番号 問題のあるページ番号 問題点の簡潔な説明 補足すべき点や改善すべき点についてのご提案もお待ちしております。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. ix 序章 x Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第1章 はじめに 本章では、本書について概説し、RealView® Compilation Tools(RVCT)を使用した コードの開発方法に関する説明を始めます。本章は以下のセクションから構成されて います。 • RVCT について(P. 1-2) ARM DUI 0203GJ • プログラミングに関する一般的な問題(P. 1-3) • ARM プロセッサをターゲットとした開発(P. 1-9) • ARM アーキテクチャ v6 のサポート(P. 1-14) Copyright © 2002-2006 ARM Limited. All rights reserved. 1-1 はじめに 1.1 RVCT について RVCT は、サポートドキュメントとサンプルを備えたアプリケーションスイートで構成 されており、ARM® ファミリの RISC プロセッサ用アプリケーションのプログラミング を実現します。RVCT を使用すると、C、C++、および ARM アセンブリ言語でプログ ラムをビルドできます。 本書では、ARM ベースのシステム用のコードを開発する際の具体的な問題について説 明します。本書の各章の説明、および本書で使用するサンプルは、RVCT の最新リリー スを使用してコードを開発していることを前提としています。 以前のリリースから RVCT にアップグレードしている場合は、RealView Compilation Tools v3.0 Essentials Guide を読んで、このリリースの新機能と拡張機能について確認し て下さい。 RVCT を初めて使用する場合は、RealView Compilation Tools v3.0 Essentials Guide を読ん で、ARM ツールの概要と、ARM ツールを開発プロジェクトで使用する方法の概要を 確認して下さい。 RVCT の以前のリリースの詳細については、RealView Compilation Tools v3.0 Essentials Guide の付録 A を参照して下さい。 ARM アセンブラ、コンパイラ、およびサポートしているソフトウェアに関する情報が 「ARM の出版 記載された、RVCT 付属マニュアルの他のマニュアルの一覧については、 物」(P. viii)を参照して下さい。 1.1.1 サンプルの使用方法 本書では、RealView Development Suite の主なサンプルディレクトリの install_directory\RVDS\Examples にあるサンプルを参照します。提供されているサン プルの概要については、RealView Development Suite スタートガイドを参照して下さい。 1-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.2 プログラミングに関する一般的な問題 ARM プロセッサは Reduced Instruction Set Computing(RISC)プロセッサです。RISC プ ロセッサ用の効率的なコードを記述するためのプログラミング手法は多数あります。 多くの RISC プロセッサと同様に、ARM プロセッサは、境界で整列されるデータにア クセスするように設計されています。つまり、4 の倍数となるアドレス上に存在する ワードと、2 の倍数となるアドレス上に存在するハーフワードがアクセスされます。こ のデータは、データ自身と同じサイズの境界上に配置されます。 通常、ARM コンパイラでは、LDR 命令と STR 命令を使用してグローバル変数に効率的 にアクセスできるように、これらの変数をデータ自身と同じサイズの境界で整列させ ます。 これは、 非境界整列データに命令から直接アクセスできるほとんどの Complex Instruction Set Computing(CISC)アーキテクチャとは対照的です。このため、CISC アーキテクチャ 向けの従来のコードを ARM プロセッサに移植する場合には注意が必要です。特に、非 境界整列データへのアクセスは、コードサイズやパフォーマンスに影響する可能性があ ります。 注 ARM11 プロセッサでは、ハードウェアでの非境界整列アクセスをサポートしています。 このセクションの説明は、主に ARM11 プロセッサファミリより前の ARM プロセッサ を対象としています。 以下のセクションでは、このようなプログラミングに関する問題について詳しく説明 します。 • 非境界整列ポインタ • 構造体に含まれる非境界整列フィールド(P. 1-5) • ARM DUI 0203GJ コードの移植と非境界整列アクセスの検出(P. 1-7) Copyright © 2002-2006 ARM Limited. All rights reserved. 1-3 はじめに 1.2.1 非境界整列ポインタ C および C++ 標準では、ある型へのポインタは、その型の自然配列と同等以上のアラ イメントである必要があると定められています。これにより、効率的なコードサイズ が生成され、パフォーマンスが向上します。このため、デフォルトでは、ARM コンパ イラは通常の C および C++ ポインタがメモリ内の境界整列されたワードを指している と想定します。型修飾子 __packed を使用すると、非境界整列ポインタにアクセスでき ます(詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide の コンパイラの参照での変数宣言キーワードについて説明したセクションを参照して下 さい)。 例えば、ワードの読み出しにポインタ int * が使用される場合、ARM コンパイラでは、 生成するコード内に LDR 命令を使用します。この命令は、アドレスが 4 の倍数(ワード 境界)である場合に、期待どおりに実行されます。しかし、アドレスが 4 の倍数でない 場合、LDR 命令では本来の非境界整列ワードのロードが実行されず、ロテートされた結 果が返されます。ロテートされた結果は、オフセットやシステムのエンディアン設定に よって異なります。 例えば、コードでアドレス 0x8006 を指すポインタからデータをロードする場合、 0x8006、0x8007、0x8008、および 0x8009 からバイトの内容がロードされることが期待さ れます。しかし、ARM プロセッサでは、このアクセスでロードされるのは、アドレス 0x8004、0x8005、0x8006、および 0x8007 のバイトをロテートした内容です。 このため、任意のアドレス(つまり、自然配列によるアドレスでない)にあるワード を指すポインタを定義する場合は、ポインタを定義する際に、以下のように __packed 修飾子を使用してこのことを指定する必要があります。 __packed int *pi; // pointer to unaligned int この場合、ARM コンパイラでは LDR を使用せずに、ポインタのアライメントに関係な くその値に正しくアクセスするコードを生成します。生成されるコードは、連続して バイトをアクセスするコードであるか、またはコンパイルオプションによっては、変 数のアライメントに依存するシフト命令およびマスク命令になります。しかし、この 方法では、パフォーマンスが低下し、コードサイズが増加します。 注 ARM コンパイラではデータの取得に複数のメモリアクセスを使用する可能性がある ため、メモリマップされたペリフェラルレジスタには __packed 修飾子を使用してアク セスしないで下さい。このようなアクセスを行うと、他のペリフェラルレジスタに対 応する周辺の位置へのアクセスが行われる可能性があります。ビットフィールドを使 用すると、現在の ARM コンパイラでは、指定されたフィールドだけでなく、コンテナ 全体にアクセスします。 1-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.2.2 構造体に含まれる非境界整列フィールド グローバル変数がデータ自身と同じサイズの境界に配置されるのと同様に、構造体内 のフィールドもデータ自身と同じサイズの境界に配置されます。そのため、多くの場 合、コンパイラでは、フィールドを境界整列させるために、フィールド間にパディン グを挿入する必要があります。コンパイラでは、構造体内のフィールド間にパディン グを挿入した場合、以下のような注釈を生成します。 #1301-D: padding inserted in struct mystruct また、構造体全体が境界整列されるように構造体の最後にパディングを挿入した場合 には、以下の注釈を生成します。 #2530-D: padding added to end of struct mystruct 注釈を表示するには、--remarks コンパイラオプションを使用します。 コンパイラによってパディングが挿入されないようにすることもできます。__packed 修飾子を使用すると、フィールド間にパディングが挿入されない構造体が作成され、こ れらの構造体は、非境界整列アクセスを必要とします。 ARM コンパイラは、特定の構造体のアライメントを検出した場合、アクセスする フィールドがパック構造体内で境界整列されているかどうかを判断できます。このよ うな場合、コンパイラでは、境界整列ワードまたはハーフワードへのアクセスを可能 な限り効率的に実行します。効率的なアクセスを実行できない場合、コンパイラでは、 複数の境界整列メモリアクセス(LDR、STR、LDM、および STM)を特定のシフト命令とマ スク命令を組み合わせて使用し、メモリ内の正しいバイトにアクセスします。 非境界整列要素へのアクセスがインラインで行なわれるか、関数呼び出しで行われる かは、-Ospace(デフォルト、関数の呼び出し)および -Otime(非境界整列のアクセス をインラインで実行)コンパイラオプションで制御されます。 以下に例を示します。 1. 以下のコードを含むファイル foo.c を作成します。 __packed struct mystruct { int aligned_i; short aligned_s; int unaligned_i; }; struct mystruct S1; int foo (int a, short b) { S1.aligned_i=a; S1.aligned_s=b; return S1.unaligned_i; } ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-5 はじめに 2. このファイルを armcc -c -Otime foo.c を使用してコンパイルします。生成され るコードを以下に示します。 MOV LDR MOV STRB STRB MOV STRB MOV STRB MOV STRB STRB ADD BIC AND LDMIA MOV MOV RSB ORR BX lr r2,r0 r0,|L1.84| r12,r2,LSR #8 r2,[r0,#0] r12,[r0,#1] r12,r2,LSR #16 r12,[r0,#2] r12,r2,LSR #24 r12,[r0,#3] r12,r1,LSR #8 r1,[r0,#4] r12,[r0,#5] r0,r0,#6 r3,r0,#3 r0,r0,#3 r3,{r3,r12} r0,r0,LSL #3 r3,r3,LSR r0 r0,r0,#0x20 r0,r3,r12,LSL r0 コンパイラに詳細な情報を渡して、境界整列されるフィールドとされないフィー ルドを通知することができます。この情報を渡すには、非境界整列フィールドを __packed として宣言し、 struct 自体から __packed 属性を削除する必要があります。 これは推奨される方法であり、struct 内の自然な境界で整列されたメンバに高速 にアクセスできる唯一の方法です。 この方法を使用すると、どのフィールドが非境界整列フィールドであるかもより 明確になりますが、struct でフィールドを追加または削除する場合には注意が必 要です。 3. foo.c に含まれる構造体の定義を以下のように変更します。 struct mystruct { int aligned_i; short aligned_s; __packed int unaligned_i; }; struct mystruct S1; 1-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 4. foo.c をコンパイルすると、以下のような効率的なコードが生成されます。 MOV LDR STR STRH LDMIB MOV ORR BX r2,r0 r0,|L1.32| r2,[r0,#0] r1,[r0,#4] r0,{r3,r12} r0,r3,LSR #16 r0,r0,r12,LSL #16 lr 同じ原則が共用体にもあてはまります。メモリ内で非境界整列される共用体のコン ポーネントには、__packed 属性を使用します。 注 ポインタを介してアクセスされる __packed オブジェクトの場合、パック構造体であっ ても、そのアライメントは不明です。 1.2.3 コードの移植と非境界整列アクセスの検出 他のアーキテクチャ(x86 CISC など)用の従来の C コードでは、ARM プロセッサでは 動作しないポインタを使用して、非境界整列データへのアクセスを実行する場合があ ります。これは移植性のないコードであるため、このようなアクセスは特定し、境界 整列データを前提とする RISC アーキテクチャ上で動作するように修正する必要があ ります。 非境界整列アドレスへのロード操作およびストア操作を使用すると誤った動作が発生 するため、非境界整列アクセスを特定するのは困難な場合があります。また、C ソース のどの部分が問題の原因であるかを追跡することも困難です。 メモリ管理ユニット(MMU)を搭載した ARM プロセッサ(ARM926™ など)では、オ プションでアライメントをチェックする機能がサポートされており、アクセスが発生 するたびに、そのアクセスが境界整列されているかどうかをプロセッサがチェックし ます。正しく境界整列されていないアクセスが行われると、MMU によってデータア ボートが生成されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-7 はじめに ARM7TDMI® な ど の シ ン プ ルなコアの場合は、Application Specific Integrated Circuit (ASIC)または Application Specific Standard Product(ASSP)内にアライメントをチェッ クする機能を実装することをお勧めします。これを行うには、ARM コアの外部に、す べてのデータアクセスのアクセスサイズとアドレスバスの最下位ビットを監視する ハードウェアブロックを追加します。ASIC/ASSP は、非境界整列アクセスが発生した 場合に ABORT 信号を生成するように設定できます。ARM Limited では、他のアーキ テクチャのコードを移植する ASIC/ASSP デバイスには、このようなロジックを含める ことをお勧めします。 非境界整列アクセスをアボートするようにシステムが設定されている場合は、データ アボート例外ハンドラをインストールする必要があります。境界整列アクセスが行わ れると、データアボートハンドラが起動され、 (r14-8)で示される異常なデータアクセ ス命令を特定できます。 このようなアクセスを特定したら、C コードのソースを変更して、そのデータアクセス を修正する必要があります。この変更は、以下のコードを使用して条件付きにするこ とができます。 #ifdef __arm #define PACKED #else #define PACKED #endif ... PACKED int *pi; ... __packed 非境界整列データへのアクセスは、コードサイズとパフォーマンスのオーバーヘッド を増加させるため、最小限に抑えることをお勧めします。 詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide で、コード 生成の制御に関するセクションの --pointer_alignment および --min_array_alignment オプションの説明を参照して下さい。 1-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.3 ARM プロセッサをターゲットとした開発 本書では、一般的な ARM プログラミング作業に関する情報とサンプルコード、および ARM アーキテクチャで作業している開発者向けの情報を提供します。このセクション は以下のサブセクションから構成されています。 • 組み込みソフトウェアの開発 • ARM コードと Thumb コードのインターワーク(P. 1-10) • 1.3.1 C、C++、およびアセンブリ言語の混用(P. 1-11) • プロセッサ例外の処理(P. 1-11) • AAPCS の使用(P. 1-13) • 従来のオブジェクトおよびライブラリとの互換性(P. 1-13) 組み込みソフトウェアの開発 ARM アーキテクチャベースのシステム用に記述される多くのアプリケーションは、 ROM に保持され、リセット時に実行される組み込みアプリケーションです。組み込み オペレーティングシステムを記述する際、またはオペレーティングシステムに依存せ ずにリセットから実行される組み込みアプリケーションを記述する際には、以下のよ うな場合に注意する必要があります。 • ROM がアドレス 0x0000 に配置されるように初期化した後、RAM をアドレス 0x0000 にリマップするなど、アドレスのリマップを行う場合 • 環境とアプリケーションを初期化する場合 • 組み込み実行可能イメージをリンクし、特定のメモリ位置にコードとデータを配 置する場合 通常、ARM コアでは、リセット時にアドレス 0x0000 から命令の実行を開始します。つ まり、組み込みシステムの場合、システムのリセット時に ROM がアドレス 0x0000 に 存在している必要があります。しかし、通常 ROM は RAM に比べて低速で、多くの場 合メモリ幅は 8 ビットまたは 16 ビットしかありません。これは、例外処理の速度に影 響します。アドレス 0x0000 に ROM が存在すると、例外ベクタを変更できません。通 常は、ROM を RAM にリマップし、起動後に例外ベクタを ROM から RAM にコピーす るという方法を使用します。詳細については、 「ROM/RAM のリマップ」(P. 2-28)を参 照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-9 はじめに リセット後、組み込みアプリケーションまたはオペレーティングシステムでは、以下 のようにシステムを初期化する必要があります。 • 例外ベクタ、スタック、I/O ペリフェラルなどの実行環境を初期化する。 • 非ゼロの書き込み可能データの初期値を書き込み可能データ領域にコピーし、ZI データ領域をゼロ初期化するなどの方法でアプリケーションを初期化する。 詳細については、「初期化シーケンス」 (P. 2-26)を参照して下さい。 組み込みシステムでは、複雑なメモリのコンフィグレーションを実装することがよく あります。例えば、組み込みシステムでは、割り込みハンドラやスタックなど、パ フォーマンスを重視したコードには高速な 32 ビット RAM を使用し、アプリケーショ ンの RW データには低速な 16 ビット RAM を使用し、通常のアプリケーションコード には ROM を使用することができます。リンカのスキャッタローディングメカニズムを 使用すると、複雑なシステムに適した実行可能イメージを作成できます。例えば、ス キャッタローディング記述ファイルでは、個々のコード領域とデータ領域のロードア ドレスと実行アドレスを指定できます。一連のサンプルと、セミホスティングなど、組 「第 2 章 組み込みソフトウェ み込みアプリケーションに影響する他の問題については、 アの開発」を参照して下さい。 1.3.2 ARM コードと Thumb コードのインターワーク Thumb 16 ビット命令セットをサポートする ARM プロセッサ用のコードを記述してい る場合は、必要に応じて ARM コードと Thumb コードを混用できます。C または C++ コードを記述している場合は、--apcs /interwork オプションを使用してコンパイルを 実行する必要があります。リンカでは、ARM 関数が Thumb 状態から呼び出されるか、 Thumb 関数が ARM 状態から呼び出されると、それを検出し、呼び出しと復帰のシーケ ンスを変更するか、またはインターワークベニアを挿入して必要に応じてプロセッサ 状態を変更します。 注 Thumb 関数への絶対アドレスを使用する場合は、「Thumb 状態の関数へのポインタ」 (P. 4-17)を参照して下さい。 ア セン ブ リ 言 語コ ー ド を 記 述 し て い る 場 合、Procedure Call Standard for the ARM Architecture(AAPCS)のインターワークバリアントに準拠する必要があります。ター ゲットアーキテクチャのバージョンに応じて、プロセッサ状態を変更する方法は複数 あります。詳細については、 「第 4 章 ARM と Thumb のインターワーク」を参照して下 さい。 1-10 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.3.3 C、C++、およびアセンブリ言語の混用 別々にコンパイルおよびアセンブルされた C、C++、および ARM アセンブリ言語モ ジュールをプログラムで混用することができます。C コードまたは C++ コード内に、 アセンブリ言語で記述された小さなルーチンを含めることができます。これらのルー チンは、ARM コンパイラのインラインアセンブラまたは組み込みアセンブラを使用し てコンパイルされます。ただし、インラインアセンブラまたは組み込みアセンブラを 使用している場合は、記述できるアセンブリ言語コードに対して多くの制限がありま す。これらの制限については、RealView Compilation Tools v3.0 Compiler and Libraries Guide のインラインアセンブラと組み込みアセンブラについて説明している章を参照して下 さい。 「第 5 章 C、C++、およびアセンブリ言語の混用」では、C、C++、およびアセン また、 ブリ言語の各モジュール間でデータを呼び出す方法に関する一般的なガイドラインと サンプルを提供しています。 1.3.4 プロセッサ例外の処理 ARM プロセッサでは、以下の例外タイプを認識します。 リセット プロセッサリセットピンがアサートされると発生します。この例外は、 起動が通知された場合またはプロセッサを起動された状態にするために リセットが行われた場合だけに発生します。ソフトリセットは、リセッ トベクタ 0x0000 に分岐させることによって実行できます。 未定義命令 実行中の命令が、プロセッサや接続されているどのコプロセッサにも認 識されない場合に発生します。 スーパーバイザコール(SVC、以前は SWI) ユーザ定義の割り込み命令です。スーパーバイザコールにより、例えば、 ユーザモードで実行中のプログラムが、スーパーバイザモードで実行さ れる RTOS 関数などの特権操作を要求できます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-11 はじめに プリフェッチアボート プロセッサが、不正なアドレスからプリフェッチされた命令を実行しよ うとしたときに発生します。不正なアドレスとは、メモリが存在しない アドレスか、または現在のモードでプロセッサからアクセスできないと メモリ管理サブシステムが判断したアドレスを指します。 データアボート データ転送命令によって不正なアドレスでのロードまたはストアが試 行された場合に発生します。 割り込み(IRQ) プロセッサ外部割り込み要求ピンが LOW でアサートされ、IRQ 割り込 みがイネーブルされているとき(CPSR 内の I ビットがクリアされている とき)に発生します。 高速割り込み(FIQ) プロセッサ外部高速割り込み要求ピンが LOW でアサートされ、FIQ 割 り込みがイネーブルされているとき(CPSR 内の F ビットがクリアされて いるとき)に発生します。通常、この例外は、割り込みレイテンシを最 小限に保つ必要がある場合に使用されます。 通常、例外処理をオペレーティングシステムに頼らない組み込みアプリケーションな どのアプリケーションを記述する場合には、例外タイプごとのハンドラを記述する必 要があります。 SVC や IRQ 割り込みなど、1 つの例外の種類に複数の発生源があるような場合には、各 「例外ハンド 発生源の例外ハンドラをチェインすることができます。詳細については、 ラのチェイン」(P. 6-42)を参照して下さい。 Thumb 命令をサポートするプロセッサは、例外を受け取ると ARM 状態に切り替わりま す。ARM コードで例外ハンドラを記述するか、またはベニアを使用して Thumb 状態に 切り替えることができます。詳細については、 「復帰アドレスと復帰命令」(P. 6-9)を 参照して下さい。 1-12 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.3.5 AAPCS の使用 Procedure Call Standard for the ARM Architecture(AAPCS)では、個々にコンパイルおよ びアセンブルされたモジュールを連携させるために従う必要があるレジスタの使用方 法とスタックの規則を定義しています。base standard に基づいたバリアントは多数あり ます。ARM コンパイラでは、常に特定の AAPCS バリアントに準拠したコードを生成 します。リンカでは、必要に応じて、リンクする適切な C または C++ ライブラリを選 択します。 ARM プロセッサ用のコードを開発する際は、適切な AAPCS バリアントを選択する必 要があります。以下に例を示します。 • ARM 状態または Thumb 状態と相互作用するコードを記述している場合は、コン パイラとアセンブラで --apcs /interwork オプションを選択する必要があります。 • C または C++ でコードを記述している場合は、各コンパイル済みモジュールに対 して互換性のある AAPCS オプションを選択する必要があります。 • アセンブリ言語で独自のルーチンを記述している場合は、適切な AAPCS バリア ントに準拠する必要があります。 install_directory\Documentation\Specifications\... にある Procedure 詳細については、 Call Standard for the ARM Architecture(aapcs.pdf)を参照して下さい。 注 C とアセンブリ言語を混用している場合は、AAPCS の内容をよく理解するようにして 下さい。 1.3.6 従来のオブジェクトおよびライブラリとの互換性 以前のリリースから RVCT にアップグレードしている場合は、RealView Compilation Tools v3.0 Essentials Guide を読んで、RVCT の新しいリリースと以前のリリースとの間 の互換性について確認して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-13 はじめに 1.4 ARM アーキテクチャ v6 のサポート RVCT のすべてのコンポーネントでは、ARMv6 をサポートしています。armasm では、 すべての ARMv6 命令を使用できます。armlink では、必要に応じて ARMv6 ライブラ リオブジェクトにリンクできます。fromelf では、ARMv6 命令を正しく逆アセンブル できます。コンパイラの組み込みアセンブラでは、すべての ARMv6 命令をサポートし ています。インラインアセンブラでは、ほとんどの ARMv6 命令をサポートしています。 ARMv6 用のコードをコンパイルするには、以下を使用します。 • ARMv6 全般をサポートするには、--cpu 6 を使用します。 特定の ARMv6 プロセッサ用のコードをコンパイルするには、プロセッサ名を指定しま す。以下に例を示します。 • ソフトウェアのベクタ浮動小数点をサポートする ARM1136J-S 用のコードを生 成するには、--cpu ARM1136J-S を使用します。 • ベクタ浮動小数点(VFP)ハードウェアを含む ARM1136JF-S 用のコードを生成 するには、--cpu ARM1136JF-S を使用します。 このセクションは以下のサブセクションから構成されています。 • 命令の生成 • アライメントのサポート(P. 1-15) • • • 1.4.1 エンディアンのサポート(P. 1-15) 例 1 - 符号拡張とゼロ拡張(P. 1-16) 例 2 - パック構造体(P. 1-17) 命令の生成 ARMv6 用のコードをコンパイルする際、コンパイラでは、必要に応じて符号拡張命令 とゼロ拡張命令(SEXT8 など)を生成します( 「例 1 - 符号拡張とゼロ拡張」 (P. 1-16)参 照)。また、指定したプロセッサ用のコードスケジューリングを実行します。 また、C ライブラリには、ARMv6 に特化して最適化されたいくつかの関数が含まれて います。例えば、memcpy()、memove()、strcmp() などがあります。 コンパイラでは、SIMD 命令を C コードの式に適切にマップできないため、SIMD 命令 を使用しません。コンパイラでは、C の式によってエンディアンの反転が実行されると 判断した場合、エンディアン反転命令(REV、REV16、および REVSH)を生成します。 1-14 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.4.2 アライメントのサポート コンパイラでは、ARMv6 の非境界整列アクセスのサポートをデフォルトで利用し、 パック構造体へのアクセスの高速化を図っています。このサポートを利用すると、LDR (または STR)で、ワード境界で整列されていないアドレスからのロード(またはスト ア)を実行できます(「例 2 - パック構造体」(P. 1-17)参照)。明示的に __packed で修 飾されていない構造体は、パックされていない状態になります。 注 ARMv6 用にコンパイルされたコードは、ARM コアで非境界整列のサポートをイネー ブルしている場合のみ正常に実行されます。非境界整列のサポートをイネーブルする には、初期化コードで CP15 レジスタ 1 の U ビット(ビット 22)を設定するか、コア への UBITINIT 入力を HIGH に設定する必要があります。 以下のコンパイラオプションを使用すると、ARMv6 より前の非境界整列アクセス動作 を使用するコードを生成できます。 --no_unaligned_access 1.4.3 エンディアンのサポート ARM コンパイラには、リトルエンディアンオブジェクトとビッグエンディアンオブ ジェクトを生成するためのオプションが用意されています。ARMv6 では、以下に示す 2 つの異なるビッグエンディアンモードをサポートしています。 BE8 ARMv6 バイトインバリアントアドレシングモードを指定します。この モードでは、リトルエンディアンコードとビッグエンディアンコードを 生成します。これは ARMv6 ビッグエンディアンイメージのデフォルト のバイトアドレシングモードです。 バイトインバリアントアドレシングモードは、ARMv6 をサポートする ARM プロセッサでのみ使用できます。 BE32 従来のビッグエンディアンモードであり、ビッグエンディアンコードお よびデータを生成します。また、ARMv6 より前にサポートされていた ビッグエンディアンモードと同一で、ARMv6 より前のビッグエンディア ンイメージのデフォルトのバイトアドレシングモードです。 ARMv6 のビッグエンディアン用にコンパイルする場合、ARM コンパイラでは、BE32 ではなく BE8 としてビッグエンディアンオブジェクトを生成します。BE8 のコードで あることを示すフラグがオブジェクトコードに設定されます。このため、CPSR の E ビッ トを設定して、ARM コアでの BE8 のサポートをイネーブルする必要があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 1-15 はじめに 従来のオブジェクト(ARMv4T など)と ARMv6 のオブジェクトを(ARMv6 で実行さ せるために)リンクさせることができますが、この場合、リンカにより従来のオブジェ クトコードのエンディアンが BE8 モードに切り替えられるため、生成されるイメージ は BE8 になります。 従来の BE32 モードを使用する場合は、初期化コード内で CP15 レジスタ 1 の B ビット (ビット 7)を設定するか、コアへの BIGENDINIT 入力を HIGH に設定する必要があり ます。 以下を使用すると、BE32 と互換性のあるコードを生成できます。 --no_unaligned_access また、BE32 と互換性のあるコードは、--BE32 リンカオプションを使用してリンクする 必要があります。このオプションを指定しないと、オブジェクトの ARMv6 属性によっ て BE8 のイメージが生成されます。 1.4.4 例 1 - 符号拡張とゼロ拡張 この例では、ARMv6 と ARMv6 より前のアーキテクチャ用にコンパイルを実行したと きに異なる命令が生成されることを示します。 signed char unpack(int i) { return (signed char)i; } ARMv6 より前のアーキテクチャのコンパイル --cpu 5 を使用してコンパイルすると、以下のようなコードが生成されます。 unpack PROC LSL ASR BX ENDP r0,r0,#24 r0,r0,#24 lr ARMv6 アーキテクチャのコンパイル --cpu 6 を使用してコンパイルすると、以下のようなコードが生成されます。 unpack PROC SXTB BX ENDP 1-16 r0,r0 lr Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ はじめに 1.4.5 例 2 - パック構造体 この例では、ARMv6 と ARMv6 より前のアーキテクチャ用にコンパイルを実行したと きに、パック構造体に対して異なる命令が生成されることを示します。 __packed struct{ char ch; short sh; int i; } foo; signed char unpack() { return (signed char)foo.i; } ARMv6 より前のアーキテクチャのコンパイル --cpu 5 を使用してコンパイルすると、以下のようなコードが生成されます。 unpack PROC LDR PUSH BL LSL ASR POP |L1.24| DCD ENDP r0,|L1.24| {r4,lr} __aeabi_uread4 r0,r0,#24 r0,r0,#24 {r4,pc} ||.data$0|| + 3 ARMv6 アーキテクチャのコンパイル --cpu 6 を使用してコンパイルすると、以下のようなコードが生成されます。 unpack PROC LDR LDR SXTB BX lr |L1.16| DCD ENDP ARM DUI 0203GJ r0,|L1.16| r0,[r0,#3] r0,r0 ||.data$0|| Copyright © 2002-2006 ARM Limited. All rights reserved. 1-17 はじめに 1-18 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第2章 組み込みソフトウェアの開発 本 章 で は、タ ー ゲ ッ ト シ ス テ ム が 存 在 す る 場 合 と 存 在 し な い 場 合 の、RealView® Compilation Tools(RVCT)による組み込みアプリケーションの開発方法について説明 します。本章は以下のセクションから構成されています。 • 組み込みソフトウェアの開発について(P. 2-2) • • • ターゲットシステムが存在しない場合の RealView Compilation Tools のデフォルト の動作(P. 2-4) ターゲットハードウェアに合わせた C ライブラリのカスタマイズ(P. 2-11) ターゲットハードウェアに合わせたイメージのメモリマップのカスタマイズ (P. 2-14) • • ARM DUI 0203GJ リセットと初期化(P. 2-25) メモリマップに関するその他の注意事項(P. 2-36) Copyright © 2002-2006 ARM Limited. All rights reserved. 2-1 組み込みソフトウェアの開発 2.1 組み込みソフトウェアの開発について ほとんどの組み込みアプリケーションは、まず、最終製品で使用可能なリソースとは 異なるリソースを使用したプロトタイプ環境で開発されます。そのため、組み込みア プリケーションを、開発環境やデバッグ環境の機能に依存する環境からターゲッド ハードウェア上でスタンドアロンで実行されるシステムに移行するプロセスを考慮す ることが重要です。 RVCT を使用して組み込みソフトウェアを開発する場合は、以下の点を考慮する必要が あります。 2.1.1 • ハードウェアが C ライブラリでどのように使用されるかについて。 • C ライブラリの一部の機能がデバッグ環境のリソースを使用して実行されること。 このような機能を使用する場合は、ターゲットハードウェアを利用するように再 実装する必要があります。 • RVCT には、特定のターゲットのメモリマップに関する情報が保持されていない こと。イメージのメモリマップは、ターゲットハードウェアのメモリレイアウト に合わせてカスタマイズする必要があります。 • 組み込みアプリケーションは、メインアプリケーションを実行する前に、何らか の初期化を実行する必要があること。完全な初期化シーケンスには、ユーザが実 装するコードと、RVCT C ライブラリの初期化ルーチンが必要となります。 サンプルコード 本章で取り上げるトピックを分かりやすく説明するため、関連するサンプルプロジェ クトが提供されています。本章で説明する Dhrystone ビルドのコードは、主なサンプル ディレクトリの ...\emb_sw_dev にあります。各ビルドは別々のディレクトリにあり、 本章で順に説明している技法のサンプルとして使用できます。各ビルドに固有の情報 については、以下のセクションを参照して下さい。 • ビルド 1 のサンプルコード(P. 2-10) • • • • ビルド 2 のサンプルコード(P. 2-13) ビルド 3 のサンプルコード(P. 2-23) ビルド 4 のサンプルコード(P. 2-35) ビルド 5 のサンプルコード(P. 2-42) Dhrystone ベンチマークプログラムは、これらのサンプルプロジェクトの基本となって います。Dhrystone プログラムには、本章で説明する概念の多くが反映されているため、 このプログラムが選択されています。 2-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 これらのサンプルプロジェクトは、ARM® Integrator™ 開発プラットフォーム上で実行で きるようにカスタマイズされています。ただし、サンプルで示される基本原理は、す べてのターゲットハードウェアに当てはまります。 注 本章で注目するのは、Dhrystone プログラム自体ではなく、完全なスタンドアロンシス テムでこのプログラムを実行できるようにするために必要な手順です。ベンチマーク ツールとしての Dhrystone の詳細については、Application Note 93 - Benchmarking with ARMulator® を参照して下さい。ARM アプリケーションノートは、ARM Web サイト http://www.arm.com の「Documentation」ページにあります。 Integrator 上での Dhrystone ビルドの実行 本章で説明する Dhrystone のビルドを Integrator 上で実行するには、以下の操作を行う 必要があります。 • ROM/RAM のリマップを実行します。リマップを実行するには、スイッチ 1 およ び 4 をオンに設定してブートモニタを起動し、ボードをリセットします。 • top_of_memory を 0x40000 に設定するか、DIMM メモリモジュールを装着します。 この操作が行われないと、スタック(デフォルトの設定 0x80000)を有効なメモ リとして使用できない場合があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-3 組み込みソフトウェアの開発 2.2 ターゲットシステムが存在しない場合の RealView Compilation Tools のデ フォルトの動作 組み込みアプリケーションのソフトウェア開発に着手する際に、ターゲットハード ウェアの技術的な仕様全体を把握していない場合があります。例えば、ターゲットの ペリフェラルデバイス、メモリマップ、さらにはプロセッサ自体についても、詳細を 把握していない場合があります。 こ の よ う な 詳 細 を 把 握 し て い な く て も ソ フ ト ウ ェ ア 開 発 を 進 め ら れ る よ う に、 RealView Compilation Tools には、ユーザがアプリケーションコードのビルドとデバッグ をすぐに開始できるようにするデフォルトの動作が用意されています。デフォルトの ビルドから完全なスタンドアロンのアプリケーションへの移行に必要な手順を理解す るためにも、このデフォルトの動作を知っておくことが重要です。 このセクションは以下のサブセクションから構成されています。 • セミホスティング(P. 2-5) • C ライブラリの構造(P. 2-6) • デフォルトのメモリマップ(P. 2-7) リンカの配置規則(P. 2-8) アプリケーションの起動(P. 2-9) ビルド 1 のサンプルコード(P. 2-10) • • • 2-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.2.1 セミホスティング ARM C ライブラリでは、ホストのデバッグ環境によって、いくつかの ISO C 言語の機 能がサポートされます。この機能を提供するメカニズムは、セミホスティングと呼ば れます。 セミホスティングは、定義された一連のスーパーバイザコール(SVC)操作によって 実装されます。セミホスティングが実行されると、デバッグエージェントによってセ ミホスティングが識別され、プログラムの実行が一時的に中断されます。その後、セ ミホスティングオペレーションがデバッグエージェントによって実行されてから、 コードの実行が再開されます。したがって、ホスト自体で実行されるタスクは、プロ グラムから見えません。 図 2-1 は、デバッガのコンソールにストリングを出力する、セミホスティングオペレー ションの例を示しています。 Application Code ... ; Set up parameters to SVC ... ; SVC to write to ; debugger console MOV r0, #5 SVC 0x123456 ; Continue program ; execution Code execution is halted while the debug target services the semihosting operation Code execution resumes when the string has finished printing 図 2-1 セミホスティングオペレーションの例 注 詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide のセミホ スティングに関する章を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-5 組み込みソフトウェアの開発 2.2.2 C ライブラリの構造 概念上、C ライブラリは、ISO C 言語仕様に含まれる関数と ISO C 言語仕様をサポート する関数に分類することができます。図 2-2 は、この概念を示しています。 Functions called by your application, for example, fputc() ISO C C Library input/ output Debug Agent error handling stack and heap setup other Device driver level. Use semihosting, for example, __sys_write() Semihosting Support Implemented by the debugging environment 図 2-2 C ライブラリの構造 ISO C 言語の機能のサポートには、ホストデバッグ環境によってデバイスドライバレベ ルで提供されるものもあります。 例えば、RVCT C ライブラリでは、デバッガのコンソールウィンドウへの書き込みに よって、ISO C 言語の printf() ファミリの関数を実装します。この機能は、__sys_write() を呼び出すことで提供されます。この関数は、セミホスティング呼び出しを実行するサ ポート関数であり、この関数によってストリングがコンソールに書き込まれます。 2-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.2.3 デフォルトのメモリマップ メモリマップが記述されていないイメージでは、図 2-3 に示すように、リンカがデフォ ルトのメモリマップに従ってコードとデータを配置します。 From Semihosting call STACK HEAP Decided at link time ZI RW RO 0x8000 図 2-3 デフォルトのメモリマップ デフォルトのメモリマップは以下のように記述できます。 ARM DUI 0203GJ • イメージは、アドレス 0x8000 でロードおよび実行されるようにリンクされます。 すべての RO(読み出し専用)セクションが最初に配置され、その後、RW(読み 出し - 書き込み)セクション、ZI(ゼロで初期化された)セクションの順に配置 されます。 • ヒープは ZI セクションの先頭から配置されるため、正確な位置はリンク時に決 まります。 • スタックベースの位置は、アプリケーションの起動時のセミホスティングオペ レーションによって決まります。このセミホスティングオペレーションによって 返される値は、デバッグ環境によって異なります。 — RealView ARMulator ISS(RVISS)では、コンフィグレーションファイル peripherals.ami に設定されている値が返されます。デフォルトは 0x08000000 です。 — Multi-ICE® および RealView ICE では、デバッガ内部変数 top_of_memory の 値が返されます。デフォルトは 0x00080000 です。 Copyright © 2002-2006 ARM Limited. All rights reserved. 2-7 組み込みソフトウェアの開発 2.2.4 リンカの配置規則 リンカは、図 2-4 に示す一連の規則に基づいて、メモリ内でのコードとデータの配置場 所を決定します。 section A from file2.o ZI B RW DATA section A from file1.o A CODE RO 図 2-4 リンカの配置規則 イメージは、まず属性別に配置されます。RO がメモリの最下位アドレスに配置され、 その後に RW、ZI と続きます。各属性では、コードがデータよりも優先されます。 次に、入力セクションが名前のアルファベット順で配置されます。入力セクションの 名前は、アセンブラの AREA ディレクティブに対応します。 入力セクションでは、各オブジェクトのコードとデータは、リンカのコマンドライン でオブジェクトファイルが指定された順序で配置されます。 コードとデータを正確に配置する必要がある場合には、これらの規則に頼ることはお 勧めしません。コードとデータの配置を正確に制御するには、スキャッタローディン 「ターゲットハードウェア グメカニズムを使用する必要があります。詳細については、 に合わせたイメージのメモリマップのカスタマイズ」(P. 2-14)を参照して下さい。 注 配置規則とスキャッタローディングの詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 2-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.2.5 アプリケーションの起動 ほとんどの組み込みシステムでは、メインタスクが実行される前に、初期シーケンス によってシステムのセットアップが実行されます。 図 2-5 は、デフォルトの初期化シーケンスを示しています。 C Library Image entry point USER CODE __main ... __scatterload copy/decompress RW data copy non-root code zero uninitialized data . .. . . main() causes the linker to link in library initialization code __rt_entry set up application stack and heap initialize library functions call top-level constructors (C++) Exit from application 図 2-5 デフォルトの初期化シーケンス 上位レベルでは、この初期化シーケンスを 3 つの機能ブロックに分けることができま す。__main は直接 __scatterload に分岐します。__scatterload ではランタイム時のイ メージのメモリマップが設定されるのに対し、__rt_entry(ランタイム時のエントリ) では C ライブラリが初期化されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-9 組み込みソフトウェアの開発 __scatterload によって、コードとデータのコピー、および ZI データのゼロ初期化が 実行されます。さらに、必要に応じて、RW データが伸張されます。 __scatterload は __rt_entry に分岐します。この分岐により、アプリケーションのス タックとヒープのセットアップ、ライブラリ関数とそのスタティックデータの初期化、 およびグローバルに宣言されたオブジェクトのコンストラクタの呼び出し(C++ のみ) が実行されます。 その後、__rt_entry は、アプリケーションのエントリである main() に分岐します。メ インアプリケーションの実行が終了すると、__rt_entry によってライブラリがシャッ トダウンされ、制御がデバッガに戻されます。 関数ラベル main() には特別な意味があります。main() 関数が存在することにより、リ ンカは __main および __rt_entry 内の初期化コードをリンクします。main() 関数が存在 しない場合は、初期化シーケンスがリンクされず、その結果、標準 C ライブラリの一 部の機能がサポートされなくなります。 2.2.6 ビルド 1 のサンプルコード ビルド 1 は、Dhrystone ベンチマークのデフォルトのビルドです。つまり、ビルド 1 は このセクションで説明した RVCT のデフォルトの動作に準拠しています。詳細につい ては、「Integrator 上での Dhrystone ビルドの実行」(P. 2-3)と、主なサンプルディレク トリ ...\emb_sw_dev\build1 にあるサンプルビルドのファイルを参照して下さい。 2-10 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.3 ターゲットハードウェアに合わせた C ライブラリのカスタマイズ デフォルトでは、C ライブラリはセミホスティングを利用してデバイスドライバレベル の機能を提供し、ホストコンピュータを入出力デバイスとして使用できるようにしま す。開発時に使用するハードウェアには、最終的なシステムのすべての入出力機能が 備わっていないことがよくあるため、この機能が役立ちます。 このセクションは以下のサブセクションから構成されています。 • C ライブラリのターゲット変更 2.3.1 • C ライブラリのセミホスティングの無効化(P. 2-12) • ビルド 2 のサンプルコード(P. 2-13) C ライブラリのターゲット変更 ターゲットハードウェアを利用し、C ライブラリの実装を優先してイメージに自動的に リンクされる C ライブラリ関数を独自に実装することができます。図 2-6 は、C ライブ ラリのターゲット変更と呼ばれるこのプロセスを示しています。 ISO C ISO C User Code C Library Retarget Debug Agent Input/ Output Input/ Output Semihosting Support Target Hardware 図 2-6 C ライブラリのターゲット変更 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-11 組み込みソフトウェアの開発 例えば、UART などのペリフェラル I/O デバイスがある場合、デバッガのコンソールへ の書き込みを行う、ライブラリで実装された fputc() を、UART に出力する実装でオー バーライドしたい場合があります。この fputc() の実装は最終イメージにリンクされる ため、printf() ファミリのすべての関数が UART への出力を行います。 例 2-1 は、fputc() の実装例を示しています。この例では、fputc() の入力文字パラメー タがシリアル出力関数 sendchar() に転送されます。このシリアル出力関数は、別のソー スファイルで実装されていると仮定しています。このように、fputc() は、ターゲット に依存する出力と C ライブラリ標準出力関数との間の抽象レイヤとして機能します。 例 2-1 fputc() の実装 extern void sendchar(char *ch); int fputc(int ch, FILE *f) { /* e.g. write a character to an UART */ char tempch = ch; sendchar(&tempch); return ch; } 2.3.2 C ライブラリのセミホスティングの無効化 スタンドアロンのアプリケーションでは、セミホスティングオペレーションがサポー トされない可能性があります。そのため、C ライブラリのセミホスティング関数がアプ リケーションにリンクされないようにする必要があります。 セミホスティングを使用する関数が C ライブラリからリンクされないようにするに は、シンボル __use_no_semihosting をインポートする必要があります。このインポー トは、以下のように、プロジェクトの C ソースファイルまたはアセンブラソースファ イルで行うことができます。 • C モジュールでは、以下のように #pragma ディレクティブを使用します。 #pragma import(__use_no_semihosting) • アセンブラモジュールでは、以下のように IMPORT ディレクティブを使用します。 IMPORT __use_no_semihosting セミホスティングを使用する関数がリンクされた状態のままの場合は、リンカによっ てエラーがレポートされます。 2-12 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 このような関数を特定するには、--verbose オプションを使用してリンクを実行しま す。その結果、C ライブラリ関数の場合には __I_use_semihosting というストリングが 出力されます。以下に例を示します。 Loading member sys_exit.o from c_a__un.l. definition:_sys_exit reference :__I_use_semihosting これらの関数(この例では _sys_exit)は、ユーザが実装する必要があります。 注 アプリケーションコード内で使用されているセミホスティング関数は、リンカによっ てレポートされません。エラーが発生するのは、セミホスティング関数が C ライブラ リからリンクされている場合のみです。 セ ミ ホ ス テ ィ ン グ を 使用する C ライブラリ関数のリストについては、RealView Compilation Tools v3.0 Compiler and Libraries Guide のセミホスティングに関する章を参 照して下さい。 2.3.3 ビルド 2 のサンプルコード Dhrystone ベンチマークのビルド 2 では、クロック処理およびストリングの入出力に、 Integrator プラットフォームのハードウェアを使用しています。このサンプルビルドの ファイルは、主なサンプルディレクトリ ...\emb_sw_dev\build2 にあります。 ビルド 2 では、ビルド 1 のサンプルプロジェクトに以下の変更が加えられています。 C ライブラリのターゲット変更 ターゲットが変更された ISO C 関数のレイヤが追加されました。例えば、 標準 I/O 関数とクロック機能に加え、特別なエラー通知機能とプログラ ム終了機能が含まれています。 ターゲットに依存するデバイスドライバ ターゲットハードウェアのペリフェラルと直接相互作用するデバイスド ライバのレイヤが追加されました。 詳細については、 「Integrator 上での Dhrystone ビルドの実行」 (P. 2-3)を参照して下さい。 シンボル __use_no_semihosting はこのプロジェクトにインポートされていません。こ れは、アプリケーションのスタックとヒープの位置をセットアップする C ライブラリ の初期化中に、セミホスティング呼び出しが実行されるためです。スタックとヒープ のセットアップにおけるターゲット変更の詳細については、 「スタックとヒープの配 置」(P. 2-20)を参照して下さい。 注 出力を確認するには、端末または端末エミュレータをシリアルポート A に接続する必 要があります。このシリアルポートは、38400 ボー、パリティなし、ストップビット 1、 およびフロー制御なしに設定する必要があります。端末は、入力行の終わりに改行を 追加し、入力された文字をローカルに反映するように設定する必要があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-13 組み込みソフトウェアの開発 2.4 イズ ターゲットハードウェアに合わせたイメージのメモリマップのカスタマ セミホスティング機能がサポートされない最終的な組み込みシステムでは、デフォル トのメモリマップが使用されない可能性があります。通常、ターゲットハードウェア では、複数のメモリデバイスが異なるアドレス範囲に配置されています。このような デバイスを最大限に活用するには、ロード時と実行時にそれぞれのメモリビューを使 用する必要があります。 このセクションは以下のサブセクションから構成されています。 • スキャッタローディング • スキャッタローディング記述ファイルの構文(P. 2-15) • • • • • • 2.4.1 スキャッタローディング記述ファイルの例(P. 2-16) スキャッタローディング記述ファイルでのオブジェクトの配置(P. 2-17) ルート領域(P. 2-18) スタックとヒープの配置(P. 2-20) ランタイムメモリモデル(P. 2-21) ビルド 3 のサンプルコード(P. 2-23) スキャッタローディング スキャッタローディングにより、スキャッタローディング記述ファイルと呼ばれるテ キストの記述ファイル内で、ロード時と実行時のメモリ内のコードとデータの位置を 指定できます。このファイルは、コマンドラインで --scatter オプションを使用する と、リンカに渡されます。以下に例を示します。 armlink --scatter scat.txt file1.o file2.o スキャッタローディング記述ファイルでは、メモリ領域をアドレス指定することによ り、ロード時と実行時におけるコードとデータの位置をリンカに示します。 スキャッタローディング領域 スキャッタローディングは以下の 2 つのカテゴリに分類されます。 • リセット時とロード時にアプリケーションコードとデータを保持するロード 領域。 • アプリケーションの実行中にコードとデータを保持する実行領域。アプリケー ションの起動中に、各ロード領域から 1 つ以上の実行領域が作成されます。 イメージ内のすべてのコードとデータは、1 つのロード領域と 1 つの実行領域に保持さ れます。 起動中、__main 内の C ライブラリ初期化コードによって、イメージのロードビューか ら実行ビューに移動する必要のあるコードとデータのコピーおよびゼロ初期化が実行 されます。 2-14 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.4.2 スキャッタローディング記述ファイルの構文 スキャッタローディング記述ファイルの構文には、スキャッタローディング自体に用 意されている機能が反映されています。図 2-7 は、このファイルの構文を示しています。 name of region start address MY_REGION 0x0000 { contents of region } 0x2000 optional length parameter 図 2-7 スキャッタローディング記述ファイルの構文 領域は、ヘッダタグで定義されます。ヘッダタグには、少なくとも領域の名前と開 始アドレスが含まれていますが、必要に応じて、最大長やさまざまな属性を追加で きます。 領域の内容は領域のタイプによって異なります。 • ロード領域には、少なくとも 1 つの実行領域が含まれている必要があります。実 際には、各ロード領域に複数の実行領域が含まれていることが一般的です。 • 領域が EMPTY 属性(「スキャッタローディング記述ファイルの EMPTY 属性の使 用」(P. 2-41)参照)で宣言されている場合を除いて、実行領域には、少なくと も 1 つのコードセクションまたはデータセクションが含まれている必要があり ます。通常、EMPTY で宣言されていない領域には、ソースファイルまたはライブ ラリオブジェクトファイルが含まれています。ワイルドカード(*)構文を使用 すると、スキャッタローディング記述ファイル内で指定されていない特定の属性 を含むすべてのセクションをグループ化できます。 注 スキャッタローディング記述ファイルの構文の詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-15 組み込みソフトウェアの開発 2.4.3 スキャッタローディング記述ファイルの例 図 2-8 は、単純なスキャッタローディングの例を示しています。 0x18000 Load View Execute View Fill with zeros RAM 0x18000 ZI RAM RW 0x10000 0x10000 Copy/ decompress 0x4000 0x4000 ROM ROM RW RO RO 0x0000 0x0000 図 2-8 単純なスキャッタローディングの例 この例では、アドレス 0x0000 で始まり、すべてのコードとデータを含む 1 つのロー ド領域を使用しています。このロード領域から 2 つの実行領域が作成されます。1 つの 実行領域には、RO のすべてのコードとデータが含まれています。この領域は、ロード 時のアドレスと同じアドレスで実行されます。もう 1 つの領域は、アドレス 0x10000 で 始まり、すべての RW データと ZI データを含んでいます。 2-16 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 例 2-2 は、図 2-8 のメモリマップを記述した記述ファイルを示しています。 例 2-2 単純なスキャッタローディング記述ファイル ROM_LOAD 0x0000 0x4000 { ROM_EXEC 0x0000 0x4000; Root region { * (+RO); All code and constant data } RAM 0x10000 0x8000 { * (+RW, +ZI); All non-constant data } } 2.4.4 スキャッタローディング記述ファイルでのオブジェクトの配置 ほとんどのイメージでは、例 2-2(2-17 ページ)に示したように、すべての属性を 1 つ にまとめるのではなく、特定のコードセクションとデータセクションの配置を制御し ます。セクションの配置は、ワイルドカード構文だけに頼るのではなく、記述ファイ ル内で各オブジェクトを直接指定することによって制御できます。 注 記述ファイルの実行領域内のオブジェクトの順序が、出力イメージ内のオブジェクト 「リンカの配置規則」(P. 2-8) の順序に影響することはありません。各実行領域には、 で説明したリンカの配置規則が適用されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-17 組み込みソフトウェアの開発 標準的なリンカの配置規則をオーバーライドするには、スキャッタローディングの +FIRST ディレクティブおよび +LAST ディレクティブを使用できます。例 2-3 は、実行領 域の先頭にベクタテーブルを配置するスキャッタローディング記述ファイルを示して います。この例では、vectors.o 内のエリア Vect がアドレス 0x0000 に配置されます。 例 2-3 セクションの配置 ROM_LOAD 0x0000 0x4000 { ROM_EXEC 0x0000 0x4000 { vectors.o (Vect, +FIRST) * (+RO) } ; more exec regions... } スキャッタローディング記述ファイル内でのオブジェクトの配置に関する詳細につい ては、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 2.4.5 ルート領域 ルート領域 とは、ロードアドレスが実行アドレスと同じ実行領域のことです。各ス キャッタローディング記述ファイルには、少なくとも 1 つのルート領域が必要です。 スキャッタローディングでは、実行領域の作成(例えば、コードとデータのコピーや ゼロ初期化)に関与するコードとデータは別の場所にコピーできないという制約があ ります。そのため、ルート領域には以下のセクションが含まれている必要があります。 • コードとデータをコピーするコードを保持する __main.o および __scatter*.o • 伸張を実行する __dc*.o • コピーまたは伸張されるコードとデータのアドレスを保持する Region$$Table セ クション 上記のセクションは InRoot$$Sections を使用して記述できます。 2-18 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 上記のセクションは読み出し専用として定義されるため、* (+RO) ワイルドカード構文 でグループ化されます。そのため、* (+RO) が非ルート領域で指定されている場合は、 上記のセクションをルート領域で明示的に宣言する必要があります。これは例 2-4 に示 しています。 例 2-4 ルート領域の指定 ROM_LOAD 0x0000 0x4000 { ROM_EXEC 0x0000 0x4000 { vectors.o (Vect, +FIRST) * (InRoot$$Sections) } RAM 0x10000 0x8000 { * (+RO, +RW, +ZI) } ; root region ; ; ; ; Vector table All library sections that must be in a root region, for example, __main.o, __scatter*.o, __dc*.o, and * Region$$Table ; all other sections } __main.o、__scatter.o、__dc*.o、および Region$$Table がルート領域に含まれていな い場合は、リンカによって以下のようなエラーメッセージが生成されます。 Error: L6202E: Section Region$$Table cannot be assigned to a non-root region. ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-19 組み込みソフトウェアの開発 2.4.6 スタックとヒープの配置 スキャッタローディングでは、コードと静的に割り当てられたデータの位置をイメー ジ内で指定する方法を提供しています。このセクションでは、アプリケーションのス タックとヒープを配置する方法について説明します。 アプリケーションのスタックとヒープは、C ライブラリの初期化中にセットアップされ ます。スタックとヒープの位置は、スタックとヒープのセットアップを実行するルー チンを再実装することによってカスタマイズできます。ARM C ライブラリでは、 __user_initial_stackheap() がこのルーチンに相当します。 図 2-9 は、再実装された __user_initial_stackheap() を使用する C ライブラリ初期化 プロセスを示しています。 C Library 1 Image entry point . . . USER CODE __main copy/decompress RW data copy non-root code . zero uninitialized data 3 2 . . . __user_initial_stackheap() set up application stack and heap __rt_entry initialize library functions call top-level constructors (C++) Exit from application 4 . main() causes the linker to link in library initialization code 図 2-9 __user_initial_stackheap() のターゲット変更 2-20 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 __user_initial_stackheap() は、C または ARM アセンブラ言語でコーディングできま す。このルーチンは以下のパラメータを返す必要があります。 • ヒープベースを r0 に格納 • スタックベースを r1 に格納 • ヒープリミットを r2 に格納(必要な場合) • r3(未使用) イメージのスキャッタローディングを行う場合は、__user_initial_stackheap() を再 実装する必要があります。再実装しないと、リンカによって以下のエラーが表示され ます。 Error: L6218E: Undefined symbol Image$$ZI$$Limit (referred from sys_stackheap.o) 2.4.7 ランタイムメモリモデル 2 つのランタイムメモリモデルが用意されています。 • 1 領域モデル(デフォルト) • 2 領域モデル(P. 2-22) どちらのランタイムメモリモデルでも、スタックはチェックされずに拡大します。 注 Integrator システムでは、どちらのモデルも使用できます。 1 領域モデル デフォルトの 1 領域モデルでは、アプリケーションのスタックとヒープが、同じメモ リ領域内で互いに向かって拡大します。この場合のヒープは、新しいヒープ空間が割 り当てられると(例えば、malloc() が呼び出されると)、スタックポインタの値に照ら し合わせてチェックされます。 P. 2-22 図 2-10 と 例 2-5(2-22 ページ)は、単純な 1 領域モデルを実装する __user_initial_stackheap() の例を示しています。このモデルでは、スタックがアド レス 0x40000 から下方に拡大し、ヒープが 0x20000 から上方に拡大します。 このルーチンは、適切な値をレジスタ r0 と r1 にロードしてから呼び出しルーチンに復 帰します。1 領域モデルではヒープリミットが使用されないため、レジスタ r2 は変更 されません。レジスタ r3 は使用されません。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-21 組み込みソフトウェアの開発 SB HB STACK 0x40000 HEAP 0x20000 図 2-10 1 領域モデル 例 2-5 1 領域モデルのルーチン EXPORT __user_initial_stackheap __user_initial_stackheap LDR r0, =0x20000 ;HB LDR r1, =0x40000 ;SB ; r2 not used (HL) ; r3 not used MOV pc, lr 2 領域モデル システム設計では、スタックとヒープを個別のメモリ領域に配置することが必要にな る場合があります。 例えば、高速 RAM の一部をスタック専用に予約しておきたい場合があるとします。2 領域モデル を使用することをリンカに通知するには、アセンブラの IMPORT ディレク ティブを使用して、シンボル __use_two_region_memory をインポートする必要がありま す。このシンボルをインポートすると、ヒープは、__user_initial_stackheap() によっ てセットアップされた専用のヒープリミットと照らし合わせてチェックされます。 P. 2-23 図 2-11 と 例 2-6(2-23 ページ)は、2 領域モデルの実装例を示しています。 この例では、ヒープが 0x28000000 から 0x28080000 まで上方に拡大し、スタックが 0x40000 から下方に拡大します。 2-22 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 HL HB SB 0x28080000 HEAP 0x28000000 STACK 0x40000 図 2-11 2 領域モデル 例 2-6 2 領域モデルのルーチン IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR r0, =0x28000000 ;HB LDR r1, =0x40000 ;SB LDR r2, =0x28080000 ;HL ; r3 not used MOV pc, lr 2.4.8 ビルド 3 のサンプルコード ビ ル ド 3 の サ ン プ ル で は、スキャッタローディングが実装され、再実装された __user_initial_stackheap() が含まれています。詳細については、主なサンプルディレ クトリ ...\emb_sw_dev\build3 にあるサンプルビルドのファイルを参照して下さい。 ビルド 3 では、ビルド 2 のサンプルプロジェクトに以下の変更が加えられています。 スキャッタローディング 単純なスキャッタローディング記述ファイルがリンカに渡されます。 ターゲットが変更された __user_initial_stackheap() 1 領域モデルと 2 領域モデルのどちらを実装するかを選択できます。デ フォルトのビルドでは、1 領域モデルが使用されます。2 領域モデルの実 装は、アセンブルする際に TWO_REGION_MODEL を定義することによって選 択できます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-23 組み込みソフトウェアの開発 C ライブラリのセミホスティングの無効化 イメージに C ライブラリのセミホスティング関数が存在しないため、シ ンボル __use_no_semihosting がビルド 3 にインポートされます。 注 clock() にセミホスティングが使用されないようにするため、Integrator AP 上のリアルタイムクロック(RTC)を読み出すようにターゲットが変 更されます。この分解能は 1 秒であるため、Dhrystone では正確な結果が 得られません。このメカニズムは、ビルド 4(「ビルド 4 のサンプルコー ド」(P. 2-35)参照)で改善されています。 このビルドを Integrator AP 上で実行するには、ROM/RAM のリマップを実行する必要 があります。リマップを実行するには、スイッチ 1 および 4 をオンに設定してブート モニタを起動します。 「Integrator 上での Dhrystone ビルドの実行」 (P. 2-3)を参照して下さい。 詳細については、 注 ARM7 コアベースのターゲットを使用している場合は、すべてのベクタキャッチとセ ミホスティングをディセーブルする必要があります。ディセーブルしない場合は、デ バッガによって、アドレス 0x0 と 0x1C 間で実行された命令は例外として解釈され、 ダイアログボックスにこの解釈がレポートされます。ベクタキャッチとセミホスティ ングをディセーブルする方法の詳細については、デバッガのマニュアルを参照して下 さい。 2-24 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.5 リセットと初期化 本章では、これまで、C ライブラリ初期化ルーチンへのエントリポイント __main で実 行が開始されると想定してきました。ただし、実際、ターゲットハードウェア上の組 み込みアプリケーションでは、起動時になんらかのシステムレベルの初期化が実行さ れます。このセクションでは、このようなシステムレベルの初期化について詳しく説 明します。このセクションは以下のサブセクションから構成されています。 • 初期化シーケンス(P. 2-26) • • • • • • • • ARM DUI 0203GJ ベクタテーブル(P. 2-27) ROM/RAM のリマップ(P. 2-28) ローカルメモリのセットアップに関する注意事項(P. 2-30) スキャッタローディングとメモリのセットアップ(P. 2-31) スタックポインタの初期化(P. 2-31) ハードウェアの初期化(P. 2-33) 実行モードに関する注意事項(P. 2-34) ビルド 4 のサンプルコード(P. 2-35) Copyright © 2002-2006 ARM Limited. All rights reserved. 2-25 組み込みソフトウェアの開発 2.5.1 初期化シーケンス 図 2-12 は、ARM アーキテクチャベースの組み込みシステムで考えられる初期化シーケ ンスを示しています。 C Library USER CODE 2 __main 3 .. . __scatterload copy/decompress RW data copy non-root code zero uninitialized data . . . 5 4 __rt_entry initialize library functions call top-level constructors (C++) .. . reset handler initialize stack pointers configure MMU/MPU setup cache/enable TCM 1 Image entry point . __user_initial_stackheap() set up application stack and heap . $Sub$$main() enable caches and interrupts 6 Exit from application 8 . main() causes the linker to link in library initialization code 7 図 2-12 初期化シーケンス リセットハンドラは、システムの起動直後に実行されます。$Sub$$main() というコード ブロックは、メインアプリケーションに入る直前に実行されます。 リセットハンドラは、アセンブラでコーディングされた小さなモジュールで、システ ムのリセット時に実行されます。リセットハンドラは、少なくとも、アプリケーショ ンが実行されているモードのスタックポインタを初期化します。キャッシュ、密結合 メモリ(TCM)、メモリ管理ユニット(MMU)、メモリ保護ユニット(MPU)などの ローカルのメモリシステムを使用するコアの場合は、この初期化プロセスの段階で何 らかの設定を行う必要があります。通常、実行後にリセットハンドラが __main に分岐 して、C ライブラリ初期化シーケンスを開始します。 割り込みのイネーブルなど、システムの初期化にはいくつかのコンポーネントがあり、 一般的には C ライブラリ初期化コードの実行終了後に実行されます。$Sub$$main() と いう名前のコードブロックは、メインアプリケーションが実行される直前に、これら の処理を実行します。 初期化シーケンスのさまざまなコンポーネントの詳細については、「ベクタテーブル」 を参照して下さい。 2-26 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.5.2 ベクタテーブル すべての ARM システムには、ベクタテーブルがあります。ベクタテーブルは、初期化 シーケンスの一部ではありませんが、例外を処理するには必要です。 例 2-7 のコードでは、他のモジュールでコーディングされているさまざまな例外ハンド ラをインポートします。ベクタテーブルは、例外ハンドラへの分岐命令のリストです。 FIQ ハンドラは、アドレス 0x1C に直接配置されます。これにより、FIQ ハンドラへの 分岐を実行する必要がなくなるため、FIQ 応答時間が最適化されます。 例 2-7 ベクタテーブルのコード PRESERVE8 AREA Vectors, CODE, READONLY IMPORT Reset_Handler ; import other exception handlers ; ... ENTRY B Reset_Handler B Undefined_Handler B SVC_Handler B Prefetch_Handler B Abort_Handler NOP ; Reserved vector B IRQ_Handler B FIQ_Handler END 注 ベクタテーブルは、ラベル ENTRY でマークされています。このラベルによって、この コードがエントリポイントである可能性がリンカに通知されるため、リンク時にイ メージから削除されることはありません。ただし、--entry リンカオプションを使用し て、イメージ内で使用されるエントリポイントのうちの 1 つを実際のエントリポイン トとして選択する必要があります。詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-27 組み込みソフトウェアの開発 2.5.3 ROM/RAM のリマップ ユーザは、最初に実行される命令のアドレス 0x0000 にどのようなメモリを配置するの かを検討する必要があります。 注 このセクションでは、ARM コアによる命令フェッチが 0x0000 で開始されることが想定 されています。このアドレスは、ARM コアベースシステムの標準です。ただし、一部 の ARM コアでは、命令フェッチを 0xFFFF0000 から開始するように設定できます。 起動時にはアドレス 0x0000 に有効な命令が存在している必要があるため、リセット時 に不揮発性メモリが 0x0000 に配置されるようにする必要があります。 1 つの方法としては、ROM を 0x0000 に配置することができます。ただし、この設定に はいくつかの欠点があります。一般的に、ROM へのアクセス速度は RAM よりも遅い ため、例外ハンドラへ分岐する際に過度のパフォーマンスペナルティが発生する場合 には、システムで問題が発生することがあります。また、ROM にベクタテーブルを配 置すると、実行時にそのベクタテーブルを変更できなくなります。 図 2-13 は、別の方法を示しています。ROM はアドレス 0x10000 に配置されますが、こ のメモリは、リセット時にメモリコントローラによって 0 にエイリアスされます。リ セット後、リセットハンドラ内のコードは ROM の実アドレスに分岐します。その後、 メモリコントローラによって、エイリアスされた ROM が削除されるため、RAM がア ドレス 0x0000 に配置されます。__main では、ベクタテーブルが 0x0000 にある RAM に コピーされるため、例外を処理することができます。 1 2 3 0x18000 0x18000 ROM ROM 0x10000 Reset Handler Reset Handler 0x4000 0x10000 0x4000 Remove Alias Aliased ROM 0x0000 Reset Handler Branch to real ROM RAM Reset Handler Reset Handler Vectors 0x0000 図 2-13 ROM/RAM のリマップ 2-28 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 例 2-8 は、ARM アセンブラモジュールで ROM/RAM のリマップを実装する方法を示し ています。ここに示されている定数は Integrator プラットフォームに固有のものですが、 ROM/RAM のリマップを同様の方法で実装するどのプラットフォームにも同じ方法を 使用できます。 例 2-8 ROM/RAM のリマップ ; --- Integrator CM control reg CM_ctl_reg EQU 0x1000000C Remap_bit EQU 0x04 ; Address of CM Control Register ; Bit 2 is remap bit of CM_ctl ENTRY ; Code execution starts here on reset ; On reset, an alias of ROM is at 0x0, so jump to 'real' ROM. LDR pc, =Instruct_2 Instruct_2 ; Remap by setting Remap bit of the CM_ctl register LDR r1, =CM_ctl_reg LDR r0, [r1] ORR r0, r0, #Remap_bit STR r0, [r1] ; RAM is now at 0x0. ; The exception vectors must be copied from ROM to RAM (in __main) ; Reset_Handler follows on from here 最初の命令は、エイリアスされた ROM から実際の ROM へのジャンプです。この命令 を実行できるのは、ラベル Instruct_2 が実際の ROM アドレスに配置されているため です。 このステップの後、Integrator コアモジュールの制御レジスタのリマップビットを反転 することによって、ROM のエイリアスが削除されます。 通常、このコードは、システムのリセット直後に実行されます。リマップは、C ライブ ラリ初期化コードが実行される前に完了する必要があります。 注 MMU が搭載されているシステムでは、システムの起動時に MMU の設定によってリ マップを実装できます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-29 組み込みソフトウェアの開発 2.5.4 ローカルメモリのセットアップに関する注意事項 多くの ARM コアには、MMU や MPU などのオンチップメモリシステムが搭載されて います。通常、これらのデバイスは、システムの起動時にセットアップおよびイネー ブルされます。そのため、ローカルメモリシステムを搭載するコアの初期化シーケン スには、特別な配慮が必要です。 本章で説明してきたように、__main の C ライブラリ初期化コードでは、イメージの実 行時メモリマップがセットアップされます。そのため、プロセッサコアのランタイム 時のメモリビューは、__main への分岐の前にセットアップする必要があります。つま り、MMU または MPU は、リセットハンドラ内でセットアップおよびイネーブルする 必要があります。 また、一般的に、コードとデータは TCM にスキャッタローディングされるため、TCM も __main への分岐の前(通常は MMU または MPU のセットアップの前)にイネーブ ルする必要があります。TCM がイネーブルされている場合には、TCM によってマスク されるメモリにアクセスする必要がないということに注意する必要があります。 また、__main への分岐の前にキャッシュがイネーブルされた場合は、キャッシュコヒー レンシに関する問題も発生します。__main のコードは、実質的には命令をデータとし て処理し、コード領域をロードアドレスから実行アドレスにコピーします。その結果、 一部の命令はデータキャッシュ内にキャッシュされる可能性があり、その場合、これ らの命令が命令パスから見えなくなります。 このようなコヒーレンシの問題を回避するには、C ライブラリ初期化シーケンスの実行 が完了した後にキャッシュをイネーブルします。 2-30 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.5.5 スキャッタローディングとメモリのセットアップ ROM/RAM のリマップまたは MMU の設定のいずれかでリセット時のコアのメモリ ビューが変更されるシステムでは、スキャッタローディング記述ファイルでリマップ が実行された後のイメージのメモリマップを記述する必要があります。 例 2-9 に示す記述ファイルは、「ROM/RAM のリマップ」(P. 2-28)で示したリマップ後 の例に関連しています。 例 2-9 ROM_LOAD 0x10000 0x8000 { ROM_EXEC 0x10000 0x8000 { reset_handler.o (+RO, +FIRST) ... } RAM 0x0000 0x4000 { vectors.o (+RO, +FIRST) ; executed on hard reset ; vector table copied ; from ROM to RAM at zero ... } } ロード領域 ROM_LOAD は、リマップが行われた後のコードとデータのロードアドレスを 示すため、0x10000 に配置されます。 2.5.6 スタックポインタの初期化 リセットハンドラは、少なくとも、アプリケーションで使用される実行モードのスタッ クポインタに初期値を割り当てる必要があります。 例 2-10 では、スタックが stack_base に配置されます。このシンボルにはハードコー ディングされたアドレスを指定することができます。または、別のアセンブラソース ファイルで定義し、スキャッタローディング記述ファイルによって配置することもで 「スキャッタローディング記述ファイルでのス きます。この方法の詳細については、 タックとヒープの配置」(P. 2-37)を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-31 組み込みソフトウェアの開発 例 2-10 スタックポインタの初期化 ; --- Amount of memory (in bytes) allocated for stacks Len_FIQ_Stack EQU 256 Len_IRQ_Stack EQU 256 ... Offset_FIQ_Stack EQU 0 Offset_IRQ_Stack EQU Offset_FIQ_Stack + Len_FIQ_Stack ... Reset_Handler ; stack_base could be defined above, or located in a description file LDR r0, stack_base ; ; Enter each mode in turn and set up the stack pointer MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit SUB sp, r0, #Offset_FIQ_Stack MSR SUB ... CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit sp, r0, #Offset_IRQ_Stack 例 2-10 では、FIQ モードおよび IRQ モードに 256 バイトのスタックを割り当てていま すが、他の実行モードにも同じように割り当てることができます。スタックポインタ をセットアップするには、各モードに(割り込みをディセーブルして)入り、スタッ クポインタに適切な値を割り当てます。 リセットハンドラでセットアップされるスタックポインタの値は、C ライブラリ初期化 コードにより、パラメータとして __user_initial_stackheap() に自動的に渡されます。 そのため、この値は __user_initial_stackheap() によって変更することはできません。 例 2-11 は、例 2-10(2-32 ページ)で示したスタックポインタのセットアップに使用で きる __user_initial_stackheap() の実装を示しています。 例 2-11 IMPORT heap_base EXPORT __user_initial_stackheap __user_initial_stackheap ; heap base could be hard-coded, or placed by description file LDR r0,=heap_base ; r1 contains SB value BX lr 2-32 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.5.7 ハードウェアの初期化 一般的には、システム初期化コードはすべてメインアプリケーションから切り離した 方が有効です。ただし、キャッシュや割り込みのイネーブルなど、システムの初期化 のいくつかのコンポーネントは、C ライブラリ初期化コードの実行後に実行される必要 があります。 $Sub および $Super という関数ラッパシンボルを使用すると、メインアプリケーション に入る直前に実行されるルーチンを(効果的に)挿入できます。このメカニズムによ り、ソースコードを変更することなく、関数を拡張できます。 例 2-12(2-33 ページ)は、この $Sub および $Super の使用方法を示しています。リンカ は、main() への関数呼び出しを、$Sub$$main() への呼び出しに置き換えます。ここか らキャッシュをイネーブルするルーチンと割り込みをイネーブルする別のルーチンを 呼び出すことができます。 このコードは、$Super$$main() を呼び出すことで実際の main() に分岐します。 注 $Sub と $Super の 詳 細 については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 例 2-12 $Sub と $Super の使用方法 extern void $Super$$main(void); void $Sub$$main(void) { cache_enable(); int_enable(); $Super$$main(); } ARM DUI 0203GJ // enables caches // enables interrupts // calls original main() Copyright © 2002-2006 ARM Limited. All rights reserved. 2-33 組み込みソフトウェアの開発 2.5.8 実行モードに関する注意事項 メインアプリケーションをどのモードで実行するかを検討する必要があります。選択 するモードによって、システムの初期化の実装方法が異なります。 起動時にリセットハンドラおよび $Sub$$main の両方で実装する可能性のある多くの機 能は、特権モードで実行する場合にしか使用できません。例えば、オンチップメモリ の操作や割り込みのイネーブルなどが挙げられます。 アプリケーションを特権モード(例えば、スーパーバイザ)で実行する場合、これは 問題になりません。リセットハンドラを終了する前に適切なモードに変更するように して下さい。 アプリケーションをユーザモードで実行する場合は、必要なタスクを特権モードで実 行した後にしかユーザモードに変更できません。通常、この変更は $Sub$$main() で行 われます。 注 __user_initial_stackheap() では、アプリケーションモードのスタックをセットアップ する必要があります。このため、ユーザモードレジスタを使用する、システムモード のリセットハンドラを終了する必要があります。その後、__user_initial_stackheap() がシステムモードで実行されるため、ユーザモードに入ったときにもアプリケーショ ンのスタックとヒープはセットアップされた状態を維持することができます。 2-34 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 2.5.9 ビルド 4 のサンプルコード ビルド 4 のサンプルは、Integrator プラットフォーム上でスタンドアロンで実行できま す。こ の サ ン プ ル ビ ル ド の フ ァ イ ル は、主 な サ ン プ ル デ ィ レ ク ト リ ...\emb_sw_dev\build4 にあります。 ビルド 4 では、ビルド 3 のサンプルプロジェクトに以下の変更が加えられています。 ベクタテーブル ベクタテーブルがプロジェクトに追加され、スキャッタローディング記 述ファイルによって配置されました。 リセットハンドラ リセットハンドラが init.s に追加されました。ARM926EJ-S™ のビルド には、TCM と MMU のセットアップをそれぞれ行う 2 つのモジュールが 含まれています。これらのモジュールは、任意のコアを搭載した Integrator システムで実行される ARM7TDMI® のビルドには含まれていません。 ROM/RAM のリマップは、リセットの直後に行われます。 $Sub$$main() ARM926EJ-S のビルドでは、メインアプリケーションに入る前に、キャッ シュが $Sub$$main() 内でイネーブルされます。 組み込み記述ファイル リマップ後のメモリビューを反映する、組み込み記述ファイルが使用さ れています。 これらのビルド両方のビルドファイルでは、アドレス 0x24000000 に存在する Integrator AP のアプリケーション用フラッシュメモリへのダウンロードに適したバイナリファ イルが生成されます。 Integrator AP マザーボード上のタイマを使用して、正確なタイマが実装されています。 このタイマによって IRQ が生成され、100 分の 1 秒(0.01 秒)ごとにカウンタをイン クリメントするハンドラがインストールされます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-35 組み込みソフトウェアの開発 2.6 メモリマップに関するその他の注意事項 これまでのセクションでは、スキャッタローディング記述ファイルでのコードとデー タの配置について説明してきました。ただし、ターゲットハードウェアのペリフェラ ルの位置と、スタックリミットとヒープリミットは、ソースファイルまたはヘッダファ イルでハードコーディングされていることを前提としていました。ターゲットのメモ リマップに関するすべての情報は記述ファイルに保存して、ソースコードから絶対ア ドレスへのすべての参照を削除すると有効です。 このセクションは以下のサブセクションから構成されています。 • スキャッタローディング記述ファイルでのターゲットのペリフェラルの配置 • スキャッタローディング記述ファイルでのスタックとヒープの配置(P. 2-37) • 2.6.1 ビルド 5 のサンプルコード(P. 2-42) スキャッタローディング記述ファイルでのターゲットのペリフェラルの配置 通常、ペリフェラルレジスタのアドレスは、プロジェクトのソースファイルまたはヘッ ダファイルにハードコーディングされています。また、ペリフェラルレジスタにマッ プされる構造体を宣言し、これらの構造体を記述ファイルに配置することもできます。 例えば、32 ビットのメモリマップレジスタを 2 つ使用するタイマペリフェラルがター ゲットに実装されているとします。例 2-13 は、これらのレジスタにマップされる C の 構造体を示しています。 例 2-13 ペリフェラルレジスタへのマップ __attribute__ ((zero_init)) struct { volatile unsigned ctrl; volatile unsigned tmr; } timer_regs; /* timer control */ /* timer value */ この構造体をメモリマップ内の特定のアドレスに配置するには、この構造体を保持す る新しい実行領域を作成します。 例 2-14(2-37 ページ)に示す記述ファイルでは、timer_regs 構造体が 0x40000000 に配 置されます。 レジスタの内容は、アプリケーションの起動時にゼロに初期化しないことが重要です。 ゼロに初期化されるとシステムの状態が変更される場合があります。実行領域に UNINIT 属性を指定すると、その領域内の ZI データがゼロで初期化されるのを防ぐこと ができます。 2-36 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 例 2-14 マップされた構造体の配置 ROM_LOAD 0x24000000 0x04000000 { ; ... TIMER 0x40000000 UNINIT { timer_regs.o (+ZI) } ; ... } 2.6.2 スキャッタローディング記述ファイルでのスタックとヒープの配置 多くの場合、スタックとヒープの位置は記述ファイルで指定することをお勧めします。 これには、主に以下の 2 つの利点があります。 • メモリマップに関するすべての情報が 1 つのファイルに保持されます。 • スタックとヒープを変更する場合は、再リンクする必要はありますが、再コンパ イルする必要はありません。 このセクションでは、この実装方法について説明します。 • シンボルの明示的な配置(最も簡単な方法) • リンカ生成シンボルの使用(P. 2-39) • スキャッタローディング記述ファイルの EMPTY 属性の使用(P. 2-41) シンボルの明示的な配置 「スタックポインタの初期化」(P. 2-31)では、シンボル stack_base および heap_base を、記述ファイル内に配置できる参照シンボルとして使用しています。これを行うに は、stackheap.s という名前のアセンブラモジュール内に、stack_base および heap_base という名前のシンボルを作成します。この方法は、2 つの領域によるメモリモデルのス タックリミットとヒープリミットにも使用できます。 例 2-15 に示すように、各シンボルは記述ファイル内のそれぞれの実行領域内に配置で きます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-37 組み込みソフトウェアの開発 例 2-15 stackheap.s 内でのシンボルの明示的な配置 AREA stacks, DATA, NOINIT EXPORT stack_base stack_base SPACE 1 AREA heap, DATA, NOINIT EXPORT heap_base heap_base END SPACE 1 図 2-14 および 例 2-16 は、ヒープベースを 0x20000 に配置し、スタックベースを 0x40000 に配置する方法を示しています。スタックベースとヒープベースの位置は、それぞれ の実行領域のアドレスを編集することによって変更できます。 この方法の欠点は、SPACE (stack_base) の 1 ワードがスタック領域を越えて使用される ことです。 0x40000 stack_base Stack Heap 0x20000 heap_base 図 2-14 シンボルの明示的な配置 2-38 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 例 2-16 スキャッタローディング記述ファイルでのシンボルの明示的な配置 LOAD_FLASH 0x24000000 0x04000000 { ; ... HEAP 0x20000 UNINIT { stackheap.o (heap) } STACKS 0x40000 UNINIT { stackheap.o (stacks) } ; ... } リンカ生成シンボルの使用 この方法では、スタックとヒープのサイズをオブジェクトファイルで指定する必要が あります。 まず、例 2-17 に示すように、stackheap.s などのアセンブラソースファイルで、スタッ クとヒープに適切なサイズのエリアを定義します。 SPACE ディレクティブを使用して、ゼロ初期化されるメモリブロックを予約します。 NOINIT エリア属性を設定して、このゼロ初期化を無効にします。 開発時には、スタックを最大限に使用できるように、スタックをゼロ初期化すること もできます。このソースファイルでは、ラベルは必要ありません。 例 2-17 スタックとヒープのセクションの配置 AREA stack, DATA, NOINIT SPACE 0x3000 ; Reserve stack space AREA heap, DATA, NOINIT SPACE 0x3000 ; Reserve heap space END 次に、例 2-18 に示すように、これらのセクションをスキャッタローディング記述ファ イルの各実行領域に配置します。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-39 組み込みソフトウェアの開発 例 2-18 スタックとヒープのセクションの配置 LOAD_FLASH 0x24000000 0x04000000 { : STACKS 0x1000 UNINIT ; length = 0x3000 { stackheap.o (stack) ; stack = 0x4000 to 0x1000 } HEAP 0x15000 UNINIT { stackheap.o (heap) } ; length = 0x3000 ; heap = 0x15000 to 0x18000 } リンカによって、各実行領域のベースとリミットを指すシンボルが生成されます。こ れらのシンボルは、__user_initial_stackheap() で使用するターゲット変更コードにイ ンポートできます。 Image$$STACKS$$ZI$$Limit = 0x4000 Image$$STACKS$$ZI$$Base = 0x1000 Image$$HEAP$$ZI$$Base = 0x15000 Image$$HEAP$$ZI$$Limit = 0x18000 例 2-19 に示すように、DCD ディレクティブを使用してこれらの値に意味のある名前を指 定することにより、このコードを読みやすくすることができます。 例 2-19 DCD ディレクティブの使用 IMPORT IMPORT IMPORT IMPORT ||Image$$STACKS$$ZI$$Base|| ||Image$$STACKS$$ZI$$Limit|| ||Image$$HEAP$$ZI$$Base|| ||Image$$HEAP$$ZI$$Limit|| stack_base DCD stack_limit DCD ||Image$$STACKS$$ZI$$Limit|| ||Image$$STACKS$$ZI$$Base|| ; = 0x4000 ; = 0x1000 heap_base heap_limit ||Image$$HEAP$$ZI$$Base|| ||Image$$HEAP$$ZI$$Limit|| ; = 0x15000 ; = 0x18000 DCD DCD これらの例を使用して、ヒープベースを 0x15000 に、スタックベースを 0x1000 に配置 できます。その後、スタックベースとヒープベースの位置は、それぞれの実行領域の アドレスを編集することによって簡単に変更できます。 2-40 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 組み込みソフトウェアの開発 スキャッタローディング記述ファイルの EMPTY 属性の使用 この方法では、リンカのスキャッタローディング記述ファイルの EMPTY 属性を使用しま す。この属性を使用すると、オブジェクトコードやデータを保持しない領域を定義で きます。この方法は、スタックまたはヒープを定義する便利な方法です。この領域の 長さは、EMPTY 属性の後に指定されます。メモリ内で上方に拡大するヒープの場合、領 域の長さは正の値になります。スタックの場合は、メモリ内で下方に拡大することを 示すように、領域の長さが負の数としてマークされます。例 2-20(2-41 ページ)は、 EMPTY 属性の使用方法を示しています。 この方法の利点は、スタックとヒープのサイズおよび位置を 1 つの場所、つまり、ス キャッタローディング記述ファイルで定義できることです。stackheap.s ファイルを作 成する必要はありません。 例 2-20 EMPTY を使用したスタック領域とヒープ領域の配置 ROM_LOAD 0x24000000 0x04000000 { ... HEAP 0x30000 EMPTY 0x3000 { } STACKS 0x40000 EMPTY -0x3000 { } ... } リンク時に、リンカによって、これらの EMPTY 領域を表すシンボルが生成されます。 Image$$HEAP$$ZI$$Base Image$$HEAP$$ZI$$Limit Image$$STACKS$$ZI$$Base Image$$STACKS$$ZI$$Limit = = = = 0x30000 0x33000 0x3D000 0x40000 その後、例 2-21 に示すように、アプリケーションコードでこれらのシンボルを処理で きます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 2-41 組み込みソフトウェアの開発 例 2-21 EMPTY 領域を表すリンカ生成シンボル heap_base heap_limit stack_base stack_limit 2.6.3 IMPORT IMPORT ||Image$$HEAP$$ZI$$Base|| ||Image$$HEAP$$ZI$$Limit|| DCD DCD ||Image$$HEAP$$ZI$$Base|| ||Image$$HEAP$$ZI$$Limit|| IMPORT IMPORT ||Image$$STACKS$$ZI$$Base|| ||Image$$STACKS$$ZI$$Limit|| DCD DCD ||Image$$STACKS$$ZI$$Limit|| ||Image$$STACKS$$ZI$$Base|| ビルド 5 のサンプルコード ビルド 5 のサンプルコードはビルド 4 と同じですが、「シンボルの明示的な配置」 (P. 2-37)で説明したように、すべてのターゲットメモリマップ情報がスキャッタロー ディング記述ファイルに記述されています。 スキャッタローディング記述ファイルのシンボル スタック、ヒープ、およびペリフェラルを配置するためのシンボルがア センブラモジュール内で宣言されています。 更新されたスキャッタローディング記述ファイル ビルド 4 の組み込み記述ファイルが、スタック、ヒープ、データ TCM、 およびペリフェラルを配置するように更新されています。 このサンプルビルドのファイルは、主なサンプルディレクトリ ...\emb_sw_dev\build5 にあります。 スタックとヒープは、リンカシンボルを使用して配置されます( 「リンカ生成シンボル の使用」(P. 2-39)参照)。 2-42 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第3章 位置非依存コードとデータの記述 本章では、Procedure Call Standard for the ARM Architecture(AAPCS)を使用する位置非 依存コードとデータを記述する方法について説明します。本章は以下のセクションか ら構成されています。 • 位置非依存(P. 3-2) • • ARM DUI 0203GJ 読み出し専用の位置非依存(P. 3-3) 読み出し - 書き込みの位置非依存(P. 3-6) Copyright © 2002-2006 ARM Limited. All rights reserved. 3-1 位置非依存コードとデータの記述 3.1 位置非依存 ARM® 命令セットおよび Thumb® 命令セットでは、どちらも PC 相対命令(例えば、BL) を使用することによって、位置非依存コード(つまり、再配置可能なコード)がサポー トされます。 注 これは、リンカによって作成されるイメージの種類である再配置可能な ELF とは異な ります。 再配置可能なアセンブラコードを記述することはできますが、コードにアドレス定数 を含めることはできません。コードの参照に使用するリテラルアドレスは、PC 相対の オフセットである必要があります。アドレスにアクセスする前に、ADD 命令を使用して PC が追加されます。 コードとデータは、どちらも位置非依存にすることができます。 • コードを異なるアドレスで実行できるようにするには、コードは位置非依存(再 配置可能)である必要があります。ただし、コードからのアクセスは、固定アド レスのスタティックデータに限定されます。 • 位置非依存のデータでは、すべてのアクセスが、スタティックベースレジスタ sb の相対位置で行われる必要があります。これは共有ライブラリのメカニズムの実 装に使用されます。 RVCT では、C およびアセンブラ言語における位置非依存のコードとデータをサポート しており、再配置可能またはリエントラントなコードを記述することができます。ただ し、C++ はサポートされていません。本章の残りの部分では、この方法について説明し ます。 詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide の以下の 章を参照して下さい。 3.1.1 • コンパイラの使用方法について説明している章の位置非依存の修飾子に関する セクション • C ライブラリと C++ ライブラリについて説明している章のリエントラントでス レッドセーフなコードの記述に関するセクション AAPCS の使用 Procedure Call Standard for the ARM Architecture(AAPCS)は、Application Binary Interface (ABI) for the ARM Architecture (base standard)[BSABI]仕様の一部です。AAPCS に準拠 したコードを記述すると、別々にコンパイルおよびアセンブルしたモジュールを連動 させることができます。 詳細については、install_directory\Documentation\... にある AAPCS 仕様を参照して 下さい。 3-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 位置非依存コードとデータの記述 3.2 読み出し専用の位置非依存 プログラムのすべての読み出し専用セグメントが位置非依存である場合、そのプログ ラムは読み出し専用の位置非依存(ROPI)となります。 ROPI セグメントは、位置非依存コード(PIC)であることが多いものの、読み出し専 用データであることもあれば、PIC と読み出し専用データの組み合わせであることもあ ります。 注 ROPI では C++ がサポートされないため、ROPI は AAPCS の一部ではありません。た だし、ROPI の C コードまたはアセンブラコードは、コンパイラまたはアセンブラのオ プション --apcs /ropi を使用してコンパイルすることができます。 コードをメモリ内の特定の場所にロードしなくても済むようにするには、ROPI オプ ションを選択します。このオプションは、以下のルーチンに使用すると特に便利です。 • ランタイムイベントに対応してロードされるルーチン • 状況によって他のルーチンとのさまざまな組み合わせでメモリにロードされる ルーチン • 実行中に別のアドレスにマップされるルーチン このセクションは以下のサブセクションから構成されています。 • ROPI に関連するレジスタの使用方法 • • • • 3.2.1 ROPI の C コードおよびアセンブラコードの記述(P. 3-4) コードのリンク(P. 3-4) FPIC アドレス指定(P. 3-4) サンプルコード(P. 3-5) ROPI に関連するレジスタの使用方法 AAPCS で定義されているように、ROPI の有無に関係なく、レジスタの使用方法は同 じです。 install_directory\Documentation\Specifications\... にある Procedure 詳細については、 Call Standard for the ARM Architecture(aapcs.pdf)を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 3-3 位置非依存コードとデータの記述 3.2.2 ROPI の C コードおよびアセンブラコードの記述 ROPI の C コードとアセンブラコードを記述する場合、以下のような要件があります。 • ROPI セグメント内のコードから、同一 ROPI セグメント内のシンボルへの参照 は、PC 相対で行う必要があります。AAPCS では、読み出し専用セグメント用の 他のベースレジスタは定義されていません。ある ROPI セグメントに含まれる項 目のアドレスを、別の ROPI セグメントの項目に割り当てることはできません。 • ROPI セグメント内のコードから、異なる ROPI セグメント内のシンボルへの参 照は、PC 相対で行う必要があります。このような 2 つのセグメントは、互いに 一定の相対位置となるようにする必要があります。 • 他の ROPI セグメントからの参照は、以下のどちらかを指している必要があり ます。 — — • 3.2.3 絶対アドレス 書き込み可能データへの sb 相対参照(「読み出し - 書き込みの位置非依存」 (P. 3-6)参照) ROPI セグメント内のシンボルをアドレス指定する読み出し - 書き込みワードは、 その ROPI セグメントが移動するたびに調整する必要があります。 コードのリンク リンカのコマンドラインオプション --ropi を使用すると、読み出し専用の出力セク ションを含むロードおよび実行領域が位置非依存になります。読み出し専用の入力セ クションは、読み出し専用の位置非依存にする必要があります。詳細については、 RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 3.2.4 FPIC アドレス指定 /fpic 修飾子を使用すると、相対アドレス参照がプログラムをロードした場所に依存し ない、読み出し専用の位置非依存コードが生成されます。コードで System V の共有ラ イブラリを使用している場合にのみ、参照アドレスが実装されます。コードで共有オ ブジェクトを使用している場合、/fpic を指定してコンパイルする必要はありません。 RVCT でサポートされている System V 共有ライブラリの詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 3-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 位置非依存コードとデータの記述 3.2.5 サンプルコード 位置非依存コードの記述に関する詳細については、RealView Development Suite 付属の PIC-PID サンプルを参照して下さい。このサンプルは、主なサンプルディレクトリの install_directory\RVDS\Examples\picpid にあります。 このサンプルは、ROM の固定アドレスにあるカーネルで構成されており、カーネルの 機能を拡張するアプリケーションモジュールも含まれています。アプリケーションモ ジュールは、カーネルに従ってメモリにロードされます。ただし、モジュールのリン ク時に、モジュールがロードされるアドレスは不明です。つまり、モジュールは、位 置非依存(ROPI、PIC)である必要があります。 サンプルには、ソースコード、メークファイル、バッチファイル、および別のモジュー ルをコンパイルしたりリンクする方法の詳細が含まれています(readme.txt 参照)。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 3-5 位置非依存コードとデータの記述 3.3 読み出し - 書き込みの位置非依存 プログラムのすべての読み出し - 書き込みセグメントが位置非依存である場合、そのプ ログラムは読み出し - 書き込みの位置非依存(RWPI)となります。 通常、RWPI セグメントは位置非依存データ(PID)です。 RWPI は AAPCS のバリアントです。コンパイラまたはアセンブラのオプション --apcs /rwpi を選択することにより、データのメモリ位置を決める必要がなくなります。この 方法は、リエントラントルーチンで何度もインスタンスを生成しなければならない データに使用すると、特に便利です。 install_directory\Documentation\Specifications\... にある Procedure 詳細については、 Call Standard for the ARM Architecture(aapcs.pdf)を参照して下さい。 このセクションは以下のサブセクションから構成されています。 • リエントラントルーチン • RWPI に関連するレジスタの使用方法 • • • • 3.3.1 位置非依存データのアドレス指定(P. 3-7) アセンブリ言語を使用した RWPI の記述(P. 3-7) コードのリンク(P. 3-7) サンプルコード(P. 3-7) リエントラントルーチン リエントラントルーチンは、同時に複数のプロセスでスレッド化 できます。各プロセ スには、リエントラントルーチンの読み出し - 書き込みセグメントのコピーが保持され ています。各コピーは、それぞれ異なる値のスタティックベースレジスタ(sb)でア ドレス指定されます。 3.3.2 RWPI に関連するレジスタの使用方法 レジスタ r9 は、スタティックベースレジスタ sb です。このレジスタは、外部から参照 できるルーチンが呼び出されるときに、常に適切なスタティックデータセグメントの ベースアドレスを指している必要があります。 sb を使用しないルーチンでは、r9 を他の目的に使用することもできます。この場合は、 sb の内容をルーチンへのエントリで保存し、終了前に復元する必要があります。また、 sb の内容は、外部ルーチンへの呼び出しが発生する前に復元する必要があります。 RWPI の有無に関係なく、レジスタの使用方法は同じです。 3-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 位置非依存コードとデータの記述 3.3.3 位置非依存データのアドレス指定 RWPI セグメントは、まだ使用されていない間であれば再配置できます。RWPI セグメ ント内のシンボルのアドレスは、以下のように計算されます。 1. 2. リンカによって、セグメント内の固定位置からの読み出し専用オフセットが計算 されます。便宜的に、この固定位置は、プログラムの中で最下位アドレスが指定 された RWPI セグメントの先頭バイトの位置になります。 ランタイム時に、この読み出し専用オフセットが、スタティックベースレジスタ sb の内容に加算されるオフセットとして使用されます。 3.3.4 アセンブリ言語を使用した RWPI の記述 読み出し専用セグメントから RWPI セグメントへの参照は、sb の値を固定(読み出し 専用)オフセットに加算することによって計算されます(RealView Compilation Tools v3.0 Assembler Guide の「ディレクティブリファレンス」の章にある DCDO 参照)。 3.3.5 コードのリンク リンカのコマンドラインオプション --rwpi を使用すると、RW 出力セクションおよび ZI 出力セクションを含むロードおよび実行領域が位置非依存になります。このオプ ションでは、--rw-base の値が必要です。--rw-base を指定しないと、--rw-base 0 が想 定されます。通常、書き込み可能な入力セクションは、RWPI にする必要があります。 こ れ ら の オ プ シ ョ ン の 詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下さい。 3.3.6 サンプルコード 位置非依存コードの記述に関する詳細については、RealView Development Suite 付属の PIC-PID サンプルを参照して下さい。このサンプルは、主なサンプルディレクトリの install_directory\RVDS\Examples\picpid にあります。 このサンプルは、ROM の固定アドレスにあるカーネルで構成されており、カーネルの 機能を拡張するアプリケーションモジュールも含まれています。モジュールにより、何 度もインスタンスを生成できて、カーネルを経由して相互に呼び出し可能な指定の サービスが実装されます。サービスが呼び出されると、カーネルは、スタティックデー タのインスタンスを作成し、サービスに制御を移します。ただし、サービスから、カー ネルが呼び出されることがあります。つまり、モジュールは、位置非依存データ(RWPI、 PID)を保持している必要があります。 サンプルには、ソースコード、メークファイル、バッチファイル、および別のモ ジュールをコンパイルしたりリンクしたりする方法の詳細が含まれています ( readme.txt 参照)。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 3-7 位置非依存コードとデータの記述 3-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第4章 ARM と Thumb のインターワーク 本章では、Thumb 命令セットを実装するプロセッサ用のコードを記述する際に ARM® 状態と Thumb® 状態を切り替える方法について説明します。本章は以下のセクションか ら構成されています。 • インターワークについて(P. 4-2) ARM DUI 0203GJ • アセンブリ言語のインターワーク(P. 4-7) • C と C++ のインターワークとベニア(P. 4-13) • ベニアを使用したアセンブリ言語のインターワーク(P. 4-19) Copyright © 2002-2006 ARM Limited. All rights reserved. 4-1 ARM と Thumb のインターワーク 4.1 インターワークについて インターワークを使用すると、ARM コードと Thumb コードを混用することにより、以 下の動作を実現できます。 • ARM ルーチンから Thumb 状態の発呼側への復帰 • Thumb ルーチンから ARM 状態の発呼側への復帰 つまり、インターワーク用のコードをコンパイルまたはアセンブルした場合、モジュー ルで使用される命令セットを考慮することなく、コードから異なるモジュールのルー チンを呼び出すことができます。 ARM リンカでは、ARM 関数がいつ Thumb 状態から呼び出され、Thumb 関数がいつ ARM 状態から呼び出されるのかを検出します。また ARM リンカでは、必要に応じて、 呼び出し命令と復帰命令を変更するか、またはベニア と呼ばれる小さなコードセグメ ントを挿入することによって、プロセッサ状態を変更します。 ARMv5T 以降のアーキテクチャでは、特別な命令を使用しなくてもプロセッサ状態を 変更できます。通常、ARMv5T プロセッサではインターワークによるオーバーヘッド はありません。 注 ARMv5TE 以降のアーキテクチャ用のコンパイルはインターワークを前提としており、 常にインターワークを行うコードを生成します。ただし、ARMv5TE 用にビルドされた アセンブリコードはインターワークを前提としていないため、--apcs /interwork アセ ンブラオプションを使用してアセンブリコードをビルドする必要があります。 4.1.1 AAPCS の使用 ARM コードと Thumb コードは、AAPCS の要件に準拠していれば、自由に混在させるこ とができます。詳細については、install_directory\Documentation\Specifications\... にある Procedure Call Standard for the ARM Architecture(aapcs.pdf)を参照して下さい。 ARM アセンブリ言語モジュールを記述する場合は、AAPCS に準拠したコードを記述 する必要があります。複数のソースファイルをリンクする場合は、すべてのファイル で互換性のある AAPCS オプションを使用する必要があります。互換性のないオプショ ンが検出されると、リンカによってエラーメッセージが生成されます。 このセクションでは、以下の内容について説明します。 • インターワークに適した状況(P. 4-3) • /interwork オプションの使用(P. 4-4) • インターワーク呼び出しの検出(P. 4-5) リンカによって生成されるベニア(P. 4-5) • 4-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.1.2 インターワークに適した状況 Thumb 命令をサポートする ARM プロセッサ用のコードを記述する場合、アプリケー ションの大部分が Thumb 状態で実行されるように記述することがほとんどです。この 方法を使用すると、コード密度を最大限に向上させることができます。さらに、8 ビッ ト幅または 16 ビット幅のメモリを使用することで最高のパフォーマンスを得ることが できます。しかし、以下のような理由で、アプリケーションの一部を ARM 状態で実行 する場合も考えられます。 速度 アプリケーションには、速度が重要になる部分があります。このような セクションは、Thumb 状態よりも ARM 状態で実行した方が効率的な場 合があります。状況によっては、1 つの ARM 命令で、等価な Thumb 命 令を使用するよりも多くの処理を行うことができます。 例えば、小容量の高速 32 ビットメモリを実装しているシステムがあると します。このメモリから ARM コードを実行する場合、8 ビットまたは 16 ビットのメモリから各命令をフェッチすることによるオーバーヘッ ドが発生せずに済みます。 機能 Thumb 命令は、等価な ARM 命令に比べると柔軟性の点で劣ります。ま た、操作によっては Thumb 状態では実行できないものもあります。以下 の操作を実行するには、ARM 状態に切り替える必要があります。 • 割り込みのイネーブルやディセーブルおよびモードの切り替えを 行うための CPSR へのアクセス • コプロセッサへのアクセス • C でサポートされていない DSP 演算命令 例外処理 プロセッサ例外が発生すると、プロセッサは自動的に ARM 状態に移行 します。つまり、例外ハンドラが後で Thumb 状態に移行して例外のメイ ン処理を実行する場合でも、この例外ハンドラの最初の部分は ARM 命 令でコーディングする必要があります。このような処理の最後にハンド ラからメインアプリケーションに戻るには、プロセッサは ARM 状態に 戻る必要があります。 スタンドアロンの Thumb プログラム Thumb 命令をサポートする ARM プロセッサは、常に ARM 状態で起動 します。デバッガで単純な Thumb アセンブリ言語プログラムを実行する には、Thumb 状態への切り替えを実行してからメイン Thumb ルーチンを 呼び出す ARM ヘッダを追加する必要があります。この例については、 「ARM ヘッダのサンプル」(P. 4-9)を参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-3 ARM と Thumb のインターワーク 4.1.3 /interwork オプションの使用 ARM コンパイラおよびアセンブラには、--apcs /interwork オプションを使用できま す。このオプションを設定した場合、以下のようになります。 • コンパイラまたはアセンブラによって、インターワーク属性がオブジェクトファ イル内に記録されます。 • リンカによって、サブルーチンエントリにインターワークベニアが挿入されます。 • アセンブリ言語では、発呼側の命令セット状態に復帰する BX lr などの関数終了 コードを記述する必要があります。 • C または C++ では、コンパイラによって発呼側の命令セット状態に復帰する関数 終了コードが生成されます。 • C または C++ では、コンパイラによって間接呼び出しまたは仮想呼び出しに BX 命令が使用されます。 オブジェクトファイルに以下のサブルーチンが含まれている場合は、 --apcs /interwork オプションを使用して下さい。 • ARM コードに復帰する必要が生じる可能性のある Thumb サブルーチン • Thumb コードに復帰する必要が生じる可能性のある ARM サブルーチン • ARM コードの間接呼び出しまたは仮想呼び出しを行う可能性のある Thumb サブ ルーチン Thumb コードの間接呼び出しまたは仮想呼び出しを行う可能性のある ARM サブ ルーチン • 注 モジュールの中に、#pragma arm または #pragma thumb でマークされた関数が含まれて いる場合は、--apcs /interwork を使用してそのモジュールをコンパイルする必要があ ります。これにより、関数を他方の(ARM または Thumb)状態から正常に呼び出すこ とができます。 上記のサブルーチンが含まれていなければ、/interwork オプションを使用する必要は ありません。例えば、オブジェクトファイルは、/interwork を必要とせず、以下のコー ドで構成することができます。 4-4 • 例外による割り込みが可能な Thumb コード。例外によってプロセッサが ARM 状 態に切り替わるため、ベニアは必要ありません。 • Thumb コードから例外を処理することが可能な例外処理コード。このコードの復 帰にベニアは必要ありません。 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.1.4 インターワーク呼び出しの検出 リンカでは、直接的な ARM/Thumb インターワーク呼び出しを検出したときに、被発呼 側のルーチンがインターワーク用にビルドされていないとエラーを生成します。この場 合、呼び出される側のルーチンをインターワーク用に再ビルドする必要があります。 例 4-3(4-15 ページ)の ARM ルーチンを、--apcs /interwork オプションを指定せずに コンパイルおよびリンクした場合、例 4-1 に示すエラーが生成されます。 例 4-1 Error:L6239E: Cannot call ARM symbol 'arm_function' in non-interworking object armsub.o from THUMB code in thumbmain.o(.text) このようなエラーは、ARM から Thumb、または Thumb から ARM へのインターワーク 呼び出しが、そのルーチンのシンボルとは異なるオブジェクトモジュールから検出さ れたにも関わらず、呼び出される側のルーチンがインターワーク用にコンパイルされ ていないことを示します。この場合は、--apcs /interwork を指定して、このシンボル を含むモジュールを再コンパイルする必要があります。 4.1.5 リンカによって生成されるベニア ベニアは、分岐で以下が必要となる場合に、リンカによって自動的に挿入される小さ なコードセグメントです。 • 状態の切り替え • 分岐命令の範囲内に収まらない分岐先 ベニアは、次にターゲットアドレスに分岐する、元の分岐のターゲットになります。 リンカでは、以前の呼び出しで生成されたベニアを、その後の同じ関数の呼び出しで 再利用できます。ただし、どちらのセクションからもそれらの呼び出しに到達できる 必要があります。 ベニアとのインターワークの詳細については、以下を参照して下さい。 • C と C++ のインターワークとベニア(P. 4-13) • ARM DUI 0203GJ ベニアを使用したアセンブリ言語のインターワーク(P. 4-19) Copyright © 2002-2006 ARM Limited. All rights reserved. 4-5 ARM と Thumb のインターワーク ベニアの型 ベニアの型を以下に示します。 long 任意の状態の切り替えを含めることができます。 short 状態の切り替えのみを実行します。 inline 状態の切り替えのみを実行しますが、ベニアが挿入される関数の先頭に 追加されます。 Veneer$$Code セクション リ ン カ で は、ベ ニ ア ご と に Veneer$$Code という入力セクションを作成します。 *(Veneer$$Code) を使用すると、スキャッタローディング記述ファイルにベニアコード を配置できます。ただし安全でない場合、リンカはベニアコードを配置しません。 実行領域のアドレス範囲やサイズ制限により、ベニア入力セクションを領域に割り当 てることができない場合があります。指定した領域にベニアを追加できない場合は、ベ ニアを生成した、再配置された入力セクションを含む実行領域にそのベニアが追加さ れます。 詳細については、RealView Compilation Tools v3.0 Linker and Utilities Guide を参照して下 さい。 使用するベニア数の削減 以下のことに注意すると、使用するベニアの数を最小限に抑えることができます。 • 呼び出された側の関数が呼び出し元の範囲内に収まるようにメモリマップを構 築します。 • 範囲内の関数を繰り返し呼び出すことによってベニアを共有するようにします。 • 状態の切り替えを最小限に抑えます。 4-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.2 アセンブリ言語のインターワーク アセンブリ言語ソースファイルには、複数のエリア(ELF セクションに対応するエリ ア)を含めることができます。各エリアは、ARM 命令と Thumb 命令の一方または両方 を保持できます。 リンカを使用して、呼び出し元とは異なる命令セットを使用するルーチンとの間の呼 び出しを設定できます。これを行うには、BL を使用してルーチンを呼び出します(「ベ ニアを使用したアセンブリ言語のインターワーク」(P. 4-19)参照)。 必要であれば、命令セットを明示的に変更するコードを記述することもできます。状 況によっては、この方法で、サイズが小さいコードや実行速度が速いコードを記述で きます。 プロセッサ状態の切り替えを行う命令は以下のとおりです。 • BX。詳細については、 「分岐命令と切り替え命令」を参照して下さい。 • BLX、LDR、LDM、および POP (ARMv5 以上のみ) 「ARM アーキ 。詳細については、 テクチャ v5T 以降とのインターワーク」(P. 4-11)を参照して下さい。 以下のディレクティブを使用して、適切な命令セットの命令をアセンブルするようア センブラに指定できます(「アセンブラモードの変更」(P. 4-8)参照)。 • ARM • THUMB このセクションは以下のサブセクションから構成されています。 • 分岐命令と切り替え命令 • アセンブラモードの変更(P. 4-8) • ARM ヘッダのサンプル(P. 4-9) 4.2.1 • ARM アーキテクチャ v5T 以降とのインターワーク(P. 4-11) • Thumb コード内のラベル(P. 4-12) 分岐命令と切り替え命令 BX 命令は、Thumb をサポートするコアでのみ使用できます。この命令は、指定したレ ジスタに含まれているアドレスに分岐します。アドレス範囲は 4GB です。この分岐ア ドレスのビット 0 の値により、ARM 状態と Thumb 状態のどちらで実行を継続するかが 「ARM アーキテクチャ 決定されます。ARMv5 で使用できるその他の命令については、 v5T 以降とのインターワーク」(P. 4-11)を参照して下さい。 アドレスのビット 0 をこの方法で使用できるのは、以下の理由によります。 ARM DUI 0203GJ • すべての ARM 命令がワード境界で整列されるため、どの ARM 命令でもアドレ スのビット 0 とビット 1 は使用されない。 • すべての Thumb 命令がハーフワード境界で整列されるため、Thumb 命令ではア ドレスのビット 0 は使用されない。 Copyright © 2002-2006 ARM Limited. All rights reserved. 4-7 ARM と Thumb のインターワーク 構文 BX の構文には以下のいずれかを使用します。 Thumb BX Rn ARM BX{cond} Rn 各パラメータには以下の意味があります。 Rn r0 ~ r15 の中で、分岐先のアドレスを保持するレジスタを指定します。 このレジスタのビット 0 の値によって、プロセッサ状態が以下のように 決定されます。 ビット 0 が設定されている場合は、 分岐アドレスにある命令が Thumb 状態で実行されます。 • ビット 0 がクリアされている場合は、分岐アドレスにある命令が ARM 状態で実行されます。 任意の条件コードを指定します。条件実行できるのは ARM バージョン の BX のみです。 cond 4.2.2 • アセンブラモードの変更 ARM アセンブラでは、Thumb コードと ARM コードの両方をアセンブルできます。 --thumb オプションを指定して呼び出さない限り、アセンブラはデフォルトで ARM コードをアセンブルします。 Thumb をサポートする ARM プロセッサは ARM 状態で起動されるため、BX 命令を使用 して Thumb 状態への分岐と切り替えを行い、以下のアセンブラディレクティブを使用 して、アセンブリモードを切り替えるようアセンブラに指定する必要があります。 THUMB 後続の命令を Thumb 命令としてアセンブルするようアセンブラに指定 します。また、後続の命令が存在するかどうかに関係なく、2 バイト境 界への整列を行います。 ARM ARM 命令のアセンブルに戻るようアセンブラに指定します。また、後 続の命令が存在するかどうかに関係なく、4 バイト境界への整列を行い ます。 これらのディレクティブの詳細については、RealView Compilation Tools v3.0 Assembler Guide を参照して下さい。 4-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.2.3 ARM ヘッダのサンプル 例 4-2 には 4 つのコードセクションが含まれています。サンプルの後に、各コードセク ションについて説明します。 例 4-2 PRESERVE8 AREA ENTRY AddReg,CODE,READONLY ; SECTION 1 start ADR r0, ThumbProg + 1 BX r0 ; SECTION 2 THUMB ThumbProg MOVS r2, #2 MOVS r3, #3 ADDS r2, r2, r3 ADR r0, ARMProg BX r0 ; SECTION 3 ARM ARMProg MOV r4, #4 MOV r5, #5 ADD r4, r4, r5 ; SECTION 4 stop MOV r0, #0x18 LDR r1, =0x20026 SVC 0x123456 END ; Name this block of code. ; Mark first instruction to call. ; ; ; ; Generate branch target address and set bit 0, hence arrive at target in Thumb state. Branch exchange to ThumbProg. ; Subsequent instructions are Thumb code. ; Load r2 with value 2. ; Load r3 with value 3. ; r2 = r2 + r3 ; Branch exchange to ARMProg. ; Subsequent instructions are ARM code. ; ; ; ; angel_SWIreason_ReportException ADP_Stopped_ApplicationExit ARM semihosting (formerly SWI) Mark end of this file. SECTION 1 には、プロセッサを Thumb 状態に切り替える、ARM コードの短いヘッダセ クションが実装されています。このヘッダコードには以下の命令が使用されています。 • ARM DUI 0203GJ 分岐アドレスをロードし、最下位ビットを設定する ADR pseudo 命令。ADR pseudo 命令では、r0 に pc+offset+1 の値をロードすることによってアドレスを生成しま す。つまり、ThumbProg に 1 を加算したアドレスを生成します。 Copyright © 2002-2006 ARM Limited. All rights reserved. 4-9 ARM と Thumb のインターワーク 注 ADR 命令は、同じセクション内のシンボルに使用されます。範囲が広い場合は、 LDR 命令を使用します。ADR および LDR pseudo 命令の詳細については、RealView Compilation Tools v3.0 Assembler Guide を参照して下さい。 • Thumb コードに分岐し、プロセッサ状態を切り替える BX 命令。 SECTION 2 にある ThumbProg という名前のラベルの前には、THUMB ディレクティブが配 置されています。このディレクティブは、後続のコードを Thumb コードとして処理す るようアセンブラに指定しています。この Thumb コードによって、2 本のレジスタの 内容が加算されます。 このコードでは、再度 ADR 命令を使用してラベル ARMProg のアドレスを取得しますが、 このとき最下位ビットはクリアされたままです。次の BX 命令で ARM 状態に戻ります。 SECTION 3 の ARMProg という名前のラベルでは、2 本のレジスタの内容が加算されます。 SECTION 4 の stop という名前のセクションでは、正常なアプリケーションの終了を通 知するセミホスティングを使用します。セミホスティングの詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide を参照して下さい。 注 Thumb セミホスティングで使用する SVC 番号は、ARM セミホスティングとは異なり ます(0x123456 ではなく 0xAB が使用されます)。 シンボルのエクスポート Thumb 命令を参照するシンボルをエクスポートすると、リンカによって Thumb コード の任意のラベルのアドレスに 1 が自動的に加算されます。 シンボルをエクスポートしなかった場合は、Thumb 命令を参照するシンボルに手動で 1 を加算する必要があります。例 4-2(4-9 ページ)では、ThumbProg+1 がこれに当たり ます。この作業は、すべての参照がアセンブラによって解決され、リンカによってシ ンボルが検出されないため必要となります。 サンプルのビルド サンプルをビルドおよび実行するには 4-10 1. テキストエディタを使用してコードを入力し、addreg.s というファイル名で保存 します。 2. コマンドプロンプトで「armasm -g addreg.s」 と入力し、このソースファイルを アセンブルします。 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 3. 4. 4.2.4 「armlink addreg.o -o addreg」 と入力し、このファイルをリンクします。 適切なデバッグターゲットで、互換性のあるデバッガ(RealView® Debugger や AXD など)を使用してイメージを実行します。このプログラムを 1 命令ずつス テップスルーすると、プロセッサが Thumb 状態に移行するのがわかります。こ の切り替えがどのように通知されるかについては、使用するデバッガのマニュア ルを参照して下さい。 ARM アーキテクチャ v5T 以降とのインターワーク ARMv5T 以降には、以下のような特徴があります。 • インターワークに使用できるその他の命令を以下に示します。 BLX address プロセッサでは、address への PC 相対リンク付き分岐を実行し、状態 を切り替えます。address は、ARM コードでは PC から 32MB 以内に、 Thumb コードでは PC から 4MB 以内に存在する必要があります。 BLX register プロセッサでは、指定されたレジスタに保持されているアドレスへの リンク付き分岐を実行します。ビット 0 の値によって、新しいプロセッ サ状態が決定されます。 いずれの場合でも、lr のビット 0 には CPSR 内の Thumb ビットの現在値が設定さ れます。したがって、復帰命令は自動的に正しいプロセッサ状態に戻ることがで きます。 • LDR、LDM、POP のいずれかによって PC へのロードが実行される場合は、PC にロー ドされた値のビット 0 が、CPSR の Thumb ビットに設定されます。この動作を利 用して命令セットを変更できます。サブルーチンからの復帰にはこの方法が特に 効果的です。同じ復帰命令で、ARM と Thumb のどちらの発呼側にも戻ることが できます。 詳細については、RealView Compilation Tools v3.0 Assembler Guide と ARM アーキテクチャ リファレンスマニュアルを参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-11 ARM と Thumb のインターワーク 4.2.5 Thumb コード内のラベル リンカでは、以下の命令を参照するラベルを区別します。 • ARM 命令 • Thumb 命令 • データ リンカでは、Thumb 命令を参照するラベルの値を再配置するときに、再配置する値の 最下位ビットを設定します。したがって、ラベルへの分岐が発生すると、自動的に適 切な命令セットが選択されます。この処理は、以下のいずれかの命令が分岐に使用さ れている場合に行われます。 • ARMv4T で BX が使用されている場合 • ARMv5T 以上で BX、BLX、または LDR が使用されている場合 バージョン 1.2 以前の ARM Developer Suite™ (ADS)では、Thumb コード内のデータを DATA ディレクティブでマークする必要がありました。現在はその必要はありません。 4-12 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.3 C と C++ のインターワークとベニア ARM 用と Thumb 用にコンパイルされた C および C++ コードは自由に混用できます が、ARMv4T で状態の切り替えを行うには、ARM コードと Thumb コード間の状態の 切り替えを行うベニアが必要です。ARM リンカでは、インターワーク呼び出しを検出 「リ すると、これらのインターワークベニアを生成します。ベニアの詳細については、 ンカによって生成されるベニア」(P. 4-5)を参照して下さい。 このセクションは以下のサブセクションから構成されています。 • インターワーク用コードのコンパイル • C と C++ のインターワークに関する基本的な規則(P. 4-16) • • 4.3.1 Thumb 状態の関数へのポインタ(P. 4-17) 同一関数の 2 つのコピーの使用(P. 4-18) インターワーク用コードのコンパイル --apcs /interwork コンパイラオプションを指定すると、もう一方のプロセッサ状態に 合わせてコンパイルされたルーチンから呼び出すことができるルーチンを含む C およ び C++ モジュールを、ARM コンパイラでコンパイルできます。 armcc armcc armcc armcc --c90 --c90 --cpp --cpp --thumb --apcs /interwork --arm --apcs /interwork --thumb --apcs /interwork --arm --apcs /interwork 注 --arm がデフォルトオプションです。は拡張子が .c のファイルのデフォルトオプショ ンは --c90 です。また、拡張子が .cpp のファイルのデフォルトオプションは --cpp です。 ARMv4T でインターワークさせるモジュールをコンパイルすると、生成されるコード のサイズが若干大きくなります。ARMv5 では違いはありません。 本体に関数呼び出しが含まれないリーフ関数の場合、コンパイラによって生成される コードで唯一異なるのは、MOV pc,lr が BX lr に置き換えられるという点です。MOV 命 令では、必要な状態の切り替えを行うことができません。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-13 ARM と Thumb のインターワーク Thumb モードの ARMv4T 用にビルドされた非リーフ関数では、コンパイラが、例えば 以下のような 1 つの命令を POP {r4,r5,pc} 以下のようなシーケンスに置き換える必要があります。 POP POP BX {r4,r5} {r3} r3 これによるパフォーマンスへの影響はほとんどありません。インターワークを使用す るモジュールがないことが確実に分かっている場合を除き、ソースモジュールはすべ てインターワーク用にコンパイルして下さい。 また、--apcs /interwork オプションを使用すると、モジュールのコンパイルによって 生成されるコードエリアにインターワーク属性が設定されます。リンカでは、この属 性を検出して適切なベニアを挿入します。 注 ARMv4T 以前のアーキテクチャのプロセッサには BX 命令が実装されていません。その ため、インターワーク用にコンパイルされた ARM コードは、ARMv4T 以降でしか使用 できません。 --info veneers リンカオプションを使 ベニアによって使用される容量を確認するには、 用して下さい。 C 言語を使用したインターワークのサンプル 例 4-3 は、ARM サブルーチンのインターワーク呼び出しを実行する Thumb ルーチンを 示しています。この ARM サブルーチン呼び出しによって、Thumb ライブラリ内の printf() のインターワーク呼び出しが実行されます。これら 2 つのモジュールは、 thumbmain.c および armsub.c という名前で、 主なサンプルディレクトリの ...\interwork に保存されています。 4-14 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 例 4-3 /********************* * thumbmain.c * **********************/ #include <stdio.h> extern void arm_function(void); int main(void) { printf("Hello from Thumb\n"); arm_function(); printf("And goodbye from Thumb\n"); return (0); } /********************* * armsub.c * **********************/ #include <stdio.h> void arm_function(void) { printf("Hello and Goodbye from ARM\n"); } 上記のモジュールをコンパイルしてリンクするには 1. 以下のように入力して、Thumb コードをインターワーク用にコンパイルします。 armcc --thumb -c -g -O1 --apcs /interwork -o thumbmain.o thumbmain.c 2. 以下のように入力して、ARM コードをインターワーク用にコンパイルします。 armcc -c -g -O1 --apcs /interwork -o armsub.o armsub.c 3. 以下のように入力して、オブジェクトファイルをリンクします。 armlink thumbmain.o armsub.o -o thumbtoarm.axf または、以下のように入力して、インターワークベニアのサイズ(例 4-4 参照) を確認します。 armlink armsub.o thumbmain.o -o thumbtoarm.axf --info veneers ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-15 ARM と Thumb のインターワーク 例 4-4 Adding Adding Adding Adding Adding Adding Adding Adding Adding Adding Adding Adding Adding Adding TA AT AT AT TA TA TA AT TA TA TA TA TA TA veneer veneer veneer veneer veneer veneer veneer veneer veneer veneer veneer veneer veneer veneer (4 bytes, Inline) for call to 'arm_function' from thumbmain.o(.text). (8 bytes, Inline) for call to '__0printf' from armsub.o(.text). (8 bytes, Inline) for call to '__rt_lib_init' from kernel.o(.text). (12 bytes, Long) for call to '__rt_lib_shutdown' from kernel.o(.text). (4 bytes, Inline) for call to '__aeabi_memclr4' from stdio.o(.text). (4 bytes, Inline) for call to '_mutex_initialize' from stdio.o(.text). (4 bytes, Inline) for call to '__rt_raise' from stdio.o(.text). (8 bytes, Inline) for call to '__raise' from rt_raise.o(.text). (4 bytes, Inline) for call to '__heap_extend' from malloc.o(.text). (4 bytes, Inline) for call to '__user_perproc_libspace' from malloc.o(.text). (8 bytes, Short) for call to '__rt_exit' from exit.o(.text). (4 bytes, Inline) for call to '_fp_init' from lib_init.o(.text). (4 bytes, Inline) for call to '__ARM_argv_veneer' from lib_init.o(.text). (4 bytes, Inline) for call to '_sys_exit' from abort.o(.text). 14 Veneer(s) (total 80bytes) added to the image. 4.3.2 C と C++ のインターワークに関する基本的な規則 アプリケーション内でのインターワークには、以下の規則が適用されます。 • 他方の(ARM または Thumb)命令セットに戻る可能性のある関数を含む C また は C++ モジュールをコンパイルするには、--apcs /interwork コマンドラインオ プションを使用する必要があります。 • 間接的な関数呼び出しまたは仮想関数呼び出しを含む C または C++ モジュール をコンパイルする場合に、それらの関数が他方の命令セットの関数である可能性 があるときは、--apcs /interwork コマンドラインオプションを使用する必要が あります。 • 他方の状態にあるコードから、インターワーク用にコンパイルされていないコー ドに対して、関数ポインタを使用した呼び出しなどの間接的な呼び出しは行わな いで下さい。 • 入力オブジェクトに Thumb コードが含まれている場合、リンカによって Thumb ランタイムライブラリが選択されます。これらのライブラリはインターワーク用 にビルドされています。 リンカコマンドラインで、独自のライブラリの 1 つを明示的に指定する場合は、 それがインターワークに適したライブラリであるようにする必要があります。 注 C または C++ モジュールの中に、#pragma arm または #pragma thumb でマークされた関 数が含まれている場合は、--apcs /interwork を使用してそのモジュールをコンパイル する必要があります。これにより、関数を他方の(ARM または Thumb)状態から正常 に呼び出すことができます。 4-16 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.3.3 Thumb 状態の関数へのポインタ Thumb 関数(Thumb コードで構成されており、Thumb 状態で実行される関数)へのポ インタには、最下位ビットのセットが含まれている必要があります。これにより、イ ンターワークが正常に行われます。 リンカでは、Thumb 命令を参照するラベルの値を再配置するときに、再配置する値の 最下位ビットを自動的に設定します。Thumb 関数の絶対アドレスを使用した場合、リ ンカではこの操作を実行できません。 このため、コードで Thumb 関数の絶対アドレスを使用する必要がある場合は、そのア ドレスに 1 を加算する必要があります。例えば、例 4-5(4-17 ページ)のような Thumb 関数へのポインタテーブルを作成する場合があります。 詳細については、「アセンブリ言語のインターワーク」(P. 4-7)を参照して下さい。 例 4-5 Thumb 関数への絶対アドレス typedef int (*FN)(); myfunc() { FN fnptrs[] = { (FN)(0x8084 + 1), // Valid Thumb address (FN)(0x8074) // Invalid Thumb address }; FN* myfunctions = fnptrs; myfunctions[0](); myfunctions[1](); // Call OK // Call Fails } ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-17 ARM と Thumb のインターワーク 4.3.4 同一関数の 2 つのコピーの使用 2 つの関数に同じ名前を付け、一方を ARM コード用、他方を Thumb コード用にコンパ イルできます。 ARM と Thumb の同義語 リンカでは、シンボルの複数の定義をイメージ内に共存させることができます。ただ し、各定義は異なるプロセッサ状態に関連付けられている必要があります。ARM と Thumb の同義語を使用してシンボルを参照するとき、リンカでは以下のような規則が 適用されます。 • ARM 状態のシンボルへの B、BL、または BLX 命令は、ARM 定義に従って解決さ れます。 • Thumb 状態のシンボルへの B、BL、または BLX 命令は、Thumb 定義に従って解決 されます。 シンボルへのその他の参照は、リンカが最初に検出した定義に従って解決されます。リ ンカでは、警告を生成し、選択したシンボルを明示します。 4-18 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク 4.4 ベニアを使用したアセンブリ言語のインターワーク 「アセンブリ言語のインターワーク」(P. 4-7)で説明した、アセンブリ言語による ARM/Thumb インターワークの方法では、必要なすべての中間処理を実行しました。こ の場合、リンカがインターワークベニアを挿入する必要はありませんでした。 このセクションでは、以下の目的におけるインターワークベニアの使用方法を説明し ます。 • アセンブリ言語モジュール間のインターワーク。詳細については、 「ベニアを使 用したアセンブリ言語のみのインターワーク」を参照して下さい。 • アセンブリ言語モジュールと C または C++ モジュールとの間のインターワーク。 「ベニアを使用した C、C++、およびアセンブリ言語のインター 詳細については、 ワーク」(P. 4-22)を参照して下さい。 ベニアの詳細については、 「リンカによって生成されるベニア」(P. 4-5)を参照して下 さい。 4.4.1 ベニアを使用したアセンブリ言語のみのインターワーク リンカによって生成されるインターワークベニアを利用する ARM/Thumb インター ワークコードは、アセンブリ言語で記述できます。この場合は以下のルーチンを記述 する必要があります。 • 非インターワークルーチンと同様に、BL 命令を使用して呼び出しを行う発呼側 ルーチン。発呼側ルーチンは、--apcs /interwork または --apcs /nointerwork の いずれかを使用してアセンブルできます。 注 BL 命令の範囲は、ARM 状態では 32MB、Thumb 状態では 4MB です。開発中、範 囲を超えるターゲットの呼び出しや、別の状態の関数の呼び出しをアプリケー ションに含める場合があります。このような場合、リンカによってベニアが自動 的に挿入されます。このベニアが元の BL の中間ターゲットになり、必要な被発 呼側のアドレスがベニアコードによって PC に設定されます。 • BX 命令を使用して復帰する被発呼側ルーチン。被発呼側ルーチンは、 --apcs /interwork を使用してアセンブルする必要があります。また、EXPORT ThumbSub などを使用した、ルーチンの関数ラベルのエクスポートが必要になる場 合もあります(例 4-6(4-20 ページ)参照)。必要に応じて、アセンブラコードを AAPCS に準拠させる必要があります。 一般的にこの作業は ARMv4T のみで必要となります。また、発呼側ルーチンと被発呼 側ルーチンが遠く離れていたり、異なるエリアに配置されている場合にも必要となり ます。ARMv5T 以降では、発呼側ルーチンと被発呼側ルーチンが十分に接近していれ ば、ベニアは必要ありません。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 4-19 ARM と Thumb のインターワーク ベニアを使用したアセンブリ言語のインターワークのサンプル 例 4-6 は、レジスタ r0 ~ r2 に、1、2、3 の値をそれぞれ設定するコードを示していま す。レジスタ r0 と r2 は ARM コードによって設定されます。r1 は、Thumb コードに よって設定されます。以下の点に注意して下さい。 • このコードは --apcs /interwork オプションを使用してアセンブルする必要があ ります。 • サブルーチンからの復帰には、通常の MOV pc,lr ではなく、BX lr 命令が使用さ れます。 例 4-6 ; ***** ; arm.s ; ***** PRESERVE8 AREA Arm,CODE,READONLY IMPORT ThumbProg ENTRY ARMProg MOV r0,#1 BL ThumbProg MOV r2,#3 MOV r0, #0x18 LDR r1, =0x20026 SVC 0x123456 END ; ******* ; thumb.s ; ******* AREA Thumb,CODE,READONLY THUMB EXPORT ThumbProg ThumbProg MOVS r1, #2 BX lr END 4-20 ; Name this block of code. ; Mark 1st instruction to call. ; ; ; ; ; ; ; Set r0 to show in ARM code. Call Thumb subroutine. Set r2 to show returned to ARM. Terminate execution. angel_SWIreason_ReportException ADP_Stopped_ApplicationExit ARM semihosting (formerly SWI) ; Name this block of code. ; Subsequent instructions are Thumb. ; Set r1 to show reached Thumb code. ; Return to ARM subroutine. ; Mark end of this file. Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク このモジュールのビルドとリンクを行い、インターワークベニアを確認するには、以 下の手順を実行します。 1. 「armasm -g arm.s」 と入力し、ARM コードをアセンブルします。 2. 「armasm --thumb -g --apcs /interwork thumb.s」と入力し、Thumb コードをアセ ンブルします。 3. 「armlink arm.o thumb.o -o count」 と入力し、2 つのオブジェクトファイルをリ ンクします。 4. 適切なデバッグターゲットで、互換性のあるデバッガ(RealView Debugger や AXD など)を使用してイメージを実行します。 例 4-7 は、逆アセンブルされたコードで、リンカによって挿入されるインターワークベ ニアを示しています。ベニアは次のワード境界に挿入され、アドレス 0x0000801C から 始まっています。 例 4-7 ARMProg: 00008000 E3A00001 00008004 EB000004 00008008 E3A02003 0000800C E3A00018 00008010 E59F1000 00008014 EF123456 00008018 00020026 0000801C E28FC001 00008020 E12FFF1C ThumbProg 00008024 2102 00008026 4770 ARM DUI 0203GJ MOV r0,#1 BL 0x801c MOV r2,#3 MOV r0,#0x18 LDR r1,0x8018 SVC 0x123456 <Data> '&' 0x00 0x02 0x00 ADR r12,{pc}+9 ; #0x8025 BX r12 MOV BX r1,#2 r14 Copyright © 2002-2006 ARM Limited. All rights reserved. 4-21 ARM と Thumb のインターワーク 4.4.2 ベニアを使用した C、C++、およびアセンブリ言語のインターワーク 一方の状態で実行するようにコンパイルされた C および C++ コードでは、他方の状態 で実行するように設計されたアセンブリ言語コードを呼び出すことが可能であり、ま たこの逆も可能です。このようなインターワークを行うには、非インターワークルー チンとして発呼側ルーチンを記述して呼び出しを行います。アセンブリ言語からの呼 び出しを行う場合には、BL 命令を使用してこの呼び出しを行います(例 4-8(4-22 ペー ジ)参照)。次に、以下の手順を実行します。 • 被発呼側ルーチンが C で記述されている場合は、--apcs /interwork を使用して コンパイルします。 • 被発呼側ルーチンがアセンブリ言語で記述されている場合は、--apcs /interwork オプションを使用してアセンブルし、BX lr を使用して復帰します。 注 この方法で使用されるアセンブリ言語コードまたはユーザライブラリコードは、必要 に応じて AAPCS に準拠させる必要があります。 例 4-8 /********************** * thumb.c * **********************/ #include <stdio.h> extern int arm_function(int); int main(void) { int i = 1; printf("i = %d\n", i); printf("And now i = %d\n", arm_function(i)); return (0); } ; ***** ; arm.s ; ***** PRESERVE8 AREA Arm,CODE,READONLY ; Name this block of code. EXPORT arm_function arm_function ADD r0,r0,#4 ; Add 4 to first parameter. BX lr ; Return END 4-22 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ ARM と Thumb のインターワーク このモジュールのビルドとリンクを行うには、以下の手順を実行します。 1. 「armcc --thumb -g -c --apcs /interwork thumb.c」と入力し、Thumb コードをコ ンパイルします。 2. ARM コードをアセンブルします。 「armasm -g --apcs /interwork arm.s」と入力し、 3. 「armlink arm.o thumb.o -o add --info veneers」 と入力し、2 つのオブジェクト ファイルをリンクしてインターワークベニアのサイズを表示します。 4. ARM DUI 0203GJ 適切なデバッグターゲットで、互換性のあるデバッガ(RealView Debugger や AXD など)を使用してイメージを実行します。 Copyright © 2002-2006 ARM Limited. All rights reserved. 4-23 ARM と Thumb のインターワーク 4-24 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第5章 C、C++、およびアセンブリ言語の混用 本章では、C、C++、および ARM® アセンブリ言語の混合コードの記述方法について説 明します。また、C および C++ 言語から ARM のインラインアセンブラや組み込みア センブラを使用する方法についても説明します。本章は以下のセクションから構成さ れています。 • インラインアセンブラと組み込みアセンブラの使用(P. 5-2) • • • ARM DUI 0203GJ アセンブリコードから C グローバル変数へのアクセス(P. 5-4) C++ からの C ヘッダファイルの使用(P. 5-5) C、C++、およびアセンブリ言語間の呼び出し(P. 5-7) Copyright © 2002-2006 ARM Limited. All rights reserved. 5-1 C、C++、およびアセンブリ言語の混用 5.1 インラインアセンブラと組み込みアセンブラの使用 ARM コンパイラに組み込まれているインラインアセンブラおよび組み込みアセンブ ラを使用すると、C または C++ 言語から直接アクセスできないターゲットプロセッサ の機能を使用できます。以下に例を示します。 • サチュレート算術演算(RealView Compilation Tools v3.0 Assembler Guide 参照) • • カスタムコプロセッサ プログラム状態レジスタ(PSR) このセクションは以下のサブセクションから構成されています。 • インラインアセンブラの機能 • 組み込みアセンブラの機能 • インラインアセンブリコードと組み込みアセンブリコードの相違点(P. 5-3) 詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide のインラ インアセンブラおよび組み込みアセンブラに関する章を参照して下さい。 5.1.1 インラインアセンブラの機能 インラインアセンブラでは、C および C++ 言語との非常に柔軟なインターワークがサ ポートされています。どのレジスタオペランドにも、任意の C または C++ の式を使用 できます。また、インラインアセンブラでは、複雑な命令を展開し、アセンブリ言語 コードを最適化します。 注 最適化コンパイラオプションの -O1、-O2、または -O3 のいずれかを使用すると、イン ラインアセンブラ言語はコンパイラによる最適化の対象となります。 ARM コード用のインラインアセンブラでは、汎用コプロセッサ命令、ハーフワード命 令、および long 乗算など、ARM 命令セットのほとんどが実装されます。 5.1.2 組み込みアセンブラの機能 組み込みアセンブラでは、ターゲットプロセッサへの低レベルなアクセスを無制限に 実行できます。また、このアセンブラにより、C および C++ のプリプロセッサディレ クティブを使用できるようになるため、構造体メンバのオフセットに簡単にアクセス できます。 組み込みアセンブラを使用すると、アセンブラディレクティブを含む、すべての ARM アセンブラ命令セットを使用できます。組み込みアセンブリコードは、C および C++ で記述されたコードとは別にアセンブルされます。コンパイルされたオブジェクトは、 生成された後、C および C++ のソースのコンパイルによって生成されたオブジェクト と結合されます。 5-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 組み込みアセンブラは、ARM コードと Thumb® コードの両方でサポートされています。 ARM および Thumb の命令セットの詳細については、RealView Compilation Tools v3.0 Assembler Guide を参照して下さい。 5.1.3 インラインアセンブリコードと組み込みアセンブリコードの相違点 表 5-1 は、インラインアセンブラと組み込みアセンブラの主な相違点について説明して います。 表 5-1 インラインアセンブラと組み込みアセンブラの相違点 機能 組み込みアセンブラ インラインアセンブラ 命令セット ARM および Thumb ARM のみ ARM アセンブラディレクティ ブ すべてサポートされています。 サポートされていません。 C または C++ の式 定数式のみ C または C++ のすべての式 アセンブリコードの最適化 最適化なし 完全な最適化 インライン展開 不可 可 レジスタへのアクセス 指定した物理レジスタが使用され ます。PC、LR、および SP も使用 できます。 仮想レジスタが使用されます。 sp(r13) 、lr(r14)、および pc(r15) を使用すると、エラーが発生します。 復帰命令 コードに追加する必要があります。 自動的に生成されます(BX、BXJ、お よび BLX 命令はサポートされていま せん)。 BKPT 命令 直接サポートされています。 サポートされていません。 注 組み込みアセンブラと C または C++ の相違点のリストは、RealView Compilation Tools v3.0 Compiler and Libraries Guide のインラインアセンブラと組み込みアセンブラに関す る章に記載されています。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 5-3 C、C++、およびアセンブリ言語の混用 5.2 アセンブリコードから C グローバル変数へのアクセス グローバル変数には、その変数のアドレスを使用して間接的にしかアクセスすること できません。グローバル変数にアクセスするには、IMPORT ディレクティブを使用して グローバル変数をインポートし、アドレスをレジスタにロードします。そのグローバ ル変数には、その型によって、ロード命令およびストア命令を使用してアクセスでき ます。 符号なし変数には、以下の命令を使用します。 • char 型の場合は LDRB または STRB • short 型の場合は LDRH または STRH • int 型の場合は LDR または STR 符号付き変数では、LDRSB や LDRSH など、上記の命令と等価な符号付き命令を使用します。 8 ワード未満の小さな構造体全体には、LDM 命令および STM 命令を使用してアクセスで きます。構造体の各メンバには、適切なタイプのロード命令またはストア命令を使用 してアクセスできます。各メンバにアクセスするには、構造体の開始位置からメンバ のオフセットを把握しておく必要があります。 例 5-1 では、整数のグローバル変数 globvar のアドレスを r1 にロードし、そのアドレ スに格納された値を r0 にロードして 2 を加算してから、その新しい値を globvar に返 します。 例 5-1 グローバル変数へのアクセス PRESERVE8 AREA globals,CODE,READONLY EXPORT IMPORT asmsubroutine globvar asmsubroutine LDR r1, =globvar LDR ADD STR BX END ; read address of globvar into ; r1 from literal pool r0, [r1] r0, r0, #2 r0, [r1] lr ARM または Thumb のコードで使用できる命令の詳細については、RealView Compilation Tools v3.0 Assembler Guide を参照して下さい。 5-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 5.3 C++ からの C ヘッダファイルの使用 C ヘッダファイルを C++ から呼び出すには、extern "C" ディレクティブでラップする 必要があります。 このセクションでは、以下の内容について説明します。 • システム C ヘッダファイルのインクルード • 5.3.1 独自の C ヘッダファイルのインクルード(P. 5-6) システム C ヘッダファイルのインクルード stdio.h などの標準システム C ヘッダファイルをインクルードする場合は、特に必要な 手順はありません。標準 C ヘッダファイルには、既に適切な extern "C" ディレクティ ブが含まれています。以下に例を示します。 #include <stdio.h> int main() { ... // C++ code return 0; } この構文を使用してヘッダをインクルードすると、すべてのライブラリ名がグローバ ルネームスペースに配置されます。 C++ 標準では、C ヘッダファイルの機能が C++ 固有のヘッダファイルから使用できる ように定義されています。この C++ 固有のヘッダファイルは、標準 C ヘッダファイル と共に、install_directory\RVCT\Data\3.0\build_num\include\platform にインストー ルされ、通常の方法で参照できます。以下に例を示します。 #include <cstdio> ARM C++ では、C ヘッダファイルのインクルードが行われます。この構文を使用して ヘッダをインクルードすると、C ライブラリの名前を含む、C++ 標準ライブラリ名の すべてがネームスペース std 内で定義されます。つまり、すべてのライブラリ名は、以 下のいずれかの方法を使用して修飾する必要があります。 • 以下のような標準ネームスペースを指定します。 std::printf("example\n"); • 以下のような C++ キーワードの using を使用して名前をグローバルネームス ペースにインポートします。 using namespace std; printf("example\n"); • ARM DUI 0203GJ コンパイラオプション --using_std を使用します。 Copyright © 2002-2006 ARM Limited. All rights reserved. 5-5 C、C++、およびアセンブリ言語の混用 5.3.2 独自の C ヘッダファイルのインクルード 独自の C ヘッダファイルをインクルードするには、#include ディレクティブを extern "C" ステートメントでラップする必要があります。以下の方法で行うことができます。 • • ファイルのインクルード時にラップします(例 5-2 参照)。 extern "C" ステートメントをヘッダファイルに追加してラップします(例 5-3 参照)。 例 5-2 インクルードファイルの前に配置されたディレクティブ // C++ code extern "C" { #include "my-header1.h" #include "my-header2.h" } int main() { // ... return 0; } 例 5-3 ヘッダファイル内に配置されたディレクティブ /* C header file */ #ifdef __cplusplus extern "C" { #endif /* Insert start of extern C construct */ /* Body of header file */ #ifdef __cplusplus } #endif 5-6 /* Insert end of extern C construct */ /* The C header file can now be */ /* included in either C or C++ code.*/ Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 5.4 C、C++、およびアセンブリ言語間の呼び出し このセクションでは、C++ から C およびアセンブリ言語のコードを呼び出したり、C およびアセンブリ言語から C++ コードを呼び出したりする場合に役立つサンプルを紹 介します。また、呼び出し規則とデータ型についても説明します。このセクションは 以下のサブセクションから構成されています。 • 複数言語間の呼び出しに関する一般規則 • C++ に関する情報(P. 5-8) • 言語間の呼び出しのサンプル(P. 5-9) AAPCS に準拠している場合は、C、C++、およびアセンブリ言語で記述したルーチン 間の呼び出しを混在させることができます。詳細については、 install_directory\Documentation\Specifications\... にあるProcedure Call Standard for the ARM Architecture(aapcs.pdf)を参照して下さい。 注 このセクションの情報は実装に依存するものであり、今後のリリースで変更される可 能性があります。 5.4.1 複数言語間の呼び出しに関する一般規則 C、C++、およびアセンブリ言語間の呼び出しには、以下の一般規則が適用されます。 詳細については、RealView Compilation Tools v3.0 Compiler and Libraries Guide を参照し て下さい。 組み込みアセンブラを使用し、Application Binary Interface (ABI) for the ARM Architecture (base standard)[BSABI]に準拠することで、複数言語を混用したプログラミングを簡 単に実装できるようになります。これにより、以下の内容がサポートされます。 • __cpp キーワードを使用した名前の符号化 ARM DUI 0203GJ • 暗黙の this パラメータを渡す方法 • • • 仮想関数の呼び出し方法 参照の表現 ベースクラスまたは仮想メンバ関数を持つ C++ クラス型のレイアウト • Plain Old Data (POD)構造体以外のクラスオブジェクトを渡す方法 Copyright © 2002-2006 ARM Limited. All rights reserved. 5-7 C、C++、およびアセンブリ言語の混用 混合言語プログラミングには、以下の一般規則が適用されます。 • C の呼び出し規則を使用します。 • C++ では、非メンバ関数を extern "C" として宣言し、C リンケージを使用する ように指定できます。RealView® Compilation Tools (RVCT)の本リリースでは、C リンケージを使用すると、関数を定義するシンボルが符号化されません。C リン ケージを使用すると、ある言語で関数を実装し、その関数を別の言語から呼び出 すことができます。 注 extern "C" として宣言される関数はオーバーロードできません。 • アセンブリ言語モジュールは、アプリケーションで使用されているメモリモデル に適した AAPCS 標準に準拠する必要があります。 C およびアセンブリ言語からの C++ 関数の呼び出しには、以下の規則が適用されます。 5.4.2 • C++ のグローバル(非メンバ)関数を呼び出すには、この関数を extern "C" と して宣言して、C リンケージを指定します。 • メンバ関数(スタティック関数と非スタティック関数の両方)の名前は必ず符号 化する必要があります。組み込みアセンブラの __cpp キーワードを使用すると、 符号化された名前を手動で検索する必要がなくなります。 • C++ インライン関数は、C++ コンパイラでその関数のアウトオブラインコピーが 生成されるようにした場合を除き、C から呼び出すことができません。例えば、 この関数のアドレスを取得すると、アウトオブラインコピーが生成されます。 • 非スタティックメンバ関数は、暗黙の this パラメータを r0 の最初の引数として 受け取ります。また、この関数が int に類似していない構造体を返す場合は、r1 の 2 番目の引数として受け取ります。スタティックメンバ関数は、暗黙の this パラメータを受け取りません。 C++ に関する情報 以下の情報は、C++ のみに当てはまります。 C++ の呼び出し規則 ARM C++ では、1 つの例外を除き、ARM C と同じ呼び出し規則が使用されます。 • 5-8 非スタティックメンバ関数は、暗黙の this パラメータを最初の引数として、ま たは呼び出される関数が int に類似していない構造体を返す場合は 2 番目の引数 として使用することで呼び出されます。この規則は今後の実装で変更される可能 性があります。 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 C++ データ型 ARM C++ では、以下の例外と追加点を除き、ARM C と同じデータ型が使用されます。 • struct 型または class 型の C++ オブジェクトには、ベースクラスまたは仮想関 数が含まれていない場合に ARM C で予測されるレイアウトと同じレイアウトが 使用されます。このような struct 型のオブジェクトにユーザ定義のコピー代入 演算子もユーザ定義のデストラクタも含まれていない場合、このオブジェクトは Plain Old Data 構造体として処理されます。 • 参照はポインタとして表現されます。 • C 関数へのポインタと C++ (非メンバ)関数へのポインタは区別されません。 シンボル名の符号化 リンカは、メッセージ内のシンボル名を符号化しません。 C で記述する名前は、C++ プログラムでは extern "C" として宣言する必要があります。 この宣言は、既に ARM ISO C ヘッダで行われています。詳細については、 「C++ から の C ヘッダファイルの使用」(P. 5-5)を参照して下さい。 5.4.3 言語間の呼び出しのサンプル 以下のセクションでは、複数言語の呼び出しを混用する方法を示すサンプルコードを 紹介します。 • C 言語からのアセンブリ言語の呼び出し(P. 5-10) • • • • • • • ARM DUI 0203GJ アセンブリ言語からの C 言語の呼び出し(P. 5-11) C++ 言語からの C 言語の呼び出し(P. 5-12) C++ 言語からのアセンブリ言語の呼び出し(P. 5-13) C 言語からの C++ 言語の呼び出し(P. 5-14) アセンブリ言語からの C++ 言語の呼び出し(P. 5-15) C 言語またはアセンブリ言語からの C++ 言語の呼び出し(P. 5-17) C 言語と C++ 言語間での参照の受け渡し(P. 5-16) Copyright © 2002-2006 ARM Limited. All rights reserved. 5-9 C、C++、およびアセンブリ言語の混用 C 言語からのアセンブリ言語の呼び出し 例 5-4 と例 5-5 では、アセンブリ言語サブルーチンへの呼び出しを使用して 1 つのスト リングを別のストリングの上にコピーする C プログラムを示しています。 例 5-4 C 言語からのアセンブリ言語の呼び出し #include <stdio.h> extern void strcopy(char *d, const char *s); int main() { const char *srcstr = "First string - source "; char dststr[] = "Second string - destination "; /* dststr is an array since we’re going to change it */ printf("Before copying:\n"); printf(" %s\n %s\n",srcstr,dststr); strcopy(dststr,srcstr); printf("After copying:\n"); printf(" %s\n %s\n",srcstr,dststr); return (0); } 例 5-5 アセンブリ言語を使用したストリングコピーサブルーチン PRESERVE8 AREA SCopy, CODE, READONLY EXPORT strcopy strcopy ; r0 points to destination string. ; r1 points to source string. LDRB r2, [r1],#1 ; Load byte and update address. STRB r2, [r0],#1 ; Store byte and update address. CMP r2, #0 ; Check for zero terminator. BNE strcopy ; Keep going if not. BX lr ; Return. END 例 5-4 は、strtest.c および scopy.s として、主なサンプルディレクトリの ...\asm に あります。 5-10 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 このサンプルをコマンドラインからビルドするには、以下の手順に従います。 1. 「armasm --debug scopy.s」 と入力し、アセンブリ言語ソースをビルドします。 2. 「armcc -c --debug strtest.c」 と入力し、C ソースをビルドします。 3. 「armlink strtest.o scopy.o -o strtest」 と入力し、オブジェクトファイルをリ ンクします。 4. 適切なデバッグターゲットで、互換性のあるデバッガ(例えば、AXD や RealView Debugger)を使用してイメージを実行します。 アセンブリ言語からの C 言語の呼び出し 例 5-6 と例 5-7 では、アセンブリ言語から C 言語を呼び出す方法を示しています。 例 5-6 C における関数の定義 int g(int a, int b, int c, int d, int e) { return a + b + c + d + e; } 例 5-7 アセンブリ言語の呼び出し ; int f(int i) { return g(i, 2*i, 3*i, 4*i, 5*i); } PRESERVE8 EXPORT f AREA f, CODE, READONLY IMPORT g ; i is in r0 STR lr, [sp, #-4]! ; preserve lr ADD r1, r0, r0 ; compute 2*i (2nd param) ADD r2, r1, r0 ; compute 3*i (3rd param) ADD r3, r1, r2 ; compute 5*i STR r3, [sp, #-4]! ; 5th param on stack ADD r3, r1, r1 ; compute 4*i (4th param) BL g ; branch to C function ADD sp, sp, #4 ; remove 5th param LDR pc, [sp], #4 ; return END ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 5-11 C、C++、およびアセンブリ言語の混用 C++ 言語からの C 言語の呼び出し 例 5-8 および例 5-9 では、C++ 言語から C 言語を呼び出す方法を示しています。 例 5-8 C++ 言語からの C 関数の呼び出し struct S { // has no base classes // or virtual functions S(int s) :i(s) { } int i; }; extern "C" void cfunc(S *); // declare the C function to be called from C++ int f(){ S s(2); // initialize 's' cfunc(&s); // call 'cfunc' so it can change 's' return s.i * 3; } 例 5-9 C 言語における関数の定義 struct S { int i; }; void cfunc(struct S *p) { /* the definition of the C function to be called from C++ */ p->i += 5; } 5-12 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 C++ 言語からのアセンブリ言語の呼び出し 例 5-10 と例 5-11 では、C++ 言語からアセンブリ言語を呼び出す方法を示しています。 例 5-10 C++ 言語からのアセンブリ言語の呼び出し struct S { // has no base classes // or virtual functions S(int s) :i(s) { } int i; }; extern "C" void asmfunc(S *); int f() { S s(2); asmfunc(&s); // declare the Asm function // to be called // initialize 's' // call 'asmfunc' so it // can change 's' return s.i * 3; } 例 5-11 アセンブリ言語で記述された関数の定義 PRESERVE8 AREA Asm, CODE EXPORT asmfunc asmfunc LDR r1, [r0] ADD r1, r1, #5 STR r1, [r0] BX lr END ARM DUI 0203GJ ; the definition of the Asm ; function to be called from C++ Copyright © 2002-2006 ARM Limited. All rights reserved. 5-13 C、C++、およびアセンブリ言語の混用 C 言語からの C++ 言語の呼び出し 例 5-12 と例 5-13 では、C 言語から C++ 言語を呼び出す方法を示しています。 例 5-12 C++ で呼び出される関数の定義 struct S { // has no base classes or virtual functions S(int s) :i(s) { } int i; }; extern "C" void cppfunc(S *p) { // Definition of the C++ function to be called from C. // The function is written in C++, only the linkage is C p->i += 5; // } 例 5-13 C 言語での関数の宣言と呼び出し struct S { int i; }; extern void cppfunc(struct S *p); /* Declaration of the C++ function to be called from C */ int f(void) { struct S s; s.i = 2; cppfunc(&s); /* initialize 's' */ /* call 'cppfunc' so it */ /* can change 's' */ return s.i * 3; } 5-14 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 アセンブリ言語からの C++ 言語の呼び出し 例 5-14 と例 5-15 では、アセンブリ言語から C++ 言語を呼び出す方法を示しています。 例 5-14 C++ 言語で呼び出される関数の定義 struct S { // has no base classes or virtual functions S(int s) :i(s) { } int i; }; extern "C" void cppfunc(S * p) { // Definition of the C++ function to be called from ASM. // The body is C++, only the linkage is C p->i += 5; } ARM アセンブラ言語では、C++ 関数の名前をインポートし、リンク付き分岐(BL)命 令を使用してその関数を呼び出します。 例 5-15 アセンブリ言語で記述された関数の定義 AREA Asm, CODE IMPORT cppfunc EXPORT ; import the name of the C++ ; function to be called from Asm f f ARM DUI 0203GJ STMFD MOV STR MOV BL sp!,{lr} r0,#2 r0,[sp,#-4]! r0,sp cppfunc LDR ADD LDMFD END r0, [sp], #4 r0, r0, r0,LSL #1 sp!,{pc} ; ; ; ; initialize struct argument is pointer to struct call 'cppfunc' so it can change the struct Copyright © 2002-2006 ARM Limited. All rights reserved. 5-15 C、C++、およびアセンブリ言語の混用 C 言語と C++ 言語間での参照の受け渡し 例 5-16 と例 5-17 では、C 言語と C++ 言語の間で参照を受け渡す方法を示しています。 例 5-16 C++ 関数の定義 extern "C" int cfunc(const int&); // Declaration of the C function to be called from C++ extern "C" int cppfunc(const int& r) { // Definition of the C++ to be called from C. return 7 * r; } int f() { int i = 3; return cfunc(i); } // passes a pointer to 'i' 例 5-17 C 関数の定義 extern int cppfunc(const int*); /* declaration of the C++ to be called from C */ int cfunc(const int *p) { /* definition of the C function to be called from C++ */ int k = *p + 4; return cppfunc(&k); } 5-16 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ C、C++、およびアセンブリ言語の混用 C 言語またはアセンブリ言語からの C++ 言語の呼び出し 例 5-18、例 5-19、および例 5-20(5-18 ページ)のコードは、C またはアセンブリ言語 から C++ 言語の非スタティックな非仮想メンバ関数を呼び出す方法を示しています。 関数の符号化された名前を参照するには、コンパイラからアセンブラ出力を使用して 下さい。 例 5-18 C++ メンバ関数の呼び出し struct T { T(int i) :t(i) { } int t; int f(int i); }; int T::f(int i) { return i + t; } // Definition of the C++ function to be called from C. extern "C" int cfunc(T*); // declaration of the C function to be called from C++ int f() { T t(5); return cfunc(&t); } // create an object of type T 例 5-19 C 関数の定義 struct T; extern int _ZN1T1fEi(struct T*, int); /* the mangled name of the C++ */ /* function to be called */ int cfunc(struct T* t) { /* Definition of the C function to be called from C++.*/ return 3 * _ZN1T1fEi(t, 2); /* like '3 * t->f(2)' */ } ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 5-17 C、C++、およびアセンブリ言語の混用 例 5-20 アセンブリ言語での関数の実装 EXPORT cfunc AREA foo, CODE IMPORT _ZN1T1fEi cfunc STMFD sp!,{lr} MOV r1, #2 BL _ZN1T1fEi ADD r0, r0, r0, LSL #1 LDMFD sp!,{pc} END ; r0 already contains the object pointer ; multiply by 3 別の方法として、例 5-21 で示すように、例 5-18(5-17 ページ)と例 5-20 は、組み込み アセンブリを使用して実装できます。このサンプルでは、関数の参照に __cpp キーワー ドが使用されます。そのため、関数の符号化された名前を把握しておく必要はありま せん。 例 5-21 組み込みアセンブリでの関数の実装 struct T { T(int i) :t(i) { } int t; int f(int i); }; int T::f(int i) { return i + t; } // Definition of asm function called from C++ __asm int asm_func(T*) { STMFD sp!, {lr} MOV r1, #2; BL __cpp(T::f); ADD r0, r0, r0, LSL #1 ; multiply by 3 LDMFD sp!, {pc} } int f() { T t(5); // create an object of type T return asm_func(&t); } 5-18 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第6章 プロセッサ例外処理 本章では、ARM® プロセッサがサポートしている例外の処理方法について説明します。 本章は以下のセクションから構成されています。 • プロセッサ例外について(P. 6-2) • • • • • • • • • • • • ARM DUI 0203GJ プロセッサ状態の判断(P. 6-5) 例外の開始と終了(P. 6-7) 例外処理(P. 6-12) 例外ハンドラのインストール(P. 6-13) SVC ハンドラ(P. 6-18) 割り込みハンドラ(P. 6-28) リセットハンドラ(P. 6-38) 未定義命令ハンドラ(P. 6-39) プリフェッチアボートハンドラ(P. 6-40) データアボートハンドラ(P. 6-41) 例外ハンドラのチェイン(P. 6-42) システムモード(P. 6-44) Copyright © 2002-2006 ARM Limited. All rights reserved. 6-1 プロセッサ例外処理 6.1 プロセッサ例外について プログラムの通常の実行フローにおいて、近くのラベルへの分岐や、サブルーチンへ の分岐とリンクなどが実行されることにより、プログラムカウンタ(PC)はアドレス 空間の中でシーケンシャルに増加していきます。 プロセッサ例外は、内部ソースまたは外部ソースによって生成されるイベントをプロ セッサが処理できるように、この通常の実行フローが迂回されるときに発生します。こ のイベントには以下のようなものがあります。 • 外部で生成された割り込み • 未定義命令を実行しようとするプロセッサの試行 • オペレーティングシステムの特権付き機能へのアクセス これらの例外が発生したときに実行していたプログラムを、適切な例外ルーチンの終 了時に再開できるようにするため、例外を処理するときは、それまでのプロセッサ状 態を保存しておく必要があります。 このセクションは以下のサブセクションから構成されています。 • 例外のタイプ • ベクタテーブル(P. 6-3) • 例外によるモードとレジスタの使用(P. 6-3) • 例外の優先度(P. 6-4) 6.1.1 例外のタイプ 表 6-1 は、ARM プロセッサによって認識される例外のタイプを示しています。 表 6-1 例外のタイプ 例外 説明 リセット プロセッサリセットピンがアサートされると発生します。この例外は、起動が通知 された場合と、プロセッサが起動された状態にするためにリセットが行われた場合 だけに発生します。ソフトリセットは、リセットベクタ 0x0000 に分岐させることに よって実行できます。 未定義命令 実行中の命令が、プロセッサや接続されているどのコプロセッサにも認識されない 場合に発生します。 スーパーバイザコール (SVC) ユーザ定義の同期割り込み命令です。スーパーバイザコールにより、例えばユーザ モードで実行中のプログラムが、スーパーバイザモードで実行される RTOS 関数な どの特権操作を要求できます。 プリフェッチアボート アドレスが不正であったためにフェッチされなかった命令をプロセッサが実行しよ (P. 6-3)参照)。 うとしたときに発生します(「不正アドレス」 6-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 表 6-1 例外のタイプ (続き) 例外 説明 データアボート データ転送命令によって不正なアドレスでのロードまたは保存が試行されたときに 発生します(「不正アドレス」参照)。 IRQ プロセッサ外部割り込み要求ピンが LOW でアサートされ、CPSR 内の I ビットがクリ アされているときに発生します。 FIQ プロセッサ外部高速割り込み要求ピンが LOW でアサートされ、CPSR 内の F ビット がクリアされているときに発生します。 不正アドレス 不正な仮想アドレスとは、物理メモリのアドレスと現時点で対応していないアドレス か、現在のモードではプロセッサからアクセスできないとメモリ管理サブシステムが 判断したアドレスを指します。 6.1.2 ベクタテーブル プロセッサ例外処理はベクタテーブル によって制御されます。ベクタテーブルは 32 バ イトの予約エリアであり、通常はメモリマップの最下位に配置されます。ベクタテー ブルでは各例外タイプに 1 ワードの空間が割り当てられ、 1 ワードが予約されています。 この空間はハンドラのコード全体を保持するには大きさが不十分であるため、通常は 各例外タイプのベクタエントリに、適切なハンドラを使用して実行を継続するための 分岐命令またはロード PC 命令が保持されています。 6.1.3 例外によるモードとレジスタの使用 通常、アプリケーションはユーザモードで実行されますが、例外処理は特権付きモー ドで実行される必要があります。例外が発生するとプロセッサモードが変更され、こ れにより各例外ハンドラは、以下のような特定のバンクレジスタのサブセットにアク セスできるようになります。 • 専用の r13、つまりスタックポインタ(sp_mode) • 専用の r14、つまりリンクレジスタ(lr_mode) • 専用の保存プログラム状態レジスタ(spsr_ mode) FIQ の場合、各例外ハンドラはさらに 5 本の汎用レジスタ(r8_FIQ ~ r12_FIQ)にアク セスできます。 各例外ハンドラは、終了時に他のレジスタを元の内容に復元させる必要があります。こ の操作は、ハンドラが必要とするすべてのレジスタ内容をスタックに保存し、復帰前 にこれらを復元することで可能となります。Angel または ARMulator® ISS を使用する 場合は、必要となるスタックが自動的に設定されます。これらを使用しない場合は、ス タックを設定する必要があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-3 プロセッサ例外処理 注 前述のとおり、register_mode 形式のシンボリックなレジスタ名はアセンブラによって 事前に宣言されません。この形式を使用するには、RN アセンブラディレクティブを使 用して、適切なシンボリック名を宣言する必要があります。例えば、lr_FIQ RN r14 を 使用すると、r14 のシンボリックレジスタ名 lr_FIQ を宣言できます。RN ディレクティ ブの詳細については、RealView Compilation Tools v3.0 Assembler Guide のディレクティブ に関する章を参照して下さい。 6.1.4 例外の優先度 複数の例外が同時に発生した場合、これらの例外は決まった優先度順に処理されま す。各例外が順に処理されてから、ユーザプログラムの実行が継続されます。すべて の例外が同時に発生することはあり得ません。例えば、未定義命令例外と SVC 例外 は、どちらもある命令の実行によってトリガがかかるため、同時に発生することはあ りません。 表 6-2 は、例外とそれに対応するプロセッサモードおよび処理の優先度を示してい ます。 表 6-2 例外の優先度 ベクタアドレス 例外のタイプ 0x0 リセット 0x4 未定義命令 0x8 例外モード スーパーバイザ (SVC) 未定義 ス ー パ ー バ イ ザ コ ー ル スーパーバイザ (SVC) (SVC) 優先度(1= 高、6= 低) 1 6 6 0xC プリフェッチアボート アボート 5 0x10 データアボート アボート 2 0x14 予約 なし なし 0x18 割り込み(IRQ) 割り込み(IRQ) 4 0x1C 高速割り込み(FIQ) 高速割り込み (FIQ) 3 データアボート例外は FIQ 例外よりも優先度が高いので、FIQ の処理前に登録されま す。データアボートハンドラが起動しますが、制御はすぐに FIQ ハンドラに渡されま す。FIQ の処理が完了すると、データアボートハンドラに制御が戻ります。これによ り、FIQ が最初に処理されてデータ転送エラーが検出を免れることはありません。 6-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.2 プロセッサ状態の判断 例外ハンドラによって、例外発生時にプロセッサが ARM 状態と Thumb® 状態のどちら にあったかを判断しなければならない場合があります。 特に、SVC ハンドラでプロセッサ状態を読み出す必要がある場合があります。プロセッ サ状態は、SPSR の T ビットを検査することで参照できます。このビットは、Thumb 状 態ではセットされ、ARM 状態ではクリアされます。 ARM と Thumb のどちらの命令セットにも SVC 命令があります。Thumb 状態から SVC を呼び出す場合には、以下の点に注意する必要があります。 • • • 命令のアドレスは、(lr - 4)ではなく(lr - 2)にあります。 命令自体は 16 ビットであるため、ハーフワードをロードする必要があります (図 6-1 参照)。 SVC 番号は、ARM 状態のときの 24 ビットでなく 8 ビットで保持されます。 15 14 13 12 11 10 9 8 7 1 1 0 1 1 1 1 1 0 8_bit_immediate comment field 図 6-1 Thumb SVC 命令 例 6-1(6-6 ページ)は、両方のソースからの SVC を処理する ARM コードを示してい ます。 以下に注意して下さい。 ARM DUI 0203GJ • 各 do_svc_x ルーチンにおいて、 コード密度を向上させるため、 必要に応じて Thumb 状態への切り替えと復帰が実行される可能性があります。 • switch() ステートメントを含む C 関数の呼び出しでジャンプテーブルを置き換 えることによって SVC を実装できます。 • SVC 番号は、呼び出されたときの状態に応じて異なる方法で処理することが可能 です。 • Thumb 状態からアクセス可能な SVC 番号の範囲は、SVC を動的に呼び出すこと によって拡張できます(「SVC ハンドラ」(P. 6-18)参照)。 Copyright © 2002-2006 ARM Limited. All rights reserved. 6-5 プロセッサ例外処理 例 6-1 SVC ハンドラ T_bit EQU 0x20 : : SVCHandler STMFD MRS TST LDRNEH BICNE LDREQ BICEQ ; Thumb bit of CPSR/SPSR, that is, ; bit 5. sp!, {r0-r3,r12,lr} r0, spsr ; ; ; ; ; ; ; ; r0, #T_bit r0,[lr,#-2] r0,r0,#0xFF00 r0,[lr,#-4] r0,r0,#0xFF000000 Store registers. Move SPSR into general purpose register. Occurred in Thumb state? Yes: load halfword and... ...extract comment field. No: load word and... ...extract comment field. ; r0 now contains SVC number CMP LDRLS B r0, #MaxSVC pc, [pc, r0, LSL#2] SVCOutOfRange ; Rangecheck ; Jump to the appropriate routine. svctable DCD DCD : : do_svc_1 do_svc_2 do_svc_1 ; Handle the SVC. LDMFD sp!, {r0-r3,r12,pc}^ ; Restore the registers and return. do_svc_2 : 6-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.3 例外の開始と終了 このセクションでは、例外に対するプロセッサの応答と、例外処理後に例外発生時の 場所に戻る方法について説明します。この復帰方法は、例外タイプによって異なりま (P. 6-2)参照)。 す(「例外のタイプ」 Thumb 対応プロセッサの基本的な例外処理メカニズムは、Thumb に対応していないプ ロセッサと同じです。例外が発生すると、適切なベクタテーブルエントリから次の命 令がフェッチされます。 Thumb 状態と ARM 状態のどちらの例外にも、同じベクタテーブルが使用されます。 「例 外に対するプロセッサの応答」で説明されている例外処理手順に、ARM 状態への切り 替えを行う最初のステップが加わります。 ここからは、Thumb 対応プロセッサに適した例外ハンドラを記述する際にこれ以外に 注意しておくべき点について記載されています。 このセクションは以下のサブセクションから構成されています。 • 例外に対するプロセッサの応答 • 例外ハンドラからの復帰(P. 6-8) • 6.3.1 復帰アドレスと復帰命令(P. 6-9) 例外に対するプロセッサの応答 例外が生成されると、プロセッサは以下のように動作します。 1. カレントプログラムステータスレジスタ(CPSR)を、例外が処理されるモード の保存プログラムステータスレジスタ(SPSR)にコピーします。これにより、現 在のモード、割り込みマスク、および条件フラグが保存されます。 2. 現在 Thumb 状態の場合、ARM 状態への切り替えが行われます。 3. 以下を行うために、適切な CPSR モードビットを変更します。 4. • 適切なモードに切り替え、そのモードの適切なバンクレジスタにマップ する。 • 割り込みをディセーブルする。どの例外が発生しても IRQ はディセーブル されます。FIQ は FIQ が発生したときと、リセット時にディセーブルされ ます。 「復帰アドレスと復帰命令」(P. 6-9)で定義されているように、lr_mode に復帰ア ドレスをセットします。 5. プログラムカウンタに、例外のベクタアドレスをセットします。 Thumb に対応していない ARM プロセッサの場合、この動作によって適切な例外 ハンドラへの分岐が発生します。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-7 プロセッサ例外処理 Thumb 対応プロセッサでは、ステップ 2 で Thumb 状態から ARM 状態に切り替わ ることで、このベクタアドレスに格納される ARM 命令(分岐または PC 相対ロー ド)のフェッチ、デコード、および実行が正しく行われます。これによって ARM コードで記述する必要のあるトップレベルベニアへの分岐が発生します。 6.3.2 例外ハンドラからの復帰 例外からの復帰方法は、例外ハンドラがスタック処理を行うかどうかによって異なり ます。いずれの場合も、実行を例外発生時の場所に戻すには、例外ハンドラが以下の 処理を行う必要があります。 • spsr_mode から CPSR を復元する。 • lr_mode に保存されている復帰アドレスを使用してプログラムカウンタを復元する。 復帰先のモードのレジスタをスタックから復元する必要のない単純な復帰では、例外 ハンドラは以下を使用するデータ処理命令を実行することによって、これらの処理を 行います。 • セットされた S フラグ • 復帰先のレジスタとして使用されるプログラムカウンタ 必要な復帰命令は、例外タイプによって異なります。各例外タイプからの復帰方法に (P. 6-9)を参照して下さい。 ついては、「復帰アドレスと復帰命令」 注 リセットハンドラはメインコードを直接実行するため、リセットハンドラから復帰す る必要はありません。 例外ハンドラのエントリコードが、スタックを使用して例外処理中も保存しておかな ければならないレジスタを格納した場合には、^ 修飾子を付けた多重ロード命令を使用 して復帰できます。例えば、以下の命令を使用して復帰できます。 LDMFD sp!,{r0-r12,pc}^ 上記の命令で復帰できるのは、以下のレジスタがスタックに保存された場合です。 • • ハンドラ呼び出し時に使用されていたすべての作業レジスタ 「復帰アドレスと復帰命令」(P. 6-9)で説明するデータ処理命令と同じ結果が得 られるように修正されたリンクレジスタ ^ 修飾子を指定することで、CPSR が SPSR から復元されます。この修飾子は、特権モー ドから使用する必要があります。スタック処理の詳細については、RealView Compilation Tools v3.0 Assembler Guide に記載されている、LDM および STM を使用したスタックの実 装に関する説明を参照して下さい。 6-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.3.3 復帰アドレスと復帰命令 例外発生時にプログラムカウンタが指す実際の位置は、例外タイプによって異なりま す。復帰アドレスは、必ずしもプログラムカウンタが指す次の命令であるとは限りま せん。 例外が ARM 状態で発生した場合、プロセッサは(PC - 4)を lr_ mode に格納します。 しかし、例外が Thumb 状態で発生した場合は、例外タイプごとに異なる値がプロセッ サによって自動的に格納されます。このような調整が必要な理由は、Thumb 命令が ARM 命令のようにフルワードではなく、ハーフワードしか使用しないためです。 この調整がプロセッサによって行われないとすると、ハンドラはプロセッサの元の状 態を判別し、ARM コードではなく Thumb コードに戻るために異なる命令を使用する必 要があります。しかしプロセッサがこの調整を行うことによって、ハンドラは例外発 生時のプロセッサ状態(ARM または Thumb)に関係なく、1 つの復帰命令で正しく復 帰できます。 これ以降のセクションでは、各タイプの例外を処理するコードから正しく復帰するた めの命令について説明します。 SVC ハンドラおよび未定義命令ハンドラからの復帰 SVC 例外と未定義命令例外は、命令自身によって生成されるため、これらの例外が発 生してもプログラムカウンタは更新されません。プロセッサは(pc - 4)を lr_ mode に 格納します。これにより、lr_mode は次に実行される命令を指します。以下の命令を使 用してリンクレジスタからプログラムカウンタを復元すると、 MOVS pc, lr ハンドラから制御が戻されます。 以下は、復帰アドレスをスタックにプッシュし、復帰時にそれをポップするハンドラ のエントリ / 終了コードを示しています。 STMFD ;... LDMFD sp!,{reglist,lr} sp!,{reglist,pc}^ Thumb 状態で例外が発生する場合を除き、このハンドラの復帰命令(MOVS pc,lr)に よって、プログラムカウンタは次に実行する命令のアドレスに変更されます。このア ドレスの値は(pc - 2)にあるため、プロセッサが lr_mode に格納する値は(pc - 2)と なります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-9 プロセッサ例外処理 FIQ ハンドラおよび IRQ ハンドラからの復帰 各命令の実行後、プロセッサは割り込みピンが LOW にセットされ、CPSR 内の割り込み ディセーブルビットがクリアされていることをチェックします。この結果、プログラム カウンタが更新されている場合にのみ IRQ 例外または FIQ 例外が生成されます。プロ セッサは(pc - 4)を lr_mode に格納します。これにより lr_mode は、例外が発生した命 令の終わりから 1 つ先の命令を指します。ハンドラ終了後、lr_mode が指す命令の前の 命令から実行が継続される必要があります。実行が再開されるアドレスは、lr_mode の 値よりも 1 ワード(4 バイト)少ない値であるため、復帰命令は以下のようになります。 SUBS pc, lr, #4 以下は、復帰アドレスをスタックにプッシュし、復帰時にそれをポップするハンドラ のエントリ / 終了コードを示しています。 SUB STMFD ;... LDMFD lr,lr,#4 sp!,{reglist,lr} sp!,{reglist,pc}^ Thumb 状態で例外が発生する場合を除き、このハンドラの復帰命令(SUBS pc,lr,#4) によって、プログラムカウンタは次に実行する命令のアドレスに変更されます。プロ グラムカウンタは例外発生前に更新されるため、次の命令の位置は(pc - 4)となりま す。したがって、プロセッサが lr_mode に格納する値は pc となります。 プリフェッチアボートハンドラからの復帰 プロセッサが不正アドレスから命令をフェッチしようとすると、その命令に無効のフ ラグが立てられます。パイプラインに既に入っている命令の実行は、その無効な命令 に到達するまで継続され、無効な命令に到達した時点でプリフェッチアボートが生成 されます。 この例外ハンドラは、マップされていない命令を物理メモリにロードし、MMU があれ ばこれを使用して、その仮想メモリの位置を物理メモリにマップします。その後、プ リフェッチアボート例外を発生させた命令の再試行に戻る必要があります。これでそ の命令がロードされ、実行されます。 プリフェッチアボートの生成時にはプログラムカウンタが更新されないため、lr_ABT はこの例外を発生させた命令の次の命令を指します。このハンドラは以下を使用して lr_ABTñ4 に復帰する必要があります。 SUBS 6-10 pc,lr, #4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 以下は、復帰アドレスをスタックにプッシュし、復帰時にそれをポップするハンドラ のエントリ / 終了コードを示しています。 SUB STMFD ;... LDMFD lr,lr,#4 sp!,{reglist,lr} sp!,{reglist,pc}^ Thumb 状態で例外が発生する場合を除き、このハンドラの復帰命令(SUBS pc,lr,#4) によって、プログラムカウンタはアボートされた命令のアドレスに変更されます。プ ログラムカウンタは例外発生前に更新されないため、アボートされた命令の位置は(pc - 4)となります。したがって、プロセッサが lr_mode に格納する値は pc となります。 データアボートハンドラからの復帰 ロード命令またはストア命令によってメモリへのアクセスが試行されるとき、プログ ラムカウンタは更新されています。lr_ABT に格納される(pc - 4)の値は、例外が発生 したアドレスから 2 つ先の命令を指します。MMU が存在し、MMU によって適切なア ドレスが物理メモリにマップされた場合には、アボートされた命令の実行を再試行で きるように、データアボートハンドラはこのアボートされた元の命令に戻る必要があ ります。したがって復帰アドレスは lr_ABT の値よりも 2 ワード(8 バイト)少ない値 となり、復帰命令は以下のようになります。 SUBS pc, lr, #8 以下は、復帰アドレスをスタックにプッシュし、復帰時にそれをポップするハンドラ のエントリ / 終了コードを示しています。 SUB STMFD ;... LDMFD lr,lr,#8 sp!,{reglist,lr} sp!,{reglist,pc}^ Thumb 状態で例外が発生する場合を除き、このハンドラの復帰命令(SUBS pc,lr,#8)に よって、プログラムカウンタはアボートされた命令のアドレスに変更されます。プログ ラムカウンタは例外発生前に更新されるため、アボートされた命令の位置は(pc - 6)と なります。したがって、プロセッサが lr_mode に格納する値は(pc + 2)となります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-11 プロセッサ例外処理 6.4 例外処理 トップレベルのベニアルーチンでは、プロセッサのステータスと必要なすべてのレジ スタをスタックに保存する必要があります。例外ハンドラを記述する際には、以下の 方法を使用できます。 • 例外ハンドラ全体を ARM コードで記述する。 • 例外を処理する Thumb コードルーチンへの BX (分岐および切り替え)を実行す る。Thumb 命令セットには SPSR から CPSR を復元できる命令がないため、例外か ら復帰するにはこのルーチンを ARM コードのベニアに戻す必要があります。 図 6-2 は、この実装方法を示しています。 Thumb coded application Vector table Switch to ARM state ARM coded veneers Save CPU and register state Entry Veneer Switch to Thumb state and return Restore CPU and register state Thumb coded handler Switch to Thumb state Handle the exception Switch to ARM state Exit veneer 図 6-2 ARM 状態または Thumb 状態での例外処理 「第 4 章 このように ARM コードと Thumb コードを組み合わせる方法については、 ARM と Thumb のインターワーク」を参照して下さい。 6-12 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.5 例外ハンドラのインストール 新しい例外ハンドラはベクタテーブルにインストールされる必要があります。インス トールが完了すると、その新しいハンドラは、対応する例外が発生するたびに実行さ れます。 このセクションは以下のサブセクションから構成されています。 • 例外ハンドラをインストールする方法 • リセット時のハンドラのインストール • C 言語からのハンドラのインストール(P. 6-15) 6.5.1 例外ハンドラをインストールする方法 例外ハンドラは以下の方法でインストールできます。 分岐命令 例外ハンドラへの到達に使用できる最も簡単な方法です。ベクタテーブ ル内の各エントリには、必要とされるハンドラルーチンへの分岐が保持 されます。しかし、この方法には制限があります。分岐命令の範囲は pc に相対して 32MB の範囲内に限定されるため、メモリ構成によっては分 岐でハンドラに到達することが不可能な場合があります。 ロード PC 命令 この方法では、以下の処理によってプログラムカウンタにハンドラのア ドレスを直接参照させます。 6.5.2 1. ハンドラの絶対アドレスを適切なメモリ位置(ベクタアドレスか ら 4KB 以内)に格納します。 2. 選択したメモリ位置の内容をプログラムカウンタにロードする命 令をベクタ内に配置します。 リセット時のハンドラのインストール アプリケーションがプログラムの実行開始をデバッガまたはデバッグモニタに依存し ていない場合には、アセンブリ言語のリセット(または起動)コードからベクタテー ブルを直接ロードできます。 ROM がメモリ内の 0x0 に配置されている場合は、コードの開始部分に各ベクタの分岐 ステートメントを記述できます。FIQ ハンドラが 0x1C から直接実行される場合は、こ の中に FIQ ハンドラを含めることも可能です(「割り込みハンドラ」(P. 6-28)参照)。 例 6-2(6-14 ページ)は、アドレス 0 にある ROM にベクタが配置されている場合に、 これらのベクタをセットアップするコードを示しています。ロード命令の代わりに分 岐ステートメントを使用できます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-13 プロセッサ例外処理 例 6-2 Vector_Init_Block LDR LDR LDR LDR LDR NOP LDR LDR Reset_Addr Undefined_Addr SVC_Addr Prefetch_Addr Abort_Addr IRQ_Addr FIQ_Addr DCD DCD DCD DCD DCD DCD DCD DCD pc, pc, pc, pc, pc, Reset_Addr Undefined_Addr SVC_Addr Prefetch_Addr Abort_Addr ;Reserved vector pc, IRQ_Addr pc, FIQ_Addr Start_Boot Undefined_Handler SVC_Handler Prefetch_Handler Abort_Handler 0 ;Reserved vector IRQ_Handler FIQ_Handler リセット時には 0x0 に ROM が配置されるように設定する必要があります。リセット コードを記述して、RAM を 0x0 にリマップできます。このリマップを行う前に、リ セットコードで ROM 内の領域からベクタ(および必要に応じて FIQ ハンドラ)をコ ピーし、RAM に配置する必要があります。 この場合は、リセットベクタコードを位置非依存にできるよう、LDR pc 命令を使用し てリセットハンドラのアドレスを指定する必要があります。 例 6-2 では、例 6-3 に示すベクタが RAM 内のベクタテーブルにコピーされます。 例 6-3 MOV ADR LDMIA STMIA LDMIA STMIA r8, #0 r9, Vector_Init_Block r9!,{r0-r7} ;Copy the vectors (8 words) r8!,{r0-r7} r9!,{r0-r7} ;Copy the DCD'ed addresses r8!,{r0-r7} ;(8 words again) 別の方法としては、スキャッタローディングメカニズムを使用して、ベクタテーブル のロード / 実行アドレスを定義することもできます。この場合は、C ライブラリによっ 「第 2 章 組み込みソフトウェアの開発」参照)。 てベクタテーブルがコピーされます( 6-14 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.5.3 C 言語からのハンドラのインストール 開発過程においては、例外ハンドラをメインアプリケーションからベクタに直接イン ストールしなければならない場合があります。このような場合には、必要とされる命 令エンコーディング処理を適切なベクタアドレスに記述する必要があります。この作 業は、ハンドラへの到達に分岐命令を使用する方法と、プログラムカウンタをロード する方法の両方で行うことができます。 分岐方式 必要な命令は以下のように構成できます。 1. 例外ハンドラのアドレスを取得します。 2. そのアドレスから、対応するベクタのアドレスを減算します。 3. プリフェッチ分の 0x8 を減算します。 4. バイトオフセットではなくワードオフセットになるように、上記の結果を 2 だけ 右にシフトします。 5. 得られた値の長さが 24 ビットであることを確認するため(分岐のオフセットは 24 ビットに制限されているため)、上位 8 ビットがクリアされていることを確認 します。 6. この結果と 0xEA000000 (分岐命令のオペコード)との論理和を取り、ベクタに 配置する値を求めます。 例 6-4(6-16 ページ)は、このアルゴリズムを実装する C 関数を示しています。 この C 関数は以下の引数を取ります。 • • ハンドラのアドレス ハンドラをインストールする先のベクタのアドレス この関数でハンドラをインストールしたり、ベクタの元の内容を戻したりすることが できます。この結果を使用して、特定の例外に使用されるハンドラのチェインを作成 できます。詳細については、「例外ハンドラのチェイン」(P. 6-42)を参照して下さい。 以下のコードで上記のルーチンを呼び出し、IRQ ハンドラをインストールします。 unsigned *irqvec = (unsigned *)0x18; Install_Handler ((unsigned)IRQHandler, irqvec); この場合、戻された IRQ ベクタの元の内容は破棄されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-15 プロセッサ例外処理 例 6-4 分岐方式の実装 unsigned Install_Handler (unsigned routine, unsigned *vector) /* Updates contents of 'vector' to contain branch instruction */ /* to reach ’routine’ from ’vector’.Function return value is */ /* original contents of 'vector'.*/ /* NB: ’Routine’ must be within range of 32MB from ’vector’.*/ { unsigned vec, oldvec; vec = ((routine - (unsigned)vector - 0x8)>>2); if ((vec & 0xFF000000)) { /* diagnose the fault */ printf ("Installation of Handler failed"); exit (1); } vec = 0xEA000000 | vec; oldvec = *vector; *vector = vec; return (oldvec); } PC のロード方法 必要な命令は以下のように構成できます。 1. 例外ハンドラのアドレスを含むワードのアドレスを取得します。 2. そのアドレスから、対応するベクタのアドレスを減算します。 3. プリフェッチ分の 0x8 を減算します。 4. 結果が 12 ビットで表現されていることを確認します。 5. この結果と 0xe59FF000 (LDR pc, [pc,#offset] のオペコード)との論理和を取 り、ベクタに配置する値を求めます。 6. このハンドラのアドレスを記憶位置に配置します。 例 6-5(6-17 ページ)は、この方式を実装する C ルーチンを示しています。 この例でも、戻される元の IRQ ベクタの内容は破棄されますが、この内容を使用して 「例外ハンドラの ハンドラのチェインを作成できる場合があります。詳細については、 チェイン」(P. 6-42)を参照して下さい。 6-16 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 例 6-5 プログラムカウンタをロードする方法の実装 unsigned Install_Handler (unsigned location, unsigned *vector) /* Updates contents of 'vector' to contain LDR pc, [pc, #offset] */ /* instruction to cause long branch to address in 'location'.*/ /* Function return value is original contents of 'vector'.*/ { unsigned vec, oldvec; vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000; oldvec = *vector; *vector = vec; return (oldvec); } 以下のコードで上記のルーチンを呼び出し、IRQ ハンドラをインストールします。 unsigned *irqvec = (unsigned *)0x18; static unsigned pIRQ_Handler = (unsigned)IRQ_handler Install_Handler (&pIRQHandler, irqvec); 注 命令キャッシュとデータキャッシュが分離されているプロセッサを使用する場合は、 キャッシュコヒーレンシの問題がベクタの新しい内容を使用するときの妨げにならな いように注意する必要があります。 データキャッシュ(または少なくとも修正されたベクタを含むエントリ)は、新しい ベクタの内容をメインメモリに書き込めるように、削除する必要があります。次に、新 しいベクタの内容をメインメモリから読み出せるように、命令キャッシュをフラッ シュする必要があります。 キャッシュの削除とフラッシュについては、ターゲットプロセッサのテクニカルリ ファレンスマニュアルを参照して下さい。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-17 プロセッサ例外処理 6.6 SVC ハンドラ このセクションでは、SVC ハンドラと以下の内容について説明します。 • • • • • • 6.6.1 呼び出す SVC の決定 アセンブリ言語を使用した SVC ハンドラ(P. 6-19) C 言語およびアセンブリ言語を使用した SVC ハンドラ(P. 6-20) スーパーバイザモードでの SVC の使用(P. 6-21) アプリケーションからの SVC の呼び出し(P. 6-23) アプリケーションからの動的な SVC の呼び出し(P. 6-25) 呼び出す SVC の決定 SVC ハンドルは起動時に、どの SVC が呼び出されているかを特定する必要がありま す。この情報は、図 6-3 に示すように命令自体の 0 ~ 23 ビットに保存するか、1 つの 整数レジスタに渡されます。通常、この整数レジスタには r0 ~ r3 のいずれかが使用さ れます。 31 28 27 26 25 24 23 cond 1 1 1 1 0 24_bit_immediate comment field 図 6-3 ARM SVC 命令 トップレベルの SVC ハンドラでは、SVC 命令をリンクレジスタの相対アドレスにロー ドできます。このロードは、アセンブリ言語か、C/C++ インラインアセンブラまたは 組み込みアセンブラで行います。 SVC ハンドラは、まず例外を発生させた SVC 命令をレジスタにロードする必要があり ます。この時点で lr_SVC には SVC 命令の次の命令のアドレスが保持されているため、 この SVC 命令は以下を使用してレジスタ(この場合は r0)にロードされます。 LDR r0, [lr,#-4] SVC ハンドラは次にコメントフィールドビットを検査し、要求されている処理を特定 します。SVC 番号は、オペコードの上位 8 ビットをクリアすることによって抽出され ます。 BIC r0, r0, #0xFF000000 例 6-6(6-19 ページ)は、これらの命令を組み合わせてトップレベルの SVC ハンドラを 形成する方法を示しています。 ARM 状態と Thumb 状態の SVC 命令を処理するハンドラの例については、 「プロセッサ 状態の判断」(P. 6-5)を参照して下さい。 6-18 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 例 6-6 トップレベルの SVC ハンドラ PRESERVE8 AREA TopLevelSVC, CODE, READONLY EXPORT SVC_Handler SVC_Handler STMFD sp!,{r0-r12,lr} LDR r0,[lr,#-4] BIC r0,r0,#0xff000000 ; Name this block of code. ; ; ; ; ; Store registers. Calculate address of SVC instruction and load it into r0. Mask off top 8 bits of instruction to give SVC number. ; ; Use value in r0 to determine which SVC routine to execute. ; LDMFD sp!, {r0-r12,pc}^ ; Restore registers and return. END ; Mark end of this file. 6.6.2 アセンブリ言語を使用した SVC ハンドラ 要求された SVC 番号のハンドラを呼び出す最も簡単な方法は、ジャンプテーブルを使 用する方法です。r0 に SVC 番号が含まれている場合、例 6-6 に示したトップレベルハ ンドラの BIC 命令に続けて、例 6-7 のコードを挿入できます。 例 6-7 SVC ジャンプテーブル CMP r0,#MaxSVC ; Range check LDRLS pc, [pc,r0,LSL #2] B SVCOutOfRange SVCJumpTable DCD SVCnum0 DCD SVCnum1 ; DCD for each of other SVC routines SVCnum0 ; SVC number 0 code B EndofSVC SVCnum1 ; SVC number 1 code B EndofSVC ; Rest of SVC handling code ; EndofSVC ; Return execution to top level ; SVC handler so as to restore ; registers and return to program. ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-19 プロセッサ例外処理 6.6.3 C 言語およびアセンブリ言語を使用した SVC ハンドラ トップレベルのハンドラは常に ARM アセンブリ言語で記述する必要がありますが、各 SVC を処理するルーチンはアセンブリ言語でも C 言語でも記述できます。この点に関 する制約条件については、「スーパーバイザモードでの SVC の使用」(P. 6-21)を参照 して下さい。 トップレベルのハンドラでは BL(リンク付き分岐)命令を使用して適切な C 関数にジャ ンプします。SVC 番号はアセンブリルーチンによって r0 にロードされるため、この値 は最初のパラメータとして C 関数に渡されます。C 関数はこの値を switch() ステート メントなどで使用できます。 例 6-6(6-19 ページ)の SVC_Handler ルーチンには、以下の行を追加できます。 BL C_SVC_Handler ; Call C routine to handle the SVC 例 6-8 は、C 関数の実装方法を示しています。 例 6-8 void C_SVC_handler (unsigned number) { switch (number) { case 0 :/* SVC number 0 code */ break; case 1 :/* SVC number 1 code */ break; ... default : /* Unknown SVC - report error */ } } 場合によってスーパーバイザスタック空間には制限があるため、大量のスタック空間 を必要とする関数の使用は避けて下さい。 MOV BL r1, sp C_SVC_Handler ; Second parameter to C routine... ; ...is pointer to register values. ; Call C routine to handle the SVC トップレベルのハンドラがスタックポインタの値を第 2 パラメータ(r1)として C 関 数に渡すことで、C 言語で記述した SVC ハンドラとの間で値を受け渡すことができ ます。 また、この C 関数はレジスタの値にアクセスできるように以下のように変更します。 void C_SVC_handler(unsigned number, unsigned *reg) 6-20 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 これでこの C 関数は、メインアプリケーションコード内で SVC 命令が検出されたとき にレジスタに保持されている値にアクセスできます(図 6-4 参照)。値はレジスタから 以下のように読み出し、 value_in_reg_0 value_in_reg_1 value_in_reg_2 value_in_reg_3 = = = = reg reg reg reg [0]; [1]; [2]; [3]; 以下のように書き戻します。 reg reg reg reg [0] [1] [2] [3] = = = = updated_value_0; updated_value_1; updated_value_2; updated_value_3; これにより、更新された値が適切なスタック位置に書き込まれ、トップレベルのハン ドラによってレジスタに復元されます。 Previous sp_SVC lr_SVC r3 reg[3] r2 r1 sp_SVC r0 reg[0] 図 6-4 スーパーバイザスタックへのアクセス 6.6.4 スーパーバイザモードでの SVC の使用 SVC 命令が実行される場合、以下のようになります。 1. プロセッサがスーパーバイザモードに入ります。 2. CPSR が spsr_SVC に格納されます。 3. ( 「例外に対するプロセッサの応答」 (P. 6-7) 復帰アドレスが lr_SVC に格納されます 参照)。 プロセッサが既にスーパーバイザモードにあるときは、lr_SVC と spsr_SVC が破壊され ます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-21 プロセッサ例外処理 スーパーバイザモードで SVC を呼び出す場合は、リンクレジスタと SPSR の元の値が失 われないように、lr_SVC および spsr_SVC を保存しておく必要があります。例えば、特 定の SVC 番号のハンドラルーチンが別の SVC を呼び出す場合は、そのハンドラルーチ この動作の結果、 ンで lr_SVC と spsr_SVC の両方をスタックに格納する必要があります。 ハンドラが呼び出されるごとに、ハンドラを呼び出した SVC に続く命令に復帰するため に必要な情報が保存されることが保証されます。例 6-9 は、この方法を示しています。 例 6-9 SVC ハンドラ AREA SVC_Area, CODE, READONLY PRESERVE8 EXPORT SVC_Handler IMPORT C_SVC_Handler T_bit EQU 0x20 SVC_Handler STMFD MOV MRS STMFD sp!,{r0-r3,r12,lr} r1, sp r0, spsr sp!, {r0, r3} ; ; ; ; ; ; Store registers. Set pointer to parameters. Get spsr. Store spsr onto stack and another register to maintain 8-byte-aligned stack.This is only really needed in case of nested SVCs. ; the next two instructions only work for SVC calls from ARM state. ; See "Example 6-18 on page 6-36" for a version that works for calls from either ARM or Thumb. LDR BIC r0,[lr,#-4] r0,r0,#0xFF000000 ; Calculate address of SVC instruction and load it into r0. ; Mask off top 8 bits of instruction to give SVC number. ; r0 now contains SVC number ; r1 now contains pointer to stacked registers BL LDMFD MSR LDMFD C_SVC_Handler sp!, {r0, r3} spsr_cf, r0 sp!, {r0-r3,r12,pc}^ ; ; ; ; Call C routine to handle the SVC. Get spsr from stack. Restore spsr. Restore registers and return. END C/C++ で記述した入れ子構造の SVC C または C++ で入れ子になった SVC を記述できます。ARM コンパイラによって生成 されるコードは、必要に応じて lr_SVC のストアと再ロードを実行します。 6-22 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.6.5 アプリケーションからの SVC の呼び出し SWI はアセンブリ言語または C/C++ から呼び出すことができます。 アセンブリ言語では、必要なレジスタ値を設定して関連 SVC を発行します。以下に例 を示します。 MOV SVC r0, #65 0x0 ; load r0 with the value 65 ; Call SVC 0x0 with parameter value in r0 SVC 命令は、ほぼすべての ARM 命令と同じように、条件付きで実行できます。 C/C++ からは SVC を __SVC 関数として宣言し、これを呼び出します。以下に例を示し ます。 __svc(0) void my_svc(int); . . . my_svc(65); これにより、以下の条件下で、呼び出しのオーバーヘッドを新たに発生させることな く SVC をインラインでコンパイルできます。 • すべての引数を r0 ~ r3 のみで渡す。 • すべての結果を r0 ~ r3 のみで返す。 SVC が実際の関数呼び出しであるかのように、パラメータが SVC に渡されます。しか し、2 ~ 4 個の戻り値がある場合には、それらの戻り値が構造体で返されることをコン パイラに通知する必要があるため、__value_in_regs ディレクティブを使用します。こ れは通常、struct の値を取る関数が、結果の構造体が配置されるアドレスを第 1 引数 に取る void 関数と同じように処理されるためです。 例 6-10(6-24 ページ)および 例 6-11(6-24 ページ)は、0x0、0x1、0x2、および 0x3 の 4 つの SVC 番号を取る SVC ハンドラを示しています。SVC 0x0 および 0x1 は、それぞ れ 2 つの整数パラメータを使用し、1 つの結果を返します。SVC 0x2 は 4 つのパラメー タを使用し、1 つの結果を返します。SVC 0x3 は 4 つのパラメータを使用し、4 つの結 果を返します。このサンプルは、主なサンプルディレクトリ ...\svc\main.c および ...\svc\svc.h に収録されています。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-23 プロセッサ例外処理 例 6-10 main.c #include <stdio.h> #include "svc.h" unsigned *svc_vec = (unsigned *)0x08; extern void SVC_Handler(void); int main( void ) { int result1, result2; struct four_results res_3; Install_Handler( (unsigned) SVC_Handler, svc_vec ); printf("result1 = multiply_two(2,4) = %d\n", result1 = multiply_two(2,4)); printf("result2 = multiply_two(3,6) = %d\n", result2 = multiply_two(3,6)); printf("add_two( result1, result2 ) = %d\n", add_two( result1, result2 )); printf("add_multiply_two(2,4,3,6) = %d\n", add_multiply_two(2,4,3,6)); res_3 = many_operations( 12, 4, 3, 1 ); printf("res_3.a = %d\n", res_3.a ); printf("res_3.b = %d\n", res_3.b ); printf("res_3.c = %d\n", res_3.c ); printf("res_3.d = %d\n", res_3.d ); return 0; } 例 6-11 svc.h __svc(0) int multiply_two(int, int); __svc(1) int add_two(int, int); __svc(2) int add_multiply_two(int, int, int, int); struct four_results { int a; int b; int c; int d; }; __svc(3) __value_in_regs struct four_results many_operations(int, int, int, int); 6-24 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.6.6 アプリケーションからの動的な SVC の呼び出し 状況によっては、SVC 番号がランタイム時までわからない SVC を呼び出さなければな らない場合があります。このような状況は、例えば 1 つのオブジェクト上で実行され る複数の処理が関連し合っていて、 各処理ごとに SVC があるような場合に発生します。 このような場合には、前述の方法は適切ではありません。 この状況には、以下のような方法で対処できます。 • SVC 番号から SVC 命令を構成し、それをどこかに格納した後で実行する。 • それ自身の引数に対して実行される実際の処理のためのコードを別の引数とし て取る汎用 SVC を使用する。この汎用 SVC によって処理がデコードされ、実行 されます。 2 番目の方法としては、要求される処理を示す値をレジスタ(通常は r0 または r12)に 渡すことで、アセンブリ言語で実装できます。これで、SVC ハンドラが適切なレジス タ内の値を基に動作するように書き直すことができます。 場合によっては値をコメントフィールドの SVC に渡す必要があるため、これらの 2 つ の方法を組み合わせて使用することもあります。 例えば、オペレーティングシステムが 1 つの SVC 命令しか使用せず、レジスタを使用 して要求される処理番号を渡すとします。この場合は残りの SVC 空間をアプリケー ション固有の SVC に使用することができます。この方法は、特定のアプリケーション で、命令からの SVC 番号の抽出にかかるオーバーヘッドが大きすぎる場合に利用でき ます。ARM (0x123456)および Thumb (0xAB)のセミホステッド SVC は、この方法 で実装されます。 例 6-12 は、C の関数呼び出しをセミホスティング SVC に対応付けるときの __svc の使 用 方 法 を 示 し て い ま す。こ の サ ン プ ル は、主 な サ ン プ ル デ ィ レ ク ト リ ...\emb_sw_dev\source\retarget.c の retarget.c に収録されています。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-25 プロセッサ例外処理 例 6-12 C 関数のセミホスティング SVC への対応付け #ifdef __thumb /* Thumb Semihosting */ #define SemiSVC 0xAB #else /* ARM Semihosting */ #define SemiSVC 0x123456 #endif /* Semihosting SVC to write a character */ __svc(SemiSVC) void Semihosting(unsigned op, char *c); #define WriteC(c) Semihosting (0x3,c) void write_a_character(int ch) { char tempch = ch; WriteC( &tempch ); } コンパイラには、r12 を使用して要求される処理番号の受け渡しを行うためのメカニズ ムが組み込まれています。AAPCS では r12 は ip レジスタであり、関数呼び出しの間だ け専用の役割が与えられます。それ以外のときは、このレジスタをスクラッチレジス タとして使用できます。前述のとおり、汎用 SVC への引数はレジスタ r0 ~ r3 で渡さ れ、それらの値は必要に応じて r0 ~ r3 に返されます( 「アプリケーションからの SVC の呼び出し」 (P. 6-23)参照)。r12 に渡す処理番号は、汎用 SVC によって呼び出される SVC の値にすることができますが、必ずしもそうである必要はありません。 例 6-13 は、汎用 SWI、つまり間接 SWI を使用する C コードを示しています。 例 6-13 __svc_indirect(0x80) unsigned SVC_ManipulateObject(unsigned operationNumber, unsigned object,unsigned parameter); unsigned DoSelectedManipulation(unsigned object, unsigned parameter, unsigned operation) { return SVC_ManipulateObject(operation, object, parameter); } 6-26 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 これによって以下のコードが生成されます。 DoSelectedManipulation PROC STMFD sp!,{r3,lr} MOV r12,r2 SVC 0x80 LDMFD sp!,{r3,pc} ENDP また、__svc の機構を使用して、C 言語から SVC 番号を r0 に入れて渡すことも可能で す。例えば、SVC 0x0 が汎用 SVC として使用され、処理 0 が文字の読み出し、処理 1 が 文字の書き込みである場合は、以下のように設定できます。 __svc (0) char __ReadCharacter (unsigned op); __svc (0) void __WriteCharacter (unsigned op, char c); 上記は、以下のように定義しておくとわかりやすくなります。 #define ReadCharacter () __ReadCharacter (0); #define WriteCharacter (c) __WriteCharacter (1, c); しかし、r0 をこの方法で使用した場合、SVC にパラメータを渡すレジスタには 3 つの レジスタしか使用できません。通常、r0 ~ r3 よりも多くのパラメータをサブルーチン に渡す必要がある場合には、スタックを使用します。ただし、スタックに格納される パラメータは、SVC ハンドラが使用するスーパーバイザスタックではなくユーザモー ドスタックに格納されるのが一般的であるため、SVC ハンドラから簡単にアクセスで きません。 別の方法としては、レジスタの 1 つ(通常、r1)を使用して、他のパラメータが保存さ れているメモリブロックを参照する方法があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-27 プロセッサ例外処理 6.7 割り込みハンドラ このセクションでは、外部割り込み FIQ および IRQ を処理する割り込みハンドラを記 述する方法、および以下の内容について説明します。 • 外部割り込みのレベル • C 言語を使用した単純な割り込みハンドラの記述 • • 6.7.1 リエントラント割り込みハンドラ(P. 6-31) アセンブリ言語を使用した割り込みハンドラのサンプル(P. 6-33) 外部割り込みのレベル ARM プロセッサには FIQ と IRQ の 2 つのレベルの外部割り込みがあり、両方ともコア に流れるレベルセンシティブな LOW アクティブ信号です。割り込みを発生させるに は、CPSR 内の適切なディセーブルビットをクリアしておく必要があります。 FIQ は、以下のように IRQ よりも優先されます。 • 複数の割り込みが発生すると、FIQ が最初に処理されます。 • FIQ の処理によって IRQ がディセーブルされ、FIQ ハンドラが IRQ を再イネーブ ルするまで IRQ の処理は開始されません。IRQ は、ハンドラの終わりで SPSR か ら CPSR を復元することによって再イネーブルするのが一般的です。 FIQ ベクタはベクタテーブルの最後のエントリ(アドレス 0x1C)であるため、FIQ ハン ドラをこのベクタ位置に直接配置して、そのアドレスからシーケンシャルに実行でき ます。したがって分岐の必要やそれに伴う遅延が発生せず、システムにキャッシュが 搭載されている場合は、ベクタテーブルと FIQ ハンドラのすべてがキャッシュ内の 1 つのブロックにロックされます。FIQ はできるだけ速く割り込みを処理する目的で設計 されるため、これは重要です。さらに FIQ モードの 5 つのバンクレジスタによって、ハ ンドラの呼び出しと呼び出しの間、状態を保持できるため、さらに実行速度を上げる ことができます。 注 割り込みハンドラには、割り込みソースをクリアするコードを含める必要があります。 6-28 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.7.2 C 言語を使用した単純な割り込みハンドラの記述 __irq 関数宣言キーワードを使用して、単純な割り込みハンドラを C 言語で記述できま す。__irq キーワードは、単純な 1 レベルの割り込みハンドラと、サブルーチンを呼び 出す割り込みハンドラの両方に使用できます。ただし、__irq キーワードを使用しても SPSR の保存と復元は行われないため、これをリエントラントな 割り込みハンドラに使 用することはできません。ここでのリエントラントとは、ハンドラが割り込みを再イ ネーブルし、それ自身に割り込ませることが可能なことを意味します。詳細について は、「リエントラント割り込みハンドラ」 (P. 6-31)を参照して下さい。 __irq キーワードの使用により、以下のようになります。 • 破壊される可能性のあるすべての AAPCS レジスタが保存されます。 • その関数に使用される他のすべてのレジスタ(浮動小数点レジスタを除く)が保 存されます。 プログラムカウンタに(lr - 4)をセットし、CPSR に元の値を復元することによっ て関数が終了します。 • 関数がサブルーチンを呼び出す場合、__irq を使用することにより、破壊される可能性 のあるレジスタが保存されるだけでなく、その割り込みモードのリンクレジスタも保 存されます。詳細については、 「割り込みハンドラからのサブルーチンの呼び出し」を 参照して下さい。 注 Thumb C コードをコンパイルする場合は、この方法で C 言語の割り込みハンドラを作 成することはできません。Thumb のコード(--thumb オプションまたは #pragma thumb) をコンパイルするときは、__irq として指定されている関数が ARM モードにコンパイ ルされます。 しかし、インターワークがイネーブルされている場合には、__irq 関数によって呼び出 されるサブルーチンを Thumb モードでコンパイルできます。インターワークの詳細に ついては、「第 4 章 ARM と Thumb のインターワーク」を参照して下さい。 割り込みハンドラからのサブルーチンの呼び出し トップレベルの割り込みハンドラからサブルーチンを呼び出す場合に __irq キーワー ドを使用すると、スタックから lr_IRQ の値が復元されます。そのため、SUBS 命令は、 割り込み処理後にこの値を使用して正しいアドレスに復帰できます。 例 6-14 はこれを実装するコードを示しています。トップレベル割り込みハンドラは、 メモリマップされた割り込みコントローラのベースアドレスの値を 0x80000000 から読 み出します。このアドレスの値が 1 のとき、トップレベルのハンドラは C で記述され たハンドラに分岐します。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-29 プロセッサ例外処理 例 6-14 __irq void IRQHandler (void) { volatile unsigned int *base = (unsigned int *) 0x80000000; if (*base == 1) { C_int_handler(); } *(base+1) = 0; // which interrupt was it? // process the interrupt // clear the interrupt } armcc でコンパイルすると、例 6-14(6-30 ページ)からは以下のコードが生成されます。 IRQHandler PROC STMFD MOV LDR SUB CMP BLEQ MOV STR ADD LDMFD SUBS ENDP sp!,{r0-r4,r12,lr} r4,#0x80000000 r0,[r4,#0] sp,sp,#4 r0,#1 C_int_handler r0,#0 r0,[r4,#4] sp,sp,#4 sp!,{r0-r4,r12,lr} pc,lr,#4 これを、__irq キーワードが使用されていない場合の以下の結果と比較して下さい。 IRQHandler PROC STMFD MOV LDR CMP BLEQ MOV STR LDMFD ENDP 6-30 sp!,{r4,lr} r4,#0x80000000 r0,[r4,#0] r0,#1 C_int_handler r0,#0 r0,[r4,#4] sp!,{r4,pc} Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.7.3 リエントラント割り込みハンドラ 割り込みハンドラが割り込みを再イネーブルしてサブルーチンを呼び出した後、別の 割り込みが発生すると、このサブルーチンの(lr_IRQ にストアされる)復帰アドレス は 2 番目の IRQ の発生時に破壊されます。C 言語で __irq キーワードを使用しても、リ エントラント割り込みハンドラに必要な SPSR の保存と復元は行われないため、トップ レベルハンドラはアセンブリ言語で記述する必要があります。 リエントラント割り込みハンドラは、IRQ 状態を保存し、プロセッサモードを切り替 え、入れ子のサブルーチンまたは C 関数に分岐する前に新しいプロセッサモードの状 態を保存する必要があります。 ARMv4 以上では、システムモードに切り替えることができます。システムモードでは ユーザモードレジスタを使用しながら、例外ハンドラで必要とされる可能性がある特 「システムモード」 (P. 6-44)を参照して下さ 権アクセスが可能です。詳細については、 い。ARMv4 よりも前の ARM アーキテクチャでは、この処理を行う場合にはスーパー バイザモードに切り替える必要があります。 注 この方法は、IRQ と FIQ の両方の割り込みに使用できます。しかし、FIQ 割り込みは できるだけ速く処理されるうえ、通常は 1 つの割り込みソースしか存在しないため、リ エントラントな機能が実装される必要はないかもしれません。 IRQ ハンドラで安全に割り込みを再イネーブルするには、以下の手順に従って下さい。 1. 復帰アドレスを作成し、IRQ スタックに保存します。 2. 作業レジスタと spsr_IRQ を保存します。 3. 割り込みソースをクリアします。 4. システムモードに切り替え、割り込みを再イネーブルします。 5. ユーザモードのリンクレジスタと、被発呼側で保存されないレジスタを保存し ます。 6. C 言語の割り込みハンドラ関数を呼び出します。 7. C 言語の割り込みハンドラが復帰するとき、ユーザモードレジスタを復元し、割 り込みをディセーブルします。 8. IRQ モードに切り替え、割り込みをディセーブルします。 9. 作業レジスタと spsr_IRQ を復元します。 10. この IRQ から復帰します。 lr_IRQ 例 6-15 は、システムモードでの方法を示しています。レジスタ r12 および r14 は、 がスタックにプッシュされた後に一時作業レジスタとして使用されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-31 プロセッサ例外処理 例 6-15 PRESERVE8 AREA INTERRUPT, CODE, READONLY IMPORT C_irq_handler IRQ SUB STMFD MRS STMFD lr, lr, #4 sp!, {lr} r14, SPSR sp!, {r12, r14} ; ; ; ; construct the return address and push the adjusted lr_IRQ copy spsr_IRQ to r14 save work regs and spsr_IRQ ; Add instructions to clear the interrupt here ; then re-enable interrupts. MSR STMFD CPSR_c, #0x1F sp!, {r0-r3, lr} BL LDMFD MSR C_irq_handler sp!, {r0-r3, lr} CPSR_c, #0x92 LDMFD MSR LDMFD END sp!, {r12, r14} SPSR_cf, r14 sp!, {pc}^ ; ; ; ; ; ; ; ; ; switch to SYS mode, FIQ and IRQ enabled.USR mode registers are now current. save lr_USR and non-callee saved registers branch to C IRQ handler. restore registers switch to IRQ mode and disable IRQs.FIQ is still enabled. ; restore work regs and spsr_IRQ ; return from IRQ. このサンプルでは、FIQ が永久にイネーブルされていると想定されています。 6-32 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.7.4 アセンブリ言語を使用した割り込みハンドラのサンプル 割り込みハンドラは、できるだけ速く実行できるようにアセンブリ言語で記述するの が一般的です。以下のセクションではいくつかのサンプルを紹介します。 • シングルチャネル DMA 転送 • • • デュアルチャネル DMA 転送(P. 6-34) 割り込みの優先度(P. 6-35) コンテキストスイッチ(P. 6-37) シングルチャネル DMA 転送 例 6-16(6-33 ページ)は、メモリ転送を行うために割り込み駆動型 I/O (ソフト DMA) を実行する割り込みハンドラを示しています。このコードは FIQ ハンドラです。バン ク付き FIQ レジスタを使用して、割り込みの間、状態が保持されます。このコードは、 0x1C に配置するのが最も適切です。 このサンプルコードでは、以下のようになります。 r8 データが読み出される I/O デバイスのベースアドレスを指します。 IOData ベースアドレスから、データが読み出される 32 ビットのレジスタまでの オフセットです。このレジスタの読み出しによって、割り込みがクリア されます。 r9 データ転送先のメモリ位置を指します。 r10 転送先の最後のアドレスを指します。 通常の転送を処理する全体のシーケンスには 4 つの命令が使用されます。条件付き の復帰後に配置されているコードは、転送が完了したことを通知するために使用さ れます。 例 6-16 FIQ ハンドラ LDR STR CMP SUBLSS r11, [r8, #IOData] r11, [r9], #4 r9, r10 pc, lr, #4 ; ; ; ; ; ; Load port data from the IO device. Store it to memory: update the pointer. Reached the end ? No, so return. Insert transfer complete code here. ロード命令をバイトロード命令に置き換えることにより、バイト転送を実行できます。 メモリから I/O デバイスへの転送は、ロード命令とストア命令の間でアドレシングモー ドをスワップすることによって実行されます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-33 プロセッサ例外処理 デュアルチャネル DMA 転送 例 6-17(6-34 ページ)は例 6-16 と似ていますが、ここでは 2 つのチャネルが処理され ます。このコードは FIQ ハンドラです。バンク付き FIQ レジスタを使用して、割り込 みの間、状態が保持されます。このコードは、0x1c に配置するのが最も適切です。 このサンプルコードでは、以下のようになります。 r8 データが読み出される I/O デバイスのベースアドレスを指します。 IOStat ベースアドレスから、2 つのポートのどちらが割り込みを発生さ せたかを示すレジスタへのオフセットです。 IOPort1Active 第 1 ポートが割り込みを発生させたことを示すビットマスクです (これ以外の場合は、第 2 ポートが割り込みを発生させたとみな されます)。 IOPort1、IOPort2 読み出される 2 つのデータレジスタへのオフセットです。データ レジスタの読み出しによって、対応するポートの割り込みがクリ アされます。 r9 第 1 ポートからのデータを転送する先のメモリ位置を指します。 r10 第 2 ポートからのデータを転送する先のメモリ位置を指します。 r11、r12 転送先の最後のアドレスを指します(第 1 ポートには r11、第 2 ポートには r12 が使用されます)。 通常の転送を処理する全体のシーケンスには 9 つの命令が使用されます。条件付き の復帰後に配置されているコードは、転送が完了したことを通知するために使用さ れます。 例 6-17 FIQ ハンドラ 6-34 LDR r13, [r8, #IOStat] ; Load status register to find which port ; caused the interrupt. TST LDREQ LDRNE STREQ STRNE CMP CMPLE SUBNES r13, #IOPort1Active r13, [r8, #IOPort1] ; Load port 1 data. r13, [r8, #IOPort2] ; Load port 2 data. r13, [r9], #4 ; Store to buffer 1. r13, [r10], #4 ; Store to buffer 2. r9, r11 ; Reached the end? r10, r12 ; On either channel? pc, lr, #4 ; Return ; Insert transfer complete code here. Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 ロード命令をバイトロード命令に置き換えることにより、バイト転送を実行できます。 メモリから I/O デバイスへの転送は、条件付きロード命令と条件付きストア命令の間で アドレシングモードをスワップすることによって実行されます。 割り込みの優先度 例 6-18(6-36 ページ)では、最高 32 個の割り込みソースが適切なハンドラルーチンに 送信されます。このサンプルは通常の割り込みベクタ(IRQ)に使用される目的で設計 されているため、0x18 から分岐させる必要があります。 外部ハードウェアは、割り込みの優先度を決定し、I/O レジスタに優先度の高いアク ティブな割り込みを割り当てるために使用します。 このサンプルコードでは、以下のようになります。 IntBase 割り込みコントローラのベースアドレスを保持します。 IntLevel 最高優先度のアクティブな割り込みを含むレジスタのオフセットを保持 します。 r13 小さな Full Descending スタックを指します。 割り込みは、このコードへの分岐も含め、10 命令実行後にイネーブルされます。 各割り込みに固有のハンドラは、さらに 2 命令実行後に(すべてのレジスタがスタッ クに保存された状態で)開始されます。 また、各ハンドラの最後の 3 命令は割り込みをもう一度オフにした状態で実行される ため、SPSR は安全にスタックから復元できます。 注 Application Note 30 - Software Prioritization of Interrupts では、上記のハードウェアを使用 する方法ではなく、ソフトウェアを使用して複数ソースの割り込みの優先度を決定す る方法について説明しています。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-35 プロセッサ例外処理 例 6-18 ; first save the critical state SUB lr, lr, #4 ; ; STMFD sp!, {lr} ; MRS r14, SPSR ; STMFD sp!, {r12, r14} ; ; ; ; MOV r12, #IntBase ; ; LDR r12, [r12, #IntLevel] ; Adjust the return address before we save it. Stack return address get the SPSR ... ... and stack that plus a working register too. Now get the priority level of the highest priority active interrupt. Get the interrupt controller's base address. Get the interrupt level (0 to 31). ; Now read-modify-write the CPSR to enable interrupts. MRS BIC r14, CPSR r14, r14, #0x80 MSR CPSR_c, r14 LDR pc, [pc, r12, LSL #2] NOP ; ; ; ; ; ; ; ; ; Read the status register. Clear the I bit (use 0x40 for the F bit). Write it back to re-enable interrupts and jump to the correct handler. PC base address points to this instruction + 8 pad so the PC indexes this table. ; Table of handler start addresses DCD Priority0Handler DCD Priority1Handler DCD Priority2Handler ; ... Priority0Handler STMFD sp!, {r0 - r11} ; ... LDMFD sp!, {r0 - r11} ; Save other working registers. ; Insert handler code here. ; Restore working registers (not r12). ; Now read-modify-write the CPSR to disable interrupts. MRS r12, CPSR ; Read the status register. ORR r12, r12, #0x80 ; Set the I bit ; (use 0x40 for the F bit). MSR CPSR_c, r12 ; Write it back to disable interrupts. ; Now that interrupt disabled, can safely restore SPSR then return. LDMFD sp!, {r12, r14} ; Restore r12 and get SPSR. MSR SPSR_csxf, r14 ; Restore status register from r14. LDMFD sp!, {pc}^ ; Return from handler. Priority1Handler ; ... 6-36 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 コンテキストスイッチ 例 6-19(6-37 ページ)では、ユーザモードのプロセスへのコンテキストスイッチが実行 されます。このコードでは、実行準備が整ったプロセッサの プロセス制御ブロック (PCB)を指すポインタのリストが使用されています。 図 6-5 は、このサンプルで使用されている PCB のレイアウトを示しています。 r14 r13 r12 r11 r10 r9 r8 r7 r6 r5 r4 r3 r2 r1 r0 lr spsr PCB pointer User mode registers 図 6-5 PCB のレイアウト r12 は次に実行されるプロセスの PCB を指すポインタを指します。このリストの終わ りにはゼロポインタが配置されます。レジスタ r13 は PCB へのポインタであり、タイ ムスライスの際には保存されるため、エントリ時には実行中のプロセスの PCB を指し ます。 例 6-19 STMIA MRS STMDB LDR CMP LDMNEDB MSRNE LDMNEIA NOP SUBNES ARM DUI 0203GJ r13, {r0 - r14}^ r0, SPSR r13, {r0, lr} r13, [r12], #4 r13, #0 r13, {r0, lr} SPSR_cxsf, r0 r13, {r0 - r14}^ ; ; ; ; ; ; ; ; Dump user registers above r13. Pick up the user status and dump with return address below. Load next process info pointer. If it is zero, it is invalid Pick up status and return address. Restore the status. Get the rest of the registers pc, lr, #4 ; and return and restore CPSR. ; Insert "no next process code" here. Copyright © 2002-2006 ARM Limited. All rights reserved. 6-37 プロセッサ例外処理 6.8 リセットハンドラ リセットハンドラの動作は、ソフトウェア開発対象のシステムによって異なります。例 えば、以下のような動作が考えられます。 • 例外ベクタのセットアップ。詳細については、 「例外ハンドラのインストール」 (P. 6-13)を参照して下さい。 • スタックとレジスタの初期化。 • MMU を使用している場合の、メモリシステムの初期化。 • 重要な I/O デバイスの初期化。 • 割り込みのイネーブル。 • プロセッサのモードまたは状態、あるいはその両方の変更。 • C 言語に必要な変数の初期化とメインアプリケーションの呼び出し。 「第 2 章 組み込みソフトウェアの開発」を参照して下さい。 詳細については、 6-38 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.9 未定義命令ハンドラ プロセッサに認識されない命令は、システムに接続されているコプロセッサに渡され ます。その命令がどのコプロセッサにも認識されない場合には、未定義命令例外が生 成されます。例えば、命令がコプロセッサ用の命令であるにもかかわらず、コプロセッ サに関連する浮動小数点アクセラレータ(FPA)などのコプロセッサがシステムに接続 されていないケースが考えられます。ただし、そのようなコプロセッサ用のソフトウェ アエミュレータを使用することも可能です。 このようなエミュレータは、以下のような処理を行う必要があります。 1. 未定義命令ベクタに接続し、古い内容を保存する必要があります。 2. 未定義命令を検査し、エミュレートする必要があるかどうかをチェックします。 このチェックは、SVC ハンドラが SVC 番号を抽出する方法と似ていますが、エ ミュレータでは下位 24 ビットを抽出するのではなく、ビット 27 ~ 24 を抽出す る必要があります。 これらのビットに基づき、以下の方法でその命令がコプロセッサ命令であるかど うかが判断されます。 3. • ビット 27 ~ 24 が b1110 または b110x の場合、その命令はコプロセッサ命 令です。 • ビット 8 ~ 11 が、コプロセッサエミュレータによってその命令を処理する 必要があることを示している場合、エミュレータはその命令を処理して ユーザプログラムに復帰する必要があります。 上記以外の場合、エミュレータはインストール時に保存されたベクタを使用し て、その例外を元のハンドラ(またはチェイン内の次のエミュレータ)に渡す必 要があります。 チェイン内のどのエミュレータもその命令を処理できない場合には、その命令の処理 を続けることができないため、未定義命令ハンドラがエラーを通知して終了する必要 があります。詳細については、 「例外ハンドラのチェイン」 (P. 6-42)を参照して下さい。 注 Thumb 命令セットにはコプロセッサ命令がないため、未定義命令ハンドラでそのよう な命令をエミュレートする必要はありません。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-39 プロセッサ例外処理 6.10 プリフェッチアボートハンドラ システムに MMU が存在しない場合は、プリフェッチアボートハンドラでエラーを通 知して実行を終了できます。MMU が存在する場合には、アボートを発生させたアドレ スが物理メモリに復元される必要があります。lr_ABT は、アボートの原因となったア ドレスの次のアドレスにある命令を指しているため、復元されるアドレスは lr_ABT 4 にあることになります。そのアドレスに対して仮想メモリのメモリ障害が処理される と、命令フェッチが再試行されます。プリフェッチアボートハンドラは、次の命令で はなく、アボートを発生させた命令に復帰します。以下に例を示します。 SUBS 6-40 pc,lr,#4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 6.11 データアボートハンドラ MMU が存在しない場合は、データアボートハンドラでエラーを通知して終了する必要 があります。MMU が存在する場合は、データアボートハンドラによって仮想メモリの メモリ障害を解決する必要があります。 lr_ABT はデータアボートを発生させた命令よりも 2 つ先の命令を指すため、データア ボートを発生させた命令の位置は lr_ABT - 8 となります。 このアボートを発生する可能性のある命令には以下のような種類があります。 単一レジスタロード / ストア命令(LDR または STR) 応答はプロセッサタイプによって異なります。 • アボートが ARM7TDMI® などの ARM7 プロセッサで発生した場 合、アドレスレジスタが更新されているので、変更を元に戻す必 要があります。 • アボートが ARM9、ARM10、または StrongARM 以降のプロセッサ で発生した場合、プロセッサは命令開始前に保持されていた値を そのアドレスに復元します。この変更を元に戻す操作は必要あり ません。 スワップ命令(SWP)この命令に関連してアドレスレジスタが更新されることはあり ません。 多重ロード / ストア命令(LDM または STM) 応答はプロセッサタイプによって異なります。 • アボートが ARM 7 ベースのプロセッサで発生し、ライトバックが イネーブルされている場合には、転送全体が発生したかのように ベースレジスタが更新されます。 レジスタリスト内にベースレジスタを指定して LDM を使用した場 合、プロセッサは後で回復できるように、上書きされた値を修正 されたベース値で置き換えます。元のベースアドレスは、使用さ れるレジスタの数を使用して再計算されます。 • アボートが ARM9、ARM10 あるいは StrongARM 以降のプロセッ サで発生し、ライトバックがイネーブルされている場合には、命 令開始前に保持されていた値をベースレジスタに復元します。 上記 3 つのどのケースでも、MMU は要求された仮想メモリを物理メモリにロードでき ます。MMU のフォールトアドレスレジスタ(FAR)には、アボートを発生させたアド レスが保持されます。これにより、ハンドラは復帰して命令を再実行することができ ます。 デ ー タ ア ボ ー ト ハ ン ド ラ の サ ン プ ル コ ー ド は、主 な サ ン プ ル デ ィ レ ク ト リ の ...\databort に収録されています。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-41 プロセッサ例外処理 6.12 例外ハンドラのチェイン 状況によっては、特定の例外に複数のソースが存在することがあります。以下に例を 示します。 • Angel は未定義命令を使用してブレークポイントを実装します。しかし、未定義 命令例外は、コプロセッサが存在しないときにコプロセッサ命令が実行される場 合にも発生します。 • Angel は、ユーザモードからスーパーバイザモードへの切り替えや、開発中のセ ミホスティング要求のサポートなど、SVC をさまざまな目的に使用します。しか し、リアルタイムオペレーティングシステム(RTOS)またはアプリケーション が SVC を実装している場合もあります。 このような状況では、以下の方法で例外処理コードを拡張できます。 • 1 つの拡張ハンドラ • 6.12.1 複数のハンドラのチェイン 1 つの拡張ハンドラ 場合によっては、例外ハンドラ内のコードを拡張して例外のソースが何であったかを 解決し、適切なコードを直接呼び出すことができます。この場合は、例外ハンドラの ソースコードを修正します。 Angel は、このアプローチを簡素化できるように記述されています。Angel によって SVC と未定義命令がデコードされます。また、Angel 例外ハンドラを拡張して Angel 以 外の SVC と未定義命令を処理できます。 ただしこの方法は、1 つの例外ハンドラを記述するときに例外のすべてのソースがわ かっている場合にのみ有効です。 6.12.2 複数のハンドラのチェイン 場合によっては複数のハンドラが必要になるときがあります。標準の Angel デバッガ を実行し、複数の SVC をサポートさせたいスタンドアロンのユーザアプリケーション (または RTOS)をダウンロードするとします。新たにロードされるアプリケーション に、インストール可能な完全に独立した例外ハンドラが備わっている場合でも、Angel ハンドラを置き換えることはできません。 この場合は、新しいハンドラが例外のソースを処理できないことを認識した場合に古 いハンドラを呼び出せるように、古いハンドラのアドレスが記録される必要がありま す。例えば、RTOS SVC ハンドラは、SVC が RTOS SVC でないことを検出すると Angel SVC ハンドラを呼び出すため、Angel SVC ハンドラによってその SVC を処理できます。 6-42 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ プロセッサ例外処理 この方法をいくつものレベルに拡張してハンドラのチェインを作成することができま す。この方法を用いるコードでは、各ハンドラを完全に独立させることが可能ですが、 1 つのハンドラを使用するコードに比べると効率が悪く、必要以上にハンドラのチェイ ンを伸ばすことによって効率は低下します。 「C 言語からのハンドラのインストール」 (P. 6-15)で示したルーチンは両方とも、ベク タの古い内容を返します。この値をデコードすることによって以下を取得できます。 分岐命令のオフセット このオフセットを使用して元のハンドラの位置を計算し、新しい分岐命 令を作成してメモリ内の適切な位置にストアできます。代用ハンドラが 例外処理に失敗すると、作成された分岐命令に分岐し、その命令が元の ハンドラに分岐します。 元のハンドラのアドレスが格納される位置 アプリケーションハンドラは例外処理に失敗すると、その位置からプロ グラムカウンタをロードする必要があります。 デバッグモニタまたは RTOS ハンドラ上の情報が利用できるので、ほとんどの場合で このような計算は必要ありません。計算が必要な場合は、次のハンドラ内でチェイン する必要のある命令をアプリケーションにハードコーディングできます。このハンド ラの最後のセクションで、例外の原因が処理されたことをチェックする必要がありま す。処理が完了していれば、ハンドラはアプリケーションに戻ることができます。処 理されていなければ、チェイン内の次のハンドラを呼び出す必要があります。 注 デバッグモニタハンドラの前のハンドラにチェインする場合、モニタをシステムから 外すときはこのチェインを外し、アプリケーションハンドラを直接インストールする 必要があります。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 6-43 プロセッサ例外処理 6.13 システムモード ARM アーキテクチャでは、15 本の汎用レジスタ、プログラムカウンタ、および CPSR を使用するユーザモードを定義しています。このモードの他にもいくつかの特権プロ セッサモードがあり、それぞれの特権モードで SPSR と、15 本のユーザモード汎用レジ スタの一部に置き換わるレジスタを使用します。 注 このセクションの内容は、ARMv4、ARMv4T、およびそれ以降のアーキテクチャを実 装するプロセッサのみに適用されます。 プロセッサ例外が発生すると、現在のプログラムカウンタがその例外モードのリンク レジスタにコピーされ、CPSR がその例外モードの SPSR にコピーされます。これにより CPSR が例外に応じて変更され、プログラムカウンタには例外ハンドラを起動するため の、例外によって決まるアドレスがセットされます。 ARM サブルーチン呼び出し命令(BL)が、プログラムカウンタの変更前に復帰アドレ スを r14 にコピーするため、このサブルーチンの復帰命令は r14 を pc に移動します (MOV pc,lr)。 これらの動作により、例外を処理する ARM モードでサブルーチンが呼び出される場合 に、サブルーチン復帰アドレスが例外復帰アドレスで上書きされる結果、同じタイプ の別の例外は発生しません。 以前のバージョンの ARM アーキテクチャでは、この問題は例外コード内にサブルー チンコールを含めないように注意するか、特権モードからユーザモードに変更するこ とによって解決されていました。最初の解決方法は制約があまりにも大きく、また 2 番目の方法ではタスクを正しく実行するために必要な特権アクセスを割り当てられま せん。 ARMv4 以上では、この問題を解決するためにシステムモードと呼ばれるプロセッサ モードが追加されています。システムモードは、ユーザモードレジスタを共有する特 権プロセッサモードです。特権モードのタスクはこのモードで実行することが可能で あり、例外によるリンクレジスタの上書きを防ぐことができます。 注 例外からシステムモードに入ることはできません。例外ハンドラはシステムモードに 「リエントラント割り込みハ 入るときに CPSR を変更します。このサンプルについては、 ンドラ」(P. 6-31)を参照して下さい。 6-44 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ 第7章 デバッグ通信チャネル 本章では、デバッグ通信チャネル(DCC)について説明します。本章は以下のセクショ ンから構成されています。 • デバッグ通信チャネルについて(P. 7-2) • • • • ARM DUI 0203GJ ターゲットのデータ転送(P. 7-3) デバッグ通信のポーリング(P. 7-4) 割り込み駆動型デバッグ通信(P. 7-8) Thumb 状態からのアクセス(P. 7-9) Copyright © 2002-2006 ARM Limited. All rights reserved. 7-1 デバッグ通信チャネル 7.1 デバッグ通信チャネルについて ARM7TDMI® や ARM9TDMI® などの ARM® コアに組み込まれている EmbeddedICE® の ロジックには、デバッグ通信チャネルが実装されています。デバッグ通信チャネルに より、JTAG ポートと Multi-ICE® などのプロトコルコンバータを使用して、プログラム の流れを停止させたりデバッグ状態に入ったりすることなく、ターゲットとホストデ バッガとの間でデータの受け渡しを行うことができます。本章では、ターゲット上で 実行されるプログラムとホストデバッガから DCC にアクセスする方法について説明 します。 7.1.1 セミホスティング $semihosting_enabled=2 を指定して Multi-ICE を使用すると、セミホスティングにデ バッグ通信チャネルを使用できます。詳細については、Multi-ICE ユーザガイド を参照 して下さい。 7-2 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ デバッグ通信チャネル 7.2 ターゲットのデータ転送 DCC には、ARM 命令の MCR と MRC を使用して、ARM コア上のコプロセッサ 14 として ターゲットからアクセスします。 データ転送には以下の 2 つのレジスタが使用されます。 通信データ読み出しレジスタ デバッガからのデータの受信に使用される 32 ビット幅のレジスタです。 以下の命令では、この読み出しレジスタの値を Rd に返します。 MRC p14, 0, Rd, c1, c0 通信データ書き込みレジスタ デバッガへのデータの送信に使用される 32 ビット幅のレジスタです。以 下の命令では、この書き込みレジスタに Rn の値を書き込みます。 MCR p14, 0, Rn, c1, c0 注 ARM10 および ARM11 コアの DCC レジスタへのアクセスについては、各コアのテクニ カルリファレンスマニュアルを参照して下さい。ARM9 以降のプロセッサでは、使用 される命令、ステータスビットの位置、およびステータスビットの解釈方法が異なり ます。 ARM DUI 0203GJ Copyright © 2002-2006 ARM Limited. All rights reserved. 7-3 デバッグ通信チャネル 7.3 デバッグ通信のポーリング DCC では、通信データ読み出しレジスタと通信データ書き込みレジスタに加え、通信 データ制御レジスタが使用されます。このセクションは以下のサブセクションから構 成されています。 • 通信データ制御レジスタ • ターゲットからデバッガへの通信(P. 7-5) • 7.3.1 デバッガからターゲットへの通信(P. 7-6) 通信データ制御レジスタ 以下の命令では、この制御レジスタの値を Rd に返します。 MRC p14, 0, Rd, c0, c0 この制御レジスタ内の 2 つのビットによって、ターゲットとホストデバッガとの間の 同期ハンドシェークが制御されます。 ビット 1 (W ビット) 通信データ書き込みレジスタが(ターゲット側から見て) 空いているかどうかを示します。 W=0 ターゲットアプリケーションが新しいデータを 書き込むことができます。 W=1 ホストデバッガが書き込みレジスタの新しい データをスキャンアウトできます。 ビット 0 (R ビット) 通信データ読み出しレジスタに(ターゲット側から見て) 新しいデータがあるかどうかを示します。 R=1 ターゲットアプリケーションが新しいデータを 読み出すことができます。 R=0 ホストデバッガが新しいデータを読み出しレジ スタにスキャンインできます。 注 コプロセッサ 14 はデバッガにとって何の意味も持たないため、デバッガがコプロセッ サ 14 を使用してデバッグ通信チャネルに直接アクセスすることはできません。その代 わり、デバッガはスキャンチェインを使用することによって、DCC レジスタとの間で 読み出しまたは書き込みを行うことができます。DCC のデータレジスタと制御レジス タは、EmbeddedICE ロジック内のアドレスにマップされます。EmbeddedICE ロジック レジスタの表示方法については、デバッガとデバッグターゲットのマニュアルを参照 して下さい。 7-4 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ デバッグ通信チャネル 7.3.2 ターゲットからデバッガへの通信 以下は、ARM コアで実行されているアプリケーションからホストで実行されているデ バッガへの通信を行うときのイベントシーケンスです。 1. ターゲットアプリケーションが、DCC 書き込みレジスタが空いているかどうか を確認します。この操作は、MRC 命令を使用してデバッグ通信チャネル制御レジ スタを読み出し、W ビットがクリアされているかどうかを確認することによって 行われます。 2. W ビットがクリアされている場合、通信データ書き込みレジスタはクリアされて いるので、アプリケーションはコプロセッサ 14 への MCR 命令を使用してこのレ ジスタにワードを書き込みます。このレジスタへの書き込みによって、W ビット が自動的に設定されます。W ビットが設定されている場合は、デバッガによって 通信データ書き込みレジスタがクリアされていません。さらにワードを送信する 必要がある場合、アプリケーションでは、W ビットがクリアされるまでこのビッ トをポーリングする必要があります。 3. デバッガが、スキャンチェイン 2 を経由して通信データ制御レジスタをポーリン グします。W ビットが設定されていることを検出した場合、デバッガは DCC デー タレジスタを読み出して、アプリケーションから送信されたメッセージを読み出 すことができます。このデータ読み出し処理によって、通信データ制御レジスタ 内の W ビットが自動的にクリアされます。 例 7-1 はこれを実装するコードを示しています。このサンプルコードは、主なサンプル ディレクトリの ...\dcc\outchan.s に収録されています。 例 7-1 AREA ENTRY MOV ADR pollout MRC TST BNE write LDR OutChannel, CODE, READONLY r1,#3 r2, outdata p14,0,r0,c0,c0 ; Read control register r0, #2 pollout ; if W set, register still full r3,[r2],#4 ; ; p14,0,r3,c1,c0 ; r1,r1,#1 ; pollout ; r0, #0x18 ; r1, =0x20026 ; 0x123456 ; MCR SUBS BNE MOV LDR SVC outdata DCB "Hello there!" END ARM DUI 0203GJ ; Number of words to send ; Address of data to send Read word from outdata into r3 and update the pointer Write word from r3 Update counter Loop if more words to be written Angel_SWIreason_ReportException ADP_Stopped_ApplicationExit ARM semihosting (formerly SWI) Copyright © 2002-2006 ARM Limited. All rights reserved. 7-5 デバッグ通信チャネル サンプルを実行するには 1. 以下のコマンドを入力し、outchan.s をアセンブルします。 armasm --debug outchan.s 2. 以下のコマンドを入力し、出力オブジェクトをリンクします。 armlink outchan.o -o outchan.axf このリンク手順で、実行可能ファイル outchan.axf が作成されます。 3. 7.3.3 イメージをロードして実行します。詳細については、デバッガのマニュアルを参 照して下さい。 デバッガからターゲットへの通信 以下は、ホストで実行されているデバッガからコアで実行されているアプリケーショ ンにメッセージを転送するときのイベントシーケンスです。 1. デバッガが通信データ制御レジスタの R ビットをポーリングします。R ビットが クリアされている場合は、通信データ読み出しレジスタがクリアされているの で、ターゲットアプリケーションが読み出すデータをこのレジスタに書き込むこ とができます。 2. デバッガがスキャンチェイン 2 を経由して、通信データ読み出しレジスタにデー タをスキャンインします。これによって通信データ制御レジスタ内の R ビットが 自動的に設定されます。 3. ターゲットアプリケーションが通信データ制御レジスタ内の R ビットをポーリ ングします。このビットが設定されている場合は、アプリケーションがコプロ セッサ 14 からの読み出しを行う MRC 命令を使用して読み出すことができるデー タが、通信データ読み出しレジスタ内に存在します。R ビットはこの読み出し命 令によってクリアされます。 例 7-2(7-7 ページ)のターゲットアプリケーションコードは、このシーケンスを示して います。このサンプルコードは、主なサンプルディレクトリの ...\dcc\inchan.s に収 録されています。 サンプルを実行するには 1. 2. And goodbye! などの文字列を含む入力ファイルをホスト上で作成します。 以下のコマンドを入力し、inchan.s をアセンブルします。 armasm --debug inchan.s 3. 以下のコマンドを入力し、出力オブジェクトをリンクします。 armlink inchan.o -o inchan.axf このリンク手順で、実行可能ファイル inchan.axf が作成されます。 7-6 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ デバッグ通信チャネル 4. イメージをロードして実行します。詳細については、デバッガのマニュアルを参 照して下さい。 例 7-2 AREA ENTRY MOV LDR pollin MRC TST BEQ read MRC STR SUBS BNE MOV LDR SVC AREA indata DCB END ARM DUI 0203GJ InChannel, CODE, READONLY r1,#3 r2, =indata ; Number of words to read ; Address to store data read p14,0,r0,c0,c0 ; Read control register r0, #1 pollin ; If R bit clear then loop p14,0,r3,c1,c0 ; read word into r3 r3,[r2],#4 ; Store to memory and ; update pointer r1,r1,#1 ; Update counter pollin ; Loop if more words to read r0, #0x18 ; Angel_SWIreason_ReportException r1, =0x20026 ; ADP_Stopped_ApplicationExit 0x123456 ; ARM semihosting (formerly SWI) Storage, DATA, READWRITE "Duffmessage#" Copyright © 2002-2006 ARM Limited. All rights reserved. 7-7 デバッグ通信チャネル 7.4 割り込み駆動型デバッグ通信 「デバッグ通信のポーリング」 (P. 7-4)で示したサンプルでは、DCC のポーリングが行 われます。これらのサンプルは、COMMRX 信号と COMMTX 信号を Embedded ICE ロジック から割り込みコントローラに接続することにより、割り込み駆動型のサンプルに変換 できます。 割り込み駆動型のサンプルに変換後、例 7-1(7-5 ページ)と例 7-2(7-7 ページ)の読み 出しコードと書き込みコードを割り込みハンドラ内に移動できます。 「割り込みハンドラ」 (P. 6-28)を参照 割り込みハンドラの書き込みの詳細については、 して下さい。 7-8 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ デバッグ通信チャネル 7.5 Thumb 状態からのアクセス Thumb® 命令セットにはコプロセッサ命令が含まれていないため、コアが Thumb 状態 の間はデバッグ通信チャネルを使用できません。 この問題の解決策としては以下の 3 つの方法が考えられます。 ARM DUI 0203GJ • 各ポーリングルーチンを SVC ハンドラ内に記述し、ARM 状態または Thumb 状 態のときに実行する。SVC ハンドラに入るとコアがすぐに ARM 状態になるた め、コプロセッサ命令を使用できるようになります。SVC の詳細については、 「第 6 章 プロセッサ例外処理」を参照して下さい。 • Thumb コードで、ポーリングを実装する ARM サブルーチンへのインターワーク 呼び出しを発生させる。ARM コードと Thumb コードの混用の詳細については、 「第 4 章 ARM と Thumb のインターワーク」を参照して下さい。 • ポーリング通信ではなく、割り込み駆動型通信を使用する。割り込みハンドラを ARM 命令で記述することにより、コプロセッサ命令に直接アクセスできます。 Copyright © 2002-2006 ARM Limited. All rights reserved. 7-9 デバッグ通信チャネル 7-10 Copyright © 2002-2006 ARM Limited. All rights reserved. ARM DUI 0203GJ