实际项目中,设计往往有许多功能模块,设计人员会各自负责一个或几个功能模块。
如何有效调试代码功能呢?
下面就结合我最近的设计阶段,所经历的问题及解决问题的感悟。
只是我自己的一些经验心得,一家之言,姑且听之。
第一阶段
站在设计人员的角度来看。
最起初,只有一个功能模块。
那么,设计人员自己先搭建一个简单的tb,验证代码的基础功能点。
由于时间有限,设计人员无法建立完备的验证module,往往只会验证最关键的功能。
之后,不同人员的设计模块联合调试,就会出现各种问题,这个时候,继续debug,将遇到的问题一一解决。
第二阶段
过了一段时间,各个设计人员陆续完成代码。
当各种模块结合到一起进行调试,这个时候,因为各个模块有不同的情况,这就造成实际的结构信号的行为与之前的预计存在偏差,甚至是非常大的偏差。
比如,设计人员最开始以为输入是连续的,但是实际可能输入是断断续续的,甚至是间隔很频繁,比如每2个周期就会暂停一次。这个时候,原始的设计就会出现各种问题,造成功能错误。
无疑,这个时候就需要在结合了多个模块的环境下调试,直到解决所有问题。
第三阶段
那么,再想想,当又过去一段时间,可能是项目的实际情况出现变化,原来调试完成的设计不再适合,需要做调整,比如,原来使用的IP延迟是1周期,现在修改成5周期,必然造成原来的时序紊乱。
这个时候,如何调试?
直接在多个模块的环境中调试?
不是不行,但是不好。
因为,多个模块的调试环境本来就很极端,这个时候,因为波形断断续续,难以准确判断并修改逻辑代码。
这个时候,需要重新使用最开始建立的简单tb去调试最新修改的代码,直到调试成功,那么至少保证了当前代码能够跑通常见case,并且能够肯定当前的代码不存在简单的逻辑错误。(因为简单的逻辑错误,会在这个简单的tb环境下暴露出来。结合第一阶段的调试经验,很容易找到问题,并排除问题。)
然后,再在结合了多个模块的环境中调试,这个时候,因为已经排除了大部分的错误,减少了干扰,才能更快完成debug。
这里,再介绍一个巧妙的语法。
当添加新的模块,而当前的环境还没有实际可用的激励时,还是需要设计人员自己添加tb去仿真。
那么,为了仿真当前的文件,就单独建立一套仿真文件?
不用!
采用bind语句,将新编写的module绑定到已有的模块,可以直接利用已有的编译仿真环境。
比如,已有了
module A();
…
endmodule
新编写了
module B();
…
endmodule
那么再写一个module,内部例化B,给B加激励。
module B_tb();
…
B b();
…
endmodule
bind A B_tb b_tb();//注意,这一行在endmodule之外,功能是将B_tb例化的b_tb()绑定在A中。功能就类似在A中例化了一个B_tb例化的b_tb()。
这样就可以直接利用原来的testbench去直接编译仿真。
总结
本文所介绍的一些实际情况,其实就是一个化繁为简的思想。
将一个复杂的问题,拆分成几个简单的问题,逐个击破,最后就能顺利解决问题。