• 【Verilog 教程】6.5 Verilog避免Latch


    关键词:触发器,锁存器
    Latch 的含义
    锁存器(Latch),是电平触发的存储单元,数据存储的动作取决于输入时钟(或者使能)信号的电平值。仅当锁存器处于使能状态时,输出才会随着数据输入发生变化。

    当电平信号无效时,输出信号随输入信号变化,就像通过了缓冲器;当电平有效时,输出信号被锁存。激励信号的任何变化,都将直接引起锁存器输出状态的改变,很有可能会因为瞬态特性不稳定而产生振荡现象。

    锁存器示意图如下:
    在这里插入图片描述

    触发器(flip-flop),是边沿敏感的存储单元,数据存储的动作(状态转换)由某一信号的上升沿或者下降沿进行同步的(限制存储单元状态转换在一个很短的时间内)。

    触发器示意图如下:

    在这里插入图片描述

    寄存器(register),在 Verilog 中用来暂时存放参与运算的数据和运算结果的变量。一个变量声明为寄存器时,它既可以被综合成触发器,也可能被综合成 Latch,甚至是 wire 型变量。但是大多数情况下我们希望它被综合成触发器,但是有时候由于代码书写问题,它会被综合成不期望的 Latch 结构。

    Latch 的主要危害有:

    1)输入状态可能多次变化,容易产生毛刺,增加了下一级电路的不确定性;
    2)在大部分 FPGA 的资源中,可能需要比触发器更多的资源去实现 Latch 结构;
    3)锁存器的出现使得静态时序分析变得更加复杂。
    Latch 多用于门控时钟(clock gating)的控制,一般设计时,我们应当避免 Latch 的产生。

    if 结构不完整
    组合逻辑中,不完整的 if - else 结构,会产生 latch。

    例如下面的模型,if 语句中缺少 else 结构,系统默认 else 的分支下寄存器 q 的值保持不变,即具有存储数据的功能,所以寄存器 q 会被综合成 latch 结构。

    实例

    module module1_latch1(
        input       data,
        input       en ,
        output reg  q) ;
       
        always @(*) begin
            if (en) q = data ;
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    避免此类 latch 的方法主要有 2 种,一种是补全 if-else 结构,或者对信号赋初值。

    例如,上面模型中的always语句,可以改为以下两种形式:

    实例

    // 补全条件分支结构
    always @(*) begin
    if (en) q = data ;
    else q = 1’b0 ;
    end

    //赋初值
    always @(*) begin
    q = 1’b0 ;
    if (en) q = data ; //如果en有效,改写q的值,否则q会保持为0
    end

    但是在时序逻辑中,不完整的 if - else 结构,不会产生 latch,例如下面模型。

    这是因为,q 寄存器具有存储功能,且其值在时钟的边沿下才会改变,这正是触发器的特性。

    实例

    module module1_ff(
        input       clk ,
        input       data,
        input       en ,
        output reg  q) ;
       
        always @(posedge clk) begin
            if (en) q <= data ;
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在组合逻辑中,当条件语句中有很多条赋值语句时,每个分支条件下赋值语句的不完整也是会产生 latch。

    其实对每个信号的逻辑拆分来看,这也相当于是 if-else 结构不完整,相关寄存器信号缺少在其他条件下的赋值行为。例如:

    实例

    module module1_latch11(
        input       data1,
        input       data2,
        input       en ,
        output reg  q1 ,
        output reg  q2) ;
       
        always @(*) begin
            if (en)   q1 = data1 ;
            else      q2 = data2 ;
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这种情况也可以通过补充完整赋值语句或赋初值来避免 latch。例如:

    实例

        always @(*) begin
            //q1 = 0; q2 = 0 ; //或在这里对 q1/q2 赋初值
            if (en)  begin
                q1 = data1 ;
                q2 = 1'b0 ;
            end
            else begin
                q1 = 1'b0 ;
                q2 = data2 ;
            end
        end
    case 结构不完整
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    case 语句产生 Latch 的原理几乎和 if 语句一致。在组合逻辑中,当 case 选项列表不全且没有加 default 关键字,或有多个赋值语句不完整时,也会产生 Latch。例如:

    实例

    module module1_latch2(
        input       data1,
        input       data2,
        input [1:0] sel ,
        output reg  q ) ;
       
        always @(*) begin
            case(sel)
                2'b00:  q = data1 ;
                2'b01:  q = data2 ;
            endcase
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    当然,消除此种 latch 的方法也是 2 种,将 case 选项列表补充完整,或对信号赋初值。

    补充完整 case 选项列表时,可以罗列所有的选项结果,也可以用 default 关键字来代替其他选项结果。

    例如,上述 always 语句有以下 2 种修改方式。

    实例

       always @(*) begin
            case(sel)
                2'b00:    q = data1 ;
                2'b01:    q = data2 ;
                default:  q = 1'b0 ;
            endcase
        end
    
        always @(*) begin
            case(sel)
                2'b00:  q = data1 ;
                2'b01:  q = data2 ;
                2'b10, 2'b11 :  
                        q = 1'b0 ;
            endcase
        end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    原信号赋值或判断
    在组合逻辑中,如果一个信号的赋值源头有其信号本身,或者判断条件中有其信号本身的逻辑,则也会产生 latch。因为此时信号也需要具有存储功能,但是没有时钟驱动。此类问题在 if 语句、case 语句、问号表达式中都可能出现,例如:

    实例

       //signal itself as a part of condition
        reg a, b ;
        always @(*) begin
            if (a & b)  a = 1'b1 ;   //a -> latch
            else a = 1'b0 ;
        end
       
        //signal itself are the assigment source
        reg        c;
        wire [1:0] sel ;
        always @(*) begin
            case(sel)
                2'b00:    c = c ;    //c -> latch
                2'b01:    c = 1'b1 ;
                default:  c = 1'b0 ;
            endcase
        end
    
        //signal itself as a part of condition in "? expression"
        wire      d, sel2;
        assign    d =  (sel2 && d) ? 1'b0 : 1'b1 ;  //d -> latch
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    避免此类 Latch 的方法,就只有一种,即在组合逻辑中避免这种写法,信号不要给信号自己赋值,且不要用赋值信号本身参与判断条件逻辑。

    例如,如果不要求立刻输出,可以将信号进行一个时钟周期的延时再进行相关逻辑的组合。上述第一个产生 Latch 的代码可以描述为:

    实例

    reg a, b ;
    reg a_r ;

    always (@posedge clk)
    a_r <= a ;

    always @(*) begin
    if (a_r & b) a = 1’b1 ; //there is no latch
    else a = 1’b0 ;
    end

    敏感信号列表不完整
    如果组合逻辑中 always@() 块内敏感列表没有列全,该触发的时候没有触发,那么相关寄存器还是会保存之前的输出结果,因而会生成锁存器。

    这种情况,把敏感信号补全或者直接用 always@(*) 即可消除 latch。

    小结
    总之,为避免 latch 的产生,在组合逻辑中,需要注意以下几点:

    1)if-else 或 case 语句,结构一定要完整
    2)不要将赋值信号放在赋值源头,或条件判断中
    3)敏感信号列表建议多用 always@(*)

  • 相关阅读:
    【C/C++笔试练习】OSI分层模型、源端口和目的端口、网段地址、SNMP、状态码、tcp报文、域名解析、HTTP协议、计算机网络、美国节日、分解因数
    JS匿名函数和数组的变换
    python脚本-网页爬虫获取网页图片
    计算机毕业设计(附源码)python中国朝鲜族民族特色服务系统
    字节流和字符流(即写文件)、FileOutputStream类
    dataX 入门指南
    docker-compose安装部署gitlab中文版
    python进度条库tqdm使用记录
    黑客必备一款API泄露的利用工具
    AI 已经在污染互联网了。。赛博喂屎成为现实
  • 原文地址:https://blog.csdn.net/qq_43158059/article/details/133364507