RL78/G13スタートアップルーチンと起動テスト

フラッシュへの書き込みプログラムを先に作ろうと思ったけど、まず、動かしてみない
事には始まらない~

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」でデバイス
に書き込む。
※書き込み後、リセットが有効になっているので、書き込み機のリセットラインをオフラインにする
必要がある。

IMG_0798s