从图1.1中我们可以看到,一个CLB由两个SLICE组成,而SLICE可以分为SLICEL和SLICEM,SLICEL仅可以实现逻辑功能,而SLICEM除了可以实现逻辑功能外,还可以实现位宽为1bit、深度为64bit的RAM, SLICEL和SLICEM的结构如图1.2和图1.3所示[1],SLICEL和SLICEM都是由4个6输入LUT(Look up table,查找表)、3个MUX(数据选择器)、1个Carry Chain(进位链)和8个Flip-Flop(触发器)组成,其中,LUT本质是一块6地址输入1数据输出的存储器(不考虑5输入LUT的情况),通过改变其内容,可以得到各种各样的真值表,也就得到了各种各样的逻辑功能,这是FPGA可编程性的基础之一。
比图1.2和图1.3可以发现,SLICEL的4个LUT,只有6bit地址输入和1bit数据输出(不考虑5输入LUT的情况),当bitstream文件配置下去后,用户是无法对LUT内容进行更改的,可以将之看作是一块ROM;而SLICEM除6bit读地址输入和1bit数据输出外,还有6bit的写地址输入(图1.2中W6:W1)和1bit的写数据使能(图1.2中WEN),2bit的写数据输入(DI1和DI2),这也是SLICEM的LUT可以被配置为位宽为1bit、深度为64bit的RAM的原因。
想要对LUT进行寻址,必须要知道LUT在FPGA中的组织方式,也就是知道FPGA的结构,需要说明的是,Xilinx 7系列FPGA与其前代产品相比,组成出现了较大的变化,在这里介绍Virtex-5 FPGA与Artix-7 FPGA的具体结构。
Virtex-5系列FPGA和Artix-7系列FPGA都是基于ASMBL(AdvancedSilicon Modular Block)架构的(但是Virtex-5基于二代ASMBL技术,Artix-7基于四代ASMBL技术) ,这里以7系列FPGA为例,对ASMBL架构进行简单介绍。
图1.1 ASMBL架构
7系列FPGA ASMBL架构如图1.1所示,该架构的关键在于,资源按列排布,同一列的资源是相同的,通过组合不同的列,可以得到面向各种应用、满足各种功能的FPGA,该架构的模块化思想,大大简化了FPGA的设计。我们来看一张真实的FPGA的内部结构图(器件型号为XC7A100T 该图通过Vivado软件得到),如图1.2所示,该FPGA分为8个区域(8个Clock Region),图中大量的淡蓝色非规则部分,是已经被占用的模块,我们选择比较“干净”的X0Y3区域进行介绍;
图1.2 XC7A100T内部结构
图1.3 X0Y3区域结构
X0Y3区域放大后的图片如图1.3所示,图中最左侧橘色部分为IO接口,蓝色部分为CLB,红色部分为BRAM资源(RAM36E1),绿色部分为DSP资源(DSP48E1),这样的资源组织方式与前面说的ASMBL架构是完全吻合的。我们再次将图2中的蓝色方块放大,如图1.4所示,可以看到,每个蓝色方块内部,都由2个SLICE组成。
图1.4 CLB内部结构
作者肉眼数了一下,一列蓝色方块中,蓝色方块的数量是50个,也就是一列CLB中包含50个CLB(这个知识后面要用到);一列红色方块中,红色方块的数量是10个,也就是一列BRAM中包含10个RAM36E1;一列绿色方块中,红色方块的数量是20个,也就是一列DSP中包含20个DSP48E1;
图1.5 FPGA中间部位的BUFG
需要特别注意的是,在FPGA的中间部分,是一组BUFG,如图1.5所示(图1.2放大,观察FPGA正中央),在BUFG的上方,是FPGA的上半部分(top),FPGA的下方是下半部分(bottom),这个知识后面会用到,可能有人会有疑问,图1.2结构如此对称,上半部分和下半部分不是一目了然的么?但是我们需要知道,不是所有的FPGA都是四行两列八个ClockRegion的,如图1.6(芯片型号为XC7Z020),为三行了两列六个Clock Region,这时只能通过观察BUFG的位置(图1.6中白框所在位置),判断哪几行是top部分,哪几行是bottom部分。
图1.6 XC7Z020内部结构
图1.7 XC5VLX110T所示
再来看一下Virtex-5FPGA的内部结构,如图1.7所示,可以看到,Virtex-5FPGA的结构与7系列FPGA很相似,都是模块化的、按列排布的;我们重点关注一下CLB方面的区别,对比图1.3和图1.7可以发现,7系列FPGA一列CLB包含CLB的个数为50个,而Virtex-5FPGA一列CLB包含CLB的个数为20个,这种差别在后期介绍LUT寻址时会体现出来。
二、FPGA配置帧格式与寻址格式
FPGA配置数据的最小单位是帧,下面先以Virtex-5为例,介绍配置帧的格式与寻址方式;然后介绍7系列FPGA与Virtex-5在配置帧格式与寻址方式方面的区别。1.Virtex-5 FPGA配置帧格式与寻址格式
Virtex-5配置帧格式如图1.8所示,图中共有36个帧,每个帧包含41个word,每个word由32bit组成,图中“X odd”部分代表一个CLB中编号为奇数的SLICE,而“X even”代表编号为偶数的SLICE,也就是“X odd”代表图1.4中右侧的SLICE,“X odd”代表图1.4中左侧的SLICE;Frame26-Frame29和Frame32-Frame35已经可以用来配置一个CLB内的8个LUT了,为什么配置一个CLB需要36个帧呢?因为除了需要配置LUT外,还需要配置触发器、连接线等其他的部分,因为我们仅需要研究LUT内容是如何重配置的,因此我们不对这些内容进行研究;另外,想要配置一个LUT,使用1个帧是搞不定的,因为1个帧只能配置1个LUT的2个字节(6输入LUT初始值为64bit,也就是8字节),需要4个帧才能配置一个LUT,但是,一个帧又同时涉及到了20个LUT的配置信息,也就是一个帧会对一列SLICE中的LUT进行配置(前面提到过,Virtex-5一列CLB中,CLB数量是20),这也是为什么要使用RMW(read-modify-write)的思想,也就是想配置1个LUT,需要先把这个LUT对应的4个帧读出来,修改该LUT对应的内容后,再将这4个帧写回去,这样才能保证其他的LUT不受影响。
图1.8 Virtex-5配置帧格式
细心的读者可以发现,图1.8中一个配置帧包含41个word,而一列SLICE包含20个SLICE,2个word可以配置一个SLICE的4个LUT,怎么多出了一个word?答案在图1.9中可以找到,一个配置帧的前20个word和后20个word都是用于配置LUT的,而中间的LUT用于其它功能,更多信息可以参考Xilinx官方文档:UG191。
图1.9 一个配置帧的具体格式
图1.10 Virtex-5 FPGA配置帧寻址格式
Virtex-5 FPGA配置帧的寻址格式如图1.10所示,Virtex-5系列FPGA配置帧的地址由24bit组成,其中,bit23-bit21作用是指示配置对象的类型,如取值为000代表对CLB进行配置;bit20作用是指示配置的对象在FPGA的上半部分还是下半部分(相关内容参考图1.5及图1.6);bit19-bit15是选择行的,如图1.2所示,该FPGA有4行,但是其编址方式是不是从上到下为0、1、2、3呢?
图1.11 行编址
当然不是的,其编址方式如图1.11所示,上半部分(top)和下半部分(bottom)是分开编址的,接近中间的行,地址为1,远离中间的行,地址依次加1,举个例子,图2.2中X0Y2、X1Y2、X0Y3、X1Y3位于上半部分,X0Y2、X1Y2行地址为0,X0Y3、X1Y3行地址为1;图2.2中X0Y0、X1Y0、X0Y1、X1Y1位于上半部分,X0Y1、X1Y1行地址为0,X0Y0、X1Y0行地址为1;bit14-bit7是列地址,需要注意的是,列地址是不分clock region的,比如图2.2中X0Y2、X0Y3的第一列,地址是相同的,另外,CLB、BRAM、DSP等列是统一编址的,列地址可以利用Viavdo软件,通过TCL命令提取出来;bit6-bit0是选择具体某一帧的地址,由图1.8可知,配置1个CLB(或者说配置一列CLB),需要36个帧,但是在对LUT重配置的时候,并不是所有帧都要重新进行RMW操作,只需要对与该LUT相关的四个帧进行RMW操作即可,对36个帧中某一具体的帧进行寻址,就需要用到minor address了。
2.7系列FPGA配置帧格式与寻址格式 7系列FPGA配置帧格式与图1.8基本类似,但是由于7系列FPGA一列CLB由50个CLB组成(而Virtex-5为20个),因此一个7系列FPGA配置帧包含101个word; 7系列FPGA的配置帧的寻址格式如图1.12所示,7系列FPGA的配置帧地址由26bit组成,与图1.10比较之后,可以发现,7系列FPGA 列地址为10个bit,而Virtex-5FPGA列地址为8bit,这是由于FPGA规模变大所致;地址其他部分基本没有发生变化。
图1.12 7系列FPGA的配置帧格式
rbt文件和bit文件一样,都是FPGA的配置文件,bit文件是二进制的,观察起来是很不方便的,然而rbt文件相当于bit文件的ASCII版本因而我们选择rbt文件作为我们解析的对象。一、做rbt文件解析的原因 为什么要做rbt文件解析?因为上节讲述配置帧格式的研究中,还存在一个不清楚的地方:图1.8中半个word可以配置1个LUT的1/4,那这半个word的bit顺序,与Verilog代码中LUT初始值的bit顺序,是否是一致的?是否存在某种映射关系?为了探索这一点,我首先想到的是对rbt文件进行解析,具体来说,建立一个工程,对一个LUT进行初始化,生成bit文件后,观察rbt文件中相应的初始化值是怎样的。后来发现,进行这项研究是十分必要的,研究结果表明,LUT的初始化值与rbt文件中对应的内容,并不是相等的,二者存在特定的映射关系, 且SLICEL和SLICEM中的LUT,映射关系是不一样的。下面对研究过程进行介绍。
图2.1 各种FPGA配置文件格式
二、准备工作 rbt文件中的配置数据是二进制格式的,观察十分不方便,因此第一项准备工作就是将二进制的rbt文件转换为十六进制,这项工作是通过一个python脚本实现的(位置:代码 bt_translation.py),如图2.2所示,因为作者此前没有写过python脚本,因此代码很不完善,下面结合代码说明需要注意的地方: (1)rbt文件的前7行是rbt文件的介绍信息,如图2.3所示,在用脚本转换进制之前,这7行需要手动去掉(作者python水平太低); (2)图2.2第一行的NUM值为956447,这是rbt文件去除前7行后的行数;需要注意的是,本工程针对的FPGA型号是XC7A100T,每个型号的FPGA规模不同,NUM的值也要相应变化; (3)图2.2第三、四行为原始rbt文件的路径(后缀改为txt)与转换后文件的路径,这个路径是绝对路径,需要根据实际情况进行修改。
图2.2 rbt进制转换代码
图2.3 rbt文件介绍信息
在UG470中,专门有一部分介绍bitstream的组成,这部分需要重点了解,详细内容见UG470的table 5-19。
三、具体流程
首先建立一个工程,在工程中例化一个LUT,这个LUT的位置必须是固定的,方便进行多次对照试验,如图3.4所示,例化LUT的位置为SLICE_X57Y53的LUT-D,如图2.4所示,为了简单起见,该LUT的初始值设置为“0x6996966996696996”,为什么设置成这个值呢?因为LUT初始值为该值时,作用为一个六输入一输出异或门,而异或门是CRC算法中的基本单元。
综合工程(工程在:工程jtag_axi_icap_lut_AX7103_simple_lut_6996文件夹下),生成rbt文件后,将rbt文件转成十六进制(见上述准备工作),结果如表2.1所示:
表2.1 rbt文件对应行数和值-1
rbt文件行数 | rbt文件值 |
510521 | 0x69960000 |
510622 | 0x 96690000 |
510723 | 0x 69960000 |
510824 | 0x 96690000 |
图2.4 LUT初始化-1
观察表2.1可以发现,rbt文件对应值的行数,总是差了101个word,与之前描述的配置帧格式是可以对应上的。那是不是可以说Verilog代码中LUT初始值的bit顺序(图2.4)和rbt中的bit顺序,是一致的呢?还不能这么说,因为“6”的二进制表示是“0110”,9的二进制表示为“1001”,可以发现这两个值都是十分对称的值。
为了进一步验证是否存在某种bit顺序映射关系,建立新的工程。LUT位置不变,但初始化值设置为“0x0123456789ABCDEF”,如图2.5所示,综合工程(工程在:工程jtag_axi_icap_lut_AX7103_simple_lut_0123文件夹下),生成rbt文件后,将rbt文件转成十六进制(上述操作),结果如表2.2所示:
表3.2 rbt文件对应行数和值-2
rbt文件行数 |
rbt文件值 |
510521 |
0x fe760000 |
510622 |
0x ba320000 |
510723 |
0x 98100000 |
510824 |
0x dc540000 |
图2.5 LUT初始化-2
可以发现,rbt文件值与LUT初始化值完全不一样,可以证明,确实存在某种特殊的映射关系。如何确定这种映射关系呢?最简单的是,建立多个工程,每个工程初始化值只有1bit(如0x0000000000000001、0x0000000000000002、0x0000000000000004、0x0000000000000008等),观察生成rbt文件中对应值的位置,理论上需要建立64个工程才能完全确定这个关系(但其实存在规律,不用这么多),成功破译了其对应关系,测试原始数据如表2.3所示(为了简单起见,将1个LUT的4部分数据写到一起,作为一个完整的word,“X”代表0x0000):
表2.3 测试原始数据-1
LUT初始值 |
rbt数据 |
0x X_X_X_0001 |
0x 8000_X_X_X |
0x X_X_X_0002 |
0x X_8000_X_X |
0x X_X_X_0004 |
0x 4000_X_X_X |
0x X_X_X_0008 |
0x X_4000_X_X |
0x X_X_X_0010 |
0x 2000_X_X_X |
0x X_X_X_0020 |
0x X_2000_X_X |
0x X_X_X_0040 |
0x 1000_X_X_X |
0x X_X_X_0080 |
0x X_1000_X_X |
0x X_X_X_0100 |
0x X_X_X_8000 |
0x X_X_X_0200 |
0x X_X_8000_X |
0x X_X_X_0400 |
0x X_X_X_4000 |
0x X_X_X_0800 |
0x X_X_4000_X |
0x X_X_X_1000 |
0x X_X_X_2000 |
0x X_X_X_2000 |
0x X_X_2000_X |
0x X_X_X_4000 |
0x X_X_X_1000 |
0x X_X_X_8000 |
0x X_X_1000_X |
0x 0100_X_X_X |
0x X_X_X_0008 |
0x 0200_X_X_X |
0x X_X_0008_X |
0x X_0100_X_X |
0x X_X_X_0080 |
0x X_0200_X_X |
0x X_X_0080_X |
0x X_X_0100_X |
0x X_X_X_0800 |
0x X_X_0200_X |
0x X_X_0800_X |
0x 0001_X_X_X |
0x 0008_X_X_X |
0x 0002_X_X_X |
0x X_0008_X_X |
0x X_0001_X_X |
0x 0080_X_X_X |
0x X_0002_X_X |
0x X_0080_X_X |
0x X_X_0001_X |
0x 0800_X_X_X |
0x X_X_0002_X |
0x X_0800_X_X |
将64bit初始值的最高位(MSB)定义为bit-64,最低位(LSB)定义为bit-1,则LUT初始值与rbt文件的位置映射关系如表2.4所示:
LUT初始值 |
rbt数据 |
1 |
64 |
2 |
48 |
3 |
63 |
4 |
47 |
5 |
62 |
6 |
46 |
7 |
61 |
8 |
45 |
9 |
16 |
10 |
32 |
11 |
15 |
12 |
31 |
13 |
14 |
14 |
30 |
15 |
13 |
16 |
29 |
57 |
4 |
58 |
20 |
41 |
8 |
42 |
24 |
25 |
12 |
26 |
28 |
49 |
52 |
50 |
36 |
33 |
56 |
34 |
40 |
17 |
60 |
18 |
44 |
观察规律后,将该映射规律用python代码(文件位置:代码map_SliceL.py)表示出来,如图2.6所示,
图2.6 对应python代码
后来又发现,SLICEL和SLICEM的LUT,从verilog代码初始值到rbt文件值的映射关系还是不一样的,上面是SLICEL的映射关系,SLICEM(位置是SLICE_X56Y53)的测试数据、映射关系、python代码分别如表2.5、表2.6、图2.7所示,表2.6中红色字体,代表这几组数据是推测得来的(后来证实推测正确)。
表2.5 测试原始数据-1
LUT初始值 |
rbt数据 |
0x X_X_X_0001 |
0x X_X_8000_X |
0x X_X_X_0002 |
0x X_X_X_8000 |
0x X_X_X_0004 |
0x X_X_4000_X |
0x X_X_X_0008 |
0x X_X_X_4000 |
0x X_X_X_0010 |
0x X_X_2000_X |
0x X_X_X_0020 |
0x X_X_X_2000 |
0x X_X_X_0040 |
0x X_X_1000_X |
0x X_X_X_0080 |
0x X_X_X_1000 |
0x X_X_X_0100 |
0x 8000_X_X_X |
0x X_X_X_0200 |
0x X_8000_X_X |
0x 0100_X_X_X |
0x 0008_X_X_X |
0x 0200_X_X_X |
0x X_0008_X_X |
0x X_0100_X_X |
0x 0080_X_X_X |
0x X_0200_X_X |
0x X_0080_X_X |
0x X_X_0100_X |
0x 0800_X_X_X |
0x X_X_0200_X |
0x X_0800_X_X |
0x 0001_X_X_X |
0x X_X_0008_X |
0x 0002_X_X_X |
0x X_X_X_0008 |
0x X_0001_X_X |
0x X_X_0080_X |
0x X_0002_X_X |
0x X_X_X_0080 |
0x X_X_0001_X |
0x X_X_0800_X |
0x X_X_0002_X |
0x X_X_X_0800 |
表2.6 映射关系-1
LUT初始值 |
rbt数据 |
1 |
32 |
2 |
16 |
3 |
31 |
4 |
15 |
5 |
30 |
6 |
14 |
7 |
29 |
8 |
13 |
9 |
64 |
10 |
48 |
11 |
63 |
12 |
47 |
13 |
62 |
14 |
46 |
15 |
61 |
16 |
45 |
57 |
52 |
58 |
36 |
41 |
56 |
42 |
40 |
25 |
60 |
26 |
44 |
49 |
20 |
50 |
4 |
33 |
24 |
34 |
8 |
17 |
28 |
18 |
12 |
图2.7 对应python代码
上述关系推导出来之后,还是得不到表2.2的结果,理论上,初始化值映射后,结果应如表2.7所示,经过多次探索尝试后,发现原因:Verilog代码中例化的LUT与FPGA上LUT的管脚的映射关系是不同的,通过一个例子说明,如图2.8所示,Verilog代码例化的LUT,初始化值“0x0123456789ABCDEF”,对应的6位地址是I5-I0,但实际FPGA内部的LUT如图2.9所示,对应的地址是A6-A1。
表2.7 rbt文件对应行数和值-3
rbt文件行数 |
rbt文件值 |
510521 |
0x d8d80000 |
510622 |
0x ffaa0000 |
510723 |
0x 55000000 |
510824 |
0x d8d80000 |
图2.8 Verilog代码例化的LUT
图2.9 FPGA内实际的LUT
而Verilog代码例化的LUT与实际的LUT,地址引脚的对应关系如图2.10所示,二者又存在一种映射关系,发生这种情况的原因是,综合工具Vivado会综合考虑各种情况,选择最优的布线,为了达到这种最优,允许一个LUT的地址线进行某种翻转。
图2.10 引脚对应关系
为了支持上述的翻转,作者写了一个简单的python脚本(目录:代码lut_pin_map.py),如图3.11所示,假如数据m是Verilog代码中的LUT初始化值,则n是映射到实际FPGA中的LUT初始化值。
图2.11 LUT引脚映射代码
四、总结
从Verilog代码中的LUT初始值到rbt文件中的LUT初始值,遵循的流程如图2.12所示,经过两次转换后,Verilog代码中的LUT初始值可以转换为rbt文件中的LUT初始值。图2.12 LUT初始化值转换流程
我们如何知道待配置的LUT,其位置是多少?代码中的LUT与实际的LUT引脚映射关系是怎样的?当然可以通过Verilog代码中的位置约束(如BEL="D6LUT",LOC="SLICE_X57Y53")和xdc文件中的引脚锁定约束(如set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6}[get_cells u_xor_lut/LUT6_inst_D_right]),在生成rbt文件之前,人为地分配好一切,但是,这样做可能会导致设计性能下降,因为Vivado的综合实现算法,是Xilinx公司多年研究的成果,可以认为其是最优的。因此,作者不建议通过添加位置约束和LUT引脚锁定约束的方式,确定待配置LUT的信息,而是生成rbt文件之后,通过Vivado软件和TCL命令,将LUT的位置信息和引脚约束信息提取出来。
图1.1 Verilog代码中的LUT例化
以图1.1中例化的LUT为例,生成rbt文件后,
LUT位置信息提取命令是:
get_tiles-of_objects [get_property SITE [get_cells -hierarchical"*LUT6_inst_D_right*" ]]
得到的结果是:CLBLM_L_X34Y53
上述结果给出的是LUT所在CLB的位置,其中X的值(34)就是前文的图1.12中的columnaddress,可以通过Y的地址,换算前文图1.12中的top/bottom address和row address。
LUT引脚映射关系提取命令是:get_bel_pins-of_objects [get_pins -hierarchical "*LUT6_inst_D_right*"]
得到的结果是:
SLICE_X57Y53/D6LUT/O6
SLICE_X57Y53/D6LUT/A1
SLICE_X57Y53/D6LUT/A2
SLICE_X57Y53/D6LUT/A3
SLICE_X57Y53/D6LUT/A4
SLICE_X57Y53/D6LUT/A5
SLICE_X57Y53/D6LUT/A6
上述结果与前文图2.10中的映射关系是一致的。
图1.1 ICAP原语
图2.2 HWICAP IP核
一、ICAP操作方法
ICAP操作方法主要参考UG953与UG470,尤其是UG470,第五节Configuration Details介绍了大量有用信息,需要重点关注的有:
(1)BitstreamComposition小节,介绍了bit文件/rbt文件的组成形式,可以从中看出一些配置流程相关的信息;
(2)ConfigurationMemory Frames 小节,与前文第一节介绍的帧格式是对应的;
(3)ConfigurationPackets小节,介绍了ConfigurationPackets的细节,上面说的ConfigurationMemory Frames主要是用于传输配置内容(如LUT的初始化值),而Configuration Packets主要用于传输一些配置相关的指令,如读写配置寄存器。ConfigurationPackets分为两种,即Type 1packet和Type 2 packet,Type 2报文必须跟在Type 1报文后面,个人认为Type 2报文的作用是对Type 1 报文进行补充,因为Type 1 报文word count字段仅有11bit,只支持2048个word传输,而Type 2报文word count字段有27bit,支持134217728个word,具体内容请参考UG470 Table 5-20、Table 5-21、Table 5-22;
(4)ConfigurationRegisters小节,配置相关寄存器,如图2.3所示,详细内容请参考UG470 ConfigurationRegisters小节。
图2.3 配置寄存器
从图2.2可知,HWICAP IP核仅支持AXI4-Lite接口,也就是我们操作HWICAP IP核的时候,其实是在配置寄存器,那么该IP核有哪些寄存器呢?如图2.4所示。
图2.4 HWICAP IP核的寄存器
下面重点介绍几个用到的寄存器。我们先分析一下我们将配置帧传输进去,需要哪些操作。
写配置帧:(1)将帧写入FIFO(2)写完后开始配置。
上面第一步,将帧写入FIFO,对应的是WriteFIFO Keyhole Register(keyhole就是钥匙孔,可以说十分形象了),寄存器格式如图2.5所示;第二步,开始配置,对应的寄存器是Control Register,该寄存器可以控制读,也可以控制写,寄存器格式如图2.6所示。
图2.5 Write FIFO Keyhole Register
图2.6 Control Register
读配置帧:(1)告诉IP核读几帧(2)开始读。
第一步对应的寄存器是Size Register,定义如图2.7所示,第二步对应的还是ControlRegister,如图5.6所示。
图2.7 Size Register
其他的寄存器也有各自的用处,建议读者参考PG134,进一步了解。
前面做的大量调研与学习,目的是为了能够做到对单个LUT的在线读和写,如何证明我们前面做的工作是正确的呢?答案是建立一个验证系统,在实际的FPGA系统中,将FPGA内部某个特定的LUT内容在线读出来,然后在线将某些内容写进去,如果可以成功做到这一点,可以说研究工作基本成功了。下面我们来介绍验证系统搭建的详细流程。
一、开发板与连接拓补开发板连接拓扑如图3.1所示,FPGA开发板型号为黑金AX7103,如图3.2所示,读者也可以用其他Xilinx FPGA板卡搭建验证系统(前文的知识适用于所有的7系列FPGA),但是Zynq系列暂时还无法调通,应该是作者忽略了些什么;PC与FPGA通过JTAG连接,PC上运行Vivado软件,在Vivado TCL Console中输入TCL命令,可以做到PC与FPGA的交互。
图3.1 验证环境拓扑
图3.2 FPGA开发板
二、FPGA内部模块设计FPGA内部模块设计如图3.3所示(工程位置:工程jtag_axi_icap_lut_AX7103_0123),JTAG2AXI IP核和HWICAP IP核负责实现接收PC的控制指令,实现对LUT内容的读写;目标LUT是被读写的对象,其Verilog例化如图3.4所示。
读LUT内容怎么验证?
答案:在PC的Vivado软件TCL Console内,写TCL命令,读取目标LUT内容,读取内容与rbt文件的相应位置比对(参见前文表2.7,为了简单起见,我们采用set_property LOCK_PINS{I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} [get_cells u_xor_lut/LUT6_inst_D_right]命令,保证代码例化LUT与真实LUT管脚一一对应,避免出现图2.10的情况)。
写LUT内容怎么验证?
答案:在PC的Vivado软件TCL Console内,写TCL命令,配置目标LUT内容,配置完成后,观察ILA。在配置之前,目标LUT初始值是0x0123456789ABCDEF,计数器产生递减的地址,因此可以用ILA观察到变化的数据;配置内容为全0,配置完成后,ILA观察到的内容为全0,证明配置成功。
图3.3 FPGA内部模块设计
图3.4 LUT例化
三、如何编写TCL代码 学习TCL本身的语法,需要参考Xilinx官方文档,UG835、UG894,熟悉TCL基本操作,另外,Xilinx高级SAE高亚军老师的TCL系列教程写的也很好; 学习第5节HWICAP和ICAP的具体操作方法,可以参考Xilinx给的HWICAP官方驱动(其中的example和src部分让作者受益匪浅),作者也给出了读写LUT需要的TCL代码(位置:代码读LUT-TCL指令 和代码写LUT-TCL指令),作者写的这些代码大量参考了Xilinx给的驱动(可以从作者代码的注释中看出来),另外一定要注意,TCL文件里面的路径是绝对路径,要修改成自己电脑上的路径。 四、验证结果
读LUT内容:
下载工程“jtag_axi_icap_lut_AX7103_0123”的bit程序(记得通过VIO复位,复位是低电平复位),运行“read_frame_510521.tcl”文件,该文件读取LUT的1/4的内容,对应rbt文件的510521行(见前文表2.7),读出的结果如图3.5所示(如果读出的结果不能完全显示出来,使用“set_param messaging.defaultLimit 5000”命令,修改TCLConsole显示的消息数量限制),可以发现是对应的(其他三行的读操作可以运行同目录下的其他TCL文件,结果都是正确的)。
图3.5 读结果
写LUT内容
仍然是同样的bit文件,在配置LUT内容之前,ILA可以看到的数据如图3.6所示,从上到下的三个信号,分别是LUT的输入(地址),LUT的输出,与LUT输出的并行化(LUT输出不方便观察,并行化之后,方便观察LUT初始值);在Vivado TCL Console行“source_write_all.tcl”文件后(记得改地址!!!),LUT的内容被重置为全0,如图3.8所示,配置成功。
图3.6 配置前的LUT输出
图3.8 配置全0后LUT的输出
获得的成果:
基本打通了单个LUT动态读写的流程,包括LUT寻址、LUT配置帧格式、rbt文件解析(对照用)、LUT信息提取、LUT配置方法(HWICAP和ICAP操作)、演示验证系统搭建。
后期还需要做的工作:
(1)后期需要用嵌入式CPU实现对HWICAP IP核的操作,而不是PC,因为PC+Vivado+TCL的方式效率太低了;作者对软件开发了解有限,需要一个精通软件的人员,协助作者开发上层软件及应用,比如开发文献中的配表系统;
(2)上述操作方法不适用于Zynq系列,原因未知,目前怀疑是某些操作不到位,Zynq系列的单个LUT动态读写,还需要进一步研究。
全部0条评论
快来发表一下你的评论吧 !