257 lines
7.5 KiB
Tcl
257 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
|
|
select area labels
|
|
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
|
|
}
|
|
|
|
|
|
#-----------------------------------------------------------------
|
|
|