0
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?

自作PCを作る4 ~CPUを作ろう~

Posted at

前回までのあらすじ

  • 自作PCを作ろう!
  • まずメモリを作ったよ!(動かしてない)
  • ISAを作ったよ!(コンパイラはまだ)
  • アセンブリ言語を作ったよ!(アセンブラはまだ)

よくある質問

Q. アセンブラを作るのではなかったですか?
A. 面倒だし本題からずれるのでやっぱやめます

前回からの変更

命令ADDISUBIを消しました.
符号ありと無しで動作が同じなので.

全体の構成

ブロックデザインはこんな感じです.
image.png

mother_boardの中はこんな感じです.
image.png

ソース

いっぱいあるんで省略表示で貼りますね.

util.svh
/**
 * 特に分類できない共通系
 */

`ifndef UTIL_SVH
`define UTIL_SVH

package util_p;
    typedef logic bool_t;

    parameter bool_t TRUE  = 1'b1;
    parameter bool_t FALSE = 1'b1;
endpackage

`endif
machine.svh
/**
 * 機械語を生成する関数
 */

`ifndef MACHINE_SVH
`define MACHINE_SVH

// 機械語の構成
//
//  31  29 28  23 22  19 18 13 12  7 6  1  0
// | type | func | mask | rs1 | rs2 | rd |imm| + イミディエイトデータ(32bit)

package machine_p;
    typedef logic [63:0] machine_t;   // 機械語のサイズ
    typedef logic [ 2:0] type_t;      // 命令タイプ
    typedef logic [ 5:0] func_t;      // 命令
    typedef logic [ 3:0] mask_t;      // マスクサイズ
    typedef logic [ 5:0] addr_t;      // レジスタのアドレスサイズ
    typedef logic [32:0] imm_t;       // イミディエイトデータのサイズ.最上位ビットはイミディエイトデータを使用するかのフラグ

    // 各機械語
    localparam func_t NOP  = 6'h00;
    localparam func_t AND  = 6'h00;
    localparam func_t OR   = 6'h01;
    localparam func_t XOR  = 6'h02;
    localparam func_t NOT  = 6'h03;
    localparam func_t NAND = 6'h04;
    localparam func_t ADD  = 6'h05;
    localparam func_t SUB  = 6'h06;
    localparam func_t SLL  = 6'h00;
    localparam func_t SRL  = 6'h01;
    localparam func_t SLA  = 6'h02;
    localparam func_t SRA  = 6'h03;
    localparam func_t MOV  = 6'h00;
    localparam func_t EQ   = 6'h00;
    localparam func_t NE   = 6'h01;
    localparam func_t LT   = 6'h02;
    localparam func_t GT   = 6'h03;
    localparam func_t ELT  = 6'h04;
    localparam func_t EGT  = 6'h05;
    localparam func_t JMP  = 6'h00;
    localparam func_t RM   = 6'h00;
    localparam func_t WM   = 6'h01;
    localparam func_t BRM  = 6'h02;
    localparam func_t BWM  = 6'h03;

    // 処理を実行しない(N系)
    function machine_t nop(

    );
        nop = {3'h0, NOP, 4'h0, 6'h00, 6'h00, 6'h00, 33'h000000000};
    endfunction

    // 演算系(P系)
    function machine_t and_(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        and_ = {3'h1, AND, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t or_(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        or_ = {3'h1, OR, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t xor_(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        xor_ = {3'h1, XOR, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t not_(
        input addr_t rs1,
        input addr_t rd,
        input imm_t imm
    );
        not_ = {3'h1, NOT, 4'h0, rs1, 6'h00, rd, imm};
    endfunction
    function machine_t nand_(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        nand_ = {3'h1, NAND, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t add(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        add = {3'h1, ADD, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t sub(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        sub = {3'h1, SUB, 4'h0, rs1, rs2, rd, imm};
    endfunction

    // シフト系(S系)
    function machine_t sll(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        sll = {3'h2, SLL, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t srl(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        srl = {3'h2, SRL, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t sla(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        sla = {3'h2, SLA, 4'h0, rs1, rs2, rd, imm};
    endfunction
    function machine_t sra(
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        sra = {3'h2, SRA, 4'h0, rs1, rs2, rd, imm};
    endfunction

    // 代入系(A系)
    function machine_t mov(
        input mask_t mask,
        input addr_t rs1,
        input addr_t rd,
        input imm_t imm
    );
        mov = {3'h3, MOV, mask, rs1, 6'h00, rd, imm};
    endfunction

    // 分岐系(F系)
    function machine_t eq(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        eq = {3'h4, EQ, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t ne(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        ne = {3'h4, NE, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t lt(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        lt = {3'h4, LT, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t gt(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        gt = {3'h4, GT, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t elt(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        elt = {3'h4, ELT, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t egt(
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        egt = {3'h4, EGT, 4'h0, rs1, rs2, 6'h00, imm};
    endfunction

    // ジャンプ系(J系)
    function machine_t jmp(
        input addr_t rs1,
        input imm_t imm
    );
        jmp = {3'h5, JMP, 4'h0, rs1, 6'h00, 6'h00, imm};
    endfunction

    // メモリ系(M系)
    function machine_t rm(
        input mask_t mask,
        input addr_t rs1,
        input addr_t rd,
        input imm_t imm
    );
        rm = {3'h6, RM, mask, rs1, 6'h00, rd, imm};
    endfunction
    function machine_t wm(
        input mask_t mask,
        input addr_t rs1,
        input addr_t rs2,
        input imm_t imm
    );
        wm = {3'h6, WM, mask, rs1, rs2, 6'h00, imm};
    endfunction
    function machine_t brm(
        input mask_t mask,
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        brm = {3'h6, BRM, mask, rs1, rs2, rd, imm};
    endfunction
    function machine_t bwm(
        input mask_t mask,
        input addr_t rs1,
        input addr_t rs2,
        input addr_t rd,
        input imm_t imm
    );
        bwm = {3'h6, BWM, mask, rs1, rs2, rd, imm};
    endfunction

endpackage

`endif
register.svh
/**
 * レジスタに関する定義
 */

`ifndef REGISTER_SVH
`define REGISTER_SVH

`include "machine.svh"

// アドレスの定義
parameter machine_p::func_t SP_ADDR      = 6'h10;
parameter machine_p::func_t FLG_ADDR     = 6'h1c;
parameter machine_p::func_t RSI_ADDR     = 6'h1d;
parameter machine_p::func_t RAX_ADDR     = 6'h1e;
parameter machine_p::func_t PC_ADDR      = 6'h1f;
parameter machine_p::func_t BTN_ADDR     = 6'h20;
parameter machine_p::func_t SW_ADDR      = 6'h21;
parameter machine_p::func_t LED_ADDR     = 6'h22;
parameter machine_p::func_t RGB_LED_ADDR = 6'h23;

`endif
decorder.svh
/**
 * デコーダーに関する関数
 */

`ifndef DECORDER_SV
`define DECORDER_SV

`include "machine.svh"

package decorder_p;
endpackage

// インターフェース定義
interface command_if;  // 機械語から各値を取得してくるインターフェース
    machine_p::machine_t machine;
    machine_p::type_t    m_type;
    machine_p::func_t    func;
    machine_p::mask_t    mask;
    machine_p::addr_t    rs1;
    machine_p::addr_t    rs2;
    machine_p::addr_t    rd;
    machine_p::imm_t     imm;

    modport slave(
        input machine,
        output m_type,
        output func,
        output mask,
        output rs1,
        output rs2,
        output rd,
        output imm
    );

    modport master(
        output machine,
        input m_type,
        input func,
        input mask,
        input rs1,
        input rs2,
        input rd,
        input imm
    );
endinterface

`endif
ram.svh
/**
 * メインメモリに関する関数
 */

`ifndef RAM_SVH
`define RAM_SVH

`include "machine.svh"

package ram_p;
    // 列挙体
    typedef enum logic [1:0] {   // メモリ読み出し・書き込み成否
        NONE,          // 初期状態
        SUCCESS        // 成功
    } code_enum;
    typedef enum logic [1:0] {   // メモリ読み出し・書き込み状態
        IDLE,          // 待機状態
        EXECUTE,       // データ読み出し・書き込みの実行
        RESPONSE       // データ読み出し・書き込みのレスポンスを返す
    } state_enum;

    // 変数型
    typedef logic [27:0] address_bus_t;      // アドレスバス幅
    typedef logic [31:0] data_bus_t;         // データバス幅
    typedef logic [27:0][ 7:0] memory_t;     // メモリのデータ全体
endpackage

// インターフェース定義
interface ram_read_if;     // メモリ読み込みインターフェース
    ram_p::address_bus_t address;
    ram_p::data_bus_t    data;
    machine_p::mask_t    mask;
    logic                valid;
    logic                ready;
    logic                last;
    ram_p::code_enum     code;

    modport slave(
        input  address,
        output data,
        input  mask,
        input  valid,
        output ready,
        input  last,
        output code
    );

    modport master(
        output address,
        input  data,
        output mask,
        output valid,
        input  ready,
        output last,
        input  code
    );
endinterface
interface ram_write_if;     // メモリ書き込みインターフェース
    ram_p::address_bus_t address;
    ram_p::data_bus_t    data;
    machine_p::mask_t    mask;
    logic                valid;
    logic                ready;
    logic                last;
    ram_p::code_enum     code;

    modport slave(
        input  address,
        input  data,
        input  mask,
        input  valid,
        output ready,
        input last,
        output code
    );

    modport master(
        output address,
        output data,
        output mask,
        output valid,
        input  ready,
        output last,
        input  code
    );
endinterface

`endif
rom.svh
/**
 * ROMに関する関数
 */

