7系列 之 查找表(Look-Up Table,LUT)

背景

  • 本系列于好友千歌离开上海后开始写起,
    至今2024年10月21日,正好三个月了。
    现在,是时候更新了。

  • 基于Xilinx的7系列FPGA,
    在通过文档《ug474_7Series_CLB.pdf》学习CLB过程中,
    对CLB中的查找表(Look-Up Table,LUT)进行展开学习,遂就有了此文。

  • 本文实际上刚刚已经发布过一遍了,
    但千歌的建议:插入一节,讨论如何把LUT6拆开使用,或者插入,不同级别LUT等效比。
    因此,笔者在原文基础上,增添了<2.2 ··· 不同级别LUT等效比>这一小节,致千歌!

系列目录与传送门

基于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:如果文章有误,欢迎大家指出、讨论,笔者也会积极改正,希望大家一起进步!



一、总述

1.1 ··· LUT的概念

查找表(Look-Up-Table,LUT),属于FPGA中实现逻辑功能的核心组件,非常重要。

查找表,顾名思义,可以简单暴力地理解为是一种可以查询内容的表;再文明一点的理解就是:一种存储预定义结果的数据结构,可以实现一定数量输入的布尔函数。

1.1.1 ··· LUT的Verilog代码

//	Vivado中的LUT模块的调用格式:
// 6 input lookup-table
LUT6_2 #(
	.INIT(64'h0000000000000000) //  Specify LUT Contents
) LUT6_2_inst (
	.O6(O6), // 1-bit LUT6 output
	.O5(O5), // 1-bit lower LUT5 output
	.I0(I0), // 1-bit LUT input
	.I1(I1), // 1-bit LUT input
	.I2(I2), // 1-bit LUT input
	.I3(I3), // 1-bit LUT input
	.I4(I4), // 1-bit LUT input
	.I5(I5)  // 1-bit LUT input (fast MUX select only available to O6 output)
);
//	通过参数INIT输入预定义的结果,再通过I0、I1、I2、I3、I4、I5的输入值从预定义的结果中选取对应的结果进行输出。
//	通过修改n输入LUT的INIT值,n输入LUT可以表示所有n位输入或者n位以内输入的布尔函数(或者门电路),这恰巧体现了LUT的灵活性。
//	如果LUT资源的数量越多,那么所能够实现的功能就越复杂。

1.1.2 ··· LUT的结构图

