跟着B站教程学了三个月FPGA,流水灯、数码管、状态机都能写,但最近自己动手写UART接收模块,波特率算对了,仿真波形也正常,上板就是收不到PC发来的数据。用ILA抓波形发现起始位检测好像有毛刺,但又不敢确定。想问问过来人,这种「仿真过、上板挂」的情况一般先从哪几个方向排查?
2026年FPGA零基础自学三个月,Verilog语法和状态机都懂了,但一写串口接收就收不到正确数据,问题到底出在哪?
5 个回答

先说个最容易被忽略的点:你仿真正常,很可能是testbench里把PC的串口信号理想化了。真实PC串口出来的空闲是高电平,起始位是拉低一个位宽,但实际线上有上升沿抖动、地弹、甚至PC那边波特率本身有偏差。ILA看到起始位有毛刺,八成不是毛刺,是你采样时钟和它没对齐。
排查顺序我一般这么走。第一,确认你的采样时钟频率是不是波特率的整数倍,常见做法是16倍过采样,用计数器在中间第8个点取值。如果你直接用波特率时钟去采,边沿附近正好踩在跳变上,收错太正常了。第二,检查复位和使能,很多新手模块上电后状态机没回到IDLE,或者rx使能信号一直没拉起来。第三,把ILA的采样深度调大,抓完整的起始位到第一个字节,别只看一小段。
还有个坑:PC端串口助手如果开了流控或者发的是连续数据,你的模块只收一个字节就停了,也会看起来像收不到。先让PC每隔一秒发一个固定字节,比如0x55,这样波形好认。
风险上提醒一句,别一上来就怀疑板子或USB转串口线,先怀疑自己的采样逻辑。替代做法是先用一个已知的、别人验证过的UART接收模块对照跑一遍,确认硬件链路没问题,再回头改自己的。
你现在的采样时钟是独立晶振分频出来的,还是从系统时钟直接计数的?这个决定了后面怎么调。
另外补个小例子:如果你系统时钟50M,波特率115200,分频系数大概是434,很多人算成433或435,差一个数在仿真里看不出来,上板累积几十个位就偏了。拿计算器把分频值精确算到小数点后,再用整数逼近,别凭感觉取整。
最后,ILA抓毛刺这件事本身要小心,ILA的采样时钟和你的逻辑时钟如果不同源,看到的跳变可能是采样混叠,不一定是真毛刺。先用同步后的信号去抓,心里才有底。

先别急着怀疑毛刺,毛刺在串口接收里基本是伪命题。你ILA看到起始位附近的抖动,大概率是采样时钟和PC波特率的相位没对齐,而不是真的有干扰。
排查按这个顺序走,比瞎改代码快得多。先确认采样方式:常见做法是用16倍波特率的时钟过采样,计数器数到第7、8个点再取值,中间点采样最稳。如果你直接拿波特率时钟去采,边沿刚好踩在跳变上,收错太正常了。然后在testbench里别用理想的tx信号,给PC的发送加一点波特率偏差,比如±2%,再跑一遍仿真,很多上板才暴露的问题这时候就出来了。
接着看复位和状态机。很多新手模块上电后rx没使能,或者状态机卡在某个态没回IDLE,波形上看就是一直不动。还有PC端串口助手如果开了流控、或者你只发一个字节但模块收完就停了,也会显得像收不到。
一个容易被忽略的维度是电平标准。PC串口出来是RS232的±12V还是TTL的3.3V?中间有没有经过CH340、MAX232这类芯片?如果直接拿RS232接到FPGA引脚,那不是收不到数据的问题,是可能烧管脚。先量一下FPGA输入引脚上的静态电平是不是高,空闲态应该是高。
替代做法:如果你手头有逻辑分析仪,直接抓PC的TX线,看一个字节的位宽对不对,比在FPGA内部猜快得多。仿真过、上板挂,九成是仿真没覆盖真实时序,把testbench写真实一点,比改RTL有用。你现在的采样时钟是波特率的多少倍?这个先确认下。

