Open cisen opened 2 years ago
从底层结构开始学习FPGA----时钟结构 https://wuzhikai.blog.csdn.net/article/details/121895673
7系列FPGA的时钟资源通过专用的全局和区域I/O和时钟资源管理复杂和简单的时钟需求。时钟管理块(CMT)提供时钟频率合成、减少偏移和抖动过滤等功能。非时钟资源,如本地布线,则在设计时钟功能时不推荐使用。
全局时钟树允许同步元素的时钟跨越设备 I/O时钟和区域时钟树允许最多三个垂直相邻的时钟区域的时钟 每个时钟管理块(CMT)包含一个混合模式时钟管理器(MMCM)和一个锁相环(PLL),位于I/O列旁边的CMT列 为了时钟使用,每个7系列设备被划分为多个时钟区域。
时钟区域的数量随着设备大小的不同而变化,最小的设备上有一个时钟区域,最大的设备上有24个时钟区域。 一个时钟区域包括所有同步元素(例如:CLB、I/O、串行收发器、DSP、块RAM、CMT),该区域跨越50个CLB和一个I/O BANK(50个I/O),其中心有一个水平时钟行(HROW)。 每个时钟区域从HROW向上跨越25个CLB,从HROW向下跨越25个CLB,并且水平地跨越设备的每一侧
每个I/O BANK包含有支持时钟(clock-capable)的输入引脚,用于将用户时钟带到7系列FPGA时钟布线资源。与专用时钟缓冲器相比,支持时钟的输入将用户时钟送到:
全局时钟线在同一设备的上/下半部 I/O时钟线在相同的I/O BANK和垂直相邻的I/O BANK内 在相同的时钟区域内的区域时钟线和垂直相邻的时钟区域 CMT,在有限情况下的同一时钟区域,和垂直相邻的时钟区域 每个7系列单片设备有32个全局时钟线,可以提供时钟和控制信号到整个设备中的所有时序资源。全局时钟缓冲器(BUFGCTRL,在本用户指南中被简化为BUFG)驱动全局时钟线,而且必须用于访问全局时钟线。使用时钟区域中的12个水平时钟线则可以做到每个时钟区域最多可以支持12个这样的全局时钟线。
全局时钟缓冲:
可以用作时钟使能电路来使能或禁用跨越多个时钟区域的时钟 可以作为无故障的多路复用器: 选择两个时钟源 更换故障的时钟源 经常被CMT驱动来用于: 消除时钟分布延迟 调整相对于另一个时钟的时钟延迟
水平时钟缓冲器(BUFH/BUFHCE)允许通过水平时钟行访问单个时钟区域中的全局时钟线。它也可以用作时钟使能电路(BUFHCE)来独立地使能或禁用跨越单个时钟区域的时钟。使用每个时钟区域中的12条水平时钟线则可以做到每个时钟区域最多可以支持12个时钟。
每个7系列FPGA都有区域和I/O 时钟树,它们可以为一个时钟区域内的所有时序资源提供时钟。每个设备还具有多时钟区域缓冲器(BUFMR),BUFMR允许区域时钟和I/O 时钟跨越三个垂直相邻的时钟区域。
I/O时钟缓冲器(BUFIO)驱动I/O时钟树,提供对同一I/O库中所有时序I/O资源的时钟访问。 区域时钟缓冲器(BUFR)驱动区域时钟树,该时钟树驱动同一时钟区域内的所有时钟目的地,可以通过编程来划分传入的时钟速率。 结合IOB中的可编程序列化器/反序列化器(参见UG471, 7系列fpga的SelectIO资源用户指南中的Advanced SelectIO逻辑资源章节),BUFIO和BUFR时钟缓冲区允许源同步系统在不使用额外逻辑资源的情况下跨时钟域。 当与相关的BUFR和BUFIO一起使用时,可以使用多时钟区域缓冲区(BUFMR)驱动相邻时钟区域的区域时钟、I/O时钟树和I/O BANK 最多支持四个独特的I/O时钟和四个独特的区域时钟。 高性能时钟路由将时钟管理块(CMT)的特定输出以非常低的抖动、最小的占空比失真直接连接到I/O上。
每个7系列FPGA有多达24个CMT,每个CMT由一个MMCM和一个PLL组成。MMCM和PLL可作为广泛频率范围的频率合成器,可作为外部或内部时钟抖动滤波器以及低偏移时钟。PLL可以看作是MMCM的一个功能子集。7系列FPGA时钟输入连接允许多个资源为MMCM和PLL的参考时钟。
7系列fpga的MMCM在任意方向都具有无限的精细相移能力,可用于动态相移模式。MMCM在反馈路径和输出路径中有一个分数计数器,这使得频率合成能力的精度进一步提高。
LogiCORE™ IP时钟向导可用于帮助利用MMCMs和PLL在7系列FPGA设计中创建时钟网络。图形用户界面用于采集时钟网络参数。计时向导选择适当的CMT资源,并以最佳方式配置CMT资源和关联的时钟路由资源。
四、时钟缓冲、管理和布线 图1-1是7系列fpga时钟架构的高层次视图。垂直时钟中心线(时钟主干道the clock backbone)将设备划分为相邻的左和右区域,而水平中心线将设备划分为其顶部和底部两侧。时钟主干道中的资源被镜像到水平相邻区域的两侧,从而将某些时钟资源扩展到水平相邻区域。顶部和底部分隔分隔了两组全局时钟缓冲器(BUFG),并对它们的连接方式施加了一些限制。但是BUFG不属于一个时钟区域,可以到达设备上的任何一个时钟点。所有水平时钟资源都包含在时钟区域水平时钟行(HROW)的中心,垂直的、非区域的时钟资源则包含在时钟主干道或CMT主干道中。
一个时钟区域总是每列包含50个clb、10个36K块RAM(除非5个36K块被PCIE的集成块替代),每列包含20个DSP Slice和12个BUFH。一个时钟区域包含,一个CMT (PLL/ MMCM),一个50个IO BANK,一个由四个串行收发器组成的GT Quad,以及在块RAM列中为PCIe的半列。
图1-1是7系列时钟资源的大致结构。时钟主要由三个部分组成:全局时钟、局部时钟和I/O时钟。
图1-2是一个时钟区域内可用时钟资源的高层次概览,以及它们的基本连接。全局时钟缓冲器可以通过HROW驱动到每个区域,即使再物理空间上不位于那里。水平时钟缓冲器(BUFH)通过HROW驱动到区域中的每个时钟点。BUFG和BUFH在HROW中共享路由通道。I/O缓冲区(BUFIO)和区域时钟缓冲区(BUFR)位于I/O BANK内部。BUFIO只驱动I/O时钟资源,BUFR驱动I/O资源和逻辑资源。BUFMR支持BUFIO和BUFR的多区域链接。具有时钟能力的输入将外部时钟连接到设备上的时钟资源。某些资源可以通过CMT主干道连接到上面和下面的区域。
图1-3为设备右侧单个时钟区域的时钟详细图:
图1-4显示了全局BUFG和区域BUFH/CMT/ CC管脚连接性的详细图,以及区域中可用资源的数量(显示为右侧区域)。
4个时钟输入引脚中的任何一个都可以驱动CMT中的的PLL/MMCM和BUFH。BUFG显示在该区域中,但在物理空间上可以位于时钟主干道中的其他物理位置。BUFG和BUFH共享HROW中的12个路由轨道,可以驱动该区域内的所有时钟点。BUFG也可以驱动BUFH(图1-4中没有显示)。这允许在其他全局时钟分布上单独启用时钟(CE)。一个GT Quad有10条专用路径来驱动CMT和时钟主干道中的时钟缓冲器。位于I/O BANK的BUFR在逻辑、CMT和BUFG中有四个驱动时钟点。CMT可以使用CMT主干道驱动相邻区域的其他CMT,但有一定的局限性。类似地,具有支持时钟的引脚可以在相同的限制下驱动相邻区域的CMT。具有支持时钟的引脚可以驱动BUFG在设备相同的顶部/底部的任何位置。在CMT主干道中有四个通道支持垂直区域之间的连接。来自一个区域的时钟源可以驱动其自身区域内的时钟缓冲区资源,也可以驱动水平相邻区域内的时钟缓冲区资源。CMT、支持时钟引脚和串行收发器可以通过BUFH将时钟驱动到水平相邻区域,也可以连接到设备相同的顶部/底部的BUFG。
逻辑互连驱动BUFG和BUFH的CE引脚。逻辑互连也可以驱动时钟进入相同的缓冲器,但必须小心,因为时间是不可预测的。
图1-5更详细地展示了I/O时钟资源和互联性。
每个I/O BANK包含4个BUFIO和4个BUFR。每个时钟缓冲器可以被一个特定的具有支持时钟管脚对的输入时钟驱动,也可以由MMCM的特定输出时钟直接驱动。两个支持时钟的输入管脚对,被称为MRCC,支持多区域时钟方案。一个MRCC管脚对可以驱动特定的BUFMR,而BUFMR又可以驱动相同和相邻区域中的BUFIO和BUFR,从而促进多区域/BANK接口。同样,GT Quad也可以驱动BUFMR。MMCM<3:0>的输出到BUFR和BUFIO有专用的高性能差分路径。这个特性也被称为高性能时钟(HPC)。
尽管所有7系列设备具有相同的基本架构,但在不同的系列和系列内的设备之间存在一些架构差异。每个7系列FPGA在设备的左侧边缘至少有一个完整的I/O列。GT可以是7系列fpga 支持的任意一个串行收发器(GTP、GTX或GTH)。带有GT的设备在设备的右边缘有一个GT和I/O的混合列(某些Artix-7和Kintex-7设备),或者设备的右边缘有一个完整的GT列(某些Virtex-7和Kintex-7设备)。其他Virtex-7器件的左右边缘都有完整的GT列,左右两侧都有完整的I/O列侧面。Artix-7200t器件在时钟列的顶部和底部有GTP收发器。
五、时钟连接概述 下表总结了7系列fpga的时钟连接。
从底层结构开始学习FPGA----分布式RAM(DRAM,Distributed RAM) https://wuzhikai.blog.csdn.net/article/details/125117803
RAM是Random Access Memory的首字母缩写。它是一种主存储器,用于存储当前正在使用的信息。信息可以是正在处理的数据或程序代码。它是一种读写存储器,这意味着它几乎可以同时存储(写入)和访问(读取)数据。但RAM是易失性或临时性存储器,即当电源被移除时其内容会被擦除。RAM是一种快速存取存储器,因为无论其物理位置如何,它都可以随时随机存储和访问数据。它存储启动设备所需的必要指令和处理器正在使用的数据。它通过在组件之间快速传输数据来提高系统的处理速度。
ROM,Read Only Memory,即只读存储器,也是一种主存储器,但它永久存储数据。它是一种非易失性存储器,即当电源被移除时,其数据内容不会被擦除。顾名思义,它是只读存储器,这意味着数据不能更改,但可以访问任意次数。只能访问数据而不能写入数据。
RAM和ROM都可以简单地视为一张表格,每个格子的内容就是其所存储的信息,而地址线则是寻找对应表格的“身份号码”。RAM可以对表格的内容进行读、写操作,而ROM只能对表格进行读操作,无法进行写操作。
FGPA内部的分布式RAM(DRAM,Distributed RAM)的概念是相对于块RAM(BRAM,Block RAM)来说的。物理上看,BRAM是fpga中固定存在的硬件资源,而DRAM则是使用逻辑单元LUT拼出来的,实际上算是LUT的延伸使用。
BRAM由一定数量固定大小的存储块构成的,使用BRAM不占用额外的逻辑资源,且速度快。但是使用的时候消耗的BRAM资源是其块大小的整数倍。如Xilinx7系列FPGA结构中每个BRAM有36Kbit的容量,既可以作为一个36Kbit的存储器使用,也可以拆分为两个独立的18Kbit存储器使用。反过来相邻两个BRAM可以结合起来实现72Kbit存储器。每个Block RAM都有两套访问存储器所需的地址总线、数据总线及控制信号等信号,因此其既可以作为单端口存储器,也可以作为双端口存储器。需要注意的时访问BRAM需要和时钟同步,异步访问是不支持的。
只有SLICEM里的查找表才可以用做DRAM,利用查找表为电路实现存储器,既可以实现芯片内部存储,又能提高资源利用率。DRAM的特点是可以实现BRAM不能实现的异步访问。不过使用分布式RAM实现大规模的存储器会占用大量的LUT,可用来实现逻辑的查找表就会减少。因此建议仅在需要小规模存储器时,使用这种分布式RAM。
DRAM使用的是没有综合的LUT单元,而BRAM是块RAM,它的大小和位置是固定的。即使你只使用了一点点BRAM,综合后同样会消耗一整块RAM。BRAM是一列一列分布的,这样可能造成用户逻辑模块和BRAM直接的距离较长,延时较长,最终导致性能下降。如果使用到多个BRAM最好合理规划一下布局。
较大的存储应用,建议用BRAM;零星的小应用,可以用DRAM。但这只是个一般原则,具体的使用得看整个设计中资源的冗余度和性能要求。
CLB(可配置逻辑单元,Configurable Logic Block)是FPGA底层的基本逻辑单元,由两个SLICE组成。SLICE的种类有两种:SLICEL与SLICEM:
SLICEL与SLICEM的组成大致相同,只有LUT6有区别。我们把两种LUT6放到一起来看看:
SLICEM中的LUT较SLICEL中的LUT,除了具备读总线A1~A6,还具有WE(写使能)、DI1~DI2(数据写入端口)和WA1~WA8(数据写地址端口),所以SLICEM还具备数据写入功能,这使得其可以作为分布式RAM和移位寄存器使用。
而SLICEL中的LUT只有地址线与输出,所以我们只能将其作为一个ROM使用,从而实现查找表功能。
由于每个SLICEM中均有4个LUT,所以其资源可以实现以下形式的DRAM:
其配置如下:
单口RAM 双口RAM 简单双端口 四端口 (一)单口RAM:同步写、异步读,读写操作共用一组地址总线
(二)双口RAM:一个端口用于同步写和异步读;一个端口用于异步读取
(三)简单双端口:一个用于同步写的端口(从写端口没有数据输出/读端口);一个端口用于异步读取
(四)四端口:一个用于同步写和异步读的端口;三个端口用于异步读取
(五)更大深度的实现
此外,可以通过多个LUT6+MUX的方式实现更大深度的DRAM。
128深度的DRAM,可以通过2个LUT6+1个MUX2实现,两个LUT6分别存储低64bit和高64bit的数据,通过MUX2来进行选取,从而拼接而成实现128深度的单口DRAM。
同样的,也可以使用相同的结构实现256深度的DRAM。
256深度的单口RAM则使用了4个LUT6+2个F7MUX+1个F8MUX,刚好是一个SLICEM里面的最大资源数量,所以单个SLICEM能实现的最大深度DRAM就是256*1的单口DRAM。
DRAM可以使用多种方式来实现,各种方法都有利弊,所以在实际使用过程中应根据需求及开发环境来灵活使用。
推断是指设计者使用符合规范的RTL代码,由综合工具(此文均指xilinx的vivado)自动推断出DRAM结构的方式。
由于推断结果一般较为理想,因此建议采用推断,除非给定用例不受支持,或者无法在性能、面积或功耗方面实现足够的结果。在此类情况下,请尝试其它方法。推断 RAM 时,赛灵思建议您使用 Vivado 工具中提供的 HDL 模板。如前文所述,使用异步复位会给 RAM 推断造成不利影响,应避免使用。
(优点)
便于移植 便于读取和理解 自我文档化 快速仿真 (不足)
不能访问所有可用的 RAM 配置 结果可能并非最佳 以下是64深度,6宽度的单端口DRAM的RTL代码实现方式:
module RTL_DRAM(
input wclk, //input clk
input [5:0] addr, //input address
input [5:0] d, //input data
input we, //input write enable
output [5:0] o //output
);
reg [5:0] dram64x6 [63:0] ; //64*6
always@(posedge wclk)
if(we) dram64x6[addr] <= d;
assign o = dram64x6[addr];
endmodule
其写操作与时钟同步,而读操作则是异步。
在FPGA上综合的结果如下:
由6个1宽度的64深度RAM级联组成,资源消耗则为6个LUT6,与理论情况一致。
原语是xilinx提供的底层设计元素,类似于嵌入式开发中提供的底层库函数。针对DRAM的实现,xilinx同样提供了数个原语,如:RAM64X1S,RAM16X4S,RAM128X1D等,具体可查阅《UG799,Xilinx 7 Series FPGA and Zynq-7000 All Programmable SoC Libraries Guide for Schematic Designs》。需要注意的是,DRAM原语通常都是固定了位宽、深度和实现方式的,对于某些不符合深度、位宽的DRAM实现,则需要找更小的原语来实现。例如,原语无法直接实现64深度6位宽的单端口DRAM,只能通过6个64深度1位宽的单端口DRAM--RAM64X1S来实现。
(优点)
对实现方案有最高控制权限 能访问块的各项功能 (不足)
代码可移植性差 功能和用途冗长繁琐,难以理解 以下是64深度,6宽度的单端口DRAM的原语实现方式:
module PRIMATE_DRAM(
input wclk, //input clk
input [5:0] addr, //input address
input [5:0] d, //input data
input we, //input write enable
output [5:0] o //output
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst0 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[0]),// 1-bit data input
.O (o[0]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst1 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[1]),// 1-bit data input
.O (o[1]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst2 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[2]),// 1-bit data input
.O (o[2]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst3 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[3]),// 1-bit data input
.O (o[3]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst4 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[4]),// 1-bit data input
.O (o[4]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
RAM64X1S #(
.INIT(64'h0000000000000000) // Initial contents of RAM
) RAM64X1S_inst5 (
.A0 (addr[0]), // Address[0] input bit
.A1 (addr[1]), // Address[1] input bit
.A2 (addr[2]), // Address[2] input bit
.A3 (addr[3]), // Address[3] input bit
.A4 (addr[4]), // Address[4] input bit
.A5 (addr[5]), // Address[5] input bit
.D (d[5]),// 1-bit data input
.O (o[5]),// 1-bit data output
.WCLK (wclk), // Write clock input
.WE (we) // Write enable input
);
endmodule
综合结果与推断的结果基本一致。
资源使用情况也与推断的结果一致--由6个1宽度的64深度RAM级联组成,资源消耗则为6个LUT6,与理论情况一致。
可以看到采用原语的DRAM开发方式,需要对基础原语进行多次例化,虽然可以借用generate语法,但是仍然很麻烦。
XILINX还提供了DRAM的IP供开发者使用,采用IP的开发方式,GUI程度高,开发简单,但是由于定制程度高,所以可移植性也一般。
(优点)
在使用多个组件时一般能提供更优化的结果 易于指定和配置 (不足)
代码可移植性差 需要管理核 DRAM的IP核全称为Distributed Memory Generator,其使用较为简单,接下来我们采用IP核配置一个64深度的6位宽单端口DRAM,配置过程如下:
(第一页)
(第二页)
(第三页)
接下来综合,再根据veo文件提供的例化模板,编写RTL对IP核进行例化:
module IP_DRAM(
input wclk, //input clk
input [5:0] addr, //input address
input [5:0] d, //input data
input we, //input write enable
output [5:0] o //output
);
//例化DRAM IP核
dram_64x6 dram_64x6_inst (
.a (addr), // input wire [5 : 0] a
.d (d), // input wire [5 : 0] d
.clk (wclk), // input wire clk
.we (we), // input wire we
.spo (o) // output wire [5 : 0] spo
);
endmodule
综合结果,资源消耗均与上述两种方法一致。
我们将三种实现DRAM的模块例化到同一个顶层文件,再编写testbench对其仿真,实现功能:先往地址0-63写入数据0-63,再从地址0-63读出数据,观察写、读数据是否一致。
顶层文件:
module test(
input wclk, //input clk
input [5:0] addr, //input address
input [5:0] d, //input data
input we, //input write enable
output [5:0] o_rtl,
output [5:0] o_primate,
output [5:0] o_ip
);
//例化RTL型
RTL_DRAM RTL_DRAM_inst(
.wclk (wclk ), //input clk
.addr (addr ), //input address
.d (d ), //input data
.we (we ), //input write enable
.o (o_rtl ) //output
);
//例化原语型
PRIMATE_DRAM PRIMATE_DRAM_inst(
.wclk (wclk ), //input clk
.addr (addr ), //input address
.d (d ), //input data
.we (we ), //input write enable
.o (o_primate) //output
);
//例化IP型
IP_DRAM IP_DRAM_inst(
.wclk (wclk ), //input clk
.addr (addr ), //input address
.d (d ), //input data
.we (we ), //input write enable
.o (o_ip ) //output
);
endmodule
testbench:
`timescale 1ns / 1ns
module tb_test();
reg wclk; //input clk
reg [5:0] addr; //input address
reg [5:0] d; //input data
reg we; //input write enable
wire [5:0] o_rtl;
wire [5:0] o_primate;
wire [5:0] o_ip;
//例化test模块
test test_inst(
.wclk (wclk ), //input clk
.addr (addr ), //input address
.d (d ), //input data
.we (we ), //input write enable
.o_rtl (o_rtl ), //output
.o_primate (o_primate ), //output
.o_ip (o_ip ) //output
);
initial begin
wclk =0;
we =1;
d = 0;
addr = 0;
wait(d == 6'd63);#10 we =0;
end
always #5 wclk = ~wclk;
always @(posedge wclk)begin
d <= d+1;
addr <= addr+1;
end
endmodule
仿真结果如下:与设想情况一致。
分布式RAM提供了对非常小的数组使用存储元素和对较大数组使用BRAM之间的权衡。建议尽可能地使用RTL方式推断内存,以提供最大的灵活性。分布式RAM也可以通过原语实例化或使用IP来生成。
一般来说,分布式RAM应该用于所有深度为64位或更少的情况,除非设备缺少SLICEM或逻辑资源。因为分布式RAM在资源、性能和功能方面更高效。
对于大于64位但小于或等于128位的深度,使用最佳资源的决定取决于以下因素:
额外块ram的可用性。如果不可用,就使用分布式RAM。 延迟的要求。如果需要异步读功能,则必须使用分布式ram。 数据宽度。宽度大于16位应该使用块RAM,如果可以的话。 必要的性能要求。寄存的分布式ram通常比Bram有更短的Tco时间和更少的布局限制。
从底层结构开始学习FPGA----移位寄存器 https://wuzhikai.blog.csdn.net/article/details/124970484
移位寄存器内的数据可以在移位脉冲(时钟信号)的作用下依次左移或右移。移位寄存器不仅可以存储数据,还可以用来实现数据的串并转换、分频,构成序列码发生器、序列码检测器,进行数值运算以及数据处理等,它也是数字系统中应用非常广泛的时序逻辑部件之一。
在FPGA的底层结构----可配置逻辑块CLB中,一个CLB由4个Slice组成,这4个Slice又可以分SliceM和SliceL(其比例大致为1:3),其中M是Memory的首字母,L是Logic的首字母,比较SliceM和SliceL,其区别就是SliceM的查找表具有RAM和ROM的功能,而SliceL的则不具备,所以SliceM比SliceL多的功能就是做存储器和移位。
SLICEM可以在不使用触发器的条件下配置为32位移位寄存器(注意:只能左移)。这样,每个LUT可以将串行数据延迟1到32个时钟周期。移位输入D(LUT DI1脚)和移位输出Q31(LUT MC31脚)可以进行级联,以形成更大的移位寄存器。一个SLICEM的4个LUT6级联可以实现128个时钟周期的延时。多个SLICEM也可以进行组合。但SLICEM之间没有直接连接以形成更长的移位寄存器,在LUT B/C/D处的MC31输出也没有。由此产生的可编程延迟可用于平衡数据pipeline的时间。
延迟或延迟补偿 同步FIFO和内容寻址存储器(CAM)
上图是由一个LUT构成的最高支持32位移位的移位寄存器SRLC32E结构。
CLK:时钟 CE:时钟使能,高电平有效 SHIFTIN(D):数据输入 A[4:0]:移位长度配置(支持1~32位),需要+1。如9位移位则A[4:0]需配置成1+8(01000) SHIFTOUT:32个长度移位后的数据输出,可用来与其他SRLC32E级联,形成更大长度的移位寄存器 OUTPUT(Q) :当移位长度被确定后,数据就会出现在Q端
通过两个32位的移位寄存器SRL32与一个MUX2,即可级联成最高支持64位的移位寄存器。
通过三个32位的移位寄存器SRL32与三个MUX2,即可级联成最高支持96位的移位寄存器。
通过四个32位的移位寄存器SRL32与三个MUX2,即可级联成最高支持128位的移位寄存器。
而4个移位寄存器由4个LUT组成,刚好一个SLICEM中有4个LUT,这说明一个SLICEM可以实现最多支持128位的移位寄存器。由于4个LUT同在一个SLICE里面,所以布线方便且延迟短,有利于时序收敛。
输入(D)被加载到移位寄存器的第一个位 前一个位被移到下一个位置并出现在Q输出上,最高位移到MC31输出。 移位寄存器固定长度为(N + 1),其中N为输入地址(0-31),由地址总线A[4:0]决定 在级联操作中,前一个移位寄存器的SHIFTOUT链接至下一个移位寄存器的SHIFTIN
数据的移位输出是异步信号 其他与静态操作一致
在实际的编写RTL过程中,我们可通过以下方式来生成一个移位寄存器。
源语方式 综合工具推断方式
(1)源语类型
SRL16E:最高可实现16个时钟周期的移位功能 SRLC32E:最高可实现32个时钟周期的移位功能 以上均可以实现移位寄存器功能,后文以SRLC32E为例进行讲解。
(2)源语例化
// SRLC32E: 32-bit variable length cascadable shift register LUT (Mapped to a SliceM LUT6)
// with clock enable
SRLC32E #(
.INIT(32'h00000000) // Initial Value of Shift Register
) SRLC32E_inst (
.Q(Q), // SRL data output
.Q31(Q31), // SRL cascade output pin
.A(A), // 5-bit shift depth select input
.CE(CE), // Clock enable input
.CLK(CLK), // Clock input
.D(D) // SRL data input
);
// End of SRLC32E_inst instantiation
关于参数与信号在上面已讲解,不赘述。
(3)示例代码
module test(
input clk,
input ce,
input shift_in, //移位输入
output Q, //移位输出
output shift_out //移位输出,可级联
);
SRLC32E #(
.INIT(32'h00000000) // Initial Value of Shift Register
) SRLC32E_inst (
.Q(Q), // SRL data output
.Q31(shift_out), // SRL cascade output pin
.A(5'b01001), // 5-bit shift depth select input,移位长度固定为10----01001+1
.CE(ce), // Clock enable input
.CLK(clk), // Clock input
.D(shift_in) // SRL data input
);
endmodule
上面的模块是直接使用SRL32源语例化的一个移位寄存器,移位长度固定为10。 下面是综合出来的结构,可以看到只使用了一个LUT。
(4)Testbench
`timescale 1ns / 1ns
module tb_test();
reg clk;
reg ce;
reg shift_in;
wire Q;
wire shift_out;
test test_inst(.clk(clk),.ce(ce),.shift_in(shift_in),.Q(Q),.shift_out(shift_out));
initial begin
clk=0;
ce=1;
shift_in = 1;
#330 $finish;
end
always
#5 clk=~clk;
always
#10 shift_in<=~shift_in;
endmodule
(5)仿真结果
在第一个周期输入信号从0跳转到1;移位长度设置为10,在第10个周期Q输出为1,此后输出与shift_in一致;在第32个周期,shift_out开始输出1,然后输出与shift_in一致。仿真结果符合Timing。
除了直接例化SRL源语外,也可以通过编写常规的RTL代码来实现移位寄存器的功能。但是需要注意的是,要注意编写RTL的风格,有些风格可能导致vivado无法综合出SRL,而是使用多个REG来实现,会造成大量的资源浪费。
(1)错误的推断方式
module test(
input clk,
input ce,
input shift_in,
input rst,
output Q,
output shift_out
);
reg [31:0] dff;
always@(posedge clk) begin
if(rst)
dff<=0;
else if(ce) begin
dff[31:0]<={dff[30:0],shift_in}; //拼接运算实现向左移位
end
end
assign shift_out=dff[31];
assign Q=dff[9];
endmodule
上面代码的综合结果如下,显然不是用的SRL32,而是数个LUT+数个FF。其原因在于RTL中使用了复位信号,而SRL32这个元件是没有复位端口的,因为流水线的结构根本就不需要复位!复位的使用完全是画蛇添足,导致不必要的资源浪费。
(2)正确的推断方式
module test(
input clk,
input ce,
input shift_in,
output Q,
output shift_out
);
reg [31:0] dff;
always@(posedge clk) begin
if(ce) begin
dff[31:0]<={dff[30:0],shift_in}; //拼接运算实现向左移位
end
end
assign shift_out=dff[31];
assign Q=dff[9];
endmodule
上面的代码是删除复位后的代码,综合结果如下。
使用的资源:3个FF+2个SRL。因为我们要做的是10位移位,所以结果在第10位被引出。vivado自动帮输入以及输出做了寄存,所以资源消耗得多一些。
仿真结果:
在第一个周期输入信号从0跳转到1;移位长度设置为10,在第10个周期Q输出为1,此后输出与shift_in一致;在第32个周期,shift_out开始输出1,然后输出与shift_in一致。仿真结果符合Timing。
移位寄存器原语不会使用同一SLICE中可用的寄存器。要实现完全同步的读和写移位寄存器,输出引脚Q必须连接到触发器FF。移位寄存器和触发器共享同一时钟,如图所示。
32位移位寄存器可级联实现任何静态长度模式的移位寄存器,而不需要使用专用的多路复用器(F7AMUX, F7BMUX和F8MUX)。下图说明了如何构建72位移位寄存器。只有最后一个SRLC32E原语需要将其地址输入绑定到ob00111。另外,可以将移位寄存器的长度限制为71位(绑定到obo0110的地址),并且可以使用一个触发器作为最后一个寄存器。(在SRLC32E原语中,移位寄存器长度为地址输入+ 1)。
一个移位操作需要一个时钟沿 对LUT的Q输出的动态移位长度读操作是异步的 对LUT的Q输出的静态移位长度读取操作是同步的 数据输入具有 setup-to-clock的时序规范 在可级联配置中,Q31输出总是包含最后一位值 Q31输出在每次移位操作后同步变化 SRL结构不需要使用复位,如果希望使用SRL32减少资源,实现移位,则建议使用源语方式例化
从底层结构开始学习FPGA----存储单元之触发器、寄存器与锁存器 https://wuzhikai.blog.csdn.net/article/details/124830516
触发器:触发器是边沿敏感的存储单元,数据存储的动作由某一信号的上升或者下降沿进行同步的。触发器是计算机记忆装置的基本单元,一个触发器能储存一位二进制代码。
锁存器:锁存器是电平触发的存储单元,数据存储的动作取决于使能信号的电平值,当锁存器处于使能状态时,输出才会随着数据输入发生变化。(简单地说,它有两个输入,分别是一个有效信号EN,一个输入数据信号DATA_IN,它有一个输出Q,它的功能就是在EN有效的时候把DATA_IN的值传给Q,也就是锁存的过程)。
寄存器:一个触发器可以组成一个一位的寄存器,多个触发器可以组成一个多位的寄存器。存储器是由大量寄存器组成的,其中每一个寄存器就称为一个存储单元。它可以存放一个有独立意义的二进制代码。
在数字电路中存储单元有两种,一种是触发器,一种是锁存器。它们两者最大的区别是:前者通过时钟沿到来改变存储的输出状态,后者是通过电平变换来改变存储的输出状态。在FPGA中我们基本多使用触发器,锁存器在很多情况下都是要避免使用的,因为其不需要时钟,所以不是时序元件,对毛刺无过滤功能,非常敏感,容易处问题。
而我们平时所说的寄存器,基本上理解为一个或者一组FF。
在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 regA
);
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
如果你还记得数电的话,就应该能看出来,上面配置的触发器实际上就是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_n,
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指令: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
举例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
上述两种情况都是,代码缺少完整条件的描述,编译工具认为在此情况下该值保持不变,就推断出了锁存器。
所以,为了防止锁存器的产生,在组合逻辑一定要将if-else语句、case语句描述完整,而时序逻辑则没有这个问题。
从底层结构开始学习FPGA----MUX多路选择器(Multiplexer) https://blog.csdn.net/wuzhikaidetb/article/details/124764526?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22124764526%22%2C%22source%22%3A%22wuzhikaidetb%22%7D&ctrtid=HqsZb
多路选择器MUX是一个多输入、单输出的组合逻辑电路,一个n输入的多路选择器就是一个n路的数字开关,可以根据通道选择控制信号的不同,从n个输入中选取一个输出到公共的输出端。
4选1的多路开关电路模型如下所示:
其真值表如下:
在FPGA底层,MUX也是作为一种基本的逻辑单元而存在。下图是FPGA的一个基本逻辑单元----SLICEL,可见其是由LUT、MUX、CARRY4和FF组成。
通过使用不同LUT6和MUX的组合级联,就可以实现想要的位数的MUX了。
首先需要说明的是,若只实现MUX2、MUX3、和MUX4是不需要使用到FPGA的MUX资源的,只需要使用LUT6即可。关于LUT6:从底层开始学习FPGA(1)----LUT查找表
不妨先写个MUX4的Verilog代码,再用vivado来implementation一下,看看使用了些什么资源。Verilog代码如下:
module test(
input A1,
input A0,
input D3,
input D2,
input D1,
input D0,
output reg Y
);
always @(*)begin
case({A1,A0})
2'b00: Y = D0;
2'b01: Y = D1;
2'b10: Y = D2;
2'b11: Y = D3;
default:;
endcase
end
endmodule
用vivado来分析一下,门级电路如下:
嗯,就是一个标准的MUX4,与上面提出的电路图一致。
再来看看会综合(synthesis)出什么东西:
可以看到,只用了一个LUT6。然后再看看在FPGA上的具体实现:
同样的,只用了一个LUT6。既然MUX4都只用一个LUT6就可以实现了,那么MUX3、MUX2就当然也只需要一个LUT6了。
MUX4的实现使用一个LUT6,其中LUT6的4个输入作为MUX4的4个输入,而另外2个输入则作为MUX4的地址线。那么MUX5~MUX8又是如何实现?(MUX5~MUX8均采用3根地址线,可以一起讨论)。
接下来的讨论以MUX8为例,既然一个LUT6可以实现MUX4,那么2个LUT6 + 1个MUX2就可以实现MUX8的功能了。MUX8的Verilog形式:
module test(
input A2,
input A1,
input A0,
input D7,
input D6,
input D5,
input D4,
input D3,
input D2,
input D1,
input D0,
output reg Y
);
always @(*)begin
case({A2,A1,A0})
3'b000: Y = D0;
3'b001: Y = D1;
3'b010: Y = D2;
3'b011: Y = D3;
3'b100: Y = D4;
3'b101: Y = D5;
3'b110: Y = D6;
3'b111: Y = D7;
default:;
endcase
end
endmodule
门级电路:
vivado综合:
综合的结果是:2个LUT6 + 1个MUXF7。MUXF7就是一个MUX2,但是注意它的输入只能是LUT6的输出。
映射到FPGA:
那么从MUX8类推MUX16就很容易了:4个LUT6 + 2个MUX2 + 1个MUX2。MUX16的Verilog形式:
module test(
input A3,
input A2,
input A1,
input A0,
input D15,
input D14,
input D13,
input D12,
input D11,
input D10,
input D9,
input D8,
input D7,
input D6,
input D5,
input D4,
input D3,
input D2,
input D1,
input D0,
output reg Y
);
always @(*)begin
case({A3,A2,A1,A0})
4'b0000: Y = D0;
4'b0001: Y = D1;
4'b0010: Y = D2;
4'b0011: Y = D3;
4'b0100: Y = D4;
4'b0101: Y = D5;
4'b0110: Y = D6;
4'b0111: Y = D7;
4'b1000: Y = D8;
4'b1001: Y = D9;
4'b1010: Y = D10;
4'b1011: Y = D11;
4'b1100: Y = D12;
4'b1101: Y = D13;
4'b1110: Y = D14;
4'b1111: Y = D15;
default:;
endcase
end
endmodule
门级电路:
vivado综合:
与料想的一致,三层级结构: 4个LUT6 + 2个MUXF7 + 1个MUXF8。
映射到FPGA:
再来看看上面出现的SLICEL的资源图:
到这基本上就明白FPGA的独立MUX了:每个SLICE中都有2个MUXF7,其输入只能为LUT6的输出,而输出只能接到MUXF8;每个SLICE中都有1个MUXF8,其输入只能为MUXF7的输出。
但是我们知道MUX2是可以用LUT6来实现的,也就是说 2个LUT6 + 1个MUXF7可以转换成2个LUT6 + 1个LUT6来实现MUX8;而4个LUT6 + 2个MUXF7+1个MUXF8可以转换成4个LUT6 + 1个LUT6来实现。那么既然LUT6可以实现MUX2的功能,FPGA里为何还要有固定的MUX这种结构?
首先可以肯定的是用LUT6来实现MUX2的效率没有固定MUX2的效率高,因为有两个输入没有利用就意味着有资源是浪费的。其次,每个SLICE中只有4个LUT6,如果只用LUT6来实现MUX16则需要5个LUT6,那么第5个LUT6则势必会布线到其他Slice,这样就容易造成不好布线和拥堵,且4个LUT6的输出分别到达第五个LUT6的时间会不同,到达时间不一致则容易产生毛刺。
而LUT6+MUX的构造则布线长度则是基本一致的,可以看下图:
重点关注紫线,可以看到这个长度基本一致。如果要布线到另外Slice的LUT6的话,可以想象这条路径有多长和多难控制长度一致。
3.1、每个SLICE中都有2个MUXF7 + 1个MUXF8。MUXF7 的输入只能为LUT6的输出,而输出只能接到MUXF8;MUXF8的输入只能为MUXF7的输出
3.2、MUX4可以由LUT6来实现,每个SLICE可以实现4个独立的MUX4
3.3、MUX8可以由2LUT6 + 1个MUXF7来实现,每个SLICE可以实现2个独立的MU8
3.4、MUX16可以由4LUT6 + 2个MUXF7 + 1个MUXF8来实现,每个SLICE可以实现1个独立的MU16
3.5、若大于16路的多路选择器则需要数个SLICE的资源进行级联来实现
从底层结构开始学习FPGA----LUT查找表 https://wuzhikai.blog.csdn.net/article/details/124642077
记得刚接触FPGA的时候,总能看见类似这样的一句话----FPGA是基于查找表LUT的可编程逻辑器件。FPGA常常被人比作“数字积木”,就是因为底层资源的丰富和灵活,要做任何“玩具”(项目要实现的功能,也可以说是电路),只需要设计好“图纸”(RTL),即可使用积木(FPGA丰富的底层逻辑资源,如LUT、FF、MUX等等)来实现。
在最底层,可配置逻辑模块(如片或逻辑单元)有着两种最基本的部件:触发器和查找表(LUT)。这很重要,因为各种FPGA家族之所以各不相同,就是因为触发器和查找表组合的方式不同。LUT查找表可以算其中相当重要的一个底层资源,通常用来实现所需的组合逻辑功能。而FF触发器则一般用来实现时序逻辑功能。
在7系列器件之前的器件多使用LUT4,即4输入LUT;而7系列后则多使用LUT6,即6输入LUT。
LUT(look up table),即查找表,其原理其实也就是一个一个查找表,根据输入去找到相应位置的信号,然后做输出。说白了就好像一个小容量的ROM,把输入当作地址信号,对LUT里面预存的内容进行寻址。
7系列的FPGA的LUT有6个输入端口(I0~I5),然后有两个输出端口(O5,O6),如下所示:
6个输入端口一共有2^6 = 64
种输入,所以可以将一个LUT6视为一个容量为64的ROM,其中存储的是64种不同的逻辑运算的结果,而不同的输入则组成了ROM的地址线。所以逻辑运算的实现实际上就是对LUT6的一个译码过程。
假设有这样一个逻辑: y = a0 & a1 & a2 & a3 & a4 & a5
。
这个例子比较简单,其只在所有输入均为1的情况下输出才为1,其他情况输出均为0。将其写成Verilog的形式:
module test(
input a0,
input a1,
input a2,
input a3,
input a4,
input a5,
output y
);
assign y = a0 & a1 & a2 & a3 & a4 & a5;
endmodule
用Vivado analysis一下,看看分析出来的门级电路是什么样子的:
很显然,就是5个与门实现6输入相与的功能。如果FPGA中没有LUT这种结构,而是由不同的逻辑门组成,那么实现上述逻辑功能则最终会映射到5个与门。可是随着设计的复杂化,仅仅使用与门显然是无法满足设计需求的,我们还需要其他逻辑门,如或门、非门、异或门等。当然这些逻辑门都可以由数个与非门或者或非门实现,但是这无疑需要进行一个译码过程以及会造成资源的浪费。
而LUT将所有可能的逻辑值均存起来,用的时候再查表就方便多了。我们不需要知道这个电路会映射成什么样的、多少个逻辑门,我们只需要根据输入直接查表找对应的输出就行了。这大大提高了灵活性以及资源的利用效率。
此外,采用传统逻辑门电路实现逻辑关系的方法也存在一些严重的缺点:
输入变量从通过逻辑电路到输出变量,存在一定的延迟,该延迟的大小和逻辑电路的复杂程度密切相关。逻辑电路越复杂,延迟越大,因此,延迟是不确定的; 延时的倒数是频率,频率和时序电路的工作速率密切相关。因为延迟不确定,所以频率也不确定,这将严重影响整个电路的工作性能; 逻辑电路的复杂程度和输入逻辑变量的个数、逻辑门的个数有关。因此输入逻辑变量越多,逻辑电路就越复杂。 说回正题,我们把上述代码再综合(synthesis)一下,看看最终的电路实现是什么样子的:
和料想的一样,就是综合出来一个LUT6。
下图是LUT6的源语形式(也可以说是网表形式),其中的INIT例化的值即是载入的64个输入的对应值。
我们把上述代码的网表文件导出来看看(我只截取了LUT6部分):
注意INIT的值,64bit中只有最高位为1,其他63位为0。也就是说,只有当6输入均为1时输出才为1,其他输入情况下输出均为0。与料想的逻辑值一致,也就是说INIT载入的是要查表的值,也就是要写入LUT这个小ROM的初始值。
再把上述代码再实现(implementation)一下,看看映射到FPGA芯片的具体实现是什么样子:
很简单,就是6个输入+1个输出+1个LUT6,LUT6的结构与理论一致。注意:LUT6中的O5输出并没有使用。
再看下LUT6的结构:
其实1个LUT6是由2个LUT5 + 1个MUX组成的。2个LUT5共用5个输入,其中一个LUT5的输出直接连接到O5;同时该输出也连接到MUX,另一个LUT5的输出也连接到MUX,而输入I5则作为MUX的选择控制信号。
当LUT6作为6输入查找表使用时,则其中一个LUT5存放I5为1时的结果,而另一个LUT5则存放I5为0时的结果,此时O5输出不使用。而O6输出则通过I5的控制来实现6输入的查找结果 当LUT6作为2个5输入查找表使用时,则将I5固定为1,则其中一个LUT5实现逻辑功能y1,并固定通过MUX到O6输出,而另一个LU5则实现逻辑功能y2,并直接通过O5输出
举例如下:
逻辑 y1 = a0 & a1 & a2 & a3 & a4
;
逻辑 y2 = a0 | a1 | a2 | a3 | a4
;
Verilog如下:
module test(
input a0,
input a1,
input a2,
input a3,
input a4,
output y1,
output y2
);
assign y1 = a0 & a1 & a2 & a3 & a4 ;
assign y2 = a0 | a1 | a2 | a3 | a4 ;
endmodule
vivado综合的电路:
结果是2个LUT5,但是我们知道7系列的FPGA是没有独立的LUT5这玩意儿的,所以最终映射到具体芯片上还不是这样子。
最后来看看vivado实现的电路(映射到FPGA芯片的具体实现):
和预想的一样,就是一个2输出的LUT6。
其他位数输入的逻辑实现就可以采用多个LUT6级联来实现,当输入位数过多,即组合逻辑复杂时,则会造成级联的LUT6过多,即逻辑级数多大,其结果是通常会造成组合逻辑延时多大,导致时序紧张,一般采用插入FF的方法来切割组合逻辑,使得每一段的逻辑级数都不过于长,从而优化时序。
一句话总结:LUT的逻辑实现是通过将期望结果存入SRAM中,通过不同的输入等于不同地址这一索引方式,来实现对逻辑真值表的查找。
https://wuzhikai.blog.csdn.net/article/details/125156420
FPGA底层的CARRY4本质上就是用来实现最基本的加、减法运算的,在了解CARRY4之前,我们需要对1bit以及多bit的二进制加法及其FPGA实现做一个了解。
1bit的二进制加法可以分为两类:无底层进位的半加器与有底层进位的全加器。减法运算本质上仍是一种加法运算,在二进制电路中采用加上负数的补码实现。
半加器电路是指对两个输入数据位相加,输出一个结果位和进位,没有进位输入的加法器电路。
半加器有两个1位2进制数输入,输出1个进位、1个结果位。真值表如下:
A | B | Carry | Sum |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 |
0 | 1 | 0 | 1 |
1 | 1 | 1 | 0 |
从真值表,我们可以推断出其实现:
结果 S = A ^ B; 进位 C = AB;
映射到电路就是一个2输入异或门加一个2输入与门:
全加器是在半加器的基础上的升级版,除了加数和被加数加和外还要加上前上一级传进来的进位信号。全加器真值表为:
A | B | Cin | Cout | Sum |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 0 | 1 |
1 | 1 | 0 | 1 | 0 |
0 | 0 | 1 | 0 | 1 |
1 | 0 | 1 | 1 | 0 |
0 | 1 | 1 | 1 | 0 |
1 | 1 | 1 | 1 | 1 |
从真值表,我们可以推断出其实现:
结果 S= A ^ B ^ Cin; 进位 C = (A&B) | (Cin & (A^B) ; 映射到电路的实现:
比半加器要复杂一些,构成为2个异或门+2个与门+1个或门。
有了单个bit的二进制加法电路(全加器)后,我们就可以通过级联来实现多bit的二进制加法了,但是多个全加器如何级联则是一个需要考虑的问题。
进行两个4bit的二进制数相加,就要用到4个全加器。那么在进行加法运算时,首先准备好的是1号全加器的3个input。而2、3、4号全加器的Cin全部来自前一个全加器的Cout,只有等到1号全加器运算完毕,2、3、4号全加器才能依次进行进位运算,最终得到结果。 这样进位输出,像波浪一样,依次从低位到高位传递, 最终产生结果的加法器,也因此得名为行波进位加法器(Ripple-Carry Adder,RCA)。
RCA的优点是电路布局简单,设计方便, 我们只要设计好了全加器,连接起来就构成了多位的加法器。 但是缺点也很明显,也就是高位的运算必须等待低位的运算完成, 这样造成了整个加法器的延迟时间很长。将4bit的RCA内部结构全部打开,就得到了如图所示的4-bit RCA的门电路图。要对一个电路的性能进行分析,我们就要找出其中的最长路径。 也就是找出所有的从输入到输出的电路连接中,经过的门数最多的那一条,也称为关键路径。
我们来做一个简单的分析, 对于最低位的全加器,它在A、B和Cin都已经准备好。其实,输入信号进入到这块电路之后,在连接线上传递需要花时间。 称为线延迟,而经过这样的门,也需要花时间,称为门延迟。
对于第一个全加器,它的最长路径,是红色线标记的那条。
那么,假设经过一个门电路的延迟时间为T,那么经过4个全加器所需要的总延迟时间就是:2T x 4 + T(第一个全加器产生3个T) = 9T。所以推出,经过n个全加器所产生的总延迟时间为2T x n + T = (2n+1)T。从此可以看出来RCA电路的最大问题就是组合逻辑延迟太高。
用前一个全加器的参数来表示后面的进位输出(Cout),即:
由此来表示4个全加器的进位输出为:
最终需要得到的是C4,经过换算,C4=G3+P3·G2+P3·P2·G1+P3·P2·P1·G0+P3·P2·P1·P0·C0,而这些参数,全部已知!并不需要前一个全加器运算输出,就可以提前计算进位输出, 用这样的方法实现了加法器就被称为超前进位加法器(Carry-Lookahead Adder,CLA)。
重新绘制CLA的布线方式:
CLA的方式获得了较短的组合逻辑延迟,但是电路的面积却大了非常的多,这就是FPGA设计中非常经典的“面积换时间”。
同时需要注意的是,上面的门电路实现仅仅是求得C4,而如果要同时求C3 C2 C1的话,电路面积还会增大许多。
所以在FPGA内部会是使用CLA这种方式来实现加法器吗?答案是也不是。
Xilinx FPGA底层的加法器(进位链)CARRY4是一种超前进位的加法器,但是为了面积与普适性其实现原理与上述的CLA电路还是有一点区别。
每个SLICE中都有1个(每个CLB则有2个)CARRY4用来实现进位逻辑,不同的进位链可级联以形成更宽的加/减逻辑:
先来看下CARRY的整体构成及其端口:
端口含义:
CI:是上一个 CARRY4 的进位输出,位宽为1;可级联构成更大的加法逻辑 CYINT:是进位的初始化值,位宽为1;0为加法,1为减法 DI:是数据的输入(可以是两个加数的任意一个,至于为什么后面再解释),位宽为4; SI:是两个加数的异或结果,位宽为4; O:是加法结果输出,位宽为4; CO:是进位输出,位宽为4;(这里的进位代表每一位加法的进位,比如CO0代表最低位的加法进位,而CO3则代表最高位的加法进位,这样就可以同样实现小于4bit的加减法)
3.2、内部组成 仅仅只看端口无法理解CARRY4是如何实现进位算法的,接下来看下内部的具体实现:
从上图可以看到,实际上CARRY4是分为相同的4个部分的,每个部分都用MUX2加异或门来实现进位逻辑。
这里需要说明的是,上述MUX2的实现逻辑均为:S = 0,结果为左侧输入; S = 1,结果为则右侧输入。
一步一步来看:
(一:O0、CO0)
最底层的结果O0等于S0异或CIN,S0则为两个加数的异或,也就是O0 = A0 ^ B0^ CIN。
若S0 = 0,则意味着两个加数相等,即00或11,此时O0的结果与CIN一致。若S0 = 1,则意味着两个加数不相等,即01或10,此时O0的结果与CIN相反。
当S0 = 0,则意味着两个加数相等,即00或11,此时MUX2选择DI0作为进位CO0的结果。若DI0为0,则意味着A0,B0,CO0都为0;若DI0为1,则意味着A0,B0,CO0都为1;
当S0 = 1,则意味着两个加数不相等,即01或10,此时MUX2选择CIN作为进位CO0的结果。若CIN为0,则意味着A0,B0,CIN为100,此时不需要进位,与CIN相等;若CIN为1,则意味着A0,B0,CIN为101,此时需要进位,仍与CIN相等;
省略(O1、CO1),(O2、CO2)的推断过程······
(四:O3、CO3)
结果O3等于S3异或CO2,S3则为两个加数的异或,也就是O3 = A3 ^ B3 ^ CO2。
若S3 = 0,则意味着两个加数相等,即00或11,此时O3的结果与CO2一致。
若S3 = 1,则意味着两个加数不相等,即01或10,此时O3的结果与CO2相反。 当S3 = 0,则意味着两个加数相等,即00或11,此时MUX2选择DI3作为进位CO3的结果。若DI3为0,则意味着A3,B3,CO3都为0;若DI3为1,则意味着A3,B3,CO3都为1;
当S3 = 1,则意味着两个加数不相等,即01或10,此时MUX2选择CO2作为进位CO3的结果。若CO2为0,则意味着A3,B3,CO2为100,此时不需要进位,与CO2相等;若CO2为1,则意味着A3,B3,CO2为101,此时需要进位,仍与CO2相等;
这样的结构看起来很像行波进位加法器RCA,最高层的进位需要从最底层一步步往上传递,实则不然。
可以举例,假如两个加数的最高位A3,B3为00或11,即S3=0时,此时的CO3直接等于DI3(因为00+0不进位和11+1要进位),也就是A3或者说B3(相等的),此时就不需要从下一层传递进位信号过来参与运算,这一情况出现的概率为50%。而另外50%的情况A3,B3不相等,即01或10(S3=1)时,此时的CO3取决于CO2(因为10或01+1进位;+0则不进位)。在次高位到最低位的情况均与上述一致。 也就是说只有在最极端的情况下,才需要在每一位考虑来自下一级的进位(如1010+0101,也就是每一位的两个加数均不一致),此时的进位组合逻辑是可能存在的最长组合逻辑路径。但是这种架构和上述的超前进位加法器比起来,其面积减少了非常多,仅使用了4个MUX2 + 4个XOR门,且这8个门电路均位于CLB的同一个SLICE里边,不同与传统的门电路的延迟,其线延迟可以控制得非常小。
为什么说其本质还是超前进位加法器,我们可以从其逻辑式入手进行推断:
对于CARRY4,S=a ^ b端口D可以任选a、b输入当中的一个,如选择b
输出端:那么O端即表示输出端:O = S ^ cin = a ^ b ^ cin
进位端: CO=(a^b)'b +(a^b)cin //多路复用器:y=s’b+scin =(a’b+ab’)‘b+(a^b)cin =(a’b)’(ab’)‘b+(a^b)cin =(a+b’)(a’+b)b+(a^b)cin =(ab+b’b)(a’+b)+(a^b)cin =ab(a’+b)+(a^b)cin =(a^b)cin+ab
假设有十进制的4bit数 4 + 8 =12,则二进制为 0100 + 1000 = 1100,用CARRY4的端口:
异或S = A ^ B = 0100 ^ 1000 = 1100 进位:CO0 = DI0 = B0 = 0;CO1 = DI1 = B1 = 0;CO2 = CO1 = 0;CO3= CO2 = 0,所以进位CO= 0000,与实际相符 位结果:O0 = S0 ^ CIN = 0 ^ 0=0;O1 = S1 ^ CO0 = 0 ^ 0=0;O2 = S2 ^ CO1 = 1 ^ 0=1;O3 = S3 ^ CO2 = 1 ^ 0=1;所以位结果O = 1100,也与实际相符
接下来写个简单的实例测试一下(2个8位数相加):
module test(
input [7:0] A,
input [7:0] B,
output [7:0] S
);
assign S = A + B;
endmodule
由于是8bit的加法,所以其综合结果是2个CARRY4级联组成。
`timescale 1ns / 1ns
module tb_test();
reg [7:0] A;
reg [7:0] B;
wire [7:0] S;
//例化test模块
test test_inst(
.A (A),
.B (B),
.S (S)
);
initial begin
A =0;
B =0;
#200 $finish;
end
always #10 begin
A ={$random }%64;
B ={$random }%64;
end
endmodule
测试结果不说了,一目了然:
从底层结构开始学习FPGA----可配置逻辑块CLB(Configurable Logic Block) 文章目录
系列目录与传送门
一、CLB概述
二、SLICEM与SLICEL
三、查找表LUT
3.1、移位寄存器SRL
3.2、分布式DRAM
四、多路选择器MUX
五、存储单元Storage Elements(FF)
六、进位链CARRY4
系列目录与传送门 《从底层结构开始学习FPGA》目录与传送门
一、CLB概述 我们可以用vivado打开一个器件的device视图:
可以看到这些花里胡哨的五颜六色就分别代表了FPGA的底层硬件单元,主要有:可编程输入输出单元(IOB)、可编程逻辑单元(CLB)、时钟管理单元(MMCM/PLL)、BRAM、布线资源、内嵌的底层功能单元和内嵌专用硬件模块。其中最为主要的是可编程输出输出单元(IOB)、可编程逻辑单元(CLB)和布线资源。
而我们今天的主角可编程逻辑单元CLB则在上图中蓝色的若隐若现的位置,我们需要把它放大才能更好地观察:
蓝色框中的就是CLB,同时也可以发现,CLB是由两个不的元素组成,这个元素我们称之为SLICE。
如果把FPGA实现的电路看做是积木拼成的摩天大楼,那么CLB就是实现大楼的最基本元素--砖块。 但是我们盖楼不可能只使用一种砖块,同样的为了实现各种功能的电路,我们也要把CLB切成了更小的块以便灵活组合实现更复杂的电路。
所以,CLB实际上是四种基本元素的集合----查找表LUT,进位链CARRY4,多路选择器Multiplexer以及存储单元FF。有了这四种基本元素后,我们就可以灵活组合实现各种时序逻辑和组合逻辑了。
二、SLICEM与SLICEL
一个CLB是由2个SLICE组成的,SLICE根据其中的LUT6能实现的功能可以分为以下两种:
SLICEM(M:Memory):其内部的LUT可以读也可以写,可以实现移位寄存器和DRAM等存储功能,还可以实现基本的查找表逻辑 SLICEL(L:Logic): 其内部的LUT只可以读,只能实现基本的查找表逻辑 CLB的组成可以是上图的1个SLICEM + 1个SLICEL,或者是2个SLICEL,但是不会是2个SLICEM 。一般情况下,CLB中比例SLICEL:SLICEM = 2:1。
总结一下:1个CLB = 2个SLICE = 2 ×(4个LUT + 3个MUX + 1个CARRY4 + 8个FF),这就是CLB和SLICE的架构了。
三、查找表LUT 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----LUT查找表
查找表Look-up Table,本质上就是1个6输入,64深度的ROM(SLICEM中的则是RAM,因为可读)。通过将所有结果保存在其内部,使用时通过由输入构建的地址线对其进行查找,从而实现6输入的函数逻辑。
比如,你要实现功能:y = a | b ^ c & d & e & f。输入一共6个,可能的结果就是2的6次方64个,我把这64个结果全部存到LUT里,只要在使用的时候根据输入(也就是地址),拿出存在对应位置的结果就行了,这就是LUT实现各种函数的原理。
需要注意的是SLICEM中的查找表,除了读功能外还具备写功能,这就使得其内部的LUT由一个ROM变成了一个RAM,这也是其实现移位寄存器功能和分布式DRAM功能的原因。
3.1、移位寄存器SRL 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----移位寄存器
SLICEM可以在不使用触发器的条件下配置为32位移位寄存器(注意:只能左移)。这样,每个LUT可以将串行数据延迟1到32个时钟周期。移位输入D(LUT DI1脚)和移位输出Q31(LUT MC31脚)可以进行级联,以形成更大的移位寄存器。一个SLICEM的4个LUT6级联可以实现128个时钟周期的延时。多个SLICEM也可以进行组合。但SLICEM之间没有直接连接以形成更长的移位寄存器,在LUT B/C/D处的MC31输出也没有。由此产生的可编程延迟可用于平衡数据pipeline的时间。
那这种 Shifter Register 可以用来做什么呢?Xilinx Guide 中也给出了回答:
Delay or latency compensation Synchronous FIFO content addressable memory (CAM) 3.2、分布式DRAM 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----分布式RAM(DRAM,Distributed RAM)
SLICEM 中的 LUT 除了用作移位寄存器外,还可以被配置为Distributed RAM(DRAM)。DRAM 的 write 是同步的,read 则是异步的。注意这里所说的同步异步不是跨时钟域的概念,而是类似于组合逻辑和时序逻辑的概念。可以理解为,write 是时序逻辑,只有在时钟有效沿且 write enable 为1时,数据才会被写入。而 read 则和时钟无关,只要地址有效,数据就会在当前周期输出。如果想要同步输出,我们可以自己在输出端加 register。
DRAM的概念是相对于BRAM来说的,BRAM是FPGA底层的固有的硬件单元,而DRAM则是使用LUT配置而成的,其位置和使用会稍微灵活一些,但是也有其他不足,两者的使用需要权衡。
DRAM可以分为以下几种:
Single-port:Write 和 Read 共享一组地址线。也就是说 write 和 read 不能同时进行。 Dual-port:一个 port 用来 write 和 read,另一个 port 只有 read。 Simple dual-port:一个 port 用来 write,一个 port 用来 read 四、多路选择器MUX 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----MUX多路选择器(Multiplexer)
多路选择器MUX是一个多输入、单输出的组合逻辑电路,一个n输入的多路选择器就是一个n路的数字开关,可以根据通道选择控制信号的不同,从n个输入中选取一个输出到公共的输出端。
在FPGA底层,MUX也是作为一种基本的逻辑单元而存在,每一个SLICE中有3个MUX:2个F7MUX(F7AMUX + F7BMUX) + 1个F8MUX,这三个MUX本质上都是一个2选一的多路选择器,其都是作为LUT的辅助而存在。
每个SLICE中都有2个MUXF7,其输入只能为LUT6的输出,而输出只能接到MUXF8;每个SLICE中都有1个MUXF8,其输入只能为MUXF7的输出。
五、存储单元Storage Elements(FF) 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----存储单元之触发器、寄存器与锁存器
Slice中的存储单元便是我们前面提到的寄存器FF,FF是实现时序逻辑最基本的单元。需要注意的是,这些FF中的一半还可以被配置为锁存器Latch,但是一旦被配置后,则剩余的一半FF就不能使用了,会造成一定的资源浪费。
FF可以通过不同的控制集(时钟使能、复位方式、复位电平)来配置成不同形式的寄存器:
异步复位(FDCE) 异步置位(FDPE) 同步复位(FDRE) 同步置位(FDSE) 六、进位链CARRY4 本章节只是大概阐述,详细内容强烈推荐阅读:从底层结构开始学习FPGA----进位链CARRY4
CARRY4是一种超前进位的加法器(或者说减法器),是FPGA内部用来实现加减法运算的基本运算单元,但同时也可以实现一些其他的函数功能。每个CLB Slice都有一个专用的加法器CARRY4,可以实现两个4bit数的加减法运算。
———————————————— 版权声明:本文为CSDN博主「孤独的单刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/125175418
从底层结构开始学习FPGA----Xilinx 7 系列 FPGA 的逻辑优势 https://wuzhikai.blog.csdn.net/article/details/125025816
摘要
可配置逻辑块是所有可编程数字电子系统的基本构建块。自从赛灵思公司在 80 年代发明 FPGA 以来,可配置逻辑(以查找表和寄存器的形式)一直是所有市场和应用数字电子系统的重要组成部分。
本白皮书介绍了 28 nm 工艺的Xilinx 7 系列 FPGA 中可配置逻辑块的特性,重点介绍了与以前的 Xilinx FPGA 相比的优势以及这些变化为数字设计工程师带来的好处。 多功能逻辑结构允许在单一资源中实现大量逻辑和存储器功能,从而在性能、功耗和成本方面提供更高的效率。
这篇文章主要是通过介绍7系列与之前产品的对比,来展示7系列产品的基本逻辑单元的优点。
介绍
可配置逻辑块 (CLB) 是 Xilinx FPGA 逻辑结构的核心。在 CLB 中存在由查找表(LUT)、进位链和寄存器组成的Slice。这些Slice可以通过配置来实现逻辑功能、算术功能、存储器功能和移位寄存器功能。多年来,CLB 内的资源数量不断发展,以适当的成本不断提供最佳效能。最初的 Virtex® 和 Spartan®-II 架构在世纪之交推出,提供了一个由两个 Slice组成的 CLB,其中一个 Slice包含两个四输入 LUT 和两个寄存器。从那以后,Slice发生了显着变化——在 7 系列 FPGA 中,Slice由四个六输入 LUT (LUT6) 和八个寄存器组成,如图 1 所示。
CLB = 2×Slice =2 ×(4×LUT + 8×FF + 3×MUX + 1×CARRY4)
。7系列 FPGA 中的Slice架构
所有 7 系列 FPGA 系列(Artix™-7、Kintex™-7 和 Virtex-7 器件)都使用相同的逻辑架构:CLB 由两个 Slice 组成。 7 系列 FPGA 架构中的 Slice 有两种类型:一种能够在 LUT 中实现逻辑、移位寄存器和存储器功能,称为 SLICEM;另一种只能在 LUT 中实现逻辑功能,称为 SLICEL。采用这种全功能 SLICEM 与减少功能SLICEL 相结合的策略可实现最佳功能和性能,同时保持低成本和低功耗。
7 系列FPGA Slice架构紧密基于 Virtex-6 和 Spartan-6 系列中引入的Slice架构。 Virtex- 6、Spartan-6 和 7 系列 FPGA Slice架构之间的相似性为现有设计和 IP 迁移到 7 系列 FPGA 提供了一条简单的路径。设计师可以以最小的重新设计工作量,将他们的设计获得到最新的功能和最高的性能。此外,所有 7 系列 FPGA 使用相同的可扩展、优化架构允许最初针对一个 7 系列 FPGA 系列的设计轻松移植到另一个 7 系列 FPGA 系列。
Slice由两个 SLICEL 或一个 SLICEL 与一个 SLICEM 成对组合在 CLB 中。 7 系列FPGA 构建在基于列的 ASMBL™ 架构之上,允许在设计人员需要的地方轻松放置资源。在这种情况下,具有存储器功能的 Slice 在 DSP Slice 的列附近最为普遍,为设计人员提供接近所需位置的系数存储。 Xilinx 设计工具全面了解资源的相对布局,并以最有效的方式智能、自动地将设计映射到资源,同时遵守用户指定的任何约束。
图 2 显示了 LUT 和寄存器是如何相互排列的。图 2 仅包括一个 LUT 及其相关的两个寄存器,并省略了进位链。在一个完整的 Slice 中,有四个 LUT 和八个寄存器。
6 输入 LUT 能够实现任何布尔逻辑函数,该函数是 6 个输入信号的乘积,但也可以拆分为两个 5 输入 LUT——只要这两个函数共享公共输入。此外,SLICEM 中的 LUT 也可配置为 64 位分布式 RAM 或最多支持32 位的移位寄存器逻辑 (SRL) 功能。有关详细信息, 请参阅UG474。
6输入的LUT是由2个LUT5 + MUX2组成的。
常见的Slice资源用法
多功能性是可编程逻辑的基础,设计人员可以根据他们的目标以多种方式使用 FPGA 的Slice资源。
该架构允许独立于寄存器使用 LUT。 Slice 的 Bypass (AX/BX/CX/DX) 输入允许在不通过 LUT 的情况下访问寄存器的 D 输入,并且组合逻辑信号可以接到 Slice 的输出(图3)。
除了直接驱动寄存器外,Bypass 输入还可用于驱动进位链。
LUT 输出可以使用触发器多路复用器直接接入相关寄存器的 D 输入(图 4)。 O5 LUT 输出可以连接到任一寄存器的输入(图 4),而 O6 LUT 输出只能连接到其中一个寄存器(图 2)。
可以在单个 LUT 中创建不共享输入的逻辑函数。 LUT 的 A6 输入连接到高电平以启用双 LUT 模式,而 LUT 的其余五个输入可用于独立的逻辑功能。例如,一个不共享输入的二输入函数和三输入函数可以打包在同一个 LUT 中(图 5)。如果寄存逻辑输出,寄存器必须共享相同的控制信号。
多路复用器 F7 和 F8 使用bypass输入在两个 LUT6 输出之间切换,提供了一种在单级 CLB 中实现比六个输入更宽的功能的方法。
LUT6+MUX2的组合可以通过分时复用的方式来实现更大位宽的函数发生功能。
控制信号
7 系列 FPGA 中的所有触发器都可以使用设置/复位、时钟和时钟使能信号进行控制, 通常称为一组控制信号或控制集。每个 Slice 可以使用不同的控制集,但如果 slice 中的一个触发器使用了某种控制信号,例如同步复位,则该 Slice 中的所有其他触发器必须使用相同的信号作为它们的复位 - 或者不使用复位。如果 LUT 有可用输入,则可以将控制信号折叠到数据路径(即 LUT)中,从而允许在同一片内使用多个复位信号(图7)。
Xilinx 建议避免在设计中使用许多低扇出控制信号,以避免因控制集过多而遇到设计限制。综合工具自动避免生成使用大量低扇出时钟使能信号的电路。在赛灵思 XST 综合工具中,选项“-reduce_control_sets”可用于控制此功能。UG429,提供了有关控制集的良好设计实践的进一步指导。
在设备上电时,可以使用初始化值 INIT 将所有寄存器初始化为已知值。如果设计只需要在上电时进行初始化,使用这种方法可以消除在每个触发器上设置和复位信号的需要。这也允许将移位寄存器折叠到可用的 SRL 逻辑中,而不是使用触发器。
除了实现用户指定的使能外,寄存器上的时钟使能端口也被智能时钟门控优化所使用。有关这如何帮助将设计的动态功耗降低多达 30% 的更多信息,请参阅WP370。
控制集尽量不要搞得太多,因为同一Slice中的控制集只支持一类,如果种类过多,则会造成大量的资源浪费。
额外资源的好处
Xilinx CLB 架构的最新变化之一是向 Slice 添加了第二个寄存器。在 Virtex-6 和Spartan-6 FPGA 之前,高端 Xilinx FPGA 中的 CLB 架构由四个六输入 LUT 和四个寄存器组成。添加第二个寄存器,它首先在 Virtex-6 和 Spartan-6 FPGA 中实现, 也出现在 7 系列 FPGA CLB 架构中,增加了显着的好处,但它的成本增长却很小。
如图 8 所示,当将 LUT 配置为两个五输入 LUT 时,两个 LUT 的输出可以寄存在同一个 slice 中。这提供了一致的逻辑来寄存时序和寄存每个逻辑功能的能力,从而通过流水线提高性能。
此外,当在单个 Slice 中实现分布式 RAM 中的已寄存 32 x 8 RAM 时,所有八个寄存器都驻留在 sSice 内。这消除了在设备其他地方使用四个寄存器的要求,并提供了从RAM到寄存器的快速、一致的路径(图 9)。
在单个 Slice 中使用所有八个寄存器可显着提高性能,并具有不耗尽其寄存器的相邻slice 的额外好处资源。与每个 LUT 相邻的第二个寄存器的存在意味着可以将共享控制集的更多寄存器打包到单个Slice中,从而释放以前跨越多个Slice的资源。对一组不同大小和复杂性的设计的分析表明,这导致用作寄存器的Slice平均减少 15%,从而为用户腾出资源以在其 7 系列FPGA 设计中构建额外的功能。将每个 LUT 的第二个寄存器保持在同一个控制集上,并取消将该寄存器配置为锁存器的能力,这意味着它已经实现并且可以非常经济高效地在FPGA 架构中使用。虽然设计人员在编写设计代码时可以从了解逻辑架构中受益,但赛灵思工具套件可以了解不同系列的架构布局,并自动利用架构中存在的资源。
简而言之,寄存器的增加,是的更大位宽的功能实现不再需要跨越Slice,而是可以在一个Slice内完成,这使得各个信号的走线长度基本一致,有利于布线和时序收敛。
结论
Xilinx 7 系列 FPGA 中的可配置逻辑块是 Virtex-6 FPGA 和 Spartan-6 FPGA 中 CLB 的演进,为设计迁移到 7 系列 FPGA 提供了一条简单的途径。借助四个六输入 LUT 和八个寄存器,灵活的 Slice 逻辑结构可用于执行组合逻辑功能、算术功能、移位寄存器功能和存储器功能等多种不同功能。与以前的架构相比,四个 LUT 与八个寄存器的组合提供了性能优势和资源减少,而第二个寄存器的低成本实现几乎不会影响设备的整体成本。