背景
- 基于Xilinx的7系列FPGA,
在通过《ug474_7Series_CLB.pdf》学习CLB过程中,
对7系列CLB中的存储单元(Storage Elements)进行展开学习,遂就有了此文。
系列目录与链接
基于Xilinx的7系列FPGA,笔者在对CLB进行学习、研究时,归纳并总结出了以下博文:
1、分篇1:《7系列 之 查找表(Look-Up Table,LUT)》
2、分篇2:《7系列 之 存储单元(Storage Elements)》
3、分篇3:《7系列 之 多路复用器(Multiplexers)》
4、分篇4:《7系列 之 进位逻辑(Carry Logic)》
5、分篇5:《7系列 之 分布式RAM(Distributed RAM)》
6、分篇6:《7系列 之 7系列 之 移位寄存器(Shift Registers)》
7、总纲 :《7系列 之 可配置逻辑块(7 Series Configurable Logic Block,CLB)》
说明1:本系列基于文档文档《ug474_7Series_CLB.pdf》而写,翻译和感悟,会出现中英文混合的情况。
说明2:虽然文中会出现一些原文的部分截图,但非常支持并推荐道友们去看原汁原味的官方文档
说明3:在查阅相关资料过程中,发现一些关于相关知识点的介绍零零散散,本系列会对其进行整合,力求详尽。
说明4:如果文章有误,欢迎道友们指出、讨论,笔者也会积极改正,希望一起进步!
Ch.1: 存储单元
1.1 ··· 锁存器(Latch)
- 具有记忆功能的存储单元电路。
- 在数字电路中,可以记录二进制数字信号“0”和“1”。
- 电平触发,锁存器的工作状态取决于输入信号的电平,而不是时钟信号的边沿。
锁存器在不锁存数据时,输出端的信号随输入信号变化,就像信号通过一个缓冲器一样。
因此,锁存器也被称为透明锁存器。
一旦锁存器锁存数据,则输出数据会被锁住,输入信号不影响输出信号。
1.2 ··· 触发器(Flip-Flop)
- 具有记忆功能的存储单元电路
- 在数字电路中,可以记录二进制数字信号“0”和“1”。
- 边沿触发,触发器的工作状态变化是在时钟信号的特定边沿(上升沿或下降沿)发生的。
所有触发器都在同一时钟信号的控制下操作,这使得它们非常适合在需要同步操作的应用中使用。
触发器能够存储一个位(bit)的信息,直到下一个时钟信号的到来。
FPGA内部的触发器都常会被配置成D触发器,
可根据其复位方式、复位电平和上电电平值的不同,分为不同的种类。
在Verilog语言中,我们定义的一个reg会在FPGA中被映射成一个或者一组触发器(Flip-Flop)。
// D触发器的 Verilog 代码
module D_Flip_Flop (
input wire clk, // 时钟信号
input wire d, // 数据输入
output reg q // 数据输出
);
always @(posedge clk) begin
q <= d; // 在时钟上升沿采样输入并更新输出
end
endmodule
1.3 ··· 锁存器与触发器、寄存器
-
触发器 和 锁存器,最大的区别是:
触发器通过时钟沿到来改变存储的输出状态,锁存器是通过电平变换来改变存储的输出状态。 -
在FPGA中,寄存器是常用的时序逻辑电路,是由 触发器 构成的。
一个触发器存储1位二进制数,由N个触发器可以构成N位的寄存器。
// 8位寄存器的 Verilog 代码
module Register (
input wire clk, // 时钟信号
input wire [7:0] d, // 8位数据输入
output reg [7:0] q // 8位数据输出
);
always @(posedge clk) begin
q <= d; // 在时钟上升沿同步更新所有位的数据
end
endmodule
1.4 ··· 补充:缓冲器(Buffer,不具有存储功能)
- 缓冲器(Buffer)在数字电路中是一个简单的电路元件,用于隔离或驱动信号。
- 缓冲器不具有存储功能,只是简单地传输信号,并可以增强信号强度。
// 示例:缓冲器的 Verilog 代码:
module Buffer (
input wire in, // 输入信号
output wire out // 输出信号
);
assign out = in; // 直接传输输入信号到输出
endmodule
Ch.2: 7系列中的存储单元(Storage Elements)
2.1 ··· 存储单元(Storage Elements)
每个Slice有8个存储单元。
后面4个可以配置为 边沿触发D型触发器(flip-flops) 或 电平敏感的锁存器(latches)。
其中,D输入可以被LUT6的输出驱动,或者AX、BX、CX或DX进行驱动。
当配置为latch且CLK为Low时,latch是”透明”的。(即,此时锁存器不在锁存状态)
前面4个存储单元只能配置为 边沿触发D型触发器。
其中,D输入可以被LUT6的O5输出驱动,或被AX、BX、CX或DX进行驱动。
当 后面4个存储单元 被配置为latch时,这 前面4个存储单元 则不能被使用。(有点浪费了)
《UltraScale FPGA 可编程逻辑块(CLB)》虽然是基于文档ug574的,但是却指出了:在AMD FPGA中,不论使用同步复位还是异步复位,都不会增加额外的逻辑资源消耗,截图如下,供道友们参考下。
2.2 ··· 控制信号(Control Signals)
对于这段描述,可以从图2-5看出,前4个、后4个存储单元的CLK、CE、SR信号时分别相连的。
”能通过配置SRHIGH SRLOW 来控制用户复位后的的电平,与输入SR的关系如下图所示,从中我们还可以看到SR都是高电平有效的,这也是为啥XILINX 7系推荐高电平复位,低电平复位的话再前面还要插个LUT做反相器而造成浪费资源。(这里的用户复位通常指的是普通IO复位,如自定义用户按键复位) 。“(《FPGA从入门到精通(6) - 存储单元&CLB总结》)
SRLOW:表示触发器在SR置位时(即用户复位),触发器的值为0
SRHIGH:表示触发器在SR置位时(即用户复位),触发器的值为1
INIT0:表示触发器在上电或者全局复位时初始化值为0
INIT1:表示触发器在上电或者全局复位时初始化值为1
这段原文,表明了SRLOW属性默认设置为“INIT0”、SRHIGH属性默认设置为“INIT1”。
参数SRLOW、SRHIGH、INIT0、INIT1的关系如下所示:
module test(
input clk,
input rst_n,
input i0,
input i1,
output o0,
output o1
);
reg INIT1 = 1'b1;
reg INIT0 = 1'b0;
reg SRHIGH,SRLOW ;
always@(posedge clk)begin
if( rst_n ) begin
SRHIGH <= 1'b1;
SRLOW <= 1'b0;
end else begin
SRHIGH <= i0;
SRLOW <= i1;
end
end
assign o0 = SRHIGH ;
assign o1 = SRLOW ;
endmodule
触发器有以下四种配置模式,且同步使能、异步复/置位较为常用。
而锁存器的配置模式如下截图所示。
2.3 ··· 触发器原语
Ch.3: 关于锁存器(Latch)的探讨
锁存器主要有以下几个缺点:
1、毛刺问题:
锁存器对毛刺不敏感,很容易在信号上产生毛刺。
2、时钟问题:
在多锁存器系统中,由于缺乏全局时钟信号,可能难以实现数据的精确同步,这可能导致数据不一致。而且,不容易进行静态时序分析。
因此,在绝大多数FPGA的代码设计中,我们应尽量避免锁存器的产生。
3.1 ··· 关于规避锁存器(Latch)
- 在代码层面导致 Latch 出现的原因为:
在组合逻辑中,if 或 case 语句描述不完整,如 if 缺少 else 分支、case 缺少 default 分支,会导致代码在综合过程中出现 latch。 - 解决办法:
在组合逻辑中,保持 if 或 case 语句的完整性描述,如:if 语句带 else 分支、case 带 default 分支。
代码缺少完整条件的描述,编译工具会认为在此情况下输出值保持不变,就推断出了锁存器。
3.3.1 ··· 组合逻辑中 if 语句描述
3.3.1.1 ··· 条件语句 if 缺少 else 分支时
module if_latch(
input a,
output reg y
);
always @(*) begin
if(a==1)
y=1;
//else
// y=0;
end
endmodule
此时, RTL ANALSIS视图为:
此时, 经过SYNTHESIS后的视图为:
由上述RTL ANALSIS视图、SYNTHESIS后的视图,
均可看出在此过程中,产生了Latch。
3.3.1.2 ··· 条件语句 if 补充 else 分支时
module if_latch(
input a,
output reg y
);
always @(*) begin
if(a==1)
y=1;
else
y=0;
end
endmodule
此时, RTL ANALSIS视图为:
此时, 经过SYNTHESIS后的视图为:
由上述RTL ANALSIS视图、SYNTHESIS后的视图,
均可看出在此过程中,没有产生Latch。
3.3.2 ··· 组合逻辑中 case 语句描述
3.3.2.1 ··· 条件语句 case 缺少 default 分支时
module if_latch(
input a,
input b,
output reg y
);
always @(*) begin
case(a)
0:y = b;
// default:y = 0;
endcase
end
endmodule
此时, RTL ANALSIS视图为:
此时, 经过SYNTHESIS后的视图为:
由上述RTL ANALSIS视图、SYNTHESIS后的视图,
均可看出在此过程中,产生了Latch。
3.3.1.2 ··· 条件语句 case 补充 default 分支时
module if_latch(
input a,
input b,
output reg y
);
always @(*) begin
case(a)
0:y = b;
default:y = 0;
endcase
end
endmodule
此时, RTL ANALSIS视图为:
此时, 经过SYNTHESIS后的视图为:
由上述RTL ANALSIS视图、SYNTHESIS后的视图,
均可看出在此过程中,没有产生Latch。
3.2 ··· FPGA中的锁存器(Latch)
通过第二章,可知FPGA中依旧有锁存器。
把 Slice 中的8个存储单元(Storage Elements)配置成4个锁存器, LDCE和LDPE两种类型。
LDCE = Latch + 一个 异步复位 端口,复位后输出是0。
LDPE = Latch + 一个 异步置位 端口,复位后输出是1。
因此,网上的一种说法(FPGA中只有LUT和FF的资源,没有现成的Latch,所以如果要用Latch,需要更多的资源来搭出来。),这是错误的。
那么,为什么FPGA中还要保留锁存器呢?
- 1、存储单元可以根据需要被配置成触发器或者锁存器,并不影响FPGA的资源。
- 2、在一些应用中,锁存器还是被需要的。
“锁存器虽然在FPGA中不怎么被使用,但在CPU中却很常见,因为锁存器比Flip-Flop快很多。”(《大家一致避免使用的锁存器为什么依然存在于FPGA中?我们对锁存器有什么误解?》)
Ch.4: 写在最后
4.1 ··· 总结
一开始的目的只是为了弄懂ug474上面关于存储单元(Storage Elements)的这部分东西,但是,随着了解的深入,感觉自己对锁存器、触发器相关的知识感到很模糊,便回过头来进行了学习、巩固。然后了解到锁存器的缺点、产生条件、在FPGA中的存在。
在上述过程在,能感觉到自己的相关知识变得相互联系了起来。当然,这也离不开一些博主、大佬的帖子、博客的归纳与总结。本博文只是在结合ug474的基础上,对那些帖子、博文做了一次汇总性的归纳与总结而已,希望能帮助到更多的人。如有问题,欢迎在评论区进行讨论与交流。
4.2 ··· 参考文献
关于锁存器、触发器的种类、电路原理、工作特性,本文并没有做深入的探讨。
打算在后续学习建立时间(Setup Time)、保持时间(Hold Time)时再去根据情况电路本身。
在查阅资料的过程中,看到了一些不错的锁存器、触发器、存储单元相关的博客,如下所示。
1、锁存器
《数数字电路基础—锁存器》
《一文搞懂锁存器和触发器》
《大家一致避免使用的锁存器为什么依然存在于FPGA中?我们对锁存器有什么误解?》
《什么是好的FPGA编码风格?(3)–尽量别用锁存器Latch》
2、触发器
《数字系统电路—触发器》
《数字电路基础–触发器》
《硬件基础-快速了解触发器》
《【第五章】锁存器和触发器》,这个总结的比较全面一点
3、存储单元
《ug474_7Series_CLB.pdf》
《FPGA从入门到精通(6) - 存储单元&CLB总结》
《从底层结构开始学习FPGA(3)----存储单元之触发器、寄存器与锁存器》
《UltraScale FPGA 可编程逻辑块(CLB)》