| #------------------------------------------------------------------- | 
 | # General-purpose routines for the PDK script in all technologies | 
 | #------------------------------------------------------------------- | 
 | #  | 
 | #---------------------------------------- | 
 | # Number Conversion Functions | 
 | #---------------------------------------- | 
 |  | 
 | #--------------------- | 
 | # Microns to Lambda | 
 | #--------------------- | 
 | proc magic::u2l {micron} { | 
 |     set techlambda [magic::tech lambda] | 
 |     set tech1 [lindex $techlambda 1] | 
 |     set tech0 [lindex $techlambda 0] | 
 |     set tscale [expr {$tech1 / $tech0}] | 
 |     set lambdaout [expr {((int([magic::cif scale output] * 10000)) / 10000.0)}] | 
 |     return [expr $micron / ($lambdaout*$tscale) ] | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Lambda to Microns | 
 | #--------------------- | 
 | proc magic::l2u {lambda} { | 
 |     set techlambda [magic::tech lambda] | 
 |     set tech1 [lindex $techlambda 1] ; set tech0 [lindex $techlambda 0] | 
 |     set tscale [expr {$tech1 / $tech0}] | 
 |     set lambdaout [expr {((int([magic::cif scale output] * 10000)) / 10000.0)}] | 
 |     return [expr $lambda * $lambdaout * $tscale ] | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Internal to Microns | 
 | #--------------------- | 
 | proc magic::i2u { value } { | 
 |     return [expr {((int([magic::cif scale output] * 10000)) / 10000.0) * $value}] | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Microns to Internal | 
 | #--------------------- | 
 | proc magic::u2i {value} { | 
 |     return [expr {$value / ((int([magic::cif scale output] * 10000)) / 10000.0)}] | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Float to Spice  | 
 | #--------------------- | 
 | proc magic::float2spice {value} {  | 
 |     if {$value >= 1.0e+6} {  | 
 | 	set exponent 1e+6 | 
 | 	set unit "meg" | 
 |     } elseif {$value >= 1.0e+3} {  | 
 | 	set exponent 1e+3 | 
 | 	set unit "k" | 
 |     } elseif { $value >= 1} {  | 
 | 	set exponent 1 | 
 | 	set unit "" | 
 |     } elseif {$value >= 1.0e-3} {  | 
 | 	set exponent 1e-3 | 
 | 	set unit "m" | 
 |     } elseif {$value >= 1.0e-6} {  | 
 | 	set exponent 1e-6 | 
 | 	set unit "u" | 
 |     } elseif {$value >= 1.0e-9} {  | 
 | 	set exponent 1e-9 | 
 | 	set unit "n" | 
 |     } elseif {$value >= 1.0e-12} {  | 
 | 	set exponent 1e-12 | 
 | 	set unit "p" | 
 |     } elseif {$value >= 1.0e-15} {  | 
 | 	set exponent 1e-15 | 
 | 	set unit "f" | 
 |     } else { | 
 | 	set exponent 1e-18 | 
 | 	set unit "a" | 
 |     } | 
 |     set val [expr $value / $exponent] | 
 |     set val [expr int($val * 1000) / 1000.0] | 
 |     if {$val == 0} {set unit ""} | 
 |     return $val$unit | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Spice to Float | 
 | #--------------------- | 
 | proc magic::spice2float {value {faultval 0.0}} {  | 
 |     # Remove trailing units, at least for some common combinations | 
 |     set value [string tolower $value] | 
 |     set value [string map {um u nm n uF n nF n pF p aF a} $value] | 
 |     set value [string map {meg "* 1.0e6" k "* 1.0e3" m "* 1.0e-3" u "* 1.0e-6" \ | 
 | 		 n "* 1.0 e-9" p "* 1.0e-12" f "* 1.0e-15" a "* 1.0e-15"} $value] | 
 |     if {[catch {set rval [expr $value]}]} { | 
 | 	puts stderr "Value is not numeric!" | 
 | 	set rval $faultval | 
 |     } | 
 |     return $rval | 
 | } | 
 |  | 
 | #--------------------- | 
 | # Numeric Precision | 
 | #--------------------- | 
 | proc magic::3digitpastdecimal {value} { | 
 |     set new [expr int([expr $value * 1000 + 0.5 ]) / 1000.0] | 
 |     return $new | 
 | } | 
 |  | 
 | #------------------------------------------------------------------- | 
 | # File Access Functions | 
 | #------------------------------------------------------------------- | 
 |  | 
 | #------------------------------------------------------------------- | 
 | # Ensures that a cell name does not already exist, either in | 
 | # memory or on disk. Modifies the name until it does. | 
 | #------------------------------------------------------------------- | 
 | proc magic:cellnameunique {cellname} { | 
 |     set i 0 | 
 |     set newname $cellname | 
 |     while {[cellname list exists $newname] != 0 || [magic::searchcellondisk $newname] != 0} { | 
 | 	incr i | 
 | 	set newname ${cellname}_$i | 
 |     } | 
 |     return $newname | 
 | } | 
 |  | 
 | #------------------------------------------------------------------- | 
 | # Looks to see if a cell exists on disk | 
 | #------------------------------------------------------------------- | 
 | proc magic::searchcellondisk {name} { | 
 |     set rlist {} | 
 |     foreach dir [path search] { | 
 | 	set ftry [file join $dir ${name}.mag] | 
 | 	if [file exists $ftry] { | 
 | 	    return 1 | 
 | 	} | 
 |     } | 
 |     return 0 | 
 | }  | 
 |  | 
 | #------------------------------------------------------------------- | 
 | # Checks to see if a cell already exists on disk or in memory | 
 | #------------------------------------------------------------------- | 
 | proc magic::iscellnameunique {cellname} { | 
 |     if {[cellname list exists $cellname] == 0 && [magic::searchcellondisk $cellname] == 0} {  | 
 | 	return 1 | 
 |     } else { | 
 | 	return 0 | 
 |     } | 
 | } | 
 |  | 
 | #-------------------------------------------------------------- | 
 | # Procedure that checks the user's "ip" subdirectory on startup | 
 | # and adds each one's maglef subdirectory to the path. | 
 | #-------------------------------------------------------------- | 
 |  | 
 | proc magic::query_mylib_ip {} { | 
 |     global TECHPATH | 
 |     global env | 
 |     if [catch {set home $env(SUDO_USER)}] { | 
 |         set home $env(USER) | 
 |     } | 
 |     set homedir /home/${home} | 
 |     set ip_dirs [glob -directory ${homedir}/design/ip *] | 
 |     set proj_dir [pwd] | 
 |     set config_dir .config | 
 |     set info_dir ${proj_dir}/${config_dir} | 
 |     if {![file exists ${info_dir}]} { | 
 | 	set config_dir .ef-config | 
 | 	set info_dir ${proj_dir}/${config_dir} | 
 |     } | 
 |  | 
 |     set info_file ${info_dir}/info | 
 |     set depends [dict create] | 
 |     if {![catch {open $info_file r} ifd]} { | 
 |         set depsec false | 
 |         while {[gets $ifd line] >= 0} { | 
 | 	    if {[string first dependencies: $line] >= 0} { | 
 | 	        set depsec true | 
 | 	    } | 
 | 	    if {$depsec} { | 
 | 		if {[string first version: $line] >= 0} { | 
 | 		    if {$ipname != ""} { | 
 | 			set ipvers [string trim [lindex [split $line] 1] '] | 
 | 			dict set depends $ipname $ipvers | 
 | 			set ipname "" | 
 | 		    } else { | 
 | 			puts stderr "Badly formatted info file in ${config_dir}!" | 
 | 		    } | 
 | 		} else { | 
 | 		    set ipname [string trim $line :] | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     foreach dir $ip_dirs { | 
 | 	# Version handling:  version dependencies are found in | 
 | 	# ${config_dir}/info.  For all other IP, use the most recent | 
 | 	# version number. | 
 | 	set ipname [lindex [file split $dir] end] | 
 | 	if {![catch {set version [dict get $depends $ipname]}]} { | 
 | 	    if {[file isdirectory ${dir}/${version}/maglef]} { | 
 | 		addpath ${dir}/${version}/maglef | 
 | 		continue | 
 | 	    } else { | 
 | 		puts stderr "ERROR:  Dependency ${ipname} version ${version} does not exist" | 
 | 	    } | 
 | 	} | 
 |  | 
 | 	# Secondary directory is the version number.  Use the highest | 
 | 	# version available. | 
 |  | 
 | 	set sub_dirs {} | 
 |         catch {set sub_dirs [glob -directory $dir *]} | 
 | 	set maxver 0.0 | 
 | 	foreach subdir $sub_dirs { | 
 | 	    set vidx [string last / $subdir] | 
 | 	    incr vidx | 
 | 	    set version [string range $subdir $vidx end] | 
 | 	    if {$version > $maxver} { | 
 | 		set maxver $version | 
 | 	    } | 
 | 	} | 
 | 	if {[file exists ${dir}/${maxver}/maglef]} { | 
 | 	    # Compatibility rule:  foundry name must match. | 
 | 	    # Get foundry name from ${config_dir}/techdir symbolic link reference | 
 | 	    if {[file exists ${dir}/${maxver}/${config_dir}/techdir]} { | 
 | 		set technodedir [file link ${dir}/${maxver}/${config_dir}/techdir] | 
 | 		set nidx [string last / $technodedir] | 
 | 		set techdir [string range $technodedir 0 $nidx-1] | 
 | 		if {$techdir == $TECHPATH} { | 
 | 		    addpath ${dir}/${maxver}/maglef | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | #-------------------------------------------------------------- | 
 | # Procedure that checks the user's design directory on startup | 
 | # and adds each one's mag subdirectory to the path. | 
 | #-------------------------------------------------------------- | 
 |  | 
 | proc magic::query_my_projects {} { | 
 |     global TECHPATH | 
 |     global env | 
 |     if [catch {set home $env(SUDO_USER)}] { | 
 |         set home $env(USER) | 
 |     } | 
 |     set homedir /home/${home} | 
 |     set proj_dirs [glob -directory ${homedir}/design *] | 
 |     foreach dir $proj_dirs { | 
 | 	# Compatibility rule:  foundry name must match. | 
 | 	# Get foundry name from ${config_dir}/techdir symbolic link reference | 
 | 	if {[file exists ${dir}/mag]} { | 
 | 	    set config_dir .config | 
 | 	    set tech_dir ${dir}/${config_dir} | 
 | 	    if {![file exists ${tech_dir}]} { | 
 | 		set config_dir .ef-config | 
 | 		set tech_dir ${dir}/${config_dir} | 
 | 	    } | 
 | 	    if {[file exists ${dir}/${config_dir}/techdir]} { | 
 | 		set technodedir [file link ${dir}/${config_dir}/techdir] | 
 | 		set nidx [string last / $technodedir] | 
 | 		set techdir [string range $technodedir 0 $nidx-1] | 
 | 		if {$techdir == $TECHPATH} { | 
 | 		    addpath ${dir}/mag | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | #---------------------------------------------------------------- |