Skip to content

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Pipeline RISC-V CPU

本项目是一个使用 Verilog 编写的 32 位 RISC-V 五级流水线 CPU,实现了取指、译码、执行、访存、写回五个阶段,并配套了仿真 testbench、指令/数据存储器、分支预测模块、冒险停顿控制以及 FPGA 顶层显示逻辑。

当前根目录下的 Verilog 文件是主版本;Pipeline/ 子目录只包含部分同名模块,更像历史版本或备份版本,除非特别需要对比,否则建议以根目录源码为准。

参考与致谢

本项目受 VenciFreeman/RISC-V 启发。该仓库是一个使用 Verilog 编写的简单 RISC-V CPU 项目,包含单周期和流水线等实现思路;本项目在其基础思路上结合当前实验目标进行了扩展和调整。

项目特性

  • 五级流水线结构:IF、ID、EX、MEM、WB。
  • 支持寄存器堆同周期读写旁路、EX 阶段前递以及 load/use 等数据冒险停顿。
  • 支持条件分支、jal、jalr 的跳转处理,并带预测错误恢复 flush 逻辑。
  • 带局部/全局方向预测表的分支预测模块 BRANCH_PRE。
  • 支持 lw、sw 访存,数据存储器按小端序读写 32 位字。
  • 支持 RV32I 常用整数指令,并扩展支持 RV32M 的乘除余指令。
  • 提供仿真顶层 riscv_soc_tb.v。
  • 提供 FPGA 顶层 fpga_top.v、数码管驱动 seg_driver.v 和管脚约束 fpga_pin.xdc。
  • 板级演示可将写入 0x10000000 的数据输出到 LED 和 8 位数码管。

目录结构

.
├── riscv.v          # CPU 顶层,连接流水线各阶段、寄存器堆、停顿控制和分支预测
├── if.v             # PC/取指地址控制
├── if_id.v          # IF/ID 流水线寄存器,支持 stall 和 flush
├── id.v             # 指令译码、立即数生成、分支判断、冒险检测、flush 生成
├── id_ex.v          # ID/EX 流水线寄存器
├── ex.v             # ALU、访存地址计算、EX 阶段前递、RV32M 运算
├── ex_mem.v         # EX/MEM 流水线寄存器
├── mem.v            # 访存阶段,生成数据存储器控制信号
├── mem_wb.v         # MEM/WB 流水线寄存器
├── wb.v             # 独立写回模块,当前主顶层未直接实例化
├── register.v       # 32 个通用寄存器,x0 恒为 0,带 WB 旁路
├── stall.v          # 全局流水线停顿控制
├── bp.v             # 分支预测器 BRANCH_PRE
├── inst_mem.v       # 指令存储器,使用 $readmemh 加载机器码
├── data_mem.v       # 仿真数据存储器,支持内存映射输出
├── riscv_soc_tb.v   # 仿真 testbench
├── fpga_top.v       # FPGA 顶层,连接 CPU、指令存储器、LED 和数码管
├── seg_driver.v     # 8 位七段数码管十进制扫描显示驱动
├── fpga_pin.xdc     # FPGA 管脚约束
├── test_all.txt     # 当前 inst_mem.v 默认加载的综合指令测试机器码
├── test_all_mem.txt # test_all.txt 的带注释说明版本
├── Fibonacci.txt    # Fibonacci 板级演示程序说明
├── sum.txt          # 平方和示例机器码
├── sum_mem.txt      # sum.txt 的带注释说明版本
├── machinecode.txt  # Fibonacci 程序机器码
├── data_mem.txt     # 示例数据内存初始化内容,当前 data_mem.v 未直接加载
└── Pipeline/         # 部分模块的历史/参考版本

CPU 架构

流水线主路径如下:

PC/IF -> IF/ID -> ID -> ID/EX -> EX -> EX/MEM -> MEM -> MEM/WB -> Registers

核心顶层 riscv.v 对外暴露两组存储器接口:

  • 指令存储器接口:inst_addr_o、inst_i、inst_ce_o
  • 数据存储器接口:data_ce_o、data_we_o、data_addr_o、data_i、data_o

riscv_soc_tb.v 将 CPU、inst_mem.v 和 data_mem.v 组合成可仿真的 SoC。fpga_top.v 则将 CPU 接到 FPGA 外设,当前板级顶层只实例化了指令存储器,没有实例化通用数据存储器;当 CPU 对 0x10000000 执行 sw 时,写入值会被锁存到 LED 和数码管显示逻辑中。

支持的指令

