Nios2のusleep
先日の時間待ちの続き記事
組み込みソフトウェアにおける時間待ちは、リセット期間を待ったり、 I2CやSPIをGPIOで制御するときの波形を作成したりと、少なくとも指定した時間を確実に待って欲しいものです。
Nios2にはusec単位の待ちを実現するusleepという関数が提供されてますが、 これが次のような実装になっているため、 Nios2の動作周波数によっては指定した時間より短くなるようです。
__asm__ volatile ( "\n0:" "\n\taddi %0,%0, -1" "\n\tbgt %0,zero,0b" "\n1:" "\n\t.pushsection .debug_alt_sim_info" "\n\t.int 4, 0, 0b, 1b" "\n\t.popsection" :: "r" (us*(ALT_CPU_FREQ/(cycles_per_loop * 1000000))));
先日の記事と比較してもらえばわかると思いますが、 上記のコードではCPU動作周波数を"(cycles_per_loop * 1000000)"で割って端数切り捨てになっています。
cycles_per_loopは、Nios2がtinyコアの場合は9で、それ以外は3ですが、 商用であれば恐らくtinyコアを使っていないので"cycles_per_loop = 3"になるでしょう。
if (!strcmp(NIOS2_CPU_IMPLEMENTATION,"tiny")) { cycles_per_loop = 9; } else { cycles_per_loop = 3; }
この実装によって、例えば83333333Hzの場合は端数切り捨てで81000000Hzとなってしまいますので、 結果的にusleepでの待ち時間が指定した時間より短くなり、 制御上の仕様を満たせない場合も出てくるので非常に困ることになります(必要なリセット期間が短くなってしまう等)。
映像関係の機器では27MHzの逓倍を使うので3や9で割り切れてしまいます。 このため、実害がなく知らずに使用している人が多いのかもしれないですね...
なお、HAL API Reference, Nios II Software Developer's Handbook には以下のように説明されています。
int usleep (unsigned int us)
The usleep() function blocks until at least us microseconds have elapsed.