Merge branch 'pre-master-47' into bt_dev
This commit is contained in:
commit
c9528aad03
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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*) ;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ libmisc_la_SOURCES = \
|
|||
dup2.c \
|
||||
dstring.c \
|
||||
dup2.h \
|
||||
engnotation.c \
|
||||
hash.c \
|
||||
ivars.c \
|
||||
ivars.h \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,4 +98,7 @@ struct coreInfo_t coreInfo =
|
|||
MIFbindCSCComplex,
|
||||
MIFbindCSCComplexToReal
|
||||
#endif
|
||||
,
|
||||
MIFnoise,
|
||||
cm_noise_add_source
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
|
|
|
|||
|
|
@ -21,3 +21,4 @@ file_source
|
|||
delay
|
||||
pwlts
|
||||
astate
|
||||
ota
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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) ;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ libmifxsp_la_SOURCES = \
|
|||
mifdelete.c \
|
||||
mifmdelete.c \
|
||||
mifdestr.c \
|
||||
mifnoise.c \
|
||||
mif.c
|
||||
|
||||
if KLU_WANTED
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -194,5 +194,7 @@ MIFdelete(GENinstance *gen_inst)
|
|||
if (here->num_conv && here->conv)
|
||||
FREE(here->conv);
|
||||
|
||||
MIF_free_noise_state(here);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,4 +89,7 @@ struct coreInfo_t coreInfo =
|
|||
MIFbindCSCComplex,
|
||||
MIFbindCSCComplexToReal
|
||||
#endif
|
||||
,
|
||||
MIFnoise,
|
||||
cm_noise_add_source
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
INFO: success
|
||||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue