自学时序约束三个月,教程里的create_clock、set_input_delay都照着写了,综合实现后还是报setup违例。我用的高云GW2A,主频跑到100MHz就过不去,报告里slack负得不多但就是收敛不了。到底是我约束写错了,还是代码本身有问题?新手排查时序违例应该先看哪几个报告、按什么顺序下手?
2026年FPGA时序约束入门,为什么照着教程写create_clock还是setup违例?
3 个回答

先别急着改约束,新手最该做的是确认「你写的时钟」和「工具实际用的时钟」是不是一回事。高云这类器件,如果你用了PLL或者外部晶振经引脚进来,只写create_clock往往不够,还要用create_generated_clock把PLL输出描述清楚,否则工具会按默认关系去分析,路径起点终点都可能是错的。
排查顺序我一般建议:先看时序报告的summary,确认违例集中在哪几个时钟域;再打开具体路径,看起点是输入端口、寄存器还是PLL输出,终点同理。slack负得不多,很可能是某一小段组合逻辑级数偏多,或者跨时钟域没做同步被当成真实路径分析了。
还有个高频坑是虚假路径和异步时钟组没声明,工具把本不该管的路径也算进来。你可以先确认约束覆盖了所有时钟域,再回头怀疑代码。你现在的设计里有没有PLL,输入输出延时是按外部器件手册算的还是拍脑袋填的?

先问一句:你的设计里有没有PLL或者外部晶振进来的时钟?如果有,只写create_clock基本是不够的,PLL输出得用create_generated_clock描述出来,不然工具压根不知道那根线的频率和相位关系。我见过太多新手卡在这一步,代码和约束都没错,就是少了一条生成时钟。
排查顺序建议这样走:先看时序summary,确认违例集中在哪个时钟域;再打开具体路径看起点是端口、寄存器还是PLL输出。slack负得不多,往往是某段组合逻辑级数偏多,或者跨时钟域路径没声明异步被当成真路径分析了。100MHz在高云GW2A上不算高,大概率不是器件跑不动,而是约束没覆盖全。你现在用的是内部PLL还是外部晶振直接进?

个人感觉新手最容易踩的坑,是把「约束写对」和「约束写全」混为一谈。create_clock只是告诉工具主时钟的周期,但工具还需要知道:哪些路径是假的、哪些时钟域之间是异步的、输入输出相对外部器件是什么关系。这些没声明,工具就会老老实实去分析本不该管的路径,报出来的违例看着吓人,其实很多是无效路径。
举个常见的例子:你的设计里有一个100MHz主时钟和一个按键消抖用的慢时钟,两者本来异步,如果没写set_clock_groups -asynchronous,工具会尝试在两个域之间做时序分析,slack当然负。这种情况改代码没用,加一条约束就干净了。
再说代码本身的问题。slack负得不多但收敛不了,通常意味着路径延迟差一点点。这时候要看report_timing里的路径详情:如果logic level(逻辑级数)到了七八级甚至更多,那是组合逻辑太深,得插寄存器打拍;如果逻辑级数不多但布线延迟占比很大,可能是布局拥塞或者引脚分配不合理。前者改RTL,后者调约束和位置约束。
顺序上我建议:summary看全局→report_timing看单条路径→区分逻辑延迟和布线延迟→再决定动代码还是动约束。别一上来就改代码,先确认工具分析的是不是你真正关心的路径。另外高云的时序报告界面和Vivado不太一样,多看几遍路径的起点终点描述,确认它分析的对象对不对。你现在的违例路径起点是输入端口还是内部寄存器?这个信息能帮你快速定位方向。
发表回答
登录后可在本页底部提交回答