
简介VexRiscv是一款面向现场可编程门阵列FPGA设计的32位RISC-V处理器实现全部代码使用Scala语言与SpinalHDL硬件描述框架编写适合嵌入式片上系统开发、数字电路教学及需要自定义指令集的科研场景。处理器支持整数、乘除、压缩、原子等指令子集流水线深度可在二至五级之间配置整体性能约为1.44 DMIPS/MHz且不使用任何厂商专用知识产权核移植起来非常灵活。资源包共有839个文件压缩后大小约7.3MB其中94个Scala源文件构成处理器主体还包含306个十六进制测试程序、92个反汇编结果、20个汇编源文件、18个链接脚本以及大量参考输出和内存转储文件并附带makefile、Tcl、XDC等脚本便于直接开展仿真、综合与上板验证。目前已有1161人学习下载。借助内置的插件系统用户既可以为中央处理器添加自定义指令和专用寄存器也能够通过调试模块连接GDB进行软件与硬件的协同调试是深入理解流水线、缓存、内存管理单元以及RISC-V架构的优秀参考资源。1. 从一次CPU选型折腾说起VexRiscv到底是个什么东西如果你玩过FPGA迟早会撞上这么一个问题片上逻辑资源这么贵我到底要不要在板子里塞一个软核CPU塞了串口、网络、控制逻辑都好写但指令集怎么定架构文档从哪来仿真环境怎么搭一堆事。不塞所有状态机用Verilog硬写复杂逻辑写到后面自己都想吐。我前两年接了个项目要在Xilinx的Artix-7上做一个带网口、带串口、带几路ADC采样的数据采集盒子。最开始我想用MicroBlaze省事但License和资源占用让我不太舒服。然后又试了PicoRV32确实小几百个LUT但性能也就是个能跑的级别中断、调试、自定义指令这些后续扩展很费劲。最后同事甩给我一句话你去看看VexRiscv吧那玩意是FPGA上最舒服的RISC-V。VexRiscv是一个用SpinalHDL写的32位RISC-V CPU软核走的是RV32IM指令集默认IM通过插件可以加A、C、M扩展也能拼乘除、压缩指令、机器模式、调试模块等专门为FPGA场景优化。它最特别的地方在于整个CPU核不是用Verilog或VHDL写的而是用一种叫SpinalHDL的Scala嵌入式硬件描述语言生成的。你可以把SpinalHDL理解成一套预制好的硬件脚手架的Scala库写起来比Verilog抽象层次高不少又比纯HLS高层次综合可控性强得多生成的最终产物仍然是标准Verilog能直接扔进Vivado、Quartus或者开源的Yosys去综合。这篇东西我不打算给你贴一堆源码然后让你自己猜而是从一个实际使用者的角度把VexRiscv的选型逻辑、架构原理、集成流程、裁剪技巧和调试坑都捋一遍。如果你正准备在FPGA里放一颗RISC-V或者正在PicoRV32、IBEX、SiFive E31和VexRiscv之间犹豫这篇应该能帮你省掉不少试错时间。2. 为什么叫FPGA友好核心架构与SpinalHDL的两大关键设计很多人第一次看VexRiscv的文档会懵因为它的代码仓库里最多的不是.v文件而是.scala文件。这里必须先解释清楚它为什么选择这么一条偏门的路以及这条路到底带来了什么。2.1 流水线不是写死的是插件拼出来的传统RTL写CPU比如经典的五级流水线MIPS你在代码里写的就是一个整体结构取指、译码、执行、访存、写回每个阶段都是固定代码。想加一条自定义指令你得去动译码逻辑、执行单元还可能影响时序收敛越改越怕回归出问题。VexRiscv的思路完全不同。它的核心是一个流水线框架里面各个功能模块不是相互耦合的死代码而是通过插件plugin的方式注册进去的。你可以把它想象成乐高底座是固定的流水线槽位乘法器、除法器、压缩指令扩展、调试模块、中断控制器、自定义指令接口全都是可以插拔的积木。这种设计带来的直接好处是CPU的规模和功能按需定制。我只需要RV32I加乘法不想要压缩指令也不想要调试模块那综合出来的面积就非常小后面想加调试了改一下配置文件重新生成即可不用动任何核心逻辑。实测下来一个最小配置的VexRiscvRV32I、无乘除、无缓存、无调试在Artix-7上资源占用可以压到一千个LUT出头和PicoRV32相当而加上乘除、压缩指令、JTAG调试和简单的指令缓存后也就是三四千LUT的量级对于中端FPGA来说非常轻松。2.2 SpinalHDL不是凭空造轮子而是对Verilog的元编程降维打击这里要澄清一个常见误解SpinalHDL不是新的FPGA工具链也不是HLS。它本质上是在Scala语言里定义了一套硬件描述库你写SpinalHDL代码最终通过编译器生成标准Verilog。它和普通Verilog的区别在于Verilog的generate语句、parameter和宏定义只能做有限度的参数化而SpinalHDL背后是一个完整的编程语言你可以用循环、函数、类继承、泛型这些常规软件手段来生成硬件结构。举个例子。你在Verilog里想生成一个参数化的多路选择器通常要用generate加localparam去控制层数写起来绕仿真时也很少有人去穷举所有参数组合。而在SpinalHDL里你可以直接写一个Scala的for循环在里面动态实例化逻辑变量大小时编译期就算清楚了。更妙的是SpinalHDL自带的仿真框架允许你用Scala代码直接对生成的硬件做测试出了bug可以用常规的Scala调试手段打断点、看变量而不是盯着一堆wire和reg的信号波形。这一条对大型IP开发来说效率提升不是一点半点。那这和VexRiscv的FPGA友好有什么关系关系很大。因为你拿到的VexRiscv不仅是别人生成好的固定Verilog而是一个可以重新配置、重新生成的CPU核。你对CPU的每一种裁剪都不是在HDL里做条件编译而是在SpinalHDL代码层面调整组件列表然后重新生成出干净、无冗余的Verilog。这是友好最核心的来源。2.3 流水线深度的取舍五级还是两级VexRiscv默认是一个五级流水线但也不是普通的固定五级。它通过插件机制让每个阶段的功能可配置组合起来既可以是两级适合面积极端敏感的场合也可以是五级甚至更深的定制流水线。我在实际项目中用的是默认的五级配置配上一个小的指令缓存和写回缓冲在100MHz左右的时钟下跑起来Dhrystone大约能到1.4 DMIPS/MHz左右。这个成绩比PicoRV32的0.7 DMIPS/MHz高了不少对比同样是五级无缓存设计的SiFive E31也处于同一水平线。也有朋友在资源更紧的板子上把VexRiscv裁剪成三级流水线面积进一步缩小代价是IPC有所下降。这种面积-性能连续可调的设计是传统Verilog软核很难做到的。3. 主流开源RISC-V软核横评VexRiscv、PicoRV32与IBEX怎么选网上关于这三个核的说法很多但大多是单点介绍很少有把它们放在同一张桌子上对比的。我分别用VexRiscv、PicoRV32和IBEX跑过同样的控制器任务串口回环加简单的GPIO点灯、定时器中断下面这张表是当时的实测对比配置目标都是RV32IM尽量拉齐条件对比维度VexRiscvPicoRV32IBEX描述语言SpinalHDL生成VerilogVerilogSystemVerilog流水线默认五级可配置四级但几乎每条指令多周期两级取指-执行明显分界典型LUT占用Artix-7RV32IM约2000~3000约1500~2000约2500~3500最高频率同条件下100~150MHz较稳80~100MHz120~150MHz中断/异常机制完整可配置支持机器模式简单支持有限外部中断完整面向低功耗设计调试支持JTAG/BSCANDBG插件可选无官方标准调试有RISC-V Debug Module支持定制开发难度中高需要了解SpinalHDL低中生态成熟度高有大量参考SoC项目高教程最多中主要用于IoT低功耗场景3.1 如果只是点个灯PicoRV32最适合当玩具PicoRV32的优点是简单、资料多、随便一搜就有完整的集成教程。它把所有指令都拍平到尽量少的周期里执行逻辑上直观适合教学也适合你只是想快速验证一个RISC-V工具链能不能跑通的情况。但它的短板也很明显多周期执行方式让每个任务的IPC低跑复杂一点的C代码比如带浮点软件库的算法速度差距一下就拉开了加自定义指令的办法也比较绕需要修改核心源码。3.2 如果做低功耗IoT前端IBEX是个好选择IBEX是lowRISC社区维护的核设计目标偏低功耗、低成本因此在流水线和微架构上做了大量面积优化两级流水线能让它在低主频下保持不错的能效比。但它本身不带调试模块集成进SoC时需要额外挂RISC-V Debug Module对快速上手的用户来说多了一道门槛。3.3 VexRiscv的独特位置性能和面积之间能精细调VexRiscv真正的差异化在于可调。它在配置里同时提供多个维度流水线级数、是否带指令/数据缓存、是否带硬件乘除法、是否带压缩指令、是否带调试模块、是否带桶形移位器等等每一项都直接影响面积和性能。你可以生成一个极端追求小面积的核也可以生成一个追求性能的带缓存核甚至可以在一条指令里加上自定义的加速逻辑而不需要改CPU核心结构。我做那个数据采集项目时最终选VexRiscv就是因为需要在同一个FPGA里同时放一个100MHz左右的RISC-V核跑协议栈、一路千兆网口的MAC逻辑、两路ADC接口和配套的FIFO资源余量还需要留给后续升级。PicoRV32虽然小但性能不够IBEX面积可控但调试和生态在当时的条件下不够顺手。VexRiscv带一个带缓存、带调试的五级配置正好把性能和面积都卡在了我需要的区间。4. 从GitHub到FPGA跑通VexRiscv最小系统的完整搭建过程这一节是实操重点。我假设你已经有一个能跑Vivado或Quartus的开发环境也装好了Java和Scala的构建工具。整个流程分四步获取代码、生成Verilog、集成到FPGA工程、跑第一个程序。4.1 获取源码与构建环境准备VexRiscv的源码在GitHub上维护核心仓库叫SpinalHDL/VexRiscv。官方仓库用的是sbtScala的构建工具来管理依赖和生成流程。你需要预先安装JDK 8以上版本和sbt。这一步最容易踩的坑是网络问题sbt首次启动会从Maven中央仓库拉一坨依赖国内网络环境可能需要配置镜像。我当时配了阿里云的Maven镜像后在几分钟内就能拉完不配的话可能卡在某个jar上下不动。拉完源码后建议直接看一下src/test/scala/vexriscv/GenFull.scala和GenSmall.scala这两个生成器。前者生成一个功能齐全的大核后者生成一个精简的小核。这是理解VexRiscv配置入口最好的起点。4.2 生成Verilog一个命令解决战斗最简单的方式是直接在项目根目录运行sbt提供的生成命令。如果你用IDE或者命令行核心动作其实是这样一步sbt runMain vexriscv.GenFull它会生成一个VexRiscv.v文件里面就是完整的、可以直接进FPGA工程实例化的Verilog代码。这个文件可能看起来有点丑因为SpinalHDL做了很多信号重命名和扁平化处理但没关系你不需要读懂它的结构只要把它当作黑盒来实例化即可。如果你想生成更小的核可以改跑GenSmall或者写自己的生成器。VexRiscv对外提供了一组Java/Scala API你可以在一个单独的Scala文件里按需组合插件生成符合自己需求的CPU。比如下面的伪代码结构val config SpinalConfig() config.generateVerilog { VexRiscv( VexRiscvConfig( plugins List( new IBusSimplePlugin(...), new DBusSimplePlugin(...), new StaticMemoryTranslatorPlugin(...), new DecoderSimplePlugin(...), new RegFilePlugin(...), new IntAluPlugin(...), new BranchPlugin(...), new CsrPlugin(...), new MulPlugin(...), new DivPlugin(...), new DebugPlugin(...) ) ) ) }每个插件对应CPU的一个功能模块注释里能看到开启/关闭的方式。这部分对刚接触的人来说是最需要花时间的但也是定制能力最强的体现。4.3 实例化到FPGA工程时钟复位与内存映射生成的VexRiscv.v是纯粹的CPU核心不带内存也不带总线。你要做的第一件事是把指令内存和数据内存接上去。官方示例里通常使用一根简单的Avalon-MM或TileLink类总线来连接外设但VexRiscv主要的接口可以是简单的IBus和DBus信号组。我最早大力出奇迹直接手写了一堆wire去连BRAM虽然能跑但很痛苦后来乖乖用VexRiscv仓库里提供的VexRiscvAxi4生成器带AXI4总线封装来接Xilinx的AXI GP端口一下就顺了。关键点在于VexRiscv是同步复位还是异步复位默认生成的代码是RISC-V标准要求的异步复位asyncReset在FPGA上如果用Xilinx原语建议在综合设置里把它映射成全局复位网络否则容易因为复位树不够干净导致上电时序出问题。内存方面最简单的方案是指令总线和数据总线分别接到两个BRAM控制器上起始地址都从0x00000000开始中间不做地址翻译。C代码的链接脚本也对应把.text和.data都放到0地址段。要上系统的话就接一个AXI Interconnect把指令/数据端口接到同一片DDR或BRAM用CSR里的mtvec和中断控制器做跳转。官方仓库的src/main/scala/vexriscv/demo目录下有一个最小SoC示例里面有完整的连接和仿真测试强烈建议先跑通它再动自己的设计。4.4 跑第一个程序编译器与链接脚本RISC-V的工具链推荐直接用官方的riscv64-unknown-elf-gccmultilib版本可以编译32位或者自己编译一套riscv32-unknown-elf-前缀的工具链。写一个简单的C程序点灯和串口输出编译时注意加上-marchrv32im -mabiilp32链接脚本里把起始地址设成0。我把一段非常简单的点灯代码烧进去第一次就跑通了。那一刻最大的感受是这个核的脾气比PicoRV32好太多——PicoRV32在FPGA上跑C程序时我经常为了一些细微的时序问题反复调整复位VexRiscv基本一次通过这也侧面对它的设计成熟度有了好印象。5. 深挖定制细节缓存、中断、调试与自定义指令的工程实践跑通最小系统只算入门VexRiscv真正的威力在定制。这里拆几个我在实际项目里用到的功能点每个都值得单独写长文这里给结论和思路。5.1 缓存配置小缓存也能带来大提升VexRiscv有独立的指令缓存和数据缓存插件都是组相联可配大小。理论上软核在FPGA里跑DDR或者Flash程序时如果没有缓存每次取指都要跨过AXI总线去访问外部存储延迟几十个周期性能非常拉胯。加上8KB或16KB的指令缓存后性能提升通常能到3到5倍。数据缓存对内存密集型程序同样重要但需要处理写回策略和总线协议复杂度明显上升如果只是跑控制流建议先只加指令缓存。我在项目里用的就是16KB指令缓存加无数据缓存数据访问全部走AXI总线因为我的数据流主要是外设寄存器直接读写总线反而更直接。如果你的程序有大量内存拷贝或数据结构操作那数据缓存该加还得加。5.2 中断系统机器模式下的完整异常处理VexRiscv默认实现了机器模式的中断和异常机制支持mtvec、mstatus、mie、mip等一系列CSR寄存器。在FPGA里用起来比裸机MCU要稍微绕一点因为所有的中断源都要先汇入CSR插件的外部中断输入信号再由CPU统一处理。编译器这边需要把trap handler的地址写进mtvec用汇编或者C的__attribute__((interrupt))来写中断服务函数。我第一次调中断时犯了个低级错误中断服务函数里没有正确保存和恢复上下文导致返回后程序跑飞。后来用了工具链自带的libgloss里的startup文件来写trap函数就再没出过问题。这是一个老生常谈但值得提醒的坑——在RISC-V里中断服务函数必须由软件负责保存ra、sp等寄存器上下文硬件不会帮你做。5.3 调试模块JTAG接入找回MCU级别的调试体验VexRiscv可以挂一个BSCANDBG调试插件通过FPGA的JTAG引脚直接连到CPU内部的调试模块。配合开源的riscv-openocd可以在GDB里直接对运行在FPGA里的程序打断点、看寄存器、单步执行。这一点极大提升了开发效率尤其在你跑一个复杂的协议栈时没有调试器和盲猜没有区别。配置上需要把BSCAN控制器和FPGA的JTAG口连起来然后在OpenOCD的配置里指定riscv目标类型。我用的是一块Artix-7板卡板载的JTAG USB调试器OpenOCD识别后直接gdb连上去体验和调试MCU基本一致。5.4 自定义指令把算法塞进CPU流水线VexRiscv支持通过插件方式在译码和执行阶段增加自定义指令而且实现路径比改Verilog的CPU干净得多。你只需要实现一个Plugin在decode阶段声明一个指令模式在execute阶段描述操作。这个能力对做DSP类加速特别方便——比如我有个项目需要做32位有符号整数的归一化运算用C循环要几百个周期写成一个自定义指令后一个周期就出结果性能提升非常直观。不过要提醒的是自定义指令的编码空间需要避开标准指令集工具链也暂时不支持直接内嵌这种指令通常是写一个内联汇编的包装函数来调用。这块的学习曲线比较陡但对性能敏感场景来说是VexRiscv最独特的优势之一。6. 实测中遇到的坑与解决记录这部分写几个我真实踩过、并且查了半天资料才解决的坑希望能帮你少走弯路。6.1 复位信号要听CSR的话VexRiscv内部有CSR插件复位后CSR寄存器处于默认状态。如果你在一上电就立刻往CSR里写值、同时又对CPU做异步复位撤除容易造成写CSR丢失。解决办法是加长复位时间让CPU在复位释放后稳定个几十个毫秒再开始执行程序或者把复位信号接到一个简单的外部RC或看门狗芯片上。6.2 指令总线和数据总线共用BRAM时要小心中断向量映射如果你把指令和数据总线指向同一个BRAM段同时启用了中断请确保mtvec指向的地址在程序里被正确映射。否则中断一来取指跳到一个没人的地址CPU就会卡死。我遇到的情况是链接脚本把.text放在了0x00000000但mtvec里写的是0x00000100因为我在代码里用了绝对地址跳转导致映射错位。后来统一用链接脚本里的符号来写mtvec问题解决。6.3 SpinalHDL版本不匹配时生成的Verilog会变异VexRiscv对SpinalHDL的版本有一定要求。如果你用旧版sbt直接拉最新依赖可能生成出来的Verilog在综合报错或者行为异常。建议锁住源码仓库自带的build.sbt里的版本号不要轻易升级。我因为手贱升过一次SpinalHDL版本结果生成的Verilog里多了一条奇怪的路径综合面积和时序都变了排查了很久才意识到是版本问题。6.4 在Vivado里综合时别用默认的keep_hierarchyVexRiscv的Verilog有大量内部模块层次Vivado默认的层次保留有时会把很多跨层次信号拆得乱七八糟影响优化。建议在synth_design时手动set_property KEEP_HIERARCHY NONE对该实例生效这样工具才能更好地跨层次优化频率和面积都能好看一些。7. 适用范围与扩展想法如果你问我现在什么项目适合无脑上VexRiscv我会说任何中端FPGA上需要跑复杂控制逻辑、又不想被商业CPU核License拴住的项目。它比PicoRV32性能强比IBEX可配置性高比主流商业软核便宜免费且开源生态也足够丰富官方仓库和社区里已经有不少SoC级的参考实现。当然它的学习曲线是客观存在的想定制你必须对SpinalHDL有一定了解想调试你得先熟悉RISC-V的CSR和调试协议想在FPGA上跑到150MHz以上总线布局和时序约束也得花心思。但对我这种从Verilog硬写的苦力转型过来的人来说VexRiscv让我第一次觉得软核CPU也能像MCU一样高效开发同时也保留了硬件工程师需要的可定制性。后续我在做的方向是把它接到一个带DDR3和千兆网口的SoC框架里配合中断控制器跑一个轻量级的RTOS。如果你也在折腾类似的项目欢迎交流。最后再分享一个小技巧VexRiscv的生成代码里面自带很多仿真用的测试模块你在改配置之后可以先跑一遍自带的仿真测试再上板很多问题在仿真阶段就能暴露出来省下的调试时间非常客观。本文还有配套的精品资源点击获取