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

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

7セグメントLEDを2つ使用して、60進のアップ・ダウンカウンタを作成。“無駄な資源を食わない回路”を設計するには? そのポイントを紹介

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

 連載第5回「階層構造を意識した設計スタイルとは?」では、10進のアップ・ダウンカウンタと7セグメントLEDのデコーダのモジュールをそれぞれインスタンスして接続し、動作させました。

 最終回となる今回は、7セグメントLEDを2つ使用して、60進のアップ・ダウンカウンタを作成します。「それほど難しくないだろう」と思われるかもしれませんが、実際に何の情報もないと結構手間取ります。今回の内容は、この連載で一番強調したかった“HDLによるハードウェア設計のエッセンス”が盛り込まれていますので期待してください。



ダイナミック点灯の原理

 今回使用している「EDX-002」は、ボード上での配線の引き回しを少なくするために、7セグメントLEDの点灯に“ダイナミック点灯”の手法を使用しています。具体的な原理は図1のとおりです。

ダイナミック点灯の原理 図1 ダイナミック点灯の原理

 「点灯させたい7セグメントLEDの選択を行う信号(SA)を出力し、その選択された7セグメントLEDに表示したい値を出力する」というのが基本動作です。また、EDX-002の仕様では選択されていない7セグメントLEDにはハイ・インピーダンスの信号を割り当てることになっています。

 この一連の動作をリスト1に示します。

