2012年9月20日木曜日

週刊 TD4をFPGAで創る 第5号 「命令デコーダを創る」

週刊 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)

1 件のコメント:

  1. とても参考になりました!実装編も楽しみにしてます

    返信削除