ChatGPTに自作CPUを作ってもらった

# はじめに

この記事は[自作CPU Advent Calendar 2024](https://qiita.com/advent-calendar/2024/make_cpu)の20日目の記事です。

いつかは作ってみたい自作CPU。でも敷居が高い。そうだChatGPT先生に教えてもらおう。

# ChatGPT先生の自作CPU

![スクリーンショット 2024-12-21 17.50.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/399efc05-ed15-9781-9fe9-b330526cd9e4.png)

![スクリーンショット 2024-12-21 17.51.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/13b481eb-bd07-c8dd-0158-23b47e51ac7a.png)

![スクリーンショット 2024-12-21 17.52.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/900aa2fe-f445-7814-5d04-63941476bdd4.png)

```asm.py
# SimpleCPU コンパイラ
import sys

# 命令セットの定義
INSTRUCTION_SET = {
    'NOP': '0000',
    'LOAD': '0001',
    'STORE': '0010',
    'ADD': '0011',
    'JMP': '0100',
    'HALT': '1111'
}

def assemble(source_code):
    binary_code = []
    for line in source_code.strip().split("\n"):
        parts = line.split()
        if not parts: continue
        instr = INSTRUCTION_SET.get(parts[0].upper(), None)
        if instr is None:
            raise ValueError(f"Unknown instruction: {parts[0]}")
        if len(parts) > 1:
            operand = format(int(parts[1]), '04b')
        else:
            operand = ''
        binary_code.append(instr + operand)
    return binary_code

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python compiler.py <source_file>")
        sys.exit(1)
    
    with open(sys.argv[1], 'r') as f:
        source_code = f.read()
    
    binary_code = assemble(source_code)
    with open("output.bin", 'w') as f:
        f.write("\n".join(binary_code))
    print("Compilation successful! Output: output.bin")
```

コンパイラというがアセンブラか？

![スクリーンショット 2024-12-21 17.53.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/6265f473-f8d0-c7f4-d3df-69a4441c5ce0.png)
```add.asm
LOAD 5       ; レジスタに5をロード
ADD 3        ; レジスタに3を加算
STORE 0      ; メモリのアドレス0に保存
HALT         ; 実行停止
```

![スクリーンショット 2024-12-21 17.54.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/90134c63-37f5-8f82-dac1-591b76727c22.png)

え？ほんとに？実行してみよう！
![スクリーンショット 2024-12-21 18.00.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/700f7144-0673-5c44-6045-3a6c5ee1db48.png)
エラーじゃん。

```add.asm
LOAD 5
ADD 3
STORE 0
HALT
```
上記のように;以下のコメントを消して再チャレンジ。

![スクリーンショット 2024-12-21 18.02.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/fd90aa41-2b44-4b2a-7057-741095b6dad1.png)

おー、できてる！

![スクリーンショット 2024-12-21 18.03.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4b7319ef-ffb0-2a5e-2b21-a0dced2bea1d.png)

```emu.py
# SimpleCPU エミュレータ
def execute(binary_code):
    memory = [0] * 16
    acc = 0
    pc = 0
    while pc < len(binary_code):
        instr = binary_code[pc][:4]
        operand = int(binary_code[pc][4:], 2) if len(binary_code[pc]) > 4 else None
        pc += 1
        if instr == '0000':  # NOP
            continue
        elif instr == '0001':  # LOAD
            acc = operand
        elif instr == '0010':  # STORE
            memory[operand] = acc
        elif instr == '0011':  # ADD
            acc = (acc + operand) & 0b1111  # 4ビットの制限
        elif instr == '0100':  # JMP
            pc = operand
        elif instr == '1111':  # HALT
            break
        else:
            raise ValueError(f"Unknown instruction: {instr}")
    print(f"Memory: {memory}")
    print(f"ACC: {acc}")

if __name__ == "__main__":
    with open("output.bin", 'r') as f:
        binary_code = f.read().strip().split("\n")
    execute(binary_code)

```
![スクリーンショット 2024-12-21 18.04.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/fe5df487-fc8d-2c72-b252-0e75d3d5cc62.png)

ほんとに？試してみる。

![スクリーンショット 2024-12-21 18.07.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/1ba44ca8-b1f5-0e78-0b0e-18d78d2df057.png)

動いた。

回路も作ってもらおう。

![スクリーンショット 2024-12-21 18.08.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/b0126f96-d204-8580-1dff-2398d5669d35.png)

![スクリーンショット 2024-12-21 18.50.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4518cfd1-0d4c-9f66-0fcf-621c3aa7a212.png)
![スクリーンショット 2024-12-21 18.50.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/cdc7fd90-a6eb-7522-3f2f-85bbefee5e76.png)


```cpu.v
module SimpleCPU (
    input clk,               // クロック
    input reset,             // リセット
    output reg [3:0] acc,    // アキュムレータ
    output reg [3:0] mem_out // メモリ出力
);

    // メモリとプログラムカウンタの定義
    reg [7:0] memory [0:15]; // 4ビットデータ×16アドレス
    reg [3:0] pc;            // プログラムカウンタ

    // 初期化 (リセット時のメモリ値設定)
    initial begin
        memory[0] = 8'b00010101; // LOAD 5
        memory[1] = 8'b00110011; // ADD 3
        memory[2] = 8'b00100000; // STORE 0
        memory[3] = 8'b11110000; // HALT
        memory[4] = 8'b00000000; // 未使用
        // 他のメモリは0に初期化される
    end

    // 制御信号
    reg halt; // 実行停止フラグ

    // クロック駆動ロジック
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            pc <= 0;        // プログラムカウンタをリセット
            acc <= 0;       // レジスタをリセット
            halt <= 0;      // HALT解除
        end else if (!halt) begin
            // フェッチ (命令をメモリから取得)
            reg [7:0] instruction;
            instruction = memory[pc];
            pc <= pc + 1;

            // デコード
            case (instruction[7:4]) // 上位4ビットで命令判定
                4'b0000: ;          // NOP (何もしない)
                4'b0001: acc <= instruction[3:0];              // LOAD
                4'b0010: memory[instruction[3:0]] <= acc;      // STORE
                4'b0011: acc <= (acc + instruction[3:0]) & 4'b1111; // ADD
                4'b0100: pc <= instruction[3:0];               // JMP
                4'b1111: halt <= 1;                            // HALT
                default: ; // 無効な命令は無視
            endcase
        end
    end

    // メモリ出力 (デバッグ用)
    always @(*) begin
        mem_out = memory[0]; // メモリアドレス0の値を出力
    end
endmodule
```

![スクリーンショット 2024-12-21 18.52.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/d599b953-9d9a-fb54-5b49-5d4f26532dc1.png)
![スクリーンショット 2024-12-21 18.53.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/5bac0d18-5392-9512-e4bf-9015c932ee91.png)

```cpu_tb.v
module SimpleCPU_tb;
    reg clk;
    reg reset;
    wire [3:0] acc;
    wire [3:0] mem_out;

    // CPUインスタンス
    SimpleCPU cpu (
        .clk(clk),
        .reset(reset),
        .acc(acc),
        .mem_out(mem_out)
    );

    // クロック生成
    initial begin
        clk = 0;
        forever #5 clk = ~clk; // 10単位時間ごとにクロック反転
    end

    // テストシーケンス
    initial begin
        // 初期化
        reset = 1;
        #10 reset = 0;

        // 実行中の状態を監視
        #100 $stop; // シミュレーション停止
    end

    // モニタ (デバッグ用)
    initial begin
        $monitor("Time: %d, ACC: %b, MEM[0]: %b", $time, acc, mem_out);
    end
endmodule
```

ほんとに？動かしてみる。

https://uzusayuu.hatenadiary.jp/entry/2018/12/17/125833
こちらを参考に
https://www.edaplayground.com/
へGoogleアカウントでログイン。

![スクリーンショット 2024-12-21 18.54.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/48d651b5-1a3b-3785-c0e1-f8b4144e7d9f.png)


右側にcpu.v、左側にcpu_tb.vを貼り付け、
左のペインで適当なSimulatorを選択する。
左上の青色のRunボタンを押すと・・

![スクリーンショット 2024-12-21 18.54.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/79284c1b-3869-ae71-7758-91fcea0c5d27.png)

合ってるか確認。

![スクリーンショット 2024-12-21 18.56.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/c90fb8c9-6210-0569-47b6-3d110d2f6118.png)
![スクリーンショット 2024-12-21 18.56.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4995f381-c077-05ab-7d1f-9e6d13284165.png)
![スクリーンショット 2024-12-21 18.56.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/269b4d83-3ae1-ec7e-3081-9a3c4e34de60.png)
![スクリーンショット 2024-12-21 18.56.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/2b5944c9-73a0-8f49-1373-8a96a028dd6d.png)
![スクリーンショット 2024-12-21 18.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/444f024a-855b-fd81-bd45-d6009c01fd7f.png)
![スクリーンショット 2024-12-21 18.56.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/122e4818-89a6-58bd-7a64-49255524dbd0.png)
![スクリーンショット 2024-12-21 18.57.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/3ddeb72a-907a-eb22-307d-b82a6fd7ec0a.png)
![スクリーンショット 2024-12-21 18.57.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/1df4849e-eb6d-04cc-ac18-bd0ede33ef5c.png)
![スクリーンショット 2024-12-21 18.57.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/43548fee-8633-0414-69dc-68735162b67e.png)

おお。素晴らしい。

ChatGPT先生と対話しながらCPU設計していけそうな雰囲気を感じられる結果になりました。

以上、ChatGPTに自作CPUを作ってもらったでした。

最後までご覧いただきましてありがとうございました。