Merge branch 'pre-master-47' into bt_dev

This commit is contained in:
Brian Taylor 2026-04-29 15:25:01 -07:00
commit c9528aad03
61 changed files with 1791 additions and 183 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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*) ;

View File

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

View File

@ -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 ;
}

View File

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

View File

@ -10,6 +10,7 @@ libmisc_la_SOURCES = \
dup2.c \
dstring.c \
dup2.h \
engnotation.c \
hash.c \
ivars.c \
ivars.h \

View File

@ -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);

86
src/misc/engnotation.c Normal file
View File

@ -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 <stdio.h>
#include <math.h>
#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;
}

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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 */

View File

@ -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")
};

View File

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

View File

@ -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,
};

View File

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

View File

@ -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);

View File

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

View File

@ -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);

View File

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

View File

@ -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 */
{

View File

@ -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;
}

View File

@ -98,4 +98,7 @@ struct coreInfo_t coreInfo =
MIFbindCSCComplex,
MIFbindCSCComplexToReal
#endif
,
MIFnoise,
cm_noise_add_source
};

View File

@ -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;}

View File

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

View File

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

View File

@ -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. */

View File

@ -21,3 +21,4 @@ file_source
delay
pwlts
astate
ota

View File

@ -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 <stdlib.h>
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;
}
}

View File

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

View File

@ -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) ;

View File

@ -19,6 +19,7 @@ libmifxsp_la_SOURCES = \
mifdelete.c \
mifmdelete.c \
mifdestr.c \
mifnoise.c \
mif.c
if KLU_WANTED

View File

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

View File

@ -194,5 +194,7 @@ MIFdelete(GENinstance *gen_inst)
if (here->num_conv && here->conv)
FREE(here->conv);
MIF_free_noise_state(here);
return OK;
}

741
src/xspice/mif/mifnoise.c Normal file
View File

@ -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;
}

View File

@ -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 */
}

View File

@ -89,4 +89,7 @@ struct coreInfo_t coreInfo =
MIFbindCSCComplex,
MIFbindCSCComplexToReal
#endif
,
MIFnoise,
cm_noise_add_source
};

View File

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

View File

@ -0,0 +1 @@
INFO: success

View File

@ -1454,6 +1454,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\misc\alloc.c" />
<ClCompile Include="..\src\misc\dstring.c" />
<ClCompile Include="..\src\misc\dup2.c" />
<ClCompile Include="..\src\misc\engnotation.c" />
<ClCompile Include="..\src\misc\getopt_long_bsd.c" />
<ClCompile Include="..\src\misc\hash.c" />
<ClCompile Include="..\src\misc\ivars.c" />
@ -2620,6 +2621,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\xspice\mif\mifgetvalue.c" />
<ClCompile Include="..\src\xspice\mif\mifload.c" />
<ClCompile Include="..\src\xspice\mif\mifmask.c" />
<ClCompile Include="..\src\xspice\mif\mifnoise.c" />
<ClCompile Include="..\src\xspice\mif\mifmdelete.c" />
<ClCompile Include="..\src\xspice\mif\mifmpara.c" />
<ClCompile Include="..\src\xspice\mif\mifsetup.c" />

View File

@ -1675,6 +1675,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\misc\alloc.c" />
<ClCompile Include="..\src\misc\dstring.c" />
<ClCompile Include="..\src\misc\dup2.c" />
<ClCompile Include="..\src\misc\engnotation.c" />
<ClCompile Include="..\src\misc\getopt_long_bsd.c" />
<ClCompile Include="..\src\misc\hash.c" />
<ClCompile Include="..\src\misc\ivars.c" />
@ -2840,6 +2841,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\xspice\mif\mifgetvalue.c" />
<ClCompile Include="..\src\xspice\mif\mifload.c" />
<ClCompile Include="..\src\xspice\mif\mifmask.c" />
<ClCompile Include="..\src\xspice\mif\mifnoise.c" />
<ClCompile Include="..\src\xspice\mif\mifmdelete.c" />
<ClCompile Include="..\src\xspice\mif\mifmpara.c" />
<ClCompile Include="..\src\xspice\mif\mifsetup.c" />

View File

@ -1690,6 +1690,7 @@
<ClCompile Include="..\src\misc\alloc.c" />
<ClCompile Include="..\src\misc\dstring.c" />
<ClCompile Include="..\src\misc\dup2.c" />
<ClCompile Include="..\src\misc\engnotation.c" />
<ClCompile Include="..\src\misc\getopt_long_bsd.c" />
<ClCompile Include="..\src\misc\hash.c" />
<ClCompile Include="..\src\misc\ivars.c" />
@ -2855,6 +2856,7 @@
<ClCompile Include="..\src\xspice\mif\mifgetvalue.c" />
<ClCompile Include="..\src\xspice\mif\mifload.c" />
<ClCompile Include="..\src\xspice\mif\mifmask.c" />
<ClCompile Include="..\src\xspice\mif\mifnoise.c" />
<ClCompile Include="..\src\xspice\mif\mifmdelete.c" />
<ClCompile Include="..\src\xspice\mif\mifmpara.c" />
<ClCompile Include="..\src\xspice\mif\mifsetup.c" />

View File

@ -258,6 +258,8 @@
<ClCompile Include="icm\analog\delay\delay-ifspec.c" />
<ClCompile Include="icm\analog\xfer\xfer-cfunc.c" />
<ClCompile Include="icm\analog\xfer\xfer-ifspec.c" />
<ClCompile Include="icm\analog\ota\ota-cfunc.c" />
<ClCompile Include="icm\analog\ota\ota-ifspec.c" />
<ClCompile Include="..\..\src\xspice\icm\dlmain.c" />
</ItemGroup>
<ItemGroup>
@ -307,6 +309,8 @@
<None Include="..\..\src\xspice\icm\analog\delay\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\analog\xfer\cfunc.mod" />
<None Include="..\..\src\xspice\icm\analog\xfer\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\analog\ota\cfunc.mod" />
<None Include="..\..\src\xspice\icm\analog\ota\ifspec.ifs" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\include\ngspice\dstring.h" />