`ifndef ROM_SVH
`define ROM_SVH

`include "ram.svh"
`include "machine.svh"

package rom_p;
    typedef ram_p::address_bus_t pc_bus_t;  // プログラムカウンタ
endpackage

// インターフェース定義
interface rom_read_if;  // ROM読み込みインターフェース
    rom_p::pc_bus_t      pc;
    machine_p::machine_t machine;

    modport slave(
        input pc,
        output machine
    );

    modport master(
        output pc,
        input machine
    );
endinterface

`endif
alu.svh
/**
 * aluに関する関数など
 */

`ifndef ALU_SVH
`define ALU_SVH

`include "util.svh"
`include "decorder.svh"

package alu_p;
    import machine_p::*;

    // 列挙体
    typedef enum type_t {   // 機械語の命令タイプ
        N_TYPE,   // 処理を実行しない
        P_TYPE,   // 演算系
        S_TYPE,   // シフト系
        A_TYPE,   // 代入系
        F_TYPE,   // 分岐系
        J_TYPE,   // ジャンプ系
        M_TYPE    // メモリ系
    } type_enum;

    // 定数
    localparam integer PC_ADDR = 6'h1f;  // PCのアドレス

    // 変数型
    typedef logic [31:0] register_t;   // レジスタサイズ

    // 関数
    function util_p::bool_t is_readable(   // そのアドレスのレジスタが読み込み可能か
        machine_p::addr_t addr
    );
        // CPU内のレジスタ
        if (6'h00 <= addr && addr <= 6'h0f)
            is_readable = util_p::TRUE;
        // スタックポインタ
        else if (addr == 6'h10)
            is_readable = util_p::TRUE;
        // 呼び出し関数の戻り先
        else if (6'h11 <= addr && addr <= 6'h1a)
            is_readable = util_p::TRUE;
        // フラグ
        else if (addr == 6'h1c)
            is_readable = util_p::TRUE;
        // 引数が格納されているレジスタの一つ目の番地
        else if (addr == 6'h1d)
            is_readable = util_p::TRUE;
        // 演算結果
        else if (addr == 6'h1e)
            is_readable = util_p::TRUE;
        // プログラムカウンタ
        else if (addr == 6'h1f)
            is_readable = util_p::TRUE;
        // タクトスイッチ
        else if (addr == 6'h20)
            is_readable = util_p::TRUE;
        // DIPスイッチ
        else if (addr == 6'h21)
            is_readable = util_p::TRUE;
        // LED
        else if (addr == 6'h22)
            is_readable = util_p::FALSE;
        // RGB LED
        else if (addr == 6'h23)
            is_readable = util_p::FALSE;
        // Pmod A
        else if (addr == 6'h24)
            is_readable = util_p::TRUE;
        // Pmod B
        else if (addr == 6'h25)
            is_readable = util_p::TRUE;
        // AR
        else if (addr == 6'h26 || addr == 6'h28)
            is_readable = util_p::TRUE;
        // I2C
        else if (addr == 6'h27)
            is_readable = util_p::TRUE;
        // AR_RST
        else if (addr == 6'h29)
            is_readable = util_p::TRUE;
        // SPI
        else if (addr == 6'h2a)
            is_readable = util_p::TRUE;
        // アナログピン
        else if (addr == 6'h2b)
            is_readable = util_p::FALSE;    // オミット
        // XADC
        else if (addr == 6'h2c)
            is_readable = util_p::FALSE;    // オミット
        // GPIO
        else if (6'h2d <= addr || addr <= 6'h30)
            is_readable = util_p::TRUE;
        // 定義されていない
        else
            is_readable = util_p::FALSE;
    endfunction
    function util_p::bool_t is_writable(  // そのアドレスのレジスタが書き込み可能か
        machine_p::addr_t addr
    );
        // CPU内のレジスタ
        if (6'h00 <= addr && addr <= 6'h0f)
            is_writable = util_p::TRUE;
        // スタックポインタ
        else if (addr == 6'h10)
            is_writable = util_p::FALSE;
        // 呼び出し関数の戻り先
        else if (6'h11 <= addr && addr <= 6'h1a)
            is_writable = util_p::FALSE;
        // フラグ
        else if (addr == 6'h1c)
            is_writable = util_p::FALSE;
        // 引数が格納されているレジスタの一つ目の番地
        else if (addr == 6'h1d)
            is_writable = util_p::FALSE;
        // 演算結果
        else if (addr == 6'h1e)
            is_writable = util_p::FALSE;
        // プログラムカウンタ
        else if (addr == 6'h1f)
            is_writable = util_p::FALSE;
        // タクトスイッチ
        else if (addr == 6'h20)
            is_writable = util_p::FALSE;
        // DIPスイッチ
        else if (addr == 6'h21)
            is_writable = util_p::FALSE;
        // LED
        else if (addr == 6'h22)
            is_writable = util_p::TRUE;
        // RGB LED
        else if (addr == 6'h23)
            is_writable = util_p::TRUE;
        // Pmod A
        else if (addr == 6'h24)
            is_writable = util_p::TRUE;
        // Pmod B
        else if (addr == 6'h25)
            is_writable = util_p::TRUE;
        // AR
        else if (addr == 6'h26 || addr == 6'h28)
            is_writable = util_p::TRUE;
        // I2C
        else if (addr == 6'h27)
            is_writable = util_p::TRUE;
        // AR_RST
        else if (addr == 6'h29)
            is_writable = util_p::FALSE;
        // SPI
        else if (addr == 6'h2a)
            is_writable = util_p::TRUE;
        // アナログピン
        else if (addr == 6'h2b)
            is_writable = util_p::FALSE;    // オミット
        // XADC
        else if (addr == 6'h2c)
            is_writable = util_p::FALSE;    // オミット
        // GPIO
        else if (6'h2d <= addr || addr <= 6'h30)
            is_writable = util_p::TRUE;
        // 定義されていない
        else
            is_writable = util_p::FALSE;
    endfunction
endpackage

// インターフェース定義

`endif
mother_board.v
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/06 13:34:58
// Design Name:
// Module Name: mother_board
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module mother_board(
    input  wire clk,
    input  wire resetn,

    input  wire [3:0] btn,
    input  wire [1:0] sw,
    output wire [3:0] led,
    output wire [5:0] rgb_led
    );

    mother_board_sv mother_board_sv_0 (
        .clk(clk), .resetn(resetn),

        .btn(btn),
        .sw(sw),
        .led(led),
        .rgb_led(rgb_led)
    );

