sdc时钟约束与综合经验总结
这次的SoC做了多时钟域处理,因此也比之前的约束起来会更复杂一些,把目前的一些小经验给总结一下。
首先描述一下这次的时钟域处理情况,对AXI总线上做了400MHz的时钟约束,AHB是二分频到200MHz,APB再二分频到100MHz,这是三路同步时钟,400MHz的时钟由PLL直接产生给到内部,200MHz和100MHz时钟是依次派生的时钟关系。此外还有一路单独的JTAG时钟,与上述时钟为异步关系。
因此这里涉及到的两个问题就是:1. 怎么约束派生时钟的关系,2. 怎么定义时钟间的异步关系。
先解决第一个问题,都知道派生时钟用create_generated_clock
命令去做,但这里的问题就是怎么和RTL代码结合起来,将时钟约束到哪个pin上。一个新手的常见误区是会去写一个计数器分频的模块,产生好分频后的时钟后,直接将时钟约束到模块的port上。我最早也是这么做的,然后就被后端怼了,这里的问题就是往往工具会在模块内部连接到port的路径上加buffer,因此内部产生的分频时钟传播到port上之后实际上就带有了buffer引入的延迟,导致产生相位差,破坏同步时钟的关系。合理的设置方法应该是在RTL中直接例化DFF,然后将产生的分频信号约束到DFF的Q端上,同理在FPGA上,如果不使用IP的话,那么就直接调用Vivado的DFF原语,然后进行约束。
第二个问题,异步关系怎么处理?这个情境下前三个时钟都是同步时钟,最后一个时钟和前三个时钟都是异步关系,如果只有两个异步时钟的话,相互设置set_flase_path
就完事了,但现在这样就比较麻烦,所以最好的办法是直接使用set_clock_groups -asynchronous
的方式来完成,简单明了。有个debug的小点是,为了便捷,这边用变量的方式声明的clock name,但是到命令里面去调用的时候发现类似于set_clock_groups -asynchronous -group [get_clocks {$clk1_name $clk2_name $clk3_name}] -group [get_clocks $clk4_name]
这种写法是不识别的,最后发现还是得老老实实声明一个list变量来处理。
最后直接放一下对应的RTL代码和sdc:
assign clk_400 = clk;
wire clk_200_inv;
wire clk_100_inv;
// clock divide register 0
assign clk_200_inv = ~clk_200;
DFCNQD0BWP7D5T20P96CPD u_div_reg0(.D(clk_200_inv), .CP(clk), .CDN(rst_n), .Q(clk_200));
// clock divide register 1
assign clk_100_inv = ~clk_100;
DFCNQD0BWP7D5T20P96CPD u_div_reg1(.D(clk_100_inv), .CP(clk_200), .CDN(rst_n), .Q(clk_100));
# Definition of Clock
set Tclk1 2.5 ; #400MHz
set Tclk2 5 ; #200MHz // generated clock
set Tclk3 10 ; #100MHz // generated clock
set Tclk4 1000 ; #1MHz
set clk1_name "clk_400"
set clk2_name "clk_200"
set clk3_name "clk_100"
set clk4_name "clk_jtag"
set unc_perc 0.05
create_clock -name $clk1_name -period $Tclk1 [get_ports clk]
set_clock_uncertainty -setup [expr $Tclk1 * $unc_perc] [get_clocks $clk1_name]
create_generated_clock -name $clk2_name -source [get_pins u_clk_rst_gen/u_div_reg0/CP] -divide_by 2 [get_pins u_clk_rst_gen/u_div_reg0/Q] -master_clock $clk1_name
create_generated_clock -name $clk3_name -source [get_pins u_clk_rst_gen/u_div_reg1/CP] -divide_by 2 [get_pins u_clk_rst_gen/u_div_reg1/Q] -master_clock $clk2_name
create_clock -name $clk4_name -period $Tclk4 [get_ports jtag_tck]
set_clock_uncertainty -setup [expr $Tclk4 * $unc_perc] [get_clocks $clk4_name]
set_clock_transition 0.4 [all_clocks]
set clk_list1 [list $clk1_name $clk2_name $clk3_name]
set clk_list2 [list $clk4_name]
set_clock_groups -asynchronous -group [get_clocks $clk_list1] -group [get_clocks $clk_list2]
当然这里也可以直接使用set_clock_groups -asynchronous -group [get_clocks "$clk1_name $clk2_name $clk3_name"] -group [get_clocks $clk4_name]
的方式。
后续发现会报告一个hold time的违例:
Point Incr Path
--------------------------------------------------------------------------
clock clk_200 (rise edge) 0.00 0.00
u_clk_rst_gen/u_div_reg0/Q (DFCNQD0BWP7D5T20P96CPD) 0.00 # 0.00 r
u_clk_rst_gen/I_0/ZN (INVRTND0BWP7D5T20P96CPD) 0.07 # 0.07 f
u_clk_rst_gen/u_div_reg0/D (DFCNQD0BWP7D5T20P96CPD) 0.00 0.07 f
data arrival time 0.07
clock clk_400 (rise edge) 0.00 0.00
clock network delay (ideal) 0.00 0.00
u_clk_rst_gen/u_div_reg0/CP (DFCNQD0BWP7D5T20P96CPD)
0.00 0.00 r
library hold time 0.12 0.12
data required time 0.12
--------------------------------------------------------------------------
data required time 0.12
data arrival time -0.07
--------------------------------------------------------------------------
slack (VIOLATED) -0.05
从这里的时序计算来看,从u_div_reg0的Q端传播到u_div_reg0的D端,这里总共只需要0.07ns,但对于D触发起来说,hold time需要0.12ns,因此这里无法满足hold的时序要求,造成了违例,解决方案其实也很简单,就是在Q到D之间查入buffer就行,这样手工fix一下:
// clock divide register 0
assign clk_200_inv = ~clk_200;
// insert buff to fix hold timing violation
wire clk_200_inv_bf0;
wire clk_200_inv_bf1;
wire clk_200_inv_bf2;
BUFFD0BWP7D5T20P96CPD u_buff_0(.I(clk_200_inv), .Z(clk_200_inv_bf0));
BUFFD0BWP7D5T20P96CPD u_buff_1(.I(clk_200_inv_bf0), .Z(clk_200_inv_bf1));
BUFFD0BWP7D5T20P96CPD u_buff_2(.I(clk_200_inv_bf1), .Z(clk_200_inv_bf2));
DFCNQD0BWP7D5T20P96CPD u_div_reg0(.D(clk_200_inv_bf2), .CP(clk), .CDN(rst_n), .Q(clk_200));
就修复好了(当然这里其实也可以后端插buffer修复)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2022-12-31 2022年度总结
2020-12-31 2020年度总结