順序回路のややこしさ
順序回路では、「1クロックですべてが並列に変化する」という挙動が、(S/W言語に慣れている人間にとっては)思わぬバグの原因となります。ここでは例を使って説明します。
試しに、0~2をループするカウンターcntと、cntが2のときに1になるisTwoを考えてみます。
reg cnt[1:0]; //ビット幅2bit
reg isTwo; //ビット幅1bit
always @(posedge CLK) begin
if (cnt < 2) cnt <= cnt + 1; //cntは0~2をループ
else cnt <= 0;
end
always @(posedge CLK) begin
isTwo <= (cnt==2); //cntが2の時に1を代入する
end
regはレジスター(信号線)の宣言で、always文の中身は指定した信号(今回はCLK)の立ち上がり(posedge)のたびに実行されます。
正しく書けているように見えますが、実際は意図した通りには動いてくれません。
この回路では、isTwoが1になるのは、cntが0のときになってしまうのです。なぜでしょうか。次のグラフを見てください。
順序回路では、評価の結果は次のクロックに反映されます。cnt==2が評価された次のクロックには、既にcntは0に戻ってしまっている、というわけです。これを正しく動作させるためには、cntがインクリメントされるのを先読みして、cnt==1と書かなければいけません。
ちなみに、上のような図を「タイミングチャート」と呼びます。順序回路では、タイミングチャートで動作を全体的に眺めながら設計できます。
このように、順序回路はS/Wの記述と比べて少し独特なので、慣れないとすぐに意図とは違う論理を書いてしまうのです。