「CPUの創りかた」掲載の4bit CPU TD4のMAX II CPLDへの実装

移転しました。

CPUの創りかた」購入から9年を経て,ようやくCPLDにTD4(4bit CPU的なもの)を実装したのでメモしておく.

  • 経過

本が出版された当初に最後まで読んで,ディスクリートで組むぞーとICを買ってきたのは良いものの,配線する元気が出ずそのままになっていました.4年くらいして,CPLDで組むぞーと,MAX IIマイクロ開発キット(~8k円)を輸入してみたものの,Quartus IIをダウンロードするのに時間がかかりすぎて飽きる.先日,そろそろやるかとQuartus IIをインストールし,Verilog HDLの書き方を覚える(週末の一日).本のTD4仕様書(回路図やら真理値表)をみながらコーディングする(昨日一晩).デバッグ(今晩).やり始めて見るとあっという間にできました.

  • 画像


LEDチカチカ中.

  • 感想

HDL/CPLDを使うと配線変更やシミュレーションができるので,ディスクリート部品を配線するのよりずっと楽に思われた.MAX II(の高いやつ)は2210LEまで入るので,もっと複雑な回路も載せられそう.オプティマイズさんで売っているMAX IIMAX2 CPLDボードは,570LE, 1600円(当時)と小さめではあるが,今回の用途にはそっちでも十分だった気がする.USB接続JTAGケーブルが必要であるが,AVRの書き込み用のものを持っているので.最近はAltera DE0という開発キットも出ていて,こちらはFPGAでもっとたくさんのロジックが入るみたいだけど,そのぶん高価である.
今まで大きめのデータを扱うときは,USB1.0 FullSpeedを介してPCからデジタル回路へ転送していたので,どうしても間に合わないことがあったり,AVRではピンがたりないということがあったりしましたが,CPLDなら,外部メモリを接続できるようにして,そこから読んで,多ピンで出力ということなどが高速にできて良さそう.液晶のドライブとか.

  • コード

他のところであげられているものの方が洗練されていると思われます.
HDLならば論理合成は計算機がやってくれますが,私は本の通り,論理合成後のものをなるべく使って書いてみました.
50LEでした.そのうち半分くらいはクロック分周用..

module td4_mb(
	input CLOCK_50,
	input [3:0] KEY,
	output [7:0] LED
);
	wire [3:0] rom_addr;
	wire [7:0] rom_data;
	reg [24:0] counter;

	always @( posedge CLOCK_50 ) begin
		counter <= counter + 1'b1;
	end

	assign LED[7:4] = 4'b1111;
	td4 td4(counter[24], KEY[0], 4'b0, LED[3:0], rom_addr, rom_data);

	ROM rom (rom_addr, rom_data);

endmodule
module td4(
input CK, CLRB,
input [3:0] In,
output [3:0] Out, ADDR,
input [7:0] rom_data
);
	//pp.201, 215
	wire [3:0] Y, SIGMA;
	wire [3:0] C0, C1;
	wire [3:0] Op, Im;
	wire [3:0] LOADB;
	wire [1:0] SEL;
	wire CARRY, CF;

	assign Op = rom_data[7:4];
	assign Im = rom_data[3:0];

	SELECTER4 sel4 (C0, C1, In, 4'b0, SEL, Y);

	FULL_ADDR4 alu (Y, Im, SIGMA, CARRY);

	COUNTER4 rega (SIGMA, CK, CLRB, LOADB[0], 1'b0, C0);
	COUNTER4 regb (SIGMA, CK, CLRB, LOADB[1], 1'b0, C1);
	//p.222
	COUNTER4 regc (SIGMA, CK, CLRB, LOADB[2], 1'b0, Out);
	//C3->gomi p.206->address p.215
	COUNTER4 counter (SIGMA, CK, CLRB, LOADB[3], 1'b1, ADDR);

	//p.210
	FLAG flag (CK, CLRB, CARRY, CF);

	//p.272
	DECODER dec (Op, CF, SEL, LOADB);

endmodule
module ROM(
	input [3:0] ADDR,
	output [7:0] DATA
);
	//p.296
	function [7:0] romout;
	input [3:0] ADDR;
		case (ADDR)
			4'd0 :	romout = 8'b1011_1100;
			4'd1 :	romout = 8'b1011_1001;
			4'd2 :	romout = 8'b1011_0011;
			4'd3 :	romout = 8'b1011_0111;
			4'd4 :	romout = 8'b1011_0111;
			4'd5 :	romout = 8'b1011_0011;
			4'd6 :	romout = 8'b1011_1001;
			4'd7 :	romout = 8'b1011_1100;
			4'd8 :	romout = 8'b1011_1110;
			4'd9 :	romout = 8'b1111_0000;
			4'd10:	romout = 8'h00;
			4'd11:	romout = 8'h00;
			4'd12:	romout = 8'h00;
			4'd13:	romout = 8'h00;
			4'd14:	romout = 8'h00;
			4'd15:	romout = 8'h00;
			default:	romout = 8'hxx;
		endcase
	endfunction
	assign DATA = romout(ADDR);

endmodule
module COUNTER4 (
	input [3:0] A,
	input CK, CLRB, LOADB, ENTENP,
	output reg [3:0] QA
);
	//74HC161
	//p.188
	//p.214
	always @ (posedge CK or negedge CLRB) begin
		if (!CLRB)
			QA <= 4'b0;
		else if (!LOADB)
			QA <= A;
		else if (ENTENP)
			QA <= QA + 1'b1;
	end

endmodule
module FULL_ADDR (
	input CIN, A, B,
	output S, C
);
	//p.198 full_addr
	assign C = (A & B) | (B & CIN) | (CIN & A);
	assign S = A ^ B ^ CIN;
	//assign {C, S} = A + B + CIN;

endmodule
module FULL_ADDR4 (
	input [3:0] a, b,
	//input cin,
	output [3:0] q,
	output cout
);
	//p.199
	wire [2:0] c;

	FULL_ADDR add0 (1'b0, a[0], b[0], q[0], c[0]);
	FULL_ADDR add1 (c[0], a[1], b[1], q[1], c[1]);
	FULL_ADDR add2 (c[1], a[2], b[2], q[2], c[2]);
	FULL_ADDR add3 (c[2], a[3], b[3], q[3], cout);

endmodule
module SELECTER4 (
	input [3:0] C0, C1, C2, C3,
	input [1:0] A,
	output [3:0] Y
);
	//p.177
	function [3:0] select;
	input [3:0] C0, C1, C2, C3;
	input [1:0] A;
		case ( A )
			2'b00:	select = C0;
			2'b01:	select = C1;
			2'b10:	select = C2;
			2'b11:	select = C3;
			default:	select = 4'bx;
		endcase
	endfunction

	assign Y = select(C0, C1, C2, C3, A);

endmodule
module FLAG (
input CK, CLRB, D,
output reg Q
);
	//p.210
	always @ (posedge CK or negedge CLRB) begin
		if (CLRB==1'b0)
			Q <= 1'b0;
		else
			Q <= D;
	end

endmodule
module DECODER (
	input [3:0] Op,
	input CF,
	output [1:0] SEL,
	output [3:0] LOADB
);
	//p.272
	assign SEL[0] = Op[0] | Op[3];//SELA
	assign SEL[1] = Op[1]; //SELB
	assign LOADB[0] = Op[2] | Op[3];
	assign LOADB[1] = ~Op[2] | Op[3];
	assign LOADB[2] = ~(~Op[2] & Op[3]);
	assign LOADB[3] = ~((CF | Op[0]) & Op[2] & Op[3]);

endmodule