简介:这是一套面向FPGA工程师与数字电路学习者的Verilog HDL源码合集,资料规模可观,共355个文件,涵盖.v源码、.sv系统Verilog、.cpp辅助程序、.inc包含文件、.sh脚本及少量文档,压缩包仅3.31MB,便于快速下载与查阅。内容按功能模块分类组织,覆盖基本逻辑门、组合逻辑、时序逻辑、数据通路、通信协议(SPI/I2C/UART)、数字信号处理、存储器、微处理器与常用IP核等工程场景,也有AES-256、RS译码、 scrambler等算法实现,适合从入门到进阶的开发者按需取用。已有376人学习下载,说明其在FPGA学习者中有一定参考价值。通过对这些源码的仿真、综合与移植练习,读者可以理解Verilog语法在真实工程中的用法,掌握模块化设计思路,并为后续自主搭建FPGA项目提供可直接复用的代码基础。 搞FPGA这行最痛苦的不是不会写代码,而是很多常用模块根本没必要从零造轮子。可每次真到用的时候,翻遍整个硬盘也找不到之前调通的UART、SPI、DDR控制器、图像缩放这些工程文件,最后只能硬着头皮重新写一遍。我整理过一份数量不小的Verilog HDL源代码库,按功能分门别类归纳过,基本上覆盖了从接口协议到算法加速、从基础组件到图像处理的常见场景。这篇博文就把这套整理思路、代码分类结构、挑选代码复用时的实操要点和一些踩过的坑一次性讲清楚,适合正在入门FPGA的开发者,也适合想把手头代码管理得更系统的工程师。
1. 为什么要整理这样一份FPGA代码库
1.1 从“东一块西一块”到系统化积累
很多FPGA工程师跟我以前一样,代码存在三个地方:公司电脑、个人笔记本、移动硬盘的某个角落。每次写新项目,先翻找旧工程,找到了就复制粘贴,找不到就重新写。这种模式最大的问题是,你根本不知道自己的“资产”到底有多少,更别说哪些代码已经验证过、哪些只是半成品。
整理代码库这件事,本质上不是整理文件,而是把过去几年碎片化的工程经验重新做一次归档。举个实际例子,我原先写过很多次UART收发模块,但每次写法都不一样——有的用状态机,有的用计数器,有的带FIFO,有的直接寄存器缓存。当我把它们统一放到一个目录下,对比着看才发现,不同写法适用的场景确实不同:简单调试用计数器版本就够,高速连续传输必须上FIFO。这种“横向对比”带来的认知提升,是单独写任何一个版本都得不到的。
1.2 分门别类的整体思路与适用人群
这份代码库的整理逻辑并不复杂,就按功能域分文件夹,每个文件夹下再按模块拆分子目录。核心目的只有一个:当项目需要某个功能时,我能快速定位到可参考的代码。
适用人群其实比想象中广。刚入门FPGA的同学,不需要买一堆开发板教程,直接啃现成代码、跑仿真、看波形,比什么教程都快;做项目的工程师,可以把验证过的模块直接拿来做原型验证,省掉前期最耗时的调试阶段;甚至做算法的人,也能在这份库里找到Cordic、FIR滤波、FFT这些数学运算的硬件实现作为参考。收录的代码来源很杂,有自己写的,有从开源项目里扒的,也有从书籍光盘里提取的,但每份我都至少跑过仿真或者上板验证过。
2. 资源库的目录结构与代码分类思路
2.1 基础组件类:从计数器到FIFO
第一个大类是基础组件,这相当于FPGA开发的“地基”。计数器、分频器、边沿检测、按键消抖、数码管驱动、LED流水灯,这些看似简单的模块,其实藏着很多细节。
以分频器为例,很多人认为是“用计数器数到N就翻转”,但工程上还得考虑占空比是不是50%、分频系数是奇数还是偶数、要不要做时钟使能而不是直接生成衍生时钟。直接分频得到的时钟在时序约束里很难处理,更好的做法是生成时钟使能信号,让所有逻辑还是跑在全局时钟上。代码库里我把这类知识沉淀下来,每个模块都备注了适用场景和坑点,比如“奇数分频如何做到50%占空比”“异步复位同步释放为什么必须做”。
FIFO是另一个重点。异步FIFO几乎是跨时钟域数据交换的标准方案,但Verilog手写异步FIFO要考虑格雷码转换、空满信号生成、读写指针同步等多级问题,一不小心就出亚稳态。代码库里我保留了一个参数化程度比较高的异步FIFO,深度、位宽都可以通过参数调整,还带仿真用的读写监测任务,方便直接集成到验证环境里。
2.2 接口协议类:从UART到PCIe
这一块是代码库的重头戏,也是复用价值最高的部分。常见的低速接口,UART、SPI、I2C、CAN,高速接口,LVDS、MIPI、PCIe、Ethernet,基本都有对应代码。
低速接口里最推荐留一份完整UART代码,因为它是验证逻辑最方便的手段之一。我保留的版本包含发送FIFO、接收FIFO、波特率自动检测三个子模块,配合状态机实现帧解析,可以直接用于上位机通信或者调试日志输出。SPI接口我单独留了主机和从机两个版本,参数化时钟极性和相位,适配不同外设,比如ADC、DAC、Flash。
高速接口这部分,很多是厂家IP,比如PCIe、MIPI在Xilinx和Intel(Altera)平台都是IP核方式提供,Verilog代码本身不能跨平台直接用。但代码库里我会保留IP核的例化模板和配置说明,因为IP核的接口时序复杂,每次从零配置又麻烦又容易错。这类注释文档甚至比代码本身更宝贵。
LVDS接收这部分,我在代码库里专门保留了一个基于Xilinx原语ISERDES的接收模块。硬件上用LVDS传输图像数据,需要通过ISERDES把串行数据转成并行。这里牵扯到位对齐、训练序列、时钟域转换等问题,代码量不大,但理解起来需要时间。我把完整的约束写法、时序要求、调试步骤都写在注释里,拿来即用基本没问题。
FMC通信那个场景,项目里用STM32H743和FPGA通过FMC总线对接,核心是把STM32的FMC总线时序和FPGA内部寄存器读写逻辑对齐。FMC地址线和数据线复用,还需要根据STM32端的访问类型产生对应的读写时序,FPGA侧用状态机响应即可。代码库里我放了一份带详细注释的FMC从机接口逻辑,包括地址译码、读写时序生成、寄存器映射,拿来改改就能用。
2.3 算法加速类:滤波、变换与数学运算
FPGA做算法加速是一个深水区,但也有很多高频使用的模块值得整理。这个分类下我分了三块:数字信号处理基础运算、滤波与变换、控制算法。
数字信号处理基础运算里,最核心的是Cordic算法。这个算法能通过迭代旋转计算三角函数、双曲函数、开方等,在FPGA里实现时不需要乘法器,只用移位和加法,非常适合硬件实现。代码库里我保留了Cordic的两种模式——旋转模式和向量模式,参数化迭代次数和位宽,并给出了精度对比测试结果。实测下来,迭代16次精度能达到小数点后四位左右,基本满足工程需求。
滤波与变换这部分,FIR滤波器、IIR滤波器、FFT变换是不可回避的。FPGA做FIR滤波有几种实现方式:直接型、转置型、分布式算法DA,各有优劣。直接型结构简单,但消耗乘法器多;DA算法用查找表替代乘法器,适合系数固定的场景。代码库我保留了两种实现,并有资源占用对比数据,方便按需选择。FFT这块我建议直接用Xilinx或者Altera的IP核,自己写FFT性价比不高,但与之配套的位宽截取、缩放策略、帧同步处理这些逻辑,才是实际工程里的关键。
控制算法里,卡尔曼滤波FPGA实现是比较有代表性的一个。卡尔曼滤波涉及矩阵运算、浮点转定点、状态更新、协方差更新,硬件实现复杂度不算低。我整理了一个一维卡尔曼滤波的定点实现版本,输入噪声协方差、测量噪声协方差做成可调参数,配合仿真数据验证滤波效果。代码里详细注释了每个矩阵运算的硬件映射方式,适合做传感器数据融合或者导航算法时参考。
2.4 图像与信号处理类
图像处理在FPGA领域几乎成了一个专门方向,因为FPGA的并行架构在做像素级处理时优势非常明显。代码库里我收集整理了完整的图像采集、预处理、显示输出链路。
图像采集接口方面,最常见的是MIPI CSI-2和DVP并口。MIPI这部分我保留了Xilinx MIPI CSI-2 RX子系统的配置流程和例化模板,因为MIPI协议分层复杂,除非有特殊需求,一般不建议从零写物理层。DVP并口相对简单,用PCLK做同步时钟,HSYNC和VSYNC做行列同步,数据线逐像素传输,代码直接写寄存器控制即可。代码库里的DVP采集模块支持8位和10位数据位宽,还带坏点校正和自动白平衡接口,方便接CMOS sensor。
预处理阶段,我保留了大量常用算法:灰度转换、中值滤波、高斯滤波、Sobel边缘检测、图像二值化、形态学开闭运算,这些都能在FPGA上用流水线结构实现,处理一帧图像只需要微秒级延迟。关键问题是行缓冲的设计——图像处理需要访问当前像素的上下左右邻域,FPGA里用行缓存(Line Buffer)解决,通过多个RAM拼接实现二维窗口滑动。我用的方案是Shift RAM结构,也就是用多个移位寄存器组拼出3x3或者5x5窗口,不用反复读写DDR。这个模块可复用性极高,后续做任何图像滤波都能直接调用。上板实测结果,Sobel边缘检测在1080p分辨率下,单帧处理时间能够控制在3ms以内,实时性远好于CPU实现。
显示输出方面,我保留了HDMI、VGA、LCD并口三种输出接口代码。HDMI相关代码重点在解决像素时钟生成和TMDS编码上,我用了Xilinx的HDMI IP和配套的像素时钟PLL配置,避免了自己写TMDS编码的麻烦。VGA接口的时序相对固定,代码量小,适合入门学习。LCD并口驱动则带RGB接口,支持SPI初始化配置和背光控制逻辑。
3. 真正从资源库挑代码时的实操要点
3.1 不要迷信“能用”,先看时钟和复位
从代码库里拿代码复用,最忌讳的就是直接综合下载。Verilog代码和C语言不一样,C语言只要逻辑正确就能在任意平台上跑出相同结果,但硬件代码的可移植性受太多因素影响,芯片制造商、具体型号、开发工具版本、约束文件,改一个条件就得重新验证。
时钟方案是最先要看的地方。原始代码用PLL产生100MHz时钟,但你的板子主时钟只有50MHz,那PLL配置必须重新生成;如果原代码用的是全局时钟,而你集成后直接用了某个引脚输出的时钟,那么时序收敛基本没戏。改代码之前,先在方案层面把时钟树画清楚——系统有哪些时钟域、哪些模块在哪个时钟域下工作、跨时钟域的信号做了同步没有。
复位逻辑同样关键。很多开源代码用的异步复位,置复位信号后所有寄存器立刻清零。这里我强烈建议同步一下:外部复位信号进来后,先经过两级寄存器同步,再分别生成每个时钟域的复位信号,配合“异步复位,同步释放”的方式,防止复位释放时刻产生亚稳态。代码库里我基本每份代码都加了这样的复位同步模块,复用的时候直接带上就行。
3.2 参数化设计才是好东西
在FPGA代码复用这件事上,参数化设计带来的收益是长远的。使用parameter或者localparam定义模块的位宽、深度、计数上限,这样在例化时只用修改参数就能适配新的需求。
举一个具体例子。我代码库里的UART模块定义了几个关键参数:系统时钟频率、波特率、数据位宽、校验位。在不同项目里,系统时钟可能是50MHz也可能100MHz,波特率也可能从9600到921600,但核心代码一行都不用改,只调整参数就行。SPI模块则是把CPOL/CPHA、时钟分频系数、数据帧长度全部参数化。FIFO定义了深度和数据位宽参数,IP核用不上的场景能直接用这个替代。
设计参数化模块时有个经验:不要把所有参数都暴露出来,否则调用的人会迷失在参数堆里。只暴露必须由使用场景决定的参数,比如时钟频率、位宽、深度;内部计算用的中间值,用localparam在模块内部定义。这样保持了模块接口的简洁,又能保证复用时的灵活性。
3.3 仿真文件和测试平台是关键
代码库里的Verilog模块再好,如果没有对应的仿真文件和测试平台,复用效率立刻减半。很多下载来的代码只有纯RTL文件,没有testbench,你根本不知道激励怎么加、期望输出是什么,等于让你盲写一遍验证环境,白白浪费了复用的初衷。
我的代码库里,几乎每个模块都配套了对应的testbench。testbench不是简单给几个信号初始化然后跑几个周期,而是要有明确的功能覆盖:正常输入下的功能验证、边界条件的异常输入、跨时钟域场景下的数据完整性检查,一般都会做。UART模块的testbench会发送几帧随机数据,同时启动接收,自动对比收发内容是否一致。异步FIFO的testbench会模拟读写时钟频率不一致的情况,检查会不会丢数据或者读空。有这些仿真文件,拿到代码后能立刻跑仿真、看波形、确认功能。
跑仿真的工具我用过Vivado Simulator、ModelSim和Verilator,个人最推荐ModelSim或者Questa的免费版,轻量、启动快、波形调试方便。新版本Vivado自带的仿真器也够用,但启动速度偏慢,做小模块验证时不太爽利。
3.4 关于版权和代码来源
整理代码库,版权问题必须留意。我在代码库里存储的来源主要有三类:一是自己独立完成的代码,二是开源项目里带MIT、Apache等宽松许可证的代码,三是书籍或教程附带的代码。
开源代码的使用,核心是保留原许可证声明。如果你把复用的代码用来做商用产品,必须逐份确认许可证类型:GPL类许可证在使用不当的情况下,可能有传染风险。我用了一个简单方法处理:代码库根目录放一个README.md,里面列出每份代码的来源、许可证类型、当初从哪下载的、适用场景。这样不管自己用还是给别人参考,都能快速查证。
4. 常见问题与排查技巧实录
4.1 代码能编译但行为不对
这是整理代码库之后最常遇到的问题。RTL代码能综合、能布局布线、能不报错,但上板之后功能就是不对。这类问题在FPGA调试中占比极高。
我遇到过的一个案例是这样一个情况:从代码库里拿了一个PWM输出模块,仿真的时候波形完全正确,但上板后示波器观察输出没有信号。排查到最后发现PWM输出引脚复用了,板卡上同一引脚连接了两个外设,IO标准还不一样,驱动能力不足导致输出被拉低。这种问题不是代码逻辑错误,而是硬件平台约束不匹配。所以代码移植上来第一步,必须逐一核对引脚约束、IO电平标准、驱动能力选项,跟硬件原理图一个个对过再烧写。
不考虑硬件问题的情况,行为不对还有一个常见原因是时序违约。综合实现报告里如果出现了负的建立时间余量或者保持时间余量,那么行为出错几乎是必然的。这时优先做三件事:第一,确认时钟约束有没有写对,创建时钟、生成时钟、伪路径、最大延迟这些约束是否齐全;第二,检查跨时钟域信号有没有用同步器或者异步FIFO;第三,看关键路径延迟,尝试用流水线切断组合逻辑链。这一套排查逻辑在很多项目里都适用。
4.2 跨时钟域处理不当
代码库里的模块组合到一块儿之后,最大的隐患往往是跨时钟域问题。单个模块看起来都正常,但模块A在100MHz时钟域、模块B在75MHz时钟域,AD之间的信号传输没有做同步处理,就会有概率性错误,而且这种错误极难复现。
跨时钟域处理的几个标准手段必须熟练:单bit电平信号用两级同步器;单bit脉冲信号用脉冲同步器,或者在目标时钟域做展宽后再同步;多bit数据总线,根据需求用异步FIFO或者握手机制。调试跨时钟域问题,重点不只是“怎么改代码”,而是“怎么发现”这个问题。我现在的做法是,在RTL代码里就把所有跨时钟域接口单独抽成一个个小模块,同时配合CDC验证工具或者静态检查,在早期阶段就把问题暴露出来,而不是等板卡出问题后在示波器上慢慢猜。
4.3 资源评估与控制
从代码库拿模块做系统集成时,资源评估是必须提前做的功课。不同芯片的资源差异极大,低端CPLD可能只有几百个逻辑单元,高端FPGA有上百万逻辑单元、几千个DSP Slice,同一段代码放到不同芯片上完全会有不同结果。
评估资源的直接方法是“综合一次看报告”,但更快的估算法是看几个关键指标的占用:逻辑单元、寄存器、BRAM、DSP Slice。以图像处理链路为例,3x3窗口的Sobel算子大致消耗两三百个逻辑单元和若干行缓冲BRAM;Cordic迭代模块大概每个迭代级消耗三四十个逻辑单元;DSP Slice被乘法器大量消耗,FIR滤波器的抽头数直接决定DSP占用。通过这种估算方式,在项目启动阶段就能判断目标芯片够不够用,避免在选型后才发现容量不足。
4.4 常见问题速查表
我把几个高频问题整理成一个速查表,方便复用代码时快速对照:
| 现象 | 可能原因 | 排查方式 |
|---|---|---|
| 综合报错找不到信号 | 模块例化时端口名不匹配 | 对比例化模板与定义,逐端口检查 |
| 时序约束报告大量违例 | 时钟约束缺失或错误 | 重新生成时钟约束,核对PLL配置 |
| 上板无输出 | 引脚约束不对或IO标准问题 | 对照原理图检查引脚和电压域 |
| 信号偶发跳变 | 跨时钟域未同步 | 加同步器或异步FIFO |
| 仿真正常上板异常 | 异步复位时序或输入毛刺 | 检查复位同步,对异步输入打拍 |
| FIFO数据读空/写满异常 | 空满信号生成逻辑错误 | 检查格雷码转换和指针同步 |
这张表基本覆盖了我实际工作中大约七成FPGA调试问题,遇到现象可以先对号入座快速定位,比从头开始看代码高效得多。
5. 这份代码库还能怎么用
5.1 串联起完整的学习路径
对于初学者来说,这份分类代码库的价值不止是“抄代码”,更是建立硬件设计思维方式的一条路径。建议按这个顺序走:先看基础组件,理解时序逻辑、组合逻辑、状态机这些基本概念;然后跑接口协议模块,学习如何与外设交互;接着做算法模块,体会FPGA并行架构带来的加速效果;最后做图像处理,把前面积累的东西串成一条完整链路。
每个阶段我都建议做两个动作:第一,把模块的testbench自己写一遍,不要用现成的,强迫自己理解输入输出关系;第二,修改原模块的参数或者增加一个小功能,比如给UART加一个奇偶校验错误计数,给FIFO增加Almost Full信号,让模块变成自己的东西。
5.2 快速原型验证
我最近一个项目做毫米波雷达的数据采集处理,算法团队给的MATLAB模型还没定稿,但他们希望FPGA团队能先出一版可以跑通的硬件原型。这种场景下不可能从零分模块开发。我直接从代码库里找到LVDS接收模块、异步FIFO、FFT接口逻辑、UART发送模块,按照算法需求稍作参数调整,半天时间就把数据通路搭出来了。
这套流程能达到这样的效率,一个重要原因是代码库里现成模块的接口风格相对统一,组合起来不需要大量胶水逻辑。整理的时候我就有意统一了模块接口规范:全局信号(clk、rst_n)放前面,配置参数用parameter,数据接口用s_axis/m_axis风格命名。这样新模块集成进来,不需要反复适配接口风格。
5.3 从Verilog向SystemVerilog迁移
近两年新项目我已经逐步从Verilog迁移到SystemVerilog,一方面是大厂IP基本都是SV,另一方面SV的interface、struct、enum等语法确实能让代码简洁不少。代码库里的Verilog模块作为迁移起点很有帮助,因为SV兼容Verilog语法,在同一工程里可以混用。
迁移过程中我总结了一个有效步骤:先改testbench,用SV的类、约束、覆盖率写出来的测试平台,效率和可读性提升明显;然后把新写的模块用SV,旧模块先保持Verilog,通过interface做连接;最后等所有模块验证完毕,再逐步把核心模块重写为SV。这种渐进式迁移在不影响项目进度的前提下,实现了“边用边学”的效果。
最后再分享一个我个人的使用习惯:代码库不是收藏夹,不能只存不用。我每年会做一次大清理,把“仿真验证过且项目真正用上”的模块放进“verified”目录,把“只写过还没验证”的放进“develop”目录,已经过时的代码直接删除。这样代码库越来越精简,而不是越来越臃肿。如果你也在整理自己的FPGA代码库,建议从今天开始,先挑三个最近用到的模块分类放好,后面再慢慢补全。等积累到一定程度回头看,你会发现自己已经拥有了一个比任何教程都宝贵的工程资产。
本文还有配套的精品资源,点击获取