基于 Vivado 的 RISC-V 五级流水线 CPU FPGA 实现详解
为什么选 RISC-V + 五级流水?
性能和资源的平衡是核心考量。单周期 CPU 虽然逻辑简单,但主频受限且资源利用率低。五级流水线将指令拆分为五个阶段并行推进,从第 5 个周期开始每个周期输出一条新指令结果,显著提升吞吐率。
RISC-V 的 RV32I 基础整数集指令简洁,控制逻辑清晰,非常适合 FPGA 开发。
配置建议:Artix-7 XC7A35T 开发板 + Vivado 2023.1 + 支持 RV32I 的轻量级核心设计
五级流水线本质
流水线思想是让多条指令像工厂装配线一样并行推进。五个阶段如下:
| 阶段 | 功能 | 关键任务 |
|---|---|---|
| IF(取指) | 取指令 | PC 寻址,从 IMEM 读取指令 |
| ID(译码) | 拆指令 | 解析 opcode,读寄存器,生成控制信号 |
| EX(执行) | 运算 | ALU 计算,地址生成,分支判断 |
| MEM(访存) | 访问内存 | Load/Store 数据,其他指令透传 |
| WB(写回) | 写结果 | 将数据写回寄存器文件 |
理想状态下每拍都有五条指令分布在不同阶段,但需处理流水线冒险。
核心模块拆解
取指单元(IF)
PC 更新逻辑需支持跳转、分支和异常来源。
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
pc <= 32'h0;
else
pc <= pc + 4;
end
IMEM 使用 XPM 原语创建双端口 RAM:
xpm_memory_sdpram #( .ADDR_WIDTH_A(10), .DATA_WIDTH_A(32) ) imem_inst (
.clka(clk),
.addra(pc[3:2]),
.douta(inst_out)
);
译码单元(ID)
核心任务是拆包指令和读取操作数。寄存器文件需注意 x0 永远为 0。
module regfile (
input clk, we,
input [4:0] waddr, wdata,
input [4:0] raddr1, raddr2,
output [31:0] rdata1, rdata2
);
reg [31:0] regs [0:31];
always @(posedge clk)
if (we && waddr != 5'd0)
regs[waddr] <= wdata;
assign rdata1 = (raddr1 == 5'd0) ? 32'd0 : regs[raddr1];
assign rdata2 = (raddr2 == 5'd0) ? 32'd0 : regs[raddr2];
endmodule
控制信号包括 reg_write, alu_op, mem_read/write 等,建议打包传递。
执行单元(EX)
ALU 设计应分层处理操作数来源和运算类型。
assign op_b = src_sel ? imm_val : rs2_data;
always @(*) begin
case (alu_ctrl)
OP_ADD: result = op_a + op_b;
OP_SUB: result = op_a - op_b;
// ... 其他运算
endcase
end

