From cb69d8b96b6188dd8ef6bc960e4aafd6ab312eda Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Mon, 3 Jul 2023 18:45:38 +0100 Subject: [PATCH] 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. --- src/frontend/com_compose.c | 31 +++- src/frontend/spiceif.c | 163 +++++++++++--------- src/frontend/vectors.c | 244 ++++++++++++++---------------- src/spicelib/devices/cpl/cplask.c | 10 +- 4 files changed, 245 insertions(+), 203 deletions(-) diff --git a/src/frontend/com_compose.c b/src/frontend/com_compose.c index 7d2249a70..e2fdb6ed8 100644 --- a/src/frontend/com_compose.c +++ b/src/frontend/com_compose.c @@ -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 { diff --git a/src/frontend/spiceif.c b/src/frontend/spiceif.c index c135b37db..206050b5e 100644 --- a/src/frontend/spiceif.c +++ b/src/frontend/spiceif.c @@ -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); } diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 26eca7494..adcbb8f1a 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -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; } diff --git a/src/spicelib/devices/cpl/cplask.c b/src/spicelib/devices/cpl/cplask.c index 4606e0313..9bef294d5 100644 --- a/src/spicelib/devices/cpl/cplask.c +++ b/src/spicelib/devices/cpl/cplask.c @@ -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: