From 4c6f75ea85ddf840c2b74b1bc5e139734115ccc6 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 30 Dec 2025 15:13:58 +0100 Subject: [PATCH 01/70] Update the reference to the manual --- examples/various/transformers1.cir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/various/transformers1.cir b/examples/various/transformers1.cir index cf26601b4..6e9b224ab 100644 --- a/examples/various/transformers1.cir +++ b/examples/various/transformers1.cir @@ -35,7 +35,7 @@ Xtr3 1 0 22 0 tr3 .ends * transformer 2 -* ngspice manual 12.2.20 and 12.2.21 +* ngspice manual 8.2.21 and 8.2.22 * px primary nodes, sx secondary nodes of electric circuit * mcx nodes of magnetic circuit .subckt tr2 p1 p2 s1 s2 From e9440d99f9cbd216dc31b2df7a2312058134e11a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 2 Jan 2026 15:27:39 +0100 Subject: [PATCH 02/70] F5 example removed, obsolete or needs to be improved. --- examples/optran/F5TurboV2thermal-optran.cir | 79 --------------------- examples/optran/F5models.lib | 78 -------------------- examples/optran/contents.txt | 5 -- examples/optran/script-optran.txt | 55 -------------- 4 files changed, 217 deletions(-) delete mode 100644 examples/optran/F5TurboV2thermal-optran.cir delete mode 100644 examples/optran/F5models.lib delete mode 100644 examples/optran/script-optran.txt diff --git a/examples/optran/F5TurboV2thermal-optran.cir b/examples/optran/F5TurboV2thermal-optran.cir deleted file mode 100644 index a9fc6d0b7..000000000 --- a/examples/optran/F5TurboV2thermal-optran.cir +++ /dev/null @@ -1,79 +0,0 @@ -.title KiCad schematic -.include "F5models.lib" -.include "script-optran.txt" -R2 in 0 47.5k -R1 Net-_Q1-Pad2_ in 1k -R5 +32 Net-_P1-Pad1_ 1k -R3 Net-_P3-Pad1_ 0 10 -R4 0 Net-_P3-Pad3_ 10 -R6 Net-_P2-Pad1_ -32 1k -R7 out Net-_P3-Pad1_ 220 -R8 out Net-_P3-Pad1_ 220 -R9 out Net-_P3-Pad3_ 220 -R10 out Net-_P3-Pad3_ 220 -R11 Net-_R11-Pad1_ Net-_P1-Pad1_ 2.2k -R13 Net-_Q3-Pad2_ Net-_P1-Pad1_ 47.5 -R15 Net-_Q5-Pad2_ Net-_P2-Pad1_ 47.5 -R12 Net-_R12-Pad1_ Net-_P2-Pad1_ 2.2k -R17 +32 Net-_D3a1-Pad1_ 1 -R21 Net-_D1a1-Pad2_ -32 1 -R18 +32 Net-_D3a1-Pad1_ 1 -R22 Net-_D1a1-Pad2_ -32 1 -R16 Net-_Q6-Pad2_ Net-_P2-Pad1_ 47.5 -R14 Net-_Q4-Pad2_ Net-_P1-Pad1_ 47.5 -R19 +32 Net-_D4a1-Pad1_ 1 -R23 Net-_D2a1-Pad2_ -32 1 -R20 +32 Net-_D4a1-Pad1_ 1 -R24 Net-_D2a1-Pad2_ -32 1 -Ra2 Net-_Ra1-Pad2_ 0 4 -D3a1 +32 Net-_D3a1-Pad1_ DMOD -D1a1 Net-_D1a1-Pad2_ -32 DMOD -D3b1 +32 Net-_D3a1-Pad1_ DMOD -D1b1 Net-_D1a1-Pad2_ -32 DMOD -D4a1 +32 Net-_D4a1-Pad1_ DMOD -D2a1 Net-_D2a1-Pad2_ -32 DMOD -D4b1 +32 Net-_D4a1-Pad1_ DMOD -D2b1 Net-_D2a1-Pad2_ -32 DMOD -XP3 Net-_P3-Pad1_ 0 Net-_P3-Pad3_ RPOT value=200 ratio={rp3} -XP1 Net-_P1-Pad1_ +32 +32 RPOT value=5k ratio={rp1} -XP2 Net-_P2-Pad1_ -32 -32 RPOT value=5k ratio={rp2} -V3 in 0 dc 0 ac 1 sin(0 2 1k 5m) -JQ1 Net-_P1-Pad1_ Net-_Q1-Pad2_ Net-_P3-Pad1_ 2SK170 -JQ2 Net-_P2-Pad1_ Net-_Q1-Pad2_ Net-_P3-Pad3_ 2SJ74 -MQ5 out Net-_Q5-Pad2_ Net-_D1a1-Pad2_ Q5tj Q5tc IRFP240 thermal -MQ4 out Net-_Q4-Pad2_ Net-_D4a1-Pad1_ Q4tj Q4tc IRFP9240 thermal -MQ6 out Net-_Q6-Pad2_ Net-_D2a1-Pad2_ Q6tj Q6tc IRFP240 thermal -Rj1 Q3tj 0 1G -Rj2 Q4tj 0 1G -Rj3 Q5tj 0 1G -Rj4 Q6tj 0 1G -Rc2 Q3hs Net-_Rc2-Pad2_ {hs} -Rc4 Q4hs Net-_Rc2-Pad2_ {hs} -Rc6 Q5hs Net-_Rc2-Pad2_ {hs} -Rc8 Q6hs Net-_Rc2-Pad2_ {hs} -Rc1 Q3tc Q3hs {chs} -Rc3 Q4tc Q4hs {chs} -Rc5 Q5tc Q5hs {chs} -Rc7 Q6tc Q6hs {chs} -Vt1 Net-_Rc2-Pad2_ 0 {envtemp} -Cc1 Q3hs 0 {hscc} -Cc3 Q5hs 0 {hscc} -Cc4 Q6hs 0 {hscc} -V1 +32 0 dc 32 ; pulse(0 32 0.4m 2m 2m 100 200) -V2 -32 0 dc -32 ; pulse(0 -32 0.4m 2m 2m 100 200) -XTH2 Net-_D1a1-Pad2_ Q5hs Net-_R12-Pad1_ th R25=4.7k -XTH1 Net-_D3a1-Pad1_ Q3hs Net-_R11-Pad1_ th R25=4.7k -MQ3 out Net-_Q3-Pad2_ Net-_D3a1-Pad1_ Q3tj Q3tc IRFP9240 thermal -Vs1 Net-_S1-Pad3_ 0 dc 0 pulse(0 5 6 1m 1m 20 20) -XS1 Net-_Ra1-Pad2_ 0 Net-_S1-Pad3_ 0 genrelay -Cc2 Q4hs 0 {hscc} -Ra1 out Net-_Ra1-Pad2_ 4 -.tran 20u 5 -.param envtemp=40 -.param chs=1 -.param hs=1.2 -.param hscc=1 -.param rp2=0.21 -.param rp1=0.36 -.param rp3=0.505 -.end diff --git a/examples/optran/F5models.lib b/examples/optran/F5models.lib deleted file mode 100644 index b1e8b3c46..000000000 --- a/examples/optran/F5models.lib +++ /dev/null @@ -1,78 +0,0 @@ -* The models of this library are in the public domain -* from https://www.diyaudio.com/forums/solid-state/252973-2sk170-2sj74-spice-model-pass-0-4ma.html -*2SJ74 Dep-Mode 20mA 400mW LowNoise pkg:TO-92B 2,1,3 -.MODEL 2SJ74 PJF(Beta=92.12m Rs=7.748 Rd=7.748 Lambda=4.464m -+Vto=-.5428 Cgd=85.67p Pb=.3905 Fc=.5 -+Cgs=78.27p Is=12.98p -+Kf=26.64E-18 Af=1) - -*2SK170 -* 20mA 400mW LowNoise Dep-Mode pkg:TO-92B 3,1,2 -.MODEL 2SK170 NJF(Beta=59.86m Rs=4.151 Rd=4.151 Lambda=1.923m -+Vto=-.5024 Cgd=20p Pb=.4746 Fc=.5 -+Cgs=25.48p Is=8.477p -+Kf=111.3E-18 Af=1) - -.subckt RPOT 1 2 3 -R1 1 2 {value*ratio + 1m} -R2 2 3 {value*(1-ratio)+ 1m} -* below are default parameters, which are required by some simulators -.param value=1k -.param ratio=1 -.ends - -.model IRFP240 VDMOS nchan -+ Vto=4 Kp=5.9 Lambda=.001 Theta=0.015 ksubthres=.27 -+ Rd=61m Rs=18m Rg=3 Rds=1e7 -+ Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n -+ Is=60p N=1.1 Rb=14m XTI=3 -+ Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 -+ Rthjc=0.4 Cthj=0.1 -+ mtriode=0.8 - -.model IRFP9240 VDMOS pchan -+ Vto=-4 Kp=8.8 Lambda=.003 Theta=0.08 ksubthres=.35 -+ Rd=180m Rs=50m Rg=3 Rds=1e7 -+ Cgdmax=1.25n Cgdmin=50p a=0.23 Cgs=1.15n -+ Is=150p N=1.3 Rb=16m XTI=2 -+ Cjo=1.3n Vj=0.8 m=0.5 -+ tcvth=0.004 MU=-1.27 texp0=1.5 -+ Rthjc=0.4 Cthj=0.1 -+ mtriode=0.6 - -.model DMOD D - -* Thermistor model -.subckt th n1 nt n2 -.param B=3977 -.param R25=4700 -*control node -Ctherm1 n1 0 100p -Ctherm2 n2 0 100p -Rtherm n1 n2 R = {R25*exp(B*(1/(v(nt)+273.15)-1/(25+273.15)))} -.ends - -* generic relay model -.subckt genrelay out1 out2 in1 in2 -.param ron = 10m -S1 out1 out2 in1 in2 SW -.MODEL SW VSWITCH(VON=4V VOFF=1V RON={ron} ROFF=100K) -.ends - -.MODEL IXTH16N10 VDMOS Nchan Vds=100 -+ VTO=-3.2 KP=9 -+ Lambda=2m -+ Mtriode=1.8 -+ Ksubthres=120m -+ subshift=160m -+ Rs=4m Rd=5m Rds=200e6 -+ Cgdmax=9000p Cgdmin=300p A=0.25 -+ Cgs=5500p Cjo=11000p -+ Is=10e-6 Rb=8m -+ BV=200 IBV=250e-6 -+ NBV=4 -+ TT=250e-9 -+ vq=100 -+ rq=0.1 -+ Rthjc=0.2 Cthj=0.1 diff --git a/examples/optran/contents.txt b/examples/optran/contents.txt index 16ac7920a..a9cfbbddb 100644 --- a/examples/optran/contents.txt +++ b/examples/optran/contents.txt @@ -1,10 +1,5 @@ ngspice input files using optran -Pass Labs F5 turbo -thermal simulation -use optran for 4s and coarse steps to obtain stable temperature -then simulate transient with high resolution - HiPass3opamps_optran.cir Just a check with three different OpAmps diff --git a/examples/optran/script-optran.txt b/examples/optran/script-optran.txt deleted file mode 100644 index 8da005aeb..000000000 --- a/examples/optran/script-optran.txt +++ /dev/null @@ -1,55 +0,0 @@ -.temp {envtemp} -.param envtemp=40 -.option savecurrents -.control -set controlswait -if $?sharedmode -* script for shared ngspice -version -rusage -else -*** script for standard ngspice -* Start optran without first iteration, -* without gmin stepping and without src stepping -* optran step size 10 ms, duration 40s, -optran 1 0 0 10m 40 0 -* check the first 10 ms, input is switched on after 5 ms -tran 1u 10m -echo -* output power -let po = @ra1[i] * v(out) -meas tran power_rms rms po from=5m to=10m -echo -rusage -* temperature transistor Q6 -set xbrushwidth=4 -settype temperature q6tj q6tc q6hs -plot q6tj q6tc q6hs q3tj q3tc q3hs ylimit 40 140 -set xbrushwidth=2 -* input and output voltages -plot in out -* power supply currents -*plot V1#branch V2#branch ylimit -50 50 -plot V1#branch V2#branch ylimit -10 10 -* output current -plot @ra1[i] -*plot @ra1[i] xlimit 0 5m ylimit -0.3 0.3 -* resistance of thermistor2 TH1, TH2 -let rth1 = (v("net-_d3a1-pad1_") - v("net-_r11-pad1_")) / (@b.xth1.brtherm[i] + 1n) -let rth2 = (v("net-_d1a1-pad2_") - v("net-_r12-pad1_")) / (@b.xth2.brtherm[i] + 1n) -settype impedance rth1 rth2 -plot rth1 rth2 ylimit 0 5k - -ac dec 10 1 1Meg -plot db(out) -plot cph(out) - -tran 1u 100m -fft out -set xbrushwidth=4 -set color0=white -plot mag(out) xlimit 0 10k -* to see the harmonics -plot mag(out) xlimit 0 10k ylimit 0 30m -end -.endc \ No newline at end of file From 00803f7c1f3a3503aca392e2f031cc9f45e9619a Mon Sep 17 00:00:00 2001 From: dwarning Date: Fri, 2 Jan 2026 20:28:45 +0100 Subject: [PATCH 03/70] Adapt VDMOS Vth temperature coefficient to usual notation with - for nch and + for pch. --- examples/probe/F5models.lib | 2 +- examples/probe/VDMOS_models.lib | 2 +- examples/vdmos/100W.sp | 2 +- examples/vdmos/100W_wingspread.sp | 2 +- examples/vdmos/crss_coss_ciss.sp | 2 +- examples/vdmos/dcdc.sp | 2 +- examples/vdmos/self-heating.sp | 2 +- examples/vdmos/soa_chk.sp | 2 +- src/spicelib/devices/vdmos/vdmostemp.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/probe/F5models.lib b/examples/probe/F5models.lib index ef74eb2f5..2b9086554 100644 --- a/examples/probe/F5models.lib +++ b/examples/probe/F5models.lib @@ -27,7 +27,7 @@ R2 2 3 {value*(1-ratio)+ 1m} + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 + Rthjc=0.4 Cthj=0.1 + mtriode=0.8 diff --git a/examples/probe/VDMOS_models.lib b/examples/probe/VDMOS_models.lib index b53103398..a95cfe5f5 100644 --- a/examples/probe/VDMOS_models.lib +++ b/examples/probe/VDMOS_models.lib @@ -4,7 +4,7 @@ + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 + Rthjc=0.4 Cthj=0.1 + mtriode=0.8 diff --git a/examples/vdmos/100W.sp b/examples/vdmos/100W.sp index c8e7d2962..6fcd63d00 100644 --- a/examples/vdmos/100W.sp +++ b/examples/vdmos/100W.sp @@ -84,7 +84,7 @@ plot inoise_spectrum + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 + Rthjc=0.4 Cthj=5e-3 + mtriode=0.8 .model IRFP9240 VDMOS pchan diff --git a/examples/vdmos/100W_wingspread.sp b/examples/vdmos/100W_wingspread.sp index 3af6383e5..5e18917db 100644 --- a/examples/vdmos/100W_wingspread.sp +++ b/examples/vdmos/100W_wingspread.sp @@ -67,7 +67,7 @@ end + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 *+ Rthjc=0.4 Cthj=5e-3 + mtriode=0.8 .model IRFP9240 VDMOS pchan diff --git a/examples/vdmos/crss_coss_ciss.sp b/examples/vdmos/crss_coss_ciss.sp index 44f44bb94..6ff310399 100644 --- a/examples/vdmos/crss_coss_ciss.sp +++ b/examples/vdmos/crss_coss_ciss.sp @@ -40,7 +40,7 @@ plot 'i(v6)/deriv(v(d4))' vs v(d2p) xlog xlimit 1 100 ylimit 0 3n title "IRFP924 + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 *+ Rthjc=0.4 Cthj=5e-3 + mtriode=0.8 .model IRFP9240 VDMOS pchan diff --git a/examples/vdmos/dcdc.sp b/examples/vdmos/dcdc.sp index 6e32a54de..46b239b89 100644 --- a/examples/vdmos/dcdc.sp +++ b/examples/vdmos/dcdc.sp @@ -26,7 +26,7 @@ V4 out2 0 0.0 + Cgdmax=.2n Cgdmin=.05n a=0.3 Cgs=.12n + Is=17p N=1.1 Rb=80m XTI=3 + Cjo=.25n Vj=0.8 m=0.5 -+ tcvth=0.007 MU=-1.27 texp0=1.5 ++ tcvth=-0.007 MU=-1.27 texp0=1.5 .model MBRS340 D(Is=22.6u Rs=.042 N=1.094 Cjo=480p M=.61 Eg=.69 Xti=2) diff --git a/examples/vdmos/self-heating.sp b/examples/vdmos/self-heating.sp index 5de456a2b..90edbc847 100644 --- a/examples/vdmos/self-heating.sp +++ b/examples/vdmos/self-heating.sp @@ -10,7 +10,7 @@ VD D 0 2V + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m XTI=3 + Cjo=1.5n Vj=0.8 m=0.5 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 + Rthjc=0.02 Cthj=1e-3 Rthca=1000 + mtriode=0.8 .control diff --git a/examples/vdmos/soa_chk.sp b/examples/vdmos/soa_chk.sp index ea84d89f2..8d1278185 100644 --- a/examples/vdmos/soa_chk.sp +++ b/examples/vdmos/soa_chk.sp @@ -5,7 +5,7 @@ VDMOS SOA check + Rd=61m Rs=18m Rg=3 Rds=1e7 + Cgdmax=2.45n Cgdmin=10p a=0.3 Cgs=1.2n + Is=60p N=1.1 Rb=14m Cjo=1.5n XTI=3 -+ tcvth=0.0065 MU=-1.27 texp0=1.5 ++ tcvth=-0.0065 MU=-1.27 texp0=1.5 + mtriode=0.8 + Vgs_max=20 Vgd_max=20 Vds_max=200 diff --git a/src/spicelib/devices/vdmos/vdmostemp.c b/src/spicelib/devices/vdmos/vdmostemp.c index 202a4611a..36010a87f 100644 --- a/src/spicelib/devices/vdmos/vdmostemp.c +++ b/src/spicelib/devices/vdmos/vdmostemp.c @@ -47,7 +47,7 @@ void VDMOStempUpdate(VDMOSmodel *inModel, VDMOSinstance *here, double Temp, CKTc here->VDMOStTransconductance = model->VDMOStransconductance * here->VDMOSm * pow(ratio, model->VDMOSmu); - here->VDMOStVth = model->VDMOSvth0 - model->VDMOStype * model->VDMOStcvth * dt; + here->VDMOStVth = model->VDMOSvth0 + model->VDMOStcvth * dt; here->VDMOStksubthres = model->VDMOSksubthres * (1.0 + (model->VDMOStksubthres1 * dt) + (model->VDMOStksubthres2 * dt * dt)); From 4cac271912ae009fdfa32975616c925dafd09b60 Mon Sep 17 00:00:00 2001 From: dwarning Date: Sat, 3 Jan 2026 09:04:40 +0100 Subject: [PATCH 04/70] more realistic default values for case to ambient thermal resistance and device transconductances --- src/spicelib/devices/vdmos/vdmosset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spicelib/devices/vdmos/vdmosset.c b/src/spicelib/devices/vdmos/vdmosset.c index dfc6a2da3..0adfce318 100644 --- a/src/spicelib/devices/vdmos/vdmosset.c +++ b/src/spicelib/devices/vdmos/vdmosset.c @@ -32,7 +32,7 @@ VDMOSsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, model->VDMOStype = NMOS; if (!model->VDMOStransconductanceGiven) - model->VDMOStransconductance = 25 + 10 * model->VDMOStype; /* IRF540, 9540 */ + model->VDMOStransconductance = 15 + 5 * model->VDMOStype; /* IRF540, 9540 */ if (!model->VDMOSvth0Given) model->VDMOSvth0 = 3 * model->VDMOStype; /* IRF540, 9540 */ @@ -128,7 +128,7 @@ VDMOSsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, model->VDMOSrthjc = 1.0; if (!model->VDMOSrthcaGiven) - model->VDMOSrthca = 1000; + model->VDMOSrthca = 23; /* e.g. TO220 case */ if (!model->VDMOScthjGiven) model->VDMOScthj = 10e-06; From a630f586d24ea489b8615cc6711d699b6c0e7222 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 7 Jan 2026 20:12:35 +0100 Subject: [PATCH 05/70] enable simulation with 'option newtrunc' --- examples/soi/ring51_40.sp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/soi/ring51_40.sp b/examples/soi/ring51_40.sp index 0c833466a..fa583f81e 100644 --- a/examples/soi/ring51_40.sp +++ b/examples/soi/ring51_40.sp @@ -14,6 +14,7 @@ cout buf ss 1pF * this is needed .option reltol=1e-4 +.option ltereltol=1e-4 .tran 0.2n 16n .print tran v(out25) v(out50) @@ -48,6 +49,7 @@ if $?batchmode else save out25 out50 run + rusage time plot out25 out50 let lin-tstart = 4n $ skip the start-up phase let lin-tstop = 14n $ end earlier(just for demonstration) From e80fc7686ba0d384de6d778feea9f856587e5bbf Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 7 Jan 2026 20:13:48 +0100 Subject: [PATCH 06/70] re-enable maling old app ngsconvert ngnutmeg is still defect --- src/frontend/rawfile.c | 3 ++- src/ngsconvert.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/frontend/rawfile.c b/src/frontend/rawfile.c index 4a1359163..2871ae5ab 100644 --- a/src/frontend/rawfile.c +++ b/src/frontend/rawfile.c @@ -114,7 +114,8 @@ void raw_write(char *name, struct plot *pl, bool app, bool binary) fprintf(fp, "Title: %s\n", pl->pl_title); fprintf(fp, "Date: %s\n", pl->pl_date); - fprintf(fp, "Command: %s-%s, Build %s\n", ft_sim->simulator, ft_sim->version, Spice_Build_Date); + if (ft_sim) /* not available when old app ngscovert is made */ + fprintf(fp, "Command: %s-%s, Build %s\n", ft_sim->simulator, ft_sim->version, Spice_Build_Date); fprintf(fp, "Plotname: %s\n", pl->pl_name); fprintf(fp, "Flags: %s%s\n", realflag ? "real" : "complex", raw_padding ? "" : " unpadded"); diff --git a/src/ngsconvert.c b/src/ngsconvert.c index c3396f461..0fb17226b 100644 --- a/src/ngsconvert.c +++ b/src/ngsconvert.c @@ -40,6 +40,9 @@ bool cp_no_histsubst = FALSE; struct compat newcompat; bool cx_degrees = FALSE; +IFsimulator *ft_sim = NULL; +char Spice_Build_Date[] = " date is not available\n"; + char *cp_program = "sconvert"; @@ -56,6 +59,15 @@ char *cp_program = "sconvert"; #define TMALLOC(t, n) (t*) tmalloc(sizeof(t) * (size_t)(n)) #define TREALLOC(t, p, n) (t*) trealloc(p, sizeof(t) * (size_t)(n)) +FILE * +newfopen(const char *fn, const char* md) +{ + FILE* fp; + if (fn == NULL) + return NULL; + fp = fopen(fn, md); + return fp; +} char * smktemp(char *id) @@ -488,7 +500,6 @@ main(int ac, char **av) exit(EXIT_NORMAL); } - void cp_pushcontrol(void) { } void cp_popcontrol(void) { } void out_init(void) { } From d239dd1afa044598b3d67275b6b7d84cdc325789 Mon Sep 17 00:00:00 2001 From: dwarning Date: Thu, 8 Jan 2026 22:34:10 +0100 Subject: [PATCH 07/70] rm ngnutmeg from build list of old apps --- src/Makefile.am | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index d1cedb9bf..76dad79db 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ bin_PROGRAMS = ngspice if OLDAPPS if !WINGUI -bin_PROGRAMS += ngnutmeg ngsconvert ngproc2mod ngmultidec ngmakeidx +bin_PROGRAMS += ngsconvert ngproc2mod ngmultidec ngmakeidx helpdatadir = $(pkgdatadir)/helpdir helpdata_DATA = ngspice.txt ngspice.idx if !NO_HELP @@ -220,50 +220,11 @@ endif $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --tag=RC --mode=compile $(RC) $(RCFLAGS) $< -o $@ if OLDAPPS -## nutmeg: - -ngnutmeg_SOURCES = \ - main.c \ - conf.c \ - conf.h \ - ngnutmeg.c - -ngnutmeg_CPPFLAGS = $(AM_CPPFLAGS) - -if WINGUI -ngnutmeg_SOURCES += winmain.c hist_info.c -endif - -ngnutmeg_LDADD = \ - frontend/libfte.la - -if WINGUI -ngnutmeg_LDADD += \ - frontend/wdisp/libwindisp.la -endif - -ngnutmeg_LDADD += \ - frontend/plotting/libplotting.la \ - frontend/parser/libparser.la \ - frontend/numparam/libnumparam.la \ - frontend/trannoise/libtrannoise.la \ - maths/cmaths/libcmaths.la \ - maths/misc/libmathmisc.la \ - maths/fft/libmathfft.la \ - maths/poly/libpoly.la \ - misc/libmisc.la \ - spicelib/parser/libinp.la - ## These programs are not required on Windows if !WINGUI if !SHWIN -if !NO_X -ngnutmeg_LDADD += \ - frontend/help/libhlp.la -endif !NO_X - ## help: nghelp_SOURCES = conf.c nghelp.c From e8016ebf6386e41b7a21d51e1a11e604889397d9 Mon Sep 17 00:00:00 2001 From: dwarning Date: Thu, 8 Jan 2026 22:35:52 +0100 Subject: [PATCH 08/70] convert to lower case for spice2 input and remove few warnings --- src/ngsconvert.c | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/ngsconvert.c b/src/ngsconvert.c index 0fb17226b..01c4d768d 100644 --- a/src/ngsconvert.c +++ b/src/ngsconvert.c @@ -21,6 +21,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include +int input(FILE *fp); FILE *cp_in = NULL; FILE *cp_out = NULL; @@ -59,6 +60,7 @@ char *cp_program = "sconvert"; #define TMALLOC(t, n) (t*) tmalloc(sizeof(t) * (size_t)(n)) #define TREALLOC(t, p, n) (t*) trealloc(p, sizeof(t) * (size_t)(n)) +#if defined(__MINGW32__) || defined(_MSC_VER) FILE * newfopen(const char *fn, const char* md) { @@ -68,6 +70,7 @@ newfopen(const char *fn, const char* md) fp = fopen(fn, md); return fp; } +#endif char * smktemp(char *id) @@ -204,7 +207,11 @@ oldread(char *name) pl->pl_scale = pl->pl_dvecs = v; end = v; tfread(buf, 1, 8, fp); - buf[8] = '\0'; + for (j = 0; j < 8; j++) { + if (buf[j] == ' ') break; + buf[j] = (char) tolower((unsigned char) buf[j]); + } + buf[j] = '\0'; v->v_name = strdup(buf); } for (v = pl->pl_dvecs; v; v = v->v_next) { @@ -392,15 +399,19 @@ oldwrite(char *name, bool app, struct plot *pl) int main(int ac, char **av) { - char *sf, *af; + char *sf = NULL, *af = NULL; char buf[BSIZE_SP]; - char t, f; + char t = 0, f = 0; struct plot *pl; size_t n; char *infile = NULL; char *outfile = NULL; FILE *fp; + cp_in = stdin; + cp_out = stdout; + cp_err = stderr; + switch (ac) { case 5: sf = av[2]; @@ -426,20 +437,32 @@ main(int ac, char **av) case 1: printf("Input file: "); (void) fflush(stdout); - (void) fgets(buf, BSIZE_SP, stdin); - sf = strdup(buf); + if (fgets(buf, BSIZE_SP, stdin) != NULL) { + sf = strdup(buf); + } else { + printf("Error reading input file."); + } printf("Input type: "); (void) fflush(stdout); - (void) fgets(buf, BSIZE_SP, stdin); - f = buf[0]; + if (fgets(buf, BSIZE_SP, stdin) != NULL) { + f = buf[0]; + } else { + printf("Error reading input type."); + } printf("Output file: "); (void) fflush(stdout); - (void) fgets(buf, BSIZE_SP, stdin); - af = strdup(buf); + if (fgets(buf, BSIZE_SP, stdin) != NULL) { + af = strdup(buf); + } else { + printf("Error reading output file."); + } printf("Output type: "); (void) fflush(stdout); - (void) fgets(buf, BSIZE_SP, stdin); - t = buf[0]; + if (fgets(buf, BSIZE_SP, stdin) != NULL) { + t = buf[0]; + } else { + printf("Error reading output type."); + } break; default: fprintf(cp_err, From 56168b4754512e17d34aa6eaeed57b4e8ca16ddb Mon Sep 17 00:00:00 2001 From: dwarning Date: Fri, 9 Jan 2026 12:57:49 +0100 Subject: [PATCH 09/70] Minor change that helps VADistiller. --- src/spicelib/devices/dio/dioload.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spicelib/devices/dio/dioload.c b/src/spicelib/devices/dio/dioload.c index d7243d5b1..6f53966eb 100644 --- a/src/spicelib/devices/dio/dioload.c +++ b/src/spicelib/devices/dio/dioload.c @@ -707,9 +707,9 @@ next2: *(ckt->CKTstate0 + here->DIOvoltage) = vd; *(ckt->CKTrhs + here->DIOtempNode) += Ith - dIth_dVdio*vd - dIth_dVrs*vrs - dIth_dT*delTemp - ceqqth; } if (model->DIOresistSWGiven) { - cdeq=cdsw-gdsw*vdsw; - *(ckt->CKTrhs + here->DIOnegNode) += cdeq; - *(ckt->CKTrhs + here->DIOposSwPrimeNode) -= cdeq; + double cdeqsw=cdsw-gdsw*vdsw; + *(ckt->CKTrhs + here->DIOnegNode) += cdeqsw; + *(ckt->CKTrhs + here->DIOposSwPrimeNode) -= cdeqsw; if (selfheat) { *(ckt->CKTrhs + here->DIOposNode) += dIrssw_dT*delTemp; *(ckt->CKTrhs + here->DIOposSwPrimeNode) += dIdioSw_dT*delTemp - dIrssw_dT*delTemp; From 660ca46667b302079649bb54458427338cfa7822 Mon Sep 17 00:00:00 2001 From: dwarning Date: Fri, 9 Jan 2026 16:07:38 +0100 Subject: [PATCH 10/70] remove redundant definition and unused variable --- src/frontend/rawfile.c | 1 - src/spicelib/analysis/noisean.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/frontend/rawfile.c b/src/frontend/rawfile.c index 2871ae5ab..1ecf834b5 100644 --- a/src/frontend/rawfile.c +++ b/src/frontend/rawfile.c @@ -22,7 +22,6 @@ Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group #include "ngspice/compatmode.h" extern IFsimulator SIMinfo; -extern char Spice_Build_Date[]; static void fixdims(struct dvec *v, char *s); diff --git a/src/spicelib/analysis/noisean.c b/src/spicelib/analysis/noisean.c index 3eeca41f2..29f880452 100644 --- a/src/spicelib/analysis/noisean.c +++ b/src/spicelib/analysis/noisean.c @@ -61,7 +61,6 @@ NOISEan(CKTcircuit* ckt, int restart) int numNames; IFuid* nameList; /* va: tmalloc'ed list of names */ - static runDesc* noiPlot = NULL; runDesc* plot = NULL; #ifdef XSPICE From 9f08301128db3f545f72c04862e2d5cfe897014e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 13 Jan 2026 15:50:03 +0100 Subject: [PATCH 11/70] Better graphics --- examples/memristor/memristor.sp | 1 + examples/memristor/memristor_x.sp | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/memristor/memristor.sp b/examples/memristor/memristor.sp index 0664f1075..c41787430 100644 --- a/examples/memristor/memristor.sp +++ b/examples/memristor/memristor.sp @@ -51,6 +51,7 @@ let newstime = stime/1.4 let deltime = newstime/100 alter @V1[sin] [ 0 $&vmax $&newfreq ] tran $&deltime $&newstime uic +set xbrushwidth=2 * the 'programming' currents plot tran1.alli tran2.alli alli title 'Memristor with threshold: Internal Programming currents' * resistance versus time plot diff --git a/examples/memristor/memristor_x.sp b/examples/memristor/memristor_x.sp index 198cb979b..a9bf93198 100644 --- a/examples/memristor/memristor_x.sp +++ b/examples/memristor/memristor_x.sp @@ -65,6 +65,7 @@ let newstime = stime/1.4 let deltime = newstime/100 alter @V1[sin] [ 0 $&vmax $&newfreq ] tran $&deltime $&newstime uic +set xbrushwidth=2 * the resistor currents plot tran1.alli tran2.alli alli title 'Memristor with threshold: currents' * calculate resistance (avoid dividing by zero) From 2813a943a736d3aeb0cfd4d8ebe97f34d0a8f4f2 Mon Sep 17 00:00:00 2001 From: dwarning Date: Fri, 16 Jan 2026 08:38:37 +0100 Subject: [PATCH 12/70] Correct vgs assignment - bug found by K. OHara --- src/spicelib/devices/vdmos/vdmosconv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spicelib/devices/vdmos/vdmosconv.c b/src/spicelib/devices/vdmos/vdmosconv.c index 49e0ccde8..2bbed8050 100644 --- a/src/spicelib/devices/vdmos/vdmosconv.c +++ b/src/spicelib/devices/vdmos/vdmosconv.c @@ -32,7 +32,7 @@ VDMOSconvTest(GENmodel *inModel, CKTcircuit *ckt) here = VDMOSnextInstance(here)) { vgs = model->VDMOStype * ( - *(ckt->CKTrhs+here->VDMOSgNode) - + *(ckt->CKTrhs+here->VDMOSgNodePrime) - *(ckt->CKTrhs+here->VDMOSsNodePrime)); vds = model->VDMOStype * ( *(ckt->CKTrhs+here->VDMOSdNodePrime) - From 75a8037f0c6fac567ddfddb471b69dc8f3e97c7e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 16 Jan 2026 15:43:48 +0100 Subject: [PATCH 13/70] Exclude B voltage source, whan '.probe alli' is used. It already gets a bxx#branch for current measurement. --- src/frontend/inpc_probe.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/inpc_probe.c b/src/frontend/inpc_probe.c index 99c4bcc5d..e77437bef 100644 --- a/src/frontend/inpc_probe.c +++ b/src/frontend/inpc_probe.c @@ -240,6 +240,10 @@ void inp_probe(struct card* deck) if (strchr("ehvk", *instname)) continue; + /* exclude B voltage source */ + if (strchr("b", *instname) && strstr(curr_line, "v=")) + continue; + /* exclude a devices (code models may have special characters in their instance line. digital nodes should not get V sources in series anyway.) */ if ('a' == *instname) From 14363ba45a4d17774b61ad49f180c27d730d574a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 20 Jan 2026 15:24:52 +0100 Subject: [PATCH 14/70] Moving window filtering with function 'newvec = mtimeavg(vec)' Window of fixed time width given by 'set mtimeavgwindow=400u' Length and scale of newvec resembles the original vetor vec. Large vec and large mtimeavgwindow take their time. OpenMP is used if available. --- src/frontend/evaluate.c | 2 +- src/frontend/parse.c | 1 + src/include/ngspice/fteext.h | 1 + src/maths/cmaths/cmath4.c | 126 +++++++++++++++++++++++++++++++++++ src/maths/cmaths/cmath4.h | 2 + 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/frontend/evaluate.c b/src/frontend/evaluate.c index 42fc4540b..01127e1d0 100644 --- a/src/frontend/evaluate.c +++ b/src/frontend/evaluate.c @@ -858,7 +858,7 @@ apply_func_funcall(struct func *func, struct dvec *v, int *newlength, short int /* Modified for passing necessary parameters to the derive function - A.Roldan */ if (eq(func->fu_name, "interpolate") || eq(func->fu_name, "deriv") || eq(func->fu_name, "group_delay") - || eq(func->fu_name, "fft") || eq(func->fu_name, "ifft") || eq(func->fu_name, "integ")) + || eq(func->fu_name, "fft") || eq(func->fu_name, "ifft") || eq(func->fu_name, "integ") || eq(func->fu_name, "mtimeavg")) { void * (*f) (void *data, short int type, int length, int *newlength, short int *newtype, diff --git a/src/frontend/parse.c b/src/frontend/parse.c index f575ecbe7..24cc1e9e7 100644 --- a/src/frontend/parse.c +++ b/src/frontend/parse.c @@ -380,6 +380,7 @@ struct func ft_funcs[] = { { "integ", (cx_function_t*)(void *) cx_integ }, { "fft", (cx_function_t*)(void *) cx_fft }, { "ifft", (cx_function_t*)(void *) cx_ifft }, + { "mtimeavg", (cx_function_t*)(void *) cx_mtimeavg }, { "v", NULL }, { NULL, NULL } }; diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 541c7f8c0..69d428b51 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -129,6 +129,7 @@ extern void *cx_integ(void *, short int , int , int *, short int *, struct plot extern void *cx_group_delay(void *, short int , int , int *, short int *, struct plot *, struct plot *, int ); extern void *cx_fft(void *, short int , int , int *, short int *, struct plot *, struct plot *, int ); extern void *cx_ifft(void *, short int , int , int *, short int *, struct plot *, struct plot *, int ); +extern void *cx_mtimeavg(void *, short int , int , int *, short int *, struct plot *, struct plot *, int ); /* define.c */ diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index 63b03f0a6..870bee140 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -33,6 +33,9 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "ngspice/sim.h" /* To get SV_TIME */ #include "ngspice/fftext.h" +#include "ngspice/cktdefs.h" /* ft_curckt */ +#include "ngspice/ftedefs.h" /* ft_curckt */ +#include "ngspice/fteext.h" /* ft_curckt */ extern bool cx_degrees; extern void vec_new(struct dvec *d); @@ -988,3 +991,126 @@ cx_ifft(void *data, short int type, int length, int *newlength, short int *newty return ((void *) outdata); } + +/* Compute the moving average of a vector, + * resulting from a transient simulation, + * over a time window given by variable mtimeavgwindow. + * Create the average over each original time point + * +/- mtimeavgwindow/2. + */ +void* +cx_mtimeavg(void* data, short int type, int length, int* newlength, short int* newtype, struct plot* pl, struct plot* newpl, int grouping) +{ + double tdelta = 0.0, tstart=0.0, tstop=0.0, tdeltahalf; + struct dvec *sc; + int i; + double* d, * dd, * dsc; + + if (grouping == 0) + grouping = length; + + int nlen = length - 1; + + if (!pl || !pl->pl_scale || !newpl || !newpl->pl_scale) { + fprintf(cp_err, "Internal error mtimeavg: bad scale\n"); + return (NULL); + } + + /* Check to see if we have the time vector as scale */ + if (!isreal(pl->pl_scale) || + ((pl->pl_scale)->v_type != SV_TIME)) { + fprintf(cp_err, "Error: mtimeavg needs real time scale\n"); + return (NULL); + } + + if (type != VF_REAL) { + fprintf(cp_err, "Error: mtimeavg needs a real valued vector.\n"); + return (NULL); + } + + if (!cp_getvar("mtimeavgwindow", CP_REAL, &tdelta, 0)) + if (ft_curckt == (struct circ*)NULL) { + tdelta = 1e-6; + fprintf(cp_out, "Note: mtimeavgwindow not given, window set to %g s\n", tdelta); + } + else { + CKTcircuit* ckt = ft_curckt->ci_ckt; + tdelta = 10.0 * ckt->CKTstep; + fprintf(cp_out, "Note: mtimeavgwindow not given, window set to %g s\n", tdelta); + } + + sc = pl->pl_scale; + dsc = sc->v_realdata; + + d = alloc_d(length); + dd = (double*)data; + + tstart = dsc[0]; + tstop = dsc[nlen]; + tdeltahalf = tdelta / 2.; + *newtype = VF_REAL; + *newlength = length; + +#ifdef USE_OMP +#pragma omp parallel + { +#pragma omp for +#endif + for (i = 0; i < length; i++) { + int j, ibeg, iend, k; + double tbeg, tend, ttruebeg, ttrueend, truedelta; + double integ; + double tmid = dsc[i]; + + ttruebeg = tmid - tdeltahalf; + ttrueend = tmid + tdeltahalf; + tbeg = tstop; + tend = tstart; + /* start of the interval */ + j = i; + while (j > 0 && tbeg > ttruebeg) { + tbeg = dsc[j]; + j--; + } + ibeg = j; + + /* end of the interval */ + j = i; + while (j < nlen && tend < ttrueend) { + tend = dsc[j]; + j++; + } + iend = j; + + /* integrate the data */ + integ = 0.; + for (k = ibeg; k < iend; k++) { + integ = integ + dd[k] * (dsc[k + 1] - dsc[k]); + } + + /* cut a little from integ on the lower border, interpolated */ + if (ibeg > 0) { + integ = integ - (dsc[ibeg] - ttruebeg) * dd[ibeg]; + } + /* add a little to integ on the upper border, interpolated */ + if (iend < nlen) { + integ = integ + (dsc[iend] - ttrueend) * dd[iend]; + } + /* when on the edges of the vector, set reduced time windows */ + if (ibeg == 0) + truedelta = dsc[i] + tdeltahalf; + else if (iend == nlen) + truedelta = dsc[nlen] - dsc[i] + tdeltahalf; + else + truedelta = tdelta; + + d[i] = integ / truedelta; + + // fprintf(stdout, "%d %d %d %e %e..%e %e\n", i, ibeg, iend, dsc[ibeg], dsc[iend], ttruebeg, ttrueend); + } +#ifdef USE_OMP + } +#endif + return ((void*)d); +} + diff --git a/src/maths/cmaths/cmath4.h b/src/maths/cmaths/cmath4.h index 5b52f90b2..f4f9111d4 100644 --- a/src/maths/cmaths/cmath4.h +++ b/src/maths/cmaths/cmath4.h @@ -25,5 +25,7 @@ void * cx_fft(void *data, short int type, int length, int *newlength, short int struct plot *pl, struct plot *newpl, int grouping); void * cx_ifft(void *data, short int type, int length, int *newlength, short int *newtype, struct plot *pl, struct plot *newpl, int grouping); +void* cx_mtimeavg(void* data, short int type, int length, int* newlength, short int* newtype, + struct plot* pl, struct plot* newpl, int grouping); #endif From 1be27f6ad055bf717d597405d0e87a5b68341d97 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 20 Jan 2026 15:30:28 +0100 Subject: [PATCH 15/70] Using mtimeavg --- examples/various/mtimeavg_test.cir | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/various/mtimeavg_test.cir diff --git a/examples/various/mtimeavg_test.cir b/examples/various/mtimeavg_test.cir new file mode 100644 index 000000000..90b8068b5 --- /dev/null +++ b/examples/various/mtimeavg_test.cir @@ -0,0 +1,27 @@ +test mtimeavg + +* noise source + +VNoiw 1 0 DC 0 TRNOISE(20n 0.5n 0 0) + +.control +tran 0.5n 500n +set color3=orange +set color5=red +set mtimeavgwindow=5n +let filtered5n = mtimeavg(V(1)) +set mtimeavgwindow=10n +let filtered10n = mtimeavg(V(1)) +set mtimeavgwindow=20n +let filtered20n = mtimeavg(V(1)) +set mtimeavgwindow=50n +let filtered50n = mtimeavg(V(1)) +set xbrushwidth=2 +set color0=white +plot filtered5n filtered10n filtered20n filtered50n ylimit -50n 50n +set xbrushwidth=1 +set color3=red +plot V(1) filtered50n ylimit -50n 50n +.endc + +.end From 82e563b1b1df3c953ea7bc74b271edcccb98b741 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 23 Jan 2026 19:50:26 +0100 Subject: [PATCH 16/70] New analogue and digital device models from TI contain the token GND in their subcircuit. Up to now ngspice has declared GND as global and replaced it by 0. Now, if PS is selected as compat mode, GND is not declared as global, and is not replaced inside of a subcircuit. --- src/frontend/inpcom.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index c388d667e..4cf3b2edf 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1860,11 +1860,13 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name comfile = TRUE; if (call_depth == 0 && !comfile) { - if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0)) + if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0) && !newcompat.ps) insert_new_line(cc, copy(".global gnd"), 1, 0, "internal"); - else + else { insert_new_line( - cc, copy("* gnd is not set to 0 automatically "), 1, 0, "internal"); + cc, copy("* gnd is not set to 0 automatically "), 1, 0, "internal"); + fprintf(stdout, "Note: gnd in a subcircuit is not set to 0 automatically\n"); + } if (!newcompat.lt && !newcompat.ps && !newcompat.s3) { /* process all library section references */ @@ -2295,12 +2297,20 @@ static char *readline(FILE *fd) static void inp_fix_gnd_name(struct card *c) { + bool found_subckt = FALSE; for (; c; c = c->nextcard) { - char *gnd = c->line; + // if inside of a subcircuit, and compatmode is ps, don't replace gnd + if (newcompat.ps) { + if (ciprefix(".subckt", c->line)) + found_subckt = TRUE; + if (ciprefix(".ends", c->line)) + found_subckt = FALSE; + } + // if there is a comment or no gnd, go to next line - if ((*gnd == '*') || !strstr(gnd, "gnd")) + if (found_subckt || (*gnd == '*') || !strstr(gnd, "gnd")) continue; // replace "?gnd?" by "? 0 ?", ? being a ' ' ',' '(' ')'. From 857f826b0a0144d9ebd188f0a143e2da22213e6e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 3 Feb 2026 13:42:25 +0100 Subject: [PATCH 17/70] Re-enable compiling with clang Patch thanks to Gon Solo, issue 826 --- src/include/cppduals/duals/dual | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/include/cppduals/duals/dual b/src/include/cppduals/duals/dual index 4cb227e84..1e46b4c23 100644 --- a/src/include/cppduals/duals/dual +++ b/src/include/cppduals/duals/dual @@ -485,9 +485,6 @@ struct is_arithmetic> : is_arithmetic {}; #endif // CPPDUALS_ENABLE_IS_ARITHMETIC -/// Duals are compound types. -template -struct is_compound> : true_type {}; // Modification of std::numeric_limits<> per // C++03 17.4.3.1/1, and C++11 18.3.2.3/1. From 26c86841dfa03f8371d49125c0054ba427efeb23 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 3 Feb 2026 15:05:24 +0100 Subject: [PATCH 18/70] Remove compiler warnings --- src/maths/cmaths/cmath2.c | 1 - src/maths/cmaths/cmath4.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/maths/cmaths/cmath2.c b/src/maths/cmaths/cmath2.c index 7af7cd84e..d6ce651af 100644 --- a/src/maths/cmaths/cmath2.c +++ b/src/maths/cmaths/cmath2.c @@ -369,7 +369,6 @@ cx_avg(void *data, short int type, int length, int *newlength, short int *newtyp void* cx_m3avg(void* data, short int type, int length, int* newlength, short int* newtype) { - double sum_real = 0.0, sum_imag = 0.0; int i; if (type == VF_REAL) { diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index 870bee140..a1f547d97 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -1028,7 +1028,7 @@ cx_mtimeavg(void* data, short int type, int length, int* newlength, short int* n return (NULL); } - if (!cp_getvar("mtimeavgwindow", CP_REAL, &tdelta, 0)) + if (!cp_getvar("mtimeavgwindow", CP_REAL, &tdelta, 0)) { if (ft_curckt == (struct circ*)NULL) { tdelta = 1e-6; fprintf(cp_out, "Note: mtimeavgwindow not given, window set to %g s\n", tdelta); @@ -1038,6 +1038,7 @@ cx_mtimeavg(void* data, short int type, int length, int* newlength, short int* n tdelta = 10.0 * ckt->CKTstep; fprintf(cp_out, "Note: mtimeavgwindow not given, window set to %g s\n", tdelta); } + } sc = pl->pl_scale; dsc = sc->v_realdata; From 5ec437f672493c0127c2f78e7eae93581c028bd5 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 3 Feb 2026 22:38:56 +0100 Subject: [PATCH 19/70] After an ac simulation, a tran measurement causes a segfault. A check prevents this issue. --- src/frontend/com_measure2.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index 7fa30d352..fd371edcd 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -439,8 +439,13 @@ com_measure_when( sp_check = TRUE; else if (cieq (meas->m_analysis, "dc")) dc_check = TRUE; - else + else { tran_check = TRUE; + if (!d->v_realdata) { + fprintf(stderr, "Error: no real data available for measurement (no tran simulation?)\n"); + return MEASUREMENT_FAILURE; + } + } for (i = 0; i < d->v_length; i++) { @@ -697,8 +702,13 @@ measure_at( sp_check = TRUE; else if (cieq (meas->m_analysis, "dc")) dc_check = TRUE; - else + else { tran_check = TRUE; + if (!d->v_realdata) { + fprintf(stderr, "Error: no real data available for measurement (no tran simulation?)\n"); + return MEASUREMENT_FAILURE; + } + } for (i = 0; i < d->v_length; i++) { if (ac_check) { From d163db99c722e348ec40765a49a87cabde520bce Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 7 Feb 2026 13:21:54 +0100 Subject: [PATCH 20/70] Add a user-settable variable measureprec to set the number of digits when printing the measure outputs. Internally this is a global variable measure_precision. Enable all measure commands to recognize measure_precision when printing an output. Default value is 5 (yielding 6 digits after .?). --- src/frontend/com_measure2.c | 43 +++++++++++++++++++++++++----------- src/frontend/miscvars.c | 1 + src/frontend/options.c | 12 +++++++++- src/include/ngspice/fteext.h | 3 +++ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index fd371edcd..29b6e63c9 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -16,6 +16,8 @@ #include "com_measure2.h" #include "breakp2.h" +int measure_precision = -1; + typedef enum { MEASUREMENT_OK = 0, MEASUREMENT_FAILURE = 1 @@ -86,6 +88,9 @@ measure_get_precision(void) if ((env_ptr = getenv("NGSPICE_MEAS_PRECISION")) != NULL) precision = atoi(env_ptr); + if (measure_precision > 0) + precision = measure_precision; + return precision; } @@ -1810,9 +1815,13 @@ get_measure2( // print results if (out_line) - sprintf(out_line, "%-20s= %e targ= %e trig= %e\n", mName, (measTarg->m_measured - measTrig->m_measured), measTarg->m_measured, measTrig->m_measured); + sprintf(out_line, "%-20s= %.*e targ= %.*e trig= %.*e\n", + mName, precision, (measTarg->m_measured - measTrig->m_measured), + precision, measTarg->m_measured, precision, measTrig->m_measured); else - fprintf(mout,"%-20s= %e targ= %e trig= %e\n", mName, (measTarg->m_measured - measTrig->m_measured), measTarg->m_measured, measTrig->m_measured); + fprintf(mout,"%-20s= %.*e targ= %.*e trig= %.*e\n", mName, precision, + (measTarg->m_measured - measTrig->m_measured), + precision, measTarg->m_measured, precision, measTrig->m_measured); *result = (measTarg->m_measured - measTrig->m_measured); @@ -1883,9 +1892,9 @@ err_ret1: // print results if (out_line) - sprintf(out_line, "%-20s= %e\n", mName, meas->m_measured); + sprintf(out_line, "%-20s= %.*e\n", mName, precision, meas->m_measured); else - fprintf(mout,"%-20s= %e\n", mName, meas->m_measured); + fprintf(mout,"%-20s= %.*e\n", mName, precision, meas->m_measured); *result = meas->m_measured; @@ -1924,7 +1933,7 @@ err_ret2: if (out_line) sprintf(out_line, "%-20s= %.*e\n", mName, precision, meas->m_measured); else - fprintf(mout, "%-20s= %e\n", mName, meas->m_measured); + fprintf(mout, "%-20s= %.*e\n", mName, precision, meas->m_measured); *result = meas->m_measured; @@ -2012,9 +2021,11 @@ err_ret4: // print results if (out_line) - sprintf(out_line, "%-20s= %e from= %e to= %e\n", mName, meas->m_measured, meas->m_at, meas->m_measured_at); + sprintf(out_line, "%-20s= %.*e from= %.*e to= %.*e\n", mName, + precision, meas->m_measured, precision, meas->m_at, precision, meas->m_measured_at); else - fprintf(mout, "%-20s= %e from= %e to= %e\n", mName, meas->m_measured, meas->m_at, meas->m_measured_at); + fprintf(mout, "%-20s= %.*e from= %.*e to= %.*e\n", mName, + precision, meas->m_measured, precision, meas->m_at, precision, meas->m_measured_at); *result = meas->m_measured; @@ -2059,17 +2070,21 @@ err_ret5: if ((mFunctionType == AT_MIN) || (mFunctionType == AT_MAX)) { // print results if (out_line) - sprintf(out_line, "%-20s= %e at= %e\n", mName, measTrig->m_measured, measTrig->m_measured_at); + sprintf(out_line, "%-20s= %.*e at= %.*e\n", + mName, precision, measTrig->m_measured, precision, measTrig->m_measured_at); else - fprintf(mout, "%-20s= %e at= %e\n", mName, measTrig->m_measured, measTrig->m_measured_at); + fprintf(mout, "%-20s= %.*e at= %.*e\n", + mName, precision, measTrig->m_measured, precision, measTrig->m_measured_at); *result = measTrig->m_measured; } else { // print results if (out_line) - sprintf(out_line, "%-20s= %e with= %e\n", mName, measTrig->m_measured_at, measTrig->m_measured); + sprintf(out_line, "%-20s= %.*e with= %.*e\n", + mName, precision, measTrig->m_measured_at, precision, measTrig->m_measured); else - fprintf(mout, "%-20s= %e with= %e\n", mName, measTrig->m_measured_at, measTrig->m_measured); + fprintf(mout, "%-20s= %.*e with= %.*e\n", + mName, precision, measTrig->m_measured_at, precision, measTrig->m_measured); *result = measTrig->m_measured_at; } @@ -2117,9 +2132,11 @@ err_ret6: // print results if (out_line) - sprintf(out_line, "%-20s= %e from= %e to= %e\n", mName, (maxValue - minValue), measTrig->m_from, measTrig->m_to); + sprintf(out_line, "%-20s= %.*e from= %.*e to= %.*e\n", + mName, precision, (maxValue - minValue), precision, measTrig->m_from, precision, measTrig->m_to); else - fprintf(mout, "%-20s= %e from= %e to= %e\n", mName, (maxValue - minValue), measTrig->m_from, measTrig->m_to); + fprintf(mout, "%-20s= %.*e from= %.*e to= %.*e\n", + mName, precision, (maxValue - minValue), precision, measTrig->m_from, precision, measTrig->m_to); *result = (maxValue - minValue); diff --git a/src/frontend/miscvars.c b/src/frontend/miscvars.c index 9632ef862..c157a9666 100644 --- a/src/frontend/miscvars.c +++ b/src/frontend/miscvars.c @@ -76,6 +76,7 @@ char *ft_setkwords[] = { "lprplot5", "lprps", "maxwins", + "measureprec", "modelcard", "modelline", "moremode", diff --git a/src/frontend/options.c b/src/frontend/options.c index 7dfe6f97a..d6da5a46c 100644 --- a/src/frontend/options.c +++ b/src/frontend/options.c @@ -342,6 +342,16 @@ cp_usrset(struct variable *var, bool isset) raw_prec = var->va_num; else fprintf(cp_err, "Bad 'rawfileprec' \"%s\"\n", var->va_name); + } + else if (eq(var->va_name, "measureprec")) { + if ((var->va_type == CP_BOOL) && (isset == FALSE)) + measure_precision = -1; + else if (var->va_type == CP_REAL) + measure_precision = (int)floor(var->va_real + 0.5); + else if (var->va_type == CP_NUM) + measure_precision = var->va_num; + else + fprintf(cp_err, "Bad 'measureprec' \"%s\"\n", var->va_name); } else if (eq(var->va_name, "numdgt")) { if ((var->va_type == CP_BOOL) && (isset == FALSE)) cp_numdgt = -1; @@ -350,7 +360,7 @@ cp_usrset(struct variable *var, bool isset) else if (var->va_type == CP_NUM) cp_numdgt = var->va_num; else - fprintf(cp_err, "Excuse me??\n"); + fprintf(cp_err, "Bad 'numdgt' \"%s\"\n", var->va_name); } else if (eq(var->va_name, "unixcom")) { cp_dounixcom = isset; if (isset) { diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 69d428b51..af74009bd 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -304,6 +304,9 @@ extern struct plot *raw_read(char *name); extern bool do_measure(char *what, bool chk_only); extern bool check_autostop(char *what); +/* com_measure2.c*/ +extern int measure_precision; + /* resource.c */ extern void ft_ckspace(void); From 658b3ad2658391fa890b9bdc56b3e8fc63561582 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 11 May 2025 16:40:31 +0200 Subject: [PATCH 21/70] Add ifspec.ifs to MS Visual C project --- visualc/xspice/spice2poly.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/visualc/xspice/spice2poly.vcxproj b/visualc/xspice/spice2poly.vcxproj index 0379e7691..408c46b0f 100644 --- a/visualc/xspice/spice2poly.vcxproj +++ b/visualc/xspice/spice2poly.vcxproj @@ -210,6 +210,7 @@ + From e6de8cd648192f114e5f3b315992507887528e1a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 1 Mar 2025 23:33:18 +0100 Subject: [PATCH 22/70] Numparam: enable the sign + in expression 'pa*+3' like already existing 'pa*-3' --- src/frontend/numparam/xpressn.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index ec1c55f8b..f1e163ca3 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -1053,11 +1053,18 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) ((oldstate == S_atom) && (state == S_binop)) || ((oldstate != S_atom) && (state != S_binop)); - if (oldstate == S_binop && state == S_binop && c == '-') { - ok = 1; - negate = 1; - continue; - } + /* c is a sign, + or - are allowed */ + if (oldstate == S_binop && state == S_binop) + if (c == '-') { + ok = 1; + negate = 1; + continue; + } + else if (c == '+') { + ok = 1; + negate = 0; + continue; + } if (!ok) error = message(dico, " Misplaced operator\n"); From 1877ce88bdef69b0bc2d3f195a0ee121582a5244 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Feb 2026 14:19:14 +0100 Subject: [PATCH 23/70] Improve error message --- src/spicelib/parser/inpdpar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spicelib/parser/inpdpar.c b/src/spicelib/parser/inpdpar.c index 0aab07537..1d2c67ba8 100644 --- a/src/spicelib/parser/inpdpar.c +++ b/src/spicelib/parser/inpdpar.c @@ -72,7 +72,7 @@ INPdevParse(char **line, CKTcircuit *ckt, int dev, GENinstance *fast, errbuf = copy(" unknown parameter ($). Check the compatibility flag!\n"); } else { - errbuf = tprintf(" unknown parameter (%s) \n", parameter); + errbuf = tprintf(" unknown instance parameter (%s) \n", parameter); } rtn = errbuf; goto quit; From b51f041b29aaac44f61d2ef2005e3874b65b682d Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 10 May 2025 20:15:31 +0200 Subject: [PATCH 24/70] Improve error message by adding the sourcing netlist line --- src/frontend/numparam/numparam.h | 1 + src/frontend/numparam/spicenum.c | 1 + src/frontend/numparam/xpressn.c | 11 +++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/frontend/numparam/numparam.h b/src/frontend/numparam/numparam.h index 148311149..fd8935d26 100644 --- a/src/frontend/numparam/numparam.h +++ b/src/frontend/numparam/numparam.h @@ -57,6 +57,7 @@ typedef struct { /* the input scanner data structure */ char *dyncategory; int hs_compatibility; /* allow extra keywords */ int linecount; /* number of lines in deck */ + char* cardline; /* line of card treated currently */ } dico_t; diff --git a/src/frontend/numparam/spicenum.c b/src/frontend/numparam/spicenum.c index 7832a37b7..7c38093c0 100644 --- a/src/frontend/numparam/spicenum.c +++ b/src/frontend/numparam/spicenum.c @@ -672,6 +672,7 @@ nupa_eval(struct card *card) dicoS->srcline = linenum; dicoS->oldline = orig_linenum; + dicoS->cardline = s; c = dicoS->dyncategory[linenum]; diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index f1e163ca3..13f08baae 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -229,14 +229,16 @@ message(dico_t *dico, const char *fmt, ...) if (ft_ngdebug) { fprintf (stderr, - "Netlist line no. %d, new internal line no. %d:\n", - dico->oldline, dico->srcline); + "Error in netlist line no. %d, new internal line no. %d:\n" + "%s\n\n", + dico->srcline, dico->oldline, dico->cardline); } else { fprintf (stderr, - "Netlist line no. %d:\n", - dico->oldline); + "Error in netlist line no. %d:\n" + "%s\n\n", + dico->srcline, dico->oldline); } } va_start(ap, fmt); @@ -273,6 +275,7 @@ initdico(dico_t *dico) dico->hs_compatibility = 1; else dico->hs_compatibility = 0; + dico->cardline = NULL; } From 1b81f19557c2ec12e13ff2a4f11458a2cfac3197 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 11 May 2025 09:14:02 +0200 Subject: [PATCH 25/70] Prepare BSIM4.7 for instance flag dtemp FIXME: The dtemp value is not yet linked to the model! --- src/spicelib/devices/bsim4v7/b4v7.c | 1 + src/spicelib/devices/bsim4v7/b4v7ask.c | 3 +++ src/spicelib/devices/bsim4v7/b4v7par.c | 4 ++++ src/spicelib/devices/bsim4v7/b4v7set.c | 3 ++- src/spicelib/devices/bsim4v7/bsim4v7def.h | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/spicelib/devices/bsim4v7/b4v7.c b/src/spicelib/devices/bsim4v7/b4v7.c index f1d70b209..cb46a904c 100644 --- a/src/spicelib/devices/bsim4v7/b4v7.c +++ b/src/spicelib/devices/bsim4v7/b4v7.c @@ -57,6 +57,7 @@ IOP( "mulu0", BSIM4v7_MULU0, IF_REAL, "Low field mobility multiplier"), IOP( "xgw", BSIM4v7_XGW, IF_REAL, "Distance from gate contact center to device edge"), IOP( "ngcon", BSIM4v7_NGCON, IF_REAL, "Number of gate contacts"), IOP( "wnflag", BSIM4v7_WNFLAG, IF_INTEGER, "W/NF device flag for bin selection"), +IOPU( "dtemp", BSIM4v7_DTEMP, IF_REAL, "Instance temperature difference"), IOP( "trnqsmod", BSIM4v7_TRNQSMOD, IF_INTEGER, "Transient NQS model selector"), IOP( "acnqsmod", BSIM4v7_ACNQSMOD, IF_INTEGER, "AC NQS model selector"), diff --git a/src/spicelib/devices/bsim4v7/b4v7ask.c b/src/spicelib/devices/bsim4v7/b4v7ask.c index 19690eda9..f20dc692b 100644 --- a/src/spicelib/devices/bsim4v7/b4v7ask.c +++ b/src/spicelib/devices/bsim4v7/b4v7ask.c @@ -114,6 +114,9 @@ BSIM4v7instance *here = (BSIM4v7instance*)inst; case BSIM4v7_MULU0: value->rValue = here->BSIM4v7mulu0; return(OK); + case BSIM4v7_DTEMP: + value->rValue = here->BSIM4v7dtemp; + return(OK); case BSIM4v7_WNFLAG: value->iValue = here->BSIM4v7wnflag; return(OK); diff --git a/src/spicelib/devices/bsim4v7/b4v7par.c b/src/spicelib/devices/bsim4v7/b4v7par.c index d8bdd6d3d..e4c5a099a 100644 --- a/src/spicelib/devices/bsim4v7/b4v7par.c +++ b/src/spicelib/devices/bsim4v7/b4v7par.c @@ -141,6 +141,10 @@ IFvalue *select) here->BSIM4v7mulu0 = value->rValue; here->BSIM4v7mulu0Given = TRUE; break; + case BSIM4v7_DTEMP: + here->BSIM4v7dtemp = value->rValue; + here->BSIM4v7dtempGiven = TRUE; + return(OK); case BSIM4v7_WNFLAG: here->BSIM4v7wnflag = value->iValue; here->BSIM4v7wnflagGiven = TRUE; diff --git a/src/spicelib/devices/bsim4v7/b4v7set.c b/src/spicelib/devices/bsim4v7/b4v7set.c index ed2fd017f..1d3246b47 100644 --- a/src/spicelib/devices/bsim4v7/b4v7set.c +++ b/src/spicelib/devices/bsim4v7/b4v7set.c @@ -2256,8 +2256,9 @@ BSIM4v7instance **InstArray; here->BSIM4v7xgw = model->BSIM4v7xgw; if (!here->BSIM4v7ngconGiven) here->BSIM4v7ngcon = model->BSIM4v7ngcon; + if (!here->BSIM4v7dtempGiven) + here->BSIM4v7dtemp = 0; - /* Process instance model selectors, some * may override their global counterparts */ diff --git a/src/spicelib/devices/bsim4v7/bsim4v7def.h b/src/spicelib/devices/bsim4v7/bsim4v7def.h index e4c1f1adc..073bec7ff 100644 --- a/src/spicelib/devices/bsim4v7/bsim4v7def.h +++ b/src/spicelib/devices/bsim4v7/bsim4v7def.h @@ -116,6 +116,7 @@ typedef struct sBSIM4v7instance int BSIM4v7wnflag; double BSIM4v7xgw; double BSIM4v7ngcon; + double BSIM4v7dtemp; /* added here to account stress effect instance dependence */ double BSIM4v7u0temp; @@ -302,6 +303,7 @@ typedef struct sBSIM4v7instance unsigned BSIM4v7wnflagGiven :1; unsigned BSIM4v7xgwGiven :1; unsigned BSIM4v7ngconGiven :1; + unsigned BSIM4v7dtempGiven : 1; unsigned BSIM4v7icVDSGiven :1; unsigned BSIM4v7icVGSGiven :1; unsigned BSIM4v7icVBSGiven :1; @@ -2843,6 +2845,7 @@ typedef struct sBSIM4v7model #define BSIM4v7_M 38 #define BSIM4v7_MULU0 39 #define BSIM4v7_WNFLAG 40 +#define BSIM4v7_DTEMP 41 /* Global parameters */ #define BSIM4v7_MOD_TEMPEOT 65 From f885d45e156b35e36fd08989f6a86ec461a1593c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Feb 2026 14:50:18 +0100 Subject: [PATCH 26/70] Prepare for instance parameter 'dtemp', required by commercial PDKs. Not yet support in the model, silently ignored. --- src/spicelib/devices/bsim4v5/b4v5.c | 2 +- src/spicelib/devices/bsim4v5/b4v5ask.c | 3 +++ src/spicelib/devices/bsim4v5/b4v5par.c | 4 ++++ src/spicelib/devices/bsim4v5/b4v5set.c | 3 ++- src/spicelib/devices/bsim4v5/bsim4v5def.h | 3 +++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/spicelib/devices/bsim4v5/b4v5.c b/src/spicelib/devices/bsim4v5/b4v5.c index 2d920af47..30d05ac74 100644 --- a/src/spicelib/devices/bsim4v5/b4v5.c +++ b/src/spicelib/devices/bsim4v5/b4v5.c @@ -49,7 +49,7 @@ IOPR( "delvt0", BSIM4v5_DELVTO, IF_REAL , "Zero bias threshold voltage variat IOP( "mulu0", BSIM4v5_MULU0, IF_REAL, "Low field mobility multiplier"), IOP( "xgw", BSIM4v5_XGW, IF_REAL, "Distance from gate contact center to device edge"), IOP( "ngcon", BSIM4v5_NGCON, IF_REAL, "Number of gate contacts"), - +IOPU("dtemp", BSIM4v5_DTEMP, IF_REAL, "Instance temperature difference"), IOP( "trnqsmod", BSIM4v5_TRNQSMOD, IF_INTEGER, "Transient NQS model selector"), IOP( "acnqsmod", BSIM4v5_ACNQSMOD, IF_INTEGER, "AC NQS model selector"), diff --git a/src/spicelib/devices/bsim4v5/b4v5ask.c b/src/spicelib/devices/bsim4v5/b4v5ask.c index 95ae3c800..829bb3e4c 100644 --- a/src/spicelib/devices/bsim4v5/b4v5ask.c +++ b/src/spicelib/devices/bsim4v5/b4v5ask.c @@ -112,6 +112,9 @@ BSIM4v5instance *here = (BSIM4v5instance*)inst; case BSIM4v5_MULU0: value->rValue = here->BSIM4v5mulu0; return(OK); + case BSIM4v5_DTEMP: + value->rValue = here->BSIM4v5dtemp; + return(OK); case BSIM4v5_XGW: value->rValue = here->BSIM4v5xgw; return(OK); diff --git a/src/spicelib/devices/bsim4v5/b4v5par.c b/src/spicelib/devices/bsim4v5/b4v5par.c index 1013ce700..f3c07dee7 100644 --- a/src/spicelib/devices/bsim4v5/b4v5par.c +++ b/src/spicelib/devices/bsim4v5/b4v5par.c @@ -139,6 +139,10 @@ IFvalue *select) here->BSIM4v5mulu0 = value->rValue; here->BSIM4v5mulu0Given = TRUE; break; + case BSIM4v5_DTEMP: + here->BSIM4v5dtemp = value->rValue; + here->BSIM4v5dtempGiven = TRUE; + return(OK); case BSIM4v5_XGW: here->BSIM4v5xgw = value->rValue; here->BSIM4v5xgwGiven = TRUE; diff --git a/src/spicelib/devices/bsim4v5/b4v5set.c b/src/spicelib/devices/bsim4v5/b4v5set.c index d16f3747b..9214bc3ca 100644 --- a/src/spicelib/devices/bsim4v5/b4v5set.c +++ b/src/spicelib/devices/bsim4v5/b4v5set.c @@ -1751,8 +1751,9 @@ BSIM4v5instance **InstArray; here->BSIM4v5xgw = model->BSIM4v5xgw; if (!here->BSIM4v5ngconGiven) here->BSIM4v5ngcon = model->BSIM4v5ngcon; + if (!here->BSIM4v5dtempGiven) + here->BSIM4v5dtemp = 0; - /* Process instance model selectors, some * may override their global counterparts */ diff --git a/src/spicelib/devices/bsim4v5/bsim4v5def.h b/src/spicelib/devices/bsim4v5/bsim4v5def.h index 2120a59d3..f397af3b7 100644 --- a/src/spicelib/devices/bsim4v5/bsim4v5def.h +++ b/src/spicelib/devices/bsim4v5/bsim4v5def.h @@ -105,6 +105,7 @@ typedef struct sBSIM4v5instance double BSIM4v5mulu0; double BSIM4v5xgw; double BSIM4v5ngcon; + double BSIM4v5dtemp; /* added here to account stress effect instance dependence */ double BSIM4v5u0temp; @@ -288,6 +289,7 @@ typedef struct sBSIM4v5instance unsigned BSIM4v5mulu0Given :1; unsigned BSIM4v5xgwGiven :1; unsigned BSIM4v5ngconGiven :1; + unsigned BSIM4v5dtempGiven : 1; unsigned BSIM4v5icVDSGiven :1; unsigned BSIM4v5icVGSGiven :1; unsigned BSIM4v5icVBSGiven :1; @@ -2536,6 +2538,7 @@ typedef struct sBSIM4v5model #define BSIM4v5_SC 37 #define BSIM4v5_M 38 #define BSIM4v5_MULU0 39 +#define BSIM4v5_DTEMP 40 /* Global parameters */ #define BSIM4v5_MOD_TEMPMOD 89 From 3d5d2390a2878d4e7982c6e37eb32ad2b456108f Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Feb 2026 15:06:30 +0100 Subject: [PATCH 27/70] Prepare for instance parameter 'dtemp', required by commercial PDKs. Not yet support in the model, silently ignored. --- src/spicelib/devices/bsim4/b4.c | 1 + src/spicelib/devices/bsim4/b4ask.c | 3 +++ src/spicelib/devices/bsim4/b4par.c | 4 ++++ src/spicelib/devices/bsim4/b4set.c | 2 ++ src/spicelib/devices/bsim4/bsim4def.h | 4 ++++ 5 files changed, 14 insertions(+) diff --git a/src/spicelib/devices/bsim4/b4.c b/src/spicelib/devices/bsim4/b4.c index 438c1498b..83f3115e5 100644 --- a/src/spicelib/devices/bsim4/b4.c +++ b/src/spicelib/devices/bsim4/b4.c @@ -59,6 +59,7 @@ IOP( "mulu0", BSIM4_MULU0, IF_REAL, "Low field mobility multiplier"), IOP( "xgw", BSIM4_XGW, IF_REAL, "Distance from gate contact center to device edge"), IOP( "ngcon", BSIM4_NGCON, IF_REAL, "Number of gate contacts"), IOP( "wnflag", BSIM4_WNFLAG, IF_INTEGER, "W/NF device flag for bin selection"), +IOPU("dtemp", BSIM4_DTEMP, IF_REAL, "Instance temperature difference"), IOP( "trnqsmod", BSIM4_TRNQSMOD, IF_INTEGER, "Transient NQS model selector"), IOP( "acnqsmod", BSIM4_ACNQSMOD, IF_INTEGER, "AC NQS model selector"), diff --git a/src/spicelib/devices/bsim4/b4ask.c b/src/spicelib/devices/bsim4/b4ask.c index d43e1a708..abced660c 100644 --- a/src/spicelib/devices/bsim4/b4ask.c +++ b/src/spicelib/devices/bsim4/b4ask.c @@ -135,6 +135,9 @@ BSIM4instance *here = (BSIM4instance*)inst; case BSIM4_WNFLAG: value->iValue = here->BSIM4wnflag; return(OK); + case BSIM4_DTEMP: + value->rValue = here->BSIM4dtemp; + return(OK); case BSIM4_XGW: value->rValue = here->BSIM4xgw; return(OK); diff --git a/src/spicelib/devices/bsim4/b4par.c b/src/spicelib/devices/bsim4/b4par.c index ac4a274c5..44373bfd6 100644 --- a/src/spicelib/devices/bsim4/b4par.c +++ b/src/spicelib/devices/bsim4/b4par.c @@ -165,6 +165,10 @@ IFvalue *select) here->BSIM4wnflag = value->iValue; here->BSIM4wnflagGiven = TRUE; break; + case BSIM4_DTEMP: + here->BSIM4dtemp = value->rValue; + here->BSIM4dtempGiven = TRUE; + return(OK); case BSIM4_XGW: here->BSIM4xgw = value->rValue; here->BSIM4xgwGiven = TRUE; diff --git a/src/spicelib/devices/bsim4/b4set.c b/src/spicelib/devices/bsim4/b4set.c index 71bc75a81..0e91f37d3 100644 --- a/src/spicelib/devices/bsim4/b4set.c +++ b/src/spicelib/devices/bsim4/b4set.c @@ -2371,6 +2371,8 @@ BSIM4instance **InstArray; here->BSIM4xgw = model->BSIM4xgw; if (!here->BSIM4ngconGiven) here->BSIM4ngcon = model->BSIM4ngcon; + if (!here->BSIM4dtempGiven) + here->BSIM4dtemp = 0; /* Process instance model selectors, some * may override their global counterparts diff --git a/src/spicelib/devices/bsim4/bsim4def.h b/src/spicelib/devices/bsim4/bsim4def.h index 6d7d652e8..17f810a19 100644 --- a/src/spicelib/devices/bsim4/bsim4def.h +++ b/src/spicelib/devices/bsim4/bsim4def.h @@ -121,6 +121,7 @@ typedef struct sBSIM4instance int BSIM4wnflag; double BSIM4xgw; double BSIM4ngcon; + double BSIM4dtemp; /* added here to account stress effect instance dependence */ @@ -316,6 +317,7 @@ typedef struct sBSIM4instance unsigned BSIM4wnflagGiven :1; unsigned BSIM4xgwGiven :1; unsigned BSIM4ngconGiven :1; + unsigned BSIM4dtempGiven : 1; unsigned BSIM4icVDSGiven :1; unsigned BSIM4icVGSGiven :1; unsigned BSIM4icVBSGiven :1; @@ -2884,6 +2886,8 @@ typedef struct sBSIM4model #define BSIM4_WEFF 46 #define BSIM4_LEFF 47 +#define BSIM4_DTEMP 48 + /* Global parameters */ #define BSIM4_MOD_TEMPEOT 66 #define BSIM4_MOD_LEFFEOT 67 From 98b84695bf4382ec09c95a0d9d5e5be93bce0a1e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Feb 2026 15:18:47 +0100 Subject: [PATCH 28/70] Prepare for instance parameter 'dtemp', required by commercial PDKs. Not yet support in the model, silently ignored. --- src/spicelib/devices/bsim4v6/b4v6.c | 2 +- src/spicelib/devices/bsim4v6/b4v6ask.c | 3 +++ src/spicelib/devices/bsim4v6/b4v6par.c | 4 ++++ src/spicelib/devices/bsim4v6/b4v6set.c | 3 ++- src/spicelib/devices/bsim4v6/bsim4v6def.h | 3 +++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/spicelib/devices/bsim4v6/b4v6.c b/src/spicelib/devices/bsim4v6/b4v6.c index 1ecb91625..ee160010d 100644 --- a/src/spicelib/devices/bsim4v6/b4v6.c +++ b/src/spicelib/devices/bsim4v6/b4v6.c @@ -55,7 +55,7 @@ IOPR( "delvt0", BSIM4v6_DELVTO, IF_REAL , "Zero bias threshold voltage variat IOP( "mulu0", BSIM4v6_MULU0, IF_REAL, "Low field mobility multiplier"), IOP( "xgw", BSIM4v6_XGW, IF_REAL, "Distance from gate contact center to device edge"), IOP( "ngcon", BSIM4v6_NGCON, IF_REAL, "Number of gate contacts"), - +IOPU("dtemp", BSIM4v6_DTEMP, IF_REAL, "Instance temperature difference"), IOP( "trnqsmod", BSIM4v6_TRNQSMOD, IF_INTEGER, "Transient NQS model selector"), IOP( "acnqsmod", BSIM4v6_ACNQSMOD, IF_INTEGER, "AC NQS model selector"), diff --git a/src/spicelib/devices/bsim4v6/b4v6ask.c b/src/spicelib/devices/bsim4v6/b4v6ask.c index 64ce8b7bc..d9a025f0e 100644 --- a/src/spicelib/devices/bsim4v6/b4v6ask.c +++ b/src/spicelib/devices/bsim4v6/b4v6ask.c @@ -114,6 +114,9 @@ BSIM4v6instance *here = (BSIM4v6instance*)inst; case BSIM4v6_MULU0: value->rValue = here->BSIM4v6mulu0; return(OK); + case BSIM4v6_DTEMP: + value->rValue = here->BSIM4v6dtemp; + return(OK); case BSIM4v6_XGW: value->rValue = here->BSIM4v6xgw; return(OK); diff --git a/src/spicelib/devices/bsim4v6/b4v6par.c b/src/spicelib/devices/bsim4v6/b4v6par.c index 795fdc77c..f9430ab78 100644 --- a/src/spicelib/devices/bsim4v6/b4v6par.c +++ b/src/spicelib/devices/bsim4v6/b4v6par.c @@ -141,6 +141,10 @@ IFvalue *select) here->BSIM4v6mulu0 = value->rValue; here->BSIM4v6mulu0Given = TRUE; break; + case BSIM4v6_DTEMP: + here->BSIM4v6dtemp = value->rValue; + here->BSIM4v6dtempGiven = TRUE; + return(OK); case BSIM4v6_XGW: here->BSIM4v6xgw = value->rValue; here->BSIM4v6xgwGiven = TRUE; diff --git a/src/spicelib/devices/bsim4v6/b4v6set.c b/src/spicelib/devices/bsim4v6/b4v6set.c index 4ad739c34..3bf919304 100644 --- a/src/spicelib/devices/bsim4v6/b4v6set.c +++ b/src/spicelib/devices/bsim4v6/b4v6set.c @@ -2105,8 +2105,9 @@ BSIM4v6instance **InstArray; here->BSIM4v6xgw = model->BSIM4v6xgw; if (!here->BSIM4v6ngconGiven) here->BSIM4v6ngcon = model->BSIM4v6ngcon; + if (!here->BSIM4v6dtempGiven) + here->BSIM4v6dtemp = 0; - /* Process instance model selectors, some * may override their global counterparts */ diff --git a/src/spicelib/devices/bsim4v6/bsim4v6def.h b/src/spicelib/devices/bsim4v6/bsim4v6def.h index 94ee3b0c0..28f2aba35 100644 --- a/src/spicelib/devices/bsim4v6/bsim4v6def.h +++ b/src/spicelib/devices/bsim4v6/bsim4v6def.h @@ -114,6 +114,7 @@ typedef struct sBSIM4v6instance double BSIM4v6mulu0; double BSIM4v6xgw; double BSIM4v6ngcon; + double BSIM4v6dtemp; /* added here to account stress effect instance dependence */ double BSIM4v6u0temp; @@ -297,6 +298,7 @@ typedef struct sBSIM4v6instance unsigned BSIM4v6mulu0Given :1; unsigned BSIM4v6xgwGiven :1; unsigned BSIM4v6ngconGiven :1; + unsigned BSIM4v6dtempGiven : 1; unsigned BSIM4v6icVDSGiven :1; unsigned BSIM4v6icVGSGiven :1; unsigned BSIM4v6icVBSGiven :1; @@ -2713,6 +2715,7 @@ typedef struct sBSIM4v6model #define BSIM4v6_SC 37 #define BSIM4v6_M 38 #define BSIM4v6_MULU0 39 +#define BSIM4v6_DTEMP 40 /* Global parameters */ #define BSIM4v6_MOD_TEMPEOT 65 From 6c8ab82e9e3be27ff61d2fd2754308588ddf2722 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 21 Feb 2026 14:20:30 +0100 Subject: [PATCH 29/70] V Pulse delivers erratic outputs, when not parameterized properly. Update the pulse generation: negative TR, TF, PW, PER not possible, get default values instead. 0 PW is allowed. Defaults are: TR negative or 0 --> TR = ckt->CKTstep TF negative or 0 --> TF = ckt->CKTstep PW < 0 --> PW = 0 PER <= 0 --> PER = TR + TF + PW */ --- src/spicelib/devices/vsrc/vsrcacct.c | 16 ++++++++++------ src/spicelib/devices/vsrc/vsrcload.c | 19 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/spicelib/devices/vsrc/vsrcacct.c b/src/spicelib/devices/vsrc/vsrcacct.c index df890ed76..6835cd0df 100644 --- a/src/spicelib/devices/vsrc/vsrcacct.c +++ b/src/spicelib/devices/vsrc/vsrcacct.c @@ -56,20 +56,24 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) double phase; double deltat; + /* TR negative or 0 --> TR = ckt->CKTstep + TF negative or 0 --> TF = ckt->CKTstep + PW < 0 --> PW = 0 + PER <= 0 --> PER = TR + TF + PW */ TD = here->VSRCfunctionOrder > 2 ? here->VSRCcoeffs[2] : 0.0; TR = here->VSRCfunctionOrder > 3 - && here->VSRCcoeffs[3] != 0.0 + && here->VSRCcoeffs[3] > 0.0 ? here->VSRCcoeffs[3] : ckt->CKTstep; TF = here->VSRCfunctionOrder > 4 - && here->VSRCcoeffs[4] != 0.0 + && here->VSRCcoeffs[4] > 0.0 ? here->VSRCcoeffs[4] : ckt->CKTstep; PW = here->VSRCfunctionOrder > 5 - && here->VSRCcoeffs[5] != 0.0 - ? here->VSRCcoeffs[5] : ckt->CKTfinalTime; + && here->VSRCcoeffs[5] >= 0.0 + ? here->VSRCcoeffs[5] : 0.0; PER = here->VSRCfunctionOrder > 6 - && here->VSRCcoeffs[6] != 0.0 - ? here->VSRCcoeffs[6] : ckt->CKTfinalTime; + && here->VSRCcoeffs[6] > 0.0 + ? here->VSRCcoeffs[6] : TR + TF + PW; PHASE = here->VSRCfunctionOrder > 7 ? here->VSRCcoeffs[7] : 0.0; diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index c74c9a756..5f88bc587 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -86,7 +86,12 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) } else { time = ckt->CKTtime; } - /* use the transient functions */ + /* use the transient functions. Parameter limits: + TR negative or 0 --> TR = ckt->CKTstep + TF negative or 0 --> TF = ckt->CKTstep + PW < 0 --> PW = 0 + PER <= 0 --> PER = TR + TF + PW + */ switch(here->VSRCfunctionType) { default: @@ -106,17 +111,17 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) TD = here->VSRCfunctionOrder > 2 ? here->VSRCcoeffs[2] : 0.0; TR = here->VSRCfunctionOrder > 3 - && here->VSRCcoeffs[3] != 0.0 + && here->VSRCcoeffs[3] > 0.0 ? here->VSRCcoeffs[3] : ckt->CKTstep; TF = here->VSRCfunctionOrder > 4 - && here->VSRCcoeffs[4] != 0.0 + && here->VSRCcoeffs[4] > 0.0 ? here->VSRCcoeffs[4] : ckt->CKTstep; PW = here->VSRCfunctionOrder > 5 - && here->VSRCcoeffs[5] != 0.0 - ? here->VSRCcoeffs[5] : ckt->CKTfinalTime; + && here->VSRCcoeffs[5] >= 0.0 + ? here->VSRCcoeffs[5] : 0.0; PER = here->VSRCfunctionOrder > 6 - && here->VSRCcoeffs[6] != 0.0 - ? here->VSRCcoeffs[6] : ckt->CKTfinalTime; + && here->VSRCcoeffs[6] > 0.0 + ? here->VSRCcoeffs[6] : TR + TF + PW; /* shift time by delay time TD */ time -= TD; From d297a14ed8334451ffd0b3d417a82507b9809e4a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 21 Feb 2026 14:30:51 +0100 Subject: [PATCH 30/70] Move comment --- src/spicelib/devices/vsrc/vsrcload.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index 5f88bc587..c4190d25d 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -86,12 +86,7 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) } else { time = ckt->CKTtime; } - /* use the transient functions. Parameter limits: - TR negative or 0 --> TR = ckt->CKTstep - TF negative or 0 --> TF = ckt->CKTstep - PW < 0 --> PW = 0 - PER <= 0 --> PER = TR + TF + PW - */ + /* use the transient functions. */ switch(here->VSRCfunctionType) { default: @@ -99,6 +94,12 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) break; case PULSE: { + /* Parameter limits : + TR negative or 0 --> TR = ckt->CKTstep + TF negative or 0 --> TF = ckt->CKTstep + PW < 0 --> PW = 0 + PER <= 0 --> PER = TR + TF + PW + */ double V1, V2, TD, TR, TF, PW, PER; double basetime = 0; double PHASE; From 3c570744dc72fffdefd07e8097386a42b101d096 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 15:43:52 +0100 Subject: [PATCH 31/70] Ignore .hdl and .biaswarn commands. --- src/frontend/inpcom.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 4cf3b2edf..33d5a4063 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1331,6 +1331,8 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name static int is_control = 0; /* We are reading from a .control section */ bool found_end = FALSE, shell_eol_continuation = FALSE; + static bool biaswarn = FALSE; + static bool hdlwarn = FALSE; #ifdef CIDER static int in_cider_model = 0; #endif @@ -1440,6 +1442,26 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name memcpy(buffer, ".inc", 4); } + if (ciprefix(".hdl", buffer)) { + if (!hdlwarn) { + fprintf(cp_err, "Warning: Dot command .hdl is not supported, ingnored\n"); + fprintf(cp_err, " line no. %d, %s", line_number, buffer); + fprintf(cp_err, " file %s\n", file_name); + fprintf(cp_err, " This message will be posted only once!\n\n"); + hdlwarn = TRUE; + } + tfree(buffer); + continue; + } + if (ciprefix(".biaschk", buffer)) { + if (!biaswarn) { + fprintf(cp_err, "Warning: Dot command .biaschk is not supported, ingnored\n"); + fprintf(cp_err, " This message will be posted only once!\n\n"); + biaswarn = TRUE; + } + tfree(buffer); + continue; + } /* now handle .include statements */ if (ciprefix(".include", buffer) || ciprefix(".inc", buffer)) { @@ -2842,6 +2864,7 @@ static void inp_fix_macro_param_func_paren_io(struct card *card) str_ptr[3] = 'c'; str_ptr[4] = ' '; } +// fprintf(stdout, "%s\n", card->line); } } } From 8e60d4dd0f0d8d650332ff82cd96cde098a4598c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 15:50:24 +0100 Subject: [PATCH 32/70] Special treatment while spiltting multiple .param lines, if .param is a .func --- src/frontend/inpcom.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 33d5a4063..c7e7c9958 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -5661,11 +5661,9 @@ static void inp_reorder_params( } -// iterate through deck and find lines with multiply defined parameters -// -// split line up into multiple lines and place those new lines immediately -// after the current multi-param line in the deck - +/* Iterate through deck and find lines with more than one parameter defined + Split line up into multiple lines and place those new lines immediately + after the current multi-param line in the deck */ static int inp_split_multi_param_lines(struct card *card, int line_num) { for (; card; card = card->nextcard) { @@ -5701,6 +5699,12 @@ static int inp_split_multi_param_lines(struct card *card, int line_num) int paren_depth = 0; beg_param = skip_back_ws(equal_ptr, curr_line); + /* Special treatment if .param is a .func: + move back to opening '(' */ + if (*(beg_param - 1) == ')') { + while (beg_param > curr_line && *beg_param != '(') + beg_param--; + } beg_param = skip_back_non_ws(beg_param, curr_line); end_param = skip_ws(equal_ptr + 1); while (*end_param && !isspace_c(*end_param)) { From 39a0fe78cd863dde017249551d891a1220773238 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 11 May 2025 16:41:47 +0200 Subject: [PATCH 33/70] Add a parameter m (output multiplicator) to spice2poly code model. --- src/xspice/icm/spice2poly/icm_spice2poly/cfunc.mod | 12 +++++++----- .../icm/spice2poly/icm_spice2poly/ifspec.ifs | 14 +++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/xspice/icm/spice2poly/icm_spice2poly/cfunc.mod b/src/xspice/icm/spice2poly/icm_spice2poly/cfunc.mod index df6fdff20..b869d0845 100644 --- a/src/xspice/icm/spice2poly/icm_spice2poly/cfunc.mod +++ b/src/xspice/icm/spice2poly/icm_spice2poly/cfunc.mod @@ -47,7 +47,7 @@ required syntax. This model may also be called directly as follows: a1 [ ] xxx - .model xxx spice2poly ( coef = [ ] ) + .model xxx spice2poly ( coef = [ ] [m = val] ) Refer to the 2G6 User Guide for an explanation of the coefficients. @@ -102,12 +102,14 @@ void spice2poly (ARGS) double sum; /* Temporary for accumulating sum of terms */ double product; /* Temporary for accumulating product */ - + double mult; /* multplicator for G and F sources */ /* Get number of input values */ num_inputs = PORT_SIZE(in); + mult = PARAM(m); + /* If this is the first call to the model, allocate the static variable */ /* array */ @@ -165,7 +167,7 @@ void spice2poly (ARGS) /* Add the product times the appropriate coefficient into the sum */ sum += coef[i] * product; } - OUTPUT(out) = sum; + OUTPUT(out) = sum * mult; /* Compute and output the partials for each input */ @@ -201,13 +203,13 @@ void spice2poly (ARGS) sum += coef[j] * product; } - PARTIAL(out,in[i]) = sum; + PARTIAL(out,in[i]) = sum * mult; /* If this is DC analysis, save the partial for use as AC gain */ /* value in an AC analysis */ if(ANALYSIS == MIF_DC) - STATIC_VAR(acgains[i]) = sum; + STATIC_VAR(acgains[i]) = sum * mult; } /* Free the allocated items and return */ diff --git a/src/xspice/icm/spice2poly/icm_spice2poly/ifspec.ifs b/src/xspice/icm/spice2poly/icm_spice2poly/ifspec.ifs index 6aab4de10..02f4b6387 100644 --- a/src/xspice/icm/spice2poly/icm_spice2poly/ifspec.ifs +++ b/src/xspice/icm/spice2poly/icm_spice2poly/ifspec.ifs @@ -16,7 +16,8 @@ AUTHORS MODIFICATIONS - + 11/05/2025 Holger Vogt + new parameter m (multiplicator) SUMMARY @@ -67,6 +68,17 @@ Vector: yes Vector_Bounds: [2 -] Null_Allowed: no +PARAMETER_TABLE: + +Parameter_Name: m +Description: "multiplicator" +Data_Type: real +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + STATIC_VAR_TABLE: From cf2293c317bbd7d2dfde2c3cf9b7ca233a5f6f5e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 16:06:43 +0100 Subject: [PATCH 34/70] Add parameter m (multiplier), while translating from G or F source to code model spice2poly --- src/xspice/enh/enhtrans.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c index 2eb23aa2a..5482003c3 100644 --- a/src/xspice/enh/enhtrans.c +++ b/src/xspice/enh/enhtrans.c @@ -301,7 +301,7 @@ static char *two2three_translate( char **out_conn; char **in_conn; char **coef; - + char* multibeg, *multiend, *multi = NULL; char *card; @@ -313,6 +313,20 @@ static char *two2three_translate( /* Put the first character into local storage for checking type */ type = *orig_card; + /* There may be a multiplier m=val + Remove it here, add it later */ + multibeg = strstr(orig_card, " m="); + if (multibeg) { + multiend = multibeg + 3; + while (*multiend == ' ') + multiend++; + while (*multiend && *multiend != ' ') + multiend++; + multi = copy_substring(multibeg, multiend); + while (multibeg < multiend) + *(multibeg++) = ' '; + } + /* Count the number of tokens for use in parsing */ num_tokens = count_tokens(orig_card); @@ -414,6 +428,9 @@ static char *two2three_translate( for(i = 0; i < num_coefs; i++) mod_card_len += strlen(coef[i]) + 1; + if (multi && (type == 'g' || type == 'G' || type == 'f'|| type == 'F')) + mod_card_len += strlen(multi) + 1; + /* Allocate space for the cards and write them into the strings */ *inst_card = TMALLOC(char, inst_card_len); @@ -460,6 +477,11 @@ static char *two2three_translate( sprintf(*mod_card + strlen(*mod_card), "%s ", coef[i]); sprintf(*mod_card + strlen(*mod_card), "]"); + if (multi && (type == 'g' || type == 'G' || type == 'f' || type == 'F')) { + sprintf(*mod_card + strlen(*mod_card), " %s", multi); + tfree(multi); + } + #ifdef TRACE /* SDB debug statement */ printf("In two2three_translate, translated statements:\n%s \n%s \n", *inst_card, *mod_card); From 359c321dcf08ee1fa857102d7f87295a555b0ed7 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 11 May 2025 17:34:39 +0200 Subject: [PATCH 35/70] Add warning message if E or H poly source has multiplier m --- src/xspice/enh/enhtrans.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c index 5482003c3..df5fbfd8e 100644 --- a/src/xspice/enh/enhtrans.c +++ b/src/xspice/enh/enhtrans.c @@ -482,6 +482,12 @@ static char *two2three_translate( tfree(multi); } + if (multi && (type == 'e' || type == 'E' || type == 'h' || type == 'H')) { + fprintf(stderr, "Warning: multiplier m not available for E and H poly sources, ignored as\n" + " %s\n", orig_card); + tfree(multi); + } + #ifdef TRACE /* SDB debug statement */ printf("In two2three_translate, translated statements:\n%s \n%s \n", *inst_card, *mod_card); From f8cecd3e5367a5829caaaa5bcb1d1e294e72917c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 16:10:17 +0100 Subject: [PATCH 36/70] Improve error message. --- src/xspice/enh/enhtrans.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c index df5fbfd8e..ce22e2025 100644 --- a/src/xspice/enh/enhtrans.c +++ b/src/xspice/enh/enhtrans.c @@ -370,8 +370,8 @@ static char *two2three_translate( if(num_coefs < 1) { char *errmsg; - printf("ERROR - Number of connections differs from poly dimension\n"); - printf("ERROR while parsing: %s\n", orig_card); + fprintf(stderr, "ERROR - Number of connections differs from poly dimension\n"); + fprintf(stderr, " while parsing: %s\n", orig_card); errmsg = copy("ERROR in two2three_translate -- Argument to poly() is not an integer\n"); *inst_card = copy("* ERROR - Number of connections differs from poly dimension\n"); *mod_card = copy(" * ERROR - Number of connections differs from poly dimension\n"); From 2612104891d91ea63618341f2ed7ad404fa156ac Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 16:30:20 +0100 Subject: [PATCH 37/70] Allow Cd as a valid C reference designator for a capacitor, i.e. lower-casing of node names, by re-organizing the setting of lower cases in the netlist: No lower-casing for cd only while in a .control section. --- src/frontend/inpcom.c | 79 ++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index c7e7c9958..7b3fb7b0c 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1774,15 +1774,16 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name } #endif /* no lower case letters for lines beginning with: */ - else if (!ciprefix("write", buffer) && - !ciprefix("wrdata", buffer) && - !ciprefix(".lib", buffer) && !ciprefix(".inc", buffer) && - !ciprefix("codemodel", buffer) && - !ciprefix("osdi", buffer) && - !ciprefix("pre_osdi", buffer) && - !ciprefix("echo", buffer) && !ciprefix("shell", buffer) && - !ciprefix("source", buffer) && !ciprefix("cd ", buffer) && - !ciprefix("load", buffer) && !ciprefix("setcs", buffer)) { + else if (!(ciprefix(".lib", buffer) || ciprefix(".inc", buffer) || + (is_control && ( + ciprefix("write", buffer) || + ciprefix("wrdata", buffer) || + ciprefix("codemodel", buffer) || + ciprefix("osdi", buffer) || + ciprefix("pre_osdi", buffer) || + ciprefix("echo", buffer) || ciprefix("shell", buffer) || + ciprefix("source", buffer) ||ciprefix("cd ", buffer) || + ciprefix("load", buffer) || ciprefix("setcs", buffer))))) { /* lower case for all other lines */ for (s = buffer; *s && (*s != '\n'); s++) *s = tolower_c(*s); @@ -1793,39 +1794,41 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name for (s = buffer; *s && (*s != '\n'); s++) ; } - /* lower case for variables or vectors in command 'echo' */ - if (ciprefix("echo", buffer)) { - char* p = buffer, *tmpstr; - while (p && *p != '\n' && *p != '\0') { - p = nexttok(p); - /* vectors or variables start with $ */ - if (p && *p == '$') { - for (tmpstr = p; *tmpstr && !isspace_c(*tmpstr); tmpstr++) - *tmpstr = tolower_c(*tmpstr); - p = tmpstr; + if (is_control) { + /* lower case for variables or vectors in command 'echo' */ + if (ciprefix("echo", buffer)) { + char* p = buffer, * tmpstr; + while (p && *p != '\n' && *p != '\0') { + p = nexttok(p); + /* vectors or variables start with $ */ + if (p && *p == '$') { + for (tmpstr = p; *tmpstr && !isspace_c(*tmpstr); tmpstr++) + *tmpstr = tolower_c(*tmpstr); + p = tmpstr; + } } } - } - /* add Inp_Path to buffer while keeping the sourcepath variable contents */ - if (ciprefix("set", buffer)) { - char *p; + /* add Inp_Path to buffer while keeping the sourcepath variable contents */ + if (ciprefix("set", buffer)) { + char* p; - p = skip_ws(buffer + 3); // Next word - if (strncmp(p, "sourcepath", 10) == 0 && - skip_non_ws(p) == p + 10) { - p = strchr(buffer, ')'); - if (p) { - *p = 0; // clear ) and insert Inp_Path in between - p = tprintf("%s %s ) %s", buffer, + p = skip_ws(buffer + 3); // Next word + if (strncmp(p, "sourcepath", 10) == 0 && + skip_non_ws(p) == p + 10) { + p = strchr(buffer, ')'); + if (p) { + *p = 0; // clear ) and insert Inp_Path in between + p = tprintf("%s %s ) %s", buffer, Inp_Path ? Inp_Path : "", p + 1); - tfree(buffer); - buffer = p; - /* s points to end of buffer */ - for (s = buffer; *s && (*s != '\n'); s++) - ; - } - else { - fprintf(stderr, "Warning: no closing parens found in 'set sourcepath' statement\n"); + tfree(buffer); + buffer = p; + /* s points to end of buffer */ + for (s = buffer; *s && (*s != '\n'); s++) + ; + } + else { + fprintf(stderr, "Warning: no closing parens found in 'set sourcepath' statement\n"); + } } } } From 1aa3fa22ffe05718ee64a13e9b9d9ae871f471a5 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Feb 2026 16:32:59 +0100 Subject: [PATCH 38/70] No quoting of potential params when comnpatmode is set to hs. --- src/frontend/inpcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 7b3fb7b0c..734c5668c 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -8419,6 +8419,9 @@ static void inp_quote_params(struct card *c, struct card *end_c, if (ft_skywaterpdk) return; + if (newcompat.hs) + return; + for (; c && c != end_c; c = c->nextcard) { int i, j, num_terminals; From a08110585fca0ef7c470d35da85b8a2966e11404 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 24 Feb 2026 14:17:37 +0100 Subject: [PATCH 39/70] Re-enable not setting lower case if commands from spinit or .spiceinit --- src/frontend/inpcom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 734c5668c..8dd284b3e 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1775,14 +1775,14 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name #endif /* no lower case letters for lines beginning with: */ else if (!(ciprefix(".lib", buffer) || ciprefix(".inc", buffer) || - (is_control && ( + ((comfile || is_control) && ( ciprefix("write", buffer) || ciprefix("wrdata", buffer) || ciprefix("codemodel", buffer) || ciprefix("osdi", buffer) || ciprefix("pre_osdi", buffer) || ciprefix("echo", buffer) || ciprefix("shell", buffer) || - ciprefix("source", buffer) ||ciprefix("cd ", buffer) || + ciprefix("source", buffer) ||ciprefix("cd", buffer) || ciprefix("load", buffer) || ciprefix("setcs", buffer))))) { /* lower case for all other lines */ for (s = buffer; *s && (*s != '\n'); s++) From c939f9610a2c01a9352be85afb7bbeb8445c7afa Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 24 Feb 2026 17:00:40 +0100 Subject: [PATCH 40/70] Improve error message, no output of line string if not 'set ngdebug' --- src/frontend/numparam/xpressn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index 13f08baae..57fde2af3 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -236,8 +236,7 @@ message(dico_t *dico, const char *fmt, ...) else { fprintf (stderr, - "Error in netlist line no. %d:\n" - "%s\n\n", + "Error in netlist line no. %d, new internal line no. %d:\n\n", dico->srcline, dico->oldline); } } From cd568e37bc198bb68d4f6c485e2dc63d0d5d8982 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 24 Feb 2026 17:02:00 +0100 Subject: [PATCH 41/70] Prevent potential buffer s overflow by limiting the characters written. Use vsnprintf instead of vsprintf. --- src/winmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/winmain.c b/src/winmain.c index a851f4242..ee19a73c6 100644 --- a/src/winmain.c +++ b/src/winmain.c @@ -1499,7 +1499,7 @@ win_x_fprintf(FILE *stream, const char *format, ...) if ((stream == stdout) || (stream == stderr)) { s[0] = SE; - result = vsprintf(s, format, args); + result = vsnprintf(s, IOBufSize, format, args); win_x_fputs(s, stream); } else result = vfprintf(stream, format, args); From b1f215149eaf779dbd23d9be16dfcf35dd389561 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 25 Feb 2026 14:14:25 +0100 Subject: [PATCH 42/70] Improve th error message: add the failing option's name. --- src/spicelib/parser/inpdoopt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/spicelib/parser/inpdoopt.c b/src/spicelib/parser/inpdoopt.c index 61d44ba0a..84fb5429c 100644 --- a/src/spicelib/parser/inpdoopt.c +++ b/src/spicelib/parser/inpdoopt.c @@ -67,10 +67,14 @@ INPdoOpts( } continue; } - - errmsg = TMALLOC(char, 100); - (void) strcpy(errmsg," Error: unknown option - ignored\n"); - optCard->error = INPerrCat(optCard->error,errmsg); - fprintf(stderr, "%s\n", optCard->error); + /* print err message only if it is not just a number */ + char* ctoken = token; + while (*ctoken && strchr("0123456789.e+-", *ctoken)) + ctoken++; + if (*ctoken) { + errmsg = tprintf("Error: unknown option %s - ignored\n", token); + optCard->error = INPerrCat(optCard->error, errmsg); + fprintf(stderr, "%s\n", optCard->error); + } } } From 04507a637d745136bd8b238e68e68c1b559d852c Mon Sep 17 00:00:00 2001 From: Enrique Date: Thu, 26 Feb 2026 22:39:34 +0100 Subject: [PATCH 43/70] Added parameter numPeriod so that the user can select the number of fundamental frequency periods used in the detection of the fundamental frequency and its harmonics. This is usefull when trying to recover the fundamental frequency and its harmonics from a noisy signal, like the output of a DCDC converter --- src/frontend/fourier.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/frontend/fourier.c b/src/frontend/fourier.c index d66efe3ee..5754700a3 100644 --- a/src/frontend/fourier.c +++ b/src/frontend/fourier.c @@ -23,7 +23,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group static char *pnum(double num); -static int CKTfour(int ndata, int numFreq, double *thd, double *Time, double *Value, +static int CKTfour(int ndata, int numFreq, int numPeriod, double *thd, double *Time, double *Value, double FundFreq, double *Freq, double *Mag, double *Phase, double *nMag, double *nPhase); @@ -32,8 +32,8 @@ static int CKTfour(int ndata, int numFreq, double *thd, double *Time, double *Va #define DEF_FOURGRIDSIZE 200 -/* CKTfour(ndata, numFreq, thd, Time, Value, FundFreq, Freq, Mag, Phase, nMag, nPhase) - * len 10 ? inp inp inp out out out out out +/* CKTfour(ndata, numFreq, numPeriod, thd, Time, Value, FundFreq, Freq, Mag, Phase, nMag, nPhase) + * len 10 1 ? inp inp inp out out out out out */ int @@ -42,7 +42,7 @@ fourier(wordlist *wl, struct plot *current_plot) struct dvec *time, *vec; struct pnode *pn, *names; double fundfreq, *data = NULL; - int nfreqs, fourgridsize, polydegree; + int nfreqs, nperiods, fourgridsize, polydegree; double *freq, *mag, *phase, *nmag, *nphase; /* Outputs from CKTfour */ double thd, *timescale = NULL; char *s; @@ -68,6 +68,8 @@ fourier(wordlist *wl, struct plot *current_plot) if (!cp_getvar("nfreqs", CP_NUM, &nfreqs, 0) || nfreqs < 1) nfreqs = 10; + if (!cp_getvar("nperiods", CP_NUM, &nperiods, 0) || nperiods < 1) + nperiods = 1; if (!cp_getvar("polydegree", CP_NUM, &polydegree, 0) || polydegree < 0) polydegree = 1; if (!cp_getvar("fourgridsize", CP_NUM, &fourgridsize, 0) || fourgridsize < 1) @@ -112,14 +114,16 @@ fourier(wordlist *wl, struct plot *current_plot) if (polydegree) { double *dp, d; + /* Get fourgridsize points per period */ + fourgridsize = fourgridsize * nperiods; /* Build the grid... */ timescale = TMALLOC(double, fourgridsize); data = TMALLOC(double, fourgridsize); dp = ft_minmax(time, TRUE); /* Now get the last fund freq... */ - d = 1 / fundfreq; /* The wavelength... */ + d = nperiods / fundfreq; /* The wavelength... */ if (dp[1] - dp[0] < d) { - fprintf(cp_err, "Error: wavelength longer than time span\n"); + fprintf(cp_err, "Error: (%d * wavelength) longer than time span\n", nperiods); goto done; } else if (dp[1] - dp[0] > d) { dp[0] = dp[1] - d; @@ -143,7 +147,7 @@ fourier(wordlist *wl, struct plot *current_plot) timescale = time->v_realdata; } - err = CKTfour(fourgridsize, nfreqs, &thd, timescale, + err = CKTfour(fourgridsize, nfreqs, nperiods, &thd, timescale, data, fundfreq, freq, mag, phase, nmag, nphase); if (err != OK) { @@ -153,9 +157,10 @@ fourier(wordlist *wl, struct plot *current_plot) fprintf(cp_out, "Fourier analysis for %s:\n", vec->v_name); fprintf(cp_out, - " No. Harmonics: %d, THD: %g %%, Gridsize: %d, Interpolation Degree: %d\n\n", + " No. Harmonics: %d, THD: %g %%, Gridsize: %d, Interpolation Degree: %d," + " No. Periods: %d\n\n", nfreqs, thd, fourgridsize, - polydegree); + polydegree, nperiods); /* Each field will have width cp_numdgt + 6 (or 7 * with HP-UX) + 1 if there is a - sign. */ @@ -288,6 +293,7 @@ static int CKTfour(int ndata, /* number of entries in the Time and Value arrays */ int numFreq, /* number of harmonics to calculate */ + int numPeriod, /* number of periods for detection */ double *thd, /* total harmonic distortion (percent) to be returned */ double *Time, /* times at which the voltage/current @@ -313,10 +319,10 @@ CKTfour(int ndata, /* number of entries in the Time and * The arrays must all be allocated by the caller. * The Time and Value array must be reasonably distributed over at * least one full period of the fundamental Frequency for the - * fourier transform to be useful. The function will take the - * last period of the frequency as data for the transform. + * fourier transform to be useful. The function will take + * numPeriod periods of the frequency as data for the transform. * - * We are assuming that the caller has provided exactly one period + * We are assuming that the caller has provided exactly numPeriod periods * of the fundamental frequency. */ int i; int j; @@ -331,10 +337,10 @@ CKTfour(int ndata, /* number of entries in the Time and Phase[i] = 0; } - for (i = 0; i < ndata; i++) + for (i = 0; i < ndata ; i++) for (j = 0; j < numFreq; j++) { - Mag[j] += Value[i] * sin(j*2.0*M_PI*i/((double)ndata)); - Phase[j] += Value[i] * cos(j*2.0*M_PI*i/((double)ndata)); + Mag[j] += Value[i] * sin(j*2.0*M_PI*numPeriod*i/((double)ndata)); + Phase[j] += Value[i] * cos(j*2.0*M_PI*numPeriod*i/((double)ndata)); } Mag[0] = Phase[0]/ndata; From 3103be7eeadcba539cd8ed841c019b87652abc55 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 27 Feb 2026 17:48:23 +0100 Subject: [PATCH 44/70] prepare for binning of N devices --- src/spicelib/parser/inp2n.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/spicelib/parser/inp2n.c b/src/spicelib/parser/inp2n.c index ef6c9a742..b01d8b44c 100644 --- a/src/spicelib/parser/inp2n.c +++ b/src/spicelib/parser/inp2n.c @@ -71,7 +71,10 @@ void INP2N(CKTcircuit *ckt, INPtables *tab, struct card *current) { if (i >= 2) { c = INPgetMod(ckt, token, &thismodel, tab); - if (c) { + /* check if using model binning -- pass in line since need 'l' and 'w' */ + if (!thismodel) + txfree(INPgetModBin(ckt, token, &thismodel, tab, line)); + if (c && !thismodel) { LITERR(c); tfree(c); tfree(token); From 5e82b63f6218e7deb038088f8c04bc84334241c4 Mon Sep 17 00:00:00 2001 From: dwarning Date: Sun, 1 Mar 2026 16:14:04 +0100 Subject: [PATCH 45/70] adapt FFT point number for real input vectors in fft cmd and vector operation wrt. bug #829 --- src/frontend/com_fft.c | 2 +- src/maths/cmaths/cmath4.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 2d592525b..baefb2c51 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -167,7 +167,7 @@ com_fft(wordlist *wl) in[j] = tdvec[0][j]*win[j]; /* data have same type and length - so we need only one plan */ - plan_forward = fftw_plan_dft_r2c_1d(length, in, out, FFTW_ESTIMATE); + plan_forward = fftw_plan_dft_r2c_1d(length-1, in, out, FFTW_ESTIMATE); for (i = 0; i Date: Sun, 1 Mar 2026 16:18:24 +0100 Subject: [PATCH 46/70] correct fft scaling at Nyquist freq. in fft cmd and vector operation --- src/frontend/com_fft.c | 4 +++- src/maths/cmaths/cmath4.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index baefb2c51..b06d1e37d 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -181,10 +181,12 @@ com_fft(wordlist *wl) scale = (double) fpts - 1.0; fdvec[i][0].cx_real = out[0][0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; - for (j = 1; j < fpts; j++) { + for (j = 1; j < fpts-1; j++) { fdvec[i][j].cx_real = out[j][0]/scale; fdvec[i][j].cx_imag = out[j][1]/scale; } + fdvec[i][fpts-1].cx_real = out[fpts-1][0]/scale/2.0; + fdvec[i][fpts-1].cx_imag = 0.0; } diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index c29e53634..92cfe458a 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -777,10 +777,12 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp scale = (double) fpts - 1.0; outdata[0].cx_real = out[0][0]/scale/2.0; outdata[0].cx_imag = 0.0; - for (i = 1; i < fpts; i++) { + for (i = 1; i < fpts-1; i++) { outdata[i].cx_real = out[i][0]/scale; outdata[i].cx_imag = out[i][1]/scale; } + outdata[fpts-1].cx_real = out[fpts-1][0]/scale/2.0; + outdata[fpts-1].cx_imag = 0.0; fftw_free(ind); From c4fc026af993b8bdded064ee8bf8d9c3d05f267a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 28 Feb 2026 16:15:28 +0100 Subject: [PATCH 47/70] Add command 'linearnp np=1024 vec1 vec2' linearises tran output on given number of time points. If none is given, 2^n new points are chosen, next to but smaller than original number of time points. --- src/frontend/commands.c | 4 ++ src/frontend/linear.c | 143 ++++++++++++++++++++++++++++++++++++++++ src/frontend/linear.h | 1 + 3 files changed, 148 insertions(+) diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 3de1ba6fb..e61aa069e 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -642,6 +642,10 @@ struct comm spcp_coms[] = { { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, " [ vec ... ] : Convert plot into one with linear scale." } , + { "linearnp", com_linearnp, FALSE, FALSE, + { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, + NULL, + " [ vec ... ] : Convert plot into one with linear scale, number of points given." }, { "cutout", com_cutout, FALSE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 3fc903824..7148b8f08 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -130,6 +130,149 @@ com_linearize(wordlist *wl) } } +/* Interpolate all the vectors in a plot to a linear time scale, which + * we determine by looking at the transient parameters in the CKT struct. + * If no circuit is loaded, e.g. because the 'load' command has been used + * to obtain data, try to get parameters from scale vector. + * Interpolation may be restricted to only a region of the input vector, + * thus creating a cutout of the original vector. The number of data points + * is given by parameter np=xx. + */ +void +com_linearnp(wordlist* wl) +{ + double tstart, tstop, tstep, d; + struct plot* new, * old; + struct dvec* newtime, * v; + struct dvec* oldtime; + struct dvec* lin; + int expo, len, i; + wordlist* wlnew; + + if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { + fprintf(cp_err, "Error: plot must be a transient analysis\n"); + return; + } + if (!plot_cur->pl_dvecs || !plot_cur->pl_scale) { + fprintf(cp_err, "Error: no vectors available\n"); + return; + } + if (!isreal(plot_cur->pl_scale)) { + fprintf(cp_err, "Error: non-real time scale for %s\n", + plot_cur->pl_typename); + return; + } + + /* check if circuit is loaded and TSTART, TSTOP, TSTEP are available + if no circuit is loaded, but vectors are available, obtain + start, stop, step data from scale vector */ + if (!ft_curckt || !ft_curckt->ci_ckt || + !if_tranparams(ft_curckt, &tstart, &tstop, &tstep)) { + fprintf(cp_err, + "Warning: Can't get transient parameters from circuit.\n" + " Use transient analysis scale vector data instead.\n"); + int length = plot_cur->pl_scale->v_length; + if (length < 1) { + fprintf(cp_err, "Error: no data in vector\n"); + return; + } + tstart = plot_cur->pl_scale->v_realdata[0]; + tstop = plot_cur->pl_scale->v_realdata[length - 1]; + tstep = (tstop - tstart) / (double)length; + } + + /* if this plot contains special vectors lin-tstart, lin-tstop or lin-tstep, use these instead */ + lin = vec_fromplot("lin-tstart", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstart is set to: %8e\n", lin->v_realdata[0]); + tstart = lin->v_realdata[0]; + } + + lin = vec_fromplot("lin-tstop", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstop is set to: %8e\n", lin->v_realdata[0]); + tstop = lin->v_realdata[0]; + } + + lin = vec_fromplot("lin-tstep", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstep is set to: %8e\n", lin->v_realdata[0]); + tstep = lin->v_realdata[0]; + } + + /* finally check if tstart, tstop and tstep are reasonable */ + if (((tstop - tstart) * tstep <= 0.0) || ((tstop - tstart) < tstep)) { + fprintf(cp_err, + "Error: bad parameters -- start = %G, stop = %G, step = %G\n", + tstart, tstop, tstep); + return; + } + old = plot_cur; + oldtime = old->pl_scale; + new = plot_alloc("transient"); + new->pl_name = tprintf("%s (linearized)", old->pl_name); + new->pl_title = copy(old->pl_title); + new->pl_date = copy(old->pl_date); + new->pl_next = plot_list; + plot_new(new); + plot_setcur(new->pl_typename); + plot_list = new; + + /* default number of points */ + expo = (int)(log2((tstop - tstart) / tstep)); + len = 1 << expo; + + wlnew = wl; + /* get the new length from 'np=xx' */ + while (wlnew) { + char* para = wlnew->wl_word; + if (ciprefix("np=", para)) { + para += 3; + len = atoi(para); + break; + } + wlnew = wlnew->wl_next; + } + + tstep = (tstop - tstart) / (double)len; + + newtime = dvec_alloc(copy(oldtime->v_name), + oldtime->v_type, + oldtime->v_flags | VF_PERMANENT, + len, NULL); + + newtime->v_plot = new; + for (i = 0, d = tstart; i < len; i++, d += tstep) + newtime->v_realdata[i] = d; + new->pl_scale = new->pl_dvecs = newtime; + + if (wl) { + while (wl) { + if (ciprefix("np=", wl->wl_word)) { + wl = wl->wl_next; + continue; + } + v = vec_fromplot(wl->wl_word, old); + if (!v) { + fprintf(cp_err, "Error: no such vector %s\n", + wl->wl_word); + wl = wl->wl_next; + continue; + } + lincopy(v, newtime->v_realdata, len, oldtime); + wl = wl->wl_next; + } + } + else { + for (v = old->pl_dvecs; v; v = v->v_next) { + if (v == old->pl_scale) + continue; + lincopy(v, newtime->v_realdata, len, oldtime); + } + } +} + + /* Cut out part of tran vectors from cut-tstart to cut-tstop and copy these into a new plot. A new scale vector 'time' will be there as well. Vectors diff --git a/src/frontend/linear.h b/src/frontend/linear.h index d1f731721..6fe15af2e 100644 --- a/src/frontend/linear.h +++ b/src/frontend/linear.h @@ -7,6 +7,7 @@ #define ngspice_LINEAR_H void com_linearize(wordlist *wl); +void com_linearnp(wordlist *wl); void com_cutout(wordlist *wl); From ee347262b56367e3be583a94938a75953babb525 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 28 Feb 2026 16:17:43 +0100 Subject: [PATCH 48/70] Do not give up when non-existing fft window is chosen, but issue a message and go on. --- src/frontend/com_fft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index b06d1e37d..1716c6450 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -84,7 +84,7 @@ com_fft(wordlist *wl) order = 2; if (fft_windows(window, win, time, length, maxt, span, order) == 0) - goto done; + fprintf(cp_err, "Warning: unknown window type %s for fft, set to \"none\" \n", window); names = ft_getpnames_quotes(wl, TRUE); vlist = NULL; From bfe496937e70b09ad1fadb9106b43dccc7936481 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 28 Feb 2026 16:19:05 +0100 Subject: [PATCH 49/70] Add patch for span to add a time slot. --- src/frontend/com_fft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 1716c6450..35b0d7b86 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -59,7 +59,7 @@ com_fft(wordlist *wl) length = (plot_cur->pl_scale)->v_length; time = (plot_cur->pl_scale)->v_realdata; - span = time[length-1] - time[0]; + span = time[length-1] - time[0] + time[length-1] - time[length-2]; #ifdef HAVE_LIBFFTW3 fpts = length/2 + 1; From 8639a053a3ec0f4b4efa8263040df7d36e725c72 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 2 Mar 2026 15:56:11 +0100 Subject: [PATCH 50/70] Enable linearizing all vectors, when none is given, with or without np statement. --- src/frontend/linear.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 7148b8f08..7cb029253 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -148,6 +148,7 @@ com_linearnp(wordlist* wl) struct dvec* lin; int expo, len, i; wordlist* wlnew; + bool nponly = FALSE; if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { fprintf(cp_err, "Error: plot must be a transient analysis\n"); @@ -246,7 +247,14 @@ com_linearnp(wordlist* wl) newtime->v_realdata[i] = d; new->pl_scale = new->pl_dvecs = newtime; - if (wl) { + /* check if "np=" is the only entry in wl. + If yes, linearize all vectors */ + if (wl && ciprefix("np=", wl->wl_word) && wl->wl_next == NULL) { + nponly = TRUE; + } + + if (wl && !nponly) { + /* check for vectors given in the command line */ while (wl) { if (ciprefix("np=", wl->wl_word)) { wl = wl->wl_next; @@ -264,6 +272,7 @@ com_linearnp(wordlist* wl) } } else { + /* linearize all vectors of the current plot */ for (v = old->pl_dvecs; v; v = v->v_next) { if (v == old->pl_scale) continue; From 6ae057b3ea2aaf228ed21917e13e620ca851f7d6 Mon Sep 17 00:00:00 2001 From: dwarning Date: Tue, 3 Mar 2026 08:54:03 +0100 Subject: [PATCH 51/70] revert commit 5e82b63 --- src/frontend/com_fft.c | 2 +- src/maths/cmaths/cmath4.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 35b0d7b86..864bf8bb9 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -167,7 +167,7 @@ com_fft(wordlist *wl) in[j] = tdvec[0][j]*win[j]; /* data have same type and length - so we need only one plan */ - plan_forward = fftw_plan_dft_r2c_1d(length-1, in, out, FFTW_ESTIMATE); + plan_forward = fftw_plan_dft_r2c_1d(length, in, out, FFTW_ESTIMATE); for (i = 0; i Date: Tue, 3 Mar 2026 09:55:57 +0100 Subject: [PATCH 52/70] Special Nyquist scaling only for even length --- src/frontend/com_fft.c | 13 ++++++++----- src/maths/cmaths/cmath4.c | 7 ++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 864bf8bb9..b141c8c29 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -185,9 +185,10 @@ com_fft(wordlist *wl) fdvec[i][j].cx_real = out[j][0]/scale; fdvec[i][j].cx_imag = out[j][1]/scale; } - fdvec[i][fpts-1].cx_real = out[fpts-1][0]/scale/2.0; - fdvec[i][fpts-1].cx_imag = 0.0; - + if (length % 2 == 0) { + fdvec[i][fpts-1].cx_real = out[fpts-1][0]/scale/2.0; + fdvec[i][fpts-1].cx_imag = 0.0; + } } fftw_destroy_plan(plan_forward); @@ -425,8 +426,10 @@ com_psd(wordlist *wl) if (!finite(noipower)) break; } - fdvec[i][fpts-1].cx_real = out[fpts-1][0]*out[fpts-1][0]/intres; - fdvec[i][fpts-1].cx_imag = 0; + if (length % 2 == 0) { + fdvec[i][fpts-1].cx_real = out[fpts-1][0]*out[fpts-1][0]/intres; + fdvec[i][fpts-1].cx_imag = 0; + } noipower += fdvec[i][fpts-1].cx_real; #else /* Green's FFT */ diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index b40c29cac..427a21dc3 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -781,9 +781,10 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp outdata[i].cx_real = out[i][0]/scale; outdata[i].cx_imag = out[i][1]/scale; } - outdata[fpts-1].cx_real = out[fpts-1][0]/scale/2.0; - outdata[fpts-1].cx_imag = 0.0; - + if (length % 2 == 0) { + outdata[fpts-1].cx_real = out[fpts-1][0]/scale/2.0; + outdata[fpts-1].cx_imag = 0.0; + } fftw_free(ind); #else /* Green's FFT */ From 5855c442e1beb402a716a9992398db5c34372dac Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 3 Mar 2026 16:16:58 +0100 Subject: [PATCH 53/70] FM and FC exchange place in the parameter sequence of SFFM voltage and current source, to become compatible with standard SPICE. Fixes bug 832. --- src/spicelib/devices/isrc/isrcload.c | 6 +++--- src/spicelib/devices/vsrc/vsrcload.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/spicelib/devices/isrc/isrcload.c b/src/spicelib/devices/isrc/isrcload.c index d88cc4072..67e96e3bf 100644 --- a/src/spicelib/devices/isrc/isrcload.c +++ b/src/spicelib/devices/isrc/isrcload.c @@ -205,18 +205,18 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) case SFFM: { - double VO, VA, FM, MDI, FC, TD, PHASEM, PHASEC; + double VO, VA, FC, MDI, FM, TD, PHASEM, PHASEC; double phasec; double phasem; static bool warn1 = FALSE, warn2 = FALSE; VO = here->ISRCcoeffs[0]; VA = here->ISRCcoeffs[1]; - FM = here->ISRCfunctionOrder > 2 + FC = here->ISRCfunctionOrder > 2 ? here->ISRCcoeffs[2] : (5./ckt->CKTfinalTime); MDI = here->ISRCfunctionOrder > 3 ? here->ISRCcoeffs[3] : 90.0; - FC = here->ISRCfunctionOrder > 4 + FM = here->ISRCfunctionOrder > 4 && here->ISRCcoeffs[4] ? here->ISRCcoeffs[4] : (500./ckt->CKTfinalTime); TD = here->ISRCfunctionOrder > 5 diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index c4190d25d..3e2f86b8e 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -233,18 +233,18 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) case SFFM: { - double VO, VA, FM, MDI, FC, TD, PHASEM, PHASEC; + double VO, VA, FC, MDI, FM, TD, PHASEM, PHASEC; double phasec; double phasem; static bool warn1 = FALSE, warn2 = FALSE; VO = here->VSRCcoeffs[0]; VA = here->VSRCcoeffs[1]; - FM = here->VSRCfunctionOrder > 2 + FC = here->VSRCfunctionOrder > 2 ? here->VSRCcoeffs[2] : (5./ckt->CKTfinalTime); MDI = here->VSRCfunctionOrder > 3 ? here->VSRCcoeffs[3] : 90.0; /* 0.9 * FC / FM */ - FC = here->VSRCfunctionOrder > 4 + FM = here->VSRCfunctionOrder > 4 && here->VSRCcoeffs[4] /* test if not 0 */ ? here->VSRCcoeffs[4] : (500./ckt->CKTfinalTime); TD = here->VSRCfunctionOrder > 5 From ad44b4ff4c9321a1b6985d1fcf1d3dffb707b74e Mon Sep 17 00:00:00 2001 From: dwarning Date: Tue, 3 Mar 2026 18:32:32 +0100 Subject: [PATCH 54/70] let nyquist bin not empty for odd vector --- src/frontend/com_fft.c | 4 ++-- src/maths/cmaths/cmath4.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index b141c8c29..ca7816008 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -181,7 +181,7 @@ com_fft(wordlist *wl) scale = (double) fpts - 1.0; fdvec[i][0].cx_real = out[0][0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; - for (j = 1; j < fpts-1; j++) { + for (j = 1; j < fpts; j++) { fdvec[i][j].cx_real = out[j][0]/scale; fdvec[i][j].cx_imag = out[j][1]/scale; } @@ -419,7 +419,7 @@ com_psd(wordlist *wl) fdvec[i][0].cx_real = out[0][0]*out[0][0]/intres; fdvec[i][0].cx_imag = 0; noipower = fdvec[i][0].cx_real; - for (j = 1; j < fpts-1; j++) { + for (j = 1; j < fpts; j++) { fdvec[i][j].cx_real = 2.* (out[j][0]*out[j][0] + out[j][1]*out[j][1])/intres; fdvec[i][j].cx_imag = 0; noipower += fdvec[i][j].cx_real; diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index 427a21dc3..9252e2260 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -777,7 +777,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp scale = (double) fpts - 1.0; outdata[0].cx_real = out[0][0]/scale/2.0; outdata[0].cx_imag = 0.0; - for (i = 1; i < fpts-1; i++) { + for (i = 1; i < fpts; i++) { outdata[i].cx_real = out[i][0]/scale; outdata[i].cx_imag = out[i][1]/scale; } From b522bcc6c0125e1f6211c81e4c8480391ef664ae Mon Sep 17 00:00:00 2001 From: dwarning Date: Tue, 3 Mar 2026 20:39:08 +0100 Subject: [PATCH 55/70] correct Nyquist bin scaling for Green branch --- src/frontend/com_fft.c | 2 +- src/maths/cmaths/cmath4.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index ca7816008..d3b68056a 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -223,7 +223,7 @@ com_fft(wordlist *wl) fdvec[i][j].cx_real = in[2*j]/scale; fdvec[i][j].cx_imag = in[2*j+1]/scale; } - fdvec[i][fpts-1].cx_real = in[1]/scale; + fdvec[i][fpts-1].cx_real = in[1]/scale/2.0; fdvec[i][fpts-1].cx_imag = 0.0; tfree(in); diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index 9252e2260..af661e968 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -811,7 +811,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp outdata[i].cx_real = datax[2*i]/scale; outdata[i].cx_imag = datax[2*i+1]/scale; } - outdata[fpts-1].cx_real = datax[1]/scale; + outdata[fpts-1].cx_real = datax[1]/scale/2.0; outdata[fpts-1].cx_imag = 0.0; #endif From a31ac54200e3872f28500f9762c49f1369898029 Mon Sep 17 00:00:00 2001 From: dwarning Date: Thu, 5 Mar 2026 09:30:55 +0100 Subject: [PATCH 56/70] make fft scaling independent from rounding behaviour for odd data --- src/frontend/com_fft.c | 4 ++-- src/maths/cmaths/cmath4.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index d3b68056a..8b74414fc 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -63,6 +63,7 @@ com_fft(wordlist *wl) #ifdef HAVE_LIBFFTW3 fpts = length/2 + 1; + scale = ((double)length)/2.0; #else /* size of fft input vector is power of two and larger or equal than spice vector */ N = 1; @@ -72,6 +73,7 @@ com_fft(wordlist *wl) M++; } fpts = N/2 + 1; + scale = ((double)N)/2; #endif win = TMALLOC(double, length); @@ -178,7 +180,6 @@ com_fft(wordlist *wl) fftw_execute(plan_forward); - scale = (double) fpts - 1.0; fdvec[i][0].cx_real = out[0][0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; for (j = 1; j < fpts; j++) { @@ -215,7 +216,6 @@ com_fft(wordlist *wl) rffts(in, M, 1); fftFree(); - scale = (double) fpts - 1.0; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ fdvec[i][0].cx_real = in[0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index af661e968..04b0b5142 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -774,7 +774,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp fftw_execute(plan_forward); - scale = (double) fpts - 1.0; + scale = ((double)length)/2.0; outdata[0].cx_real = out[0][0]/scale/2.0; outdata[0].cx_imag = 0.0; for (i = 1; i < fpts; i++) { @@ -803,7 +803,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp rffts(datax, M, 1); fftFree(); - scale = (double) fpts - 1.0; + scale = ((double)N)/2; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ outdata[0].cx_real = datax[0]/scale/2.0; outdata[0].cx_imag = 0.0; From 32e0d5f12f72cb35ffcca719029513917c58f3f9 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 6 Mar 2026 16:31:29 +0100 Subject: [PATCH 57/70] Revert "make fft scaling independent from rounding behaviour for odd data" This reverts commit a31ac54200e3872f28500f9762c49f1369898029. --- src/frontend/com_fft.c | 4 ++-- src/maths/cmaths/cmath4.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 8b74414fc..d3b68056a 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -63,7 +63,6 @@ com_fft(wordlist *wl) #ifdef HAVE_LIBFFTW3 fpts = length/2 + 1; - scale = ((double)length)/2.0; #else /* size of fft input vector is power of two and larger or equal than spice vector */ N = 1; @@ -73,7 +72,6 @@ com_fft(wordlist *wl) M++; } fpts = N/2 + 1; - scale = ((double)N)/2; #endif win = TMALLOC(double, length); @@ -180,6 +178,7 @@ com_fft(wordlist *wl) fftw_execute(plan_forward); + scale = (double) fpts - 1.0; fdvec[i][0].cx_real = out[0][0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; for (j = 1; j < fpts; j++) { @@ -216,6 +215,7 @@ com_fft(wordlist *wl) rffts(in, M, 1); fftFree(); + scale = (double) fpts - 1.0; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ fdvec[i][0].cx_real = in[0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index 04b0b5142..af661e968 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -774,7 +774,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp fftw_execute(plan_forward); - scale = ((double)length)/2.0; + scale = (double) fpts - 1.0; outdata[0].cx_real = out[0][0]/scale/2.0; outdata[0].cx_imag = 0.0; for (i = 1; i < fpts; i++) { @@ -803,7 +803,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp rffts(datax, M, 1); fftFree(); - scale = ((double)N)/2; + scale = (double) fpts - 1.0; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ outdata[0].cx_real = datax[0]/scale/2.0; outdata[0].cx_imag = 0.0; From 1c0bc1d5c965e5b1fba434a35b974fe91e0a84cd Mon Sep 17 00:00:00 2001 From: geraldfournier Date: Fri, 6 Mar 2026 16:34:20 +0100 Subject: [PATCH 58/70] make fft scaling independent from rounding behaviour for odd data --- src/frontend/com_fft.c | 4 ++-- src/maths/cmaths/cmath4.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index d3b68056a..8b74414fc 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -63,6 +63,7 @@ com_fft(wordlist *wl) #ifdef HAVE_LIBFFTW3 fpts = length/2 + 1; + scale = ((double)length)/2.0; #else /* size of fft input vector is power of two and larger or equal than spice vector */ N = 1; @@ -72,6 +73,7 @@ com_fft(wordlist *wl) M++; } fpts = N/2 + 1; + scale = ((double)N)/2; #endif win = TMALLOC(double, length); @@ -178,7 +180,6 @@ com_fft(wordlist *wl) fftw_execute(plan_forward); - scale = (double) fpts - 1.0; fdvec[i][0].cx_real = out[0][0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; for (j = 1; j < fpts; j++) { @@ -215,7 +216,6 @@ com_fft(wordlist *wl) rffts(in, M, 1); fftFree(); - scale = (double) fpts - 1.0; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ fdvec[i][0].cx_real = in[0]/scale/2.0; fdvec[i][0].cx_imag = 0.0; diff --git a/src/maths/cmaths/cmath4.c b/src/maths/cmaths/cmath4.c index af661e968..04b0b5142 100644 --- a/src/maths/cmaths/cmath4.c +++ b/src/maths/cmaths/cmath4.c @@ -774,7 +774,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp fftw_execute(plan_forward); - scale = (double) fpts - 1.0; + scale = ((double)length)/2.0; outdata[0].cx_real = out[0][0]/scale/2.0; outdata[0].cx_imag = 0.0; for (i = 1; i < fpts; i++) { @@ -803,7 +803,7 @@ cx_fft(void *data, short int type, int length, int *newlength, short int *newtyp rffts(datax, M, 1); fftFree(); - scale = (double) fpts - 1.0; + scale = ((double)N)/2; /* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */ outdata[0].cx_real = datax[0]/scale/2.0; outdata[0].cx_imag = 0.0; From 2301d2e6aa14571f2820131161c2a9de5ed0603c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 10 Mar 2026 23:21:59 +0100 Subject: [PATCH 59/70] Update to warning messages if osdi or code model files cannot be loaded --- src/frontend/com_dl.c | 2 ++ src/frontend/options.c | 1 + src/include/ngspice/fteext.h | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/frontend/com_dl.c b/src/frontend/com_dl.c index c2d3f4e49..6cc5202e9 100644 --- a/src/frontend/com_dl.c +++ b/src/frontend/com_dl.c @@ -15,6 +15,7 @@ if (wl && wl->wl_word) if (load_opus(wl->wl_word)) { fprintf(stderr, "Error: Library %s couldn't be loaded!\n", wl->wl_word); ft_spiniterror = TRUE; + ft_codemodelerror = TRUE; if (ft_stricterror) /* if set in spinit */ controlled_exit(EXIT_BAD); } @@ -34,6 +35,7 @@ void com_osdi(wordlist *wl) if (load_osdi(ww->wl_word)) { fprintf(cp_err, "Error: Library %s couldn't be loaded!\n", ww->wl_word); ft_spiniterror = TRUE; + ft_osdierror = TRUE; if (ft_stricterror) controlled_exit(EXIT_BAD); } diff --git a/src/frontend/options.c b/src/frontend/options.c index d6da5a46c..6e14d99b0 100644 --- a/src/frontend/options.c +++ b/src/frontend/options.c @@ -25,6 +25,7 @@ bool ft_acctprint = FALSE, ft_noacctprint = FALSE, ft_listprint = FALSE; bool ft_nodesprint = FALSE, ft_optsprint = FALSE, ft_noinitprint = FALSE; bool ft_norefprint = FALSE, ft_skywaterpdk = FALSE; bool ft_ngdebug = FALSE, ft_nginfo = FALSE, ft_stricterror = FALSE, ft_spiniterror = FALSE; +bool ft_codemodelerror = FALSE, ft_osdierror = FALSE; static void setdb(char *str); static struct variable *cp_enqvec_as_var(const char *vec_name, diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index af74009bd..7aa0202b6 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -270,6 +270,8 @@ extern bool ft_ngdebug; extern bool ft_nginfo; extern bool ft_stricterror; extern bool ft_spiniterror; +extern bool ft_codemodelerror; +extern bool ft_osdierror; extern bool ft_skywaterpdk; /* parse.c */ From 7d808f991e9d99f15e6b2a233a633075be8354c6 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 10 Mar 2026 23:24:15 +0100 Subject: [PATCH 60/70] Update 2 to warning messages if osdi or code model files cannot be loaded --- src/frontend/inpcom.c | 10 ++++++++++ src/spicelib/devices/dev.c | 12 +++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 8dd284b3e..a3447aee2 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1105,6 +1105,16 @@ struct card *inp_readall(FILE *fp, const char *dir_name, const char* file_name, if (newcompat.ps && newcompat.a) pspice_compat_a(working); + /* another warning that codemodels or osdi libs have not been loaded successfully */ + if (ft_osdierror) { + fprintf(stderr, "Warning: OSDI libs have not been loaded successfully.\n"); + fprintf(stderr, " Any of the following steps may fail, if Verilog A models are involved!.\n\n"); + } + if (ft_codemodelerror) { + fprintf(stderr, "Warning: code models like analog.cm have not been loaded successfully.\n"); + fprintf(stderr, " Any of the following steps may fail, if code models are involved!.\n\n"); + } + struct nscope *root = inp_add_levels(working); inp_probe(working); diff --git a/src/spicelib/devices/dev.c b/src/spicelib/devices/dev.c index 3e07b1beb..b3514919e 100644 --- a/src/spicelib/devices/dev.c +++ b/src/spicelib/devices/dev.c @@ -58,6 +58,7 @@ static void free_dlerr_msg(char *msg); #define RTLD_NOW 2 /* immediate function call binding */ #define RTLD_GLOBAL 4 /* symbols in this dlopen'ed obj are visible to other * dlopen'ed objs */ +#define F_OK 0 #endif /* ifndef HAS_WINGUI */ #include "ngspice/dllitf.h" /* the coreInfo Structure*/ @@ -405,9 +406,14 @@ int load_opus(const char *name) lib = dlopen(name, RTLD_NOW); // fprintf(stdout, "Lib %s has handle %p\n", name, lib); if (!lib) { - msg = dlerror(); - fprintf(stderr, "Error opening code model \"%s\"\n: %s\n", name, msg); - FREE_DLERR_MSG(msg); + int acc = access(name, F_OK); + if (acc != 0) { + fprintf(stderr, "Error opening code model \"%s\": No such file or directory!\n", + name); + } + else + fprintf(stderr, "Error opening code model \"%s\"\n", name); + return 1; } From f7ef88b637977782853ce5f5f5e4d12c0a3e7b45 Mon Sep 17 00:00:00 2001 From: dwarning Date: Sat, 14 Mar 2026 12:03:49 +0100 Subject: [PATCH 61/70] Diode soft recovery model based on the idea of A. Buermen with iterated charge node for diffusion charge and model parameter Vp. Small simplifications in AC model and capacitance reporting are made. No self-heating for this effect. Pole-Zero and distortion analysis is not implemented for this model extension. --- src/spicelib/devices/dio/dio.c | 3 +- src/spicelib/devices/dio/dioacld.c | 14 +++ src/spicelib/devices/dio/dioask.c | 4 + src/spicelib/devices/dio/diobindCSC.c | 24 +++++ src/spicelib/devices/dio/diodefs.h | 48 ++++++--- src/spicelib/devices/dio/dioload.c | 137 +++++++++++++++++++++++--- src/spicelib/devices/dio/diomask.c | 5 +- src/spicelib/devices/dio/diompar.c | 6 +- src/spicelib/devices/dio/diosetup.c | 34 ++++++- src/spicelib/devices/dio/diotrunc.c | 2 + 10 files changed, 244 insertions(+), 33 deletions(-) diff --git a/src/spicelib/devices/dio/dio.c b/src/spicelib/devices/dio/dio.c index 2ebeb2e41..c7b160f79 100644 --- a/src/spicelib/devices/dio/dio.c +++ b/src/spicelib/devices/dio/dio.c @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ #include "ngspice/ngspice.h" @@ -122,6 +122,7 @@ IFparm DIOmPTable[] = { /* model parameters */ OPU( "cond", DIO_MOD_COND,IF_REAL, "Ohmic conductance"), IOP( "isr", DIO_MOD_ISR, IF_REAL, "Recombination saturation current"), IOP( "nr", DIO_MOD_NR, IF_REAL, "Recombination current emission coefficient"), + IOP( "vp", DIO_MOD_VP, IF_REAL, "Soft reverse recovery parameter"), /* SOA parameters */ IOPX( "fv_max", DIO_MOD_FV_MAX, IF_REAL, "maximum voltage in forward direction"), diff --git a/src/spicelib/devices/dio/dioacld.c b/src/spicelib/devices/dio/dioacld.c index 62ad65efd..62116d8f5 100644 --- a/src/spicelib/devices/dio/dioacld.c +++ b/src/spicelib/devices/dio/dioacld.c @@ -91,6 +91,20 @@ DIOacLoad(GENmodel *inModel, CKTcircuit *ckt) (*(here->DIOnegTempPtr) += -dIdioSw_dT); } } + if ((here->DIOqpNode > 0) && (model->DIOsoftRevRecParam!=0) && (here->DIOtTransitTime!=0)) { + /* QP subcircuit */ + double gdres= *(ckt->CKTstate0 + here->DIOresConduct); + double fac = here->DIOtTransitTime / model->DIOsoftRevRecParam; + double dcrrdvd = fac * gdres; + *(here->DIOqpQpPtr) += 1/model->DIOsoftRevRecParam; + *(here->DIOqpQpPtr + 1) += here->DIOtTransitTime * ckt->CKTomega; + *(here->DIOqpPosPrimePtr) += -dcrrdvd; + *(here->DIOqpNegPtr) += dcrrdvd; + /* Gain of VCVS (1-vp)/tau * j*omega*tau = (1-vp) * j*omega */ + double xgain = (1 - model->DIOsoftRevRecParam) * ckt->CKTomega; + *(here->DIOposPrimeQpPtr + 1) += xgain; + *(here->DIOnegQpPtr + 1) += -xgain; + } } } return(OK); diff --git a/src/spicelib/devices/dio/dioask.c b/src/spicelib/devices/dio/dioask.c index 67009b889..e40d5c4d0 100644 --- a/src/spicelib/devices/dio/dioask.c +++ b/src/spicelib/devices/dio/dioask.c @@ -75,9 +75,13 @@ DIOask (CKTcircuit *ckt, GENinstance *inst, int which, IFvalue *value, return(OK); case DIO_CURRENT: value->rValue = *(ckt->CKTstate0+here->DIOcurrent); + if ((here->DIOqpNode > 0) && (here->DIOtTransitTime!=0)) + value->rValue += here->DIOqpGain * *(ckt->CKTstate0 + here->DIOcqcsr); return(OK); case DIO_CAP: value->rValue = here->DIOcap; + if ((here->DIOqpNode > 0) && (here->DIOtTransitTime!=0)) + value->rValue += here->DIOtTransitTime * *(ckt->CKTstate0+here->DIOconduct); return(OK); case DIO_CHARGE: value->rValue = *(ckt->CKTstate0+here->DIOcapCharge); diff --git a/src/spicelib/devices/dio/diobindCSC.c b/src/spicelib/devices/dio/diobindCSC.c index 29cee9cff..89994448e 100644 --- a/src/spicelib/devices/dio/diobindCSC.c +++ b/src/spicelib/devices/dio/diobindCSC.c @@ -54,6 +54,14 @@ DIObindCSC (GENmodel *inModel, CKTcircuit *ckt) CREATE_KLU_BINDING_TABLE(DIOposSwPrimeTempPtr, DIOposSwPrimeTempBinding, DIOposSwPrimeNode, DIOtempNode); } } + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + CREATE_KLU_BINDING_TABLE(DIOqpQpPtr , DIOqpQpBinding , DIOqpNode, DIOqpNode); + CREATE_KLU_BINDING_TABLE(DIOqpPosPrimePtr, DIOqpPosPrimeBinding, DIOqpNode, DIOposPrimeNode); + CREATE_KLU_BINDING_TABLE(DIOqpNegPtr , DIOqpNegBinding , DIOqpNode, DIOnegNode); + CREATE_KLU_BINDING_TABLE(DIOposPrimeQpPtr, DIOposPrimeQpBinding, DIOposPrimeNode, DIOqpNode); + CREATE_KLU_BINDING_TABLE(DIOnegQpPtr , DIOnegQpBinding , DIOnegNode, DIOqpNode); + } } } @@ -103,6 +111,14 @@ DIObindCSCComplex (GENmodel *inModel, CKTcircuit *ckt) CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOposSwPrimeTempPtr, DIOposSwPrimeTempBinding, DIOposSwPrimeNode, DIOtempNode); } } + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOqpQpPtr , DIOqpQpBinding , DIOqpNode, DIOqpNode); + CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOqpPosPrimePtr, DIOqpPosPrimeBinding, DIOqpNode, DIOposPrimeNode); + CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOqpNegPtr , DIOqpNegBinding , DIOqpNode, DIOnegNode); + CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOposPrimeQpPtr, DIOposPrimeQpBinding, DIOposPrimeNode, DIOqpNode); + CONVERT_KLU_BINDING_TABLE_TO_COMPLEX(DIOnegQpPtr , DIOnegQpBinding , DIOnegNode, DIOqpNode); + } } } @@ -152,6 +168,14 @@ DIObindCSCComplexToReal (GENmodel *inModel, CKTcircuit *ckt) CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOposSwPrimeTempPtr, DIOposSwPrimeTempBinding, DIOposSwPrimeNode, DIOtempNode); } } + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOqpQpPtr , DIOqpQpBinding , DIOqpNode, DIOqpNode); + CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOqpPosPrimePtr, DIOqpPosPrimeBinding, DIOqpNode, DIOposPrimeNode); + CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOqpNegPtr , DIOqpNegBinding , DIOqpNode, DIOnegNode); + CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOposPrimeQpPtr, DIOposPrimeQpBinding, DIOposPrimeNode, DIOqpNode); + CONVERT_KLU_BINDING_TABLE_TO_REAL(DIOnegQpPtr , DIOnegQpBinding , DIOnegNode, DIOqpNode); + } } } diff --git a/src/spicelib/devices/dio/diodefs.h b/src/spicelib/devices/dio/diodefs.h index 298f330b9..0a1f056a8 100644 --- a/src/spicelib/devices/dio/diodefs.h +++ b/src/spicelib/devices/dio/diodefs.h @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ #ifndef DIO #define DIO @@ -44,6 +44,7 @@ typedef struct sDIOinstance { const int DIOtempNode; /* number of the temperature node of the diode */ int DIOposPrimeNode; /* number of positive prime node of diode */ int DIOposSwPrimeNode; /* number of positive prime node of diode sidewall */ + int DIOqpNode; /* number of soft recovery charge node */ double *DIOposPosPrimePtr; /* pointer to sparse matrix at * (positive,positive prime) */ @@ -83,9 +84,19 @@ typedef struct sDIOinstance { double *DIOtempPosSwPrimePtr; double *DIOposSwPrimeTempPtr; + /* rev-rec */ + double *DIOqpQpPtr; + double *DIOqpPosPrimePtr; + double *DIOqpNegPtr; + double *DIOposPrimeQpPtr; + double *DIOnegQpPtr; + double DIOcap; /* stores the diode capacitance */ double DIOcapSW; /* stores the diode Sw capacitance */ + /* rev-rec */ + double DIOqpGain;/* converts iterated diffcharge current */ + double *DIOsens; /* stores the perturbed values of geq and ceq in ac sensitivity analyis */ @@ -229,13 +240,19 @@ typedef struct sDIOinstance { /* self heating */ BindElement *DIOtempPosBinding; BindElement *DIOtempPosPrimeBinding; - BindElement *DIOtempNegBinding; + BindElement *DIOtempNegBinding; BindElement *DIOtempTempBinding; BindElement *DIOposTempBinding; BindElement *DIOposPrimeTempBinding; BindElement *DIOnegTempBinding; BindElement *DIOtempPosSwPrimeBinding; BindElement *DIOposSwPrimeTempBinding; + /* rev-rec */ + BindElement *DIOqpQpBinding; + BindElement *DIOqpPosPrimeBinding; + BindElement *DIOqpNegBinding; + BindElement *DIOposPrimeQpBinding; + BindElement *DIOnegQpBinding; #endif } DIOinstance ; @@ -244,7 +261,6 @@ typedef struct sDIOinstance { #define DIOsenCeq DIOsens + 3 /* stores the perturbed values of ceq */ #define DIOdphidp DIOsens + 6 - #define DIOvoltage DIOstate #define DIOcurrent DIOstate+1 #define DIOconduct DIOstate+2 @@ -256,18 +272,25 @@ typedef struct sDIOinstance { #define DIOcapChargeSW DIOstate+8 #define DIOcapCurrentSW DIOstate+9 -#define DIOqth DIOstate+10 /* thermal capacitor charge */ -#define DIOcqth DIOstate+11 /* thermal capacitor current */ - -#define DIOdeltemp DIOstate+12 /* thermal voltage over rth0 */ +#define DIOqth DIOstate+10 /* thermal capacitor charge */ +#define DIOcqth DIOstate+11 /* thermal capacitor current */ +#define DIOdeltemp DIOstate+12 /* thermal voltage over rth0 */ #define DIOdIdio_dT DIOstate+13 #define DIOdIdioSW_dT DIOstate+14 +/* rev-rec */ +#define DIOsrcapCharge DIOstate+15 +#define DIOsrcapCurrent DIOstate+16 +#define DIOqp DIOstate+17 +#define DIOresCurrent DIOstate+18 +#define DIOresConduct DIOstate+19 +#define DIOcqcsr DIOstate+20 +#define DIOgqcsr DIOstate+21 -#define DIOnumStates 15 +#define DIOnumStates 22 -#define DIOsensxp DIOstate+15 /* charge sensitivities and their derivatives. - * +16 for the derivatives - pointer to the - * beginning of the array */ +#define DIOsensxp DIOstate+22 /* charge sensitivities and their derivatives. + * +23 for the derivatives - pointer to the + * beginning of the array */ #define DIOnumSenStates 2 @@ -342,6 +365,7 @@ typedef struct sDIOmodel { /* model structure for a diode */ unsigned DIOte_maxGiven : 1; unsigned DIOrecSatCurGiven : 1; unsigned DIOrecEmissionCoeffGiven : 1; + unsigned DIOsoftRevRecParamGiven : 1; unsigned DIOrth0Given :1; unsigned DIOcth0Given :1; @@ -418,6 +442,7 @@ typedef struct sDIOmodel { /* model structure for a diode */ double DIOte_max; /* maximum temperature */ double DIOrecSatCur; /* Recombination saturation current */ double DIOrecEmissionCoeff; /* Recombination emission coefficient */ + double DIOsoftRevRecParam; /* Soft reverse recovery parameter */ double DIOrth0; double DIOcth0; @@ -526,6 +551,7 @@ enum { DIO_MOD_PD_MAX, DIO_MOD_ISR, DIO_MOD_NR, + DIO_MOD_VP, DIO_MOD_RTH0, DIO_MOD_CTH0, diff --git a/src/spicelib/devices/dio/dioload.c b/src/spicelib/devices/dio/dioload.c index 6f53966eb..80ade0970 100644 --- a/src/spicelib/devices/dio/dioload.c +++ b/src/spicelib/devices/dio/dioload.c @@ -2,7 +2,7 @@ Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles Modified: 2000 AlansFixes -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ #include "ngspice/ngspice.h" @@ -65,6 +65,10 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) double dIdio_dT, dIth_dVdio=0.0, dIrs_dT=0.0, dIth_dVrs=0.0, dIth_dT=0.0; double dIdioSw_dT=0.0, dIth_dVdioSw=0.0, dIth_dVrssw=0.0, dIrssw_dT=0.0; double argsw_dT, csat_dT, csatsw_dT; + /* rev-rec */ + double cdres, gdres; + double vqp; + double capsr, gqcsr, cqcsr; /* loop through all the diode models */ for( ; model != NULL; model = DIOnextModel(model)) { @@ -74,6 +78,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) here=DIOnextInstance(here)) { int selfheat = ((here->DIOtempNode > 0) && (here->DIOthermal) && (model->DIOrth0Given)); + int revrec = ((here->DIOqpNode > 0) && (model->DIOsoftRevRecParam!=0) && (here->DIOtTransitTime!=0)); /* * this routine loads diodes for dc and transient analyses. @@ -120,10 +125,12 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) vd = *(ckt->CKTstate1 + here->DIOvoltage); if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate1 + here->DIOvoltageSW); delTemp = *(ckt->CKTstate1 + here->DIOdeltemp); + vqp = *(ckt->CKTstate1 + here->DIOqp); } else{ vd = *(ckt->CKTstate0 + here->DIOvoltage); if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate0 + here->DIOvoltageSW); delTemp = *(ckt->CKTstate0 + here->DIOdeltemp); + vqp = *(ckt->CKTstate0 + here->DIOqp); } #ifdef SENSDEBUG @@ -137,24 +144,30 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) vd= *(ckt->CKTstate0 + here->DIOvoltage); if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate0 + here->DIOvoltageSW); delTemp = *(ckt->CKTstate0 + here->DIOdeltemp); + vqp= *(ckt->CKTstate0 + here->DIOqp); } else if (ckt->CKTmode & MODEINITTRAN) { vd= *(ckt->CKTstate1 + here->DIOvoltage); if (model->DIOresistSWGiven) vdsw = *(ckt->CKTstate1 + here->DIOvoltageSW); delTemp = *(ckt->CKTstate1 + here->DIOdeltemp); + vqp= *(ckt->CKTstate1 + here->DIOqp); } else if ( (ckt->CKTmode & MODEINITJCT) && (ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC) ) { vd=here->DIOinitCond; if (model->DIOresistSWGiven) vdsw = here->DIOinitCond; + vqp=0; } else if ( (ckt->CKTmode & MODEINITJCT) && here->DIOoff) { vd=vdsw=0; delTemp = 0.0; + vqp=0; } else if ( ckt->CKTmode & MODEINITJCT) { vd=here->DIOtVcrit; vdsw=here->DIOtVcritSW; delTemp = 0.0; + vqp=0; } else if ( ckt->CKTmode & MODEINITFIX && here->DIOoff) { vd=vdsw=0; delTemp = 0.0; + vqp=0; } else { #ifndef PREDICTOR if (ckt->CKTmode & MODEINITPRED) { @@ -177,12 +190,22 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) *(ckt->CKTstate0 + here->DIOdIdioSW_dT) = *(ckt->CKTstate1 + here->DIOdIdioSW_dT); } + vqp = DEVpred(ckt,here->DIOqp); + *(ckt->CKTstate0 + here->DIOresCurrent) = + *(ckt->CKTstate1 + here->DIOresCurrent); + *(ckt->CKTstate0 + here->DIOresConduct) = + *(ckt->CKTstate1 + here->DIOresConduct); + *(ckt->CKTstate0 + here->DIOcqcsr) = + *(ckt->CKTstate1 + here->DIOcqcsr); + *(ckt->CKTstate0 + here->DIOgqcsr) = + *(ckt->CKTstate1 + here->DIOgqcsr); } else { #endif /* PREDICTOR */ vd = *(ckt->CKTrhsOld+here->DIOposPrimeNode)- *(ckt->CKTrhsOld + here->DIOnegNode); if (model->DIOresistSWGiven) vdsw = *(ckt->CKTrhsOld+here->DIOposSwPrimeNode)- *(ckt->CKTrhsOld + here->DIOnegNode); + if (selfheat) delTemp = *(ckt->CKTrhsOld + here->DIOtempNode); else @@ -192,6 +215,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) *(ckt->CKTstate1+here->DIOqth) = *(ckt->CKTstate0+here->DIOqth); } + vqp = *(ckt->CKTrhsOld+here->DIOqpNode); #ifndef PREDICTOR } #endif /* PREDICTOR */ @@ -238,6 +262,11 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) gdsw= *(ckt->CKTstate0 + here->DIOconductSW); dIdioSw_dT= *(ckt->CKTstate0 + here->DIOdIdioSW_dT); } + vqp= *(ckt->CKTstate0 + here->DIOqp); + cdres= *(ckt->CKTstate0 + here->DIOresCurrent); + gdres= *(ckt->CKTstate0 + here->DIOresConduct); + cqcsr= *(ckt->CKTstate0 + here->DIOcqcsr); + gqcsr= *(ckt->CKTstate0 + here->DIOgqcsr); goto load; } } @@ -304,7 +333,7 @@ next1: if (model->DIOsatSWCurGiven) { /* sidewall current */ double vds; - if (model->DIOresistSWGiven) + if (model->DIOresistSWGiven) vds = vdsw; /* sidewall voltage used */ else vds = vd; /* common voltage used */ @@ -470,7 +499,6 @@ next1: gdb = ((1+sqrt_ikx)*gdb + cdb*gdb/(2*sqrt_ikx*ikr_area_m))/(1+2*sqrt_ikx - cdb/ikr_area_m); cdb = cdb/(1+sqrt_ikx); } - } if ( (model->DIOforwardSWKneeCurrentGiven) && (cdsw > 1.0e-18) ) { @@ -493,6 +521,11 @@ next1: dIdioSw_dT = cdsw_dT; } + cdres = cd; + gdres = gd; + cqcsr = 0; + gqcsr = 0; + if ((ckt->CKTmode & (MODEDCTRANCURVE | MODETRAN | MODEAC | MODEINITSMSIG)) || ((ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC))) { /* @@ -528,22 +561,46 @@ next1: deplcapSW = czof2SW*(here->DIOtF3SW+model->DIOgradingSWCoeff*vdx/here->DIOtJctSWPot); } - diffcharge = here->DIOtTransitTime*cd; - diffcap = here->DIOtTransitTime*gd; - if (!model->DIOresistSWGiven) { + if (revrec) { + /* + soft recovery with TT!=0 + add only depletion capacitance. + */ *(ckt->CKTstate0 + here->DIOcapCharge) = - diffcharge + deplcharge + deplchargeSW + (here->DIOcmetal + here->DIOcpoly)*vd; - capd = diffcap + deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; + deplcharge + deplchargeSW + (here->DIOcmetal + here->DIOcpoly)*vd; + + capd = deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; here->DIOcap = capd; + /* + DIOcap is now equal only to depletion capacitance + overlap capacitance. + Diffusion capacitance is modelled via Qp so there is no clear way to define it. + */ + + /* Now prepare the charge for the capacitor connected to the QP node */ + *(ckt->CKTstate0 + here->DIOsrcapCharge) = here->DIOtTransitTime * vqp; + capsr = here->DIOtTransitTime; } else { - *(ckt->CKTstate0 + here->DIOcapCharge) = - diffcharge + deplcharge + (here->DIOcmetal + here->DIOcpoly)*vd; - capd = diffcap + deplcap + here->DIOcmetal + here->DIOcpoly; - here->DIOcap = capd; - *(ckt->CKTstate0 + here->DIOcapChargeSW) = - deplcapSW; - capdsw = deplcapSW; - here->DIOcapSW = capdsw; + /* no soft recovery of soft recovery with TT=0 (i.e. no soft recovery due to TT=0) */ + diffcharge = here->DIOtTransitTime*cd; + diffcap = here->DIOtTransitTime*gd; + if (!model->DIOresistSWGiven) { + *(ckt->CKTstate0 + here->DIOcapCharge) = + diffcharge + deplcharge + deplchargeSW + (here->DIOcmetal + here->DIOcpoly)*vd; + capd = diffcap + deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; + here->DIOcap = capd; + } else { + *(ckt->CKTstate0 + here->DIOcapCharge) = + diffcharge + deplcharge + (here->DIOcmetal + here->DIOcpoly)*vd; + capd = diffcap + deplcap + here->DIOcmetal + here->DIOcpoly; + here->DIOcap = capd; + *(ckt->CKTstate0 + here->DIOcapChargeSW) = + deplcapSW; + capdsw = deplcapSW; + here->DIOcapSW = capdsw; + } + + *(ckt->CKTstate0 + here->DIOsrcapCharge) = 0; + capsr = 0; } /* * store small-signal parameters @@ -564,6 +621,10 @@ next1: *(ckt->CKTstate0 + here->DIOconductSW) = gdsw; *(ckt->CKTstate0 + here->DIOdIdioSW_dT) = dIdioSw_dT; } + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; + *(ckt->CKTstate0 + here->DIOresConduct) = gdres; + *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr; + *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr; #ifdef SENSDEBUG printf("storing small signal parameters\n"); printf("cd = %.7e,vd = %.7e\n",cd,vd); @@ -580,6 +641,7 @@ next1: *(ckt->CKTstate0 + here->DIOcurrent) = cd; if (model->DIOresistSWGiven) *(ckt->CKTstate0 + here->DIOcurrentSW) = cdsw; + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; #ifdef SENSDEBUG printf("storing parameters for transient sensitivity\n" ); @@ -613,6 +675,21 @@ next1: *(ckt->CKTstate1 + here->DIOcapCurrentSW) = *(ckt->CKTstate0 + here->DIOcapCurrentSW); } + if (revrec) { + /* soft recovery subcircuit */ + if (ckt->CKTmode & MODEINITTRAN) { + *(ckt->CKTstate1 + here->DIOsrcapCharge) = + *(ckt->CKTstate0 + here->DIOsrcapCharge); + } + error = NIintegrate(ckt,&geq,&ceq,capsr,here->DIOsrcapCharge); + if(error) return(error); + gqcsr = geq; + cqcsr = *(ckt->CKTstate0 + here->DIOsrcapCurrent); + if (ckt->CKTmode & MODEINITTRAN) { + *(ckt->CKTstate1 + here->DIOsrcapCurrent) = + *(ckt->CKTstate0 + here->DIOsrcapCurrent); + } + } if (selfheat) { error = NIintegrate(ckt, &gcTt, &ceqqth, model->DIOcth0, here->DIOqth); @@ -654,6 +731,11 @@ next2: *(ckt->CKTstate0 + here->DIOvoltage) = vd; *(ckt->CKTstate0 + here->DIOconductSW) = gdsw; *(ckt->CKTstate0 + here->DIOdIdioSW_dT) = dIdioSw_dT; } + *(ckt->CKTstate0 + here->DIOqp) = vqp; + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; + *(ckt->CKTstate0 + here->DIOresConduct) = gdres; + *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr; + *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr; if(SenCond) continue; #ifndef NOBYPASS @@ -754,6 +836,29 @@ next2: *(ckt->CKTstate0 + here->DIOvoltage) = vd; (*(here->DIOnegTempPtr) += -dIdioSw_dT); } } + + if (revrec) { + double fac, ceqrr, dcrrdvd, grr; + double ceqrrd, geqrrd; + /* QP subcircuit */ + fac = here->DIOtTransitTime / model->DIOsoftRevRecParam; + dcrrdvd = fac*gdres; + ceqrr = -fac*cdres + cqcsr + dcrrdvd*vd - gqcsr*vqp; + grr = 1/model->DIOsoftRevRecParam; + *(ckt->CKTrhs + here->DIOqpNode) -= ceqrr; + *(here->DIOqpQpPtr) += grr + gqcsr; + *(here->DIOqpPosPrimePtr) += -dcrrdvd; + *(here->DIOqpNegPtr) += dcrrdvd; + /* Contribution to diode current */ + here->DIOqpGain = (1 - model->DIOsoftRevRecParam) / here->DIOtTransitTime; + /* Linear contribution -(1-vp)/tau*ddt(Qp) */ + geqrrd = here->DIOqpGain*gqcsr; + ceqrrd = here->DIOqpGain*cqcsr - geqrrd*vqp; + *(ckt->CKTrhs + here->DIOposPrimeNode) -= ceqrrd; + *(ckt->CKTrhs + here->DIOnegNode) += ceqrrd; + *(here->DIOposPrimeQpPtr) += geqrrd; + *(here->DIOnegQpPtr) += -geqrrd; + } } } return(OK); diff --git a/src/spicelib/devices/dio/diomask.c b/src/spicelib/devices/dio/diomask.c index 1f4e11ce5..f786f96c9 100644 --- a/src/spicelib/devices/dio/diomask.c +++ b/src/spicelib/devices/dio/diomask.c @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ /* */ @@ -199,6 +199,9 @@ DIOmAsk (CKTcircuit *ckt, GENmodel *inModel, int which, IFvalue *value) case DIO_MOD_NR: value->rValue = model->DIOrecEmissionCoeff; return(OK); + case DIO_MOD_VP: + value->rValue = model->DIOsoftRevRecParam; + return(OK); case DIO_MOD_RTH0: value->rValue = model->DIOrth0; return(OK); diff --git a/src/spicelib/devices/dio/diompar.c b/src/spicelib/devices/dio/diompar.c index 498421160..a6780034b 100644 --- a/src/spicelib/devices/dio/diompar.c +++ b/src/spicelib/devices/dio/diompar.c @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ /* */ @@ -245,6 +245,10 @@ DIOmParam(int param, IFvalue *value, GENmodel *inModel) model->DIOrecEmissionCoeff = value->rValue; model->DIOrecEmissionCoeffGiven = TRUE; break; + case DIO_MOD_VP: + model->DIOsoftRevRecParam = value->rValue; + model->DIOsoftRevRecParamGiven = TRUE; + break; case DIO_MOD_RTH0: model->DIOrth0 = value->rValue; model->DIOrth0Given = TRUE; diff --git a/src/spicelib/devices/dio/diosetup.c b/src/spicelib/devices/dio/diosetup.c index df9555be8..867b7562c 100644 --- a/src/spicelib/devices/dio/diosetup.c +++ b/src/spicelib/devices/dio/diosetup.c @@ -2,7 +2,7 @@ Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles Modified: 2000 AlansFixes -Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 +Modified by Paolo Nenzi 2003, Dietmar Warning 2012 and Arpad Buermen 2025 **********/ /* load the diode structure with those pointers needed later @@ -222,6 +222,9 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) if(!model->DIOrecSatCurGiven) { model->DIOrecSatCur = 1e-14; } + if (!model->DIOsoftRevRecParamGiven) { + model->DIOsoftRevRecParam = 0.0; + } /* set lower limit of saturation current */ if (model->DIOsatCur < ckt->CKTepsmin) @@ -412,6 +415,18 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) } } + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + if(here->DIOqpNode == 0) { + error = CKTmkVolt(ckt, &tmp, here->DIOname, "qp"); + if(error) return(error); + here->DIOqpNode = tmp->number; + } + } else { + here->DIOqpNode = 0; + } + + int selfheat = ((here->DIOtempNode > 0) && (here->DIOthermal) && (model->DIOrth0Given)); /* macro to make elements with built in test for out of memory */ @@ -450,7 +465,15 @@ do { if((here->ptr = SMPmakeElt(matrix, here->first, here->second)) == NULL){\ TSTALLOC(DIOposSwPrimeTempPtr, DIOposSwPrimeNode, DIOtempNode); } } - + + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + TSTALLOC(DIOqpQpPtr , DIOqpNode, DIOqpNode); + TSTALLOC(DIOqpPosPrimePtr, DIOqpNode, DIOposPrimeNode); + TSTALLOC(DIOqpNegPtr , DIOqpNode, DIOnegNode); + TSTALLOC(DIOposPrimeQpPtr, DIOposPrimeNode, DIOqpNode); + TSTALLOC(DIOnegQpPtr, DIOnegNode, DIOqpNode); + } } } return(OK); @@ -474,7 +497,7 @@ DIOunsetup( if (here->DIOposPrimeNode > 0 && here->DIOposPrimeNode != here->DIOposNode) CKTdltNNum(ckt, here->DIOposPrimeNode); - here->DIOposPrimeNode = 0; + here->DIOposPrimeNode = 0; if(model->DIOresistSWGiven) { /* separate sidewall */ @@ -484,6 +507,11 @@ DIOunsetup( here->DIOposSwPrimeNode = 0; } + /* rev-rec */ + if (here->DIOqpNode > 0) + CKTdltNNum(ckt, here->DIOqpNode); + here->DIOqpNode = 0; + } } return OK; diff --git a/src/spicelib/devices/dio/diotrunc.c b/src/spicelib/devices/dio/diotrunc.c index b940528be..04070a1d8 100644 --- a/src/spicelib/devices/dio/diotrunc.c +++ b/src/spicelib/devices/dio/diotrunc.c @@ -22,6 +22,8 @@ DIOtrunc(GENmodel *inModel, CKTcircuit *ckt, double *timeStep) for(here=DIOinstances(model);here!=NULL;here = DIOnextInstance(here)){ CKTterr(here->DIOcapCharge,ckt,timeStep); if (model->DIOresistSWGiven) CKTterr(here->DIOcapChargeSW,ckt,timeStep); + if (model->DIOsoftRevRecParam!=0 && here->DIOtTransitTime!=0) + CKTterr(here->DIOsrcapCharge,ckt,timeStep); } } return(OK); From e95f19af460b65d4ec51f541c021781641105b66 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 16 Mar 2026 13:15:40 +0100 Subject: [PATCH 62/70] Revert "Enable linearizing all vectors, when none is given, with or without np statement." This reverts commit 8639a053a3ec0f4b4efa8263040df7d36e725c72. --- src/frontend/linear.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 7cb029253..7148b8f08 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -148,7 +148,6 @@ com_linearnp(wordlist* wl) struct dvec* lin; int expo, len, i; wordlist* wlnew; - bool nponly = FALSE; if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { fprintf(cp_err, "Error: plot must be a transient analysis\n"); @@ -247,14 +246,7 @@ com_linearnp(wordlist* wl) newtime->v_realdata[i] = d; new->pl_scale = new->pl_dvecs = newtime; - /* check if "np=" is the only entry in wl. - If yes, linearize all vectors */ - if (wl && ciprefix("np=", wl->wl_word) && wl->wl_next == NULL) { - nponly = TRUE; - } - - if (wl && !nponly) { - /* check for vectors given in the command line */ + if (wl) { while (wl) { if (ciprefix("np=", wl->wl_word)) { wl = wl->wl_next; @@ -272,7 +264,6 @@ com_linearnp(wordlist* wl) } } else { - /* linearize all vectors of the current plot */ for (v = old->pl_dvecs; v; v = v->v_next) { if (v == old->pl_scale) continue; From 08a8ea275cdb5ff4a9b681d76cc30bb7cd3e5fd7 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 16 Mar 2026 13:15:53 +0100 Subject: [PATCH 63/70] Revert "Add command 'linearnp np=1024 vec1 vec2'" This reverts commit c4fc026af993b8bdded064ee8bf8d9c3d05f267a. --- src/frontend/commands.c | 4 -- src/frontend/linear.c | 143 ---------------------------------------- src/frontend/linear.h | 1 - 3 files changed, 148 deletions(-) diff --git a/src/frontend/commands.c b/src/frontend/commands.c index e61aa069e..3de1ba6fb 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -642,10 +642,6 @@ struct comm spcp_coms[] = { { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, " [ vec ... ] : Convert plot into one with linear scale." } , - { "linearnp", com_linearnp, FALSE, FALSE, - { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, - NULL, - " [ vec ... ] : Convert plot into one with linear scale, number of points given." }, { "cutout", com_cutout, FALSE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 7148b8f08..3fc903824 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -130,149 +130,6 @@ com_linearize(wordlist *wl) } } -/* Interpolate all the vectors in a plot to a linear time scale, which - * we determine by looking at the transient parameters in the CKT struct. - * If no circuit is loaded, e.g. because the 'load' command has been used - * to obtain data, try to get parameters from scale vector. - * Interpolation may be restricted to only a region of the input vector, - * thus creating a cutout of the original vector. The number of data points - * is given by parameter np=xx. - */ -void -com_linearnp(wordlist* wl) -{ - double tstart, tstop, tstep, d; - struct plot* new, * old; - struct dvec* newtime, * v; - struct dvec* oldtime; - struct dvec* lin; - int expo, len, i; - wordlist* wlnew; - - if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { - fprintf(cp_err, "Error: plot must be a transient analysis\n"); - return; - } - if (!plot_cur->pl_dvecs || !plot_cur->pl_scale) { - fprintf(cp_err, "Error: no vectors available\n"); - return; - } - if (!isreal(plot_cur->pl_scale)) { - fprintf(cp_err, "Error: non-real time scale for %s\n", - plot_cur->pl_typename); - return; - } - - /* check if circuit is loaded and TSTART, TSTOP, TSTEP are available - if no circuit is loaded, but vectors are available, obtain - start, stop, step data from scale vector */ - if (!ft_curckt || !ft_curckt->ci_ckt || - !if_tranparams(ft_curckt, &tstart, &tstop, &tstep)) { - fprintf(cp_err, - "Warning: Can't get transient parameters from circuit.\n" - " Use transient analysis scale vector data instead.\n"); - int length = plot_cur->pl_scale->v_length; - if (length < 1) { - fprintf(cp_err, "Error: no data in vector\n"); - return; - } - tstart = plot_cur->pl_scale->v_realdata[0]; - tstop = plot_cur->pl_scale->v_realdata[length - 1]; - tstep = (tstop - tstart) / (double)length; - } - - /* if this plot contains special vectors lin-tstart, lin-tstop or lin-tstep, use these instead */ - lin = vec_fromplot("lin-tstart", plot_cur); - if (lin) { - fprintf(cp_out, "linearize tstart is set to: %8e\n", lin->v_realdata[0]); - tstart = lin->v_realdata[0]; - } - - lin = vec_fromplot("lin-tstop", plot_cur); - if (lin) { - fprintf(cp_out, "linearize tstop is set to: %8e\n", lin->v_realdata[0]); - tstop = lin->v_realdata[0]; - } - - lin = vec_fromplot("lin-tstep", plot_cur); - if (lin) { - fprintf(cp_out, "linearize tstep is set to: %8e\n", lin->v_realdata[0]); - tstep = lin->v_realdata[0]; - } - - /* finally check if tstart, tstop and tstep are reasonable */ - if (((tstop - tstart) * tstep <= 0.0) || ((tstop - tstart) < tstep)) { - fprintf(cp_err, - "Error: bad parameters -- start = %G, stop = %G, step = %G\n", - tstart, tstop, tstep); - return; - } - old = plot_cur; - oldtime = old->pl_scale; - new = plot_alloc("transient"); - new->pl_name = tprintf("%s (linearized)", old->pl_name); - new->pl_title = copy(old->pl_title); - new->pl_date = copy(old->pl_date); - new->pl_next = plot_list; - plot_new(new); - plot_setcur(new->pl_typename); - plot_list = new; - - /* default number of points */ - expo = (int)(log2((tstop - tstart) / tstep)); - len = 1 << expo; - - wlnew = wl; - /* get the new length from 'np=xx' */ - while (wlnew) { - char* para = wlnew->wl_word; - if (ciprefix("np=", para)) { - para += 3; - len = atoi(para); - break; - } - wlnew = wlnew->wl_next; - } - - tstep = (tstop - tstart) / (double)len; - - newtime = dvec_alloc(copy(oldtime->v_name), - oldtime->v_type, - oldtime->v_flags | VF_PERMANENT, - len, NULL); - - newtime->v_plot = new; - for (i = 0, d = tstart; i < len; i++, d += tstep) - newtime->v_realdata[i] = d; - new->pl_scale = new->pl_dvecs = newtime; - - if (wl) { - while (wl) { - if (ciprefix("np=", wl->wl_word)) { - wl = wl->wl_next; - continue; - } - v = vec_fromplot(wl->wl_word, old); - if (!v) { - fprintf(cp_err, "Error: no such vector %s\n", - wl->wl_word); - wl = wl->wl_next; - continue; - } - lincopy(v, newtime->v_realdata, len, oldtime); - wl = wl->wl_next; - } - } - else { - for (v = old->pl_dvecs; v; v = v->v_next) { - if (v == old->pl_scale) - continue; - lincopy(v, newtime->v_realdata, len, oldtime); - } - } -} - - /* Cut out part of tran vectors from cut-tstart to cut-tstop and copy these into a new plot. A new scale vector 'time' will be there as well. Vectors diff --git a/src/frontend/linear.h b/src/frontend/linear.h index 6fe15af2e..d1f731721 100644 --- a/src/frontend/linear.h +++ b/src/frontend/linear.h @@ -7,7 +7,6 @@ #define ngspice_LINEAR_H void com_linearize(wordlist *wl); -void com_linearnp(wordlist *wl); void com_cutout(wordlist *wl); From 7482de588b4217625c8dce553c9e103f2688a4be Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 16 Mar 2026 18:04:39 +0100 Subject: [PATCH 64/70] Parameter np=xx added. xx may be a number or auto2n. If np is not set, 'linearize' behaves as usual, (TSTOP-TSTART)/TSTEP + 1.5 If np=, integer is used If np=auto2n, the number of points is 2^n, close to (TSTOP-TSTART)/TSTEP. --- src/frontend/linear.c | 45 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 3fc903824..ca44d7d8f 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -28,7 +28,9 @@ com_linearize(wordlist *wl) struct dvec *newtime, *v; struct dvec *oldtime; struct dvec *lin; - int len, i; + int expo, len = 1024, i; + bool nponly = FALSE, np = FALSE; + wordlist * wlnew; if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { fprintf(cp_err, "Error: plot must be a transient analysis\n"); @@ -98,7 +100,34 @@ com_linearize(wordlist *wl) plot_new(new); plot_setcur(new->pl_typename); plot_list = new; - len = (int)((tstop - tstart) / tstep + 1.5); + + /* check if "np=" is the only entry in wl. +- If yes, linearize all vectors */ + if (wl && ciprefix("np=", wl->wl_word) && wl->wl_next == NULL) { + nponly = TRUE; + } + + wlnew = wl; + /* get the new length from 'np=xx' */ + while (wlnew) { + char* para = wlnew->wl_word; + if (ciprefix("np=", para)) { + np = TRUE; + para += 3; + len = atoi(para); + if (len == 0 && ciprefix("auto2n", para)) { + /* number of points as 2^n */ + expo = (int)round(log2((tstop - tstart) / tstep)); + len = 1 << expo; + } + break; + } + wlnew = wlnew->wl_next; + } + + if(!np) + len = (int)((tstop - tstart) / tstep + 1.5); + newtime = dvec_alloc(copy(oldtime->v_name), oldtime->v_type, oldtime->v_flags | VF_PERMANENT, @@ -109,12 +138,17 @@ com_linearize(wordlist *wl) newtime->v_realdata[i] = d; new->pl_scale = new->pl_dvecs = newtime; - if (wl) { + if (wl && !nponly) { + /* check for vectors given in the command line */ while (wl) { + if (ciprefix("np=", wl->wl_word)) { + wl = wl->wl_next; + continue; + } v = vec_fromplot(wl->wl_word, old); if (!v) { - fprintf(cp_err, "Error: no such vector %s\n", - wl->wl_word); + fprintf(cp_err, "Error: command 'linearize': no such vector %s\n", + wl->wl_word); wl = wl->wl_next; continue; } @@ -122,6 +156,7 @@ com_linearize(wordlist *wl) wl = wl->wl_next; } } else { + /* linearize all vectors of the current plot */ for (v = old->pl_dvecs; v; v = v->v_next) { if (v == old->pl_scale) continue; From 2d9e42669bfcb76286c72a3a648a1572f11cb2a8 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Fri, 9 Jan 2026 00:20:21 +0100 Subject: [PATCH 65/70] Add command line support for the 'version [-s|-f]' command With this commit the mentioned ngspice command(s) are also available on the command line. See ngspice --help for more information. --- src/main.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index c547d5475..01702c1f2 100644 --- a/src/main.c +++ b/src/main.c @@ -755,6 +755,8 @@ show_help(void) " -t, --term=TERM set the terminal type\n" " -h, --help display this help and exit\n" " -v, --version output version information and exit\n" + " -f, --version-full output full version information\n" + " --version-small output small version information\n" "\n" "Report bugs to %s.\n", cp_program, Bug_Addr); } @@ -952,6 +954,8 @@ int main(int argc, char **argv) {"define", required_argument, NULL, 'D'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, + {"version-full", no_argument, NULL, 'f'}, + {"version-small", no_argument, NULL, 256}, {"batch", no_argument, NULL, 'b'}, {"autorun", no_argument, NULL, 'a'}, {"circuitfile", required_argument, NULL, 'c'}, @@ -969,7 +973,7 @@ int main(int argc, char **argv) int option_index = 0; - int c = getopt_long(argc, argv, "D:hvbac:ino:pqr:st:", + int c = getopt_long(argc, argv, "D:hvfbac:ino:pqr:st:", long_options, &option_index); if (c == -1) { @@ -1004,6 +1008,22 @@ int main(int argc, char **argv) sp_shutdown(EXIT_INFO); break; + case 'f': /* Full version info */ + { + wordlist wl = { "-f", NULL, NULL }; + com_version(&wl); + sp_shutdown(EXIT_INFO); + } + break; + + case 256: /* --version-small */ + { + wordlist wl = { "-s", NULL, NULL }; + com_version(&wl); + sp_shutdown(EXIT_INFO); + } + break; + case 'b': /* Batch mode */ { bool x_false = FALSE; From 5066fe05f3c813a87b4c847c217d3fe456c0b5a0 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Mar 2026 16:01:19 +0100 Subject: [PATCH 66/70] In case of transient simulation error, bail out when fft is called. --- src/frontend/com_fft.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index 8b74414fc..062f40977 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -58,6 +58,13 @@ com_fft(wordlist *wl) } length = (plot_cur->pl_scale)->v_length; + + /* in case of tran error */ + if (length < 2) { + fprintf(cp_err, "Error: fft needs more than one time point, check the tran simulation!\n"); + goto done; + } + time = (plot_cur->pl_scale)->v_realdata; span = time[length-1] - time[0] + time[length-1] - time[length-2]; From d126c71f6a04fb3732887c5bb13d6d6e31f77175 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Mar 2026 16:51:24 +0100 Subject: [PATCH 67/70] Remove memory leak. --- src/xspice/enh/enhtrans.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c index ce22e2025..44a2b7a59 100644 --- a/src/xspice/enh/enhtrans.c +++ b/src/xspice/enh/enhtrans.c @@ -117,15 +117,13 @@ ENHtranslate_poly( l1->nextcard = l2; d->nextcard = l1; - /* PN 2004: Add original linenumber to ease the debug process - * for malfromned netlist - */ - + /* Add original linenumber to ease the debug process + * for malformed netlist */ l1->linenum = d->linenum; l2->linenum = d->linenum; - l1->linesource = copy("internal"); - l2->linesource = copy("internal"); + l1->linesource = "internal"; + l2->linesource = "internal"; /* Create the translated cards */ d->error = two2three_translate(d->line, &(l1->line), &(l2->line)); From ab5010506e80aca4371f14fca3524a1caf8446ed Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 21 Mar 2026 19:57:16 +0100 Subject: [PATCH 68/70] A memory leak occurs when command 'step' is followed by 'quit' without having reached the final TSTOP. New function DCtran_step_quit() removes the leak. --- src/frontend/breakp.c | 13 ++++++++++++- src/frontend/runcoms2.c | 5 +++++ src/include/ngspice/fteext.h | 27 ++------------------------- src/spicelib/analysis/dctran.c | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/frontend/breakp.c b/src/frontend/breakp.c index 5dae629bc..8fd822eef 100644 --- a/src/frontend/breakp.c +++ b/src/frontend/breakp.c @@ -517,7 +517,7 @@ ft_bpcheck(struct plot *runplot, int iteration) if ((howmanysteps > 0) && (--howmanysteps == 0)) { if (steps > 1) - fprintf(cp_err, "Stopped after %d steps.\n", steps); + fprintf(cp_out, "Note: Stopped after %d steps.\n", steps); return (FALSE); } @@ -694,3 +694,14 @@ printcond(struct dbcomm *d, FILE *fp) } } } + + +/* just check if we are in 'step' mode */ +bool +ft_stepcheck(void) +{ + if ((steps > 0) && (howmanysteps == 0)) { + return (TRUE); + } + return (FALSE); +} diff --git a/src/frontend/runcoms2.c b/src/frontend/runcoms2.c index baf43ee79..1e98f0073 100644 --- a/src/frontend/runcoms2.c +++ b/src/frontend/runcoms2.c @@ -35,6 +35,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group extern void line_free_x(struct card *deck, bool recurse); extern INPmodel *modtab; extern NGHASHPTR modtabhash; +extern int DCtran_step_quit(CKTcircuit* ckt); #ifdef SHARED_MODULE extern void exec_controls(wordlist *newcontrols); @@ -219,6 +220,10 @@ com_remcirc(wordlist *wl) EVTunsetup(ft_curckt->ci_ckt); #endif + /* remove remnants of run after incomplete transient sim with 'step' */ + if (ft_curckt->ci_ckt) + DCtran_step_quit(ft_curckt->ci_ckt); + if_cktfree(ft_curckt->ci_ckt, ft_curckt->ci_symtab); for (v = ft_curckt->ci_vars; v; v = next) { next = v->va_next; diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 7aa0202b6..cb6fffd81 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -26,9 +26,10 @@ Modified: 1999 Paolo Nenzi - 2000 AlansFixes extern void ft_checkkids(void); -/* breakpoint.c */ +/* breakp.c */ extern bool ft_bpcheck(struct plot *runplot, int iteration); +extern bool ft_stepcheck(void); extern void dbfree(struct dbcomm *db); extern void dbfree1(struct dbcomm *db); @@ -173,29 +174,6 @@ extern struct dvec *ft_evaluate(struct pnode *node); /* ftesopt.c */ extern struct variable *ft_getstat(struct circ *, char *); -/* ginterface.c - - extern bool gi_init(); - extern bool gi_endpause; - extern bool gi_rottext; - extern int gi_fntheight; - extern int gi_fntwidth; - extern int gi_maxx; - extern int gi_maxy; - extern int gi_nolst; - extern int gi_nocolors; - extern int gi_package; - extern void gi_arc(); - extern void gi_clearscreen(); - extern void gi_close(); - extern void gi_drawline(); - extern void gi_redraw(); - extern void gi_setcolor(); - extern void gi_resetcolor(); - extern void gi_setlinestyle(); - extern void gi_text(); - extern void gi_update(); -*/ /* graf.c */ @@ -385,6 +363,5 @@ extern struct dvec* copycut(struct dvec* ov, struct dvec* newscalevec, int istar extern bool ft_intrpt; extern bool ft_setflag; -/* error.c */ #endif diff --git a/src/spicelib/analysis/dctran.c b/src/spicelib/analysis/dctran.c index 4c55678d5..033ca6a61 100644 --- a/src/spicelib/analysis/dctran.c +++ b/src/spicelib/analysis/dctran.c @@ -969,3 +969,22 @@ chkStep: } /* NOTREACHED */ } + + +/* If we have a 'step' command, and then 'quit' before reaching the final time, + remove the run plot memory. */ +int +DCtran_step_quit(CKTcircuit* ckt) { + if (!ckt || !ckt->CKTcurJob) /* nothing to delete */ + return 0; + if (ckt->CKTcurJob->JOBtype != 4) /* only tran sim */ + return 0; + if (!ft_stepcheck()) /* only after 'step' */ + return 0; + TRANan* job = (TRANan*)ckt->CKTcurJob; + if (!job->TRANplot) /* already done */ + return 0; + SPfrontEnd->OUTendPlot(job->TRANplot); + job->TRANplot = NULL; + return(OK); +} From 16e6f21523c7abfdb3aa219d5633b0dc45b81ce0 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 22 Mar 2026 11:08:35 +0100 Subject: [PATCH 69/70] Revert "No quoting of potential params when comnpatmode is set to hs." This reverts commit 1aa3fa22ffe05718ee64a13e9b9d9ae871f471a5. --- src/frontend/inpcom.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index a3447aee2..50ec5ab68 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -8429,9 +8429,6 @@ static void inp_quote_params(struct card *c, struct card *end_c, if (ft_skywaterpdk) return; - if (newcompat.hs) - return; - for (; c && c != end_c; c = c->nextcard) { int i, j, num_terminals; From 35c63e10082b3baa395ef6332f8e93a32e731c7e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 22 Mar 2026 11:34:29 +0100 Subject: [PATCH 70/70] No quoting of potential params when comnpatmode is set to hs and variable no_auto_braces is set. --- src/frontend/inpcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 50ec5ab68..be727600e 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -8429,6 +8429,9 @@ static void inp_quote_params(struct card *c, struct card *end_c, if (ft_skywaterpdk) return; + if (newcompat.hs && cp_getvar("no_auto_braces", CP_BOOL, NULL, 0)) + return; + for (; c && c != end_c; c = c->nextcard) { int i, j, num_terminals;