发布日期:2024-10-05 12:11 点击次数:91
FPGA才智的调试,尤其是大型才智,一直都是耗时耗力的使命。领先是因为HDL话语沿空间并发扩展的本性不同于一般基于定时间线性叙事的策动机话语,各个元素之间的逻辑干系愈加详尽在线av 乱伦,不易统一和想考,debug时刻也未几。同期,FPGA才智的抽象布线进程都比较迟缓,频繁一个中等大小的才智会需要恭候数十分钟才能得到输出文献,大型想象run隔夜是常见的事情,搪塞一个笔误都会带来很高的千里没老本。每次说到这里,都不由取得忆起多年夙昔某次出差作念试验,相逢过一位议论所老迈打趣说最爱带领指派FPGA任务,因为只消按下抽象按钮,便能宽解歇息半个午后。当时候,全国初开,摸鱼这个词还莫得发明,也莫得意想即等于当下最强最fancy的CPU也没能赈济这种恭候。
离题万里。在逻辑构想基本正确,才智编写节略圭表的前提下,大大都的问题,都仅仅写代码时的一些小果决。通过实施功能仿真,其实不错把大都逻辑问题都找出来。关联词,本色使命中,频繁没就怕辰或耐烦作念仿真,而是径直编码后下硬件调试了。此时,通过仔细阅读抽象器给出的申饬信息,就不错在最耗时的布局布线之前把多样小问题找出来并修正,从而灵验地升迁开荒后果。在调试阶段,如若遭逢了难以统一的景观,回过甚去分析申饬信息亦然一种灵验的目的。在本文中,咱们以vivado自带抽象器为例、以verilog为编程话语,望望如何统一和应用申饬信息摈斥代码中的小bug。
(以上图片来自鸠合: My digital designing diary by Mandapati) 为了疏漏禀报,咱们先建一个样例工程,包含模块top和adder。
为了幸免像许多经典教科书中的 “a=(b++)+(++c)” 那样被指为“例子代码不珍摄软件工程”,额外把这个才智写的尽量逼近工程本色一些(除了莫得安宁)。领先它具有特定的功能,对两路并发输入的数据流先相加再累加。其次,数据端口界说选拔时下常用的AXI-Stream立场。
模块top是顶层模块,其源码Top.v如下图。该模块具备时钟信号clk和异步复位信号rst。输入数据端口din_tdata[31:0],配套流控执手信号din_tvalid和din_tready。在模块里面,输入数据领先被劈成2个16bit数据,代表要相加的两路数据流,并注入加法器模块adder。加法器的输出数据是adder_out[15:0]。第31行脱手的always语句则完成对加法终端的累加操作。累加终端从端口acc_tdata输出,配套数据灵验秀丽acc_tvalid。
再望望加法器的源码Adder.v,如下图。两路数据din1和din2,分享统一组流控执手信号din_tvalid和din_tready。加法终端从端口dout输出。在模块里面,第14行的always语句讲求完成整个逻辑措置。中枢语句在第21行:当输入数据灵验何况后级确立准备好时,进行一次加法操作。写到这里作家亦然十分咨嗟,三十多行代码就是为了伺候第23行的这个“+”号。
至此,例子才智搭建好了。才智比较小,按下Run Synthesis按钮恭候半分钟把握就能看到抽象终端。底下咱们一齐一边修改代码一边望望常见的申饬信息都有哪些。 1. 常数驱动 申饬之是以是申饬,是因为抽象器分不清它是否真的有问题。许多申饬是不错忽略的。举例上述例程,看上去很无缺,但抽象完成后仍然会得到如下的申饬信息:
这里,抽象器提醒咱们,top模块的端口din_tready被驱动为常数1,可能是一个潜在的问题。在top的逻辑中,din_tready的功能是提醒模块外部的前级数据源“是否准备好经受数据”。被驱动为1则示意“永恒都准备好经受数据”。用追根求源的规律分析代码,不错看到din_tready是被加法器实例adder1驱动的,而在加法器里面(adder.v的第31行),该信号来自于加法器输出端的tready。
再看回top.v的第17行,竟然加法器的tready被置为常数1,根源在此。具体到这个例子,此逻辑本人莫得大问题,因为top模块的输入端口唯稀有据灵验信号acc_tvalid,并莫得配套的tready,讲解该端口是强行输出的,并不探求后级莫得准备好的情况。 是以,在此例中,这个常数驱动申饬“基本上”不错被忽略。关联词,问题禁不住细琢磨,比如斯例程并莫得探求在复位信号rst灵验时间din_tready应当拉低来拦阻数据输入,不太周密。
进一步的,咱们还能瞎意想,在果真的系统中,这个接口界说是否存在隐患? 即,后级模块是否真的不错无条目经受数据? 这些都是申饬信息带来的福利。 2. 不必信号 底下脱手折腾代码。领先把top.v line27处的端口一语气去掉,只留住空括号。这么,adder1实例的dout_tvalid输出就悬空了。
抽象之后在线av 乱伦,得到如下图的申饬。抽象器见告adder1中的dout_tvalid所对应的寄存器资源被移除。
显著,这是由于在top中断开了信号一语气,于是dout_tvalid信号在adder里面天然被赋值,关联词在整个这个词逻辑中莫得被任何其它所在使用,也莫得输出,于是抽象器在给出申饬后就将其删除了。从这个例子不错看到,如若一个信号被自动移除了,应当领先应当探求它是否莫得在别处被用到。不外,不才一个例子里立时不错看到这并不是信号被优化掉的惟一的原因。 3. 无源信号 领先,先把源码修起,然后试着把Top.v第17行安宁掉:
抽象之后,得到如下申饬信息:
第一条信息直奔主题:adder_tready信号莫得被驱动。这显著是前述修改带来的,源才智里缺少对adder_tready的赋值操作。第二条以及随后更多的信息则会让东谈主困惑:adder1/dout[15:0]被从逻辑中移除了。这些信号明明都有被后续的累加操作用到,为什么还会被优化掉? 通过分析adder中的逻辑干系不错知谈,这仍然是因为adder_tready莫得被驱动,于是抽象器以为但凡依赖于adder_tready的后续信号都一经莫得存在的真谛真谛,于是一股脑全拿掉了。这就教导咱们,如若发现存大片的逻辑磨灭了,不但要往后寻找看是否缺少最终的输出,何况要往前寻找看是否存在不笃定大要无驱动的输入。
天然,关于多样异常情况,不同的抽象器以及统一个抽象器的不同的参数,会推崇很大的各异。比如作家也见过有的抽象器会径直给无驱动信号赋值为0,这种真心诚意的掩盖反而导致就怕候问题很难查找。 4. 多重驱动 在top.v的第16行,把原先的adder_d2改成adder_d1,变成一个典型的笔误。原本是要分辩给信号addr_d1和addr_d2赋值,一不堤防变成了给信号adder_d1赋值两次。
关于上述情况,抽象器明确指出了有信号被multi-driven了,如下图。
关联词,它指出的对象却并不是addr_d2,而是我数据源din_tdata。这是因为,在抽象器看来,din_tdata[15:0]和din_tdata[31:16]都一语气到了addr_d2[15:0],其实就是din_tdata[15:0]与din_tdata[31:16]径直点对点短接了,是以它们本人就面对多驱动问题,addr_d2此时仅仅一个“别号”良友。好比你外出健忘戴帽子,而抽象器告诉你:请珍摄凉风一经战役头皮。这种机器式的禀报立场,就怕候果真会带来一些小劳苦,不外习尚了就好了。 5. 复位缺失 top.v第31行的always语句选拔了异步复位。复位信号rst与时钟clk一齐手脚语句触发条目,在语句里面先按判断rst是否为真来取舍扩张复位操作。这是verilog典型的异步复位语句写法。这里,尝试把第36行安宁掉,如下图。
抽象器会给出如下的申饬。
字面背后的真谛不错统一为:语句中存在复位语段,关联词并莫得对acc_tvalid信号作念复位操作,导致逻辑缺失,大要说抽象器分不清应该set如故reset,于是牵记抽象终端会与仿真终端不符。 这类申饬不错匡助咱们找出因为忘了写复位而开动值不笃定的寄存器,这雷同是许多要紧bug的开头。如若存在某些寄存器的确不需要复位操作,则应当单独写一个唯有clk作念触发的always句段,就能幸免上述申饬。 那么,此时抽象终端有莫得生成渴望的逻辑呢? 掀开抽象输出的逻辑图(如下),不错看到acc_tvalid由一个莫得复位和置位的D触发器驱动,安妥修改后的语句同意。关联词,咱们仍然应该设法幸免这类不太圭表的写法。尤其是关于生人,务必要了解话语与果真逻辑的映射干系,难忘verilog就那么几种常见的语句套路。新奇的写法,可能导致完竣不成预期的抽象终端。
6. 位宽失配
修改adder模块的端口声明,如下图,把din1和din2的位宽从16bit分辩改为17和15。
如下图,抽象器会明确指出在top.v中罢了adder模块时遭逢了端口宽度不匹配的问题。
需要指出的是,至少关于vivado + verilog,位宽失配申饬只对模块端口一语气有用。如若是两个位宽不同的信号赋值,抽象器将会径直作念高位截断大要高位补零,而不给任何申饬,除非截断操作触发了不必信号申饬。是以,岂论是wire类型如故reg类型,赋值时的位宽对都问题,完竣需要编程者自行存眷。举例底下的语段,16bit的src被赋值给16bit的dst1和15bit的dst2,显著赋值给dst2时最高位会丢失,关联词此时抽象器不会给出申饬,这是verilog话语本人的特色,改不了。何况,因为dst1用到了src的整个bit位,是以在抽象器看来src里也不存在不必的bit位,也不会触发不必信号申饬。最驱逐尾就是,可能你就是笔误给dst2少写了1位,但这个异常要到后期调试时通过多样故障才被发现。这里并不是抽象器犯懒,而是verilog话语本人就是这么想象的,比拟之下VHDL就要严格的多,不同位宽信号彼此赋值不给申饬,而是径直报错。
自慰
7. 不应有的锁存器 把top.v中对adder_d1和adder_d2的径直赋值语句改为always句段,如下图:
上述修改将产生如下图的申饬信息:adder_d1和adder_d2变量引入了锁存器(latch)。
分析上述语句,不错看到din_tvalid的确相配于锁存使能信号,当它为1时din_tdata可穿透到adder_d1和adder_d2。如若掀开schematic不雅察抽象终端,会发现此处使用了一个名为LDCE的锁存器元件。 咱们知谈,FPGA公认的基础逻辑资源是查找表和D触发器,是否具备锁存器要看具体的FPGA型号和抽象器的算法,是以在HDL话语中书写锁存器立场的语句并不是好目的,亦然这条申饬存在的真谛真谛。 至此,咱们疏漏先容了在抽象阶段常见的一些申饬问题。天然,在后续的implementation操作中,还会有许多更难统一的教导和申饬出现,它们愈加地与具体器件的里面结构和元素关联。到了这些阶段,愈加需要去存眷XDC文献、存眷物理和时序络续,而不是HDL话语本人。
审核裁剪:黄飞在线av 乱伦