1#-----------------------------------------------------
2# Magic/TCL general-purpose toolkit procedures
3#-----------------------------------------------------
4# Tim Edwards
5# February 11, 2007
6# Revision 0
7# December 15, 2016
8# Revision 1
9# October 29, 2020
10# Revision 2	(names are hashed from properties)
11# March 9, 2021
12# Added spice-to-layout procedure
13#--------------------------------------------------------------
14# Sets up the environment for a toolkit.  The toolkit must
15# supply a namespace that is the "library name".  For each
16# parameter-defined device ("gencell") type, the toolkit must
17# supply five procedures:
18#
19# 1. ${library}::${gencell_type}_defaults {}
20# 2. ${library}::${gencell_type}_convert  {parameters}
21# 3. ${library}::${gencell_type}_dialog   {parameters}
22# 4. ${library}::${gencell_type}_check    {parameters}
23# 5. ${library}::${gencell_type}_draw     {parameters}
24#
25# The first defines the parameters used by the gencell, and
26# declares default parameters to use when first generating
27# the window that prompts for the device parameters prior to
28# creating the device.  The second converts between parameters
29# in a SPICE netlist and parameters used by the dialog,
30# performing units conversion and parameter name conversion as
31# needed.  The third builds the dialog window for entering
32# device parameters.  The fourth checks the parameters for
33# legal values.  The fifth draws the device.
34#
35# If "library" is not specified then it defaults to "toolkit".
36# Otherwise, where specified, the name "gencell_fullname"
37# is equivalent to "${library}::${gencell_type}"
38#
39# Each gencell is defined by cell properties as created by
40# the "cellname property" command.  Specific properties used
41# by the toolkit are:
42#
43# library    --- name of library (see above, default "toolkit")
44# gencell    --- base name of gencell (gencell_type, above)
45# parameters --- list of gencell parameter-value pairs
46#--------------------------------------------------------------
47
48# Initialize toolkit menus to the wrapper window
49
50global Opts
51
52#----------------------------------------------------------------
53# Add a menu button to the Magic wrapper window for the toolkit
54#----------------------------------------------------------------
55
56proc magic::add_toolkit_menu {framename button_text {library toolkit}} {
57   menubutton ${framename}.titlebar.mbuttons.${library} \
58		-text $button_text \
59		-relief raised \
60		-menu ${framename}.titlebar.mbuttons.${library}.toolmenu \
61		-borderwidth 2
62
63   menu ${framename}.titlebar.mbuttons.${library}.toolmenu -tearoff 0
64   pack ${framename}.titlebar.mbuttons.${library} -side left
65}
66
67#-----------------------------------------------------------------
68# Add a menu item to the toolkit menu calling the default function
69#-----------------------------------------------------------------
70
71proc magic::add_toolkit_button {framename button_text gencell_type \
72		{library toolkit} args} {
73   set m ${framename}.titlebar.mbuttons.${library}.toolmenu
74   $m add command -label "$button_text" -command \
75	"magic::gencell $library::$gencell_type {} $args"
76}
77
78#----------------------------------------------------------------
79# Add a menu item to the toolkit menu that calls the provided
80# function
81#----------------------------------------------------------------
82
83proc magic::add_toolkit_command {framename button_text \
84		command {library toolkit} args} {
85   set m ${framename}.titlebar.mbuttons.${library}.toolmenu
86   $m add command -label "$button_text" -command "$command $args"
87}
88
89#----------------------------------------------------------------
90# Add a separator to the toolkit menu
91#----------------------------------------------------------------
92
93proc magic::add_toolkit_separator {framename {library toolkit}} {
94   set m ${framename}.titlebar.mbuttons.${library}.toolmenu
95   $m add separator
96}
97
98#-----------------------------------------------------
99# Add "Ctrl-P" key callback for device selection
100#-----------------------------------------------------
101
102magic::macro ^P "magic::gencell {} ; raise .params"
103
104#-------------------------------------------------------------
105# Add tag callback to select to update the gencell window
106#-------------------------------------------------------------
107
108magic::tag select "[magic::tag select]; magic::gencell_update %1"
109
110#--------------------------------------------------------------
111# Supporting procedures for netlist_to_layout procedure
112#--------------------------------------------------------------
113
114# move_forward_by_width --
115#
116#    Given an instance name, find the instance and position the
117#    cursor box at the right side of the instance.
118
119proc magic::move_forward_by_width {instname} {
120    select cell $instname
121    set anum [lindex [array -list count] 1]
122    set xpitch [lindex [array -list pitch] 0]
123    set bbox [box values]
124    set posx [lindex $bbox 0]
125    set posy [lindex $bbox 1]
126    set width [expr [lindex $bbox 2] - $posx]
127    set posx [expr $posx + $width + $xpitch * $anum]
128    box position ${posx}i ${posy}i
129    return [lindex $bbox 3]
130}
131
132# get_and_move_inst --
133#
134#    Given a cell name, creat an instance of the cell named "instname"
135#    at the current cursor box position.  If option "anum" is given
136#    and > 1, then array the cell.
137
138proc magic::get_and_move_inst {cellname instname {anum 1}} {
139    set newinst [getcell $cellname]
140    select cell $newinst
141    if {$newinst == ""} {return}
142    identify $instname
143    if {$anum > 1} {array 1 $anum}
144    set bbox [box values]
145    set posx [lindex $bbox 2]
146    set posy [lindex $bbox 1]
147    box position ${posx}i ${posy}i
148    return [lindex $bbox 3]
149}
150
151# create_new_pin --
152#
153#    Create a new pin of size 1um x 1um at the current cursor box
154#    location.  If "layer" is given, then create the pin on the
155#    given layer.  Otherwise, the pin is created on the m1 layer.
156
157proc magic::create_new_pin {pinname portnum {layer m1}} {
158    box size 1um 1um
159    paint $layer
160    label $pinname FreeSans 16 0 0 0 c $layer
161    port make $portnum
162    box move s 2um
163}
164
165# generate_layout_add --
166#
167#    Add a new subcircuit to a layout and seed it with components
168#    as found in the list "complist", and add pins according to the
169#    pin names in "subpins".  Each entry in "complist" is a single
170#    device line from a SPICE file.
171
172proc magic::generate_layout_add {subname subpins complist library} {
173    global PDKNAMESPACE
174
175    # Create a new subcircuit
176    load $subname -quiet
177    box 0 0 0 0
178
179    # Generate pins
180    if {[llength $subpins] > 0} {
181	set pinlist [split $subpins]
182	set i 0
183	foreach pin $pinlist {
184	    # Escape [ and ] in pin name
185	    set pin_esc [string map {\[ \\\[ \] \\\]} $pin]
186	    magic::create_new_pin $pin_esc $i
187	    incr i
188	}
189    }
190
191    # Set initial position for importing cells
192    box size 0 0
193    set posx 0
194    set posy [expr {round(3 / [cif scale out])}]
195    box position ${posx}i ${posy}i
196
197    # Seed layout with components
198    foreach comp $complist {
199	set pinlist {}
200	set paramlist {}
201
202	# NOTE:  This routine deals with subcircuit calls and devices
203	# with models.  It needs to determine when a device is instantiated
204	# without a model, and ignore such devices.
205
206	# Parse SPICE line into pins, device name, and parameters.  Make
207	# sure parameters incorporate quoted expressions as {} or ''.
208
209	set rest $comp
210	while {$rest != ""} {
211	    if {[regexp -nocase {^[ \t]*[^= \t]+=[^=]+} $rest]} {
212		break
213	    } elseif {[regexp -nocase {^[ \t]*([^ \t]+)[ \t]*(.*)$} $rest \
214			valid token rest]} {
215		lappend pinlist $token
216	    } else {
217		set rest ""
218	    }
219	}
220
221	while {$rest != ""} {
222	    if {[regexp -nocase {^([^= \t]+)=\'([^\']+)\'[ \t]*(.*)} $rest \
223			valid pname value rest]} {
224		lappend paramlist [list $pname "{$value}"]
225	    } elseif {[regexp -nocase {^([^= \t]+)=\{([^\}]+)\}[ \t]*(.*)} $rest \
226			valid pname value rest]} {
227		lappend paramlist [list $pname "{$value}"]
228	    } elseif {[regexp -nocase {^([^= \t]+)=([^= \t]+)[ \t]*(.*)} $rest \
229			valid pname value rest]} {
230		lappend paramlist [list $pname $value]
231	    } else {
232		puts stderr "Error parsing line \"$comp\""
233		puts stderr "at:  \"$rest\""
234		set rest ""
235	    }
236	}
237
238	if {[llength $pinlist] < 2} {
239	    puts stderr "Error:  No device type found in line \"$comp\""
240	    puts stderr "Tokens found are: \"$pinlist\""
241	    continue
242	}
243
244	set instname [lindex $pinlist 0]
245	set devtype [lindex $pinlist end]
246	set pinlist [lrange $pinlist 0 end-1]
247
248	set mult 1
249	foreach param $paramlist {
250	    set parmname [lindex $param 0]
251	    set parmval [lindex $param 1]
252	    if {[string toupper $parmname] == "M"} {
253		if {[catch {set mult [expr {int($parmval)}]}]} {
254		    set mult [expr [string trim $parmval "'"]]
255		}
256	    }
257	}
258
259        # devtype is assumed to be in library.  If not, it will attempt to
260	# use 'getcell' on devtype.  Note that this code depends on the
261	# PDK setting varible PDKNAMESPACE.
262
263	if {$library != ""} {
264	    set libdev ${library}::${devtype}
265	} else {
266	    set libdev ${PDKNAMESPACE}::${devtype}
267	}
268
269	set outparts {}
270	lappend outparts "magic::gencell $libdev $instname"
271
272	# Output all parameters.  Parameters not used by the toolkit are
273	# ignored by the toolkit.
274
275	lappend outparts "-spice"
276	foreach param $paramlist {
277	    lappend outparts [string tolower [lindex $param 0]]
278	    lappend outparts [lindex $param 1]
279	}
280
281	if {[catch {eval [join $outparts]}]} {
282	    # Assume this is not a gencell, and get an instance.
283	    magic::get_and_move_inst $devtype $instname $mult
284	} else {
285	    # Move forward for next gencell
286	    magic::move_forward_by_width $instname
287	}
288    }
289    save $subname
290}
291
292#--------------------------------------------------------------
293# Wrapper for generating an initial layout from a SPICE netlist
294# using the defined PDK toolkit procedures
295#
296#    "netfile" is the name of a SPICE netlist
297#    "library" is the name of the PDK library namespace
298#--------------------------------------------------------------
299
300proc magic::netlist_to_layout {netfile library} {
301
302   if {![file exists $netfile]} {
303      puts stderr "No such file $netfile"
304      return
305   }
306
307   # Read data from file.  Remove comment lines and concatenate
308   # continuation lines.
309
310   set topname [file rootname [file tail $netfile]]
311   puts stdout "Creating layout from [file tail $netfile]"
312
313   if {[file ext $netfile] == ".cdl"} {
314      set is_cdl true
315   } else {
316      set is_cdl false
317   }
318
319   if [catch {open $netfile r} fnet] {
320      puts stderr "Error:  Cannot open file \"$netfile\" for reading."
321      return
322   }
323
324   set fdata {}
325   set lastline ""
326   while {[gets $fnet line] >= 0} {
327       # Handle CDL format *.PININFO (convert to .PININFO ...)
328       if {$is_cdl && ([string range $line 0 1] == "*.")} {
329           if {[string tolower [string range $line 2 8]] == "pininfo"} {
330               set line [string range $line 1 end]
331           }
332       }
333       if {[string index $line 0] != "*"} {
334           if {[string index $line 0] == "+"} {
335               if {[string range $line end end] != " "} {
336                  append lastline " "
337               }
338               append lastline [string range $line 1 end]
339           } else {
340               lappend fdata $lastline
341               set lastline $line
342           }
343       }
344   }
345   lappend fdata $lastline
346   close $fnet
347
348   set insub false
349   set incmd false
350   set subname ""
351   set subpins ""
352   set complist {}
353   set toplist {}
354
355   # suspendall
356
357   set ignorekeys {.global .ic .option .end}
358
359   # Parse the file
360   foreach line $fdata {
361      if {$incmd} {
362	 if {[regexp -nocase {^[ \t]*\.endc} $line]} {
363	    set incmd false
364	 }
365      } elseif {! $insub} {
366         set ftokens [split $line]
367         set keyword [string tolower [lindex $ftokens 0]]
368
369         if {[lsearch $ignorekeys $keyword] != -1} {
370	    continue
371         } elseif {$keyword == ".command"} {
372	    set incmd true
373         } elseif {$keyword == ".subckt"} {
374	    set subname [lindex $ftokens 1]
375	    set subpins [lrange $ftokens 2 end]
376	    set insub true
377         } elseif {[regexp -nocase {^[xmcrdq]([^ \t]+)[ \t](.*)$} $line \
378		    valid instname rest]} {
379	    lappend toplist $line
380         } elseif {[regexp -nocase {^[ivbe]([^ \t]+)[ \t](.*)$} $line \
381		    valid instname rest]} {
382	    # These are testbench devices and should be ignored
383	    continue
384         }
385      } else {
386	 if {[regexp -nocase {^[ \t]*\.ends} $line]} {
387	    set insub false
388	    magic::generate_layout_add $subname $subpins $complist $library
389	    set subname ""
390	    set subpins ""
391	    set complist {}
392         } elseif {[regexp -nocase {^[xmcrdq]([^ \t]+)[ \t](.*)$} $line \
393		    valid instname rest]} {
394	    lappend complist $line
395         } elseif {[regexp -nocase {^[ivbe]([^ \t]+)[ \t](.*)$} $line \
396		    valid instname rest]} {
397	    # These are testbench devices and should be ignored
398	    continue
399	 }
400      }
401   }
402
403   # Add in any top-level components (not in subcircuits)
404   if {[llength $toplist] > 0} {
405      magic::generate_layout_add $topname "" $toplist $library
406   }
407
408   # resumeall
409}
410
411#-------------------------------------------------------------
412# gencell
413#
414#   Main routine to call a cell from either a menu button or
415#   from a script or command line.  The name of the device
416#   is required, followed by the name of the instance, followed
417#   by an optional list of parameters.  Handling depends on
418#   instname and args:
419#
420#   gencell_name is either the name of an instance or the name
421#   of the gencell in the form <library>::<device>.
422#
423#   name        args      action
424#-----------------------------------------------------------------
425#   none        empty     interactive, new device w/defaults
426#   none        specified interactive, new device w/parameters
427#   instname    empty     interactive, edit device
428#   instname    specified non-interactive, change device
429#   device      empty     non-interactive, new device w/defaults
430#   device	specified non-interactive, new device w/parameters
431#
432#-------------------------------------------------------------
433# Also, if instname is empty and gencell_name is not specified,
434# and if a device is selected in the layout, then gencell
435# behaves like line 3 above (instname exists, args is empty).
436# Note that macro Ctrl-P calls gencell this way.  If gencell_name
437# is not specified and nothing is selected, then gencell{}
438# does nothing.
439#
440# "args" must be a list of the cell parameters in key:value pairs,
441# and an odd number is not legal;  the exception is that if the
442# first argument is "-spice", then the list of parameters is
443# expected to be in the format used in a SPICE netlist, and the
444# parameter names and values will be treated accordingly.
445#-------------------------------------------------------------
446
447proc magic::gencell {gencell_name {instname {}} args} {
448
449    # Pull "-spice" out of args, if it is the first argument
450    if {[lindex $args 0] == "-spice"} {
451	set spicemode 1
452	set args [lrange $args 1 end]
453    } else {
454	set spicemode 0
455    }
456    set argpar [dict create {*}$args]
457
458    if {$gencell_name == {}} {
459	# Find selected item  (to-do:  handle multiple selections)
460
461	set wlist [what -list]
462	set clist [lindex $wlist 2]
463	set ccell [lindex $clist 0]
464	set ginst [lindex $ccell 0]
465	set gname [lindex $ccell 1]
466	set library [cellname list property $gname library]
467	if {$library == {}} {
468	    set library toolkit
469        }
470	set gencell_type [cellname list property $gname gencell]
471	if {$gencell_type == {}} {
472	   if {![regexp {^(.*)_[0-9]*$} $gname valid gencell_type]} {
473	      # Error message
474	      error "No gencell device is selected!"
475	   }
476	}
477        # need to incorporate argpar?
478        set parameters [cellname list property $gname parameters]
479	set parameters [magic::gencell_defaults $gencell_type $library $parameters]
480	magic::gencell_dialog $ginst $gencell_type $library $parameters
481    } else {
482	# Parse out library name from gencell_name, otherwise default
483	# library is assumed to be "toolkit".
484	if {[regexp {^([^:]+)::([^:]+)$} $gencell_name valid library gencell_type] \
485			== 0} {
486	    set library "toolkit"
487	    set gencell_type $gencell_name
488	}
489
490    	# Check that the device exists as a gencell, or else return an error
491    	if {[namespace eval ::${library} info commands ${gencell_type}_convert] == ""} {
492	    error "No import routine for ${library} library cell ${gencell_type}!"
493    	}
494
495	if {$instname == {}} {
496	    # Case:  Interactive, new device with parameters in args (if any)
497	    if {$spicemode == 1} {
498		# Legal not to have a *_convert routine
499		if {[info commands ${library}::${gencell_type}_convert] != ""} {
500		    set argpar [${library}::${gencell_type}_convert $argpar]
501		}
502	    }
503	    set parameters [magic::gencell_defaults $gencell_type $library $argpar]
504	    magic::gencell_dialog {} $gencell_type $library $parameters
505	} else {
506	    # Check if instance exists or not in the cell
507	    set cellname [instance list celldef $instname]
508
509	    if {$cellname != ""} {
510		# Case:  Change existing instance, parameters in args (if any)
511		select cell $instname
512		set devparms [cellname list property $gencell_type parameters]
513	        set parameters [magic::gencell_defaults $gencell_type $library $devparms]
514		if {[dict exists $parameters nocell]} {
515		    set arcount [array -list count]
516		    set arpitch [array -list pitch]
517
518		    dict set parameters nx [lindex $arcount 1]
519		    dict set parameters ny [lindex $arcount 3]
520		    dict set parameters pitchx $delx
521		    dict set parameters pitchy $dely
522		}
523		if {[dict size $argpar] == 0} {
524		    # No changes entered on the command line, so start dialog
525		    magic::gencell_dialog $instname $gencell_type $library $parameters
526		} else {
527		    # Apply specified changes without invoking the dialog
528		    if {$spicemode == 1} {
529			set argpar [${library}::${gencell_type}_convert $argpar]
530		    }
531		    set parameters [dict merge $parameters $argpar]
532		    magic::gencell_change $instname $gencell_type $library $parameters
533		}
534	    } else {
535		# Case:  Non-interactive, create new device with parameters
536		# in args (if any)
537		if {$spicemode == 1} {
538		    set argpar [${library}::${gencell_type}_convert $argpar]
539		}
540	        set parameters [magic::gencell_defaults $gencell_type $library $argpar]
541		set inst_defaultname [magic::gencell_create \
542				$gencell_type $library $parameters]
543		select cell $inst_defaultname
544		identify $instname
545	    }
546	}
547    }
548    return 0
549}
550
551#-------------------------------------------------------------
552# gencell_makecell
553#
554# This is a variation of magic::gencell and is used to generate
555# a cell and return the cell name without creating or placing
556# an instance.
557#-------------------------------------------------------------
558
559proc magic::gencell_makecell {gencell_fullname args} {
560
561    set argpar [dict create {*}$args]
562    set gencell_basename [namespace tail $gencell_fullname]
563    set library [namespace qualifiers $gencell_fullname]
564    set parameters [magic::gencell_defaults $gencell_basename $library $argpar]
565    set gsuffix [magic::get_gencell_hash ${parameters}]
566    set gname ${gencell_basename}_${gsuffix}
567    suspendall
568    cellname create $gname
569    pushstack $gname
570    if {[catch {${library}::${gencell_basename}_draw $parameters} drawerr]} {
571        puts stderr $drawerr
572    }
573    property library $library
574    property gencell $gencell_basename
575    property parameters $parameters
576    popstack
577    resumeall
578    return $gname
579}
580
581#-------------------------------------------------------------
582# gencell_getparams
583#
584#   Go through the parameter window and collect all of the
585#   named parameters and their values.  Return the result as
586#   a dictionary.
587#-------------------------------------------------------------
588
589proc magic::gencell_getparams {} {
590   set parameters [dict create]
591   set slist [grid slaves .params.edits]
592   foreach s $slist {
593      if {[regexp {^.params.edits.(.*)_ent$} $s valid pname] != 0} {
594	 set value [subst \$magic::${pname}_val]
595      } elseif {[regexp {^.params.edits.(.*)_chk$} $s valid pname] != 0} {
596	 set value [subst \$magic::${pname}_val]
597      } elseif {[regexp {^.params.edits.(.*)_sel$} $s valid pname] != 0} {
598	 set value [subst \$magic::${pname}_val]
599      }
600      dict set parameters $pname $value
601   }
602   return $parameters
603}
604
605#-------------------------------------------------------------
606# gencell_setparams
607#
608#   Fill in values in the dialog from a set of parameters
609#-------------------------------------------------------------
610
611proc magic::gencell_setparams {parameters} {
612   if {[catch {set state [wm state .params]}]} {return}
613   set slist [grid slaves .params.edits]
614   foreach s $slist {
615      # ignore .params.edits.gencell_sel, as that does not exist in the
616      # parameters dictionary
617      if {$s == ".params.edits.gencell_sel"} {continue}
618      if {[regexp {^.params.edits.(.*)_ent$} $s valid pname] != 0} {
619	 set value [dict get $parameters $pname]
620         set magic::${pname}_val $value
621      } elseif {[regexp {^.params.edits.(.*)_chk$} $s valid pname] != 0} {
622	 set value [dict get $parameters $pname]
623         set magic::${pname}_val $value
624      } elseif {[regexp {^.params.edits.(.*)_sel$} $s valid pname] != 0} {
625	 set value [dict get $parameters $pname]
626         set magic::${pname}_val $value
627	 .params.edits.${pname}_sel configure -text $value
628      } elseif {[regexp {^.params.edits.(.*)_txt$} $s valid pname] != 0} {
629	 if {[dict exists $parameters $pname]} {
630	    set value [dict get $parameters $pname]
631	    .params.edits.${pname}_txt configure -text $value
632	 }
633      }
634   }
635}
636
637#-------------------------------------------------------------
638# gencell_change
639#
640#   Recreate a gencell with new parameters.  Note that because
641#   each cellname is uniquely identified by the (hashed) set
642#   of parameters, changing parameters effectively means
643#   creating a new cell.  If the original cell has parents
644#   other than the parent of the instance being changed, then
645#   it is retained;  otherwise, it is deleted.  The instance
646#   being edited gets replaced by an instance of the new cell.
647#   If the instance name was the cellname + suffix, then the
648#   instance name is regenerated.  Otherwise, the instance
649#   name is retained.
650#-------------------------------------------------------------
651
652proc magic::gencell_change {instname gencell_type library parameters} {
653    global Opts
654    suspendall
655
656    set newinstname $instname
657    if {$parameters == {}} {
658        # Get device defaults
659	set pdefaults [${library}::${gencell_type}_defaults]
660        # Pull user-entered values from dialog
661        set parameters [dict merge $pdefaults [magic::gencell_getparams]]
662	set newinstname [.params.title.ient get]
663	if {$newinstname == "(default)"} {set newinstname $instname}
664	if {$newinstname == $instname} {set newinstname $instname}
665	if {[instance list exists $newinstname] != ""} {set newinstname $instname}
666    }
667    if {[dict exists $parameters gencell]} {
668        # Setting special parameter "gencell" forces the gencell to change type
669	set gencell_type [dict get $parameters gencell]
670    }
671    if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
672		checkerr]} {
673	puts stderr $checkerr
674    }
675    magic::gencell_setparams $parameters
676    if {[dict exists $parameters gencell]} {
677	set parameters [dict remove $parameters gencell]
678    }
679
680    set old_gname [instance list celldef $instname]
681    set gsuffix [magic::get_gencell_hash ${parameters}]
682    set gname ${gencell_type}_${gsuffix}
683
684    # Guard against instance having been deleted.  Also, if parameters have not
685    # changed as evidenced by the cell suffix not changing, then nothing further
686    # needs to be done.
687    if {$gname == "" || $gname == $old_gname} {
688	resumeall
689        return
690    }
691
692    set snaptype [snap list]
693    snap internal
694    set savebox [box values]
695
696    catch {setpoint 0 0 $Opts(focus)}
697    if [dict exists $parameters nocell] {
698        select cell $instname
699	set abox [instance list abutment]
700	delete
701	if {$abox != ""} {box values {*}$abox}
702	if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \
703		drawerr]} {
704	    puts stderr $drawerr
705	}
706        select cell $newinst
707    } elseif {[cellname list exists $gname] != 0} {
708	# If there is already a cell of this type then it is only required to
709	# remove the instance and replace it with an instance of the cell
710        select cell $instname
711	# check rotate/flip before replacing and replace with same
712	set orient [instance list orientation]
713	set abox [instance list abutment]
714	delete
715
716	if {$abox != ""} {box values {*}$abox}
717	set newinstname [getcell $gname $orient]
718        select cell $newinstname
719	expand
720
721	# If the old instance name was not formed from the old cell name,
722	# then keep the old instance name.
723	if {[string first $old_gname $instname] != 0} {
724	    set newinstname $instname
725	}
726
727	if {[cellname list parents $old_gname] == []} {
728	    # If the original cell has no intances left, delete it.  It can
729	    # be regenerated if and when necessary.
730	    cellname delete $old_gname
731	}
732
733    } else {
734        select cell $instname
735	set orient [instance list orientation]
736	set abox [instance list abutment]
737	delete
738
739	# There is no cell of this name, so generate one and instantiate it.
740	if {$abox != ""} {box values {*}$abox}
741	set newinstname [magic::gencell_create $gencell_type $library $parameters $orient]
742	select cell $newinstname
743
744	# If the old instance name was not formed from the old cell name,
745	# then keep the old instance name.
746	if {[string first $old_gname $instname] != 0} {
747	    set newinstname $instname
748	} else {
749	    # The buttons "Apply" and "Okay" need to be changed for the new
750	    # instance name
751	    catch {.params.buttons.apply config -command \
752			"magic::gencell_change $newinstname $gencell_type $library {}"}
753	    catch {.params.buttons.okay config -command \
754			"magic::gencell_change $newinstname $gencell_type $library {} ;\
755			destroy .params"}
756	}
757    }
758    identify $newinstname
759    eval "box values $savebox"
760    snap $snaptype
761
762    # Update window
763    if {$gname != $old_gname} {
764        catch {.params.title.glab configure -text "$gname"}
765    }
766    if {$instname != $newinstname} {
767        catch {.params.title.ient delete 0 end}
768        catch {.params.title.ient insert 0 "$newinstname"}
769    }
770
771    resumeall
772    redraw
773}
774
775#-------------------------------------------------------------
776# gencell_change_orig
777#
778#   Original version:  Redraw a gencell with new parameters,
779#   without changing the cell itself.
780#-------------------------------------------------------------
781
782proc magic::gencell_change_orig {instname gencell_type library parameters} {
783    global Opts
784    suspendall
785
786    set newinstname $instname
787    if {$parameters == {}} {
788        # Get device defaults
789	set pdefaults [${library}::${gencell_type}_defaults]
790        # Pull user-entered values from dialog
791        set parameters [dict merge $pdefaults [magic::gencell_getparams]]
792	set newinstname [.params.title.ient get]
793	if {$newinstname == "(default)"} {set newinstname $instname}
794	if {$newinstname == $instname} {set newinstname $instname}
795	if {[instance list exists $newinstname] != ""} {set newinstname $instname}
796    }
797    if {[dict exists $parameters gencell]} {
798        # Setting special parameter "gencell" forces the gencell to change type
799	set gencell_type [dict get $parameters gencell]
800    }
801    if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
802		checkerr]} {
803	puts stderr $checkerr
804    }
805    magic::gencell_setparams $parameters
806    if {[dict exists $parameters gencell]} {
807	set parameters [dict remove $parameters gencell]
808    }
809
810    set gname [instance list celldef $instname]
811
812    # Guard against instance having been deleted
813    if {$gname == ""} {
814	resumeall
815        return
816    }
817
818    set snaptype [snap list]
819    snap internal
820    set savebox [box values]
821
822    catch {setpoint 0 0 $Opts(focus)}
823    if [dict exists $parameters nocell] {
824        select cell $instname
825	delete
826	if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \
827		drawerr]} {
828	    puts stderr $drawerr
829	}
830        select cell $newinst
831    } else {
832	pushstack $gname
833	select cell
834	tech unlock *
835	erase *
836	if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} {
837	    puts stderr $drawerr
838	}
839	property parameters $parameters
840	property gencell ${gencell_type}
841	tech revert
842	popstack
843        select cell $instname
844    }
845    identify $newinstname
846    eval "box values $savebox"
847    snap $snaptype
848    resumeall
849    redraw
850}
851
852#-------------------------------------------------------------
853# Assign a unique name for a gencell
854#
855# Note:  This depends on the unlikelihood of the name
856# existing in a cell on disk.  Only cells in memory are
857# checked for name collisions.  Since the names will go
858# into SPICE netlists, names must be unique when compared
859# in a case-insensitive manner.  Using base-36 (alphabet and
860# numbers), each gencell name with 6 randomized characters
861# has a 1 in 4.6E-10 chance of reappearing.
862#-------------------------------------------------------------
863
864proc magic::get_gencell_name {gencell_type} {
865    while {true} {
866        set postfix ""
867        for {set i 0} {$i < 6} {incr i} {
868	    set pint [expr 48 + int(rand() * 36)]
869	    if {$pint > 57} {set pint [expr $pint + 39]}
870	    append postfix [format %c $pint]
871	}
872	if {[cellname list exists ${gencell_type}_$postfix] == 0} {break}
873    }
874    return ${gencell_type}_$postfix
875}
876
877#----------------------------------------------------------------
878# get_gencell_hash
879#
880#   A better approach to the above.  Take the parameter
881#   dictionary, and run all the values through a hash function
882#   to generate a 30-bit value, then convert to base32.  This
883#   gives a result that is repeatable for the same set of
884#   parameter values with a very low probability of a collision.
885#
886#   The hash function is similar to elfhash but reduced from 32
887#   to 30 bits so that the result can form a 6-character value
888#   in base32 with all characters being valid for a SPICE subcell
889#   name (e.g., alphanumeric only and case-insensitive).
890#----------------------------------------------------------------
891
892proc magic::get_gencell_hash {parameters} {
893    set hash 0
894    # Apply hash
895    dict for {key value} $parameters {
896	foreach s [split $value {}] {
897	    set hash [expr {($hash << 4) + [scan $s %c]}]
898	    set high [expr {$hash & 0x03c0000000}]
899	    set hash [expr {$hash ^ ($high >> 30)}]
900	    set hash [expr {$hash & (~$high)}]
901	}
902    }
903    # Divide hash up into 5 bit values and convert to base32
904    # using letters A-Z less I and O, and digits 2-9.
905    set cvals ""
906    for {set i 0} {$i < 6} {incr i} {
907	set oval [expr {($hash >> ($i * 5)) & 0x1f}]
908        if {$oval < 8} {
909	    set bval [expr {$oval + 50}]
910	} elseif {$oval < 16} {
911	    set bval [expr {$oval + 57}]
912	} elseif {$oval < 21} {
913	    set bval [expr {$oval + 58}]
914	} else {
915	    set bval [expr {$oval + 59}]
916	}
917	append cvals [binary format c* $bval]
918    }
919    return $cvals
920}
921
922#-------------------------------------------------------------
923# gencell_create
924#
925#   Instantiate a new gencell called $gname.  If $gname
926#   does not already exist, create it by calling its
927#   drawing routine.
928#
929#   Don't rely on pushbox/popbox since we don't know what
930#   the drawing routine is going to do to the stack!
931#-------------------------------------------------------------
932
933proc magic::gencell_create {gencell_type library parameters {orient 0}} {
934    global Opts
935    suspendall
936
937    set newinstname ""
938
939    # Get device defaults
940    if {$parameters == {}} {
941        # Pull user-entered values from dialog
942        set dialogparams [magic::gencell_getparams]
943	if {[dict exists $dialogparams gencell]} {
944	    # Setting special parameter "gencell" forces the gencell to change type
945	    set gencell_type [dict get $dialogparams gencell]
946	}
947	set pdefaults [${library}::${gencell_type}_defaults]
948        set parameters [dict merge $pdefaults $dialogparams]
949	set newinstname [.params.title.ient get]
950	if {$newinstname == "(default)"} {set newinstname ""}
951	if {[instance list exists $newinstname] != ""} {set newinstname ""}
952    } else {
953	if {[dict exists $parameters gencell]} {
954	    # Setting special parameter "gencell" forces the gencell to change type
955	    set gencell_type [dict get $parameters gencell]
956	}
957	set pdefaults [${library}::${gencell_type}_defaults]
958        set parameters [dict merge $pdefaults $parameters]
959    }
960
961    if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
962		checkerr]} {
963	puts stderr $checkerr
964    }
965    magic::gencell_setparams $parameters
966    if {[dict exists $parameters gencell]} {
967	set parameters [dict remove $parameters gencell]
968    }
969
970    set snaptype [snap list]
971    snap internal
972    set savebox [box values]
973
974    catch {setpoint 0 0 $Opts(focus)}
975    if [dict exists $parameters nocell] {
976	if {[catch {set instname [${library}::${gencell_type}_draw $parameters]} \				drawerr]} {
977	    puts stderr $drawerr
978	}
979	set gname [instance list celldef $instname]
980	eval "box values $savebox"
981    } else {
982        set gsuffix [magic::get_gencell_hash ${parameters}]
983        set gname ${gencell_type}_${gsuffix}
984	cellname create $gname
985	pushstack $gname
986	if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} {
987	    puts stderr $drawerr
988	}
989	property library $library
990	property gencell $gencell_type
991	property parameters $parameters
992	popstack
993	eval "box values $savebox"
994	set instname [getcell $gname $orient]
995	expand
996    }
997    if {$newinstname != ""} {
998	identify $newinstname
999	set instname $newinstname
1000    }
1001    snap $snaptype
1002    resumeall
1003    redraw
1004    return $instname
1005}
1006
1007#-----------------------------------------------------
1008#  Add a standard entry parameter to the gencell window
1009#-----------------------------------------------------
1010
1011proc magic::add_entry {pname ptext parameters} {
1012
1013   if [dict exists $parameters $pname] {
1014        set value [dict get $parameters $pname]
1015   } else {
1016       set value ""
1017   }
1018
1019   set numrows [lindex [grid size .params.edits] 1]
1020   label .params.edits.${pname}_lab -text $ptext
1021   entry .params.edits.${pname}_ent -background white -textvariable magic::${pname}_val
1022   grid .params.edits.${pname}_lab -row $numrows -column 0 \
1023	-sticky ens -ipadx 5 -ipady 2
1024   grid .params.edits.${pname}_ent -row $numrows -column 1 \
1025	-sticky ewns -ipadx 5 -ipady 2
1026   .params.edits.${pname}_ent insert end $value
1027   set magic::${pname}_val $value
1028}
1029
1030#----------------------------------------------------------
1031# Default entry callback, without any dependencies.  Each
1032# parameter changed
1033#----------------------------------------------------------
1034
1035proc magic::add_check_callbacks {gencell_type library} {
1036    set wlist [winfo children .params.edits]
1037    foreach w $wlist {
1038        if {[regexp {\.params\.edits\.(.+)_ent} $w valid pname]} {
1039	    # Add callback on enter or focus out
1040	    bind $w <Return> \
1041			"magic::update_dialog {} $pname $gencell_type $library"
1042	    bind $w <FocusOut> \
1043			"magic::update_dialog {} $pname $gencell_type $library"
1044	}
1045    }
1046}
1047
1048#----------------------------------------------------------
1049# Add a dependency between entries.  When one updates, the
1050# others will be recomputed according to the callback
1051# function.
1052#
1053# The callback function is passed the value of all
1054# parameters for the device, overridden by the values
1055# in the dialog.  The routine computes the dependent
1056# values and writes them back to the parameter dictionary.
1057# The callback function must return the modified parameters
1058# dictionary.
1059#
1060# Also handle dependencies on checkboxes and selection lists
1061#----------------------------------------------------------
1062
1063proc magic::add_dependency {callback gencell_type library args} {
1064    if {[llength $args] == 0} {
1065	# If no arguments are given, do for all parameters
1066	set parameters ${library}::${gencell_type}_defaults
1067	magic::add_dependency $callback $gencell_type $library \
1068			{*}[dict keys $parameters]
1069	return
1070    }
1071    set clist [winfo children .params.edits]
1072    foreach pname $args {
1073        if {[lsearch $clist .params.edits.${pname}_ent] >= 0} {
1074	    # Add callback on enter or focus out
1075	    bind .params.edits.${pname}_ent <Return> \
1076			"magic::update_dialog $callback $pname $gencell_type $library"
1077	    bind .params.edits.${pname}_ent <FocusOut> \
1078			"magic::update_dialog $callback $pname $gencell_type $library"
1079	} elseif {[lsearch $clist .params.edits.${pname}_chk] >= 0} {
1080	    # Add callback on checkbox change state
1081	    .params.edits.${pname}_chk configure -command \
1082			"magic::update_dialog $callback $pname $gencell_type $library"
1083	} elseif {[lsearch $clist .params.edits.${pname}_sel] >= 0} {
1084	    set smenu .params.edits.${pname}_sel.menu
1085	    set sitems [${smenu} index end]
1086	    for {set idx 0} {$idx <= $sitems} {incr idx} {
1087		set curcommand [${smenu} entrycget $idx -command]
1088		${smenu} entryconfigure $idx -command "$curcommand ; \
1089		magic::update_dialog $callback $pname $gencell_type $library"
1090	    }
1091	}
1092    }
1093}
1094
1095#----------------------------------------------------------
1096# Execute callback procedure, then run bounds checks
1097#----------------------------------------------------------
1098
1099proc magic::update_dialog {callback pname gencell_type library} {
1100    set pdefaults [${library}::${gencell_type}_defaults]
1101    set parameters [dict merge $pdefaults [magic::gencell_getparams]]
1102
1103    if {[dict exists $parameters gencell]} {
1104        # Setting special parameter "gencell" forces the gencell to change type
1105	set gencell_type [dict get $parameters gencell]
1106	set pdefaults [${library}::${gencell_type}_defaults]
1107	set parameters [dict merge $pdefaults [magic::gencell_getparams]]
1108    }
1109
1110    if {$callback != {}} {
1111       set parameters [$callback $pname $parameters]
1112    }
1113    if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
1114		checkerr]} {
1115	puts stderr $checkerr
1116    }
1117    magic::gencell_setparams $parameters
1118}
1119
1120#----------------------------------------------------------
1121#  Add a standard checkbox parameter to the gencell window
1122#----------------------------------------------------------
1123
1124proc magic::add_checkbox {pname ptext parameters} {
1125
1126   if [dict exists $parameters $pname] {
1127        set value [dict get $parameters $pname]
1128   } else {
1129       set value ""
1130   }
1131
1132   set numrows [lindex [grid size .params.edits] 1]
1133   label .params.edits.${pname}_lab -text $ptext
1134   checkbutton .params.edits.${pname}_chk -variable magic::${pname}_val
1135   grid .params.edits.${pname}_lab -row $numrows -column 0 -sticky ens
1136   grid .params.edits.${pname}_chk -row $numrows -column 1 -sticky wns
1137   set magic::${pname}_val $value
1138}
1139
1140#----------------------------------------------------------
1141# Add a message box (informational, not editable) to the
1142# gencell window.  Note that the text does not have to be
1143# in the parameter list, as it can be upated through the
1144# textvariable name.
1145#----------------------------------------------------------
1146
1147proc magic::add_message {pname ptext parameters {color blue}} {
1148
1149   if [dict exists $parameters $pname] {
1150      set value [dict get $parameters $pname]
1151   } else {
1152      set value ""
1153   }
1154
1155   set numrows [lindex [grid size .params.edits] 1]
1156   label .params.edits.${pname}_lab -text $ptext
1157   label .params.edits.${pname}_txt -text $value \
1158		-foreground $color -textvariable magic::${pname}_val
1159   grid .params.edits.${pname}_lab -row $numrows -column 0 -sticky ens
1160   grid .params.edits.${pname}_txt -row $numrows -column 1 -sticky wns
1161}
1162
1163#----------------------------------------------------------
1164#  Add a selectable-list parameter to the gencell window
1165#----------------------------------------------------------
1166
1167proc magic::add_selectlist {pname ptext all_values parameters {itext ""}} {
1168
1169   if [dict exists $parameters $pname] {
1170        set value [dict get $parameters $pname]
1171   } else {
1172       set value $itext
1173   }
1174
1175   set numrows [lindex [grid size .params.edits] 1]
1176   label .params.edits.${pname}_lab -text $ptext
1177   menubutton .params.edits.${pname}_sel -menu .params.edits.${pname}_sel.menu \
1178		-relief groove -text ${value}
1179   grid .params.edits.${pname}_lab -row $numrows -column 0 -sticky ens
1180   grid .params.edits.${pname}_sel -row $numrows -column 1 -sticky wns
1181   menu .params.edits.${pname}_sel.menu -tearoff 0
1182   foreach item ${all_values} {
1183       .params.edits.${pname}_sel.menu add radio -label $item \
1184	-variable magic::${pname}_val -value $item \
1185	-command ".params.edits.${pname}_sel configure -text $item"
1186   }
1187   set magic::${pname}_val $value
1188}
1189
1190#----------------------------------------------------------
1191#  Add a selectable-list parameter to the gencell window
1192#  Unlike the routine above, it returns the index of the
1193#  selection, not the selection itself.  This is useful for
1194#  keying the selection to other parameter value lists.
1195#----------------------------------------------------------
1196
1197proc magic::add_selectindex {pname ptext all_values parameters {ival 0}} {
1198
1199   if [dict exists $parameters $pname] {
1200        set value [dict get $parameters $pname]
1201   } else {
1202       set value $ival
1203   }
1204
1205   set numrows [lindex [grid size .params.edits] 1]
1206   label .params.edits.${pname}_lab -text $ptext
1207   menubutton .params.edits.${pname}_sel -menu .params.edits.${pname}_sel.menu \
1208		-relief groove -text [lindex ${all_values} ${value}]
1209   grid .params.edits.${pname}_lab -row $numrows -column 0 -sticky ens
1210   grid .params.edits.${pname}_sel -row $numrows -column 1 -sticky wns
1211   menu .params.edits.${pname}_sel.menu -tearoff 0
1212   set idx 0
1213   foreach item ${all_values} {
1214       .params.edits.${pname}_sel.menu add radio -label $item \
1215	-variable magic::${pname}_val -value $idx \
1216	-command ".params.edits.${pname}_sel configure -text $item"
1217       incr idx
1218   }
1219   set magic::${pname}_val $value
1220}
1221
1222#-------------------------------------------------------------
1223# gencell_defaults ---
1224#
1225# Set all parameters for a device.  Start by calling the base
1226# device's default value list to generate a dictionary.  Then
1227# parse all values passed in 'parameters', overriding any
1228# defaults with the passed values.
1229#-------------------------------------------------------------
1230
1231proc magic::gencell_defaults {gencell_type library parameters} {
1232    set basedict [${library}::${gencell_type}_defaults]
1233    set newdict [dict merge $basedict $parameters]
1234    return $newdict
1235}
1236
1237#-------------------------------------------------------------
1238# Command tag callback on "select".  "select cell" should
1239# cause the parameter dialog window to update to reflect the
1240# selected cell.  If a cell is unselected, then revert to the
1241# default 'Create' window.
1242#-------------------------------------------------------------
1243
1244proc magic::gencell_update {{command {}}} {
1245    if {[info level] <= 1} {
1246        if {![catch {set state [wm state .params]}]} {
1247	    if {[wm state .params] == "normal"} {
1248		if {$command == "cell"} {
1249		    # If multiple devices are selected, choose the first in
1250		    # the list returned by "what -list".
1251		    set instname [lindex [lindex [lindex [what -list] 2] 0] 0]
1252		    magic::gencell_dialog $instname {} {} {}
1253		}
1254	    }
1255	}
1256    }
1257}
1258
1259#-------------------------------------------------------------
1260# gencell_dialog ---
1261#
1262# Create the dialog window for entering device parameters.  The
1263# general procedure then calls the dialog setup for the specific
1264# device.
1265#
1266# 1) If gname is NULL and gencell_type is set, then we
1267#    create a new cell of type gencell_type.
1268# 2) If gname is non-NULL, then we edit the existing
1269#    cell of type $gname.
1270# 3) If gname is non-NULL and gencell_type or library
1271#    is NULL or unspecified, then we derive the gencell_type
1272#    and library from the existing cell's property strings
1273#
1274# The device setup should be built using the API that defines
1275# these procedures:
1276#
1277# magic::add_entry	 Single text entry window
1278# magic::add_checkbox    Single checkbox
1279# magic::add_selectlist  Pull-down menu with list of selections
1280#
1281#-------------------------------------------------------------
1282
1283proc magic::gencell_dialog {instname gencell_type library parameters} {
1284   if {$gencell_type == {}} {
1285       # Revert to default state for the device that was previously
1286       # shown in the parameter window.
1287       if {![catch {set state [wm state .params]}]} {
1288          if {$instname == {}} {
1289	     set devstr [.params.title.lab1 cget -text]
1290	     if {$devstr == "Edit device:"} {
1291		 set gencell_type [.params.title.lab2 cget -text]
1292		 set library [.params.title.lab4 cget -text]
1293	     } else {
1294	         return
1295	     }
1296	  }
1297       }
1298   }
1299
1300   if {$instname != {}} {
1301      # Remove any array component of the instance name
1302      set instname [string map {\\ ""} $instname]
1303      if {[regexp {^(.*)\[[0-9,]+\]$} $instname valid instroot]} {
1304	 set instname $instroot
1305      }
1306      set gname [instance list celldef [subst $instname]]
1307      set gencell_type [cellname list property $gname gencell]
1308      if {$library == {}} {
1309	 set library [cellname list property $gname library]
1310      }
1311      if {$parameters == {}} {
1312	 set parameters [cellname list property $gname parameters]
1313      }
1314      if {$gencell_type == {} || $library == {}} {return}
1315
1316      if {$parameters == {}} {
1317	 set parameters [${library}::${gencell_type}_defaults]
1318      }
1319
1320      # If the default parameters contain "nocell", then set the
1321      # standard parameters for fixed devices from the instance
1322      if {[dict exists $parameters nocell]} {
1323	 select cell $instname
1324	 set arcount [array -list count]
1325	 set arpitch [array -list pitch]
1326
1327	 dict set parameters nx [expr [lindex $arcount 1] - [lindex $arcount 0] + 1]
1328	 dict set parameters ny [expr [lindex $arcount 3] - [lindex $arcount 2] + 1]
1329	 dict set parameters pitchx [lindex $arpitch 0]
1330	 dict set parameters pitchy [lindex $arpitch 1]
1331      }
1332      set ttext "Edit device"
1333      set itext $instname
1334   } else {
1335      set parameters [magic::gencell_defaults $gencell_type $library $parameters]
1336      set gname "(default)"
1337      set itext "(default)"
1338      set ttext "New device"
1339   }
1340
1341   # Destroy children, not the top-level window, or else window keeps
1342   # bouncing around every time something is changed.
1343   if {[catch {toplevel .params}]} {
1344       .params.title.lab1 configure -text "${ttext}:"
1345       .params.title.lab2 configure -text "$gencell_type"
1346       .params.title.lab4 configure -text "$library"
1347       .params.title.glab configure -foreground blue -text "$gname"
1348       .params.title.ient delete 0 end
1349       .params.title.ient insert 0 "$itext"
1350       foreach child [winfo children .params.edits] {
1351	  destroy $child
1352       }
1353       foreach child [winfo children .params.buttons] {
1354	  destroy $child
1355       }
1356   } else {
1357       frame .params.title
1358       label .params.title.lab1 -text "${ttext}:"
1359       label .params.title.lab2 -foreground blue -text "$gencell_type"
1360       label .params.title.lab3 -text "Library:"
1361       label .params.title.lab4 -foreground blue -text "$library"
1362       label .params.title.clab -text "Cellname:"
1363       label .params.title.glab -foreground blue -text "$gname"
1364       label .params.title.ilab -text "Instance:"
1365       entry .params.title.ient -foreground brown -background white
1366       .params.title.ient insert 0 "$itext"
1367       ttk::separator .params.sep
1368       frame .params.edits
1369       frame .params.buttons
1370
1371       grid .params.title.lab1 -padx 5 -row 0 -column 0
1372       grid .params.title.lab2 -padx 5 -row 0 -column 1 -sticky w
1373       grid .params.title.lab3 -padx 5 -row 0 -column 2
1374       grid .params.title.lab4 -padx 5 -row 0 -column 3 -sticky w
1375
1376       grid .params.title.clab -padx 5 -row 1 -column 0
1377       grid .params.title.glab -padx 5 -row 1 -column 1 -sticky w
1378       grid .params.title.ilab -padx 5 -row 1 -column 2
1379       grid .params.title.ient -padx 5 -row 1 -column 3 -sticky ew
1380       grid columnconfigure .params.title 3 -weight 1
1381
1382       pack .params.title -fill x -expand true
1383       pack .params.sep -fill x -expand true
1384       pack .params.edits -side top -fill both -expand true -ipadx 5
1385       pack .params.buttons -fill x
1386
1387       grid columnconfigure .params.edits 1 -weight 1
1388   }
1389
1390   if {$instname == {}} {
1391	button .params.buttons.apply -text "Create" -command \
1392		[subst {set inst \[magic::gencell_create \
1393		$gencell_type $library {}\] ; \
1394		magic::gencell_dialog \$inst $gencell_type $library {} }]
1395	button .params.buttons.okay -text "Create and Close" -command \
1396		[subst {set inst \[magic::gencell_create \
1397		$gencell_type $library {}\] ; \
1398		magic::gencell_dialog \$inst $gencell_type $library {} ; \
1399		destroy .params}]
1400   } else {
1401	button .params.buttons.apply -text "Apply" -command \
1402		"magic::gencell_change $instname $gencell_type $library {}"
1403	button .params.buttons.okay -text "Okay" -command \
1404		"magic::gencell_change $instname $gencell_type $library {} ;\
1405		 destroy .params"
1406   }
1407   button .params.buttons.reset -text "Reset" -command \
1408		"magic::gencell_dialog {} ${gencell_type} ${library} {}"
1409   button .params.buttons.close -text "Close" -command {destroy .params}
1410
1411   pack .params.buttons.apply -padx 5 -ipadx 5 -ipady 2 -side left
1412   pack .params.buttons.okay  -padx 5 -ipadx 5 -ipady 2 -side left
1413   pack .params.buttons.close -padx 5 -ipadx 5 -ipady 2 -side right
1414   pack .params.buttons.reset -padx 5 -ipadx 5 -ipady 2 -side right
1415
1416   # Invoke the callback procedure that creates the parameter entries
1417
1418   ${library}::${gencell_type}_dialog $parameters
1419
1420   # Add standard callback to all entry fields to run parameter bounds checks
1421   magic::add_check_callbacks $gencell_type $library
1422
1423   # Make sure the window is raised
1424   raise .params
1425}
1426
1427#-------------------------------------------------------------
1428