转载:从底层结构开始学习FPGA(3)— 存储单元之触发器、寄存器与锁存器

本文转载自CSDN博主「孤独的单刀」的原创文章,原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/124830516

一、基本概念

📌 触发器:触发器是 边沿敏感的存储单元,数据存储的动作由某一信号的上升或者下降沿进行同步的。触发器是计算机记忆装置的基本单元,一个触发器能储存一位二进制代码。
📌 锁存器:锁存器是 电平触发的存储单元,数据存储的动作取决于使能信号的电平值,当锁存器处于使能状态时,输出才会随着数据输入发生变化。(简单地说,它有两个输入,分别是一个有效信号EN,一个输入数据信号DATA_IN,它有一个输出Q,它的功能就是在EN有效的时候把DATA_IN的值传给Q,也就是锁存的过程)。
📌 寄存器:一个触发器可以组成一个一位的寄存器,多个触发器可以组成一个多位的寄存器。存储器是由大量寄存器组成的,其中每一个寄存器就称为一个存储单元。它可以存放一个有独立意义的二进制代码。

  在数字电路中存储单元有两种,一种是触发器,一种是锁存器。它们两者最大的区别是:前者通过时钟沿到来改变存储的输出状态,后者是通过电平变换来改变存储的输出状态。在 FPGA 中我们基本多使用触发器,锁存器在很多情况下都是要避免使用的,因为其不需要时钟,所以不是时序元件,对毛刺无过滤功能,非常敏感,容易出问题。

  而我们平时所说的寄存器,基本上理解为一个或者一组触发器(Flip Flop, FF)。


二、FPGA内部的存储单元

  在7系列 FPGA 的底层 — 可编程逻辑块(CLB)中有两个 SLICE,其中每个 SLICE 都含有8个存储单元。虽然说是存储单元,但实际上是4个触发器 FF+ 4个触发器FF或锁存器LATCH(可配置为其中一种)。其结构如下图:

  左边的4个存储单元只能作为触发器使用,而右边的4个存储单元则不光能作为触发器使用,还能作为锁存器使用。但是需要注意的是:一旦 SLICE 中(也就是8个FF)中的4个FF被作为LATCH使用,那么剩下的4个FF就无法使用了,会造成资源浪费


三、触发器

  在 FPGA 内部的触发器都常会被配置成D触发器,根据其复位方式、复位电平和上电电平值的不同,可将其分为不同的种类。

  在 Verilog 语言中,我们定义一个 reg 会在 FPGA 映射成一个或者一组FF。举例如下:

module test(
    input             clk,
    input             rst_n,
    input             in1,
    output reg        A
);
 
always@(posedge clk)begin    
    if(~rst_n)
        A <= 1'b0;
    else
        A <= in1;
end
endmodule

  显然,在 FPGA 的实现就是1bit的 FF:

  而下面的语句在 FPGA 的实现则会类推到4个 FF:

module test(
    input             clk,
    input             rst_n,
    input      [3:0]  in1,
    output reg [3:0]  A
);
 
always@(posedge clk)begin    
    if(~rst_n)
        A <= 4'd0;
    else
        A <= in1;
end
endmodule

  在 FPGA 中的映射如下:

  如果你还记得数电的话,就应该能看出来,上面配置的触发器实际上就是D触发器(DFF)。通过对复位方式、复位电平参数的配置,可以将D触发器约定为下述4种基本组成情况:

  • 异步复位(FDCE)

  • 异步置位(FDPE)

  • 同步复位(FDRE)

  • 同步置位(FDSE)

  这4种类型的D触发器如何用 Verilog 实现?接下来我们就建个工程看看究竟,Verilog代码:

module test(
    input   clk,
    input   rst,
    input   in1,
    input   in2,
    input   in3,
    input   in4,
    output  reg out1,
    output  reg out2,
    output  reg out3,
    output  reg out4
 );
    
 // FDCE 异步复位
 always @ ( posedge clk or posedge rst)begin
    if(rst)
        out1 <= 1'b0;
    else
        out1 <= in1;
 end
 
 // FDPE 异步置位
 always @ ( posedge clk or posedge rst )begin
    if(rst)
        out2 <= 1'b1;
    else
        out2 <= in2;
 end
 
 // FDRE 同步复位
 always @ ( posedge clk )begin
    if(rst)
        out3 <= 1'b0;
    else
        out3 <= in3;
 end
 
 // FDSE 同步置位
 always @ ( posedge clk )begin
    if(rst)
        out4 <= 1'b1;
    else
        out4 <= in4;
 end
    
