Change the code that makes variables from device/model parameters

(spiceif.c) and vectors from variables (vectors.c) so that
array and string-valued parameters and the resulting variables
are handled correctly.  Fix a memory leak in converting
array parameters; that requires a change in cplask.c so that
CPL devices behave the same way as others.  Add a "device" variant of
the Compose command so that generated vectors are available in scripts.
This commit is contained in:
Giles Atkinson 2023-07-03 18:45:38 +01:00 committed by Holger Vogt
parent 02172448e7
commit cb69d8b96b
4 changed files with 245 additions and 203 deletions

View File

@ -226,6 +226,36 @@ com_compose(wordlist *wl)
}
length *= blocksize;
} else if (eq(wl->wl_word, "device") && resname[0] == '@') {
/* Make vector(s) from device parameters; also works with models. */
result = vec_get(resname);
/* With @dev[all] a chain of vectors is returned. */
while (result) {
char *cp;
/* Change name so it is not an array reference. */
for (cp = result->v_name; cp && *cp; ++cp) {
if (*cp == '[')
*cp = '_';
if (*cp == ']') {
*cp = '\0';
break;
}
}
/* Set dimension info */
result->v_numdims = 1;
result->v_dims[0] = length;
result->v_flags |= VF_PERMANENT;
result = result->v_link2;
}
goto done;
#ifdef XSPICE
} else if (eq(wl->wl_word, "xspice")) {
/* Make vectors from an event node. */
@ -240,7 +270,6 @@ com_compose(wordlist *wl)
result->v_scale->v_flags |= VF_PERMANENT;
vec_new(result->v_scale);
cp_addkword(CT_VECTOR, result->v_scale->v_name);
txfree(resname); // It was copied
goto finished;
#endif
} else {

View File

@ -79,7 +79,8 @@ extern INPmodel *modtab;
extern NGHASHPTR modtabhash;
extern bool ft_batchmode;
static struct variable *parmtovar(IFvalue *pv, IFparm *opt);
static struct variable *parmtovar(IFvalue *pv, IFparm *opt,
int use_description);
static IFparm *parmlookup(IFdevice *dev, GENinstance **inptr, char *param,
int do_model, int inout);
static IFvalue *doask(CKTcircuit *ckt, int typecode, GENinstance *dev, GENmodel *mod,
@ -688,17 +689,12 @@ spif_getparam_special(CKTcircuit *ckt, char **name, char *param, int ind, int do
continue;
pv = doask(ckt, typecode, dev, mod, opt, ind);
if (pv) {
tv = parmtovar(pv, opt);
/* With the following we pack the name and the acronym of the parameter */
{
char *x = tv->va_name;
tv->va_name = tprintf("%s [%s]", tv->va_name, device->instanceParms[i].keyword);
tfree(x);
tv = parmtovar(pv, opt, 0);
if (tv) {
if (vv)
tv->va_next = vv;
vv = tv;
}
if (vv)
tv->va_next = vv;
vv = tv;
} else {
fprintf(cp_err,
"Internal Error: no parameter '%s' on device '%s'\n",
@ -725,26 +721,12 @@ spif_getparam_special(CKTcircuit *ckt, char **name, char *param, int ind, int do
continue;
pv = doask(ckt, typecode, dev, mod, opt, ind);
if (pv) {
tv = parmtovar(pv, opt);
/* Inside parmtovar:
* 1. tv->va_name = copy(opt->description);
* 2. Copy the type of variable of IFparm into a variable (thus parm-to-var)
* vv->va_type = opt->dataType
* The long description of the parameter:
* IFparm MOS_SGTmPTable[] = { // model parameters //
* OP("type", MOS_SGT_MOD_TYPE, IF_STRING, "N-channel or P-channel MOS")
* goes into tv->va_name to put braces around the parameter of the model
* tv->va_name += device->modelParms[i].keyword;
*/
{
char *x = tv->va_name;
tv->va_name = tprintf("%s [%s]", tv->va_name, device->modelParms[i].keyword);
tfree(x);
tv = parmtovar(pv, opt, 0);
if (tv) {
if (vv)
tv->va_next = vv;
vv = tv;
}
/* tv->va_string = device->modelParms[i].keyword; Put the name of the variable */
if (vv)
tv->va_next = vv;
vv = tv;
} else {
fprintf(cp_err,
"Internal Error: no parameter '%s' on device '%s'\n",
@ -768,7 +750,7 @@ spif_getparam_special(CKTcircuit *ckt, char **name, char *param, int ind, int do
}
pv = doask(ckt, typecode, dev, mod, opt, ind);
if (pv)
vv = parmtovar(pv, opt);
vv = parmtovar(pv, opt, 0);
return (vv);
} else {
return (if_getstat(ckt, *name));
@ -814,10 +796,12 @@ spif_getparam(CKTcircuit *ckt, char **name, char *param, int ind, int do_model)
continue;
pv = doask(ckt, typecode, dev, mod, opt, ind);
if (pv) {
tv = parmtovar(pv, opt);
if (vv)
tv->va_next = vv;
vv = tv;
tv = parmtovar(pv, opt, 0);
if (tv) {
if (vv)
tv->va_next = vv;
vv = tv;
}
} else {
fprintf(cp_err,
"Internal Error: no parameter '%s' on device '%s'\n",
@ -843,7 +827,7 @@ spif_getparam(CKTcircuit *ckt, char **name, char *param, int ind, int do_model)
}
pv = doask(ckt, typecode, dev, mod, opt, ind);
if (pv)
vv = parmtovar(pv, opt);
vv = parmtovar(pv, opt, 0);
return (vv);
} else {
return (if_getstat(ckt, *name));
@ -991,49 +975,90 @@ if_setparam(CKTcircuit *ckt, char **name, char *param, struct dvec *val, int do_
}
}
/* Make a linked list where the first node is a CP_LIST variable
* pointing to the different values of the vector variables.
*
*
* In the case of Vin_sin 1 0 sin (0 2 2000)
* and of print @vin_sin[sin]
*
* vv->va_V.vV_list->va_V.vV_real = 2000
* vv->va_V.vV_list->va_next->va_V.vV_real = 2
* vv->va_V.vV_list->va_next->va_next->va_V.vV_real = 0
* So the list is starting from behind, but no problem
* This works fine
*/
static struct variable *
parmtovar(IFvalue *pv, IFparm *opt)
parmtolist(IFvalue *pv, IFparm *opt, char *name)
{
/* It is not clear whether we want to name the variable
* by `keyword' or by `description' */
struct variable *list = NULL;
int i;
for (i = pv->v.numValue; --i >= 0;) {
switch (opt->dataType & (IF_VARTYPES & ~IF_VECTOR)) {
case IF_INTEGER:
list = var_alloc_num(NULL, pv->v.vec.iVec[i], list);
break;
case IF_REAL:
case IF_COMPLEX:
list = var_alloc_real(NULL, pv->v.vec.rVec[i], list);
break;
case IF_STRING:
list = var_alloc_string(NULL, copy(pv->v.vec.sVec[i]), list);
break;
case IF_FLAG:
list = var_alloc_bool(NULL, pv->v.vec.iVec[i] ? TRUE : FALSE,
list);
break;
default:
fprintf(cp_err,
"parmtolist: Internal Error: bad PARM type "
"%#x for %s (%s).\n",
opt->dataType, opt->keyword, opt->description);
if (name)
free(name);
break;
}
}
if (i || pv->v.numValue == 0)
list = var_alloc_vlist(name, list, NULL);
if (pv->v.vec.iVec) { // All the union members are pointers
free(pv->v.vec.iVec);
pv->v.vec.iVec = NULL;
}
return list;
}
static struct variable *
parmtovar(IFvalue *pv, IFparm *opt, int use_description)
{
char *name;
name = use_description ? opt->description : opt->keyword;
if (name)
name = copy(name);
if (opt->dataType & IF_VECTOR)
return parmtolist(pv, opt, name);
switch (opt->dataType & IF_VARTYPES) {
case IF_INTEGER:
return var_alloc_num(copy(opt->description), pv->iValue, NULL);
return var_alloc_num(name, pv->iValue, NULL);
case IF_REAL:
case IF_COMPLEX:
return var_alloc_real(copy(opt->description), pv->rValue, NULL);
return var_alloc_real(name, pv->rValue, NULL);
case IF_STRING:
return var_alloc_string(copy(opt->description), pv->sValue, NULL);
return var_alloc_string(name, copy(pv->sValue), NULL);
case IF_FLAG:
return var_alloc_bool(copy(opt->description), pv->iValue ? TRUE : FALSE, NULL);
case IF_REALVEC: {
struct variable *list = NULL;
int i;
for (i = pv->v.numValue; --i >= 0;)
list = var_alloc_real(NULL, pv->v.vec.rVec[i], list);
return var_alloc_vlist(copy(opt->description), list, NULL);
/* It is a linked list where the first node is a variable
* pointing to the different values of the variables.
*
* To access the values of the real variable vector must be
* vv->va_V.vV_real = valor node ppal that is of no use.
*
* In the case of Vin_sin 1 0 sin (0 2 2000)
* and of print @vin_sin[sin]
*
* vv->va_V.vV_list->va_V.vV_real = 2000
* vv->va_V.vV_list->va_next->va_V.vV_real = 2
* vv->va_V.vV_list->va_next->va_next->va_V.vV_real = 0
* So the list is starting from behind, but no problem
* This works fine
*/
}
return var_alloc_bool(name, pv->iValue ? TRUE : FALSE,
NULL);
default:
fprintf(cp_err,
"parmtovar: Internal Error: bad PARM type %d.\n",
opt->dataType);
"parmtovar: Internal Error: bad PARM type %#x for %s (%s).\n",
opt->dataType, opt->keyword, opt->description);
if (name)
free(name);
return (NULL);
}
}
@ -1317,7 +1342,7 @@ if_getstat(CKTcircuit *ckt, char *name)
return (NULL);
}
return (parmtovar(&parm, if_parm));
return (parmtovar(&parm, if_parm, 1));
} else {
@ -1339,7 +1364,7 @@ if_getstat(CKTcircuit *ckt, char *name)
return (NULL);
}
*v = parmtovar(&parm, if_parm);
*v = parmtovar(&parm, if_parm, 1);
v = &((*v)->va_next);
}

View File

@ -580,8 +580,8 @@ vec_get(const char *vec_name) {
struct dvec *d, *end = NULL, *newv = NULL;
struct plot *pl;
char buf[BSIZE_SP], *s, *wd, *word, *whole, *name = NULL, *param;
int i = 0;
struct variable *vv;
int i = 0;
struct variable *vv, *v;
wd = word = copy(vec_name); /* Gets mangled below... */
@ -638,6 +638,8 @@ vec_get(const char *vec_name) {
}
if (!d && (*word == SPECCHAR)) { /* "@" */
int multiple;
/* This is a special quantity... */
if (ft_nutmeg) {
fprintf(cp_err,
@ -646,6 +648,12 @@ vec_get(const char *vec_name) {
return (NULL); /* va: use NULL */
}
if (!ft_curckt) {
fprintf(cp_err, "Error: No circuit loaded.\n");
tfree(wd);
return (NULL);
}
whole = copy(word);
name = ++word;
for (param = name; *param && (*param != '['); param++)
@ -660,157 +668,131 @@ vec_get(const char *vec_name) {
param = NULL;
}
/*
* This is what is done in case of "alter r1 resistance = 1234"
* r1 resistance, 0
* if_setparam(ft_curckt->ci_ckt, &dev, param, dv, do_model);
*/
if (ft_curckt) {
/*
* This is what is done in case of "alter r1 resistance = 1234"
* r1 resistance, 0
* if_setparam(ft_curckt->ci_ckt, &dev, param, dv, do_model);
*/
/* vv = if_getparam (ft_curckt->ci_ckt, &name, param, 0, 0); */
vv = if_getparam(ft_curckt->ci_ckt, &name, param, 0, 0);
if (!vv) {
tfree(whole);
tfree(wd);
return (NULL);
}
} else {
fprintf(cp_err, "Error: No circuit loaded.\n");
vv = if_getparam(ft_curckt->ci_ckt, &name, param, 0, 0);
if (!vv) {
tfree(whole);
tfree(wd);
return (NULL);
}
d = dvec_alloc(copy(whole), /* MW. The same as word before */
SV_NOTYPE,
VF_REAL, /* No complex values yet... */
1, NULL);
/* In case the represented variable is a REAL vector this takes
* the actual value of the first element of the linked list which
* does not make sense.
* This is an error.
/* If vec_name was "@dev", "@model", "@dev[all]" or @model[all]",
* if_getparam() returns a list.
*/
/* This will copy the contents of the structure vv in another structure
* dvec (FTEDATA.H) that do not have INTEGER so that those parameters
* defined as IF_INTEGER are not given their value when using
* print @pot[pos_node]
* To fix this, it is necessary to define:
* OPU( "pos_node", POT_QUEST_POS_NODE, IF_REAL,"Positive node of potenciometer"),
* int POTnegNode; // number of negative node of potenciometer (Nodo_3)
* case POT_QUEST_POS_NODE:
* value->rValue = (double)fast->POTposNode;
* return (OK);
* Works but with the format 1.00000E0
*/
multiple = (vv->va_next != NULL);
if (multiple && param)
*--param = '\0';
/* We must make a change in format between the data that carries a variable to
* put in a dvec.
*/
for (v = vv; v; v = v->va_next) {
struct dvec *nd;
char *vec_name, new_name[256];
/*
* #define va_bool va_V.vV_bool
* #define va_num va_V.vV_num
* #define va_real va_V.vV_real
* #define va_string va_V.vV_string
* #define va_vlist va_V.vV_list
* enum cp_types {
* CP_BOOL,
* CP_NUM,
* CP_REAL,
* CP_STRING,
* CP_LIST
° };
*/
/* The variable is a vector */
if (vv->va_type == CP_LIST) {
/* Compute the length of the vector,
* used with the parameters of isrc and vsrc
*/
struct variable *nv;
/* Count the number of nodes in the list */
i = 0;
for (nv = vv->va_vlist; nv; nv = nv->va_next) {
i++;
if (multiple) {
snprintf(new_name, sizeof new_name, "@%s[%s]",
name, v->va_name);
vec_name = new_name;
} else {
vec_name = whole;
}
nd = dvec_alloc(copy(vec_name),
SV_NOTYPE,
VF_REAL, /* No complex values yet... */
1, NULL);
dvec_realloc(d, i, NULL); /* Resize to # nodes */
switch (v->va_type) {
case CP_BOOL:
*nd->v_realdata = (double)v->va_bool;
break;
case CP_NUM:
*nd->v_realdata = (double)v->va_num;
break;
case CP_REAL:
*nd->v_realdata = v->va_real;
break;
case CP_STRING:
fprintf(stderr,
"ERROR: can not handle string value "
"of '%s' in vec_get(%s)\nIgnoring...\n",
v->va_name, vec_name);
dvec_free(nd);
continue;
break;
case CP_LIST:
{
struct variable *nv;
enum cp_types ft;
/* Step through the list again, setting values this time */
i = 0;
for (nv = vv->va_vlist; nv; nv = nv->va_next) {
d->v_realdata[i++] = nv->va_real;
}
/* To be able to identify the vector to represent
* belongs to a special "conunto" and should be printed in a
* special way.
*/
d->v_dims[1] = 1;
}
else if (vv->va_type == CP_NUM) { /* Variable is an integer */
*d->v_realdata = (double) vv->va_num;
}
else if (vv->va_type == CP_REAL) { /* Variable is a real */
if (!(vv->va_next)) {
/* Only a real data
* usually normal
*/
*d->v_realdata = vv->va_real;
}
else {
/* Real data set
* When you print a model @ [all]
* Just print numerical values, not the string
*/
struct variable *nv;
/* We go to print the list of values
* nv->va_name = Parameter description
* nv->va_string = Parameter
* nv->va_real= Value
*/
nv = vv;
for (i = 1; ; i++) {
switch (nv->va_type) {
case CP_REAL:
fprintf(stdout, "%s=%g\n", nv->va_name, nv->va_real);
break;
case CP_STRING:
fprintf(stdout, "%s=%s\n", nv->va_name, nv->va_string);
break;
case CP_NUM:
fprintf(stdout, "%s=%d\n", nv->va_name, nv->va_num);
break;
case CP_BOOL:
fprintf(stdout, "%s=%d\n", nv->va_name, nv->va_bool);
break;
default: {
fprintf(stderr, "ERROR: enumeration value `CP_LIST' not handled in vec_get\nIgnoring...\n");
break;
}
}
nv = nv->va_next;
/* Array values are presented as a list.
* Compute the length of the vector, and check that
* it is homogenous:
* used with the parameters of isrc and vsrc
*/
i = 0;
nv = v->va_vlist;
if (!nv) {
break;
dvec_free(nd);
continue;
}
ft = nv->va_type;
for (; nv; nv = nv->va_next) {
/* Count the number of nodes in the list */
i++;
if (nv->va_type != ft)
break;
}
if (nv || ft == CP_STRING || ft == CP_LIST) {
fprintf(stderr,
"ERROR: can not handle mixed, string or list "
"value of '%s' in vec_get(%s)\nIgnoring...\n",
v->va_name, vec_name);
dvec_free(nd);
continue;
}
dvec_realloc(nd, i, NULL); /* Resize to # nodes */
/* Step through the list again, setting values this time */
i = 0;
for (nv = v->va_vlist; nv; nv = nv->va_next) {
switch (ft) {
case CP_BOOL:
nd->v_realdata[i++] = (double)nv->va_bool;
break;
case CP_NUM:
nd->v_realdata[i++] = (double)nv->va_num;
break;
default:
case CP_REAL:
nd->v_realdata[i++] = nv->va_real;
break;
}
/* To be able to identify the vector to represent
* belongs to a special "conunto" and should be printed
* in a special way.
*/
nd->v_dims[1] = 1;
}
}
/* To distinguish those does not take anything for print screen to
* make a print or M1 @ @ M1 [all] leaving only the correct data
* and not the last
*/
d->v_rlength = 1;
break;
}
/* Chain it on. */
vec_new(nd);
nd->v_link2 = d;
d = nd;
}
free_struct_variable(vv);
tfree(wd);
vec_new(d);
tfree(whole);
return d;
}

View File

@ -24,12 +24,18 @@ CPLask(CKTcircuit *ckt, GENinstance *inst, int which, IFvalue *value, IFvalue *s
NG_IGNORE(select);
switch(which) {
/* String tables are expected to be freed, but not the strings. */
case CPL_POS_NODE:
value->v.vec.sVec = here->in_node_names;
value->v.vec.sVec = TMALLOC(char *, here->dimension);
memcpy(value->v.vec.sVec, here->in_node_names,
here->dimension * sizeof (char *));
value->v.numValue = here->dimension;
return(OK);
case CPL_NEG_NODE:
value->v.vec.sVec = here->out_node_names;
value->v.vec.sVec = TMALLOC(char *, here->dimension);
memcpy(value->v.vec.sVec, here->out_node_names,
here->dimension * sizeof (char *));
value->v.numValue = here->dimension;
return(OK);
case CPL_DIM: