magic/tcltk/readspice.tcl

259 lines
7.5 KiB
Tcl

#-----------------------------------------------------------------
# readspice.tcl
#-----------------------------------------------------------------
# Defines procedure "readspice <netlist>", requiring a SPICE
# netlist as an argument. Each .SUBCKT line in the netlist
# is used to force the port ordering in the layout of the cell
# with the same subcircuit name.
#
# NOTE: This is NOT a schematic-to-layout function! Its purpose
# is to annotate cells with information in a netlist. The cell
# layout must exist before reading the corresponding netlist.
#-----------------------------------------------------------------
global Opts
proc readspice {netfile} {
if {[file ext $netfile] == ".cdl"} {
set is_cdl true
} else {
set is_cdl false
}
if [catch {open $netfile r} fnet] {
set netname [file rootname $netfile]
# Check for standard extensions (.spi, .spc, .spice, .sp, .ckt, .cdl)
set testnetfile ${netname}.spi
if [catch {open ${testnetfile} r} fnet] {
set testnetfile ${netname}.spc
if [catch {open ${testnetfile} r} fnet] {
set testnetfile ${netname}.spice
if [catch {open ${testnetfile} r} fnet] {
set testnetfile ${netname}.sp
if [catch {open ${testnetfile} r} fnet] {
set testnetfile ${netname}.ckt
if [catch {open ${testnetfile} r} fnet] {
set testnetfile ${netname}.cdl
if [catch {open ${testnetfile} r} fnet] {
puts stderr "Error: Can't read netlist file $netfile"
return 1;
} else {
set is_cdl true
}
}
}
}
}
}
set netfile $testnetfile
}
# Read data from file. Remove comment lines and concatenate
# continuation lines.
puts stdout "Annotating port orders from $netfile"
flush stdout
set fdata {}
set lastline ""
while {[gets $fnet line] >= 0} {
# Handle CDL format *.PININFO (convert to .PININFO ...)
if {$is_cdl && ([string range $line 0 1] == "*.")} {
if {[string tolower [string range $line 2 8]] == "pininfo"} {
set line [string range $line 1 end]
}
}
if {[string index $line 0] != "*"} {
if {[string index $line 0] == "+"} {
if {[string range $line end end] != " "} {
append lastline " "
}
append lastline [string range $line 1 end]
} else {
lappend fdata $lastline
set lastline $line
}
}
}
lappend fdata $lastline
close $fnet
# Now look for all ".subckt" lines
set cell ""
set status 0
suspendall
foreach line $fdata {
set ftokens [split $line]
set keyword [string tolower [lindex $ftokens 0]]
# Handle SPECTRE model format
if {$keyword == "inline"} {
if {[string tolower [lindex $ftokens 1]] == "subckt"} {
set ftokens [lrange [split $line " \t()"] 1 end]
set keyword ".subckt"
}
}
if {$keyword == ".subckt"} {
set cell [lindex $ftokens 1]
set status [cellname list exists $cell]
set pindict [dict create]
if {$status != 0} {
load $cell
box values 0 0 0 0
set n 1
set changed false
# Make sure pins aren't duplicated by first moving all pin
# indexes above the number of pins to check.
puts stdout "Annotating cell $cell"
flush stdout
set npins [expr {[llength $ftokens] - 1}]
set highport [port last]
set outport $highport
if {$outport < $npins} {set outport $npins}
set p [port first]
while {$p != -1 && $p <= $highport} {
if {$p == ""} {
puts stderr "Error: $cell port numbering failed."
break
}
set p1 [port $p next]
set testpin [port $p name -quiet]
if {$testpin != ""} {
port $p index $outport
incr outport
}
set p $p1
}
# Get the complete set of labels in the top cell and make a list
select top cell
unexpand
select area labels
expand
set all [lindex [what -list] 1]
select clear
foreach pin [lrange $ftokens 2 end] {
# If "=" is in the name, then we have finished the pins
# and are looking at parameters, and so parsing is done.
if {[string first = $pin] >= 0} {break}
# Tcl "split" will not group spaces and tabs but leaves
# empty strings.
if {$pin == {}} {continue}
# NOTE: Should probably check for CDL-isms, global bang
# characters, case insensitive matches, etc. This routine
# currently expects a 1:1 match between netlist and layout.
# This routine will also make ports out of labels in the
# layout if they have not been read in or created as ports.
# However, if there are multiple labels with the same port
# name, only the one triggered by "goto" will be made into
# a port.
set testpin $pin
set pinidx [port $testpin index -quiet]
if {$pinidx == ""} {
set testpin [string map {\[ < \] >]} $pin]
set pinidx [port $testpin index -quiet]
}
if {$pinidx == ""} {
set testpin [string map {< \[ > \]} $pin]
set pinidx [port $testpin index -quiet]
}
# Handle issues with case insensitivity by getting
# a list of ports and doing a case comparison.
if {$pinidx == ""} {
set highport [port last]
for {set p 0} {$p <= $highport} {incr p} {
set testpin [port $p name -quiet]
if {[string tolower $testpin] == [string tolower $pin]} {
set pinidx [port $testpin index -quiet]
break
}
}
}
# Finally, check if there is a bare label that matches the
# port name. If so, convert it into a port
if {$pinidx == ""} {
foreach labrec $all {
set testpin [lindex $labrec 0]
if {[string tolower $testpin] == [string tolower $pin]} {
goto $testpin
set pinidx -1
port $testpin make $n
break
}
}
}
if {$pinidx != ""} {
port $testpin index $n
if {$pinidx != $n} {
set changed true
}
incr n
# Record the original and modified pin names
dict set pindict $pin $testpin
} else {
set layer [goto $pin]
if {$layer != ""} {
port $pin make $n
incr n
set changed true
}
# Record the pin name as unmodified
dict set pindict $pin $pin
}
}
if {$changed} {
puts stdout "Cell $cell port order was modified."
}
} else {
puts stdout "Cell $cell in netlist has not been loaded."
}
} elseif {$keyword == ".pininfo"} {
if {($cell != "") && ($status != 0)} {
foreach pininfo [lrange $ftokens 1 end] {
set infopair [split $pininfo :]
set pinname [lindex $infopair 0]
set pindir [lindex $infopair 1]
if {![catch {set pin [dict get $pindict $pinname]}]} {
# Only set pin class if the pin class is currently default
set pinclass [port $pin class]
if {$pinclass == "default"} {
case $pindir {
B {port $pin class inout}
I {port $pin class input}
O {port $pin class output}
}
}
} elseif {$pinname != ""} {
puts stderr ".PININFO error: Pin $pinname not found."
}
}
}
} elseif {$keyword == ".ends"} {
set cell ""
set status 0
}
}
resumeall
}
#-----------------------------------------------------------------