さて、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 ポートの部分しか無いので、ちょくちょく更新、及び追加すると思います。