CPLD でナイトライダー




CPLDで作ったナイトライダー

製作のきっかけ

昨年、大学の研究で設計したCPUのチップを1つ分けてもらいました。 動作確認は行っていませんが、いくつかバグがあることがわかっています。 また、初めて設計したチップだったのでノウハウもなく、シュミレーションも完璧でないまま 締め切りに追われてテープアウト(設計データを提出)したチップでした。

このようなチップなので、「動かないだろう」と思っていたのですが、もらって眺めているうちに、 試しに動かしてみたくなりました。

CPUの周辺回路は、汎用ロジックを集めて作るより、CPLDで作ってしまった方が楽そうです。 CPLDは、たくさんの組み合わせ回路(ゲート)とフリップフロップを持っていて、それらをプログラミングによってつなぎ変えることで目的の回路を作るというものです。FPGAの規模をずっと小さくしたようなものです。 今回使用したCPLDは、基板につけたまま何度も書き換えができるので、回路を簡単に変更できます。

ということで、CPLDのライターと練習用としてテスト基板を作ることにしました。

テスト基板は、簡単に作れるということで、「ナイトライダー」のランプに決めました。 LEDを直線にならべ、往復点灯させます。PICを使えばずっと楽に作れてしまいますが、ともかく練習 ということで作ってみました。

どのCPLDにしようか

CPLDは、ALTERA, Xilinx, Latticeなどが出しています。開発ツールも各社のホームページから 無料でダウンロードできます。まず、入手できるデバイスを使わなければならないので、部品店のホームページ を見て簡単に入手できるデバイスを調べました。

次に開発ツールです。ALTERAのものを使おうと思い、ツールをインストールしてみたのですが、 Verilog で記述できないのでやめました。結局、Xilinx のものを使うことにしました。

ライター製作

JTAGケーブル

CPLD への書き込みは、ライター(ダウンロードケーブル)を用いて行います。 ダウンロードケーブルは、パラレルポートにつないで使います。書き込みソフトは、開発ツールに付いています。

ダウンロードケーブルの回路図は、下記のリンクなどから入手できます。 3-stateバッファをいくつか使った、簡単な回路なので、すぐに製作できます。

回路を記述

CPLDの回路を記述する言語として、ABELなどがよく使われているようですが、 私は Verilog を使って記述しました。

クロックをカウントし、1024になったら点灯するLEDを1つ横に移動という動作をします。


