working an loop analysis

This commit is contained in:
Florian Ballenegger 2020-09-04 15:44:23 +02:00
parent 4f076ed210
commit 17b1eec2aa
6 changed files with 954 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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