明德扬小冉 发表于 2019-10-17 17:34:19

【技巧分享】FPGA定位错误的方法

FPGA定位错误的方法
   FPGA设计过程中,任何人都难免写出错误的代码。代码出错不可怕,也非常正常,关键是要把错误找出来。对于工具问题、IP核使用问题,在论坛、QQ群里求助是一个非常好的方法。但对于设计问题,估计求助就非常困难了,因为没人凭着现象就知道是什么原因导致出现了错误,大神也是要去定位。求人不如求己,自己掌握定位方法自己定位。 FPGA说白了,核心就两种能力:设计能力和定位问题能力,理论上掌握了这两个能力,什么都能设计出来。         检查思维不可取。当工程出现错误的时候,在寻找错误代码环节,大部分人会反复一行一行检查代码,如果是小实验,只有一百行代码还好;但是如果是完整的工程,这样的做法无异于大海捞针,往往耗费了巨大时间和精力但收效甚微。人是有思维惯性的,检查的时候很容易就把错误给漏过去。
      还存在一小部分工程师,在面对错误的时候抱有极大的消极态度,稍微定位一下,就认定是遇到了非常奇怪的现象,或者是软件有BUG。从内心不愿意认为是自己有问题,认为自己按照思路做的,不可能出错。            可以很明确地说,出现奇怪现象,出现了错误,99%是自己的粗心大意,越是奇怪,越表明有多粗心,那么定位时就需要越仔细。有些同学对此不是很认可,但是根据多年的培训经验,不论是大小项目,很多同学最后找出的问题有芯片信号写错数完,有引脚配置错误的,甚至有的只是编写程序不习惯对齐,看漏、些少end的。在定位出这些错误之前,每一位同学都是不敢相信是这种小地方影响全局的。但是事实就是这样,这些由于粗心大意而产生的错误,并不是错误寻找环节可以被思考到的,所以需要大家可以更加细心的定位错误。            希望这一小部分心态不好的工程师,可以调整心态,遇到问题先不要着急说是外部原因,要认定绝大情况都是自己代码写错的,认真寻找bug。
      有了好的心态才可以快速定位错误,这是一种科学的方法,方法很简单,就像破案一样需要一个个线索顺藤摸瓜的寻找。但是希望大家学习此方法前,一定调整好自己的心态,不要没有耐心半途而废,这样是无法得到想要的结果的。下面我们就一起来学习明德扬的错误定位方法。

一、明德扬错误定位方法解析      在讲错误定位方法之前,先来思考一个问题:我们是如何确定代码中存在错误的?想必大家都知道答案,当输出结果与设计要求不一样的时候,说明代码中存在错误。思考了这个问题,我们可以得出错误定位的一个思路要点——从输出开始检查。(从错误现象中找出错误的信号)VGA没有显示图像 VS 行场同步信号错误      串口调试助手没有收到数据VS 代码的串送发送信号uart_tx有没有变低      串口收到的数据乱序VS din和din_vld是不是乱序

      这也就引出了第二个问题:从输出开始检查什么?当然是查找产生这个输出结果的原因。可以总结出错误定位的第二个思路要点——查这个输出的赋值语句是怎么写的。          总结了思路,我们就需要落实,要从赋值语句中查出错误,明德扬提出了一套定位错误的方法:      1.确定错误时刻:找到信号第一个错误时刻。关于错误时刻,一定要精确到某一时钟的上升沿,这是寻找错误的开始,如果连时刻都定位不准确,那么后续的一切都是无用功,所以找出错误时刻一定要找准时刻点;并且记住一定要寻找第一个错误时刻,只有将最开始的错误改正,后续的很多连锁错误可能就正确了。      2.找到代码:找到产生信号的代码。      3.排列信号:根据代码,将相关信号集中到一起,并按顺序排好(MODELSIM或者SIGNALTAP)。这里强调一下,一定要将相关信号按顺序排列好,如前面所说的,定位问题考验的是细心,越是奇怪的问题越是要细心,按顺序排列好,有助于逐个核对检查。            当你找不到问题所在时,试试严格把信号排列好。世界上没有笨人只有懒人,这个地方不犯懒,认认真真排列好一个个来,比耍小聪明挑着检验可以更加快速方便的找出错误点。      4.代入信号数据:将这一错误时刻的信号数据代入代码以确定错误原因。关于这里代入数据检验,希望大家一定要认真细心,我们既然知道了这里极有可能存在错误,还想当然的按照内心预想计算,是无法得到结果的。错误的出现大多是一时的粗心与手误,那么检验的时候一定要按照代码的要求来,才可以发现错误的地方。      5.三选一判断:a,此时的条件信号有错误;2.本ALWAYS代码有错误;3.本来的预想有错误。如果是条件有错误,按2~5的步骤重新定位错误的条件信号;如果是本ALWAYS代码,那就要修改代码;如果是原来的预想有错误,那就说明波形是正确的,那就修改原来的想法。做三选一判断,逼着自己一定要做出一个选择,这是让自己认真仔细思考问题。思考此时此刻输入信号是不是正常,思考自己的设计有没有缺陷,或者是自己想法对不对。      见过太多的例子,遇到这种困难问题时,需要思考,很多人就会打退堂鼓,就会放过,就会思维跟踪着去看其他信号,思维跳来跳去,但就是不解决问题。
      出理论上来说,本方法可以定位所有的问题,不管是测试文件问题、代码问题、模块结构问题、语法理解错误问题、甚至是您没学过的语法,使用本方法都能定位出来。所以希望大家遇到问题可以按照此方法静下心来定位错误,多进行几次尝试就会发现此方法的方便之处。                (注:每次修正错误后,需要重新编译执行以检查修改后的结果)