module night_top ( led_out,clk);
    output [7:0] led_out;
    input clk;

	reg [9:0]	tcount;
	reg [2:0]	lpos;
	reg	up ;

	always@(posedge clk) begin
		tcount <= tcount + 1;
		if(tcount == 10'd0) begin
			if(lpos == 3'd0)
				up <= 1;
			if(lpos == 3'd7)
				up <= 0;

			if((up && lpos != 3'd7) || lpos == 3'd0)
				lpos <= lpos + 1;
			else
				lpos <= lpos - 1;
	 	end
	end

	 assign led_out[0] = (lpos == 3'd0) ? 1 : 0;
	 assign led_out[1] = (lpos == 3'd1) ? 1 : 0;
	 assign led_out[2] = (lpos == 3'd2) ? 1 : 0;
	 assign led_out[3] = (lpos == 3'd3) ? 1 : 0;
	 assign led_out[4] = (lpos == 3'd4) ? 1 : 0;
	 assign led_out[5] = (lpos == 3'd5) ? 1 : 0;
	 assign led_out[6] = (lpos == 3'd6) ? 1 : 0;
	 assign led_out[7] = (lpos == 3'd7) ? 1 : 0;

endmodule

これを論理合成(回路に変換)し、フィット(CPLDに内蔵されいてるゲートに割り当て)し、 書き込みデータを作って、書き込めば回路が動作します。

少々とまどったのは、リセットをどのようにかけるかわからなかったことです。


	always@(posedge clk) begin
		if(!rst) begin
			... リセット時の動作を記述
		end
		else begin
			... リセットでないときの動作を記述
		end
	end
のように記述したところ、シュミレーションで正しく動作しませんでした。

シュミレーションを行うときには、まず glbl.PRLD を1にし、しばらくして0にしなければなりませんでした。 たとえば、テストベンチを


	reg PRLD
	
	assign glbl.PRLD = PRLD;
	
	initial begin
		PRLD = 0;
		clk <= 0;
		rst <= 0;
		...その他の信号を初期化
		#20
		PRLD = 1;
		
		ここからシュミレーション開始
	end
のように記述すれば、シュミレーションを走らせることができました。

ところで今回の回路は、リセットをかける必要がないので、リセット端子はつけませんでした。

結局、テストベンチは、次のようにしました。



`timescale 1ns/1ns

module testbench;
	
	reg		clk;
	reg 	PRLD;
	wire [7:0] led_out;
	integer i;

	night_top night_top0(.led_out(led_out), .clk(clk));
	assign glbl.PRLD = PRLD;	

    initial begin
		PRLD = 1;
	  	clk <= 0;
		#20 PRLD = 0;
		for(i = 0; i < 1000000; i = i+1) begin
			#20
			clk <= 1;
			#20
		  	clk <= 0;
		end
	end

endmodule

シュミレーション結果 波形

シュミレーション結果は、このように表示されます。

使用するピンの位置を指定してからフィットすれば、ピン配置がバラバラになることはなくなります。 ただ、クロックやリセットなど、位置が決まっているピンがあるので注意が必要です。

ターゲット基板の製作

CPLDを載せ、LEDを点滅される基板を製作しました。開発ツールにあるChipViewerを使えば、ピン配置が表示されるので便利です。

クロックは、タイマーIC 555で15kHzくらいを作りました。

動作確認と感想

作った書き込みデータをデバイスへ書き込めば動作します。 大きな問題もなく、すぐに動作しました。

感想
初めてCPLDを使ってみたのですが、趣味の工作として使うとすると、PLCCのパッケージを使っているところが 気に入りませんでした。PLDDのソケットは、ピンが2列で四角形に配置しているので、内側の列に錫メッキ線で電源配線をするのが面倒でした。 高々44pinのデバイスの電源配線であることを考えると、あまりにも時間がかかり、「ちょっとCPLDを使う」という気にはなりません。DIPパッケージより小さいことはいいのですが。

プリント基板で製作する場合には、この手間はなくなるので、CPLDを使うのはいいと思いました。

また、今回作った回路はフリッププロップをクロックカウントに10個、LEDの位置保存に3個、向き保存に1個の合計14個使うつもりで論理記述しました。 これがレジスタ数最小となる設計だと思うのですが、論理合成しフィットした結果を見ると、マクロセルが22個も使われていました。

1つのマクロセルが、組み合わせ回路+その出力につながるフリッププロップという構成になっているため、 組み合わせ回路だけ使っているマクロセルが8個もできてしまいました。 この8個のマクロセルでは、4bitのLED位置をデコードしているのでしょう。

そこで、使用マクロセル数最小を目指した論理記述をしてみました。 LED1つにフリッププロップ1つを使う構成です。前の記述はLEDの位置を保存し、それをデコードしていましたが、 今度はそのデコード結果をフリッププロップに保存するような感じです。


module night_top ( led_out,clk);
    output [7:0] led_out;
    input clk;


	 
	reg [9:0]	tcount;
	reg [7:0]	led_out;
	reg	up ;

	initial begin
		tcount <= 10'd0;
		led_out <= 'd1;
		up <= 1'b1;
	end

	always@(posedge clk) begin
		tcount <= tcount + 1;
		if(tcount == 10'd0) begin
			if(led_out[0])
				up <= 1;
			if(led_out[7])
				up <= 0;

			casex(led_out)
			8'b0000000x : led_out <= 8'b00000010;
			8'b000000xx : led_out <= up ? 8'b00000100 : 8'b00000001;
			8'b00000xxx : led_out <= up ? 8'b00001000 : 8'b00000010;
			8'b0000xxxx : led_out <= up ? 8'b00010000 : 8'b00000100;
			8'b000xxxxx : led_out <= up ? 8'b00100000 : 8'b00001000;
			8'b00xxxxxx : led_out <= up ? 8'b01000000 : 8'b00010000;
			8'b0xxxxxxx : led_out <= up ? 8'b10000000 : 8'b00100000;
			8'bxxxxxxxx : led_out <= 8'b01000000;
			//default: led_out <= 8'b00000001;
			endcase
		 end
	end
endmodule

今度はフリッププロップ19個使っていますので、マクロセルも19個となることを期待したのですが、 そこまでは減らず、21個使う結果になりました。組み合わせ回路だけ使用するマクロセルが2つあります。 これらの組み合わせ回路は、きっと入りきらなかったのでしょう。

それはいいとしても、この結果をデバイスに書き込んでみると動かなくなってしまいました。 LED[0], [1], [2] ...と順に点灯していき、折り返して[7],[6],..と点灯してくるのですが、[2]がとばされてしまうのです。 [0],[1]...の順で増えながら点灯していくときは問題ないのですが、逆向きのときは[2]が点灯しません。 どうしてでしょう?  配置結果のシミュレーションでは正常に動作し、動作周波数は十分すぎるほど余裕があります。 シミュレーションで動いているので、厄介なエラーです。

原因はまだよくわかりません。 もっとデバイスの中身を知ってから作らなければいけないようです。

関連リンク

  • CPLDの説明があるページ

  • CPLDメーカ




    (c) 1999 Ishijima Seiichiro
  • 電子工作メニューへ

    ホームページへ