CPUとは巨大な case 文である

CPUはクロックに従って ROM/RAM を読み込んで内部状態を変更し、外部出力を行うステートマシンと思うことができます。このとき、次の状態への変更は論理モデルでは case 文で記述することが可能です。実際に Verilog HDL で 8bit CPU を記述してみます。

まず、8bit RAM を定義します。

module ram8(q, ad, dt, we, clk) 
  output[7:0] q;
  input [7:0] ad;
  input [7:0] dt;
  input we;
  input clk;
  reg [7:0] mem[127:0];

  always @posedge(clk) begin
    if (we)
        mem[ad] <= dt;
    q <= mem[ad];
  end
endmodule

8bit CPU の仕様は以下とします。

  • 内部レジスタは16個(レジスタ0はプログラムカウンタ PC 専用)
  • リセットは同期式
  • ポートは入力16個、出力16個
  • 1ワードは8bit長
  • 命令長は2ワード
  • 1命令は3クロックで実行
命令ワード(bit表現)(MSB:LSB)説明
0000rrrr aaaaaaaaRAMからアドレスaの内容をレジスタrにロードする。
0001rrrr aaaaaaaaレジスタrをRAMのアドレスaにストアする。
0010rrrr 0000qqqqレジスタqをアドレスとして、RAMの内容をレジスタrにロードする。
0011rrrr 0000qqqqレジスタqをアドレスとして、レジスタrの内容をRAMにストアする。
0100qqqq rrrrttttレジスタrとレジスタtの和をレジスタqに代入する。
0110qqqq rrrrttttレジスタrからレジスタtを減算してレジスタqに代入する。
1000rrrr cccccccc定数cをレジスタrにロードする。
11101100 pppprrrr入力ポートpからレジスタrに値をロードする。
11101101 pppprrrrレジスタrの値を出力ポートpにストアする。
上記以外の組合わせエラーとしてアドレス0をPCにロード。

Verilog HDL で論理レベルで書き下すと以下のようになります。

module cpu8(clk, rst, pin, pout)
  input wire clk;
  input wire rst;
  input wire [7:0] pin[0:15];
  output reg [7:0] pout[0:15];

  // 内部変数
  wire [7:0] q;
  reg [7:0] ad;
  reg [7:0] dt;
  reg we;
  reg [1:0] phase;
  reg [7:0] phase0;
  wire [3:0] dec0_upper = phase0[7:4];
  wire [3:0] dec0_lower = phase0[3:0];

  // ROM/RAM
  ram8 ram8(.q(q), .ad(ad), .dt(dt), .we(we), .clk(clk));

  // レジスタ
  reg [7:0] ireg[0:15];
  wire pc = ireg[0];
  wire sp = ireg[15];

  always @posedge(clk) begin
    if (rst) begin
      // リセット信号
      genvar i;
      for (i = 0; i < 16; i++) begin
        pout[i] <= 0;
        ireg[i] <= 0;
      end
      ad <= 0;
      we <= 0;
      dt <= 0;
      phase <= 0;
      phase0 <= 0;
    end else begin
      if (phase == 2'b00) begin
        // 命令1ワード目フェッチ
        phase0 <= q;
        we <= 0;
        dt <= 0;
        phase <= 2'b01;
        ad <= pc + 1;
      end else if (phase == 2'b01') begin
        // 命令2ワード目フェッチ
        we <= dec0_upper[0];
        case (dec0_upper[3:1])
          "000": begin ad <= q; dt <= ireg[dec0_lower]; ireg[0] <= pc + 1; end
          "001": begin ad <= ireg[dec0_lower]; dt <= ireg[q[3:0]]; ireg[0] <= pc + 1; end
          "010": begin
              ireg[dec0_lower] <= ireg[q[7:4]] + ireg[q[3:0]];
              if (dec0_lower != 0)
                ireg[0] <= pc + 1;
            end
          "011": begin
              ireg[dec0_lower] <= ireg[q[7:4]] - ireg[q[3:0]];
              if (dec0_lower != 0)
                ireg[0] <= pc + 1;
            end
          "100": begin
              ireg[dec0_lower] <= q;
              if (dec0_lower != 0)
                ireg[0] <= pc + 1;
            end
          "111": case (dec0_lower)
             "1100": begin
               ireg[q[7:4]] <= pin[3:0];
               if (q[7:4] != 0)
                 ireg[0] <= pc + 1;
               end
             "1101": begin pout[q[3:0]] <= ireg[q[7:4]]; ireg[0] <= pc + 1; end
             default: ireg[0] <= 0;
          endcase
          default: ireg[0] <= 0;
        endcase
        phase <= 2'10;
    end else begin
      // 実行サイクル
      if (!dec0_upper[0]) begin
        case (dec0_upper[3:1])
          "000":  begin
            ireg[dec0_lower] <= q;
            ad <= (dec0_lower) ? pc : q;
          end
          "001":  begin
            ireg[dec0_lower] <= q;
            ad <= (dec0_lower) ? pc : q;
          end
          default:
            ad <= pc;
        endcase
      else
       ad <= pc;
      end 
      phase <= 0;
    end
  end
end
endmodule
投稿者について
みのしす

小さいときは科学者になろうとしたのに、その時にたまたま身に着けたプログラミングで未だに飯を食っているしがないおじさんです。(年齢的にはもうすぐおじいさん)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です