未だによく分からない例外優先度
投稿:2013-03-23、更新: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 | -3 | Reset | |
2 | -2 | NMI | |
3 | -1 | Hard Fault | |
4 | 0 | SVCall | |
5 | 0 | Bus Fault | |
6 | 0 | Memory Management Fault | |
7 | 0 | Usage Fault | |
8 | 5 | - | APIが以下の例外をマスク、内部のリスト更新やタスクスイッチ発生時に起きる(portSET_INTERRUPT_MASK) |
9 | 30 | その他 | ペリフェラルの例外 |
10 | 31 | PendSV | タスクスイッチ切替の例外に使用 |
11 | 31 | SysTick | タスクスイッチを定期的に発生させるタイマー |
定期的にSysTick例外を発生させてその延長でPendSV例外を発生させる。
PendSV例外処理でタスクスイッチが起きる。
タスクスイッチ処理中の割り込み禁止期間(例外禁止期間)は優先度5~31の例外を保留する。
他の例外処理中の期間やAPIの中のマスク期間に発生すると、SysTick/PendSV例外は優先度が31だから期間終了まで保留する。
一方、SysTick/PendSV例外処理中でマスク期間外なら優先度30のペリフェラルの例外が横取りする。
同じ優先度、つまり
- PendSVとSysTickの間(優先度31)
- ペリフェラルの例外同士(優先度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); }