diff --git a/examples/pss/19-nand-ro-IHP_ctrl.cir b/examples/pss/19-nand-ro-IHP_ctrl.cir index e3ec97712..75772bdde 100644 --- a/examples/pss/19-nand-ro-IHP_ctrl.cir +++ b/examples/pss/19-nand-ro-IHP_ctrl.cir @@ -47,7 +47,7 @@ set temp=0 option klu run rusage -set xbrushwidth=3 +set xbrushwidth=2 *plot i(Vss) ylimit 0 500u xlimit 50n 60n plot out plot out xlimit 50n 60n @@ -60,8 +60,11 @@ plot mag(out) xlimit 300Meg 2300Meg meas sp fmax MAX_AT out from=1e8 to=1e9 echo reset -pss 500e6 10n out 256 10 5 5e-3 uic +pss 500e6 8n out 1024 10 5 5e-3 uic +setplot pss3 plot out xlimit 300Meg 2300Meg +setplot pss2 +plot out inventory .endc diff --git a/examples/pss/colpitt_osc_pss_ctrl.cir b/examples/pss/colpitt_osc_pss_ctrl.cir index 74ea012df..58bdf5db9 100644 --- a/examples/pss/colpitt_osc_pss_ctrl.cir +++ b/examples/pss/colpitt_osc_pss_ctrl.cir @@ -25,13 +25,13 @@ plot V(2) v(3) linearize v(3) fft v(3) let dbv3 = db(v(3)) -plot dbv3 xlimit 1Meg 5Meg +plot dbv3 xlimit 1Meg 15Meg ** measure the frequency of oscillation meas sp fosc MAX_AT dbv3 from=1Meg to=5Meg ** periodic steady state sim pss 1e6 50e-6 3 256 10 50 5e-3 set xbrushwidth=3 -plot v(3) ylimit 0 0.3 +plot v(3) ylimit 0 0.3 xlimit 1Meg 15Meg .endc .end diff --git a/examples/pss/hartley_osc_pss_ctrl.cir b/examples/pss/hartley_osc_pss_ctrl.cir index 6594a93d5..7f41fee25 100644 --- a/examples/pss/hartley_osc_pss_ctrl.cir +++ b/examples/pss/hartley_osc_pss_ctrl.cir @@ -1,31 +1,35 @@ Hartley's Oscillator Circuit * Hartley is an harmonic oscillator (LC based) which use * an inductive partition of resonator to feed the single -* active device. Output is taken on node 2. +* active device. Output is taken on node n2. * Prediceted frequency is about 121.176 Hz. -* -* PLOT V(3) * Models: .model qnl npn(level=1 bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf cjc=2pf va=50) vcc 1 0 5 pwl 0 0 1e-5 5 -r1 1 2 0.2k -q1 2 3 0 qnl +r1 1 n2 0.2k +q1 n2 3 0 qnl c1 3 4 633n l1 3 0 1.5 l2 0 4 500m -r2 4 2 100 +r2 4 n2 100 .control -set xbrushwidth=3 +set xbrushwidth=2 tran 10u 1 -plot v(2) -linearize v(2) -fft v(2) -plot mag(V(2)) xlimit 0 500 ylimit 0 1.5 +rusage time +plot v(n2) xlimit 0 100m +linearize v(n2) np=65536 +fft v(n2) +plot mag(V(n2)) xlimit 0 500 ylimit 0 1.5 reset -pss 50 200e-3 2 1024 11 10 5e-3 -plot v(2) xlimit 0 500 ylimit 0 1.5 +* gfreq tstab oscnob psspoints harms sciter steadycoeff +pss 120 100e-3 n2 1024 11 10 5e-3 +rusage +setplot pss1 +plot v(n2) xlimit 0 500 ylimit 0 1.5 +setplot pss2 +plot v(n2) .endc diff --git a/examples/pss/vackar_osc_pss_ctrl.cir b/examples/pss/vackar_osc_pss_ctrl.cir index 27f1c8eab..871ed07b4 100644 --- a/examples/pss/vackar_osc_pss_ctrl.cir +++ b/examples/pss/vackar_osc_pss_ctrl.cir @@ -26,7 +26,7 @@ plot mag(v(4)) xlimit 0 10Meg let maxfft=mag(v(4)) meas sp fosc MAX_AT maxfft from=1Meg to=10Meg reset -pss 1.8e6 10e-6 4 1024 10 50 5e-3 uic +pss 1.8e6 20e-6 4 1024 10 50 5e-3 uic plot v(4) xlimit 0 10Meg .endc .end diff --git a/examples/xspice/noise/ota_noise_test1.cir b/examples/xspice/noise/ota_noise_test1.cir new file mode 100644 index 000000000..507398f15 --- /dev/null +++ b/examples/xspice/noise/ota_noise_test1.cir @@ -0,0 +1,27 @@ +* noise analysis with analogue code model + +aota inn 0 out newota +*.model newota ota (gm=1 rout=1e12 rin=1Meg en=31n enk=100 in_noise=100u ink=10k incm=0 incmk=2k noise_programmatic=TRUE) +*.model newota ota (gm=1m rout=1e12 rin=1Meg en=31n enk=100 noise_programmatic=TRUE) +*.model newota ota (gm=1m rout=1e12 rin=1Meg in_noise=2p ink=500 incm=0 incmk=500 noise_programmatic=TRUE) +.model newota ota (gm=1m rout=1e12 rin=1Meg en=31n enk=100 in_noise=20p ink=2k incm=0 incmk=2k noise_programmatic=TRUE) + +Vin in 0 dc 0 ac 1 sin (0 2 1.5k) +Rsource in inn 10k + + +Rout out 0 1 + +.control +*tran 1u 10m +*plot out +noise v(out) Vin dec 10 1 10MEG +print inoise_total onoise_total +display +setplot noise1 +display +plot loglog inoise_spectrum +plot loglog onoise_spectrum +.endc + +.end diff --git a/examples/xspice/state/state-machine.cir b/examples/xspice/state/state-machine.cir index 2184b477e..eb9d7a709 100644 --- a/examples/xspice/state/state-machine.cir +++ b/examples/xspice/state/state-machine.cir @@ -32,6 +32,7 @@ tran 1us 10ms rusage write spifsim.raw plot cntl out_msb+2 out_lsb+8 +plot n_one clk n_zero msb lsb digitop eprvcd n_one clk n_zero msb lsb > spifsim.vcd * plotting the vcd file (e.g. with GTKWave) if $oscompiled = 1 | $oscompiled = 8 ; MS Windows diff --git a/src/frontend/breakp.c b/src/frontend/breakp.c index 8fd822eef..eb20980fc 100644 --- a/src/frontend/breakp.c +++ b/src/frontend/breakp.c @@ -241,7 +241,7 @@ com_iplot(wordlist *wl) return; } - struct dbcomm *d, *td, *currentdb = NULL; + struct dbcomm *d, *td = NULL, *currentdb = NULL; double window = 0.0; #ifdef XSPICE int event_auto_incr = 0; @@ -311,7 +311,7 @@ com_iplot(wordlist *wl) /* Chain in expected order. */ - if (currentdb) + if (currentdb && td) td->db_also = d; else currentdb = d; diff --git a/src/frontend/com_gnuplot.c b/src/frontend/com_gnuplot.c index 23dede2af..7dd906a0f 100644 --- a/src/frontend/com_gnuplot.c +++ b/src/frontend/com_gnuplot.c @@ -25,6 +25,10 @@ com_gnuplot(wordlist *wl) if (!wl) return; +#ifdef HAS_PROGREP + SetAnalyse("gnuplot", 0); +#endif + if (cieq(fname, "temp") || cieq(fname, "tmp")) { fname = smktemp("gp"); /* Is this the correct name ? */ tempf = TRUE; diff --git a/src/frontend/com_shell.c b/src/frontend/com_shell.c index d6f9c6f2c..3a523fc31 100644 --- a/src/frontend/com_shell.c +++ b/src/frontend/com_shell.c @@ -24,6 +24,10 @@ com_shell(wordlist *wl) int status; char *shell = NULL; +#ifdef HAS_PROGREP + SetAnalyse("shell", 0); +#endif + shell = getenv("SHELL"); if (shell == NULL) { shell = SHELL; diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index b09c201cd..f2c9361c8 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1491,6 +1491,7 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name struct card* newcard; struct compat tmpcomp; + memset(&tmpcomp, 0, sizeof(tmpcomp)); bool compset = FALSE; /* special treatment of special .inc commands */ @@ -2340,7 +2341,8 @@ static char *readline(FILE *fd) /* Replace "gnd" by " 0 " Delimiters of gnd may be ' ' or ',' or '(' or ')', - may be disabled by setting variable no_auto_gnd */ + may be disabled by setting variable no_auto_gnd. + If called by KiCad, replace /gnd and /0 by 0 */ static void inp_fix_gnd_name(struct card *c) { @@ -2348,6 +2350,10 @@ static void inp_fix_gnd_name(struct card *c) for (; c; c = c->nextcard) { char *gnd = c->line; + // if there is a comment, go to next line + if (*gnd == '*') + continue; + // if inside of a subcircuit, and compatmode is ps, don't replace gnd if (newcompat.ps) { if (ciprefix(".subckt", c->line)) @@ -2356,10 +2362,13 @@ static void inp_fix_gnd_name(struct card *c) found_subckt = FALSE; } - // if there is a comment or no gnd, go to next line - if (found_subckt || (*gnd == '*') || !strstr(gnd, "gnd")) + // if there is a subcircuit or no gnd, go to next line + if (found_subckt || (!strstr(gnd, "gnd") && !strstr(gnd, "/0"))) continue; + // a gnd node will not occur in the first token of the line + gnd = nexttok(gnd); + // replace "?gnd?" by "? 0 ?", ? being a ' ' ',' '(' ')'. while ((gnd = strstr(gnd, "gnd")) != NULL) { if ((isspace_c(gnd[-1]) || gnd[-1] == '(' || gnd[-1] == ',') && @@ -2369,6 +2378,32 @@ static void inp_fix_gnd_name(struct card *c) gnd += 3; } + // Special treatment for KiCad: replace local /gnd and /0 by 0 + if (newcompat.ki) { + /* replace local "/gnd" by " 0 " */ + gnd = c->line; + // a gnd node will not occur in the first token of the line + gnd = nexttok(gnd); + while ((gnd = strstr(gnd, "/gnd")) != NULL) { + if ((isspace_c(gnd[-1]) || gnd[-1] == '(' || gnd[-1] == ',') && + (isspace_c(gnd[4]) || gnd[4] == ')' || gnd[4] == ',')) { + memcpy(gnd, " 0 ", 4); + } + gnd += 4; + } + // replace " /0 " by " 0 " + gnd = c->line; + // a gnd node will not occur in the first token of the line + gnd = nexttok(gnd); + while ((gnd = strstr(gnd, "/0")) != NULL) { + if ((isspace_c(gnd[-1]) || gnd[-1] == '(' || gnd[-1] == ',') && + (isspace_c(gnd[2]) || gnd[2] == ')' || gnd[2] == ',')) { + gnd[0] = ' '; + } + gnd += 2; + } + } + // now remove the extra white spaces around 0 c->line = inp_remove_ws(c->line); } @@ -9923,7 +9958,7 @@ static void inp_meas_control(struct card* card) { int is_control = 0; static int replaceno = 1; - struct card* prevcard; + struct card* prevcard = NULL; for (; card; prevcard = card, card = card->nextcard) { diff --git a/src/frontend/inpcompat.c b/src/frontend/inpcompat.c index d1c22f5a2..e970b517c 100644 --- a/src/frontend/inpcompat.c +++ b/src/frontend/inpcompat.c @@ -65,7 +65,9 @@ void set_compat_mode(void) newcompat.ps = FALSE; newcompat.xs = FALSE; newcompat.lt = FALSE; - newcompat.ki = FALSE; +#ifndef SHARED_MODULE + newcompat.ki = FALSE; /* has already been set in sharedspice.c */ +#endif newcompat.a = FALSE; newcompat.spe = FALSE; newcompat.isset = FALSE; diff --git a/src/frontend/outitf.c b/src/frontend/outitf.c index 586813947..a59dc6030 100644 --- a/src/frontend/outitf.c +++ b/src/frontend/outitf.c @@ -38,6 +38,8 @@ extern int EVTsetup_plot(CKTcircuit* ckt, char* plotname); extern IFsimulator SIMinfo; extern char Spice_Build_Date[]; +extern char* eng(double value, int digits, bool numeric, bool bytes); + extern unsigned long long getMemorySize(void); extern unsigned long long getPeakRSS(void); extern unsigned long long getCurrentRSS(void); @@ -111,16 +113,54 @@ OUTpBeginPlot(CKTcircuit *circuitPtr, JOB *analysisPtr, int numNames, IFuid *dataNames, int dataType, runDesc **plotPtr) { char *name; + int ret, n; + runDesc* plot; if (ft_curckt->ci_ckt == circuitPtr) name = ft_curckt->ci_name; else name = "circuit name"; - return (beginPlot(analysisPtr, circuitPtr, name, + ret = beginPlot(analysisPtr, circuitPtr, name, analName, refName, refType, numNames, dataNames, dataType, FALSE, - plotPtr)); + plotPtr); + plot = *plotPtr; + n = plot->numData; + + if (!cp_getvar("no_mem_check", CP_BOOL, NULL, 0)) { + /* Estimate the required memory */ + double timesteps = ft_curckt->ci_ckt->CKTfinalTime / ft_curckt->ci_ckt->CKTstep; + double dmemrequ = timesteps * (double)n * sizeof(double); + double dmemavail = (double)getAvailableMemorySize(); + char *cmemrequ = eng(dmemrequ, 6, FALSE, TRUE); + char *cmemavail = eng(dmemavail, 6, FALSE, TRUE); + char *ctimesteps = eng(timesteps, 6, TRUE, FALSE); + if (dmemrequ > dmemavail) { +#ifdef _WIN32 + fprintf(stderr, "\nError: memory required (%sB), made of\n" + " %d nodes and approximately %s time steps,\n" + " is more than the memory available (%sB)!\n", + cmemrequ, n,ctimesteps, cmemavail); + fprintf(stderr, "Setting the output memory is not possible.\n"); + tfree(cmemrequ); + tfree(cmemavail); + tfree(ctimesteps); + controlled_exit(1); +#else + fprintf(stderr, "\nWarning: memory required (%sB), made of\n" + " %d nodes and approximately %s time steps,\n" + " is more than the DRAM memory available (%sB)!\n", + cmemrequ, n, ctimesteps, cmemavail); + fprintf(stderr, " Swapping data to SSD may slow down the simulation.\n"); + tfree(cmemrequ); + tfree(cmemavail); + tfree(ctimesteps); +#endif + } + } + + return ret; } @@ -556,25 +596,6 @@ OUTpD_memory(runDesc *run, IFvalue *refValue, IFvalue *valuePtr) { int i, n = run->numData; - -#ifndef __APPLE__ - if (!cp_getvar("no_mem_check", CP_BOOL, NULL, 0)) { - /* Estimate the required memory */ - int timesteps = vlength2delta(0); - size_t memrequ = (size_t)n * timesteps * sizeof(double); - size_t memavail = getAvailableMemorySize(); - - if (memrequ > memavail) { - fprintf(stderr, "\nError: memory required (%zu Bytes), made of\n" - " %d nodes and approximately %d time steps,\n" - " is more than the memory available (%zu Bytes)!\n", - memrequ, n, timesteps, memavail); - fprintf(stderr, "Setting the output memory is not possible.\n"); - controlled_exit(1); - } - } -#endif - for (i = 0; i < n; i++) { dataDesc *d; diff --git a/src/frontend/plotting/graf.c b/src/frontend/plotting/graf.c index 2e29e07fa..6a6ca5f57 100644 --- a/src/frontend/plotting/graf.c +++ b/src/frontend/plotting/graf.c @@ -84,7 +84,7 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ const char *commandline, /* For xi_zoomdata() */ int prevgraph) /* plot id, if started from a previous plot*/ { - GRAPH *graph, *pgraph; + GRAPH *graph, *pgraph = NULL; wordlist *wl; NG_IGNORE(nplots); @@ -182,7 +182,7 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ } /* restore data from previous graph, e.g. for zooming */ - if (prevgraph > 0) { + if (prevgraph > 0 && pgraph) { int i; /* transmit colors */ @@ -1359,7 +1359,7 @@ void gr_iplot(struct plot *plot) struct dbcomm *dd; int dup = 0; #ifdef XSPICE - char *offp, save_sign; + char *offp, save_sign = '\0'; #endif if (dc->db_nodename1 == NULL) diff --git a/src/include/ngspice/cmconstants.h b/src/include/ngspice/cmconstants.h index 73563e14f..f85084e7b 100644 --- a/src/include/ngspice/cmconstants.h +++ b/src/include/ngspice/cmconstants.h @@ -49,6 +49,7 @@ NON-STANDARD FEATURES #define DC MIF_DC #define AC MIF_AC #define TRANSIENT MIF_TRAN +#define NOISE MIF_NOI #define ANALOG MIF_ANALOG #define EVENT MIF_EVENT_DRIVEN diff --git a/src/include/ngspice/cmproto.h b/src/include/ngspice/cmproto.h index d137264c5..d3e743384 100644 --- a/src/include/ngspice/cmproto.h +++ b/src/include/ngspice/cmproto.h @@ -112,6 +112,8 @@ Complex_t cm_complex_subtract(Complex_t x, Complex_t y); Complex_t cm_complex_multiply(Complex_t x, Complex_t y); Complex_t cm_complex_divide(Complex_t x, Complex_t y); +int cm_noise_add_source(const char *name, int conn_index, int port_index, Mif_Noise_Src_Type_t type); + char *cm_get_path(void); CKTcircuit *cm_get_circuit(void); diff --git a/src/include/ngspice/dllitf.h b/src/include/ngspice/dllitf.h index dd79ee4fc..0bea481f0 100644 --- a/src/include/ngspice/dllitf.h +++ b/src/include/ngspice/dllitf.h @@ -92,6 +92,8 @@ struct coreInfo_t { int ((*dllitf_MIFbindCSCComplex) (GENmodel *, CKTcircuit *)) ; int ((*dllitf_MIFbindCSCComplexToReal) (GENmodel *, CKTcircuit *)) ; #endif + int ((*dllitf_MIFnoise)(int, int, GENmodel *, CKTcircuit *, Ndata *, double *)); + int ((*dllitf_cm_noise_add_source)(const char *, int, int, Mif_Noise_Src_Type_t)); }; #endif diff --git a/src/include/ngspice/mif.h b/src/include/ngspice/mif.h index b01488a1b..312894273 100644 --- a/src/include/ngspice/mif.h +++ b/src/include/ngspice/mif.h @@ -82,6 +82,7 @@ extern int MIFiSize; extern int MIFmSize; -extern Mif_Info_t g_mif_info; +extern Mif_Info_t g_mif_info; +extern Mif_Private_t g_mif_noise_cm_data; #endif diff --git a/src/include/ngspice/mifcmdat.h b/src/include/ngspice/mifcmdat.h index 00504f5a2..85d542292 100644 --- a/src/include/ngspice/mifcmdat.h +++ b/src/include/ngspice/mifcmdat.h @@ -386,6 +386,28 @@ typedef struct Mif_Inst_Var_Data_s { +/* ************************************************************************* */ + + +/* + * Noise analysis context passed to code models via mif_private->noise. + * Stack-local in MIFnoise, valid only during cm_func call. + */ + +typedef struct Mif_Noise_Data_s { + Mif_Boolean_t registering; /* TRUE during N_OPEN, FALSE during N_CALC */ + int next_index; /* Monotonic counter, reset before each cm_func call */ + int num_prog_srcs; /* Total programmatic sources after N_OPEN */ + int max_prog_srcs; /* Allocated capacity of registration arrays */ + Mif_Noise_Src_Type_t *prog_types; /* Source type per programmatic source */ + int *prog_conn; /* Connection index per source */ + int *prog_port; /* Port index per source */ + char **prog_names; /* Source name suffix per source */ + double *density; /* Spectral density array (N_CALC, sized num_prog_srcs) */ + double freq; /* Current noise frequency in Hz */ +} Mif_Noise_Data_t; + + /* ************************************************************************* */ @@ -405,6 +427,7 @@ struct Mif_Private { int num_inst_var; /* Number of instance variables */ Mif_Inst_Var_Data_t **inst_var; /* Information about each inst variable */ Mif_Callback_t *callback; /* Callback function */ + Mif_Noise_Data_t *noise; /* Noise context, NULL when not in noise analysis */ }; diff --git a/src/include/ngspice/mifdefs.h b/src/include/ngspice/mifdefs.h index c3564e754..785c10899 100644 --- a/src/include/ngspice/mifdefs.h +++ b/src/include/ngspice/mifdefs.h @@ -84,6 +84,17 @@ struct MIFinstance { int inst_index; /* Index into inst_table in evt struct in ckt */ Mif_Callback_t callback; /* instance callback function */ + + int num_noise_srcs; /* Total noise sources (declarative + programmatic) */ + Mif_Boolean_t noise_initialized; /* TRUE once sources discovered and allocated */ + double *MIFnVar; /* Flat array [NSTATVARS * num_noise_srcs] */ + int *noise_node1; /* Positive/branch node per source */ + int *noise_node2; /* Negative/ground node per source */ + char **noise_src_names; /* Suffix name per source */ + int noise_decl_nv_base; /* Base index of voltage noise group, -1 if none */ + int noise_decl_nc_base; /* Base index of current noise group, -1 if none */ + int noise_prog_offset; /* Index where programmatic sources start */ + double *noise_prog_density; /* Reusable density array for cm_func */ }; diff --git a/src/include/ngspice/mifproto.h b/src/include/ngspice/mifproto.h index 2daabcb93..c97cb629c 100644 --- a/src/include/ngspice/mifproto.h +++ b/src/include/ngspice/mifproto.h @@ -46,6 +46,7 @@ NON-STANDARD FEATURES #include "ngspice/inpdefs.h" #include "ngspice/smpdefs.h" #include "ngspice/cktdefs.h" +#include "ngspice/noisedef.h" #include "ngspice/miftypes.h" @@ -154,6 +155,17 @@ extern Mif_Cntl_Src_Type_t MIFget_cntl_src_type( extern char *MIFcopy(char *); +extern int MIFnoise( + int mode, + int operation, + GENmodel *inModel, + CKTcircuit *ckt, + Ndata *data, + double *OnDens +); + +extern void MIF_free_noise_state(MIFinstance *here); + #ifdef KLU extern int MIFbindCSC (GENmodel*, CKTcircuit*) ; extern int MIFbindCSCComplex (GENmodel*, CKTcircuit*) ; diff --git a/src/include/ngspice/miftypes.h b/src/include/ngspice/miftypes.h index f7c05e6cd..bc0135b4d 100644 --- a/src/include/ngspice/miftypes.h +++ b/src/include/ngspice/miftypes.h @@ -232,4 +232,16 @@ typedef struct MIFmodel MIFmodel; typedef void (* Mif_Callback_t)(Mif_Private_t *, Mif_Callback_Reason_t); +/* + * Noise source type for MIFnoise + */ + +typedef enum { + MIF_NOISE_CURRENT, /* Current source between port nodes (pos_node, neg_node) */ + MIF_NOISE_VOLTAGE, /* Voltage source at port branch equation (branch, 0) */ + MIF_NOISE_CURRENT_POS, /* Current source from pos_node to ground */ + MIF_NOISE_CURRENT_NEG, /* Current source from neg_node to ground */ +} Mif_Noise_Src_Type_t; + + #endif diff --git a/src/maths/KLU/klusmp.c b/src/maths/KLU/klusmp.c index d22c36f94..1546fd3a7 100644 --- a/src/maths/KLU/klusmp.c +++ b/src/maths/KLU/klusmp.c @@ -593,6 +593,10 @@ SMPluFac (SMPmatrix *Matrix, double PivTol, double Gmin) if (ret == 0) { + if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { + fprintf (stderr, "Error (ReFactor): KLUcommon object is NULL. A problem occurred\n") ; + return 1; + } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_SINGULAR) { if (ft_ngdebug) { fprintf(stderr, "Warning (ReFactor): KLU Matrix is SINGULAR\n"); @@ -601,9 +605,6 @@ SMPluFac (SMPmatrix *Matrix, double PivTol, double Gmin) } return E_SINGULAR ; } - if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { - fprintf (stderr, "Error (ReFactor): KLUcommon object is NULL. A problem occurred\n") ; - } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_EMPTY_MATRIX) { fprintf (stderr, "Error (ReFactor): KLU Matrix is empty\n") ; @@ -611,8 +612,9 @@ SMPluFac (SMPmatrix *Matrix, double PivTol, double Gmin) } if (Matrix->SMPkluMatrix->KLUmatrixNumeric == NULL) { fprintf (stderr, "Error (ReFactor): KLUnumeric object is NULL. A problem occurred\n") ; + return 1 ; } - return 1 ; + return 1; } else { return 0 ; } @@ -660,6 +662,10 @@ SMPluFacKLUforCIDER (SMPmatrix *Matrix) if (ret == 0) { + if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { + fprintf (stderr, "Error (ReFactor for CIDER): KLUcommon object is NULL. A problem occurred\n") ; + return 1; + } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_SINGULAR) { if (ft_ngdebug) { fprintf(stderr, "Warning (ReFactor for CIDER): KLU Matrix is SINGULAR\n"); @@ -668,9 +674,6 @@ SMPluFacKLUforCIDER (SMPmatrix *Matrix) } return E_SINGULAR ; } - if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { - fprintf (stderr, "Error (ReFactor for CIDER): KLUcommon object is NULL. A problem occurred\n") ; - } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_EMPTY_MATRIX) { fprintf (stderr, "Error (ReFactor for CIDER): KLU Matrix is empty\n") ; @@ -678,8 +681,9 @@ SMPluFacKLUforCIDER (SMPmatrix *Matrix) } if (Matrix->SMPkluMatrix->KLUmatrixNumeric == NULL) { fprintf (stderr, "Error (ReFactor for CIDER): KLUnumeric object is NULL. A problem occurred\n") ; + return 1 ; } - return 1 ; + return 1; } else { return 0 ; } @@ -785,6 +789,7 @@ SMPreorder (SMPmatrix *Matrix, double PivTol, double PivRel, double Gmin) if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { fprintf (stderr, "Error (Factor): KLUnumeric object is NULL. A problem occurred\n") ; fprintf (stderr, "Error (Factor): KLUcommon object is NULL. A problem occurred\n") ; + return 1; } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_EMPTY_MATRIX) { fprintf (stderr, "Error (Factor): KLU Matrix is empty\n") ; @@ -846,6 +851,11 @@ SMPreorderKLUforCIDER (SMPmatrix *Matrix) if (Matrix->SMPkluMatrix->KLUmatrixNumeric == NULL) { + if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { + fprintf (stderr, "Error (Factor for CIDER): KLUnumeric object is NULL. A problem occurred\n") ; + fprintf (stderr, "Error (Factor for CIDER): KLUcommon object is NULL. A problem occurred\n") ; + return 1; + } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_SINGULAR) { if (ft_ngdebug) { fprintf(stderr, "Warning (Factor for CIDER): KLU Matrix is SINGULAR\n"); @@ -854,10 +864,6 @@ SMPreorderKLUforCIDER (SMPmatrix *Matrix) } return E_SINGULAR ; } - if (Matrix->SMPkluMatrix->KLUmatrixCommon == NULL) { - fprintf (stderr, "Error (Factor for CIDER): KLUnumeric object is NULL. A problem occurred\n") ; - fprintf (stderr, "Error (Factor for CIDER): KLUcommon object is NULL. A problem occurred\n") ; - } if (Matrix->SMPkluMatrix->KLUmatrixCommon->status == KLU_EMPTY_MATRIX) { fprintf (stderr, "Error (Factor for CIDER): KLU Matrix is empty\n") ; return 0 ; @@ -865,8 +871,9 @@ SMPreorderKLUforCIDER (SMPmatrix *Matrix) if (Matrix->SMPkluMatrix->KLUmatrixSymbolic == NULL) { fprintf (stderr, "Error (Factor for CIDER): KLUnumeric object is NULL. A problem occurred\n") ; fprintf (stderr, "Error (Factor for CIDER): KLUsymbolic object is NULL. A problem occurred\n") ; + return 1 ; } - return 1 ; + return 1; } else { return 0 ; } diff --git a/src/maths/ni/nipzmeth.c b/src/maths/ni/nipzmeth.c index dda44d7a2..f24922ee0 100644 --- a/src/maths/ni/nipzmeth.c +++ b/src/maths/ni/nipzmeth.c @@ -258,7 +258,7 @@ NIpzSym2(PZtrial **set, PZtrial *new) R_NORM(c,c_mag); if (c == 0.0 || ((a == 0.0 || c_mag < a_mag - 40) - && (b = 0.0 ||c_mag < b_mag - 40))) { + && (b == 0.0 || c_mag < b_mag - 40))) { /*fprintf(stderr, "\t- linear (%g, %d)\n", c, c_mag);*/ if (a == 0.0) { a = b; diff --git a/src/misc/Makefile.am b/src/misc/Makefile.am index d20d905ae..b6d7b1e5f 100644 --- a/src/misc/Makefile.am +++ b/src/misc/Makefile.am @@ -10,6 +10,7 @@ libmisc_la_SOURCES = \ dup2.c \ dstring.c \ dup2.h \ + engnotation.c \ hash.c \ ivars.c \ ivars.h \ diff --git a/src/misc/alloc.c b/src/misc/alloc.c index 6b3a7bd7c..f47c9bd90 100644 --- a/src/misc/alloc.c +++ b/src/misc/alloc.c @@ -75,7 +75,7 @@ tmalloc(size_t num) mutex_unlock(&allocMutex); #endif if (!s){ - fprintf(stderr, "Error: malloc: can't allocate %lld bytes.\n", (long long)num); + fprintf(stderr, "Error: malloc: can't allocate %zu bytes.\n", num); fprintf(stderr, " Not enough memory or heap corruption\n"); #if defined HAS_WINGUI || defined SHARED_MODULE controlled_exit(EXIT_FAILURE); @@ -120,7 +120,7 @@ trealloc(const void *ptr, size_t num) #endif } if (!s) { - fprintf(stderr,"Error: realloc: can't allocate %lld bytes.\n", (long long)num); + fprintf(stderr,"Error: realloc: can't allocate %zu bytes.\n", num); fprintf(stderr," Not enough memory or heap corruption\n"); #if defined HAS_WINGUI || defined SHARED_MODULE controlled_exit(EXIT_FAILURE); diff --git a/src/misc/engnotation.c b/src/misc/engnotation.c new file mode 100644 index 000000000..6a029fc85 --- /dev/null +++ b/src/misc/engnotation.c @@ -0,0 +1,86 @@ +/* Print a floating-point number in engineering notation. + Documentation: http://www.cs.tut.fi/~jkorpela/c/eng.html + BSD-style license */ + +#define PREFIX_START (-24) +/* Smallest power of ten for which there is a prefix defined. + If the set of prefixes will be extended, change this constant + and update the table "prefix". */ + +#include +#include +#include "ngspice/ngspice.h" + +/* Print a floating-point number in engineering notation. + Return string needs to be freed by the caller after its use. + numeric selects e3, e6, e9 etc. or k, M, G etc. + If flag bytes is set true, numeric is overwritten, bytes + in multiples of 1024 are issued using k, M, G, T, P. */ +char *eng(double value, int digits, bool numeric, bool bytes) +{ + static char *prefix[] = { + "y", "z", "a", "f", "p", "n", "u", "m", "", + "k", "M", "G", "T", "P", "E", "Z", "Y" +}; +#define PREFIX_END (PREFIX_START+\ + (int)((sizeof(prefix)/sizeof(char *)-1)*3)) + + + double display, fract; + int expof10; + char *result, *sign; + + if (bytes) { + int i = 0; + + // Divide by 1024 until the unit is reached + while (value >= 1024. && i < 5) { + value /= 1024.; + i++; + } + result = tprintf("%.*g %s", digits - 1, value, prefix[i + 8]); + return result; + } + + if(value < 0.0) { + sign = "-"; + value = -value; + } else { + sign = ""; + } + + // correctly round to desired precision + expof10 = lrint( floor( log10(value) ) ); + value *= pow(10.0, digits - 1 - expof10); + + fract = modf(value, &display); + if(fract >= 0.5) display += 1.0; + + value = display * pow(10.0, expof10 - digits + 1); + + + if(expof10 > 0) + expof10 = (expof10/3)*3; + else + expof10 = ((-expof10+3)/3)*(-3); + + value *= pow(10.0, -expof10); + if (value >= 1000.0) { + value /= 1000.0; + expof10 += 3; + } + else if(value >= 100.0) + digits -= 2; + else if(value >= 10.0) + digits -= 1; + + if(numeric || (expof10 < PREFIX_START) || (expof10 > PREFIX_END)) + if (expof10 == 0) + result = tprintf("%s%.*f", sign, digits-1, value); + else + result = tprintf("%s%.*fe%d", sign, digits-1, value, expof10); + else + result = tprintf("%s%.*f %s", sign, digits-1, value, prefix[(expof10-PREFIX_START)/3]); + + return result; +} diff --git a/src/sharedspice.c b/src/sharedspice.c index acc814887..7fef62bc4 100644 --- a/src/sharedspice.c +++ b/src/sharedspice.c @@ -55,6 +55,8 @@ int myputs(const char* inp, FILE* f); int myputc(int inp, FILE* f); int myfputc(int inp, FILE* f); +static char* get_calling_process_name(void); + int myputs(const char* inp, FILE* f) { @@ -80,6 +82,7 @@ myfputc(int inp, FILE* f) #include "ngspice/ngspice.h" #include "misc/misc_time.h" #include "ngspice/randnumb.h" +#include "ngspice/compatmode.h" /*Use Windows threads if on W32 without pthreads*/ #ifndef HAVE_LIBPTHREAD @@ -965,12 +968,15 @@ ngSpice_Init(SendChar* printfcn, SendStat* statusfcn, ControlledExit* ngspiceexi struct passwd *pw; pw = getpwuid(getuid()); - s = tprintf("%s" DIR_PATHSEP "%s", pw->pw_dir, INITSTR); + if (pw) { + s = tprintf("%s" DIR_PATHSEP "%s", pw->pw_dir, INITSTR); - if (access(s, 0) == 0) - inp_source(s); + if (access(s, 0) == 0) { + inp_source(s); + } - tfree(s); + tfree(s); + } } #else /* ~ HAVE_PWD_H */ /* load user's initialisation file @@ -1050,6 +1056,16 @@ ngSpice_Init(SendChar* printfcn, SendStat* statusfcn, ControlledExit* ngspiceexi /* initialize display to 'no display at all'*/ DevInit(); + /* Check if we are called by Eeschema */ + char* thisproc = get_calling_process_name(); + if (thisproc) { + if( ciprefix("eeschema", thisproc)) + newcompat.ki = TRUE; + else + newcompat.ki = FALSE; + tfree(thisproc); + } + #ifdef FastRand // initialization and seed for FastNorm Gaussian random generator { @@ -2603,3 +2619,33 @@ static int totalreset(void) return 0; }; + +char * get_calling_process_name(void) { +#if defined(HAVE__PROC_MEMINFO) + char proc_name[16]; // Linux process names are limited to 15 chars + null + FILE* f = fopen("/proc/self/comm", "r"); + if (f) { + if (fgets(proc_name, sizeof(proc_name), f)) { + fclose(f); + return copy(proc_name); + } + else + fclose(f); + return NULL; + } +#elif defined(_WIN32) + char processPath[MAX_PATH]; + // Passing NULL retrieves the path of the executable for the current process + DWORD length = GetModuleFileNameA(NULL, processPath, MAX_PATH); + if (length > 0) { + // Find the last backslash to isolate the executable name from the full path + char* processName = strrchr(processPath, '\\'); + if (processName) { + return copy(processName + 1); + } + } + return NULL; +#else + return NULL; +#endif +} diff --git a/src/spicelib/analysis/cktpzstr.c b/src/spicelib/analysis/cktpzstr.c index ae784ef21..26171a7d0 100644 --- a/src/spicelib/analysis/cktpzstr.c +++ b/src/spicelib/analysis/cktpzstr.c @@ -504,7 +504,7 @@ int CKTpzStrat(PZtrial** set) else if (Last_Move == MID_RIGHT || Last_Move == NEAR_LEFT) suggestion = SPLIT_RIGHT; else - abort(); /* XXX */ + controlled_exit(EXIT_BAD); Consec_Moves = 0; } } diff --git a/src/spicelib/analysis/dcpss.c b/src/spicelib/analysis/dcpss.c index 4a60f9d14..aa0cee3c8 100644 --- a/src/spicelib/analysis/dcpss.c +++ b/src/spicelib/analysis/dcpss.c @@ -24,6 +24,8 @@ /* gtri - end - wbk - Add headers */ #endif +extern char* eng(double value, int digits, bool numeric, bool bytes); + #define INIT_STATS() \ do { \ startTime = SPfrontEnd->IFseconds(); \ @@ -51,6 +53,7 @@ do { \ #define GF_LAST 313 //#define PSSDEBUG +//#define STEPDEBUG static int DFT(long int, int, double *, double *, double *, double, double *, double *, double *, double *, double *); @@ -111,13 +114,13 @@ DCpss(CKTcircuit *ckt, /* Print some useful information */ - fprintf (stderr, "Periodic Steady State Analysis Started\n\n") ; - fprintf (stderr, "PSS Guessed Frequency %g\n", ckt->CKTguessedFreq) ; - fprintf (stderr, "PSS Points %ld\n", ckt->CKTpsspoints) ; - fprintf (stderr, "PSS Harmonics number %d\n", ckt->CKTharms) ; - fprintf (stderr, "PSS Steady Coefficient %g\n", ckt->CKTsteady_coeff) ; - fprintf (stderr, "PSS sc_iter %d\n", ckt->CKTsc_iter) ; - fprintf (stderr, "PSS Stabilization Time %g\n", ckt->CKTstabTime) ; + fprintf (stdout, "Periodic Steady State Analysis Started\n\n") ; + fprintf (stdout, "PSS Guessed Frequency %g\n", ckt->CKTguessedFreq) ; + fprintf (stdout, "PSS Points %ld\n", ckt->CKTpsspoints) ; + fprintf (stdout, "PSS Harmonics number %d\n", ckt->CKTharms) ; + fprintf (stdout, "PSS Steady Coefficient %g\n", ckt->CKTsteady_coeff) ; + fprintf (stdout, "PSS sc_iter %d\n", ckt->CKTsc_iter) ; + fprintf (stdout, "PSS Stabilization Time %g\n", ckt->CKTstabTime) ; oscnNode = job->PSSoscNode->number ; @@ -159,7 +162,7 @@ DCpss(CKTcircuit *ckt, /* Delta timestep and circuit time setup */ delta = ckt->CKTstep ; - ckt->CKTtime = ckt->CKTinitTime ; + ckt->CKTtime = 0; ckt->CKTfinalTime = ckt->CKTstabTime ; /* Starting PSS Algorithm, based on Transient Analysis */ @@ -228,10 +231,7 @@ DCpss(CKTcircuit *ckt, tfree(nameList); if(error) return(error); - /* Time initialization for Transient Analysis */ - ckt->CKTtime = 0; - ckt->CKTdelta = 0; - ckt->CKTbreak = 1; + /* Initialization for Transient Analysis */ firsttime = 1; save_mode = (ckt->CKTmode&MODEUIC) | MODETRANOP | MODEINITJCT; save_order = ckt->CKTorder; @@ -316,7 +316,7 @@ DCpss(CKTcircuit *ckt, fprintf (stderr, "delta initialized to %g\n", ckt->CKTdelta); #endif - ckt->CKTsaveDelta = ckt->CKTfinalTime/50; + ckt->CKTsaveDelta = ckt->CKTfinalTime/50; ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN; /* Changing Circuit MODE */ @@ -344,7 +344,7 @@ DCpss(CKTcircuit *ckt, 666, NULL, 666, &(job->PSSplot_td)); if(error) { - fprintf(stderr, "Couldn't relink rawfile\n"); + fprintf(stderr, "Error: Couldn't relink rawfile\n"); return error; } /* end saj*/ @@ -396,7 +396,7 @@ DCpss(CKTcircuit *ckt, #ifdef STEPDEBUG fprintf (stderr, "Delta %g accepted at time %g (finaltime: %g)\n", ckt->CKTdelta, ckt->CKTtime, ckt->CKTfinalTime) ; - fflush(stdout); + fflush(stderr); #endif /* STEPDEBUG */ ckt->CKTstat->STATaccepted ++; ckt->CKTbreak = 0; @@ -423,7 +423,7 @@ DCpss(CKTcircuit *ckt, nextstep = time_temp + 1 / ckt->CKTguessedFreq * ((double)(pss_points_cycle) / (double)ckt->CKTpsspoints) ; /* If in_pss, store data for Time Domain Plot and gather ordered data for FFT computing */ - if ((AlmostEqualUlps (ckt->CKTtime, nextstep, 10)) || (ckt->CKTtime > time_temp + 1 / ckt->CKTguessedFreq)) + if ((AlmostEqualUlps (ckt->CKTtime, nextstep, 10)) || (ckt->CKTtime > time_temp + 1 / ckt->CKTguessedFreq)) { #ifdef PSSDEBUG @@ -485,8 +485,8 @@ DCpss(CKTcircuit *ckt, /* Set the new Final Time - This is important because the last breakpoint is always CKTfinalTime */ ckt->CKTfinalTime = time_temp + 2 / ckt->CKTguessedFreq ; - fprintf (stderr, "Exiting from stabilization\n") ; - fprintf (stderr, "Time of first shooting evaluation will be %1.10g\n", time_temp + 1 / ckt->CKTguessedFreq) ; + fprintf (stdout, "Exiting from stabilization\n") ; + fprintf (stdout, "Time of first shooting evaluation will be %1.10g\n", time_temp + 1 / ckt->CKTguessedFreq) ; /* Next time is no more in stabilization - Unset the flag */ pss_state = SHOOTING; @@ -494,15 +494,16 @@ DCpss(CKTcircuit *ckt, /* Save the RHS_copy_der as the NEW CKTrhsOld */ for (i = 1 ; i <= msize ; i++) RHS_copy_der [i - 1] = ckt->CKTrhsOld [i] ; - - /* Print RHS on exiting from stabilization */ - fprintf (stderr, "RHS on exiting from stabilization: ") ; - for (i = 1 ; i <= msize ; i++) - { - RHS_copy_se [i - 1] = ckt->CKTrhsOld [i] ; - fprintf (stderr, "%-15g ", RHS_copy_se [i - 1]) ; + if (ft_ngdebug) { + /* Print RHS on exiting from stabilization */ + fprintf(stdout, "RHS on exiting from stabilization: "); + for (i = 1; i <= msize; i++) + { + RHS_copy_se[i - 1] = ckt->CKTrhsOld[i]; + fprintf(stdout, "%-15g ", RHS_copy_se[i - 1]); + } + fprintf(stdout, "\n"); } - fprintf (stderr, "\n") ; /* RHS_max and RHS_min initialization - HUGE_VAL is the maximum machine error */ for (i = 0 ; i < msize ; i++) @@ -510,7 +511,7 @@ DCpss(CKTcircuit *ckt, RHS_max [i] = -HUGE_VAL ; RHS_min [i] = HUGE_VAL ; } - } + } } break; @@ -574,9 +575,6 @@ DCpss(CKTcircuit *ckt, /* Force the tran analysis to evaluate requested breakpoints. Breakpoints are even more closer as the next occurence of guessed period is approaching. La lunga notte dei robot viventi... */ -/* double offset, interval, nextBreak ; - int i ; -*/ if ((ckt->CKTtime > time_temp + (1 / ckt->CKTguessedFreq) * 0.995) && (ckt->CKTtime <= time_temp + (1 / ckt->CKTguessedFreq))) { offset = time_temp + (1 / ckt->CKTguessedFreq) * 0.995 ; @@ -617,8 +615,8 @@ DCpss(CKTcircuit *ckt, nextBreak = offset + (i + 1) * interval ; CKTsetBreak (ckt, nextBreak) ; } else { - fprintf (stderr, "Strange behavior\n\n") ; - fprintf (stderr, "CKTtime: %g\ntime_temp: %g\n\n", ckt->CKTtime, time_temp) ; + fprintf (stderr, "Error: Strange behavior\n") ; + fprintf (stderr, " CKTtime: %g\ntime_temp: %g\n\n", ckt->CKTtime, time_temp) ; } /* *************************************** */ @@ -629,6 +627,8 @@ DCpss(CKTcircuit *ckt, /* If evolution is near shooting... */ if ((AlmostEqualUlps (ckt->CKTtime, time_temp + 1 / ckt->CKTguessedFreq, 10)) || (ckt->CKTtime > time_temp + 1 / ckt->CKTguessedFreq)) { + char* freq = NULL; + int excessive_err_nodes = 0 ; /* Calculation of error norms of RHS solution of every accepted nextTime */ @@ -637,18 +637,23 @@ DCpss(CKTcircuit *ckt, { /* Pitagora ha sempre ragione!!! :))) */ /* pred is treated as FREQUENCY to avoid numerical overflow when derivative is close to ZERO */ - pred [i] = RHS_derivative [i] / err_conv [i] ; + if(RHS_derivative[i] == 0) { + pred[i] = 0.; + } + else { + pred[i] = RHS_derivative[i] / err_conv[i]; + } #ifdef PSSDEBUG fprintf (stderr, "Pred is so high or so low! Diff is: %g\n", err_conv [i]) ; #endif - if ((fabs (pred [i]) > 1.0e6 * ckt->CKTguessedFreq) || (err_conv [i] == 0)) + if ((fabs (pred [i]) > ckt->CKTguessedFreq) || (err_conv [i] == 0)) { if (pred [i] > 0) - pred [i] = 1.0e6 * ckt->CKTguessedFreq ; + pred [i] = ckt->CKTguessedFreq ; else - pred [i] = -1.0e6 * ckt->CKTguessedFreq ; + pred [i] = -1.* ckt->CKTguessedFreq ; } predsum += pred [i] ; @@ -660,15 +665,18 @@ DCpss(CKTcircuit *ckt, } -// int excessive_err_nodes = 0 ; + /* no error, let's leave shooting */ + if (predsum == 0.) { + goto shootingexit; + } if (shooting_cycle_counter == 0) { - /* If first time in shooting we warn about that ! */ - fprintf (stderr, "In shooting...\n") ; + /* If first time in shooting we tell about it ! */ + fprintf (stdout, "In shooting...\n") ; } -//#ifdef STEPDEBUG +#ifdef PSSDEBUG /* For debugging purpose */ fprintf (stderr, "\n----------------\n") ; fprintf (stderr, "Shooting cycle iteration number: %3d ||", shooting_cycle_counter) ; @@ -680,7 +688,7 @@ DCpss(CKTcircuit *ckt, // fprintf (stderr, "Print of dynamically consistent nodes voltages or branches currents:\n") ; /* --------------------- */ -//#endif +#endif for (i = 0, node = ckt->CKTnodes->next ; node ; i++, node = node->next) { @@ -734,7 +742,7 @@ DCpss(CKTcircuit *ckt, else if ((time_err_min_0 - time_temp) < 0) { /* Something has gone wrong... */ - fprintf (stderr, "Cannot find a minimum for error vector in estimated period. Try to adjust tstab! PSS analysis aborted\n") ; + fprintf (stderr, "Error: Cannot find a minimum for error vector in estimated period. Try to adjust tstab! PSS analysis aborted\n") ; /* Terminates plot in Time Domain and frees the allocated memory */ SPfrontEnd->OUTendPlot (job->PSSplot_td) ; @@ -754,7 +762,7 @@ DCpss(CKTcircuit *ckt, //#endif /* Take the mean value of time prediction trough the dynamic test variable - predsum becomes TIME */ - predsum = 1 / (predsum * dynamic_test) ; + predsum = 1 / (predsum * dynamic_test); /* Store the predsum history as absolute value */ predsum_history [shooting_cycle_counter] = fabs (predsum) ; @@ -794,9 +802,10 @@ DCpss(CKTcircuit *ckt, rr_history [shooting_cycle_counter] = err ; gf_history [shooting_cycle_counter] = ckt->CKTguessedFreq ; shooting_cycle_counter++ ; - - fprintf (stderr, "Updated guessed frequency: %1.10lg .\n", ckt->CKTguessedFreq) ; - fprintf (stderr, "Next shooting evaluation time is %1.10g and current time is %1.10g.\n", + freq = eng(ckt->CKTguessedFreq, 10, TRUE, FALSE); + fprintf (stdout, "Updated guessed frequency: %s Hz.\n", freq) ; + tfree(freq); + fprintf (stdout, "Next shooting evaluation time is %1.10g and current time is %1.10g.\n", time_temp + 1 / ckt->CKTguessedFreq, ckt->CKTtime) ; /* Restore maximum and minimum error for next search */ @@ -824,37 +833,34 @@ DCpss(CKTcircuit *ckt, RHS_min [i] = HUGE_VAL ; } - fprintf (stderr, "----------------\n\n") ; + fprintf (stdout, "----------------\n\n") ; +shootingexit: /* Shooting Exit Condition */ if ((shooting_cycle_counter > ckt->CKTsc_iter) || (excessive_err_nodes == 0)) { int k ; - double minimum ; - + double minimum; pss_state = PSS ; #ifdef PSSDEBUG fprintf (stderr, "\nFrequency estimation (FE) and RHS period residual (PR) evolution\n") ; #endif -// minimum = rr_history [0] ; minimum = predsum_history [0] ; k = 0 ; for (i = 0 ; i < shooting_cycle_counter ; i++) { /* Print some statistics */ - fprintf (stderr, "%-3d -> FE: %-15.10g || RR: %15.10g", i, gf_history [i], rr_history [i]) ; + fprintf (stdout, "%-3d -> FE: %-15.10g || RR: %15.10g", i, gf_history [i], rr_history [i]) ; /* Take the minimum residual iteration */ -// if (minimum > rr_history [i]) if (minimum > predsum_history [i]) { -// minimum = rr_history [i] ; minimum = predsum_history [i] ; k = i ; } - fprintf (stderr, " || predsum/dynamic_test: %15.10g || minimum: %15.10g\n", predsum_history [i], minimum) ; + fprintf (stdout, " || predsum/dynamic_test: %15.10g || minimum: %15.10g\n", predsum_history [i], minimum) ; } if (excessive_err_nodes == 0) /* SHOOTING has converged */ @@ -878,10 +884,12 @@ DCpss(CKTcircuit *ckt, pss_points_cycle++ ; CKTsetBreak (ckt, time_temp + (1 / ckt->CKTguessedFreq) * ((double)pss_points_cycle / (double)ckt->CKTpsspoints)) ; + freq = eng(ckt->CKTguessedFreq, 10, TRUE, FALSE); /* engineering notation */ if (excessive_err_nodes == 0) - fprintf (stderr, "\nConvergence reached. Final circuit time is %1.10g seconds (iteration n° %d) and predicted fundamental frequency is %15.10g Hz\n", ckt->CKTtime, shooting_cycle_counter - 1, ckt->CKTguessedFreq) ; + fprintf (stdout, "\nConvergence reached. Final circuit time is %1.10g seconds (iteration n° %d) and predicted fundamental frequency is %s Hz\n", ckt->CKTtime, shooting_cycle_counter - 1, freq) ; else - fprintf (stderr, "\nConvergence not reached. However the most near convergence iteration has predicted (iteration %d) a fundamental frequency of %15.10g Hz\n", k, ckt->CKTguessedFreq) ; + fprintf (stdout, "\nConvergence not reached. However the most near convergence iteration has predicted (iteration %d) a fundamental frequency of %s Hz\n", k, freq) ; + tfree(freq); #ifdef PSSDEBUG fprintf (stderr, "time_temp %g\n", time_temp) ; @@ -967,10 +975,6 @@ DCpss(CKTcircuit *ckt, /* Terminates plot in Frequency Domain and frees the allocated memory */ SPfrontEnd->OUTendPlot (job->PSSplot_fd) ; - - - /* Francesco Lannutti's MOD */ - /* Verify the frequency found */ max_freq = pssResults [msize] ; /* max_freq = pssResults [1 * msize + 0] ; */ position = 1 ; @@ -989,8 +993,8 @@ DCpss(CKTcircuit *ckt, if (pssfreqs [position] != ckt->CKTguessedFreq) { ckt->CKTguessedFreq = pssfreqs [position] ; - fprintf (stderr, "The predicted fundamental frequency is incorrect.\nRelaunching the analysis...\n\n") ; - fprintf (stderr, "The new guessed fundamental frequency is: %.6g\n\n", ckt->CKTguessedFreq) ; + fprintf (stdout, "The predicted fundamental frequency is incorrect.\nRelaunching the analysis...\n\n") ; + fprintf (stdout, "The new guessed fundamental frequency is: %.6g\n\n", ckt->CKTguessedFreq) ; DCpss (ckt, 1) ; } /****************************/ @@ -1043,11 +1047,11 @@ resume: #endif #ifdef HAS_PROGREP if (ckt->CKTtime == 0.) - SetAnalyse( "tran init", 0); + SetAnalyse( "ptran init", 0); else if ((pss_state != PSS) && (shooting_cycle_counter > 0)) SetAnalyse("shooting", shooting_cycle_counter) ; else - SetAnalyse( "tran", (int)((ckt->CKTtime * 1000.) / ckt->CKTfinalTime)); + SetAnalyse( "ptran", (int)((ckt->CKTtime * 1000.) / ckt->CKTfinalTime)); #endif ckt->CKTdelta = MIN(ckt->CKTdelta,ckt->CKTmaxStep); @@ -1110,9 +1114,18 @@ resume: ckt->CKTsaveDelta = ckt->CKTdelta; ckt->CKTdelta = ckt->CKTbreaks[0] - ckt->CKTtime; /* fprintf (stderr, "delta cut to %g to hit breakpoint\n" ,ckt->CKTdelta) ; */ - fflush(stdout); + fflush(stderr); ckt->CKTbreak = 1; /* why? the current pt. is not a bkpt. */ } + /* Try to equalise the last two time steps before the breakpoint, + if the second step would be smaller than CKTdelta otherwise.*/ + else if (ckt->CKTtime + 1.9 * ckt->CKTdelta > ckt->CKTbreaks[0]) { + ckt->CKTsaveDelta = ckt->CKTdelta; + ckt->CKTdelta = (ckt->CKTbreaks[0] - ckt->CKTtime) / 2.; +#ifdef STEPDEBUG + fprintf(stdout, "Delta equalising step at time %e with delta %e\n", ckt->CKTtime, ckt->CKTdelta); +#endif + } #endif /* !XSPICE */ @@ -1152,6 +1165,15 @@ resume: ckt->CKTsaveDelta = ckt->CKTdelta; ckt->CKTdelta = ckt->CKTbreaks[0] - ckt->CKTtime; } + /* Try to equalise the last two time steps before the breakpoint, + if the second step would be smaller than CKTdelta otherwise.*/ + else if (ckt->CKTtime + 1.9 * ckt->CKTdelta > ckt->CKTbreaks[0]) { + ckt->CKTsaveDelta = ckt->CKTdelta; + ckt->CKTdelta = (ckt->CKTbreaks[0] - ckt->CKTtime) / 2.; + #ifdef STEPDEBUG + fprintf(stdout, "Delta equalising step at time %e with delta %e\n", ckt->CKTtime, ckt->CKTdelta); + #endif + } /* gtri - end - wbk - Modify Breakpoint stuff */ @@ -1294,7 +1316,7 @@ resume: ckt->CKTdelta = ckt->CKTdelta/8; #ifdef STEPDEBUG fprintf (stderr, "delta cut to %g for non-convergence\n", ckt->CKTdelta) ; - fflush(stdout); + fflush(stderr); #endif if(firsttime) { ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN; @@ -1347,22 +1369,10 @@ resume: } /* time point OK - 630 */ ckt->CKTdelta = newdelta; -#ifdef NDEV - if (!ft_norefprint) { - /* show a time process indicator, by Gong Ding, gdiso@ustc.edu */ - if (ckt->CKTtime / ckt->CKTfinalTime * 100 < 10.0) - fprintf(stderr, "%%%3.2lf\b\b\b\b\b", ckt->CKTtime / ckt->CKTfinalTime * 100); - else if (ckt->CKTtime / ckt->CKTfinalTime * 100 < 100.0) - fprintf(stderr, "%%%4.2lf\b\b\b\b\b\b", ckt->CKTtime / ckt->CKTfinalTime * 100); - else - fprintf(stderr, "%%%5.2lf\b\b\b\b\b\b\b", ckt->CKTtime / ckt->CKTfinalTime * 100); - fflush(stdout); - } -#endif #ifdef STEPDEBUG fprintf (stderr, "delta set to truncation error result: %g. Point accepted at CKTtime: %g\n", ckt->CKTdelta, ckt->CKTtime) ; - fflush(stdout); + fflush(stderr); #endif diff --git a/src/spicelib/analysis/noisean.c b/src/spicelib/analysis/noisean.c index 29f880452..07158d659 100644 --- a/src/spicelib/analysis/noisean.c +++ b/src/spicelib/analysis/noisean.c @@ -203,7 +203,7 @@ NOISEan(CKTcircuit* ckt, int restart) ckt->CKTdcMaxIter); if (error) { - fprintf(stdout, "\nNOISE operating point failed -\n"); + fprintf(stderr, "\nError: NOISE operating point failed -\n"); CKTncDump(ckt); return(error); } @@ -336,8 +336,9 @@ NOISEan(CKTcircuit* ckt, int restart) g_mif_info.circuit.anal_init = MIF_TRUE; - /* Tell the code models what mode we're in */ - /* MIF_NOI is not yet supported by code models, so use their AC capabilities */ + /* MIFnoise handles noise analysis for code models via DEVnoise. + * Set anal_type to MIF_AC so that MIFload generates correct AC matrix + * entries needed for gain computation during noise analysis. */ g_mif_info.circuit.anal_type = MIF_AC; /* gtri - end - wbk */ diff --git a/src/spicelib/devices/isrc/isrc.c b/src/spicelib/devices/isrc/isrc.c index 8272357f8..331937717 100644 --- a/src/spicelib/devices/isrc/isrc.c +++ b/src/spicelib/devices/isrc/isrc.c @@ -39,6 +39,8 @@ IFparm ISRCpTable[] = { /* parameters */ OP ("v", ISRC_VOLTS, IF_REAL, "Voltage across the supply"), OP ("p", ISRC_POWER, IF_REAL, "Power supplied by the source"), OP ("current", ISRC_CURRENT, IF_REAL, "Current in DC or Transient mode"), + IP ("r", ISRC_R, IF_REAL, "pwl repeat value"), + IP ("td", ISRC_TD, IF_REAL, "pwl delay value"), IP ("distof1", ISRC_D_F1, IF_REALVEC,"f1 input for distortion"), IP ("distof2", ISRC_D_F2, IF_REALVEC,"f2 input for distortion") }; diff --git a/src/spicelib/devices/isrc/isrcacct.c b/src/spicelib/devices/isrc/isrcacct.c index abf232f01..94ae4ba6e 100644 --- a/src/spicelib/devices/isrc/isrcacct.c +++ b/src/spicelib/devices/isrc/isrcacct.c @@ -183,6 +183,60 @@ ISRCaccept(CKTcircuit *ckt, GENmodel *inModel) } break; + case PWL: { + if (ckt->CKTtime >= here->ISRCbreak_time) { + double time, atime, end, period; + int i; + + time = ckt->CKTtime - here->ISRCrdelay; + end = + here->ISRCcoeffs[here->ISRCfunctionOrder - 2]; + if (time > end) { + if (here->ISRCrGiven) { + /* Repeating. */ + + period = end - + here->ISRCcoeffs[here->ISRCrBreakpt]; + time -= + here->ISRCcoeffs[here->ISRCrBreakpt]; + time -= period * floor(time / period); + time += + here->ISRCcoeffs[here->ISRCrBreakpt]; + } + else { + here->ISRCbreak_time = ckt->CKTfinalTime; + break; + } + } + + /* A request for a breakpoint very close + * to the current time will be ignored. + * Adjust so the next corner will be + * selected. + */ + + atime = time + ckt->CKTminBreak; + + for (i = 0; + i < here->ISRCfunctionOrder; + i += 2) { + if (here->ISRCcoeffs[i] > atime) { + here->ISRCbreak_time = + ckt->CKTtime + + here->ISRCcoeffs[i] - time; + error = CKTsetBreak(ckt, + here->ISRCbreak_time); + if (error) + return error; + here->ISRCbreak_time -= ckt->CKTminBreak; + break; + } + } + } + break; + } + +/* case PWL: { int i; if(ckt->CKTtime < *(here->ISRCcoeffs)) { @@ -200,7 +254,7 @@ ISRCaccept(CKTcircuit *ckt, GENmodel *inModel) } break; } - +*/ /**** tansient noise routines: INoi2 2 0 DC 0 TRNOISE(10n 0.5n 0 0n) : generate gaussian distributed noise rms value, time step, 0 0 @@ -320,7 +374,7 @@ ISRCaccept(CKTcircuit *ckt, GENmodel *inModel) } // switch } // if ... else -bkptset: ; +// bkptset: ; } // for } // for diff --git a/src/spicelib/devices/isrc/isrcdefs.h b/src/spicelib/devices/isrc/isrcdefs.h index d6f474fec..c599022c8 100644 --- a/src/spicelib/devices/isrc/isrcdefs.h +++ b/src/spicelib/devices/isrc/isrcdefs.h @@ -32,6 +32,8 @@ typedef struct sISRCinstance { int ISRCfunctionType; /* code number of function type for source */ int ISRCfunctionOrder; /* order of the function for the source */ + int ISRCrBreakpt; /* pwl repeat breakpoint index */ + double ISRCbreak_time; /* time of most-recent breakpoint */ double *ISRCcoeffs; /* pointer to array of coefficients */ double ISRCdcValue; /* DC and TRANSIENT value of source */ @@ -51,6 +53,9 @@ typedef struct sISRCinstance { struct trnoise_state *ISRCtrnoise_state; /* transient noise */ struct trrandom_state *ISRCtrrandom_state; /* transient random source */ + double ISRCr; /* pwl repeat */ + double ISRCrdelay; /* pwl delay period */ + /* needed for outputting results */ double ISRCcurrent; /* current value */ @@ -64,6 +69,7 @@ typedef struct sISRCinstance { unsigned ISRCdGiven :1 ; /* flag to indicate source is a distortion input */ unsigned ISRCdF1given :1 ; /* flag to indicate source is an f1 distortion input */ unsigned ISRCdF2given :1 ; /* flag to indicate source is an f2 distortion input */ + unsigned ISRCrGiven : 1; /* flag to indicate repeating pwl */ } ISRCinstance ; @@ -123,11 +129,10 @@ enum { ISRC_D_F2, ISRC_VOLTS, ISRC_AM, + ISRC_R, + ISRC_TD, ISRC_CURRENT, -}; - -enum { - ISRC_TRNOISE = 25, + ISRC_TRNOISE, ISRC_TRRANDOM, ISRC_EXTERNAL, }; diff --git a/src/spicelib/devices/isrc/isrcload.c b/src/spicelib/devices/isrc/isrcload.c index 5e02b0372..13a19f68e 100644 --- a/src/spicelib/devices/isrc/isrcload.c +++ b/src/spicelib/devices/isrc/isrcload.c @@ -31,7 +31,7 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) { ISRCmodel *model = (ISRCmodel *) inModel; ISRCinstance *here; - double value; + double value = 0.0; double time; double m; @@ -225,11 +225,11 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) && here->ISRCcoeffs[4] ? here->ISRCcoeffs[4] : (500./ckt->CKTfinalTime); TD = here->ISRCfunctionOrder > 5 - ? here->ISRCcoeffs[5] : 0; - PHASEM = here->ISRCfunctionOrder > 5 ? here->ISRCcoeffs[5] : 0.0; - PHASEC = here->ISRCfunctionOrder > 6 + PHASEM = here->ISRCfunctionOrder > 6 ? here->ISRCcoeffs[6] : 0.0; + PHASEC = here->ISRCfunctionOrder > 7 + ? here->ISRCcoeffs[7] : 0.0; /* limit the modulation index */ if (MDI > FC / FM) { @@ -251,10 +251,16 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) phasec = PHASEC * M_PI / 180.0; phasem = PHASEM * M_PI / 180.0; - /* compute waveform value */ - value = VO + VA * - sin((2.0 * M_PI * FC * time + phasec) + - MDI * sin(2.0 * M_PI * FM * time + phasem)); + time -= TD; + if (time <= 0) { + value = 0; + } + else { + /* compute waveform value */ + value = VO + VA * + sin((2.0 * M_PI * FC * time + phasec) + + MDI * sin(2.0 * M_PI * FM * time + phasem)); + } } break; @@ -294,6 +300,51 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) break; case PWL: { + int i; + double end_time, itime; + + time -= here->ISRCrdelay; + if (time <= here->ISRCcoeffs[0]) { + value = here->ISRCcoeffs[1]; + break; + } + + end_time = + here->ISRCcoeffs[here->ISRCfunctionOrder - 2]; + if (time > end_time) { + double period; + + if (here->ISRCrGiven) { + /* Repeating. */ + + period = end_time - + here->ISRCcoeffs[here->ISRCrBreakpt]; + time -= here->ISRCcoeffs[here->ISRCrBreakpt]; + time -= period * floor(time / period); + time += here->ISRCcoeffs[here->ISRCrBreakpt]; + } + else { + value = + here->ISRCcoeffs[here->ISRCfunctionOrder - 1]; + break; + } + } + + for (i = 2; i < here->ISRCfunctionOrder; i += 2) { + itime = here->ISRCcoeffs[i]; + if (itime >= time) { + time -= here->ISRCcoeffs[i - 2]; + time /= here->ISRCcoeffs[i] - + here->ISRCcoeffs[i - 2]; + value = here->ISRCcoeffs[i - 1]; + value += time * + (here->ISRCcoeffs[i + 1] - + here->ISRCcoeffs[i - 1]); + break; + } + } + break; +/* int i; if(time < *(here->ISRCcoeffs)) { value = *(here->ISRCcoeffs + 1) ; @@ -317,6 +368,7 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) } value = *(here->ISRCcoeffs+ here->ISRCfunctionOrder-1) ; break; + */ } /**** tansient noise routines: @@ -390,7 +442,7 @@ INoi1 1 0 DC 0 TRNOISE(0n 0.5n 1 10n) : generate 1/f noise } // switch } // else (line 48) -loadDone: +// loadDone: /* gtri - begin - wbk - modify for supply ramping option */ #ifdef XSPICE_EXP diff --git a/src/spicelib/devices/isrc/isrcpar.c b/src/spicelib/devices/isrc/isrcpar.c index f66b3bd03..7f0fc3e84 100644 --- a/src/spicelib/devices/isrc/isrcpar.c +++ b/src/spicelib/devices/isrc/isrcpar.c @@ -123,6 +123,50 @@ ISRCparam(int param, IFvalue *value, GENinstance *inst, IFvalue *select) break; + case ISRC_TD: + here->ISRCrdelay = value->rValue; + break; + + case ISRC_R: { + double end_time; + /* Parameter r of pwl may now be parameterized: + if r == -1, no repetition done. + if r == 0, repeat forever. + if r == xx, repeat from time xx to last time point given. */ + if (value->rValue < -0.5) { + here->ISRCrGiven = FALSE; + break; + } + + /* buggy input? r is not a repetition coefficient */ + if (!here->ISRCcoeffs || here->ISRCfunctionOrder < 2) { + here->ISRCrGiven = FALSE; + break; + } + + here->ISRCr = value->rValue; + here->ISRCrGiven = TRUE; + + for (i = 0; i < here->ISRCfunctionOrder; i += 2) { + here->ISRCrBreakpt = i; + if (here->ISRCr == *(here->ISRCcoeffs + i)) + break; + } + + end_time = *(here->ISRCcoeffs + here->ISRCfunctionOrder - 2); + if (here->ISRCr >= end_time) { + fprintf(stderr, "ERROR: repeat start time value %g for pwl voltage source must be smaller than final time point given!\n", here->ISRCr); + return (E_PARMVAL); + } + + if (here->ISRCr != *(here->ISRCcoeffs + here->ISRCrBreakpt)) { + fprintf(stderr, "ERROR: repeat start time value %g for pwl voltage source does not match any time point given!\n", here->ISRCr); + return (E_PARMVAL); + } + + break; + } + case ISRC_SFFM: if(value->v.numValue < 2) return(E_BADPARM); diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index 5bb468994..e2191bf9d 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -254,7 +254,7 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) && here->VSRCcoeffs[4] /* test if not 0 */ ? here->VSRCcoeffs[4] : (500./ckt->CKTfinalTime); TD = here->VSRCfunctionOrder > 5 - ? here->VSRCcoeffs[5] : 0; + ? here->VSRCcoeffs[5] : 0.0; PHASEM = here->VSRCfunctionOrder > 6 ? here->VSRCcoeffs[6] : 0.0; PHASEC = here->VSRCfunctionOrder > 7 diff --git a/src/spicelib/devices/vsrc/vsrcpar.c b/src/spicelib/devices/vsrc/vsrcpar.c index 27bed7c4f..a111c9bdc 100644 --- a/src/spicelib/devices/vsrc/vsrcpar.c +++ b/src/spicelib/devices/vsrc/vsrcpar.c @@ -142,8 +142,9 @@ VSRCparam(int param, IFvalue *value, GENinstance *inst, IFvalue *select) here->VSRCrGiven = TRUE; for ( i = 0; i < here->VSRCfunctionOrder; i += 2 ) { - here->VSRCrBreakpt = i; - if ( here->VSRCr == *(here->VSRCcoeffs+i) ) break; + here->VSRCrBreakpt = i; + if ( here->VSRCr == *(here->VSRCcoeffs+i) ) + break; } end_time = *(here->VSRCcoeffs + here->VSRCfunctionOrder-2); diff --git a/src/spicelib/parser/inp2n.c b/src/spicelib/parser/inp2n.c index b01d8b44c..2593c7465 100644 --- a/src/spicelib/parser/inp2n.c +++ b/src/spicelib/parser/inp2n.c @@ -30,7 +30,7 @@ void INP2N(CKTcircuit *ckt, INPtables *tab, struct card *current) { GENinstance *fast; /* Pointer to the actual instance. */ int waslead; /* Funny unlabeled number was found. */ double leadval; /* Value of unlabeled number. */ - INPmodel *thismodel; /* Pointer to model description for user's model. */ + INPmodel *thismodel = NULL; /* Pointer to model description for user's model. */ GENmodel *mdfast; /* Pointer to the actual model. */ IFdevice *dev; CKTnode *node; diff --git a/src/tclspice.c b/src/tclspice.c index 7e8e47b92..b98668a05 100644 --- a/src/tclspice.c +++ b/src/tclspice.c @@ -2525,10 +2525,15 @@ Spice_Init(Tcl_Interp *interp) struct passwd *pw; pw = getpwuid(getuid()); - s = tprintf("%s" DIR_PATHSEP "%s", pw->pw_dir, INITSTR); + if (pw) { + s = tprintf("%s" DIR_PATHSEP "%s", pw->pw_dir, INITSTR); - if (access(s, 0) == 0) - inp_source(s); + if (access(s, 0) == 0) { + inp_source(s); + } + + tfree(s); + } } #else /* ~ HAVE_PWD_H */ { diff --git a/src/xspice/cm/cm.c b/src/xspice/cm/cm.c index ce4fdcdc7..a05320066 100644 --- a/src/xspice/cm/cm.c +++ b/src/xspice/cm/cm.c @@ -926,3 +926,85 @@ bool cm_probe_node(unsigned int conn_index, // Connection index this->output_value[edata->output_subindex] = hold; return TRUE; } + + +/* +cm_noise_add_source + +Register a programmatic noise source during noise analysis. + +During N_OPEN (registering=TRUE): validates parameters, stores source info +in the noise data registration arrays, and returns a sequential index. + +During N_CALC (registering=FALSE): returns the same sequential index +without storing anything. The code model uses this index to set +NOISE_DENSITY(index). +*/ + +int +cm_noise_add_source(const char *name, int conn_index, int port_index, + Mif_Noise_Src_Type_t type) +{ + Mif_Noise_Data_t *nd; + int idx; + + nd = g_mif_noise_cm_data.noise; + + if (!nd || !name) + return -1; + + if (type != MIF_NOISE_CURRENT && type != MIF_NOISE_VOLTAGE && + type != MIF_NOISE_CURRENT_POS && type != MIF_NOISE_CURRENT_NEG) + return -1; + + idx = nd->next_index++; + + if (!nd->registering) + return idx; + + /* Registering mode: validate conn/port. + * Always store the source to keep indices aligned between N_OPEN and N_CALC. + * Invalid sources get conn_index = -1 so MIFnoise skips them during evaluation. */ + { + MIFinstance *inst = g_mif_info.instance; + Mif_Boolean_t valid = MIF_TRUE; + + if (conn_index < 0 || conn_index >= inst->num_conn || + inst->conn[conn_index]->is_null || + port_index < 0 || port_index >= inst->conn[conn_index]->size) { + + fprintf(stderr, "cm_noise_add_source: invalid conn %d port %d for '%s'\n", + conn_index, port_index, name); + valid = MIF_FALSE; + } + + if (!valid) + conn_index = -1; + } + + /* Grow arrays if needed */ + if (nd->num_prog_srcs >= nd->max_prog_srcs) { + int new_max = (nd->max_prog_srcs == 0) ? 8 : nd->max_prog_srcs * 2; + Mif_Noise_Src_Type_t *t = TREALLOC(Mif_Noise_Src_Type_t, nd->prog_types, new_max); + int *c = TREALLOC(int, nd->prog_conn, new_max); + int *p = TREALLOC(int, nd->prog_port, new_max); + char **n = TREALLOC(char *, nd->prog_names, new_max); + + if (!t || !c || !p || !n) + return -1; + + nd->prog_types = t; + nd->prog_conn = c; + nd->prog_port = p; + nd->prog_names = n; + nd->max_prog_srcs = new_max; + } + + nd->prog_types[nd->num_prog_srcs] = type; + nd->prog_conn[nd->num_prog_srcs] = conn_index; + nd->prog_port[nd->num_prog_srcs] = port_index; + nd->prog_names[nd->num_prog_srcs] = tprintf("_%s", name); + nd->num_prog_srcs++; + + return idx; +} diff --git a/src/xspice/cm/cmexport.c b/src/xspice/cm/cmexport.c index 56cb974eb..a306e37e3 100644 --- a/src/xspice/cm/cmexport.c +++ b/src/xspice/cm/cmexport.c @@ -98,4 +98,7 @@ struct coreInfo_t coreInfo = MIFbindCSCComplex, MIFbindCSCComplexToReal #endif + , + MIFnoise, + cm_noise_add_source }; diff --git a/src/xspice/cmpp/mod_lex.l b/src/xspice/cmpp/mod_lex.l index fbb86eb5b..14b1f9043 100644 --- a/src/xspice/cmpp/mod_lex.l +++ b/src/xspice/cmpp/mod_lex.l @@ -124,6 +124,8 @@ OUTPUT_STATE {return TOK_OUTPUT_STATE;} OUTPUT_STRENGTH {return TOK_OUTPUT_STRENGTH;} OUTPUT_TYPE {return TOK_OUTPUT_TYPE;} OUTPUT_CHANGED {return TOK_OUTPUT_CHANGED;} +NOISE_DENSITY {return TOK_NOISE_DENSITY;} +NOISE_FREQ {return TOK_NOISE_FREQ;} "(" {return TOK_LPAREN;} ")" {return TOK_RPAREN;} diff --git a/src/xspice/cmpp/mod_yacc.y b/src/xspice/cmpp/mod_yacc.y index 92433aca4..d7bb6fd5b 100644 --- a/src/xspice/cmpp/mod_yacc.y +++ b/src/xspice/cmpp/mod_yacc.y @@ -369,6 +369,8 @@ static void append (char *str) %token TOK_TOTAL_LOAD %token TOK_MESSAGE %token TOK_CALL_TYPE +%token TOK_NOISE_DENSITY +%token TOK_NOISE_FREQ %start mod_file @@ -589,9 +591,14 @@ macro : TOK_INIT subscript($3));} | TOK_MESSAGE TOK_LPAREN subscriptable_id TOK_RPAREN {int i = valid_subid ($3, CONN); - fprintf (mod_yyout, + fprintf (mod_yyout, "mif_private->conn[%d]->port[%s]->msg", i, subscript($3));} + | TOK_NOISE_DENSITY TOK_LPAREN buffered_c_code TOK_RPAREN + {fprintf (mod_yyout, + "mif_private->noise->density[%s]", $3);} + | TOK_NOISE_FREQ + {fprintf (mod_yyout, "mif_private->noise->freq");} ; subscriptable_id : id diff --git a/src/xspice/cmpp/writ_ifs.c b/src/xspice/cmpp/writ_ifs.c index 32d1c18ac..b6e5df038 100644 --- a/src/xspice/cmpp/writ_ifs.c +++ b/src/xspice/cmpp/writ_ifs.c @@ -1106,7 +1106,7 @@ static int write_SPICEdev( " .DEVsenPrint = NULL,\n" " .DEVsenTrunc = NULL,\n" " .DEVdisto = NULL,\n" - " .DEVnoise = NULL,\n" + " .DEVnoise = MIFnoise,\n" " .DEVsoaCheck = NULL,\n" " .DEVinstSize = &val_sizeofMIFinstance,\n" " .DEVmodSize = &val_sizeofMIFmodel,\n" diff --git a/src/xspice/evt/evtprint.c b/src/xspice/evt/evtprint.c index 3e8b909f5..c5c8c04e2 100644 --- a/src/xspice/evt/evtprint.c +++ b/src/xspice/evt/evtprint.c @@ -594,7 +594,7 @@ EVTprintvcd(wordlist *wl) char *node_value[EPRINT_MAXARGS]; char *old_node_value[EPRINT_MAXARGS]; char node_ident[EPRINT_MAXARGS + 1]; - char vbuf[24][2][EPRINT_MAXARGS]; // Analog value strings + char vbuf[EPRINT_MAXARGS][2][24]; // Analog value strings CKTcircuit *ckt; @@ -607,6 +607,8 @@ EVTprintvcd(wordlist *wl) char *value; + memset(vbuf, 0, sizeof(vbuf)); + /* Check for the "-a" option (output analog values at timesteps) * and "-t nn": specifies the VCD timestep with range 1fs to 1s */ @@ -804,10 +806,10 @@ EVTprintvcd(wordlist *wl) if (ctx.node_vector[i]) { /* Analog node or expression. */ - sprintf(vbuf[0][i], "%.16g", get_real(i, 0.0, &ctx)); - node_value[i] = vbuf[0][i]; - old_node_value[i] = vbuf[1][i]; - strcpy(vbuf[1][i], vbuf[0][i]); + sprintf(vbuf[i][0], "%.16g", get_real(i, 0.0, &ctx)); + node_value[i] = vbuf[i][0]; + old_node_value[i] = vbuf[i][1]; + strcpy(vbuf[i][1], vbuf[i][0]); } else { /* This must return a pointer to a statically-allocated string. */ diff --git a/src/xspice/icm/analog/modpath.lst b/src/xspice/icm/analog/modpath.lst index 1c6f1595d..c67e517f5 100644 --- a/src/xspice/icm/analog/modpath.lst +++ b/src/xspice/icm/analog/modpath.lst @@ -21,3 +21,4 @@ file_source delay pwlts astate +ota diff --git a/src/xspice/icm/analog/ota/cfunc.mod b/src/xspice/icm/analog/ota/cfunc.mod new file mode 100644 index 000000000..e82ee0e16 --- /dev/null +++ b/src/xspice/icm/analog/ota/cfunc.mod @@ -0,0 +1,110 @@ +/* +================================================================================ + +FILE ota/cfunc.mod + +Public Domain + +AUTHORS + + 20 Mar 2026 Seth Hillbrand + +SUMMARY + + This file contains the model-specific routines for the OTA + (Operational Transconductance Amplifier) code model. + + DC/TRAN: Vout as current = gm * (Vp - Vn) + AC: Complex gain = gm + NOISE: Programmatic noise with en, in_noise, enk, ink, incm, incmk. + + Parameter naming follows LTSPICE convention for OTA compatibility. + +================================================================================ +*/ + +#include + + +void cm_ota(ARGS) +{ + double gm_val = PARAM(gm); + double rout = PARAM(rout); + double rin = PARAM(rin); + + Mif_Complex_t ac_gain; + + if (ANALYSIS == NOISE) { + /* Register noise sources on every call (N_OPEN and N_CALC). + * During N_OPEN: registers and returns index. + * During N_CALC: returns same sequential index. */ + int src_en_w = cm_noise_add_source("en_white", 1, 0, MIF_NOISE_CURRENT); + int src_en_f = cm_noise_add_source("en_flicker", 1, 0, MIF_NOISE_CURRENT); + int src_in_w = cm_noise_add_source("in_white", 0, 0, MIF_NOISE_CURRENT); + int src_in_f = cm_noise_add_source("in_flicker", 0, 0, MIF_NOISE_CURRENT); + + /* Common-mode current noise: independent sources from each input pin to ground */ + int src_icm_pw = cm_noise_add_source("incm_p_white", 0, 0, MIF_NOISE_CURRENT_POS); + int src_icm_pf = cm_noise_add_source("incm_p_flicker", 0, 0, MIF_NOISE_CURRENT_POS); + int src_icm_nw = cm_noise_add_source("incm_n_white", 0, 0, MIF_NOISE_CURRENT_NEG); + int src_icm_nf = cm_noise_add_source("incm_n_flicker", 0, 0, MIF_NOISE_CURRENT_NEG); + + if (!mif_private->noise->registering) { + double en = PARAM(en); + double in_n = PARAM(in_noise); + double enk_val = PARAM(enk); + double ink_val = PARAM(ink); + double incm = PARAM(incm); + double incmk_val = PARAM(incmk); + double f = NOISE_FREQ; + + /* en referred to output as current noise: (en * gm)^2 A^2/Hz */ + NOISE_DENSITY(src_en_w) = en * en * gm_val * gm_val; + NOISE_DENSITY(src_en_f) = (enk_val > 0 && f > 0) ? + en * en * gm_val * gm_val * enk_val / f : 0.0; + + /* in as differential current noise at input: in^2 A^2/Hz */ + NOISE_DENSITY(src_in_w) = in_n * in_n; + NOISE_DENSITY(src_in_f) = (ink_val > 0 && f > 0) ? + in_n * in_n * ink_val / f : 0.0; + + /* incm as current noise from each input pin to ground */ + NOISE_DENSITY(src_icm_pw) = incm * incm; + NOISE_DENSITY(src_icm_pf) = (incmk_val > 0 && f > 0) ? + incm * incm * incmk_val / f : 0.0; + NOISE_DENSITY(src_icm_nw) = incm * incm; + NOISE_DENSITY(src_icm_nf) = (incmk_val > 0 && f > 0) ? + incm * incm * incmk_val / f : 0.0; + } + + return; + } + + if (ANALYSIS != MIF_AC) { + double v_in = INPUT(inp); + double v_out = INPUT(out); + + OUTPUT(out) = gm_val * v_in + v_out / rout; + PARTIAL(out, inp) = gm_val; + PARTIAL(out, out) = 1.0 / rout; + + OUTPUT(inp) = v_in / rin; + PARTIAL(inp, inp) = 1.0 / rin; + } + else { + Mif_Complex_t ac_rout; + Mif_Complex_t ac_rin; + + ac_gain.real = gm_val; + ac_gain.imag = 0.0; + AC_GAIN(out, inp) = ac_gain; + + ac_rout.real = 1.0 / rout; + ac_rout.imag = 0.0; + AC_GAIN(out, out) = ac_rout; + + ac_rin.real = 1.0 / rin; + ac_rin.imag = 0.0; + AC_GAIN(inp, inp) = ac_rin; + } +} diff --git a/src/xspice/icm/analog/ota/ifspec.ifs b/src/xspice/icm/analog/ota/ifspec.ifs new file mode 100644 index 000000000..0a6721a09 --- /dev/null +++ b/src/xspice/icm/analog/ota/ifspec.ifs @@ -0,0 +1,114 @@ +/* +================================================================================ +Public Domain + +SUMMARY + + Interface specification for the OTA (Operational Transconductance Amplifier) + code model. LTSPICE-compatible parameter naming convention. + + Noise is implemented via the programmatic API (cm_noise_add_source / + NOISE_DENSITY). Set noise_programmatic = TRUE to enable noise analysis. + +================================================================================ +*/ + +NAME_TABLE: + + +C_Function_Name: cm_ota +Spice_Model_Name: ota +Description: "Operational Transconductance Amplifier with noise" + + +PORT_TABLE: + + +Port_Name: inp out +Description: "input" "output" +Direction: inout inout +Default_Type: gd g +Allowed_Types: [gd] [g] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + + +PARAMETER_TABLE: + + +Parameter_Name: gm rout +Description: "transconductance" "output resistance" +Data_Type: real real +Default_Value: 1.0e-3 1.0e12 +Limits: [1e-15 -] [0 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +PARAMETER_TABLE: + + +Parameter_Name: rin +Description: "input resistance" +Data_Type: real +Default_Value: 1.0e12 +Limits: [0 -] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: + + +Parameter_Name: en in_noise +Description: "input voltage noise density V/rtHz" "input current noise density A/rtHz" +Data_Type: real real +Default_Value: 0.0 0.0 +Limits: [0 -] [0 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +PARAMETER_TABLE: + + +Parameter_Name: enk ink +Description: "voltage noise 1/f corner Hz" "current noise 1/f corner Hz" +Data_Type: real real +Default_Value: 0.0 0.0 +Limits: [0 -] [0 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +PARAMETER_TABLE: + + +Parameter_Name: incm incmk +Description: "CM current noise density A/rtHz" "CM current noise 1/f corner Hz" +Data_Type: real real +Default_Value: 0.0 0.0 +Limits: [0 -] [0 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +PARAMETER_TABLE: + + +Parameter_Name: noise_programmatic +Description: "enable programmatic noise sources" +Data_Type: boolean +Default_Value: TRUE +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/icm/dlmain.c b/src/xspice/icm/dlmain.c index dae5c7993..8d2099598 100644 --- a/src/xspice/icm/dlmain.c +++ b/src/xspice/icm/dlmain.c @@ -18,6 +18,7 @@ #include "ngspice/devdefs.h" #include "ngspice/dstring.h" #include "ngspice/dllitf.h" +#include "ngspice/noisedef.h" #include "ngspice/evtudn.h" #include "ngspice/inpdefs.h" #include "ngspice/inertial.h" @@ -157,10 +158,21 @@ int MIFload( } +int MIFnoise( + int mode, + int operation, + GENmodel *inModel, + CKTcircuit *ckt, + Ndata *data, + double *OnDens + ) { + return (coreitf->dllitf_MIFnoise)(mode,operation,inModel,ckt,data,OnDens); +} + int MIFmParam( int param_index, IFvalue *value, - GENmodel *inModel + GENmodel *inModel ) { return (coreitf->dllitf_MIFmParam)(param_index,value,inModel); } @@ -444,6 +456,10 @@ void cm_cexit(const int exitcode) { (coreitf->dllitf_cexit)(exitcode); } +int cm_noise_add_source(const char *name, int conn_index, int port_index, Mif_Noise_Src_Type_t type) { + return (coreitf->dllitf_cm_noise_add_source)(name, conn_index, port_index, type); +} + #ifdef KLU int MIFbindCSC (GENmodel *inModel, CKTcircuit *ckt) { return (coreitf->dllitf_MIFbindCSC) (inModel, ckt) ; diff --git a/src/xspice/mif/Makefile.am b/src/xspice/mif/Makefile.am index 6fccc76a1..d1dbd3c3d 100644 --- a/src/xspice/mif/Makefile.am +++ b/src/xspice/mif/Makefile.am @@ -19,6 +19,7 @@ libmifxsp_la_SOURCES = \ mifdelete.c \ mifmdelete.c \ mifdestr.c \ + mifnoise.c \ mif.c if KLU_WANTED diff --git a/src/xspice/mif/mif.c b/src/xspice/mif/mif.c index 6dac61cab..77bc7a6f4 100644 --- a/src/xspice/mif/mif.c +++ b/src/xspice/mif/mif.c @@ -57,3 +57,7 @@ Mif_Info_t g_mif_info = { { 0.0, 0.0,}, { MIF_FALSE, MIF_FALSE,}, }; + +/* Mif_Private_t used by MIFnoise to call cm_func during noise analysis. + * Also accessed by cm_noise_add_source() in cm.c. */ +Mif_Private_t g_mif_noise_cm_data; diff --git a/src/xspice/mif/mifdelete.c b/src/xspice/mif/mifdelete.c index 60230b6ff..e91f464bf 100644 --- a/src/xspice/mif/mifdelete.c +++ b/src/xspice/mif/mifdelete.c @@ -194,5 +194,7 @@ MIFdelete(GENinstance *gen_inst) if (here->num_conv && here->conv) FREE(here->conv); + MIF_free_noise_state(here); + return OK; } diff --git a/src/xspice/mif/mifnoise.c b/src/xspice/mif/mifnoise.c new file mode 100644 index 000000000..65edfbe8e --- /dev/null +++ b/src/xspice/mif/mifnoise.c @@ -0,0 +1,741 @@ +/*============================================================================ +FILE MIFnoise.c + +MEMBER OF process XSPICE + +Public Domain + +AUTHORS + + 20 Mar 2026 Seth Hillbrand + +SUMMARY + + This file contains the generic noise callback for all XSPICE code models. + It supports two noise source discovery mechanisms: + + Declarative: Models that define reserved parameter names (noise_voltage, + noise_current, noise_corner, noise_exponent) get automatic noise sources + bound to their first output or input port. + + Programmatic: Models that set noise_programmatic=TRUE have their cm_func + called with MIF_NOI, allowing them to register arbitrary noise sources + via cm_noise_add_source() and set densities via NOISE_DENSITY(). + +INTERFACES + + MIFnoise() + +============================================================================*/ + +#include "ngspice/ngspice.h" +#include "ngspice/cktdefs.h" +#include "ngspice/noisedef.h" +#include "ngspice/iferrmsg.h" +#include "ngspice/devdefs.h" + +#include "ngspice/mif.h" +#include "ngspice/mifdefs.h" +#include "ngspice/mifproto.h" +#include "ngspice/mifparse.h" +#include "ngspice/mifcmdat.h" + +#include "ngspice/suffix.h" + + +/* Declarative noise source layout: groups of 3 (white, flicker, total) */ +#define DECL_SRCS_PER_GROUP 3 +#define DECL_WHITE 0 +#define DECL_FLICKER 1 +#define DECL_TOTAL 2 + +/* Indices for reserved parameter names, -1 if not present */ +typedef struct { + int nv_idx; /* noise_voltage parameter index */ + int nc_idx; /* noise_current parameter index */ + int corner_idx; /* noise_corner parameter index */ + int exp_idx; /* noise_exponent parameter index */ + int prog_idx; /* noise_programmatic parameter index */ +} Mif_Noise_Param_Indices_t; + + +static void +find_noise_params(int mod_type, Mif_Noise_Param_Indices_t *idx) +{ + int i; + int num_param = DEVices[mod_type]->DEVpublic.num_param; + Mif_Param_Info_t *pinfo; + + idx->nv_idx = -1; + idx->nc_idx = -1; + idx->corner_idx = -1; + idx->exp_idx = -1; + idx->prog_idx = -1; + + for (i = 0; i < num_param; i++) { + pinfo = &(DEVices[mod_type]->DEVpublic.param[i]); + + if (strcmp(pinfo->name, "noise_voltage") == 0) + idx->nv_idx = i; + else if (strcmp(pinfo->name, "noise_current") == 0) + idx->nc_idx = i; + else if (strcmp(pinfo->name, "noise_corner") == 0) + idx->corner_idx = i; + else if (strcmp(pinfo->name, "noise_exponent") == 0) + idx->exp_idx = i; + else if (strcmp(pinfo->name, "noise_programmatic") == 0) + idx->prog_idx = i; + } +} + + +/* Find first output port that is a voltage-type (has branch equation) */ +static int +find_first_output_conn(MIFinstance *here, int *out_port) +{ + int i, j; + + for (i = 0; i < here->num_conn; i++) { + if (here->conn[i]->is_null || !here->conn[i]->is_output) + continue; + + for (j = 0; j < here->conn[i]->size; j++) { + if (here->conn[i]->port[j]->is_null) + continue; + + *out_port = j; + return i; + } + } + + return -1; +} + + +/* Find first input port */ +static int +find_first_input_conn(MIFinstance *here, int *in_port) +{ + int i, j; + + for (i = 0; i < here->num_conn; i++) { + if (here->conn[i]->is_null || !here->conn[i]->is_input) + continue; + + for (j = 0; j < here->conn[i]->size; j++) { + if (here->conn[i]->port[j]->is_null) + continue; + + *in_port = j; + return i; + } + } + + return -1; +} + + +/* + * Resolve noise source nodes from a connection/port and source type. + * Returns 0 on success, -1 on invalid combination. + */ +static int +resolve_noise_nodes(MIFinstance *here, int conn, int port, + Mif_Noise_Src_Type_t type, int *node1, int *node2) +{ + Mif_Port_Data_t *pdata = here->conn[conn]->port[port]; + Mif_Smp_Ptr_t *smp = &(pdata->smp_data); + + if (type == MIF_NOISE_CURRENT) { + *node1 = smp->pos_node; + *node2 = smp->neg_node; + return 0; + } + + if (type == MIF_NOISE_CURRENT_POS) { + *node1 = smp->pos_node; + *node2 = 0; + return 0; + } + + if (type == MIF_NOISE_CURRENT_NEG) { + *node1 = smp->neg_node; + *node2 = 0; + return 0; + } + + /* MIF_NOISE_VOLTAGE */ + switch (pdata->type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + /* Voltage-type output ports have a branch equation */ + *node1 = smp->branch; + *node2 = 0; + return 0; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + /* Current-type input ports have an ibranch equation */ + *node1 = smp->ibranch; + *node2 = 0; + return 0; + + default: + return -1; + } +} + + +static Mif_Boolean_t +is_voltage_noise_port(Mif_Port_Type_t type) +{ + switch (type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + return MIF_TRUE; + + default: + return MIF_FALSE; + } +} + + +/* + * Allocate noise state arrays for an instance. + * Must be called after num_noise_srcs and noise_prog_offset have been set. + */ +static void +alloc_noise_state(MIFinstance *here) +{ + int nsrcs = here->num_noise_srcs; + + if (nsrcs <= 0) + return; + + here->MIFnVar = TMALLOC(double, NSTATVARS * nsrcs); + here->noise_node1 = TMALLOC(int, nsrcs); + here->noise_node2 = TMALLOC(int, nsrcs); + here->noise_src_names = TMALLOC(char *, nsrcs); + + if (!here->MIFnVar || !here->noise_node1 || !here->noise_node2 || !here->noise_src_names) { + here->num_noise_srcs = 0; + return; + } + + memset(here->MIFnVar, 0, (size_t)(NSTATVARS * nsrcs) * sizeof(double)); + memset(here->noise_node1, 0, (size_t)nsrcs * sizeof(int)); + memset(here->noise_node2, 0, (size_t)nsrcs * sizeof(int)); + memset(here->noise_src_names, 0, (size_t)nsrcs * sizeof(char *)); + + if (here->noise_prog_offset < nsrcs) { + int num_prog = nsrcs - here->noise_prog_offset; + + here->noise_prog_density = TMALLOC(double, num_prog); + } +} + + +/* + * Set up declarative noise sources during N_OPEN. + * When count_only is TRUE, just counts eligible sources without writing arrays. + * Always sets noise_decl_nv_base and noise_decl_nc_base on the instance. + * Returns the number of declarative sources. + */ +static int +setup_declarative_sources(MIFinstance *here, + Mif_Noise_Param_Indices_t *idx, + int base_idx, + Mif_Boolean_t count_only) +{ + int count = 0; + int out_conn, out_port, in_conn, in_port; + int node1, node2; + + here->noise_decl_nv_base = -1; + here->noise_decl_nc_base = -1; + + /* noise_voltage: 3 sources attached to first output port */ + if (idx->nv_idx >= 0 && !here->param[idx->nv_idx]->is_null) { + out_conn = find_first_output_conn(here, &out_port); + + if (out_conn >= 0 && + is_voltage_noise_port(here->conn[out_conn]->port[out_port]->type) && + resolve_noise_nodes(here, out_conn, out_port, + MIF_NOISE_VOLTAGE, &node1, &node2) == 0) { + + here->noise_decl_nv_base = base_idx + count; + + if (!count_only) { + int s = base_idx + count; + + here->noise_node1[s + DECL_WHITE] = node1; + here->noise_node2[s + DECL_WHITE] = node2; + here->noise_src_names[s + DECL_WHITE] = tprintf("_nv_white"); + + here->noise_node1[s + DECL_FLICKER] = node1; + here->noise_node2[s + DECL_FLICKER] = node2; + here->noise_src_names[s + DECL_FLICKER] = tprintf("_nv_flicker"); + + here->noise_node1[s + DECL_TOTAL] = node1; + here->noise_node2[s + DECL_TOTAL] = node2; + here->noise_src_names[s + DECL_TOTAL] = tprintf("_nv_total"); + } + + count += DECL_SRCS_PER_GROUP; + } + } + + /* noise_current: 3 sources attached to first input port */ + if (idx->nc_idx >= 0 && !here->param[idx->nc_idx]->is_null) { + in_conn = find_first_input_conn(here, &in_port); + + if (in_conn >= 0 && + resolve_noise_nodes(here, in_conn, in_port, + MIF_NOISE_CURRENT, &node1, &node2) == 0) { + + here->noise_decl_nc_base = base_idx + count; + + if (!count_only) { + int s = base_idx + count; + + here->noise_node1[s + DECL_WHITE] = node1; + here->noise_node2[s + DECL_WHITE] = node2; + here->noise_src_names[s + DECL_WHITE] = tprintf("_nc_white"); + + here->noise_node1[s + DECL_FLICKER] = node1; + here->noise_node2[s + DECL_FLICKER] = node2; + here->noise_src_names[s + DECL_FLICKER] = tprintf("_nc_flicker"); + + here->noise_node1[s + DECL_TOTAL] = node1; + here->noise_node2[s + DECL_TOTAL] = node2; + here->noise_src_names[s + DECL_TOTAL] = tprintf("_nc_total"); + } + + count += DECL_SRCS_PER_GROUP; + } + } + + return count; +} + + +/* + * Evaluate declarative noise for one group (white + flicker + total). + */ +static void +eval_declarative_group(MIFinstance *here, CKTcircuit *ckt, Ndata *data, + NOISEAN *job, double *OnDens, + int param_idx, int corner_idx, int exp_idx, + int src_base, int nsrcs) +{ + double Sv, fc, n, f; + double noizDens[3], lnNdens[3]; + double tempOutNoise, tempInNoise; + int i; + + Sv = here->param[param_idx]->element[0].rvalue; + fc = (corner_idx >= 0) ? here->param[corner_idx]->element[0].rvalue : 0.0; + n = (exp_idx >= 0) ? here->param[exp_idx]->element[0].rvalue : 1.0; + f = data->freq; + + /* White noise */ + NevalSrc(&noizDens[DECL_WHITE], &lnNdens[DECL_WHITE], ckt, + N_GAIN, here->noise_node1[src_base + DECL_WHITE], + here->noise_node2[src_base + DECL_WHITE], 0.0); + noizDens[DECL_WHITE] *= Sv * Sv; + lnNdens[DECL_WHITE] = log(MAX(noizDens[DECL_WHITE], N_MINLOG)); + + /* Flicker noise */ + NevalSrc(&noizDens[DECL_FLICKER], NULL, ckt, + N_GAIN, here->noise_node1[src_base + DECL_FLICKER], + here->noise_node2[src_base + DECL_FLICKER], 0.0); + noizDens[DECL_FLICKER] *= (fc > 0 && f > 0) ? Sv * Sv * pow(fc / f, n) : 0.0; + lnNdens[DECL_FLICKER] = log(MAX(noizDens[DECL_FLICKER], N_MINLOG)); + + /* Total */ + noizDens[DECL_TOTAL] = noizDens[DECL_WHITE] + noizDens[DECL_FLICKER]; + lnNdens[DECL_TOTAL] = log(MAX(noizDens[DECL_TOTAL], N_MINLOG)); + + *OnDens += noizDens[DECL_TOTAL]; + + /* Integration, following resnoise.c pattern */ + if (data->delFreq == 0.0) { + + for (i = 0; i < DECL_SRCS_PER_GROUP; i++) + here->MIFnVar[LNLSTDENS * nsrcs + src_base + i] = lnNdens[i]; + + if (data->freq == job->NstartFreq) { + + for (i = 0; i < DECL_SRCS_PER_GROUP; i++) { + here->MIFnVar[OUTNOIZ * nsrcs + src_base + i] = 0.0; + here->MIFnVar[INNOIZ * nsrcs + src_base + i] = 0.0; + } + } + } + else { + + for (i = 0; i < DECL_SRCS_PER_GROUP; i++) { + if (i == DECL_TOTAL) + continue; + + tempOutNoise = Nintegrate(noizDens[i], lnNdens[i], + here->MIFnVar[LNLSTDENS * nsrcs + src_base + i], data); + tempInNoise = Nintegrate(noizDens[i] * data->GainSqInv, + lnNdens[i] + data->lnGainInv, + here->MIFnVar[LNLSTDENS * nsrcs + src_base + i] + data->lnGainInv, + data); + + here->MIFnVar[LNLSTDENS * nsrcs + src_base + i] = lnNdens[i]; + data->outNoiz += tempOutNoise; + data->inNoise += tempInNoise; + + if (job->NStpsSm != 0) { + here->MIFnVar[OUTNOIZ * nsrcs + src_base + i] += tempOutNoise; + here->MIFnVar[OUTNOIZ * nsrcs + src_base + DECL_TOTAL] += tempOutNoise; + here->MIFnVar[INNOIZ * nsrcs + src_base + i] += tempInNoise; + here->MIFnVar[INNOIZ * nsrcs + src_base + DECL_TOTAL] += tempInNoise; + } + } + } + + if (data->prtSummary) { + + for (i = 0; i < DECL_SRCS_PER_GROUP; i++) + data->outpVector[data->outNumber++] = noizDens[i]; + } +} + + +/* + * Set up XSPICE context for calling cm_func during noise analysis. + * Uses the global g_mif_noise_cm_data (defined in mif.c) so that + * cm_noise_add_source() in cm.c can access the noise data. + */ +static void +setup_noise_cm_context(MIFinstance *here, CKTcircuit *ckt, + Mif_Noise_Data_t *noise_data) +{ + g_mif_info.ckt = ckt; + g_mif_info.instance = here; + g_mif_info.errmsg = ""; + g_mif_info.circuit.call_type = MIF_ANALOG; + g_mif_info.circuit.init = MIF_FALSE; + g_mif_info.circuit.anal_init = MIF_FALSE; + g_mif_info.circuit.anal_type = MIF_NOI; + + g_mif_noise_cm_data.circuit.anal_type = MIF_NOI; + g_mif_noise_cm_data.circuit.anal_init = MIF_FALSE; + g_mif_noise_cm_data.circuit.init = MIF_FALSE; + g_mif_noise_cm_data.circuit.call_type = MIF_ANALOG; + g_mif_noise_cm_data.circuit.frequency = ckt->CKTomega; + g_mif_noise_cm_data.circuit.temperature = ckt->CKTtemp - 273.15; + g_mif_noise_cm_data.circuit.time = 0.0; + memset(g_mif_noise_cm_data.circuit.t, 0, sizeof(g_mif_noise_cm_data.circuit.t)); + g_mif_noise_cm_data.num_conn = here->num_conn; + g_mif_noise_cm_data.conn = here->conn; + g_mif_noise_cm_data.num_param = here->num_param; + g_mif_noise_cm_data.param = here->param; + g_mif_noise_cm_data.num_inst_var = here->num_inst_var; + g_mif_noise_cm_data.inst_var = here->inst_var; + g_mif_noise_cm_data.callback = &(here->callback); + g_mif_noise_cm_data.noise = noise_data; +} + + +static void +restore_after_cm_func(void) +{ + g_mif_info.circuit.anal_type = MIF_AC; + g_mif_noise_cm_data.noise = NULL; +} + + +/* + * MIFnoise - Generic noise callback for all XSPICE code models. + */ +int +MIFnoise(int mode, int operation, GENmodel *genmodel, CKTcircuit *ckt, + Ndata *data, double *OnDens) +{ + NOISEAN *job = (NOISEAN *) ckt->CKTcurJob; + MIFmodel *model; + MIFinstance *here; + int mod_type; + Mif_Noise_Param_Indices_t parm_idx; + int i; + + model = (MIFmodel *) genmodel; + mod_type = model->MIFmodType; + + /* Scan parameter names once per model type */ + find_noise_params(mod_type, &parm_idx); + + for (; model != NULL; model = MIFnextModel(model)) { + + if (!model->analog) + continue; + + for (here = MIFinstances(model); here != NULL; here = MIFnextInstance(here)) { + + if (!here->analog) + continue; + + switch (operation) { + + case N_OPEN: + { + int decl_count = 0; + int prog_count = 0; + Mif_Boolean_t has_programmatic = MIF_FALSE; + + /* Check if model has noise_programmatic parameter. + * If present but null (user didn't specify), defaults to TRUE. + * If present and not null, use the user's value. */ + if (parm_idx.prog_idx >= 0) { + + if (here->param[parm_idx.prog_idx]->is_null || + here->param[parm_idx.prog_idx]->element[0].bvalue) { + has_programmatic = MIF_TRUE; + } + } + + if (!here->noise_initialized) { + /* First N_OPEN (N_DENS pass): discover and allocate. + * Count pass determines eligible sources and caches base indices. */ + decl_count = setup_declarative_sources(here, &parm_idx, 0, MIF_TRUE); + here->noise_prog_offset = decl_count; + + /* Count programmatic sources by calling cm_func */ + if (has_programmatic) { + Mif_Noise_Data_t noise_data; + memset(&noise_data, 0, sizeof(noise_data)); + noise_data.registering = MIF_TRUE; + + setup_noise_cm_context(here, ckt, &noise_data); + DEVices[mod_type]->DEVpublic.cm_func(&g_mif_noise_cm_data); + restore_after_cm_func(); + + prog_count = noise_data.num_prog_srcs; + here->num_noise_srcs = decl_count + prog_count; + alloc_noise_state(here); + setup_declarative_sources(here, &parm_idx, 0, MIF_FALSE); + + /* Resolve programmatic source nodes. + * Sources with conn == -1 were rejected during registration + * and get node1=node2=0 (zero noise contribution). */ + for (i = 0; i < prog_count; i++) { + int si = decl_count + i; + int n1 = 0, n2 = 0; + + if (noise_data.prog_conn[i] >= 0 && + resolve_noise_nodes(here, + noise_data.prog_conn[i], + noise_data.prog_port[i], + noise_data.prog_types[i], + &n1, &n2) == 0) { + here->noise_node1[si] = n1; + here->noise_node2[si] = n2; + } + + here->noise_src_names[si] = noise_data.prog_names[i]; + noise_data.prog_names[i] = NULL; + } + + /* Free temporary registration arrays */ + if (noise_data.prog_types) + FREE(noise_data.prog_types); + + if (noise_data.prog_conn) + FREE(noise_data.prog_conn); + + if (noise_data.prog_port) + FREE(noise_data.prog_port); + + if (noise_data.prog_names) { + + for (i = 0; i < prog_count; i++) { + if (noise_data.prog_names[i]) + FREE(noise_data.prog_names[i]); + } + + FREE(noise_data.prog_names); + } + } + else { + here->num_noise_srcs = decl_count; + alloc_noise_state(here); + setup_declarative_sources(here, &parm_idx, 0, MIF_FALSE); + } + + here->noise_initialized = MIF_TRUE; + } + + /* Register output variable names (both N_DENS and INT_NOIZ passes) */ + if (here->num_noise_srcs > 0 && job->NStpsSm != 0) { + + switch (mode) { + case N_DENS: + for (i = 0; i < here->num_noise_srcs; i++) { + NOISE_ADD_OUTVAR(ckt, data, "onoise_%s%s", + here->MIFname, + here->noise_src_names[i] ? here->noise_src_names[i] : ""); + } + break; + + case INT_NOIZ: + for (i = 0; i < here->num_noise_srcs; i++) { + NOISE_ADD_OUTVAR(ckt, data, "onoise_total_%s%s", + here->MIFname, + here->noise_src_names[i] ? here->noise_src_names[i] : ""); + NOISE_ADD_OUTVAR(ckt, data, "inoise_total_%s%s", + here->MIFname, + here->noise_src_names[i] ? here->noise_src_names[i] : ""); + } + break; + } + } + break; + } + + case N_CALC: + { + int nsrcs = here->num_noise_srcs; + int prog_offset = here->noise_prog_offset; + + if (nsrcs <= 0) + break; + + switch (mode) { + case N_DENS: + { + /* Evaluate declarative groups using cached base indices */ + if (here->noise_decl_nv_base >= 0) { + eval_declarative_group(here, ckt, data, job, OnDens, + parm_idx.nv_idx, parm_idx.corner_idx, parm_idx.exp_idx, + here->noise_decl_nv_base, nsrcs); + } + + if (here->noise_decl_nc_base >= 0) { + eval_declarative_group(here, ckt, data, job, OnDens, + parm_idx.nc_idx, parm_idx.corner_idx, parm_idx.exp_idx, + here->noise_decl_nc_base, nsrcs); + } + + /* Evaluate programmatic noise sources */ + if (nsrcs > prog_offset && here->noise_prog_density) { + int num_prog = nsrcs - prog_offset; + Mif_Noise_Data_t noise_data; + double noizDens_p, lnNdens_p; + double tempOutNoise, tempInNoise; + + memset(&noise_data, 0, sizeof(noise_data)); + noise_data.registering = MIF_FALSE; + noise_data.freq = data->freq; + noise_data.density = here->noise_prog_density; + memset(noise_data.density, 0, (size_t)num_prog * sizeof(double)); + + setup_noise_cm_context(here, ckt, &noise_data); + DEVices[mod_type]->DEVpublic.cm_func(&g_mif_noise_cm_data); + restore_after_cm_func(); + + for (i = 0; i < num_prog; i++) { + int si = prog_offset + i; + + NevalSrc(&noizDens_p, &lnNdens_p, ckt, + N_GAIN, + here->noise_node1[si], + here->noise_node2[si], 0.0); + + noizDens_p *= noise_data.density[i]; + lnNdens_p = log(MAX(noizDens_p, N_MINLOG)); + + *OnDens += noizDens_p; + + if (data->delFreq == 0.0) { + here->MIFnVar[LNLSTDENS * nsrcs + si] = lnNdens_p; + + if (data->freq == job->NstartFreq) { + here->MIFnVar[OUTNOIZ * nsrcs + si] = 0.0; + here->MIFnVar[INNOIZ * nsrcs + si] = 0.0; + } + } + else { + tempOutNoise = Nintegrate(noizDens_p, lnNdens_p, + here->MIFnVar[LNLSTDENS * nsrcs + si], data); + tempInNoise = Nintegrate( + noizDens_p * data->GainSqInv, + lnNdens_p + data->lnGainInv, + here->MIFnVar[LNLSTDENS * nsrcs + si] + data->lnGainInv, + data); + + here->MIFnVar[LNLSTDENS * nsrcs + si] = lnNdens_p; + data->outNoiz += tempOutNoise; + data->inNoise += tempInNoise; + + if (job->NStpsSm != 0) { + here->MIFnVar[OUTNOIZ * nsrcs + si] += tempOutNoise; + here->MIFnVar[INNOIZ * nsrcs + si] += tempInNoise; + } + } + + if (data->prtSummary) + data->outpVector[data->outNumber++] = noizDens_p; + } + } + break; + } + + case INT_NOIZ: + if (job->NStpsSm != 0) { + + for (i = 0; i < nsrcs; i++) { + data->outpVector[data->outNumber++] = + here->MIFnVar[OUTNOIZ * nsrcs + i]; + data->outpVector[data->outNumber++] = + here->MIFnVar[INNOIZ * nsrcs + i]; + } + } + break; + } + break; + } + + case N_CLOSE: + return (OK); + + } /* switch operation */ + } /* for instances */ + } /* for models */ + + return (OK); +} + + +void +MIF_free_noise_state(MIFinstance *here) +{ + int i; + + if (here->noise_src_names) { + + for (i = 0; i < here->num_noise_srcs; i++) + tfree(here->noise_src_names[i]); + + tfree(here->noise_src_names); + } + + tfree(here->MIFnVar); + tfree(here->noise_node1); + tfree(here->noise_node2); + tfree(here->noise_prog_density); + here->num_noise_srcs = 0; + here->noise_prog_offset = 0; + here->noise_decl_nv_base = -1; + here->noise_decl_nc_base = -1; + here->noise_initialized = MIF_FALSE; +} diff --git a/src/xspice/mif/mifsetup.c b/src/xspice/mif/mifsetup.c index f280503a2..ec2a27951 100644 --- a/src/xspice/mif/mifsetup.c +++ b/src/xspice/mif/mifsetup.c @@ -574,6 +574,8 @@ MIFunsetup(GENmodel *inModel,CKTcircuit *ckt) here->callback(&cm_data, MIF_CB_DESTROY); } + MIF_free_noise_state(here); + here->initialized = MIF_FALSE; } /* end for all instances */ } diff --git a/src/xspice/xspice.c b/src/xspice/xspice.c index 4fe1cc597..605bfb603 100644 --- a/src/xspice/xspice.c +++ b/src/xspice/xspice.c @@ -89,4 +89,7 @@ struct coreInfo_t coreInfo = MIFbindCSCComplex, MIFbindCSCComplexToReal #endif + , + MIFnoise, + cm_noise_add_source }; diff --git a/tests/regression/misc/convergence.cir b/tests/regression/misc/convergence.cir new file mode 100644 index 000000000..0819c32d0 --- /dev/null +++ b/tests/regression/misc/convergence.cir @@ -0,0 +1,28 @@ +* Branch currents should settle in this behavioural model of a diode + +.options noacct +.options reltol=0.001 abstol=1e-12; defaults as of 2024 + +I2 0 N01 pulse 2µ 20µ 50n 50n 50n 1µ 2µ +R2 N01 0 50K +.param VT=26m +.param Isat=1e-14 +B2 N01 0 I=Isat*(exp(v(N01)/VT)-1) + +.control +set savecurrents +tran 10n 5u +let residual = @I2[current] - @B2[i] - @R2[i] + +* error in current sum at a node should be +* less than RELTOL times the 20-µA currents, +* which is 20 nA, plus ABSTOL +if vecmax(abs(residual)) > 20.001e-9 + echo "ERROR: B-device convergence failed" + quit 1 +else + echo "INFO: success" + quit 0 +end +.endc +.end diff --git a/tests/regression/misc/convergence.out b/tests/regression/misc/convergence.out new file mode 100644 index 000000000..8e9036969 --- /dev/null +++ b/tests/regression/misc/convergence.out @@ -0,0 +1 @@ +INFO: success diff --git a/visualc/sharedspice.vcxproj b/visualc/sharedspice.vcxproj index d8f8ff189..b7d9d1e81 100644 --- a/visualc/sharedspice.vcxproj +++ b/visualc/sharedspice.vcxproj @@ -1454,6 +1454,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + @@ -2620,6 +2621,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + diff --git a/visualc/vngspice-fftw.vcxproj b/visualc/vngspice-fftw.vcxproj index 9274e0228..54d130b5c 100644 --- a/visualc/vngspice-fftw.vcxproj +++ b/visualc/vngspice-fftw.vcxproj @@ -1675,6 +1675,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + @@ -2840,6 +2841,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index b4e342411..94de43637 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -1690,6 +1690,7 @@ + @@ -2855,6 +2856,7 @@ + diff --git a/visualc/xspice/analog.vcxproj b/visualc/xspice/analog.vcxproj index 4c81fd959..222219f72 100644 --- a/visualc/xspice/analog.vcxproj +++ b/visualc/xspice/analog.vcxproj @@ -258,6 +258,8 @@ + + @@ -307,6 +309,8 @@ + +