我们队用高云GW2A系列FPGA搭了一个RISC-V软核,跑通了基本的LED点灯,但是一上串口就乱码。波特率配置了115200,时钟是50MHz,用PLL分频到100MHz给软核。查了UART的寄存器配置,感觉没问题,但打印出来的全是乱码。队友说是软核启动时序问题,我还怀疑是PLL锁定时间不够。有没有大佬遇到过类似情况?求一个系统的排查思路,从哪一步开始查比较高效?下周就要提交初赛报告了,急!
2026年了,FPGA大赛用国产高云FPGA做RISC-V软核,串口打印乱码,是波特率配置问题还是软核启动时序问题?求排查思路
6 个回答

先把PLL锁定信号拉出来看吧,这个最省时间。高云GW2A的PLL锁定输出一般是LOCK或LOCKED引脚,软核复位最好等它拉高后再释放。很多乱码其实是软核在时钟还没稳的时候就跑了,UART波特率自然全偏。你现在的流程如果是上电直接复位软核,先改成检测LOCK信号高电平后再延迟几百微秒复位,大概率能解决。如果改完还乱,再回头查UART的DIV参数,100MHz下115200的分频系数不是整数,得用带小数分频的UART或者把系统时钟调成92.16MHz这种能整除的。别一上来就怀疑寄存器配置,先解决时序再谈波特率。你们PLL的LOCK信号现在接出来看过了吗?

这问题我在自己做GD32和软核的时候踩过,给你一个从底层往上的排查顺序,别一上来就盯寄存器。第一步,确认PLL锁定信号,高云GW2A的PLL输出LOCK引脚要接到软核的复位逻辑里,复位释放必须在LOCK有效之后,而且最好再加个计数器延迟,比如锁定后等100us再释放软核复位。如果你们只是上电延时复位,PLL还没锁定时软核就跑了,那UART的波特率发生器从一开始就是错的,打印自然全乱。第二步,核对时钟树,你说是50MHz进PLL,分频到100MHz给软核,但UART模块的时钟是独立接的,还是和软核共用100MHz?如果UART挂在AHB总线上,用的就是系统时钟,那么115200波特率需要分频系数=100MHz/115200≈868.06,这个不是整数,一般UART IP会用整数分频加误差修正,实际波特率误差可能到0.5%以上,串口在长字符串时就会乱码。解决办法是把系统时钟改成92.16MHz(115200800),或者用带小数分频的UART IP,高云的软核生态里一般有现成的UART16550,它支持小数分频,但你要确认配置寄存器里DLL和DLM的值对不对。第三步,用ILA抓UART TX引脚波形,看每个bit的宽度是不是8.68us(1/115200),如果宽度明显不对,比如是7us或10us,那就是分频系数算错了;如果宽度对但数据还是乱,那可能是数据位、停止位配置不一致,比如PC端设了8N1,你这边设了7E1。常见误区是只查UART控制寄存器,忽略PLL锁定和时钟频率的匹配,这两个是串联关系,时序不稳时寄存器配置再对也没用。你们现在PLL锁定信号有没有接到软核复位上?还是用的固定延时?如果是固定延时,建议改成LOCK门控,这个改动很小,但能排除一大半问题。

先别急着改代码,我建议你们做一个最小化串口测试:只写一个简单状态机,不跑RISC-V软核,直接用FPGA逻辑把固定字节(比如0x55)循环发送到UART TX引脚,用逻辑分析仪或者示波器看波形。0x55的二进制是01010101,每个bit交替,如果波特率正确,波形上每个bit宽度应该是8.68us,一眼就能看出偏差。这个测试能直接把问题分成两半:如果最小测试波特率不对,那就是时钟或分频配置问题,跟软核启动时序无关;如果最小测试波形正确,但接上软核就乱码,那才是软核启动时序或总线配置问题。你们现在的情况是LED能跑,说明FPGA配置和基本时钟没问题,但PLL锁定和软核复位之间的时序关系确实容易被忽略。另外提醒一点,高云IDE里PLL的IP核配置界面有个参数叫'Locked Output',默认可能是低电平有效还是高电平有效,你得看数据手册确认,接反了等于没接。还有个取巧的办法:如果时间太紧,可以把系统时钟直接改成50MHz,不经过PLL,这样波特率分频系数变成整数434,误差为零,先让串口通起来再说性能。软核跑慢点不影响调通,等初赛报告交了再优化时钟。你们现在有没有多余的IO口能引出PLL的LOCK信号?或者直接用ILA看内部信号也行,但高云的调试核用起来比Xilinx的ILA繁琐些,得先确认你们环境里建好没。

