ルモーリン

ASSERTマクロ

投稿:2013-02-22、更新:2016-04-03

2016.04.03 改良しましたので以下の能書きを読んだ後は、改良版ASSERTマクロをどうぞ。 デバッグ版で想定外の動作を検知するマクロにASSERTってのがよくあります。 書式は「ASSERT(条件式);」で条件が不成立になると実行を中断、開発環境によってはマクロの位置でソースを表示してくれます。
ほい来た。 条件不成立でSVCを発行するだけの簡単なお仕事。 あ、リリース版でマクロを無効にするのを忘れてる(苦笑)。
#define KSRK_ASSERT(eval)	if (!(eval)) __asm volatile ("svc #1\n")
SVCを発行するとSVCall_Handlerに飛びます。 本当はベクタテーブルの12番目にあるアドレスに飛びますけれど、この辺りの話はcr_startup_lpc17.cに書いてあります。 スーパバイザコールはlrで判定してメインスタックかプロセススタックを切り替えます。 SVCのオペランド(0~255の定数)はハンドラへ渡りませんから、SVCの命令列からオペランドを取得します。 FreeRTOSのvPortStartFirstTaskがSVCを発行しますからASSERTマクロによるものと区別しています。
__attribute__ ((naked)) void SVCall_Handler(void)
{
	__asm volatile (
		"	tst	lr,	#4		\n"	// 使用中のスタックはどちらか?

		"	ite	eq			\n"
		"	mrseq	r0,	msp		\n"	// メインスタックポインタを取得(FreeRTOSのマルチタスク開始のSVC発行)
		"	mrsne	r0,	psp		\n"	// プロセススタックポインタを取得(KSRKのASSERTのSVC発行)

		"	ldr	r0,	[r0, #24]	\n"	// スタック上のPC⇒戻りアドレス⇒SVC命令の次の命令
		"	ldrb	r0,	[r0, #-2]	\n"	// SVC命令の1バイト目⇒即値 ※2バイト命令でリトルエンディアン
		"	teq	r0,	#1		\n"	// 即値は1?

		"	ittt	eq			\n"	// ASSERT発行は0xffffffffへジャンプしてわざとHardFault発生
		"	movweq	r0,	#0xffff		\n"
		"	movteq	r0,	#0xffff		\n"
		"	bxeq	r0			\n"
		"	b	vPortSVCHandler		\n"	// FreeRTOSはマルチタスク開始の際に「SVC 0」を発行しているので引き継ぐ
	);
}
SVC発行時のスタックがメインスタックかプロセススタックか、その判定方法を見つけるまでが長かった。 たまたま別件でARM公式サイトのドキュメントを読んでいてサンプルプログラムで上記の方法を見つけた。 おそらくデータシートのどこかには、こっそり目立たず小さく書いてあるのでしょう(もう慣れっこ)。