allow 'temper' keyword in .param, .model and device instance lines
inpcom.c: fcns inp_fix_temper_in_param(), inp_new_func(), inp_rem_func() to convert .param with 'temper' to .func, and replace all affected parameter tokens xxx by their func counterpart xxx() inpcom.c; fcns inp_temper_compat(), inp_modify_exp() added to prepare expression with 'temper' for numparam inp.c: fcns inp_parse_temper(), inp_parse_temper_trees() added inp_evaluate_temper() to prepare, parse and evaluate the expressions containing 'temper' cktdefs.h: add global variable to expressions with 'temper' dctrcurv.c: add fcn inp_evaluate_temper() when temp has changed
This commit is contained in:
parent
8c85064018
commit
d70865a162
|
|
@ -31,6 +31,7 @@ Author: 1985 Wayne A. Christopher
|
|||
#include "subckt.h"
|
||||
#include "spiceif.h"
|
||||
#include "com_let.h"
|
||||
#include "com_commands.h"
|
||||
|
||||
#ifdef XSPICE
|
||||
#include "ngspice/ipctiein.h"
|
||||
|
|
@ -51,10 +52,25 @@ static bool doedit(char *filename);
|
|||
static struct line *com_options = NULL;
|
||||
static void cktislinear(CKTcircuit *ckt, struct line *deck);
|
||||
static void dotifeval(struct line *deck);
|
||||
static int inp_parse_temper(struct line *deck);
|
||||
static void inp_parse_temper_trees(void);
|
||||
|
||||
void line_free_x(struct line *deck, bool recurse);
|
||||
void create_circbyline(char *line);
|
||||
|
||||
void inp_evaluate_temper(void);
|
||||
|
||||
/* structure used to save expression parse trees for .model and
|
||||
* device instance lines
|
||||
*/
|
||||
|
||||
struct pt_temper {
|
||||
char *expression;
|
||||
wordlist *wl;
|
||||
wordlist *wlend;
|
||||
INPparseTree *pt;
|
||||
struct pt_temper *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* create an unique artificial *unusable* FILE ptr
|
||||
|
|
@ -572,6 +588,10 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
|
|||
tmp_options->li_next = options;
|
||||
}
|
||||
|
||||
/* prepare parse trees from 'temper' expressions */
|
||||
if (expr_w_temper)
|
||||
inp_parse_temper(deck);
|
||||
|
||||
/* now load deck into ft_curckt -- the current circuit. */
|
||||
inp_dodeck(deck, tt, wl_first, FALSE, options, filename);
|
||||
/* inp_dodeck did take ownership */
|
||||
|
|
@ -664,6 +684,13 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
|
|||
(void) fclose(fdo);
|
||||
}
|
||||
|
||||
if (expr_w_temper) {
|
||||
/* Now the circuit is defined, so generate the parse trees */
|
||||
inp_parse_temper_trees();
|
||||
/* Get the actual data for model and device instance parameters */
|
||||
inp_evaluate_temper();
|
||||
}
|
||||
|
||||
/* linked list dbs is used to store the "save" or .save data (defined in breakp2.c),
|
||||
(When controls are executed later on, also stores TRACE, IPLOT, and STOP data) */
|
||||
/* set to NULL to allow generation of a new dbs */
|
||||
|
|
@ -1286,3 +1313,214 @@ dotifeval(struct line *deck)
|
|||
tfree(dottoken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* List of all expressions found in .model lines */
|
||||
static struct pt_temper *modtlist = NULL;
|
||||
|
||||
/* List of all expressions found in device instance lines */
|
||||
static struct pt_temper *devtlist = NULL;
|
||||
|
||||
/*
|
||||
Evaluate expressions containing 'temper' keyword, found in
|
||||
.model lines or device instance lines.
|
||||
Activity has four steps:
|
||||
1) Prepare the expressions to survive numparam expansion
|
||||
(see function inp_temper_compat() in inpcom.c). A global
|
||||
variable expr_w_temper is set TRUE if any expression with
|
||||
'temper' has been found.
|
||||
2) After numparam insertion and subcircuit expansion,
|
||||
get the expressions, store them with a place holder for the
|
||||
pointer to the expression parse tree and a wordlist containing
|
||||
device/model name, parameter name and a placeholder for the
|
||||
evaluation result ready to be used by com_alter(mod) functions,
|
||||
in linked lists modtlist (model) or devtlist (device instance).
|
||||
(done function inp_parse_temper()).
|
||||
3) After the circuit structure has been established, generate
|
||||
the parse trees. We can do it only then because pointers to
|
||||
ckt->CKTtemp and others are stored in the trees.
|
||||
(done in function inp_parse_temper_trees()).
|
||||
4) Evaluation of the parse trees is requested by calling function
|
||||
inp_evaluate_temper(). The B Source parser is invoked here.
|
||||
ckt->CKTtemp is used to replace the 'temper' token by the actual
|
||||
circuit temperature. The evaluation results are added to the
|
||||
wordlist, com_alter(mod) is called to set the new parameters
|
||||
to the model parameters or device instance parameters.
|
||||
*/
|
||||
|
||||
static int
|
||||
inp_parse_temper(struct line *card)
|
||||
{
|
||||
int error = 0;
|
||||
char *end_tstr, *beg_tstr, *beg_pstr, *str_ptr, *devmodname, *paramname, *expression;
|
||||
|
||||
/* skip title line */
|
||||
card = card->li_next;
|
||||
for (; card; card = card->li_next) {
|
||||
|
||||
char *curr_line = card->li_line;
|
||||
|
||||
/* exclude some elements */
|
||||
if ((*curr_line == '*') || (*curr_line == 'v') || (*curr_line == 'b') || (*curr_line == 'i') ||
|
||||
(*curr_line == 'e') || (*curr_line == 'g') || (*curr_line == 'f') || (*curr_line == 'h'))
|
||||
continue;
|
||||
/* exclude all dot commands except .model */
|
||||
if ((*curr_line == '.') && (!prefix(".model", curr_line)))
|
||||
continue;
|
||||
/* exclude lines not containing 'temper' */
|
||||
if (strstr(curr_line, "temper") == NULL)
|
||||
continue;
|
||||
/* now start processing of the remaining lines containing 'temper' */
|
||||
if (prefix(".model", curr_line)) {
|
||||
struct pt_temper *modtlistnew = NULL;
|
||||
/* remove '.model' */
|
||||
str_ptr = gettok(&curr_line);
|
||||
tfree(str_ptr);
|
||||
devmodname = gettok(&curr_line);
|
||||
beg_tstr = curr_line;
|
||||
while ((end_tstr = beg_tstr = strstr(beg_tstr, "temper")) != NULL) {
|
||||
wordlist *wl = NULL, *wlend = NULL;
|
||||
modtlistnew = TMALLOC(struct pt_temper, 1);
|
||||
while ((*beg_tstr) != '=')
|
||||
beg_tstr--;
|
||||
beg_pstr = beg_tstr;
|
||||
/* go back over param name */
|
||||
while(isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
while(!isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
/* get parameter name */
|
||||
paramname = copy_substring(beg_pstr + 1, beg_tstr);
|
||||
/* find end of expression string */
|
||||
while (((*end_tstr) != '\0') && ((*end_tstr) != '='))
|
||||
end_tstr++;
|
||||
/* go back over next param name */
|
||||
if (*end_tstr == '=') {
|
||||
end_tstr--;
|
||||
while(isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
while(!isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
}
|
||||
expression = copy_substring(beg_tstr + 1, end_tstr);
|
||||
modtlistnew->expression = copy(expression);
|
||||
/* now remove this parameter entry by overwriting with ' '
|
||||
ngspice then will use the default parameter to set up the circuit */
|
||||
for (str_ptr = beg_pstr; str_ptr < end_tstr; str_ptr++)
|
||||
*str_ptr = ' ';
|
||||
|
||||
modtlistnew->next = NULL;
|
||||
/* create wordlist suitable for com_altermod */
|
||||
wl_append_word(&wl, &wlend, devmodname);
|
||||
wl_append_word(&wl, &wlend, paramname);
|
||||
wl_append_word(&wl, &wlend, copy("="));
|
||||
/* to be filled in by evaluation function */
|
||||
wl_append_word(&wl, &wlend, NULL);
|
||||
modtlistnew->wl = wl;
|
||||
modtlistnew->wlend = wlend;
|
||||
|
||||
/* fill in the linked parse tree list */
|
||||
if (modtlist) {
|
||||
struct pt_temper *modtlisttmp = modtlist;
|
||||
modtlist = modtlistnew;
|
||||
modtlist->next = modtlisttmp;
|
||||
} else {
|
||||
modtlist = modtlistnew;
|
||||
}
|
||||
}
|
||||
} else { /* instance expression with 'temper' */
|
||||
struct pt_temper *devtlistnew = NULL;
|
||||
/* get device name */
|
||||
devmodname = gettok(&curr_line);
|
||||
beg_tstr = curr_line;
|
||||
while ((end_tstr = beg_tstr = strstr(beg_tstr, "temper")) != NULL) {
|
||||
wordlist *wl = NULL, *wlend = NULL;
|
||||
devtlistnew = TMALLOC(struct pt_temper, 1);
|
||||
while ((*beg_tstr) != '=')
|
||||
beg_tstr--;
|
||||
beg_pstr = beg_tstr;
|
||||
/* go back over param name */
|
||||
while(isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
while(!isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
/* get parameter name */
|
||||
paramname = copy_substring(beg_pstr + 1, beg_tstr);
|
||||
/* find end of expression string */
|
||||
while (((*end_tstr) != '\0') && ((*end_tstr) != '='))
|
||||
end_tstr++;
|
||||
/* go back over next param name */
|
||||
if (*end_tstr == '=') {
|
||||
end_tstr--;
|
||||
while(isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
while(!isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
}
|
||||
expression = copy_substring(beg_tstr + 1, end_tstr);
|
||||
devtlistnew->expression = copy(expression);
|
||||
/* now remove this parameter entry by overwriting with ' '
|
||||
ngspice then will use the default parameter to set up the circuit */
|
||||
for (str_ptr = beg_pstr; str_ptr < end_tstr; str_ptr++)
|
||||
*str_ptr = ' ';
|
||||
|
||||
devtlistnew->next = NULL;
|
||||
/* create wordlist suitable for com_altermod */
|
||||
wl_append_word(&wl, &wlend, devmodname);
|
||||
wl_append_word(&wl, &wlend, paramname);
|
||||
wl_append_word(&wl, &wlend, copy("="));
|
||||
/* to be filled in by evaluation function */
|
||||
wl_append_word(&wl, &wlend, NULL);
|
||||
devtlistnew->wl = wl;
|
||||
devtlistnew->wlend = wlend;
|
||||
|
||||
/* fill in the linked parse tree list */
|
||||
if (devtlist) {
|
||||
struct pt_temper *devtlisttmp = devtlist;
|
||||
devtlist = devtlistnew;
|
||||
devtlist->next = devtlisttmp;
|
||||
} else {
|
||||
devtlist = devtlistnew;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
inp_parse_temper_trees(void)
|
||||
{
|
||||
struct pt_temper *d;
|
||||
|
||||
for(d = devtlist; d; d = d->next)
|
||||
INPgetTree(&d->expression, &d->pt, ft_curckt->ci_ckt, NULL);
|
||||
|
||||
for(d = modtlist; d; d = d->next)
|
||||
INPgetTree(&d->expression, &d->pt, ft_curckt->ci_ckt, NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
inp_evaluate_temper(void)
|
||||
{
|
||||
struct pt_temper *d;
|
||||
double result;
|
||||
char fts[128];
|
||||
|
||||
for(d = devtlist; d; d = d->next) {
|
||||
IFeval((IFparseTree *) d->pt, 1e-12, &result, NULL, NULL);
|
||||
sprintf(fts, "%g", result);
|
||||
d->wlend->wl_word = copy(fts);
|
||||
com_alter(d->wl);
|
||||
}
|
||||
|
||||
for(d = modtlist; d; d = d->next) {
|
||||
IFeval((IFparseTree *) d->pt, 1e-12, &result, NULL, NULL);
|
||||
sprintf(fts, "%g", result);
|
||||
d->wlend->wl_word = copy(fts);
|
||||
com_altermod(d->wl);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@ struct function_env
|
|||
} *functions;
|
||||
};
|
||||
|
||||
struct func_temper
|
||||
{
|
||||
char* funcname;
|
||||
int subckt_depth;
|
||||
int subckt_count;
|
||||
struct func_temper *next;
|
||||
};
|
||||
|
||||
|
||||
static COMPATMODE_T inp_compat_mode;
|
||||
|
||||
|
|
@ -77,6 +85,9 @@ int dynMaxckt = 0; /* subckt.c 307 */
|
|||
/* number of parameter substitutions */
|
||||
long dynsubst; /* spicenum.c 221 */
|
||||
|
||||
/* Expression handling with 'temper' parameter required */
|
||||
bool expr_w_temper = FALSE;
|
||||
|
||||
|
||||
static char *readline(FILE *fd);
|
||||
static int get_number_terminals(char *c);
|
||||
|
|
@ -96,7 +107,12 @@ static void inp_sort_params(struct line *start_card, struct line *end_card, stru
|
|||
static char *inp_remove_ws(char *s);
|
||||
static void inp_compat(struct line *deck);
|
||||
static void inp_bsource_compat(struct line *deck);
|
||||
static void inp_temper_compat(struct line *card);
|
||||
static void inp_dot_if(struct line *deck);
|
||||
static char *inp_modify_exp(char* expression);
|
||||
static void inp_new_func(char *funcname, char *funcbody, struct line *card,
|
||||
struct func_temper **new_func, int *sub_count, int subckt_depth);
|
||||
static void inp_rem_func(struct func_temper **new_func);
|
||||
|
||||
static bool chk_for_line_continuation(char *line);
|
||||
static void comment_out_unused_subckt_models(struct line *start_card, int no_of_lines);
|
||||
|
|
@ -110,6 +126,7 @@ static char *get_quoted_token(char *string, char **token);
|
|||
static void replace_token(char *string, char *token, int where, int total);
|
||||
static void inp_add_series_resistor(struct line *deck);
|
||||
static void subckt_params_to_param(struct line *deck);
|
||||
static void inp_fix_temper_in_param(struct line *deck);
|
||||
|
||||
static char *skip_back_non_ws(char *d) { while (d[-1] && !isspace(d[-1])) d--; return d; }
|
||||
static char *skip_back_ws(char *d) { while (isspace(d[-1])) d--; return d; }
|
||||
|
|
@ -779,6 +796,7 @@ inp_readall(FILE *fp, int call_depth, char *dir_name, bool comfile, bool intfile
|
|||
|
||||
inp_fix_macro_param_func_paren_io(working);
|
||||
inp_fix_ternary_operator(working);
|
||||
inp_fix_temper_in_param(working);
|
||||
|
||||
inp_expand_macros_in_deck(NULL, working);
|
||||
inp_fix_param_values(working);
|
||||
|
|
@ -813,6 +831,7 @@ inp_readall(FILE *fp, int call_depth, char *dir_name, bool comfile, bool intfile
|
|||
/* B source numparam compatibility transformation */
|
||||
inp_bsource_compat(working);
|
||||
inp_dot_if(working);
|
||||
inp_temper_compat(working);
|
||||
}
|
||||
|
||||
inp_add_series_resistor(working);
|
||||
|
|
@ -5504,6 +5523,263 @@ inp_bsource_compat(struct line *card)
|
|||
}
|
||||
|
||||
|
||||
/* Find all expression containing the keyword 'temper',
|
||||
* except for B lines and some other exclusions. Prepare
|
||||
* these expressions by calling inp_modify_exp() and return
|
||||
* a modified card->li_line
|
||||
*/
|
||||
|
||||
static void
|
||||
inp_temper_compat(struct line *card)
|
||||
{
|
||||
int skip_control = 0;
|
||||
char *beg_str, *end_str, *new_str = NULL, *beg_tstr, *end_tstr, *exp_str;
|
||||
char actchar;
|
||||
|
||||
for (; card; card = card->li_next) {
|
||||
|
||||
char *curr_line = card->li_line;
|
||||
|
||||
/* exclude any command inside .control ... .endc */
|
||||
if (ciprefix(".control", curr_line)) {
|
||||
skip_control ++;
|
||||
continue;
|
||||
} else if (ciprefix(".endc", curr_line)) {
|
||||
skip_control --;
|
||||
continue;
|
||||
} else if (skip_control > 0) {
|
||||
continue;
|
||||
}
|
||||
/* exclude some elements */
|
||||
if ((*curr_line == '*') || (*curr_line == 'v') || (*curr_line == 'b') || (*curr_line == 'i') ||
|
||||
(*curr_line == 'e') || (*curr_line == 'g') || (*curr_line == 'f') || (*curr_line == 'h'))
|
||||
continue;
|
||||
/* exclude all dot commands except .model */
|
||||
if ((*curr_line == '.') && (!prefix(".model", curr_line)))
|
||||
continue;
|
||||
/* exclude lines not containing 'temper' */
|
||||
if (strstr(curr_line, "temper") == NULL)
|
||||
continue;
|
||||
/* now start processing of the remaining lines containing 'temper' */
|
||||
/* remove white spaces of everything inside {}*/
|
||||
card->li_line = inp_remove_ws(card->li_line);
|
||||
curr_line = card->li_line;
|
||||
/* now check if 'temper' is a token or just a substring of another string, e.g. mytempers */
|
||||
/* we may have multiple temper and mytempers in multiple expressions in a line */
|
||||
beg_str = beg_tstr = curr_line;
|
||||
while ((beg_tstr = strstr(beg_tstr, "temper")) != NULL) {
|
||||
actchar = *(beg_tstr - 1);
|
||||
if (!isspace(actchar) && !is_arith_char(actchar)) {
|
||||
beg_tstr++;
|
||||
continue;
|
||||
}
|
||||
actchar = *(beg_tstr + 6);
|
||||
if (!isspace(actchar) && !is_arith_char(actchar))
|
||||
continue;
|
||||
/* we have found a true 'temper' */
|
||||
/* set the global variable */
|
||||
expr_w_temper = TRUE;
|
||||
/* find the expression: first go back to the opening '{',
|
||||
then find the closing '}' */
|
||||
while ((*beg_tstr) != '{')
|
||||
beg_tstr--;
|
||||
end_str = end_tstr = beg_tstr;
|
||||
exp_str = gettok_char(&end_tstr, '}', TRUE, TRUE);
|
||||
/* modify the expression string */
|
||||
exp_str = inp_modify_exp(exp_str);
|
||||
/* add the intermediate string between previous and next expression to the new line */
|
||||
new_str = INPstrCat(new_str, copy_substring(beg_str, end_str), " ");
|
||||
/* add the modified expression string to the new line */
|
||||
new_str = INPstrCat(new_str, exp_str, " ");
|
||||
new_str = INPstrCat(new_str, copy(" "), " ");
|
||||
/* move on to the next intermediate string */
|
||||
beg_str = beg_tstr = end_tstr;
|
||||
}
|
||||
if (*beg_str)
|
||||
new_str = INPstrCat(new_str, copy(beg_str), " ");
|
||||
tfree(card->li_line);
|
||||
new_str = inp_remove_ws(new_str);
|
||||
card->li_line = new_str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* lines containing expressions with keyword 'temper':
|
||||
* no parsing in numparam code, just replacement of parameters.
|
||||
* Parsing done with B source parser in function inp_parse_temper
|
||||
* in inp.c. Evaluation is the done with fcn inp_evaluate_temper
|
||||
* from inp.c, taking the actual temperature into account.
|
||||
* To achive this, do the following here:
|
||||
* Remove all '{' and '}' --> no parsing of equations in numparam
|
||||
* Place '{' and '}' directly around all potential parameters,
|
||||
* but skip function names like exp (search for 'exp(' to detect fcn name),
|
||||
* functions containing nodes like v(node), v(node1, node2), i(branch)
|
||||
* and other keywords like TEMPER. --> Only parameter replacement in numparam
|
||||
*/
|
||||
|
||||
static char *
|
||||
inp_modify_exp(char* expr)
|
||||
{
|
||||
char * str_ptr, *tmp_char, *new_str;
|
||||
char actchar;
|
||||
wordlist *wl = NULL, *wlist = NULL;
|
||||
char buf[512];
|
||||
size_t i, xlen, ustate = 0;
|
||||
int error1;
|
||||
|
||||
/* scan the expression and remove all '{' and '}' */
|
||||
str_ptr = expr;
|
||||
while (*str_ptr) {
|
||||
if ((*str_ptr == '{') || (*str_ptr == '}'))
|
||||
*str_ptr = ' ';
|
||||
str_ptr++;
|
||||
}
|
||||
/* scan the expression */
|
||||
str_ptr = expr;
|
||||
while (*str_ptr != '\0') {
|
||||
str_ptr = skip_ws(str_ptr);
|
||||
if (*str_ptr == '\0')
|
||||
break;
|
||||
actchar = *str_ptr;
|
||||
wl_append_word(&wlist, &wl, NULL);
|
||||
if ((actchar == ',') || (actchar == '(') || (actchar == ')') ||
|
||||
(actchar == '*') || (actchar == '/') || (actchar == '^') ||
|
||||
(actchar == '+') || (actchar == '?') || (actchar == ':'))
|
||||
{
|
||||
if ((actchar == '*') && (str_ptr[1] == '*')) {
|
||||
actchar = '^';
|
||||
str_ptr++;
|
||||
}
|
||||
buf[0] = actchar;
|
||||
buf[1] = '\0';
|
||||
wl->wl_word = copy(buf);
|
||||
str_ptr++;
|
||||
if (actchar == ')')
|
||||
ustate = 0;
|
||||
else
|
||||
ustate = 1; /* we have an operator */
|
||||
} else if ((actchar == '>') || (actchar == '<') ||
|
||||
(actchar == '!') || (actchar == '='))
|
||||
{
|
||||
/* >=, <=, !=, ==, <>, ... */
|
||||
char *beg = str_ptr++;
|
||||
if ((*str_ptr == '=') || (*str_ptr == '<') || (*str_ptr == '>'))
|
||||
str_ptr++;
|
||||
wl->wl_word = copy_substring(beg, str_ptr);
|
||||
ustate = 1; /* we have an operator */
|
||||
} else if ((actchar == '|') || (actchar == '&')) {
|
||||
char *beg = str_ptr++;
|
||||
if ((*str_ptr == '|') || (*str_ptr == '&'))
|
||||
str_ptr++;
|
||||
wl->wl_word = copy_substring(beg, str_ptr);
|
||||
ustate = 1; /* we have an operator */
|
||||
} else if ((actchar == '-') && (ustate == 0)) {
|
||||
buf[0] = actchar;
|
||||
buf[1] = '\0';
|
||||
wl->wl_word = copy(buf);
|
||||
str_ptr++;
|
||||
ustate = 1; /* we have an operator */
|
||||
} else if ((actchar == '-') && (ustate == 1)) {
|
||||
wl->wl_word = copy("");
|
||||
str_ptr++;
|
||||
ustate = 2; /* place a '-' in front of token */
|
||||
} else if (isalpha(actchar)) {
|
||||
/* unary -, change sign */
|
||||
if (ustate == 2) {
|
||||
i = 1;
|
||||
buf[0] = '-';
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (((actchar == 'v') || (actchar == 'i')) && (str_ptr[1] == '(')) {
|
||||
while (*str_ptr != ')') {
|
||||
buf[i] = *str_ptr;
|
||||
i++;
|
||||
str_ptr++;
|
||||
}
|
||||
buf[i] = *str_ptr;
|
||||
buf[i+1] = '\0';
|
||||
wl->wl_word = copy(buf);
|
||||
str_ptr++;
|
||||
} else {
|
||||
while (isalnum(*str_ptr) ||
|
||||
(*str_ptr == '!') || (*str_ptr == '#') ||
|
||||
(*str_ptr == '$') || (*str_ptr == '%') ||
|
||||
(*str_ptr == '_') || (*str_ptr == '[') ||
|
||||
(*str_ptr == ']'))
|
||||
{
|
||||
buf[i] = *str_ptr;
|
||||
i++;
|
||||
str_ptr++;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
/* no parens {} around time, hertz, temper, the constants
|
||||
pi and e which are defined in inpptree.c, around pwl and temp. coeffs */
|
||||
if ((*str_ptr == '(') ||
|
||||
cieq(buf, "hertz") || cieq(buf, "temper") ||
|
||||
cieq(buf, "time") || cieq(buf, "pi") ||
|
||||
cieq(buf, "e") || cieq(buf, "pwl"))
|
||||
{
|
||||
wl->wl_word = copy(buf);
|
||||
|
||||
} else if (cieq(buf, "tc1") || cieq(buf, "tc2") ||
|
||||
cieq(buf, "reciproctc"))
|
||||
{
|
||||
str_ptr = skip_ws(str_ptr);
|
||||
/* no {} around tc1 = or tc2 = , these are temp coeffs. */
|
||||
if (str_ptr[0] == '=' && str_ptr[1] != '=') {
|
||||
buf[i++] = '=';
|
||||
buf[i] = '\0';
|
||||
str_ptr++;
|
||||
wl->wl_word = copy(buf);
|
||||
} else {
|
||||
xlen = strlen(buf);
|
||||
tmp_char = TMALLOC(char, xlen + 3);
|
||||
sprintf(tmp_char, "{%s}", buf);
|
||||
wl->wl_word = tmp_char;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* {} around all other tokens */
|
||||
xlen = strlen(buf);
|
||||
tmp_char = TMALLOC(char, xlen + 3);
|
||||
sprintf(tmp_char, "{%s}", buf);
|
||||
wl->wl_word = tmp_char;
|
||||
}
|
||||
}
|
||||
ustate = 0; /* we have a number */
|
||||
} else if (isdigit(actchar) || (actchar == '.')) { /* allow .5 format too */
|
||||
/* allow 100p, 5MEG etc. */
|
||||
double dvalue = INPevaluate(&str_ptr, &error1, 0);
|
||||
char cvalue[19];
|
||||
/* unary -, change sign */
|
||||
if (ustate == 2)
|
||||
dvalue *= -1;
|
||||
sprintf(cvalue, "%18.10e", dvalue);
|
||||
wl->wl_word = copy(cvalue);
|
||||
ustate = 0; /* we have a number */
|
||||
/* skip the `unit', FIXME INPevaluate() should do this */
|
||||
while (isalpha(*str_ptr))
|
||||
str_ptr++;
|
||||
} else { /* strange char */
|
||||
printf("Preparing expression for numparam\nWhat is this?\n%s\n", str_ptr);
|
||||
buf[0] = *str_ptr;
|
||||
buf[1] = '\0';
|
||||
wl->wl_word = copy(buf);
|
||||
str_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
new_str = wl_flatten(wlist);
|
||||
wl_free(wlist);
|
||||
wlist = NULL;
|
||||
wl = NULL;
|
||||
tfree(expr);
|
||||
return(new_str);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* destructively fetch a token from the input string
|
||||
* token is either quoted, or a plain nonwhitespace sequence
|
||||
|
|
@ -5776,3 +6052,270 @@ inp_dot_if(struct line *card)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Convert .param lines containing keyword 'temper' into .func lines:
|
||||
* .param xxx1 = 'temper + 25' ---> .func xxx1() 'temper + 25'
|
||||
* Add info about the functions (name, subcircuit depth, number of
|
||||
* subckt) to linked list new_func.
|
||||
* Then scan new_func, for each xxx1 scan all lines of deck,
|
||||
* find all xxx1 and convert them to a function:
|
||||
* xxx1 ---> xxx1()
|
||||
* If this happens to be in another .param line, convert it to .func,
|
||||
* add info to end of new_func and continue scanning.
|
||||
*/
|
||||
|
||||
static void
|
||||
inp_fix_temper_in_param(struct line *deck)
|
||||
{
|
||||
int skip_control = 0, subckt_depth = 0, j, *sub_count;
|
||||
char *beg_pstr, *beg_tstr, *end_tstr, *funcbody, *funcname;
|
||||
char actchar;
|
||||
struct func_temper *new_func = NULL, *beg_func;
|
||||
struct line *card;
|
||||
|
||||
sub_count = TMALLOC(int, 16);
|
||||
for(j = 0; j < 16; j++)
|
||||
sub_count[j] = 0;
|
||||
|
||||
/* first pass: determine all .param with temper inside and replace by .func
|
||||
.param xxx1 = 'temper + 25'
|
||||
will become
|
||||
.func xxx1() 'temper + 25'
|
||||
*/
|
||||
card = deck;
|
||||
for (; card; card = card->li_next) {
|
||||
|
||||
char *curr_line = card->li_line;
|
||||
|
||||
if (*curr_line == '*')
|
||||
continue;
|
||||
|
||||
/* determine nested depths of subcircuits */
|
||||
if (ciprefix(".subckt", curr_line)) {
|
||||
subckt_depth ++;
|
||||
sub_count[subckt_depth]++;
|
||||
continue;
|
||||
} else if (ciprefix(".ends", curr_line)) {
|
||||
subckt_depth --;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* exclude any command inside .control ... .endc */
|
||||
if (ciprefix(".control", curr_line)) {
|
||||
skip_control ++;
|
||||
continue;
|
||||
} else if (ciprefix(".endc", curr_line)) {
|
||||
skip_control --;
|
||||
continue;
|
||||
} else if (skip_control > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ciprefix(".param", curr_line)) {
|
||||
/* check if we have a true 'temper' */
|
||||
beg_tstr = curr_line;
|
||||
while ((end_tstr = beg_tstr = strstr(beg_tstr, "temper")) != NULL) {
|
||||
actchar = *(beg_tstr - 1);
|
||||
if (!(actchar == '{') && !isspace(actchar) && !is_arith_char(actchar)) {
|
||||
beg_tstr++;
|
||||
continue;
|
||||
}
|
||||
actchar = *(beg_tstr + 6);
|
||||
if (!(actchar == '}') && !isspace(actchar) && !is_arith_char(actchar))
|
||||
continue;
|
||||
/* we have found a true 'temper', so start conversion */
|
||||
/* find function name and function body: We may have multiple
|
||||
params in a linie!
|
||||
*/
|
||||
while ((*beg_tstr) != '=')
|
||||
beg_tstr--;
|
||||
beg_pstr = beg_tstr;
|
||||
/* go back over param name */
|
||||
while(isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
while(!isspace(*beg_pstr))
|
||||
beg_pstr--;
|
||||
/* get function name from parameter name */
|
||||
funcname = copy_substring(beg_pstr + 1, beg_tstr);
|
||||
/* find end of function body */
|
||||
while (((*end_tstr) != '\0') && ((*end_tstr) != '='))
|
||||
end_tstr++;
|
||||
/* go back over next param name */
|
||||
if (*end_tstr == '=') {
|
||||
end_tstr--;
|
||||
while(isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
while(!isspace(*end_tstr))
|
||||
end_tstr--;
|
||||
}
|
||||
|
||||
funcbody = copy_substring(beg_tstr + 1, end_tstr);
|
||||
inp_new_func(funcname, funcbody, card, &new_func, sub_count, subckt_depth);
|
||||
|
||||
beg_tstr = end_tstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* second pass */
|
||||
/* for each .func entry in new_func start the insertion operation:
|
||||
search each line from the deck, which has the suitable subcircuit nesting data,
|
||||
for tokens xxx equalling the funcname, replace xxx by xxx(). After insertion,
|
||||
remove the respective entry in new_fuc. If the replacement is done in a
|
||||
.param line, convert it to a .func line and add an entry to new_func.
|
||||
Continue until new_func is empty.
|
||||
*/
|
||||
|
||||
beg_func = new_func;
|
||||
for (; new_func; new_func = new_func->next) {
|
||||
|
||||
for(j = 0; j < 16; j++)
|
||||
sub_count[j] = 0;
|
||||
|
||||
card = deck;
|
||||
for (; card; card = card->li_next) {
|
||||
|
||||
char *new_str = NULL; /* string we assemble here */
|
||||
char *curr_str;/* where we are in curr_line */
|
||||
char *add_str;/* what we add */
|
||||
char *curr_line = card->li_line;
|
||||
char * new_tmp_str, *tmp_str;
|
||||
|
||||
if (*curr_line == '*')
|
||||
continue;
|
||||
|
||||
/* determine nested depths of subcircuits */
|
||||
if (ciprefix(".subckt", curr_line)) {
|
||||
subckt_depth ++;
|
||||
sub_count[subckt_depth]++;
|
||||
continue;
|
||||
} else if (ciprefix(".ends", curr_line)) {
|
||||
subckt_depth --;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* exclude any command inside .control ... .endc */
|
||||
if (ciprefix(".control", curr_line)) {
|
||||
skip_control ++;
|
||||
continue;
|
||||
} else if (ciprefix(".endc", curr_line)) {
|
||||
skip_control --;
|
||||
continue;
|
||||
} else if (skip_control > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* exclude lines which do not have the same subcircuit
|
||||
nesting depth and number as found in new_func */
|
||||
if (subckt_depth != new_func->subckt_depth)
|
||||
continue;
|
||||
if (sub_count[subckt_depth] != new_func->subckt_count)
|
||||
continue;
|
||||
|
||||
curr_str = curr_line;
|
||||
while ((beg_tstr = strstr(curr_str, new_func->funcname)) != NULL) {
|
||||
/* start of token */
|
||||
actchar = *(beg_tstr - 1);
|
||||
if (!(actchar == '{') && !isspace(actchar) && !is_arith_char(actchar)) {
|
||||
curr_str++;
|
||||
continue;
|
||||
}
|
||||
/* end of token */
|
||||
end_tstr = beg_tstr + strlen(new_func->funcname);
|
||||
actchar = *(end_tstr);
|
||||
if (!(actchar == '}') && !isspace(actchar) && !is_arith_char(actchar)) {
|
||||
curr_str++;
|
||||
continue;
|
||||
}
|
||||
if (actchar == '(') {
|
||||
curr_str++;
|
||||
continue; /* not the .func xxx() itself */
|
||||
}
|
||||
/* we have found a true token equaling funcname, so start insertion */
|
||||
add_str = copy_substring(curr_str, end_tstr);
|
||||
new_str = INPstrCat(new_str, add_str, "");
|
||||
new_str = INPstrCat(new_str, copy("()"), "");
|
||||
curr_str = end_tstr;
|
||||
}
|
||||
if (new_str) /* add final part of line */
|
||||
new_str = INPstrCat(new_str, copy(curr_str), "");
|
||||
else
|
||||
continue;
|
||||
|
||||
/* if we have inserted into a .param line, convert to .func */
|
||||
new_tmp_str = new_str;
|
||||
if (prefix(".param", new_tmp_str)) {
|
||||
tmp_str = gettok(&new_tmp_str);
|
||||
tfree(tmp_str);
|
||||
funcname = gettok_char(&new_tmp_str, '=', FALSE, FALSE);
|
||||
funcbody = copy(new_tmp_str + 1);
|
||||
inp_new_func(funcname, funcbody, card, &new_func, sub_count, subckt_depth);
|
||||
} else {
|
||||
/* Or just enter new line into deck */
|
||||
card->li_next = xx_new_line(card->li_next, new_str, 0, card->li_linenum);
|
||||
*card->li_line = '*';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* final memory clearance */
|
||||
tfree(sub_count);
|
||||
/* remove new_func */
|
||||
inp_rem_func(&beg_func);
|
||||
}
|
||||
|
||||
|
||||
/* enter function name, nested .subckt depths, and
|
||||
* number of .subckt at given level into struct new_func
|
||||
* and add line to deck
|
||||
*/
|
||||
|
||||
static void
|
||||
inp_new_func(char *funcname, char *funcbody, struct line *card, struct func_temper **new_func,
|
||||
int *sub_count, int subckt_depth)
|
||||
{
|
||||
struct func_temper *new_func_tmp;
|
||||
static struct func_temper *new_func_end;
|
||||
char *new_str;
|
||||
|
||||
new_func_tmp = TMALLOC(struct func_temper, 1);
|
||||
new_func_tmp->funcname = funcname;
|
||||
new_func_tmp->next = NULL;
|
||||
new_func_tmp->subckt_depth = subckt_depth;
|
||||
new_func_tmp->subckt_count = sub_count[subckt_depth];
|
||||
|
||||
/* Insert at the back */
|
||||
if (*new_func == NULL) {
|
||||
*new_func = new_func_end = new_func_tmp;
|
||||
} else {
|
||||
new_func_end->next = new_func_tmp;
|
||||
new_func_end = new_func_tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
if (*new_func == NULL)
|
||||
*new_func = new_func_tmp;
|
||||
else {
|
||||
new_func_tmp->next = *new_func;
|
||||
*new_func = new_func_tmp;
|
||||
}
|
||||
*/
|
||||
/* replace line in deck */
|
||||
new_str = TMALLOC(char, strlen(funcname) + strlen(funcbody) + 10);
|
||||
sprintf(new_str, ".func %s() %s", funcname, funcbody);
|
||||
card->li_next = xx_new_line(card->li_next, new_str, 0, card->li_linenum);
|
||||
*card->li_line = '*';
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
inp_rem_func(struct func_temper **beg_func)
|
||||
{
|
||||
struct func_temper *b = *beg_func;
|
||||
|
||||
for(; b; b = b->next)
|
||||
tfree(b->funcname);
|
||||
|
||||
tfree(*beg_func);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -434,5 +434,6 @@ extern int NIpred(CKTcircuit *ckt);
|
|||
#endif
|
||||
|
||||
extern IFfrontEnd *SPfrontEnd;
|
||||
extern bool expr_w_temper;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ Modified: 1999 Paolo Nenzi
|
|||
static double actval, actdiff;
|
||||
#endif
|
||||
|
||||
extern void inp_evaluate_temper(void);
|
||||
|
||||
int
|
||||
DCtrCurv(CKTcircuit *ckt, int restart)
|
||||
|
||||
|
|
@ -157,6 +159,8 @@ DCtrCurv(CKTcircuit *ckt, int restart)
|
|||
job->TRCVvType[i] = TEMP_CODE; /* Set the sweep type code */
|
||||
ckt->CKTtemp = job->TRCVvStart[i] + CONSTCtoK; /* Set the new circuit temp */
|
||||
CKTtemp(ckt);
|
||||
if (expr_w_temper)
|
||||
inp_evaluate_temper();
|
||||
goto found;
|
||||
}
|
||||
|
||||
|
|
@ -298,6 +302,8 @@ resume:
|
|||
} else if (job->TRCVvType[i] == TEMP_CODE) {
|
||||
ckt->CKTtemp = job->TRCVvStart[i] + CONSTCtoK;
|
||||
CKTtemp(ckt);
|
||||
if (expr_w_temper)
|
||||
inp_evaluate_temper();
|
||||
|
||||
} else if (job->TRCVvType[i] == rcode) {
|
||||
((RESinstance *)(job->TRCVvElt[i]))->RESresist =
|
||||
|
|
@ -508,6 +514,8 @@ nextstep:;
|
|||
{
|
||||
ckt->CKTtemp += job->TRCVvStep[i];
|
||||
CKTtemp(ckt);
|
||||
if (expr_w_temper)
|
||||
inp_evaluate_temper();
|
||||
} /* else not possible */
|
||||
|
||||
if(SPfrontEnd->IFpauseTest()) {
|
||||
|
|
@ -551,6 +559,8 @@ nextstep:;
|
|||
else if (job->TRCVvType[i] == TEMP_CODE) {
|
||||
ckt->CKTtemp = job->TRCVvSave[i];
|
||||
CKTtemp(ckt);
|
||||
if (expr_w_temper)
|
||||
inp_evaluate_temper();
|
||||
} /* else not possible */
|
||||
}
|
||||
SPfrontEnd->OUTendPlot (plot);
|
||||
|
|
|
|||
Loading…
Reference in New Issue