言归正传,在P&R整个流程中,有两个对于design的PPA(Power Performance Area)起决定性的步骤:Floorplan和CTS。虽然如此,在实际项目中,很多人可能工作数年也没有机会做一次比较全面的CTS。一方面原因在于,P&R工具尤其是次世代的ICC2和Innovus对于相对简单的clock tree都能够很好的处理;另一方面的原因是,对于大多数无法接触top的工程师来说,block内部的clock结构大概率是比较简单的。因此就导致一些同学在做过几个项目后面对复杂的clock仍然无从下手。
针对这个现象,我希望根据自己的经验把CTS的基本思路的做法介绍给大家。如果大家以后遇到复杂的clock时发现这里的内容对实际工作多有裨益,本人也将不胜荣幸。
Clock Tree Synthesis,顾名思义,就是对design的时钟树进行综合。主要的目的是让每个clock都能够在尽量短的时间内传达到它们驱动的所有DFF(寄存器)。对于CTS,我们有三个指标希望能够尽量做到更好:
每个clock到达其所驱动的sink(DFF)的latency都尽量短;
每个clock之内,以及有时序关系的clock之间的skew尽量小;
每个clock的common path尽量长。
对于大多数P&R工具,CTS的flow可以总结为以下形式:
CTS策略
这是CTS的第一步,虽然在流程中只是简单的几个字,但是针对复杂的clock,这里可能需要花费大量的时间分析clock的结构,找出潜在可能出现的问题和瓶颈,并针对我们上述提出的三个指标,决定如何去综合时钟树。这一步如此重要却又非常容易被忽略,以至于有些同学在遇到复杂的clock出现问题时无从分析,遑论如何改进。下面我将通过一个例子来说明,CTS策略到底是什么。
其中function clock从两个PLL和两个IO(FCLK1, FCLK2)进来,test clock从两个IO(TCLK1, TCLK2)进来。从图中可以看出,在分频电路之后,进入A和D的clock不与其他模块共享。
与此同时,我们假设芯片的floorplan可以表示为如下形式(注意clock source和模块位置与clock结构的对应关系):
如果模块之间的时序关系可以简述为如下形式:
Block B <-> Macro1
Block C <-> Macro2
Macro1 <-> Macro2
Block A/D为独立模块
考虑到上述时钟结构,floorplan和时序关系,我们该如何决定CTS策略呢?
在考虑策略之前,我们先回忆一下CTS的三个目标:latency尽量短;skew尽量小;common path尽量长。
先从最简单的开始:鉴于Block A和D为独立模块,它们不与其他模块产生时序关系,同时从clock结构图可以看到进入A和D的clock在选择器之后没有与其他模块有交集,因此,我们只需要把A和D的clock尽量做短即可。
我们也可以把一切交给EDA工具,但是这样的做的结果不能保证得到我们想要的结果。最好的办法是把进入到A、D两个Block的clock选择器以及其他clock分频逻辑固定在这两个block附近(下图中紫色区域),这样不仅能够使clock line尽量以最短的距离到达Block,同时也能够尽可能地增加common path的长度。
接下来考虑Block B/C和Macro/Macro2。鉴于他们之间的时序关系,我们希望它们之间的skew尽量短,同时common path要尽量长。在floorplan上我们可以看到它们被摆放成对称 的形式,因此,最简单直接的办法就是将所有与这几个block相关的逻辑都固定在两个Macro上方的正中间(假设其clock port在黄色标识的部分),如下图红色方框所示:
到目前位置我们确定了需要preplace的逻辑以及其放置区域,接下来我们需要考虑如何实现我们的设想。其中首要的任务就是定义CTS的sdc。
对于CTS工具来说,要进行时钟树综合,有一个必备的前提条件就是clock的定义,而后端设计者是可以根据自己的策略自己调整clock的定义的。让我们再回忆以下clock的结构图:
1.直接从所有的clock源头定义,此方法基本可以从func/test的sdc中直接复制clock的定义:
create_clock -period 833.33 -name PLL1_CLK [get_pins PLL1/CLK] ;# 1.2GHz
create_clock -period 833.33 -name PLL2_CLK [get_pins PLL2/CLK] ;# 1.2GHz
create_clock -period 2000.00 -name FCLK1 [get_ports FCLK1] ;# 500MHz
create_clock -period 2000.00 -name FCLK2 [get_ports FCLK2] ;# 500MHz
create_clock -period 20000.00 -name TCLK1 [get_ports TCLK1] ;# 50MHz
create_clock -period 20000.00 -name TCLK2 [get_ports TCLK2] ;# 50MHz
第2段:clock sources ->各个选择器
create_clock -period 833.33 -name PLL1_CLK [get_pins PLL1/CLK] ;# 1.2GHz
create_clock -period 833.33 -name PLL2_CLK [get_pins PLL2/CLK] ;# 1.2GHz
create_clock -period 2000 -name FCLK1 [get_ports FCLK1] ;# 500MHz
create_clock -period 2000 -name FCLK2 [get_ports FCLK2] ;# 500MHz
create_clock -period 20000.00 -name TCLK1 [get_ports TCLK1] ;# 50MHz
create_clock -period 20000.00 -name TCLK2 [get_ports TCLK2] ;# 50MHz
至于如何使工具在选择器停止,我们将在接下来的文章中继续讨论。同时我们将会把上述思路和策略通过真实的脚本和命令反映出来,希望大家继续关注。
另外,在本文中涉及的design通过上述几幅图可以了解到相对详细的时钟结构,但是在实际设计中我们如何事先得知clock的结构和时序关系呢?一般来说对于流程完善的大公司,绘制详尽的时钟结构是前端设计者的工作之一。但是在实际项目中,面对的大部分design的clock结构图可能是十分简单甚至完全没有的情况,这时就需要后端工程师多一些耐心去分析design,并积极与前端和constraint设计者沟通,从而实现对design的时钟结构快速把握。
至此,CTS的策略基本构建完成。在接下来的文章中,我们将继续详细讨论这些策略的实现方法以及如何查看和debug clock的结果。