1   module DCOUNT (CLK, ENABLE, L1, L2, L3, L4, SA, L);
2   input CLK, ENABLE;
3   input [7:0] L1, L2, L3, L4;
4   output [3:0] SA;
5   output [7:0] L;
6
7   parameter MAX_COUNT = 3'b111;
8   reg [2:0] sa_count_tmp;
9   reg [3:0] sa_count;
10  reg [7:0] L_tmp;
11
12  assign SA[3] = (sa_count[3]==1'b0)? 1'b0 : 1'bz;
13  assign SA[2] = (sa_count[2]==1'b0)? 1'b0 : 1'bz;
14  assign SA[1] = (sa_count[1]==1'b0)? 1'b0 : 1'bz;
15  assign SA[0] = (sa_count[0]==1'b0)? 1'b0 : 1'bz;
16  assign L = L_tmp;
17
18  always @(posedge CLK)
19  begin
20     if (ENABLE==1'b1)
21         if (sa_count_tmp==MAX_COUNT)
22             sa_count_tmp <= 3'b000;
23         else
24             sa_count_tmp <= sa_count_tmp + 1'b1;
25  end
26
27  always @(posedge CLK)
28  begin
29     if (sa_count_tmp[0]==1'b0)
30         begin
31            sa_count <= 4'b1111;L_tmp <= L_tmp;
32         end
33     else
34         case (sa_count_tmp[2:1])
35               2'b00:begin
36                         sa_count <= 4'b1110;L_tmp <= L4;
37                      end
38               2'b01:begin
39                         sa_count <= 4'b1101;L_tmp <= L3;
40                      end
41               2'b10:begin
42                         sa_count <= 4'b1011;L_tmp <= L2;
43                      end
44               2'b11:begin
45                         sa_count <= 4'b0111;L_tmp <= L1;
46                      end
47             default:begin
48                         sa_count <= 4'bxxxx;L_tmp <= 8'bxxxxxxxx;
49                     end
50         endcase
51  end
52
53  endmodule
リスト1 ダイナミック点灯させるためのモジュール(dcount.v

18〜25行目

 sa_count_tmpという3bitのカウンタで0〜7までカウント。このときにENABLE信号(kHzオーダーで1回有効になる信号)が‘1’のときにのみカウントアップ

27〜51行目

 このカウンタの値が奇数(sa_count_tmp[0]が‘1’)のときに、出力したい7セグメントLEDのデコーダの信号を選択

12〜15行目

 選択していない7セグメントLEDにはハイ・インピーダンスを出力

 今回の7セグメントLEDの2けたの点灯には、このモジュール(リスト1)を用います。

回路構成の検討

 まず、60進カウンタを設計するに当たり、どのようにこのカウンタを構成するのかを考えてみましょう。

 単純に考えると「0〜59」、すなわち60回カウントすればいいので、「6bitのカウンタを作成すれば簡単に実現できる! 」と思い付きます。

 しかし、最終的に7セグメントLEDに出力するためには、10の位と1の位に6bitのカウンタの値を分けなくてはなりません。

 分ける方法はいくつかありますが、ソフトウェア開発が得意な方の場合、

  • 1の位:60進カウンタ%10(10で割った余り)
  • 10の位:60進カウンタ/10(10で割った商)

で、求めることを思い浮かべるのではないでしょうか? 確かに、アルゴリズム的には正しいのですが、残念ながら上記のような「%」や「/」の使い方は、論理合成ツールがサポートしていません。つまり、回路が作成できないので、RTLではないということになります。ということで、この方法は「使用不可」です。

 気を取り直して、さらに考えてみましょう。

 次に思い付くのは、「6bitのカウンタの値を10の位と1の位にデコードして分ける」という方法です(リスト2)。

(省略)
8  always @(count60)
9     case (count60)
10          6'd0:{cnt10,cnt1}=7'h0_0;
11          6'd1:{cnt10,cnt1}=7'h0_1;
12          6'd2:{cnt10,cnt1}=7'h0_2;
13          6'd3:{cnt10,cnt1}=7'h0_3;
14          6'd4:{cnt10,cnt1}=7'h0_4;
15          6'd5:{cnt10,cnt1}=7'h0_5;
16          6'd6:{cnt10,cnt1}=7'h0_6;
17          6'd7:{cnt10,cnt1}=7'h0_7;
18          6'd8:{cnt10,cnt1}=7'h0_8;
19          6'd9:{cnt10,cnt1}=7'h0_9;
20          6'd10:{cnt10,cnt1}=7'h1_0;
21          6'd11:{cnt10,cnt1}=7'h1_1;
22          6'd12:{cnt10,cnt1}=7'h1_2;
23          6'd13:{cnt10,cnt1}=7'h1_3;
24          6'd14:{cnt10,cnt1}=7'h1_4;
25          6'd15:{cnt10,cnt1}=7'h1_5;
26          6'd16:{cnt10,cnt1}=7'h1_6;
27          6'd17:{cnt10,cnt1}=7'h1_7;
28          6'd18:{cnt10,cnt1}=7'h1_8;
29          6'd19:{cnt10,cnt1}=7'h1_9;
30          6'd20:{cnt10,cnt1}=7'h2_0;
31          6'd21:{cnt10,cnt1}=7'h2_1;
32          6'd22:{cnt10,cnt1}=7'h2_2;
33          6'd23:{cnt10,cnt1}=7'h2_3;
34          6'd24:{cnt10,cnt1}=7'h2_4;
35          6'd25:{cnt10,cnt1}=7'h2_5;
36          6'd26:{cnt10,cnt1}=7'h2_6;
37          6'd27:{cnt10,cnt1}=7'h2_7;
38          6'd28:{cnt10,cnt1}=7'h2_8;
39          6'd29:{cnt10,cnt1}=7'h2_9;
40          6'd30:{cnt10,cnt1}=7'h3_0;
41          6'd31:{cnt10,cnt1}=7'h3_1;
42          6'd32:{cnt10,cnt1}=7'h3_2;
43          6'd33:{cnt10,cnt1}=7'h3_3;
44          6'd34:{cnt10,cnt1}=7'h3_4;
45          6'd35:{cnt10,cnt1}=7'h3_5;
46          6'd36:{cnt10,cnt1}=7'h3_6;
47          6'd37:{cnt10,cnt1}=7'h3_7;
48          6'd38:{cnt10,cnt1}=7'h3_8;
49          6'd39:{cnt10,cnt1}=7'h3_9;
50          6'd40:{cnt10,cnt1}=7'h4_0;
51          6'd41:{cnt10,cnt1}=7'h4_1;
52          6'd42:{cnt10,cnt1}=7'h4_2;
53          6'd43:{cnt10,cnt1}=7'h4_3;
54          6'd44:{cnt10,cnt1}=7'h4_4;
55          6'd45:{cnt10,cnt1}=7'h4_5;
56          6'd46:{cnt10,cnt1}=7'h4_6;
57          6'd47:{cnt10,cnt1}=7'h4_7;
58          6'd48:{cnt10,cnt1}=7'h4_8;
59          6'd49:{cnt10,cnt1}=7'h4_9;
60          6'd50:{cnt10,cnt1}=7'h5_0;
61          6'd51:{cnt10,cnt1}=7'h5_1;
62          6'd52:{cnt10,cnt1}=7'h5_2;
63          6'd53:{cnt10,cnt1}=7'h5_3;
64          6'd54:{cnt10,cnt1}=7'h5_4;
65          6'd55:{cnt10,cnt1}=7'h5_5;
66          6'd56:{cnt10,cnt1}=7'h5_6;
67          6'd57:{cnt10,cnt1}=7'h5_7;
68          6'd58:{cnt10,cnt1}=7'h5_8;
69          6'd59:{cnt10,cnt1}=7'h5_9;
70       default:{cnt10,cnt1}=7'hx;
71    endcase
(省略)
リスト2 10の位と1の位分離デコーダ部分(cnt60_dec.v

 しかし、この方法では図2のように7セグメントLEDのデコーダの前に1の位と10の位を分けるデコーダが入るので、回路が大きくなり、LEDの出力まで考えるといままでのデコーダ出力よりも遅くなってしまいます(今回のようなLED表示に関しては、本来それほど神経をとがらせる必要はありません)。つまり、今回の場合には60進カウンタを作成するという方法は、あまり得策とはいえません。

60進カウンタを作成したときのハードウェア構成 図2 60進カウンタを作成したときのハードウェア構成

 そこで、いちいち10の位と1の位を分けるデコーダを作成することがないように、カウンタをあらかじめ「10進カウンタ」と「6進カウンタ」に分けて設計します(図3)。こうすれば、いままでと同様にカウンタの出力をそのまま7セグメントLEDのデコーダに接続できます。

10進と6進カウンタを作成したときのハードウェア構成 図3 10進と6進カウンタを作成したときのハードウェア構成

       1|2|3|4 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.