回路構成が決まったら早速設計に入ります。ここでは、本連載の集大成ともいうべきエッセンスを紹介します。
この「10進のカウンタ」と「6進のカウンタ」を分けて設計したときの最大のポイントは、10進カウンタから6進カウンタへのけた上げ信号、つまり「キャリー」をいかに記述するかです。キャリーが図4のようにアップカウントする場合は、10進カウンタの値が‘9’になったときです。
この考えから「キャリー信号の代入は“if (COUNT_TMP == 4'h9)”となっているところで行えば良い! 」と思うでしょう。つまり、前回のリスト2に変更を加えて、リスト3のような記述をイメージすると思います。
(省略) 28 always @(posedge CLK or negedge RESET) 29 begin 30 if (RESET == 1'b0) 31 begin 32 COUNT_TMP <= 4'h0; 33 CARRY <= 1'b0; 34 end 35 else if (ENABLE == 1'b1) 36 // else if (DEC == 1'b1) 37 if (DEC == 1'b1) 38 if (COUNT_TMP == 4'h9) 39 begin 40 COUNT_TMP <= 4'h0; 41 CARRY <= 1'b1; 42 end 43 else 44 begin 45 COUNT_TMP <= COUNT_TMP + 4'h1; 46 CARRY <= 1'b0; 47 end 48 else 49 if (COUNT_TMP == 4'h0) 50 begin 51 COUNT_TMP <= 4'h9; 52 CARRY <= 1'b1; 53 end 54 else 55 begin 56 COUNT_TMP <= COUNT_TMP - 4'h1; 57 CARRY <= 1'b0; 58 end 59 end (省略)
リスト3 不正なキャリーの作り方(UPDOWN10.v) |
一見するとこれで良さそうに思えますが、これが落とし穴の始まりです……。ここにキャリーの代入を書いて、シミュレーションしてみると画面1のようになります。
アップカウントの値が‘0’のときと、ダウンカウントの値が‘9’のときににキャリーが出力されています……。ここで「なぜこうなるんだろう? 」と追究していただければよいのですが、「じゃあ、10進のカウンタの値を‘9’で見ているのが悪いので、‘8’で見ればOKだろう! 」と、リスト4のように力ずくで記述を変更すると画面2のような結果になってしまいます。
(省略) 28 always @(posedge CLK or negedge RESET) 29 begin 30 if (RESET == 1'b0) 31 begin 32 COUNT_TMP <= 4'h0; 33 CARRY <= 1'b0; 34 end 35 else if (ENABLE == 1'b1) 36 // else if (DEC == 1'b1) 37 if (DEC == 1'b1) 38 if (COUNT_TMP == 4'h8) 39 begin 40 COUNT_TMP <= 4'h0; 41 CARRY <= 1'b1; 42 end 43 else 44 begin 45 COUNT_TMP <= COUNT_TMP + 4'h1; 46 CARRY <= 1'b0; 47 end 48 else 49 if (COUNT_TMP == 4'h1) 50 begin 51 COUNT_TMP <= 4'h9; 52 CARRY <= 1'b1; 53 end 54 else 55 begin 56 COUNT_TMP <= COUNT_TMP - 4'h1; 57 CARRY <= 1'b0; 58 end 59 end (省略)
リスト4 不正な変更によるキャリーの作り方(UPDOWN10-2.v) |
確かに‘8’の次の値でキャリーは‘1’になりましたが、10進カウンタが9進になってしまいました……。これはいけません。
次に「アップカウント時に10進を戻す値のデコードを‘9’にして、キャリーの生成を‘8’でデコードすればOKだ! 」と思い付くのではないでしょうか。その記述をリスト5に示します。
(省略) 28 always @(posedge CLK or negedge RESET) 29 begin 30 if (RESET == 1'b0) 31 begin 32 COUNT_TMP <= 4'h0; 33 CARRY <= 1'b0; 34 end 35 else if (ENABLE == 1'b1) 36 // else if (DEC == 1'b1) 37 if (DEC == 1'b1) 38 begin 39 if (COUNT_TMP == 4'h9) 40 COUNT_TMP <= 4'h0; 41 else 42 COUNT_TMP <= COUNT_TMP + 4'h1; 43 if (COUNT_TMP == 4'h8) 44 CARRY <= 1'b1; 45 else 46 CARRY <= 1'b0; 47 end 48 else 49 begin 50 if (COUNT_TMP == 4'h0) 51 COUNT_TMP <= 4'h9; 52 else 53 COUNT_TMP <= COUNT_TMP - 4'h1; 54 if (COUNT_TMP == 4'h1) 55 CARRY <= 1'b1; 56 else 57 CARRY <= 1'b0; 58 end 59 end (省略)
リスト5 不正なハードウェア構成によるキャリーの作り方(UPDOWN10-3.v) |
これをシミュレーションすると、確かに10進カウンタの動作で、キャリーも思っていたとおりのタイミングで出力されます(画面3)。
「めでたし、めでたし」といいたいところですが、ハードウェア設計としては実はこのやり方は“最低の対処方法”です。
この設計方法のハードウェア構成は図5のようになります。
図5のハードウェア構成ですと、アップカウント時のカウンタの値を‘8’と比較して、その結果をさらにF/Fで受けて出力しています。そうです。このF/Fによりキャリー信号が1サイクル遅れてしまうのです。
なぜ、F/Fが使われたのでしょうか?
なぜなら、always @(posedge CLK .....)と記述されている部分は、その出力、つまり代入されている左辺の信号に必ずF/Fが付いてくるからです。
元の設計では、このキャリー信号を組み合わせ回路の出力として作成するべきでした(図6)。つまり、キャリー信号の作成は、この順序回路になるalways文中で行ってはいけないのです。
ここが普通のソフトウェアプログラミングとの大きな違いです。“HDLを記述するときには、順序回路を記述しているのか、組み合わせ回路を記述しているのかを意識して記述する必要がある”のです。これが今回の連載の最大のポイントです。
Copyright © ITmedia, Inc. All Rights Reserved.