endmodule

  vivado推断的门级电路如下:

  推断出了2个同步寄存器和2个异步寄存器,其他信息暂时还看不到,但也和我们预料的基本一致。vivado 综合的原理图:

  到这一步,基本就是综合出了上面例举的4种不同类型的DFF了。再来看看到FPGA的映射:

   ❓ 这里有个很有意思的地方就是:4个DFF会映射到2个不同的SLICE里边,而它们所在的SLICE其实都还是有空间的,那这是为什么?
  时钟使能信号CE、时钟信号CLOCK和置位/复位信号SR是DFF的控制信号,它们的一组值构成DFF的一个控制集。 在同一个SLICE中,必须保证所有DFF的控制集是相同的。
  在上述Verilog代码中,4个DFF根据控制集的不同,可以分为2类:异步类与同步类(SR区分),所以在FPGA实现时,会把这两类DFF分别映射到2个SLICE里去。

  在上面的部分我们还了解了DFF的一些参数如下:

INIT1:表示 FF 在 上电或者全局复位时初始化值为1
INIT0:表示 FF 在上电或者全局复位时初始化值为0
SRHIGH:表示 FF 在 SR置位时(即用户复位),FF的值为1
SRLOW :表示 FF 在SR置位时(即用户复位),FF的值为0

  接下来我举例来说明这几个参数究竟对应着什么:

module test(
    input     clk,
    input     rst,
    input     in1,
    input     in2,
    output    out1,
    output    out2
);
 
reg    out1_r = 1'b1;            // INIT1
reg    out2_r = 1'b0;            // INTT0
 
assign out1 = out1_r;
assign out2 = out2_r;
 
always@(posedge clk)begin    
    if(rst)
        out1_r <= 1'b1;          // SRHIGH
    else
        out1_r <= in1;
end

always@(posedge clk)begin    
    if(rst)
        out2_r <= 1'b0;          // SRLOW
    else
        out2_r <= in2;
end
 
endmodule

  这次我们不看图了,我们使用TCL(Tool Command Language)指令:write_verilog -force test.v 来看看实现的网表。如下(截取部分有用信息):

  第1个always块的DFF被综合成立FDSE(同步置位set,即1)且上电初始值为1, 这与Verilog代码一致;第2个always块的DFF则被综合成立FDRE(同步复位reset,即0)且上电初始值为0, 同样与Verilog代码一致。

  最后要说明的是:SR默认高电平有效,所以在Xilinx器件的代码中,一般建议使用高电平复位,如果使用低电平复位则需要在前面加个LUT6作为反相器有点浪费资源,而Altera的底层逻辑则是低电平复位有效。当然了,我一般是不建议你使用复位的,除非是一些没办法的控制逻辑。这可以参考:FPGA的复位设计要醒目点啦


四、锁存器

  从寄存数据的角度来讲,触发器和锁存器的功能是相同的。它们的区别在于触发器是同步时钟控制,而锁存器是电位信号控制。触发器是指由时钟边沿触发的存储单元。锁存器指由信号而不是时钟控制的电平敏感的设备。

  锁存器的工作原理:锁存器不同于触发器,锁存器在不锁存数据时,输出端的信号随输入信号变化,就像信号通过一个缓存器一样;一旦锁存信号起锁存作用,则数据被锁住,输入信号不起作用。因此锁存器也称为透明锁存器,指的是不锁存时输出对输入是透明的。

  锁存器具备下列缺点:

  • 对毛刺敏感,不能异步复位,因此在上电后处于不确定的状态。

  • 锁存器会使静态时序分析变得非常复杂,不具备可重用性。(首先, 锁存器没有时钟参与信号传递,无法做 STA;其次,综合工具会将 latch 优化掉,造成前后仿真结果不一致)

  根据锁存器的特点可以看出,在电路设计中,要对锁存器特别谨慎,如果设计经过综合后产生出和设计意图不一致的锁存器,则将导致设计错误,包括仿真和综合。因此,在设计中需要避免产生意想不到的锁存器。

  下列场景会产生锁存器(时序逻辑不会产生锁存器):

  • 不完整的组合逻辑语句always块中if-else语句不完整

  • 不完整的组合逻辑语句always块中case语句不完整

  🚩 举例1:if 语句中缺少 else

module test
(
    input        in,
    input        en,
    output  reg  out
);
 
always@(*)begin    
    if(en)    out = in;        
    //else    out1_r = in1;    // 缺少else
end
 
endmodule

  在 vivado 中 analysis 的原理图如下:

  🚩 举例2:组合逻辑的 case 语句不完整:

module test(
    input            a,
    input            b,
    input    [1:0]   en,
    output    reg    out
);
 
always@(*)begin    
    case(en)
        2'b00:out = a;
        2'b01:out = b;
        // case 语句不完整
    endcase
end
 
endmodule

  在 vivado 中 analysis 的原理图如下:

  上述两种情况都是,代码缺少完整条件的描述,编译工具认为在此情况下该值保持不变,就推断出了锁存器。

  所以,为了防止锁存器的产生,在组合逻辑一定要将 if-else语句、case 语句描述完整,而时序逻辑则没有这个问题。

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值