...

PDF version - ARM Information Center

by user

on
Category: Documents
58

views

Report

Comments

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
Fly UP