二、错误定位案例      下面通过对一个案例的详解,让大家能进一步弄清楚具体的方法:

modelsim.vhttps://thumbnail0.baidupcs.com/thumbnail/41037e9932c96d9334c8bbc76b4894d2?fid=2398848961-250528-372496666023519&time=1571302800&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-P9sWJw365q%2FaIwHRmI6COV0Gy18%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6719808328334660148&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      设计思路:本设计使用了一个计数器,用来计算250个时钟周期,当达到250个时钟时,就让dout为1。也就是说cnt是0~249循环变化的,当cnt==249时,dout等于1。

test_modelsim.vhttps://thumbnail0.baidupcs.com/thumbnail/234b25944b21c7e1bc710a0d07027d78?fid=2398848961-250528-17688803255465&time=1571302800&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-kOCBwgAhMGgVlmzuwqQric1nrBo%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6719822330808922457&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video

      测试文件思路:测试文件很简单,首先例化了设计文件,然后产生了时钟和复位信号。
      图一显示的结果没有满足250时钟周期输出1个周期高电平脉冲的功能要求,如何定位错误?
(一)定位错误信号dout      1.确定错误时刻:找到信号第一个错误时刻(精确到某一时钟的上升沿)https://thumbnail0.baidupcs.com/thumbnail/2c055eec44ef8a8ccf9098207c629be2?fid=2398848961-250528-145329196116149&time=1571302800&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-ANy%2Fp6hsA8vD2oBXitzeYjhxAVU%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6719794582275455283&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
图一modelsim.v仿真运行结果
      本设计是每250个时钟周期输出1个高电平脉冲。时钟是50M,周期是20ns,则预期是复位后第5000ns的时候dout输出一个高电平脉冲,但此时dout仍然为0。牢牢记住这个错误时刻。注意“5000ns”这个时刻,要精确到某时钟上升沿。
      2. 找到代码:找到产生信号的代码      本信号dout有错,所以要找这个信号的代码。      注意MOELSIM波形显示的信号,其全名为test_modelsim/dout,它与test_modelsim/uut/dout是不同的,不要认为相同,哪个信号错就看哪个(常见粗心点)。      test_modelsim/dout产生的代码如下。从第11行可知,test_modelsim/dout产生自test_modelsim/uut/dout。
https://thumbnail0.baidupcs.com/thumbnail/03e795de76431243514f5de26e83ac86?fid=2398848961-250528-221366970175501&time=1571302800&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-MaDWFJ5Cmbrd7u2Ud5pEzlTMstw%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6719824598036388132&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      3.排列信号:根据代码,将相关信号集中到一起,并按顺序排好      产生该信号的代码是.dout(dout)   ,也就是uut.dout一个信号给了dout。      所以拉信号uut.dout和dout,并按顺序排列好。
https://thumbnail0.baidupcs.com/thumbnail/d88a9348813f5ae3cc8ea8136fee23d7?fid=2398848961-250528-1114125971763033&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-hcgKh6tltl1RbHXPlMwmpdBciCY%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696647488534927796&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      4.代入数据:将这一错误时刻的数据代入代码以确定错误原因。      在5000ns的时候,可以看到uut.dout的值为0.

      5.三选一判断:a,此时的条件信号有错误;2.本ALWAYS代码有错误;3.本来的预想有错误。      此时uut.dout的值为0,导致dout的值为0,预期为1.很明显,dout的错误来自于uut.dout。所以转而定位uut.dout。

