[http://documentation.renesas.com/doc/products/mpumcu/rej09b0137_m16csm.pdf|M16C/60 M16C/20 M16CTiny Series Software Manual] セクション 1.2 に説明のある通り M16C/62P プロセッサは0x400 -- 0x7fff 番地まで 31キロバイトの RAM、ならびに 0xa0000 -- 0xffdb まで 0x5ffdc バイトの ROM (フラッシュメモリ)が存在している。
0xfffdc -- 0xfffff までの 32 バイトは固定ベクター領域である。
コンパイラーによって生成されたコードはリンカによってファームウエアのイメージに展開されフラッシュメモリの 0xa0000 番地以降に配置されるが、その際 fixed_vects.asm に記述された固定ベクターテーブルが必ず 0xfffdc 番地から配置されるよう指定する(この操作はツールチェインによって自動的に行われる)。この固定ベクターテーブルの最期の4バイトがリセットベクターであるので、 CPU リセットによってここに記述された番地にジャンプする。このベクタに start.asm の _start の番地を指定することで、CPU の制御を start.asm にジャンプさせることにより処理が開始される。
;:(重要ではないので読み飛ばしてよい)
前節にのべた、オブジェクトコードに対する番地の指定はリンカに対するリンカースクリプトによって行われる。リンカースクリプトは GNU Toolchain では通常 .ld の拡張子であるが、今回使用している KPIT GNU Toolchain では .ld ファイルが見当たらなかった。それと思われるファイルは、
C:\Program Files (x86)\Renesas\Hew\Tools\KPIT Cummins\GNUM16CM32C-ELF\v11.01\m32c-elf\m32c-elf\lib\ldscripts\
ディレクトリに拡張子 .x で複数存在する。ビルドディレクトリに .gsi 拡張子で実際に使用されたリンカスクリプトが保存されているが、このメモリ配置を変更する方法は見いだせなかった。(通常はコンパイラーオプションでスクリプトファイル名を指定するが、HEW 上ではコンパイラーオプションを指定する方法が存在しない)
後日調べてところ、HEW の Build メニューから ~np~KPIT GNUM16CM32C[ELF]Toolchain...~/np~ を選択し表示されるダイアログから ~np~Link/Library~/np~ タブで Category: に Sections を指定することでセクションの配置が表示される。この値を変更することでメモリ配置を決定できると思われるが実際には試していない。
_start は、リセット処理によって処理が移されるエントリーポイントであり、第一にスタック領域を確保する。 CPU の isp (割り込みスタックポインタ)に _istack を代入しており、この _istack は前述のリンカースクリプトで 0x0f00 と指定されている。しかしながら、同じリンカースクリプトにおいて .data セクションが 0x0400 となっておりアプリケーションの作業領域と重なってしまう。
前述のとおり、リンカスクリプトを修正する適切な手段が見当たらなかったため、スタートアップコードで isp に 0x8000 を即値で設定することとした。
.text
.global _start
.type _start, @function
_start:
/* ldc #_istack, isp /* set interrupt stack pointer */
/* workaround for HEW/KPIT GCC that generate wrong liker script */
ldc #0x8000, isp
同様にユーザスタックポインタ usp を 0x7d00 とした。
前述の結果、プロセッサ全体に対するプログラムセクションは以下のようになる。
address | section | description |
---|---|---|
0x000400 | .bss section | プログラム作業メモリ |
0x007b04 | .ebss | .bss section 上限 |
.ustack | スタック領域 | |
0x007f00 | usp | ユーザスタック下限 |
.istack | 割り込みスタック領域 | |
0x008000 | isp | 割り込みスタック下限 |
0x0a0000 | .text | 実行コード領域開始アドレス |
0x0aa862 | .rodata | Read only データ領域開始アドレス |
表中、青で示したアドレスはリンカーによって自動的に計算される。
設計者は、 .map ファイルを注視し特に .bss セグメント上限を示す .ebss が ユーザスタック領域と重複しないよう注意が必要である
設計者が記述したプログラムコードは .text セクションに書き込まれ、初期データは .rodata セクションに書き込まれる。プログラムが作業するメモリは .bss セクションにおかれる。 .bss セクションに配置されるのは C/C++ 言語で static あいはグローバル宣言されている変数領域である。
C/C++ において関数内に宣言される変数はスタック領域に格納される。上記の例ではユーザスタック初期値が 0x7f00, .ebss が 0x7b04 なのでスタックとして利用可能なメモリは 0x3fc (1020) バイト以下である。仮にある関数で 1020 バイトの配列を自動変数宣言し、その配列を初期化すると確実にメモリを破壊しその予期しない結果をもたらすので注意が必要である。
KPIT GNU Tools により生成された startup code では、スタックポインタの初期化に続き _hw_iniialize を関数コールする。設計者は、自身のファームウエアの構成に応じたハードウエアの初期化コードを C 言語で記述できる。
hw_initialze() 関数は hwinit.c ファイルに記述されており、ペリフェラルの初期化など最低限の初期化を行う。
重要なことは、 .bss セグメントの初期化は hw_initialize 関数が呼び出された後に行われることである。したがって hw_initialize 関数内では static や global に宣言されたテーブルを参照するなどのコードは予期しない結果を招く。
startup code (start.asm) は、hw_initialize の後、.bss -- .ebss の範囲をゼロクリアし、 プログラム中の data セクション(初期値のあるデータ)をフラッシュメモリ領域から .bss セクションの所定のアドレスにコピーする。これにより、 C 言語で記述されたプログラムが通常通り処理できる環境が構築されたことになる。 _main を呼び出すことで startup code は役割を終える。