当前译码逻辑在 id.v 中通过 casex 匹配指令,执行逻辑在 ex.v 中按 ALUop 完成运算。

RV32I 子集

类型 指令
U-type lui, auipc
J-type jal
I-type 跳转 jalr
B-type 分支 beq, bne, blt, bge, bltu, bgeu
Load/Store lw, sw
I-type 算术/逻辑 addi, slti, sltiu, xori, ori, andi, slli, srli, srai
R-type 算术/逻辑 add, sub, sll, slt, sltu, xor, srl, sra, or, and

RV32M 扩展

支持:

  • mul
  • mulh
  • div
  • divu
  • rem
  • remu

ex.v 中的 RV32M 运算当前是组合/单周期风格实现,StallReq_o 固定为 0。综合到 FPGA 时,乘除法资源和时序压力会比普通 ALU 指令更高。

暂不支持或未实现的内容

当前代码没有实现以下 RISC-V 功能:

  • lb/lh/lbu/lhu/sb/sh 等非字宽访存。
  • CSR、异常、中断、特权级。
  • 压缩指令 RV32C。
  • 原子指令 RV32A。
  • 完整总线协议,如 AXI、Wishbone 等。

冒险处理

项目中与冒险相关的主要模块和逻辑如下:

  • register.v:寄存器堆在 WB 同周期读写同一寄存器时,读口直接返回写入值。
  • ex.v:EX 阶段支持从 MEM/WB 结果向当前执行指令前递。
  • id.v:当 ID 阶段源寄存器依赖 EX 或 MEM 阶段待写寄存器时,产生 StallReqLoad。
  • stall.v:根据 load/use、分支或 EX 阶段请求生成 6 位 stall 控制信号。
  • if_id.v:在 flush 时向 ID 阶段注入 NOP。
  • PC 模块:Flush 优先级高于预测跳转和顺序取指。

分支预测与恢复

bp.v 中的 BRANCH_PRE 提供分支预测:

  • 使用全局历史寄存器 GlobalRec。
  • 使用全局预测表 GlobalPre。
  • 使用局部地址记录/预测表 LocalAddrRec、LocalPre。
  • 使用 AllPre 在局部/全局预测之间选择。

id.v 在分支实际结果与预测结果不一致时产生 Flush 和 CorrectPC,PC 模块据此恢复正确取指地址。jal 和 jalr 当前也会触发 flush,并将 PC 修正到跳转目标。

内存与地址约定

指令存储器

inst_mem.v 定义了 1024 个 32 位字的指令内存,总容量 4 KiB。CPU 输出的是字节地址,指令存储器使用 addr[31:2] 转为字地址。

当前代码中机器码加载路径是硬编码绝对路径:

$readmemh("E:/RISC-V/Pipeline/test_all.txt", inst_memory);

如果在本项目目录直接运行仿真,建议把它改成当前目录下的相对路径:

$readmemh("test_all.txt", inst_memory);

也可以按需要替换为:

  • sum.txt
  • machinecode.txt
  • 其他自行生成的 32 位十六进制机器码文件

数据存储器

data_mem.v 定义了 1025 字节的数据存储器:

  • lw:从 addr 起按小端序读取 4 字节。
  • sw:向 addr 起按小端序写入 4 字节。
  • verify:输出 data[15:12] 组成的 32 位值,便于仿真观察。

特殊地址:

0x10000000

在仿真数据存储器中,写入该地址不会写 RAM,而是通过 $display 打印:

Fibonacci Output: <decimal> (0x<hex>)

在 fpga_top.v 中,写入该地址会更新 LED 和数码管显示数据。

仿真运行

推荐先确认 inst_mem.v 中的 $readmemh 路径能被仿真器访问。若使用当前目录中的测试程序,请将路径改为相对路径。

使用 Icarus Verilog

如果安装了 Icarus Verilog,可以在项目根目录运行:

iverilog -g2012 -o riscv_soc_tb.vvp `
  riscv_soc_tb.v riscv.v if.v if_id.v id.v id_ex.v ex.v ex_mem.v mem.v mem_wb.v `
  register.v stall.v bp.v inst_mem.v data_mem.v
vvp riscv_soc_tb.vvp

仿真中可以观察:

  • 寄存器写回打印:register.v 中的 $display("x%d = %h", ...)
  • Fibonacci 内存映射输出:data_mem.v 中写 0x10000000 时的 $display
  • riscv_soc_tb.v 中的内部信号,例如 inst_addr、inst、data_addr、wdata、rdata、verify

