2000-04-27 22:03:57 +02:00
|
|
|
/**********
|
|
|
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
|
|
|
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
|
|
|
|
|
**********/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Interface routines. These are specific to spice. The only changes to FTE
|
|
|
|
|
* that should be needed to make FTE work with a different simulator is
|
|
|
|
|
* to rewrite this file. What each routine is expected to do can be
|
|
|
|
|
* found in the programmer's manual. This file should be the only one
|
|
|
|
|
* that includes ngspice.header files.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "ngspice.h"
|
|
|
|
|
#include "cpdefs.h"
|
|
|
|
|
#include "ftedefs.h"
|
|
|
|
|
#include "fteinp.h"
|
|
|
|
|
#include "inpdefs.h"
|
|
|
|
|
#include "iferrmsg.h"
|
|
|
|
|
#include "ifsim.h"
|
|
|
|
|
|
2000-06-27 18:09:02 +02:00
|
|
|
#include "circuits.h"
|
|
|
|
|
#include "spiceif.h"
|
|
|
|
|
#include "variable.h"
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
/* static declarations */
|
|
|
|
|
static struct variable * parmtovar(IFvalue *pv, IFparm *opt);
|
|
|
|
|
static IFparm * parmlookup(IFdevice *dev, GENinstance **inptr, char *param,
|
|
|
|
|
int do_model, int inout);
|
|
|
|
|
static IFvalue * doask(void *ckt, int typecode, GENinstance *dev, GENmodel *mod,
|
|
|
|
|
IFparm *opt, int ind);
|
|
|
|
|
static int doset(void *ckt, int typecode, GENinstance *dev, GENmodel *mod,
|
|
|
|
|
IFparm *opt, struct dvec *val);
|
|
|
|
|
static int finddev(void *ck, char *name, void **devptr, void **modptr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Input a single deck, and return a pointer to the circuit. */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
if_inpdeck(struct line *deck, INPtables **tab)
|
|
|
|
|
{
|
|
|
|
|
void *ckt;
|
|
|
|
|
int err, i, j;
|
|
|
|
|
struct line *ll;
|
|
|
|
|
IFuid taskUid;
|
|
|
|
|
IFuid optUid;
|
|
|
|
|
int which = -1;
|
|
|
|
|
|
|
|
|
|
for (i = 0, ll = deck; ll; ll = ll->li_next)
|
|
|
|
|
i++;
|
|
|
|
|
*tab = INPtabInit(i);
|
|
|
|
|
ft_curckt->ci_symtab = *tab;
|
|
|
|
|
if ((err = (*(ft_sim->newCircuit))(&ckt))
|
|
|
|
|
!= OK) {
|
|
|
|
|
ft_sperror(err, "CKTinit");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
err = IFnewUid(ckt,&taskUid,(IFuid)NULL,"default",UID_TASK,(void**)NULL);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newUid");
|
|
|
|
|
return(NULL);
|
|
|
|
|
}
|
|
|
|
|
err = (*(ft_sim->newTask))(ckt,(void**)&(ft_curckt->ci_defTask),taskUid);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newTask");
|
|
|
|
|
return(NULL);
|
|
|
|
|
}
|
|
|
|
|
for(j=0;j<ft_sim->numAnalyses;j++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[j]->name,"options")==0) {
|
|
|
|
|
which = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(which != -1) {
|
|
|
|
|
err = IFnewUid(ckt,&optUid,(IFuid)NULL,"options",UID_ANALYSIS,
|
|
|
|
|
(void**)NULL);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newUid");
|
|
|
|
|
return(NULL);
|
|
|
|
|
}
|
|
|
|
|
err = (*(ft_sim->newAnalysis))(ft_curckt->ci_ckt,which,optUid,
|
|
|
|
|
(void**)&(ft_curckt->ci_defOpt),
|
|
|
|
|
(void*)ft_curckt->ci_defTask);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"createOptions");
|
|
|
|
|
return(NULL);
|
|
|
|
|
}
|
|
|
|
|
ft_curckt->ci_curOpt = ft_curckt->ci_defOpt;
|
|
|
|
|
}
|
|
|
|
|
ft_curckt->ci_curTask = ft_curckt->ci_defTask;
|
|
|
|
|
INPpas1((void *) ckt, (card *) deck->li_next,(INPtables *)*tab);
|
|
|
|
|
INPpas2((void *) ckt, (card *) deck->li_next,
|
|
|
|
|
(INPtables *) *tab,ft_curckt->ci_defTask);
|
|
|
|
|
INPkillMods();
|
|
|
|
|
return (ckt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do a run of the circuit, of the given type. Type "resume" is special --
|
|
|
|
|
* it means to resume whatever simulation that was in progress. The
|
|
|
|
|
* return value of this routine is 0 if the exit was ok, and 1 if there was
|
|
|
|
|
* a reason to interrupt the circuit (interrupt typed at the keyboard,
|
|
|
|
|
* error in the simulation, etc). args should be the entire command line,
|
|
|
|
|
* e.g. "tran 1 10 20 uic"
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
if_run(char *t, char *what, wordlist *args, char *tab)
|
|
|
|
|
{
|
|
|
|
|
void *ckt = (void *) t;
|
|
|
|
|
int err;
|
|
|
|
|
struct line deck;
|
|
|
|
|
char buf[BSIZE_SP];
|
|
|
|
|
int j;
|
|
|
|
|
int which = -1;
|
|
|
|
|
IFuid specUid,optUid;
|
|
|
|
|
|
|
|
|
|
/* First parse the line... */
|
|
|
|
|
if (eq(what, "tran") || eq(what, "ac") || eq(what, "dc")
|
|
|
|
|
|| eq(what, "op") || eq(what, "pz") || eq(what,"disto")
|
|
|
|
|
|| eq(what, "adjsen") || eq(what, "sens") || eq(what,"tf")
|
|
|
|
|
|| eq(what, "noise")) {
|
|
|
|
|
(void) sprintf(buf, ".%s", wl_flatten(args));
|
|
|
|
|
deck.li_next = deck.li_actual = NULL;
|
|
|
|
|
deck.li_error = NULL;
|
|
|
|
|
deck.li_linenum = 0;
|
|
|
|
|
deck.li_line = buf;
|
|
|
|
|
if(ft_curckt->ci_specTask) {
|
|
|
|
|
err=(*(ft_sim->deleteTask))(ft_curckt->ci_ckt,
|
|
|
|
|
ft_curckt->ci_specTask);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"deleteTask");
|
|
|
|
|
return(2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err = IFnewUid(ft_curckt->ci_ckt,&specUid,(IFuid)NULL,"special",
|
|
|
|
|
UID_TASK,(void**)NULL);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newUid");
|
|
|
|
|
return(2);
|
|
|
|
|
}
|
|
|
|
|
err = (*(ft_sim->newTask))(ft_curckt->ci_ckt,
|
|
|
|
|
(void**)&(ft_curckt->ci_specTask),specUid);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newTask");
|
|
|
|
|
return(2);
|
|
|
|
|
}
|
|
|
|
|
for(j=0;j<ft_sim->numAnalyses;j++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[j]->name,"options")==0) {
|
|
|
|
|
which = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(which != -1) {
|
|
|
|
|
err = IFnewUid(ft_curckt->ci_ckt,&optUid,(IFuid)NULL,"options",
|
|
|
|
|
UID_ANALYSIS,(void**)NULL);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"newUid");
|
|
|
|
|
return(2);
|
|
|
|
|
}
|
|
|
|
|
err = (*(ft_sim->newAnalysis))(ft_curckt->ci_ckt,which,optUid,
|
|
|
|
|
(void**)&(ft_curckt->ci_specOpt),
|
|
|
|
|
(void*)ft_curckt->ci_specTask);
|
|
|
|
|
if(err) {
|
|
|
|
|
ft_sperror(err,"createOptions");
|
|
|
|
|
return(2);
|
|
|
|
|
}
|
|
|
|
|
ft_curckt->ci_curOpt = ft_curckt->ci_specOpt;
|
|
|
|
|
}
|
|
|
|
|
ft_curckt->ci_curTask = ft_curckt->ci_specTask;
|
|
|
|
|
INPpas2(ckt, (card *) &deck, (INPtables *)tab, ft_curckt->ci_specTask);
|
|
|
|
|
if (deck.li_error) {
|
|
|
|
|
fprintf(cp_err, "Warning: %s\n", deck.li_error);
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if( eq(what,"run") ) {
|
|
|
|
|
ft_curckt->ci_curTask = ft_curckt->ci_defTask;
|
|
|
|
|
ft_curckt->ci_curOpt = ft_curckt->ci_defOpt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( (eq(what, "tran")) ||
|
|
|
|
|
(eq(what, "ac")) ||
|
|
|
|
|
(eq(what, "dc")) ||
|
|
|
|
|
(eq(what, "op")) ||
|
|
|
|
|
(eq(what, "pz")) ||
|
|
|
|
|
(eq(what, "disto")) ||
|
|
|
|
|
(eq(what, "noise")) ||
|
|
|
|
|
eq(what, "adjsen") || eq(what, "sens") || eq(what,"tf") ||
|
|
|
|
|
(eq(what, "run")) ) {
|
|
|
|
|
if ((err = (*(ft_sim->doAnalyses))(ckt, 1, ft_curckt->ci_curTask))!=OK){
|
|
|
|
|
ft_sperror(err, "doAnalyses");
|
|
|
|
|
/* wrd_end(); */
|
|
|
|
|
if (err == E_PAUSE)
|
|
|
|
|
return (1);
|
|
|
|
|
else
|
|
|
|
|
return (2);
|
|
|
|
|
}
|
|
|
|
|
} else if (eq(what, "resume")) {
|
|
|
|
|
if ((err = (*(ft_sim->doAnalyses))(ckt, 0, ft_curckt->ci_curTask))!=OK){
|
|
|
|
|
ft_sperror(err, "doAnalyses");
|
|
|
|
|
/* wrd_end(); */
|
|
|
|
|
if (err == E_PAUSE)
|
|
|
|
|
return (1);
|
|
|
|
|
else
|
|
|
|
|
return (2);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(cp_err, "if_run: Internal Error: bad run type %s\n",
|
|
|
|
|
what);
|
|
|
|
|
return (2);
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set an option in the circuit. Arguments are option name, type, and
|
|
|
|
|
* value (the last a char *), suitable for casting to whatever needed...
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static char *unsupported[] = {
|
|
|
|
|
"itl3",
|
|
|
|
|
"itl5",
|
|
|
|
|
"lvltim",
|
|
|
|
|
"maxord",
|
|
|
|
|
"method",
|
|
|
|
|
NULL
|
|
|
|
|
} ;
|
|
|
|
|
|
|
|
|
|
static char *obsolete[] = {
|
|
|
|
|
"limpts",
|
|
|
|
|
"limtim",
|
|
|
|
|
"lvlcod",
|
|
|
|
|
NULL
|
|
|
|
|
} ;
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
if_option(void *ckt, char *name, int type, char *value)
|
|
|
|
|
{
|
|
|
|
|
IFvalue pval;
|
|
|
|
|
int err, i;
|
|
|
|
|
void *cc = (void *) ckt;
|
|
|
|
|
char **vv;
|
|
|
|
|
int which = -1;
|
|
|
|
|
|
|
|
|
|
if (eq(name, "acct")) {
|
|
|
|
|
ft_acctprint = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (eq(name, "list")) {
|
|
|
|
|
ft_listprint = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (eq(name, "node")) {
|
|
|
|
|
ft_nodesprint = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (eq(name, "opts")) {
|
|
|
|
|
ft_optsprint = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (eq(name, "nopage")) {
|
|
|
|
|
ft_nopage = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (eq(name, "nomod")) {
|
|
|
|
|
ft_nomod = TRUE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(i=0;i<ft_sim->numAnalyses;i++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[i]->name,"options")==0) {
|
|
|
|
|
which = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(which==-1) {
|
|
|
|
|
fprintf(cp_err,"Warning: .options line unsupported\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ft_sim->analyses[which]->numParms; i++)
|
|
|
|
|
if (eq(ft_sim->analyses[which]->analysisParms[i].keyword, name) &&
|
|
|
|
|
(ft_sim->analyses[which]->analysisParms[i].dataType & IF_SET))
|
|
|
|
|
break;
|
|
|
|
|
if (i == ft_sim->analyses[which]->numParms) {
|
|
|
|
|
/* See if this is unsupported or obsolete. */
|
|
|
|
|
for (vv = unsupported; *vv; vv++)
|
|
|
|
|
if (eq(name, *vv)) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Warning: option %s is currently unsupported.\n", name);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
for (vv = obsolete; *vv; vv++)
|
|
|
|
|
if (eq(name, *vv)) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Warning: option %s is obsolete.\n", name);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ft_sim->analyses[which]->analysisParms[i].dataType & IF_VARTYPES) {
|
|
|
|
|
case IF_REAL:
|
|
|
|
|
if (type == VT_REAL)
|
|
|
|
|
pval.rValue = *((double *) value);
|
|
|
|
|
else if (type == VT_NUM)
|
|
|
|
|
pval.rValue = *((int *) value);
|
|
|
|
|
else
|
|
|
|
|
goto badtype;
|
|
|
|
|
break;
|
|
|
|
|
case IF_INTEGER:
|
|
|
|
|
if (type == VT_NUM)
|
|
|
|
|
pval.iValue = *((int *) value);
|
|
|
|
|
else if (type == VT_REAL)
|
|
|
|
|
pval.iValue = *((double *) value);
|
|
|
|
|
else
|
|
|
|
|
goto badtype;
|
|
|
|
|
break;
|
|
|
|
|
case IF_STRING:
|
|
|
|
|
if (type == VT_STRING)
|
|
|
|
|
pval.sValue = copy(value);
|
|
|
|
|
else
|
|
|
|
|
goto badtype;
|
|
|
|
|
break;
|
|
|
|
|
case IF_FLAG:
|
|
|
|
|
/* Do nothing. */
|
|
|
|
|
pval.iValue = *((int *) value);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"if_option: Internal Error: bad option type %d.\n",
|
|
|
|
|
ft_sim->analyses[which]->analysisParms[i].dataType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ckt) {
|
|
|
|
|
/* XXX No circuit loaded */
|
|
|
|
|
fprintf(cp_err, "Simulation parameter \"%s\" can't be set until\n",
|
|
|
|
|
name);
|
|
|
|
|
fprintf(cp_err, "a circuit has been loaded.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((err = (*(ft_sim->setAnalysisParm))(cc, (void *)ft_curckt->ci_curOpt,
|
|
|
|
|
ft_sim->analyses[which]->analysisParms[i].id, &pval,
|
|
|
|
|
(IFvalue *)NULL)) != OK)
|
|
|
|
|
ft_sperror(err, "setAnalysisParm(options)");
|
|
|
|
|
return 1;
|
|
|
|
|
badtype:
|
|
|
|
|
fprintf(cp_err, "Error: bad type given for option %s --\n", name);
|
|
|
|
|
fprintf(cp_err, "\ttype given was ");
|
|
|
|
|
switch (type) {
|
|
|
|
|
case VT_BOOL: fputs("boolean", cp_err); break;
|
|
|
|
|
case VT_NUM: fputs("integer", cp_err); break;
|
|
|
|
|
case VT_REAL: fputs("real", cp_err); break;
|
|
|
|
|
case VT_STRING: fputs("string", cp_err); break;
|
|
|
|
|
case VT_LIST: fputs("list", cp_err); break;
|
|
|
|
|
default: fputs("something strange", cp_err); break;
|
|
|
|
|
}
|
|
|
|
|
fprintf(cp_err, ", type expected was ");
|
|
|
|
|
switch(ft_sim->analyses[which]->analysisParms[i].dataType & IF_VARTYPES) {
|
|
|
|
|
case IF_REAL: fputs("real.\n", cp_err); break;
|
|
|
|
|
case IF_INTEGER:fputs("integer.\n", cp_err); break;
|
|
|
|
|
case IF_STRING: fputs("string.\n", cp_err); break;
|
|
|
|
|
case IF_FLAG: fputs("flag.\n", cp_err); break;
|
|
|
|
|
default: fputs("something strange.\n", cp_err); break;
|
|
|
|
|
}
|
|
|
|
|
if (type == VT_BOOL)
|
|
|
|
|
fputs("\t(Note that you must use an = to separate option name and value.)\n",
|
|
|
|
|
cp_err);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
if_dump(void *ckt, FILE *file)
|
|
|
|
|
{
|
|
|
|
|
/*void *cc = (void *) ckt;*/
|
|
|
|
|
|
|
|
|
|
fprintf(file,"diagnostic output dump unavailable.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
if_cktfree(void *ckt, char *tab)
|
|
|
|
|
{
|
|
|
|
|
void *cc = (void *) ckt;
|
|
|
|
|
|
|
|
|
|
(*(ft_sim->deleteCircuit))(cc);
|
|
|
|
|
INPtabEnd((INPtables *) tab);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a string describing an error code. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* BLOW THIS AWAY.... */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
if_errstring(int code)
|
|
|
|
|
{
|
|
|
|
|
return (INPerror(code));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get a parameter value from the circuit. If name is left unspecified,
|
|
|
|
|
* we want a circuit parameter.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct variable *
|
|
|
|
|
spif_getparam(void *ckt, char **name, char *param, int ind, int do_model)
|
|
|
|
|
{
|
|
|
|
|
struct variable *vv = NULL, *tv;
|
|
|
|
|
IFvalue *pv;
|
|
|
|
|
IFparm *opt;
|
|
|
|
|
int typecode, i;
|
|
|
|
|
GENinstance *dev=(GENinstance *)NULL;
|
|
|
|
|
GENmodel *mod=(GENmodel *)NULL;
|
|
|
|
|
IFdevice *device;
|
|
|
|
|
|
|
|
|
|
/* fprintf(cp_err, "Calling if_getparam(%s, %s)\n", *name, param); */
|
|
|
|
|
|
|
|
|
|
if (param && eq(param, "all")) {
|
|
|
|
|
|
|
|
|
|
/* MW. My "special routine here" */
|
|
|
|
|
INPretrieve(name,(INPtables *)ft_curckt->ci_symtab);
|
|
|
|
|
|
|
|
|
|
typecode = finddev(ckt, *name,(void**) &dev,(void **) &mod);
|
|
|
|
|
if (typecode == -1) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Error: no such device or model name %s\n",
|
|
|
|
|
*name);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
device = ft_sim->devices[typecode];
|
|
|
|
|
for (i = 0; i < *(device->numInstanceParms); i++) {
|
|
|
|
|
opt = &device->instanceParms[i];
|
|
|
|
|
if(opt->dataType & IF_REDUNDANT || !opt->description)
|
|
|
|
|
continue;
|
|
|
|
|
if(!(opt->dataType & IF_ASK)) continue;
|
|
|
|
|
pv = doask(ckt, typecode, dev, mod, opt, ind);
|
|
|
|
|
if (pv) {
|
|
|
|
|
tv = parmtovar(pv, opt);
|
|
|
|
|
if (vv)
|
|
|
|
|
tv->va_next = vv;
|
|
|
|
|
vv = tv;
|
|
|
|
|
} else
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Internal Error: no parameter '%s' on device '%s'\n",
|
|
|
|
|
device->instanceParms[i].keyword,
|
|
|
|
|
device->name);
|
|
|
|
|
}
|
|
|
|
|
return (vv);
|
|
|
|
|
} else if (param) {
|
|
|
|
|
|
|
|
|
|
/* MW. */
|
|
|
|
|
INPretrieve(name,(INPtables *)ft_curckt->ci_symtab);
|
|
|
|
|
typecode = finddev(ckt, *name, (void**)&dev, (void **)&mod);
|
|
|
|
|
if (typecode == -1) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Error: no such device or model name %s\n",
|
|
|
|
|
*name);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
device = ft_sim->devices[typecode];
|
|
|
|
|
opt = parmlookup(device, &dev, param, do_model, 0);
|
|
|
|
|
if (!opt) {
|
|
|
|
|
fprintf(cp_err, "Error: no such parameter %s.\n",
|
|
|
|
|
param);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
pv = doask(ckt, typecode, dev, mod, opt, ind);
|
|
|
|
|
if (pv)
|
|
|
|
|
vv = parmtovar(pv, opt);
|
|
|
|
|
return (vv);
|
|
|
|
|
} else
|
|
|
|
|
return (if_getstat(ckt, *name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
if_setparam(void *ckt, char **name, char *param, struct dvec *val, int do_model)
|
|
|
|
|
{
|
|
|
|
|
IFparm *opt;
|
|
|
|
|
IFdevice *device;
|
|
|
|
|
GENmodel *mod=(GENmodel *)NULL;
|
|
|
|
|
GENinstance *dev=(GENinstance *)NULL;
|
|
|
|
|
int typecode;
|
|
|
|
|
|
|
|
|
|
/* PN */
|
|
|
|
|
INPretrieve(name,(INPtables *)ft_curckt->ci_symtab);
|
|
|
|
|
typecode = finddev(ckt, *name, (void**)&dev, (void **)&mod);
|
|
|
|
|
if (typecode == -1) {
|
|
|
|
|
fprintf(cp_err, "Error: no such device or model name %s\n", *name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
device = ft_sim->devices[typecode];
|
|
|
|
|
opt = parmlookup(device, &dev, param, do_model, 1);
|
|
|
|
|
if (!opt) {
|
|
|
|
|
if (param)
|
|
|
|
|
fprintf(cp_err, "Error: no such parameter %s.\n", param);
|
|
|
|
|
else
|
|
|
|
|
fprintf(cp_err, "Error: no default parameter.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (do_model && !mod) {
|
|
|
|
|
mod = dev->GENmodPtr;
|
|
|
|
|
dev = (GENinstance *)NULL;
|
|
|
|
|
}
|
|
|
|
|
doset(ckt, typecode, dev, mod, opt, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct variable *
|
|
|
|
|
parmtovar(IFvalue *pv, IFparm *opt)
|
|
|
|
|
{
|
|
|
|
|
struct variable *vv = alloc(struct variable);
|
|
|
|
|
struct variable *nv;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
switch (opt->dataType & IF_VARTYPES) {
|
|
|
|
|
case IF_INTEGER:
|
|
|
|
|
vv->va_type = VT_NUM;
|
|
|
|
|
vv->va_num = pv->iValue;
|
|
|
|
|
break;
|
|
|
|
|
case IF_REAL:
|
|
|
|
|
case IF_COMPLEX:
|
|
|
|
|
vv->va_type = VT_REAL;
|
|
|
|
|
vv->va_real = pv->rValue;
|
|
|
|
|
break;
|
|
|
|
|
case IF_STRING:
|
|
|
|
|
vv->va_type = VT_STRING;
|
|
|
|
|
vv->va_string = pv->sValue;
|
|
|
|
|
break;
|
|
|
|
|
case IF_FLAG:
|
|
|
|
|
vv->va_type = VT_BOOL;
|
|
|
|
|
vv->va_bool = pv->iValue ? TRUE : FALSE;
|
|
|
|
|
break;
|
|
|
|
|
case IF_REALVEC:
|
|
|
|
|
vv->va_type = VT_LIST;
|
|
|
|
|
for (i = 0; i < pv->v.numValue; i++) {
|
|
|
|
|
nv = alloc(struct variable);
|
|
|
|
|
nv->va_next = vv->va_vlist;
|
|
|
|
|
vv->va_vlist = nv;
|
|
|
|
|
nv->va_type = VT_REAL;
|
|
|
|
|
nv->va_real = pv->v.vec.rVec[i];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"parmtovar: Internal Error: bad PARM type %d.\n",
|
|
|
|
|
opt->dataType);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* It's not clear whether we want the keyword or the desc here... */
|
|
|
|
|
vv->va_name = copy(opt->description);
|
|
|
|
|
vv->va_next = NULL;
|
|
|
|
|
return (vv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Extract the IFparm structure from the device. If isdev is TRUE, then get
|
|
|
|
|
* the DEVmodQuest, otherwise get the DEVquest.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static IFparm *
|
|
|
|
|
parmlookup(IFdevice *dev, GENinstance **inptr, char *param, int do_model, int inout)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* fprintf(cp_err, "Called: parmlookup(%x, %c, %s)\n",
|
|
|
|
|
dev, isdev, param); */
|
|
|
|
|
|
|
|
|
|
/* First try the device questions... */
|
|
|
|
|
if (!do_model && dev->numInstanceParms) {
|
|
|
|
|
for (i = 0; i < *(dev->numInstanceParms); i++) {
|
|
|
|
|
if (!param && (dev->instanceParms[i].dataType & IF_PRINCIPAL))
|
|
|
|
|
return (&dev->instanceParms[i]);
|
|
|
|
|
else if (!param)
|
|
|
|
|
continue;
|
|
|
|
|
else if ((((dev->instanceParms[i].dataType & IF_SET) && inout == 1)
|
|
|
|
|
|| ((dev->instanceParms[i].dataType & IF_ASK) && inout == 0))
|
|
|
|
|
&& eq(dev->instanceParms[i].keyword, param))
|
|
|
|
|
{
|
|
|
|
|
if (dev->instanceParms[i].dataType & IF_REDUNDANT)
|
|
|
|
|
i -= 1;
|
|
|
|
|
return (&dev->instanceParms[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dev->numModelParms) {
|
|
|
|
|
for (i = 0; i < *(dev->numModelParms); i++)
|
|
|
|
|
if ((((dev->modelParms[i].dataType & IF_SET) && inout == 1)
|
|
|
|
|
|| ((dev->modelParms[i].dataType & IF_ASK) && inout == 0))
|
|
|
|
|
&& eq(dev->modelParms[i].keyword, param))
|
|
|
|
|
{
|
|
|
|
|
if (dev->modelParms[i].dataType & IF_REDUNDANT)
|
|
|
|
|
i -= 1;
|
|
|
|
|
return (&dev->modelParms[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform the CKTask call. We have both 'fast' and 'modfast', so the other
|
|
|
|
|
* parameters aren't necessary.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static IFvalue *
|
|
|
|
|
doask(void *ckt, int typecode, GENinstance *dev, GENmodel *mod, IFparm *opt, int ind)
|
|
|
|
|
{
|
|
|
|
|
static IFvalue pv;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
pv.iValue = ind; /* Sometimes this will be junk and ignored... */
|
|
|
|
|
|
|
|
|
|
/* fprintf(cp_err, "Calling doask(%d, %x, %x, %x)\n",
|
|
|
|
|
typecode, dev, mod, opt); */
|
|
|
|
|
if (dev)
|
|
|
|
|
err = (*(ft_sim->askInstanceQuest))((void *)ckt, (void *)dev,
|
|
|
|
|
opt->id, &pv, (IFvalue *)NULL);
|
|
|
|
|
else
|
|
|
|
|
err = (*(ft_sim->askModelQuest))((void*)ckt, (void *) mod,
|
|
|
|
|
opt->id, &pv, (IFvalue *)NULL);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
ft_sperror(err, "if_getparam");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (&pv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform the CKTset call. We have both 'fast' and 'modfast', so the other
|
|
|
|
|
* parameters aren't necessary.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
doset(void *ckt, int typecode, GENinstance *dev, GENmodel *mod, IFparm *opt, struct dvec *val)
|
|
|
|
|
{
|
|
|
|
|
IFvalue nval;
|
|
|
|
|
int err;
|
|
|
|
|
int n;
|
|
|
|
|
int *iptr;
|
|
|
|
|
double *dptr;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Count items */
|
|
|
|
|
if (opt->dataType & IF_VECTOR) {
|
|
|
|
|
n = nval.v.numValue = val->v_length;
|
|
|
|
|
|
|
|
|
|
dptr = val->v_realdata;
|
|
|
|
|
/* XXXX compdata!!! */
|
|
|
|
|
|
|
|
|
|
switch (opt->dataType & (IF_VARTYPES & ~IF_VECTOR)) {
|
|
|
|
|
case IF_FLAG:
|
|
|
|
|
case IF_INTEGER:
|
|
|
|
|
iptr = nval.v.vec.iVec = NEWN(int, n);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
|
*iptr++ = *dptr++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IF_REAL:
|
|
|
|
|
nval.v.vec.rVec = val->v_realdata;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Can't assign value to \"%s\" (unsupported vector type)\n",
|
|
|
|
|
opt->keyword);
|
|
|
|
|
return E_UNSUPP;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
switch (opt->dataType & IF_VARTYPES) {
|
|
|
|
|
case IF_FLAG:
|
|
|
|
|
case IF_INTEGER:
|
|
|
|
|
nval.iValue = *val->v_realdata;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IF_REAL:
|
|
|
|
|
/*kensmith don't blow up with NULL dereference*/
|
|
|
|
|
if (!val->v_realdata) {
|
|
|
|
|
fprintf(cp_err,"Unable to determine the value\n");
|
|
|
|
|
return E_UNSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nval.rValue = *val->v_realdata;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Can't assign value to \"%s\" (unsupported type)\n",
|
|
|
|
|
opt->keyword);
|
|
|
|
|
return E_UNSUPP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* fprintf(cp_err, "Calling doask(%d, %x, %x, %x)\n",
|
|
|
|
|
typecode, dev, mod, opt); */
|
|
|
|
|
|
|
|
|
|
if (dev)
|
|
|
|
|
err = (*(ft_sim->setInstanceParm))((void *)ckt, (void *)dev,
|
|
|
|
|
opt->id, &nval, (IFvalue *)NULL);
|
|
|
|
|
else
|
|
|
|
|
err = (*(ft_sim->setModelParm))((void*)ckt, (void *) mod,
|
|
|
|
|
opt->id, &nval, (IFvalue *)NULL);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get pointers to a device, its model, and its type number given the name. If
|
|
|
|
|
* there is no such device, try to find a model with that name.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
finddev(void *ck, char *name, void **devptr, void **modptr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
int type = -1;
|
|
|
|
|
|
|
|
|
|
err = (*(ft_sim->findInstance))((void *)ck,&type,devptr,name,NULL,NULL);
|
|
|
|
|
if(err == OK) return(type);
|
|
|
|
|
type = -1;
|
|
|
|
|
*devptr = (void *)NULL;
|
|
|
|
|
err = (*(ft_sim->findModel))((void *)ck,&type,modptr,name);
|
|
|
|
|
if(err == OK) return(type);
|
|
|
|
|
*modptr = (void *)NULL;
|
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get an analysis parameter by name instead of id */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
if_analQbyName(void *ckt, int which, void *anal, char *name, IFvalue *parm)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for(i=0;i<ft_sim->analyses[which]->numParms;i++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[which]->analysisParms[i].keyword,name)==0) {
|
|
|
|
|
return( (*(ft_sim->askAnalysisQuest))(ckt,anal,
|
|
|
|
|
ft_sim->analyses[which]->analysisParms[i].id,parm,
|
|
|
|
|
(IFvalue *)NULL) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return(E_BADPARM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the parameters tstart, tstop, and tstep from the CKT struct. */
|
|
|
|
|
|
|
|
|
|
/* BLOW THIS AWAY TOO */
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
if_tranparams(struct circ *ci, double *start, double *stop, double *step)
|
|
|
|
|
{
|
|
|
|
|
IFvalue tmp;
|
|
|
|
|
int err;
|
|
|
|
|
int which = -1;
|
|
|
|
|
int i;
|
|
|
|
|
void *anal;
|
|
|
|
|
IFuid tranUid;
|
|
|
|
|
|
|
|
|
|
if(!ci->ci_curTask) return(FALSE);
|
|
|
|
|
for(i=0;i<ft_sim->numAnalyses;i++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[i]->name,"TRAN")==0){
|
|
|
|
|
which = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(which == -1) return(FALSE);
|
|
|
|
|
|
|
|
|
|
err = IFnewUid(ci->ci_ckt,&tranUid,(IFuid)NULL,"Transient Analysis",
|
|
|
|
|
UID_ANALYSIS, (void**)NULL);
|
|
|
|
|
if(err != OK) return(FALSE);
|
|
|
|
|
err =(*(ft_sim->findAnalysis))(ci->ci_ckt,&which, &anal,tranUid,
|
|
|
|
|
ci->ci_curTask,(IFuid *)NULL);
|
|
|
|
|
if(err != OK) return(FALSE);
|
|
|
|
|
err = if_analQbyName(ci->ci_ckt,which,anal,"tstart",&tmp);
|
|
|
|
|
if(err != OK) return(FALSE);
|
|
|
|
|
*start = tmp.rValue;
|
|
|
|
|
err = if_analQbyName(ci->ci_ckt,which,anal,"tstop",&tmp);
|
|
|
|
|
if(err != OK) return(FALSE);
|
|
|
|
|
*stop = tmp.rValue;
|
|
|
|
|
err = if_analQbyName(ci->ci_ckt,which,anal,"tstep",&tmp);
|
|
|
|
|
if(err != OK) return(FALSE);
|
|
|
|
|
*step = tmp.rValue;
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the statistic called 'name'. If this is NULL get all statistics
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct variable *
|
|
|
|
|
if_getstat(void *ckt, char *name)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct variable *v, *vars;
|
|
|
|
|
IFvalue parm;
|
|
|
|
|
int which = -1;
|
|
|
|
|
|
|
|
|
|
for(i=0;i<ft_sim->numAnalyses;i++) {
|
|
|
|
|
if(strcmp(ft_sim->analyses[i]->name,"options")==0) {
|
|
|
|
|
which = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(which==-1) {
|
|
|
|
|
fprintf(cp_err,"Warning: statistics unsupported\n");
|
|
|
|
|
return(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
|
for (i = 0; i < ft_sim->analyses[which]->numParms; i++)
|
|
|
|
|
if (eq(ft_sim->analyses[which]->analysisParms[i].keyword, name))
|
|
|
|
|
break;
|
|
|
|
|
if (i == ft_sim->analyses[which]->numParms)
|
|
|
|
|
return (NULL);
|
|
|
|
|
if ((*(ft_sim->askAnalysisQuest))(ckt, ft_curckt->ci_curTask,
|
|
|
|
|
ft_sim->analyses[which]->analysisParms[i].id, &parm,
|
|
|
|
|
(IFvalue *)NULL) == -1) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"if_getstat: Internal Error: can't get %s\n",
|
|
|
|
|
name);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
return (parmtovar(&parm, &(ft_sim->analyses[which]->analysisParms[i])));
|
|
|
|
|
} else {
|
|
|
|
|
for (i = 0, vars = v = NULL; i<ft_sim->analyses[which]->numParms; i++) {
|
|
|
|
|
if(!(ft_sim->analyses[which]->analysisParms[i].dataType&IF_ASK)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ((*(ft_sim->askAnalysisQuest))(ckt, ft_curckt->ci_curTask,
|
|
|
|
|
ft_sim->analyses[which]->analysisParms[i].id,
|
|
|
|
|
&parm, (IFvalue *)NULL) == -1) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"if_getstat: Internal Error: can't get %s\n",
|
|
|
|
|
name);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
if (v) {
|
|
|
|
|
v->va_next = parmtovar(&parm,
|
|
|
|
|
&(ft_sim->analyses[which]->analysisParms[i]));
|
|
|
|
|
v = v->va_next;
|
|
|
|
|
} else {
|
|
|
|
|
vars = v = parmtovar(&parm,
|
|
|
|
|
&(ft_sim->analyses[which]->analysisParms[i]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (vars);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|