週刊 TD4をFPGAで創る 第5号 「命令デコーダを創る」
第9章からです。ついにシミュレーション編完結!!
命令デコーダがどのような経緯でIC2つに収まったかは渡波(2003)をご参照くださいな。
Amazon.co.jp - 渡波 郁 「CPUの創りかた」 毎日コミュニケーションズ (2003)
1.74HC10
まずは3入力NAND3つ入り!module LOGIC_74HC10(A1, B1, C1, Y1, A2, B2, C2, Y2, A3, B3, C3, Y3); input A1, B1, C1, A2, B2, C2, A3, B3, C3; output Y1, Y2, Y3; assign Y1 = ~(A1 & B1 & C1); assign Y2 = ~(A2 & B2 & C2); assign Y3 = ~(A3 & B3 & C3); endmodule
テストベンチはいらないよね?
2.74HC32
次に2入力OR4つ入り!module LOGIC_74HC32(A1, B1, Y1, A2, B2, Y2, A3, B3, Y3, A4, B4, Y4); input A1, B1, A2, B2, A3, B3, A4, B4; output Y1, Y2, Y3, Y4; assign Y1 = A1 | B1; assign Y2 = A2 | B2; assign Y3 = A3 | B3; assign Y4 = A4 | B4; endmodule
これもテストベンチはいらないよね?
3.命令デコーダ
いよいよ命令デコーダづくり!気合入れていきましょう。
module InstructionDecoder(OPERATION_CODE, C_FLAG_BAR, LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B); input [3:0] OPERATION_CODE; input C_FLAG_BAR; output LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B; reg Vcc = 1; reg GND = 0; wire [1:0] w; assign SELECT_B = OPERATION_CODE[1]; LOGIC_74HC32 logic_74hc32 ( OPERATION_CODE[2], OPERATION_CODE[3], LOAD_0, w[0], OPERATION_CODE[3], LOAD_1, OPERATION_CODE[0], OPERATION_CODE[3], SELECT_A, OPERATION_CODE[0], C_FLAG_BAR, w[1] ); LOGIC_74HC10 logic_74hc10 ( OPERATION_CODE[2], Vcc, Vcc, w[0], Vcc, w[0], OPERATION_CODE[3], LOAD_2, OPERATION_CODE[2], OPERATION_CODE[3], w[1], LOAD_3 ); endmodule
はい!出来上がり!
テストベンチはこんな感じ!
module instruction_decoder_test; reg [3:0] OPCODE; reg C_FLAG_BAR; wire LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B; InstructionDecoder decoder(OPCODE, C_FLAG_BAR, LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B); initial begin $dumpfile("instruction_decoder_test.vcd"); $dumpvars(0, instruction_decoder_test); $monitor ("%t: OPCODE = %b%b%b%b, C_FLAG_BAR = %b, (SELECT_B, A, LOAD_0, 1, 2, 3) = %b %b %b %b %b %b", $time, OPCODE[3], OPCODE[2], OPCODE[1], OPCODE[0], C_FLAG_BAR, SELECT_B, SELECT_A, LOAD_0, LOAD_1, LOAD_2, LOAD_3); OPCODE = 4'b0000; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0001; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0010; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0011; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0100; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0101; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0110; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b0111; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b1001; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b1011; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b1110; C_FLAG_BAR = 1'b0; #10 OPCODE = 4'b1110; C_FLAG_BAR = 1'b1; #10 OPCODE = 4'b1111; C_FLAG_BAR = 1'b0; #10 $finish; end endmodule
出力結果をよーく本と見比べてね!
4.そして,完成へ…
長かったCPUづくりもいよいよ一段落です。すべてのパーツを組み合わせましょう。
module Processor(RESET, CLOCK, INPUT, OUTPUT); input RESET, CLOCK; input [3:0] INPUT; output [3:0] OUTPUT; reg Vcc = 1'b1; reg GND = 1'b0; wire [5:0] NOT_IN_USE; wire [3:0] ROM_ADDRESS; wire [7:0] ROM_DATA; wire [3:0] OPERATION_CODE; wire [3:0] IMEEDIATE_DATA; wire LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B; wire [3:0] REGISTER_IN; wire [3:0] REGISTER_A_OUT; wire [3:0] REGISTER_B_OUT; wire [3:0] REGISTER_C_OUT; wire [3:0] REGISTER_D_OUT; wire [3:0] ALU_IN_A; wire [3:0] ALU_IN_B; wire CARRY_OUT, C_FLAG, C_FLAG_BAR; assign ROM_ADDRESS = REGISTER_D_OUT; assign OPERATION_CODE = ROM_DATA[7:4]; assign IMEEDIATE_DATA = ROM_DATA[3:0]; assign ALU_IN_B = IMEEDIATE_DATA; assign OUTPUT = REGISTER_C_OUT; ROM_16WORD ROM(ROM_ADDRESS, ROM_DATA); InstructionDecoder Instruction_Decoder (OPERATION_CODE, C_FLAG_BAR, LOAD_0, LOAD_1, LOAD_2, LOAD_3, SELECT_A, SELECT_B); LOGIC_74HC161 Register_A (RESET, CLOCK, REGISTER_IN[0], REGISTER_IN[1], REGISTER_IN[2], REGISTER_IN[3], GND, LOAD_0, GND, REGISTER_A_OUT[0], REGISTER_A_OUT[1], REGISTER_A_OUT[2], REGISTER_A_OUT[3], NOT_IN_USE[0]); LOGIC_74HC161 Register_B (RESET, CLOCK, REGISTER_IN[0], REGISTER_IN[1], REGISTER_IN[2], REGISTER_IN[3], GND, LOAD_1, GND, REGISTER_B_OUT[0], REGISTER_B_OUT[1], REGISTER_B_OUT[2], REGISTER_B_OUT[3], NOT_IN_USE[1]); LOGIC_74HC161 Register_C (RESET, CLOCK, REGISTER_IN[0], REGISTER_IN[1], REGISTER_IN[2], REGISTER_IN[3], GND, LOAD_2, GND, REGISTER_C_OUT[0], REGISTER_C_OUT[1], REGISTER_C_OUT[2], REGISTER_C_OUT[3], NOT_IN_USE[2]); LOGIC_74HC161 Register_D (RESET, CLOCK, REGISTER_IN[0], REGISTER_IN[1], REGISTER_IN[2], REGISTER_IN[3], Vcc, LOAD_3, Vcc, REGISTER_D_OUT[0], REGISTER_D_OUT[1], REGISTER_D_OUT[2], REGISTER_D_OUT[3], NOT_IN_USE[3]); LOGIC_74HC153 Register_Selector_0 (SELECT_A, SELECT_B, GND, GND, REGISTER_A_OUT[0], REGISTER_B_OUT[0], REGISTER_C_OUT[0], GND, REGISTER_A_OUT[1], REGISTER_B_OUT[1], REGISTER_C_OUT[1], GND, ALU_IN_A[0], ALU_IN_A[1]); LOGIC_74HC153 Register_Selector_1 (SELECT_A, SELECT_B, GND, GND, REGISTER_A_OUT[2], REGISTER_B_OUT[2], REGISTER_C_OUT[2], GND, REGISTER_A_OUT[3], REGISTER_B_OUT[3], REGISTER_C_OUT[3], GND, ALU_IN_A[2], ALU_IN_A[3]); LOGIC_74HC283 ALU (GND, ALU_IN_A[0], ALU_IN_A[1], ALU_IN_A[2], ALU_IN_A[3], ALU_IN_B[0], ALU_IN_B[1], ALU_IN_B[2], ALU_IN_B[3], REGISTER_IN[0], REGISTER_IN[1], REGISTER_IN[2], REGISTER_IN[3], CARRY_OUT); LOGIC_74HC74 C_Flag (RESET, CARRY_OUT, CLOCK, Vcc, C_FLAG, C_FLAG_BAR, GND, GND, GND, GND, NOT_IN_USE[4], NOT_IN_USE[5]); endmodule
完成!!!!!!!!!
実際に動かしてみよう!
テストベンチは次の通り。
module processor_test; reg RESET, CLOCK; reg [3:0] INPUT; wire [3:0] OUTPUT; Processor processor(RESET, CLOCK, INPUT, OUTPUT); initial begin $dumpfile("processor_test.vcd"); $dumpvars(0, processor_test); $monitor ("%t: CLOCK = %b, RESET = %b, INPUT = %b%b%b%b, OUTPUT = %b%b%b%b", $time, CLOCK, RESET, INPUT[3], INPUT[2], INPUT[1], INPUT[0], OUTPUT[3], OUTPUT[2], OUTPUT[1], OUTPUT[0]); CLOCK = 0; RESET = 1; INPUT = 4'b0000; #400 $finish; end always #1 CLOCK = ~CLOCK; endmodule
LEDチカチカとラーメンタイマーそれぞれ動いたでしょうか?
これにて一段落。
次回予告
次回は実際にFPGAに実装してみたいと思います。(多分)Amazon.co.jp - 渡波 郁 「CPUの創りかた」 毎日コミュニケーションズ (2003)