連載
» 2007年06月15日 00時00分 公開

触って学ぼう FPGA開発入門(6):順序回路と組み合わせ回路を意識した記述を! (2/4)

[鳥海佳孝 設計アナリスト,@IT MONOist]

キャリーによるけた上げ

 回路構成が決まったら早速設計に入ります。ここでは、本連載の集大成ともいうべきエッセンスを紹介します。

 この「10進のカウンタ」と「6進のカウンタ」を分けて設計したときの最大のポイントは、10進カウンタから6進カウンタへのけた上げ信号、つまり「キャリー」をいかに記述するかです。キャリーが図4のようにアップカウントする場合は、10進カウンタの値が‘9’になったときです。


キャリーによるけた上げ 図4 キャリーによるけた上げ

 この考えから「キャリー信号の代入は“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のようになります。

不正なタイミングでのキャリーによるけた上げ 画面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

不正なタイミングでのキャリーによるけた上げと不正なカウンタの動作 画面2 不正なタイミングでのキャリーによるけた上げと不正なカウンタの動作

 確かに‘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)。

正確なタイミングでのキャリーによるけた上げと正確なカウンタの動作 画面3 正確なタイミングでのキャリーによるけた上げと正確なカウンタの動作

 「めでたし、めでたし」といいたいところですが、ハードウェア設計としては実はこのやり方は“最低の対処方法”です。

 この設計方法のハードウェア構成は図5のようになります。

誤った60進カウンタのハードウェア構成 図5 誤った60進カウンタのハードウェア構成

 図5のハードウェア構成ですと、アップカウント時のカウンタの値を‘8’と比較して、その結果をさらにF/Fで受けて出力しています。そうです。このF/Fによりキャリー信号が1サイクル遅れてしまうのです。

 なぜ、F/Fが使われたのでしょうか?

 なぜなら、always @(posedge CLK .....)と記述されている部分は、その出力、つまり代入されている左辺の信号に必ずF/Fが付いてくるからです。

 元の設計では、このキャリー信号を組み合わせ回路の出力として作成するべきでした(図6)。つまり、キャリー信号の作成は、この順序回路になるalways文中で行ってはいけないのです。

正しい60進カウンタのハードウェア構成 図6 正しい60進カウンタのハードウェア構成

 ここが普通のソフトウェアプログラミングとの大きな違いです。“HDLを記述するときには、順序回路を記述しているのか、組み合わせ回路を記述しているのかを意識して記述する必要がある”のです。これが今回の連載の最大のポイントです。

Copyright © ITmedia, Inc. All Rights Reserved.