working an loop analysis
This commit is contained in:
parent
4f076ed210
commit
17b1eec2aa
|
|
@ -0,0 +1,99 @@
|
|||
/**********
|
||||
Copyright 1990 Regents of the University of California. All rights reserved.
|
||||
Author: 1988 Jaijeet S Roychowdhury
|
||||
Modified: 2000 AlansFixes
|
||||
|
||||
Copyright 2020 Anamosic Ballenegger Design. All rights reserved.
|
||||
Author: 2020 Florian Ballenegger
|
||||
|
||||
**********/
|
||||
|
||||
#ifndef ngspice_LOOPDEF_H
|
||||
#define ngspice_LOOPDEF_H
|
||||
|
||||
#ifdef D_DBG_ALLTIMES
|
||||
#define D_DBG_BLOCKTIMES
|
||||
#define D_DBG_SMALLTIMES
|
||||
#endif
|
||||
|
||||
#include "ngspice/jobdefs.h"
|
||||
#include "ngspice/gendefs.h"
|
||||
#include "ngspice/cktdefs.h"
|
||||
|
||||
/* function prototypes that usually go into cktdefs.h */
|
||||
extern int LOOPsetParm(CKTcircuit *ckt, JOB *anal, int which, IFvalue *value);
|
||||
extern int LOOPaskQuest(CKTcircuit *ckt, JOB *anal, int which, IFvalue *value);
|
||||
extern int LOOPpreset(CKTcircuit *ckt, JOB *anal);
|
||||
extern int LOOPan(CKTcircuit *ckt, int restart);
|
||||
|
||||
|
||||
/* structure used to describe an LOOP analysis to be performed */
|
||||
/* cktnewan.c is patched to initialiaze this data structure to all zero */
|
||||
typedef struct {
|
||||
int JOBtype;
|
||||
JOB *JOBnextJob; /* pointer to next thing to do */
|
||||
char *JOBname; /* name of this job */
|
||||
double LOOPstartFreq; /* the start value of
|
||||
frequency for loop stability analysis */
|
||||
double LOOPstopFreq; /* the stop value ove above */
|
||||
double LOOPfreqDelta; /* multiplier for decade/octave
|
||||
stepping, step for linear steps. */
|
||||
double LOOPsaveFreq; /* frequency at which we left off last time*/
|
||||
int LOOPstepType; /* values described below */
|
||||
int LOOPnumSteps;
|
||||
double LOOPomega; /* current omega1 */
|
||||
IFuid LOOPprobeSrc; /* probe source */
|
||||
char* LOOPname;
|
||||
char* LOOPportname;
|
||||
int LOOPportnum;
|
||||
int LOOPdirection; /* 0: not set, 1: forward, 2:reverse */
|
||||
IFuid LOOPinSrc;
|
||||
IFuid LOOPoutSrc;
|
||||
CKTnode *LOOPoutPos;
|
||||
CKTnode *LOOPoutNeg;
|
||||
|
||||
double** LoopGain;
|
||||
double** iLoopGain;
|
||||
|
||||
unsigned LOOPnameGiven : 1;
|
||||
unsigned LOOPportnumGiven : 1;
|
||||
unsigned LOOPportnameGiven : 1;
|
||||
unsigned LOOPinSrcGiven : 1;
|
||||
unsigned LOOPoutSrcGiven : 1;
|
||||
unsigned LOOPoutPosGiven : 1;
|
||||
unsigned LOOPoutNegGiven : 1;
|
||||
} LOOPAN;
|
||||
|
||||
/* available step types: */
|
||||
|
||||
#define DECADE 1
|
||||
#define OCTAVE 2
|
||||
#define LINEAR 3
|
||||
|
||||
/* defns. used in DsetParm */
|
||||
|
||||
#define LOOP_DEC 1
|
||||
#define LOOP_OCT 2
|
||||
#define LOOP_LIN 3
|
||||
#define LOOP_START 4
|
||||
#define LOOP_STOP 5
|
||||
#define LOOP_STEPS 6
|
||||
|
||||
#define LOOP_PROBESRC 10
|
||||
#define LOOP_PORTNAME 11
|
||||
#define LOOP_PORTNUM 12
|
||||
#define LOOP_DIR 13
|
||||
#define LOOP_PROBESRCNEG 15
|
||||
#define LOOP_PORTNAMENEG 16
|
||||
#define LOOP_PORTNUMNEG 17
|
||||
#define LOOP_DIRNEG 18
|
||||
|
||||
#define LOOP_INSRC 20
|
||||
#define LOOP_OUTSRC 21
|
||||
#define LOOP_OUTPOS 22
|
||||
#define LOOP_OUTNEG 23
|
||||
|
||||
#define LOOP_NAME 30
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -29,6 +29,8 @@ CKTnewAnal(CKTcircuit *ckt, int type, IFuid name, JOB **analPtr, TSKtask *taskPt
|
|||
}
|
||||
*analPtr = (JOB *) tmalloc((size_t) analInfo[type]->size);
|
||||
if(*analPtr==NULL) return(E_NOMEM);
|
||||
/* F.B.: clear data structure to all zero, required for loop analysis */
|
||||
memset(*analPtr, 0, analInfo[type]->size);
|
||||
(*analPtr)->JOBname = name;
|
||||
(*analPtr)->JOBtype = type;
|
||||
(*analPtr)->JOBnextJob = taskPtr->jobs;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,586 @@
|
|||
/**********
|
||||
Copyright 2020 Anamosic Ballenegger Design. All rights reserved.
|
||||
Author: 2020 Florian Ballenegger
|
||||
**********/
|
||||
|
||||
#include "ngspice/ngspice.h"
|
||||
#include "ngspice/cktdefs.h"
|
||||
#include "ngspice/acdefs.h"
|
||||
#include "ngspice/loopdefs.h"
|
||||
#include "ngspice/devdefs.h"
|
||||
#include "ngspice/sperror.h"
|
||||
|
||||
#ifdef XSPICE
|
||||
#include "ngspice/evt.h"
|
||||
#include "ngspice/enh.h"
|
||||
/* gtri - add - wbk - 12/19/90 - Add headers */
|
||||
#include "ngspice/mif.h"
|
||||
#include "ngspice/evtproto.h"
|
||||
#include "ngspice/ipctiein.h"
|
||||
/* gtri - end - wbk */
|
||||
#endif
|
||||
|
||||
|
||||
#define INIT_STATS() \
|
||||
do { \
|
||||
startTime = SPfrontEnd->IFseconds(); \
|
||||
startdTime = ckt->CKTstat->STATdecompTime; \
|
||||
startsTime = ckt->CKTstat->STATsolveTime; \
|
||||
startlTime = ckt->CKTstat->STATloadTime; \
|
||||
startkTime = ckt->CKTstat->STATsyncTime; \
|
||||
} while(0)
|
||||
|
||||
#define UPDATE_STATS(analysis) \
|
||||
do { \
|
||||
ckt->CKTcurrentAnalysis = analysis; \
|
||||
ckt->CKTstat->STATacTime += SPfrontEnd->IFseconds() - startTime; \
|
||||
ckt->CKTstat->STATacDecompTime += ckt->CKTstat->STATdecompTime - startdTime; \
|
||||
ckt->CKTstat->STATacSolveTime += ckt->CKTstat->STATsolveTime - startsTime; \
|
||||
ckt->CKTstat->STATacLoadTime += ckt->CKTstat->STATloadTime - startlTime; \
|
||||
ckt->CKTstat->STATacSyncTime += ckt->CKTstat->STATsyncTime - startkTime; \
|
||||
} while(0)
|
||||
|
||||
extern SPICEdev **DEVices;
|
||||
|
||||
int
|
||||
LOOPpreset(CKTcircuit *ckt, JOB *anal)
|
||||
{
|
||||
LOOPAN *job = (LOOPAN *) anal;
|
||||
GENinstance* inst;
|
||||
GENinstance* probesrc;
|
||||
GENmodel* modvsrc;
|
||||
CKTnode *nodeinj, *node;
|
||||
IFdevice* dev;
|
||||
IFuid eltUid;
|
||||
IFuid modUid;
|
||||
IFvalue ptemp;
|
||||
int termidx;
|
||||
int error;
|
||||
int vtype;
|
||||
|
||||
printf("LOOPpreset with direction %d!\n", job->LOOPdirection);
|
||||
|
||||
if(job->LOOPportnameGiven || job->LOOPportnumGiven)
|
||||
{
|
||||
inst = CKTfndDev(ckt, job->LOOPprobeSrc);
|
||||
if (!inst || inst->GENmodPtr->GENmodType < 0) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Loop probe source %s not in circuit",
|
||||
job->LOOPprobeSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
dev = &DEVices[inst->GENmodPtr->GENmodType]->DEVpublic;
|
||||
if (inst->GENmodPtr->GENmodType == CKTtypelook("Vsource"))
|
||||
printf("Consider to specify the whole Vsource %s as loop probe\n", job->LOOPprobeSrc);
|
||||
|
||||
if(job->LOOPportnameGiven) {
|
||||
for(termidx=0;termidx<(*(dev->numNames));termidx++) {
|
||||
if(0) printf("cmp %s with %s\n", job->LOOPportname, dev->termNames[termidx]);
|
||||
if(strcasecmp(job->LOOPportname, dev->termNames[termidx])==0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
termidx = job->LOOPportnum-1; /* LOOPportnum counts from 1, termidx counts from 0 */
|
||||
if(termidx<0 || termidx>=*(dev->numNames)) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING, "No such terminal %d", termidx);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
/* now break the loop at terminal termidx */
|
||||
printf("Break the loop at device %s terminal %s\n", inst->GENname, dev->termNames[termidx]);
|
||||
node = CKTnum2nod(ckt, GENnode(inst)[termidx]);
|
||||
error = CKTmkVolt(ckt, &nodeinj, inst->GENname, "loopinj");
|
||||
if(error) return(error);
|
||||
error = SPfrontEnd->IFnewUid (ckt, &eltUid, inst->GENname,
|
||||
"probe", UID_INSTANCE, NULL);
|
||||
if(error) return(error);
|
||||
error = SPfrontEnd->IFnewUid (ckt, &modUid, inst->GENname,
|
||||
"probemod", UID_MODEL, NULL);
|
||||
if(error) return(error);
|
||||
vtype = CKTtypelook("Vsource");
|
||||
modvsrc = NULL;
|
||||
error = CKTmodCrt(ckt,vtype,&modvsrc, modUid);
|
||||
if(error) return(error);
|
||||
error = CKTcrtElt(ckt, modvsrc, &probesrc, eltUid);
|
||||
if(error) return(error);
|
||||
ptemp.rValue = 0;
|
||||
error = CKTpName("dc",&ptemp,ckt,vtype,"probe",&probesrc);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 2 : 1,nodeinj);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 1 : 2 ,node);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,inst,termidx+1,nodeinj); /* bindNode counts from 1 ! */
|
||||
ptemp.rValue = 0;
|
||||
job->LOOPprobeSrc = probesrc->GENname;
|
||||
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
LOOPan(CKTcircuit *ckt, int restart)
|
||||
{
|
||||
LOOPAN *job = (LOOPAN *) ckt->CKTcurJob;
|
||||
|
||||
double freq;
|
||||
double freqTol; /* tolerence parameter for finding final frequency */
|
||||
double startdTime;
|
||||
double startsTime;
|
||||
double startlTime;
|
||||
double startkTime;
|
||||
double startTime;
|
||||
|
||||
double Vy[3],Iy[3];
|
||||
double iVy[3],iIy[3];
|
||||
double phasemargin;
|
||||
double gainmargin;
|
||||
double gainsq, phase, curTr;
|
||||
|
||||
int error;
|
||||
int numNames;
|
||||
IFuid *nameList; /* va: tmalloc'ed list of names */
|
||||
IFuid freqUid;
|
||||
static runDesc *acPlot = NULL;
|
||||
runDesc *plot = NULL;
|
||||
GENinstance *inst = NULL;
|
||||
int size, i;
|
||||
int probe_brnum;
|
||||
|
||||
|
||||
|
||||
inst = CKTfndDev(ckt, job->LOOPprobeSrc);
|
||||
|
||||
if (!inst || inst->GENmodPtr->GENmodType < 0) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Loop probe source %s not in circuit",
|
||||
job->LOOPprobeSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
if (inst->GENmodPtr->GENmodType != CKTtypelook("Vsource"))
|
||||
{
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Loop probe source %s not of proper type",
|
||||
job->LOOPprobeSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
printf("LOOPan Vy at %s\n", CKTnodName(ckt,GENnode(inst)[0]));
|
||||
probe_brnum = CKTfndBranch(ckt, job->LOOPprobeSrc);
|
||||
phasemargin = 180;
|
||||
gainmargin = strtod("NaN", NULL);
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - add - wbk - 12/19/90 - Add IPC stuff and anal_init and anal_type */
|
||||
|
||||
/* Tell the beginPlot routine what mode we're in */
|
||||
g_ipc.anal_type = IPC_ANAL_AC;
|
||||
|
||||
/* Tell the code models what mode we're in */
|
||||
g_mif_info.circuit.anal_type = MIF_DC;
|
||||
g_mif_info.circuit.anal_init = MIF_TRUE;
|
||||
|
||||
/* gtri - end - wbk */
|
||||
#endif
|
||||
|
||||
/* start at beginning */
|
||||
if (job->LOOPsaveFreq == 0 || restart) {
|
||||
if (job->LOOPnumSteps < 1)
|
||||
job->LOOPnumSteps = 1;
|
||||
|
||||
switch (job->LOOPstepType) {
|
||||
|
||||
case DECADE:
|
||||
if (job->LOOPstartFreq <= 0) {
|
||||
fprintf(stderr, "ERROR: LOOP startfreq <= 0\n");
|
||||
return E_PARMVAL;
|
||||
}
|
||||
job->LOOPfreqDelta =
|
||||
exp(log(10.0)/job->LOOPnumSteps);
|
||||
break;
|
||||
case OCTAVE:
|
||||
if (job->LOOPstartFreq <= 0) {
|
||||
fprintf(stderr, "ERROR: LOOP startfreq <= 0\n");
|
||||
return E_PARMVAL;
|
||||
}
|
||||
job->LOOPfreqDelta =
|
||||
exp(log(2.0)/job->LOOPnumSteps);
|
||||
break;
|
||||
case LINEAR:
|
||||
if (job->LOOPnumSteps-1 > 1)
|
||||
job->LOOPfreqDelta =
|
||||
(job->LOOPstopFreq -
|
||||
job->LOOPstartFreq) /
|
||||
(job->LOOPnumSteps - 1);
|
||||
else
|
||||
/* Patch from: Richard McRoberts
|
||||
* This patch is for a rather pathological case:
|
||||
* a linear step with only one point */
|
||||
job->LOOPfreqDelta = 0;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
}
|
||||
#ifdef XSPICE
|
||||
/* gtri - begin - wbk - Call EVTop if event-driven instances exist */
|
||||
|
||||
if(ckt->evt->counts.num_insts != 0) {
|
||||
error = EVTop(ckt,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
||||
ckt->CKTdcMaxIter,
|
||||
MIF_TRUE);
|
||||
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
|
||||
EVTop_save(ckt, MIF_TRUE, 0.0);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* If no event-driven instances, do what SPICE normally does */
|
||||
if (!ckt->CKTnoopac) { /* skip OP if option NOOPAC is set and circuit is linear */
|
||||
error = CKTop(ckt,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
||||
ckt->CKTdcMaxIter);
|
||||
|
||||
if(error){
|
||||
fprintf(stdout,"\nLOOP operating point failed -\n");
|
||||
CKTncDump(ckt);
|
||||
return(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
fprintf(stdout,"\n Linear circuit, option noopac given: no OP analysis\n");
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - add - wbk - 12/19/90 - Add IPC stuff */
|
||||
|
||||
/* Send the operating point results for Mspice compatibility */
|
||||
if(g_ipc.enabled)
|
||||
{
|
||||
/* Call CKTnames to get names of nodes/branches used by
|
||||
BeginPlot */
|
||||
/* Probably should free nameList after this block since
|
||||
called again... */
|
||||
error = CKTnames(ckt,&numNames,&nameList);
|
||||
if(error) return(error);
|
||||
|
||||
/* We have to do a beginPlot here since the data to return is
|
||||
* different for the DCOP than it is for the AC analysis.
|
||||
* Moreover the begin plot has not even been done yet at this
|
||||
* point...
|
||||
*/
|
||||
SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
|
||||
ckt->CKTcurJob->JOBname,
|
||||
NULL, IF_REAL,
|
||||
numNames, nameList, IF_REAL,
|
||||
&acPlot);
|
||||
txfree(nameList);
|
||||
|
||||
ipc_send_dcop_prefix();
|
||||
CKTdump(ckt, 0.0, acPlot);
|
||||
ipc_send_dcop_suffix();
|
||||
|
||||
SPfrontEnd->OUTendPlot (acPlot);
|
||||
}
|
||||
/* gtri - end - wbk */
|
||||
#endif
|
||||
|
||||
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
|
||||
error = CKTload(ckt);
|
||||
if(error) return(error);
|
||||
|
||||
error = CKTnames(ckt,&numNames,&nameList);
|
||||
if(error) return(error);
|
||||
|
||||
if (ckt->CKTkeepOpInfo) {
|
||||
/* Dump operating point. */
|
||||
error = SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
|
||||
"LOOP Operating Point",
|
||||
NULL, IF_REAL,
|
||||
numNames, nameList, IF_REAL,
|
||||
&plot);
|
||||
if(error) return(error);
|
||||
CKTdump(ckt, 0.0, plot);
|
||||
SPfrontEnd->OUTendPlot (plot);
|
||||
plot = NULL;
|
||||
}
|
||||
|
||||
SPfrontEnd->IFnewUid (ckt, &freqUid, NULL, "frequency", UID_OTHER, NULL);
|
||||
error = SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
|
||||
ckt->CKTcurJob->JOBname,
|
||||
freqUid, IF_REAL,
|
||||
numNames, nameList, IF_COMPLEX,
|
||||
&acPlot);
|
||||
tfree(nameList);
|
||||
if(error) return(error);
|
||||
|
||||
if (job->LOOPstepType != LINEAR) {
|
||||
SPfrontEnd->OUTattributes (acPlot, NULL, OUT_SCALE_LOG, NULL);
|
||||
}
|
||||
freq = job->LOOPstartFreq;
|
||||
|
||||
} else { /* continue previous analysis */
|
||||
freq = job->LOOPsaveFreq;
|
||||
job->LOOPsaveFreq = 0; /* clear the 'old' frequency */
|
||||
/* fix resume? saj, indeed !*/
|
||||
error = SPfrontEnd->OUTpBeginPlot (NULL, NULL,
|
||||
NULL,
|
||||
NULL, 0,
|
||||
666, NULL, 666,
|
||||
&acPlot);
|
||||
/* saj*/
|
||||
}
|
||||
|
||||
switch (job->LOOPstepType) {
|
||||
case DECADE:
|
||||
case OCTAVE:
|
||||
freqTol = job->LOOPfreqDelta *
|
||||
job->LOOPstopFreq * ckt->CKTreltol;
|
||||
break;
|
||||
case LINEAR:
|
||||
freqTol = job->LOOPfreqDelta * ckt->CKTreltol;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
}
|
||||
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - add - wbk - 12/19/90 - Set anal_init and anal_type */
|
||||
|
||||
g_mif_info.circuit.anal_init = MIF_TRUE;
|
||||
|
||||
/* Tell the code models what mode we're in */
|
||||
g_mif_info.circuit.anal_type = MIF_AC;
|
||||
|
||||
/* gtri - end - wbk */
|
||||
#endif
|
||||
|
||||
INIT_STATS();
|
||||
|
||||
ckt->CKTcurrentAnalysis = DOING_AC;
|
||||
|
||||
/* main loop through all scheduled frequencies */
|
||||
while (freq <= job->LOOPstopFreq + freqTol) {
|
||||
if(SPfrontEnd->IFpauseTest()) {
|
||||
/* user asked us to pause via an interrupt */
|
||||
job->LOOPsaveFreq = freq;
|
||||
return(E_PAUSE);
|
||||
}
|
||||
ckt->CKTomega = 2.0 * M_PI *freq;
|
||||
|
||||
/* Update opertating point, if variable 'hertz' is given */
|
||||
if (ckt->CKTvarHertz) {
|
||||
#ifdef XSPICE
|
||||
/* Call EVTop if event-driven instances exist */
|
||||
|
||||
if(ckt->evt->counts.num_insts != 0) {
|
||||
error = EVTop(ckt,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
||||
ckt->CKTdcMaxIter,
|
||||
MIF_TRUE);
|
||||
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
|
||||
EVTop_save(ckt, MIF_TRUE, 0.0);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
// If no event-driven instances, do what SPICE normally does
|
||||
error = CKTop(ckt,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
||||
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
||||
ckt->CKTdcMaxIter);
|
||||
|
||||
if(error){
|
||||
fprintf(stdout,"\nLOOP operating point failed -\n");
|
||||
CKTncDump(ckt);
|
||||
return(error);
|
||||
}
|
||||
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
|
||||
error = CKTload(ckt);
|
||||
if(error) return(error);
|
||||
}
|
||||
|
||||
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODEAC;
|
||||
|
||||
/************* Main part starts here ********************/
|
||||
size = SMPmatSize(ckt->CKTmatrix);
|
||||
for(int simno=0;simno<2;simno++)
|
||||
{
|
||||
for (i=0; i<=size; i++)
|
||||
{
|
||||
ckt->CKTrhs[i] = 0.0;
|
||||
ckt->CKTirhs[i] = 0.0;
|
||||
}
|
||||
error = CKTacLoad(ckt);
|
||||
/* clear any AC stimulus set by acLoad */
|
||||
for (i=1; i<=size; i++)
|
||||
{
|
||||
ckt->CKTrhs[i] = 0.0;
|
||||
ckt->CKTirhs[i] = 0.0;
|
||||
}
|
||||
|
||||
switch(simno)
|
||||
{
|
||||
case 1:
|
||||
/* AC current src magn=1 from ground to Vx */
|
||||
ckt->CKTrhs[GENnode(inst)[1]] -= 1;
|
||||
ckt->CKTrhs[0] += 1;
|
||||
break;
|
||||
case 0:
|
||||
/* AC voltage src magn=1 accross probe */
|
||||
ckt->CKTrhs[probe_brnum] += 1;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
}
|
||||
|
||||
|
||||
/*error = NIacIter(ckt);*/
|
||||
error = NIdIter(ckt);
|
||||
if (error) {
|
||||
UPDATE_STATS(DOING_AC);
|
||||
return(error);
|
||||
}
|
||||
Vy[simno] = ckt->CKTrhsOld[GENnode(inst)[0]];
|
||||
iVy[simno] = ckt->CKTirhsOld[GENnode(inst)[0]];
|
||||
Iy[simno] = ckt->CKTrhsOld[probe_brnum];
|
||||
iIy[simno] = ckt->CKTirhsOld[probe_brnum];
|
||||
if(0) printf("|Vy| = %g, |Iy|=%g\n", sqrt(Vy[simno]*Vy[simno]+iVy[simno]*iVy[simno]), sqrt(Iy[simno]*Iy[simno]+iIy[simno]*iIy[simno]));
|
||||
}
|
||||
|
||||
/* V(y)@1*I(Viy)@3-V(y)@3*I(Viy)@1 */
|
||||
{
|
||||
double D, iD, T, iT, ngainsq, nphase;
|
||||
|
||||
D = Vy[0]*Iy[1] - iVy[0]*iIy[1] - Vy[1]*Iy[0] + iVy[1]*iIy[0];
|
||||
iD = Vy[0]*iIy[1] + iVy[0]*Iy[1] - Vy[1]*iIy[0] - iVy[1]*Iy[0];
|
||||
/*T = (D-iD*iD/(1-D))/(1-D+iD/(1-D));*/
|
||||
T = (D*(1-D)-iD*iD)/((1-D)*(1-D) + iD*iD);
|
||||
iT = iD/((1-D)*(1-D) + iD*iD);
|
||||
nphase = 180*atan2(-iD,-D)/M_PI;
|
||||
ngainsq = T*T + iT*iT;
|
||||
if((ngainsq-1)*(gainsq-1)<0)
|
||||
{ /* gain cross 1 - interpolate lin/log for more accurate results */
|
||||
double crossphase = (log(gainsq)*nphase -
|
||||
log(ngainsq)*phase)/(log(gainsq)-log(ngainsq));
|
||||
if(fabs(crossphase)<phasemargin)
|
||||
phasemargin = fabs(crossphase);
|
||||
/* idea: binary search/sim + for frequency where gain=1 ? */
|
||||
}
|
||||
|
||||
if(ngainsq>=1)
|
||||
if(fabs(nphase)<phasemargin)
|
||||
phasemargin = fabs(nphase);
|
||||
|
||||
if(T>0 && curTr>0)
|
||||
if(nphase * phase <= 0)
|
||||
{
|
||||
/* right quandrant and cross phase=0 */
|
||||
double gmsqlog;
|
||||
gmsqlog = (nphase*log(gainsq)-phase*log(ngainsq))/(nphase-phase);
|
||||
gainmargin = 10*gmsqlog;
|
||||
}
|
||||
|
||||
gainsq=ngainsq;
|
||||
phase=nphase;
|
||||
curTr = T; /* remember real part only */
|
||||
|
||||
if(1) printf("f=%g |T| = %g, |D|=%g, phase=%g\n", freq, sqrt(T*T+iT*iT), sqrt(D*D+iD*iD), 180*atan2(-iD,-D)/M_PI);
|
||||
}
|
||||
|
||||
|
||||
/************* End of main part ********************/
|
||||
|
||||
#ifdef WANT_SENSE2
|
||||
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode&ACSEN) ){
|
||||
long save;
|
||||
int save1;
|
||||
|
||||
save = ckt->CKTmode;
|
||||
ckt->CKTmode=(ckt->CKTmode&MODEUIC)|MODEDCOP|MODEINITSMSIG;
|
||||
save1 = ckt->CKTsenInfo->SENmode;
|
||||
ckt->CKTsenInfo->SENmode = ACSEN;
|
||||
if (freq == job->LOOPstartFreq) {
|
||||
ckt->CKTsenInfo->SENacpertflag = 1;
|
||||
}
|
||||
else{
|
||||
ckt->CKTsenInfo->SENacpertflag = 0;
|
||||
}
|
||||
error = CKTsenAC(ckt);
|
||||
if (error)
|
||||
return (error);
|
||||
ckt->CKTmode = save;
|
||||
ckt->CKTsenInfo->SENmode = save1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
|
||||
|
||||
if(g_ipc.enabled)
|
||||
ipc_send_data_prefix(freq);
|
||||
|
||||
error = CKTacDump(ckt,freq,acPlot);
|
||||
|
||||
if(g_ipc.enabled)
|
||||
ipc_send_data_suffix();
|
||||
|
||||
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
|
||||
#else
|
||||
error = CKTacDump(ckt,freq,acPlot);
|
||||
#endif
|
||||
if (error) {
|
||||
UPDATE_STATS(DOING_AC);
|
||||
return(error);
|
||||
}
|
||||
|
||||
/* increment frequency */
|
||||
|
||||
switch (job->LOOPstepType) {
|
||||
case DECADE:
|
||||
case OCTAVE:
|
||||
|
||||
/* inserted again 14.12.2001 */
|
||||
#ifdef HAS_PROGREP
|
||||
{
|
||||
double endfreq = job->LOOPstopFreq;
|
||||
double startfreq = job->LOOPstartFreq;
|
||||
endfreq = log(endfreq);
|
||||
if (startfreq == 0.0)
|
||||
startfreq = 1e-12;
|
||||
startfreq = log(startfreq);
|
||||
|
||||
if (freq > 0.0)
|
||||
SetAnalyse( "loop", (int)((log(freq)-startfreq) * 1000.0 / (endfreq-startfreq)));
|
||||
}
|
||||
#endif
|
||||
|
||||
freq *= job->LOOPfreqDelta;
|
||||
if (job->LOOPfreqDelta == 1) goto endsweep;
|
||||
break;
|
||||
case LINEAR:
|
||||
|
||||
#ifdef HAS_PROGREP
|
||||
{
|
||||
double endfreq = job->LOOPstopFreq;
|
||||
double startfreq = job->LOOPstartFreq;
|
||||
SetAnalyse( "loop", (int)((freq - startfreq)* 1000.0 / (endfreq-startfreq)));
|
||||
}
|
||||
#endif
|
||||
|
||||
freq += job->LOOPfreqDelta;
|
||||
if (job->LOOPfreqDelta == 0) goto endsweep;
|
||||
break;
|
||||
default:
|
||||
return(E_INTERN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
endsweep:
|
||||
printf("Loop analysis '%s': phase margin is %g [degrees], gain margin is %g [dB]\n", job->LOOPname ? job->LOOPname : "anon", phasemargin, gainmargin);
|
||||
|
||||
SPfrontEnd->OUTendPlot (acPlot);
|
||||
acPlot = NULL;
|
||||
UPDATE_STATS(0);
|
||||
return(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**********
|
||||
Copyright 1990 Regents of the University of California. All rights reserved.
|
||||
Author: 1988 Jaijeet S Roychowdhury
|
||||
|
||||
Copyright 2020 Anamosic Ballenegger Design. All rights reserved.
|
||||
Author: 2020 Florian Ballenegger
|
||||
**********/
|
||||
|
||||
#include "ngspice/ngspice.h"
|
||||
#include "ngspice/ifsim.h"
|
||||
#include "ngspice/iferrmsg.h"
|
||||
#include "ngspice/cktdefs.h"
|
||||
#include "ngspice/loopdefs.h"
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
LOOPaskQuest(CKTcircuit *ckt, JOB *anal, int which, IFvalue *value)
|
||||
{
|
||||
LOOPAN *job = (LOOPAN *) anal;
|
||||
|
||||
NG_IGNORE(ckt);
|
||||
|
||||
switch(which) {
|
||||
|
||||
case LOOP_START:
|
||||
value->rValue = job->LOOPstartFreq;
|
||||
break;
|
||||
|
||||
case LOOP_STOP:
|
||||
value->rValue = job->LOOPstopFreq ;
|
||||
break;
|
||||
|
||||
case LOOP_STEPS:
|
||||
value->iValue = job->LOOPnumSteps;
|
||||
break;
|
||||
|
||||
case LOOP_DEC:
|
||||
if (job->LOOPstepType == DECADE) {
|
||||
value->iValue=1;
|
||||
} else {
|
||||
value->iValue=0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOOP_OCT:
|
||||
if (job->LOOPstepType == OCTAVE) {
|
||||
value->iValue=1;
|
||||
} else {
|
||||
value->iValue=0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOOP_LIN:
|
||||
if (job->LOOPstepType == LINEAR) {
|
||||
value->iValue=1;
|
||||
} else {
|
||||
value->iValue=0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/**********
|
||||
Copyright 1990 Regents of the University of California. All rights reserved.
|
||||
Author: 1988 Jaijeet S Roychowdhury
|
||||
|
||||
Copyright 2020 Anamosic Ballenegger Design. All rights reserved.
|
||||
Author: 2020 Florian Ballenegger
|
||||
**********/
|
||||
|
||||
#include "ngspice/ngspice.h"
|
||||
#include "ngspice/ifsim.h"
|
||||
#include "ngspice/iferrmsg.h"
|
||||
#include "ngspice/cktdefs.h"
|
||||
#include "ngspice/loopdefs.h"
|
||||
|
||||
#include "analysis.h"
|
||||
|
||||
/* .loop output insrc loopbreakpoint dec/oct/lin n fstart fstop */
|
||||
|
||||
int
|
||||
LOOPsetParm(CKTcircuit *ckt, JOB *anal, int which, IFvalue *value)
|
||||
{
|
||||
LOOPAN *job = (LOOPAN *) anal;
|
||||
|
||||
NG_IGNORE(ckt);
|
||||
|
||||
switch(which) {
|
||||
|
||||
case LOOP_PROBESRC:
|
||||
job->LOOPprobeSrc = value->uValue;
|
||||
break;
|
||||
|
||||
case LOOP_START:
|
||||
if (value->rValue <= 0.0) {
|
||||
errMsg = copy("Frequency of 0 is invalid");
|
||||
job->LOOPstartFreq = 1.0;
|
||||
return(E_PARMVAL);
|
||||
}
|
||||
|
||||
job->LOOPstartFreq = value->rValue;
|
||||
break;
|
||||
|
||||
case LOOP_STOP:
|
||||
if (value->rValue <= 0.0) {
|
||||
errMsg = copy("Frequency of 0 is invalid");
|
||||
job->LOOPstartFreq = 1.0;
|
||||
return(E_PARMVAL);
|
||||
}
|
||||
|
||||
job->LOOPstopFreq = value->rValue;
|
||||
break;
|
||||
|
||||
case LOOP_STEPS:
|
||||
job->LOOPnumSteps = value->iValue;
|
||||
break;
|
||||
|
||||
case LOOP_DEC:
|
||||
job->LOOPstepType = DECADE;
|
||||
break;
|
||||
|
||||
case LOOP_OCT:
|
||||
job->LOOPstepType = OCTAVE;
|
||||
break;
|
||||
|
||||
case LOOP_LIN:
|
||||
job->LOOPstepType = LINEAR;
|
||||
break;
|
||||
|
||||
case LOOP_NAME:
|
||||
job->LOOPname = value->sValue;
|
||||
break;
|
||||
|
||||
case LOOP_DIR:
|
||||
job->LOOPdirection = value->iValue;
|
||||
break;
|
||||
|
||||
case LOOP_PORTNUM:
|
||||
job->LOOPportnum = value->iValue;
|
||||
job->LOOPportnumGiven = 1;
|
||||
break;
|
||||
|
||||
case LOOP_PORTNAME:
|
||||
job->LOOPportname = value->sValue;
|
||||
job->LOOPportnameGiven = 1;
|
||||
break;
|
||||
|
||||
case LOOP_INSRC:
|
||||
job->LOOPinSrc = value->uValue;
|
||||
job->LOOPinSrcGiven = 1;
|
||||
break;
|
||||
|
||||
case LOOP_OUTSRC:
|
||||
job->LOOPoutSrc = value->uValue;
|
||||
job->LOOPoutSrcGiven = 1;
|
||||
job->LOOPoutPosGiven = 0; /* exclusive */
|
||||
job->LOOPoutNegGiven = 0; /* exclusive */
|
||||
break;
|
||||
|
||||
case LOOP_OUTPOS:
|
||||
job->LOOPoutPos = value->nValue;
|
||||
job->LOOPoutPosGiven = 1;
|
||||
job->LOOPoutSrcGiven = 0; /* exclusive */
|
||||
break;
|
||||
|
||||
case LOOP_OUTNEG:
|
||||
job->LOOPoutNeg = value->nValue;
|
||||
job->LOOPoutNegGiven = 1;
|
||||
job->LOOPoutSrcGiven = 0; /* exclusive */
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
printf("loopsetp Badparam %d\n", which);
|
||||
return(E_BADPARM);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
static IFparm LOOPparms[] = {
|
||||
{ "start", LOOP_START, IF_SET|IF_REAL, "starting frequency" },
|
||||
{ "stop", LOOP_STOP, IF_SET|IF_REAL, "ending frequency" },
|
||||
{ "numsteps", LOOP_STEPS, IF_SET|IF_INTEGER, "number of frequencies" },
|
||||
{ "dec", LOOP_DEC, IF_SET|IF_FLAG, "step by decades" },
|
||||
{ "oct", LOOP_OCT, IF_SET|IF_FLAG, "step by octaves" },
|
||||
{ "lin", LOOP_LIN, IF_SET|IF_FLAG, "step linearly" },
|
||||
{ "name", LOOP_NAME, IF_SET|IF_STRING, "analysis name" },
|
||||
{ "probe", LOOP_PROBESRC,IF_SET|IF_INSTANCE, "probe inloop" },
|
||||
{ "portnum", LOOP_PORTNUM, IF_SET|IF_INTEGER, "terminal number for breaking the loop" },
|
||||
{ "portname", LOOP_PORTNAME, IF_SET|IF_STRING, "terminal name for breaking the loop" },
|
||||
{ "dir", LOOP_DIR, IF_SET|IF_INTEGER, "loop direction" },
|
||||
{ "probe-", LOOP_PROBESRC,IF_SET|IF_INSTANCE, "probe inloop (diff)" },
|
||||
{ "portnum-", LOOP_PORTNUM, IF_SET|IF_INTEGER, "terminal number for breaking the loop (diff)" },
|
||||
{ "portname-", LOOP_PORTNAME, IF_SET|IF_STRING, "terminal name for breaking the loop (diff)" },
|
||||
{ "dir-", LOOP_DIR, IF_SET|IF_INTEGER, "loop direction (diff)" },
|
||||
{ "insrc", LOOP_INSRC, IF_SET|IF_INSTANCE, "input source" },
|
||||
{ "outsrc", LOOP_OUTSRC, IF_SET|IF_INSTANCE, "output source branch" },
|
||||
{ "outpos", LOOP_OUTPOS, IF_SET|IF_NODE, "Positive output node" },
|
||||
{ "outneg", LOOP_OUTNEG, IF_SET|IF_NODE, "Negative output node" },
|
||||
};
|
||||
|
||||
SPICEanalysis LOOPinfo = {
|
||||
{
|
||||
"LOOP",
|
||||
"Small signal loop stability analysis",
|
||||
|
||||
NUMELEMS(LOOPparms),
|
||||
LOOPparms
|
||||
},
|
||||
sizeof(LOOPAN),
|
||||
FREQUENCYDOMAIN,
|
||||
1,
|
||||
LOOPsetParm,
|
||||
LOOPaskQuest,
|
||||
NULL,
|
||||
LOOPan,
|
||||
LOOPpreset
|
||||
};
|
||||
|
|
@ -16,15 +16,46 @@ Modified: 2000 AlansFixes
|
|||
|
||||
/* F.B. 2020
|
||||
parse additionnal parameters in the form key=val.
|
||||
Use analysis INFO IFparm list to check if key is recognized and
|
||||
the type of val.
|
||||
Use from analysis info IFparm list to check if key is recognized and
|
||||
to extract the type of value.
|
||||
*/
|
||||
extern IFanalysis *analInfo[]; /* SPICEanalysis and IFanalysis must be compatible */
|
||||
|
||||
static int
|
||||
dot_params(char *line, CKTcircuit *ckt, int ana, JOB* job)
|
||||
dot_params(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, JOB* job)
|
||||
{
|
||||
char *name; /* the resistor's name */
|
||||
int error; /* error code temporary */
|
||||
char *name;
|
||||
int i;
|
||||
IFvalue ptemp; /* a value structure */
|
||||
IFvalue *parm;
|
||||
IFanalysis *ana = analInfo[job->JOBtype];
|
||||
|
||||
while(*line)
|
||||
{
|
||||
INPgetTok(&line, &name, 0);
|
||||
if(*line!='=') {
|
||||
printf("Expect param=val format for %s, got %c\n", name, *line);
|
||||
LITERR("Expect param=val format\n");
|
||||
return(0);
|
||||
}
|
||||
line++; /* skip '=' */
|
||||
for(i=0;i<ana->numParms;i++)
|
||||
if(strcmp(name, ana->analysisParms[i].keyword)==0)
|
||||
break;
|
||||
tfree(name); /* don't it need anymore, we still know the keyword */
|
||||
if(i<ana->numParms)
|
||||
{
|
||||
int dataType = ana->analysisParms[i].dataType;
|
||||
if(0) printf("will set parameter %s to analysis\n", ana->analysisParms[i].keyword);
|
||||
parm = INPgetValue(ckt, &line, dataType, tab);
|
||||
GCA(INPapName, (ckt, job->JOBtype, job, ana->analysisParms[i].keyword, parm));
|
||||
}
|
||||
else
|
||||
{
|
||||
LITERR("Unrecognized parameter\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -662,6 +693,14 @@ int error; /* error code temporary */
|
|||
|
||||
if(*line)
|
||||
{
|
||||
char* nline;
|
||||
char* nname;
|
||||
|
||||
nline=line;
|
||||
INPgetTok(&nline, &nname, 0);
|
||||
tfree(nname);
|
||||
if(*nline=='=') goto end; /* was actually a key=value parameter */
|
||||
|
||||
/* input & output follows for transfert function calculations */
|
||||
INPgetTok(&line, &name, 1);
|
||||
INPinsert(&name, tab);
|
||||
|
|
@ -693,6 +732,8 @@ int error; /* error code temporary */
|
|||
}
|
||||
|
||||
}
|
||||
end:
|
||||
dot_params(line, ckt, tab, current, foo);
|
||||
|
||||
return (0);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue