順序回路の基本! カウンタを作成しよう:触って学ぼう FPGA開発入門(3)(3/3 ページ)
4bitカウンタを作成して「順序回路」の基本をマスターしよう。また、スイッチによるカウントダウン動作や10進カウンタへの変更についても解説
スイッチによるカウントダウン動作を追加しよう
次は、スイッチにアップ・ダウンの機能を割り当てます。スイッチが押されたらカウントダウンするように変更してみましょう。
4bitアップ・ダウンカウンタのソース
4bitアップ・ダウンカウンタのVerilog-HDLソースを以下に示します。
1 module UPDOWN(RESET, CLK, DEC, COUNT); 2 input RESET, CLK, DEC; 3 output [3:0] COUNT; 4 5 reg [22:0] tmp_count; 6 reg [3:0] COUNT_TMP; 7 8 always @(posedge CLK or negedge RESET) 9 begin 10 if (RESET == 1'b0) 11 tmp_count <= 23'h000000; 12 else 13 tmp_count <= tmp_count + 23'h1; 14 end 15 16 assign DIVIDE_CLK = tmp_count[22]; 17 18 always @(posedge DIVIDE_CLK or negedge RESET) 19 begin 20 if (RESET == 1'b0) 21 COUNT_TMP <= 4'h0; 22 else if (DEC == 1'b1) 23 COUNT_TMP <= COUNT_TMP + 4'h1; 24 else 25 COUNT_TMP <= COUNT_TMP - 4'h1; 26 end 27 28 assign COUNT = ~COUNT_TMP; 29 30 endmodule
以下に重要と思われる部分を説明します。
1〜2行目
カウントダウンさせるためのスイッチを割り当てたので、入力ポート「DEC」を追加します。
6行目
出力する赤色LEDが負論理なので、最終的にカウントの値を反転させる必要があります。そのため一時的にカウンタの値を入れるための信号(変数)として「COUNT_TMP」をreg宣言します。
22〜25行目
「DEC」のスイッチが押されていないときは「1'b1」なので、カウントアップの動作を記述し、「DEC」のスイッチが押されているときは「1'b0」なので、カウントダウンの動作を記述します。
28行目
赤色LEDの出力が負論理なので、反転して出力します。
FPGAボード上での動作確認
それでは、完成したRTLをFPGAのボード上で動かしてみましょう。
リスト6のピンアサインを行って、論理合成、配置配線を実行します。
I/O Name | Loc |
---|---|
CLK | P39 |
RESET | P17 |
DEC | P16 |
COUNT<0> | P68 |
COUNT<1> | P67 |
COUNT<2> | P66 |
COUNT<3> | P65 |
1 NET "CLK" LOC = "P39" ; 2 NET "RESET" LOC = "P17" ; 3 NET "DEC" LOC = "P16" ; 4 NET "COUNT<0>" LOC = "P68" ; 5 NET "COUNT<1>" LOC = "P67" ; 6 NET "COUNT<2>" LOC = "P66" ; 7 NET "COUNT<3>" LOC = "P65" ;
特に問題がなければ、ボード上に設計データ(bitファイル)をダウンロードします。
いかがでしょうか? 今度は、何もしないと順調にカウントアップして、真ん中のスイッチを押し続けるとカウントダウンするはずです。
10進アップ・ダウンカウンタへ変更してみよう
ここまでは、単なるバイナリ(0〜F)のカウンタの動作でした。次に、カウンタを10進(0〜9)で動作させてみましょう。
10進アップ・ダウンカウンタのソース
10進アップ・ダウンカウンタのVerilog-HDLソースを以下に示します。
1 module UPDOWN(RESET, CLK, DEC, COUNT); 2 input RESET, CLK, DEC; 3 output [3:0] COUNT; 4 5 reg [22:0] tmp_count; 6 reg [3:0] COUNT_TMP; 7 8 always @(posedge CLK or negedge RESET) 9 begin 10 if (RESET == 1'b0) 11 tmp_count <= 23'h000000; 12 else 13 tmp_count <= tmp_count + 23'h1; 14 end 15 16 assign DIVIDE_CLK = tmp_count[22]; 17 18 always @(posedge DIVIDE_CLK or negedge RESET) 19 begin 20 if (RESET == 1'b0) 21 COUNT_TMP <= 4'h0; 22 else if (DEC == 1'b1) 23 if (COUNT_TMP == 4'h9) 24 COUNT_TMP <= 4'h0; 25 else 26 COUNT_TMP <= COUNT_TMP + 4'h1; 27 else 28 if (COUNT_TMP == 4'h0) 29 COUNT_TMP <= 4'h9; 30 else 31 COUNT_TMP <= COUNT_TMP - 4'h1; 32 end 33 34 assign COUNT = ~COUNT_TMP; 35 36 endmodule
以下に重要と思われる部分を説明します。
23〜31行目
カウントアップ時とカウントダウン時のどのタイミングで、0または9に戻すのかがポイントとなっています。プログラムが得意な方であれば、一度カウントアップするか、あるいはカウントダウンしてからその値を0ないし9に戻す、という方法を思い付くでしょう。その辺りの理屈は次回に譲ることにして、今回は、
- カウントアップ時:COUNT_TMPの値が9のときに0を代入
- カウントダウン時:COUNT_TMPの値が0のときに9を代入
と記述をします。
ポイントは、条件を分離してそれに対してif文をネスティング(入れ子)にして記述することです。通常は、順調にカウントアップしてほしいので、上記の条件がある意味「例外」です。このような条件では、if文をネスティングさせて優先順位付けをしっかりと行います(リスト5からの変更点はこれだけです)。
FPGAボード上での動作確認
完成したRTLをFPGAのボード上で動かしてみましょう。
リスト6をそのまま使用してピンアサイン、論理合成、配置配線を実行します。特に問題がなければ、ボード上に設計データ(bitファイル)をダウンロードします。
いかがでしょうか? 何もしない状態だと順調にカウントアップし、なおかつ9(両側の赤色LEDが点灯)の次が0(すべて消灯)になるはずです。また、真ん中のスイッチを押し続けるとカウントダウンし、なおかつ0(すべて消灯)の次が9(両側の赤色LEDが点灯)になるはずです。
次回は、FPGA設計を行ううえで基本となる「単相同期設計」を紹介します。また、分周クロックを使用せずにカウンタの動作を遅らせる方法とその際のシミュレーション方法について解説する予定です。(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.