Building, Viewing, and Debugging Parasitic Graphs During STA and IR Analysis

Building, Viewing, and Debugging Parasitic Graphs During STA and IR Analysis

An inexpensive and elegant way to handle large circuits for timing, power, and IR is to use Python, scientific libraries, and Dask (or some other) distributed computing library. Still may suffer from scalability issues (but that is why we have CPP, Boost Graph Library, and MPI). For a quick debug of parasitics in a Tcl environment (aka Tcl shell), I still use Tcl scripts as it is simpler to code Tcl and interface with an EDA tool.

Debugging parasitics during EM/IR and delayCal/STA analyses is a tedious process. I like debugging parasitics by visualizing the graph of parasitic nets of interest so that I can selectively focus on narrowing down the problem. For example, nodes that are present in one net but not in another net, incompletely represented parasitics, floating nodes, isolated capacitances, etc., are easier to debug by visualizing. At least that is how I prefer to debug such problems.

Here is a simple piece of Tcl code that works well to build full parasitic graphs and then selectively view and debug sub-graphs (D_NET or a group of D_NETs). This small technique has come in handy while debugging EM/IR and STA problems many times. BTW, if you have not looked at how Cadence's Voltus and Tempus handle large designs then you are missing out. You should reach out to my team or me:)

#!/usr/bin/env tclsh
# I make no guarantee that this code works for you. But you are welcome to use it or enhance it. 
# Note that we can optimize this code a lot more but time is the cost!
# Original Date: 2016. 
# Contact: Deen Kotturi ([email protected])
## Read the SPEF and build the parasitic graph
proc createGraph {spef} {
 ## nameMapH is a dictionary of the named indices and the net names
 global nameMapH
 ## graphH is a dictionary of we will use for the graphs of the nets
 global graphH
 set inNameMapFlg 0
 set inDNetFlg 0
 set inConnFlg 0
 set inCapFlg 0
 set inResFlg 0
 set netIdx ""
 set nameMapH [dict create]
 set graphH [dict create]
 if [file exists $spef] {
  ## Get the normalized path to the SPEF file
  set fileNm [file normalize $spef]
  set fId ""
  ## Test if the file is a gzipped file or an ascii file. This is actually a lazy way to test. 
  ## A better way is to check the initial byte sequence in the file
  if [regexp {\.gz$} $fileNm] {
   set fId [open "|zcat $fileNm"]
  } else {
   set fId [open $fileNm "r"]
  }
  ## Process a line at a time
  while { [gets $fId line] >= 0 } {
  ## Check for name mapping (this is just an alias to the net names)
   if [regexp {^\s*\*NAME_MAP\s*$} $line] {
    set inNameMapFlg 1
   }
   if [expr {$inNameMapFlg == 1}] {
    ## Check if we are in PORTS section of the SPEF
    if [regexp {^\s*\*PORTS\s*$} $line] {
     set inNameMapFlg 0
    } elseif [regexp {^\s*(\S+)\s+(\S+)\s*$} $line] {
     ## build the nameMapH LUT
     if [regexp {^\s*(\S+)\s+(\S+)\s*$} $line all idx netNm] {
      dict set nameMapH $idx $netNm
     }
    }
   }
  ## Process D_NET sections and build graph
   if [regexp {^\s*\*D_NET\s+(\S+)\s+(\S+)\s*$} $line all netIdx tcap] {
    set inDNetFlg 1
    dict set graphH $netIdx "TCAP" $tcap
   }
   if [expr {$inDNetFlg == 1}] {
    if [regexp {^\s*\*CONN\s*$} $line] {
     set inConnFlg 1
    }
    if [expr {$inConnFlg == 1}] {
     if [regexp {^\s*\*CAP\s*$} $line] {
      set inConnFlg 0
      set inCapFlg 1
     } else {
      if ![regexp {^\s*\*CONN\s*$} $line] {
       if ![regexp {^\s*$} $line] {
        dict set graphH $netIdx "CONN" $line 1
       }
      }
     }
    } elseif [expr {$inCapFlg == 1}] {
     if [regexp {^\s*\*RES\s*$} $line] {
      set inCapFlg 0
      set inResFlg 1
     } else {
      if [regexp {^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$} $line all capElemNum leftNode rightNode ccVal] {
       set cc [list]
       lappend cc $ccVal $capElemNum
       dict set graphH $netIdx "CCAP" $leftNode $rightNode $ccVal
      } elseif [regexp {^\s*(\S+)\s+(\S+)\s+(\S+)\s*$} $line all capElemNum leftNode cgVal] {
       set cg [list]
       lappend cg $cgVal $capElemNum
       dict set graphH $netIdx "CGCAP" $leftNode "GND" $cgVal
      }
     }
    } elseif [expr {$inResFlg == 1}] {
     if [regexp {^\s*\*END\s*$} $line] {
      set inResFlg 0
      set inCapFlg 0
      set inDNetFlg 0
     } else {
      if [regexp {^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$} $line all resElemNum leftNode rightNode resVal] {
       set res [list]
       lappend res $resVal $resElemNum
       dict set graphH $netIdx "RES" $leftNode $rightNode $resVal
      }
     }
    }
   }
  }
close $fId
 }
}
## This proc prints the graph for the nets
proc printIdxGraph {idx} {
 regsub {^\s*\*} $idx "N" fileNm
 append fileNm "\.dot"
 set fileId [open $fileNm "w"]
 global dict graphH
 global dict nameMapH
 set nodesH [dict create]
 set edgesH [dict create]
 set connH [dict create]
 set nodeLinesH [dict create]
 puts $fileId "graph G \{"
 #In CCAP and RES, code is repeated. Ideally, put it into a proc. 
 if [dict exists $graphH $idx] {
  puts $fileId "DeenKotturi \[shape=box\];"
  set idGraphH [dict get $graphH $idx]
  dict for {type typeDict} $idGraphH {
   if [ string equal $type "CONN"] {
    dict for {connLine dummyVal} $typeDict {
     puts $fileId "\/\/ $connLine"
     if [regexp {^\s*\S+\s+(\S+)\s+(\S+)\s+.*$} $connLine all connNode direction] {
      dict set connH $connNode $direction
     }
    }
   }
   if [ string equal $type "CCAP"] {
    set leftNodeLine ""
    set righttNodeLine ""
    set edgeLine ""
    dict for {leftNode rightNodeSubDict} $typeDict {
   dict for {rightNode value} $rightNodeSubDict {
      set cCapLine [list]
      set leftNodeMapped [getNetName $leftNode]
      set rightNodeMapped [getNetName $rightNode]
      lappend cCapLine "\/\/$leftNode $leftNodeMapped"
      lappend cCapLine "\/\/$rightNode $rightNodeMapped"
      regsub -all {\*} $leftNode "N" leftNodeNew
      regsub -all {:} $leftNodeNew "_HSP_" leftNodeNew
      if [dict exists $connH $leftNode] {
       set direction [dict get $connH $leftNode]
       if [string equal $direction I] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $leftNodeNew $cc
       } elseif [string equal $direction B] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $leftNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $leftNodeNew $cc
       }
      } else {
       set cc "\[label=\"$leftNodeNew\"\]"
       dict set nodeLinesH $leftNodeNew $cc
      }
      regsub -all {\*} $rightNode "N" rightNodeNew
      regsub -all {:} $rightNodeNew "_HSP_" rightNodeNew
      if [dict exists $connH $rightNode] {
       set direction [dict get $connH $rightNode]
       if [string equal $direction I] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal $direction B] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $rightNodeNew $cc
       }
      } else {
       set cc "\[label=\"$rightNodeNew\"\]"
       dict set nodeLinesH $rightNodeNew $cc
    }
      lappend cCapLine "\"$leftNodeNew\"--\"$rightNodeNew\" \[label=\"CC:$value\"\,color=green\];"
      puts $fileId [join $cCapLine "\n"]
      puts $fileId "https://=======================\n\n"
     }
    }
   } elseif [string equal $type "CGCAP"] {
    set leftNodeLine ""
    set righttNodeLine ""
    set edgeLine ""
    dict for {leftNode rightNodeSubDict} $typeDict {
     dict for {rightNode value} $rightNodeSubDict {
      set gCapLine [list]
      set leftNodeMapped [getNetName $leftNode]
      set rightNodeMapped [getNetName $rightNode]
      lappend gCapLine "\/\/$leftNode $leftNodeMapped"
      lappend gCapLine "\/\/$rightNode $rightNodeMapped"
      regsub -all {\*} $leftNode "N" leftNodeNew
      regsub -all {:} $leftNodeNew "_HSP_" leftNodeNew
      if [dict exists $connH $leftNode] {
       set direction [dict get $connH $leftNode]
       if [string equal $direction I] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $leftNodeNew $cc
       } elseif [string equal $direction B] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $leftNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $leftNodeNew $cc
       }
      } else {
       set gc "\[label=\"$leftNodeNew\"\]"
       dict set nodeLinesH $leftNodeNew $gc
      }
   regsub -all {\*} $rightNode "N" rightNodeNew
      regsub -all {:} $rightNodeNew "_HSP_" rightNodeNew
      if [dict exists $connH $rightNode] {
       set direction [dict get $connH $rightNode]
       if [string equal $direction I] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal $direction B] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $rightNodeNew $cc
       }
      } else {
       set gc "\[label=\"$rightNodeNew\"\]"
       dict set nodeLinesH $rightNodeNew $gc
      }
      lappend gCapLine "\"$leftNodeNew\"--\"$rightNodeNew\" \[label=\"GC:$value\"\,color=blue\];"
      puts $fileId [join $gCapLine "\n"]
      puts $fileId "https://=======================\n\n"
     }
    }
   } elseif [string equal $type "RES"] {
    set leftNodeLine ""
    set righttNodeLine ""
    set edgeLine ""
    dict for {leftNode rightNodeSubDict} $typeDict {
     dict for {rightNode value} $rightNodeSubDict {
      set resLine [list]
      set leftNodeMapped [getNetName $leftNode]
      set rightNodeMapped [getNetName $rightNode]
      lappend resLine "\/\/$leftNode $leftNodeMapped"
      lappend resLine "\/\/$rightNode $rightNodeMapped"
      regsub -all {\*} $leftNode "N" leftNodeNew
      regsub -all {:} $leftNodeNew "_HSP_" leftNodeNew
      if [dict exists $connH $leftNode] {
       set direction [dict get $connH $leftNode]
       if [string equal $direction I] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $leftNodeNew $cc
     } elseif [string equal $direction B] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $leftNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$leftNodeNew\([dict get $connH $leftNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $leftNodeNew $cc
       }
      } else {
       set res "\[label=\"$leftNodeNew\"\]"
       dict set nodeLinesH $leftNodeNew $res
      }
      regsub -all {\*} $rightNode "N" rightNodeNew
      regsub -all {:} $rightNodeNew "_HSP_" rightNodeNew
      if [dict exists $connH $rightNode] {
       set direction [dict get $connH $rightNode]
       if [string equal $direction I] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=limegreen\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal $direction B] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=yellow\]"
        dict set nodeLinesH $rightNodeNew $cc
       } elseif [string equal  $direction O] {
        set cc "\[label=\"$rightNodeNew\([dict get $connH $rightNode]\)\", style=filled, color=cyan\]"
        dict set nodeLinesH $rightNodeNew $cc
       }
      } else {
       set res "\[label=\"$rightNodeNew\"\]"
       dict set nodeLinesH $rightNodeNew $res
      }
      lappend resLine "\"$leftNodeNew\"--\"$rightNodeNew\" \[label=\"R:$value\"\,color=red\];"
      puts $fileId [join $resLine "\n"]
      puts $fileId "https://=======================\n\n"
     }
    }
   }
  }
