2017-04-25 14:41:48 +02:00
|
|
|
#-----------------------------------------------------
|
|
|
|
|
# Magic/TCL general-purpose toolkit procedures
|
|
|
|
|
#-----------------------------------------------------
|
|
|
|
|
# Tim Edwards
|
|
|
|
|
# February 11, 2007
|
|
|
|
|
# Revision 0
|
|
|
|
|
# December 15, 2016
|
|
|
|
|
# Revision 1
|
2020-10-30 16:20:19 +01:00
|
|
|
# October 29, 2020
|
|
|
|
|
# Revision 2 (names are hashed from properties)
|
2021-03-10 04:07:51 +01:00
|
|
|
# March 9, 2021
|
|
|
|
|
# Added spice-to-layout procedure
|
2026-03-04 19:34:31 +01:00
|
|
|
# March 4, 2026
|
|
|
|
|
# Changed to make use of new "units" command
|
2026-03-17 16:32:53 +01:00
|
|
|
# March 26, 2026
|
2026-03-17 16:42:16 +01:00
|
|
|
# Added behavior to handle ideal devices (resistor, capacitor,
|
|
|
|
|
# inductor)
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
# April 2, 2026
|
|
|
|
|
# Changed the hash to MurmurHash3, as the existing hash
|
|
|
|
|
# is prone to creating name collisions (rare, but not rare
|
|
|
|
|
# enough).
|
2017-04-25 14:41:48 +02:00
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
# Sets up the environment for a toolkit. The toolkit must
|
|
|
|
|
# supply a namespace that is the "library name". For each
|
|
|
|
|
# parameter-defined device ("gencell") type, the toolkit must
|
|
|
|
|
# supply five procedures:
|
|
|
|
|
#
|
|
|
|
|
# 1. ${library}::${gencell_type}_defaults {}
|
|
|
|
|
# 2. ${library}::${gencell_type}_convert {parameters}
|
|
|
|
|
# 3. ${library}::${gencell_type}_dialog {parameters}
|
|
|
|
|
# 4. ${library}::${gencell_type}_check {parameters}
|
|
|
|
|
# 5. ${library}::${gencell_type}_draw {parameters}
|
|
|
|
|
#
|
|
|
|
|
# The first defines the parameters used by the gencell, and
|
|
|
|
|
# declares default parameters to use when first generating
|
|
|
|
|
# the window that prompts for the device parameters prior to
|
|
|
|
|
# creating the device. The second converts between parameters
|
|
|
|
|
# in a SPICE netlist and parameters used by the dialog,
|
|
|
|
|
# performing units conversion and parameter name conversion as
|
|
|
|
|
# needed. The third builds the dialog window for entering
|
|
|
|
|
# device parameters. The fourth checks the parameters for
|
|
|
|
|
# legal values. The fifth draws the device.
|
|
|
|
|
#
|
|
|
|
|
# If "library" is not specified then it defaults to "toolkit".
|
|
|
|
|
# Otherwise, where specified, the name "gencell_fullname"
|
|
|
|
|
# is equivalent to "${library}::${gencell_type}"
|
|
|
|
|
#
|
|
|
|
|
# Each gencell is defined by cell properties as created by
|
|
|
|
|
# the "cellname property" command. Specific properties used
|
|
|
|
|
# by the toolkit are:
|
|
|
|
|
#
|
|
|
|
|
# library --- name of library (see above, default "toolkit")
|
|
|
|
|
# gencell --- base name of gencell (gencell_type, above)
|
|
|
|
|
# parameters --- list of gencell parameter-value pairs
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# Initialize toolkit menus to the wrapper window
|
|
|
|
|
|
|
|
|
|
global Opts
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
# Add a menu button to the Magic wrapper window for the toolkit
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_toolkit_menu {framename button_text {library toolkit}} {
|
|
|
|
|
menubutton ${framename}.titlebar.mbuttons.${library} \
|
|
|
|
|
-text $button_text \
|
|
|
|
|
-relief raised \
|
|
|
|
|
-menu ${framename}.titlebar.mbuttons.${library}.toolmenu \
|
|
|
|
|
-borderwidth 2
|
|
|
|
|
|
|
|
|
|
menu ${framename}.titlebar.mbuttons.${library}.toolmenu -tearoff 0
|
|
|
|
|
pack ${framename}.titlebar.mbuttons.${library} -side left
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------
|
|
|
|
|
# Add a menu item to the toolkit menu calling the default function
|
|
|
|
|
#-----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_toolkit_button {framename button_text gencell_type \
|
|
|
|
|
{library toolkit} args} {
|
|
|
|
|
set m ${framename}.titlebar.mbuttons.${library}.toolmenu
|
|
|
|
|
$m add command -label "$button_text" -command \
|
|
|
|
|
"magic::gencell $library::$gencell_type {} $args"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
# Add a menu item to the toolkit menu that calls the provided
|
|
|
|
|
# function
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_toolkit_command {framename button_text \
|
|
|
|
|
command {library toolkit} args} {
|
|
|
|
|
set m ${framename}.titlebar.mbuttons.${library}.toolmenu
|
|
|
|
|
$m add command -label "$button_text" -command "$command $args"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
# Add a separator to the toolkit menu
|
|
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_toolkit_separator {framename {library toolkit}} {
|
|
|
|
|
set m ${framename}.titlebar.mbuttons.${library}.toolmenu
|
|
|
|
|
$m add separator
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------
|
|
|
|
|
# Add "Ctrl-P" key callback for device selection
|
|
|
|
|
#-----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
magic::macro ^P "magic::gencell {} ; raise .params"
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# Add tag callback to select to update the gencell window
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
2025-10-08 23:11:27 +02:00
|
|
|
magic::tag add select "magic::gencell_update %1"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
# Supporting procedures for netlist_to_layout procedure
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# move_forward_by_width --
|
|
|
|
|
#
|
|
|
|
|
# Given an instance name, find the instance and position the
|
|
|
|
|
# cursor box at the right side of the instance.
|
|
|
|
|
|
|
|
|
|
proc magic::move_forward_by_width {instname} {
|
|
|
|
|
select cell $instname
|
2026-03-04 19:34:31 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
2021-03-10 04:07:51 +01:00
|
|
|
set anum [lindex [array -list count] 1]
|
|
|
|
|
set xpitch [lindex [array -list pitch] 0]
|
|
|
|
|
set bbox [box values]
|
|
|
|
|
set posx [lindex $bbox 0]
|
|
|
|
|
set posy [lindex $bbox 1]
|
|
|
|
|
set width [expr [lindex $bbox 2] - $posx]
|
|
|
|
|
set posx [expr $posx + $width + $xpitch * $anum]
|
2026-03-04 19:34:31 +01:00
|
|
|
box position ${posx} ${posy}
|
|
|
|
|
units {*}$curunits
|
2021-03-10 04:07:51 +01:00
|
|
|
return [lindex $bbox 3]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# get_and_move_inst --
|
|
|
|
|
#
|
|
|
|
|
# Given a cell name, creat an instance of the cell named "instname"
|
|
|
|
|
# at the current cursor box position. If option "anum" is given
|
|
|
|
|
# and > 1, then array the cell.
|
|
|
|
|
|
|
|
|
|
proc magic::get_and_move_inst {cellname instname {anum 1}} {
|
|
|
|
|
set newinst [getcell $cellname]
|
|
|
|
|
select cell $newinst
|
|
|
|
|
if {$newinst == ""} {return}
|
|
|
|
|
identify $instname
|
|
|
|
|
if {$anum > 1} {array 1 $anum}
|
2026-03-04 19:34:31 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
2021-03-10 04:07:51 +01:00
|
|
|
set bbox [box values]
|
|
|
|
|
set posx [lindex $bbox 2]
|
|
|
|
|
set posy [lindex $bbox 1]
|
2026-03-04 19:34:31 +01:00
|
|
|
box position ${posx} ${posy}
|
|
|
|
|
units {*}$curunits
|
2021-03-10 04:07:51 +01:00
|
|
|
return [lindex $bbox 3]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# create_new_pin --
|
|
|
|
|
#
|
|
|
|
|
# Create a new pin of size 1um x 1um at the current cursor box
|
|
|
|
|
# location. If "layer" is given, then create the pin on the
|
|
|
|
|
# given layer. Otherwise, the pin is created on the m1 layer.
|
|
|
|
|
|
|
|
|
|
proc magic::create_new_pin {pinname portnum {layer m1}} {
|
2026-03-04 19:34:31 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units microns
|
|
|
|
|
box size 1 1
|
2021-03-10 04:07:51 +01:00
|
|
|
paint $layer
|
2026-03-04 19:34:31 +01:00
|
|
|
label $pinname FreeSans 1 0 0 0 c $layer
|
2021-03-10 04:07:51 +01:00
|
|
|
port make $portnum
|
2026-03-04 19:34:31 +01:00
|
|
|
box move s 2
|
|
|
|
|
units {*}$curunits
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# generate_layout_add --
|
|
|
|
|
#
|
|
|
|
|
# Add a new subcircuit to a layout and seed it with components
|
|
|
|
|
# as found in the list "complist", and add pins according to the
|
|
|
|
|
# pin names in "subpins". Each entry in "complist" is a single
|
|
|
|
|
# device line from a SPICE file.
|
|
|
|
|
|
|
|
|
|
proc magic::generate_layout_add {subname subpins complist library} {
|
|
|
|
|
global PDKNAMESPACE
|
|
|
|
|
|
2026-03-04 19:34:31 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
|
|
|
|
|
2023-10-07 01:38:53 +02:00
|
|
|
# Create a new subcircuit.
|
2021-03-10 04:07:51 +01:00
|
|
|
load $subname -quiet
|
2023-10-07 01:38:53 +02:00
|
|
|
|
|
|
|
|
# In the case where subcells of circuit "subname" do not exist,
|
|
|
|
|
# delete the placeholders so that they can be regenerated.
|
|
|
|
|
|
|
|
|
|
set children [cellname list children $subname]
|
|
|
|
|
foreach child $children {
|
|
|
|
|
set flags [cellname flags $child]
|
|
|
|
|
foreach flag $flags {
|
|
|
|
|
if {$flag == "not-found"} {
|
|
|
|
|
set insts [cellname list instances $child]
|
|
|
|
|
foreach inst $insts {
|
|
|
|
|
select cell $inst
|
|
|
|
|
delete
|
|
|
|
|
}
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
cellname delete $child -noprompt
|
2023-10-07 01:38:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
box 0 0 0 0
|
|
|
|
|
|
2024-02-27 21:41:17 +01:00
|
|
|
# Generate pins. This routine can be made re-entrant; if a
|
|
|
|
|
# cell exists, then its contents are compared to the incoming
|
|
|
|
|
# netlist contents. Pins that exist in the netlist but not
|
|
|
|
|
# the layout are added. Pins that exist in the layout but not
|
|
|
|
|
# in the netlist are removed.
|
|
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
if {[llength $subpins] > 0} {
|
|
|
|
|
set pinlist [split $subpins]
|
2024-02-27 21:41:17 +01:00
|
|
|
|
|
|
|
|
# Determine if this cell already has pins.
|
|
|
|
|
set existpins []
|
|
|
|
|
for {set i [port first]} {$i > -1} {set i [port $i next]} {
|
|
|
|
|
lappend existpins [port $i name]
|
|
|
|
|
}
|
|
|
|
|
set addpins []
|
2021-03-10 04:07:51 +01:00
|
|
|
foreach pin $pinlist {
|
2024-02-27 21:41:17 +01:00
|
|
|
if {[lsearch $existpins $pin] == -1} {
|
|
|
|
|
lappend addpins $pin
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set rmpins []
|
|
|
|
|
foreach pin $existpins {
|
|
|
|
|
if {[lsearch $pinlist $pin] == -1} {
|
|
|
|
|
lappend rmpins $pin
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set i 0
|
|
|
|
|
foreach pin $addpins {
|
2021-03-10 04:07:51 +01:00
|
|
|
# Escape [ and ] in pin name
|
|
|
|
|
set pin_esc [string map {\[ \\\[ \] \\\]} $pin]
|
|
|
|
|
magic::create_new_pin $pin_esc $i
|
|
|
|
|
incr i
|
|
|
|
|
}
|
2024-02-27 21:41:17 +01:00
|
|
|
foreach pin $rmpins {
|
|
|
|
|
set llayer [goto $pin]
|
|
|
|
|
select area $llayer
|
|
|
|
|
erase label
|
|
|
|
|
}
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Set initial position for importing cells
|
|
|
|
|
box size 0 0
|
|
|
|
|
set posx 0
|
|
|
|
|
set posy [expr {round(3 / [cif scale out])}]
|
2026-03-04 19:34:31 +01:00
|
|
|
box position ${posx} ${posy}
|
2021-03-10 04:07:51 +01:00
|
|
|
|
2024-02-27 21:41:17 +01:00
|
|
|
# Find all instances in the circuit
|
|
|
|
|
select top cell
|
|
|
|
|
set compexist [instance list children]
|
|
|
|
|
set compcells []
|
|
|
|
|
# Find the matching cell defs
|
|
|
|
|
foreach comp $compexist {
|
|
|
|
|
lappend compcells [instance list celldef $comp]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Diagnostic
|
|
|
|
|
puts stdout "Cells already in the layout of ${subname}:"
|
|
|
|
|
for {set i 0} {$i < [llength $compexist]} {incr i} {
|
|
|
|
|
set comp [lindex $compexist $i]
|
|
|
|
|
set ccell [lindex $compcells $i]
|
|
|
|
|
puts stdout "${comp} (${ccell})"
|
|
|
|
|
}
|
|
|
|
|
flush stdout
|
|
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
# Seed layout with components
|
|
|
|
|
foreach comp $complist {
|
|
|
|
|
set pinlist {}
|
|
|
|
|
set paramlist {}
|
|
|
|
|
|
2021-03-11 19:30:28 +01:00
|
|
|
# NOTE: This routine deals with subcircuit calls and devices
|
2026-03-17 16:32:53 +01:00
|
|
|
# with models. There are two exceptions, for toolkits which
|
2026-03-17 16:42:16 +01:00
|
|
|
# wish to implement a way to generate unmodeled capacitors,
|
|
|
|
|
# resistors, or inductors based on value; for example, metal
|
|
|
|
|
# interdigitated capacitors. For those exceptions, the device
|
|
|
|
|
# value is recast as a parameter called "value", and the device
|
|
|
|
|
# is given a model "capacitor", "resistor", or "inductor",
|
|
|
|
|
# respectively.
|
2021-03-11 19:30:28 +01:00
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
# Parse SPICE line into pins, device name, and parameters. Make
|
|
|
|
|
# sure parameters incorporate quoted expressions as {} or ''.
|
|
|
|
|
|
|
|
|
|
set rest $comp
|
|
|
|
|
while {$rest != ""} {
|
|
|
|
|
if {[regexp -nocase {^[ \t]*[^= \t]+=[^=]+} $rest]} {
|
|
|
|
|
break
|
|
|
|
|
} elseif {[regexp -nocase {^[ \t]*([^ \t]+)[ \t]*(.*)$} $rest \
|
|
|
|
|
valid token rest]} {
|
|
|
|
|
lappend pinlist $token
|
|
|
|
|
} else {
|
|
|
|
|
set rest ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while {$rest != ""} {
|
2021-03-11 17:09:37 +01:00
|
|
|
if {[regexp -nocase {^([^= \t]+)=\'([^\']+)\'[ \t]*(.*)} $rest \
|
2021-03-10 04:07:51 +01:00
|
|
|
valid pname value rest]} {
|
|
|
|
|
lappend paramlist [list $pname "{$value}"]
|
2021-03-11 17:09:37 +01:00
|
|
|
} elseif {[regexp -nocase {^([^= \t]+)=\{([^\}]+)\}[ \t]*(.*)} $rest \
|
2021-03-10 04:07:51 +01:00
|
|
|
valid pname value rest]} {
|
|
|
|
|
lappend paramlist [list $pname "{$value}"]
|
2021-03-11 17:09:37 +01:00
|
|
|
} elseif {[regexp -nocase {^([^= \t]+)=([^= \t]+)[ \t]*(.*)} $rest \
|
2021-03-10 04:07:51 +01:00
|
|
|
valid pname value rest]} {
|
|
|
|
|
lappend paramlist [list $pname $value]
|
|
|
|
|
} else {
|
|
|
|
|
puts stderr "Error parsing line \"$comp\""
|
|
|
|
|
puts stderr "at: \"$rest\""
|
|
|
|
|
set rest ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {[llength $pinlist] < 2} {
|
|
|
|
|
puts stderr "Error: No device type found in line \"$comp\""
|
|
|
|
|
puts stderr "Tokens found are: \"$pinlist\""
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set instname [lindex $pinlist 0]
|
|
|
|
|
set devtype [lindex $pinlist end]
|
|
|
|
|
set pinlist [lrange $pinlist 0 end-1]
|
|
|
|
|
|
2026-03-17 16:32:53 +01:00
|
|
|
# Ideal device check: "devtype" will start with a digit.
|
2026-03-17 16:42:16 +01:00
|
|
|
# The instname will begin with "c", "r", or "l".
|
2026-03-17 16:32:53 +01:00
|
|
|
|
|
|
|
|
if {[regexp {^([0-9\.]+.*)} $devtype pval]} {
|
|
|
|
|
set comptype [string tolower [string range $instname 0 0]]
|
|
|
|
|
if {$comptype == "c"} {
|
|
|
|
|
lappend paramlist [list value $pval]
|
|
|
|
|
set devtype capacitor
|
|
|
|
|
} elseif {$comptype == "r"} {
|
|
|
|
|
lappend paramlist [list value $pval]
|
|
|
|
|
set devtype resistor
|
2026-03-17 16:42:16 +01:00
|
|
|
} elseif {$comptype == "l"} {
|
|
|
|
|
lappend paramlist [list value $pval]
|
|
|
|
|
set devtype inductor
|
2026-03-17 16:32:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
set mult 1
|
|
|
|
|
foreach param $paramlist {
|
|
|
|
|
set parmname [lindex $param 0]
|
|
|
|
|
set parmval [lindex $param 1]
|
|
|
|
|
if {[string toupper $parmname] == "M"} {
|
|
|
|
|
if {[catch {set mult [expr {int($parmval)}]}]} {
|
|
|
|
|
set mult [expr [string trim $parmval "'"]]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 17:18:42 +01:00
|
|
|
# Check if devtype has routines by looking for ${devtype}_defaults.
|
|
|
|
|
# If not found, do a case-insensitive check against all devices
|
|
|
|
|
# before deciding that devtype is a subcircuit and not a device.
|
|
|
|
|
# If found by case-insensitive check, then change the device name
|
|
|
|
|
# to the one used in the library.
|
|
|
|
|
|
|
|
|
|
if {$library != ""} {
|
|
|
|
|
set alldevices [namespace eval ::${library} {info procs}]
|
|
|
|
|
} else {
|
|
|
|
|
set alldevices [namespace eval ::${PDKNAMESPACE} {info procs}]
|
|
|
|
|
}
|
|
|
|
|
set devdefault [lsearch $alldevices ${devtype}_defaults]
|
|
|
|
|
if {$devdefault == -1} {
|
|
|
|
|
set devdefault [lsearch -nocase $alldevices ${devtype}_defaults]
|
|
|
|
|
if {$devdefault != -1} {
|
|
|
|
|
set devprocname [lindex $alldevices $devdefault]
|
|
|
|
|
set devproclist [split $devprocname "_"]
|
|
|
|
|
set devtype [lindex $devproclist 0]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 04:07:51 +01:00
|
|
|
# devtype is assumed to be in library. If not, it will attempt to
|
|
|
|
|
# use 'getcell' on devtype. Note that this code depends on the
|
|
|
|
|
# PDK setting varible PDKNAMESPACE.
|
|
|
|
|
|
|
|
|
|
if {$library != ""} {
|
|
|
|
|
set libdev ${library}::${devtype}
|
|
|
|
|
} else {
|
|
|
|
|
set libdev ${PDKNAMESPACE}::${devtype}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set outparts {}
|
2024-04-01 22:42:10 +02:00
|
|
|
# Escape any brackets in the instance name, since it gets passed to "eval"
|
|
|
|
|
set minstname [string map {[ \\\[ ] \\\]} $instname]
|
|
|
|
|
lappend outparts "magic::gencell $libdev $minstname"
|
2021-03-10 04:07:51 +01:00
|
|
|
|
|
|
|
|
# Output all parameters. Parameters not used by the toolkit are
|
|
|
|
|
# ignored by the toolkit.
|
|
|
|
|
|
|
|
|
|
lappend outparts "-spice"
|
|
|
|
|
foreach param $paramlist {
|
|
|
|
|
lappend outparts [string tolower [lindex $param 0]]
|
|
|
|
|
lappend outparts [lindex $param 1]
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 21:41:17 +01:00
|
|
|
set inst_exists false
|
|
|
|
|
set existidx [lsearch $compexist $instname]
|
|
|
|
|
if {$existidx > -1} {
|
|
|
|
|
set existcell [lindex $compcells $existidx]
|
|
|
|
|
if {$devtype == $existcell} {
|
|
|
|
|
set inst_exists true
|
|
|
|
|
} else {
|
|
|
|
|
# Instance name exists but is the wrong device type.
|
|
|
|
|
# To do: Attempt to maintain the same position
|
|
|
|
|
pushbox
|
|
|
|
|
select cell $instname
|
|
|
|
|
delete
|
|
|
|
|
popbox
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {$inst_exists == false} {
|
|
|
|
|
if {[catch {eval [join $outparts]}]} {
|
|
|
|
|
# Assume this is not a gencell, and get an instance.
|
|
|
|
|
magic::get_and_move_inst $devtype $instname $mult
|
|
|
|
|
} else {
|
|
|
|
|
# Move forward for next gencell
|
|
|
|
|
magic::move_forward_by_width $instname
|
|
|
|
|
}
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
save $subname
|
2026-03-04 19:34:31 +01:00
|
|
|
units {*}$curunits
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
# Wrapper for generating an initial layout from a SPICE netlist
|
|
|
|
|
# using the defined PDK toolkit procedures
|
|
|
|
|
#
|
|
|
|
|
# "netfile" is the name of a SPICE netlist
|
|
|
|
|
# "library" is the name of the PDK library namespace
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::netlist_to_layout {netfile library} {
|
|
|
|
|
|
|
|
|
|
if {![file exists $netfile]} {
|
|
|
|
|
puts stderr "No such file $netfile"
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Read data from file. Remove comment lines and concatenate
|
|
|
|
|
# continuation lines.
|
|
|
|
|
|
|
|
|
|
set topname [file rootname [file tail $netfile]]
|
|
|
|
|
puts stdout "Creating layout from [file tail $netfile]"
|
|
|
|
|
|
|
|
|
|
if {[file ext $netfile] == ".cdl"} {
|
|
|
|
|
set is_cdl true
|
|
|
|
|
} else {
|
|
|
|
|
set is_cdl false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if [catch {open $netfile r} fnet] {
|
|
|
|
|
puts stderr "Error: Cannot open file \"$netfile\" for reading."
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
set insub false
|
2021-03-11 19:30:28 +01:00
|
|
|
set incmd false
|
2021-03-10 04:07:51 +01:00
|
|
|
set subname ""
|
|
|
|
|
set subpins ""
|
|
|
|
|
set complist {}
|
|
|
|
|
set toplist {}
|
|
|
|
|
|
|
|
|
|
# suspendall
|
|
|
|
|
|
2021-03-11 19:30:28 +01:00
|
|
|
set ignorekeys {.global .ic .option .end}
|
|
|
|
|
|
2023-11-15 17:46:22 +01:00
|
|
|
# Parse the file once and find all subcircuits being defined, to
|
|
|
|
|
# catch any issues with subcircuits being used before they are
|
|
|
|
|
# defined.
|
|
|
|
|
set allsubs {}
|
|
|
|
|
foreach line $fdata {
|
|
|
|
|
set ftokens [split $line]
|
|
|
|
|
set keyword [string tolower [lindex $ftokens 0]]
|
|
|
|
|
if {$keyword == ".subckt"} {
|
|
|
|
|
set subname [lindex $ftokens 1]
|
|
|
|
|
lappend allsubs $subname
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
# Generate list of pre-existing top level cells
|
|
|
|
|
set existing [cellname list top]
|
|
|
|
|
# Pre-generate placeholders for all subcircuits.
|
|
|
|
|
set curtop [cellname list self]
|
|
|
|
|
|
|
|
|
|
foreach subckt $allsubs {
|
2024-02-27 21:41:17 +01:00
|
|
|
# Diagnostic output
|
|
|
|
|
puts stdout "Pre-generating subcircuit $subckt placeholder"
|
2023-11-15 17:46:22 +01:00
|
|
|
load $subckt -silent
|
|
|
|
|
}
|
|
|
|
|
load $curtop
|
|
|
|
|
|
|
|
|
|
# Parse the file and process all lines
|
2021-03-10 04:07:51 +01:00
|
|
|
foreach line $fdata {
|
2021-03-11 19:30:28 +01:00
|
|
|
if {$incmd} {
|
|
|
|
|
if {[regexp -nocase {^[ \t]*\.endc} $line]} {
|
|
|
|
|
set incmd false
|
|
|
|
|
}
|
|
|
|
|
} elseif {! $insub} {
|
2021-03-10 04:07:51 +01:00
|
|
|
set ftokens [split $line]
|
|
|
|
|
set keyword [string tolower [lindex $ftokens 0]]
|
|
|
|
|
|
2021-03-11 19:30:28 +01:00
|
|
|
if {[lsearch $ignorekeys $keyword] != -1} {
|
|
|
|
|
continue
|
|
|
|
|
} elseif {$keyword == ".command"} {
|
|
|
|
|
set incmd true
|
|
|
|
|
} elseif {$keyword == ".subckt"} {
|
2021-03-10 04:07:51 +01:00
|
|
|
set subname [lindex $ftokens 1]
|
|
|
|
|
set subpins [lrange $ftokens 2 end]
|
|
|
|
|
set insub true
|
2026-03-17 16:42:16 +01:00
|
|
|
} elseif {[regexp -nocase {^[xmcrldq]([^ \t]+)[ \t](.*)$} $line \
|
2021-03-10 04:07:51 +01:00
|
|
|
valid instname rest]} {
|
|
|
|
|
lappend toplist $line
|
2021-03-11 19:30:28 +01:00
|
|
|
} elseif {[regexp -nocase {^[ivbe]([^ \t]+)[ \t](.*)$} $line \
|
|
|
|
|
valid instname rest]} {
|
|
|
|
|
# These are testbench devices and should be ignored
|
|
|
|
|
continue
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if {[regexp -nocase {^[ \t]*\.ends} $line]} {
|
|
|
|
|
set insub false
|
|
|
|
|
magic::generate_layout_add $subname $subpins $complist $library
|
|
|
|
|
set subname ""
|
|
|
|
|
set subpins ""
|
|
|
|
|
set complist {}
|
2026-03-17 16:42:16 +01:00
|
|
|
} elseif {[regexp -nocase {^[xmcrldq]([^ \t]+)[ \t](.*)$} $line \
|
2021-03-10 04:07:51 +01:00
|
|
|
valid instname rest]} {
|
|
|
|
|
lappend complist $line
|
2021-03-11 19:30:28 +01:00
|
|
|
} elseif {[regexp -nocase {^[ivbe]([^ \t]+)[ \t](.*)$} $line \
|
|
|
|
|
valid instname rest]} {
|
|
|
|
|
# These are testbench devices and should be ignored
|
|
|
|
|
continue
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Add in any top-level components (not in subcircuits)
|
|
|
|
|
if {[llength $toplist] > 0} {
|
2023-11-15 17:46:22 +01:00
|
|
|
# Make sure the top level cell is loaded before adding top-level
|
|
|
|
|
# components.
|
|
|
|
|
load $curtop
|
2021-03-10 04:07:51 +01:00
|
|
|
magic::generate_layout_add $topname "" $toplist $library
|
2023-11-15 17:46:22 +01:00
|
|
|
} else {
|
|
|
|
|
# There was no top level, so load the first new top cell that
|
|
|
|
|
# was generated by the import.
|
|
|
|
|
set allcells [cellname list top]
|
|
|
|
|
foreach cell $allcells {
|
|
|
|
|
if {[lsearch $existing $cell] < 0} {
|
|
|
|
|
load $cell
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-10 04:07:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# resumeall
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell
|
|
|
|
|
#
|
|
|
|
|
# Main routine to call a cell from either a menu button or
|
|
|
|
|
# from a script or command line. The name of the device
|
|
|
|
|
# is required, followed by the name of the instance, followed
|
|
|
|
|
# by an optional list of parameters. Handling depends on
|
|
|
|
|
# instname and args:
|
|
|
|
|
#
|
|
|
|
|
# gencell_name is either the name of an instance or the name
|
|
|
|
|
# of the gencell in the form <library>::<device>.
|
|
|
|
|
#
|
|
|
|
|
# name args action
|
|
|
|
|
#-----------------------------------------------------------------
|
|
|
|
|
# none empty interactive, new device w/defaults
|
|
|
|
|
# none specified interactive, new device w/parameters
|
|
|
|
|
# instname empty interactive, edit device
|
|
|
|
|
# instname specified non-interactive, change device
|
|
|
|
|
# device empty non-interactive, new device w/defaults
|
|
|
|
|
# device specified non-interactive, new device w/parameters
|
|
|
|
|
#
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# Also, if instname is empty and gencell_name is not specified,
|
|
|
|
|
# and if a device is selected in the layout, then gencell
|
|
|
|
|
# behaves like line 3 above (instname exists, args is empty).
|
|
|
|
|
# Note that macro Ctrl-P calls gencell this way. If gencell_name
|
|
|
|
|
# is not specified and nothing is selected, then gencell{}
|
|
|
|
|
# does nothing.
|
|
|
|
|
#
|
|
|
|
|
# "args" must be a list of the cell parameters in key:value pairs,
|
|
|
|
|
# and an odd number is not legal; the exception is that if the
|
|
|
|
|
# first argument is "-spice", then the list of parameters is
|
|
|
|
|
# expected to be in the format used in a SPICE netlist, and the
|
|
|
|
|
# parameter names and values will be treated accordingly.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell {gencell_name {instname {}} args} {
|
|
|
|
|
|
|
|
|
|
# Pull "-spice" out of args, if it is the first argument
|
|
|
|
|
if {[lindex $args 0] == "-spice"} {
|
|
|
|
|
set spicemode 1
|
|
|
|
|
set args [lrange $args 1 end]
|
|
|
|
|
} else {
|
|
|
|
|
set spicemode 0
|
|
|
|
|
}
|
|
|
|
|
set argpar [dict create {*}$args]
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if {$gencell_name == {}} {
|
2017-04-25 14:41:48 +02:00
|
|
|
# Find selected item (to-do: handle multiple selections)
|
|
|
|
|
|
|
|
|
|
set wlist [what -list]
|
|
|
|
|
set clist [lindex $wlist 2]
|
|
|
|
|
set ccell [lindex $clist 0]
|
|
|
|
|
set ginst [lindex $ccell 0]
|
|
|
|
|
set gname [lindex $ccell 1]
|
|
|
|
|
set library [cellname list property $gname library]
|
|
|
|
|
if {$library == {}} {
|
|
|
|
|
set library toolkit
|
|
|
|
|
}
|
|
|
|
|
set gencell_type [cellname list property $gname gencell]
|
|
|
|
|
if {$gencell_type == {}} {
|
|
|
|
|
if {![regexp {^(.*)_[0-9]*$} $gname valid gencell_type]} {
|
|
|
|
|
# Error message
|
|
|
|
|
error "No gencell device is selected!"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
# need to incorporate argpar?
|
|
|
|
|
set parameters [cellname list property $gname parameters]
|
|
|
|
|
set parameters [magic::gencell_defaults $gencell_type $library $parameters]
|
|
|
|
|
magic::gencell_dialog $ginst $gencell_type $library $parameters
|
|
|
|
|
} else {
|
|
|
|
|
# Parse out library name from gencell_name, otherwise default
|
|
|
|
|
# library is assumed to be "toolkit".
|
|
|
|
|
if {[regexp {^([^:]+)::([^:]+)$} $gencell_name valid library gencell_type] \
|
|
|
|
|
== 0} {
|
|
|
|
|
set library "toolkit"
|
|
|
|
|
set gencell_type $gencell_name
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 18:27:14 +01:00
|
|
|
# Check that the device exists as a gencell, or else return an error
|
|
|
|
|
if {[namespace eval ::${library} info commands ${gencell_type}_convert] == ""} {
|
|
|
|
|
error "No import routine for ${library} library cell ${gencell_type}!"
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if {$instname == {}} {
|
|
|
|
|
# Case: Interactive, new device with parameters in args (if any)
|
|
|
|
|
if {$spicemode == 1} {
|
|
|
|
|
# Legal not to have a *_convert routine
|
|
|
|
|
if {[info commands ${library}::${gencell_type}_convert] != ""} {
|
|
|
|
|
set argpar [${library}::${gencell_type}_convert $argpar]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
set parameters [magic::gencell_defaults $gencell_type $library $argpar]
|
|
|
|
|
magic::gencell_dialog {} $gencell_type $library $parameters
|
|
|
|
|
} else {
|
|
|
|
|
# Check if instance exists or not in the cell
|
|
|
|
|
set cellname [instance list celldef $instname]
|
|
|
|
|
|
|
|
|
|
if {$cellname != ""} {
|
|
|
|
|
# Case: Change existing instance, parameters in args (if any)
|
|
|
|
|
select cell $instname
|
2023-10-06 23:51:40 +02:00
|
|
|
set devparms [cellname list property $cellname parameters]
|
2017-04-25 14:41:48 +02:00
|
|
|
set parameters [magic::gencell_defaults $gencell_type $library $devparms]
|
|
|
|
|
if {[dict exists $parameters nocell]} {
|
|
|
|
|
set arcount [array -list count]
|
|
|
|
|
set arpitch [array -list pitch]
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
dict set parameters nx [lindex $arcount 1]
|
|
|
|
|
dict set parameters ny [lindex $arcount 3]
|
|
|
|
|
dict set parameters pitchx $delx
|
|
|
|
|
dict set parameters pitchy $dely
|
|
|
|
|
}
|
|
|
|
|
if {[dict size $argpar] == 0} {
|
|
|
|
|
# No changes entered on the command line, so start dialog
|
|
|
|
|
magic::gencell_dialog $instname $gencell_type $library $parameters
|
|
|
|
|
} else {
|
|
|
|
|
# Apply specified changes without invoking the dialog
|
|
|
|
|
if {$spicemode == 1} {
|
|
|
|
|
set argpar [${library}::${gencell_type}_convert $argpar]
|
|
|
|
|
}
|
|
|
|
|
set parameters [dict merge $parameters $argpar]
|
|
|
|
|
magic::gencell_change $instname $gencell_type $library $parameters
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
# Case: Non-interactive, create new device with parameters
|
|
|
|
|
# in args (if any)
|
|
|
|
|
if {$spicemode == 1} {
|
|
|
|
|
set argpar [${library}::${gencell_type}_convert $argpar]
|
|
|
|
|
}
|
|
|
|
|
set parameters [magic::gencell_defaults $gencell_type $library $argpar]
|
|
|
|
|
set inst_defaultname [magic::gencell_create \
|
|
|
|
|
$gencell_type $library $parameters]
|
|
|
|
|
select cell $inst_defaultname
|
|
|
|
|
identify $instname
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-11 18:27:14 +01:00
|
|
|
return 0
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_makecell
|
|
|
|
|
#
|
|
|
|
|
# This is a variation of magic::gencell and is used to generate
|
|
|
|
|
# a cell and return the cell name without creating or placing
|
|
|
|
|
# an instance.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_makecell {gencell_fullname args} {
|
|
|
|
|
|
|
|
|
|
set argpar [dict create {*}$args]
|
|
|
|
|
set gencell_basename [namespace tail $gencell_fullname]
|
|
|
|
|
set library [namespace qualifiers $gencell_fullname]
|
|
|
|
|
set parameters [magic::gencell_defaults $gencell_basename $library $argpar]
|
|
|
|
|
set gsuffix [magic::get_gencell_hash ${parameters}]
|
|
|
|
|
set gname ${gencell_basename}_${gsuffix}
|
|
|
|
|
suspendall
|
|
|
|
|
cellname create $gname
|
|
|
|
|
pushstack $gname
|
|
|
|
|
if {[catch {${library}::${gencell_basename}_draw $parameters} drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
|
|
|
|
property library $library
|
|
|
|
|
property gencell $gencell_basename
|
|
|
|
|
property parameters $parameters
|
|
|
|
|
popstack
|
|
|
|
|
resumeall
|
|
|
|
|
return $gname
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_getparams
|
|
|
|
|
#
|
|
|
|
|
# Go through the parameter window and collect all of the
|
|
|
|
|
# named parameters and their values. Return the result as
|
|
|
|
|
# a dictionary.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_getparams {} {
|
|
|
|
|
set parameters [dict create]
|
2023-03-12 21:57:47 +01:00
|
|
|
set slist [grid slaves .params.body.area.edits]
|
2017-04-25 14:41:48 +02:00
|
|
|
foreach s $slist {
|
2023-09-29 15:38:48 +02:00
|
|
|
if {[regexp {^\.params\.body\.area\.edits\.(.*)_ent$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [subst \$magic::${pname}_val]
|
2023-09-29 15:38:48 +02:00
|
|
|
} elseif {[regexp {^\.params\.body\.area\.edits\.(.*)_chk$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [subst \$magic::${pname}_val]
|
2023-09-29 15:38:48 +02:00
|
|
|
} elseif {[regexp {^\.params\.body\.area\.edits\.(.*)_sel$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [subst \$magic::${pname}_val]
|
|
|
|
|
}
|
|
|
|
|
dict set parameters $pname $value
|
|
|
|
|
}
|
|
|
|
|
return $parameters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_setparams
|
|
|
|
|
#
|
|
|
|
|
# Fill in values in the dialog from a set of parameters
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_setparams {parameters} {
|
|
|
|
|
if {[catch {set state [wm state .params]}]} {return}
|
2023-03-12 21:57:47 +01:00
|
|
|
set slist [grid slaves .params.body.area.edits]
|
2017-04-25 14:41:48 +02:00
|
|
|
foreach s $slist {
|
2023-03-12 21:57:47 +01:00
|
|
|
# ignore .params.body.area.edits.gencell_sel, as that does not exist in the
|
2020-10-30 16:20:19 +01:00
|
|
|
# parameters dictionary
|
2023-03-12 21:57:47 +01:00
|
|
|
if {$s == ".params.body.area.edits.gencell_sel"} {continue}
|
|
|
|
|
if {[regexp {^.params.body.area.edits.(.*)_ent$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
set magic::${pname}_val $value
|
2023-03-12 21:57:47 +01:00
|
|
|
} elseif {[regexp {^.params.body.area.edits.(.*)_chk$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
set magic::${pname}_val $value
|
2023-03-12 21:57:47 +01:00
|
|
|
} elseif {[regexp {^.params.body.area.edits.(.*)_sel$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
set magic::${pname}_val $value
|
2023-03-12 21:57:47 +01:00
|
|
|
.params.body.area.edits.${pname}_sel configure -text $value
|
|
|
|
|
} elseif {[regexp {^.params.body.area.edits.(.*)_txt$} $s valid pname] != 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
if {[dict exists $parameters $pname]} {
|
|
|
|
|
set value [dict get $parameters $pname]
|
2023-03-12 21:57:47 +01:00
|
|
|
.params.body.area.edits.${pname}_txt configure -text $value
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_change
|
|
|
|
|
#
|
2020-10-30 16:20:19 +01:00
|
|
|
# Recreate a gencell with new parameters. Note that because
|
|
|
|
|
# each cellname is uniquely identified by the (hashed) set
|
|
|
|
|
# of parameters, changing parameters effectively means
|
|
|
|
|
# creating a new cell. If the original cell has parents
|
|
|
|
|
# other than the parent of the instance being changed, then
|
|
|
|
|
# it is retained; otherwise, it is deleted. The instance
|
|
|
|
|
# being edited gets replaced by an instance of the new cell.
|
|
|
|
|
# If the instance name was the cellname + suffix, then the
|
|
|
|
|
# instance name is regenerated. Otherwise, the instance
|
|
|
|
|
# name is retained.
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_change {instname gencell_type library parameters} {
|
|
|
|
|
global Opts
|
|
|
|
|
suspendall
|
|
|
|
|
|
|
|
|
|
set newinstname $instname
|
|
|
|
|
if {$parameters == {}} {
|
|
|
|
|
# Get device defaults
|
|
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
|
|
|
|
# Pull user-entered values from dialog
|
|
|
|
|
set parameters [dict merge $pdefaults [magic::gencell_getparams]]
|
|
|
|
|
}
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
|
|
|
|
|
# Attempt to set the new instance name as specified in the dialog.
|
|
|
|
|
# If the entry is "(default)" or if there is a name collision,
|
|
|
|
|
# revert the name to the original name.
|
|
|
|
|
|
|
|
|
|
set newinstname [.params.title.ient get]
|
|
|
|
|
if {$newinstname == "(default)"} {set newinstname $instname}
|
|
|
|
|
if {[instance list exists $newinstname] != ""} {set newinstname $instname}
|
|
|
|
|
|
2019-11-21 21:48:24 +01:00
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
# Setting special parameter "gencell" forces the gencell to change type
|
|
|
|
|
set gencell_type [dict get $parameters gencell]
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
|
|
|
|
|
checkerr]} {
|
|
|
|
|
puts stderr $checkerr
|
|
|
|
|
}
|
|
|
|
|
magic::gencell_setparams $parameters
|
2019-11-21 21:48:24 +01:00
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
set parameters [dict remove $parameters gencell]
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
set old_gname [instance list celldef $instname]
|
|
|
|
|
set gsuffix [magic::get_gencell_hash ${parameters}]
|
|
|
|
|
set gname ${gencell_type}_${gsuffix}
|
|
|
|
|
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
# Handle instance name changing first. If no parameters changed, then
|
|
|
|
|
# we're done.
|
|
|
|
|
if {$newinstname != $instname} {
|
|
|
|
|
identify $newinstname
|
|
|
|
|
# The buttons "Apply" and "Okay" need to be changed for the new
|
|
|
|
|
# instance name
|
|
|
|
|
catch {.params.buttons.apply config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {}"}
|
|
|
|
|
catch {.params.buttons.okay config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {} ;\
|
|
|
|
|
destroy .params"}
|
|
|
|
|
set instname $newinstname
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
# Guard against instance having been deleted. Also, if parameters have not
|
|
|
|
|
# changed as evidenced by the cell suffix not changing, then nothing further
|
|
|
|
|
# needs to be done.
|
|
|
|
|
if {$gname == "" || $gname == $old_gname} {
|
|
|
|
|
resumeall
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 21:59:21 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
2020-10-30 16:20:19 +01:00
|
|
|
set savebox [box values]
|
|
|
|
|
|
|
|
|
|
catch {setpoint 0 0 $Opts(focus)}
|
|
|
|
|
if [dict exists $parameters nocell] {
|
|
|
|
|
select cell $instname
|
|
|
|
|
set abox [instance list abutment]
|
|
|
|
|
delete
|
|
|
|
|
if {$abox != ""} {box values {*}$abox}
|
|
|
|
|
if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \
|
|
|
|
|
drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
|
|
|
|
select cell $newinst
|
|
|
|
|
} elseif {[cellname list exists $gname] != 0} {
|
|
|
|
|
# If there is already a cell of this type then it is only required to
|
|
|
|
|
# remove the instance and replace it with an instance of the cell
|
|
|
|
|
select cell $instname
|
|
|
|
|
# check rotate/flip before replacing and replace with same
|
|
|
|
|
set orient [instance list orientation]
|
|
|
|
|
set abox [instance list abutment]
|
|
|
|
|
delete
|
|
|
|
|
|
|
|
|
|
if {$abox != ""} {box values {*}$abox}
|
|
|
|
|
set newinstname [getcell $gname $orient]
|
|
|
|
|
select cell $newinstname
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
set origname $newinstname
|
2020-10-30 16:20:19 +01:00
|
|
|
expand
|
|
|
|
|
|
|
|
|
|
# If the old instance name was not formed from the old cell name,
|
|
|
|
|
# then keep the old instance name.
|
|
|
|
|
if {[string first $old_gname $instname] != 0} {
|
|
|
|
|
set newinstname $instname
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
} else {
|
|
|
|
|
# The buttons "Apply" and "Okay" need to be changed for the new
|
|
|
|
|
# instance name
|
|
|
|
|
catch {.params.buttons.apply config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {}"}
|
|
|
|
|
catch {.params.buttons.okay config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {} ;\
|
|
|
|
|
destroy .params"}
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {[cellname list parents $old_gname] == []} {
|
|
|
|
|
# If the original cell has no intances left, delete it. It can
|
|
|
|
|
# be regenerated if and when necessary.
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
cellname delete $old_gname -noprompt
|
|
|
|
|
select cell $origname
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
select cell $instname
|
|
|
|
|
set orient [instance list orientation]
|
|
|
|
|
set abox [instance list abutment]
|
|
|
|
|
delete
|
|
|
|
|
|
|
|
|
|
# There is no cell of this name, so generate one and instantiate it.
|
|
|
|
|
if {$abox != ""} {box values {*}$abox}
|
|
|
|
|
set newinstname [magic::gencell_create $gencell_type $library $parameters $orient]
|
|
|
|
|
select cell $newinstname
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
set origname $newinstname
|
2020-10-30 16:20:19 +01:00
|
|
|
|
|
|
|
|
# If the old instance name was not formed from the old cell name,
|
|
|
|
|
# then keep the old instance name.
|
|
|
|
|
if {[string first $old_gname $instname] != 0} {
|
|
|
|
|
set newinstname $instname
|
2021-02-23 15:58:52 +01:00
|
|
|
} else {
|
|
|
|
|
# The buttons "Apply" and "Okay" need to be changed for the new
|
|
|
|
|
# instance name
|
|
|
|
|
catch {.params.buttons.apply config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {}"}
|
|
|
|
|
catch {.params.buttons.okay config -command \
|
|
|
|
|
"magic::gencell_change $newinstname $gencell_type $library {} ;\
|
|
|
|
|
destroy .params"}
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
|
|
|
|
|
# If the old cell is not used anywhere, delete it
|
|
|
|
|
if {[cellname list parents $old_gname] == []} {
|
|
|
|
|
# If the original cell has no intances left, delete it. It can
|
|
|
|
|
# be regenerated if and when necessary.
|
|
|
|
|
cellname delete $old_gname -noprompt
|
|
|
|
|
select cell $origname
|
|
|
|
|
}
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
|
|
|
|
identify $newinstname
|
|
|
|
|
eval "box values $savebox"
|
2026-02-24 22:40:07 +01:00
|
|
|
units {*}$curunits
|
2020-10-30 16:20:19 +01:00
|
|
|
|
|
|
|
|
# Update window
|
|
|
|
|
if {$gname != $old_gname} {
|
|
|
|
|
catch {.params.title.glab configure -text "$gname"}
|
|
|
|
|
}
|
|
|
|
|
if {$instname != $newinstname} {
|
|
|
|
|
catch {.params.title.ient delete 0 end}
|
|
|
|
|
catch {.params.title.ient insert 0 "$newinstname"}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resumeall
|
2025-10-28 20:10:03 +01:00
|
|
|
drc check ;# force a DRC update if DRC is on.
|
2020-10-30 16:20:19 +01:00
|
|
|
redraw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_change_orig
|
|
|
|
|
#
|
|
|
|
|
# Original version: Redraw a gencell with new parameters,
|
|
|
|
|
# without changing the cell itself.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_change_orig {instname gencell_type library parameters} {
|
|
|
|
|
global Opts
|
|
|
|
|
suspendall
|
|
|
|
|
|
|
|
|
|
set newinstname $instname
|
|
|
|
|
if {$parameters == {}} {
|
|
|
|
|
# Get device defaults
|
|
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
|
|
|
|
# Pull user-entered values from dialog
|
|
|
|
|
set parameters [dict merge $pdefaults [magic::gencell_getparams]]
|
|
|
|
|
set newinstname [.params.title.ient get]
|
|
|
|
|
if {$newinstname == "(default)"} {set newinstname $instname}
|
|
|
|
|
if {$newinstname == $instname} {set newinstname $instname}
|
|
|
|
|
if {[instance list exists $newinstname] != ""} {set newinstname $instname}
|
|
|
|
|
}
|
|
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
# Setting special parameter "gencell" forces the gencell to change type
|
|
|
|
|
set gencell_type [dict get $parameters gencell]
|
|
|
|
|
}
|
|
|
|
|
if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
|
|
|
|
|
checkerr]} {
|
|
|
|
|
puts stderr $checkerr
|
|
|
|
|
}
|
|
|
|
|
magic::gencell_setparams $parameters
|
|
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
set parameters [dict remove $parameters gencell]
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
set gname [instance list celldef $instname]
|
|
|
|
|
|
|
|
|
|
# Guard against instance having been deleted
|
|
|
|
|
if {$gname == ""} {
|
|
|
|
|
resumeall
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 21:59:21 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
2017-04-25 14:41:48 +02:00
|
|
|
set savebox [box values]
|
|
|
|
|
|
|
|
|
|
catch {setpoint 0 0 $Opts(focus)}
|
|
|
|
|
if [dict exists $parameters nocell] {
|
|
|
|
|
select cell $instname
|
|
|
|
|
delete
|
|
|
|
|
if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \
|
|
|
|
|
drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
|
|
|
|
select cell $newinst
|
|
|
|
|
} else {
|
|
|
|
|
pushstack $gname
|
|
|
|
|
select cell
|
|
|
|
|
tech unlock *
|
|
|
|
|
erase *
|
|
|
|
|
if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
|
|
|
|
property parameters $parameters
|
2019-11-21 21:48:24 +01:00
|
|
|
property gencell ${gencell_type}
|
2017-04-25 14:41:48 +02:00
|
|
|
tech revert
|
|
|
|
|
popstack
|
|
|
|
|
select cell $instname
|
|
|
|
|
}
|
|
|
|
|
identify $newinstname
|
|
|
|
|
eval "box values $savebox"
|
2026-02-24 22:40:07 +01:00
|
|
|
units {*}$curunits
|
2017-04-25 14:41:48 +02:00
|
|
|
resumeall
|
|
|
|
|
redraw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# Assign a unique name for a gencell
|
|
|
|
|
#
|
|
|
|
|
# Note: This depends on the unlikelihood of the name
|
|
|
|
|
# existing in a cell on disk. Only cells in memory are
|
|
|
|
|
# checked for name collisions. Since the names will go
|
|
|
|
|
# into SPICE netlists, names must be unique when compared
|
|
|
|
|
# in a case-insensitive manner. Using base-36 (alphabet and
|
|
|
|
|
# numbers), each gencell name with 6 randomized characters
|
|
|
|
|
# has a 1 in 4.6E-10 chance of reappearing.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::get_gencell_name {gencell_type} {
|
|
|
|
|
while {true} {
|
|
|
|
|
set postfix ""
|
|
|
|
|
for {set i 0} {$i < 6} {incr i} {
|
|
|
|
|
set pint [expr 48 + int(rand() * 36)]
|
|
|
|
|
if {$pint > 57} {set pint [expr $pint + 39]}
|
|
|
|
|
append postfix [format %c $pint]
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if {[cellname list exists ${gencell_type}_$postfix] == 0} {break}
|
|
|
|
|
}
|
|
|
|
|
return ${gencell_type}_$postfix
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
# get_gencell_hash
|
|
|
|
|
#
|
|
|
|
|
# A better approach to the above. Take the parameter
|
|
|
|
|
# dictionary, and run all the values through a hash function
|
|
|
|
|
# to generate a 30-bit value, then convert to base32. This
|
|
|
|
|
# gives a result that is repeatable for the same set of
|
|
|
|
|
# parameter values with a very low probability of a collision.
|
|
|
|
|
#
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
# The hash function is murmur3 but reduced from 32 to 30 bits
|
|
|
|
|
# so that the result can form a 6-character value in base36
|
|
|
|
|
# with all characters being valid for a SPICE subcell name
|
|
|
|
|
# (e.g., alphanumeric only and case-insensitive). This
|
|
|
|
|
# reduces the space from ~4 billion unique suffixes to ~1
|
|
|
|
|
# billion; however, even a complex mixed-signal chip design
|
|
|
|
|
# is unlikely to have more than a few hundred unique parameter
|
|
|
|
|
# sets for any given device.
|
|
|
|
|
#
|
|
|
|
|
# Code courtesy of ChatGPT, derived from my implementation.
|
2020-10-30 16:20:19 +01:00
|
|
|
#----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::get_gencell_hash {parameters} {
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
# Canonicalize: sort by key
|
|
|
|
|
set keys [lsort [dict keys $parameters]]
|
|
|
|
|
|
|
|
|
|
# Build input string (values only, but delimited)
|
|
|
|
|
set input ""
|
|
|
|
|
foreach k $keys {
|
|
|
|
|
set value [magic::normalize_value [dict get $parameters $k]]
|
|
|
|
|
append input "${value};"
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
|
|
|
|
|
# Compute Murmur hash
|
|
|
|
|
set hash [magic::murmur3_32 $input]
|
|
|
|
|
|
|
|
|
|
# Convert to 6-character base32
|
2020-10-30 16:20:19 +01:00
|
|
|
set cvals ""
|
|
|
|
|
for {set i 0} {$i < 6} {incr i} {
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
set oval [expr {($hash >> ($i * 5)) & 0x1f}]
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
if {$oval < 8} {
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
set bval [expr {$oval + 50}] ;# '2'-'9'
|
|
|
|
|
} elseif {$oval < 16} {
|
|
|
|
|
set bval [expr {$oval + 57}] ;# 'A'-'H'
|
|
|
|
|
} elseif {$oval < 21} {
|
|
|
|
|
set bval [expr {$oval + 58}] ;# 'J'-'N'
|
|
|
|
|
} else {
|
|
|
|
|
set bval [expr {$oval + 59}] ;# 'P'-'Z'
|
|
|
|
|
}
|
|
|
|
|
append cvals [binary format c* $bval]
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
|
|
|
|
return $cvals
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_create
|
|
|
|
|
#
|
|
|
|
|
# Instantiate a new gencell called $gname. If $gname
|
|
|
|
|
# does not already exist, create it by calling its
|
|
|
|
|
# drawing routine.
|
|
|
|
|
#
|
|
|
|
|
# Don't rely on pushbox/popbox since we don't know what
|
|
|
|
|
# the drawing routine is going to do to the stack!
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
proc magic::gencell_create {gencell_type library parameters {orient 0}} {
|
2017-04-25 14:41:48 +02:00
|
|
|
global Opts
|
|
|
|
|
suspendall
|
|
|
|
|
|
|
|
|
|
set newinstname ""
|
|
|
|
|
|
|
|
|
|
# Get device defaults
|
|
|
|
|
if {$parameters == {}} {
|
|
|
|
|
# Pull user-entered values from dialog
|
2019-11-21 21:48:24 +01:00
|
|
|
set dialogparams [magic::gencell_getparams]
|
|
|
|
|
if {[dict exists $dialogparams gencell]} {
|
|
|
|
|
# Setting special parameter "gencell" forces the gencell to change type
|
|
|
|
|
set gencell_type [dict get $dialogparams gencell]
|
|
|
|
|
}
|
|
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
|
|
|
|
set parameters [dict merge $pdefaults $dialogparams]
|
2017-04-25 14:41:48 +02:00
|
|
|
set newinstname [.params.title.ient get]
|
|
|
|
|
if {$newinstname == "(default)"} {set newinstname ""}
|
|
|
|
|
if {[instance list exists $newinstname] != ""} {set newinstname ""}
|
|
|
|
|
} else {
|
2019-11-21 21:48:24 +01:00
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
# Setting special parameter "gencell" forces the gencell to change type
|
|
|
|
|
set gencell_type [dict get $parameters gencell]
|
|
|
|
|
}
|
|
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
2017-04-25 14:41:48 +02:00
|
|
|
set parameters [dict merge $pdefaults $parameters]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
|
|
|
|
|
checkerr]} {
|
|
|
|
|
puts stderr $checkerr
|
|
|
|
|
}
|
|
|
|
|
magic::gencell_setparams $parameters
|
2019-11-21 21:48:24 +01:00
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
set parameters [dict remove $parameters gencell]
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2026-02-24 21:59:21 +01:00
|
|
|
set curunits [units]
|
|
|
|
|
units internal
|
2017-04-25 14:41:48 +02:00
|
|
|
set savebox [box values]
|
|
|
|
|
|
|
|
|
|
catch {setpoint 0 0 $Opts(focus)}
|
|
|
|
|
if [dict exists $parameters nocell] {
|
|
|
|
|
if {[catch {set instname [${library}::${gencell_type}_draw $parameters]} \ drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
|
|
|
|
set gname [instance list celldef $instname]
|
|
|
|
|
eval "box values $savebox"
|
|
|
|
|
} else {
|
2020-10-30 16:20:19 +01:00
|
|
|
set gsuffix [magic::get_gencell_hash ${parameters}]
|
|
|
|
|
set gname ${gencell_type}_${gsuffix}
|
2017-04-25 14:41:48 +02:00
|
|
|
cellname create $gname
|
|
|
|
|
pushstack $gname
|
|
|
|
|
if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} {
|
|
|
|
|
puts stderr $drawerr
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
property library $library
|
2017-04-25 14:41:48 +02:00
|
|
|
property gencell $gencell_type
|
|
|
|
|
property parameters $parameters
|
|
|
|
|
popstack
|
|
|
|
|
eval "box values $savebox"
|
2020-10-30 16:20:19 +01:00
|
|
|
set instname [getcell $gname $orient]
|
2017-04-25 14:41:48 +02:00
|
|
|
expand
|
|
|
|
|
}
|
|
|
|
|
if {$newinstname != ""} {
|
|
|
|
|
identify $newinstname
|
|
|
|
|
set instname $newinstname
|
|
|
|
|
}
|
2026-02-24 22:40:07 +01:00
|
|
|
units {*}$curunits
|
2017-04-25 14:41:48 +02:00
|
|
|
resumeall
|
|
|
|
|
redraw
|
|
|
|
|
return $instname
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
# Add a standard entry parameter to the gencell window
|
2017-04-25 14:41:48 +02:00
|
|
|
#-----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_entry {pname ptext parameters} {
|
|
|
|
|
|
|
|
|
|
if [dict exists $parameters $pname] {
|
|
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
} else {
|
|
|
|
|
set value ""
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
set numrows [lindex [grid size .params.body.area.edits] 1]
|
|
|
|
|
label .params.body.area.edits.${pname}_lab -text $ptext
|
|
|
|
|
entry .params.body.area.edits.${pname}_ent -background white -textvariable magic::${pname}_val
|
|
|
|
|
grid .params.body.area.edits.${pname}_lab -row $numrows -column 0 \
|
2017-04-25 14:41:48 +02:00
|
|
|
-sticky ens -ipadx 5 -ipady 2
|
2023-03-12 21:57:47 +01:00
|
|
|
grid .params.body.area.edits.${pname}_ent -row $numrows -column 1 \
|
2017-04-25 14:41:48 +02:00
|
|
|
-sticky ewns -ipadx 5 -ipady 2
|
2023-03-12 21:57:47 +01:00
|
|
|
.params.body.area.edits.${pname}_ent insert end $value
|
2017-04-25 14:41:48 +02:00
|
|
|
set magic::${pname}_val $value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
# Default entry callback, without any dependencies. Each
|
2025-10-16 23:08:15 +02:00
|
|
|
# parameter changed causes an update to the dialog. Also
|
|
|
|
|
# add default callbacks on checkboxes and choice menus,
|
|
|
|
|
# using a null function which does not get executed, but
|
|
|
|
|
# the dialog gets updated afterward.
|
2017-04-25 14:41:48 +02:00
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_check_callbacks {gencell_type library} {
|
2023-03-12 21:57:47 +01:00
|
|
|
set wlist [winfo children .params.body.area.edits]
|
2017-04-25 14:41:48 +02:00
|
|
|
foreach w $wlist {
|
2026-03-17 01:02:09 +01:00
|
|
|
if {[regexp {\.params\.body\.area\.edits\.(.+)_.+} $w valid pname]} {
|
2025-10-16 23:08:15 +02:00
|
|
|
magic::add_dependency \{\} $gencell_type $library $pname
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
# Add a dependency between entries. When one updates, the
|
|
|
|
|
# others will be recomputed according to the callback
|
|
|
|
|
# function.
|
|
|
|
|
#
|
|
|
|
|
# The callback function is passed the value of all
|
|
|
|
|
# parameters for the device, overridden by the values
|
|
|
|
|
# in the dialog. The routine computes the dependent
|
|
|
|
|
# values and writes them back to the parameter dictionary.
|
|
|
|
|
# The callback function must return the modified parameters
|
|
|
|
|
# dictionary.
|
|
|
|
|
#
|
|
|
|
|
# Also handle dependencies on checkboxes and selection lists
|
2026-03-17 01:02:09 +01:00
|
|
|
#
|
|
|
|
|
# If dependency callbacks exist, then chain them together.
|
|
|
|
|
# A final default dependency will be added to all entries
|
|
|
|
|
# to run the "check" procedure for the device. Dependencies
|
|
|
|
|
# that are more targeted get run first.
|
2017-04-25 14:41:48 +02:00
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_dependency {callback gencell_type library args} {
|
|
|
|
|
if {[llength $args] == 0} {
|
|
|
|
|
# If no arguments are given, do for all parameters
|
|
|
|
|
set parameters ${library}::${gencell_type}_defaults
|
|
|
|
|
magic::add_dependency $callback $gencell_type $library \
|
|
|
|
|
{*}[dict keys $parameters]
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-03-12 21:57:47 +01:00
|
|
|
set clist [winfo children .params.body.area.edits]
|
2017-04-25 14:41:48 +02:00
|
|
|
foreach pname $args {
|
2023-03-12 21:57:47 +01:00
|
|
|
if {[lsearch $clist .params.body.area.edits.${pname}_ent] >= 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
# Add callback on enter or focus out
|
2026-03-17 01:02:09 +01:00
|
|
|
set oldbind [bind .params.body.area.edits.${pname}_ent <Return>]
|
|
|
|
|
set newbind "magic::update_dialog $callback $pname $gencell_type $library"
|
|
|
|
|
if {$oldbind != {}} {set newbind "$oldbind ; $newbind"}
|
|
|
|
|
bind .params.body.area.edits.${pname}_ent <Return> $newbind
|
|
|
|
|
set oldbind [bind .params.body.area.edits.${pname}_ent <FocusOut>]
|
|
|
|
|
set newbind "magic::update_dialog $callback $pname $gencell_type $library"
|
|
|
|
|
if {$oldbind != {}} {set newbind "$oldbind ; $newbind"}
|
|
|
|
|
bind .params.body.area.edits.${pname}_ent <FocusOut> $newbind
|
2023-03-12 21:57:47 +01:00
|
|
|
} elseif {[lsearch $clist .params.body.area.edits.${pname}_chk] >= 0} {
|
2017-04-25 14:41:48 +02:00
|
|
|
# Add callback on checkbox change state
|
2026-03-17 01:02:09 +01:00
|
|
|
set oldcmd [.params.body.area.edits.${pname}_chk cget -command]
|
|
|
|
|
set newcmd "magic::update_dialog $callback $pname $gencell_type $library"
|
|
|
|
|
if {$oldcmd != {}} {set newcmd "$oldcmd ; $newcmd"}
|
|
|
|
|
.params.body.area.edits.${pname}_chk configure -command $newcmd
|
2023-03-12 21:57:47 +01:00
|
|
|
} elseif {[lsearch $clist .params.body.area.edits.${pname}_sel] >= 0} {
|
|
|
|
|
set smenu .params.body.area.edits.${pname}_sel.menu
|
2017-04-25 14:41:48 +02:00
|
|
|
set sitems [${smenu} index end]
|
|
|
|
|
for {set idx 0} {$idx <= $sitems} {incr idx} {
|
2026-03-17 01:02:09 +01:00
|
|
|
set oldcmd [${smenu} entrycget $idx -command]
|
|
|
|
|
set newcmd "magic::update_dialog $callback $pname $gencell_type $library"
|
|
|
|
|
if {$oldcmd != {}} {set newcmd "$oldcmd ; $newcmd"}
|
|
|
|
|
${smenu} entryconfigure $idx -command $newcmd
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
# Execute callback procedure, then run bounds checks
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::update_dialog {callback pname gencell_type library} {
|
2019-11-22 17:37:04 +01:00
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
|
|
|
|
set parameters [dict merge $pdefaults [magic::gencell_getparams]]
|
|
|
|
|
|
2019-11-21 21:48:24 +01:00
|
|
|
if {[dict exists $parameters gencell]} {
|
|
|
|
|
# Setting special parameter "gencell" forces the gencell to change type
|
|
|
|
|
set gencell_type [dict get $parameters gencell]
|
2019-11-22 17:37:04 +01:00
|
|
|
set pdefaults [${library}::${gencell_type}_defaults]
|
|
|
|
|
set parameters [dict merge $pdefaults [magic::gencell_getparams]]
|
2019-11-21 21:48:24 +01:00
|
|
|
}
|
2019-11-22 17:37:04 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if {$callback != {}} {
|
|
|
|
|
set parameters [$callback $pname $parameters]
|
|
|
|
|
}
|
|
|
|
|
if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \
|
|
|
|
|
checkerr]} {
|
|
|
|
|
puts stderr $checkerr
|
|
|
|
|
}
|
|
|
|
|
magic::gencell_setparams $parameters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
# Add a standard checkbox parameter to the gencell window
|
2017-04-25 14:41:48 +02:00
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_checkbox {pname ptext parameters} {
|
|
|
|
|
|
|
|
|
|
if [dict exists $parameters $pname] {
|
|
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
} else {
|
|
|
|
|
set value ""
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
set numrows [lindex [grid size .params.body.area.edits] 1]
|
|
|
|
|
label .params.body.area.edits.${pname}_lab -text $ptext
|
|
|
|
|
checkbutton .params.body.area.edits.${pname}_chk -variable magic::${pname}_val
|
|
|
|
|
grid .params.body.area.edits.${pname}_lab -row $numrows -column 0 -sticky ens
|
|
|
|
|
grid .params.body.area.edits.${pname}_chk -row $numrows -column 1 -sticky wns
|
2017-04-25 14:41:48 +02:00
|
|
|
set magic::${pname}_val $value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
# Add a message box (informational, not editable) to the
|
|
|
|
|
# gencell window. Note that the text does not have to be
|
|
|
|
|
# in the parameter list, as it can be upated through the
|
|
|
|
|
# textvariable name.
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::add_message {pname ptext parameters {color blue}} {
|
|
|
|
|
|
|
|
|
|
if [dict exists $parameters $pname] {
|
|
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
} else {
|
|
|
|
|
set value ""
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
set numrows [lindex [grid size .params.body.area.edits] 1]
|
|
|
|
|
label .params.body.area.edits.${pname}_lab -text $ptext
|
|
|
|
|
label .params.body.area.edits.${pname}_txt -text $value \
|
2017-04-25 14:41:48 +02:00
|
|
|
-foreground $color -textvariable magic::${pname}_val
|
2023-03-12 21:57:47 +01:00
|
|
|
grid .params.body.area.edits.${pname}_lab -row $numrows -column 0 -sticky ens
|
|
|
|
|
grid .params.body.area.edits.${pname}_txt -row $numrows -column 1 -sticky wns
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
# Add a selectable-list parameter to the gencell window
|
2022-12-15 18:25:23 +01:00
|
|
|
# (NOTE: Use magic::add_dependency to add a callback to
|
|
|
|
|
# the selection list choice.)
|
2017-04-25 14:41:48 +02:00
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
2022-12-15 18:25:23 +01:00
|
|
|
proc magic::add_selectlist {pname ptext all_values parameters {itext ""}} {
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if [dict exists $parameters $pname] {
|
|
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
} else {
|
2019-11-21 21:48:24 +01:00
|
|
|
set value $itext
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
set numrows [lindex [grid size .params.body.area.edits] 1]
|
|
|
|
|
label .params.body.area.edits.${pname}_lab -text $ptext
|
|
|
|
|
menubutton .params.body.area.edits.${pname}_sel -menu .params.body.area.edits.${pname}_sel.menu \
|
2020-05-23 23:13:14 +02:00
|
|
|
-relief groove -text ${value}
|
2023-03-12 21:57:47 +01:00
|
|
|
grid .params.body.area.edits.${pname}_lab -row $numrows -column 0 -sticky ens
|
|
|
|
|
grid .params.body.area.edits.${pname}_sel -row $numrows -column 1 -sticky wns
|
|
|
|
|
menu .params.body.area.edits.${pname}_sel.menu -tearoff 0
|
2017-04-25 14:41:48 +02:00
|
|
|
foreach item ${all_values} {
|
2023-03-12 21:57:47 +01:00
|
|
|
set cmdtxt ".params.body.area.edits.${pname}_sel configure -text $item"
|
|
|
|
|
.params.body.area.edits.${pname}_sel.menu add radio -label $item \
|
2017-04-25 14:41:48 +02:00
|
|
|
-variable magic::${pname}_val -value $item \
|
2022-12-14 23:38:19 +01:00
|
|
|
-command $cmdtxt
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
set magic::${pname}_val $value
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:01:14 +01:00
|
|
|
#----------------------------------------------------------
|
2020-05-23 23:13:14 +02:00
|
|
|
# Add a selectable-list parameter to the gencell window
|
2019-11-20 19:01:14 +01:00
|
|
|
# Unlike the routine above, it returns the index of the
|
|
|
|
|
# selection, not the selection itself. This is useful for
|
|
|
|
|
# keying the selection to other parameter value lists.
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
|
2019-11-21 21:48:24 +01:00
|
|
|
proc magic::add_selectindex {pname ptext all_values parameters {ival 0}} {
|
2019-11-20 19:01:14 +01:00
|
|
|
|
|
|
|
|
if [dict exists $parameters $pname] {
|
|
|
|
|
set value [dict get $parameters $pname]
|
|
|
|
|
} else {
|
2019-11-21 21:48:24 +01:00
|
|
|
set value $ival
|
2019-11-20 19:01:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
set numrows [lindex [grid size .params.body.area.edits] 1]
|
|
|
|
|
label .params.body.area.edits.${pname}_lab -text $ptext
|
|
|
|
|
menubutton .params.body.area.edits.${pname}_sel -menu .params.body.area.edits.${pname}_sel.menu \
|
2019-11-20 19:01:14 +01:00
|
|
|
-relief groove -text [lindex ${all_values} ${value}]
|
2023-03-12 21:57:47 +01:00
|
|
|
grid .params.body.area.edits.${pname}_lab -row $numrows -column 0 -sticky ens
|
|
|
|
|
grid .params.body.area.edits.${pname}_sel -row $numrows -column 1 -sticky wns
|
|
|
|
|
menu .params.body.area.edits.${pname}_sel.menu -tearoff 0
|
2019-11-20 19:01:14 +01:00
|
|
|
set idx 0
|
|
|
|
|
foreach item ${all_values} {
|
2023-03-12 21:57:47 +01:00
|
|
|
.params.body.area.edits.${pname}_sel.menu add radio -label $item \
|
2019-11-20 19:01:14 +01:00
|
|
|
-variable magic::${pname}_val -value $idx \
|
2023-03-12 21:57:47 +01:00
|
|
|
-command ".params.body.area.edits.${pname}_sel configure -text $item"
|
2019-11-20 19:01:14 +01:00
|
|
|
incr idx
|
|
|
|
|
}
|
|
|
|
|
set magic::${pname}_val $value
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_defaults ---
|
|
|
|
|
#
|
|
|
|
|
# Set all parameters for a device. Start by calling the base
|
|
|
|
|
# device's default value list to generate a dictionary. Then
|
|
|
|
|
# parse all values passed in 'parameters', overriding any
|
|
|
|
|
# defaults with the passed values.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_defaults {gencell_type library parameters} {
|
|
|
|
|
set basedict [${library}::${gencell_type}_defaults]
|
|
|
|
|
set newdict [dict merge $basedict $parameters]
|
|
|
|
|
return $newdict
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# Command tag callback on "select". "select cell" should
|
|
|
|
|
# cause the parameter dialog window to update to reflect the
|
|
|
|
|
# selected cell. If a cell is unselected, then revert to the
|
|
|
|
|
# default 'Create' window.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_update {{command {}}} {
|
|
|
|
|
if {[info level] <= 1} {
|
|
|
|
|
if {![catch {set state [wm state .params]}]} {
|
|
|
|
|
if {[wm state .params] == "normal"} {
|
|
|
|
|
if {$command == "cell"} {
|
|
|
|
|
# If multiple devices are selected, choose the first in
|
|
|
|
|
# the list returned by "what -list".
|
|
|
|
|
set instname [lindex [lindex [lindex [what -list] 2] 0] 0]
|
|
|
|
|
magic::gencell_dialog $instname {} {} {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-13 17:28:07 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# updateParamsScrollRegion ---
|
|
|
|
|
#
|
|
|
|
|
# Change the canvas size when the parameter window changes
|
|
|
|
|
# size so that the scrollbar works correctly.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc updateParamsScrollRegion {} {
|
|
|
|
|
set bbox [.params.body.area bbox all]
|
|
|
|
|
.params.body.area configure -scrollregion $bbox
|
|
|
|
|
.params.body.area configure -width [lindex $bbox 2]
|
|
|
|
|
.params.body.area configure -height [lindex $bbox 3]
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# gencell_dialog ---
|
|
|
|
|
#
|
|
|
|
|
# Create the dialog window for entering device parameters. The
|
|
|
|
|
# general procedure then calls the dialog setup for the specific
|
|
|
|
|
# device.
|
|
|
|
|
#
|
|
|
|
|
# 1) If gname is NULL and gencell_type is set, then we
|
|
|
|
|
# create a new cell of type gencell_type.
|
|
|
|
|
# 2) If gname is non-NULL, then we edit the existing
|
|
|
|
|
# cell of type $gname.
|
|
|
|
|
# 3) If gname is non-NULL and gencell_type or library
|
|
|
|
|
# is NULL or unspecified, then we derive the gencell_type
|
|
|
|
|
# and library from the existing cell's property strings
|
|
|
|
|
#
|
|
|
|
|
# The device setup should be built using the API that defines
|
|
|
|
|
# these procedures:
|
|
|
|
|
#
|
|
|
|
|
# magic::add_entry Single text entry window
|
|
|
|
|
# magic::add_checkbox Single checkbox
|
|
|
|
|
# magic::add_selectlist Pull-down menu with list of selections
|
|
|
|
|
#
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::gencell_dialog {instname gencell_type library parameters} {
|
|
|
|
|
if {$gencell_type == {}} {
|
|
|
|
|
# Revert to default state for the device that was previously
|
|
|
|
|
# shown in the parameter window.
|
|
|
|
|
if {![catch {set state [wm state .params]}]} {
|
|
|
|
|
if {$instname == {}} {
|
|
|
|
|
set devstr [.params.title.lab1 cget -text]
|
|
|
|
|
if {$devstr == "Edit device:"} {
|
|
|
|
|
set gencell_type [.params.title.lab2 cget -text]
|
|
|
|
|
set library [.params.title.lab4 cget -text]
|
|
|
|
|
} else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {$instname != {}} {
|
|
|
|
|
# Remove any array component of the instance name
|
2024-04-05 23:47:44 +02:00
|
|
|
set baseinstname [string map {\\ ""} $instname]
|
|
|
|
|
if {[regexp {^(.*)\[[0-9,]+\]$} $baseinstname valid instroot]} {
|
|
|
|
|
set originstname $instname
|
2017-04-25 14:41:48 +02:00
|
|
|
set instname $instroot
|
2024-04-05 23:47:44 +02:00
|
|
|
} else {
|
|
|
|
|
set instroot ""
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
set gname [instance list celldef [subst $instname]]
|
2024-04-05 23:47:44 +02:00
|
|
|
if {$gname == ""} {
|
|
|
|
|
# Check if name inherited brackets but is not an array
|
|
|
|
|
if {$instroot != ""} {
|
|
|
|
|
set testinstname [string map {\[ \\\[ \] \\\]} $baseinstname]
|
|
|
|
|
set gname [instance list celldef [subst $testinstname]]
|
|
|
|
|
set instname $originstname
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-21 21:48:24 +01:00
|
|
|
set gencell_type [cellname list property $gname gencell]
|
2017-04-25 14:41:48 +02:00
|
|
|
if {$library == {}} {
|
|
|
|
|
set library [cellname list property $gname library]
|
|
|
|
|
}
|
|
|
|
|
if {$parameters == {}} {
|
|
|
|
|
set parameters [cellname list property $gname parameters]
|
|
|
|
|
}
|
|
|
|
|
if {$gencell_type == {} || $library == {}} {return}
|
|
|
|
|
|
|
|
|
|
if {$parameters == {}} {
|
|
|
|
|
set parameters [${library}::${gencell_type}_defaults]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# If the default parameters contain "nocell", then set the
|
|
|
|
|
# standard parameters for fixed devices from the instance
|
|
|
|
|
if {[dict exists $parameters nocell]} {
|
|
|
|
|
select cell $instname
|
|
|
|
|
set arcount [array -list count]
|
|
|
|
|
set arpitch [array -list pitch]
|
|
|
|
|
|
|
|
|
|
dict set parameters nx [expr [lindex $arcount 1] - [lindex $arcount 0] + 1]
|
|
|
|
|
dict set parameters ny [expr [lindex $arcount 3] - [lindex $arcount 2] + 1]
|
|
|
|
|
dict set parameters pitchx [lindex $arpitch 0]
|
|
|
|
|
dict set parameters pitchy [lindex $arpitch 1]
|
|
|
|
|
}
|
|
|
|
|
set ttext "Edit device"
|
|
|
|
|
set itext $instname
|
|
|
|
|
} else {
|
|
|
|
|
set parameters [magic::gencell_defaults $gencell_type $library $parameters]
|
|
|
|
|
set gname "(default)"
|
|
|
|
|
set itext "(default)"
|
|
|
|
|
set ttext "New device"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Destroy children, not the top-level window, or else window keeps
|
|
|
|
|
# bouncing around every time something is changed.
|
|
|
|
|
if {[catch {toplevel .params}]} {
|
|
|
|
|
.params.title.lab1 configure -text "${ttext}:"
|
|
|
|
|
.params.title.lab2 configure -text "$gencell_type"
|
|
|
|
|
.params.title.lab4 configure -text "$library"
|
|
|
|
|
.params.title.glab configure -foreground blue -text "$gname"
|
|
|
|
|
.params.title.ient delete 0 end
|
|
|
|
|
.params.title.ient insert 0 "$itext"
|
2023-03-12 21:57:47 +01:00
|
|
|
foreach child [winfo children .params.body.area.edits] {
|
2017-04-25 14:41:48 +02:00
|
|
|
destroy $child
|
|
|
|
|
}
|
|
|
|
|
foreach child [winfo children .params.buttons] {
|
|
|
|
|
destroy $child
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
frame .params.title
|
|
|
|
|
label .params.title.lab1 -text "${ttext}:"
|
|
|
|
|
label .params.title.lab2 -foreground blue -text "$gencell_type"
|
|
|
|
|
label .params.title.lab3 -text "Library:"
|
|
|
|
|
label .params.title.lab4 -foreground blue -text "$library"
|
|
|
|
|
label .params.title.clab -text "Cellname:"
|
|
|
|
|
label .params.title.glab -foreground blue -text "$gname"
|
|
|
|
|
label .params.title.ilab -text "Instance:"
|
|
|
|
|
entry .params.title.ient -foreground brown -background white
|
|
|
|
|
.params.title.ient insert 0 "$itext"
|
|
|
|
|
ttk::separator .params.sep
|
2023-03-12 21:57:47 +01:00
|
|
|
frame .params.body
|
|
|
|
|
canvas .params.body.area
|
|
|
|
|
scrollbar .params.body.sb -command {.params.body.area yview}
|
2017-04-25 14:41:48 +02:00
|
|
|
frame .params.buttons
|
|
|
|
|
|
|
|
|
|
grid .params.title.lab1 -padx 5 -row 0 -column 0
|
|
|
|
|
grid .params.title.lab2 -padx 5 -row 0 -column 1 -sticky w
|
|
|
|
|
grid .params.title.lab3 -padx 5 -row 0 -column 2
|
|
|
|
|
grid .params.title.lab4 -padx 5 -row 0 -column 3 -sticky w
|
|
|
|
|
|
|
|
|
|
grid .params.title.clab -padx 5 -row 1 -column 0
|
|
|
|
|
grid .params.title.glab -padx 5 -row 1 -column 1 -sticky w
|
|
|
|
|
grid .params.title.ilab -padx 5 -row 1 -column 2
|
|
|
|
|
grid .params.title.ient -padx 5 -row 1 -column 3 -sticky ew
|
|
|
|
|
grid columnconfigure .params.title 3 -weight 1
|
|
|
|
|
|
2023-03-12 21:57:47 +01:00
|
|
|
grid .params.body.area -row 0 -column 0 -sticky nsew
|
|
|
|
|
grid .params.body.sb -row 0 -column 1 -sticky ns
|
|
|
|
|
grid columnconfigure .params.body 0 -weight 1
|
|
|
|
|
grid columnconfigure .params.body 1 -weight 0
|
|
|
|
|
grid rowconfigure .params.body 0 -weight 1
|
|
|
|
|
|
|
|
|
|
grid .params.title -row 0 -column 0 -sticky nsew
|
|
|
|
|
grid .params.sep -row 1 -column 0 -sticky nsew
|
|
|
|
|
grid .params.body -row 2 -column 0 -sticky nsew
|
|
|
|
|
grid .params.buttons -row 3 -column 0 -sticky nsew
|
|
|
|
|
|
|
|
|
|
grid rowconfigure .params 0 -weight 0
|
|
|
|
|
grid rowconfigure .params 1 -weight 0
|
|
|
|
|
grid rowconfigure .params 2 -weight 1
|
|
|
|
|
grid rowconfigure .params 3 -weight 0
|
|
|
|
|
grid columnconfigure .params 0 -weight 1
|
|
|
|
|
|
|
|
|
|
frame .params.body.area.edits
|
|
|
|
|
.params.body.area create window 0 0 -anchor nw -window .params.body.area.edits
|
|
|
|
|
.params.body.area config -yscrollcommand {.params.body.sb set}
|
2023-09-13 17:28:07 +02:00
|
|
|
|
|
|
|
|
# Make sure scrollbar tracks any window size changes
|
|
|
|
|
bind .params <Configure> updateParamsScrollRegion
|
|
|
|
|
|
|
|
|
|
# Allow mouse wheel to scroll the window up and down.
|
|
|
|
|
bind .params.body.area <Button-4> {.params.body.area yview scroll -1 units}
|
|
|
|
|
bind .params.body.area <Button-5> {.params.body.area yview scroll +1 units}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if {$instname == {}} {
|
|
|
|
|
button .params.buttons.apply -text "Create" -command \
|
|
|
|
|
[subst {set inst \[magic::gencell_create \
|
|
|
|
|
$gencell_type $library {}\] ; \
|
|
|
|
|
magic::gencell_dialog \$inst $gencell_type $library {} }]
|
|
|
|
|
button .params.buttons.okay -text "Create and Close" -command \
|
|
|
|
|
[subst {set inst \[magic::gencell_create \
|
|
|
|
|
$gencell_type $library {}\] ; \
|
|
|
|
|
magic::gencell_dialog \$inst $gencell_type $library {} ; \
|
|
|
|
|
destroy .params}]
|
|
|
|
|
} else {
|
2024-04-05 23:47:44 +02:00
|
|
|
set instname [string map {\[ \\\[ \] \\\]} $instname]
|
2017-04-25 14:41:48 +02:00
|
|
|
button .params.buttons.apply -text "Apply" -command \
|
|
|
|
|
"magic::gencell_change $instname $gencell_type $library {}"
|
|
|
|
|
button .params.buttons.okay -text "Okay" -command \
|
|
|
|
|
"magic::gencell_change $instname $gencell_type $library {} ;\
|
|
|
|
|
destroy .params"
|
|
|
|
|
}
|
|
|
|
|
button .params.buttons.reset -text "Reset" -command \
|
|
|
|
|
"magic::gencell_dialog {} ${gencell_type} ${library} {}"
|
|
|
|
|
button .params.buttons.close -text "Close" -command {destroy .params}
|
|
|
|
|
|
|
|
|
|
pack .params.buttons.apply -padx 5 -ipadx 5 -ipady 2 -side left
|
|
|
|
|
pack .params.buttons.okay -padx 5 -ipadx 5 -ipady 2 -side left
|
|
|
|
|
pack .params.buttons.close -padx 5 -ipadx 5 -ipady 2 -side right
|
|
|
|
|
pack .params.buttons.reset -padx 5 -ipadx 5 -ipady 2 -side right
|
|
|
|
|
|
|
|
|
|
# Invoke the callback procedure that creates the parameter entries
|
|
|
|
|
|
|
|
|
|
${library}::${gencell_type}_dialog $parameters
|
|
|
|
|
|
|
|
|
|
# Add standard callback to all entry fields to run parameter bounds checks
|
|
|
|
|
magic::add_check_callbacks $gencell_type $library
|
|
|
|
|
|
|
|
|
|
# Make sure the window is raised
|
|
|
|
|
raise .params
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
Made a fairly major overhaul to the toolkit for generated devices.
The main changes are:
(1) Changed the hash function used to generate the 6-character
suffix for generated device cell names. The original hash
function is not good for ensuring unique names, and can
cause cell name collisions (two different parameter sets
having the same cell name). The chance of name collisions
should now be diminishingly small.
(2) Modified the string passed to the hash:
(A) Ordered the parameter names alphabetically, since iterating
through dictionary keys is not guaranteed to be in any
specific order, leading to different strings for the same
parameter set
(B) Normalized numerical parameters, so that "2", "2.0", "2e0"
are all hashed the same, again to avoid having multiple
cell names for the same set of parameters.
(3) Fixed a problem in which when changing parameters for a cell
instance, the instance would become unselected and the instance
name would be lost and revert to magic's auto-generated name.
(4) Fixed the annoyance of having a pop-up dialog whenever magic
decides that a parameterized cell name is not being used anywhere,
and it can safely delete the cell.
(5) Fixed an issue where the check for whether a cell can be deleted
is not run consistently.
The result is, I hope, a much more pleasant experience with generated
cells.
2026-04-02 23:13:17 +02:00
|
|
|
# Implementation of murmur3 hash, 32 bits
|
|
|
|
|
# Code courtesy of ChatGPT.
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::murmur3_32 {key {seed 0}} {
|
|
|
|
|
set length [string length $key]
|
|
|
|
|
|
|
|
|
|
set c1 0xcc9e2d51
|
|
|
|
|
set c2 0x1b873593
|
|
|
|
|
|
|
|
|
|
set h1 $seed
|
|
|
|
|
|
|
|
|
|
# Body (process 4 bytes at a time)
|
|
|
|
|
set nblocks [expr {$length / 4}]
|
|
|
|
|
for {set i 0} {$i < $nblocks} {incr i} {
|
|
|
|
|
binary scan [string range $key [expr {$i*4}] [expr {$i*4+3}]] i k1
|
|
|
|
|
|
|
|
|
|
set k1 [expr {($k1 * $c1) & 0xffffffff}]
|
|
|
|
|
set k1 [expr {(($k1 << 15) | (($k1 & 0xffffffff) >> 17)) & 0xffffffff}]
|
|
|
|
|
set k1 [expr {($k1 * $c2) & 0xffffffff}]
|
|
|
|
|
|
|
|
|
|
set h1 [expr {$h1 ^ $k1}]
|
|
|
|
|
set h1 [expr {(($h1 << 13) | (($h1 & 0xffffffff) >> 19)) & 0xffffffff}]
|
|
|
|
|
set h1 [expr {(($h1 * 5) + 0xe6546b64) & 0xffffffff}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Tail
|
|
|
|
|
set k1 0
|
|
|
|
|
set tail_index [expr {$nblocks * 4}]
|
|
|
|
|
set tail [string range $key $tail_index end]
|
|
|
|
|
set remaining [string length $tail]
|
|
|
|
|
|
|
|
|
|
if {$remaining >= 3} {
|
|
|
|
|
binary scan [string index $tail 2] c b
|
|
|
|
|
set k1 [expr {$k1 ^ (($b & 0xff) << 16)}]
|
|
|
|
|
}
|
|
|
|
|
if {$remaining >= 2} {
|
|
|
|
|
binary scan [string index $tail 1] c b
|
|
|
|
|
set k1 [expr {$k1 ^ (($b & 0xff) << 8)}]
|
|
|
|
|
}
|
|
|
|
|
if {$remaining >= 1} {
|
|
|
|
|
binary scan [string index $tail 0] c b
|
|
|
|
|
set k1 [expr {$k1 ^ ($b & 0xff)}]
|
|
|
|
|
set k1 [expr {($k1 * $c1) & 0xffffffff}]
|
|
|
|
|
set k1 [expr {(($k1 << 15) | (($k1 & 0xffffffff) >> 17)) & 0xffffffff}]
|
|
|
|
|
set k1 [expr {($k1 * $c2) & 0xffffffff}]
|
|
|
|
|
set h1 [expr {$h1 ^ $k1}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Finalization (fmix)
|
|
|
|
|
set h1 [expr {$h1 ^ $length}]
|
|
|
|
|
set h1 [expr {$h1 ^ (($h1 & 0xffffffff) >> 16)}]
|
|
|
|
|
set h1 [expr {($h1 * 0x85ebca6b) & 0xffffffff}]
|
|
|
|
|
set h1 [expr {$h1 ^ (($h1 & 0xffffffff) >> 13)}]
|
|
|
|
|
set h1 [expr {($h1 * 0xc2b2ae35) & 0xffffffff}]
|
|
|
|
|
set h1 [expr {$h1 ^ (($h1 & 0xffffffff) >> 16)}]
|
|
|
|
|
|
|
|
|
|
return $h1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
# Numerical normalization: Avoid having different suffixes
|
|
|
|
|
# for cells with the same parameter values due to Tcl handling
|
|
|
|
|
# numerical values as strings, which makes "2", "2.0", and "2e0"
|
|
|
|
|
# all separate values. If hashed on the verbatim values, then
|
|
|
|
|
# the same device with the same parameters can have many
|
|
|
|
|
# different cell names, even though the layout is exactly the
|
|
|
|
|
# same. Avoid this by detecting when a parameter value is
|
|
|
|
|
# numeric and enforcing a consistent format (fixed precision,
|
|
|
|
|
# four decimal places).
|
|
|
|
|
#
|
|
|
|
|
# Code courtesy of ChatGPT
|
|
|
|
|
#-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::normalize_value {value} {
|
|
|
|
|
# Detect if value is numeric
|
|
|
|
|
if {[string is double -strict $value]} {
|
|
|
|
|
set num [expr {double($value)}]
|
|
|
|
|
|
|
|
|
|
# Check if effectively integer
|
|
|
|
|
if {abs($num - round($num)) < 1e-9} {
|
|
|
|
|
return [format "%d" [expr {int(round($num))}]]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Otherwise, format to fixed precision (3 decimal places)
|
|
|
|
|
set str [format "%.3f" $num]
|
|
|
|
|
|
|
|
|
|
# Strip trailing zeros
|
|
|
|
|
regsub {\.?0+$} $str "" str
|
|
|
|
|
|
|
|
|
|
return $str
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Non-numeric: return as-is
|
|
|
|
|
return $value
|
|
|
|
|
}
|