你们LED能跑,说明FPGA配置和基本逻辑没问题,问题多半出在时钟域交接上。我建议按这个顺序查:第一步,把PLL的LOCK信号接到一个空闲IO上,用示波器看它上电后多久才拉高,同时看软核复位信号什么时候释放。如果复位先于LOCK,那软核在时钟不稳时启动,UART的波特率分频虽然写对了,但实际跑出来的频率是乱的。第二步,确认UART模块的时钟源——是高云那个软核IP自带UART,还是你自己写的?如果是自己写的,分频系数=100MHz/115200≈868.06,这个不是整数,整数分频会带来约0.007%的误差,理论上是能接受的,但如果你的分频器写的是868,实际波特率是115207,打印短字符串没问题,长字符串会累积误差。第三步,用逻辑分析仪抓UART TX引脚,看每个bit的宽度是不是8.68us,这个最直观。如果波形宽度不对,再回去查PLL输出频率是否真的是100MHz,可以用SignalTap或者高云的在线逻辑分析仪抓内部时钟计数。另外,你们PLL配置里有没有勾选锁定输出作为复位条件?高云IDE里PLL IP有个选项是输出LOCK信号,一般要接到软核的复位逻辑上。

其实最快的方法是做个最小化串口回环测试:写个简单的状态机,不跑软核,直接让FPGA逻辑循环发送0x55到UART TX,用逻辑分析仪抓波形。0x55是01010101,每个bit交替,一眼就能看出波特率准不准。如果这个测试波形正确,说明时钟和分频都没问题,那就是软核启动时序的锅;如果波形都错,那就别纠结软核了,先把PLL和分频搞定。这个测试半小时就能做完,比翻寄存器配置高效得多。你们现在最缺的就是把问题二分:到底是硬件时钟问题还是软核启动问题,别混在一起猜。顺便问一句,你们软核的复位信号是接到PLL的LOCK上了,还是用的独立按键?

你们这个情况我去年带学生打集创赛时见过一模一样的,LED能跑但串口乱码,最后查出来就是PLL锁定和软核复位的先后问题。高云GW2A的PLL上电后需要几百微秒才能锁定,LOCK引脚拉高之前输出时钟是不稳定的,如果软核在这个窗口期就开始执行UART初始化,那波特率分频寄存器虽然写对了,但实际采到的时钟沿是乱的,打印自然全花。你们现在最该做的不是翻寄存器,而是把PLL的LOCK信号引到板子上一个空闲LED或者直接接逻辑分析仪,看它上电后多久才拉高,再确认软核复位释放是不是在LOCK之后。如果复位比LOCK早,那就得改复位逻辑,常见做法是用LOCK信号做门控,再加个计数器延迟比如100us再释放软核复位。另外还有个坑,你们100MHz给软核,UART模块如果是挂在AHB总线上共用这个时钟,那115200的分频系数是868.06,不是整数,整数分频器写868实际波特率是115207,短字符串打印没问题,但你们如果一次打印超过几十个字符,误差累积就会乱码。建议先用固定0x55循环发送测一下,看每个bit宽度是不是8.68us,如果波形对那就是软核启动时序,如果波形都不对就先解决分频。你们软核复位现在具体是怎么接的?是直接上电延时还是用了LOCK信号?
发表回答
登录后可在本页底部提交回答