跳變點是所有重要時序分析工具中的一個重要概念。跳變點被時序分析工具用來計算設計節點上的時延與過渡值。跳變點的有些不同含義可能會被時序分析工程師忽略。而這在SOC設計后期,也就是要對時序簽字時可能會導致問題。后端設計工程師要知道跳變點的概念及其含義,這個非常重要。這也正是本文目的之所在。 1.跳變點定義: 跳變點可定義為邏輯高電平的百分比,作為測量壓擺率和時延值的參照。圖1(a)演示的是壓擺率跳變點。 圖1(a) 圖1(b)演示的是時延跳變點。 圖1(b) 跳變點用來描述標準單元或硬模塊(IP)等的引腳過渡值特征,同時檢查SoC時序。通常跳變點的值在時序模型(自由格式)中提及,時序分析工具在計算時延和壓擺率時會使用這些值。 2.如何固定跳變點: 在描述標準單元或硬模塊(IP)特征時,跳變點對于特定的技術節點是固定的。將跳變點固定的目的是確保測量的時延/壓擺率接近實際(Spice)的波形。如圖2(a)所示,當跳變點位于切換波形的線性區(20%-80%)時,壓擺率值比位于非線性區(10%-90%)時要令人樂觀。 圖2(a) 通常情況下,在過渡跳變點位于線性區時,時序工具計算的單元時延更接近Spice結果。 同時,晶體管的電壓閾值(Vt)特性對于決定跳變點發揮了重要作用,因為輸出波形在輸入電壓超過晶體管的Vt值后會發生線性化。 時延閾值在輸入和輸出波形的線性區是固定的。只要時延跳變點位于波形的線性部分,那么時延跳變點是20-80還是50-50都沒有關系。 3.自由格式提及的跳變點句法 下面給出的是典型時序模型(.liberty文件)的快照,以指示正在使用的跳變點 input_threshold_pct_rise:50; input_threshold_pct_fall:50; output_threshold_pct_rise:50; output_threshold_pct_fall:50; slew_lower_threshold_pct_rise:20; slew_lower_threshold_pct_fall:20; slew_upper_threshold_pct_rise:80; slew_upper_threshold_pct_fall:80; 4.時序工具如何處理跳變點來計算時延 a)當跳變點對于界面都相同時: 圖3(a)描述了驅動程序和負載具有相同時延閾值時的情況。 在給出的示例中,兩者都為50%。 圖3(a) 此時,,考慮壓擺率降級(由網絡引起)后,時序工具計算的驅動程序達到其50%的邏輯高值和負載達到其50%的邏輯高值時兩者之間的時間差。 類似的解釋對于特定單元的輸入和輸出產生的下降信號和時延同樣適用。壓擺率值根據.lib中提及的變量進行計算。 b)當跳變點對于一個界面而不同時 (i)20%比50%: 圖4(a)描述了驅動程序時延跳變點為20%而對負載單元跳變點為50%的情況。 在這種情況下,與負載信號相比,驅動程序的信號會快速達到其時延跳變點值。因此此類界面的網絡時延會大于驅動程序也達到50%情形下的時延[圖3(a)]。 圖4(a) 時序工具可通過線性或非線性擴展計算網絡上出現的額外時延。 (ii)50%比20% 圖4(b)描述了驅動程序跳變點為50%而負載單元的跳變點為20%的情形。 在這種情況下,與驅動信號相比,負載的信號會更早達到其時延跳變點值。這種情況通過時序工具借助擴展(線性或非線性)來進行處理。 圖4(b) 這里需要注意的是:在這種情況下,擴展會引起“負時延”。 應注意:盡管現實世界不能在時域中后向穿越,但是時序工具需要將這種時延考慮在內,這樣,從開始點(在本例中為驅動單元的輸入引腳)到終端點(在本例中為負載單元的輸出引腳)的整體路徑時延接近現實世界時延(Spice)。 5.與跳變點相關的其他問題: (i)SDF中的負時延:在通過時序工具完成擴展后產生的負時延將以標準時延格式(SDF)進行復制,用于門級模擬。不希望發生這種情況,因為門級模擬器無法處理負時延。 它們要么標志錯誤消息要么表示此類情況的零時延。作為一種變通方法,可編寫一個腳本(附錄A),根據所計算的負時延,增加(或減少)負載單元(或驅動單元)時延。 (ii)端口和IO單元之間的附加時延: 通常時序工具報告端口到I/O單元的時延。在硅片上,該網絡作為接合線出現在芯片外部。因此,對于該網絡物理信息不能進行量化。 時序工具提供此類網絡的時延報告。原因包括: a)由于沒有時序模型可用于端口,因此時序工具采用用戶定義的或默認跳變點和電壓電平計算時延。 b)由于假定跳變點和端口w.r.tI/O單元跳變點的電壓電平值之間有差額。圖5(a)和圖5(b)描述了此類情況。 圖5(a) 圖5(b) 要克服這種情況,可執行以下操作: 1)為與I/O單元的端口相同的端口定義運行條件。 2)編寫腳本為此類網注釋零時延。 (iii)帶有丟失跳變點的庫 如果我們擁有不包含跳變點閾值或電壓電平值的時序模型,那么來自/到此類模型的界面的時延可能不正確。因為時序工具使用跳變點和電壓電平的默認值,分析這些路徑。作為一種變通方法,用戶應在向負責庫的人員進行咨詢后,再定義跳變點。 6.如果不通過時序工具進行擴展會怎樣:激烈。這可能會導致芯片故障,因為使用時序工具計算的時延不接近Spice值。如果可以為跳變點不同的界面進行Spice分析,應當是一種很好的操作方法。 如果對SOC中所使用的所有模型(硬模塊)使用同樣的跳變點,應當是一種很好的操作方法。 這完全可以消除因閾值不同所產生的問題。 7.Spice合作關系: 對多閾值路徑進行Spice分析,是增強信心并解決時序問題的好方法。 盡管在Spice世界,閾值實際并不存在。在時序模型中使用它們是為了簡化時序分析工具。 時序工具提供一個選項,復制出特定時序路徑的spice網列表。除了spice網列表外,還復制出包含輸入矢量的激勵文件。復制出的spice網列表可能不包含針對時序路徑中標準單元和/或硬模塊的spice網列表。 Spice模擬工具可讀取復制的spice網列表和標準單元/硬模塊的spice網列表,然后提供激勵文件來模擬重要路徑。 需要通過分析生成的波形,查看路徑是否滿足時序要求。測量SPICE中此類路徑的時延和過渡值時需要謹記:要采用與時序模型中所提及的跳變點相同的跳變點。 8.擴展類型: 大多數行業標準工具使用線性或非線性擴展作為其運作方式 a)線性擴展:在該方法中,工具假定不同閾值的線性斜坡。 該方法使用相似三角形的概念擴展驅動程序到負載單元的時延。 b)非線性擴展:此時,工具使用電流源模型來定義坡道。這需要通過復雜的數學方程來計算時延。 總之,本文以跳變點定義開始,然后闡釋如何固定用于特定技術庫的跳變點。然后本文論述了時序工具如何解釋跳變點,以及當驅動程序與負載單元跳變點不同時可能會引發的問題。 附錄 A:在SDF中考慮負互連時延的腳本 #!/usr/local/bin/perl print "\n***************RUNNING PERL SCRIPT negativedelinsdf_assarray.pl****\n\n"; if (!exists $ARGV[0] || !exists $ARGV) { print "USAGE:: negativedelinsdf_assarray.pl <old SDF> <new(corrected) SDF> \n\n"; exit; } open(FILEA,$ARGV[0]) || die ("ERROR:: SDF file $ARGV[0] cannot be opened for reading\n"); open(file1, ">$ARGV") || die ("ERROR:: file $ARGV cannot be opened for writing\n"); while(<FILEA>) { $line = $_; $line1 = $line; $line =" s/^\s+//g ; if ( $line =" "INTERCONNECT.*-[0-9]") ######## FINDING NEGATIVE INTERCONNECT ###### { @array1 = split(/\s+/,$line); $count = @array1; if ($count == 4){ ## To accont for the fact that only one INTERCONNECT triplet is present $array1[$count]= $array1[$count-1]; $count = $count + 1; } for($i=0;$i<= $count -3;$i++) {print file1 "$array1[$i] ";} $value2 = $array1[$count -1]; $value1 = $array1[$count -2]; $instance_name = $array1; @instance = split(/\//,$instance_name); $count2 = @instance; $pin_name = $instance[$count2 -1];####### GETTING LOAD INSTANCE PIN NAME ############### $instance_name=" s/\/[^(.*\/)][A-Z0-9a-z_\[0-9\]+]*$//g;####### GETTING LOAD INSTANCE NAME ############### if( $value1 =" "::") {$value1 =" s/[()]//g; @correct_value1 = split(/::/,$value1); $load_correct_value1_0{$instance_name} = $correct_value1[0]; $load_correct_value1_1{$instance_name} = $correct_value1; if($correct_value1[0] <= -0.0)#####NEGATIVE CHANGED TO ZERO DELAY###### {$correct_value1[0] = "0.000";} if($correct_value1 <= -0.0) {$correct_value1 = "0.000";} print file1 "($correct_value1[0]::$correct_value1)"; } elsif( $value1 =" ":[^:]") {$value1 =" s/[()]//g; @correct_value1 = split(/:/,$value1); $load_correct_value1_0{$instance_name} = $correct_value1[0]; $load_correct_value1_1{$instance_name} = $correct_value1; $load_correct_value1_2{$instance_name} = $correct_value1; if($correct_value1[0] <= -0.0)#####NEGATIVE CHANGED TO ZERO DELAY###### {$correct_value1[0] = "0.000";} if($correct_value1 <= -0.0) {$correct_value1 = "0.000";} if($correct_value1 <= -0.0) {$correct_value1 = "0.000";} print file1 "($correct_value1[0]:$correct_value1:$correct_value1)"; } else{print file1 "$value1 ";} if( $value2 =" "::") {$value2 =" s/[()]//g; @correct_value2 = split(/::/,$value2); $load_correct_value2_0{$instance_name} = $correct_value2[0]; ########NEGATIVE CHANGED TO ZERO DELAY##### $load_correct_value2_1{$instance_name} = $correct_value2; if($correct_value2[0] <= -0.0) {$correct_value2[0] = "0.000";} if($correct_value2 <= -0.0) {$correct_value2 = "0.000";} if ($correct_value1[0] == $correct_value2[0] && $correct_value1 == $correct_value2 ) { # Print these values only if they are differnet from the already printed values for the INTERCONNECT print file1 ")\n"; } else { print file1 " ($correct_value2[0]::$correct_value2))\n"; } } elsif( $value2 =" ":[^:]") {$value2 =" s/[()]//g; @correct_value2 = split(/:/,$value2); $load_correct_value2_0{$instance_name} = $correct_value2[0]; ########NEGATIVE CHANGED TO ZERO DELAY##### $load_correct_value2_1{$instance_name} = $correct_value2; $load_correct_value2_2{$instance_name} = $correct_value2; if($correct_value2[0] <= -0.0) {$correct_value2[0] = "0.000";} if($correct_value2 <= -0.0) {$correct_value2 = "0.000";} if($correct_value2 <= -0.0) {$correct_value2 = "0.000";} if ($correct_value1[0] == $correct_value2[0] && $correct_value1 == $correct_value2 && $correct_value1 == $correct_value2) { # Print these values only if they are differnet from the already printed values for the INTERCONNECT print file1 ")\n"; } else { print file1 " ($correct_value2[0]:$correct_value2:$correct_value2))\n"; } } $load_instance{$instance_name} = $instance_name;##SAVE ALL LOAD INSTACES FOR NEG DELAY IN THE ASSOCIATIVE ARRAY## $load_pin{$instance_name.$pin_name} = $pin_name;##SAVE ALL LOAD PINS FOR NEG DELAY IN THE ASSOCIATIVE ARRAY## } elsif ($line =" "CELL" ) { print file1 "$line"; $find = 0; } elsif($line =" "INSTANCE ")##CHECKING CORRESPONDING INSTANCES AND FIND FLAG = 1 IF FOUND###### { print file1 "$line1"; @array2 = split(/\s+/,$line); $instance_name2 = $array2; ##GETTING THE LOAD INSTANCE NAME FOUND HERE########## $instance_name2 =" s/[()]//g; $instance_definition{$instance_name2} = "$instance_name2" ; if(exists $load_instance{$instance_name2})####COMPARE INSTANCE NAME WITH THAT SAVED IN ASSO ARRAY##### { if($load_instance{$instance_name2} eq "$instance_name2") {$find = 1;} } } elsif($line =" "IOPATH" && $find == 1)##AFTER INSTANCES ARE FOUND CHECKING FOR CORRSPONDING PINS###### { @array4 = split(/\s+/,$line); if ($array4[0] =" "COND" ) { ## Take care of COND statements $pin_name2 = $array4; } else { $pin_name2 = $array4; } if($line =" "IOPATH" && exists $load_pin{$instance_name2.$pin_name2} ) { if ( $load_pin{$instance_name2.$pin_name2} eq "$pin_name2" ) { @array3 = split(/\s+/,$line); $count3 = @array3 ; @value_IOPATH = ""; # This is to initialize the array to blank if ($array3[0] =" "COND" ) { for ($j=5; $j<=$count3 ; $j++) { $value_IOPATH[$j-5] = $array3[$j]; $value_IOPATH_width = @value_IOPATH; if ($count3 == 6) { #### In case there is only one triplet for COND.*IOPATH $value_IOPATH = $value_IOPATH[0]; } #$value1_IOPATH = $array3; #$value2_IOPATH = $array3; } $constant_fields = 4; } elsif ($array3[0] =" "IOPATH") { for ($k=3; $k<=$count3 ; $k++) { $value_IOPATH[$k-3] = $array3[$k]; $value_IOPATH_width = @value_IOPATH; if ($count3 == 4) { #### In case there is only one triplet for IOPATH $value_IOPATH = $value_IOPATH[0]; } #$value1_IOPATH = $array3; #$value2_IOPATH = $array3; } $constant_fields = 2; } for($i=0;$i<= $constant_fields;$i++) {print file1 "$array3[$i] ";} if( $value_IOPATH[0] =" "::")####CORRECT OR ACCOMODATING THE NEG DELAY VALUES HERE ######### {$value_IOPATH[0] =" s/[()]//g; @correct_value1_IOPATH = split(/::/,$value_IOPATH[0]); if(exists $load_correct_value1_0{$instance_name2}) { if($load_correct_value1_0{$instance_name2} < 0.0) { # So that only negative delay value triplet is changed in the IOPATH syntax if ( abs($load_correct_value1_0{$instance_name2}) < $correct_value1_IOPATH[0] ) { ###the absolute value to negative delay is larger than the timing of load arc. ###### $correct_value1_IOPATH[0] = $correct_value1_IOPATH[0] + $load_correct_value1_0{$instance_name2}; } else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";} } } if(exists $load_correct_value1_1{$instance_name2}) {if( $load_correct_value1_1{$instance_name2} < 0.0) { if ( abs($load_correct_value1_1{$instance_name2}) < $correct_value1_IOPATH ) { $correct_value1_IOPATH = $correct_value1_IOPATH + $load_correct_value1_1{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} print file1 "($correct_value1_IOPATH[0]::$correct_value1_IOPATH)"; } elsif( $value_IOPATH[0] =" ":[^:]")####CORRECT OR ACCOMODATING THE NEG DELAY VALUES HERE ######### {$value_IOPATH[0] =" s/[()]//g; @correct_value1_IOPATH = split(/:/,$value_IOPATH[0]); if(exists $load_correct_value1_0{$instance_name2}) { if($load_correct_value1_0{$instance_name2} < 0.0) { # So that only negative delay value triplet is changed in the IOPATH syntax if ( abs($load_correct_value1_0{$instance_name2}) < $correct_value1_IOPATH[0] ) { $correct_value1_IOPATH[0] = $correct_value1_IOPATH[0] + $load_correct_value1_0{$instance_name2}; } else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";} } } if(exists $load_correct_value1_1{$instance_name2}) { if($load_correct_value1_1{$instance_name2} < 0.0) { # So that only negative delay value triplet is changed in the IOPATH syntax if ( abs($load_correct_value1_1{$instance_name2}) < $correct_value1_IOPATH ) { $correct_value1_IOPATH = $correct_value1_IOPATH + $load_correct_value1_1{$instance_name2}; } else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";} } } if(exists $load_correct_value1_2{$instance_name2}) { if($load_correct_value1_2{$instance_name2} < 0.0) { # So that only negative delay value triplet is changed in the IOPATH syntax if ( abs($load_correct_value1_2{$instance_name2}) < $correct_value1_IOPATH ) { $correct_value1_IOPATH = $correct_value1_IOPATH + $load_correct_value1_2{$instance_name2}; } else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";} } } print file1 "($correct_value1_IOPATH[0]:$correct_value1_IOPATH:$correct_value1_IOPATH)"; } else{print file1 "$value_IOPATH[0] ";} # This is to print empty brackets in case rise and fall triplets are empty if( $value_IOPATH =" "::") {$value_IOPATH =" s/[()]//g; @correct_value2_IOPATH = split(/::/,$value_IOPATH); if(exists $load_correct_value2_0{$instance_name2}) { if( $load_correct_value2_0{$instance_name2} < 0.0) {if ( abs($load_correct_value2_0{$instance_name2}) < $correct_value2_IOPATH[0] ) { $correct_value2_IOPATH[0] = $correct_value2_IOPATH[0] + $load_correct_value2_0{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} if(exists $load_correct_value2_1{$instance_name2}) { if( $load_correct_value2_1{$instance_name2} < 0.0) {if ( abs($load_correct_value2_1{$instance_name2}) < $correct_value2_IOPATH ) { $correct_value2_IOPATH = $correct_value2_IOPATH + $load_correct_value2_1{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} if ($correct_value1_IOPATH[0] == $correct_value2_IOPATH[0] && $correct_value1_IOPATH == $correct_value2_IOPATH) { # Print these values only if they are differnet from the already printed values for the IOPATH } else { print file1 " ($correct_value2_IOPATH[0]::$correct_value2_IOPATH)"; } } elsif( $value_IOPATH =" ":[^:]") {$value_IOPATH =" s/[()]//g; @correct_value2_IOPATH = split(/:/,$value_IOPATH); if(exists $load_correct_value2_0{$instance_name2}) { if( $load_correct_value2_0{$instance_name2} < 0.0) { if ( abs($load_correct_value2_0{$instance_name2}) < $correct_value2_IOPATH[0] ) { $correct_value2_IOPATH[0] = $correct_value2_IOPATH[0] + $load_correct_value2_0{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} if(exists $load_correct_value2_1{$instance_name2}) { if( $load_correct_value2_1{$instance_name2} < 0.0) { if ( abs($load_correct_value2_1{$instance_name2}) < $correct_value2_IOPATH ) { $correct_value2_IOPATH = $correct_value2_IOPATH + $load_correct_value2_1{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} if(exists $load_correct_value2_2{$instance_name2}) { if( $load_correct_value2_2{$instance_name2} < 0.0) { if ( abs($load_correct_value2_2{$instance_name2}) < $correct_value2_IOPATH ) { $correct_value2_IOPATH = $correct_value2_IOPATH + $load_correct_value2_2{$instance_name2};} else { print "IOPATH delay for $load_pin{$instance_name2.$pin_name2} of $instance_name2 is smaller than the INTERCONNECT delay \n";}}} if ($correct_value1_IOPATH[0] == $correct_value2_IOPATH[0] && $correct_value1_IOPATH == $correct_value2_IOPATH && $correct_value1_IOPATH == $correct_value2_IOPATH) { # Print these values only if they are differnet from the already printed values for the IOPATH } else { print file1 " ($correct_value2_IOPATH[0]:$correct_value2_IOPATH:$correct_value2_IOPATH)"; } } else{print file1 "$value_IOPATH ";} # This is to print empty brackets in case rise and fall triplets are empty for ($m=2;$m<=$value_IOPATH_width;$m++) { if (exists $value_IOPATH[$m]) { $value_IOPATH[$m] =" s/\)+/\)/; # To account for the brackets print file1 " $value_IOPATH[$m]"; } } if ($array4[0] =" "COND" ) { ## Extra bracket for COND statements print file1 "))\n"; } else {print file1 ")\n";} } } else {print file1 "$line1";}#######DUMPING OUT OF SDF IN FILE1###################### } else {print file1 "$line1";} } close(file1); close(fileA); open(FILEA,$ARGV[0]) || die ("ERROR:: SDF file $ARGV[0] cannot be opened for reading\n"); print"\n\n#############REPORTING INSTANCES WHOSE DEFINITION IS NOT THERE IN THE SDF#############\n"; while(<FILEA>) { $line2 = $_; $line2 =" s/^\s+//g ; if ( $line2 =" "INTERCONNECT.*-[0-9]") { @array4 = split(/\s+/,$line2); $instance_name3 = $array4; $instance_name3=" s/\/[^(.*\/)][A-Z0-9a-z_\[0-9\]+]*$//g; if(exists $instance_definition{$instance_name3}) {} else {print "$line2";} } } close(fileA); print "\n"; |