基于 Xilinx FPGA 的 RISC-V 五级流水线 CPU 设计
本文记录在 Xilinx FPGA 上从零实现一个完整的 RISC-V 五级流水线 CPU 的过程。该设计包含取指、译码、执行、访存、写回五大阶段,并解决真实数据冒险与控制冒险。
为什么选择 RISC-V + FPGA?
- 开放免费:没有授权费,文档齐全,寄存器编码明确。
- 简洁清晰:RV32I 指令集仅 40 多条,无历史包袱。
详细记录了在 Xilinx FPGA 上从零实现 RISC-V 五级流水线 CPU 的全过程。内容涵盖取指、译码、执行、访存、写回五大阶段的设计,重点讲解了结构冲突、数据冒险与控制冒险的解决方案,包括前递单元和暂停逻辑的实现。文章提供了基于 Vivado 的工具链搭建步骤、SDC 约束设置、BRAM 内存模拟及 ILA 调试方法,并给出了计算数组求和的实测案例验证了 CPU 的正确性。适合希望深入理解计算机组成原理与硬件设计的开发者参考。
本文记录在 Xilinx FPGA 上从零实现一个完整的 RISC-V 五级流水线 CPU 的过程。该设计包含取指、译码、执行、访存、写回五大阶段,并解决真实数据冒险与控制冒险。
FPGA 允许灵活修改逻辑,重新综合后快速烧录验证。ILA(集成逻辑分析仪)可抓取内部任意信号波形,便于调试。
现代 CPU 通过让多条指令同时处于不同阶段来提高吞吐率。
| 阶段 | 对应操作 | 类比 |
|---|---|---|
| IF(Instruction Fetch) | 根据 PC 读取指令 | 店员去仓库拿食谱 |
| ID(Instruction Decode) | 解码指令,读取寄存器值 | 看懂食谱,准备好原料 |
| EX(Execute) | ALU 运算或地址计算 | 开始揉面、发酵 |
| MEM(Memory Access) | 访问内存(load/store) | 把成品放进/拿出烤箱 |
| WB(Write Back) | 写结果回寄存器 | 把做好的面包摆上货架 |
理想情况下,每个时钟周期都有一个新任务进入流程,也有一个成品离开。吞吐率接近每周期一条指令。
关键指标:
- CPI(Clocks Per Instruction)≈ 1(理想状态)
- 加速比 ≈ 5 倍于非流水线设计
流水线会遇到三大障碍:结构冲突、数据冒险、控制冒险。
目标:使用 Digilent Nexys A7 开发板(XC7A35T),基于 Vivado 2023.1 工具链,构建完整 SoC。
整个系统采用哈佛架构(分离指令与数据存储),关键模块如下:
+------------------+
| Clock (50MHz) |
+--------+---------+
|
+--------v-----------------------------------+
| IF Stage |
| (fetch instruction & update PC) |
+-------------------+------------------------+
|
+-------------------v------------------------+
| ID |
| Control Unit ← Opcode Decoder |
| RegFile Read ← rs1, rs2 |
+-------------------+------------------------+
|
+-------------------v------------------------+
| EX |
| ALU Control ← funct3/funct7 |
| ALU Operation (add/sub/and/or/slt/etc.) |
+-------------------+------------------------+
|
+-------------------v------------------------+
| MEM |
| Data Memory (Block RAM) |
| Handle lw/sw |
+-------------------+------------------------+
|
+-------------------v------------------------+
| WB |
| Write Mux ← ALU out / MEM data |
| RegFile Write Enable |
+----------------------------------------------+
此外还有两个关键单元:
所有模块通过跨级寄存器连接,形成稳定的流水推进机制。
先验证一条 add x1, x2, x3 能否正确执行。重点检查 PC 自增、指令解析、ALU 输出及写回结果。
// 示例:最简 WB 阶段
always_ff @(posedge clk) begin
if (wb_we && rd != 0) begin
regfile[rd] <= wb_data; // 写回结果
end
end
加入各阶段间的缓冲寄存器。
// IF/ID 寄存器组
always_ff @(posedge clk) begin
if (!stall) begin
if_id_pc <= pc;
if_id_inst <= inst_rom[pc>>2];
end
end
注意 !stall 条件——这是未来插入气泡的基础。跳转指令会让后面的预取指令失效,需清空 IF/ID 流水线并更新 PC。
典型场景:
lw x1, 0(x2) # MEM 阶段才拿到数据
add x3, x1, x4 # 下一条就要用 x1 → RAW 危险!
有两个选择:暂停一拍(Insert Bubble)或前递(Forwarding)。对于普通算术指令之间的依赖,可以通过前递解决。
监控 MEM 和 WB 阶段的输出,提前借用'热数据':
// Forwarding Unit 核心逻辑
always_comb begin
forward_a = 2'b00;
forward_b = 2'b00;
// EX/MEM 阶段有写操作,且目标寄存器匹配源操作数
if (ex_mem_we && ex_mem_rd != 0 && ex_mem_rd == id_ex_rs1)
forward_a = 2'b01;
// 来自 WB 阶段写回数据
else if (mem_wb_we && mem_wb_rd != 0 && mem_wb_rd == id_ex_rs1)
forward_a = 2'b10;
// 同理处理第二个源操作数
if (ex_mem_we && ex_mem_rd != 0 && ex_mem_rd == id_ex_rs2)
forward_b = 2'b01;
else if (mem_wb_we && mem_wb_rd != 0 && mem_wb_rd == id_ex_rs2)
forward_b = 2'b10;
end
然后在 EX 阶段之前,用 MUX 选择实际输入:
assign src1 = (forward_a == 2'b01) ? ex_mem_alu_out :
(forward_a == 2'b10) ? mem_wb_wdata : id_ex_src1;
assign src2 = (forward_b == 2'b01) ? ex_mem_alu_out :
(forward_b == 2'b10) ? mem_wb_wdata : id_ex_src2;
当检测到当前是 lw 指令,且下一条要用其结果时,必须插入一个气泡。
assign stall = (id_ex_mem_read && (id_ex_rd == if_id_rs1 || id_ex_rd == if_id_rs2));
必须告诉 Vivado 时钟频率:
create_clock -period 10.000 [get_ports clk]
set_input_delay -clock clk 1.0 [all_inputs]
set_output_delay -clock clk 1.0 [all_outputs]
推荐将指令存储和数据存储分别映射到两个独立的 Block RAM 中,初始化为 .bin 文件。可用脚本将 RISC-V 汇编编译成机器码生成 coe 文件。
提前插入 ILA 核,抓取以下关键信号:
pc, instructionregfile[32]alu_result, mem_data_outforward_a, forward_b, stall测试程序功能为计算数组求和:
.global _start
_start:
li x5, 10 # counter
li x6, 0 # sum
la x7, arr # base address
loop:
lw x8, 0(x7) # load element
add x6, x6, x8 # accumulate
addi x7, x7, 4 # next address
addi x5, x5, -1 # decrement counter
bne x5, zero, loop
trap:
j trap # infinite loop
观测结果:
x6 最终值为 0x1E(即 30),符合预期lw 后确实插入了一拍停顿mul, div 指令
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online