演習D
★★★ 上級
所要:45–60分
Accumulator CPU
アキュムレータ(累算器)型の小型 CPU を Verilog で設計し、実際にプログラムを実行させよう。
📚 概念説明:アキュムレータ CPU の構造
アキュムレータ型 CPU はもっともシンプルな CPU アーキテクチャの一つです。 演算の中間結果をすべて ACC(アキュムレータ)レジスタに保持します。
┌────────────────────────────────────────┐
│ Accumulator CPU │
│ │
│ ┌──────┐ addr ┌──────────────┐ │
│ │ PC │───────▶│ 命令メモリ │ │
│ │(4bit)│ │ (imem[0..15])│ │
│ └──────┘ └──────┬───────┘ │
│ ▲ │ instr[7:0] │
│ │ PC+1 ▼ │
│ │ ┌──────────┐ │
│ └─────────────│ 制御部 │ │
│ │ (Decoder)│ │
│ └────┬─────┘ │
│ │ │
│ ┌──────────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────┐ │
│ │ ACC │ │ ALU │ │ dmem │ │
│ │ (8 bit) │──▶│ (+/-/and)│ │(data)│ │
│ └──────────┘ └────┬─────┘ └──────┘ │
│ ▲ │ │
│ └─────────────┘ │
└────────────────────────────────────────┘
主要コンポーネント
- PC(Program Counter):次に実行する命令のアドレス
- ACC(Accumulator):演算の中間結果を保持するレジスタ
- imem:命令メモリ(プログラムを格納)
- dmem:データメモリ(変数を格納)
- ALU:算術・論理演算を行う回路
命令フォーマット(8ビット)
[7:6] opcode(2ビット)
[5:0] address(6ビット,データメモリアドレス)
00 xxxxxx → LOAD ACC ← dmem[addr]
01 xxxxxx → STORE dmem[addr] ← ACC
10 xxxxxx → ADD ACC ← ACC + dmem[addr]
11 000000 → HALT 停止
実行サイクル(フェッチ → デコード → 実行):
1. Fetch:imem[PC] から命令を読み出す
2. Decode:opcode で命令を解釈する
3. Execute:ACC / dmem を更新し、PC を PC+1 にする
1. Fetch:imem[PC] から命令を読み出す
2. Decode:opcode で命令を解釈する
3. Execute:ACC / dmem を更新し、PC を PC+1 にする
📖 サンプルプログラム
データメモリに dmem[0]=5, dmem[1]=3 を置き、足し算して dmem[2] に保存するプログラム:
| アドレス | 命令(2進数) | ニーモニック | 動作 |
|---|---|---|---|
| 0 | 00_000000 | LOAD 0 | ACC ← dmem[0] (= 5) |
| 1 | 10_000001 | ADD 1 | ACC ← ACC + dmem[1] (= 5+3 = 8) |
| 2 | 01_000010 | STORE 2 | dmem[2] ← ACC (= 8) |
| 3 | 11_000000 | HALT | 停止 |
💻 Verilog テンプレート
Design(右パネル)
// ================================================ // Accumulator CPU(シングルサイクル実装) // 命令:LOAD=00, STORE=01, ADD=10, HALT=11 // ================================================ module cpu_accum ( input clk, input rst, output [3:0] pc_out, output [7:0] acc_out, output halted ); // ---------- 命令メモリ(プログラム) ---------- reg [7:0] imem [0:15]; // ---------- データメモリ ----------------------- reg [7:0] dmem [0:63]; // ---------- レジスタ --------------------------- reg [3:0] pc; reg [7:0] acc; reg halt_reg; assign pc_out = pc; assign acc_out = acc; assign halted = halt_reg; // ---------- 命令デコード ---------------------- wire [7:0] instr = imem[pc]; wire [1:0] opcode = instr[7:6]; wire [5:0] addr = instr[5:0]; // ---------- 初期化 ---------------------------- initial begin // プログラム(5 + 3 を計算し dmem[2] に格納) imem[0] = 8'b00_000000; // LOAD dmem[0] imem[1] = 8'b10_000001; // ADD dmem[1] imem[2] = 8'b01_000010; // STORE dmem[2] imem[3] = 8'b11_000000; // HALT // データ初期値 dmem[0] = 8'd5; dmem[1] = 8'd3; dmem[2] = 8'd0; end // ---------- 実行ループ(クロック同期) --------- always @(posedge clk or posedge rst) begin if (rst) begin pc <= 4'd0; acc <= 8'd0; halt_reg <= 1'b0; end else if (!halt_reg) begin case (opcode) 2'b00: begin // LOAD acc <= dmem[addr]; pc <= pc + 1; end 2'b01: begin // STORE dmem[addr] <= acc; pc <= pc + 1; end 2'b10: begin // ADD acc <= acc + dmem[addr]; pc <= pc + 1; end 2'b11: begin // HALT halt_reg <= 1'b1; end endcase end end endmodule
Testbench(左パネル)
module tb_cpu_accum; reg clk, rst; wire [3:0] pc_out; wire [7:0] acc_out; wire halted; cpu_accum uut (.clk(clk), .rst(rst), .pc_out(pc_out), .acc_out(acc_out), .halted(halted)); // クロック生成(10ns周期) initial clk = 0; always #5 clk = ~clk; initial begin $dumpfile("dump.vcd"); $dumpvars(0, tb_cpu_accum); $monitor("t=%0t | pc=%0d acc=%0d halted=%0d", $time, pc_out, acc_out, halted); // リセット rst = 1; #12; rst = 0; // HALT になるまで待機(最大20クロック) repeat(20) @(posedge clk); $display("最終 ACC = %0d(期待値 = 8)", acc_out); $display("--- シミュレーション完了 ---"); $finish; end endmodule
▶ EDA Playground で開く
PC の変化・ACC の変化・HALT 信号の立ち上がりを波形で確認してみましょう。
🚀 EDA Playground を開く →※ ログインに Gmail が必要です。 使い方ガイドを見る
🚀 発展課題
余裕のある人はぜひ挑戦してみよう。
① SUB 命令を追加する
opcode 3'b10 → ADD の代わりに SUB(ACC - dmem[addr])を実装してみよう。
② JMP 命令を追加する
指定したアドレスにジャンプする命令 JMP addr を追加。PC を addr にセットすると実現できる。
③ BEQ 命令を追加する(条件分岐)
ACC == 0 のとき指定アドレスにジャンプする BEQ 命令を実装しよう。ループプログラムが書けるようになる。
④ 乗算プログラムをアセンブリで書く
LOAD / STORE / ADD の命令だけを使って、3 × 4 = 12 を計算するプログラムを書いてみよう(ループを使う)。
🤔 考えてみよう
- シミュレーションで PC がどのように変化するか確認しよう。LOAD → ADD → STORE → HALT の順に pc_out が 0→1→2→3 になりますか?
- このCPUの「1クロック」で完了する処理のサイクルを「シングルサイクル」といいます。実際のCPU(マルチサイクル・パイプライン)と何が違うでしょうか?
- 現代の CPU(例:ARM Cortex-A)と比べて、このアキュムレータCPUに足りないものは何でしょうか?
- データメモリのアドレスが 6ビット(0〜63)に制限されているのはなぜですか?命令フォーマットと関係しています。