维护遗留代码(1)——从代码移交说起
0赞2009年6月下旬,riple开始着手维护一套第三方开发的FPGA代码。在移交之前,所有的代码维护都是由第三方完成的,我们很难控制项目的时间进 度,所以决定自己维护FPGA代码,以求加快项目进度。由于先前没能谈妥代码移交的问题,第三方代码对我们是不公开的,只有最终的二进制文件发布给我们, 所以我们对第三方代码的质量、风格一直都没有什么认识。直到移交之后,我们才逐渐对这套第三方开发的FPGA代码有了清晰的认识,一些先前没有出现的问题 暴露了出来,一些先前无法解释的问题也逐步有了答案。
在代码移交的过程中我们得知,这套代码是近10年前由第三方的一名工程师开发、维护至今的。这套代码经历了Altera绝大部分的FPGA器件系列,曾经 在Flex、ACEX、APEX系列的FPGA器件中实现过,直到现在移交给我们的Cyclone III系列。在这10年间,由于器件性价比提高,这套代码的功能也得以逐步增强,其复杂程度也逐渐提高。由于其历史悠久,生命周期长,所以我把这套代码称 为“遗留代码”(Legacy Code)。
从代码移交到现在的5个月的时间里,我用在学习、约束、修改这套代码上的时间总共有4个月。这期间完成了对这套代码的仿真环境搭建、时序收敛评估、时序约 束、时钟方案修改、编译优化的一系列工作。直到现在,这套代码的时序收敛问题才得到了初步解决,编译结果才基本稳定下来。在这一系列的博文中,我计划把已 经解决的问题中涉及到的“通用”的技术问题总结出来,供大家参考;同时还有一些尚未解决的问题,跟大家共同探讨,请大家帮忙。
先说说代码移交的问题。在第三方抵达之前,我们初步制定了一个移交清单,由于对第三方代码几乎一无所知,所以清单列得尽量全面。由于没有涉及具体的项目内 容,这个清单符合“通用”原则,我列在下面,供大家参考、指正。
Checklist for Third-Party FPGA Projcet Transfer:
1. (Mandatory) Source code of all the FPGA projects
2. (Mandatory) Projects list and build sequence and description of file dependence
3. (Mandatory) Project constraints including pin assignments and area and timing constraints for each project
4. (Mandatory) The revision number and required updates of the EDA tools used
5. (Mandatory) EDA tool license or description of where and how to obtain the license
6. (Mandatory) IP core source code and instantiation tool with detailed procedure for instantiation
7. (Mandatory) IP core license or description of where and how to obtain the license
8. (Mandatory) Any .mif files used for ROM/RAM initialization
9. (Mandatory) FPGA image file format and any tool used to regenerate the specific file format
10. (Mandatory) Design documents including SW and HW interface register description
11. (Mandatory) Datasheet of the IP core
12. (Mandatory) The schematic files of the PCB
13. (Mandatory) Proprietary dataset or header format defined inside of FPGA design
14. (Mandatory) JTAG debug interface on board and pin definition
15. (Optional) The configurations for any of the FPGA vendor dependent primitives instantiated in the FPGA design
16. (Optional) Testbench and simulation script for the FPGA source code
17. (Optional) Validation code for the test of the newly fabricated and mounted PCB
18. (Optional) A list of known issues of the latest FPGA design
尽管清单列得很详细,在项目移交之后的半个多月的时间内,我们多次编译出的二进制文件在功能和稳定性上仍然与第三方提供的二进制文件有很大差异——代码移 交并不成功。我们排除了所有的软件、代码版本上的问题,最终找到了差异所在:我们在移交过程中没有要求获得db和incremental_db文件夹下的 内容。
项目中混合使用了基于逻辑锁定(LogicLock)的版图规划(Floor Planning)功能和基于设计分割(Design Partition)的增量编译(Incremental Compilation)功能,而我在先前的排查过程中恰好对这两项功能与工程完整性之间的关系存在着三点错误的认识:
1. 我错误地认为,采用了版图规划的设计,由于其时序约束和位置约束(逻辑锁定)内容都完整地保存在了QSF文件中,这些约束可以保证编译结果的可重现性。也 就是说,我错误地认为时序约束和位置约束是工程完整性的充分条件,只要获得了这些信息,工程就是完备可重现的。这一观点在大多数情况下都是正确的,但是由 于该工程同时采用了基于设计分割的增量编译方法,仅仅是QSF文件中保存的信息并不能保证工程的完整性。
2. 我错误地认为,在采用增量编译的过程中,综合后网表并不像布局布线后网表那么重要,甚至是可以忽略的。事实证明,只要采用了增量编译,每个设计分割块对应 的任何一种级别的网表都需要像对待设计源文件一样保留下来。如果没有保留,增量编译就只能从设计源文件重新编译获得对应网表。这一重新编译的过程,等价于 增量编译过程中的初次编译——重新构建了增量编译过程中第二次编译所需的网表。这一重新构建获得的网表,就是与原工程的差异所在。(这里需要说明的是:获 得完全一致的增量编译网表在理论上是可行的,前提是编译环境完全一致,通过恰当的版本控制,这是可以做到的;但是只要编译环境中的任何因素发生了变化,比 如编译优化选项或其他设计文件发生了变化,就不能获得完全一致的增量编译网表。这里的原理可以参考Quartus II手册中Fitter编译选项下关于seed取值的说明:布局初始位置的选取,与特定编译环境是唯一对应的)
3. 我错误地认为,第三方工程师采用了和我们相同的编译过程和环境。事实上,第三方工程师没有使用版本控制工具,在自己的机器上复制工程时总是包含db和 incremental_db文件夹,而移交代码时出于减小压缩包尺寸的初衷,没有给我们移交这两个文件夹下的内容;而我们采用了版本控制工具,出于减小 服务器压力的考虑,也没有要求这两个文件夹下的内容。现在看来,第三方工程师在获得综合后增量编译网表时,采用了不同的优化选项(还有可能在初次编译获得 网表后修改了其他设计源文件),由于他也没有意识到上述第二点问题,他没有从工程中把增量编译网表输出为单独的文件保存下来。虽然编译结果在他的机器上是 可重现的(初次编译获得的网表以数据库的形式一直保存在incremental_db文件夹下),但是在我们的机器上无法重现他的初次和二次编译过程(由 于编译环境发生了变化,重新构建是得不到他的最初网表的。事实上,由于缺少版本管理,除了保存incremental_db文件夹这一方法外,他也无法再 次生成同样的网表了),所以造成了工程的不完整和编译结果的不一致。
这一问题,通过重新获取db和incremental_db文件夹(第三方工程师把这两个文件夹压缩成了160多个分卷压缩包,通过相应的160多个邮件 发送给了我们)得到了表面上的解决(虽然编译结果可以重现,但是我们双方都无法重现第一次编译获得增量网表的过程了),生成了功能和稳定性相同的二进制文 件(为什么在设计源文件相同的情况下,编译结果如此依赖于增量编译网表?在时序约束和分析上花费了两个月的时间之后, 我们才证明了正确完整的时序约束对设计稳定性的影响——没有明确的时序收敛目标,编译过程不能得到有效控制,偶然性就占了上风。这是后话)。只有依靠版本 控制工具的“时光倒流”功能把编译环境恢复到初始状态下,这一问题才能得到根本解决。
README in incremental_db folder:
This folder contains data for incremental compilation.
The compiled_partitions sub-folder contains previous compilation results for each partition.
As long as this folder is preserved, incremental compilation results from earlier compiles
can be re-used. To perform a clean compilation from source files for all partitions, both
the db and incremental_db folder should be removed.
The imported_partitions sub-folder contains the last imported QXP for each imported partition.
As long as this folder is preserved, imported partitions will be automatically re-imported
when the db or incremental_db/compiled_partitions folders are removed.
June 23 2009 - June 26 2009- July 16 2009