関数を RAM に配置するテクニック

関数を RAM に配置するテクニック
弊社の Embedded Workbench for ARM(以後 EWARM)で組込みソフトウェアを開発するときに、関数を
RAM に配置したい場合にどうするのか?と言った問い合わせがかなりあります。通常、弊社ではこう
したプログラムをどう作るのか?というプログラミング自体についてお答えしておりませんが、今回こ
1
の記事で一般的なやり方をご紹介いたします。ここで説明に利用したプロジェクトは からダウンロー
ドできますので、必要があればご利用ください。ダウンロードしたファイルを解凍すると以下のフォル
ダ構成になっており、RAMalloc1~5 以下に EWARM 用のプロジェクトがあります。
RAMfuncProj
RAMalloc1
RAMalloc2
RAMalloc3
RAMalloc4
RAMalloc5
ここでのサンプルは、CortexM3 がターゲット MCU で、メモリマップは ROM が 0x0000-0000~
0x0007FFFF で RAM が 0x2000-0000~0x2000FFFF、スタックサイズは 0x400 で、ベクタテーブルは
0x0000-0000 に配置という前提で説明を実施させていただきます。
関数を RAM に配置する
関数を RAM に配置する場合には大きく分けて2つのパターンがあります。
・ICE 接続(デバッグ)時に RAM に関数/データを配置
・ROM(もしくは通信, SD カード, FLASH メモリなど)に置いたデータから RAM に関数をコピーし
てから実行
ICE 接続(デバッグ)時に RAM に関数/データを配置(フォルダ:RAMalloc1)
ICE から RAM に直接ダウンロードする場合には、リンカ設定ファイルで関数を RAM に配置します。
プログラム上での指定は特別には不要です。
1
http://www.iarsys.co.jp/archive/sampleproject/arm/RAMfuncProj20140227.zip
Page 2
volatile int x=0;
void func1( ){
x ++;
}
int main()
{
while (1) {
func1();
}
return 0;
}
define memory mem with size = 4G;
define region RAM1_region = mem:[from 0x20000000 to 0x2000FFFF ];
define region RAM2_region = mem:[from 0x20010000 to 0x2001FFFF ];
define block CSTACK with alignment = 8, size = 0x400 { };
place at address mem:0x00000000 { readonly section .intvec };
place in RAM1_region { readonly };
place in RAM2_region { readwrite, block CSTACK };
EWARM で使用するリンカ設定ファイルの詳細については2をご覧ください。ここでポイントは最後の 2 行
にある place in です。属性 readonly はプログラムコードやソフトウェア中の定数などを指し、
readwrite は実行時に変更する変数などを指します。 “block CSTACK”はスタックもこの領域に配置
するように指定しています。
この結果はマップファイルで確認出来ます。主だった変数・関数を抽出したものが以下になります。
先頭にアンダーバーが付いているものはコンパイラ側が作成した関数や変数になります。これで、すべ
ての関数や変数が RAM 領域に入っていることが確認出来ます。
Entry
----・・・
__iar_data_init3
__iar_program_start
__iar_zero_init3
__low_level_init
__vector_table
_call_main
_exit
_main
func1
main
Address Size Type
------- ---- ----
Object
------
0x20000001 0x28 Code Gb data_init.o [4]
0x200000bd
Code Gb cstartup_M.o [4]
0x20000029 0x22 Code Gb zero_init3.o [4]
0x2000007b 0x4 Code Gb low_level_init.o [3]
0x00000000
Data Gb vector_table_M.o [4]
0x2000006d
Code Gb cmain.o [4]
0x2000009d
Code Gb cexit.o [4]
0x20000077
Code Gb cmain.o [4]
0x20000081 0xc Code Gb main.o [1]
0x20000091 0x8 Code Gb main.o [1]
x
0x20010000
0x4 Data Gb main.o [1]
ここで EWARM のマップファイルは THUMB 命令の関数の場合は最下位ビットに 1 が立った状態のアドレス
で表示されています。実際に配置されるアドレスは偶数なのでご注意ください。
2
http://www.iar.com/Global/KK_pages/UserGuide/EW_IlinkGuide.JPN.pdf
Page 3
ROM に置いたデータから RAM に関数をコピーしてから実行
実機では RAM に配置した関数を実行するには、初期化で ROM(もしくは通信や SD カード、FLASH
メモリなど)から RAM に必要な情報をコピーしてから、RAM の関数を呼び出す必要があります。
これについては、関数の種類と記述方法により 3 つに分けて説明をします。
1.RAM に配置する関数を個別に指定する方法
2.RAM に配置する関数をファイル単位で指定する方法
3.RAM に配置する関数を全体で指定する方法
RAM に配置する関数を個別に指定する方法(フォルダ:RAMalloc2)
特定の関数だけを RAM 上に配置したい場合には、その関数に __ramfunc を付ける方法が利用できます。
その記述が以下のようになります__ramfunc は EWARM が用意した拡張キーワードになっています。
volatile int x=0;
__ramfunc
void func1( ){
x ++;
}
int main()
{
while (1) {
func1();
}
return 0;
}
define memory mem with size = 4G;
define region ROM_region = mem:[from 0x00000000 to 0x0007FFFF ];
define region RAM_region = mem:[from 0x20000000 to 0x2001FFFF ];
define block CSTACK with alignment = 8, size = 0x400 { };
place at address mem:0x00000000 { readonly section .intvec };
initialize by copy { rw };
place in ROM_region { readonly };
place in RAM_region { readwrite, block CSTACK };
Page 4
マップファイルを見ると、__ramfunc 宣言をした関数 func1 と変数 x が RAM に配置され、その他の関数
は ROM 領域に配置されています。
Entry
----・・・
__exit
__iar_copy_init3
__iar_data_init3
__iar_program_start
__low_level_init
__vector_table
_call_main
_exit
_main
func1
main
Address Size Type
------- ---- ---0x000000ed
0x00000041
0x00000071
0x00000101
0x000000d1
0x00000000
0x000000b9
0x000000e1
0x000000c3
0x20000001
0x000000d5
x
0x20000010
Object
------
0x14 Code Gb exit.o [5]
0x2e Code Gb copy_init3.o [4]
0x28 Code Gb data_init.o [4]
Code Gb cstartup_M.o [4]
0x4 Code Gb low_level_init.o [3]
Data Gb vector_table_M.o [4]
Code Gb cmain.o [4]
Code Gb cexit.o [4]
Code Gb cmain.o [4]
0x10 Code Gb main.o [1]
0x8 Code Gb main.o [1]
0x4 Data Gb main.o [1]
RAM に配置する関数をファイル単位で指定する方法(フォルダ:RAMalloc3)
ここでは、このファイルが main.c に含まれているとします。このファイルをコンパイルした結果が
main.o に格納されています。このファイルの関数を RAM 領域に配置することを考えます。
volatile int x=0;
const int y = 1000;
void func1( ){
x +=y ;
}
int main()
{
while (1) {
func1();
}
return 0;
}
この場合には、リンカ設定ファイルで initialize by copy というキーワードを利用します。初期化付
き変数の初期化を実行する場合に利用することが多いですが、関数を ROM から RAM にコピーする初期化
する際にも利用することが出来ます。その時の記述が以下のになります。
rw というキーワードが Read および Write のあるオブジェクトの初期値を RAM 側にコピーする指定にな
ります。
ro というキーワードで Read Only のオブジェクトのコードにあたる部分を RAM にコピーする指定にな
ります。
Page 5
define memory mem with size = 4G;
define region ROM_region = mem:[from 0x00000000 to 0x0007FFFF ];
define region RAM_region = mem:[from 0x20000000 to 0x2001FFFF ];
define block CSTACK with alignment = 8, size = 0x400 { };
place at address mem:0x00000000 { readonly section .intvec };
initialize by copy { rw, ro code object main.o};
place in ROM_region { readonly };
place in RAM_region { readwrite, block CSTACK };
このケースでは、関数 func1,main と変数 x が RAM 領域に配置され、定数(変数)y が ROM 領域に配置
されます。
Entry
----・・・
_call_main
_exit
_main
func1
main
x
Address Size Type
------- ---- ---0x000000c1
Code Gb
0x000000d9
Code Gb
0x000000cb
Code Gb
0x20000001 0x10 Code Gb
0x20000019 0x8 Code Gb
0x20000020 0x4 Data Gb
y
0x00000104
0x4 Data
Object
-----cmain.o [4]
cexit.o [4]
cmain.o [4]
main.o [1]
main.o [1]
main.o [1]
Gb main.o [1]
ここで関数だけでなく、定数となる変数 y も RAM に配置したい場合には、「ro code object main.o」
を「ro object main.o」とすることで対応できます。
ユーザ関数をすべて RAM に配置する方法(フォルダ:RAMalloc4)
先ほどの main.c を使って、リンカファイルを設定のみを変更することでユーザ関数をすべて RAM に配
置することが出来ます。ただし、この設定では割込みのベクタテーブルは RAM 側に作成したいため
except を使って割込みベクタの領域を initialize by copy から外しています。また、この記述ではユ
ーザ関数以外でもコンパイラが用意した関数のいくつかも RAM 側に配置されています。本当にユーザの
関数のみを指定したい場合には、前の節で説明したファイル名を指定するなどの手法を利用する必要が
あります。
define memory mem with size = 4G;
define region ROM_region = mem:[from 0x00000000 to 0x0007FFFF ];
define region RAM_region = mem:[from 0x20000000 to 0x2001FFFF ];
define block CSTACK with alignment = 8, size = 0x400 { };
place at address mem:0x00000000 { readonly section .intvec };
initialize by copy { rw, ro code } except { section .intvec };
place in ROM_region { readonly };
place in RAM_region { readwrite, block CSTACK };
Page 6
注意点
RAM 上に関数を作成し動作させるにはいくつか条件があります。
割込みベクタもリセット時に正しく構築されていないといけません。
初期化コードは ROM 上に配置する必要があります。
そのため、リセットから初期化されるまでに必要とされる関数が ROM 上で実行されるようにプログラム
を作成してください。
ライブラリ関数の RAM 化(フォルダ:RAMalloc5)
今回は、以下のコードで使用している C の標準関数の strcpy を RAM に配置を実施します。
#include <stdio.h>
char buf[128];
void func1( char *s ){
strcpy(buf,s);
}
int main()
{
while (1) {
func1("aaaaa");
}
return 0;
}
まず、コンパイルしてマップファイルを確認します。strcpy に関係する情報を探します。実際に実装
している名前は、__iar_unaligned_strcpy であり、そのファイルは strcpy_unaligned.o にある(正式
には[4]で指定されているライブラリファイルに格納されています)。
__iar_unaligned_strcpy
・・・
0x20000001
Code Gb
strcpy_unaligned.o [4]
[4] = rt7M_tl.a
リンカ設定ファイルで、strcpy_unaligned.o の関数を RAM に配置するのは、以下の記述となります。
define memory mem with size = 4G;
define region ROM_region
= mem:[from 0x00000000 to 0x0007FFFF ];
define region RAM_region = mem:[from 0x20000000 to 0x2001FFFF ];
define block CSTACK
with alignment = 8, size = 0x400 { };
place at address mem:0x00000000 { readonly section .intvec };
initialize by copy { rw, ro code object strcpy_unaligned.o
place in ROM_region { readonly };
place in RAM_region { readwrite,
block CSTACK };
};
Page 7
これによりマップを見ると以下のように strcpy が RAM 上に配置されていることが解ります。
Entry
----・・・
__iar_unaligned_strcpy
・・・
_main
buf
exit
func1
main
Address Size Type
------- ---- ---0x20000001
Code
Object
-----Gb strcpy_unaligned.o [4]
0x0000011b
Code Gb
0x20000044 0x80 Data Gb
0x00000123 0x4 Code Gb
0x000000e5 0xc Code Gb
0x000000f1
cmain.o [4]
main.o [1]
exit.o [3]
main.o [1]
0xa Code Gb main.o [1]
結論
EWARM の拡張キーワード__ramfunc はリンカ設定ファイルにて関数を RAM に配置することが出来ま
す。こうした機能を活用することで、必要に応じてソフトウェアの実装を自由に変えることが出来ます。
3
3
本資料は EWARM 7.10 でプロジェクトを実施しております。