使用 Vivado 仿真

  1. 新建 Vivado 工程。
  2. 添加根目录下所有 Verilog 源码。
  3. 将 riscv_soc_tb.v 设置为仿真顶层。
  4. 确认 inst_mem.v 中 $readmemh 指向存在的机器码文件。
  5. 运行 Behavioral Simulation。

FPGA 上板

上板相关文件:

  • 顶层模块:fpga_top.v
  • 约束文件:fpga_pin.xdc
  • 显示驱动:seg_driver.v
  • CPU 和流水线相关全部根目录 Verilog 文件

fpga_top.v 的顶层端口:

端口 方向 说明
sys_clk input FPGA 系统时钟,约束到 P17
sys_rst input 复位按键/输入,约束到 P15,当前顶层未接入 CPU 复位
sw_polarity input 拨码输入,已约束但当前逻辑未使用
led[7:0] output LED 输出
seg_sel[7:0] output 数码管位选
seg_led_0[7:0] output 第一组段选
seg_led_1[7:0] output 第二组段选

板级逻辑说明:

  • sys_clk 通过简单计数器分频生成 cpu_clk。
  • CPU 复位在当前代码中固定接 1'b0。
  • 当 CPU 写 0x10000000 时,wdata[6:0] 显示到 led[6:0],完整 wdata 显示到数码管。
  • led[7] 每个 CPU 时钟翻转一次,可作为运行心跳。
  • TEST_MODE 设为 1 时,数码管固定显示 12345678;默认 0 时显示 CPU 写出的数据。

上板建议:

  1. 将 inst_mem.v 中 $readmemh 指向上板要运行的机器码。
  2. 若使用 Fibonacci 演示,可加载 machinecode.txt,并参考 Fibonacci.txt。
  3. 在 Vivado 中将 fpga_top 设置为综合/实现顶层。
  4. 添加 fpga_pin.xdc。
  5. 综合、实现、生成 bitstream 并下载。

测试程序说明

test_all.txt / test_all_mem.txt

当前 inst_mem.v 默认加载的综合测试程序。覆盖内容包括:

  • 立即数与逻辑运算:addi, andi, ori
  • 比较:slt, sltu
  • 移位:sll, srl, sra
  • 乘除余:div, rem
  • 条件分支:beq, bne, bge
  • PC 相对与间接跳转:auipc, jalr
  • 程序末尾使用 jal x0, 0 自循环结束

test_all_mem.txt 是对应的带地址和注释版本,便于核对预期结果。

sum.txt / sum_mem.txt

平方和与访存示例程序,包含循环、mul、sw、lw、blt 等操作。sum_mem.txt 是带注释版本。

machinecode.txt / Fibonacci.txt

Fibonacci 演示程序。程序会通过 lui x3, 0x10000 构造 0x10000000,再把 Fibonacci 数写到该地址:

sw x2, 0(x3)

在仿真中会打印 Fibonacci 输出;在 FPGA 顶层中会更新 LED 和数码管显示。

常见注意事项

  1. inst_mem.v 当前使用绝对路径 E:/RISC-V/Pipeline/test_all.txt。如果路径不存在,仿真器会无法加载指令或指令内存保持为 0。
  2. data_mem.txt 当前没有被 data_mem.v 自动 $readmemh 加载,如需初始化数据内存,需要自行添加读取逻辑。
  3. fpga_top.v 当前没有实例化 data_mem.v,只处理写 0x10000000 的外设显示场景。
  4. sys_rst 在 fpga_top.v 中已经声明和约束,但当前 CPU 复位固定为 1'b0,如需上板复位功能,需要把 sys_rst 接入 CPU 和相关模块。
  5. RV32M 乘除余使用组合逻辑实现,上板时建议关注综合资源、时序报告和最大频率。
  6. wb.v 在当前 riscv.v 主顶层中未实例化,写回直接由 MEM_WB 输出驱动寄存器堆。
  7. Pipeline/ 子目录存在部分同名模块,加入 Vivado 工程时不要同时添加根目录和子目录里的同名模块,否则可能出现模块重复定义或版本混用。

开发建议

  • 新增指令时,通常需要同步修改 id.v 的译码、ex.v 的执行逻辑,必要时还要修改 mem.v 和冒险处理逻辑。
  • 修改测试程序后,保持机器码文件一行一条 32 位十六进制指令。
  • 对分支、跳转、load/use、前递和乘除法指令分别建立小测试程序,便于定位流水线问题。
  • 上板前先通过仿真确认寄存器写回、访存地址和分支目标符合预期。

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages