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/spicelib/analysis/noisean.c b/src/spicelib/analysis/noisean.c index 29f880452..7eb0fdcba 100644 --- a/src/spicelib/analysis/noisean.c +++ b/src/spicelib/analysis/noisean.c @@ -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/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/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/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/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/visualc/sharedspice.vcxproj b/visualc/sharedspice.vcxproj index d8f8ff189..34eb25343 100644 --- a/visualc/sharedspice.vcxproj +++ b/visualc/sharedspice.vcxproj @@ -2620,6 +2620,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 fb9c4eabf..a83f0d244 100644 --- a/visualc/vngspice-fftw.vcxproj +++ b/visualc/vngspice-fftw.vcxproj @@ -2840,6 +2840,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..0f8adb416 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -2855,6 +2855,7 @@ +