From 17b1eec2aa47cd98d510d0cdbfba19e1121b35d4 Mon Sep 17 00:00:00 2001 From: Florian Ballenegger Date: Fri, 4 Sep 2020 15:44:23 +0200 Subject: [PATCH] working an loop analysis --- src/include/ngspice/loopdefs.h | 99 ++++++ src/spicelib/analysis/cktnewan.c | 2 + src/spicelib/analysis/loopan.c | 586 +++++++++++++++++++++++++++++++ src/spicelib/analysis/loopaskq.c | 66 ++++ src/spicelib/analysis/loopsetp.c | 156 ++++++++ src/spicelib/parser/inp2dot.c | 49 ++- 6 files changed, 954 insertions(+), 4 deletions(-) create mode 100644 src/include/ngspice/loopdefs.h create mode 100644 src/spicelib/analysis/loopan.c create mode 100644 src/spicelib/analysis/loopaskq.c create mode 100644 src/spicelib/analysis/loopsetp.c diff --git a/src/include/ngspice/loopdefs.h b/src/include/ngspice/loopdefs.h new file mode 100644 index 000000000..178d0e01b --- /dev/null +++ b/src/include/ngspice/loopdefs.h @@ -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 diff --git a/src/spicelib/analysis/cktnewan.c b/src/spicelib/analysis/cktnewan.c index f705caded..a004779d4 100644 --- a/src/spicelib/analysis/cktnewan.c +++ b/src/spicelib/analysis/cktnewan.c @@ -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; diff --git a/src/spicelib/analysis/loopan.c b/src/spicelib/analysis/loopan.c new file mode 100644 index 000000000..422136ee3 --- /dev/null +++ b/src/spicelib/analysis/loopan.c @@ -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)=1) + if(fabs(nphase)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); +} diff --git a/src/spicelib/analysis/loopaskq.c b/src/spicelib/analysis/loopaskq.c new file mode 100644 index 000000000..b53e5ad7c --- /dev/null +++ b/src/spicelib/analysis/loopaskq.c @@ -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); +} diff --git a/src/spicelib/analysis/loopsetp.c b/src/spicelib/analysis/loopsetp.c new file mode 100644 index 000000000..a5b0e5b26 --- /dev/null +++ b/src/spicelib/analysis/loopsetp.c @@ -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 +}; diff --git a/src/spicelib/parser/inp2dot.c b/src/spicelib/parser/inp2dot.c index a68206ea1..d4833c166 100644 --- a/src/spicelib/parser/inp2dot.c +++ b/src/spicelib/parser/inp2dot.c @@ -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;inumParms;i++) + if(strcmp(name, ana->analysisParms[i].keyword)==0) + break; + tfree(name); /* don't it need anymore, we still know the keyword */ + if(inumParms) + { + 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);