「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仕様書(回路図やら真理値表)をみながらコーディングする(昨日一晩).デバッグ(今晩).やり始めて見るとあっという間にできました.
- 画像
- 感想
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