Verilog抠细节系列- -1 (小小的reset)

参加工作也很久了,一直都没有好好总结一下技术方面的所得。
想想从今后开始就在这里把自己的所学到的归纳一下吧。
这里只是本人自己的设计过程中的所得,对于一些高手来讲只是小儿科
所以,你认为很幼稚的,请就此忽略。假若你认为看了后学到了些东西,那就回帖顶顶给个支持吧。
这里声明一下,虽然是幼稚的文字,但也是一个个字碼出来的,所以也算是个版权吧。
万一谁想转载或引用,麻烦请先告知一声,谢谢!

个人习惯,今后所有默认触发以下降沿为准 -- negedge

鲁棒(Robutness)
“鲁棒”这个词,就是让设计更安全,但这个翻译我一直认为翻译的挺恶心。所以,今后就用“更R”来代替吧。
怎么样让设计更R呢?
举个例子。在设计中会用到许多计数器,是计数器就要reset吧。
那你会怎么样让它reset呢?(reset也是你自己产生的,非外部进入)
大家看看下面这个reset有没有问题呢:

clk:  __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
reset:   """""""""|___|""""""""""""""""""""""""""""""""""""
cnt: ========x0__x1__x2__x3.............................

我开始设计的时候就是这么做的,而且很长时间都是这么做的,没出过问题。
下降沿来1个clk的reset,设0,下一个下降沿开始计数,这不就行了么,一点问题也没有。
的确,很久都没有出过问题,但有一次,发现有个计数器总是少计一个数,苦恼许久。
这帖暂时先写到这,各位也可以一起想想会出现什么问题吧。
高手知道的请不要那么快回答,让和我们一样曾经是初学者的朋友先想想。

[ 本帖最后由 leolf 于 2008-7-21 15:18 编辑 ]
我也来说两句 查看全部回复

最新回复

  • wanynal (2008-7-20 16:23:15)

    我是新手
    是来这里学习的
    希望以后能够得到楼主的帮助

    像您这样能够把自己的经验无私的传授给我们这些新手
    我衷心地感谢您
    希望您以后能够坚持多发这种帖子

    新手 还没有进行过什么实质性的编程工作
    因此我只能是猜一猜了
    个人感觉应该是延时的问题
    估计是由于延时的原因
    使reset的为0的时间与cnt要进行计数的时间有冲突
    因此造成了少计一个数
  • leolf (2008-7-20 17:59:00)

    继续呀,有个人回复了,说得很对。就是reset信号的结束时间问题。
    因为同是用下降沿触发,所以reset信号也同样用下降沿来结束的话,就会有可能因为内部延迟和下一个计数产生冲突。
    所以,计数结果很可能少一个。例如:
    clk:  __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
    reset:   """"""""""|___|""""""""""""""""""""""""""""""""""""
    cnt: ========x0__x0__x1__x2.............................

    就是这样,设计越多,你就会越来越感觉芯片内部的信号的延迟是那么的可怕。
    如果你追求"更R“的设计,那首先你要打破芯片内部的信号是理想的方波这一个幻想!!
    2. 解决
    好的,问题找到了,那我们怎么才能处理好这个reset呢?
    当然,最简单的,产生reset的时候,用上一个上升沿,例如底下的波形:

    clk:  __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
    reset:   """"""|___|""""""""""""""""""""""""""""""""""""
    cnt: ========x0__x1__x2__x3.............................

    当然,对于上一个问题,这应该可以满足了。
    但是,我们再想想计数器之后的器件。因为判断计数器数值的比较器也是用下降沿的,那这样的设计就会产生一个问题。

    clk:  __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
    reset:   """"""""""""|___|""""""""""""""""""""""""""""""""""""
    cnt: ........x8__x9_x0000_x1__x2__x3.............................
    ?eq9: .....x7__x8___x0__x0__x1...................................           (where is the 9?)

    因为我们的判断器也是用下降沿来触发的(目标数是9然后reset)。所以,当计数器来到9的时候,比较器还没来得及将9这个数拿走,
    就已经被复位了。哎呀呀,这不就出错了么。对了!!将比较器的触发沿也改成下降沿不就行了么!!
    各位,真的可以将比较器的触发沿也改成下降沿来解决这个问题么??我们有没有更好的方法来解决呢?
    先去吃饭呀。

    [ 本帖最后由 leolf 于 2008-7-20 18:09 编辑 ]
  • jiangwei (2008-7-20 21:03:20)

    我就是复位还是使用同步好些哈,可能会解决问题
  • kevin249 (2008-7-20 21:41:32)

    我觉得判断还是来组合逻辑的吧
    然后再用同步采
  • leolf (2008-7-20 23:35:42)

    继续上面的话题。有的朋友看了上面那个问题,可能会说,这个比较为什么要用一个register锁存。
    这延迟一个clk,多麻烦呀。计数器结果一出来我就直接比较不就好了么?比如像下面这样

    clk: __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
    reset:  """"""""""""|___|""""""""""""""""""""""""""""""""""""
    cnt: .......x8__x9_x0000_x1__x2__x3.............................
    ?eq9: ....?9__=9__?9__?9__?9...................................

    结果一出来就能比较到了,方便又快速。用register先锁存一下是否多此一举呢?
    答案是否定的!因为计数器是由多个级联的器件产生,“多个器件”这个词语会不会给了你一些提示呢?
    是的,因为最终结果是由多个器件的值产生的。所以,这几个器件都有它本身的反应时间,而且反应时间肯定是
    不一样的,这就造成了在最后有稳定的结果之前,计数器会产生意想不到的数。
    假设有一个4位的简单计数器,我们把时钟下降沿计数器跳变的那一刻的延迟放大来看看:

    clk: """"""""""""""""""""|________|""""""""""""""|_________________
    cnt1: ____________|""""""""""""""""""""""""""""|___________________
    cnt2:"""""""""""""""""""""""""""""""""""""""""""""""""""|__________________
    cnt3:"""""""""""""""""""""""""""""""""""""""""""""""""""""|__________________
    cnt4:"""""""""""""""""""""""""""""""""""""""""""""""""""""""|__________________
    cntresult: 14_14_!4_14_15_15_15_15_15141280000000000000   

    这只是一个4位计数器。但在最后的跳变中,在到达稳定的0之前,它会产生14,12,8,这几个数字。
    这个,和一般的计算机语言编程不一样吧。
    套用周星驰一句感慨“有没有搞……错!”变0就变0嘛,竟然会自己跑那么多数出来!!
    所以,直接使用计数器跳变边沿的结果来比较是十分危险的!!因为这会产生很多的"glitch"!“glitch"就是中文中著名的“毛刺”!
    在电路设计中,“毛刺”可以定义为:贬义的,不受欢迎的,不请自来的,看到要绕着走的,总之就是一个不好的东西。
    所以,我们要在它稳定之后再使用这个结果。这也就是为什么我们要先锁存一下,以确定我们取的是稳定值。
    上面有朋友说先比较后锁存,其实也是一样的。没有太大分别。

    [ 本帖最后由 leolf 于 2008-7-20 23:50 编辑 ]
  • leolf (2008-7-21 00:53:18)

    说了那么久,该回到reset了。
    在这里,怎么样能产生一个安全的reset呢?其实之前所说的上升沿reset是可以拿来参考的。
    但是,使用和整个系统不同的触发边沿是会造成混乱的。所以,假如我们能把这个混乱控制在一个小范围之内。
    就可以既安全,又不影响其它人。
    所以,我们可以产生一个半周期的reset,那样就能满足我们的应用。波形如下:

    clk:  __|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|__|""|
    reset:   """""""""""""""|__|""""""""""""""""""""""""""""""""""""
    cnt: ........x8__x9__x0___x1__x2__x3.............................
    ?eq9: .....x7__x8__=9___x0__x1...................................           (9 is safety!)

    怎么样,看了这个波形,是不是觉得很容易呀。一切事物在我们知道之后都会觉得很容易。
    比如说牛顿的三大定律,可是在牛顿之前的数千年里,为什么没人总结出来呢?人类的思维发展真是有些奇怪……
    下面简单写下verilog语句。
    假设triger是一个下降沿产生的1个clk负pulse

    always @(negedge clk)
       rstb1 <= triger;

    always @(posedge clk)
       rstb2 <= rstb1;

    wire reset = rstb1 | ~rstb2;

    clk:  __|""|__|""|__|""|__|""|__|""|__|""|
    triger: """""""""|____|"""""""""""""""""
      rstb1: """""""""|____|""""""""""""""""
    ~rstb2: ________|""""""|________
    reset:  """"""""""|__|""""""""""""""""""""""

    这样子,一个相对来说安全的reset就诞生了。
    虽然硬件上来说会多一个register。
    但一个regsiter就能够让你周末放心睡大觉。不用担心被叫回公司debug!
    那是多么值得呀!!
    好了,今天算是圆满了,睡觉喽!!

    [ 本帖最后由 leolf 于 2008-7-21 00:58 编辑 ]
  • wanynal (2008-7-21 08:58:26)

    学习了 谢谢楼主指教
  • ishock (2008-7-21 09:30:52)

    看完后终于明白,这里的reset非通常意义上的芯片复位reset,而只是让计数器复位的reset,名字取得有些让人迷惑。但是,让计数器复位有很多种方法,楼主说的方法,是首先给出了一个很强的限制环境,然后想出来的一种方法。建议楼主举出一个问题的例子,然后大家讨论可以用什么方法来解决。个人觉得楼主的一些想法有问题,但不是就具体的问题,也不能说肯定是不对的,因为在不同的领域为了解决不同的问题是需要用不同的方法。
    在FPGA领域,最好还是用上升沿做设计,因为器件内部的寄存器本来就是上升沿触发的。用下降沿做设计,外部时钟进去后要先经过一个反相器才驱动内部寄存器。
  • leolf (2008-7-21 11:26:48)

    QUOTE:

    原帖由 ishock 于 2008-7-21 09:30 发表
    看完后终于明白,这里的reset非通常意义上的芯片复位reset,而只是让计数器复位的reset,名字取得有些让人迷惑。但是,让计数器复位有很多种方法,楼主说的方法,是首先给出了一个很强的限制环境,然后想出来的一种方 ...
    这个例子的确很有局限性,但并不是只给计数器复位的。
    因为一些类似的设计也会遇到这种问题。
    总得来说,我写这些只是为了提醒大家,在一些细节方面要多加注意。大家可以举一反三。
  • tenggui (2008-7-21 11:30:08)

    长见识了
    多谢
  • x512775199 (2008-7-21 12:15:13)

    嗯 觉得楼主写的很好 期待后续,,,
  • caiyuxiang (2008-7-21 14:40:28)

    继续呀,我还等着看呢,建设性的意见我提不出,只能在旁边借鉴了。。
  • zou132 (2008-7-21 15:35:59)

    我个人认为不会出现楼主所说的情况(由于延时的原因有个计数器总是少计一个数),应该是其它的设计错误所造成的。
    时钟走全局网络所造成的时延可以忽略不计,
  • starguoxia (2008-7-21 15:48:45)

    QUOTE:

    原帖由 zou132 于 2008-7-21 15:35 发表
    我个人认为不会出现楼主所说的情况(由于延时的原因有个计数器总是少计一个数),应该是其它的设计错误所造成的。
    时钟走全局网络所造成的时延可以忽略不计,
    还是有可能出现楼主说的情况的吧?时延不一定是时钟引起的吧,门电路自己本事也有时延的。
    不知道说的对不对
  • yel27 (2008-7-21 16:29:23)

    学习了
    新手
  • wu.weihai (2008-7-21 16:44:29)

    不错的帖子 ,楼主何不整理一下
  • 店小二 (2008-7-21 16:47:23)

    好帖子,赞一个!
  • hover_edacn (2008-7-21 17:11:34)

    很佩服lz的研究精神。不过计数器很少采用异步复位逻辑的,lz说了这么一大堆,其实用一个同步复位就能解决。
    很多公司对异步复位的使用都有严格限制,而这种由内部逻辑产生的异步复位危害更大。
  • qingchuyu (2008-7-21 17:24:54)

    rst is synchronous ,it must meet setup and hold timing; rst is asynchronous it must meet removal and recovery timing;
    if these timing is meeting
    there will not be race condition like that.
  • leolf (2008-7-21 17:45:06)

    QUOTE:

    原帖由 qingchuyu 于 2008-7-21 17:24 发表
    rst is synchronous ,it must meet setup and hold timing; rst is asynchronous it must meet removal and recovery timing;
    if these timing is meeting
    there will not be race condition like that.
    The enviroment that our product apply to may meet some extreme enviroment such as unexpect voltage drop or bad process.
    And in IC design, the asynchronous "rst" also need EDA to help to place and route.
    In large scale design, the EDA is not 100% creditable.
    That's why we need to think a little more.