(二)定位错误信号uut.dout         接下来重复定位步骤来定位错误信号uut.dout。         1.确定错误时刻      继续定位test_modelsim/uut/dout,但要牢记之前的错误时刻,也就是复位后的5000ns,在此时刻,预期test_modelsim/uut/dout输出为1,但实际为0。
      2.找到代码      找到test_modelsim/uut/dout的代码,产生此信号的代码如下:https://thumbnail0.baidupcs.com/thumbnail/cea36448361214ceb076c2d92636463e?fid=2398848961-250528-474739330484632&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-Hv5h%2BJG5HIpwQGhSAfbHFwpUru8%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696198802175047015&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      3. 排列信号      从第31~38行代码可以看出,产生test_modelsim/uut/dout的相关信号包括:clk,rst_n,cnt,都拉到波形窗口来看。
https://thumbnail0.baidupcs.com/thumbnail/f44de63315637bf6c7dbf7be6bfcf4e6?fid=2398848961-250528-154928230650457&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-k3Jm32rqlO0Wht1rEtI7P7mCJDc%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696228067795187411&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      按照时钟、复位、条件信号和输出信号的顺序排列好。
https://thumbnail0.baidupcs.com/thumbnail/6c2bd85d67da4ae12ba3adb1856fa8d5?fid=2398848961-250528-1054297830866114&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-LCUd4x0HwhmvhQ2x3pPmQfdFhtc%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696241749705426477&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video

          注意:a.不要漏掉信号,一定要齐全,特别是时钟和复位;b.一定要按顺序排列好,越细心越好。(常见粗心点!!!最常用见!)         4. 代入数据      仍然要牢记之前我们认定的错误时刻:复位后约5000ns时,预期dout为1,实质为0。见图四。
图四250时钟周期上升沿时刻的uut波形
      我们光标放到此时刻。

      此时,rst_n为1,cnt==121,dout==1(上升沿前),并且查询到CNT_N==121。将此值代入代码,如下图。

https://thumbnail0.baidupcs.com/thumbnail/85964e569c8338c65d154b297b4a57e0?fid=2398848961-250528-923904969847683&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-Tp6stCIlcpXoleafeFvwj1rVs68%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696279224937038661&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      注意:一个数一个数代进入,然后看执行了哪一行代码。不要跳过看条件,例如不看32行的,直接看35行(常见粗心点)。      从上面代码可以看出,第32行和第35行都不满足,所以执行了37行,因此dout为0。      代码和波形保持一致,没有奇怪的现象!!!
      5. 三选一判断      三选一判断:a. 条件信号有问题;b. 本逻辑代码有问题;c. 预期有问题。逼着自己做三选一判断,肯定是其中一个或者多个有问题。      首先分析预期是否有问题:本设计的需求就是每隔250个时钟就输出一个高电平,这预期是没有错的。      其次分析是不是本逻辑代码有问题。本逻辑代码是看cnt算到249时就变1,否则为0。从逻辑上“大概”是没错。      最后再看条件,此时cnt为121,不是预期的249。这就是问题了,经过了这么多时间,cnt怎么才数到121呢?所以判断:条件信号cnt有问题。      预期此时cnt为249,实际为121,所以cnt有问题,转成定位uut.cnt。
(三)定位错误信号uut.cnt          1.确定错误时刻      如前所述,预期这个时候uut.cnt的值为249,实际为121,我们来定位这个错误。      按设计思路uut.cnt是0~249递增的。将cnt信号拉到波形窗口,看这个信号是不是递增的。发现第一个错误时刻如下图,当其为127时,预期下一个时钟为128,结果为0,也预期不符合,如下图。注意错误时刻改为2949ns。

      2.找到代码      找到test_modelsim/uut/cnt的代码,产生此信号的代码如下:
