C言語

イニシャル時ROMチェック処理の実装

イニシャル時ROMチェック処理の実装について解説します。
このイニシャル時とは、CS+でプロジェクト作成時に「cstart.asm」ファイルが作成されたと思います。
このファイルがマイコン起動時に一番最初に処理されるアセンブラコードになります。
今回は、この「cstart.asm」にROMチェック処理を追加していきます。
また、このファイルからメイン処理を呼び出しますが、プロジェクト作成時に作成されたmain.cファイルは、AppMain.cファイルに置き換えたのと、関数名をmainからAppMainにしているため、このままでは、AppMain関数が呼ばれないため、AppMain関数を呼び出すように合わせて変更します。

main関数の変更

CS+のプロジェクトを作成したときの「cstart.asm」ファイルのmain関数の呼び出しは次のようになっています。

	;--------------------------------------------------
	; call main function
	;--------------------------------------------------
	CALL	!!_main		; main();

今回のロボットカー制御プログラムは、main関数ではなく、AppMain関数に名称を変更しているため、次のように変更することで、AppMain関数を呼び出すように変更します。

	;--------------------------------------------------
	; call main function
	;--------------------------------------------------
	CALL	!!_AppMain		; AppMain();

CALLは、関数を呼び出す命令です。
関数名の頭に _(アンダースコア)を付けることになっています。また、CALLで呼び出すときは!!も付けます。
!は、絶対アドレッシングの開始の意味となります。

これで、プログラムを実行すると、AppMain関数が呼び出されると思います。

ROMチェックの概要

次にROMチェックについて説明します。
マイコン故障の設計」のところで、高速CRCを使用してROMエリアの領域全体をチェックすると説明しました。
今回は、実際にどのように実装するかを解説します。

ROMチェックは、RL78マイコンのフラッシュ・メモリCRC機能(高速CRC、汎用CRC)のうち、高速CRCを使用します。
この高速CRCは、CPUを停止させて、コード領域全体のメモリをチェックすることができます。
高速CRCを使用するためには、フラッシュ・メモリCRC制御レジスタ(CRC0CTL)と、フラッシュ・メモリ演算結果レジスタ(PGCRCL)を使用します。

高速CRCの処理の流れ

高速CRCは次のような流れになります。

  • 高速CRCの範囲を設定(CRC0CTLのFEA5~FEA0レジスタを設定)
  • HALT命令、RET命令を設定
  • HALT命令、RET命令をROM→RAMにコピーし、RET命令以降の10バイトをNOPで初期化
  • 割り込みマスクを割り込み禁止
  • CRC0ENレジスタを1に設定(CRC演算動作許可)
  • CRC演算結果レジスタを初期化(PGCRCL)
  • RAMにコピーしたHALT命令、RET命令を実行
  • CRC0ENを0に設定(CRC演算動作禁止)
  • CRC演算結果を取得
  • CRC期待値と演算結果を比較
  • 結果が一致しなかった場合は、マイコンリセット
  • 正常の場合は、高速CRC処理を正常終了

以上が高速CRCの流れになります。

フラッシュ・メモリCRC制御レジスタ(CRC0CTL)レジスタ

高速CRC演算器の動作制御と演算範囲の設定を行うレジスタです。
ビット7:CRC0ENが1だとHALT命令実行により演算開始、0は動作停止です。

FEA5~FEA0:高速CRC演算範囲

今回は、すべてのコード・フラッシュ・メモリの領域である、00000H~0FFFBHを高速CRC演算の対象とします。
このため、FEA5~FEA0:は、000011bです。

フラッシュ・メモリCRC演算結果レジスタ(PGCRCL)

高速CRC演算結果を格納するレジスタです。

特に、設定するレジスタではなく、高速CRCを行うと、このレジスタに結果がセットされるため、このレジスタを読み出すことで結果の値がわかります。

ROMチェック処理の実装

CS+でプロジェクト作成時に作成される「cstart.asm」ファイルを変更していきます。

;-----------------------------------------------------------------------------
;	高速CRC
;-----------------------------------------------------------------------------
.SECTION .hispcrc_ram, BSS
	.DS		0x10

;-----------------------------------------------------------------------------
;	高速CRC初期化用
;-----------------------------------------------------------------------------
.SECTION .hispcrc_chk, TEXTF
	HALT
	RET
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP

;これ以降は既存コードです。
;-----------------------------------------------------------------------------
;	startup
;-----------------------------------------------------------------------------
.SECTION .text, TEXT
_start:

4,5行目では、hispcrc_ramセクションを切って、RAM上に16バイト確保できるようにしています。
10~23行目では、HALT命令、RET命令、10バイトの初期化をROM→RAMにコピーする際のコードをhispcrc_chkセクションに登録しておきます。
これらのセクション名は、任意なので好きな名前を使用してもらっても大丈夫です。

次にHALT命令、RET命令をROM→RAMにコピーするコードを作成していきます。
作成する箇所は、「cstart.asm」ファイルに、「; copy .text to RAM」となっている箇所です。
ここでは既にコメントアウトされたコードがあると思います。
この処理を下記のように変更していきます。

	; copy .text to RAM
	MOV	C,#HIGHW(STARTOF(.hispcrc_chk))
	MOVW	HL,#LOWW(STARTOF(.hispcrc_chk))
	MOVW	DE,#0xED0A
	BR	$.L2_TEXT
.L1_TEXT:
	MOV	A,C
	MOV	ES,A
	MOV	A,ES:[HL]
	MOV	X,A
	MOV	A,#0x0F
	MOV	ES,A
	MOV	A,X
	MOV ES:[DE],A
	INCW	DE
	INCW	HL
	CLRW	AX
	CMPW	AX,HL
	SKNZ
	INC	C
