板子回来了,代码烧进去,不工作。这时候第一反应是打开ILA抓信号。但ILA资源有限,抓哪些信号最有效?顺序是什么?很多人上来就抓数据总线,结果波形一大堆,看半天看不出问题。
调试和看病一样,得讲个望闻问切的顺序。我自己的习惯是:从时钟到复位,从配置到控制,最后才看数据。这个顺序能避开大部分无效抓取。
第一步:时钟和复位------先确认芯片"活着"
时钟是FPGA的心跳。如果时钟有问题,后面所有信号都没有意义。
先抓系统时钟域下的一个自由计数器。比如一个32位计数器,每个时钟沿加一。如果这个计数器不动,说明时钟没进来,或者时钟约束有问题导致布局布线把时钟路径搞坏了。
verilog
reg [31:0] heartbeat;
always @(posedge sys_clk) begin
heartbeat <= heartbeat + 1;
end
// ILA抓 heartbeat,看它是否在递增
如果心跳正常,再看复位。复位信号最容易被忽略。抓复位同步后的版本,看它是不是在某个时刻干净地释放了。如果复位释放的时刻刚好在时钟沿附近,触发器可能进入亚稳态,导致部分逻辑没复位、部分复位了。这种"半初始化"状态上板必挂。
第二步:配置和电源------FPGA到底启动没有
有时候代码没问题,是FPGA根本没配置成功。抓DONE信号,看它有没有拉高。如果DONE一直是低,检查配置模式引脚、配置时钟、Flash里的bit文件是否完整。
电源方面,板上调试时万用表比ILA更直接。先量核心电压、IO电压、辅助电压是否在正常范围。如果电压对但FPGA不工作,再看电源纹波------纹波过大可能导致内部逻辑随机出错。
第三步:关键控制信号------状态机、使能、握手
时钟复位都正常了,接下来看控制路径。状态机是最值得抓的信号。看它是否按照预期跳转,有没有卡在某个状态出不来。
verilog
typedef enum logic [2:0] {
IDLE, INIT, RUN, DONE, ERROR
} state_t;
state_t current_state, next_state;
// ILA抓 current_state,看跳转是否符合预期
除了状态机,还要看使能信号和握手信号。比如一个模块的start信号是否拉高过,busy信号是否在预期时间内拉低,valid/ready握手是否完成。这些信号位宽小,占ILA资源少,但信息量很大。
第四步:数据通路------最后才看数据
很多人一上来就抓数据总线,这是效率最低的做法。因为数据出问题,根源往往不在数据本身,而在控制逻辑。
当控制信号都正常了,再抓数据。抓的时候注意几点:只在valid有效时看数据,否则看到的可能是无效值;抓数据的同时抓valid和last,判断数据包的边界;如果数据来自外部接口,还要抓相应的时钟和同步信号。
比如一个AXI-Stream接口,抓取时应该同时抓tvalid、tready、tdata、tlast。如果tvalid和tready同时为高,tdata才是有效数据。
排查顺序总结
把上面的步骤串起来,就是一个从底层到上层的排查链:
-
时钟:有没有?频率对不对?抖动大不大?
-
复位:释放是否干净?有没有毛刺?同步处理了吗?
-
配置:DONE拉高了吗?电源正常吗?
-
控制:状态机跳转对吗?使能信号有效吗?握手完成了吗?
-
数据:valid时数据对吗?边界处理对吗?
这个顺序的核心逻辑是:先确认基础设施正常,再看业务逻辑。跳过前几步直接抓数据,就像地基没打好就去检查墙面裂缝------方向错了。
一点实际经验
在由你创科技接触过的项目中,很多"上板不工作"的问题最后都定位到了时钟或复位上。比如有一次,一个图像处理项目上板后偶尔花屏。抓数据通路折腾了两天没结果,后来抓复位信号发现复位释放的时刻和像素时钟的相位关系有问题,导致行缓冲区的写指针偶尔错位。把复位改成异步复位同步释放之后,问题消失。
板上调试这件事,工具是其次,顺序才是关键。ILA资源有限,抓什么、先抓什么,直接决定调试效率。下次板子不工作的时候,不妨按这个顺序走一遍------先看心跳,再看复位,然后状态机,最后才数据。
如果你在调试中遇到那种"时好时坏"的问题,欢迎交流。FPGA开发里,很多坑不是代码写错了,而是对硬件行为的理解差了一层。