网络知识 娱乐 二、8【FPGA】Verilog中锁存器(Latch)原理、危害及避免

二、8【FPGA】Verilog中锁存器(Latch)原理、危害及避免

前言

学习说明此文档为本人的学习笔记,对一下资料进行总结,并添加了自己的理解。

一、基本概念

        如果拿到了数字电路技术基础的书,翻开书本的目录你会发现,关于锁存器的章节与内容非常少,也就是在触发器前面有一小节进行了简单说明。但是真的就这么简单么?

答案是否定的。

        在组合逻辑电路与时序逻辑电路中间夹了一章触发器,而触发器作为了时序逻辑电路的基本构成单元,而锁存器是构成触发器的基本结构(却不是时序逻辑电路的构成单元),但是锁存器又是通过组合电路得来的(锁存器严格来说属于组合逻辑电路)。

上面那个问题的答案解释呼之欲出,锁存器不就是组合逻辑电路与时序电路的桥梁么?人们发现了锁存器才进一步有了触发器(由于锁存器有触发器的储存特性,有的教材也将其归为触发器,我认为这样是不合理的),进而才有了时序逻辑电路。

1、组合逻辑电路与时序逻辑电路

数字电子技术基础主要分为组合逻辑电路与时序逻辑电路

组合逻辑电路:由门电路组成,其某一时刻的输出状态只与该时刻的输入状态有关,而与电路原来的状态无关,并没有记忆功能。

时序逻辑电路:由锁存器、触发器和寄存器等单元组成,其某一时刻的输出状态不仅与该时刻的输入状态有关,而且与电路原来的状态有关,具有记忆功能。

2、基本逻辑储存单元

(1)锁存器(latch)

        锁存器(latch)是一种在异步电路系统中,对输入信号电平敏感的单元,用来存储信息。锁存器在数据未锁存时,输出端的信号随输入信号变化,此时信号通过锁存器就相当于通过了一个缓存器,一旦锁存信号有效,则数据被锁存,输入信号不起作用。因此锁存器也被称为透明锁存器,指的是不锁存时输出对于输入是透明的。

《数字电子技术基础》上的SR锁存器的定义:

SR锁存器实际上是一个组合逻辑电路,在时序分析时模型就等效为两个各组合电路互为反馈的反馈系统,因此,系统有可能会因为瞬态特性不稳定而产生振荡现象。

(2)触发器(flip-flop)

        触发器分为电平触发、脉冲触发、边沿触发。触发器是构成时序逻辑电路的基本单元,能够存储1位二值信号的基本单元电路统称触发器(Flip-Flop)。为实现记忆1位二值信号功能,触发器具备以下两个基本特点:具有两个能自行保持的稳定状态,用来表示逻辑状态0和1。在触发信号的操作下,根据输入信号可以将触发器置为0或1。

可以清楚的看到相对于SR锁存器,SR触发器增加了CLK触发端,这样就能够在时序电路中通过控制CLK端来控制SR触发器,而在时序电路中SR锁存器只有在时钟平稳时才可以控制。 

 (3)寄存器(register)

        寄存器(register)用来暂时存放参与运算的数据和运算结果。在实际的数字系统中,通常把能够用来存储一组二进制代码的同步时序逻辑电路称为寄存器。
区别与联系:由于触发器内有记忆功能,因此利用触发器可以方便地构成寄存器。由于一个触发器能够存储一位二进制码,所以把 n 个触发器的时钟端口连接起来就能构成一个存储 n 位二进制码的寄存器。
        从寄存数据的角度来讲,寄存器和锁存器的功能是相同的;它们的区别在于寄存器是同步时钟控制,而锁存器是电位信号控制。 一般的设计规则是:在绝大多数设计中避免产生锁存器。它会让您设计的时序完蛋,并且它的隐蔽性很强,非老手不能查出。

3、异步电路与同步电路

异步电路:异步电路主要是组合逻辑电路,用于产生FIFO或RAM的读写控制信号脉冲,但它同时也用在时序电路中,此时它没有统一的时钟,状态变化的时刻是不稳定的,通常输入信号只在电路处于稳定状态时才发生变化。

