AKI-SH2/7045の起動と割り込み
Copyright(C) 20Feb2004
coskx
【1】はじめに
この文書はsh2/7045の起動,メモリ配置,割り込みなどについて記述している。
【2】SH8/7045の起動
マイクロコンピュータSH2は電源投入直後,あるいはリセット信号入力直後に割り込みベクタ0に格納されているアドレスにある命令から実行を開始する。
割り込みベクタ0とはアドレス0x00000から0x00003までの4バイトのことである。この場所に通常は0x00280が格納されており,アドレス0x280から始まるプログラムが実行される。(割り込みベクタとは,割り込み関数の開始アドレスのことである。ただし0は特別扱いで,起動時の開始アドレスになっている。)
アドレス | 内容 | 備考 |
0x00000〜0x00003 | 0x00280 | 割り込みベクタ0 実行開始アドレス *1 |
0x00004〜4バイトごと | 割り込みベクタテーブル (割り込み関数の先頭アドレス表) |
さまざまな割り込み関数の先頭アドレスが並んでいる |
0x00280 | プログラムの先頭 | ここに書いてあるプログラムはスタートアップルーチンと呼ばれる。 次の2つのことを行なう (1)Cプログラムのmain()を呼び出す前にすべきことを行なう。 (2)Cプログラムのmain()を呼び出す |
なお,SH2/7045では,アドレス0x00000-0x03FFFFの範囲はROM領域(実際にはフラッシュメモリ)になっていて,プログラムを格納でき,電源が切れてもプログラムが消えてしまうことは無い。
GCCでは割り込みベクタ0はリンカスクリプトに書かれている。
「start」はスタートアップルーチンの先頭アドレス(リセットベクトル)を示している。
LONG(ABSOLUTE(_start)) |
【3】SH2/7045のアドレス
AKI-SH2/7045は,モード2で動作しているこの時のアドレスとメモリの関係は次のようになっている。
メモリの種類 | アドレス | 備考 |
ROM | 0x00000-0x03FFFF (256kbyte) | 実際にはフラッシュメモリ,書き込みモードでのみ書き込むことが出来る。 電源が切れても,内容は保たれる。 割り込みベクタテーブル,プログラム,定数,変数の初期値の格納場所 |
RAM | 0x00400000〜0x0041FFFF ( 1024kbyte) 0xFFFFF000〜0xFFFFFFFF ( 4kbyte) |
電源が切れると内容は消滅する。 0x00400000〜変数領域 0xFFFFF000スタック領域(オート変数,戻りアドレス,レジスタ退避)の格納場所 |
I/Oレジスタ | 0xFFFF8000〜0xFFFF87FF (2kbyte) | I/Oレジスタ |
プログラム・定数をROM上に,変数をRAM上に配置することは,リンカスクリプトに記述する。
.text :
{ CREATE_OBJECT_SYMBOLS *(.text) /*すべてのソースファイルの.textセクションをここに*/ _dataROM_begin = .; /*変数_dataROM_beginにカレントのアドレスを代入*/ } > rom /*ここまでをrom領域に割り付ける*/ .data : AT (_dataROM_begin) { /*AT 初期値データを_dataROM_beginに書き込む*/ _dataRAM_begin = .; /*変数_dataRAM_beginにカレントのアドレスを代入*/ *(.data) /*すべてのソースファイルの.dataセクションをここに*/ _dataRAM_end = .; /*変数_dataRAM_endにカレントのアドレスを代入*/ } > ram2 /*ここまでをram2領域に割り付ける*/ .bss : { _bss_begin = .; /*変数_bss_beginにカレントのアドレスを代入*/ *(.bss) /*すべてのソースファイルの.bssセクションをここに*/ _bss_end = .; /*変数_bss_endにカレントのアドレスを代入*/ } >ram2 /*ここまでをram2領域に割り付ける*/ |
「.text」,「.data」,「.bss」はセクションの名前である。
また,「.data」セクションはram領域におかれるが,「AT (_dataROM_begin)」によって初期値が「_dataROM_begin」のアドレスに書き込まれる。「_dataROM_begin」のアドレスは「_dataROM_begin = . ;」によって「*(.text) 」の直後ということになっている。
【4】変数のメモリ上の配置
Cプログラミングでの変数の内,グローバル変数あるいはstatic変数で,初期値を持つものがある。
これらの変数はプログラム中で値が変更されるため,RAM領域に置かなければならない。ところがRAM領域では電源を切ると値が失われるため,初期値を持たせることが出来ない。
この矛盾を解決するため,初期値をROM領域に生成し,スタートアップルーチンで,起動時にRAM領域にコピーするからくりを使う。
この作業を行なうには次のようにする必要がある。
(1)リンカスクリプトに,RAM上の変数の初期値をROM上に置くことを指示する。
.data : AT
(_dataROM_begin) { _dataRAM_begin = . ; *(.data) _dataRAM_end = . ; } > ram /*ram領域に配置*/ |
!----------------------------------------------------------------------------- |
【5】H8/3048のCPUレジスタの構成
CPU内の汎用レジスタは8個ある。ただし,ER7はスタックポインタとして使われる。
R0 |
32bit汎用レジスタ |
R1 |
32bit汎用レジスタ |
R2 |
32bit汎用レジスタ |
R3 |
32bit汎用レジスタ |
R4 |
32bit汎用レジスタ |
R5 |
32bit汎用レジスタ |
R6 |
32bit汎用レジスタ |
R7 |
32bit汎用レジスタ |
R8 |
32bit汎用レジスタ |
R9 |
32bit汎用レジスタ |
R10 |
32bit汎用レジスタ |
R11 |
32bit汎用レジスタ |
R12 |
32bit汎用レジスタ |
R13 |
32bit汎用レジスタ |
R14 |
32bit汎用レジスタ |
R15 | 32bitレジスタ(スタックポインタ) |
コントロールレジスタには次の2つがある。
SR |
ステータスレジスタ 32bitのレジスタ 10ビットのみ使われている |
GBR | グローバルベースレジスタ 32bitのレジスタ 間接アドレッシング |
VBR | ベクタベースレジスタ 32bitのレジスタ 割り込みベクタのオフセット 通常は0 |
システムレジスタ
MAC | 積和演算のアキュムレータ |
PR | プロシージャレジスタ 32bit サブルーチン(関数)call時の戻りアドレス格納用 |
PC | プログラムカウンタ 32bit |
スタックポインタはサブルーチンコール時の戻りアドレスなど重要な内容の格納場所を示すレジスタなので,CPU起動時にスタック領域の一番後ろを指すようにしておく。
_start: ! プログラム開始位置
!-----------------------------------------------------------------------------
! スタックポインタ設定
!
! 汎用レジスタR0〜R15のうちR15がハードウェアスタックポインタに指定されている
! アドレスは 0xFFFFF000〜0xFFFFFFFF の内臓RAMを使用するため、
! STACK_ROOTは0x0を入れておく
!-----------------------------------------------------------------------------
MOV.L STACK_ROOT, r15
【6】 割り込み関数
通常の関数はプログラムから呼び出される。しかし割り込み関数は機能ユニットがある状態になった時に,CPUがある作業をしている最中にその作業を中断して,起動する。関数の作業が終わると,元の作業の続きを行なう。プログラム中から呼び出してはいけない関数である。
【7】 「割り込み関数」の起動
7.1 準備
(1)割り込み要因によってあらかじめ定められた割り込みベクタ番号の位置に格納されている割り込みベクタ(割り込み関数の先頭アドレス)にジャンプする。
(2)割り込みを起こす機能ユニットを初期化設定する。
(3)CPUを割り込み受付可能にする。
7.2 起動
SH2/7045の割り込みは次のように起こる。
(1)割り込みが起こると自動的にSRの内容と,戻るべきアドレスがスタックに退避される。
(2)必要な作業を行なう
1)割り込み処理で使用する汎用レジスタの退避
2)割り込み要因の解消(割り込みフラッグのクリア)
3)割り込み処理
4)退避したレジスタの復帰
(3)関数の終わりにRTE命令が置かれており,これを実行するとSRをもとに戻し,もとの実行アドレスへ戻る。
【8】 「割り込み関数」の起動例(コンペアマッチタイマ割り込み)
コンペアマッチタイマ割り込みは,CMT(コンペアマッチタイマCompare Match Timer Unit)があらかじめ設定されている時間間隔で引き起こす割り込みである。
この割り込み動作のためには次の内容の準備が必要である。
(1)あらかじめ定められた割り込みベクタ番号の位置に割り込みベクタ(割り込み関数の先頭アドレス)を格納しておく。
例えばCMT1のコンペアマッチ割り込みは,割り込みベクタ番号148でアドレス0x0250〜0x0253に割り込み関数の先頭アドレスを書いておく必要がある。
GCCの場合はリンカスクリプトに記述する。
GCCのリンカスクリプト場合は3項演算子を使って記述する
もしint_imia1という関数が使われていたらその先頭アドレスを登録,そうでなかったらstartを登録LONG(DEFINED(_int_cmi1)?ABSOLUTE(_int_cmi1):ABSOLUTE(_start))
(2)割り込み要因が割り込み要求を発生するように初期化する。
例えばCMT1のコンペアマッチ割り込みを行なうためにinitCMTINT()のような関数で初期化し,startCMT1()で起動する。
この記述はCソースファイル中かあるいはインクルードファイル中に書く
initCMTInt(500000,1,15); /*500000μsec,CMT.ch1使用,割り込みレベル15*/
startCMT1(); /* コンペアマッチタイマ1スタート */
(3)割り込みを受け付けるためには,CPUの割り込みマスクが所定の値になっている必要がある。(SRの割り込みマスクビット)
setIntMask()の関数を使う。この作業はC言語では記述できないため,アセンブリ言語で書かれた関数になる。
setIntMask(14); //レベル15は割り込み許可
setIntMask()の記述はスタートアップルーチンの書いてあるアセンブリソースファイル中に記述しておけばよい。
! void setIntMask(int mask) r4:mask r2:work r1:srreg
.align 4
.global _setIntMask
_setIntMask:
stc sr,r1 ! srreg = __sr__
mov.l MASKVALUER,r2
and r2,r1 ! srreg &= 0xffffff0f
shll2 r4 ! mask <<= 2
shll2 r4 ! mask <<= 2
mov.l MASKVALUE,r2
and r2,r4 ! mask &= 0x00f0
or r4,r1 ! srreg |= mask
ldc r1,sr
rts
nop! この関数はIntMaskを返します。
! int getIntMask(void)
.align 4
.global _getIntMask
_getIntMask:
stc sr,r0
mov.l MASKVALUE,r2
and r2,r0
shlr2 r0
shlr2 r0
rts
nop
【9】 「割り込み関数」の実装例
割り込み関数は次の作業を行なう
1)前処理 a)割り込み処理で使用する汎用レジスタの退避 2)処理 a)処理本体 b)割り込み要因の解消(割り込みフラッグのクリア) 3)後処理 a)退避したレジスタの復帰 b)関数の終わりにRTE命令を置く。 |
「#pragma
interrupt」擬似命令で,関数が割り込み関数であると宣言すると,その関数では次の内容をコンパイラが自動的に生成する
1)前処理
a)割り込み処理で使用する汎用レジスタの退避
3)後処理
a)退避したレジスタの復帰
b)関数の終わりにRTE命令を置く。
そこで割り込み関数内に次の内容のみを記述すればよい
2)処理
a)処理本体
b)割り込み要因の解消(割り込みフラッグのクリア)