要求,这可以通过约束和指令来优化RTL模型。而这个优化过程,必然是一个设计反复迭代的过程。
4)高层次综合器最终产生的RTL输出可以是RTL文件(VHDL 或 Verilog 代码),也可以是打包好的IP包,以便被其他设计工具方便的调用。
4. 高层次综合器的核心工作
在上述过程中,高层次综合器完成的最重要,也是最复杂的是第2)步——由高级语言综合产生RTL描述模型的部分。这个步骤又可以细分为:提取数据通路和控制通路、调度和绑定、优化三个阶段。其中,“提取数据通路和控制通路”是指分析高级语言代码,解释所需的功能。将其分解为“数据通路”部分电路和“控制”部分电路,用以对数据流进行处理、运算,以及控制、协调数据流的计算过程。“调度和绑定”则将电路要完成的运算和控制工作分配到不同的时钟节拍和逻辑资源来完成。“优化”则是指通过图8.1.3中的约束和指令来限制高层次综合器的调度和绑定,从而实现反复迭代、改进综合结果的过程。
1)设计延迟和设计吞吐量
设计延迟和吞吐量是衡量高层次综合器产生RTL模型最重要的两个指标参数,在介绍高层次综合器的工作之前先将要介绍这两个概念。
设计延迟(Latency)是指不考虑器件资源性能限制条件下,实现算法所需要的时钟节拍数。
1 void foo(a,b,c,d,*x,*y){
2 ……
3 func_A(a, b, t1);
4 func_B(a, b, t2);
5 func_C(c, t1, &x);
6 func_D(d, t2, &y);
7 }
代码8.1
使用高层次综合器实现代码8.1所示的算法,如不进行任何优化,将得到如图8.1.4所示的电路工作时序图,其设计延迟就是10个时钟周期。
图8.1.4 设计延迟举例
设计吞吐量(Throughput)是指不考虑器件资源性能限制条件下,两笔新输入(或输出)之间的周期数,图8.1.4中吞吐量也可能是10个时钟周期。
阅读至此读者一定充满了疑惑,既然设计延迟和设计吞吐量含义相同,为什么要用两个不同名字?其实我们图8.1.4中在讨论“吞吐量”的含义时,并未考虑电路“并发”的情况——硬件电路和高级语言实现算法不一样,可编程器件上不同部分的硬件电路可以同时执行多步运算,不像CPU执行高级语言语句只能一次逐条执行。这样当可编程器件中的一部分电路执行图8.1.4的func_B时,原来执行func_A的电路就可以再次取一笔新的数据执行,从而提高整个电路的吞吐量。如图8.1.5所示,电路的吞吐量就提高为4个周期。
图8.1.5 设计吞吐量举例
2)调度和绑定
下面以一段具体的C语言代码来说明高层次综合器可以如何通过“调度”将不同的数据操作映射到不同的时钟节拍中的。代码如下所示,其中变量t1、t2、t3和out之间存在依赖关系,只有依次计算得到上一个变量的值,才能得到下一个变量的值。
void foo{
……
t1 = a * b;
t2 = c + t1;
t3 = d * t2;
out = t3 – e;
}
代码8.2
如果设计要求不太关心上述算法所耗费的时间(设计延迟),则可以采用如图8.1.6所示的调度策略。即第一个时钟节拍实现a×b,第二个时钟节拍实现实现c+t1,第三个时钟节拍实现实现d×t2,第四个时钟节拍实现实现t3–e。总共花费4个时钟的时间,每个节拍对应的计算结果都采用触发器锁存输出,该调度方法对于所使用器件逻辑资源延迟的要求也最低。
图8.1.6 调度策略一示意图
若设计需要算法消耗较少的时间以实现更高的吞吐量,则可以通过去掉某些锁存触发器的方法,将更多的运算合并到单个时钟节拍中来完成。当然,这样做的代价是所消耗的器件资源能够在更短的时间内完成更多的组合逻辑运算。图8.1.7所示的是将a×b、c+t1、d×t2运算合并到一个时钟节拍中完成,从而将总耗时降低到2个时钟节拍的调度策略。
图8.1.7 调度策略二示意图
“绑定”是指把调度中涉及的运算“指派”给具体的逻辑资源来实现,即实现资源的映射,绑定策略可以大致分为共享和非共享两种。顾名思义,共享策略指多个运算共享同一个可编程硬件资源。当然共享的前提是不同运算不能再同一个时钟节拍中使用该资源。如图8.1.7所示的调度策略中,两个乘法运算都被分配到第一个时钟节拍中完成,这两个时钟节拍所使用的乘法器资源就是不可以共享的。
调度和绑定是一个相互关联的过程,它们共同建立在技术库的基础上,又受到用户指令的限制,实现合理的调度和绑定注定是一个反复试错和优化的过程。
图8.1.8 调度和绑定过程示意图
3)改善设计延迟和吞吐量
高层次综合器的核心工作,无非就是通过调度和绑定在满足设计占用资源/面积、时钟频率和功耗总体要求的前提下,获得最佳的延迟和吞吐量。而改善设计延迟和吞吐量的思路无非如下。
其一,利用数据之间的关联性,改善设计延迟。
再考虑代码8.1所示的算法,如果是在传统的CPU上执行这些代码,则只能如图8.1.4所示的时序依次执行func_A、func_B、func_C、func_D,设计延迟为10个时钟周期。但考虑各个函数模块之间的依赖关系是:只有完成func_A才能得到t1,才能执行func_C;只有完成func_B才能得到t2,以执行func_D。而func_A/func_C与func_B/func_D之间并没有制约关系,如图8.1.9所示。
图8.1.9 数据依赖关系的调整
则开发者可以通过相关约束和命令优化调度和绑定方式,将设计的数据通路调整为图8.1.10所示的两条,整体设计延迟降低为6个。
图8.1.10 设计延迟优化示意图
其二,在数据依赖关系允许的条件下,调整被绑定硬件资源的工作时序,最大程度的提高各个模块的并行度。使完成不同功能的硬件资源像“流水线”上的工人一样有序的持续工作,轮流对被处理的数据流中的具体数据分别进行“加工处理”,从而改善数据吞吐量。同样以上面的C语言伪代码为例,不考虑图8.1.10所示的延迟优化(四个函数顺序执行)。其中func_A、func_B、func_C和func_D相当于流水线中完成特定功能的“工人”,而不断进入的数据则相当于需要加工的“工件”。假设func_A、func_B、func_C和func_D所需的延迟分别为2、4、2、2个时钟周期,则可通过调度将重复执行的数据通路并行化,如图8.1.11所示。每个数据通路并行化后的延迟,为四个函数中执行时间最长的4个时钟周期。这样当大量数据依次通过数据通路后,电路的整体延迟虽然仍为10个时钟周期,但吞吐量缩减为3个时钟周期。
图8.1.11 设计吞吐量优化示意图
[原创www.cnblogs.com/helesheng]