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的复位端口之上,连接方式没有任何区别.

 

posted @ 2020-08-06 15:43  天山明月  阅读(684)  评论(0编辑  收藏  举报