热度 1| |
很多人觉得写verilog是一件很简单的事情,事实上也确实不难,语法上比C还少,就是always和assign倒来倒去,不是reg就是wire。如果对性能和面积要求不大的话,随便写写easy的很。
可是如果你有点追求,打算优化一半的面积,同时timing也要meet,那就有难度了。
简单从方法上讲讲,考虑点有两个地方:
(1)不是reg就是wire,可是究竟用reg还是用wire?
(2)timing是看不见的,写完综合之后才知道。
reg跟wire其实是等价的,表达同一个逻辑,定义成wire,那么就会当T实现,定义成reg就下1T实现。那么究竟用reg还是用wire呢?有人写代码不太注意这些,reg随便用,导致的结果就是设计延时很大,面积也很大。
对于timing无法meet的path,组合逻辑太大,必须用寄存器切开,这个时候寄存器的作用是使得timing meet,跑上一个较高的频率,从而提高性能。
但是对于timing本来就可以meet的path,无端的添加一个reg,不仅浪费了面积,还将整个计算过程多延了1T,性能会下降。
可是在写代码的时候,你又怎么知道timing能不能meet呢?又怎么知道组合逻辑是不是太大,需要切开呢?
你不知道,必须等到综合。而综合的时候,通常你的代码已经写完了。
综合的结果可能好,可能坏。timing可能meet,也可能不meet。
但是你不能因为不知道timing是否meet就不写代码。
所以,写代码不是一次性工作,写完了要check timing,然后修改关键路径的相关逻辑。
但是寄存器究竟使用多少,在写第一版design的时候,仍然是很重要的。
少用寄存器,综合工具会因为timing不meet,给你报path,你还有改进的机会。
多用了寄存器,综合工具可什么都不会告诉你。
所以最优设计的原则是,第一版design要尽可能少用寄存器。
多用一个reg,就多一个always赋值,多几行代码。代码一多,bug就多,trace代码得多一个双击,看代码的人多一次痛苦。
对于一个设计来说,设计规范定下来后,不考虑逻辑复用的话,组合逻辑需要多少基本就确定了,这个是确定值,就算你写得烂点,综合器上来给你一通优化,其实也差不离。但是你插了一级reg之后,将一条完整的组合逻辑一切为二,那么综合器就得分两段分别优化,最后的综合结果可能就不会那么好。
同样一个功能,不同的人来写,模块面积可以完全不一样,而这其中,设计者最需要控制的就是寄存器,因为这体现在每一行的代码中。代码中使用组合逻辑通常不需要理由,但是用寄存器是需要理由的。举个栗子,你做2个32bit数异或,异或门这种组合逻辑自然少不了,没有这个你完成不了这个运算,但是有必要使用寄存器regout计算结果吗?你需要仔细想想。
组合逻辑负责运算,但是寄存器只是负责存储,存储并不完成任何功能,为什么需要存储,你需要思考。比如为了将来使用,为了凑时序,比如防止组合逻辑loop。不管怎么样,你不能没有理由的使用寄存器。
timing坑爹的地方在于,你永远不知道你的代码能不能meet timing需求,必须等综合之后。
举个栗子,你要写一段代码给1200个1bit数相加,满足时钟400M。这1200个数肯定不可能1T之内完成加法,需要多T完成,这就涉及到path的切割。比如分成5级或者10级。问题在于你并不知道究竟是5级还是10级,或者多少级。换句话说,你不知道2.5ns能塞下多少组合逻辑运算。
那些声称自己可以看看RTL代码,来评估自己的timing是否meet,并凭感觉插入寄存器的人都是神棍。
你需要做个实验,写个小加法器,然后综合一下,确定400M的时钟可以允许1T之内做多少bit加法。
reg的一个坑爹的地方在于,它很多时候是为了解决时序不meet的问题,不得不插入的,但是reg带来的1T延时,有时候可能是灾难性的,因为假如你的逻辑已经是严丝密合的,那么突然间,某条path不得不插入一个寄存器,很有可能其他的path同步都会存在问题,你不得不改写很多地方。
这归因于reg的双重属性。它既可以解决timing不meet这种问题,也会带来延时这种搞硬件设计的人最不愿看到的副作用。因为代码写多了,有时候你自己都搞不清,哪个信号在哪1T,有时候需要画波形图辅助记忆。
对组合逻辑过大的path使用寄存器进行切割,听起来是一件很简单的事情。
但是要知道,切割path需要引入很多寄存器,这会增加gatecount。理想情况下的path切割,需要切在path最细的地方,这样插入的寄存器最少,增加的gatecount最小。
但是切割path还有另一个标准,最好是刚好切在path的中间点,这样一条path均匀分割成两半,timing最好。
流水线的频率取决于最慢的一拍,所以最好每拍的组合逻辑一般长。
悲剧的是,以上两个标准通常难以同时实现。
在组合逻辑的中间点切割path,或许timing最好,但是可能引入的寄存器非常多,gatecount很大。
要想切割在path最细的地方,又很有可能不是中间点,导致一半松一半紧,甚至不得不对紧的一半再切一次。
这需要权衡,准确的说,需要
试。