《FPGA程序的一种调试方案解读.docx》由会员分享,可在线阅读,更多相关《FPGA程序的一种调试方案解读.docx(12页珍藏版)》请在第一文库网上搜索。
1、FPGA程序的一种调试方案解读FPGA程序的调试,尤其是大型程序,一直都是耗时耗力的工作。首先是因为HD1语言沿空间并发扩展的特性不同于一般基于按时间线性叙事的计算机语言,各个元素之间的逻辑关系更加紧密,不易理解和思考,debug手段也不多。同时,FPGA程序的综合布线过程都比较缓慢,经常一个中等大小的程序会需要等待数十分钟才能得到输出文件,大型设计run隔夜是常见的事情,随便一个笔误都会带来很高的沉没成本。每次说到这里,都不由得回忆起多年以前某次出差做实验,邂逅过一位研究所大哥玩笑说最爱领导指派FPGA任务,因为只要按下综合按钮,便能安心歇息半个午后。那时候,天地初开,摸鱼这个词还没有发明,
2、也没有想到即便是当下最强最fancy的CPU也没能拯救这种等待。言归正传。在逻辑构思基本正确,程序编写大致规范的前提下,大多数的问题,都只是写代码时的一些小疏忽。通过实施功能仿真,其实可以把多数逻辑问题都找出来。但是,实际工作中,经常没有时间或耐心做仿真,而是直接编码后下硬件调试了。此时,通过仔细阅读综合器给出的警告信息,就可以在最耗时的布局布线之前把各种小问题找出来并修正,从而有效地提高开发效率。在调试阶段,如果遇到了难以理解的现象,回过头去分析警告信息也是一种有效的办法。在本文中,我们以ViVadO自带综合器为例、以Veri1Og为编程语言,看看如何理解和利用警告信息排除代码中的小bugo
3、(以上图片来自网络:Mydigita1designingdiarybyMandapati)为了方便叙述,我们先建一个样例工程,包含模块top和adder。为了避免像很多经典教科书中的“a=(b+)+(+c)”那样被指为“例子代码不注重软件工程”,特意把这个程序写的尽量贴近工程实际一些(除了没有注释)。首先它具有特定的功能,对两路并发输入的数据流先相加再累加。其次,数据端口定义采用时下常用的AXI-Stream风格。模块top是顶层模块,其源码Top”如下图。该模块具备时钟信号dk和异步复位信号rst。输入数据端口din_tdata31:0,配套流控握手信号din_tVaIid和din_trea
4、dy。在模块内部,输入数据首先被劈成2个16bit数据,代表要相加的两路数据流,并注入加法器模块adder。加法器的输出数据是adder_out15:0o第31行开始的a1ways语句则完成对加法结果的累加操作。累加结果从端口acc_tdata输出,配套数据有效标志acc_tva1idoTop.vtimesca1eIns/Ipsendendmodu1ewire15:0adder_d1fadder_d2,adder_out;wireadder_tva1id,adder_tready;a1ways(osedgeCikorposedgerst)beginif(rst)beginendendassig
5、naddejd1=din_tdata15:0;assignadder_d2=din-tdata31:16;assignadder_tready=1,b1;123456789101112131415161718293031323334353637383940414243444546modu1etop(inputinputinput31:0inputoutputoutputreg15:0outputregc1kfrstfdin-tdatafdin_tva1idfdin_tready,acc_tdataracc_tva1id);19adderadder1(20.c1k(e1k),21-rst(rst
6、)f22.din1(adder_d1)f23,din2(adder_d2)f24.din_tva1id(din-tva1id)f25din_tready(din_tready),26.dout(adder_out)f27.dout_tva1id(adder_tva1id)f28.dout-tready(adder_tready));acJtdata=16b0;acc_tva1id=1,b0;e1sebeginif(adder_tva1id)beginacc_tdata=acJtdata+adder_out;endacc_tva1id=adder_tva1id;再看看加法器的源码Adder.v,
7、如下图。两路数据din1和din2,共享同一组流控握手信号din_tva1id和din_tready。加法结果从端口dout输出。在模块内部,第14行的a1ways语句负责完成所有逻辑处理。核心语句在第21行:当输入数据有效而且后级设备准备好时,进行一次加法操作。写到这里作者也是十分感慨,三十多行代码就是为了伺候第23行的这个“+”号。17181920212223242526272829Adder.v3modu1eadder(4inpute1k,5inputrst,6input15:0din1f7input15:0din2f8inputdin-tva1idf9outputdin_tready,
8、10outputreg15:0doutf11outputregdout_tva1id,12inputdout-tready);1314a1ways(posedgee1korposedgerst)15begin16if(rst)1timesca1eIns/Ips2begindout=161b0;dout_tva1id=1,b1;ende1sebeginif(din_tva1id&dout_tready)begindout=din1+din2;dout_tva1id=1,b1;ende1sebegindout_tva1id=1,b0;endendend3031assigndin_tready=do
9、ut-tready;3233endmodu1e至此,例子程序搭建好了。程序比较小,按下RUnSyntheSiS按钮等待半分钟左右就能看到综合结果。下面我们一起一边修改代码一边看看常见的警告信息都有哪些。1常数驱动警告之所以是警告,是因为综合器分不清它是否真的有问题。很多警告是可以忽略的。例如上述例程,看上去很完美,但综合完成后仍然会得到如下的警告信息:7Synthesis(1warning)Synth8-3917designtophasportdInJreadydrivenbyconstant1.蚱二:这里,综合器提醒我们,top模块的端口dinjready被驱动为常数1,可能是一个潜在的问题
10、。在top的逻辑中,dinjeady的功能是提醒模块外部的前级数据源“是否准备好接收数据”o被驱动为1则表示“永远都准备好接收数据”。用顺藤摸瓜的方法分析代码,可以看到din_tready是被加法器实例adder1驱动的,而在加法器内部(adder.v的第31行),该信号来自于加法器输出端的tready。再看回top.v的第17行,果然加法器的tready被置为常数1,根源在此。具体到这个例子,此逻辑本身没有大问题,因为top模块的输入端口只有数据有效信号acjtva1id,并没有配套的tready,说明该端口是强行输出的,并不考虑后级没有准备好的情况。所以,在此例中,这个常数驱动警告“基本上
11、”可以被忽略。但是,问题禁不住细琢磨,比如此例程并没有考虑在复位信号rst有效期间dinjeady应当拉低来禁止数据输入,不太周全。进一步的,我们还能联想到,在真实的系统中,这个接口定义是否存在隐患?即,后级模块是否真的可以无条件接收数据?这些都是警告信息带来的福利。2.无用信号下面开始折腾代码。首先把top.v1ine27处的端口连接去掉,只留下空括号。这样,adder1实例的dout_tVaIid输出就悬空了。Top.v19adderadder1(20.e1k(e1k),21rst(rst)f22.din1(adder_d1),23.di2(adder_d2),24.din-tva1id(
12、din_tva1id),25.din_tready(din_tready),26.dout(adder_out)f27.dout_tva1idO,28.dout-tready(adder_tready)29);Gk电子发烧反综合之后,得到如下图的警告。综合器告知adder1中的doutjva1id所对应的寄存器资源被移除。Synth8-6014Unusedsequentia1e1ementadder1/dout_tva1id_regwasremoved.Adder?r显然,这是由于在top中断开了信号连接,于是dout_tva1id信号在adder内部虽然被赋值,但是在整个逻辑中没有被任何其它
13、地方使用,也没有输出,于是综合器在给出警告后就将其删除了。从这个例子可以看到,如果一个信号被自动移除了,应当首先应当考虑它是否没有在别处被用到。不过,在下一个例子里马上可以看到这并不是信号被优化掉的唯一的原因。3.无源信号首先,先把源码复原,然后试着把TOPV第17行注释掉:Top.v15assignadder_d1=din_tdata15:0;1617ujuw.e1assignadder_d2=din_tdata31:16;/assignadder-tready=1,b1;综合之后,得到如下警告信息:Synthesis(17warnings)Synth8-3848Netadderjready
14、inmodu1e/entitytopdoesnothavedriver.Top.v:13Synth8-3332Sequentia1e1ement(adder1dou1reg15)isunusedandwi11beremovedfrommodu1etop.(15moreIiJ第一条信息直奔主题:addejtready信号没有被驱动。这显然是前述修改带来的,源程序里缺乏对addejtready的赋值操作。第二条以及随后更多的信息则会让人困惑:adder1/dout15:0被从逻辑中移除了。这些信号明明都有被后续的累加操作用到,为什么还会被优化掉?通过分析adder中的逻辑关系可以知道,这仍然是因为adderJTeady没有被驱动,于是综合器认为凡是依赖于addejtready的后续信号都已经没有存在的意义,于是一股脑全拿掉了。这就提示我们,如果发现有大片的逻辑消失了,不但要往后寻找看是否缺乏最终的输出,而且要往前寻找看是否存在不确定或者无驱动的输入。当然,对于各种异常情况,不同的综合器以及同一个综合器的不同的参数,会表现很大的差异。比如作者也见过有的综合器会直接给无驱动信号赋值为0,这种好心好意的掩饰反而导致有时候问