草庐IT

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

追逐者-桥 2023-08-17 原文

前言

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

一、基本概念

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

答案是否定的。

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

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

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/jyTNFhttp://t.csdn.cn/MBORdhttp://t.csdn.cn/k6HTQ

有关二、8【FPGA】Verilog中锁存器(Latch)原理、危害及避免的更多相关文章

  1. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  2. Verilog使用inout信号的方法 - 2

    目录一、inout在设计文件中的使用方法1.1、inout的第一种使用方法1.2、inout实现的第二种使用方法1.3、inout使用总结 二、inout在仿真测试中的使用方法一、inout在设计文件中的使用方法在FPGA的设计过程中,有时候会遇到双向信号(既能作为输出,也能作为输入的信号叫双向信号)。比如,IIC总线中的SDA信号就是一个双向信号,QSPIFlash的四线操作的时候四根信号线均为双向信号。在Verilog中用关键字inout定义双向信号,这里总结一下双向信号的处理方法。1.1、inout的第一种使用方法  实际上,双向信号的本质是由一个三态门组成的,三态门可以输出高电平,低电

  3. ruby - 在 Ruby 中为变量赋值时如何避免控制台输出 - 2

    赋值时是否可以避免这种影响:irb(main):584:0>a=true=>trueirb(main):584:0>我有一个代码有很多赋值,当我试图测试它时,由于所有这些返回值,我看不到结果:truefalsetruefalsetruetrue.. 最佳答案 您可以启动irb或附加--noecho选项的控制台。$irb--noecho2.0.0p353:001>true2.0.0p353:002>否则,如果控制台由另一个进程启动,只需设置conf.echo=false$irb2.0.0p353:001>true=>true2.0.0

  4. ruby-on-rails - Rails - 如何避免在 View 中使用 hidden_​​fields 将值传递给 Controller ​​? - 2

    有没有一种方法可以避免hidden_​​field方法将View中的值传递给Controller​​?出于安全原因,我更喜欢Controller方法。不幸的是,strong_parameters不支持值对@variables。EDIT6/181:00PMESTI'verenamedmygaragescontrollertoappointmentscars_controllernolongercreatesanewappointment(formallygarages).Anewappointmentiscreatedintheappointments_controller我目前的结构路

  5. ruby - 如何使用 ruby​​ fibers 避免阻塞 IO - 2

    我需要将目录中的一堆文件上传到S3。由于上传所需的90%以上的时间都花在了等待http请求完成上,所以我想以某种方式同时执行其中的几个。Fibers能帮我解决这个问题吗?它们被描述为解决此类问题的一种方法,但我想不出在http调用阻塞时我可以做任何工作的任何方法。有什么方法可以在没有线程的情况下解决这个问题? 最佳答案 我没有使用1.9中的纤程,但是1.8.6中的常规线程可以解决这个问题。尝试使用队列http://ruby-doc.org/stdlib/libdoc/thread/rdoc/classes/Queue.html查看文

  6. 【Unity游戏破解】外挂原理分析 - 2

    文章目录认识unity打包目录结构游戏逆向流程Unity游戏攻击面可被攻击原因mono的打包建议方案锁血飞天无限金币攻击力翻倍以上统称内存挂透视自瞄压枪瞬移内购破解Unity游戏防御开发时注意数据安全接入第三方反作弊系统外挂检测思路狠人自爆实战查看目录结构用il2cppdumper例子2-森林whoishe后记认识unity打包目录结构dll一般很大,因为里面是所有的游戏功能编译成的二进制码游戏逆向流程开发人员代码被编译打包到GameAssembly.dll中使用il2ppDumper工具,并借助游戏名_Data\il2cpp_data\Metadata\global-metadata.dat

  7. FPGA 之 时钟,时钟域, 以及复位系统的设计 - 2

    FPGA时钟和时钟域时钟树所谓时钟树为FPGA内部资源,分:全局时钟树,区域时钟树,IO时钟树原则上优先使用全局时钟树,在GT接口上使用IO时钟树,一般工具也会对GT时钟加以限制;时钟树使用方式正确的物理连接FPGA会由物理管脚专门用于全局时钟设置,通过查询数据手册可以在PCB设计阶段进行确认,当外部时钟接入此管脚时,工具会自动占有全局时钟树资源,当接入普通信号时不会分配时钟树资源;恰当的代码描述原语的使用,即BUFG的使用,可以将PLL的输出等内部时钟进行全局时钟资源的分配;IO时钟资源需要参考相应接口手册,以ultrascale的GTH为例,其JESD204的时钟方案针对不同的子类会由不同

  8. Slowloris DoS攻击的原理与简单实现 - 2

    前言    Slowloris攻击是我在李华峰老师的书——《MetasploitWeb 渗透测试实战》里面看的,感觉既简单又使用,现在这种攻击是很容易被防护的啦。不过我也不敢真刀实战的去试,只是拿个靶机玩玩罢了。         废话还是写在结语里面吧。(划掉)结语可以不看(划掉)Slowloris攻击的原理        Slowloris是一种资源消耗类DoS攻击,它利用部分HTTP请求进行操作。也叫做慢速攻击,这里的慢速并不是说发动攻击慢,而是访问一条链接的速度慢。Slowloris攻击的功能是打开与目标Web服务器的连接,然后尽可能长时间的保持这些连接打开。如果由多台电脑同时发起Slo

  9. MicroBlaze在纯FPGA下 Xilinx SDK固化程序到外部SPI FLASH - 2

    外部SPIFLASH:MicronN25Q128A13ESE40G(128Mbit(16MByte))FPGA:XC7A100T CPU:Microblaze第一种情况:Microblaze在简单的应用,比如运行LED,IIC,SPI,UART之类的低俗接口驱动,或做一些简单的辅助型工作时,一般生成的applicationelf文件都不大,在10几KB或者几十,百几KB,此时使用FPGA内部的BRAM资源已经足够。XC7A100T本身就有600几KB的BRAM资源。这种情况下直接将硬件流文件和elf文件合并为download.bit文件,在直接烧录到外部SPIFLAH即可。1.Xilinx--

  10. ruby - 阅读用户输入时如何避免回显换行符? - 2

    我正在用Ruby编写类似curses的程序,我正在使用stty和ansi转义字符来实现我想要的。当我想获得用户输入时,我的问题就出现了。像许多基于控制台的程序一样,我想从终端底部获取用户输入。因此,我将光标放在屏幕底部并调用Readline.readline(或任何获取用户输入的方法)。像往常一样,它会读取所有内容,直到我按下回车键,并打印一个换行符。由于光标位于终端的最后一行,它会滚动一行,这会弄乱屏幕。我怎样才能避免这种情况?我试图使用stty来停止回显换行符,但我没有成功。也许可以使用stty来阻止终端滚动?当然,我可以编写自己的方法来通过一次读取一个字符(并捕获“返回”)来捕获

随机推荐