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修复)。

posted @   sasasatori  阅读(267)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-12-31 2022年度总结
2020-12-31 2020年度总结
主题色彩
点击右上角即可分享
微信分享提示