endmodule
mother_board_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/06 13:34:58
// Design Name:
// Module Name: mother_board_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


`include "ram.svh"
`include "rom.svh"

module mother_board_sv(
    input  logic clk,
    input  logic resetn,

    input  logic [3:0] btn,
    input  logic [1:0] sw,
    output logic [3:0] led,
    output logic [5:0] rgb_led
    );

    // メインメモリ読み込み・書き込みインターフェース
    ram_read_if  ram_read();
    ram_write_if ram_write();

    // メモリ読み込みインターフェース
    rom_read_if rom_read();

    // メインメモリ
    ram_sv ram_sv_0 (
        .clk(clk), .resetn(resetn),
        // メインメモリデータ読み込み
        .ram_read(ram_read),
        // メインメモリデータ書き込み
        .ram_write(ram_write)
    );

    // CPU
    cpu_sv cpu_sv_0 (
        .clk(clk), .resetn(resetn),
        // プログラムデータ読み込み
        .rom_read(rom_read),
        // メモリデータ読み書き
        .ram_read(ram_read),
        .ram_write(ram_write),
        // IO
        .btn(btn),
        .sw(sw),
        .led(led),
        .rgb_led(rgb_led)
    );

    // プログラムメモリ
    rom_sv rom_sv_0 (
        // プログラムデータ読み込み
        .rom_read(rom_read)
    );

endmodule
ram_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/02 20:50:34
// Design Name:
// Module Name: ram_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


`include "ram.svh"

module ram_sv import ram_p::*; (
    input  logic clk,
    input  logic resetn,

    // メモリデータ読み出し
    ram_read_if.slave ram_read,

    // メモリデータ書き込み
    ram_write_if.slave ram_write
    );

    // メモリに保存されるデータ
    memory_t memory_data = 0;

    // メモリ読み出し・書き込み状態
    state_enum ram_read_state = IDLE;
    state_enum ram_write_state = IDLE;

    always_ff @(posedge clk) begin
        // リセット
        if (!resetn) begin
            // IO
            ram_read.data <= 32'b0;
            ram_read.ready <= 1'b0;
            ram_read.code <= NONE;
            ram_write.ready <= 1'b0;
            ram_write.code <= NONE;

            // 内部変数
            memory_data <= 0;
            ram_read_state <= IDLE;
            ram_write_state <= IDLE;
        end
        // メモリ実行
        else begin
            // メモリデータ読み出し
            unique case (ram_read_state)
                // 待機
                IDLE: begin
                    ram_read.code <= NONE;
                    // 読み出し命令を検知
                    if (ram_read.valid) begin
                        ram_read_state <= EXECUTE;
                    end
                end

                // メモリ読み込み実行
                EXECUTE: begin
                    if (ram_read.valid) begin
                        ram_read.ready <= 1'b1;
                        // マスクに応じて読み込み
                        foreach (ram_read.mask[i]) begin
                            if (ram_read.mask[i])
                                // 32ビットのうち,8ビット分を出力
                                ram_read.data[i*8 + 7:i*8] <= memory_data[ram_read.address + i];
                            else
                                ram_read.data[i*8 + 7:i*8] <= 8'h00;
                        end
                        // 読み込みラスト?
                        if (ram_read.last) begin
                            ram_read_state <= RESPONSE;
                        end
                    end else begin
                        ram_read.ready <= 1'b0;
                    end
                end

                // メモリ読み込みの実行結果を返す
                RESPONSE: begin
                    ram_read.ready <= 1'b0;
                    ram_read.code <= SUCCESS;
                    ram_read_state <= IDLE;
                end
            endcase

            // メモリデータ書き込み
            unique case (ram_write_state)
                // 待機
                IDLE: begin
                    ram_write.code <= NONE;
                    // 書き込み命令を検知
                    if (ram_write.valid) begin
                        ram_write_state <= EXECUTE;
                    end
                end

                // メモリ書き込み実行
                EXECUTE: begin
                    if (ram_write.valid) begin
                        ram_write.ready <= 1'b1;
                        // マスクに応じて書き込み
                        foreach (ram_write.mask[i]) begin
                            if (ram_write.mask[i])
                                // 32ビットのうち,8ビット分を入力
                                memory_data[ram_write.address + i] <= ram_write.data[i*8 + 7:i*8];
                            else
                                memory_data[ram_write.address + i] <= 8'h00;
                        end
                        // 書き込みラスト?
                        if (ram_write.last) begin
                            ram_write_state <= RESPONSE;
                        end
                    end else begin
                        ram_write.ready <= 1'b0;
                    end
                end

                // メモリ書き込みの実行結果を返す
                RESPONSE: begin
                    ram_write.ready <= 1'b0;
                    ram_write.code <= SUCCESS;
                    ram_write_state <= IDLE;
                end
            endcase
        end
    end

endmodule
cpu_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/19 20:35:14
// Design Name:
// Module Name: cpu_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


`include "machine.svh"
`include "rom.svh"
`include "decorder.svh"

module cpu_sv import machine_p::*; (
    input logic clk,
    input logic resetn,

    rom_read_if.master rom_read,

    ram_read_if.master ram_read,
    ram_write_if.master ram_write,

    input  logic [3:0] btn,
    input  logic [1:0] sw,
    output logic [3:0] led,
    output logic [5:0] rgb_led
    );

    // デコーダーとのインターフェース
    command_if command();

    // デコーダー
    decorder_sv decorder_sv_0(
        .resetn(resetn),
        .command(command)
    );

    // ALU
    alu_sv alu_sv_0(
        .clk(clk), .resetn(resetn),
        .rom_read(rom_read),
        .command(command),
        .ram_read(ram_read),
        .ram_write(ram_write),
        .btn(btn),
        .sw(sw),
        .led(led),
        .rgb_led(rgb_led)
    );

endmodule
rom_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/19 20:51:41
// Design Name:
// Module Name: rom_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////

`include "rom.svh"
`include "machine.svh"
`include "register.svh"

module rom_sv import machine_p::*; (
    // メモリデータ読み出し
    rom_read_if.slave rom_read
    );

    localparam integer ROM_SIZE = 2;

    // ボタンの状態をLEDに出力するだけのプログラム
    machine_t machines[0:ROM_SIZE - 1] = {
        // ボタンの状態をメインメモリに書き込む
        wm(4'h1, 0, BTN_ADDR, {1'b1, 0}),
        // メインメモリからボタンの状態をLEDに出力
        rm(4'h1, 0, LED_ADDR, {1'b1, 0}),
        // pcを先頭に戻す
        jmp(0, {1'b1, 32'b0})
    };

    // メモリデータの読み出し
    always_comb begin
        if (rom_read.pc >= ROM_SIZE) begin
            rom_read.machine = nop();
        end else begin
            rom_read.machine = machines[rom_read.pc];
        end
    end

endmodule
decorder_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/20 16:22:23
// Design Name:
// Module Name: decorder_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


`include "decorder.svh"