同步电路:同步电路是由时序电路(寄存器和各种触发器)和组合逻辑电路构成的电路,其所有操作都是在严格的时钟控制下完成的。这些时序电路共享同一个CLK,而所有的状态变化都是在时钟的上升沿(或下降沿)完成的。

二、锁存器(Latch)

1、Latch  

        在进行FPGA开发的过程中,经常会在编译程序时发现有一些warning提示生成了一些latch,而且一般FPGA的设计规则也不建议有latch生成。在Verilog对FPGA开发时,有时语法正确的代码不一定电路就是合理的。

        当你在FPGA开发时想组合出纯组合逻辑电路(没有时钟控制端),但有时候你的代码最后综合出的结果却是“组合逻辑电路+锁存器(触发器)”。这说明在你的Verilog代码中有保持不变的情况,这种“保持输出不变”的行为意味着需要记住当前的状态,由于组合逻辑中的门电路是无法储存状态的,此时代码综合后就会产生锁存器(Latch),来储存产生的状态。这就是组合逻辑中产生Latch的主要原因。

        在组合逻辑电路中产生的Latch,在同步电路中尽量避免,但并不表示没用或者是错误的,Latch在异步电路中是非常有用的,只是我们设计的是同步电路,要尽量避免。

2、Latch的特点及危害

(1)对毛刺敏感

使能信号有效时,输出状态可能随输入多次变化,产生空翻,对下一级电路很危险,不能异步复位,因此在上电后处于不确定的状态。并且其隐蔽性很强,不易查出。因此,在设计中,应尽量避免latch的使用。

(2)不能异步复位

由于其能够储存上次状态的原因,上电后Latch处于不定态。

(3)复杂的静态时序电路分析

首先,锁存器没有时钟信号参与信号传递,无法做STA;其次,综合工具会将 latch 优化掉,造成前后仿真结果不一致。

(4)占用更多资源

在FPGA中基本的单元是由查找表和触发器组成的,大部分器件没有锁存器这个东西,若生成锁存器反而需要更多的资源。(不同的观点:当然,目前网上还有一种说法是FPGA中只有LUT和FF的资源,没有现成的Latch,所以如果要用Latch,需要更多的资源来搭出来。但这一观点,是错误的!!——因为每一个slice包含FF和latch通用的资源。)

(5)额外的延时

在ASIC设计中,锁存器也会带来额外的延时和DFT,并不利于提高系统的工作频率。

(6)锁存器的优点

 如果锁存器和触发器两者都由与非门搭建的话,锁存器耗用的逻辑资源要比D触发器少(D触发器需要12个MOS管,锁存器只需6个MOS管),锁存器的集成度更高,所以在ASIC设计中会用到锁存器,且只有在CPU高速电路或者RAM这种面积很敏感的电路才使用锁存器。

综上所述

根据锁存器的特点可以看出,在电路设计中,要对锁存器特别谨慎,如果设计经过综合后产生出和设计意图不一致的锁存器,则将导致设计错误,包括仿真和综合。因此,在设计中需要避免产生意想不到的锁存器。如果组合逻辑的语句完全不使用 always 语句块,就可以保证综合器不会综合出锁存器。虽然在的ASIC设计中会用到锁存器。但锁存器对毛刺敏感,无异步复位端,不能让芯片在上电时 处在确定的状态;另外,锁存器会使静态时序分析变得很复杂,不利于设计的可重用。

3、Latch的产生情况及避免

Latch一般产生在组合逻辑电路的always中,主要有三种情况:

(1)组合逻辑中if语句中没有else

module decoder3_8(
    input wire in_1,
    input wire in_2,
    input wire in_3,
    
    output reg [7:0] out             
    );

