やかんです。

授業課題で出ていたCPU作成を無事完了したので、記録していきます。

東大生やかんのブログ
やかん

3週間かかった、、

※本当に自分の記録用なので、メモ書きみたいになってます。

制作要件

4クロック1命令でCPUを作ります。

  • 1クロック目:命令フェッチ
  • 2クロック目:デコード、レジスタファイルからのデータ取得
  • 3クロック目:演算実行
  • 4クロック目:書き込み

な感じの4クロック。

授業では「このテストベンチを満たすように書いてください」と指示がありましたが、そのテストベンチは一応著作権とか問題がありそうなので掲載は控えさせていただきます。

実装結果

module CPU(
  input wire CK, RST,
  output wire[15:0] IA,
  input wire[15:0] ID,
  output wire[15:0] DA,
  inout wire[15:0] DD,
  output reg RW
);
  reg[15:0] RF[0:14];
  reg[15:0] PC;

  reg[1:0] STAGE;
  reg[15:0] INSTR;

  reg[15:0] FUA, FUB;
  reg[15:0] FUc;

  reg[15:0] LSUA, LSUB;
  reg[15:0] LSUc;

  reg[15:0] PCi; // NPC
  reg[15:0] PCc; // PC for CBUS

  reg FLAG;

  wire[15:0] ABUS, BBUS, CBUS;

  assign ABUS = ((INSTR[7:4] != 4'b1100) ? RF[INSTR[7:4]] : 16'bz);
  assign BBUS = ((INSTR[7:4] != 4'b1100) ? RF[INSTR[3:0]] : 16'bz);
  assign CBUS = ((INSTR[15] == 0) ? FUc :
              (INSTR[15:12] == 4'b1011) ? LSUc :
              (INSTR[15:12] == 4'b1000) ? PCc :
              (INSTR[15:12] == 4'b1100) ? {8'b0, INSTR[7:0]} :
              16'bz);

  assign DD = ((RW == 1) ? 16'bz : LSUA); // RW == 1: load, RW == 0: store
  assign DA = LSUB;

  assign IA = PC;

  always @(posedge CK) begin
    if(RST == 1) begin
      STAGE <= 2'b00;
      PC <= 16'b0000000000000000;
      PCi <= 16'b0000000000000000;
      RW <= 1; // load
    end else begin
      case(STAGE)
        2'b00: 
          begin
            INSTR <= ID;
            RW <= 1;
            STAGE <= 2'b01;
          end
        2'b01: 
          begin
            if(INSTR[15] == 0) begin
              FUA <= ABUS;
              FUB <= BBUS;
            end

            if(INSTR[15:12] == 4'b1010) begin
              // store
              RW <= 0;
              LSUA <= ABUS;
              LSUB <= BBUS;
              PCi <= PC + 16'b0000000000000001;
            end else if (INSTR[15:12] == 4'b1011) begin
              // load
              LSUA <= ABUS;
              PCi <= PC + 16'b0000000000000001;
            end else if(INSTR[15:12] == 4'b1000) begin
              // jump
              PCi <= BBUS;
            end else if(INSTR[15:12] == 4'b1001) begin
              // branch
              PCi <= ((FLAG == 1) ? BBUS : PC + 16'b0000000000000001);
            end else PCi <= PC + 16'b0000000000000001;
            
            STAGE <= 2'b10;
          end
        2'b10: 
          begin
            if(INSTR[15] == 0) begin
              case(INSTR[15:12])
                4'b0000: FUc <= FUA + FUB;
                4'b0001: FUc <= FUA - FUB;
                4'b0010: FUc <= FUA >> FUB; 
                4'b0011: FUc <= FUA << FUB; 
                4'b0100: FUc <= FUA | FUB; 
                4'b0101: FUc <= FUA & FUB; 
                4'b0110: FUc <= ~FUA; 
                4'b0111: FUc <= FUA ^ FUB;
              endcase
            end

            if(INSTR[15:12] == 4'b1011) begin
              // load
              LSUc <= DD;
            end else if(INSTR[15:12] == 4'b1000) begin
              // jump
              PCc <= PC + 16'b0000000000000001;
            end
            
            STAGE <= 2'b11;
          end
        2'b11: 
          begin
            RF[INSTR[11:8]] <= CBUS;
            PC <= PCi;
            RW <= 1;

            STAGE <= 2'b00;
          end
      endcase
    end
  end
endmodule

ベースになっているのはもちろんシンプルな順序機械です。4クロック1命令ということで、状態を4つ定義しています。

詰まったところ

メモリ転送命令です。。

東大生やかんのブログ
やかん

まじで意味わからなかった

結論、上記の実装だと

  • DDポート:inoutで定義されている通り、loadの場合はデータを受け取り、storeの場合はデータを吐き出すポート
  • DAポート:loadにしろstoreにしろ外部のレジスタを使用するわけなので、そのレジスタのアドレスを吐き出すポート

となります。

言われてみるとすごいシンプルですが、これを理解するのにめちゃめちゃ時間かかった。。

まとめ

うん。難しかった。激ムズだったけど、理解が深まったと感じている。

ということで、こちらの記事(メモ書き)は終了。最後までお読みいただき、ありがとうございます。