2012年8月30日木曜日

週刊 TD4をFPGAで創る 第2号 「ROMを創る」

週刊 TD4をFPGAで創る 第2号 「ROMを創る」

さぁ,実装するでございます。

第5章からです。
え,ROMって作れないんじゃなかったのって気がしますが,
実はこの時点では機械スイッチを使ってることをすっかり忘れていてまして。
74HC540と74HC154作っちゃたので載せます。

下の方にある「3.ROM」の部分までは読み飛ばしてOKですよ?

1.74HC540

バスバッファ8個入り!
Gの値によってはハイ・インピーダンスになるのですが,その場合は「1'hz」と書きます。

module LOGIC_74HC540(G1, G2, A1, A2, A3, A4, A5, A6, A7, A8, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8);
	input	G1, G2;
	input	A1, A2, A3, A4, A5, A6, A7, A8;
	output	Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8;
	
	wire	G = G1 | G2;
	
	assign	Y1 = (G == 1'b0) ? ~A1 : 1'hz;
	assign	Y2 = (G == 1'b0) ? ~A2 : 1'hz;
	assign	Y3 = (G == 1'b0) ? ~A3 : 1'hz;
	assign	Y4 = (G == 1'b0) ? ~A4 : 1'hz;
	assign	Y5 = (G == 1'b0) ? ~A5 : 1'hz;
	assign	Y6 = (G == 1'b0) ? ~A6 : 1'hz;
	assign	Y7 = (G == 1'b0) ? ~A7 : 1'hz;
	assign	Y8 = (G == 1'b0) ? ~A8 : 1'hz;
endmodule

テストベンチはこんな感じ?

module logic_74hc540_test;
	reg	[1:0] G;
	reg	[7:0] A;
	wire	[7:0] Y;
	
	LOGIC_74HC540	logic_74hc540(G[0], G[1], 
		A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], 
		Y[0], Y[1], Y[2], Y[3], Y[4], Y[5], Y[6], Y[7]);
	
	initial begin
		$dumpfile("logic_74hc540_test.vcd");
		$dumpvars(0, logic_74hc540_test);
		$monitor ("%t: (G[1], G[0]) = (%b, %b), A = %b%b%b%b%b%b%b%b, Y = %b%b%b%b%b%b%b%b",
			$time, G[0], G[1], 
			A[7], A[6], A[5], A[4], A[3], A[2], A[1], A[0], 
			Y[7], Y[6], Y[5], Y[4], Y[3], Y[2], Y[1], Y[0]);
			

			A = 8'b00000000;	G = 0;
		#10	A = 8'b10101010;
		#10	A = 8'b11001100;
		#10	A = 8'b11110000;
		#10	A = 8'b00111100;
		#10	A = 8'b00000000;	G = 1;
		#10	A = 8'b10101010;	
		#10	A = 8'b11001100;
		#10	A = 8'b11110000;
		#10	A = 8'b00111100;
		#10	A = 8'b00000000;	G = 2;
		#10	A = 8'b10101010;	
		#10	A = 8'b11001100;
		#10	A = 8'b11110000;
		#10	A = 8'b00111100;
		#10	A = 8'b00000000;	G = 3;
		#10	A = 8'b10101010;	
		#10	A = 8'b11001100;
		#10	A = 8'b11110000;
		#10	A = 8'b00111100;
		#10	$finish;
	end
endmodule

2.74HC154

4ビットの2進数を16ビットの信号線にバラします。
ギョーカイヨーゴでは4-16デコーダって言います。

module LOGIC_74HC154(G1, G2, A, B, C, D, Y0, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8, Y9, Y10, Y11, Y12, Y13, Y14, Y15);
	input	G1, G2, A, B, C, D;
	output	Y0, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8, Y9, Y10, Y11, Y12, Y13, Y14, Y15;
	wire	G = (~G1) & (~G2);
	
	assign	Y0  = ~(G & ~A & ~B & ~C & ~D);
	assign	Y1  = ~(G &  A & ~B & ~C & ~D);
	assign	Y2  = ~(G & ~A &  B & ~C & ~D);
	assign	Y3  = ~(G &  A &  B & ~C & ~D);
	assign	Y4  = ~(G & ~A & ~B &  C & ~D);
	assign	Y5  = ~(G &  A & ~B &  C & ~D);
	assign	Y6  = ~(G & ~A &  B &  C & ~D);
	assign	Y7  = ~(G &  A &  B &  C & ~D);
	assign	Y8  = ~(G & ~A & ~B & ~C &  D);
	assign	Y9  = ~(G &  A & ~B & ~C &  D);
	assign	Y10 = ~(G & ~A &  B & ~C &  D);
	assign	Y11 = ~(G &  A &  B & ~C &  D);
	assign	Y12 = ~(G & ~A & ~B &  C &  D);
	assign	Y13 = ~(G &  A & ~B &  C &  D);
	assign	Y14 = ~(G & ~A &  B &  C &  D);
	assign	Y15 = ~(G &  A &  B &  C &  D);
endmodule

テストベンチはこんな感じ?

module logic_74hc154_test;
	reg		[1:0] G;
	reg		[3:0] A;
	wire	[15:0] Y;
	
	LOGIC_74HC154	logic_74hc154(G[0], G[1], A[0], A[1], A[2], A[3], 
		Y[0], Y[1], Y[2], Y[3], Y[4], Y[5], Y[6], Y[7], 
		Y[8], Y[9], Y[10], Y[11], Y[12], Y[13], Y[14], Y[15]);
	
	initial begin
		$dumpfile("logic_74hc154_test.vcd");
		$dumpvars(0, logic_74hc154_test);
		$monitor ("%t: G = %b%b, A = %b%b%b%b, Y = %b%b%b%b%b%b%b%b%b%b%b%b%b%b%b%b",
			$time, G[1], G[0], A[3], A[2], A[1], A[0], 
			Y[15], Y[14], Y[13], Y[12], Y[11], Y[10], Y[9], Y[8], 
			Y[7], Y[6], Y[5], Y[4], Y[3], Y[2], Y[1], Y[0]);
			
			A = 4'b0000;	G = 0;
		#10	A = 4'b0001;
		#10	A = 4'b0011;
		#10	A = 4'b0100;
		#10	A = 4'b0101;
		#10	A = 4'b0110;
		#10	A = 4'b0111;
		#10	A = 4'b1000;
		#10	A = 4'b1001;
		#10	A = 4'b1011;
		#10	A = 4'b1100;
		#10	A = 4'b1101;
		#10	A = 4'b1110;
		#10	A = 4'b1111;
		#10	A = 4'b0000;	G = 1;
		#10	A = 4'b0001;
		#10	A = 4'b0011;
		#10	A = 4'b0100;
		#10	A = 4'b0101;
		#10	A = 4'b0110;
		#10	A = 4'b0111;
		#10	A = 4'b1000;
		#10	A = 4'b1001;
		#10	A = 4'b1011;
		#10	A = 4'b1100;
		#10	A = 4'b1101;
		#10	A = 4'b1110;
		#10	A = 4'b1111;
		#10	A = 4'b0000;	G = 2;
		#10	A = 4'b0001;
		#10	A = 4'b0011;
		#10	A = 4'b0100;
		#10	A = 4'b0101;
		#10	A = 4'b0110;
		#10	A = 4'b0111;
		#10	A = 4'b1000;
		#10	A = 4'b1001;
		#10	A = 4'b1011;
		#10	A = 4'b1100;
		#10	A = 4'b1101;
		#10	A = 4'b1110;
		#10	A = 4'b1111;
		#10	A = 4'b0000;	G = 3;
		#10	A = 4'b0001;
		#10	A = 4'b0011;
		#10	A = 4'b0100;
		#10	A = 4'b0101;
		#10	A = 4'b0110;
		#10	A = 4'b0111;
		#10	A = 4'b1000;
		#10	A = 4'b1001;
		#10	A = 4'b1011;
		#10	A = 4'b1100;
		#10	A = 4'b1101;
		#10	A = 4'b1110;
		#10	A = 4'b1111;
		#10	$finish;
	end
ここからは本題です。

3.ROM

さて,ここでROMモジュールを作ろうとして頓挫したわけです。

で,結局Verilog様の偉大なる力に頼ることにします。
今だけはVerilogのことを偉大なる指導者と読んでもバチは当たらない!

こんな感じになりました。
実際に本に乗っているコードをのっけてあります。
コメントアウトしたりしながら楽しんでね!

module ROM_16WORD(ROM_ADDRESS, ROM_DATA);
	input	[3:0] ROM_ADDRESS;
	output	[7:0] ROM_DATA;
	
	reg	[7:0] ROM [0:15];
	
	assign	ROM_DATA = ROM[ROM_ADDRESS];
	
	initial begin
		// BLANK
		/*
		ROM[0]  <= 8'b00000000;	// NOP
		ROM[1]  <= 8'b00000000;	// NOP
		ROM[2]  <= 8'b00000000;	// NOP
		ROM[3]  <= 8'b00000000;	// NOP
		ROM[4]  <= 8'b00000000;	// NOP
		ROM[5]  <= 8'b00000000;	// NOP
		ROM[6]  <= 8'b00000000;	// NOP
		ROM[7]  <= 8'b00000000;	// NOP
		ROM[8]  <= 8'b00000000;	// NOP
		ROM[9]  <= 8'b00000000;	// NOP
		ROM[10] <= 8'b00000000;	// NOP
		ROM[11] <= 8'b00000000;	// NOP
		ROM[12] <= 8'b00000000;	// NOP
		ROM[13] <= 8'b00000000;	// NOP
		ROM[14] <= 8'b00000000;	// NOP
		ROM[15] <= 8'b00000000;	// NOP
		*/
		
		// LED NIGHT RIDER
		/*
		ROM[0]  <= 8'b10110011;	// OUT 0011
		ROM[1]  <= 8'b10110110;	// OUT 0110
		ROM[2]  <= 8'b10111100;	// OUT 1100
		ROM[3]  <= 8'b10111000;	// OUT 1000
		ROM[4]  <= 8'b10111000;	// OUT 1000
		ROM[5]  <= 8'b10111100;	// OUT 1100
		ROM[6]  <= 8'b10110110;	// OUT 0110
		ROM[7]  <= 8'b10110011;	// OUT 0011
		ROM[8]  <= 8'b10110001;	// OUT 0001
		ROM[9]  <= 8'b11110000;	// JMP 0000
		ROM[10] <= 8'b00000000;	// NOP
		ROM[11] <= 8'b00000000;	// NOP
		ROM[12] <= 8'b00000000;	// NOP
		ROM[13] <= 8'b00000000;	// NOP
		ROM[14] <= 8'b00000000;	// NOP
		ROM[15] <= 8'b00000000;	// NOP
		*/
		
		// RAMEN TIMER (For 1Hz CLOCK)
		ROM[0]  <= 8'b10110111;	// OUT 0111
		ROM[1]  <= 8'b00000001;	// ADD A, 0001
		ROM[2]  <= 8'b11100001;	// JNC 0001
		ROM[3]  <= 8'b00000001;	// ADD A, 0001
		ROM[4]  <= 8'b11100011;	// JNC 0011
		ROM[5]  <= 8'b10110110;	// OUT 0110
		ROM[6]  <= 8'b00000001;	// ADD A, 0001
		ROM[7]  <= 8'b11100110;	// JNC 0110
		ROM[8]  <= 8'b00000001;	// ADD A, 0001
		ROM[9]  <= 8'b11101000;	// JNC 1000
		ROM[10] <= 8'b10110000;	// OUT 0000
		ROM[11] <= 8'b10110100;	// OUT 0100
		ROM[12] <= 8'b00000001;	// ADD A, 0001
		ROM[13] <= 8'b11101010;	// JNC 1010
		ROM[14] <= 8'b10111000;	// OUT 1000
		ROM[15] <= 8'b11111111;	// JMP 1111
	end
endmodule

コメントで命令をNOPって書いてますけど,実際には「8'b00000000」は「ADD A, 0」だったりします。
まぁ,要するにNOPなんですけどね。

テストベンチはこんな感じ?

module test_rom;
	reg	[3:0] ROM_ADDRESS;
	wire	[7:0] ROM_DATA;
	
	ROM_16WORD	ROM(ROM_ADDRESS, ROM_DATA);
	
	initial begin
		$dumpfile("test_rom.vcd");
		$dumpvars(0, test_rom);
		$monitor ("%t: ROM_ADDRESS = %b%b%b%b, ROM_DATA = %b%b%b%b%b%b%b%b", $time,
			ROM_ADDRESS[3], ROM_ADDRESS[2], ROM_ADDRESS[1], ROM_ADDRESS[0],
			ROM_DATA[7], ROM_DATA[6], ROM_DATA[5], ROM_DATA[4], 
			ROM_DATA[3], ROM_DATA[2], ROM_DATA[1], ROM_DATA[0]
		);
		
			ROM_ADDRESS = 4'b0000;
		#10	ROM_ADDRESS = 4'b0001;
		#10	ROM_ADDRESS = 4'b0010;
		#10	ROM_ADDRESS = 4'b0011;
		#10	ROM_ADDRESS = 4'b0100;
		#10	ROM_ADDRESS = 4'b0101;
		#10	ROM_ADDRESS = 4'b0110;
		#10	ROM_ADDRESS = 4'b0111;
		#10	ROM_ADDRESS = 4'b1000;
		#10	ROM_ADDRESS = 4'b1001;
		#10	ROM_ADDRESS = 4'b1010;
		#10	ROM_ADDRESS = 4'b1011;
		#10	ROM_ADDRESS = 4'b1100;
		#10	ROM_ADDRESS = 4'b1101;
		#10	ROM_ADDRESS = 4'b1110;
		#10	ROM_ADDRESS = 4'b1111;
		#10	$finish;
	end
endmodule


次回予告

次は何だっけ?

えーと,レジスタファイルの話っぽいよ!

Amazon.co.jp - 渡波 郁 「CPUの創りかた」 毎日コミュニケーションズ (2003)

2012年8月23日木曜日

週刊 TD4をFPGAで創る 創刊号

週刊 TD4をFPGAで創る 創刊号

創刊号は特に何にもつかずに0円。
というかこれからもずっと0円の予定。

1.TD4

TD4って知ってますか?

この世の中には日本語に限定しても数多の電子工作本が出版されているのですが,
その中でも異彩を放っているのが「CPUの創りかた」っていう本でございます。

Amazon.co.jp - 渡波 郁 「CPUの創りかた」 毎日コミュニケーションズ (2003)

この本が市井でどんな評価を受けているかは上のAmazonレビューなんかをご参照してくださいな。
実際,書店で現物を見られると「感動モノ」だと思いますよ?

TD4っていうのは渡波(2003)の中で実際に制作されている4bit CPUでありまして,
なんと10個のLogic ICで構成されているという驚きのプロセッサでございます。

ICといっても別にマイコン持ってくるとかそんなトンチのきいた話をしているわけではなく,
74 Logicシリーズ(Hi-speed CMOS)だけで構成されちゃってたりします。

いわゆるTTLで5V動作するのでハンダゴテをニギニギしながら作業しても作れちゃいますし,
実際に渡波(2003)の中で制作されています。

というか,実装部分の話もかなり載っていて,電子工作初心者に向けたいくらいです。

ちなみに,TD4の名前の意味は……。本を読んでみるとわかります(笑)

そんでもって,次のようなISA(命令セットアーキテクチャ)を持っています。
MOV A, Im
MOV B, Im
MOV A, B
MOV B, A
ADD A, Im
ADD B, Im
IN A
IN B
OUT Im
OUT B
JMP Im
JNC Im

2.FPGAで作る

いやね,はじめはディスクリートで作ろうと思ったんですよ?
でもね,さすがに大変じゃないですか。
本にも書いてありますが,配線量が半端じゃ無いんですよ。とくにROM。

その点HDLって!!
便利だし!
拡張も簡単だし!

実際に物理的に作ろうと思えばFPGAに実装しちゃえばいいわけですしね!

といわけで,まずはVerilog HDLで書いてシミュレーションします。
んで,うまく行ったらネットリスト書いてFPGA上に論理合成の結果を書き込んで完成です!

3.必要なもの

1.本
買いましょう!
絶対に買いましょう!
大事なことなので二度言いました。

2.Verilog HDLの開発環境
Icarus Verilogとか使えばいいんじゃね?
gtkwaveがあれば見やすいかもしれないけど,個人的にはずっとコンソールからvvpしてます。
詳細はググレカス。

3.FPGAボード&開発環境
実際にあれば現物が見れます。
必須ってわけではないけどね。

個人的には研究室に転がってるお借りしたXilinx ML501を使う予定です。

え,なんで予定かって?
この記事書いてる時点じゃまだ実装してないんですよ。

4.元気!ヤル気!シャカリキ!
これ大事!

4.お約束

あのー。
Verilogとかちょっち齧ったことある人は知ってると思うんですけど,別に中で74Logicを再現する必要はないんですよね。

でも,今回はできるだけ再現します。
しかも,assignとか使いまくって再現します。
本来はお行儀が悪いから,ちゃんとRTLで再現するんですけどね。

ただし,ROMとクロックジェネレータは再現できないので,Verilog様の偉大なる力をお借りしちゃいます。
(ROMって再現できないんですかね?メカニカルスイッチはだめかなぁ?)

以上お約束でした。
(意味がわからなかった人は,読み飛ばしちゃえ!)

次回予告

いよいよ実装するよ!