(博客截图,道友们可以去看看原文,原文链接为:http://t.csdnimg.cn/WJQPC)
在这里插入图片描述

1.1.3 ··· LUT在Vivado中的Device视图

以xc7a100tfgg676-2 (active) 为例,
在Vivado的Device视图中放大并进行观察,如下图所示。
在这里插入图片描述
上图,是对 SLICE_X30Y78(SLICEM)的Device视图中的LUT6放大截图。

在这里插入图片描述
上图,是对 SLICE_X31Y78(SLICEL)的Device视图中的LUT6放大截图。

将上述两种LUT的结构进行对比,可得:
1、 相同点:都具有地址输入线(A1-A6),两个输出口(O5-O6)。
2、不同点:SLICEM的LUT6具有写地址输入线(WA1-WA8),写数据端(DI1 DI2),写使能端(WE),而SLICEL的LUT6没有。

1.2 ··· LUT的本质

LUT本质是一个RAM(Random Access Memory,随机存取存储器),目前是基于SRAM(Static Random Access Memory,静态随机存取存储器)工艺的。一个6位输入LUT,即LUT6,可以被看成一个有6位地址线的大小为64x1的RAM。

LUT可以将n位输入数据作为n位RAM的地址,然后通过该n位地址找到对应的值,并将该值作为结果进行输出。可以理解为,LUT的RAM结构,存储了输入值与输出值之间的对应关系,即真值表。那么,真值表对应的便是上述代码中的参数INIT

查阅资料时,发现有一些博主在博客里说LUT本质是一个ROM(Read-Only Memory,只读存储器)。笔者在这里的理解是:
1、他们是站在了LUT工作时(或是被读取数据时)的角度来看待LUT的,此时的LUT就是一个ROM。
2、SLICEL中的的LUT只可以被读取数据,不能被写入数据,只能实现基本的查找表逻辑。

其实,正是SLICEL、SLICEM中的LUT6的不同,才使得SliceL的LUT只具有存储数据的能力,只能作为ROM使用,而SliceM的LUT还具备了数据写入的功能,可以作为DRAM或移位寄存器使用。

也在一个博主的评论区看到了相应的问题互动,如下图所示。
在这里插入图片描述
RAM与ROM虽然在 读写操作 和 掉电后的效果 上有所差异。但在这里,我们只需要弄清LUT的原理或本质就好。

在好多篇博客和文章里面都看到过这样的一句话,现引用过来供道友们参考:
”当用户通过原理图或HDL语言描述了一个逻辑电路以后,PLD/FPGA开发软件会自动计算逻辑电路的所有可能的结果,并把结果事先写入RAM,这样,每输入一个信号进行逻辑运算就等于输入一个地址进行查表,找出地址对应的内容,然后输出即可“

除此之外,还看到这样一句话,忘了保留原文出处了,等想起来了在加上吧:
”在设计阶段,设计者可以使用硬件描述语言(如Verilog或VHDL)编写逻辑,编译器会自动将这些逻辑映射到查找表中。“

二、7系列中的LUT

2.1 ··· 文档《ug474_7Series_CLB.pdf》中的LUT

在这里插入图片描述

  • 7系列FPGA中的LUT可以被配置成 一个具有单输出的六输入LUT6 ,或者 两个具有单独输出但共用同样地址或逻辑输入的五输入LUT5 ,每个五输入LUT 的输出可以选择性地寄存在一个触发器(flip-flop)中。

  • 7系列中,函数生成器(function generators) 可用六输入查找表LUT6实现。一个Slice中的四个LUT6(分别用A、B、C、D编号)中的每个LUT6有六个独立的输入(分别用A1至A6的编号表示),两个独立的输出(分别用O5、O6两个编号表示)。

  • 逻辑单元与六输入LUT的数量比为 1.6 :1 。
    ( 原文:The ratio between the number of logic cells and 6-input LUTs is 1.6:1.)

  • 下图为ug474_7Series_CLB.pdf的原文截图
    在这里插入图片描述

函数生成器(function generators) 可以实现:

  • 任意的六输入布尔函数
  • 两个任意的五输入的布尔函数,只要这两个函数的输入一样
  • 两个任意的3输入,或2输入,或更少输入的布尔函数

两个任意的5输入或少于5输入的布尔函数使用:

  • A1-A5输入
  • A6接高电平
  • O5和O6输出

通过一个LUT的传播延迟与所实现的功能无关,来自 function generators 的信号可以:

  • 直接出Slice(通过A、B、C、D 输出O6,或通过AMUX、BMUX、CMUX、DMUX输出O5)
  • 从O6输出进入专用异或XOR门
  • 从O5输出进入进位逻辑链(carry-logic chain)
  • 从O6输出进入进位逻辑多路选择器的选择行
  • 提供存储单元的D输入(Feed the D input of the storage element)
  • 从O6输出到 F7AMUX/F7BMUX 宽多路选择器

除了基本的 LUTs 外,Slices 还包含三个多路选择器( F7AMUX、F7BMUX 和 F8MUX ),这些多路选择器用于组合4个 function generators 来实现 Slice 中7或8个输入的任何函数。

  • F7AMUX:用于从LUT A 和LUT B生成7输入函数
  • F7BMUX:用于从LUT C和LUT D生成7输入函数
  • F8MUX:用于组合所有LUTs来生成8输入函数。

个人理解: 一个Slice有4个LUT6,即可实现4个六输入函数。也就是说,如果需要实现7输入的函数或LUT7,就可以用 F7AMUX 将最下面两个 LUT6(A、B)组合起来,也可以用 F7BMUX 将另外两个 LUT6(C、D)组合起来;如果使用F8MUX将两个LUT7进行组合,即可得到一个8输入函数或者LUT8。 具有8个以上输入的函数可以使用多个Slice来实现,但在CLB中,不能通过Slice之间直接相连,来得到输入个数大于8的function generators。

”但是级联的LUT越多,潜在的组合逻辑时延就越严重,对于系统时延的要求就越苛刻,一般采用寄存器来同步后再将结果输出,以确保输出结果正确。“(原文链接:http://t.csdnimg.cn/eCByP)

”在实际开发中,如果采用多个LUT6级联来实现,当输入位数过多,即组合逻辑复杂时,则会造成级联的LUT6过多,逻辑级数变大大,其结果是通常会造成组合逻辑延时多大,导致时序紧张,因此,在设计中,我们一般采用插入FF的方法来切割组合逻辑,防止级数偶合,使得每一段的逻辑级数都不过于长,从而优化时序。(原文链接:http://t.csdnimg.cn/jzc2N)“

2.2 ··· 不同级别LUT等效比

本节,我们可以通过代码、RTL视图、综合后的视图、资源消耗的角度,
来探讨LUT6、LUT5、LUT4之间的联系。

2.2.1 ··· LUT6

下面这段代码,按照前文所叙述,可以用LUT6来实现。
那么,我们先观察其在Vivado中的RTL视图和综合后的视图。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout
    );
 	assign dout = din0 & din1 & din2 & din3 & din4 & din5;
 
endmodule

RTL视图如下,就是几个简单的与门电路的组合,实际上这个可以直接从Verilog代码中看出来的。
在这里插入图片描述

那么,它综合后是什么样子的呢?综合后的视图,如下所示。
在这里插入图片描述不错所料,是个LUT6电路。
那么,如果把其中的 与门 换成 或门,或者是 异或等其它逻辑门,改变代码逻辑如下。
理论上来说,代码综合后的结果依旧是个LUT6电路,只是LUT6里面的真值表发生了改变。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout
    );
 //   assign dout = din0 & din1 & din2 & din3 & din4 & din5;
    assign dout = ( (din0&din2) ^ din1 | din4 & din5 ^ din3);
 
endmodule

在这里插入图片描述
如果接着改变代码逻辑如下,
理论上来说,RTL电路不会发生太大变化,只是改改门电路的位置与输出,
那么,代码综合后的结果依旧是个LUT6嘛?还是说会发生改变?

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout0,
    output dout1
    );
   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout1 = din0 & din1 & din2 & din3 & din4 ;
