P/ECE研究室〜続・S1C33分室


* Wed Oct 03 19:31:24 JST 2007 Naoyuki Sawa

- デバッグユニット#1

 五年前、P/ECEを入手して一年目の年に、P/ECEのCPU S1C33209に内蔵されている、さまざまな回路の使い方を実験しました。プログラマブルタイマやDMAコントローラ、シリアルコントローラなどです。僕は、本格的な組み込み用マイコンのプログロミングはこれが初めてだったので、ここで得た知識はたいへん役立ちました。さてそんな中、興味深い回路が、未実験のまま残っていました。デバッグユニットです。

 デバッグユニットは、オンチップデバッグ機能を提供する回路です。オンチップデバッグ機能とは、CPUに内蔵されたデバッグ機能の総称で、ブレークポイント機能やトレース機能などを含みます。昔のCPUはデバッグ機能を内蔵しておらず、開発時だけ、ICEというデバッグ装置に差し替えて使うのが一般的でした。しかし、CPUが高性能・高機能・多品種化するにつれ、基板上のCPUをICEに差し替えてデバッグするのが難しくなってきました。そこで、CPU自身にデバッグ機能が内蔵されるようになりました。

 余談ながら、昔のインテル8086やモトローラ68000も、デバッグ機能のひとつである、単純なシングルステップ機能を内蔵しています。しかし、シングルステップ機能だけでは、オンチップデバッグ機能とは呼ばないのが一般的みたいです。80386以降には、本格的なオンチップデバッグ機能が内蔵されています。逆に、新しい製品でも、Z80の派生品のように単純なCPUは、オンチップデバッグ機能を内蔵していないことも多いようです。

 さて、そんなわけでCPUにデバッグ機能が内蔵されるようになったのですが、ICEという装置が完全に不要になったわけではありません。昔のICEはそれ自身が「CPU+デバッグ機能」という中心的な役割だったのに対して、現在のICEは役割を縮小し、CPUに内蔵されたデバッグ機能を外部から制御したり監視する役割となりました。P/ECEのCPU S1C33209の場合、「S5U1C33001H」というICE製品がEPSONから販売されています。これを買ってきてP/ECEの拡張端子につなげると、デバッグユニットのオンチップデバッグ機能が使えるようになります──とはいえ、趣味のプログラミングのために、プロ用のICE製品を購入するのは現実的ではありませんね。どれくらいの値段か知らないのですけれど、きっと高価なのでしょう。仮にICEを購入したとしても、P/ECEの拡張端子改造が必要という点も少しハードルです。なんとかして、ICE無し・無改造でデバッグユニットを使えないでしょうか。

 デバッグユニットのデバッグ機能は、大きく二種類に分けられます。特定の条件でプログラムを中断してデバッグルーチンを実行するブレークポイント機能と、拡張端子を通じてCPUの実行状態をリアルタイムに監視するトレース機能です。トレース機能を利用するにはどう考えてもICEが必要なのであきらめて、ブレークポイント機能に注目してみることにします。

 S1C33シリーズのCPUマニュアルに、以下の記述があります。

 S1C33000はアドレス空間の中のエリア2(0x0060000〜0x007FFFFの128Kバイト)をICE(In-Circuit Emulator)用の予約エリアとしています。
 0x0060010〜0x0077FFFがICE用制御プログラム領域として予約され、0x0078000以降の領域はデバッグ機能を制御するレジスタとCPUの専用領域として予約されています。
 エリア2のデバッグ機能を制御するレジスタへのデータ書き込みは、ユーザーモードでは行えません。デバッグ例外発生後のデバッグモードで行う必要があります。なお、デバッグモードでは特に各エリアへのアクセス条件はありませんので、すべてのエリアに対してアクセスが可能です。
『S1C33000コアCPUマニュアル』「3.6.2 エリア2の構成」より

 デバッグ機能を使うためには、デバッグ機能を制御するレジスタへ書き込む必要があります。デバッグ機能を制御するレジスタへ書き込むためには、一時的にデバッグモードへ移行しなければなりません。最初にデバッグモードへ移行するには、二つの方法があります。

 一つ目は、拡張端子にICEを接続して、ハードウェア的にデバッグモードへ移行する方法です。

 DSIOは唯一の入力ピンで、内部の120kΩでプルアップされ、Lowパルスが入るとデバッグモードになります。
『S1C33 Familyスタンダードコア用アプリケーションノート』「4.10 デバッグ用の接続」より

 二つ目は、プログラム内でbrk命令を使って、ソフトウェア的にデバッグモードへ移行する方法です。