.L2_TEXT:
	MOVW	AX,HL
	CMPW	AX,#LOWW(STARTOF(.hispcrc_chk) + SIZEOF(.hispcrc_chk))
	BNZ	$.L1_TEXT

アセンブラ言語のため、解らない方もいると思いますので、1行毎に説明していきたいと思います。

1行目、; は、コメントであることを示しています。アセンブラ言語では、; を記述すると、それ以降の1行をコメントと見なします。
2行目:.hispcrc_chkセクションの上位アドレス値の1バイトをCにコピーします。
3行目:.hispcrc_chkセクションの下位アドレス値の2バイトをHLにコピーします。
4行目:0xED0AをDEにコピーします。
5行目:ラベル.L2_TEXTに遷移します。
6行目、21行目:ラベルになります。C言語でいうとgoto文のラベルと同じ意味です。
22行目:LH(.hispcrc_chkセクションの下位アドレス値)をAXにコピーします。
23行目:AX(.hispcrc_chkセクションの下位アドレス値)と、.hispcrc_chkセクション領域の最後の下位アドレス値を比較します。
24行目:25行目の比較で一致していない場合は、.L1_TEXTに遷移します。※一致した場合は、処理を終了します。
7行目:CをAに退避します。
8行目:A(.hispcrc_chkセクションの上位アドレス値の1バイト)をESにコピーします。
9行目:ESとHLのアドレスを繋げたアドレス値に入っている値をAにコピーします。(例:ESが0x01で、HLが0x1234の場合、0x011234のアドレスにある値をAにコピー)
10行目:Aの値をXに退避します。
11行目:Aに0x0Fをコピーします。
12行目:0x0FをESにコピーします。
13行目:Xに退避したESとHLのアドレスを繋げたアドレス値に入っている値をAに戻します。
14行目:0x0FED0AアドレスにAをコピーします。
15行目:DEをインクリメントします。(DEのアドレス値を+1進めます。※RAMのアドレス)
16行目:HLをインクリメントします。( .hispcrc_chkセクションの下位アドレス値をインクリメントします。※ROMのアドレス)
17行目:AXをクリアします。
18行目:.hispcrc_chkセクションの下位アドレス値と0を比較します。
19行目:0ではない場合、20行目をスキップして21行目から実行します。.hispcrc_chkセクションの下位アドレス値が0の場合は、桁が繰り上がったので、20行目を実行します。
20行目:Cをインクリメントします。

各行の説明を行いましたが、処理の説明としては、ROMの .hispcrc_chkセクションの値をRAMの0xFED0Aアドレス以降に順にコピーします。.hispcrc_chkセクションの最後までコピーが完了したら処理を終了します。

次にROM→RAMにコピーが完了した後、ROMチェック処理を記述します。

$IF (__DEBUG__ == 1)
	;
$ELSE
  ROM_CHK
$ENDIF	

__DEBUG__に1と設定している場合は、ROMチェックを実施しないようにします。これは、デバッグするときは、デバッグ・モニタ領域を使用するため、高速CRCの演算結果が合わなくなるため、デバッグではROMチェックを行わなくします。
DEBUG==1でない場合、ROM_CHKマクロ関数を実行します。マクロ関数にする必要はないのですが、記述が長くなるので、ROMチェック処理を別ファイルに定義します。
ここでは、ROMチェックマクロ関数を呼び出すのみです。

実際のROMチェックは次のようになります。

	.LOCAL	L5_100, EXIT_8
	MOV	ES,#0x00
	MOVW HL,#0xFFFC
	MOVW AX,#0x0000

	MOV	!CRC0CTL,#0x03

	MOV	!MK0L,#0xFF
	MOV	!MK0H,#0xFF
	MOV !MK1L,#0xFF
	MOV !MK1H,#0xFF
	MOV !MK2L,#0xFF
	MOV	!MK2H,#0xFF

	MOV	!CRC0CTL,#0x83
	MOVW !PGCRCL,AX
	CALL !!0xFE900
	MOV	!CRC0CTL,#0x03
	MOVW AX,!PGCRCL
	CMPW AX,ES:[HL]
	BZ $L5_100
EXIT_8:
	DI
	BR $EXIT_8
L5_100:
  .ENDM

1行目:ローカルのラベルを定義
2行目:ESに0x00をセット(高速CRC演算の期待値を設定しているアドレスの上位1バイトをセット)
3行目:HLに0xFFFCをセット(高速CRC演算の期待値を設定しているアドレスの下位2バイトをセット)
4行目:AXをクリア
6行目:CRC0CTLレジスタに0x03をセット(高速CRC演算対応範囲を00000H-0FFFBHに設定)
8-13行目:すべて割り込みマスクを割り込み処理禁止に設定
15行目:CRC0CTLレジスタに0x83をセット(高速CRC開始)
16行目:PGCRCLレジスタに0をセットし、演算結果をクリア
17行目:HALT命令、RET命令をRAMにコピーしたアドレスを指定して、処理を呼び出すことで、ROMチェックを実施
18行目:CRC0CTLレジスタに0x03をセット(高速CRC停止)
19行目:演算結果をAXにコピー
20行目:演算結果と0xFFFCアドレスに格納されている値(演算結果期待値)を比較
21行目:20行目の比較した結果、一致していれば、マクロ関数終了
22行目:一致していない場合
23行目:割り込み禁止
24行目:EXIT_8に遷移し、無限ループさせる(※無限ループさせることにより、内蔵WDTのタイムアウトが発生してリセットが行われる)

駆け足ではありましたが、イニシャル時ROMチェック処理の実装については以上となります。

-C言語