//    assign dout = ( (din0&din2) ^ din1 | din4 & din5 ^ din3);
 
endmodule

其RTL视图如下:
在这里插入图片描述
代码综合后的视图为:
在这里插入图片描述哦豁!这份代码综合后的结果为一个LUT6与一个LUT5了,已经发生了改变。
那接下来就掰扯掰扯LUT5。

2.2.2 ··· LUT5

如下,把上述代码中的dout0输出注释掉,可以推测出下列代码的综合结果是个LUT5。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout0,
    output dout1
    );
//   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout1 = din0 & din1 & din2 & din3 & din4 ;
//    assign dout = ( (din0&din2) ^ din1 | din4 & din5 ^ din3);
 
endmodule

其代码综合后的结果为:
在这里插入图片描述
如下,把上述代码接着做更改,下列代码的综合结果会是两个LUT5嘛?
盲猜不是。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout0,
    output dout1
    );
//   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout1 = din0 & din1 & din2 & din3 & din4 ;
//    assign dout = ( (din0&din2) ^ din1 | din4 & din5 ^ din3);
 
endmodule

综合后的结果如下,是两个LUT5?(可能会有道友觉得应该是个LUT6呀!)
在这里插入图片描述道友莫慌,我们先看综合报告:
在这里插入图片描述
在这里插入图片描述
综合报告里面确实也说了,两个LUT5。
但是,所给出的资源利用里面,也明确地表明只消耗了一个LUT(即LUT6),且 LUT as Logic 。
结论便是:即便综合后的视图显示是两个LUT5,但最终资源消耗里面只占用了1个LUT6。

2.2.3 ··· LUT4

对于LUT4,诸位道友们,咱们也不用装了,直接上代码。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout0,
    output dout1
    );
//   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout1 = din0 & din1 & din2 & din3 & din4 ;
//    assign dout = ( (din0&din2) ^ din1 | din4 & din5 ^ din3);
 
endmodule

此时,综合后的结果为:两个LUT4。那么,资源消耗会显示是一个LUT5嘛?
在这里插入图片描述当然不会!7系列里面的LUT资源是LUT6 。且看资源利率用报告,
在这里插入图片描述
最终资源消耗里面只占用了1个LUT6。

那么,把代码的输出加到3输出呢?

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
//    output dout0,
    output dout1,
    output dout2,
    output dout3
    );
//   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout3 = din0 | din1 | din2 | din3;
   assign dout2 = din1 ^ din2 ^ din3 ^ din4;
   assign dout1 = din0 & din1 & din2 & din3;
 //  assign dout0 = din1 & din2 | din3 ^ din4;
 
endmodule

综合结果,是符合预期猜想的。
在这里插入图片描述那么,资源利用呢?是一个LUT6嘛?
在这里插入图片描述
不是的,因为一个LUT6最多有两个输出,而且还是作为两个LUT5时才可以的。
因此,当有三个LUT4输出时,那就需要消耗两个LUT6了。

那么,接着该代码,把代码的LUT4输出加到4个输出,理论上是消耗两个LUT6。