dict for {node value} $nodeLinesH {
   puts $fileId "$node $value"
  }
  puts $fileId "\}"
 } else {
  puts "$idx does not exist!"
 }
 close $fileId
 if [file exists $fileNm] {
  puts "Creating PNG Graph file for:$fileNm"
  set graphPng $fileNm
  append graphPng "_ps"
  puts "$graphPng $fileNm"
  exec dot -Tps $fileNm -o $graphPng
 }
}        

We can source this code and then execute a series of commands (see below) to read the SPEF, build a graph for the SPEF, and then print out a visual graph of any net or a group of nets. We can easily extend this code to add delay, power, current, voltage, IR, and other timing/power/physical attributes. Note that for large circuits, this is not the efficient way.

So, here is how to use this code:

(base) [deenkotturi@vignette SANDBOX 08:44 PM]$ tclsh
# I saved the above code as mgp.tcl
% source mgp.tcl  
# Below command to read the SPEF and build the parasitic graph
% createGraph aes_core.spef
# Below command is used to create the sub-graph of the net: net_574
% printIdxGraph net_574
Creating PNG Graph file for:net_574.dot
net_574.dot_ps net_574.dot
% exit        

Once we run these commands successfully, we can either view the graph output from printIdxGraph command. Fig 1., shows the graph generated from this code plotted using Yifan Hu graph algorithm [1].


