288 lines
8.7 KiB
Tcl
288 lines
8.7 KiB
Tcl
# OpenSTA, Static Timing Analyzer
|
|
# Copyright (c) 2025, Parallax Software, Inc.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# The origin of this software must not be misrepresented; you must not
|
|
# claim that you wrote the original software.
|
|
#
|
|
# Altered source versions must be plainly marked as such, and must not be
|
|
# misrepresented as being the original software.
|
|
#
|
|
# This notice may not be removed or altered from any source distribution.
|
|
|
|
namespace eval sta {
|
|
|
|
define_cmd_args "write_path_spice" { -path_args path_args\
|
|
-spice_directory spice_directory\
|
|
-lib_subckt_file lib_subckts_file\
|
|
-model_file model_file\
|
|
-power power\
|
|
-ground ground\
|
|
[-simulator hspice|ngspice|xyce]}
|
|
|
|
proc write_path_spice { args } {
|
|
parse_key_args "write_path_spice" args \
|
|
keys {-spice_directory -lib_subckt_file -model_file \
|
|
-power -ground -path_args -simulator} \
|
|
flags {}
|
|
|
|
if { [info exists keys(-spice_directory)] } {
|
|
set spice_dir [file nativename $keys(-spice_directory)]
|
|
if { ![file exists $spice_dir] } {
|
|
sta_error 1920 "Directory $spice_dir not found."
|
|
}
|
|
if { ![file isdirectory $spice_dir] } {
|
|
sta_error 1921 "$spice_dir is not a directory."
|
|
}
|
|
if { ![file writable $spice_dir] } {
|
|
sta_error 1922 "Cannot write in $spice_dir."
|
|
}
|
|
} else {
|
|
sta_error 1923 "No -spice_directory specified."
|
|
}
|
|
|
|
if { [info exists keys(-lib_subckt_file)] } {
|
|
set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
|
|
if { ![file readable $lib_subckt_file] } {
|
|
sta_error 1924 "-lib_subckt_file $lib_subckt_file is not readable."
|
|
}
|
|
} else {
|
|
sta_error 1925 "No -lib_subckt_file specified."
|
|
}
|
|
|
|
if { [info exists keys(-model_file)] } {
|
|
set model_file [file nativename $keys(-model_file)]
|
|
if { ![file readable $model_file] } {
|
|
sta_error 1926 "-model_file $model_file is not readable."
|
|
}
|
|
} else {
|
|
sta_error 1927 "No -model_file specified."
|
|
}
|
|
|
|
if { [info exists keys(-power)] } {
|
|
set power $keys(-power)
|
|
} else {
|
|
sta_error 1928 "No -power specified."
|
|
}
|
|
|
|
if { [info exists keys(-ground)] } {
|
|
set ground $keys(-ground)
|
|
} else {
|
|
sta_error 1929 "No -ground specified."
|
|
}
|
|
|
|
set ckt_sim [parse_ckt_sim_key keys]
|
|
|
|
if { ![info exists keys(-path_args)] } {
|
|
sta_error 1930 "No -path_args specified."
|
|
}
|
|
set path_args $keys(-path_args)
|
|
set path_ends [eval [concat find_timing_paths $path_args]]
|
|
if { $path_ends == {} } {
|
|
sta_error 1931 "No paths found for -path_args $path_args."
|
|
} else {
|
|
set path_index 1
|
|
foreach path_end $path_ends {
|
|
set path [$path_end path]
|
|
set path_name "path_$path_index"
|
|
set spice_file [file join $spice_dir "$path_name.sp"]
|
|
set subckt_file [file join $spice_dir "$path_name.subckt"]
|
|
write_path_spice_cmd $path $spice_file $subckt_file \
|
|
$lib_subckt_file $model_file $power $ground $ckt_sim
|
|
incr path_index
|
|
}
|
|
}
|
|
}
|
|
|
|
set ::ckt_sims {hspice ngspice xyce}
|
|
|
|
proc parse_ckt_sim_key { keys_var } {
|
|
upvar 1 $keys_var keys
|
|
global ckt_sims
|
|
|
|
set ckt_sim "ngspice"
|
|
if { [info exists keys(-simulator)] } {
|
|
set ckt_sim [file nativename $keys(-simulator)]
|
|
if { [lsearch $ckt_sims $ckt_sim] == -1 } {
|
|
sta_error 1910 "Unknown circuit simulator"
|
|
}
|
|
}
|
|
return $ckt_sim
|
|
}
|
|
|
|
################################################################
|
|
|
|
define_cmd_args "write_gate_spice" \
|
|
{ -gates {{instance input_port driver_port edge [delay]}...}\
|
|
-spice_filename spice_filename\
|
|
-lib_subckt_file lib_subckts_file\
|
|
-model_file model_file\
|
|
-power power\
|
|
-ground ground\
|
|
[-simulator hspice|ngspice|xyce]\
|
|
[-corner corner]\
|
|
[-min] [-max]}
|
|
|
|
proc write_gate_spice { args } {
|
|
parse_key_args "write_gate_spice" args \
|
|
keys {-gates -spice_filename -lib_subckt_file -model_file \
|
|
-power -ground -simulator -corner}\
|
|
flags {-measure_stmts -min -max}
|
|
|
|
if { [info exists keys(-gates)] } {
|
|
set gates $keys(-gates)
|
|
} else {
|
|
sta_error 1932 "Missing -gates argument."
|
|
}
|
|
if { [info exists keys(-spice_filename)] } {
|
|
set spice_file [file nativename $keys(-spice_filename)]
|
|
set spice_dir [file dirname $spice_file]
|
|
if { ![file writable $spice_dir] } {
|
|
sta_error 1903 "Cannot write $spice_dir."
|
|
}
|
|
} else {
|
|
sta_error 1904 "No -spice_filename specified."
|
|
}
|
|
|
|
if { [info exists keys(-lib_subckt_file)] } {
|
|
set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
|
|
if { ![file readable $lib_subckt_file] } {
|
|
sta_error 1905 "-lib_subckt_file $lib_subckt_file is not readable."
|
|
}
|
|
} else {
|
|
sta_error 1906 "No -lib_subckt_file specified."
|
|
}
|
|
|
|
if { [info exists keys(-model_file)] } {
|
|
set model_file [file nativename $keys(-model_file)]
|
|
if { ![file readable $model_file] } {
|
|
sta_error 1907 "-model_file $model_file is not readable."
|
|
}
|
|
} else {
|
|
sta_error 1908 "No -model_file specified."
|
|
}
|
|
|
|
if { [info exists keys(-power)] } {
|
|
set power $keys(-power)
|
|
} else {
|
|
sta_error 1909 "No -power specified."
|
|
}
|
|
|
|
if { [info exists keys(-ground)] } {
|
|
set ground $keys(-ground)
|
|
} else {
|
|
sta_error 1915 "No -ground specified."
|
|
}
|
|
|
|
set ckt_sim [parse_ckt_sim_key keys]
|
|
|
|
set corner [parse_corner keys]
|
|
set min_max [parse_min_max_flags flags]
|
|
check_argc_eq0 "write_gate_spice" $args
|
|
|
|
set spice_dir [file dirname $spice_file]
|
|
set spice_root [file rootname [file tail $spice_file]]
|
|
set subckt_file [file join $spice_dir "$spice_root.subckt"]
|
|
write_gate_spice_cmd $gates $spice_file $subckt_file \
|
|
$lib_subckt_file $model_file $power $ground $ckt_sim \
|
|
$corner $min_max
|
|
}
|
|
|
|
################################################################
|
|
|
|
# plot_pins defaults to input_pin, driver_pina and load pins for each driver.
|
|
define_cmd_args "write_gate_gnuplot" \
|
|
{ -gates {{instance input_port driver_port edge [delay]}...}\
|
|
-plot_pins plot_pins\
|
|
-plot_basename plot_basename\
|
|
[-corner corner] [-min] [-max]}
|
|
|
|
proc write_gate_gnuplot { args } {
|
|
parse_key_args "write_gate_gnuplot" args \
|
|
keys {-gates -plot_pins -plot_basename -spice_waveforms -corner} \
|
|
flags {-min -max}
|
|
|
|
if { [info exists keys(-gates)] } {
|
|
set gates $keys(-gates)
|
|
} else {
|
|
sta_error 1933 "Missing -gates argument."
|
|
}
|
|
if { [info exists keys(-plot_pins)] } {
|
|
set plot_pins [get_port_pins_error "-plot_pins" $keys(-plot_pins)]
|
|
} else {
|
|
set plot_pins {}
|
|
set plot_all_loads 0
|
|
set gate_idx 0
|
|
foreach gate $gates {
|
|
set in_pin [parse_gate_in_pin $gate]
|
|
set drvr_pin [parse_gate_drvr_pin $gate]
|
|
lappend plot_pins $in_pin
|
|
lappend plot_pins $drvr_pin
|
|
# Only plot driver loads.
|
|
if { $plot_all_loads || $gate_idx == 0 } {
|
|
set pin_iter [$drvr_pin connected_pin_iterator]
|
|
while { [$pin_iter has_next] } {
|
|
set pin [$pin_iter next]
|
|
if { [$pin is_load] } {
|
|
lappend plot_pins $pin
|
|
}
|
|
}
|
|
$pin_iter finish
|
|
}
|
|
incr gate_idx
|
|
}
|
|
}
|
|
|
|
if { [info exists keys(-plot_basename)] } {
|
|
set plot_base [file nativename $keys(-plot_basename)]
|
|
set plot_dir [file dirname $plot_base]
|
|
if { ![file writable $plot_dir] } {
|
|
sta_error 1913 "Cannot write $plot_dir."
|
|
}
|
|
} else {
|
|
sta_error 1914 "No -plot_basename specified."
|
|
}
|
|
set gnuplot_filename "${plot_base}.gnuplot"
|
|
set csv_filename "${plot_base}.csv"
|
|
|
|
set sim_wave_filename ""
|
|
if { [info exists keys(-spice_waveforms)] } {
|
|
set sim_wave_filename $keys(-spice_waveforms)
|
|
}
|
|
|
|
set corner [parse_corner keys]
|
|
set min_max [parse_min_max_flags flags]
|
|
|
|
write_gate_gnuplot_cmd $gates $plot_pins $sim_wave_filename \
|
|
$gnuplot_filename $csv_filename $corner $min_max
|
|
}
|
|
|
|
proc parse_gate_drvr_pin { gate_arg } {
|
|
lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
|
|
set inst [get_instance_error "instance" $inst_name]
|
|
set drvr_pin [$inst find_pin $drvr_port_name]
|
|
return $drvr_pin
|
|
}
|
|
|
|
proc parse_gate_in_pin { gate_arg } {
|
|
lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
|
|
set inst [get_instance_error "instance" $inst_name]
|
|
set in_pin [$inst find_pin $in_port_name]
|
|
return $in_pin
|
|
}
|
|
|
|
# sta namespace end.
|
|
}
|