`timescale 1ns / 1ps

module prj_top(
    input din0,
    input din1,
    input din2,
    input din3,
    input din4,
    input din5,
    output dout0,
    output dout1,
    output dout2,
    output dout3
    );
//   assign dout0 = din0 & din1 & din2 & din3 & din4 & din5;
   assign dout3 = din0 | din1 | din2 | din3;
   assign dout2 = din1 ^ din2 ^ din3 ^ din4;
   assign dout1 = din0 & din1 & din2 & din3;
   assign dout0 = din1 & din2 | din3 ^ din4;
 
endmodule

代码综合后的结果为:
在这里插入图片描述
综合后的资源消耗:
在这里插入图片描述

2.2.4 ··· 小结

千歌给我发了一句话:”一般实际使用中,真大佬,会压榨每一个LUT。所以会涉及这个,如何把LUT6拆开使用。如果只写概念,会比较浅。“

三、逛博客时所看到的LUT6

3.1 ··· 逻辑单元的发展历程

3.2 ··· LUT6的被选择

3.3 ··· LUT与真值表、LUT6与LUT5

四、总结与补充

在这部分,留下一些LUT学习的后话,留给未来。

  • LUT与传统逻辑门电路
    传统门电路的复杂度随着输入逻辑变量数量的增加而增加,
    且电路内部的逻辑门、线路的延迟不可避免、也不尽相同,且不同的延迟会引起时序相关问题;
    而LUT本质上是通过读取输入信息来选择预设的结果进行输出,因此,每次LUT内部的查找的延迟都是一定的。
    这点结合着 章节3.3 中的 LUT与真值表的关系 来看,就会很容易理解,为什么在FPGA中会选择用LUT来代替传统门电路。

  • 在博客上看到的两段话

”LUT本质上是一个RAM。当逻辑块实现组合逻辑电路时,查找表中存储的真值表就可以作为小规模的存储器。通常来说,FPGA不可能将所有的查找表都用来实现组合逻辑电路。因此,利用查找表为用户电路实现存储器,既能实现芯片内部的存储功能,又能提高硬件资源使用率。“(这段话的出处,当时忘了记录是在哪篇博客上看到的)

”在Xilinx的FPGA架构中,只有SLICEM的逻辑块里的查找表才能被用作存储器,由查找表构成的存储器称为分布式RAM。分布式RAM能够实现异步访问,但如果使用分布式RAM实现大规模存储器,那么实现逻辑的查找表就会变少。因此,建议仅在需要小规模存储器时使用这种实现方法。“(原文链接:http://t.csdnimg.cn/eiM6G)

  • 《ug474_7Series_CLB.pdf》官方文档,建议诸位道友们原汁原味读一遍。

  • 这篇CSDN上的博客《FPGA从入门到精通(2) - LUT》写的还不错,与知乎上的《FPGA从入门到精通(2) - LUT》是同一人所写、所发表,后者被引用、借鉴的次数比较多,道友可以去看一看。

  • 文档ug474有句话”Device capacity is often measured in terms of logic cells, which are the logical equivalent of a classic four-input LUT and a flip-flop.“,意思是说器件容量通常用逻辑单元来衡量,逻辑单元相当于一个经典的四输入LUT和一个触发器。

  • 插个题外话:上次参加了上海慕尼黑电子展,看了下复旦微、高云、紫光同创、智多晶、安路、京微齐力这几家的FPGA产品,他们的产品手册上都会标注芯片所对应的逻辑资源容量,或是直接标注LUT6的资源数量。

### Lookup Table 的定义 Lookup 表是一种数据结构,用于存储键值对映射关系。这种表允许通过给定的键快速检索对应的值。在编程和数据库环境中,lookup 表通常用来提高查询效率并简化复杂的数据处理逻辑[^1]。 ### 编程中的 Lookup 表用法 在编程中,lookup 表可以由多种方式实现: - **字典/哈希表**: 这是最常见的形式,在 Python 中可以通过 `dict` 实现。 ```python lookup_table = { "apple": "fruit", "carrot": "vegetable" } ``` - **数组或列表**: 当索引本身就是键时适用。 ```python days_of_week = ["Monday", "Tuesday", "Wednesday"] ``` 这些方法使得程序能够高效地访问预定义好的关联数据集[^2]。 ### 数据库中的 Lookup 表应用 在数据库设计里,lookup 表经常被创建来维护参照完整性以及减少冗余信息。例如,产品类别可能存放在单独的一张表内作为 lookup 表供其他表格引用。这不仅有助于保持数据一致性还便于后期管理和扩展[^3]。 ```sql CREATE TABLE product_categories ( category_id INT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE ); ``` ### Lookup 表的具体实现案例 对于具体的应用场景而言,假设有一个电影分类系统,则会建立如下所示的一个简单的 lookup 表模型[^4]: #### 创建 Movie 类别 Lookup 表 ```sql CREATE TABLE movie_genres ( genre_id SERIAL PRIMARY KEY, title TEXT NOT NULL UNIQUE ); INSERT INTO movie_genres (title) VALUES ('Action'), ('Comedy'), ('Drama'); ``` 此 SQL 片段展示了如何构建一个名为 `<abstract-schema-name>Movie</abstract-schema-name>` 的抽象模式下的 genres 子表,其中包含了不同类型的影片种类条目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值