diff --git a/xschem_library/analyses/analyses.init.tcl b/xschem_library/analyses/analyses.init.tcl index 91665ea3..a160923b 100644 --- a/xschem_library/analyses/analyses.init.tcl +++ b/xschem_library/analyses/analyses.init.tcl @@ -48,22 +48,32 @@ proc format_props {symname props} { return [join $args "\n"] } -# Helper function for spectre netlisting +# Helper function for spectre/spice netlisting # N .. normal # NV .. normal, value only +# UNV .. unquote, normal, value only # S .. string (dump quoted) -proc format_args_spectre {symname props} { +proc format_args {symname props} { set str "" set args {} foreach {propname type} $props { set val [ xschem getprop instance $symname $propname ] set len [string len $val] + # Unquote value if requested + if { $type eq "UNV" } { + if {[string match "\"*\"" $val]} { + set val [string range $val 1 end-1] + } + } + # Add to formatted arguments list if {$len > 0} { set first false if { $type eq "N" } { lappend args "$propname=$val" } elseif { $type eq "NV" } { lappend args "$val" + } elseif { $type eq "UNV" } { + lappend args "$val" } elseif { $type eq "S" } { lappend args "$propname=\"$val\"" } @@ -120,7 +130,7 @@ proc display_dcinc {symname} { # Display DCXF analysis proc display_dcxf {symname} { - set names [list sweep N outp N outn N nodeset SG store N write N writeop N] + set names [list sweep N outp N outn N in N nodeset SG store N write N writeop N] return [format_props $symname $names] } @@ -160,12 +170,20 @@ proc display_postprocess {symname} { return [format_props $symname $names] } -# Netlister for spectre +# +# Netlister +# + proc netlister {netlist_type} { set cmds {} set types [dict create] set prefix "netlist_command_" foreach {name symfile type} [xschem instance_list] { + # Do not netlist entry point symbol + if {[string match netlist_command_header $type]} { + continue + } + # Collect only symbols of type netlist_* if {[string match netlist_* $type]} { dict set types $name $type } @@ -188,21 +206,31 @@ proc netlister {netlist_type} { set suffix [string range $type [string length $prefix] end] # Construct formatter function name set func [join [list "format_" "$suffix" "_" "$netlist_type"] ""] - try { + #try { set cmd [$func $name] if {[string length $cmd] > 0} { - lappend blocks [format_sweep_chain_spectre $name $cmd types] + lappend blocks [format_sweep_chain_$netlist_type $name $cmd types] } - } on error (msg) { - puts "Error during formatting command $name" - continue - } + #} on error (msg) { + # puts "Error during formatting command $name" + # continue + #} } - set control [indent [join $blocks "\n"] " "] + return [wrap_control_$netlist_type $blocks] +} + + +# +# Netlister for VACASK +# + +# Wrap in control block +proc wrap_control_spectre {cmds} { + set control [indent [join $cmds "\n"] " "] return [join [list "//// begin user architecture code" "control" "$control" "endc" "//// end user architecture code"] "\n"] } -# Add sweep chain to analysis (spectre) +# Add sweep chain to analysis proc format_sweep_chain_spectre {name anstr types} { upvar 1 $types typesdict set sweep [ xschem getprop instance $name sweep ] @@ -213,14 +241,14 @@ proc format_sweep_chain_spectre {name anstr types} { # Yes, reverse sweep chain set sweeplist [lreverse $sweeplist] # Join sweeps - set sweeps [join [lreverse $sweeplist] "\n"] + set sweeps [join $sweeplist "\n"] return "$sweeps\n $anstr" } else { return "$anstr" } } -# Sweep formatter (spectre), construct sweep chain, innermost first +# Sweep formatter, construct sweep chain, innermost first proc format_sweep_spectre {parent sweeplist types} { upvar 1 $sweeplist swl upvar 1 $types typesdict @@ -246,22 +274,21 @@ proc format_sweep_spectre {parent sweeplist types} { # Fomat a single sweep proc format_single_sweep_spectre {sweep} { - # Parent has sweep property set tag [ xschem getprop instance $sweep tag ] return "sweep $tag ( [format_sweep_spectre_params $sweep] [format_sweep_spectre_range $sweep] )" } -# Sweep formatter (spectre), what to sweep +# Sweep formatter, what to sweep proc format_sweep_spectre_params {name} { - return [format_args_spectre $name [list instance N model N parameter N "option" N "variable" N]] + return [format_args $name [list instance N model N parameter N "option" N "variable" N]] } -# Sweep formatter (spectre), how to sweep +# Sweep formatter, how to sweep proc format_sweep_spectre_range {name} { - return [format_args_spectre $name [list from N to N step N mode N points N "values" N continuation N]] + return [format_args $name [list from N to N step N mode N points N "values" N continuation N]] } -# Analysis formatters, spectre +# Analysis formatters proc format_verbatim_spectre {name} { set sim [ xschem getprop instance $name simulator ] set dump false @@ -277,17 +304,17 @@ proc format_verbatim_spectre {name} { if { !$dump } { return "" } - return [format_args_spectre $name [list verbatim NV]] + return [format_args $name [list verbatim NV]] } proc format_analysis_op_spectre {name} { - set args [format_args_spectre $name [list nodeset N store N write N]] + set args [format_args $name [list nodeset N store N write N]] return "analysis $name op [parenthesize $args]" } proc format_analysis_dc1d_spectre {name} { # OP formatting - set args [format_args_spectre $name [list nodeset N store N write N]] + set args [format_args $name [list nodeset N store N write N]] set anstr "analysis $name op [parenthesize $args]" # 1D sweep formatting set swp [format_single_sweep_spectre $name] @@ -295,7 +322,7 @@ proc format_analysis_dc1d_spectre {name} { } proc format_analysis_dcinc_spectre {name} { - set args [format_args_spectre $name [list nodeset N store N write N writeop N]] + set args [format_args $name [list nodeset N store N write N writeop N]] return "analysis $name dcinc [parenthesize $args]" } @@ -312,49 +339,238 @@ proc format_signal_output_spectre {name} { proc format_analysis_dcxf_spectre {name} { set args "out=[format_signal_output_spectre $name] " - append args [format_args_spectre $name [list nodeset N store N write N writeop N]] + append args [format_args $name [list nodeset N store N write N writeop N]] return "analysis $name dcxf [parenthesize $args]" } proc format_analysis_ac_spectre {name} { set args "[format_sweep_spectre_range $name] " - append args [format_args_spectre $name [list nodeset N store N write N writeop N]] + append args [format_args $name [list nodeset N store N write N writeop N]] return "analysis $name ac [parenthesize $args]" } proc format_analysis_acxf_spectre {name} { set args "out=[format_signal_output_spectre $name] " append args "[format_sweep_spectre_range $name] " - append args [format_args_spectre $name [list nodeset N store N write N writeop N]] + append args [format_args $name [list nodeset N store N write N writeop N]] return "analysis $name acxf [parenthesize $args]" } proc format_analysis_noise_spectre {name} { set args "out=[format_signal_output_spectre $name] " - append args "[format_args_spectre $name [list in N]] " + append args "[format_args $name [list in N]] " append args "[format_sweep_spectre_range $name] " - append args [format_args_spectre $name [list nodeset N store N write N writeop N]] + append args [format_args $name [list nodeset N store N write N writeop N]] return "analysis $name noise [parenthesize $args]" } proc format_analysis_tran_spectre {name} { - set args [format_args_spectre $name [list step N stop N start N maxstep N icmode N nodeset N ic N store N write N]] + set args [format_args $name [list step N stop N start N maxstep N icmode N nodeset N ic N store N write N]] return "analysis $name tran [parenthesize $args]" } proc format_analysis_hb_spectre {name} { - set args [format_args_spectre $name [list freq N nharm N immax N truncate N samplefac N nper N sample N harmonic N imorder N nodeset N store N write N]] + set args [format_args $name [list freq N nharm N immax N truncate N samplefac N nper N sample N harmonic N imorder N nodeset N store N write N]] return "analysis $name hb [parenthesize $args]" } proc format_postprocess_spectre {name} { - set tool [format_args_spectre $name [list tool NV]] - set file [format_args_spectre $name [list file NV]] + set tool [format_args $name [list tool NV]] + set file [format_args $name [list file NV]] return "postprocess($tool, $file)" } -# TODO: add x, meg, mil to vacask SI prefixes to VACASK -# TODO: add all VACASK analyses + +# +# Netlister for Ngspice +# + +# Wrap in control block +proc wrap_control_spice {cmds} { + set control [join $cmds "\n"] + return [join [list "**** begin user architecture code" ".control" "$control" ".endc" "**** end user architecture code"] "\n"] +} + +# Add sweep chain to analysis +proc format_sweep_chain_spice {name anstr types} { + upvar 1 $types typesdict + # Get analysis type (op/dc1d or ac/noise) + set antype [dict get $typesdict $name] + # Is it a dec/oct/lin sweep + if {$antype eq "netlist_command_analysis_ac"} { + set decoctlin true + } elseif {$antype eq "netlist_command_analysis_noise"} { + set decoctlin true + } elseif {$antype eq "netlist_command_analysis_op"} { + # For swept op rename analysis to dc + set decoctlin false + set anstr "dc" + } else { + set decoctlin false + } + set sweep [ xschem getprop instance $name sweep ] + set sweeplist {} + format_sweep_spice $decoctlin $name sweeplist typesdict + # TODO: maximal sweep chain depths + # - op 2 + # - dc1d 1 + # - all others 0 + # Do we have any sweeps + if {[llength $sweeplist] > 0} { + # Yes, join sweeps + set sweeps [join $sweeplist " "] + return "$anstr $sweeps" + } else { + return "$anstr" + } +} + +# Sweep formatter, construct sweep chain, innermost first +proc format_sweep_spice {decoctlin parent sweeplist types} { + upvar 1 $sweeplist swl + upvar 1 $types typesdict + set sweep [ xschem getprop instance $parent sweep ] + if {[string length $sweep] > 0} { + # Parent has sweep property + set tag [ xschem getprop instance $sweep tag ] + set type [dict get $typesdict $sweep] + if {[string length $tag] > 0 && ($type eq "netlist_modifier_sweep")} { + # Sweep has tag property + try { + lappend swl [format_single_sweep_spice $decoctlin $sweep] + } on error (msg) { + # Stop traversing chain on error + puts "Error during formating command $sweep" + return + } + # Recursion into parent sweep + format_sweep_spice $decoctlin $sweep swl typesdict + } + } +} + +# Fomat a single sweep +proc format_single_sweep_spice {decoctlin sweep} { + return "[format_sweep_spice_params $sweep] [format_sweep_spice_range $decoctlin $sweep]" +} + +# Sweep formatter, what to sweep +proc format_sweep_spice_params {name} { + return [format_args $name [list instance UNV]] +} + +# Sweep formatter, how to sweep +proc format_sweep_spice_range {decoctlin name} { + if {$decoctlin} { + return [format_args $name [list mode UNV points NV from NV to NV]] + } else { + return [format_args $name [list from NV to NV step NV]] + } +} + +proc format_verbatim_spice {name} { + return [format_verbatim_spectre $name] +} + +proc format_analysis_op_spice {name} { + return "op" +} + +proc format_analysis_dc1d_spice {name} { + # 1D sweep formatting + set swp [format_single_sweep_spice false $name] + return "dc $swp" +} + +proc format_analysis_dcinc_spice {name} { + puts "$name: dcinc is not supported by Ngspice" + return "" +} + +proc format_signal_output_spice {name} { + set outp [ xschem getprop instance $name outp ] + set outn [ xschem getprop instance $name outn ] + # If outp starts with v(, use it literally, ignore outn + if {[string match "v(*" $outp]} { + return $outp + } + # If outp starts with i(, use it literally, ignore outn + if {[string match "i(*" $outp]} { + return $outp + } + # Unquote outp, outn + if {[string match "\"*\"" $outp]} { + set outp [string range $outp 1 end-1] + } + if {[string match "\"*\"" $outn]} { + set outn [string range $outn 1 end-1] + } + # Is outn empty + if {[string length $outn] == 0} { + return "v($outp)" + } else { + return "v($outp,$outn)" + } +} + +proc format_analysis_dcxf_spice {name} { + set output "[format_signal_output_spice $name]" + return "tf $output [format_args $name [list in UNV]]" +} + +proc format_analysis_ac_spice {name} { + set swp "[format_sweep_spice_range true $name] " + return "ac $swp" +} + +proc format_analysis_acxf_spice {name} { + puts "$name: acxf is not supported by Ngspice" + return "" +} + +proc format_analysis_noise_spice {name} { + set output "[format_signal_output_spice $name]" + set swp "[format_sweep_spice_range true $name] " + return "noise $output [format_args $name [list in UNV]] $swp [format_args $name [list ptssum NV]]" +} + +proc format_analysis_tran_spice {name} { + set args [format_args $name [list step NV stop NV start NV maxstep NV]] + set icmode [format_args $name [list icmode UNV]] + if {$icmode eq "uic"} { + append args " uic" + } + return "tran $args" +} + +proc format_analysis_hb_spice {name} { + puts "$name: hb is not supported by Ngspice" + return "" +} + +proc format_postprocess_spice {name} { + global tcl_platform + set tool [format_args $name [list tool NV]] + if {[string match "\"*\"" $tool]} { + # Quoted, unquote, use literally + set tool [string range $tool 1 end-1] + } else { + # Not quoted, check if it is PYTHON (VACASK variable for autodetected Python) + if {$tool eq "PYTHON"} { + if {$tcl_platform(platform) eq "windows"} { + set tool "python.exe" + } else { + set tool "python3" + } + } + } + # Keep quotes around file + set file [format_args $name [list file NV]] + return "shell $tool $file" +} + + + # TODO: add ngspice formatting } diff --git a/xschem_library/analyses/dcxf.sym b/xschem_library/analyses/dcxf.sym index cd34f455..ddf2b036 100644 --- a/xschem_library/analyses/dcxf.sym +++ b/xschem_library/analyses/dcxf.sym @@ -27,6 +27,7 @@ order=\\"\\" sweep=\\"\\" outp=\\"out\\" outn=\\"\\" +in="\\"\\" nodeset=\\"\\" store=\\"\\" write=\\"\\" diff --git a/xschem_library/analyses/demo.sch b/xschem_library/analyses/demo.sch index d40c65b5..799824cf 100644 --- a/xschem_library/analyses/demo.sch +++ b/xschem_library/analyses/demo.sch @@ -102,6 +102,7 @@ order="" sweep="" outp="\\"out\\"" outn="" +in="\\"v1\\"" nodeset="" store="" write=""