12、实现(Implementation)流程:PR1(初始实现)、PR2(重构实现)、实现结果分析
动态部分重构的实现流程,说白了就是两轮“跑实现”。
第一轮叫PR1,把整个设计当成普通工程做一遍。第二轮叫PR2,只针对重构区域做增量实现。这两轮跑完,你才能拿到真正的、可用的部分重构比特流。
我刚开始接触这个流程时,总觉得PR1和PR2差不多,结果踩了不少坑。今天我把这两轮的关键点、我的个人习惯,以及怎么看结果,一次性讲清楚。
12.1 PR1:初始实现——先搭好“地基”
PR1的目标很简单:把整个设计(包括静态区和所有重构模块)当成一个普通工程,完成综合、布局布线。
为什么要先做这一步?因为你需要一个“参考点”。
说白了,PR1就是给静态区定好位置、定好资源。后续PR2再怎么折腾重构区,静态区的布局布线都不能动。
PR1的关键产出:
- 静态区的布局布线结果(.dcp文件)
- 静态区的时序约束(已收敛)
- 重构模块的“占位”信息(黑盒)
我个人习惯在PR1阶段就把时序约束做到位。别想着“反正后面还要调”,静态区的时序一旦在PR1没跑通,PR2基本没戏。
小技巧:PR1跑完后,务必检查一下静态区的资源利用率。我建议静态区占用不要超过70%,留出余量给后续的PR2调整。
12.2 PR2:重构实现——只动“重构区”
PR2才是真正体现“部分重构”价值的一步。
在PR2中,工具会读取PR1的静态区结果,然后只对重构模块进行布局布线。静态区完全不动。
你想想看,这意味着什么?
意味着你可以为同一个重构区,编译出多个不同的比特流。每个比特流对应一个重构模块,而静态区保持不变。
PR2的流程大致如下:
- 读入PR1的静态区dcp
- 读入重构模块的网表(综合后的)
- 只对重构模块做布局布线
- 生成部分比特流(.partial.bit)
注意:PR2阶段,重构模块的管脚分配必须和PR1中的“占位”完全一致。我曾经因为改了一个管脚名,导致PR2跑出来的结果根本不能用。嗯,那次排查花了我整整一天。
12.3 实现结果分析——怎么看“跑没跑通”
跑完PR1和PR2,不是看一眼“没有错误”就完事了。你得学会看报告。
我一般会重点看三份报告:
| 报告名称 | 关注点 | 我的经验 |
|---|---|---|
| 时序报告(timing summary) | WNS、TNS、Fmax | WNS必须为正,TNS最好为0 |
| 资源利用率报告(utilization) | LUT、FF、BRAM、DSP | 重构区资源不要超过90% |
| DRC报告 | 部分重构相关规则 | 重点关注PR-related violations |
为什么会特别关注DRC报告?因为部分重构有很多“潜规则”。比如重构区边界不能有跨区域的组合逻辑路径,这些DRC会帮你查出来。
一个实用的检查方法:
PR2跑完后,用report_utilization -hierarchical看一下重构模块内部的资源分布。如果某个slice的利用率超过95%,说明布线压力很大,时序可能出问题。
12.4 核心逻辑流程图
下面这张图,把PR1和PR2的关系、输入输出、以及结果分析的关键点,一次性画清楚了。
12.5 避坑指南
最后,分享几个我踩过的坑:
- PR1和PR2的约束要一致。我曾经在PR1里用了set_max_delay,PR2里忘了加,结果时序差了200ps。嗯,排查了很久才发现。
- 重构区的时钟要单独处理。如果重构区有自己的时钟,记得在PR2里重新声明。别指望PR1的时钟约束能自动继承。
- 跑完PR2后,一定要做bit验证。我见过有人只看了时序报告就以为没问题,结果下载到板子上,重构区根本不工作。后来发现是部分比特流的地址配错了。
我的个人习惯:每次跑完PR2,我都会用write_bitstream -partial_file生成部分比特流,然后用pr_verify工具检查静态区是否被意外修改。这一步虽然简单,但能省下不少调试时间。
好了,关于实现流程,今天就聊到这儿。PR1和PR2看似简单,但细节很多。你只要把静态区锁死、重构区做增量、结果分析到位,这步基本不会出大问题。
公众号:蓝海资料掘金营,微信deep3321