プログラムを RAM で実行するためのXLINKの設定
テクニカル・ノート 80460
アーキテクチャ:
All
コンポーネント:
linker
更新日:
2018/08/25 14:12
はじめに
本テキストでは、実行対象のコンテンツをROMからRAMにコピーする方法について説明します。一部のIARコンパイラでは、そうした動作がキーワードでサポートされています。本テキストでは、そうしたサポートがないことを想定し、より一般的な手法について説明します。最も一般的なケースは、使用するRAMエリアにROMからコンテンツをコピーするというものですが、ここでは、より一般的なメカニズム、すなわち、ある場所にコンテンツをリンクし、別の場所にバイトデータを配置する方法について説明します。
基本
コードをRAMにコピーするには、以下のステップを実行します。
- オリジナルのセグメントにコードを配置します。
- -Qリンカオプションを使用して、イニシャライザセグメントを作成します。
- オリジナルのセグメントをRAMに配置します。
- イニシャライザセグメントをROMに配置します。
- イニシャライザセグメントから対象セグメントにバイトデータをコピーします。
- アプリケーションをデバッグします。
1.コンテンツをセグメントに配置する
ROMからRAMにコピーしたいコンテンツを、オリジナルのセグメントに配置します。これを実現する手段として、アセンブラを使用する方法、コンパイラを使用する方法、イメージ入力を使用する方法が用意されています。
- アセンブラを使用する場合。例:
アセンブラファイルでは、どのセグメントを使用して構築するかをRSEGディレクティブで制御します。
RSEG RAMCODE:CODE:NOROOT(2)
これにより、セグメントタイプがCODEであるセグメントがRAMCODEというセグメント内に作成され、do_stuffというエントリが定義されます。これはROOTではありません。これは4バイトアラインメントです。
do_stuff:
.
.
.
注: 通常、CODEという名前のセグメントは、CODEというセグメントタイプを持ちますが、セグメントタイプとしてのCODEは、セグメント名としてのCODEとは異なります。
- コンパイラを使用する場合:
IARのコンパイラによっては、モジュール全体に対して同様の機能を提供するコマンドラインオプションが用意されているものがあります(詳細については、各コンパイラのマニュアルを参照してください)。最新のIARコンパイラでは、セグメントを制御するためのpragma (#pragma location)が用意されています。これは、その後ろにあるステートメントにのみ作用します。
#pragma location="SPECIAL_CODE"
int do_strange_things(int a, int b)
{
.
.
.
}この場合、do_strange_things関数がSPECIAL_CODEセグメントに配置されます。これは、そのファイルにおける前後の宣言には影響を与えません。
#pragma locationを多用することはお勧めしません。これらの文字列を何百あるいは何千も記述すると、コードが汚くなってしまい、場所を変更したい場合に面倒なことになる可能性があります。通常、#pragmaとマクロは相性がよくありませんが、IARには、そのような用途で非常に役に立つ拡張キーワード_Pragmaが用意されています。
-
#define RAMCODE(x) _Pragma("location=\"RAMCODE\"") x
このマクロによって、RAMCODEセグメントに配置される引数が宣言されます。例:
-
RAMCODE(int do_strange_things(int a, int b))
{
.
.
.
}当然ながら、これはコード以外にも使用できます。
-
#define CONST(x) _Pragma("location=\"CONST_SEG\"") x
このマクロによって、CONST_SEGセグメントに配置される引数が宣言されます。例:
-
CONST(const int a = 4711;)
CONST(const int arr[3] = { 45, 34, -2};) - --image_inputを使用する場合:
リンカオプション--image_inputを使用すると、あらゆる種類のファイルをバイト単位でインポートでき、それらをセグメントに配置できます。
-
--image_input=image1.raw,image1_start,IMAGE_SEG,0
これにより、image1.rawというファイルのコンテンツがIMAGE_SEGセグメントにインポートされます。このセグメントによって、image1_startというシンボルが定義されます。このセグメントは、境界にアラインメントされていません。なお、--image_inputを使用してコードをインポートする場合、そのイメージを生成したリンクによって配置されたアドレスと同じアドレスにそのコードを配置する必要があります。位置に依存しないコードなどのようにいくつか例外がありますが、そのような場合を除けば、インポートされたコードが元々リンクされていた場所でしか実行されないように注意を払う必要があります。
2.イニシャライザセグメントを作成する
-Qリンカオプションを使用して、イニシャライザセグメントを作成します。-Qオプションを使用すると、ある場所(今回の場合はRAM)にコンテンツを配置し、別の場所(今回の場合はROM)にバイトデータを配置することができます。
-QRAMCODE=RAMCODE_ID
これにより、RAMCODEセグメントのイニシャライザセグメントであるRAMCODE_IDが作成されます。
RAMCODEには、ラベルとデバッグ情報が含まれています。実際にコードを実行する際、この場所にコードが配置されます。RAMCODEが参照されると、常にこの場所が参照され、RAMCODEが何かを参照すると、常にこの場所から参照が行われます。
RAMCODE_ID (これは、RAMCODEと全く同じサイズを持ちます)には、RAMCODEの実際のバイトデータのみ含まれます。ラベルやデバッグ情報は含まれません。このバイトデータは、RAMCODEにコピーするためだけに使用されます。このバイトデータを他の用途で使用したり(コピー前の現在の場所でコードを実行するなど)、他のアドレスにコピーしたりすると、予期せぬ動作が引き起こされます。
3.オリジナルのセグメントをRAMに配置する
RAMCODEは、コピーされて初期化されたセグメントです。そのため、セグメント内の各部の順序がそのまま維持されるように、-Zセグメント配置コマンドを使用してこのセグメントを配置する必要があります。
-Z(DATA)RAMCODE=RAM_START-RAM_END
セグメント配置コマンドは、.xclファイルで記述された順に処理されます。また、各セグメントは、先に配置されたセグメントを考慮して配置されます。場合によっては、ユーザがセグメントを移動して、特定のセグメントの前(または後)にセグメントが配置されるようにしなければなりません。
4.イニシャライザセグメントをROMに配置する
RAMCODE_IDは、コピーされたイニシャライザセグメントです。そのため、セグメント内の各部の順序がそのまま維持されるように、-Zセグメント配置コマンドを使用してこのセグメントを配置する必要があります。
-Z(CONST)RAMCODE_ID=ROM_START-ROM_END
5.イニシャライザセグメントから対象セグメントにバイトデータをコピーします。
ツールでは、RAMCODE_IDからRAMCODEへのバイトデータのコピーは実際には行われません。実行時にデータがコピーされるように、プログラマがプログラムを作成する必要があります。起動コードを修正して、実行箇所がmainに到達する前に自動的にこのコピーが行われるようにできますが、RAMCODEが使用される前であればバイトデータがコピーされるタイミングはいつであっても構いません。
実際のコピーは比較的簡単です。memcpyを呼び出せば十分です。ただし、IARの拡張機能を使用してセグメントのアドレスを取得する部分については注意が必要です(あるいは、アセンブラで書かれた関数を呼び出しても構いません)。このコードは、アドレス空間を1つだけ持つプロセッサ上でテストされています。ROMからRAMへのコピーに関して特殊な要件があるプロセッサの場合(複数のアドレス空間、特殊な命令、特殊なアドレス要件など)、コピーのメカニズムにおいてそれらの要件をすべて満たす必要があります。
/* copy all bytes between s (inclusive) and e (exclusive) to d */
void activate(void * s, void * e, void * d)
{
size_t size = (size_t)e - (size_t)s;
memcpy(d,s,size);
}
/* copy the bytes from RAMCODE_ID to RAMCODE */
void activate_RAMCODE(void)
{
#pragma segment="RAMCODE"
#pragma segment="RAMCODE_ID"
activate(__sfb("RAMCODE_ID"), __sfe("RAMCODE_ID"), __sfb("RAMCODE"));
}
#pragma segment=""は、その名前のセグメントが存在することをコンパイラに通知します。これは、そのセグメントで__sfbと__sfeが使えるようになる点を除いては、何の影響も及ぼしません。
IARの拡張機能である__sfb (segment frame begin: セグメントフレーム開始位置)は、セグメントの開始アドレスを返します。このとき、先行する#pragmaによってそのセグメントが指定されていなければなりません。このセグメントが0x500~0x52Fに配置されている場合、__sfbによって0x500が返されます。
__sfbと対になっている__sfe (segment frame end: セグメントフレーム終了位置)は、セグメントの後ろにある最初の空きバイトのアドレスを返します。このセグメントが0x500~0x52Fに配置されている場合、__sfeによって0x530が返されます。
activate_RAMCODEを呼び出すと、コードを使用する準備がすべて整います。
6.デバッグする
今回の例では、コピーされて初期化されたコードは、バイトデータがコピーされると完全にデバッグ可能になります。
全ての製品名は、それぞれの所有者の商標または登録商標です。