FPGA设计中如何才能不出现Latch锁存器-FPGA常见问题社区-FPGA CPLD-ChipDebug

FPGA设计中如何才能不出现Latch锁存器

在进行FPGA设计的过程中,经常会在编译程序时发现有一些warning提示生成了一些latch,而且一般FPGA的设计规则也不建议有latch生成。那么,latch究竟是什么东西呢?如果在FPGA设计中不允许latch中现,又如何避免呢?
1 锁存器、触发器和寄存器的比较
(1)锁存器
锁存器(latch)是电平触发的存储单元,数据存储的动作(状态转换)取决于输入时钟(或者使能)信号的电平值,仅当锁存器处于使能状态时,输出才会随着数据输入发生变化。
分为普通锁存器和门控锁存器。普通锁存器无控制信号,输出状态始终直接由输入决定。在实际的数字系统中,为了协调各部分的工作,往往需要有一个特定的控制信号去控制锁存器状态转换的时间,在控制信号无效时,输出保持不变,不随输入变换;当控制信号有效时,输出由输入决定,跟随输入变化。
①正是因为门控锁存器在控制信号有效的期间内,都可以接收输入信号,所以,激励信号的任何变化,都将直接引起锁存器输出状态的改变。这时输入信号若发生多次变化,输出状态也可能发生多次变化,这一现象称为锁存器的空翻。
②其次,当门控锁存器的控制信号有效时,锁存器就变成了一个组合电路,时序逻辑电路的模型就等效为两个各组合电路互为反馈的反馈系统,因此,系统有可能会因为瞬态特性不稳定而产生振荡现象。
(2) 触发器(flip-flop)
触发器(flip-flop)是边沿敏感的存储单元,数据存储的动作(状态转换)由某一信号的上升或者下降沿进行同步的(限制存储单元状态转换在一个很短的时间内)。(钟控D触发器其实就是 D 锁存器,边沿 D 触发器才是真正的 D 触发器)
触发器分为两种,一种是主从触发器和边沿触发器。主从触发器在时钟有效期内(主触发器)接收数据,在时钟边沿输出状态转换。边沿触发器在时钟边沿期间, 触发器才接收数据并使输出状态转换。目前,主从触发器基本上已经很少见了,实际使用的大都是边沿触发器。
(3)寄存器(register)
寄存器(register)用来暂时存放参与运算的数据和运算结果。在实际的数字系统中,通常把能够用来存储一组二进制代码的同步时序逻辑电路称为寄存器。
区别与联系:由于触发器内有记忆功能,因此利用触发器可以方便地构成寄存器。由于一个触发器能够存储一位二进制码,所以把 n 个触发器的时钟端口连接起来就能构成一个存储 n 位二进制码的寄存器。
从寄存数据的角度来讲,寄存器和锁存器的功能是相同的;它们的区别在于寄存器是同步时钟控制,而锁存器是电位信号控制。一般的设计规则是:在绝大多数设计中避免产生锁存器。它会让您设计的时序完蛋,并且它的隐蔽性很强,非老手不能查出。
2 锁存器具备的特点
(1)对毛刺敏感(使能信号有效时,输出状态可能随输入多次变化,产生空翻,对下一级电路很危险),不能异步复位,因此在上电后处于不确定的 状态。
(2)锁存器会使静态时序分析变得非常复杂,不具备可重用性。(首先, 锁存器没有时钟参与信号传递,无法做 STA;其次,综合工具会将 latch 优化掉,造成前后仿真结果不一致)
(3)在FPGA中基本的单元是由查找表和触发器组成的,若生成锁存器反而需要更多的资源。根据锁存器的特点可以看出,在电路设计中,要对锁存器特别谨慎,如果设计经过综合后产生出和设计意图不一致的锁存器,则将导致设计错误,包括仿真和综合。因此,在设计中需要避免产生意想不到的锁存器。如果组合逻辑的语句完全不使用 always 语句块,就可以保证综合器不会综合出锁存器。
(4)但如果锁存器和触发器两者都由与非门搭建的话,锁存器耗用的逻辑资源要比D触发器少(D触发器需要12个MOS管,锁存器只需6个MOS管),锁存器的集成度更高。
所以在的ASIC设计中会用到锁存器。但锁存器对毛刺敏感,无异步复位端,不能让芯片在上电时 处在确定的状态;另外,锁存器会使静态时序分析变得很复杂,不利于设计的可重用,所以,在ASIC设计中,除了CPU这高速电路,或者RAM这种对面积很敏感的电路,一般不提倡用锁存器。
3 锁存器的出现以及解决办法
在基于always的组合逻辑描述语句中容易综合出锁存器的地方:
在FPGA程序设计过程中,出现latch绝大多数情况是因为if和case语句的不完全描述,导致程序在综合过程中出现了latch的功能行为。举个简单的例子,写个译码器,输入为a,输出为b,其中a为2位输入,b为8位输出,若如下所写:
always@( * )
        case(a)
            2’b00: b = 8’d1;
            2’b01: b = 8’d5;
            2’b10: b = 8’d8;
            2’b11: b = 8’d17;
        endcase  
但是若将a的位数改为3,如下所写:
always@( * )
case(a)
            3’b000: b = 8’d1;
            3’b001: b = 8’d5;
            3’b010: b = 8’d8;
            3’b011: b = 8’d17;
        Endcase
可以看到,第二张综合图里面出现了latch。然而,两段程序的不同仅仅是a的位数从2变到了3,这究竟是为什么?
其实这就是因为case的不完全描述所致。在第一段程序中,输入a可能的四种取值都穷举到了,所以是完全描述的。而第二段程序中,a变为了3位数,而取值只有3’b000、3’b001、3’b010、3’b011四个,而3’b100到3’b111之间的数据并没有列举出,虽然在实际执行过程中输入并不会出现未列举出的数值,但是系统在编译时并不知情,所以它会在3’b100到3’b111之间的值出现时将b的输出锁存,也就是出现了latch的功能行为。
之所以在硬件设计中避免latch的出现,主要原因是latch会产生毛刺(glitch),这种毛刺对下一级电路是很危险的。并且其隐蔽性很强,不易查出。因此,在设计中,应尽量避免latch的使用。
从其产生原因可以看出,为了防止出现latch,对于if语句和case语句一定要做到完全描述,最常用的方法就是总是加上else和default。
总结:
锁存器产生的根本原因:当组合逻辑需要保持时,就会综合出锁存器。
锁存器的危害:
(1)latch会产生毛刺(glitch),这种毛刺对下一级电路是很危险的;
(2)不能异步复位,因此在上电后处于不确定的 状态;
(3)锁存器会使静态时序分析变得非常复杂,不具备可重用性

 

请登录后发表评论

    没有回复内容