从零开始的FPGA探索之旅—探秘CLB_fpga clb
从零开始的FPGA探索之旅—探秘CLB
- 1.什么是CLB--理论
-
- 1.1SLICEM和SLICEL
-
- 1.1.1SLICEL图
- 1.1.2SLICEM图
- 1.2LUT
- 1.3FF
- 1.4MUX
- 1.5CARRY4
- 2.LUT是如何进行查表的
- 3 参考文献
文章内容是基于作者学习过程中的个人理解和总结,记录这些内容是为了自己后续复习或参考。笔者致力于理清学习脉络,书写更全面的学习手册,以后会不定时更新。如果觉得有用,不妨点点大拇指,读者的认可也是笔者前进的动力。
打开Vivado软件点击open elaborated design->device,我们可以看到FPGA的整体硬件结构(如下图),由不同颜色的块组成,这些块分别完成不同的功能和任务。这些块主要分为三大部分:第一部分是实现逻辑电路的逻辑要素、第二部分是和外部进行输入/输出的要素,第三部分是连接前两种要素的布线要素。还有其他的诸如时钟管理模块、存储资源、数字信号处理模块、高速串行收发器、嵌入式处理器核等等。而笔者从头开始,也是时间和精力有限,挑选当前学习最重要的部分(CLB),深入了解其底层原理。而其他的部分就在以后用到需要深挖的时候再去了解了,整个FPGA底层硬件的原理都可以从Xilinx(现AMD)官方网站上查找(UG470-UG480),这一系列的文档都详细的对FPGA底层各个功能模块作出了详细介绍。
1.什么是CLB–理论
在官方文档UG474中这样写到:一个CLB( configurable logic block,可编程逻辑块)里面包括了 2 个 Slices,每个 Slice 又是由 4 个 6输入LUT(Look-Up Table,查找表)、8 个触发器(Flip-Flop)、三个多路复用器( Multiplexers: F7AMUX, F7BMUX, and F8MUX )、一个进位链(CARRY4)组成(如下图)。
1.1SLICEM和SLICEL
从图中可以看出,左上角是当前CLB处于芯片中的位置。并且CLB中的2 个 Slices还并不一样,分为:SLICEM(存储片,memory)和SLICEL(逻辑片:logic),而SLICEM的功能更加强大,可配置为分布式RAM存储数据或者是移位寄存器进行数据移位。那么在Xilinx-7系列的芯片中,SLICEM占整个Slice的三分之一,从下表中可以看出数据。(重点:根据需求,能够准确的选型,看的就是不同型号的FPGA板上资源量)
1.1.1SLICEL图
1.1.2SLICEM图
从以上两图中可知,SLICEL和SLICEM的最主要区别在于LUT是否有读写IO口。那么在SLICEM中,一个6输入的LUT最大可配置存储容量为2^6也就是64bit。比如上表中型号7K07T的芯片,有3350个SLICEM,容量为:
3350 X 4 X 64bit = 857.600Kb。
官方文档中描述LUT可如下配置:
- Single-Port 32 x 1-bit RAM
- Dual-Port 32 x 1-bit RAM
- Quad-Port 32 x2-bit RAM
- Simple Dual-Port 32 x 6-bit RAM
- Single-Port 64 x 1-bit RAM
- Dual-Port 64 x 1-bit RAM
- Quad-Port 64 x 1-bit RAM
- Simple Dual-Port64 x 3-bit RAM
- Single-Port 128 x 1-bit RAM
- Dual-Port 128 x 1-bit RAM
- Single-Port 256 x 1-bit RAM
一个SLICEM最大可配置256bit 的RAM,而一个LUT64bit,需要四个LUT才行,而实现LUT连接的是MUX,如下图所示。至于如何编程,笔者目前还没有学到那个阶段,后面再行补充。
1.2LUT
本来LUT这一小节应该提在SLICE前面写得,一步一步叠加嘛,但笔者写博客还不太熟练,没有对图像命名,网站还是没office好用。LUT 本质上就是一个 RAM,它把数据事先写入 RAM 后, 每当输入一个信号就等于输入一个地址进行查表,找出地址对应的内容,然后输出。其结构如下图所示:
上图中是一个6输入的查找表,A1-A6为输入,O5、O6为输出,一个查找表可实现的功能多种多样,如:
- 六输入功能使用: A1-A6 输入, O6 输出
- 两个五输入或更少的输入使用: A1–A5 输入, A6 驱动高, O5 和 O6 输出
- 联合多路复选器 MUX:用于 生成七输入和八输入
1.3FF
FPGA中的触发器类型为D触发器,每一个Slice模块中有8个触发器,其中有四个触发器即可以配置为D触发器又可以配置为锁存器。官方文档中描述: D 输入可以通过AFFMUX,BFFMUX,CFFMUX 或 DFFMUX 的 LUT 输出直接驱动,也可以通过 AX,BX,CX 或 DX 输入绕过函数发生器的 BYPASS slice 输入直接驱动。 当配置为锁存器时,当 CLK 为低电平时,锁存器是透明的。而我们在编写程序时应当尽量避免锁存器产生,否则会发生不可预测的错误,笔者有一次写状态机时,没有补全else语句,仿真波形准确的,但就是上板现象错误,ILA抓取了好久的信号才找到原因,一定要形成良好的编程风格呀,可以避免好多小错误。
那么它到底是如何工作的呢?
上升沿触发:当时钟信号从低电平变为高电平时,D触发器捕获输入 D 的值并更新输出 Q。
下降沿触发:当时钟信号从高电平变为低电平时,D触发器捕获输入 D 的值并更新输出 Q。
也就是说当时钟clk跳变的时候,输入的值给到了输出,时钟不变输出不变。
1.4MUX
MUX(Multiplexer)多路复选器、多路复用器、多路选择器、数据选择器,叫法不一样,其实是同一个东西。它是如何工作的呢?举个例子二选一多路选择器:
A、B作为输入,Y作为输出,SEL信号作为控制信号(对于复选器也是输入),控制输出Y是等于A还是B。功能非常好理解,但重点不是这个,我输入的信号增加之后,我在FPGA上是如何实现的,这才是重点。
分析:
- 两个输入,SEL一位就能控制(0、1)
- 三个输入,SEL需要两位控制(00、01、10)、
- 四个输入,SEL需要两位控制(00、01、10、11)
- 以此类推,8位输入SEL需要三位控制,16位需要四位控制
在FPGA中还有LUT的,一个LUT有6个输入,四个作为输入信号2个作为输出信号,那么四输入以下的复选器LUT就能实现了。那么超过了四输入,想要在FPGA上实现,就需要用到MUX了。一个Slice中有三个MUX,最多可组合位16位输入的多路复选器:
图中16选1的复选器,由四个4选1的复选器加上3个MUX共同组成。第一步:由4选1复选器选择O6所输出的值,第二步;通过F7AMUX选择输出Ao6的值还是Bo6的值,CD同理,第三步:由F8BMUX选择输出是来自F7AMUX还是F7BMUX的值。16选1的复选器工作就是如此,层层递进,有点儿类似咱们打比赛的感觉哈,层层筛选。
1.5CARRY4
进位链,肯定是有进位需求才会用到,在作运算的时候结果溢出了会产生进位。在FPGA中如何实现的呢?答案是CARRY4,它的接口如下:(以下都以加法举例)
DI
: 4位输入,来自于LUT的输出,是两个加数中的任意一个;
S
:4位输入,来自于LUT的输出,两个加数的异或;
CYINIT
:1位输入,控制作加法还是减法,为0作加法,为1作减法;
CI
: 1位输入,来自于前一个进位链的最高进位CO[3],电路中的第一个进位链为0;
O
:4位输出,4位实际加法运算结果输出;
CO
:4位输入,每一位加法运算进位输出;
内部结构图:
这内部是如何工作的呢?以一个实际的案例进行分析:
MUXCY为多路选择器:S = 0 选择左边输出,S = 1选择右边输出;
//实现A + B;A = 1011;B = 1100;S=A^B = 0111; //S等于A异或B;DI = A = 1011; //随意给A或BCYINIT = 0; //加法运算0CIN = 0; //第一个进位链为0//看图从下往上计算 //第一个全加器: O[0] = S[0]^CIN = 1^0 = 1; CO[0] = CIN = 0; //S[0]=1,选右边 //第二个全加器: O[1] = S[1]^CO[0] = 1^0 = 1; CO[1] = CO[0] = 0 ; //S[1]=1,选右边 //第三个全加器: O[2] = S[2]^CO[1] = 1^0 = 1; CO[2] = CO[1] = 0 ; //S[2]=1,选右边 //第四个全加器: O[3] = S[3]^CO[2] = 0^0 = 0; CO[3] = D[3] = 1 ; //S[3]=0,选左边 //最终结果 O[3:0] = 0111; CO[3:0] = 1000; A + B = 10111;//结果正确
而这仅仅是两个个四位数据相加,想要更多位数的相加就需要用到更多的进位链了。例如一个计时1S的计时器,系统时钟为50MHz,那么我们就需要定义一个28位的计数器来进行计数,一个进位链是四位,最终需要7个进位链进行级联,完成28位计数器的计数。
加法器部分可看该链接:CARRY链接
2.LUT是如何进行查表的
用一个小小的加法器例子进行剖析
module block_top2( input wire sys_clk , input wire reset_n , input wire a , input wire b , output reg [1:0] add );always@(posedge sys_clk or negedge reset_n)begin if(!reset_n) add <= 2\'d0; else add <= a + b;end
程序不用多说,直接上综合后的电路图:
执行add <= a + b;这条语句,首先进行查表[LUT2],然后再将结果通过D触发器寄存输出。
在软件中选中LUT可以查看LUT的初始值,add[0]为4’h6、add[1]为4’h8:
根据输入(I0/I1)我们就可以选择对应地址的值进行输出,把所有情况列出来:
分析(a接到LUT第0位,b接到LUT第1位):
-
a=0,b=0,对add[0]_i_1查表,输出地址00中存的值0,给到add第0位;
对add[1]_i_1查表,输出地址00中存的值0,给到add第1位,输出为00; -
a=1,b=0,对add[0]_i_1查表,输出地址01中存的值1,给到add第0位;
对add[1]_i_1查表,输出地址01中存的值0,给到add第1位,输出为01; -
a=0,b=1,对add[0]_i_1查表,输出地址10中存的值1,给到add第0位;
对add[1]_i_1查表,输出地址10中存的值0,给到add第1位,输出为01; -
a=1,b=1,对add[0]_i_1查表,输出地址11中存的值0,给到add第0位;
对add[1]_i_1查表,输出地址11中存的值1,给到add第1位,输出为10;
通过以上的分析,我们就能知道LUT到底是如何进行工作的啦!而这,就是LUT 实现逻辑单元功能的本质。
3 参考文献
孤独的单刀作者的文章写得很好,对于弄懂FPGA的底层结构很有帮助,强烈推荐一波。
老规矩米联客的教程不能忘,米联客官网上也有很多资源,米联客的板子做得真的很好!!!
觉得有用三连一下哇!!!