17
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Verilog自作CPU】シリーズ

Part タイトル 内容
Part1 レジスタファイルとALU 基本部品
Part2 命令デコーダ 命令解析
Part3 メモリとロード・ストア 本記事
Part4 分岐とジャンプ BEQ/JAL命令の実装
Part5 CPU統合と動作テスト 完成・プログラム実行

はじめに

前回は命令デコーダを作りました。今回はメモリモジュールを実装します。

CPUには2種類のメモリが必要です:

  • 命令メモリ (I-MEM): プログラムを格納(読み取り専用)
  • データメモリ (D-MEM): データを読み書き

命令メモリの実装

命令メモリは、PCの値(アドレス)を受け取って、対応する命令を返すシンプルなROMです。

src/imem.v
// 命令メモリ (ROM)
module imem (
    input  [31:0] addr,
    output [31:0] data
);
    // 256ワード = 1KB
    reg [31:0] memory [0:255];
    
    // ワードアドレスでアクセス(下位2ビットは無視)
    wire [7:0] word_addr = addr[9:2];
    
    assign data = memory[word_addr];
    
endmodule

ポイント:

  • ワードアドレス: RISC-Vの命令は4バイト単位なので、アドレスの下位2ビットは常に0
  • 非同期読み出し: クロック不要、アドレスを与えれば即座にデータが出る

データメモリの実装

データメモリは読み書き両方できるRAMです。

src/dmem.v
// データメモリ (RAM)
module dmem (
    input         clk,
    input  [31:0] addr,
    input  [31:0] write_data,
    input         write_enable,
    output [31:0] read_data
);
    // 256ワード = 1KB
    reg [31:0] memory [0:255];
    
    // ワードアドレスでアクセス
    wire [7:0] word_addr = addr[9:2];
    
    // 同期書き込み
    always @(posedge clk) begin
        if (write_enable)
            memory[word_addr] <= write_data;
    end
    
    // 非同期読み出し
    assign read_data = memory[word_addr];
    
endmodule

ポイント:

  • 書き込みは同期(クロックの立ち上がりで書き込み)
  • 読み出しは非同期(組み合わせ回路)

ロード・ストア命令

LW(Load Word)

メモリからレジスタにデータを読み込む:

LW rd, offset(rs1)
rd = memory[rs1 + offset]

例:LW x5, 4(x1) → x5 = memory[x1 + 4]

SW(Store Word)

レジスタからメモリにデータを書き込む:

SW rs2, offset(rs1)
memory[rs1 + offset] = rs2

例:SW x2, 8(x1) → memory[x1 + 8] = x2


データパスの拡張

ロード・ストア命令を実行するため、データパスを拡張します:

                    ┌─────────────────────────────┐
                    │                             │
┌─────┐   ┌────────┐│  ┌─────────┐   ┌─────┐    │
│ PC  │──▶│ I-MEM  │┴─▶│ DECODER │──▶│ REG │────┤
└──┬──┘   └────────┘   └────┬────┘   └──┬──┘    │
   │                        │           │        │
   │                        │     ┌─────▼─────┐  │
   │                        │     │    MUX    │◀─┤ alu_src
   │                        │     └─────┬─────┘  │
   │                        │           │        │
   │                        │     ┌─────▼─────┐  │
   │                        │     │    ALU    │  │
   │                        │     └─────┬─────┘  │
   │                        │           │        │
   │                        │     ┌─────▼─────┐  │
   │                        └────▶│   D-MEM   │──┤
   │                              └─────┬─────┘  │
   │                                    │        │
   │                              ┌─────▼─────┐  │
   │                              │    MUX    │◀─┤ mem_to_reg
   │                              └─────┬─────┘  │
   │                                    │        │
   ◀────────────────────────────────────┴────────┘
                                    (writeback)

新しいマルチプレクサ:

  • alu_src: ALU第2入力を rs2 か 即値 かで選択
  • mem_to_reg: ライトバックデータを ALU結果 か メモリデータ かで選択

メモリテスト

データメモリのテストベンチ:

