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