WDT(Watch Dog Timer)とは
組み込みエンジニアならすでに知っていると思うので省略しますが、そういえばWDTの存在を忘れていました。もちろんハングアップしないファームウェアが理想ではありますし、原則そうなっているとは思うのですが、経年劣化でICが故障したときなどにリセットがかからないのもよろしくないですよね。
サンプルがそのまま使える
もちろんWDTのサンプルプロジェクトも当然用意されています。プロジェクト名はwatchdogです。
何も考えずにそのまま動く
特にソースコードをいじることもなく、コンパイルすればそのまま動かすことができます。サンプルコードでは5回WDTをフィード(クリア)した後にリセットが発生するものになっています。
問題はここからだ!
WDTがちゃんと実装されていて動くことが確認できました、わーい。
ソースコードはサンプルから全くいじっていないので省略します
・・・。
あれ、これってどうやって使うんだろう?RTOSにおけるWDTの正しい使い方とは・・・?
だって考えてみてくださいよ。仮にどこかのキューでスレッドが終了せずに次に進まなくなったとしても、システムキュー内でWDTをフィードするスレッドが生きていたらWDTはクリアされてしまうんですよね?
どこかのキューがハングアップしていようがそんなことはお構いなしにタイムスライスは無情に動き続けるわけです。
ちなみにこの理解は間違っていました・・・
調べてみる
何事も自分で試してみるのが基本です、ええ(笑)
ということでサンプルコードに無限ループスレッドを追加してみました。
なお、ここには変更点のみ記載しています。
void my_work_handler(struct k_work *work)
{
printk("Worker Thread\n");
while (1);
}
K_WORK_DEFINE(my_work, my_work_handler);
void main(void)
{
...
err = wdt_setup(wdt, WDT_OPT_PAUSE_HALTED_BY_DBG);
if (err < 0) {
printk("Watchdog setup error\n");
return;
}
// Start Thread
k_work_schedule(&my_work, K_NO_WAIT);
/* Waiting for the SoC reset. */
while (1) {
printk("Feeding watchdog...\n");
wdt_feed(wdt, wdt_channel_id);
k_sleep(K_MSEC(50));
}
}
こんなことをしても動いちゃうんだよね、きっと・・・と思いながら走らせてみると、なんと!WDTリセットが発生しました。
どういうこと?!?!?!
タスクのスイッチはスリープ時に発生
理解が間違っていたのはまさにこの部分でして、タスクのスイッチはスリープ相当の状態になって初めて発生します。言われてみれば当たり前のことなのですが、例えば非常に長ったらしい文字列の出力をしている時、途中でスレッドがスイッチしてしまったら出力が途切れてしまいますよね。
ここまでくるとほとんど理解ができると思いますが、先ほどのwhile句の無限ループにもsleepを挟んでやるとちゃんとスイッチしてWDTリセットがかからなくなります。
void my_work_handler(struct k_work *work)
{
printk("Worker Thread\n");
while (1) {
k_sleep(K_MSEC(50));
}
}
K_WORK_DEFINE(my_work, my_work_handler);
ちなみにセマフォで待機中のスレッドはと言うと・・・これも大丈夫なようです。セマフォ待機中はスリープ扱いってことですね。(当たり前か)
K_SEM_DEFINE(k_sem, 0, 1);
void my_work_handler(struct k_work *work)
{
k_sem_take(&k_sem, K_FOREVER);
printk("Worker Thread\n");
while (1);
}
K_WORK_DEFINE(my_work, my_work_handler);
セマフォギブしてスレッドの中身を進めると当然アウトですよ・・・
総括
つまり、何らかの理由で抜けられないスレッドができてしまった場合、タスクのスイッチが発生しないことになるため、メインとなるスレッドにWDTをクリアする命令を定期的に実行するようにしておけばよいということになります。
ここでいうメインとなるスレッドというのはメインキューもしくはシステムキューを指します
結論
WDTを使う場合は何も考えずにメインキュー内にwdt_feedを置けばいいと思われます。
そんな結論でいいんかよ!って思うわけですが、調べた結果としてそんな結論になったのできっといいはずです(笑)
ワケも分からずそうしているわけではない、というのは大きいですよ