フラッシュへの書き込みプログラムを先に作ろうと思ったけど、まず、動かしてみない
事には始まらない~
gcc はビルドしてあるので、スタートアップルーチンを作って起動するまでの道を作る。
(1)まず、リンカースクリプトを精査する。
/usr/local/rl78-elf/rl78-elf/lib/rl78.ld
を雛形にして改造すれば良さそうだ~
※標準のリンカースクリプトは、RL78/G13 (Flash:64K / RAM:4K) 仕様のようだ。
このリンカースクリプトを参考に、4つのデバイス用を作成
※オリジナルでは、0x0000 ~ 0x1FFF までは、開発用のブート領域のようで、使わない
ので、それを無効にした。
.rodata (MAX(__romdatastart + __romdatacopysize, 0x2000)) : {
を
.rodata (MAX(__romdatastart + __romdatacopysize, 0x0000)) : {
追記:
この0x2000は、ミラー領域内で、データフラッシュ領域をバイパスするオフセットで、
重要な事が判明、又、128KB、256KB品では、データフラッシュ領域は、倍なので、
オフセットを0x3000にする必要がある・・
※「ミラー領域」の事が良く判ってなかった・・・
また、ハードウェアーベクターセクションに多少の問題がある。
.vec :
{
*(.vec)
} > VEC
通常、ハードウェアーベクターはソフトウェアーからは参照されないので、何もしないと、
最適化で、省かれてしまう・・・
.vec :
{
KEEP(*(.vec))
} > VEC
そこで、「KEEP」キーワードで囲む必要がある。
※「ivec」(割り込みベクターテーブル)も同じ。
また、各種デバイス用に、ROM、RAM の開始アドレス、長さなど設定して、4つのデバイス用
リンカースクリプトを作成した。
R5F100LCAFB: 32K (0x00000 - 0x07FFF) / 2K (0xFF700 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LEAFB: 64K (0x00000 - 0x0FFFF) / 4K (0xFEF00 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LGAFB: 128K (0x00000 - 0x1FFFF) / 12K (0xFCF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)
R5F100LJAFB: 256K (0x00000 - 0x3FFFF) / 20K (0xFAF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)
RL78/G13リンカースクリプト
※データフラッシュ領域は記述が無いので、何らかの対応を行う必要があると思う。
(2)次にスタートアップルーチン
今までは、独自に、スタックをセットするとか、アセンブラで書いたのだけど、ライブラリー
に含まれる標準のスタートアップオブジェクト「crt0.o」を、objdump でアセンブルソースを
出力して、参考にする方法が確実で簡単な事が判った。
rl78-elf-objdump -h -S crt0.o > crt0.lst
※これなら、まねるだけなので、アセンブラを詳細に理解する必要がほぼ無い。
※最低限の知識は必要だが、手本があれば、非常に簡単だ。
「crt0.o」は、以下の構成のようだ。
・ハードウェアースタックの設定
※スタックの開始アドレスは、リンカースクリプトで指定されたラベルを使う、また、
スタックの深さは、リンカースクリプトで指示する。(通常は、RAM 領域の最後から取る)
・ROM 領域から RAM 領域への転送
※定数などを読み書き可能な変数として設定している場合は、変数はRAM上に配置されるの
で、初期値をコピーしておく必要がある。
・.bss セクションのクリア
・C++ コンストラクター呼び出し
※rl78_run_preinit_array()、rl78_run_init_array()、rl78_run_fini_array()
※C++ では、main が始まる前にコンストラクターを走らせて、初期化しておく必要性がある。
・init 関数呼び出し
・main 関数呼び出し
・exit 関数呼び出し
とりあえず、適等に切り貼りして、「start.s」を作成。
呼び出し部は、「init.c」で実行。
最後に、リセットベクターに、start.sの開始アドレスを指示する必要がある。「vect.c」
const void* vec_[] __attribute__ ((section (".vec"))) = {
start,
};
これで、全て準備が整った、後は、自分のプログラムをコンパイルして、先に作ったプログラムを
リンクするだけで起動するはず。
-nostartfiles -----> 標準のスタートファイルを使わない
-T xxxxx -----> xxxxx のリンカースクリプトを使う
リンカーオプションでは、以上の二つが重要となる。
(3)LED点滅を書いて、実行してみる・・
以上で、main 関数が実行される準備が整った。
今回、LEDを接続するポートとして、P43を使った。
最初、LEDは点灯したままだったが、無効ループが短すぎたようだ、そこで、以下のように無効
ループの回数を増やした。
※「int」型は16ビットなので、「uint32_t」を使った。
int main(int argc, char* argv[])
{
device::PM4.B3 = 0; // output
bool f = false;
while(1) {
for(uint32_t i = 0; i < 100000; ++i) {
asm("nop");
}
device::P4.B3 = f;
f = !f;
}
}
※無効ループ内では、「asm("nop")」を実行する、そうしないと最適化で、ループ自体が無くな
ってしまう。
これで、無事にLEDの点滅まで出来た。
プロジェクト全体のソースコードは、GitHub に全てある、
cd FIRST_test
make
で、実行バイナリー、「first_sample.mot」が出来るので、これを、「Flash Programmer」でデバイス
に書き込む。
※書き込み後、リセットが有効になっているので、書き込み機のリセットラインをオフラインにする
必要がある。