だんだんむずかしくなってきましたが、がんばっていきましょう。今回は、モジュールを接続してみよう、という問題です。
問題が求めているものは、10 進カウンタのモジュールと 7 セグメント LED デコーダのモジュールとを接続したモジュールをつくれ、ということです。なので解答は、2 つのモジュールをそれぞれつくり、さらに上位のモジュールをつくって、上位のモジュールのなかで 2 つのモジュールをインスタンス化する、ということになります。
それは、たとえば Arduino のスケッチならば、ライブラリをインスタンス化して利用することと、似たような感じなのだと思います。
で、逆らっちゃうのですが、Arduino のスケッチで C++ クラスを使うとき、俺はスケッチを分割せずに書いてるわけで。それほど大きなスケッチ書くこともないし、そのほうが見通しがいいもんだから。おなじように、VerilogHDL でも、当面は 1 つのモジュールに全部の回路を書いちゃおうと思います。
そのうち、必要を感じたら、分割して記述することもあるでしょう。たぶん。
ちなみに、テストベンチも回路と一緒に書けるそうです。でも、これはさすがに、目的が違うだろうって感じるので、これまで同様に分けておきます。
接続された回路をシミュレーションする
ということで、タイトルが微妙に異なる (^_^;)
CLOCK をカウントし、7 セグメント LED に表示させる回路のシミュレーションをします。
CLOCK の立ち上がりエッジでカウントアップ。7 セグメント LED は HIGH で点灯、出力ビットはセグメント a が MSB、セグメント g が LSB で、ドットは使わない。
RESET はアクティブローです。
回路記述
前述したように、1 つのモジュールに回路を記述します。
- module COUNTER_7SEG (
- input wire CLOCK, RESET,
- output wire [6:0] OUTPUT
- );
モジュール名とポートリスト。信号の宣言も () 内でやっています。CLOCK と RESET はネット宣言の 1 ビット入力。OUTPUT はネット宣言の 7 ビット出力。
- reg [3:0] COUNTER_OUTPUT;
内部で使う信号の宣言。10 進カウンタの出力なので、変数宣言しています。4 ビットです。
- always @(posedge CLOCK) begin
- if(1'b0 == RESET)
- COUNTER_OUTPUT = 4'd0;
- else if(4'd9 == COUNTER_OUTPUT)
- COUNTER_OUTPUT = 4'd0;
- else
- COUNTER_OUTPUT = COUNTER_OUTPUT + 4'd1;
- end
10 進カウンタの構成要素。always による順序回路です。RESET が 0 のときと、9 のときに、0 に戻す、それ以外はカウントアップ。RESET は同期。
順序回路のときはノンブロッキング代入にすべきなのかもしれないですが、まだ学習してないので、ブロッキング代入になってる。ここでは、どっちでも影響ないんじゃないのかな。
always 内の処理が if 文ひとつだけなので、begin~end はなくてもいいそうです。が、ちゃんと形を整えておくのが吉。
- function [6:0] DECODER(
- input [3:0] DECODER_INPUT
- );
- begin
- case(DECODER_INPUT)
- 4'd0: DECODER = 7'b1111110;
- 4'd1: DECODER = 7'b0110000;
- 4'd2: DECODER = 7'b1101101;
- 4'd3: DECODER = 7'b1111001;
- 4'd4: DECODER = 7'b0110011;
- 4'd5: DECODER = 7'b1011011;
- 4'd6: DECODER = 7'b0011111;
- 4'd7: DECODER = 7'b1110000;
- 4'd8: DECODER = 7'b1111111;
- 4'd9: DECODER = 7'b1110011;
- default: DECODER = 7'b0000000;
- endcase
- end
- endfunction
7 セグメント LED デコーダの構成要素。function による組み合わせ回路です。DECODER_INPUT が入力として宣言されています。
ここも、function 内の処理が case 文ひとつだけなので、begin~end はなくてもいい。が、ちゃんと形を整えておくのが吉。
- assign OUTPUT = DECODER(COUNTER_OUTPUT);
- endmodule
function は呼び出して初めて仕事する。assign で DECODER() を呼び出して、OUTPUT に代入しています。
最後は endmodule で締めくくる、と。
テストベンチ
テストベンチファイルです。
- module COUNTER_7SEG_TEST;
- reg CLOCK, RESET;
- wire [6:0] OUTPUT;
- parameter CYCLE = 10;
モジュール名、入力と出力の宣言。パラメータはクロックの周期。
- COUNTER_7SEG COUNTER_7SEG(CLOCK, RESET, OUTPUT);
対象回路のインスタンス化。
- always begin
- CLOCK = 0; #(CYCLE/2);
- CLOCK = 1; #(CYCLE/2);
- end
クロックの生成。
- initial begin
- RESET = 0;
- #33 RESET = 1;
- #133 RESET = 0;
- #30 $finish;
- end
入力の印加。33 ユニット時間経過したら、カウントアップを開始する。133 ユニット時間カウントして、リセット。
- initial begin
- $dumpfile("counter_7seg.vcd");
- $dumpvars(0, COUNTER_7SEG_TEST);
- end
- endmodule
VCD ファイル出力。
$dumpvars は、VCD ファイルに出力する対象です。数字は階層レベルで、0 は全階層の全変数の意。
シミュレーション結果
まぁ、想定通りってことで。
カウントイネーブルつけたら、最後がリセットじゃなくて、4 を保持できるよなぁ、とか。
出力をみながらいろいろ考えちゃうようになってきたのは、少しばかり VerilogHDL が使えるようになってきた、ってことかしらん (^_^;)