FPGA设计中对输入信号的处理

http://tech.ic5.cn/News_1228.html

我们将问题分解为2部分,来自同步时钟域信号的处理和来自异步时钟域信号的处理。前者要简单许多,所以先讨论前者,再讨论后者。

    

      1.同步时钟域信号的处理

      一般来说,在全同步设计中,如果信号来自同一时钟域,各模块的输入不需要寄存。只要满足建立时间,保持时间的约束,可以保证在时钟上升沿到来时,输入信号已经稳定,可以采样得到正确的值。但是如果模块需要使用输入信号的跳变沿(比如帧同步信号),千万不要直接这样哦。

      always @ (posedge inputs)

            begin

                     ...

            end

      因为这个时钟inputs很有问题。如果begin ... end语句段涉及到多个D触发器,你无法保证这些触发器时钟输入的跳变沿到达的时刻处于同一时刻(准确的说是相差在一个很小的可接受的范围)。因此,如果写出这样的语句,EDA工具多半会报clock skew > data delay,造成建立/保持时间的冲突。本人曾经也写出过这样的语句,当时是为了做分频,受大二学的数字电路的影响,直接拿计数器的输出做了后面模块的时钟。当初用的开发工具是max+plusII,编译也通过了,烧到板子上跑倒也能跑起来(估计是因为时钟频率较低, 6M :lol:),但后来拿到QuartusII中编译就报clock skew > data delay。大家可能会说分频电路很常见的啊,分频输出该怎么用呢。我一直用的方法是采用边沿检测电路,用HDL语言描述大概是这样:

      always @ (posedge Clk)        

      begin

            inputs_reg <= inputs;

            if (inputs_reg == 1'b0 && inputs == 1'b1)

            begin

                 ...

            end

            ...

      end

      这是上跳沿检测的电路,下跳沿电路大家依此推。

 

      2.异步时钟域信号的处理

      这个问题也得分单一信号和总线信号来讨论。

 

        2.1单一信号(如控制信号)的处理

      如果这个输入信号来自异步时钟域(比如FPGA芯片外部的输入),一般采用同步器进行同步。最基本的结构是两个紧密相连的触发器,第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少(注意是减少)亚稳态带来的影响。这种最基本的结构叫做电平同步器。

 

      如果我们需要用跳变沿而不是电平又该怎样处理呢,还记得1里面讲的边沿检测电路么?在电平同步器之后再加一级触发器,用第二级触发器的输出和第三级触发器的输出来进行操作。这种结构叫做边沿同步器。

      always @ (posedge Clk)       

      begin

            inputs_reg1 <= inputs;

            inputs_reg2 <= inputs_reg1;

            inputs_reg3 <= inputs_reg2;

            if (inputs_reg2 == 1'b1 && inputs_reg3 == 1'b0)

            begin

                 ...

            end

            ...

      end

 

      以上两种同步器在慢时钟域信号同步入快时钟域时工作的很好,但是反过来的话,可能就工作不正常了。举一个很简单的例子,如果被同步的信号脉冲只有一个快时钟周期宽,且位于慢时钟的两个相邻跳变沿之间,那么是采不到的。这时就需要采用脉冲同步器。这种同步器也是由3个触发器组成,同时需要对发送信号做一些处理,具体结构大家可以在网上搜。

 

        2.2总线信号的处理

      如果简单的对异步时钟域过来的一组信号分别用同步器的话,那么对这一组信号整体而言,亚稳态出现的几率将大大上升。基于这一观点,对于总线信号的处理可以有两种方式。

    

      如果这组信号只是顺序变化的话(存储器的地址),可以将其转换为格雷码后再发送,由于格雷码相邻码字只相差一个比特,上面说的同步器可以很好的发挥作用。

 

      但是如果信号的变化是随机的(存储器的数据),这种方法便失效了,这时可以采用握手的方式或者采用FIFODPRAM进行缓存。RAM缓存的方式在突发数据传输中优势比较明显,现在高档一点的FPGA中都有不少的BlockRAM资源,且支持配置为DPRAMFIFO,这种处理方法在通信电路中非常常用。

 

 

http://blog.163.com/sunhuifxd@126/blog/static/5585944320101016993467/

1.输入信号为什么要寄存

    一般来说,在全同步设计中,如果信号来自同一时钟域,各模块的输入不需要寄存。只要满足建立时间和保持时间的约束,可以保证在时钟上升沿到来时,输入信号已经稳定,可以采样得到正确的值,但是如果模块需要使用输入信号的跳变沿(比如帧同步信号),千万不要直接这样用:

always@(posedge inputs)

begin

        ...

end

2.所有信号都需要寄存两拍吗

    如果这个输入信号来自异步时钟域(比如FPGA芯片外部的输入),必须寄存两拍,第一拍将输入信号同步化,同步化的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少亚稳态带来的影响。

    如果这个输入信号来自同一时钟域且需要用到跳变沿,需要寄存一拍,否则时序报告多半会报clock skew > data delay,造成建立/保持时间的冲突。

总而言之,五条原则:

  1. 全局时钟的跳变沿最可靠
  2. 来自异步时钟域的输入需要寄存一次以同步化,再寄存一次以减少亚稳态带来的影响
  3. 不需要用到跳变沿的来自同一时钟域的输入,没有必要对信号进行寄存
  4. 需要用到跳变沿的来自同一时钟域的输入,寄存一次即可
  5. 需要用到跳变沿的来自不同时钟域的输入,需要用到3个触发器,前两个用来同步,第3个触发器的输出和第2个的输出经过逻辑门来判断跳变沿

给出一个verilog模板“

always@(posedge clk)  //不对输入信号进行寄存

begin

    if(inputs) begin

    ....

    end

end

always @(posedge clk) // 对输入信号寄存一拍

begin

    inputs_reg <= inputs;

    if(inputs_reg  == 1'b0 && inputs == 1'b1)

    begin

    ....

    end

end

always@(posedge clk) //对输入信号寄存三拍

begin

    inputs_reg1 <= inputs;

    inputs_reg2 <= inputs_reg1;

    inputs_reg3 <= inputs_reg2;

    if(inputs_reg2 == 1'b1 && inputs_reg3 == 1'b0) begin

    ....

    end

end

你可能感兴趣的:(FPGA)