维护遗留代码(4)——时序问题初露端倪
0赞在关于代码移交的那篇文 章中,我在最后提到了“正确完整的时序约束对编译结果的稳定性影响巨大”。那么这一结论是如何得出的呢?在得出这一结论之前,是怎样的现象导致我 们开始向这一方向探索?又是怎样的证据支持我们得出了这一结论呢?在接下来的几篇文章中,我就谈一谈在维护遗留代码的过程中,我们在时序约束和收敛问题上 的一些收获。我们先来看看问题是怎么出现的。
代码移交后的一个月内,我们收到多次代码更新,并作了相应的代码编译。编译结果表现出了很大的不稳定性:局部的代码修改会导致大范围内其他功能的变化(变 差)。从仿真和代码分析上看,发生变化的功能与被修改的代码之间并没有直接关联关系。进一步,即使是编译选项的设置差异,也会导致两次编译结果在功能正确 性上的差异。这时,我们开始怀疑是时序问题导致了这一现象。
进一步的测试,佐证了我们的这一怀疑。该设计由两个完全对称的部分组成,实现了两个功能相同的接口。这两部分采用同一套源代码,仅仅是在顶层例化时起了不 同的名称,分配了不同的引脚。这样的结构,使得我们可以观察到同一套代码,经过同一次编译,在FPGA芯片两个不同区域上实现后的差异。这一差异,在测试 中表现得很明显:相同的功能,在一个接口上表现正常,在另一个接口上表现异常;两次编译,一次表现为接口A异常,另一次表现为接口B异常。逻辑上相同的代 码为何在编译后会表现如此不同?是哪一个接口正常,哪一个接口不正常?还是两个接口都不正常,只是不正常的表现有所不同?
在这一个月内,经过了多次编译和测试,我们并没有找到这里面的规律,也找不到解决的办法,得到的只有更多的疑问。与此同时,我们开始回顾与第三方合作的过 程。在近一年的合作过程中,第三方一直负责代码修改和编译发布,由他们发布的编译结果也曾经表现出类似的不稳定现象,由于编译结果不符合要求,多次发布都 不得不反复和延期,第三方FPGA工程师也一直陷于“找(稳定)版本”的泥潭之中。在代码移交的过程中,我们曾经对第三方FPGA工程师在 LogicLock布局锁定参数的选取原则上提出过疑问,他的解答是没有确切原则,只是随机找到的“较佳”位置。在此后的多次代码更新中,我们也看到过他 对LogicLock布局锁定参数的修改,经过这些修改也确实能够得到功能符合要求的编译结果;但是这样修改的效果并不能在随后的多次编译中得到保持。以 上种种迹象表明,对于编译结果的稳定性,第三方工程师心里也没数。
从最初的怀疑,到证据逐渐充足,来自各个方面的碎片终于拼成了一幅完整的图片:该设计存在时序收敛问题,时序上不能收敛的逻辑电路表现出功能异常,由于每 次编译时序收敛的结果不同,导致不同的逻辑电路发生功能异常;在多次不收敛的编译结果中,“找到”一个“较佳”编译结果的过程是不可重复因而也是不可靠 的;只有解决了该设计的时序收敛问题,我们才能得到稳定的编译结果。riple
接下来,就是确定该设计的时序收敛问题在哪里,并加以解决。有了这一明确的目标,该设计在时序收敛上存在的问题很快就列了出来:
1. 这一FPGA设计缺少完整有效的核心时序约束。该设计采用了Quartus II中集成的Classic Timing Analyzer进行时序约束和分析,但是时序约束并不完整,甚至不正确;针对这些不完整、不正确的约束,每次编译结果都存在时序违规(Timing Violation)。显然,在最初给该设计写了简单约束后,第三方工程师并没有持续地维护这些约束,以至于代码和时序约束已经发生了分离。
2. 这一FPGA设计缺少接口时序约束。该设计采用了双边沿的高速双向接口,但是并没有对该接口进行输入/输出延时约束,而且Classic Timing Analyzer也不具备这样的约束能力。这是导致该设计不定期地在编译后出现底层数据错误的原因。
3. 这一FPGA设计采用了过于复杂的时钟结构。通过对编译报告中Fitter部分的分析,我们看到,该设计至少存在30个高扇出时钟信号,而该设计采用的器 件仅有20条全局时钟资源,更没有局部时钟资源。前面提到过,该设计包含两个完全对等的部分,但是从布局布线结果上看,EDA工具并不能做到把这20条全 局时钟资源均等地分配到两个部分中,余下的10条高扇出时钟的时序性能更是无从保证,这就是导致每次编译后,两个逻辑对等部分功能不对等的原因之一。
4. 这一FPGA设计中存在过于复杂的组合逻辑,但是缺少足够的流水线级别来分割这些关键路径。这一结论是通过比较我们自己开发的另一个类似(最高运行频率相 同,数据通路宽度相同)的设计得到的。在我们自己开发的设计中,大量地(甚至是过量地)采用了流水线结构,这一方面保证了设计的时序性能,另一方面表现为 组合逻辑与时序逻辑的比例为1.3:1;而第三方开发的设计中,组合逻辑与时序逻辑的比例为2.6:1。1.3:1的比例意味着该设计用到的每一个LE包 含的LUT和REG都得到了应用;而2.6:1的比例意味着有一半以上用到的LE中的REG资源没有得到利用。
5. 这一FPGA设计的资源利用率已经趋于饱和。该设计中100%的引脚、100%的存储器和90%以上的逻辑资源都被占用了。这样的资源余量给EDA工具留 下了不多的权衡空间。
上面列表中的4、5两条并不是该设计时序问题的关键所在。针对1至3条列出的问题,我们一一提出了解决方案:
1. 采用TimeQuest时序分析工具,对现有的时钟结构进行全面约束,包括添加所有必需的时序例外约束。
2. 采用TimeQuest时序分析工具,补充缺少的接口时序约束。
3. 通过修改代码,减少时钟个数,简化时钟结构。
现在看来是顺理成章的工作,在开始实施之初却存在着很大的压力和风险。从保证整个项目的质量和可维护性上来讲这一工作是至关重要的,但是由于没有相关经验 可以借鉴,我对这一工作能否达到预期目标并没有十分的把握,甚至在时序约束添加完整之前都很难拿出阶段性的成果,最终的效果只有在全部工作完成后才能看 到。在接下来两个多月的时间里,我们逐步完成了上述工作,实现了设计的时序收敛目标,编译结果最终稳定了下来。现在回首,这两个月是漫长而艰辛的!