このドキュメントは http://icrus.org/fpga_atlys_design/ 上にあります.
本日はダイナミック点灯,時計回路(時計ブロックダイアグラム),単相同期回路について解説と実習を行います.先ずは,前回のカウンタ表示の答え合わせからです. 皆さんはすでに,7セグLEDの表示パターンを確認して,ここまでできているはずです.
001 `timescale 1ns / 1ps
002 //////////////////////////////////////////////////////////////////////////////////
003 // Create Date: 19:23:52 10/21/2015
004 // Module Name: seg7_counter6_10
005 //////////////////////////////////////////////////////////////////////////////////
006 module seg7_counter6_10(
007 input wire clk0,
008 output wire [7:0] seg7,
009 output wire [3:0] line,
010 output wire [6:0] led
011 );
012 assign line=c[20]?4'b0001:4'b0010; // ←変更
013 assign led[6:4] = count6;
014 assign led[3:0] = count10;
015 reg[26:0] c=0;
016
017 assign seg7[6:0] = disp;
018 assign seg7[7] = 0;
019
020 always @( posedge clk0 )begin
021 if( c==27'd99999999 )
022 c <= 0;
023 else
024 c <= c + 1'b1;
025 end
026
027 reg[3:0] count10=4'b0;
028 reg[6:0] disp=7'b0;
029
030 always @( negedge c[26] )begin
031 if(count10==4'd0)
032 disp<=7'b0111111;
033 else if(count10==4'd1)
034 disp<=7'b0000110;
035 else if(count10==4'd2)
036 disp<=7'b1011011;
037 else if(count10==4'd3)
038 disp<=7'b1001111;
039 else if(count10==4'd4)
040 disp<=7'b1100110;
041 else if(count10==4'd5)
042 disp<=7'b1101101;
043 else if(count10==4'd6)
044 disp<=7'b1111101;
045 else if(count10==4'd7)
046 disp<=7'b0100111;
047 else if(count10==4'd8)
048 disp<=7'b1111111;
049 else if(count10==4'd9)
050 disp<=7'b1101111;
051
052 if( count10==4'd9 )
053 count10<=1'b0;
054 else
055 count10 <= count10 + 1'b1;
056 end
057
058 reg[2:0] count6=3'b0;
059 always @( negedge count10[3] )begin
060 if( count6==3'd5 )
061 count6 <= 1'b0;
062 else
063 count6 <= count6 + 1'b1;
064 end
065
066 endmodule
seg7_counter6_10.ucfはseg7_counter6.ucfと同じです.
reg[6:0] disp2=7'b0;
if(count6==4'd0)
disp2<=7'b0111111;
else if(count6==4'd1)
disp2<=7'b0000110;
else if(count6==4'd2)
disp2<=7'b1011011;
else if(count6==4'd3)
disp2<=7'b1001111;
else if(count6==4'd4)
disp2<=7'b1100110;
else if(count6==4'd5)
disp2<=7'b1101101;
assign seg7[6:0] = (line==4'b0001)?disp:disp2;
3カ所の変更で60進カウンタの2桁表示ができます.
ここまでをまとめて,case文なども使い少しきれいに書いてみました.
001 `timescale 1ns / 1ps
002 //////////////////////////////////////////////////////////////////////////////////
003 // Create Date: 20:11:40 10/21/2015
004 // Module Name: seg7_drive
005 //////////////////////////////////////////////////////////////////////////////////
006 module seg7_drive(
007 input wire clk0,
008 output wire [7:0] seg7,
009 output wire [3:0] line,
010 output wire [6:0] led
011 );
012 assign line = 4'b0001<<ab;
013 assign led = { 1'b0, count6, count10 };
014 assign seg7 = { 1'b0, disp };
015
016 // 表示
017 reg[6:0] disp=7'b0;
018 reg[3:0] x;
019 reg ab=0;
020 always @( negedge c[20] )begin
021 x <= ab?count6:count10;
022 case(x)
023 4'b0000 : disp <= 7'b1110000; //0
024 4'b0001 : disp <= 7'b0001111; //1
025 4'b0010 : disp <= 7'b1110000; //2
026 4'b0011 : disp <= 7'b0001111; //3
027 4'b0100 : disp <= 7'b1110000; //4
028 4'b0101 : disp <= 7'b0001111; //5
029 4'b0110 : disp <= 7'b1110000; //6
030 4'b0111 : disp <= 7'b0001111; //7
031 4'b1000 : disp <= 7'b1110000; //8
032 4'b1001 : disp <= 7'b0001111; //9
033 default : disp <= 7'b0000000;
034 endcase
035
036 ab <= ab+1'b1;
037 end
038
039 // 1秒生成
040 reg[26:0] c=27'b0;
041 always @( posedge clk0 ) c <= (c==27'd99999999) ? 1'b0 : (c+1'b1);
042
043 // 10進カウンタ
044 reg[3:0] count10=4'b0;
045 always @(negedge c[26]) count10 <= (count10==27'd9) ? 1'b0 : (count10+1'b1);
046
047 // 6進カウンタ
048 reg[2:0] count6=3'b0;
049 always @( negedge count10[3] ) count6 <= (count6==27'd9) ? 1'b0 : (count6+1'b1);
050
051 endmodule
052
なお,seg7_drive.ucfはseg7_counter6.ucfと同じです.
リスト3-1 counter6_10_ssd2.v,リスト3-2 ssd_drive.vを合成(コンパイル)すると次のようなワーニングが出ます.
WARNING:PhysDesignRules:372 - Gated clock. Clock net count10<3> is sourced by a combinatorial pin. This is not good design practice. Use the CE pin to control the loading of data into the flip-flop.
翻訳すると
警告:PHYSデザインルール:372 - ゲーテッドクロック。時計ネットcount10(3)は、コンビナトリアルピンによってソースされます。これは良いデザインの練習ではありません。フリップフロップへのデータのロードを制御するためにCEピンを使用してください。
だそうです.要約すると『count10(3)はalways文の入力としてはふさわしくない』と言うことです.FPGA回路は単相同期回路として設計しなければなりません.このワーニングはこれが成立していないことをあらわします.
話が後先になりましたが,単相同期回路とは何でしょう.ここでは
『複数の回路ブロックがクロックにちゃんと同期していないと,動きがばらばらになってタイミングに起因した解決できないバグを生むことがあり, そうならないように各回路ブロックがちゃんとクロックに同期するように設計した回路』
とでも定義しましょう.(ISEが生成した回路はalwaysブロック単位でそれぞれが回路ブロックになるように合成されます.)参考サイト http://monoist.atmarkit.co.jp/mn/articles/0704/12/news121.html
ではseg7_drive.vを単相同期回路で書き直します.その上で単相同期回路について説明していきます.
001 `timescale 1ns / 1ps
002 //////////////////////////////////////////////////////////////////////////////////
003 // Create Date: 21:21:19 10/21/2015
004 // Module Name: seg7_drive_sp
005 //////////////////////////////////////////////////////////////////////////////////
006 module seg7_drive_sp(
007 input wire clk0,
008 output wire [7:0] seg7,
009 output wire [3:0] line,
010 output wire [6:0] led
011 );
012 assign line = 4'b0001<<ab;
013 assign led = { 1'b0, count6, count10 };
014 assign seg7 = { 1'b0, disp };
015
016 // ダイナミック表示
017 reg[6:0] disp=7'b0;
018 reg[3:0] x;
019 reg ab=0;
020 always @( posedge clk0 )begin
021 if(c[19:0]==0)begin
022 x <= ab?count6:count10;
023 case(x)
024 4'b0000 : disp <= 7'b0000001; //0
025 4'b0001 : disp <= 7'b0000010; //1
026 4'b0010 : disp <= 7'b0000100; //2
027 4'b0011 : disp <= 7'b0001000; //3
028 4'b0100 : disp <= 7'b0010000; //4
029 4'b0101 : disp <= 7'b0100000; //5
030 4'b0110 : disp <= 7'b1000000; //6
031 4'b0111 : disp <= 7'b0000110; //7
032 4'b1000 : disp <= 7'b0001100; //8
033 4'b1001 : disp <= 7'b0011000; //9
034 default : disp <= 7'b0000000;
035 endcase
036 ab <= ab + 1'b1;
037 end
038 end
039
040 // 1秒生成
041 reg[26:0] c=27'b0;
042 reg sec_enable=1'b0;
043 always @( posedge clk0 )begin
044 c <= ( c==27'd99999999 )?1'b0:(c+1'b1);
045 sec_enable <= ( c==27'd99999999 )?1'b1:1'b0;
046 end
047
048 // 10進カウンタ
049 reg[3:0] count10=4'b0;
050 reg sec10_enable = 1'b0;
051 always @( posedge clk0 )begin
052 if( sec_enable )begin
053 count10 <= ( count10==4'd9 )?1'b0:(count10+1'b1);
054 sec10_enable <= ( count10==4'd9 )?1'b1:1'b0;
055 end
056 else begin
057 sec10_enable <= 1'b0;
058 end
059 end
060
061 // 6進カウンタ
062 reg[2:0] count6=3'b0;
063 always @( posedge clk0 )
064 if( sec10_enable ) count6<=(count6==3'd5)?1'b0:(count6+1'b1);
065
066 endmodule
このように全てのalwaysブロックを同じクロック(clk0)で動作させ,同期を保証するするのが単相同期回路です
seg7_counter10.ucf同等
時計回路としては欠かせない24進カウンタを作りましょう.
ここまでの話を総合的に理解できていれば,皆さんはこれらのノウハウのみでFPGAを使ってディジタル時計を作成できます.
最後にヒント的に30進カウンタと24進カウンタを書きますが, 時間があったら(時間がなくても)必ず自分で考えて作成し,答え合わせのためだけにリンク先のプログラムを利用してください.
10進カウンタと3進カウンタを使用して30進カウンタを作成せよ.その際,作成する回路は単相同期回路とする.
問題1の回路を利用して24進カウンタを作成せよ.
以上,今回はダイナミック点灯と単相同期回路の話しでした.
次回は,『4桁7セグLED』を使って本格的24時間表示時計を作成しましょう.
といっても,学ぶべきノウハウはすでに全てお伝えしましたので,『4桁7セグLEDの表示回路』の回路図(ブレッドボード工作用)と部品さえあれば出来上がりますのでこれらを提供します.プログラムは表示回路動作チェックプログラムのみ配布します.インターネット・サイトを見ないで書籍も見ないで(参照しないで)自己流で構いませんので『自作』してみてください.
ssatoh@
足立工科大学 工学部 情報通信工学科