brk
 デバッグ処理ルーチンを呼び出すソフトウェア例外です。
 次の命令のアドレスと汎用レジスタR0をデバッグ用スタックにセーブ後、デバッグ用ベクタアドレス0x0000000(もしくは0x0060000)からデバッグルーチンへのベクタを読み出してPCにロードします。これによりデバッグ処理ルーチンに分岐します。また、CPUはデバッグモードに移行します。
retd
 brk命令実行時にデバッグ用スタックに退避させたR0とPCの内容をそれぞれに戻して、デバッグ処理ルーチン(デバッグモード)からリターンします。
『S1C33000コアCPUマニュアル』「4.3 個別命令リファレンス」より

 今回利用するのはもちろん、二つ目の方法です。ですが、「デバッグ用ベクタアドレス0x0000000(もしくは0x0060000)からデバッグルーチンへのベクタを読み出してPCにロードします。」という記述が気になります。デバッグ用ベクタアドレスは、0x0000000なのでしょうか、0x0060000なのでしょうか。マニュアルの隅々まで探しても、実際にどちらのアドレスが利用されるのか、はっきりしません。

 先の引用によると、通常のユーザーモードでは、エリア2(0x0060000〜0x007FFFF)を書き換えることができません。もし、デバッグ用ベクタアドレスが0x0060000であったとすると、そこにデバッグ処理ルーチンへのベクタを書き込むことができず、自分で用意したデバッグ処理ルーチンを呼び出せないことになってしまいます。従って、消去法で、デバッグ用ベクタアドレスは0x0000000でなくてはならないはずです。

 デバッグ用ベクタアドレスが0x0000000であると仮定して、テストプログラムを作ってみましょう。brk命令でデバッグモードに切り替え、すぐにユーザーモードに切り戻すだけのプログラムです。デバッグ処理ルーチンの中では、とりあえず何もしません。ソースコードは以下のとおりです。


#include <piece.h>

unsigned char vbuff[DISP_X * DISP_Y];

