时间:2026-09-08 来源:FPGA_UCY 关于我们 0
![]()
你有没有想过一个问题: 为什么我们学了这么多年计算机组成原理,却很少有人真正动手设计过一个CPU?
答案很简单——以前太难了。
要设计一个CPU,你得自己定义指令集、自己写汇编器、自己搞定编译器……光是搭建一套能跑起来的工具链,就能劝退99%的人。
但RISC-V的出现,把这件事的门槛 从“几乎不可能”降到了“咬牙能搞定” 。
今天这篇文章,我就带你走一遍这条路: 从零开始,在FPGA上实现一个属于自己的RISC-V软核 。不堆砌概念,只说实操。
![]()
一、为什么是“RISC-V + FPGA”?
先回答一个最基本的问题: 为什么非要用FPGA做RISC-V?
原因很简单—— FPGA让你可以“物理上”实现一个CPU ,而不是在仿真器里跑一跑就结束了。
用FPGA做RISC-V软核,本质上是在做三件事:
用Verilog/VHDL描述一个CPU ——这是“设计”
把设计烧录到FPGA里 ——这是“实现”
在真实的硬件上跑程序 ——这是“验证”
当你的RISC-V软核在FPGA上跑起来、串口打印出“Hello World”的那一刻,你对计算机体系结构的理解,会比看一百遍教科书都深刻。
更重要的是, RISC-V是开源的 。你不需要支付任何授权费,就可以自由地设计、修改、分发你的处理器核心。这种FPGA加RISC-V处理器软核的方式,可以快速针对不同需求实现嵌入式平台的客制化。
二、准备工作:你需要什么?
硬件
一块FPGA开发板 :推荐小脚丫STEP CYC10、Xilinx Artix-7系列或Altera Cyclone系列。入门级即可,不必追求高端。
下载器 :用于烧录程序。
软件
FPGA开发工具 :Vivado(Xilinx)或Quartus(Altera)
仿真工具 :ModelSim或Vivado自带的仿真器
RISC-V工具链 :GCC编译器和汇编器
国内已有高校将这类实验纳入课程体系,以FPGA为主要工具,引导学生完整地设计、实现并优化一个RISC-V处理器核心。这说明这条路已经被验证是可行的。
三、第一步:从RV32I开始
RISC-V指令集有很多版本——RV32I、RV32E、RV64I……对于初学者, 从RV32I开始是最明智的选择 。
RV32I是RISC-V的 基础整数指令集 ,包含40多条指令。别看只有40多条,它们已经足以实现一个能运行C程序的完整CPU。
你需要实现的指令类别
算术运算 :ADD、SUB、ADDI等
逻辑运算 :AND、OR、XOR等
移位运算 :SLL、SRL等
分支跳转 :BEQ、BNE、JAL等
访存指令 :LW、SW、LB等
环境调用 :ECALL、EBREAK
关键建议 :不要一开始就想搞个大新闻。先实现 单周期CPU ——每条指令在一个时钟周期内完成。虽然性能不高,但逻辑清晰、容易调试。等你跑通了单周期版本,再考虑流水线。
四、第二步:设计数据通路
CPU的核心是 数据通路(Datapath) 。简单说,就是数据在CPU内部怎么流动。
一个基本的RV32I单周期CPU,需要这些模块:
1. PC(程序计数器)
指向当前执行的指令地址。每个周期要么+4(顺序执行),要么跳转到目标地址。
2. 指令存储器(IMEM)
存放程序指令。用FPGA的BRAM(块RAM)来实现。
3. 寄存器堆(Register File)
32个通用寄存器,每个32位。这是CPU的“临时变量区”。
4. ALU(算术逻辑单元)
执行加减、与或、比较等运算。
5. 数据存储器(DMEM)
存放程序数据。同样用BRAM实现。
6. 控制单元(Control Unit)
根据指令的opcode,生成所有控制信号——选择哪个数据源、ALU做什么运算、是否写回寄存器……
一个重要的设计原则 :每个模块都独立设计、独立仿真。不要把所有代码写在一个文件里—— 模块化是硬件设计的基本功 。
五、第三步:指令译码是核心
指令译码是整个CPU设计中最关键的部分。
RISC-V的指令格式非常规整,主要有四种:
R型 :寄存器-寄存器操作(如ADD)
I型 :立即数操作(如ADDI)
S型 :存储指令(如SW)
B型 :分支指令(如BEQ)
每种格式的 opcode、funct3、funct7 字段位置都是固定的。这意味着你的译码逻辑可以非常简洁。
实战建议 :用 case 语句根据opcode分发到不同的指令处理逻辑。先把所有指令的译码框架搭好,再逐个填充具体实现。
六、第四步:仿真——你的“显微镜”
很多初学者一上来就想把代码烧到FPGA上跑。 这是个巨大的坑。
在FPGA上调试硬件,就像蒙着眼睛修电路板——你根本不知道哪里出了问题。
正确的做法是: 先在仿真环境里跑通 。
用ModelSim或Vivado仿真器,写一个简单的testbench:
初始化PC=0
往指令存储器里放几条测试指令(比如:ADDI x1, x0, 5 → ADD x2, x1, x1 → SW x2, 0(x0))
跑仿真,观察每个模块的信号变化
确认每一步的结果都符合预期
只有仿真通过了,才考虑烧录到FPGA 。
七、第五步:上板运行
仿真通过后,就可以把设计烧录到FPGA开发板上了。
这里有几个坑需要提前知道:
坑1:时钟
FPGA上的时钟和仿真里的理想时钟不一样。注意 时钟约束 和 时序收敛 。
坑2:复位
确保CPU上电后能正确复位到初始状态。
坑3:串口输出
想让你的RISC-V处理器“说话”,需要实现一个 UART模块 ,把要打印的字符通过串口发送到电脑终端。
当你的处理器第一次在串口终端打印出字符时,那种成就感—— 值得熬三个通宵 。
八、开源项目:站在巨人的肩膀上
如果你觉得完全从零开始太艰难,完全可以利用开源资源。
目前比较成熟的RISC-V开源软核项目包括:
VexRiscv :用SpinalHDL编写,已在多款FPGA上成功运行Linux
蜂鸟E203 :国内开发者熟悉的开源RISC-V核
香山(XiangShan) :中科院计算所的高性能开源RISC-V处理器项目
建议的学习路径 :
先读这些开源项目的代码,理解别人的设计思路
然后尝试自己重写一个简化版本
最后在理解的基础上加入自己的创新
有一本书叫《基于FPGA与RISC-V的嵌入式系统设计》,详细介绍了RISC-V指令集及其设计思想,并引入了一种称为FARM的软硬件开发模式,将FPGA同RISC-V CPU软核相结合。这本书可以作为你的案头参考。
九、从单周期到流水线
当你跑通了单周期CPU,就可以考虑升级到 五级流水线 了。
五级流水线将指令执行分为五个阶段:
IF(取指) :从指令存储器读取指令
ID(译码) :译码并读取寄存器
EX(执行) :ALU运算
MEM(访存) :访问数据存储器
WB(写回) :将结果写回寄存器
流水线的难点在于 冒险(Hazard)处理 :
数据冒险 :后一条指令依赖前一条指令的结果
控制冒险 :分支跳转导致PC不确定
解决这些问题,才是真正理解CPU设计精髓的开始。
十、给新手的三个建议
1. 不要追求一步到位
先实现RV32I的一个子集(比如只实现算术和分支指令),跑通了再逐步添加。 能跑的简单CPU,远好于不能跑的复杂CPU 。
2. 仿真比上板重要十倍
在仿真环境里把每个模块、每条指令都验证一遍。 仿真不通过,绝不上板 。
3. 工具链要提前搞定
RISC-V的GCC工具链需要提前配置好。否则你写好了CPU,却没法编译测试程序——那就尴尬了。
写在最后
三年前,我在一个技术论坛上看到有人分享自己用FPGA实现RISC-V软核的经历。当时我觉得这离自己很远——那是“大神”才做的事。
后来我硬着头皮开始做,踩了无数坑,熬了无数夜。但当我在串口终端看到自己设计的CPU打印出第一行字符时,那种感觉—— 就像亲手点亮了一盏灯 。
RISC-V的开放生态,加上FPGA的可编程特性,让每个硬件工程师都有机会从头设计一个属于自己的CPU。这件事的门槛,从来没有像今天这么低过。
如果你也在犹豫要不要开始,我的建议只有一句:
别想太多,动手就对了。
下一篇:fpga是什么