S SmartDocs
シリーズ: RISC V Hardware verilog 244 行 · 更新日 2026-07-04

riscv_pipeline.v

RISC_V_Hardware/verilog/rtl/riscv_pipeline.v

// ============================================================
// riscv_pipeline.v -- 5-stage pipelined RV32I CPU core
//   IF -> ID -> EX -> MEM -> WB
// Features:
//   * full forwarding (EX/MEM and MEM/WB -> EX)
//   * load-use hazard detection (1-cycle stall)
//   * branches/jumps resolved in EX, 2-cycle flush on taken
// Reuses: alu, regfile, imm_gen, control, branch_unit
// ============================================================
`timescale 1ns/1ps

module riscv_pipeline (
    input  wire        clk,
    input  wire        rst_n,
    // instruction memory port (combinational ROM)
    output wire [31:0] imem_addr,
    input  wire [31:0] imem_data,
    // data memory port
    output wire        dmem_read,
    output wire        dmem_write,
    output wire [31:0] dmem_addr,
    output wire [2:0]  dmem_funct3,
    output wire [31:0] dmem_wdata,
    input  wire [31:0] dmem_rdata,
    // debug
    output wire [31:0] dbg_pc
);
    // ================= IF stage =================
    reg  [31:0] pc;
    wire [31:0] pc_plus4 = pc + 32'd4;

    wire        stall;          // freeze PC and IF/ID (load-use)
    wire        redirect;       // EX resolved taken branch / jump
    wire [31:0] redirect_pc;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)         pc <= 32'b0;
        else if (redirect)  pc <= redirect_pc;
        else if (!stall)    pc <= pc_plus4;
    end

    assign imem_addr = pc;
    assign dbg_pc    = pc;

    // ---------------- IF/ID register ----------------
    reg [31:0] ifid_pc, ifid_instr;
    reg        ifid_valid;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ifid_pc <= 32'b0; ifid_instr <= 32'h00000013; ifid_valid <= 1'b0;
        end else if (redirect) begin            // flush
            ifid_instr <= 32'h00000013; ifid_valid <= 1'b0;
        end else if (!stall) begin
            ifid_pc <= pc; ifid_instr <= imem_data; ifid_valid <= 1'b1;
        end
        // stall: hold
    end

    // ================= ID stage =================
    wire [6:0] id_opcode = ifid_instr[6:0];
    wire [4:0] id_rd     = ifid_instr[11:7];
    wire [2:0] id_funct3 = ifid_instr[14:12];
    wire [4:0] id_rs1    = ifid_instr[19:15];
    wire [4:0] id_rs2    = ifid_instr[24:20];

    wire       id_reg_write, id_alu_src, id_mem_read, id_mem_write;
    wire       id_branch, id_jump, id_jalr, id_alu_a_pc;
    wire [1:0] id_wb_sel;
    wire [3:0] id_alu_op;

    control u_control (
        .opcode(id_opcode), .funct3(id_funct3), .funct7b5(ifid_instr[30]),
        .reg_write(id_reg_write), .alu_src(id_alu_src), .wb_sel(id_wb_sel),
        .mem_read(id_mem_read), .mem_write(id_mem_write),
        .branch(id_branch), .jump(id_jump), .jalr(id_jalr),
        .alu_a_pc(id_alu_a_pc), .alu_op(id_alu_op)
    );

    wire [31:0] id_rs1_data, id_rs2_data;
    wire [4:0]  wb_rd;
    wire        wb_reg_write;
    wire [31:0] wb_data;

    regfile #(.BYPASS(1)) u_regfile (
        .clk(clk), .we(wb_reg_write),
        .ra1(id_rs1), .ra2(id_rs2), .wa(wb_rd), .wd(wb_data),
        .rd1(id_rs1_data), .rd2(id_rs2_data)
    );

    wire [31:0] id_imm;
    imm_gen u_immgen (.instr(ifid_instr), .imm(id_imm));

    // ---- load-use hazard detection ----
    // instruction in EX is a load whose rd matches ID's rs1/rs2
    reg        idex_mem_read;
    reg [4:0]  idex_rd;
    wire uses_rs1 = (id_opcode != 7'b0110111) && (id_opcode != 7'b1101111); // not LUI/JAL
    wire uses_rs2 = (id_opcode == 7'b0110011) || (id_opcode == 7'b0100011) ||
                    (id_opcode == 7'b1100011);   // R-type, store, branch
    assign stall = idex_mem_read && (idex_rd != 5'd0) && ifid_valid &&
                   ((uses_rs1 && (idex_rd == id_rs1)) ||
                    (uses_rs2 && (idex_rd == id_rs2)));

    // ---------------- ID/EX register ----------------
    reg [31:0] idex_pc, idex_rs1_data, idex_rs2_data, idex_imm;
    reg [4:0]  idex_rs1, idex_rs2;
    reg [2:0]  idex_funct3;
    reg        idex_reg_write, idex_alu_src, idex_mem_write;
    reg        idex_branch, idex_jump, idex_jalr, idex_alu_a_pc;
    reg [1:0]  idex_wb_sel;
    reg [3:0]  idex_alu_op;

    wire id_bubble = stall | redirect | ~ifid_valid;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n || id_bubble) begin
            idex_reg_write <= 1'b0; idex_mem_read <= 1'b0; idex_mem_write <= 1'b0;
            idex_branch <= 1'b0; idex_jump <= 1'b0; idex_jalr <= 1'b0;
            idex_alu_a_pc <= 1'b0; idex_alu_src <= 1'b0;
            idex_wb_sel <= 2'b00; idex_alu_op <= 4'b0;
            idex_pc <= 32'b0; idex_imm <= 32'b0;
            idex_rs1_data <= 32'b0; idex_rs2_data <= 32'b0;
            idex_rs1 <= 5'b0; idex_rs2 <= 5'b0; idex_rd <= 5'b0;
            idex_funct3 <= 3'b0;
        end else begin
            idex_pc <= ifid_pc;
            idex_rs1_data <= id_rs1_data; idex_rs2_data <= id_rs2_data;
            idex_imm <= id_imm;
            idex_rs1 <= id_rs1; idex_rs2 <= id_rs2; idex_rd <= id_rd;
            idex_funct3 <= id_funct3;
            idex_reg_write <= id_reg_write; idex_alu_src <= id_alu_src;
            idex_mem_read <= id_mem_read; idex_mem_write <= id_mem_write;
            idex_branch <= id_branch; idex_jump <= id_jump; idex_jalr <= id_jalr;
            idex_alu_a_pc <= id_alu_a_pc;
            idex_wb_sel <= id_wb_sel; idex_alu_op <= id_alu_op;
        end
    end

    // ================= EX stage =================
    // ---- forwarding muxes ----
    reg  [31:0] exmem_alu_y, exmem_imm, exmem_pc_plus4;
    reg  [4:0]  exmem_rd;
    reg         exmem_reg_write;
    reg  [1:0]  exmem_wb_sel;

    // value available at end of MEM for a non-load producer
    wire [31:0] exmem_fwd_val = (exmem_wb_sel == 2'b10) ? exmem_pc_plus4 :
                                (exmem_wb_sel == 2'b11) ? exmem_imm      :
                                                          exmem_alu_y;

    wire fwd1_exmem = exmem_reg_write && (exmem_rd != 5'd0) && (exmem_rd == idex_rs1);
    wire fwd2_exmem = exmem_reg_write && (exmem_rd != 5'd0) && (exmem_rd == idex_rs2);
    wire fwd1_memwb = wb_reg_write   && (wb_rd    != 5'd0) && (wb_rd    == idex_rs1);
    wire fwd2_memwb = wb_reg_write   && (wb_rd    != 5'd0) && (wb_rd    == idex_rs2);

    wire [31:0] ex_rs1_fwd = fwd1_exmem ? exmem_fwd_val :
                             fwd1_memwb ? wb_data       :
                                          idex_rs1_data;
    wire [31:0] ex_rs2_fwd = fwd2_exmem ? exmem_fwd_val :
                             fwd2_memwb ? wb_data       :
                                          idex_rs2_data;

    wire [31:0] ex_alu_a = idex_alu_a_pc ? idex_pc  : ex_rs1_fwd;
    wire [31:0] ex_alu_b = idex_alu_src  ? idex_imm : ex_rs2_fwd;

    wire [31:0] ex_alu_y;
    wire        ex_alu_zero;
    alu u_alu (.a(ex_alu_a), .b(ex_alu_b), .alu_op(idex_alu_op),
               .y(ex_alu_y), .zero(ex_alu_zero));

    wire ex_br_taken;
    branch_unit u_branch (.rs1(ex_rs1_fwd), .rs2(ex_rs2_fwd),
                          .funct3(idex_funct3), .taken(ex_br_taken));

    wire ex_take_branch = idex_branch & ex_br_taken;
    wire [31:0] ex_branch_target = idex_pc + idex_imm;
    wire [31:0] ex_jalr_target   = (ex_rs1_fwd + idex_imm) & ~32'b1;

    assign redirect    = idex_jump | idex_jalr | ex_take_branch;
    assign redirect_pc = idex_jalr ? ex_jalr_target : ex_branch_target;

    // ---------------- EX/MEM register ----------------
    reg [31:0] exmem_rs2_data;
    reg [2:0]  exmem_funct3;
    reg        exmem_mem_read, exmem_mem_write;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            exmem_reg_write <= 1'b0; exmem_mem_read <= 1'b0; exmem_mem_write <= 1'b0;
            exmem_wb_sel <= 2'b00; exmem_rd <= 5'b0;
            exmem_alu_y <= 32'b0; exmem_rs2_data <= 32'b0;
            exmem_imm <= 32'b0; exmem_pc_plus4 <= 32'b0; exmem_funct3 <= 3'b0;
        end else begin
            exmem_alu_y <= ex_alu_y;
            exmem_rs2_data <= ex_rs2_fwd;
            exmem_imm <= idex_imm;
            exmem_pc_plus4 <= idex_pc + 32'd4;
            exmem_rd <= idex_rd;
            exmem_funct3 <= idex_funct3;
            exmem_reg_write <= idex_reg_write;
            exmem_mem_read <= idex_mem_read;
            exmem_mem_write <= idex_mem_write;
            exmem_wb_sel <= idex_wb_sel;
        end
    end

    // ================= MEM stage =================
    assign dmem_read   = exmem_mem_read;
    assign dmem_write  = exmem_mem_write;
    assign dmem_addr   = exmem_alu_y;
    assign dmem_funct3 = exmem_funct3;
    assign dmem_wdata  = exmem_rs2_data;

    // ---------------- MEM/WB register ----------------
    reg [31:0] memwb_alu_y, memwb_mem_rdata, memwb_imm, memwb_pc_plus4;
    reg [4:0]  memwb_rd;
    reg        memwb_reg_write;
    reg [1:0]  memwb_wb_sel;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            memwb_reg_write <= 1'b0; memwb_wb_sel <= 2'b00; memwb_rd <= 5'b0;
            memwb_alu_y <= 32'b0; memwb_mem_rdata <= 32'b0;
            memwb_imm <= 32'b0; memwb_pc_plus4 <= 32'b0;
        end else begin
            memwb_alu_y <= exmem_alu_y;
            memwb_mem_rdata <= dmem_rdata;
            memwb_imm <= exmem_imm;
            memwb_pc_plus4 <= exmem_pc_plus4;
            memwb_rd <= exmem_rd;
            memwb_reg_write <= exmem_reg_write;
            memwb_wb_sel <= exmem_wb_sel;
        end
    end

    // ================= WB stage =================
    assign wb_data = (memwb_wb_sel == 2'b01) ? memwb_mem_rdata :
                     (memwb_wb_sel == 2'b10) ? memwb_pc_plus4  :
                     (memwb_wb_sel == 2'b11) ? memwb_imm       :
                                               memwb_alu_y;
    assign wb_rd        = memwb_rd;
    assign wb_reg_write = memwb_reg_write;
endmodule

関連記事