第3回 結果を得る(ダイナミック点灯,時計回路,単相同期回路)
アインシュタインの公式
\[ R_{\mu\nu}-\frac{1}{2}Rg_{\mu\nu}+\Lambda g_{\mu\nu}=\frac{8\pi G}{c^4}T_{\mu\nu} \]それでどうした?

内 容


3-1 ダイナミック点灯(1)

本日はダイナミック点灯,時計回路(時計ブロックダイアグラム),単相同期回路について解説と実習を行います.先ずは,前回のカウンタ表示の答え合わせからです. 皆さんはすでに,7セグLEDの表示パターンを確認して,ここまでできているはずです.

回答

リスト3-1 counter6_10.v


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と同じです.

解説

改造手順

  • 029にdisp2を宣言
  • 
    	reg[6:0] disp2=7'b0;
    
  • 050 051間でdisp2にcount6の値を代入
  • 
    		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;
    
  • 017をlineの値で切り換えられるように変更
  • 
    	assign seg7[6:0] = (line==4'b0001)?disp:disp2; 
    

    3カ所の変更で60進カウンタの2桁表示ができます.

    ごちゃごちゃしてきたプログラムをきれいにまとめる

    ここまでをまとめて,case文なども使い少しきれいに書いてみました.

    リスト3-2 seg7_drive.v

    
    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-2 単相同期回路

    リスト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を単相同期回路で書き直します.その上で単相同期回路について説明していきます.

    リスト3-3 seg7_drive_sp.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
    

    解説

    1. まず020,043,051,063のalwaysブロックを全てclk0(クロック)の立ち上がりで起動するようにします.このように,共通の1つのクロック立ち上がり(clk0)を使って全てのalwaysブロックを起動します.それぞれのalwaysブロックは完全に同期して起動されることになります.
    2. そのため,これまで,カウンタ用のregから桁を選び出して,alwaysの起動のために利用してきた『繰り上がり信号』を別途用意してあげる必要が出てきます.

    3. 042 1秒信号(1秒に1クロックだけ1になる)のためにreg sec_enableを新たに定義します.
    4. 045 カウンタが規定の値に達したときだけ,sec_enableを1にしています.他は0
    5. このため,次のclk0の立ち上がりタイミングで,sec_enableが1になります.
    6. これを次の10進カウンタで利用します.

    7. 050 10秒信号(10秒に1クロックだけ1になる)のためにreg sec10_enablを定義します
    8. 052 10進カウンタはsec_enableが1の時のみ動作させます.(秒信号が来ているときのみ)
    9. 054 そして10進カウンタが規定の値に達したときのみ,sec10_enableを1にします.
    10. 057 そのほかの場合はsec10_enableを0にします.

    11. 054 6進カウンタはsec10_enableが1の時のみ動作させます.(10秒信号が来ているときのみ)

    このように全てのalwaysブロックを同じクロック(clk0)で動作させ,同期を保証するするのが単相同期回路です

    リスト3-4 seg7_drive_sp.ucf

    seg7_counter10.ucf同等

    3-3 時計回路

    時計回路としては欠かせない24進カウンタを作りましょう.

    ここまでの話を総合的に理解できていれば,皆さんはこれらのノウハウのみでFPGAを使ってディジタル時計を作成できます.

    最後にヒント的に30進カウンタと24進カウンタを書きますが, 時間があったら(時間がなくても)必ず自分で考えて作成し,答え合わせのためだけにリンク先のプログラムを利用してください.

    問題 1

    10進カウンタと3進カウンタを使用して30進カウンタを作成せよ.その際,作成する回路は単相同期回路とする.

    回答

    count30.v

    問題 2

    問題1の回路を利用して24進カウンタを作成せよ.

    回答

    count24.v



    以上,今回はダイナミック点灯と単相同期回路の話しでした.

    次回は,『4桁7セグLED』を使って本格的24時間表示時計を作成しましょう.

    といっても,学ぶべきノウハウはすでに全てお伝えしましたので,『4桁7セグLEDの表示回路』の回路図(ブレッドボード工作用)と部品さえあれば出来上がりますのでこれらを提供します.プログラムは表示回路動作チェックプログラムのみ配布します.インターネット・サイトを見ないで書籍も見ないで(参照しないで)自己流で構いませんので『自作』してみてください.


    ssatoh@

    足立工科大学 工学部 情報通信工学科