https://thumbnail0.baidupcs.com/thumbnail/2dd9460341f5c2e0b3b736218969ae9a?fid=2398848961-250528-254454216429001&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-tHM5YmaPVi2WCp5KhHDRfx%2FJBAY%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696328826703110871&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video
      3. 排列信号      从第19~29行代码可以看出,产生test_modelsim/uut/cnt的相关信号包括:clk,rst_n,cnt,都拉到波形窗口来看。全部都要拉进来(常见粗心点!)      按照时钟、复位、条件信号和输出信号的顺序排列好。(常见粗心点!)

      4. 代入数据      我们光标放到此错误时刻。

      此时,rst_n为1,cnt==127(上升沿前),并且查询到CNT_N==250。将此值代入代码,如下图。


      从上面代码可以看出,第20行和第23行都不满足,所以执行了27行,因此cnt加1。加1预期是128,但结果是0。      代码和波形好像没有保持一致,很奇怪的现象?!!!
      5. 三选一判断      三选一判断:a. 条件信号有问题;b. 本逻辑代码有问题;c. 预期有问题。逼着自己做三选一判断,肯定是其中一个或者多个有问题。      首先分析预期是否有问题:本设计的预期就是cnt从0递增到249,用来计数250个时钟,所以预期应该是正确的。      其次分析是不是本逻辑代码有问题。本逻辑代码的想法是:当cnt数到249时清0,否则就加1。从逻辑上“大概”是没错。      最后再看条件,此时条件cnt为127,也是预期的值。      很奇怪。但不要觉得很奇怪,一定是我们的问题,绝不是软件有问题。
      虽然很奇怪,但我们可以确定,一定是执行27行代码时出现了问题,也就是一定是cnt+1出现了问题。在什么情况下,cnt+1会变成0而不是128呢?如果稍微有经验的工程师,就大概知道是信号位宽问题了。      这个时候,我们要把128转成二进制来看,128=8’b1000_0000。我们展开cnt这个信号,发现cnt没有cnt,也就是无法得到8’b1000_0000。      没有cnt,就说明定义有问题,检查定义位宽。


      修改成:https://thumbnail0.baidupcs.com/thumbnail/c8365f1bfcaef26a212ab7fb134cbc79?fid=2398848961-250528-689447127453661&time=1571216400&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-ooNPVIfa2Yy%2Fxez99Db1IwzLJWg%3D&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=6696448748840176951&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video

      至此,问题解决。
      上面就是定位错误的完整步骤,本案例其实一共定位了三个信号,由最终的输出信号dout,再定位到uut.dout,最后再定位到uut.cnt。无论哪个信号的定位,都是遵循了明德扬的定位错误方法。            常有人跟我说,这方法很简单我已经完全掌握了,但有问题又定位不出来,这种情况就是眼高手低惹的祸,说白了这些同学不愿意把定位工作做细。例如,明明都已经定位不出问题了,还不愿意把信号排列好,一个一个代值进去看;再或者代值的时候想当然,总是按照思路计算,不按照写的代码一点点代入。            有同学觉得定位错误的能力不是一定需要掌握的,认为有问题了应该是老师指出来,而不是自己定位出来,所谓老师就是传道授业“解惑”也。遇到问题就要求老师直接告诉我哪里错了,我就专心学知识专心写代码。如果老师真这么服务了,这个学员的职业生涯也注定长远不了。老师可以帮助您走一段路,但是不可能这样携手一直走下去,总有一点同学们需要进入工作岗位,需要自己设计代码,走上工程师的道路,如果没有掌握定位错误的能力,这个时候谁又能来帮你“解惑”呢?         工程师最重要的是两个能力:设计能力和定位问题能力。有这两个能力,理论上什么项目搞不定?这两个能力如果要分一个高低,你们说哪个能力更重要?很多人都认为是设计能力,而我却认为定位问题能力更重要,想一想职业发展就明白了。设计能力厉害的就发展成技术专家,定位问题并且解决问题厉害的,就发展成管理层,研发工作的日常,就是定位问题和解决问题。优秀的研发管理,往往会协助员工制定清晰的定位思路和步骤,综合运用排除法、假设法、替换法等,逐步把定位范围缩小,直至找到最后的那一个问题点。因此,在可以学习可以进步的阶段,希望每一位同学都可以学会定位错误的方法,成为更加优秀的工程师。

Bell 发表于 2021-1-14 22:15:33

很实用,学习了
页: [1]
查看完整版本: 【技巧分享】FPGA定位错误的方法