さて、R8C用 gcc が出来たのでー、早速、最初の一歩、スタートアップルーチンの実装です~
RX マイコン用 gcc で色々学習したので、今回はかなり楽です。
ただ、R8C のアセンブラは初めてなので、少し苦労しそう・・・
全体の構成としてー
・start.s ---> CPU リセット時に開始するルーチン
・init.c ---> 初期化関係
・vect.c ---> ベクターアドレス関係
・xxx.ld ---> リンカースクリプト
この4つの構成になります。
「start.s」
.text
.global _reset_start
_reset_start:
.global _start
_start:
/* 割り込みスタック設定 */
.extern _isp_init
ldc #_isp_init,isp
/* ユーザースタック設定 */
.extern _usp_init
fset u
ldc #_usp_init,sp
/* 割り込み許可 */
fset i
.extern _init
jmp.w _init
.global _exit
_exit:
jmp.w _exit
・「_reset_strat」、「_start」は同一アドレスに向けておきます。
※リンク時、「-nostartfiles」を指定しても、「_start」のシンボルは必要なようです。
・「_isp_init」、「_usp_init」はリンカースクリプトで指定してあります。
※それぞれ、割り込み用スタック、ユーザースタックです、プログラムの性質に合わせて、少ないメモリーのどこにどれくらい配置するか考える必要があります。
「init.c」
int main(int argc, char**argv);
extern short _datainternal;
extern short _datastart;
extern short _dataend;
extern short _bssstart;
extern short _bssend;
extern short _preinit_array_start;
extern short _preinit_array_end;
extern short _init_array_start;
extern short _init_array_end;
int init(void)
{
// R/W-data セクションのコピー
{
short *src = &_datainternal;
short *dst = &_datastart;
while(dst < &_dataend) {
*dst++ = *src++;
}
}
// bss セクションのクリア
{
short *dst = &_bssstart;
while(dst < &_bssend) {
*dst++ = 0;
}
}
// 静的コンストラクターの実行(C++ )
{
short *p = &_preinit_array_start;
while(p < &_preinit_array_end) {
void (*prog)(void) = (void *)*p++;
(*prog)();
}
}
{
short *p = &_init_array_start;
while(p < &_init_array_end) {
void (*prog)(void) = (void *)*p++;
(*prog)();
}
}
// main の起動
static int argc = 0;
static char **argv = 0;
int ret = main(argc, argv);
return ret;
}
void __dso_handle(void)
{
}
初期化ルーチンでは、一連の準備をする必要があります。
・メモリーのクリア(通常、bss セクション)
・初期値が設定してあるメモリーにROMエリアからコピー
・静的コンストラクターの実行(C++ の場合のみ必要)
・最後に「main」関数の起動
「vect.c」
#include <stdlib.h>
#define INTERRUPT_FUNC __attribute__ ((interrupt))
extern void reset_start(void);
// 未定義命令
INTERRUPT_FUNC void undef_inst_(void)
{
}
// null interrupt TASK
INTERRUPT_FUNC void null_task_(void)
{
}
// R8C M110AN, M120AN の場合、ポインターは2バイト、ベクターテーブルは4バイト単位のアドレスを想定
const void* fixed_vectors_[] __attribute__ ((section (".vec"))) = {
// 0xFFD8 予約領域 (with OSF2)
(const void*)0xffff, (const void*)0xffff,
// 0xFFDC 未定義命令 (with ID1)
undef_inst_, (const void*)0xff00,
// 0xFFE0 オーバーフロー (with ID2)
null_task_, (const void*)0xff00,
// 0xFFE4 BRK 命令
null_task_, (const void*)0xff00,
// 0xFFE8 アドレス一致 (with ID3)
null_task_, (const void*)0xff00,
// 0xFFEC シングルステップ (with ID4)
null_task_, (const void*)0xff00,
// 0xFFF0 ウオッチドッグタイマ、発振停止検出、電圧監視1 (with ID5)
null_task_, (const void*)0xff00,
// 0xFFF4 予約 (with ID6)
(const void*)0xffff, (const void*)0xffff,
// 0xFFF8 予約 (with ID7)
(const void*)0xffff, (const void*)0xffff,
// 0xFFFC リセット (with OFS)
reset_start, (const void*)0xff00,
};
※R8C/M110AN、R8C/M120AN では、ポインターは2バイトです、ですが、ハードウェアーベクターは4バイト構成になっています。
※また、R8Cのアーキテクチャー的には、最大1Mバイトの空間までアクセス出来るので、ベクターは3バイトあれば十分です、最上位バイトは、別の機能(「IDコードチェック機能」、「オプション機能選択」などのレジスター)として使われています、通常0xFFにしておけば良いようです。
※間違って書き換えてしまうと、フラッシュの書き換えが出来なくなる場合があるようなので、注意が必要です
「リンカースクリプト」(抜粋)
MEMORY {
RAM (w) : ORIGIN = 0x0300, LENGTH = 0x0480
ROM (r) : ORIGIN = 0x8000, LENGTH = 0x7FD8
VEC (r) : ORIGIN = 0xFFD8, LENGTH = 40
}
SECTIONS
{
_usp_init = 0x07E0 - 2;
_isp_init = 0x0800 - 2;
...
.rodata : {
. = ALIGN(2);
*(.plt)
KEEP (*(.init))
KEEP (*(.fini))
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.rodata1)
*(.eh_frame_hdr)
KEEP (*(.eh_frame))
KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
. = ALIGN(2);
PROVIDE(__romdatastart = .); /* IF_ROROM */
} > ROM
PROVIDE( __datainternal = ABSOLUTE(LOADADDR(.data)));
...
.vec : {
*(.vec)
} > VEC
...
・リンカースクリプトは、通常 gcc のバージョンによって構造が異なります、今回 gcc-4.7.4 ベースの M32C-elf をビルドしましたが、
m32c-elf/m32c-elf/lib/r8c.ld に基本になるリンカースクリプトがありますので、これを修正して使っています。
※正しい書き方や、意味を完全に理解している訳では無いので、カットアンドトライ的ですが、とりあえず、現状のスクリプトで大丈夫と思います。
「main.cpp」Lチカのプログラムです。
#include "port.hpp"
int main(int argc, char *ragv[])
{
using namespace device;
PD1.B0 = 1;
while(1) {
for(int i = 0; i < 1000; ++i) P1.B0 = 0;
for(int i = 0; i < 1000; ++i) P1.B0 = 1;
}
}
・初期状態の内臓発振器を想定したソフトループとなっています。
・port.hpp がポートのテンプレートクラス定義です。
・Makefile により、自動で、ファイルの従属規則を生成しています。
とりあえず、ソースコードは、GitHub にプッシュしてありますので参考にして下さい。
※定義は I/O ポートの部分しか無いので、ちょくちょく更新、及び追加すると思います。