Fig 1. Graph for net_574 (using Yifan Hu algorithm)

In Fig 1., green edge represents a coupled cap, blue edges represent a ground cap, and red edges represent a resistor. If the graph is built, and edge, node, driver, and receiver attributes are annotated on to the graph, we can do all subsequent computing such as stage-by-stage delays, slews, and other timing attributes. In fact, we can use the same approach to compute power and IR but this time using the parasitics of the power grid for analyzing IR of the power grid. Here is an article on power grid analysis that I published previously in case if you are curious: https://www.dhirubhai.net/pulse/full-solution-power-grid-matrix-solving-example-kotturi-msee-mba-3qhgc/?trackingId=Vd1WgC4hu6QsKuXeIgH71g%3D%3D

While Tcl is amazingly simple and easy to use, large problems in circuit analysis require a different approach. I have been using RNNs and Transformer models more often to solve certain repetitive problems with sub-steps of various levels of difficulty.

Feel free to repost this article, use this code, or improve this code for your own purposes.

References:

[1] https://yifanhu.net/PUB/graph_draw_small.pdf

Myles Prather

Power Integrity Lead at Ericsson

1 个月

Cool man! I copied it into ChatGPT and asked for a python transcode. And off it went :).

要查看或添加评论,请登录

Deen Kotturi, MSEE, MBA的更多文章