あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先

未だによく分からない例外優先度 (2014.08.24)

2014.08.24 と書いていましたが色々と分かってきましたので修正しました。 「例外」が全ての「割り込み」を含むので用語を「例外」に書き換えました。 FreeRTOSのソースはINTERRUPT=割り込みと表記、このサイトはLPCに習って例外と表記します。

英語わかりません

次のソースはFreeRTOSConfig.hの一部です。
/* The lowest priority. */
#define configKERNEL_INTERRUPT_PRIORITY 	( 31 << (8 - configPRIO_BITS) )
/* Priority 5, or 160 as only the top three bits are implemented. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( 5 << (8 - configPRIO_BITS) )

/* Priorities passed to NVIC_SetPriority() do not require shifting as the
function does the shifting itself.  Note these priorities need to be equal to
or lower than configMAX_SYSCALL_INTERRUPT_PRIORITY - therefore the numeric
value needs to be equal to or greater than 5 (on the Cortex-M3 the lower the
numeric value the higher the interrupt priority). */

現在の設定

現在の例外優先度設定
項番優先度例外説明
1-3Reset
2-2NMI
3-1Hard Fault
4SVCall
5Bus Fault
6Memory Management Fault
7Usage Fault
8APIが以下の例外をマスク、内部のリスト更新やタスクスイッチ発生時に起きる(portSET_INTERRUPT_MASK)
930その他ペリフェラルの例外
1031PendSVタスクスイッチ切替の例外に使用
1131SysTickタスクスイッチを定期的に発生させるタイマー

分かった事

定期的にSysTick例外を発生させてその延長でPendSV例外を発生させる。 PendSV例外処理でタスクスイッチが起きる。 タスクスイッチ処理中の割り込み禁止期間(例外禁止期間)は優先度5~31の例外を保留する。 他の例外処理中の期間やAPIの中のマスク期間に発生すると、SysTick/PendSV例外は優先度が31だから期間終了まで保留する。 一方、SysTick/PendSV例外処理中でマスク期間外なら優先度30のペリフェラルの例外が横取りする。 同じ優先度、つまり では、横取りじゃなくて保留される。

今回の鉄ゲタは…

ペリフェラルの例外優先度を設定する際に、前述のconfigMAX_SYSCALL_INTERRUPT_PRIORITYを使い最近までこんなコードを使っていたのです。
	// 例外優先度設定
	// 例外処理からFreeRTOSを呼び出す場合、例外優先度をconfigMAX_SYSCALL_INTERRUPT_PRIORITYより上げるとリスト処理が失敗するので一律設定
	// そしてAPI使用が出来るようにconfigKERNEL_INTERRUPT_PRIORITYに一律設定
	// ※実質最低優先度
	uint32_t i;
	for (i = 0; i < sizeof(irq_list) / sizeof(irq_list[0]); i++)
	{
		NVIC_SetPriority(irq_list[i], configMAX_SYSCALL_INTERRUPT_PRIORITY);
	}
ところがどっこい、例外についてソースを眺めて最近気が付きました。 configMAX_SYSCALL_INTERRUPT_PRIORITYは例外設定のレジスタに代入できるよう予め3ビット左シフトしてあります。 そしてNVIC_SetPriorityがあるcore_cm3.hを見ると、こっちも3ビット左シフトしてるから都合6ビット左シフトになってた(泣)。
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts      */
}
そこでこんなコードに修正しました。
	// 例外優先度設定
	// 例外処理中にFreeRTOSを呼び出す場合、例外優先度をconfigMAX_SYSCALL_INTERRUPT_PRIORITY(のシフト前)より上げるとリスト処理が失敗するので一律設定
	// そしてAPI使用が出来るようにconfigKERNEL_INTERRUPT_PRIORITY(のシフト前)に一律設定
	// ※実質最低優先度
#define FREERTOS_PRIORITY_SHIFTED configKERNEL_INTERRUPT_PRIORITY
#define FREERTOS_PRIORITY (FREERTOS_PRIORITY_SHIFTED >> (8 - configPRIO_BITS))
#define KSRK_PRIORITY (FREERTOS_PRIORITY - 1)
	uint32_t i;
	for (i = 0; i < sizeof(irq_list) / sizeof(irq_list[0]); i++)
	{
		NVIC_SetPriority(irq_list[i], KSRK_PRIORITY);
	}