module decorder_sv (
    input logic resetn,
    command_if.slave command
    );

    // 組み合わせ回路
    always_comb begin
        // リセット
        if (!resetn) begin
            command.m_type = 0;
            command.func   = 0;
            command.mask   = 0;
            command.rs1    = 0;
            command.rs2    = 0;
            command.rd     = 0;
            command.imm    = 0;
        end
        // 機械語を展開
        else begin
            command.m_type = command.machine[31 + 32:29 + 32];
            command.func   = command.machine[28 + 32:23 + 32];
            command.mask   = command.machine[22 + 32:19 + 32];
            command.rs1    = command.machine[18 + 32:13 + 32];
            command.rs2    = command.machine[12 + 32: 7 + 32];
            command.rd     = command.machine[ 6 + 32: 1 + 32];
            command.imm    = command.machine[31:0];
        end
    end

endmodule
alu_sv.sv
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/20 16:41:07
// Design Name:
// Module Name: alu_sv
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


`include "rom.svh"
`include "decorder.svh"
`include "alu.svh"
`include "ram.svh"
`include "machine.svh"

module alu_sv import alu_p::*, ram_p::*, machine_p::*; (
    input logic clk,
    input logic resetn,

    rom_read_if.master rom_read,
    command_if.master command,

    // メモリ読み込みインターフェース
    ram_read_if.master ram_read,
    // メモリ書き込みインターフェース
    ram_write_if.master ram_write,

    input  logic [3:0] btn,
    input  logic [1:0] sw,
    output logic [3:0] led,
    output logic [5:0] rgb_led
    );

    // 内部レジスタ
    register_t register[6'h30:0] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    // メモリ読み込み・書き出し状態
    ram_p::state_enum ram_read_state = IDLE;
    ram_p::state_enum ram_write_state = IDLE;

    // 強制リセット
    logic force_reset = 1'b0;

    // デバッグ用
    type_t m_type = 0;
    func_t func = 0;
    mask_t mask = 0;
    addr_t rs1 = 0;
    addr_t rs2 = 0;
    addr_t rd = 0;
    imm_t imm = 0;

    // 組み合わせ回路
    always_comb begin
        // 機械語を分解してもらう
        command.machine = rom_read.machine;

        // メモリのバースト転送はオミットする
        ram_read.last = 1'b1;
        ram_write.last = 1'b1;

        // IOにレジスタの値を出力する
        led     = register[6'h22][3:0];
        rgb_led = register[6'h23][5:0];

        // pcを出力する
        rom_read.pc = register[PC_ADDR];

        // デバッグ用
        m_type = command.m_type;
        func = command.func;
        mask = command.mask;
        rs1 = command.rs1;
        rs2 = command.rs2;
        rd = command.rd;
        imm = command.imm;
    end

    // 順序回路
    always_ff @(posedge clk) begin
        // リセット
        if (!resetn || force_reset) begin
            // メモリの読み込み・書き出し状態をリセット
            ram_read_state <= IDLE;
            ram_write_state <= IDLE;

            // メモリ読み書き
            ram_read.address = 0;
            ram_read.mask = 0;
            ram_read.valid = 0;
            ram_write.address = 0;
            ram_write.data = 0;
            ram_write.mask = 0;
            ram_write.valid = 0;

            // レジスタ
            for (logic [5:0] i = 0; i <= 6'h31; i++) begin
                register[i] = 0;
            end
        end
        // 命令実行
        else begin
            // IOからレジスタに値を格納する
            register[6'h20] = {4'b0, btn};
            register[6'h21] = {6'b0, sw};

            // 関数タイプごとに実行
            unique case (command.m_type)
                // 処理を実行しない(N系)
                N_TYPE: begin
                    // pcをカウントアップ
                    register[PC_ADDR] <= register[PC_ADDR] + 1;

                    // 不正な値が入っても全て無視する
                end

                // 演算系(P系)
                P_TYPE: begin
                    // 演算系はどの命令でもpcをカウントアップする
                    register[PC_ADDR] <= register[PC_ADDR] + 1;

                    // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                    if (
                        is_readable(command.rs1)
                        && is_readable(command.rs2)
                        && is_writable(command.rd)
                    ) begin
                        unique case (command.func)
                            AND:  register[command.rd] <= register[command.rs1] & register[command.rs2];
                            OR:   register[command.rd] <= register[command.rs1] | register[command.rs2];
                            XOR:  register[command.rd] <= register[command.rs1] ^ register[command.rs2];
                            NOT:  register[command.rd] <= ~register[command.rs1];
                            NAND: register[command.rd] <= ~(register[command.rs1] & register[command.rs2]);
                            ADD:  register[command.rd] <= register[command.rs1] + register[command.rs2];
                            SUB:  register[command.rd] <= register[command.rs1] - register[command.rs2];
                            default: force_reset <= 1'b1;
                        endcase
                    end
                    else begin
                        force_reset <= 1'b1;
                    end
                end

                // シフト系
                S_TYPE: begin
                    // シフト系はどの命令でもpcをカウントアップする
                    register[PC_ADDR] <= register[PC_ADDR] + 1;

                    // イミディエイトデータを使用する?
                    if (command.imm[32]) begin
                        // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                        if (
                            is_readable(command.rs1)
                            && is_writable(command.rd)
                        ) begin
                            unique case (command.func)
                                SLL: register[command.rd] <= register[command.rs1] << command.imm[31:0];
                                SRL: register[command.rd] <= register[command.rs1] >> command.imm[31:0];
                                SLA: register[command.rd] <= register[command.rs1] <<< command.imm[31:0];
                                SRA: register[command.rd] <= register[command.rs1] >>> command.imm[31:0];
                                default: force_reset <= 1'b1;
                            endcase
                        end
                        else begin
                            force_reset <= 1'b1;
                        end
                    end else begin
                        // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                        if (
                            is_readable(command.rs1)
                            && is_readable(command.rs2)
                            && is_writable(command.rd)
                        ) begin
                            unique case (command.func)
                                SLL: register[command.rd] <= register[command.rs1] << register[command.rs2];
                                SRL: register[command.rd] <= register[command.rs1] >> register[command.rs2];
                                SLA: register[command.rd] <= register[command.rs1] <<< register[command.rs2];
                                SRA: register[command.rd] <= register[command.rs1] >>> register[command.rs2];
                                default: force_reset <= 1'b1;
                            endcase
                        end
                        else begin
                            force_reset <= 1'b1;
                        end
                    end
                end

                // 代入系
                A_TYPE: begin
                    // 代入系はどの命令でもpcをカウントアップする
                    register[PC_ADDR] <= register[PC_ADDR] + 1;

                    // 命令がMOVの時だけ実行
                    if (command.func == MOV) begin
                        // イミディエイトデータを使用する?
                        if (command.imm[32]) begin
                            // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                            if (is_writable(command.rd)) begin
                                register[command.rd] <= command.imm[31:0];
                            end
                            else begin
                                force_reset <= 1'b1;
                            end
                        end else begin
                            // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                            if (is_readable(command.rs1) && is_writable(command.rd)) begin
                                register[command.rd] <= register[command.rs1];
                            end
                            else begin
                                force_reset <= 1'b1;
                            end
                        end
                    end
                    else begin
                        force_reset <= 1'b1;
                    end
                end

                // 分岐系
                F_TYPE: begin
                    // 指定されているアドレスが全て使用可能
                    // かつイミディエイトデータを使用する設定の時だけ,処理を実行する
                    if (
                        is_readable(command.rs1)
                        && is_readable(command.rs2)
                        && is_writable(command.rd)
                        && command.imm[32]
                    ) begin
                        unique case (command.func)
                            EQ: begin
                                if (register[command.rs1] == register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            NE: begin
                                if (register[command.rs1] != register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            LT: begin
                                if (register[command.rs1] < register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            GT: begin
                                if (register[command.rs1] > register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            ELT: begin
                                if (register[command.rs1] <= register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            EGT: begin
                                if (register[command.rs1] >= register[command.rs2])
                                    register[PC_ADDR] <= register[PC_ADDR] + command.imm[31:0];
                                else
                                    register[PC_ADDR] <= register[PC_ADDR] + 1;
                            end
                            default: begin
                                force_reset <= 1'b1;
                            end
                        endcase
                    end
                    else begin
                        force_reset <= 1'b1;
                    end
                end

                // ジャンプ系
                J_TYPE: begin
                    // 命令がジャンプの時だけ実行
                    if (command.func == JMP) begin
                        // イミディエイトデータを使用する?
                        if (command.imm[32]) begin
                            register[PC_ADDR] <= command.imm[31:0];
                        end else begin
                            // 指定されているアドレスが全て使用可能な場合のみ,処理を実行する
                            if (is_readable(command.rs1)) begin
                                register[PC_ADDR] <= register[command.rs1];
                            end
                            else begin
                                force_reset <= 1'b1;
                            end
                        end
                    end
                    else begin
                        force_reset <= 1'b1;
                    end
                end

                // メモリ系
                M_TYPE: begin
                    unique case (command.func)
                        // メモリ読み込み
                        RM: begin
                            unique case (ram_read_state)
                                // 待機
                                IDLE: begin
                                    // 実行を指示
                                    ram_read_state <= EXECUTE;
                                    // 実行状態であることを送る
                                    ram_read.valid <= 1'b1;

                                    // マスク情報を送る
                                    ram_read.mask <= command.mask;
                                    // アドレス情報を送る
                                    if (command.imm[32]) begin
                                        // イミディエイトデータを使用する指定なら,それを送る
                                        ram_read.address <= command.imm[31:0];
                                    end
                                    else begin
                                        // rs1を使用するなら,書き込み可能であることを確認して送る
                                        if (is_writable(command.rs1)) begin
                                            ram_read.address <= register[command.rs1];
                                        end
                                        else begin
                                            // 強制リセット
                                            force_reset <= 1'b1;
                                        end
                                    end
                                end

                                // メモリ読み込み実行
                                EXECUTE: begin
                                    // 読み込みが完了したなら
                                    if (ram_read.ready) begin
                                        // 待機状態に遷移
                                        ram_read_state <= IDLE;
                                        // 実行状態をオフ
                                        ram_read.valid <= 1'b0;

                                        // データを受け取る
                                        if (is_writable(command.rd)) begin
                                            register[command.rd] <= ram_read.data;
                                        end
                                        else begin
                                            force_reset <= 1'b1;
                                        end

                                        // プログラムカウンタをインクリメント
                                        register[PC_ADDR] <= register[PC_ADDR] + 1;
                                    end
                                end

                                // その他
                                default: begin
                                    force_reset <= 1'b1;
                                end
                            endcase
                        end
                        // メモリ書き込み
                        WM: begin
                            unique case(ram_write_state)
                                // 待機
                                IDLE: begin
                                    // 実行を指示
                                    ram_write_state <= EXECUTE;
                                    // 実行状態であることを送る
                                    ram_write.valid <= 1'b1;

                                    // マスク情報を送る
                                    ram_write.mask <= command.mask;
                                    // アドレス情報を送る
                                    if (command.imm[32]) begin
                                        // イミディエイトデータを使用する指定なら,それを送る
                                        ram_write.address <= command.imm[31:0];
                                    end
                                    else begin
                                        // rs1を使用するなら,書き込み可能であることを確認して送る
                                        if (is_writable(command.rs1)) begin
                                            ram_write.address <= register[command.rs1];
                                        end
                                        else begin
                                            // 強制リセット
                                            force_reset <= 1'b1;
                                        end
                                    end
                                    // データを送る
                                    if (is_readable(command.rs2)) begin
                                        ram_write.data <= register[command.rs2];
                                    end
                                    else begin
                                        // 強制リセット
                                        force_reset <= 1'b1;
                                    end
                                end

                                // メモリ書き込み実行
                                EXECUTE: begin
                                    // 書き込みが完了したなら
                                    if (ram_write.ready) begin
                                        // 待機状態に遷移
                                        ram_write_state <= IDLE;
                                        // 実行状態をオフ
                                        ram_write.valid <= 1'b0;

                                        // プログラムカウンタをインクリメント
                                        register[PC_ADDR] <= register[PC_ADDR] + 1;
                                    end
                                end

                                // その他
                                default: begin
                                    force_reset <= 1'b1;
                                end
                            endcase
                        end
                        // バーストはオミット
                        // メモリ読み込み(バースト)
                        BRM: begin
                            force_reset <= 1'b1;
                        end
                        // メモリ書き込み(バースト)
                        BWM: begin
                            force_reset <= 1'b1;
                        end

                        default: begin
                            force_reset <= 1'b1;
                        end
                    endcase
                end

                default: begin
                    force_reset <= 1'b1;
                end
            endcase
        end
    end

endmodule

テストコード

大したテストはしてないですが一応.

top_sim.v
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/08/24 20:10:28
// Design Name:
// Module Name: top_sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module top_sim();
    // クロック関係
    reg clk = 1'b0, resetn = 1'b1;
    always #(10/2) begin
        clk <= !clk;
    end

    // 入力
    reg [3:0] btn = 0;
    reg [1:0] sw = 0;

    // 出力
    wire [3:0] led;
    wire [5:0] rgb_led;

    // モジュール
    mother_board mother_board_0(
        .clk(clk), .resetn(resetn),
        .btn(btn), .sw(sw),
        .led(led), .rgb_led(rgb_led)
    );

    initial begin
        // ボタンに値を入れる
        btn[0] = 1'b1;

        $finish();
    end

endmodule

実行結果

次回の目標

最終的にはCUIで何かコントロールしたいので,その途中になるものを何か考えておきます.

0
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
0
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?