この投稿では、優先順位の異なる処理での共有資源の稀頻度エラーについて説明します。
共有資源:static変数、MCU機能レジスタなど
処 理:優先順位の異なる処理で共有資源の書込みを行う
仕様
FUNC()実行毎にAを+1インクリメントし、INT()でAをゼロにする。
現象
下図のタイミングで割込みイベントが発生した場合、INT()でAをゼロにしたはずがゼロとならない。
この現象は、通常テストでは見つからずに量産して市場に出すと発覚することが多いです。
発生メカニズム
次のような処理タイミングで処理実行したときに発生します。
1) r3にAのアドレスを書き込む
2) r4にAの値xを書き込む
3) 割込みイベント発生、割込み用スタック退避し、INT()開始
4) r3にAのアドレスを書き込む
5) Aに0を書き込む
6) 割込み用スタック復帰
7) r4=x+1
8) Aにx+1を書き込む
対策方法
対策方法1
この対策方法は、次のように割込み優先順位を変更することで回避できます。異なる優先順位でアクセスすることが多いMCU機能レジスタなどで用いる事が多い方法です。
static unsigned char A = 0;
void FUNC(void)
{
unsigned int mask;
......
mask = get_set_ipr(level_max); // Interrupt priority max Level
A = A + 1;
mask = get_set_ipr(mask); // Interrupt priority original Level
......
}
// Interrupt
void INT(void)
{
......
A = 0;
......
}
対策方法2
この対策方法は、共有資源を異なる処理タイミングで書込みしないようにすることで回避できます。
Sébastien Miguet氏の指摘により修正。Sébastien Miguet氏に感謝します。(2024/7/16)
static unsigned char A = 0;
static unsigned char CLEAR_N1 = 0;
static unsigned char CLEAR_N2 = 0;
void FUNC(void)
{
......
unsigned char CLEAR_N1_CPY = CLEAR_N1;
if( CLEAR_N1_CPY == CLEAR_N2 ) // Check for occurrence of INT interrupt
{
A = A + 1;
}
else
{
A = 0;
}
CLEAR_N2 = CLEAR_N1_CPY;
......
}
void INT(void)
{
......
CLEAR_N1 = CLEAR_N1 + 1;
......
}
根本原因
この不具合は、次のことが根本原因です。
- 仕様に書いていない暗黙知であること
- 仕様とコードの一致性を重要視する文化であること
- C言語の1コードが複数の命令コードで実行されることを知らないエンジニアが増えたこと