本帖最后由 FPGA课程 于 2024-8-15 14:46 编辑
软件版本:无
操作系统:WIN10 64bit
硬件平台:适用所有系列FPGA
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述FPGA是一个非常神奇的芯片,是无所不能的芯片,掌握FPGA就是宝剑在手,但是宝剑在手的人,却不一定做可以天下无敌,因为宝剑只是一把好剑,关键的还是得有会用宝剑的人。比如你拿着宝剑去切猪肉,肯定比不上卖猪肉的剁刀。所以我们得先了解FPGA,才能更有利于我们正确使用FPGA,发挥FPGA的作用。 对于不少FPGA工程师来说,虽然会采用相关的FPGA编程语言和工具对FPGA编程开发。但是忽略了对FPGA底层的硬件知识的了解,在FPGA的编程之路上,可能会遇到很多自己搞不清楚的疑问。其实,我们并不需要花费太多时间去了解FPGA的内部的硬件结构,而是重点分析FPGA的CLB资源、BRAM、时钟资源、IO资源。 由于CLB资源对我们学习FPGA的基本编程有很大关系,所以本文重点介绍CLB资源。CLB是FPGA的基本逻辑单元,我们可以把CLB理解为FPGA的细胞单元。每个CLB里面包括了2个Slices,每个Slice又是由4个LUT(查找表),8个寄存器以及一些逻辑门组成。掌握这些基本单元的功能应用,以及功能特性,可以为我们未来对采取代码编程的时候,更好的实现代码和硬件的最佳配合。 2认识FPGA的容量很多人搞了很久的FPGA都没搞清楚FPGA的容量是什么概念,这是一件非常可笑的事情,你说你是一个FPGA程序员,连FPGA的资源概念都搞不清楚,如何才能正确选型FPGA呢?当我们选型的时候,我们需要评估FPGA的资源。下表是来自于XILINX 官方文档关于资源的描述。如果你看到这个表格还不能明白其中的含义,那么下面的内容你一定不要错过,作为一个专业的FPGA开发者,你是必须要知道的内容。
上表中给出的是K7系列FPGA的资源概述,我们和别人交流FPGA资源的时候,或者选型FPGA的时候需要重点考虑以上资源。接下来,笔者对其中重点的资源进行介绍。 2.1Slices单元一个CLB包括了2个Slices,但是我们通常描述FPGA资源的时候不会说有多少CLB。Slices是构成CLB的基本单元。所以知道Slices的数量就可以知道FPGA的“大概”逻辑资源容量了。”大概”?,为什么不是精确的数量,作为技术工程师,听到这种不确定的描述,心真的非常不爽。但是上表给出的还真实一个概述,不够精确,所以我们需要进一步分析。 一个Slice等于4个6输入LUT+8个触发器(flip-flop)+算数运算逻辑,每个Slice的4个触发器(虽然有8个flip-flop,但是每个LUT分配一个flip-flop)可以配置成锁存器,这样会有4个触发器(flip-flop)未被使用。 对于CLB来说,里面的Slice居然还不是长一样,有2中类型的Slice。一种是SLICEL,另外一种是SLICEM。SLICEM的功能更强大,SLICEM可以当作分布式RAM或者ROM,或者实现移位寄存器。 2.2Distributed RAM上表中,Max Distributed RAM就是由SLICEM数量决定的。我们再看下一张表:
对于XILINX 7系列的FPGA来说,SLICEM约占三分一的Slices。上表中我们可以看到精确的数据。比如对于7K325T的FPGA,有16000个SLICEM那么,16000X4X64bit=4096,000b(4000kb)的distributed RAM或者2000kb的Shift Register。 2.3Logic Cells我们经常说到FPGA容量是多少,FPGA器件的容量通常用逻辑单元来衡量,这在逻辑上等同于经典的4输入LUT和触发器。 7系列FPGA逻辑单元和6输入LUT的数量之比为1.6:1,这是相对于经典的4输入LUT而言得出的。所以,逻辑单元和Slice的数量比是6.4:1。我们在前面第一张表格
仍然以XC7K325T为例子,50950X6.4=326080。通过计算可以看到Slices的数量对应的逻辑单元的数量,这个计算就是等效计算,因为传统的CPLD是逻辑门电路,而现在的FPGA是以查找表的方式实现了逻辑门的等效功能。 3认识FPGA Slice的功能每个slice包含: •四个逻辑函数生成器(查找表) •8个存储单元 •多功能多路复选器 • 快速进位逻辑 FPGA的逻辑、算术和ROM功能就是靠Slice实现,其中slice又分SLICEL和SLICEM,SLICEM的功能更强大,除了具备SLICEL的所有功能,还能实现分布式RAM、ROM和实现移位逻辑。 3.1SLICEL
3.2SLICEM
对比SLICEM和SLICEL,可以看到以上结构图中,他们的差异在查找表上,SLICEM的查找表结构功能更复杂。SLICEM增加了写操作的控制信号,以及移动位进位的功能。 3.3LUT查找表由于FPGA需要被反复烧写,它实现组合逻辑的基本结构不可能像ASIC那样通过固定的与非门来完成,而只能采用一种易于反复配置的结构。查找表可以很好地满足这一要求,目前主流FPGA都采用了基于SRAM工艺的查找表结构。LUT本质上就是一个RAM。它把数据事先写入RAM后, 每当输入一个信号就等于输入一个地址进行查表,找出地址对应的内容,然后输出。后面我们编程后,可以看到软件综合工具,对于使用了多少查找表的计算。所以FPGA的程序是事先把相关的查找表中填入参数,然后通过查找表实现了逻辑功能。我们经常遇到有人提问FPGA的可重新配置的问题,比如我们程序运行后,需要对部分的代码重新配置代码,理论上我们只要解释代码后,把相关的参数写入指定的FPGA逻辑就可以。这个课题不是我们这里关心的,如果有人要研究可重部分配置,必然需要掌握FPGA的硬件构造。 7系列FPGA中的函数发生器通过六输入查找表(LUT)实现。slice的4个查找表(A,B,C和D)中的每一个都有六个独立输入(A输入A1至A6)和两个独立输出(O5和O6)。
查找表可以实现的函数功能: •任意定义的六输入布尔函数 •两个任意定义的五输入布尔函数,只要这两个函数共享公共输入 •两个任意定义的布尔函数(3个和2个或更少输入) 六输入功能使用: •A1-A6输入 •O6输出 两个五输入或更少的输入使用: •A1–A5输入 •A6驱动高 •O5和O6输出 多路复选器F7AMUX,F7BMUX和F8MUX •F7AMUX:用于从LUT A和B生成七个输入 •F7BMUX:用于从LUT C和D生成七个输入 •F8MUX:用于组合所有LUT以生成八个输入 什么是6输入,什么是5输入或者更少,以上是什么意思,你明白了吗? 对于6输入功能,查找表可以实现2^6次方,64个存储空间的寻址查表,而且只能输出Q6。显然很多时候我们只有2个输入、3个输入、4个输入、5个输入的情况发生,那么岂不是很浪费LUT了。所以通过设置A6为高,这样低于6输入的查表运算可以通过Q5和Q6同时完成查表输出。但是这里依然有一个问题,就是同时查表的时候A1~A5的输入是相同的,虽然资料里面没有详细说明,但是我们可以通过内部结构推测,相同的输入可以查到不同的表的位置,所以Q5和Q6可以输出2个独立的计算结果。这样有利于提高资源的利用率。 6输入LUT查表原语: LUT6 #(
.INIT(64'h0000000000000000) // Specify LUT Contents
) LUT6_inst (
.O(O), // LUT general output
.I0(I0), // LUT input
.I1(I1), // LUT input
.I2(I2), // LUT input
.I3(I3), // LUT input
.I4(I4), // LUT input
.I5(I5) // LUT input
);
比如代 码 assign DQ = A[5:0]== 6’d1 ? 1’b1 : 1’b0
可以实现6输入查找表,实现逻辑功能。 通过对查找表的认识,我们知道了FPGA如何实现算术逻辑功能,以及我们也可以推测FPGA如果要实现软件实时自动编程应该会采用SLICEM资源。虽然现在笔者还没能力验证这一点。所以如果作为读者的你有有机会在这个领域研究,希望也能和笔者一起分享FPGA软件可以部分重编程的方案。 3.4寄存器、触发器、锁存器
每个slice有8个存储元素,如上图所示。
其中四个为DFF/LATCH,可以配置为边沿触发D型触发器或电平敏感锁存器输入上图。 D输入可以通过AFFMUX,BFFMUX,CFFMUX或DFFMUX的LUT输出直接驱动,也可以通过AX,BX,CX或DX输入绕过函数发生器的BYPASS slice输入直接驱动。 当配置为锁存器时,当CLK为低电平时,锁存器是透明的。
另外四个为仅为DFF,它们只能配置为边沿触发的D型触发器。 D输入可以由LUT的O5输出驱动,也可以由AX,BX,CX或DX输入的BYPASS slice输入驱动。 当原始的四个存储单元配置为闩锁时,这四个附加的存储单元将无法使用。 触发器代码如下,当敏感型号CLK上升沿到来把B值赋给A - always@(posedge CLK)
- A <= B;
复制代码
锁存器代码如下,当C为1时候把B赋值给A,锁存器应该尽力避免,因为会影响到电路时序收敛,对毛刺敏感,容易导致意想不到的错误。 - always@(*)
- if(C==1’b1)
- A <= B;
复制代码
避免锁存器的代码 - always@(*)
- if(C==1’b1)
- A <= B;
- else
- A <= 1’b0;
复制代码
3.5分布式RAM(Distributed RAM)在前面的章节中我们已经知道SLICEM资源可以实现分布式RAM。这里再次介绍分布式RAM的功能单元。笔者这里先给出可以实现的RAM类型: •单口RAM •双端口 •简单的双端口 •四端口 下表给出了通过1 SLICEM中的4个LUT可以实现的RAM类型
一下列举部分RAM的形式加深大家的理解,但是注意,以下列举的并不是所有的RAM形式: 1) 32 X 2 Quad Port Distributed RAM (RAM32M) 在前面的内容部分,我们介绍过把6输入LUT 当作2个5输入LUT使用,在这里,就可以同一个LUT实现数据位宽的增加。对于32X2的4口RAM,如下图所以,代表了输入和输出的数据位宽都是2bit,深度是32。4口代表了4个LUT都处于工作状态,它们的DI0和DI1,共用,第一个LUT的A1-A5和WA1-WA5相连接,并且4个LUT的WA1-WA5全部接到了一起。所以我们可以同时把数据写入到每个LUT的地址空间,然后可以分别读出每个LUT的数据。写操作是同步写入,读操作是异步读出。4口RAM实际上用的不多,我们重点看后面的单口RAM和双口RAM。
原语调用 - RAM32M #(
- .INIT_A(64'h0000000000000000), // Initial contents of A Port
- .INIT_B(64'h0000000000000000), // Initial contents of B Port
- .INIT_C(64'h0000000000000000), // Initial contents of C Port
- .INIT_D(64'h0000000000000000) // Initial contents of D Port
- ) RAM32M_inst (
- .DOA(DOA), // Read port A 2-bit output
- .DOB(DOB), // Read port B 2-bit output
- .DOC(DOC), // Read port C 2-bit output
- .DOD(DOD), // Read/write port D 2-bit output
- .ADDRA(ADDRA), // Read port A 5-bit address input
- .ADDRB(ADDRB), // Read port B 5-bit address input
- .ADDRC(ADDRC), // Read port C 5-bit address input
- .ADDRD(ADDRD), // Read/write port D 5-bit address input
- .DIA(DIA), // RAM 2-bit data write input addressed by ADDRD,
- // read addressed by ADDRA
- .DIB(DIB), // RAM 2-bit data write input addressed by ADDRD,
- // read addressed by ADDRB
- .DIC(DIC), // RAM 2-bit data write input addressed by ADDRD,
- // read addressed by ADDRC
- .DID(DID), // RAM 2-bit data write input addressed by ADDRD,
- // read addressed by ADDRD
- .WCLK(WCLK), // Write clock input
- .WE(WE) // Write enable input
- );
复制代码
2) 64 X 1 Single Port Distributed RAM (RAM64X1S) 对于64X1的单口RAM只要使用一个LUT就够了,地址A0-A5和WA0-WA5接到一起,当WE为1的时候为写操作,当WE为0的时候为读操作。
原语调用: - RAM64X1S #(
- .INIT(64'h0000000000000000) // Initial contents of RAM
- ) RAM64X1S_inst (
- .O(O), // 1-bit data output
- .A0(A0), // Address[0] input bit
- .A1(A1), // Address[1] input bit
- .A2(A2), // Address[2] input bit
- .A3(A3), // Address[3] input bit
- .A4(A4), // Address[4] input bit
- .A5(A5), // Address[5] input bit
- .D(D), // 1-bit data input
- .WCLK(WCLK), // Write clock input
- .WE(WE) // Write enable input
- );
复制代码3) 64 X 1 Dual Port Distributed RAM (RAM64X1D) 对于双口RAM,如下图所示,使用了2个LUT,第一个LUT的读写地址是连接到一起的,而第二个LUT的写地址和对一个LUT的写地址连接到一起,但是读地址是独立,所以双口RAM可以同时进行读和写操作。
原语调用: - RAM64X1D #(
- .INIT(64'h0000000000000000) // Initial contents of RAM
- ) RAM64X1D_inst (
- .DPO(DPO), // Read-only 1-bit data output
- .SPO(SPO), // Rw/ 1-bit data output
- .A0(A0), // Rw/ address[0] input bit
- .A1(A1), // Rw/ address[1] input bit
- .A2(A2), // Rw/ address[2] input bit
- .A3(A3), // Rw/ address[3] input bit
- .A4(A4), // Rw/ address[4] input bit
- .A5(A5), // Rw/ address[5] input bit
- .D(D), // Write 1-bit data input
- .DPRA0(DPRA0), // Read-only address[0] input bit
- .DPRA1(DPRA1), // Read-only address[1] input bit
- .DPRA2(DPRA2), // Read-only address[2] input bit
- .DPRA3(DPRA3), // Read-only address[3] input bit
- .DPRA4(DPRA4), // Read-only address[4] input bit
- .DPRA5(DPRA5), // Read-only address[5] input bit
- .WCLK(WCLK), // Write clock input
- .WE(WE) // Write enable input
- );
复制代码
4) 128 X 1 Single Port Distributed RAM (RAM128X1S) 理解了前面的内容,理解128X1的RAM就不难了,下图中可以看出来,通过2个LUT的组合使用可以串联实现更大深度的分布式RAM。下图中出现了F7BMUX的加入,F7BMUX可以用于LUT输出的选通。
原语调用: - RAM128X1S #(
- .INIT(128'h00000000000000000000000000000000) // Initial contents of RAM
- ) RAM128X1S_inst (
- .O(O), // 1-bit data output
- .A0(A0), // Address[0] input bit
- .A1(A1), // Address[1] input bit
- .A2(A2), // Address[2] input bit
- .A3(A3), // Address[3] input bit
- .A4(A4), // Address[4] input bit
- .A5(A5), // Address[5] input bit
- .A6(A6), // Address[6] input bit
- .D(D), // 1-bit data input
- .WCLK(WCLK), // Write clock input
- .WE(WE) // Write enable input
- );
复制代码5) 128 X 1 Dual Port Distributed RAM (RAM128X1D) 128x1的双口RAM需要4个LUT实现,正好是一个SLICEM,并且分别使用了F7BMUX和F7AMUX.
原语调用: - RAM128X1D #(
- .INIT(128'h00000000000000000000000000000000)
- ) RAM128X1D_inst (
- .DPO(DPO), // Read port 1-bit output
- .SPO(SPO), // Read/write port 1-bit output
- .A(A), // Read/write port 7-bit address input
- .D(D), // RAM data input
- .DPRA(DPRA), // Read port 7-bit address input
- .WCLK(WCLK), // Write clock input
- .WE(WE) // Write enable input
- );
复制代码6)256 X 1 Single Port Distributed RAM (RAM256X1S) 256x1的单口RAM需要4个LUT实现,也正好是一个SLICEM,并且分别使用了F7BMUX和F7AMUX以及一个F8MUX.
原语调用: - RAM256X1S #(
- .INIT(256'h0000000000000000000000000000000000000000000000000000000000000000)
- ) RAM256X1S_inst (
- .O(O), // Read/write port 1-bit output
- .A(A), // Read/write port 8-bit address input
- .WE(WE), // Write enable input
- .WCLK(WCLK), // Write clock input
- .D(D) // RAM data input
- );
复制代码 3.6分布式ROM(Distributed ROM)SLICEL中的每个LUT都可以实现64 x 1位ROM。提供三种配置:ROM64X1,ROM128X1和ROM256X1。 ROM内容会在每种设备配置中加载。 下表显示了每种ROM配置大小所占用的LUT数量。
- ROM32X1 #(
- .INIT(32'h00000000) // Contents of ROM
- ) ROM32X1_inst (
- .O(O), // ROM output
- .A0(A0), // ROM address[0]
- .A1(A1), // ROM address[1]
- .A2(A2), // ROM address[2]
- .A3(A3), // ROM address[3]
- .A4(A4) // ROM address[4]
- );
- ROM64X1 #(
- .INIT(64'h0000000000000000) // Contents of ROM
- ) ROM64X1_inst (
- .O(O), // ROM output
- .A0(A0), // ROM address[0]
- .A1(A1), // ROM address[1]
- .A2(A2), // ROM address[2]
- .A3(A3), // ROM address[3]
- .A4(A4), // ROM address[4]
- .A5(A5) // ROM address[5]
- );
- ROM128X1 #(
- .INIT(128'h00000000000000000000000000000000) // Contents of ROM
- ) ROM128X1_inst (
- .O(O), // ROM output
- .A0(A0), // ROM address[0]
- .A1(A1), // ROM address[1]
- .A2(A2), // ROM address[2]
- .A3(A3), // ROM address[3]
- .A4(A4), // ROM address[4]
- .A5(A5), // ROM address[5]
- .A6(A6) // ROM address[6]
- );
- ROM256X1 #(
- .INIT(256'h0000000000000000000000000000000000000000000000000000000000000000) // Contents of ROM
- ) ROM256X1_inst (
- .O(O), // ROM output
- .A0(A0), // ROM address[0]
- .A1(A1), // ROM address[1]
- .A2(A2), // ROM address[2]
- .A3(A3), // ROM address[3]
- .A4(A4), // ROM address[4]
- .A5(A5), // ROM address[5]
- .A6(A6), // ROM address[6]
- .A7(A7) // ROM address[7]
- );
复制代码
3.7移位寄存器(Shift Register)是不是有点累了,是不是有点云里雾里了?但是这些知识很少在书本里面出现,只要不是大家都容易获取到学习到的内容,你掌握了,你就是大神了,所以坚持住。打好FPGA的底层硬件基础,为我们成为FPGA技术大神添砖加瓦。 前来重点讲解了基于SLICELM实现的RAM,这里的移位寄存器功能也是非常重要的。 SLICEM中的LUT可以配置为32位移位寄存器,而无需使用slice中可用的触发器。 以这种方式使用,每个LUT可以将串行数据延迟1到32个时钟周期。 移入D(DI1 LUT引脚)和移出Q31(MC31 LUT引脚)线路将LUT级联,以形成更大的移位寄存器。 因此,SLICEM中的四个LUT被级联以产生高达128个时钟周期的延迟。 32位移位寄存器调用原语:
举例说明了在7系列FPGA Slice(一个配置为SRL的LUT)中实现的移位寄存器的时序特性
Slice SRL Timing Characteristics 如下图所示,1个SLICEM内的4个LUT级联后可以实现128bit的移位寄存器
3.8多路复选器(Multiplexers)7系列FPGA中的LTU和相关的多路复选器可以实现以下功能: •使用一个LUT的4:1多路复选器 •使用两个LUT的8:1多路复选器 •使用四个LUT的16:1多路复选器 4:1复选器 1个LUT可以配置为4:1多路复选器。 4:1多路复选器可以通过触发器在同一片中实现。 一个slice中最多可以实现四个4:1多路复选器,如下所示:
以LUTA为例不经过D触发器的对应代码: - always@(*)
- case (SELA[1:0])
- 2'b00: A = DATA_A[0];
- 2'b01: A = DATA_A[1];
- 2'b10: A = DATA_A[2];
- 2'b11: A = DATA_A[3];
- default:A = 1'bx;
- endcase
复制代码继续以LUTA为例经过D触发器的对应代码: - always@(posedge CLK)
- case (SELA[1:0])
- 2'b00: A <= DATA_A[0];
- 2'b01: A <= DATA_A[1];
- 2'b10: A <= DATA_A[2];
- 2'b11: A <= DATA_A[3];
- default:A < = 1'bx;
- endcase
复制代码8:1复选器 每个slice具有一个F7AMUX和一个F7BMUX。 F7AMUX和F7BMUX原语: - MUXF7 MUXF7_inst (
- .O(O), // Output of MUX to general routing
- .I0(I0), // Input (tie to LUT6 O6 pin)
- .I1(I1), // Input (tie to LUT6 O6 pin)
- .S(S) // Input select to MUX
- );
复制代码这两个多路复选器组合了两个LUT的输出,以形成多达13个输入(或8:1多路复选器)的组合功能。 一个slice中最多可以实现两个8:1 MUX,如下图所示。
以LUTA和LUTB组从的8:1复选器为例不经过D触发器的对应代码: - always@(*)
- if(AX)begin
- case (SELA[1:0])
- 2'b00: A = DATA_A[0];
- 2'b01: A = DATA_A[1];
- 2'b10: A = DATA_A[2];
- 2'b11: A = DATA_A[3];
- default:A = 1'bx;
- endcase
- end
- else begin
- case (SELB[1:0])
- 2'b00: B = DATA_B[0];
- 2'b01: B = DATA_B[1];
- 2'b10: B = DATA_B[2];
- 2'b11: B = DATA_B[3];
- default:B = 1'bx;
- endcase
- end
- assign AMUX = AX ? A : B;
复制代码以上是没有经过优化的代码,代码可以优化为: - always@(*)
- case ({AX,SELB[1:0]})
- 3'b000: AMUX = DATA_A[0];
- 3'b001: AMUX = DATA_A[1];
- 3'b010: AMUX = DATA_A[2];
- 3'b011: AMUX = DATA_A[3];
- 3'b100: AMUX = DATA_B[0];
- 3'b101: AMUX = DATA_B[1];
- 3'b110: AMUX = DATA_B[2];
- 3'b111: AMUX = DATA_B[3];
- default: AMUX = 1'bx;
- endcase
复制代码继续以以LUTA和LUTB组从的8:1复选器为例经过D触发器的对应代码: - always@(posedge CLK)
- case ({AX,SELB[1:0]})
- 3'b000: AMUX <= DATA_A[0];
- 3'b001: AMUX <= DATA_A[1];
- 3'b010: AMUX <= DATA_A[2];
- 3'b011: AMUX <= DATA_A[3];
- 3'b100: AMUX <= DATA_B[0];
- 3'b101: AMUX <= DATA_B[1];
- 3'b110: AMUX <= DATA_B[2];
- 3'b111: AMUX <= DATA_B[3];
- default: AMUX <= 1'bx;
- endcase
复制代码16:1复选器 每个slice都有一个F8MUX。 F8MUX原语: - MUXF8 MUXF8_inst (
- .O(O), // Output of MUX to general routing
- .I0(I0), // Input (tie to MUXF7 L/LO out)
- .I1(I1), // Input (tie to MUXF7 L/LO out)
- .S(S) // Input select to MUX
- );
复制代码F8MUX组合了F7AMUX和F7BMUX的输出,以形成多达27个输入(或16:1多路复选器)的组合功能。 一个slice中只能实现一个16:1多路复选器,如图下图所示。
16:1复选器不经过D触发器的对应代码: - always@(*)
- case ({BX,AX,SELB[1:0]})
- 4'b0000: AMUX = DATA_A[0];
- 4'b0001: AMUX = DATA_A[1];
- 4'b0010: AMUX = DATA_A[2];
- 4'b0011: AMUX = DATA_A[3];
- 4'b0100: AMUX = DATA_B[0];
- 4'b0101: AMUX = DATA_B[1];
- 4'b0110: AMUX = DATA_B[2];
- 4'b0111: AMUX = DATA_B[3];
- 4'b1000: AMUX = DATA_C[0];
- 4'b1001: AMUX = DATA_C[1];
- 4'b1010: AMUX = DATA_C[2];
- 4'b1011: AMUX = DATA_C[3];
- 4'b1100: AMUX = DATA_D[0];
- 4'b1101: AMUX = DATA_D[1];
- 4'b1110: AMUX = DATA_D[2];
- 4'b1111: AMUX = DATA_D[3];
- default: AMUX = 1'bx;
- endcase
复制代码16:1复选器经过D触发器的对应代码: - always@(posedge CLK)
- case ({BX,AX,SELB[1:0]})
- 4'b0000: AMUX <= DATA_A[0];
- 4'b0001: AMUX <= DATA_A[1];
- 4'b0010: AMUX <= DATA_A[2];
- 4'b0011: AMUX <= DATA_A[3];
- 4'b0100: AMUX <= DATA_B[0];
- 4'b0101: AMUX <= DATA_B[1];
- 4'b0110: AMUX <= DATA_B[2];
- 4'b0111: AMUX <= DATA_B[3];
- 4'b1000: AMUX <= DATA_C[0];
- 4'b1001: AMUX <= DATA_C[1];
- 4'b1010: AMUX <= DATA_C[2];
- 4'b1011: AMUX <= DATA_C[3];
- 4'b1100: AMUX <= DATA_D[0];
- 4'b1101: AMUX <= DATA_D[1];
- 4'b1110: AMUX <= DATA_D[2];
- 4'b1111: AMUX <= DATA_D[3];
- default: AMUX <= 1'bx;
- endcase
复制代码 3.9进位逻辑(Carry Logic)Slice中除了LUT,寄存器,触发器,锁存器外,还提供了专用的快速超前进位逻辑, 可以在slice中执行快速算术加法和减法。 CLB中的专用进位逻辑提高了算术功能(如加法器,计数器和比较器)的性能。包含简单计数器或加法器/减法器的设计会自动推断进位逻辑。如果是更复杂的乘法器可以使用单独的DSP48E1 Slice实现。 7系列FPGA CLB具有两个独立的进位链,如图下所示,1个CLB中的2个Slice各有1个CIN。
进位链可级联以形成更宽的加/减逻辑,如图下图所示。
进位链向上延伸,每个slice的高度为4bits。对于每个bit,都有一个进位多路复选器(MUXCY)和专用的XOR门,用于用选定的进位位加/减操作数。专用的进位路径和进位多路复选器(MUXCY)也可以用于级联LUT,以实现广泛的逻辑功能。如下图所示:
- CARRY4 CARRY4_inst (
- .CO(CO), // 4-bit carry out
- .O(O), // 4-bit carry chain XOR data out
- .CI(CI), // 1-bit carry cascade input
- .CYINIT(CYINIT), // 1-bit carry initialization
- .DI(DI), // 4-bit carry-MUX data in
- .S(S) // 4-bit carry-MUX select input
- );
复制代码进位链与功能生成器一起使用进位超前逻辑。有10个独立输入和8个独立输出: •输入 •S输入S0至S3 -进位超前逻辑的“传播”信号 -来自LUT的O6输出 •DI输入-DI1至DI4 -进位超前逻辑的“生成”信号 -来自LUT的O5输出 -创建乘数 -或SLICE的BYPASS输入(AX,BX,CX或DX) • CYINIT -进位链的第一位的CIN -0表示加 -1表示减 -动态第一进位位的AX输入 •CIN -级联Slice以形成更长的进位链 •输出 •O输出-O0至O3 -包含加法/减法之和 •CO输出-CO0到CO3 -计算每一位的进位 -CO3连接到Slice的COUT输出以通过级联多个Slice形成更长的进位链-创建加法器/累加器 4其他资源对于入门掌握FPGA,了解FPGA的CLB是必须的,当然除了CLB还有IO资源、时钟资源、高速GTP/GTX资源等,我们在后续的学习中,在相关的课程中继续解密FPGA的其他资源结构。 5总结本文对于如何选型FPGA,以及如何使用FPGA的LUT资源起到理论支持作用,在后面编程的时候,也有利于加深理解硬件编程语言和硬件电路的对应关系。
|