//情况一:组合逻辑中if语句中没有else
    always@(*)     
        if( {in_1, in_2, in_3} == 3'b000 )
            out = 8'b0000_0001;                  
        else if({in_1, in_2, in_3} == 3'b001)
            out = 8'b0000_0010;
        else if({in_1, in_2, in_3} == 3'b010) 
            out = 8'b0000_0100;
        else if({in_1, in_2, in_3} == 3'b011) 
            out = 8'b0000_1000;
        else if({in_1, in_2, in_3} == 3'b100) 
            out = 8'b0001_0000;
        else if({in_1, in_2, in_3} == 3'b101) 
            out = 8'b0010_0000;
        else if({in_1, in_2, in_3} == 3'b110) 
            out = 8'b0100_0000;
        else if({in_1, in_2, in_3} == 3'b111) 
            out = 8'b1000_0000;
        // else              //去除esle产生latch(锁存器)               
            // out = 8'b0000_0001; 
endmodule

 如图进行RTL代码逻辑综合后出现了latch

(2)组合逻辑中case的条件不能够完全列举且没有default

module decoder3_8(
    input wire in_1,
    input wire in_2,
    input wire in_3,
    
    output reg [7:0] out            
    );
    always@(*)
        case ({in_1, in_2, in_3})
            3'b000 : out = 8'b0000_0001;
            3'b001 : out = 8'b0000_0010;
            3'b010 : out = 8'b0000_0100;
            3'b011 : out = 8'b0000_1000;
            3'b100 : out = 8'b0001_0000;
            3'b101 : out = 8'b0010_0000;
            3'b110 : out = 8'b0100_0000;
            // 3'b111 : out = 8'b1000_0000;           
            // default : out = 8'b0000_0001;    //latch的产生
        endcase

endmodule

RTL代码逻辑综合后产生latch 

 (3)组合逻辑中输出变量赋值给自己

module decoder3_8(
    input wire in_1,
    input wire in_2,
    input wire in_3,
    
    output reg [7:0] out            
    );
//情况三:组合逻辑中输出变量赋值给自己(一)
    always@(*)     
        if( {in_1, in_2, in_3} == 3'b000 )
            out = 8'b0000_0001;                  
        else if({in_1, in_2, in_3} == 3'b001)
            out = 8'b0000_0010;
        else if({in_1, in_2, in_3} == 3'b010) 
            out = 8'b0000_0100;
        else if({in_1, in_2, in_3} == 3'b011) 
            out = 8'b0000_1000;
        else if({in_1, in_2, in_3} == 3'b100) 
            out = 8'b0001_0000;
        else if({in_1, in_2, in_3} == 3'b101) 
            out = 8'b0010_0000;
        else if({in_1, in_2, in_3} == 3'b110) 
            out = 8'b0100_0000;
        else if({in_1, in_2, in_3} == 3'b111) 
            out = 8'b1000_0000;
        else                          
            out = out;     //产生latch(锁存器)  
endmodule

module decoder3_8(
    input wire in_1,
    input wire in_2,
    input wire in_3,
    
    output reg [7:0] out            
    );
//情况三:组合逻辑中输出变量赋值给自己(二)            
    always@(*)
        case ({in_1, in_2, in_3})
            3'b000 : out = 8'b0000_0001;
            3'b001 : out = 8'b0000_0010;
            3'b010 : out = 8'b0000_0100;
            3'b011 : out = 8'b0000_1000;
            3'b100 : out = 8'b0001_0000;
            3'b101 : out = 8'b0010_0000;
            3'b110 : out = 8'b0100_0000;
            3'b111 : out = out;         //latch的产生   
            default : out = 8'b0000_0001;    
        endcase  
endmodule

 综上所述:

在组合逻辑电路的always中,if-else语句中不能缺少else;case语句中条件不能够完全列举或缺少default;在if-else和case中均不能出现变量自己将值赋给自己。

本文档参考资料

 学习视频:是根据野火FPGA视频教程——第十讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3

学习资料:《数字电子技术基础》清华大学出版社,请参考本人的学习笔记专栏

https://blog.csdn.net/arm_qiao/category_11744079.htmlhttps://blog.csdn.net/arm_qiao/category_11744079.html《Verilog HDL数字设计与综合》第二版

参考文章:

http://t.csdn.cn/jyTNF、http://t.csdn.cn/MBORd、http://t.csdn.cn/k6HTQ