「keilのフリー版は32KB制限がありますよね」という記事を書いていたのだが、読んでくださった方々から情報をいただいた。
ありがたやありがたや。
私があんまり社交的じゃないためか、このブログはマイナーなんですよ。
ときどき、閉鎖して本業に集中しようかとも思うけど、見てくださる方がいるのであれば、もうちょっと続けようかという感じでやっとります。
さて、本題。
hiro99ma blog: [ble]BLEモジュールを使った場合のがんばりどころは?
コメントをいただいた中で、誰もが「32KBを超えても大丈夫だった」ということだった(2人しかコメントがないが、これを「誰もが」と書くことで、すごく多くの人がコメントをくれたようなイメージを与えるテクニックだ)。
では、試してみなくてはならぬ。
まず、32KB、がどこを指しているのか。
ARM KEIL組み込み開発システム 統合開発環境 <MDK-ARM 正規代理店>|横河ディジタルコンピュータ株式会社
こちらの表を見ると、デバッガ、シミュレータ、そしてコンパイラが32KBとのこと。
しかし、コンパイルのどこら辺が32KBかは、見てもわからない。
セカンド・オピニオン (412) MCUで遊ぼう Part2 (3) | マイナビニュース
これを読むと、コード/データが32KBとある。
プログラムは基本的にROMとRAMの2つに分かれるが、ROMもデータとプログラムに分かれる。
RAMも、初期値があるものと、ないものに分かれる。
初期値があるものは、その初期値自体はROMに置かれることになる。じゃないと、初期値を保持しておくところがないからね。
C言語では、初期値を持つ変数というのは、staticなものしかない。
そのstaticな変数の中でも、初期値を代入しているものは「初期値あり」だが、代入していないものは「ゼロ」になる。
だから、staticな変数であっても、初期値の有無でROMのサイズが変わってくる。
なんでこんなことをごにゃごにゃ書いたかというと、32KBのROMをどうやったら作り出せるか、ということを考えていたからだ。
プログラムを書いて32KB作るのは、けっこう難しい。
できれば、var[32768]みたいな感じで、楽に32KBを作りたい。
でも、staticとかvolatileとかつけても、単に宣言しただけだとRAM領域の話になるので、コードサイズにならない。
初期値がないから、ゼロになるだけだ。
私が見たことのあるコンパイラだと、main()が呼ばれる前にmemset()とか、あるいはアセンブラなどでゼロ埋めするものがほとんどだったが、オプションによってゼロ埋めする処理すら省略するものもあった。
我々が求めているのは、ゼロ以外の初期値を持つ変数だ。
そうなると、初期値の部分がROMに配置されるので、今回のコードサイズ制限を調べるのに役立つ。
#include <nrf51.h>#include <stdint.h>#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16#define FILL1K FILL256,FILL256,FILL256,FILL256static const uint8_t mem[256 * 1024 * 1024] = {FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K,FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K};int main(void){uint8_t jan = mem[256];__wfe();return 0;
}
16byteのFILL16を、16個並べたFILL256を、4つ配置。これでFILL1K。
とりあえずエラーを見てみたいので、これを16個並べたら、16KBだからよさそうだけど、エラーになった。
Build target 'Target 1'
compiling main_32k.c...main_32k.c(15): warning: #177-D: variable "jan" was declared but never referenced
uint8_t jan = mem[256];main_32k.c: 1 warning, 0 errorslinking....\Objects\test.axf: error: L6047U: The size of this image (268436284 bytes) exceeds the maximum allowed for this version of the linkerFinished: 0 information, 0 warning, 0 error and 1 fatal error messages.".\Objects\test.axf" - 1 Error(s), 1 Warning(s).Target not created.
でたー。
これが、maximumなエラーメッセージらしい。
少し満足。
まあ、256 * 1024 * 1024だと、どの環境でもエラーになりそうよねぇ、ということで、32KB制限近くでやってみた。
#include <nrf51.h>#include <stdint.h>#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16#define FILL1K FILL256,FILL256,FILL256,FILL256static const uint8_t mem[31 * 1024] = {
FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K,FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K};int main(void)
{uint8_t jan = mem[256];__wfe();return 0;}
これのビルド結果は、こう。
Rebuild target 'Target 1'compiling main_32k.c...main_32k.c(15): warning: #177-D: variable "jan" was declared but never referenced
uint8_t jan = mem[256];main_32k.c: 1 warning, 0 errorsassembling arm_startup_nrf51.s...compiling system_nrf51.c...linking...Program Size: Code=598 RO-data=31970 RW-data=4 ZI-data=4196FromELF: creating hex file...".\Objects\test.axf" - 0 Error(s), 1 Warning(s).
この、memの31を32にすると、リンクエラーになった。
Rebuild target 'Target 1'compiling main_32k.c...main_32k.c(15): warning: #177-D: variable "jan" was declared but never referenced
uint8_t jan = mem[256];main_32k.c: 1 warning, 0 errorsassembling arm_startup_nrf51.s...compiling system_nrf51.c...linking....\Objects\test.axf: error: L6047U: The size of this image (33596 bytes) exceeds the maximum allowed for this version of the linkerFinished: 0 information, 0 warning, 0 error and 1 fatal error messages.".\Objects\test.axf" - 1 Error(s), 1 Warning(s).Target not created.
こっちが、warningだけだったコード。
#include <nrf51.h>#include <stdint.h>#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16#define FILL1K FILL256,FILL256,FILL256,FILL256static const uint8_t mem[31 * 1024] = {
FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K,FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K};int main(void)
{uint8_t jan = mem[256];__wfe();return 0;}
こっちが、エラーになったコード。
#include <nrf51.h>#include <stdint.h>#define FILL16 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16#define FILL256 FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16,FILL16#define FILL1K FILL256,FILL256,FILL256,FILL256static const uint8_t mem[32 * 1024] = {
FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K,FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K, FILL1K};int main(void)
{uint8_t jan = mem[256];__wfe();return 0;}
違いは、memの添字になっている値が、31か32か、だ。
こうやって見る分には、ちゃんと32KBで制限がかかっていると感じます。
翌日。
飲みながら調べると、なんかいろいろ馬鹿なことやってるなー、と反省(したふり)。
リンクができる境界を調べて、以下のソースになった。
配列の202を203にすると、リンクエラーになる。
static const unsigned char mem[31 * 1024 + 202] = {0};
int main(void)
{return mem[0];}
mapファイルのお尻だけ拾うと、こんな感じ。
Code (inc. data) RO Data RW Data ZI Data Debug594 88 32170 4 4196 7352 Grand Totals594 88 32170 4 4196 7352 ELF Image Totals594 88 32170 4 0 0 ROM Totals==============================================================================Total RO Size (Code + RO Data) 32764 ( 32.00kB)Total RW Size (RW Data + ZI Data) 4200 ( 4.10kB)Total ROM Size (Code + RO Data + RW Data) 32768 ( 32.00kB)
配列の要素数は、1024 * 31 + 202だから、31946。
mapでは32170になってるから、あと224バイトはコード以外のROMデータがあるらしい。
Code (inc. data) RO Data RW Data ZI Data Debug Object Name84 36 192 0 4096 648 arm_startup_nrf51.o12 6 31946 0 0 2055 main_32k.o232 30 0 4 0 4241 system_nrf51.o----------------------------------------------------------------------328 72 32170 4 4096 6944 Object Totals0 0 32 0 0 0 (incl. Generated)0 0 0 0 0 0 (incl. Padding)
incl. Generated、というのが32バイトあるらしい。
ARMのヘルプを見ると、これはリンカが生成したオブジェクトらしい。
ともかく、うちで試した限りでは32KB制限というのは発生していることが確認できた。
{0}なのに初期値ありとみなされるんだ、というのが多少ショックだった。
前見たコンパイラは、{n}のような感じでnに数値を入れるとmemset(0)で勝手に0初期化してたのだ。
組込用のコンパイラだったから、それを正とみなしてたらいかん、という教訓を得た。
境界の実験ありがとうございます。とても勉強になりました。
返信削除やっぱりMAPファイルのTotal ROM Sizeが32KBを越えなければ大丈夫ということですね。
サンプルプログラムをちょこちょこ改造して試しているところですが、20KB位が最大でしたのでそうそう32KB超えることはなさそうです。
20KBくらいなら、まだ余裕がありますね。
削除BLEのスタックが別になっているので、広々しててありがたいです。
(CC2540とかは一緒にリンクするので制限有りコンパイラでは使えないという記述を見たことがあったので)
詳しく調査していただき、公開していただいてどうもありがとうございました。
返信削除日本語の情報が少ない中、とても貴重な Blog だと思います。
愛読していますので、今後も頑張ってください。
ありがとうございます。
削除好き勝手に調べているだけのブログではありますが、いつか世に羽ばたく日が来る(かも)、という野望を胸に抱きつつ続けていきます。