学习vivadoHLS第6章例程中文版.docx
《学习vivadoHLS第6章例程中文版.docx》由会员分享,可在线阅读,更多相关《学习vivadoHLS第6章例程中文版.docx(29页珍藏版)》请在冰豆网上搜索。
学习vivadoHLS第6章例程中文版
学习vivado第6章Lab1——设计分析
概述
从C,C++或者SystemC用一般的设计方法来创建的RTL实现包括以下任务:
•综合设计
•查看最初实现的结果
•应用优化指令提高性能
你可以重复上述步骤直到要求的性能已经达到。
随后,你可以重新设计,以提高资源使用率。
这个过程关键部分是对结果的分析。
教程解释了如何使用报告和用户分析视图来分析设计和决定优化的应用。
教程由一个lab练习组成:
•体现了HLS交互分析功能•带你经历一个设计从最初的实施,通过六个步骤,并多次优化,产生最终优化设计的过程
在整个教程中证明在一个项目中执行这些步骤使您可以轻松地比较不同的解决方案的能力
Lab1综合和分析DCT设计,使用从设计分析的见解来进行优化,并判断该最优化的效果。
教程设计描述从xilinx网站下载教程设计文件,在教程设计中查看信息。
教程所用的设计文件在教程目录vivado_HLS_Tutorial\Design_Analysis用在Lab练习中的设计样本是2-DDCT函数。
为了突出设计分析功能,您的目标是让这个设计在100或更小的间隔内操作。
设计应该能够在每100时钟周期内处理新设定的输入数据。
Lab1:
设计优化这个练习解释了用户分析界面视角的基本操作,还有你如何用这个界面来驱动设计优化。
重要:
在教程中的图片和命令假设教程数据路径vivado_HLS_Tutorial被解压放置在c:
\vivado_HLS_Tutorial中。
如果教程数据目录解压缩到不同的位置,或者在Linux系统上,调整一些路径名引用到的位置您选择放置Vivado_HLS_Tutorial目录。
步骤1:
创建并打开工程1.打开VivadoHLS命令提示符a.在windows系统中,采用Start>AllPrograms>XilinxDesignTools>Vivado2014.2>VivadoHLS>VivadoHLS2014.2CommandPrompt,如下图b.在linux系统下,打开新的shell,
2.用命令提示符窗口,如图107,从lab1中将目录切换到设计分析教程。
3.执行TCL并建立vivadoHLSProject,采用的是vivado_hls–frun_hls.tcl如图107所示
4.当vivadoHLS完成,在用户界面里打开工程。
用vivado_hls–pdct_prj命令打开,如图108
步骤2:
查看源代码并创建最初的设计1.双击原文件夹中的dct.cpp文件,打开原文件代码并查看。
本例使用的DCT函数。
图109示出了该代码的概述。
•在图109的左边显示了代码的层次•dct顶层函数有三个子函数:
read_data、dct_2d、write_data.•函数dct_2d有一个子函数dct_1d•图109的中间展示了在每个函数中的循环•在图109的右边展示了数据是如何通过函数和循环被处理的•执行read_data函数,数据通过循环RD_Loop_Row处理,RD_Loop_Row有一个子循环RD_Loop_Col.•在read_data函数完成后,dct_2d函数执行。
•在函数dct_2d中,Row_DCT_Loop处理数据,Row_DCT_Loop里面嵌套了两个循环:
DCT_output_loop和DCT_inner_loop•DCT_inner_loop调用函数dct_1d等等,直到函数write_data处理数据
2.在工具栏中点击RunCSynthesis来把设计综合成RTL级
步骤3:
用综合报告查看性能当综合完成后,综合报告会自动打开,见图110在报告的部分展示了性能
图110突出了下列信息:
•满足8ns的时钟频率•顶层设计花费3959个时钟周期用来写所有的输出•在3960个时钟周期后你可以采用新的输入,在输出数据已经完成后的一个时钟。
这立即显示,设计不是流水线,但这一事实也注意到报告中:
类型设置为none,而不是流水线。
•顶层有一个实例,其中有3668等待时间和启动时间间隔。
这个模块也不是流水线,占大多数的时钟周期。
•注意read_data函数和write_data函数没有被作为顶层的实例所注意到。
图111展示了,在综合过程中,这个模块被自动内联起来。
(层次被删除)高层次综合必须自动内联小的函数为了提高结果的质量。
你可以通过添加内联指令与-off选项的功能,防止这种情况。
•在read_data和write_data函数中的循环,因在顶层中实现,因作为在顶层函数中的循环被报告。
•每个循环有144时钟周期的延迟。
(因为循环不是流水线,没有启动间隔。
)•用RD_Loop_Col作为一个例子,你可以看到为什么循环延迟了144个周期•子循环RD_Loop_Col有两个周期的延迟,在每次循环间隔中(循环等待时间)和8个tripcount:
循环总延迟为2×8=16个时钟周期。
(这个循环要迭代8次,每次花费2个时钟周期。
所以这次循环结束需要花费8*2=16个时钟周期)•从RD_Loop_Row中,需花费1个时钟进入到循环RD_Loop_Col中,还需花费1个时钟周期返回到RD_Loop_Row中。
因此对于RD_Loop_Row间隔延迟为(1+16+1)18个时钟周期。
•RD_Loop_Row有8个tripcount,因此总循环延迟为8*18=144时钟周期。
•因此DCT模块总的延迟为:
•RD_Loop_Row为144时钟•加dct_2d3668个时钟•加WR_Loop_Row144个时钟•加进入每个模块的1个时钟
要查看实例子块dct_2d和dct_1d的细节,从资源管理器中的soltion1下的SYN/reports文件夹,打开各自的报告。
您也可以使用设计分析的角度来看,以更互动的方式查看这些细节。
步骤4:
用分析视窗查看性能在综合完成后的任意时间都可使用分析视窗1.点击Analysis视窗按钮(图112),来开始交互设计分析。
分析视窗有5个窗口组成,每一个都在图113中高亮显示。
您在教程中使用它们。
模块和循环层次显示扩展(默认情况下,它们显示收起)。
使用模块的层次结构窗格浏览层次结构。
该模块层次结构窗格显示既为整个设计的性能和资源使用的信息。
性能配置窗格显示这个层次级的性能细节。
你刚才在报告中查看的信息(顶级DCT块),在这两个窗格里是相似的。
性能的视图还被示出(图113右侧)。
该图显示了在特定模块中的操作是如何调度时钟周期的。
•左列列出的资源。
•子模块是绿色的•在源代码中的循环操作结果是黄色的。
•标准作业是紫色•请注意,DCT有三个主要的资源•调用RD_Loop_Row的循环。
+符号表示这个循环下层结构,你可以展开这个循环并查看。
•调用dct_2d的子模块•调用WR_Loop_Row的循环
最上面一行列出了在设计中的控制状态。
控制状态的内部状态,高层次综合使用操作计划转成时钟周期。
在RTL有限状态机(FSM)的最终状态和控制状态之间有密切的关系,但没有一个一对一的映射。
2.点击RD_Loop_Row和子循环RD_Loop_Col,全展开循环层次如图114
由此看来,你可以看到,在RD_Loop_Row的第一状态(C1),检查循环退出条件并添加操作执行。
该加法可能是循环迭代的计数,我们可以证实这一点。
3.在C1状态中选择加号,右击并选择C源代码图115。
高亮显示打开C源代码,这个操作在C源代码中创建一个加法器。
在屏幕上更多细节如图115,你能确定它的确是循环计数器。
它是唯一在这行,而变量被命名为“r”。
RD_Loop_Row循环下个状态中(状态2),循环RD_Loop_Col开始执行。
4.在RD_Loop_Col里点击任何操作,看见高亮源代码更新。
这将有助于确认在C源代码的操作如何在RTL的实现理解。
•检查循环退出情况•一个加法器,用于循环计数变量“c”的。
•从RAM中执行读(一个周期用以产生地址,一个周期用来读的数据)。
•向RAM中执行写
5.在C源代码窗口中点击X,关闭窗口。
6.在ModuleHierarchy窗口中,点dct_2d函数导航到这个函数的视图。
如图116
同样,你可以看到一些循环(图116显示为黄色)。
循环确保设计将有小资源,但设计需要多个迭代的状态来完成:
循环的每次迭代都会在下一次迭代开始前完成。
您可以对循环使用流水线,以改善性能。
在性能配置文件的细节中显示大部分延迟是由循环Row_DCT_Loop和Col_DCT_Loop造成的。
7.点击循环Row_DCT_Loop和Col_DCT_Loop,在性能窗口中完全展开它们,如图117。
在性能视图中展开这些循环显示了两个循环调用函数dct_1d。
除非这个功能本身是流水线,否则流水线对这个循环来说没有好处。
该模块的层次结构显示dct_1d的时间间隔为210个时钟周期,这意味着它只能接受一个新的输入每210个时钟周期后。
8.在模块层次结构,单击函数dct_1d导航到这个函数的视图中。
9.展开在PerformancePro中的循环来看到如图117。
在图117中,你可以看到一系列嵌套循环可变为流水线。
您可以选择执行下列操作之一:
•您可以用流水线函数,然后流水线调用它的循环。
(由于该函数是流水线,循环可以利用流水线部分的优点。
)•您可以流水线此函数内的循环,使该功能执行得更快。
用流水线方式函数将展开所有的循环,从而大大增加了资源的使用。
如果目标是获得尽可能高的性能,没有考虑资源,这可能是要执行的最佳优化。
你可以找到关于流水线循环和函数教程优化设计的更多细节。
对于这种情况下,该方法是优化循环,并保持资源使用在最低限度。
10,点击综合按钮,返回到主综合图上。
步骤5:
应用循环流水线并查看循环优化在这步中,你创建一个新的解决方案,并向循环添加流水线指令。
当流水线嵌套循环时,在内部循环中采用流水线通常来说是最好的。
通常,高层次综合一般可以自动展开的循环嵌套(允许外循环简单地反馈内循环)。
为什么执行某些循环优化比其他循环更好,请参阅教程“优化设计”的更多信息。
1.在工具栏上选择NewSolution按钮,或者用菜单Project>NewSolution来创建一个新的方案2.点击Finish,接受默认。
3.保证能在信息窗口中看见C源代码4.在指令标签中,向函数dct_1d中的循环DCT_Inner_Loop中添加流水线指令。
a.在指令窗中右击DCT_Inner_Loop,并选择InsertDirectiveb.在指令编辑对话框中激活上部的指令下拉菜单并选择PIPELINWc.点击OK并选择默认的最大流水线率5.重复步骤4,对以下循环a.在函数dct_2d中,循环Xpose_Row_Inner_Loopb.在函数dct_2d中,循环Xpose_Col_Inner_Loopc.在函数read_data中,循环RD_Loop_Cold.在函数write_data中,循环WR_Loop_Col指令窗口展示了以下(高亮)优化指令的应用图119
6.在工具栏里点击RunCSynthesis按钮,把设计综合成RTL级7.当综合完成,用CompareReports工具栏按钮或者用菜单Project>CompareReports来比较方案1和2图120展示了方案1和2的比较结果。
流水线的循环降低设计的延迟,在solution2的减少了近50%。
接下来,您再一次打开分析视窗,分析结果,并决定是否有更多进行优化的机会。
8.点击Analysis视窗按钮,开始交互设计分析。
当分析视窗打开,您可以观察到延迟主要还是由于模块dct_2d。
在继续前一步分析之前,可以查看在这个层次的循环如何都得到了优化。
PerformanceProfile(图121)展示了这两个循环的延迟已经从方案1的144个时钟减少到65个时钟。
流水线循环传输延迟从Latency=iterationlatency*(tripcount*interval)到Latency=iterationlatency+(tripcount*interval)HLS也使这一切成为可能,通过自动执行循环打散(不再有任何循环层次)。
您可以通过查看控制台窗格中看到这一点,或solution2的日志文件。
图122显示已自动优化的循环。
9.在ModuleHierarchy中,点击函数dct_2d,导航到这个函数的显示上。
在PerformanceProfile中,您可以看到所有循环的延迟已经被大幅减少。
(Row_DCT_Loop和Col_DCT_loop已经大约从早先的报告减少了一半,如图116)。
但是,主要的延迟任然由于这两个循环,每次在调用dct_1b模块的时候.
10.在ModuleHierarchy中,点击函数dct_1d,导航到这个函数的显示上。
在PerformanceProfile中,图123。
显示循环延迟已经减少。
但在这仍然有循环层次。
(仍然有的循环DCT_Outer_Loop,如图123,因此没有循环打散发生)
在性能视图中查看这些循环说明了为什么这个循环没有进一步的优化。
11.在Performance视图中,点击循环DCT_Outer_Loop和DCT_Inner_Loop打开循环层次。
如124
12.选择writeoperationinstateC5.
13.右击并选择GotoSource
图124展示出了这个循环是有层次的,因为在DCT_Outer_Loop的水平上,DCT_Inner_Loop以外还有其他操作,阻止了循环展开。
一个防止循环展开的操作,如图124高亮显示,如下。
向数组中写不能够在内部循环中展开。
为了实现关于DCTOuterLoop有1的间隔,则您需要在外循环实现流水线-内循环本身实现流水线并没有好处。
你应该用流水线取代外循环。
这会导致内部循环被完全展开。
增加资源使用的结果,但你仍然远离100的目标,还没有准备好为整个函数用流水线来操作(当外循环也是完全展开,会使用更多的资源)。
14.点击综合按钮,返回到综合视图上。
学习vivado第6章Lab2——设计分析
步骤6:
为了瓶颈对循环进行优化和修改1.在工具栏中选择Newsolution按钮或用菜单Project>NewSolution来创建新的方案2.点击Finish并且接收默认设置来创建solution33.保证C源代码显示在信息窗口中4.在指令标签中a.在dct_1d函数中,选择对DCT_Inner_Loop使用流水线指令b.右击,并且选择RemoveDirectivec.任然在dct_1d函数中,选择loopDCT_Outer_Loopd.右击,并选择InsertDirectivee.在DirectivesEditor的对话框中,在上部激活Directives下拉菜单并选择PIPELINEf.点击OK并选择默认最大的流水线率
指令窗口展示了如下的优化指令(高亮的)如图125
5.在工具栏里点击RunCSynthesis按钮,把设计综合成RTL级6.当综合完成,用CompareReports工具栏按钮或者用菜单Project>CompareReports来比较方案2和3图126展示了方案2和3的比较结果。
输出循环流水线有提高性能和增加资源的实际结果。
获得改善的延迟好处,因为在这个设计中有多个循环调用dct_1d这个函数很多次。
在这个模块中节省延迟是翻倍的,因为这个函数在很多循环中用到。
现在所有的循环都是流水线,值得重新审视设计观察是否还有性能限制瓶颈。
瓶颈就是限制数据流,并阻止逻辑块工作在最大数据速率下。
在数据流中的这些限制来自很多方面。
例如I/O端口和采用块RAM实现的数组。
在这两种情况。
有限的端口数量(在I/O或块RAM上)限制了数据读写的速率。
另一个瓶颈源是数据依赖前面源代码。
在一些例子中,这些数据依赖固有算法如何工作,因为只有完成先前的计算后面的计算才能进行。
有时候采用优化指令或一个小的改动的C代码可以将瓶颈消除。
首要任务是辨别在RTL设计中存在的问题。
还有一些可以采取的方法:
•启动在ModuleHierarchy中的最大间隔延迟报告并导航到下一层找到一些大延迟或间隔的原因•单击ResourceProfile检查I/O和内存的使用情况。
•单击图形查看器,并查看性能视图模式,用来指示数据流的限制。
在这种情况,你可能会有一些方法。
您可以使用分析视图在设计中快速识别这些地方。
7.点击Analysis视窗按钮,开始交互式设计分析8.在ModuleHierarchy,保证模块dct被选择。
9.在Performance视图中,在设计中展开第一个循环。
如图127.RD_Loop_Row_RD_Loop_Col(这些循环被打平并命名,现在两个循环是串行)
在两个状态中实现循环,红色箭头在图127中,展示了路径从循环开始到循环结束。
箭头几乎是垂直的(一切都发生在两个时钟周期),这个循环在延迟方面的实现也得到改善。
1.在性能视图,扩展WR_Loop_Row执行相同的分析。
它同样也为延迟优化。
2.双击函数dct_2d并导航到函数dct_2d内部。
您可以通过使用层次相同的分析流程下来。
如果您执行此分析,会发现,所有的功能块和循环也执行类似的优化(几个周期),直到dct_1d块进行检查。
3.在Performance窗口中,双击dct_1d函数,并导航到dct_1内部4.扩展DCT_Outer_Loop,在图128查看图128展示了与早期循环调度(调度仅有延迟一些时钟)非常不同观点。
调度展示了从输入到输出有很长的路径。
有两件典型的事情影响调度类型:
在源代码中的数据依赖性和由于I/O和RAM块的限制。
现在,您将检查该块资源共享。
5.在Performance视窗中,window底部点击Resourcetab6.展开MemoryPorts,如图129
资源共享视图显示了在设计中资源是如何在不同的控制状态中使用的。
行列出了在设计中的资源,图129,展开内存资源。
在列中展示了在状态中用到的资源。
如果资源在多个状态都是激活的,这个资源在不同的时钟被重复利用。
图129展示了BRAMsrc内存接口被每个时钟最大的利用。
(顶多,块RAM可以是双端口而且两个端口都被使用)。
这是一个很好的迹象表明设计可能使用了的是带宽有限的内存资源。
要确定这是否真的是这样的话,你可以进一步检查。
7.为块RAM的src选择一个读操作。
8.右击并选择GotoSource来查看视图如图130
图130展示出了在src变量上的读操作是从内循环DCT_Inner_Loop的读操作。
当DCT_Outer_Loop被流水线化,这个循环被自动展开。
而且此循环中的所有操作都可以并行发生(如果数据依赖性允许)。
八个读取被迫发生过多个周期,因为数组src为实现在RTL块RAM和RAM块可以只允许两次读取(最大)中的任何一个时钟周期。
在图130中,读操作需要2个时钟周期:
一个周期,以产生地址的RAM块和一个循环来读取数据。
只发射(地址生成周期)被显示,因为它在下一时钟周期的操作重叠。
您可以优化使用优化指令来分割的块RAMBlockRAM的访问。
发挥功能dct_1d阵列存取被定义为输入参数的函数,因此,位于这个块的外部。
•输入数组dct_1d的第一个实例是函数DCT中的buf_2d_in。
•输入数组dct_1d的第二个实例是是函数dct_2d的col_inbuf。
在这两种情况下,该数组是尺寸是二维,DCT_SIZE*DCT_SIZE(8×8)。
默认情况下,这结果在64元素的单端口RAM块中。
因为数组中配置的代码行由列的表格,我们可以分区第二大小和创建八个独立块RAM的:
一个用于每一行,从而允许该行数据,以并行地访问。
18.点击综合按钮,返回到综合视图上。
步骤7:
分区块RAM和分析并行性1.在工具栏里选择NewSolution按钮,创建一个新的解决方案2.点击Finish并接受默认设置,创建解决方案43.保证C源代码在信息窗口是可见的4.在指令标签中a.在函数dct中,选择数组buf_2d_inb.右击并选择InsertDirectivec.在DirectivesEditor对话框中在上部激活Directives下拉菜单,并选择ARRAU_PARTITIONd.保持Complete类型E.更改维度,设置为2,为了让数组分区保持为2维。
f.点击OK
5.为了在函数dct_2d中的数组col_inbuf重复此过程。
指令窗口现在展示了以下优化指令(新的指令是高亮的)如图131
6.在工具栏中点击RunCSynthesis按钮,并把设计综合成RTL7.当综合完成,使用CompareReports按钮,或使用菜单Project>CompareReports来比较解决方案3和4图132展示了方案3和方案4的比较结果,在dct_1d模块的源ram块中提高对数据这样提高了整体性能因为dct_1d模块块执行频率。
您可以重新检查分布式指令在器件源中的影响8.点击Analysis视窗按钮,开始交互式设计分析9.在ModuleHierarchy中,保证模块dct被选择10.选择ResourceProfile在左下角中,选择ResourceProfile标签。
11.扩展MemoriesandExpressions看图133
ResourceProfile显示,在当前级别的层级使用的资源(在ModuleHierarchy窗格中选定的块)。
如图133所示:
这个模块有端口大多数资源是由于在该块内的实例(子块)。
•有九个内存,8个用在分布式RAM块
在级别的层级内大多数逻辑(表达式)是由于加法。
伴随一些比较器和选择器。
先前优化中重要的一点是你能够看到现在有额外的内存是由于数组分布式优化。
你仍然有一个目标来保证设计能够接受新设定的样本在每100个时钟周期。
图132.展示了你仅仅能接受新数据在每509时钟。
这个已经比先前的好多了,先前优化设计(大概3700时钟),但离优化要求还有很远。
到这一点,已经集中在提高每一个单独的循环和函数中的设计的延迟和时间间隔。
现在,您必须应用数据流的优化,这使得各个环路和功能并行执行,从而提高了整体的设计周期。
12.点击综合按钮,返回到综合视图上。
步骤8:
分区块RAM和应用数据流优化5.在工具栏里选择NewSolution按钮,创建一个新的解决方案6.点击Finish并接受默认设置,创建解决方案57.保证C源代码在信息窗口是可见的8.在指令标签中a.选择顶层函数dctb.右击并选择InsertDirectivec.在DirectivesEditor对话框中在上部激活Directives下拉菜单,并选择DATAFLOW.d.点击OK指令窗口现在展示了以下优化指令(新的指令是高亮的)见图134
5.在工具栏中点击RunCSynthesis按钮,并把设计综合成RTL6.当综合完成,使用CompareReports按钮,或使用菜单Project>CompareReports来比较解决方案4和5
图135展示了方案4和方案5的比较结果,你可以看到间隔改善了。
设计花费539时钟来产生结果,但是现在可以接受新的输入在每个405个时钟周期。
这个任然大于100时钟要求,因此你必须分析当前的性能7.点击Analysis视窗按钮