这个问题其实不是UART的问题,是「仿真和真实世界之间那道墙」的问题。你三个月能写状态机说明基础够用,卡在这里很正常,因为教程里的testbench和真实PC串口根本是两个世界。
先说主线:仿真是理想时序,上板是异步时序。教程里tx信号是干净方波,PC串口出来虽然也是方波,但波特率有晶振误差,通常能到1%到2%,再加上你的系统时钟和它不同源,两个时钟各跑各的。这时候如果你的采样点不在位中间,而是靠边,误差累积几个位就偏出去了。所以核心不是找毛刺,是找采样点位置。
往深一层想,这里有个取舍:你是用16倍过采样还是直接用波特率时钟?16倍多写一个计数器,但抗误差能力强得多,因为每个位你都能重新对齐到中间。直接拿波特率时钟采,省资源,但对时钟精度要求极高,PC和FPGA晶振稍微差一点就崩。零基础阶段我建议一律上16倍过采样,这不是性能问题,是让你少调两天。
再往深一层,是状态机的边界处理。起始位检测的本质是「在空闲高电平里找到第一个下降沿」,然后延时半个位宽再开始逐位采样。很多新手写成检测到低电平就立刻进状态,结果把起始位的跳变沿当成了数据。正确做法是检测到下降沿后,等半个位宽,再确认还是低,才算真起始位,这一步叫中点确认,能滤掉大部分边沿抖动。
学习路径上,你现在的瓶颈不是Verilog语法,是「时序思维」。建议你下一个阶段专门做两件事:一是把testbench写真实,给PC的波特率加偏差、加初始相位偏移,看你的模块什么时候崩;二是学会用ILA抓完整的帧,从起始位到停止位,标出你的采样点,一眼就能看出偏在哪。
工程上还有个现实问题:很多开发板的串口是经过USB转串口芯片的,CH340这类芯片输出的TTL电平和FPGA引脚之间可能有电平不匹配,或者TX/RX接反了。先拿万用表量空闲态,应该是3.3V高电平,如果不对,先解决硬件再谈逻辑。你板子上串口是直连FPGA还是过了转换芯片?这个信息能帮你省不少时间。

先量一下FPGA串口RX引脚的空闲电平是不是高。如果不是,硬件或接线先背锅,别急着改代码。

先说结论:你这个阶段卡住,大概率不是「毛刺」三个字能解释的,而是仿真世界和真实世界之间少了一层建模。你三个月能写状态机,说明写代码的能力够了,真正缺的是「时序意识」和「验证意识」这两块。
顺着一条主线往下想:仿真里的 tx 信号是谁给你的?是你自己在 testbench 里按波特率翻转出来的,它和你的接收模块共用同一个时钟基准,所以相位永远对得整整齐齐。但 PC 串口是另一套晶振,一般有百分之一到百分之二的偏差,而且和你 FPGA 的时钟完全异步。两个不同源的东西凑在一起,如果你的采样点没落在每个位的正中间,误差就会一位一位累积,几个位之后就采到跳变边沿上了——ILA 里看到的「起始位毛刺」,很多时候其实是采样时钟刚好踩在跳变附近,不是真有干扰。
所以排查主线就一条:把「采样点对齐」这件事先解决掉。常见做法是用 16 倍波特率的时钟过采样,计数器数到第 8 个点再取值,这样每个位都能重新对齐到中间,抗误差能力强很多。如果你现在是直接拿波特率时钟去采,边沿一抖就崩,这是最该先改的地方。改完再上板,很多「毛刺」会自己消失。
第二条线是验证方式。你现在的 testbench 太理想了,建议给 PC 侧发送加一点波特率偏差,比如正负百分之二,再把起始位和停止位做成真实线宽,跑一遍仿真。如果仿真里就开始错,那问题根本不用上板找。这一步做完,你会发现自己对「仿真过」这四个字的信任度会下降很多,这是好事。
第三条线才是硬件。确认 RX 引脚空闲是高电平、电平标准对不对(PC 出来是 RS232 还是经过 CH340 转成 TTL,别接错)、共地了没有。这几个是接线层面的,五分钟能查完,别放到最后。
最后提一句取舍:零基础阶段,别急着去追「为什么会有毛刺」这种细节,先把 16 倍过采样这套标准写法练熟,等你能稳定收发一整串数据了,再回头看时序约束和亚稳态。顺序反了会很痛苦。
你现在用的是直接波特率时钟采样,还是已经上了 16 倍过采样?这个决定了下一步该往哪查。
发表回答
登录后可在本页底部提交回答