「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
参考
- 渡波 郁 (著)
- 毎日コミュニケーションズ
- 価格¥3,080(2024/10/14 20:37時点)
- Amazonで口コミ・レビューを見る