• Latch and STA


      在我刚接触FPGA的时候,我的师傅就告诉我不要写latch,并且告诉我latch来源于分支不完全的组合条件逻辑.不要用latch的原因我不太明白,网上的说法主要有两个:

    1. 对毛刺不敏感,会产生毛刺并且传递毛刺.
    2. 不利于STA.

      第一个很好理解,但是第二个并不明白.在网络上搜索答案的时候虽然没有马上找到答案,却收获了一些其他的知识.

      

      Latch设计之Time borrowing

      虽然在FPGA设计中要尽量避免latch,但是在ASIC设计中却应用得很普遍,原因在于latch比FF消耗更少的资源,所以能带来更小的面积.

        这个主要是因为FF可以理解为增强功能的Latch,它们都有存储的功能,但是latch是电平触发,而FF只有在Clock有效沿才触发,多了一个Clock控制,自然会消耗更多的门电路资源.

      除了面积的优势,Latch还能带来更快的速度,这就涉及到我们说的time borrowing.所谓time borrowing,其本质是因为锁存器是电平触发,即使在有效沿没有准备好,只要能在有效电平准备好,同样可以收到数据.所以它的时序要求比FF要宽泛.例如,可以在电路存在时延最长为8ns的信号通路的情况下,满足5ns的时钟周期.

      那么borrow了,还得还,怎么还,请看下图.

      为了便于分析,我们设FF和Latch本身的器件传播延迟Tpd为0.这并不会影响我们的分析结果.

      我们设时钟周期为10ns,也就是100Mhz,这在FPGA上是非常常见的频率,设F1到F2的Tcomb为12ns,F2到F3的Tcomb为5ns,那么很显然,我们的时序存在不满足的路径,因为12ns超过了10ns,而后面的路径comb延迟仅为5ns,可以认为有较大的余量,但是在一般的FPGA设计中,这个余量似乎浪费掉了.所以假如我们整体地看F1到F3,comb延迟是17ns,符合20ns的要求.也就是说,假如我们能把后面5ns路径的余量挪到前面,就可以满足STA的要求.

      那么此时我们再次考虑触发器和锁存器的特性,触发器要求数据其实主要是不能太晚,必须在时钟有效沿的建立时间之前到达,而锁存器不一样,锁存器在SR输入有效电平时会变成透明状态,因此假如我们把FF换成Latch,如下图

      假设这个Latch的有效电平正好在时钟的有效沿,同样,这个设定也是不影响核心分析的.

      那么当F1的输出错过有效时钟沿到达L2时,虽然错过了有效时钟沿,但是Latch并不是靠时钟沿接收数据的,而是有效电平,有效电平长达半个时钟周期,那么这个时候latch就可以接受到数据,并且在后半个周期将数据锁存,输出给下一级F3,因为L2到F3时间仅有5ns,所以F3必定能成功在有效时钟沿建立时间之前接收到数据.成功地满足了10ns的周期要求,这也就是我们说的borrowing time,显然,假如L2到F3同样超时,那就要另外再考虑了,这个时候再用Latch也不一定能满足要求.

      在上面的例子中,F1到F3要添加多周期时钟约束?多周期也不一定能解决问题,因为它只是时序变得宽松,但是还是有要求的,如果有效沿过去了,信号还没到,那锁存器也不好使。

      在这里是两个.这里提一下,Vivado的时序分析 都是以一个周期为准.什么意思呢,就是说认为源信号只有一个周期,接收端要在一个周期内接受到这个信号.除了在上面例子的情况下,多周期时钟约束主要出现在慢时钟域信号到快时钟域上,其本质就是发送端给的源信号不止一个周期,对时序要求其实比较松. 

      同步设计综合出锁存器?

     1 module Decode(
     2 input A,
     3 input B,
     4 input C,
     5 output reg[31:0] edata,
     6 output reg[31:0] eCapData,
     7 input bCap,
     8 output reg CapSt,
     9 input n_rst,
    10 input [31:0] rstVal,
    11 input clk);
    12  
    13 reg[1:0] state;
    14  
    15 always@(posedge clk or negedge n_rst)
    16 begin
    17     if(!n_rst)
    18     begin
    19         edata <= rstVal;
    20         state <= {A,C};
    21     end
    22     else
    23     begin
    24         state <= {A,B};
    25         edata <= {31'd0,A};
    26     *****此处省略一万*****
    27     end

      所谓同步设计出现锁存器,在于复位时信号给错,我们先看触发器在FPGA中怎么实现的

       这个就是实现触发器的东西,锁存器也可以用这个实现 

      ce是使能,一般用在触发器赋值时的条件判断语句中

      sr是锁存输入,有效时变成透明锁存器,无效时锁存数据,配置为FF时作为复位端口

    触发器

    异步复位 FDCE 复位后Q输出0
    FDPE 复位后Q输出1
    同步复位 FDRE 复位后Q输出0
    FDSE 复位后Q输出1

    锁存器

    异步复位 LDCE 复位后Q输出0
    LDPE 复位后Q输出1

      FF复位的输入应该是来自接地或者参考电平,所以把另外的信号作为复位肯定不会综合得到FF.根本就狗屁不通不可能实现的东西.

      另外驳斥一个所谓Latch比FF省资源的说法,至少在xilinx7系的板子上实现之后,都可以发现是同一个资源实现FF或者Latch的.而纯用门电路实现FF和Latch,肯定是Latch省资源.

      这里再补充一下所谓异步复位同步释放,其本质就是一个异步复位信号两级同步避免亚稳态的设计,说的玄乎.唯一有一点值得记录的就是,复位信号都是电平有效,因此生效沿一般无需考虑亚稳态,而失效沿假如和时钟有效沿的建立保持时间区间有重叠,就有可能引起亚稳态状态的产生.

      基本代码长这样:

     1 module moduleName (
     2     input clk,
     3     input rst,
     4     output rst_r
     5 );
     6     
     7     reg [1:0] rst_ff;
     8 
     9     assign rst_r = rst_ff[1];
    10 
    11     always @(posedge(rst),posedge(clk)) begin
    12         if(rst) 
    13           begin
    14               rst_ff <= 2'b0;
    15           end
    16         else
    17           begin
    18               rst_ff <= {rst_ff[0],rst};
    19           end
    20     end
    21 
    22 endmodule

      对于xilinx的触发器,一般直接综合出来rst信号的连接没有区别,而综合出来的触发器有区别,如下图

       代码:

     1 library IEEE;
     2 use IEEE.STD_LOGIC_1164.all;
     3 
     4 -- Uncomment the following library declaration if using
     5 -- arithmetic functions with Signed or Unsigned values
     6 --use IEEE.NUMERIC_STD.ALL;
     7 
     8 -- Uncomment the following library declaration if instantiating
     9 -- any Xilinx leaf cells in this code.
    10 --library UNISIM;
    11 --use UNISIM.VComponents.all;
    12 
    13 entity case_test is
    14     port (
    15         clk      : in STD_LOGIC;
    16         rst      : in STD_LOGIC;
    17         ce_lc    : in STD_LOGIC;
    18         ce_ff    : in STD_LOGIC;
    19         data_i   : in std_logic_vector(2 downto 0);
    20         dout_lc  : out std_logic_vector(2 downto 0);
    21         dout_ff  : out std_logic_vector(2 downto 0);
    22         dout_ffs : out std_logic_vector(2 downto 0)
    23     );
    24 end case_test;
    25 
    26 architecture Behavioral of case_test is
    27 
    28 begin
    29     proc_name : process (ce_lc)
    30     begin
    31         if ce_lc = '1' then
    32             dout_lc <= data_i;
    33             -- case ce_lc is
    34             --     when B"000" =>
    35             --         dout_lc <= data_i;
    36             -- when B"001"     =>
    37             --     dout_lc <= B"10";
    38             -- when B"010" =>
    39             --     dout_lc <= B"01";
    40             -- when B"011" =>
    41             --     dout_lc <= B"11";
    42             -- when B"100" =>
    43             --     dout_lc <= B"00";
    44             -- when B"101" =>
    45             --     dout_lc <= B"10";
    46             -- when B"110" =>
    47             --     dout_lc <= B"11";
    48             -- when B"111" =>
    49             --     dout_lc <= B"00";
    50             --     when others =>
    51             --         null;
    52             -- end case;
    53         end if;
    54     end process proc_name;
    55 
    56     ff_proc : process (clk, rst)
    57     begin
    58         if rst = '1' then
    59             dout_ff <= (others => '0');
    60         elsif rising_edge(clk) then
    61             if ce_ff = '1' then
    62                 dout_ff <= data_i;
    63             end if;
    64         end if;
    65     end process ff_proc;
    66 
    67     ffs_proc : process (clk)
    68     begin
    69         if rising_edge(clk) then
    70             if rst = '1' then
    71                 dout_ffs <= (others => '0');
    72             else
    73                 if ce_ff = '1' then
    74                     dout_ffs <= data_i;
    75                 end if;
    76             end if;
    77         end if;
    78     end process ffs_proc;
    79 end Behavioral;

      可以看到, 第一个进程综合出了锁存器,LDCE,我没有写复位.第二个进程是异步复位FDCE,第三个进程是同步复位FDRE,但是查看综合出来的结果,rst都是直接连到了FDCE和FDRE的复位端口之上,连接方式没有任何区别.

  • 相关阅读:
    Git 使用规范流程
    关于Python的super用法研究
    python中try except处理程序异常的三种常用方法
    break 和 continue 语句, 以及循环中的 else 子句
    杂记(python)
    Request和Response
    MVC项目开发步骤
    Web中单元测试步骤
    JSP中的细节
    WEB中地址的写法
  • 原文地址:https://www.cnblogs.com/aliothx/p/13439206.html
Copyright © 2020-2023  润新知