xilinx FPGA触发器和锁存器

我们知道触发器是边沿敏感,锁存器是电感敏感的存储单元。那么它们在FPGA内部究竟有什么区别呢?本文通过几个实际的案例来说明。

在xilinx 7系列的FPGA中,CLB(Configurable Logic Block)是逻辑实现的主要资源,在ug474中详细介绍了CLB。每个CLB包含两个slices,每个slices由4个(A,B,C,D)6输入LUT和8个寄存器,1个CARRY4,3个MUX(多路选择器)组成。同一CLB中的两片slices没有直接的线路连接,分属于两个不同的列,每列拥有独立的快速进位链资源。slice分为两种类型 SLICEL,  SLICEM .  SLICEL可用于产生逻辑,算术,ROM。 SLICEM除以上作用外还可配置成分布式RAM或32位的移位寄存器。每个CLB可包含两个SLICEL或者一个SLICEL与一个SLICEM.(https://www.eefocus.com/b3574027/blog/15-05/312609_2e5ad.html

打开vivado的device就能看到每个蓝色框内就是1个CLB,左起第三个slice就是slicem。

在ug953中介绍各种底层硬件资源,其中CLB中的寄存器可以配置为D Flip-Flop (D触发器)和Transparent Data Latch(锁存器),在245页上有描述,具体又可以分为以下几种:

触发器:

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

锁存器:

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

触发器的基本结构如下图所示:

当复位端CLR拉高时Q输出0,当CE等于0时,Q保持不变,当CLR = 0;CE = 1; 在C端边沿触发时将D的数据同步到Q端。

锁存器的基本结构如下图所示:

当复位端CLR拉高时Q输出0,当GE G有一个拉低时,Q端保持不变,都为高时,Q输出D的值。可以看到锁存器是没有同步复位的。无论altera还是xilinx的ff都可以配置成fd或者ld。

1.异步复位时序电路

以4分频器为例。

module test(
    output c,
    input clk,
    input rst
    );
    
    
    reg clk2 = 1'd0;
    reg [1:0]cnt = 1'd0;
    always@(posedge clk or posedge rst)
    if(rst)
        begin
            clk2 <= 1'd0;   
            cnt  <= 1'd0;
        end
    else if(cnt == 1'd1)
        begin
            clk2 <= ~clk2;
            cnt  <= 1'd0;
        end
    else
        cnt <= cnt + 1'd1;
    
    assign    c = clk2;
   
endmodule

综合后的RTL图为:

可以看到占用了2个FF和2个LUT,而且FF都是FDCE,是异步复位,初始值为0的触发器,这和代码是一致的。LUT1的作用就是一个反相器,当cnt = 1时,cnt = 0;cnt = 0时,cnt = 1;同理,可以分析LUT2的真值表,当I0 = 1时,O输出的是I1的取反,这和代码也是一致的。总之,我们可以看出异步复位电路,RST异步接入,高电平有效时,FF配置为FDCE。

当代码修改为if(!rst)时,RTL如下图所示:

编译器会用一个lut对rst做反相,这将占用更多的资源,所以一般来说用高电平复位,同时,复位时应将用到的寄存器都初始化。

2.同步复位时序电路

同步复位就是在always 敏感列表里不添加rst,仅在clk边沿变化时,D发生变化。代码修改为:

    always@(posedge clk)
    if(rst)
        begin
            clk2 <= 1'd0;   
            cnt  <= 1'd0;
        end
    else if(cnt == 1'd1)
        begin
            clk2 <= ~clk2;
            cnt  <= 1'd0;
        end
    else
        cnt <= cnt + 1'd1;

 综合后的RTL图为:

可以看到rst信号接入到了lut,FF配置为了FDRE。对于xilinx的LUT来说,触发器支持高电平的异步和同步复位。

3.完整if else组合逻辑电路 

一般认为组合逻辑是指输出只与当前的输入状态有关电路,而时序逻辑是指输出不仅与当前输入有关,还跟历史输入有关,例如前面的电路。

例如,a = (b&c&d) + e; 这明显是组合逻辑。组合逻辑里没有时钟,输入信号至LUT里后,直接输出Q,不经过触发器,除非电路中生成了锁存器,Q输出后会接入一个由FF配置成的LD。锁存器会在什么地方产生呢?我们一般回答在组合逻辑里分支没有完全描述的情况下产生,例如if后没有else,case没有default。

下面是完整的if else例子。

module test(
    input a,b,
    output [1:0]c,
    input clk,
    input rst
);
    
    reg [1:0]reg_c = 1'd0;
    always @(rst or a or b)
    if(rst)
        reg_c  = 2'd0;
    else if(a)
        reg_c = 2'd1;
    else if(b)
        reg_c = 2'd2;
    else
        reg_c = 1'd0;
        
    assign c = reg_c;
endmodule

生成的RTL图如下图所示:

由于它是组合逻辑,敏感列表里写不写都是一样的(我猜的)。

4不完整if else if组合逻辑电路

如果少了else会是什么情况呢?

module test(
    input a,b,
    output [1:0]c,
    input clk,
    input rst
);
    
    reg [1:0]reg_c = 1'd0;
    always @(*)
    if(rst)
        reg_c  = 2'd0;
    else if(a)
        reg_c = a - b;
    else if(b)
        reg_c = a + b;
        
    assign c = reg_c;
endmodule

注意代码,没有了else,同时else if里的内容也有修改。RTL图为:

可以看到,在LUT输出后,加入了一个LDCE,这便是锁存器。因为在两个else if以外的情况下,编译器会将逻辑定为保持当前输出不变,因此,观察LDCE的端口,rst接到了异步复位的CLR,锁存器只支持异步复位,当a b条件不满足else if时,即在LUT2中描述情况下a b同时为0时,数据将锁存,因此LUT会输出0到LDCE的G端,此时Q将保持当前值。可以看到当条件罗列完全时,仅需要LUT就可以完成组合逻辑的描述,未罗列完全时,还需要LUT用于描述LDCE的G端,因此会多占用FF资源。同时,锁存器也不利于静态时序分析,所谓静态时序分析,我们通常是以clk的周期为参考,去分析DATA和CLK的路径,计算是否满足下一级FF的setup和hold时间,而latch的复位是异步的,G端的变化是异步的,latch和时钟毫无关系,分析起来自然十分麻烦。因此我们应尽量避免产生latch

时序逻辑中if 或case不完整会不会产生latch呢?

答案是不一定产生,因为时序逻辑是在clk的变化沿同步数据,它会用一个lut来描述没有穷举的情况,并输出到FF的CE端,使FF在输出时保持前一个值。如下图所示。

反正最好是把else和default写完整。也可以在组合逻辑always@(*)的首行,把信号先用阻塞方式(=)把信号先赋值,效果等同于else或default。

那么如何检查代码中有latch呢?

最简单的方法是在综合后的message窗口,ctrl + f,搜索latch或者设置条件为latch,点击确定,即可看到有多少个latch。从资源占用中看到只用了1个LUT 和1个FF就实现了,可是明明是2个LUT和1个FF?这时候要打开版图来看。

如果在FLOP_LATCH下没有LATCH选项,说明该设计中就没有latch产生,同理,用Find命令可以查看其它资源。也可以在输入脚本命令 all_latches。输入all_ 会有命令提示,可以查看许多资源,请自行尝试。

至此,本文介绍了latch和ff的区别,和latch产生的原因。实际上数字电路基础中花了很多篇幅去讲晶体三极管和MOS管组成TTL和CMOS电路,有或与非等基本电路,分析它们的电流和电压特性,但FPGA里负责大量逻辑运算是基于SRAM的LUT,是查找表,应该有一个区别的认识,不是FPGA里有很多或与非门,是用LUT组成的或与非逻辑。

FPGA设计时常说的设计流程就是先综合生成原理图,再布局布线映射到FPGA中CLB中去。比如有16个并行总线端口输入后经clk采样,在原理图看都是一样的路径,但由于映射到BGA引脚上,信号从PAD到16个寄存器有无数种布线方法,它们之间的延时也是不固定的,当采样速度很高,布线的延时差异不能忽略时,就容易采样错误,因此就要用时序约束,set input delay来描述这些信号相对于clk到来的时刻,布局布线时就根据这些约束保证输入的路径是差不多的。输出也是同理。为了得到更好的采样结果,通常会将输出输入打1个节拍,用IOB的FF去输出或采样,可以保证每次布局布线时,从FF到PAD的时间都是固定的,而且最短。

5.阻塞和非阻塞

简单说来,阻塞就是 = ,非阻塞就是 <=。为什么称它为阻塞呢?因为 = 在同一个always 它是有先后执行顺序的,后面的=被前面的 = 阻塞了。非阻塞就是并行的,一起运行。

懒得写了,直接引用吧。

https://blog.csdn.net/August_cwj/article/details/77989071

限于水平,还有很多问题没有描述,权当一个敲门砖,我们FPGAer还是应该要理解你的代码实际上对应的是什么电路,毕竟这是自古流传的传统。

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章