面向模块的综合技术之重定时优化(六)
面向模块的综合技术之重定时优化(六)
文章目录
- 面向模块的综合技术之重定时优化(六)
- 前言
- ✅ 一、Retiming 的基本原理
- ✅ 二、Vivado 中的 Retiming 类型
- ✅ 三、常用 Retiming 属性
- 1. `retiming_forward`
- 2. `retiming_backward`
- ✅ 四、模块级控制(BLOCK_SYNTH.RETIMING)
- ✅ 五、Retiming 的限制与注意事项
- ✅ 六、Retiming 的实际效果
- ✅ 七、总结建议
前言
在 Vivado 中,Retiming(重定时)是一种不改变电路功能、仅移动寄存器位置以优化时序性能的关键综合技术。它的核心思想是:通过在组合逻辑之间前后移动寄存器,来平衡逻辑延迟,从而缩短关键路径延迟、提高时钟频率。
✅ 一、Retiming 的基本原理
Retiming 遵循 Leiserson-Saxe 定理,即:
在不改变输入输出行为的前提下,通过前后移动寄存器,优化逻辑级数分布,从而改善时序。
Vivado 会自动识别逻辑路径中的寄存器,并尝试将其前移或后移,以减少高逻辑级数路径的延迟。
✅ 二、Vivado 中的 Retiming 类型
| 类型 | 控制方式 | 描述 |
|---|---|---|
| 全局 Retiming | 综合选项 -retiming | 对整个设计自动进行寄存器移动,需配合时序约束使用 |
| 局部 Retiming | RTL 属性或 XDC 约束 | 精确控制某寄存器或路径是否参与 retiming,适用于精细优化 |
✅ 三、常用 Retiming 属性
你可以在 RTL 或 XDC 中使用以下属性:
1. retiming_forward
允许寄存器向前移动(朝输出方向),穿越组合逻辑。
(* retiming_forward = 1 *) reg my_reg;
2. retiming_backward
允许寄存器向后移动(朝输入方向),穿越组合逻辑。
(* retiming_backward = 1 *) reg my_reg;
注意:这些属性不受全局 retiming 设置影响,可独立生效。
✅ 四、模块级控制(BLOCK_SYNTH.RETIMING)
你也可以对某个模块开启 retiming:
set_property BLOCK_SYNTH.RETIMING 1 [get_cells inst1/inst2]
这会对该模块内的逻辑进行寄存器重定时,适用于局部优化。
✅ 五、Retiming 的限制与注意事项
- 以下情况不会进行 retiming:
- 寄存器上有
DONT_TOUCH或MARK_DEBUG属性 - 路径上有
false_path或multicycle_path约束 - 用户实例化的寄存器(非推断寄存器)
- 寄存器上有
✅ 六、Retiming 的实际效果
- 减少逻辑级数分布不均带来的时序压力
- 无需修改 RTL 代码,即可提升性能
- 可配合
report_design_analysis -logic_level_distribution分析优化效果
✅ 七、总结建议
Vivado 眼里的一条“路径”并不是一根线,而是 “起点 FF → 组合云 → 终点 FF”
| 场景 | 建议 |
|---|---|
| 时序收敛困难 | 开启全局 retiming(-retiming) |
| 局部路径时序差 | 使用 retiming_forward/backward 精确控制 |
| 模块级优化 | 使用 BLOCK_SYNTH.RETIMING |
| 调试阶段 | 避免对调试信号使用 retiming(加 DONT_TOUCH) |
以下情况下不能进行重定时操作:
- 寄存器时序异常(多循环路径、错误的路径、最大延迟路径),即时序例外不可
| 工具不敢搬的原因 | Vivado 实际行为 | 一句话定位 | 放行/变通办法 |
|---|---|---|---|
| 搬动后“锚点”消失,多周期/假路径/MaxDelay 约束失去参照,时序报告会错。 | 综合日志出现 Retiming blocked: user timing exception;对应寄存器 retiming_forward/retiming_backward 被内部强制 0。 | report_timing_exceptions -retiming_blocked | 把例外粒度从“时钟到时钟”改成“端口到端口”或“寄存器到寄存器”,再允许搬动;例外结束后重新加回。 |
| 搬动后“锚点”消失,多周期/假路径/MaxDelay 约束失去参照,时序报告会错。 | 综合日志出现 Retiming blocked: user timing exception;对应寄存器 retiming_forward/retiming_backward 被内部强制 0。 | report_timing_exceptions -retiming_blocked | 把例外粒度从“时钟到时钟”改成“端口到端口”或“寄存器到寄存器”,再允许搬动;例外结束后重新加回。 |
- 寄存器的类型属性不能改变(DONT_TOUCH, MARK_DEBUG)
| 工具不敢搬的原因 | Vivado 实际行为 | 一句话定位 | 放行/变通办法 | ||
|---|---|---|---|---|---|
| 这些属性=“网表节点必须比特级固定”,任何优化(含 retiming)都跳过,否则调试/层次化会崩。 | opt_design 日志 Retiming blocked: attribute DONT_TOUCH;report_retiming 显示 0 registers moved。 | `get_cells -hier -filter {DONT_TOUCH | MARK_DEBUG}` | 调试阶段保留;最终发布前脚本批量 set_property DONT_TOUCH false 再跑一轮综合。 |
- 要执行后向重定时操作,组合逻辑必须仅驱动寄存器,而不是扇出到其他逻辑。要执行前向重定时操作,门的每个输入必须由具有相同控制集
| 工具不敢搬的原因 | Vivado 实际行为 | 一句话定位 | 放行/变通办法 |
|---|---|---|---|
| 时钟/使能/复位/置位任意一项不同,搬动后 RTL 行为会变(如 CE 无效时多采一次)“不同控制集 = 不同采样规则”搬动寄存器 = 改变采样时刻 → latency/数值都可能变 → 功能不再等价。因此 Vivado 内部规则:跨控制集 → 立即阻断 retiming,除非用户手动统一控制集。。 | 日志 Retiming blocked: incompatible control set;同一条数据链上寄存器无法合并或穿越。 | report_control_sets -verbose | 统一控制集:给无 CE 寄存器包一层 CE=1'b1;或手动复制同控集寄存器供工具搬。 |
原始:
din ──►│LUT1│──►│LUT2│──►(a_reg)──►│LUT3│──►(q_reg)
时序报告:
LUT3 延迟 0.9 ns,关键路径 0.9 ns → WNS = -0.2 ns
目标:把 q_reg 搬过 LUT3,让 LUT3 的延迟不再属于“a→q”路径
搬动后:
din ──►│LUT1│──►│LUT2│──►│LUT3│──►(q_reg_new)──►(a_reg) <- 顺序反了
组合云从 {LUT3} 变成 {},路径延迟 -0.9 ns → WNS 转正
但 a_reg 与 q_reg_new 必须同控制集,否则工具报:
Retiming blocked: incompatible control set
retiming 的“滑块”必须横跨组合云,且滑块两端触发器必须同款控制集;只要 a_reg 与 q_reg 的 CLK/CE/SR 任一不同,滑块就被卡住,整条路径动不了。因此所谓“只搬 q”是不存在的——搬 q 的同时一定要求 a 与 q 同控制级,否则工具直接拒绝。
- 寄存器驱动输出或由输入驱动(除非设计被标记为 out-of-context)。
| 工具不敢搬的原因 | Vivada 实际行为 | 一句话定位 | 放行/变通办法 |
|---|---|---|---|
| 顶层端口是“芯片边界”,搬动会改变 I/O 时序协议(Hold、Setup、skew、对齐)。 非 OOC 模式下,工具看不到外部世界,不敢改边界寄存器。 | 综合日志 Retiming blocked: boundary register;report_retiming 里 BOUNDARY_REGISTER 分类为 0 moved。 | get_cells -filter {IS_BOUNDARY_REGISTER} | 1. 把顶层包成 OOC 模块,set_property IS_OOC true [current_fileset] → 工具把端口当内部节点,可自由搬。2. 不改 OOC 则手动在边界加/减寄存器,再允许内部搬动。 |
- 重定时无法提升反馈循环中的关键路径的示例:当路径的源寄存器和目标寄存器相同时,重定时优化有可能无法改善逻辑层次。反馈环 = 单 FF 闭环,重定时因“寄存器数量守恒”无法滑动;手动插入一级 dummy FF → 变成“双 FF 链”,工具就能搬动,频率提升,代价是 latency +1。