test/dmem_tb.v
`timescale 1ns/1ps

module dmem_tb;
    reg         clk;
    reg  [31:0] addr, write_data;
    reg         write_enable;
    wire [31:0] read_data;
    
    dmem dut (
        .clk(clk),
        .addr(addr),
        .write_data(write_data),
        .write_enable(write_enable),
        .read_data(read_data)
    );
    
    initial clk = 0;
    always #5 clk = ~clk;
    
    initial begin
        $display("=== Data Memory Test ===\n");
        
        // 初期化
        write_enable = 0;
        addr = 0;
        write_data = 0;
        
        // Test 1: アドレス0に書き込み
        addr = 32'h00000000;
        write_data = 32'hDEADBEEF;
        write_enable = 1;
        @(posedge clk); #1;
        write_enable = 0;
        
        $display("Test 1: Write 0xDEADBEEF to addr 0");
        $display("  Read back: 0x%h", read_data);
        
        // Test 2: アドレス4に書き込み
        addr = 32'h00000004;
        write_data = 32'hCAFEBABE;
        write_enable = 1;
        @(posedge clk); #1;
        write_enable = 0;
        
        $display("Test 2: Write 0xCAFEBABE to addr 4");
        $display("  Read back: 0x%h", read_data);
        
        // Test 3: アドレス0を再度読み出し
        addr = 32'h00000000;
        #1;
        $display("Test 3: Read addr 0 again");
        $display("  Read: 0x%h (expected: 0xDEADBEEF)", read_data);
        
        if (read_data == 32'hDEADBEEF)
            $display("\n*** TEST PASSED ***");
        else
            $display("\n*** TEST FAILED ***");
        
        #20; $finish;
    end
endmodule

実行結果:

=== Data Memory Test ===

Test 1: Write 0xDEADBEEF to addr 0
  Read back: 0xdeadbeef
Test 2: Write 0xCAFEBABE to addr 4
  Read back: 0xcafebabe
Test 3: Read addr 0 again
  Read: 0xdeadbeef (expected: 0xDEADBEEF)

*** TEST PASSED ***

LW/SWプログラムのシミュレーション

メモリアクセスを含む簡単なプログラム:

# メモリアドレス0に値を保存し、読み戻す
ADDI x1, x0, 100   # x1 = 100 (ベースアドレス)
ADDI x2, x0, 42    # x2 = 42  (保存する値)
SW   x2, 0(x1)     # memory[100] = 42
LW   x3, 0(x1)     # x3 = memory[100]
# x3 = 42 になるはず

機械語に変換:

命令 機械語
ADDI x1, x0, 100 0x06400093
ADDI x2, x0, 42 0x02a00113
SW x2, 0(x1) 0x0020a023
LW x3, 0(x1) 0x0000a183

アドレス計算の詳細

LW/SW命令では、ALUを使ってアドレスを計算します:

実効アドレス = rs1 + 即値(符号拡張)

例:LW x3, -4(x1) の場合

  • rs1 = x1 の値(例:100)
  • imm = -4(符号拡張で 0xFFFFFFFC)
  • 実効アドレス = 100 + (-4) = 96

デコーダでは:

  • alu_src = 1(即値を使う)
  • alu_op = ADD(加算)

バイト・ハーフワードアクセス

RISC-Vには LB, LH, SB, SH 命令もありますが、今回は簡略化のためワード(32ビット)アクセスのみ実装しています。

完全な実装には:

  • バイトイネーブル信号
  • アライメントチェック
  • 符号拡張(LB, LH)とゼロ拡張(LBU, LHU)

が必要になります。


まとめ

Part3では、メモリモジュールを実装しました:

  • 命令メモリ(IMEM): 256ワードのROM
  • データメモリ(DMEM): 256ワードのRAM
  • LW/SW命令: ロード・ストアの動作原理

次回Part4では、分岐命令(BEQ)とジャンプ命令(JAL)を実装します。

Part1 [完了] レジスタファイル + ALU
Part2 [完了] 命令デコーダ
Part3 [完了] メモリアクセス
Part4 [次回] 分岐・ジャンプ
Part5 [予定] 統合テスト

次回: 【Verilogで自作CPU】RISC-Vプロセッサを作る Part4 - 分岐とジャンプ

17
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?