1#----------------------------------------------------------------- 2# readspice.tcl 3#----------------------------------------------------------------- 4# Defines procedure "readspice <netlist>", requiring a SPICE 5# netlist as an argument. Each .SUBCKT line in the netlist 6# is used to force the port ordering in the layout of the cell 7# with the same subcircuit name. 8# 9# NOTE: This is NOT a schematic-to-layout function! Its purpose 10# is to annotate cells with information in a netlist. The cell 11# layout must exist before reading the corresponding netlist. 12#----------------------------------------------------------------- 13 14global Opts 15 16proc readspice {netfile} { 17 if {[file ext $netfile] == ".cdl"} { 18 set is_cdl true 19 } else { 20 set is_cdl false 21 } 22 23 if [catch {open $netfile r} fnet] { 24 set netname [file rootname $netfile] 25 26 # Check for standard extensions (.spi, .spc, .spice, .sp, .ckt, .cdl) 27 28 set testnetfile ${netname}.spi 29 if [catch {open ${testnetfile} r} fnet] { 30 set testnetfile ${netname}.spc 31 if [catch {open ${testnetfile} r} fnet] { 32 set testnetfile ${netname}.spice 33 if [catch {open ${testnetfile} r} fnet] { 34 set testnetfile ${netname}.sp 35 if [catch {open ${testnetfile} r} fnet] { 36 set testnetfile ${netname}.ckt 37 if [catch {open ${testnetfile} r} fnet] { 38 set testnetfile ${netname}.cdl 39 if [catch {open ${testnetfile} r} fnet] { 40 puts stderr "Error: Can't read netlist file $netfile" 41 return 1; 42 } else { 43 set is_cdl true 44 } 45 } 46 } 47 } 48 } 49 } 50 set netfile $testnetfile 51 } 52 53 # Read data from file. Remove comment lines and concatenate 54 # continuation lines. 55 56 puts stdout "Annotating port orders from $netfile" 57 flush stdout 58 set fdata {} 59 set lastline "" 60 while {[gets $fnet line] >= 0} { 61 # Handle CDL format *.PININFO (convert to .PININFO ...) 62 if {$is_cdl && ([string range $line 0 1] == "*.")} { 63 if {[string tolower [string range $line 2 8]] == "pininfo"} { 64 set line [string range $line 1 end] 65 } 66 } 67 if {[string index $line 0] != "*"} { 68 if {[string index $line 0] == "+"} { 69 if {[string range $line end end] != " "} { 70 append lastline " " 71 } 72 append lastline [string range $line 1 end] 73 } else { 74 lappend fdata $lastline 75 set lastline $line 76 } 77 } 78 } 79 lappend fdata $lastline 80 close $fnet 81 82 # Now look for all ".subckt" lines 83 84 set cell "" 85 set status 0 86 87 suspendall 88 foreach line $fdata { 89 set ftokens [split $line] 90 set keyword [string tolower [lindex $ftokens 0]] 91 92 # Handle SPECTRE model format 93 if {$keyword == "inline"} { 94 if {[string tolower [lindex $ftokens 1]] == "subckt"} { 95 set ftokens [lrange [split $line " \t()"] 1 end] 96 set keyword ".subckt" 97 } 98 } 99 100 if {$keyword == ".subckt"} { 101 set cell [lindex $ftokens 1] 102 set status [cellname list exists $cell] 103 set pindict [dict create] 104 if {$status != 0} { 105 load $cell 106 box values 0 0 0 0 107 set n 1 108 set changed false 109 110 # Make sure pins aren't duplicated by first moving all pin 111 # indexes above the number of pins to check. 112 113 puts stdout "Annotating cell $cell" 114 flush stdout 115 set npins [expr {[llength $ftokens] - 1}] 116 set highport [port last] 117 set outport $highport 118 if {$outport < $npins} {set outport $npins} 119 set p [port first] 120 while {$p != -1 && $p <= $highport} { 121 if {$p == ""} { 122 puts stderr "Error: $cell port numbering failed." 123 break 124 } 125 set p1 [port $p next] 126 set testpin [port $p name -quiet] 127 if {$testpin != ""} { 128 port $p index $outport 129 incr outport 130 } 131 set p $p1 132 } 133 134 # Get the complete set of labels in the top cell and make a list 135 select top cell 136 select area labels 137 set all [lindex [what -list] 1] 138 select clear 139 140 foreach pin [lrange $ftokens 2 end] { 141 # If "=" is in the name, then we have finished the pins 142 # and are looking at parameters, and so parsing is done. 143 if {[string first = $pin] >= 0} {break} 144 145 # Tcl "split" will not group spaces and tabs but leaves 146 # empty strings. 147 if {$pin == {}} {continue} 148 149 # NOTE: Should probably check for CDL-isms, global bang 150 # characters, case insensitive matches, etc. This routine 151 # currently expects a 1:1 match between netlist and layout. 152 153 # This routine will also make ports out of labels in the 154 # layout if they have not been read in or created as ports. 155 # However, if there are multiple labels with the same port 156 # name, only the one triggered by "goto" will be made into 157 # a port. 158 159 set testpin $pin 160 set pinidx [port $testpin index -quiet] 161 162 if {$pinidx == ""} { 163 set testpin [string map {\[ < \] >]} $pin] 164 set pinidx [port $testpin index -quiet] 165 } 166 if {$pinidx == ""} { 167 set testpin [string map {< \[ > \]} $pin] 168 set pinidx [port $testpin index -quiet] 169 } 170 171 # Handle issues with case insensitivity by getting 172 # a list of ports and doing a case comparison. 173 174 if {$pinidx == ""} { 175 set highport [port last] 176 for {set p 0} {$p <= $highport} {incr p} { 177 set testpin [port $p name -quiet] 178 if {[string tolower $testpin] == [string tolower $pin]} { 179 set pinidx [port $testpin index -quiet] 180 break 181 } 182 } 183 } 184 185 # Finally, check if there is a bare label that matches the 186 # port name. If so, convert it into a port 187 188 if {$pinidx == ""} { 189 foreach labrec $all { 190 set testpin [lindex $labrec 0] 191 if {[string tolower $testpin] == [string tolower $pin]} { 192 goto $testpin 193 set pinidx -1 194 port $testpin make $n 195 break 196 } 197 } 198 } 199 200 if {$pinidx != ""} { 201 port $testpin index $n 202 if {$pinidx != $n} { 203 set changed true 204 } 205 incr n 206 # Record the original and modified pin names 207 dict set pindict $pin $testpin 208 } else { 209 set layer [goto $pin] 210 if {$layer != ""} { 211 port $pin make $n 212 incr n 213 set changed true 214 } 215 # Record the pin name as unmodified 216 dict set pindict $pin $pin 217 } 218 } 219 if {$changed} { 220 puts stdout "Cell $cell port order was modified." 221 } 222 } else { 223 puts stdout "Cell $cell in netlist has not been loaded." 224 } 225 } elseif {$keyword == ".pininfo"} { 226 if {($cell != "") && ($status != 0)} { 227 foreach pininfo [lrange $ftokens 1 end] { 228 set infopair [split $pininfo :] 229 set pinname [lindex $infopair 0] 230 set pindir [lindex $infopair 1] 231 if {![catch {set pin [dict get $pindict $pinname]}]} { 232 # Only set pin class if the pin class is currently default 233 set pinclass [port $pin class] 234 if {$pinclass == "default"} { 235 case $pindir { 236 B {port $pin class inout} 237 I {port $pin class input} 238 O {port $pin class output} 239 } 240 } 241 } elseif {$pinname != ""} { 242 puts stderr ".PININFO error: Pin $pinname not found." 243 } 244 } 245 } 246 } elseif {$keyword == ".ends"} { 247 set cell "" 248 set status 0 249 } 250 } 251 resumeall 252} 253 254 255#----------------------------------------------------------------- 256 257