/* デバッグ処理ルーチン */
void dbg_isr();
asm("
	.code
	.align	1
dbg_isr:
	retd
");

void pceAppInit() {
	pceLCDSetBuffer(vbuff);
	pceLCDDispStart();

	/* デバッグ用ベクタアドレスに、デバッグ処理ルーチンへのベクタを書き込む */
	*(volatile int*)0x0000000 = (int)dbg_isr;
}

void pceAppExit() {
}

void pceAppProc(int count) {
	/* プログラムが動いていることを確かめるために、カウントアップを表示する */
	memset(vbuff, 0, sizeof vbuff);
	pceFontSetPos(0, 0);
	pceFontPrintf("%d", count);
	pceLCDTrans();

	/* Aボタンが押されたら、brk命令を実行する */
	if(pcePadGet() & TRG_A) {
		asm("brk");
	}
}

 推測が正しければ、Aボタンを押しても何も起こらずに、カウントアップし続けるはずです。それでは、実行。 Aボタンを押すと──ハングアップしました(T_T)

 困りました。どうやら、デバッグ用ベクタアドレスは0x0060000のようです。アドレス0x0060000には、あらかじめ0x0070000という値が設定されていて、書き換えることができません。アドレス0x0070000〜には、ICE用制御プログラムが格納されています。先ほどのテストプログラムは、brk命令を実行するとICE用制御プログラムが呼び出され、ICEが接続されていないために通信できず、ハングアップしてしまっていたみたいです。

 状況を打破するには、デバッグ用ベクタアドレスを0x0000000に切り替えるしかありません。マニュアルに記載されている以上、きっと何らかの方法があるはずです。しかし、さまざまな資料を探しても、その方法が見つかりません。隠し機能なのでしょうか。わからないままに、五年間が過ぎました。もう、ICE無しでデバッグユニットを使うことは、ほとんどあきらめていました。

 先日、Web上で偶然、S1C33401 CPUの旧い資料を見つけました。『S1C33401テクニカルマニュアル(Rev.0.6)』です。2004年発行と記されています。現在、EPSONのサイトから入手できる正式版『S1C33401テクニカルマニュアル』は2005年2月発行ですので、約一年旧い資料です。Rev.0.6とありますから、たぶん、量産出荷前のサンプル版の資料だと思います。内容は、正式版とほとんど同じようです。なにげなく眺めていたところ、I/Oマップのページに、以下の記述を見つけました。

『S1C33401テクニカルマニュアル(Rev.0.6)』「Appendix I/Oマップ」より

 00402E0と00402E4の説明に注目。デバッグ用ベクタアドレスを0x0000000に切り替える方法が記載されているではありませんか!! これまで、マニュアルの記載を見落としていたのか? あわてて、正式版『S1C33401テクニカルマニュアル』の該当ページを再確認します。

『S1C33401テクニカルマニュアル』「Appendix I/Oマップ」より

 正式版の資料では、デバッグ用ベクタアドレスを0x0000000に切り替える方法も含めて、一部の情報が削られています。念のため、『S1C33209/221/222テクニカルマニュアル』のI/Oマップも再確認すると、

『S1C33209/221/222テクニカルマニュアル』「Appendix I/Oマップ」より

 00402DFの次は0048120まで飛んでいて、前述の情報は一切記載されていません。見落としではなかったようです。

 S1C33401は、P/ECEのCPU S1C33209を大幅にパワーアップしたCPUです。基本的には上位互換なのですが、S1C33401とS1C33209とでは仕様が異なる点もあります。S1C33401の資料に記載されている方法が、S1C33209でも使えるとは限りません。実験してみましょう。テストプログラムのソースコードは以下のとおりです。


#include <piece.h>

unsigned char vbuff[DISP_X * DISP_Y];

/* デバッグ処理ルーチン */
void dbg_isr();
asm("
	.code
	.align	1
dbg_isr:
	retd
");

void pceAppInit() {
	pceLCDSetBuffer(vbuff);
	pceLCDDispStart();

	/* 0x00402E4の書き込みプロテクトを解除する */
	*(volatile unsigned char*)0x00402E0 = 0xA9;

	/* デバッグ用ベクタアドレスを0x0000000に切り替える */
	*(volatile unsigned char*)0x00402E4 = 0x01;

	/* デバッグ用ベクタアドレスに、デバッグ処理ルーチンへのベクタを書き込む */
	*(volatile int*)0x0000000 = (int)dbg_isr;
}

void pceAppExit() {
}

void pceAppProc(int count) {
	/* プログラムが動いていることを確かめるために、カウントアップを表示する */
	memset(vbuff, 0, sizeof vbuff);
	pceFontSetPos(0, 0);
	pceFontPrintf("%d", count);
	pceLCDTrans();

	/* Aボタンが押されたら、brk命令を実行する */
	if(pcePadGet() & TRG_A) {
		asm("brk");
	}
}

 実行。Aボタンを押すと──ハングアップしません!! 成功です!! (何も起こらないのが成功というのも、いまいち感動が薄いですが…(^^;) デバッグ用ベクタアドレスを0x0000000に切り替えるには、S1C33401用の方法が、S1C33209でも使えるようです。

 前述のとおり、正式版『S1C33401テクニカルマニュアル』や『S1C33209/221/222テクニカルマニュアル』には、デバッグ用ベクタアドレスを0x0000000に切り替える方法が記載されていませんでした。しかし、今あらためて見返してみると、方法を推測することは可能だったかも知れません。正式版『S1C33401テクニカルマニュアル』は、デバッグユニットを制御する非公開レジスタが存在することを、わずかながら説明しているからです。

 DBGの制御レジスタは0x402E0〜0x402F4の8ビットデバイスエリアに割り付けられており、バイトアクセスが可能です。
注:・ここでは、デバッグ用端子を入出力ポートに切り替えるために必要なレジスタ(0x402E8、0x402EC)のみを説明します。これ以外のDBG制御レジスタは、アプリケーションプログラムからアクセスしないでください。
『S1C33401テクニカルマニュアル』「II.7.4 制御レジスタの詳細」より

 説明されている0x00402E8と0x00402ECのレジスタペアの並びを見ると、0x00402E0と0x00402E4にも何らかのレジスタペア、0x00402F0と0x00402F4にも何らかのレジスタペアが存在すると推測できます。S1C33209でも同様と推測し、P/ECEのプログラムを作って総当りで試してみれば、もっと早くに、デバッグ用ベクタアドレスを0x0000000に切り替える方法を見つけられていたかも知れません。

 正式版『S1C33401テクニカルマニュアル』や『S1C33209/221/222テクニカルマニュアル』には、なぜ、これらの情報が公開されていないのでしょうか。デバッグユニットの機能は、本来、アプリケーションプログラムから直接利用するものでなく、ICEなどの開発ツールと組み合わせて利用するものです。今後開発されるS1C33シリーズのCPUでは、開発ツールと合わせてデバッグユニットの仕様が変更される可能性があります。ユーザーが作成したプログラム資産の互換性が失われるリスクを防ぐために、公開されていないのだと思います。Rev.0.6の資料では、正式版で非公開とする情報が、まだ削られずに残っていたのでしょう。P/ECEにとっては、(悲しむべきか)将来の互換性はまったく重要でなく、今あるものが全てです。動く機能は全部使ってしまいましょう。

 幸運にも、ICE無しでデバッグユニットを使う手がかりがつかめました。今回のテストプログラムは、一時的にデバッグモードへ移行するだけで、まだデバッグ機能を利用していません。次回は、実際にデバッグ機能を利用するプログラムを作って、実験してみようと思います。

(…続きます)


nsawa@piece-me.org