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 @@
+