7833 lines
252 KiB
C
7833 lines
252 KiB
C
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1985 Wayne A. Christopher
|
|
**********/
|
|
|
|
/*
|
|
For dealing with spice input decks and command scripts
|
|
|
|
Central function is inp_readall()
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
|
|
#include "ngspice/cpdefs.h"
|
|
#include "ngspice/ftedefs.h"
|
|
#include "ngspice/fteext.h"
|
|
#include "ngspice/dvec.h"
|
|
#include "ngspice/fteinp.h"
|
|
#include "ngspice/compatmode.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#if !defined(__MINGW32__) && !defined(_MSC_VER)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "inpcom.h"
|
|
#include "variable.h"
|
|
#include "subckt.h"
|
|
#include "../misc/util.h" /* ngdirname() */
|
|
#include "ngspice/stringutil.h"
|
|
#include "ngspice/stringskip.h"
|
|
#include "ngspice/wordlist.h"
|
|
|
|
#ifdef XSPICE
|
|
/* gtri - add - 12/12/90 - wbk - include new stuff */
|
|
#include "ngspice/ipctiein.h"
|
|
#include "ngspice/enh.h"
|
|
/* gtri - end - 12/12/90 */
|
|
#endif
|
|
|
|
/* SJB - Uncomment this line for debug tracing */
|
|
/*#define TRACE*/
|
|
|
|
/* globals -- wanted to avoid complicating inp_readall interface */
|
|
#define N_LIBRARIES 1000
|
|
#define N_PARAMS 1000
|
|
#define N_SUBCKT_W_PARAMS 4000
|
|
|
|
#define NPARAMS 10000
|
|
#define FCN_PARAMS 1000
|
|
|
|
#define VALIDCHARS "!$%_#?@.[]&"
|
|
|
|
static struct library {
|
|
char *realpath;
|
|
char *habitat;
|
|
struct card *deck;
|
|
} libraries[N_LIBRARIES];
|
|
|
|
static int num_libraries;
|
|
|
|
struct names {
|
|
char *names[N_SUBCKT_W_PARAMS];
|
|
int num_names;
|
|
};
|
|
|
|
struct function_env
|
|
{
|
|
struct function_env *up;
|
|
|
|
struct function {
|
|
struct function *next;
|
|
char *name;
|
|
char *body;
|
|
char *params[N_PARAMS];
|
|
int num_parameters;
|
|
const char *accept;
|
|
} *functions;
|
|
};
|
|
|
|
struct func_temper
|
|
{
|
|
char* funcname;
|
|
int subckt_depth;
|
|
int subckt_count;
|
|
struct func_temper *next;
|
|
};
|
|
|
|
extern void line_free_x(struct card *deck, bool recurse);
|
|
|
|
COMPATMODE_T inp_compat_mode;
|
|
|
|
/* Collect information for dynamic allocation of numparam arrays */
|
|
/* number of lines in input deck */
|
|
int dynmaxline; /* inpcom.c 1529 */
|
|
/* number of lines in deck after expansion */
|
|
int dynMaxckt = 0; /* subckt.c 307 */
|
|
/* number of parameter substitutions */
|
|
long dynsubst; /* spicenum.c 221 */
|
|
|
|
static bool has_if = FALSE; /* if we have an .if ... .endif pair */
|
|
|
|
static char *readline(FILE *fd);
|
|
static int get_number_terminals(char *c);
|
|
static void inp_stripcomments_deck(struct card *deck, bool cs);
|
|
static void inp_stripcomments_line(char *s, bool cs);
|
|
static void inp_fix_for_numparam(struct names *subckt_w_params, struct card *deck);
|
|
static void inp_remove_excess_ws(struct card *deck);
|
|
static void expand_section_references(struct card *deck, char *dir_name);
|
|
static void inp_grab_func(struct function_env *, struct card *deck);
|
|
static void inp_fix_inst_calls_for_numparam(struct names *subckt_w_params, struct card *deck);
|
|
static void inp_expand_macros_in_func(struct function_env *);
|
|
static struct card *inp_expand_macros_in_deck(struct function_env *, struct card *deck);
|
|
static void inp_fix_param_values(struct card *deck);
|
|
static void inp_reorder_params(struct names *subckt_w_params, struct card *list_head);
|
|
static int inp_split_multi_param_lines(struct card *deck, int line_number);
|
|
static void inp_sort_params(struct card *param_cards, struct card *card_bf_start, struct card *s_c, struct card *e_c);
|
|
static char *inp_remove_ws(char *s);
|
|
static void inp_compat(struct card *deck);
|
|
static void inp_bsource_compat(struct card *deck);
|
|
static bool inp_temper_compat(struct card *card);
|
|
static void inp_meas_current(struct card *card);
|
|
static void inp_dot_if(struct card *deck);
|
|
static char *inp_modify_exp(char* expression);
|
|
static struct func_temper *inp_new_func(char *funcname, char *funcbody, struct card *card,
|
|
int *sub_count, int subckt_depth);
|
|
static void inp_delete_funcs(struct func_temper *funcs);
|
|
|
|
static bool chk_for_line_continuation(char *line);
|
|
static void comment_out_unused_subckt_models(struct card *start_card);
|
|
static void rem_mfg_from_models(struct card *start_card);
|
|
static void inp_fix_macro_param_func_paren_io(struct card *begin_card);
|
|
static void inp_fix_gnd_name(struct card *deck);
|
|
static void inp_chk_for_multi_in_vcvs(struct card *deck, int *line_number);
|
|
static void inp_add_control_section(struct card *deck, int *line_number);
|
|
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 card *deck);
|
|
static void subckt_params_to_param(struct card *deck);
|
|
static void inp_fix_temper_in_param(struct card *deck);
|
|
static void inp_fix_agauss_in_param(struct card *deck, char *fcn);
|
|
static void inp_vdmos_model(struct card *deck);
|
|
static void inp_check_syntax(struct card *deck);
|
|
|
|
static char *inp_spawn_brace(char *s);
|
|
|
|
static char *inp_pathresolve(const char *name);
|
|
static char *inp_pathresolve_at(char *name, char *dir);
|
|
static char *search_plain_identifier(char *str, const char *identifier);
|
|
void tprint(struct card *deck);
|
|
static struct card *pspice_compat(struct card *newcard);
|
|
static void pspice_compat_a(struct card *oldcard);
|
|
static struct card *ltspice_compat(struct card *oldcard);
|
|
static void ltspice_compat_a(struct card *oldcard);
|
|
|
|
struct inp_read_t
|
|
{ struct card *cc;
|
|
int line_number;
|
|
};
|
|
|
|
static struct inp_read_t inp_read(FILE *fp, int call_depth, char *dir_name, bool comfile, bool intfile);
|
|
|
|
|
|
#ifndef XSPICE
|
|
static void inp_poly_err(struct card *deck);
|
|
#endif
|
|
|
|
|
|
/* insert a new card, just behind the given card */
|
|
static struct card *
|
|
insert_new_line(struct card *card, char *line, int linenum, int linenum_orig)
|
|
{
|
|
struct card *x = TMALLOC(struct card, 1);
|
|
|
|
x->nextcard = card ? card->nextcard : NULL;
|
|
x->error = NULL;
|
|
x->actualLine = NULL;
|
|
x->line = line;
|
|
x->linenum = linenum;
|
|
x->linenum_orig = linenum_orig;
|
|
|
|
if (card)
|
|
card->nextcard = x;
|
|
|
|
return x;
|
|
}
|
|
|
|
|
|
/* insert new_card, just behind the given card */
|
|
static struct card *
|
|
insert_deck(struct card *card, struct card *new_card)
|
|
{
|
|
if (card) {
|
|
new_card->nextcard = card->nextcard;
|
|
card->nextcard = new_card;
|
|
} else {
|
|
new_card->nextcard = NULL;
|
|
}
|
|
return new_card;
|
|
}
|
|
|
|
|
|
static struct library *
|
|
new_lib(void)
|
|
{
|
|
if (num_libraries >= N_LIBRARIES) {
|
|
fprintf(stderr, "ERROR, N_LIBRARIES overflow\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return & libraries[num_libraries++];
|
|
}
|
|
|
|
|
|
static void
|
|
delete_libs(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_libraries; i++) {
|
|
tfree(libraries[i].realpath);
|
|
tfree(libraries[i].habitat);
|
|
line_free_x(libraries[i].deck, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
static struct library *
|
|
find_lib(char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_libraries; i++)
|
|
if (cieq(libraries[i].realpath, name))
|
|
return & libraries[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct card *
|
|
find_section_definition(struct card *c, char *name)
|
|
{
|
|
for (; c; c = c->nextcard) {
|
|
|
|
char *line = c->line;
|
|
|
|
if (ciprefix(".lib", line)) {
|
|
|
|
char *s, *t, *y;
|
|
|
|
s = skip_non_ws(line);
|
|
while (isspace_c(*s) || isquote(*s))
|
|
s++;
|
|
for (t = s; *t && !isspace_c(*t) && !isquote(*t); t++)
|
|
;
|
|
y = t;
|
|
while (isspace_c(*y) || isquote(*y))
|
|
y++;
|
|
|
|
if (!*y) {
|
|
/* library section definition: `.lib <section-name>' .. `.endl' */
|
|
|
|
char keep_char = *t;
|
|
*t = '\0';
|
|
|
|
if (strcasecmp(name, s) == 0) {
|
|
*t = keep_char;
|
|
return c;
|
|
}
|
|
|
|
*t = keep_char;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct library *
|
|
read_a_lib(char *y, char *dir_name)
|
|
{
|
|
char *yy, *y_resolved;
|
|
|
|
struct library *lib;
|
|
|
|
y_resolved = inp_pathresolve_at(y, dir_name);
|
|
|
|
if (!y_resolved) {
|
|
fprintf(cp_err, "Error: Could not find library file %s\n", y);
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__MINGW32__) || defined(_MSC_VER)
|
|
yy = _fullpath(NULL, y_resolved, 0);
|
|
#else
|
|
yy = realpath(y_resolved, NULL);
|
|
#endif
|
|
|
|
if (!yy) {
|
|
fprintf(cp_err, "Error: Could not `realpath' library file %s\n", y);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
lib = find_lib(yy);
|
|
|
|
if (!lib) {
|
|
|
|
FILE *newfp = fopen(y_resolved, "r");
|
|
|
|
if (!newfp) {
|
|
fprintf(cp_err, "Error: Could not open library file %s\n", y);
|
|
return NULL;
|
|
}
|
|
|
|
/* lib points to a new entry in global lib array libraries[N_LIBRARIES] */
|
|
lib = new_lib();
|
|
|
|
lib->realpath = copy(yy);
|
|
lib->habitat = ngdirname(yy);
|
|
|
|
lib->deck = inp_read(newfp, 1 /*dummy*/, lib->habitat, FALSE, FALSE) . cc;
|
|
|
|
fclose(newfp);
|
|
}
|
|
|
|
tfree(yy);
|
|
tfree(y_resolved);
|
|
|
|
return lib;
|
|
}
|
|
|
|
|
|
static struct names *
|
|
new_names(void)
|
|
{
|
|
struct names *p = TMALLOC(struct names, 1);
|
|
p -> num_names = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
static void
|
|
delete_names(struct names *p)
|
|
{
|
|
int i;
|
|
for (i = 0; i < p->num_names; i++)
|
|
tfree(p->names[i]);
|
|
tfree(p);
|
|
}
|
|
|
|
|
|
|
|
/* line1
|
|
+ line2
|
|
---->
|
|
line1 line 2
|
|
Proccedure: store regular card in prev, skip comment lines (*..) and some others
|
|
*/
|
|
|
|
static void
|
|
inp_stitch_continuation_lines(struct card *working)
|
|
{
|
|
struct card *prev = NULL;
|
|
|
|
while (working) {
|
|
char *s, c, *buffer;
|
|
|
|
for (s = working->line; (c = *s) != '\0' && c <= ' '; s++)
|
|
;
|
|
|
|
#ifdef TRACE
|
|
/* SDB debug statement */
|
|
printf("In inp_read, processing linked list element line = %d, s = %s . . . \n", working->linenum, s);
|
|
#endif
|
|
|
|
switch (c) {
|
|
case '#':
|
|
case '$':
|
|
case '*':
|
|
case '\0':
|
|
/* skip these cards, and keep prev as the last regular card */
|
|
working = working->nextcard; /* for these chars, go to next card */
|
|
break;
|
|
|
|
case '+': /* handle continuation */
|
|
if (!prev) {
|
|
working->error = copy("Illegal continuation line: ignored.");
|
|
working = working->nextcard;
|
|
break;
|
|
}
|
|
|
|
/* We now may have lept over some comment lines, which are located among
|
|
the continuation lines. We have to delete them here to prevent a memory leak */
|
|
while (prev->nextcard != working) {
|
|
struct card *tmpl = prev->nextcard->nextcard;
|
|
line_free_x(prev->nextcard, FALSE);
|
|
prev->nextcard = tmpl;
|
|
}
|
|
|
|
/* create buffer and write last and current line into it. */
|
|
buffer = tprintf("%s %s", prev->line, s + 1);
|
|
|
|
/* replace prev->line by buffer */
|
|
s = prev->line;
|
|
prev->line = buffer;
|
|
prev->nextcard = working->nextcard;
|
|
working->nextcard = NULL;
|
|
/* add original line to prev->actualLine */
|
|
if (prev->actualLine) {
|
|
struct card *end;
|
|
for (end = prev->actualLine; end->nextcard; end = end->nextcard)
|
|
;
|
|
end->nextcard = working;
|
|
tfree(s);
|
|
} else {
|
|
prev->actualLine = insert_new_line(NULL, s, prev->linenum, 0);
|
|
prev->actualLine->nextcard = working;
|
|
}
|
|
working = prev->nextcard;
|
|
break;
|
|
|
|
default: /* regular one-line card */
|
|
prev = working;
|
|
working = working->nextcard;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* search for `=' assignment operator
|
|
* take care of `!=' `<=' `==' and `>='
|
|
*/
|
|
|
|
char *
|
|
find_assignment(const char *str)
|
|
{
|
|
const char *p = str;
|
|
|
|
while ((p = strchr(p, '=')) != NULL) {
|
|
|
|
// check for equality '=='
|
|
if (p[1] == '=') {
|
|
p += 2;
|
|
continue;
|
|
}
|
|
|
|
// check for '!=', '<=', '>='
|
|
if (p > str)
|
|
if (p[-1] == '!' || p[-1] == '<' || p[-1] == '>') {
|
|
p += 1;
|
|
continue;
|
|
}
|
|
|
|
return (char *) p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* backward search for an assignment
|
|
* fixme, doesn't honour neither " nor ' quotes
|
|
*/
|
|
|
|
char *
|
|
find_back_assignment(const char *p, const char *start)
|
|
{
|
|
while (--p >= start) {
|
|
if (*p != '=')
|
|
continue;
|
|
// check for '!=', '<=', '>=', '=='
|
|
if (p <= start || !strchr("!<=>", p[-1]))
|
|
return (char *) p;
|
|
p--;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Set a compatibility flag.
|
|
Currently available are flags for:
|
|
- ngspice (standard)
|
|
- a commercial simulator
|
|
- Spice3
|
|
- all compatibility stuff
|
|
*/
|
|
static COMPATMODE_T
|
|
ngspice_compat_mode(void)
|
|
{
|
|
char behaviour[80];
|
|
|
|
if (cp_getvar("ngbehavior", CP_STRING, behaviour, sizeof(behaviour))) {
|
|
if (strcasecmp(behaviour, "all") == 0)
|
|
return COMPATMODE_ALL;
|
|
if (strcasecmp(behaviour, "hs") == 0)
|
|
return COMPATMODE_HS;
|
|
if (strcasecmp(behaviour, "ps") == 0)
|
|
return COMPATMODE_PS;
|
|
if (strcasecmp(behaviour, "lt") == 0)
|
|
return COMPATMODE_LT;
|
|
if (strcasecmp(behaviour, "ltps") == 0)
|
|
return COMPATMODE_LTPS;
|
|
if (strcasecmp(behaviour, "psa") == 0)
|
|
return COMPATMODE_PSA;
|
|
if (strcasecmp(behaviour, "lta") == 0)
|
|
return COMPATMODE_LTA;
|
|
if (strcasecmp(behaviour, "ltpsa") == 0)
|
|
return COMPATMODE_LTPSA;
|
|
if (strcasecmp(behaviour, "spice3") == 0)
|
|
return COMPATMODE_SPICE3;
|
|
}
|
|
|
|
return COMPATMODE_ALL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Read the entire input file and return a pointer to the first line of
|
|
the linked list of 'card' records in data. The pointer is stored in
|
|
*data.
|
|
Called from fcn inp_spsource() in inp.c to load circuit or command files.
|
|
Called from fcn com_alter_mod() in device.c to load model files.
|
|
Called from here to load .library or .include files.
|
|
|
|
Procedure:
|
|
read in all lines & put them in the struct cc
|
|
read next line
|
|
process .TITLE line
|
|
store contents in string new_title
|
|
process .lib lines
|
|
read file and library name, open file using fcn inp_pathopen()
|
|
read file contents and put into struct libraries[].deck, one entry per .lib line
|
|
process .inc lines
|
|
read file and library name, open file using fcn inp_pathopen()
|
|
read file contents and add lines to cc
|
|
make line entry lower case
|
|
allow for shell end of line continuation (\\)
|
|
add '+' to beginning of next line
|
|
add line entry to list cc
|
|
add '.global gnd'
|
|
add libraries
|
|
find library section
|
|
add lines
|
|
add .end card
|
|
strip end-of-line comments
|
|
make continuation lines a single line
|
|
*** end of processing for command files ***
|
|
start preparation of input deck for numparam
|
|
...
|
|
debug printout to debug-out.txt
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
struct card *
|
|
inp_readall(FILE *fp, char *dir_name, bool comfile, bool intfile, bool *expr_w_temper_p)
|
|
{
|
|
struct card *cc;
|
|
struct inp_read_t rv;
|
|
|
|
num_libraries = 0;
|
|
inp_compat_mode = ngspice_compat_mode();
|
|
|
|
rv = inp_read(fp, 0, dir_name, comfile, intfile);
|
|
cc = rv . cc;
|
|
|
|
/* files starting with *ng_script are user supplied command files */
|
|
if (cc && ciprefix("*ng_script", cc->line))
|
|
comfile = TRUE;
|
|
|
|
/* The following processing of an input file is not required for command files
|
|
like spinit or .spiceinit, so return command files here. */
|
|
|
|
if (!comfile && cc) {
|
|
|
|
unsigned int no_braces; /* number of '{' */
|
|
size_t max_line_length; /* max. line length in input deck */
|
|
struct card *tmp_ptr1;
|
|
struct names *subckt_w_params = new_names();
|
|
|
|
/* skip title line */
|
|
struct card *working = cc->nextcard;
|
|
|
|
delete_libs();
|
|
|
|
/* some syntax checks, including title line */
|
|
inp_check_syntax(cc);
|
|
|
|
if(inp_compat_mode == COMPATMODE_LTA)
|
|
ltspice_compat_a(working);
|
|
else if(inp_compat_mode == COMPATMODE_PSA)
|
|
pspice_compat_a(working);
|
|
else if (inp_compat_mode == COMPATMODE_LTPSA) {
|
|
ltspice_compat_a(working);
|
|
pspice_compat_a(working);
|
|
}
|
|
|
|
inp_fix_for_numparam(subckt_w_params, working);
|
|
|
|
inp_remove_excess_ws(working);
|
|
|
|
inp_vdmos_model(working);
|
|
/* don't remove unused model if we have an .if clause, because we cannot yet
|
|
decide here which model we finally will need */
|
|
if (!has_if)
|
|
comment_out_unused_subckt_models(working);
|
|
|
|
rem_mfg_from_models(working);
|
|
|
|
subckt_params_to_param(working);
|
|
|
|
rv . line_number = inp_split_multi_param_lines(working, rv . line_number);
|
|
|
|
inp_fix_macro_param_func_paren_io(working);
|
|
|
|
static char *statfcn[] = { "agauss", "gauss", "aunif", "unif", "limit" };
|
|
int ii;
|
|
for (ii = 0; ii < 5; ii++)
|
|
inp_fix_agauss_in_param(working, statfcn[ii]);
|
|
inp_fix_temper_in_param(working);
|
|
|
|
inp_expand_macros_in_deck(NULL, working);
|
|
inp_fix_param_values(working);
|
|
|
|
inp_reorder_params(subckt_w_params, cc);
|
|
inp_fix_inst_calls_for_numparam(subckt_w_params, working);
|
|
|
|
delete_names(subckt_w_params);
|
|
subckt_w_params = NULL;
|
|
if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0))
|
|
inp_fix_gnd_name(working);
|
|
inp_chk_for_multi_in_vcvs(working, &rv. line_number);
|
|
|
|
if (cp_getvar("addcontrol", CP_BOOL, NULL, 0))
|
|
inp_add_control_section(working, &rv . line_number);
|
|
#ifndef XSPICE
|
|
inp_poly_err(working);
|
|
#endif
|
|
bool expr_w_temper = FALSE;
|
|
if (inp_compat_mode != COMPATMODE_SPICE3) {
|
|
/* Do all the compatibility stuff here */
|
|
working = cc->nextcard;
|
|
inp_meas_current(working);
|
|
/* E, G, L, R, C compatibility transformations */
|
|
inp_compat(working);
|
|
working = cc->nextcard;
|
|
/* B source numparam compatibility transformation */
|
|
inp_bsource_compat(working);
|
|
inp_dot_if(working);
|
|
expr_w_temper = inp_temper_compat(working);
|
|
}
|
|
if (expr_w_temper_p)
|
|
*expr_w_temper_p = expr_w_temper;
|
|
|
|
inp_add_series_resistor(working);
|
|
|
|
/* get max. line length and number of lines in input deck,
|
|
and renumber the lines,
|
|
count the number of '{' per line as an upper estimate of the number
|
|
of parameter substitutions in a line */
|
|
dynmaxline = 0;
|
|
max_line_length = 0;
|
|
no_braces = 0;
|
|
for (tmp_ptr1 = cc; tmp_ptr1; tmp_ptr1 = tmp_ptr1->nextcard) {
|
|
char *s;
|
|
unsigned int braces_per_line = 0;
|
|
/* count number of lines */
|
|
dynmaxline++;
|
|
/* renumber the lines of the processed input deck */
|
|
tmp_ptr1->linenum = dynmaxline;
|
|
if (max_line_length < strlen(tmp_ptr1->line))
|
|
max_line_length = strlen(tmp_ptr1->line);
|
|
/* count '{' */
|
|
for (s = tmp_ptr1->line; *s; s++)
|
|
if (*s == '{')
|
|
braces_per_line++;
|
|
if (no_braces < braces_per_line)
|
|
no_braces = braces_per_line;
|
|
}
|
|
|
|
if (ft_ngdebug) {
|
|
FILE *fd = fopen("debug-out.txt", "w");
|
|
if (fd) {
|
|
/*debug: print into file*/
|
|
struct card *t;
|
|
fprintf(fd, "**************** uncommented deck **************\n\n");
|
|
/* always print first line */
|
|
fprintf(fd, "%6d %6d %s\n", cc->linenum_orig, cc->linenum, cc->line);
|
|
/* here without out-commented lines */
|
|
for (t = cc->nextcard; t; t = t->nextcard) {
|
|
if (*(t->line) == '*')
|
|
continue;
|
|
fprintf(fd, "%6d %6d %s\n", t->linenum_orig, t->linenum, t->line);
|
|
}
|
|
fprintf(fd, "\n****************** complete deck ***************\n\n");
|
|
/* now completely */
|
|
for (t = cc; t; t = t->nextcard)
|
|
fprintf(fd, "%6d %6d %s\n", t->linenum_orig, t->linenum, t->line);
|
|
fclose(fd);
|
|
|
|
fprintf(stdout, "max line length %d, max subst. per line %d, number of lines %d\n",
|
|
(int)max_line_length, no_braces, dynmaxline);
|
|
}
|
|
else
|
|
fprintf(stderr, "Warning: Cannot open file debug-out.txt for saving debug info\n");
|
|
}
|
|
}
|
|
|
|
return cc;
|
|
}
|
|
|
|
|
|
struct inp_read_t
|
|
inp_read(FILE *fp, int call_depth, char *dir_name, bool comfile, bool intfile)
|
|
/* fp: in, pointer to file to be read,
|
|
call_depth: in, nested call to fcn
|
|
dir_name: in, name of directory of file to be read
|
|
comfile: in, TRUE if command file (e.g. spinit, .spiceinit)
|
|
intfile: in, TRUE if deck is generated from internal circarray
|
|
*/
|
|
{
|
|
struct inp_read_t rv;
|
|
struct card *end = NULL, *cc = NULL;
|
|
char *buffer = NULL;
|
|
/* segfault fix */
|
|
#ifdef XSPICE
|
|
char big_buff[5000];
|
|
int line_count = 0;
|
|
#endif
|
|
char *new_title = NULL;
|
|
int line_number = 1; /* sjb - renamed to avoid confusion with struct card */
|
|
int line_number_orig = 1;
|
|
int cirlinecount = 0; /* length of circarray */
|
|
static int is_control = 0; /* We are reading from a .control section */
|
|
|
|
bool found_end = FALSE, shell_eol_continuation = FALSE;
|
|
|
|
/* First read in all lines & put them in the struct cc */
|
|
for (;;) {
|
|
/* derive lines from circarray */
|
|
if (intfile) {
|
|
buffer = circarray[cirlinecount++];
|
|
if (!buffer) {
|
|
tfree(circarray);
|
|
break;
|
|
}
|
|
}
|
|
/* read lines from file fp */
|
|
else {
|
|
|
|
#ifdef XSPICE
|
|
/* gtri - modify - 12/12/90 - wbk - read from mailbox if ipc enabled */
|
|
|
|
/* If IPC is not enabled, do equivalent of what SPICE did before */
|
|
if (! g_ipc.enabled) {
|
|
if (call_depth == 0 && line_count == 0) {
|
|
line_count++;
|
|
if (fgets(big_buff, 5000, fp))
|
|
buffer = copy(big_buff);
|
|
} else {
|
|
buffer = readline(fp);
|
|
if (!buffer)
|
|
break;
|
|
}
|
|
} else {
|
|
/* else, get the line from the ipc channel. */
|
|
/* We assume that newlines are not sent by the client */
|
|
/* so we add them here */
|
|
char ipc_buffer[1025]; /* Had better be big enough */
|
|
int ipc_len;
|
|
Ipc_Status_t ipc_status =
|
|
ipc_get_line(ipc_buffer, &ipc_len, IPC_WAIT);
|
|
if (ipc_status == IPC_STATUS_END_OF_DECK) {
|
|
buffer = NULL;
|
|
break;
|
|
} else if (ipc_status == IPC_STATUS_OK) {
|
|
buffer = TMALLOC(char, strlen(ipc_buffer) + 3);
|
|
strcpy(buffer, ipc_buffer);
|
|
strcat(buffer, "\n");
|
|
} else { /* No good way to report this so just die */
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* gtri - end - 12/12/90 */
|
|
#else
|
|
|
|
buffer = readline(fp);
|
|
if(!buffer)
|
|
break;
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifdef TRACE
|
|
/* SDB debug statement */
|
|
printf("in inp_read, just read %s", buffer);
|
|
#endif
|
|
|
|
if (!buffer)
|
|
continue;
|
|
|
|
/* OK -- now we have loaded the next line into 'buffer'. Process it. */
|
|
/* If input line is blank, ignore it & continue looping. */
|
|
if ((strcmp(buffer, "\n") == 0) || (strcmp(buffer, "\r\n") == 0))
|
|
if (call_depth != 0 || (call_depth == 0 && cc != NULL)) {
|
|
line_number_orig++;
|
|
tfree(buffer); /* was allocated by readline() */
|
|
continue;
|
|
}
|
|
|
|
if (*buffer == '@') {
|
|
tfree(buffer); /* was allocated by readline() */
|
|
break;
|
|
}
|
|
|
|
/* now check if we are in a .control section */
|
|
if (ciprefix(".control", buffer))
|
|
is_control++;
|
|
else if (ciprefix(".endc", buffer))
|
|
is_control--;
|
|
|
|
/* now handle .title statement */
|
|
if (ciprefix(".title", buffer)) {
|
|
char *s;
|
|
s = skip_non_ws(buffer); /* skip over .title */
|
|
s = skip_ws(s); /* advance past space chars */
|
|
|
|
/* only the last title line remains valid */
|
|
tfree(new_title);
|
|
new_title = copy(s);
|
|
if ((s = strchr(new_title, '\n')) != NULL)
|
|
*s = ' ';
|
|
*buffer = '*'; /* change .TITLE line to comment line */
|
|
}
|
|
|
|
/* now handle old style .lib entries */
|
|
/* new style .lib entries handling is in expand_section_references() */
|
|
if (ciprefix(".lib", buffer))
|
|
if (inp_compat_mode == COMPATMODE_PS || inp_compat_mode == COMPATMODE_PSA
|
|
|| inp_compat_mode == COMPATMODE_LTPS || inp_compat_mode == COMPATMODE_LTPSA) {
|
|
/* compatibility mode,
|
|
* this is neither a libray section definition nor a reference
|
|
* interpret as old style
|
|
* .lib <file name> (no lib name given)
|
|
*/
|
|
char *s = skip_non_ws(buffer); /* skip over .lib */
|
|
fprintf(cp_err, " File included as: .inc %s\n", s);
|
|
memcpy(buffer, ".inc", 4);
|
|
}
|
|
|
|
/* now handle .include statements */
|
|
if (ciprefix(".include", buffer) || ciprefix(".inc", buffer)) {
|
|
|
|
char *y = NULL;
|
|
char *s;
|
|
|
|
struct card *newcard;
|
|
|
|
inp_stripcomments_line(buffer, FALSE);
|
|
|
|
s = skip_non_ws(buffer); /* advance past non-space chars */
|
|
|
|
s = get_quoted_token(s, &y);
|
|
|
|
if (!y) {
|
|
fprintf(cp_err, "Error: .include filename missing\n");
|
|
tfree(buffer); /* was allocated by readline() */
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
{
|
|
char *y_resolved = inp_pathresolve_at(y, dir_name);
|
|
char *y_dir_name;
|
|
FILE *newfp;
|
|
|
|
if (!y_resolved) {
|
|
fprintf(cp_err, "Error: Could not find include file %s\n", y);
|
|
rv . line_number = line_number;
|
|
rv . cc = NULL;
|
|
return rv;
|
|
}
|
|
|
|
newfp = fopen(y_resolved, "r");
|
|
|
|
if (!newfp) {
|
|
fprintf(cp_err, "Error: .include statement failed.\n");
|
|
tfree(buffer); /* allocated by readline() above */
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
y_dir_name = ngdirname(y_resolved);
|
|
|
|
newcard = inp_read(newfp, call_depth+1, y_dir_name, FALSE, FALSE) . cc; /* read stuff in include file into netlist */
|
|
|
|
tfree(y_dir_name);
|
|
tfree(y_resolved);
|
|
|
|
(void) fclose(newfp);
|
|
}
|
|
|
|
/* Make the .include a comment */
|
|
*buffer = '*';
|
|
|
|
/* append `buffer' to the (cc, end) chain of decks */
|
|
{
|
|
end = insert_new_line(end, copy(buffer), line_number, line_number);
|
|
|
|
if (!cc)
|
|
cc = end;
|
|
|
|
line_number++;
|
|
}
|
|
|
|
if (newcard) {
|
|
if (inp_compat_mode == COMPATMODE_LT)
|
|
newcard = ltspice_compat(newcard);
|
|
else if (inp_compat_mode == COMPATMODE_PS)
|
|
newcard = pspice_compat(newcard);
|
|
else if (inp_compat_mode == COMPATMODE_LTPS) {
|
|
newcard = ltspice_compat(newcard);
|
|
newcard = pspice_compat(newcard);
|
|
}
|
|
|
|
int line_number_inc = 1;
|
|
end->nextcard = newcard;
|
|
/* Renumber the lines */
|
|
for (end = newcard; end && end->nextcard; end = end->nextcard) {
|
|
end->linenum = line_number++;
|
|
end->linenum_orig = line_number_inc++;
|
|
}
|
|
end->linenum = line_number++; /* SJB - renumber the last line */
|
|
end->linenum_orig = line_number_inc++; /* SJB - renumber the last line */
|
|
}
|
|
|
|
/* Fix the buffer up a bit. */
|
|
(void) strncpy(buffer + 1, "end of: ", 8);
|
|
} /* end of .include handling */
|
|
|
|
/* loop through 'buffer' until end is reached. Make all letters lower
|
|
* case except for the commands given below. Special treatment for
|
|
* commands 'hardcopy' and 'plot', where all letters are made lower
|
|
* case except for the tokens following xlabel, ylabel and title.
|
|
* These tokens may contain spaces, if they are enclosed in single or
|
|
* double quotes. Single quotes are later on swallowed and disappear,
|
|
* double quotes are printed. */
|
|
{
|
|
char *s;
|
|
/* no lower case letters for lines beginning with: */
|
|
if (!ciprefix("write", buffer) &&
|
|
!ciprefix("wrdata", buffer) &&
|
|
!ciprefix(".lib", buffer) &&
|
|
!ciprefix(".inc", buffer) &&
|
|
!ciprefix("codemodel", buffer) &&
|
|
!ciprefix("echo", buffer) &&
|
|
!ciprefix("shell", buffer) &&
|
|
!ciprefix("source", buffer) &&
|
|
!ciprefix("load", buffer) &&
|
|
!ciprefix("plot", buffer) &&
|
|
!ciprefix("hardcopy", buffer) &&
|
|
!ciprefix("setcs", buffer)
|
|
)
|
|
{
|
|
/* lower case for all other lines */
|
|
for (s = buffer; *s && (*s != '\n'); s++)
|
|
*s = tolower_c(*s);
|
|
} else if (ciprefix("plot", buffer) || ciprefix("gnuplot", buffer) ||ciprefix("hardcopy", buffer)) {
|
|
/* lower case excluded for tokens following title, xlabel, ylabel.
|
|
* tokens may contain spaces, then they have to be enclosed in quotes.
|
|
* keywords and tokens have to be separated by spaces. */
|
|
int j;
|
|
char t = ' ';
|
|
for (s = buffer; *s && (*s != '\n'); s++) {
|
|
*s = tolower_c(*s);
|
|
if (ciprefix("title", s)) {
|
|
/* jump beyond title */
|
|
for (j = 0; j < 5; j++) {
|
|
s++;
|
|
*s = tolower_c(*s);
|
|
}
|
|
while (*s == ' ')
|
|
s++;
|
|
if (!s || (*s == '\n'))
|
|
break;
|
|
/* check if single quote is at start of token */
|
|
else if (*s == '\'') {
|
|
s++;
|
|
t = '\'';
|
|
}
|
|
/* check if double quote is at start of token */
|
|
else if (*s == '\"') {
|
|
s++;
|
|
t = '\"';
|
|
}
|
|
else
|
|
t = ' ';
|
|
/* jump beyond token without lower casing */
|
|
while ((*s != '\n') && (*s != t))
|
|
s++;
|
|
}
|
|
else if (ciprefix("xlabel", s) || ciprefix("ylabel", s)) {
|
|
/* jump beyond xlabel, ylabel */
|
|
for (j = 0; j < 6; j++) {
|
|
s++;
|
|
*s = tolower_c(*s);
|
|
}
|
|
while (*s == ' ')
|
|
s++;
|
|
if (!s || (*s == '\n'))
|
|
break;
|
|
/* check if single quote is at start of token */
|
|
else if (*s == '\'') {
|
|
s++;
|
|
t = '\'';
|
|
}
|
|
/* check if double quote is at start of token */
|
|
else if (*s == '\"') {
|
|
s++;
|
|
t = '\"';
|
|
}
|
|
else
|
|
t = ' ';
|
|
/* jump beyond token without lower casing */
|
|
while ((*s != '\n') && (*s != t))
|
|
s++;
|
|
}
|
|
}
|
|
}
|
|
else if (ciprefix("print", buffer) || ciprefix("asciiplot", buffer)) {
|
|
/* lower case excluded for tokens following output redirection '>' */
|
|
bool redir = FALSE;
|
|
for (s = buffer; *s && (*s != '\n'); s++) {
|
|
if (*s == '>')
|
|
redir = TRUE; /* do not lower, but move to end of string */
|
|
if (!redir)
|
|
*s = tolower_c(*s);
|
|
}
|
|
}
|
|
else {
|
|
/* add Inp_Path to sourcepath variable */
|
|
char *p = strstr(buffer, "sourcepath");
|
|
if (p) {
|
|
p = strchr(buffer, ')');
|
|
if (p) {
|
|
*p = 0; // clear ) and insert Inp_Path in between
|
|
p = tprintf("%s %s ) %s", buffer, Inp_Path ? Inp_Path : "", p + 1);
|
|
tfree(buffer);
|
|
buffer = p;
|
|
}
|
|
}
|
|
/* exclude commands listed above to preserve filename case */
|
|
for (s = buffer; *s && (*s != '\n'); s++)
|
|
;
|
|
}
|
|
|
|
if (!*s) {
|
|
// fprintf(cp_err, "Warning: premature EOF\n");
|
|
}
|
|
*s = '\0'; /* Zap the newline. */
|
|
|
|
if ((s-1) >= buffer && *(s-1) == '\r') /* Zop the carriage return under windows */
|
|
*(s-1) = '\0';
|
|
}
|
|
|
|
/* find the true .end command out of .endc, .ends, .endl, .end (comments may follow) */
|
|
if (ciprefix(".end", buffer))
|
|
if ((buffer[4] == '\0') || isspace_c(buffer[4])) {
|
|
found_end = TRUE;
|
|
*buffer = '*';
|
|
}
|
|
|
|
if (shell_eol_continuation) {
|
|
char *new_buffer = tprintf("+%s", buffer);
|
|
|
|
tfree(buffer);
|
|
buffer = new_buffer;
|
|
}
|
|
|
|
/* If \\ at end of line is found, next line in loop will get + (see code above) */
|
|
shell_eol_continuation = chk_for_line_continuation(buffer);
|
|
|
|
{
|
|
end = insert_new_line(end, copy(buffer), line_number++, line_number_orig++);
|
|
|
|
if (!cc)
|
|
cc = end;
|
|
}
|
|
|
|
tfree(buffer);
|
|
} /* end while ((buffer = readline(fp)) != NULL) */
|
|
|
|
if (!cc) /* No stuff here */
|
|
{
|
|
rv . line_number = line_number;
|
|
rv . cc = cc;
|
|
return rv;
|
|
}
|
|
|
|
/* files starting with *ng_script are user supplied command files */
|
|
if (call_depth == 0 && ciprefix("*ng_script", cc->line))
|
|
comfile = TRUE;
|
|
|
|
if (call_depth == 0 && !comfile) {
|
|
if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0))
|
|
insert_new_line(cc, copy(".global gnd"), 1, 0);
|
|
else
|
|
insert_new_line(cc, copy("* gnd is not set to 0 automatically "), 1, 0);
|
|
|
|
if (inp_compat_mode == COMPATMODE_ALL ||
|
|
inp_compat_mode == COMPATMODE_HS ||
|
|
inp_compat_mode == COMPATMODE_NATIVE)
|
|
{
|
|
/* process all library section references */
|
|
expand_section_references(cc, dir_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
add a terminal ".end" card
|
|
*/
|
|
|
|
if (call_depth == 0 && !comfile)
|
|
if (found_end == TRUE)
|
|
end = insert_new_line(end, copy(".end"), line_number++, line_number_orig++);
|
|
|
|
/* Replace first line with the new title, if available */
|
|
if (call_depth == 0 && !comfile && new_title) {
|
|
tfree(cc->line);
|
|
cc->line = new_title;
|
|
}
|
|
|
|
/* Strip or convert end-of-line comments.
|
|
Afterwards stitch the continuation lines.
|
|
If the line only contains an end-of-line comment then it is converted
|
|
into a normal comment with a '*' at the start. Some special handling
|
|
if this is a command file or called from within a .control section. */
|
|
inp_stripcomments_deck(cc->nextcard, comfile || is_control);
|
|
|
|
inp_stitch_continuation_lines(cc->nextcard);
|
|
|
|
rv . line_number = line_number;
|
|
rv . cc = cc;
|
|
return rv;
|
|
}
|
|
|
|
|
|
static bool
|
|
is_absolute_pathname(const char *p)
|
|
{
|
|
#if defined(__MINGW32__) || defined(_MSC_VER)
|
|
/* /... or \... or D:\... or D:/... */
|
|
return
|
|
p[0] == DIR_TERM ||
|
|
p[0] == DIR_TERM_LINUX ||
|
|
(isalpha_c(p[0]) && p[1] == ':' &&
|
|
(p[2] == DIR_TERM_LINUX || p[2] == DIR_TERM));
|
|
#else
|
|
return
|
|
p[0] == DIR_TERM;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
static bool
|
|
is_plain_filename(const char *p)
|
|
{
|
|
#if defined(__MINGW32__) || defined(_MSC_VER)
|
|
return
|
|
!strchr(p, DIR_TERM) &&
|
|
!strchr(p, DIR_TERM_LINUX);
|
|
#else
|
|
return
|
|
!strchr(p, DIR_TERM);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
FILE *
|
|
inp_pathopen(char *name, char *mode)
|
|
{
|
|
char *path = inp_pathresolve(name);
|
|
|
|
if (path) {
|
|
FILE *fp = fopen(path, mode);
|
|
tfree(path);
|
|
return fp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
Look up the variable sourcepath and try everything in the list in order
|
|
if the file isn't in . and it isn't an abs path name.
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
static char *
|
|
inp_pathresolve(const char *name)
|
|
{
|
|
char buf[BSIZE_SP];
|
|
struct variable *v;
|
|
struct stat st;
|
|
|
|
#if defined(__MINGW32__) || defined(_MSC_VER)
|
|
|
|
/* If variable 'mingwpath' is set: convert mingw /d/... to d:/... */
|
|
if (cp_getvar("mingwpath", CP_BOOL, NULL, 0) && name[0] == DIR_TERM_LINUX && isalpha_c(name[1]) && name[2] == DIR_TERM_LINUX) {
|
|
strcpy(buf, name);
|
|
buf[0] = buf[1];
|
|
buf[1] = ':';
|
|
return inp_pathresolve(buf);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* just try it */
|
|
if (stat(name, &st) == 0)
|
|
return copy(name);
|
|
|
|
/* fail if this was an absolute filename or if there is no sourcepath var */
|
|
if (is_absolute_pathname(name) || !cp_getvar("sourcepath", CP_LIST, &v, 0))
|
|
return NULL;
|
|
|
|
for (; v; v = v->va_next) {
|
|
|
|
switch (v->va_type) {
|
|
case CP_STRING:
|
|
cp_wstrip(v->va_string);
|
|
(void) sprintf(buf, "%s%s%s", v->va_string, DIR_PATHSEP, name);
|
|
break;
|
|
case CP_NUM:
|
|
(void) sprintf(buf, "%d%s%s", v->va_num, DIR_PATHSEP, name);
|
|
break;
|
|
case CP_REAL: /* This is foolish */
|
|
(void) sprintf(buf, "%g%s%s", v->va_real, DIR_PATHSEP, name);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "ERROR: enumeration value `CP_BOOL' or `CP_LIST' not handled in inp_pathresolve\nAborting...\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
if (stat(buf, &st) == 0)
|
|
return copy(buf);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
static char *
|
|
inp_pathresolve_at(char *name, char *dir)
|
|
{
|
|
char buf[BSIZE_SP], *end;
|
|
|
|
/* if name is an absolute path name,
|
|
* or if we haven't anything to prepend anyway
|
|
*/
|
|
|
|
if (is_absolute_pathname(name) || !dir || !dir[0])
|
|
return inp_pathresolve(name);
|
|
|
|
if (name[0] == '~' && name[1] == '/') {
|
|
char *y = cp_tildexpand(name);
|
|
if (y) {
|
|
char *r = inp_pathresolve(y);
|
|
tfree(y);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Try in current dir and then in the actual dir the file was read.
|
|
* Current dir . is needed to correctly support absolute paths in sourcepath
|
|
*/
|
|
|
|
strcpy(buf, ".");
|
|
|
|
end = strchr(buf, '\0');
|
|
if (end[-1] != DIR_TERM)
|
|
*end++ = DIR_TERM;
|
|
|
|
strcpy(end, name);
|
|
|
|
char *r = inp_pathresolve(buf);
|
|
if (r)
|
|
return r;
|
|
|
|
strcpy(buf, dir);
|
|
|
|
end = strchr(buf, '\0');
|
|
if (end[-1] != DIR_TERM)
|
|
*end++ = DIR_TERM;
|
|
|
|
strcpy(end, name);
|
|
|
|
return inp_pathresolve(buf);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' *
|
|
* and returns a pointer to the resulting null terminated string. *
|
|
* The '\n' if found, is included in the returned string. *
|
|
* From: jason@ucbopal.BERKELEY.EDU (Jason Venner) *
|
|
* Newsgroups: net.sources *
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
#define STRGROW 256
|
|
|
|
static char *
|
|
readline(FILE *fd)
|
|
{
|
|
int c;
|
|
int memlen;
|
|
char *strptr;
|
|
int strlen;
|
|
|
|
strlen = 0;
|
|
memlen = STRGROW;
|
|
strptr = TMALLOC(char, memlen);
|
|
memlen -= 1; /* Save constant -1's in while loop */
|
|
|
|
while ((c = getc(fd)) != EOF) {
|
|
|
|
if (strlen == 0 && (c == '\t' || c == ' ')) /* Leading spaces away */
|
|
continue;
|
|
|
|
strptr[strlen++] = (char) c;
|
|
|
|
if (strlen >= memlen) {
|
|
memlen += STRGROW;
|
|
if ((strptr = TREALLOC(char, strptr, memlen + 1)) == NULL)
|
|
return (NULL);
|
|
}
|
|
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
|
|
if (!strlen) {
|
|
tfree(strptr);
|
|
return (NULL);
|
|
}
|
|
|
|
// strptr[strlen] = '\0';
|
|
/* Trim the string */
|
|
strptr = TREALLOC(char, strptr, strlen + 1);
|
|
strptr[strlen] = '\0';
|
|
|
|
return (strptr);
|
|
}
|
|
|
|
|
|
/* Replace "gnd" by " 0 "
|
|
Delimiters of gnd may be ' ' or ',' or '(' or ')',
|
|
may be disabled by setting variable no_auto_gnd */
|
|
|
|
static void
|
|
inp_fix_gnd_name(struct card *c)
|
|
{
|
|
for (; c; c = c->nextcard) {
|
|
|
|
char *gnd = c->line;
|
|
|
|
// if there is a comment or no gnd, go to next line
|
|
if ((*gnd == '*') || !strstr(gnd, "gnd"))
|
|
continue;
|
|
|
|
// replace "?gnd?" by "? 0 ?", ? being a ' ' ',' '(' ')'.
|
|
while ((gnd = strstr(gnd, "gnd")) != NULL) {
|
|
if ((isspace_c(gnd[-1]) || gnd[-1] == '(' || gnd[-1] == ',') &&
|
|
(isspace_c(gnd[3]) || gnd[3] == ')' || gnd[3] == ',')) {
|
|
memcpy(gnd, " 0 ", 3);
|
|
}
|
|
gnd += 3;
|
|
}
|
|
|
|
// now remove the extra white spaces around 0
|
|
c->line = inp_remove_ws(c->line);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* transform a VCVS "gate" instance into a XSPICE instance
|
|
*
|
|
* Exx out+ out- <VCVS> {nand|nor|and|or}(n)
|
|
* + in[1]+ in[1]- ... in[n]+ in[n]-
|
|
* + x1,y1 x2,y2
|
|
* ==>
|
|
* Axx %vd[ in[1]+ in[1]- ... in[n]+ in[n]- ]
|
|
* + %vd( out+ out- ) Exx
|
|
* .model Exx multi_input_pwd ( x = [x1 x2] x = [y1 y2] model = {nand|nor|and|or} )
|
|
*
|
|
* fixme,
|
|
* `n' is not checked
|
|
* the x,y list is fixed to length 2
|
|
*/
|
|
|
|
static void
|
|
inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
|
|
{
|
|
int skip_control = 0;
|
|
|
|
for (; c; c = c->nextcard) {
|
|
|
|
char *line = c->line;
|
|
|
|
/* there is no e source inside .control ... .endc */
|
|
if (ciprefix(".control", line)) {
|
|
skip_control ++;
|
|
continue;
|
|
} else if (ciprefix(".endc", line)) {
|
|
skip_control --;
|
|
continue;
|
|
} else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
|
|
if (*line == 'e') {
|
|
|
|
char *fcn_b;
|
|
|
|
if (((fcn_b = strstr(line, "nand(")) != NULL ||
|
|
(fcn_b = strstr(line, "and(")) != NULL ||
|
|
(fcn_b = strstr(line, "nor(")) != NULL ||
|
|
(fcn_b = strstr(line, "or(")) != NULL) &&
|
|
isspace_c(fcn_b[-1]))
|
|
{
|
|
char keep, *comma_ptr, *xy_values1[5], *xy_values2[5];
|
|
char *out_str, *ctrl_nodes_str, *xy_values1_b = NULL, *ref_str, *fcn_name, *fcn_e = NULL, *out_b, *out_e, *ref_e;
|
|
char *m_instance, *m_model;
|
|
char *xy_values2_b = NULL, *xy_values1_e = NULL, *ctrl_nodes_b = NULL, *ctrl_nodes_e = NULL;
|
|
int xy_count1, xy_count2;
|
|
bool ok = FALSE;
|
|
|
|
#ifndef XSPICE
|
|
fprintf(stderr,
|
|
"\n"
|
|
"Error: XSPICE is required to run the 'multi-input pwl' option in line %d\n"
|
|
" %s\n"
|
|
"\n"
|
|
"See manual chapt. 31 for installation instructions\n",
|
|
*line_number,
|
|
line
|
|
);
|
|
controlled_exit(EXIT_BAD);
|
|
#endif
|
|
|
|
do {
|
|
ref_e = skip_non_ws(line);
|
|
|
|
out_b = skip_ws(ref_e);
|
|
|
|
out_e = skip_back_ws(fcn_b, out_b);
|
|
if (out_e <= out_b)
|
|
break;
|
|
|
|
fcn_e = strchr(fcn_b, '(');
|
|
|
|
ctrl_nodes_b = strchr(fcn_e, ')');
|
|
if (!ctrl_nodes_b)
|
|
break;
|
|
ctrl_nodes_b = skip_ws(ctrl_nodes_b + 1);
|
|
|
|
comma_ptr = strchr(ctrl_nodes_b, ',');
|
|
if (!comma_ptr)
|
|
break;
|
|
|
|
xy_values1_b = skip_back_ws(comma_ptr, ctrl_nodes_b);
|
|
if (xy_values1_b[-1] == '}') {
|
|
while (--xy_values1_b >= ctrl_nodes_b)
|
|
if (*xy_values1_b == '{')
|
|
break;
|
|
} else {
|
|
xy_values1_b = skip_back_non_ws(xy_values1_b, ctrl_nodes_b);
|
|
}
|
|
if (xy_values1_b <= ctrl_nodes_b)
|
|
break;
|
|
|
|
ctrl_nodes_e = skip_back_ws(xy_values1_b, ctrl_nodes_b);
|
|
if (ctrl_nodes_e <= ctrl_nodes_b)
|
|
break;
|
|
|
|
xy_values1_e = skip_ws(comma_ptr + 1);
|
|
if (*xy_values1_e == '{') {
|
|
xy_values1_e = inp_spawn_brace(xy_values1_e);
|
|
} else {
|
|
xy_values1_e = skip_non_ws(xy_values1_e);
|
|
}
|
|
if (!xy_values1_e)
|
|
break;
|
|
|
|
xy_values2_b = skip_ws(xy_values1_e);
|
|
|
|
ok = TRUE;
|
|
} while(0);
|
|
|
|
if (!ok) {
|
|
fprintf(stderr, "ERROR: malformed line: %s\n", line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ref_str = copy_substring(line, ref_e);
|
|
out_str = copy_substring(out_b, out_e);
|
|
fcn_name = copy_substring(fcn_b, fcn_e);
|
|
ctrl_nodes_str = copy_substring(ctrl_nodes_b, ctrl_nodes_e);
|
|
|
|
keep = *xy_values1_e;
|
|
*xy_values1_e = '\0';
|
|
xy_count1 = get_comma_separated_values(xy_values1, xy_values1_b);
|
|
*xy_values1_e = keep;
|
|
|
|
xy_count2 = get_comma_separated_values(xy_values2, xy_values2_b);
|
|
|
|
// place restrictions on only having 2 point values; this can change later
|
|
if (xy_count1 != 2 && xy_count2 != 2)
|
|
fprintf(stderr, "ERROR: only expecting 2 pair values for multi-input vcvs!\n");
|
|
|
|
m_instance = tprintf("%s %%vd[ %s ] %%vd( %s ) %s",
|
|
ref_str, ctrl_nodes_str, out_str, ref_str);
|
|
m_instance[0] = 'a';
|
|
|
|
m_model = tprintf(".model %s multi_input_pwl ( x = [%s %s] y = [%s %s] model = \"%s\" )",
|
|
ref_str, xy_values1[0], xy_values2[0],
|
|
xy_values1[1], xy_values2[1], fcn_name);
|
|
|
|
tfree(ref_str);
|
|
tfree(out_str);
|
|
tfree(fcn_name);
|
|
tfree(ctrl_nodes_str);
|
|
tfree(xy_values1[0]);
|
|
tfree(xy_values1[1]);
|
|
tfree(xy_values2[0]);
|
|
tfree(xy_values2[1]);
|
|
|
|
*c->line = '*';
|
|
c = insert_new_line(c, m_instance, (*line_number)++, 0);
|
|
c = insert_new_line(c, m_model, (*line_number)++, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* If ngspice is started with option -a, then variable 'autorun'
|
|
* will be set and the following function scans the deck.
|
|
* If 'run' is not found, a .control section will be added:
|
|
* .control
|
|
* run
|
|
* op ; if .op is found
|
|
* write rawfile ; if rawfile given
|
|
* .endc
|
|
*/
|
|
static void
|
|
inp_add_control_section(struct card *deck, int *line_number)
|
|
{
|
|
struct card *c, *prev_card = NULL;
|
|
bool found_control = FALSE, found_run = FALSE;
|
|
bool found_end = FALSE;
|
|
char *op_line = NULL, rawfile[1000], *line;
|
|
|
|
for (c = deck; c; c = c->nextcard) {
|
|
|
|
if (*c->line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".op ", c->line)) {
|
|
*c->line = '*';
|
|
op_line = c->line + 1;
|
|
}
|
|
|
|
if (ciprefix(".end", c->line))
|
|
found_end = TRUE;
|
|
|
|
if (found_control && ciprefix("run", c->line))
|
|
found_run = TRUE;
|
|
|
|
if (ciprefix(".control", c->line))
|
|
found_control = TRUE;
|
|
|
|
if (ciprefix(".endc", c->line)) {
|
|
found_control = FALSE;
|
|
|
|
if (!found_run) {
|
|
prev_card = insert_new_line(prev_card, copy("run"), (*line_number)++, 0);
|
|
found_run = TRUE;
|
|
}
|
|
|
|
if (cp_getvar("rawfile", CP_STRING, rawfile, sizeof(rawfile))) {
|
|
line = tprintf("write %s", rawfile);
|
|
prev_card = insert_new_line(prev_card, line, (*line_number)++, 0);
|
|
}
|
|
}
|
|
|
|
prev_card = c;
|
|
}
|
|
|
|
// check if need to add control section
|
|
if (!found_run && found_end) {
|
|
|
|
deck = insert_new_line(deck, copy(".control"), (*line_number)++, 0);
|
|
|
|
deck = insert_new_line(deck, copy("run"), (*line_number)++, 0);
|
|
|
|
if (op_line)
|
|
deck = insert_new_line(deck, copy(op_line), (*line_number)++, 0);
|
|
|
|
if (cp_getvar("rawfile", CP_STRING, rawfile, sizeof(rawfile))) {
|
|
line = tprintf("write %s", rawfile);
|
|
deck = insert_new_line(deck, line, (*line_number)++, 0);
|
|
}
|
|
|
|
deck = insert_new_line(deck, copy(".endc"), (*line_number)++, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/* overwrite shell-style end-of-line continuation '\\' with spaces,
|
|
* and return TRUE when found */
|
|
static bool
|
|
chk_for_line_continuation(char *line)
|
|
{
|
|
if (*line != '*' && *line != '$') {
|
|
|
|
char *ptr = skip_back_ws(strchr(line, '\0'), line);
|
|
|
|
if ((ptr - 2 >= line) && (ptr[-1] == '\\') && (ptr[-2] == '\\')) {
|
|
ptr[-1] = ' ';
|
|
ptr[-2] = ' ';
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// change .macro --> .subckt
|
|
// .eom --> .ends
|
|
// .subckt name 1 2 3 params: w=9u l=180n --> .subckt name 1 2 3 w=9u l=180n
|
|
// .subckt name (1 2 3) --> .subckt name 1 2 3
|
|
// x1 (1 2 3) --> x1 1 2 3
|
|
// .param func1(x,y) = {x*y} --> .func func1(x,y) {x*y}
|
|
|
|
static void
|
|
inp_fix_macro_param_func_paren_io(struct card *card)
|
|
{
|
|
char *str_ptr, *new_str;
|
|
|
|
for (; card; card = card->nextcard) {
|
|
|
|
if (*card->line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".macro", card->line) || ciprefix(".eom", card->line)) {
|
|
str_ptr = skip_non_ws(card->line);
|
|
|
|
if (ciprefix(".macro", card->line)) {
|
|
new_str = tprintf(".subckt%s", str_ptr);
|
|
} else {
|
|
new_str = tprintf(".ends%s", str_ptr);
|
|
}
|
|
|
|
tfree(card->line);
|
|
card->line = new_str;
|
|
}
|
|
|
|
if (ciprefix(".subckt", card->line) || ciprefix("x", card->line)) {
|
|
/* remove () */
|
|
str_ptr = skip_non_ws(card->line); // skip over .subckt, instance name
|
|
str_ptr = skip_ws(str_ptr);
|
|
if (ciprefix(".subckt", card->line)) {
|
|
str_ptr = skip_non_ws(str_ptr); // skip over subckt name
|
|
str_ptr = skip_ws(str_ptr);
|
|
}
|
|
if (*str_ptr == '(') {
|
|
*str_ptr = ' ';
|
|
while (*str_ptr != '\0') {
|
|
if (*str_ptr == ')') {
|
|
*str_ptr = ' ';
|
|
break;
|
|
}
|
|
str_ptr++;
|
|
}
|
|
card->line = inp_remove_ws(card->line); /* remove the extra white spaces just introduced */
|
|
}
|
|
}
|
|
|
|
if (ciprefix(".para", card->line)) {
|
|
bool is_func = FALSE;
|
|
str_ptr = skip_non_ws(card->line); // skip over .param
|
|
str_ptr = skip_ws(str_ptr);
|
|
while (!isspace_c(*str_ptr) && *str_ptr != '=') {
|
|
if (*str_ptr == '(')
|
|
is_func = TRUE;
|
|
str_ptr++;
|
|
}
|
|
|
|
if (is_func) {
|
|
str_ptr = strchr(card->line, '=');
|
|
if (str_ptr)
|
|
*str_ptr = ' ';
|
|
str_ptr = card->line + 1;
|
|
str_ptr[0] = 'f';
|
|
str_ptr[1] = 'u';
|
|
str_ptr[2] = 'n';
|
|
str_ptr[3] = 'c';
|
|
str_ptr[4] = ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static char *
|
|
get_instance_subckt(char *line)
|
|
{
|
|
char *end_ptr, *inst_name_ptr;
|
|
char *equal_ptr = strchr(line, '=');
|
|
|
|
// see if instance has parameters
|
|
if (equal_ptr) {
|
|
end_ptr = skip_back_ws(equal_ptr, line);
|
|
end_ptr = skip_back_non_ws(end_ptr, line);
|
|
} else {
|
|
end_ptr = strchr(line, '\0');
|
|
}
|
|
|
|
end_ptr = skip_back_ws(end_ptr, line);
|
|
|
|
inst_name_ptr = skip_back_non_ws(end_ptr, line);
|
|
|
|
return copy_substring(inst_name_ptr, end_ptr);
|
|
}
|
|
|
|
|
|
static char*
|
|
get_subckt_model_name(char *line)
|
|
{
|
|
char *name, *end_ptr;
|
|
|
|
name = skip_non_ws(line); // eat .subckt|.model
|
|
name = skip_ws(name);
|
|
|
|
end_ptr = skip_non_ws(name);
|
|
|
|
return copy_substring(name, end_ptr);
|
|
}
|
|
|
|
|
|
static char*
|
|
get_model_name(char *line, int num_terminals)
|
|
{
|
|
char *beg_ptr, *end_ptr;
|
|
int i = 0;
|
|
|
|
beg_ptr = skip_non_ws(line); /* eat device name */
|
|
beg_ptr = skip_ws(beg_ptr);
|
|
|
|
for (i = 0; i < num_terminals; i++) { /* skip the terminals */
|
|
beg_ptr = skip_non_ws(beg_ptr);
|
|
beg_ptr = skip_ws(beg_ptr);
|
|
}
|
|
|
|
if (*line == 'r') /* special dealing for r models */
|
|
if ((*beg_ptr == '+') || (*beg_ptr == '-') || isdigit_c(*beg_ptr)) { /* looking for a value before model */
|
|
beg_ptr = skip_non_ws(beg_ptr); /* skip the value */
|
|
beg_ptr = skip_ws(beg_ptr);
|
|
}
|
|
|
|
end_ptr = skip_non_ws(beg_ptr);
|
|
|
|
return copy_substring(beg_ptr, end_ptr);
|
|
}
|
|
|
|
|
|
static char*
|
|
get_model_type(char *line)
|
|
{
|
|
char *beg_ptr;
|
|
|
|
if (!ciprefix(".model", line))
|
|
return NULL;
|
|
|
|
beg_ptr = skip_non_ws(line); /* eat .model */
|
|
beg_ptr = skip_ws(beg_ptr);
|
|
|
|
beg_ptr = skip_non_ws(beg_ptr); /* eat model name */
|
|
beg_ptr = skip_ws(beg_ptr);
|
|
|
|
return gettok_noparens(&beg_ptr);
|
|
}
|
|
|
|
|
|
static char *
|
|
get_adevice_model_name(char *line)
|
|
{
|
|
char *ptr_end, *ptr_beg;
|
|
|
|
ptr_end = skip_back_ws(strchr(line, '\0'), line);
|
|
ptr_beg = skip_back_non_ws(ptr_end, line);
|
|
|
|
return copy_substring(ptr_beg, ptr_end);
|
|
}
|
|
|
|
|
|
/*
|
|
* To distinguish modelname tokens from other tokens
|
|
* by checking if token is not a valid ngspice number
|
|
*/
|
|
static int
|
|
is_a_modelname(const char *s)
|
|
{
|
|
char *st;
|
|
double testval;
|
|
/* first character of model name is character from alphabet */
|
|
if (isalpha_c(s[0]))
|
|
return TRUE;
|
|
/* first characters not allowed in model name (including '\0')*/
|
|
if (strchr("{*^@\\\'", s[0]))
|
|
return FALSE;
|
|
/* not beeing a valid number */
|
|
testval = strtod(s, &st);
|
|
/* conversion failed, so no number */
|
|
if (eq(s, st))
|
|
return TRUE;
|
|
else
|
|
/* test if we have a true number */
|
|
if (*st == '\0' || isspace(*st))
|
|
return FALSE;
|
|
else {
|
|
/* look for the scale factor (alphabetic) and skip it.
|
|
* INPevaluate will not do it because is does not swallow
|
|
* the scale factor from the string.
|
|
*/
|
|
switch (*st) {
|
|
case 't':
|
|
case 'T':
|
|
case 'g':
|
|
case 'G':
|
|
case 'k':
|
|
case 'K':
|
|
case 'u':
|
|
case 'U':
|
|
case 'n':
|
|
case 'N':
|
|
case 'p':
|
|
case 'P':
|
|
case 'f':
|
|
case 'F':
|
|
st = st + 1;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
if (((st[1] == 'E') || (st[1] == 'e')) &&
|
|
((st[2] == 'G') || (st[2] == 'g')))
|
|
{
|
|
st = st + 3; /* Meg */
|
|
}
|
|
else if (((st[1] == 'I') || (st[1] == 'i')) &&
|
|
((st[2] == 'L') || (st[2] == 'l')))
|
|
{
|
|
st = st + 3; /* Mil */
|
|
}
|
|
else {
|
|
st = st + 1; /* m, milli */
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
/* test if we have a true scale factor */
|
|
if (*st == '\0' || isspace(*st))
|
|
return FALSE;
|
|
|
|
/* test if people use Ohms, F, H for RLC, like pF or uOhms */
|
|
if (ciprefix("ohms", st))
|
|
st = st + 4;
|
|
else if (ciprefix("farad", st))
|
|
st = st + 5;
|
|
else if (ciprefix("henry", st))
|
|
st = st + 5;
|
|
else if ((*st == 'f') || (*st == 'h'))
|
|
st = st + 1;
|
|
|
|
if (*st == '\0' || isspace(*st))
|
|
return FALSE;
|
|
}
|
|
|
|
/* token starts with non alphanum character */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
struct nlist {
|
|
char **names;
|
|
int num_names;
|
|
int size;
|
|
};
|
|
|
|
|
|
static const char *
|
|
nlist_find(const struct nlist *nlist, const char *name)
|
|
{
|
|
int i;
|
|
for (i = 0; i < nlist->num_names; i++)
|
|
if (strcmp(nlist->names[i], name) == 0)
|
|
return nlist->names[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static const char *
|
|
nlist_model_find(const struct nlist *nlist, const char *name)
|
|
{
|
|
int i;
|
|
for (i = 0; i < nlist->num_names; i++)
|
|
if (model_name_match(nlist->names[i], name))
|
|
return nlist->names[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
nlist_adjoin(struct nlist *nlist, char *name)
|
|
{
|
|
if (nlist_find(nlist, name)) {
|
|
tfree(name);
|
|
return;
|
|
}
|
|
|
|
if (nlist->num_names >= nlist->size)
|
|
nlist->names = TREALLOC(char *, nlist->names, nlist->size *= 2);
|
|
|
|
nlist->names[nlist->num_names++] = name;
|
|
}
|
|
|
|
|
|
static struct nlist *
|
|
nlist_allocate(int size)
|
|
{
|
|
struct nlist *t = TMALLOC(struct nlist, 1);
|
|
|
|
t->names = TMALLOC(char *, size);
|
|
t->size = size;
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
static void
|
|
nlist_destroy(struct nlist *nlist)
|
|
{
|
|
int i;
|
|
for (i = 0; i < nlist->num_names; i++)
|
|
tfree(nlist->names[i]);
|
|
|
|
tfree(nlist->names);
|
|
tfree(nlist);
|
|
}
|
|
|
|
|
|
static void
|
|
get_subckts_for_subckt(struct card *start_card, char *subckt_name,
|
|
struct nlist *used_subckts, struct nlist *used_models,
|
|
bool has_models)
|
|
{
|
|
struct card *card;
|
|
int first_new_subckt = used_subckts->num_names;
|
|
|
|
bool found_subckt = FALSE;
|
|
int i, fence;
|
|
|
|
for (card = start_card; card; card = card->nextcard) {
|
|
|
|
char *line = card->line;
|
|
|
|
/* no models embedded in these lines */
|
|
if (strchr("*vibefghkt", *line))
|
|
continue;
|
|
|
|
if ((ciprefix(".ends", line) || ciprefix(".eom", line)) && found_subckt)
|
|
break;
|
|
|
|
if (ciprefix(".subckt", line) || ciprefix(".macro", line)) {
|
|
char *curr_subckt_name = get_subckt_model_name(line);
|
|
|
|
if (strcmp(curr_subckt_name, subckt_name) == 0)
|
|
found_subckt = TRUE;
|
|
|
|
tfree(curr_subckt_name);
|
|
}
|
|
|
|
if (found_subckt) {
|
|
if (*line == 'x') {
|
|
char *inst_subckt_name = get_instance_subckt(line);
|
|
nlist_adjoin(used_subckts, inst_subckt_name);
|
|
} else if (*line == 'a') {
|
|
char *model_name = get_adevice_model_name(line);
|
|
nlist_adjoin(used_models, model_name);
|
|
} else if (has_models) {
|
|
int num_terminals = get_number_terminals(line);
|
|
if (num_terminals != 0) {
|
|
char *model_name = get_model_name(line, num_terminals);
|
|
if (is_a_modelname(model_name))
|
|
nlist_adjoin(used_models, model_name);
|
|
else
|
|
tfree(model_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now make recursive call on instances just found above
|
|
fence = used_subckts->num_names;
|
|
for (i = first_new_subckt; i < fence; i++)
|
|
get_subckts_for_subckt(start_card, used_subckts->names[i],
|
|
used_subckts, used_models, has_models);
|
|
}
|
|
|
|
|
|
/*
|
|
iterate through the deck and comment out unused subckts, models
|
|
(don't want to waste time processing everything)
|
|
also comment out .param lines with no parameters defined
|
|
*/
|
|
|
|
static void
|
|
comment_out_unused_subckt_models(struct card *start_card)
|
|
{
|
|
struct card *card;
|
|
struct nlist *used_subckts, *used_models;
|
|
int i = 0, fence;
|
|
bool processing_subckt = FALSE, remove_subckt = FALSE, has_models = FALSE;
|
|
int skip_control = 0, nested_subckt = 0;
|
|
|
|
used_subckts = nlist_allocate(100);
|
|
used_models = nlist_allocate(100);
|
|
|
|
for (card = start_card; card; card = card->nextcard) {
|
|
if (ciprefix(".model", card->line))
|
|
has_models = TRUE;
|
|
if (ciprefix(".cmodel", card->line))
|
|
has_models = TRUE;
|
|
if (ciprefix(".para", card->line) && !strchr(card->line, '='))
|
|
*card->line = '*';
|
|
}
|
|
|
|
for (card = start_card; card; card = card->nextcard) {
|
|
|
|
char *line = card->line;
|
|
|
|
/* no models embedded in these lines */
|
|
if (strchr("*vibefghkt", *line))
|
|
continue;
|
|
|
|
/* there is no .subckt, .model or .param inside .control ... .endc */
|
|
if (ciprefix(".control", line)) {
|
|
skip_control ++;
|
|
continue;
|
|
} else if (ciprefix(".endc", line)) {
|
|
skip_control --;
|
|
continue;
|
|
} else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".subckt", line) || ciprefix(".macro", line))
|
|
processing_subckt = TRUE;
|
|
if (ciprefix(".ends", line) || ciprefix(".eom", line))
|
|
processing_subckt = FALSE;
|
|
|
|
if (!processing_subckt) {
|
|
if (*line == 'x') {
|
|
char *subckt_name = get_instance_subckt(line);
|
|
nlist_adjoin(used_subckts, subckt_name);
|
|
} else if (*line == 'a') {
|
|
char *model_name = get_adevice_model_name(line);
|
|
nlist_adjoin(used_models, model_name);
|
|
} else if (has_models) {
|
|
/* This is a preliminary version, until we have found a reliable
|
|
method to detect the model name out of the input line (Many
|
|
options have to be taken into account.). */
|
|
int num_terminals = get_number_terminals(line);
|
|
if (num_terminals != 0) {
|
|
char *model_name = get_model_name(line, num_terminals);
|
|
if (is_a_modelname(model_name))
|
|
nlist_adjoin(used_models, model_name);
|
|
else
|
|
tfree(model_name);
|
|
}
|
|
} /* if (has_models) */
|
|
} /* if (!processing_subckt) */
|
|
} /* for loop through all cards */
|
|
|
|
fence = used_subckts->num_names;
|
|
for (i = 0; i < fence; i++)
|
|
get_subckts_for_subckt(start_card, used_subckts->names[i],
|
|
used_subckts, used_models, has_models);
|
|
|
|
/* comment out any unused subckts, currently only at top level */
|
|
for (card = start_card; card; card = card->nextcard) {
|
|
|
|
char *line = card->line;
|
|
|
|
if (*line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".subckt", line) || ciprefix(".macro", line)) {
|
|
char *subckt_name = get_subckt_model_name(line);
|
|
/* check if unused, only at top level */
|
|
if (nested_subckt++ == 0)
|
|
remove_subckt = !nlist_find(used_subckts, subckt_name);
|
|
tfree(subckt_name);
|
|
}
|
|
|
|
if (ciprefix(".ends", line) || ciprefix(".eom", line)) {
|
|
if (remove_subckt)
|
|
*line = '*';
|
|
if (--nested_subckt == 0)
|
|
remove_subckt = FALSE;
|
|
}
|
|
|
|
if (remove_subckt)
|
|
*line = '*';
|
|
else if (has_models &&
|
|
(ciprefix(".model", line) || ciprefix(".cmodel", line)))
|
|
{
|
|
char *model_type = get_model_type(line);
|
|
char *model_name = get_subckt_model_name(line);
|
|
|
|
/* keep R, L, C models because in addition to no. of terminals the value may be given,
|
|
as in RE1 1 2 800 newres dtemp=5, so model name may be token no. 4 or 5,
|
|
and, if 5, will not be detected by get_subckt_model_name()*/
|
|
if (!cieq(model_type, "c") &&
|
|
!cieq(model_type, "l") &&
|
|
!cieq(model_type, "r") &&
|
|
!nlist_model_find(used_models, model_name))
|
|
{
|
|
*line = '*';
|
|
}
|
|
|
|
tfree(model_type);
|
|
tfree(model_name);
|
|
}
|
|
}
|
|
|
|
nlist_destroy(used_subckts);
|
|
nlist_destroy(used_models);
|
|
}
|
|
|
|
|
|
#if 0
|
|
// find closing paren
|
|
static char *
|
|
inp_search_closing_paren(char *s)
|
|
{
|
|
int count = 0;
|
|
// assert(*s == '(')
|
|
while (*s) {
|
|
if (*s == '(')
|
|
count++;
|
|
if (*s == ')')
|
|
count--;
|
|
if (count == 0)
|
|
return s + 1;
|
|
s++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if 0
|
|
/* search backwards for opening paren */
|
|
static char *
|
|
inp_search_opening_paren(char *s, char *start)
|
|
{
|
|
int count = 0;
|
|
// assert(*s == ')')
|
|
while (s >= start) {
|
|
if (*s == '(')
|
|
count--;
|
|
if (*s == ')')
|
|
count++;
|
|
if (count == 0)
|
|
return s;
|
|
s--;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* search forward for closing brace */
|
|
static char *
|
|
inp_spawn_brace(char *s)
|
|
{
|
|
int count = 0;
|
|
// assert(*s == '{')
|
|
while (*s) {
|
|
if (*s == '{')
|
|
count++;
|
|
if (*s == '}')
|
|
count--;
|
|
if (count == 0)
|
|
return s + 1;
|
|
s++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
removes " " quotes, returns lower case letters,
|
|
replaces non-printable characterss with '_' *
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
void
|
|
inp_casefix(char *string)
|
|
{
|
|
#ifdef HAVE_CTYPE_H
|
|
if (string)
|
|
while (*string) {
|
|
#ifdef HAS_ASCII
|
|
/* ((*string) & 0177): mask off all but the first seven bits, 0177: octal */
|
|
*string = (char) strip(*string);
|
|
#endif
|
|
if (*string == '"') {
|
|
*string++ = ' ';
|
|
while (*string && *string != '"')
|
|
string++;
|
|
if (*string == '\0')
|
|
continue; /* needed if string is "something ! */
|
|
if (*string == '"')
|
|
*string = ' ';
|
|
}
|
|
if (!isspace_c(*string) && !isprint_c(*string))
|
|
*string = '_';
|
|
if (isupper_c(*string))
|
|
*string = tolower_c(*string);
|
|
string++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Strip all end-of-line comments from a deck
|
|
For cf == TRUE (script files, command files like spinit, .spiceinit)
|
|
and for .control sections only '$ ' is accepted as end-of-line comment,
|
|
to avoid conflict with $variable definition, otherwise we accept '$'. */
|
|
static void
|
|
inp_stripcomments_deck(struct card *c, bool cf)
|
|
{
|
|
bool found_control = FALSE;
|
|
for (; c; c = c->nextcard) {
|
|
/* exclude lines between .control and .endc from removing white spaces */
|
|
if (ciprefix(".control", c->line))
|
|
found_control = TRUE;
|
|
if (ciprefix(".endc", c->line))
|
|
found_control = FALSE;
|
|
inp_stripcomments_line(c->line, found_control|cf);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Support for end-of-line comments that begin with any of the following:
|
|
* ';'
|
|
* '$' (only outside of a .control section)
|
|
* '$ '
|
|
* '//' (like in c++ and as per the numparam code)
|
|
* Any following text to the end of the line is ignored.
|
|
* Note requirement for $ to be followed by a space, if we are inside of a
|
|
* .control section or in a command file. This is to avoid conflict
|
|
* with use of $ in front of a variable.
|
|
* Comments on a continuation line (i.e. line begining with '+') are allowed
|
|
* and are removed before lines are stitched.
|
|
* Lines that contain only an end-of-line comment with or without leading white
|
|
* space are also allowed.
|
|
|
|
If there is only white space before the end-of-line comment the
|
|
the whole line is converted to a normal comment line (i.e. one that
|
|
begins with a '*').
|
|
BUG: comment characters in side of string literals are not ignored
|
|
('$' outside of .control section is o.k. however).
|
|
|
|
If the comaptibility mode is PS, LTPS or LTPSA, '$' is treated as a valid
|
|
character, not a s end-of-line comment delimiter, except for that it is
|
|
located at the beginning of a line. If inside of a control section,
|
|
still '$ ' is read a an end-of-line comment delimiter.*/
|
|
static void
|
|
inp_stripcomments_line(char *s, bool cs)
|
|
{
|
|
char c = ' '; /* anything other than a comment character */
|
|
char *d = s;
|
|
if (*s == '\0')
|
|
return; /* empty line */
|
|
if (*s == '*')
|
|
return; /* line is already a comment */
|
|
/* look for comments */
|
|
while ((c = *d) != '\0') {
|
|
d++;
|
|
if (*d == ';') {
|
|
break;
|
|
}
|
|
/* outside of .control section, and not in PS mode */
|
|
else if (!cs && (c == '$') &&
|
|
inp_compat_mode != COMPATMODE_PS &&
|
|
inp_compat_mode != COMPATMODE_LTPS &&
|
|
inp_compat_mode != COMPATMODE_LTPSA) {
|
|
/* The character before '&' has to be ',' or ' ' or tab.
|
|
A valid numerical expression directly before '$' is not yet supported. */
|
|
if ((d - 2 >= s) && ((d[-2] == ' ') || (d[-2] == ',') || (d[-2] == '\t'))) {
|
|
d--;
|
|
break;
|
|
}
|
|
} else if (cs && (c == '$') && (*d == ' ')) {/* inside of .control section or command file */
|
|
d--; /* move d back to first comment character */
|
|
break;
|
|
} else if ((c == '/') && (*d == '/')) {
|
|
d--; /* move d back to first comment character */
|
|
break;
|
|
}
|
|
}
|
|
/* d now points to the first comment character or the null at the string end */
|
|
|
|
/* check for special case of comment at start of line */
|
|
if (d == s) {
|
|
*s = '*'; /* turn into normal comment */
|
|
return;
|
|
}
|
|
|
|
if (d > s) {
|
|
d--;
|
|
/* d now points to character just before comment */
|
|
|
|
/* eat white space at new end of line */
|
|
while (d >= s) {
|
|
if ((*d != ' ') && (*d != '\t'))
|
|
break;
|
|
d--;
|
|
}
|
|
d++;
|
|
/* d now points to the first white space character before the
|
|
end-of-line or end-of-line comment, or it points to the first
|
|
end-of-line comment character, or to the begining of the line */
|
|
}
|
|
|
|
/* Check for special case of comment at start of line
|
|
with or without preceeding white space */
|
|
if (d <= s) {
|
|
*s = '*'; /* turn the whole line into normal comment */
|
|
return;
|
|
}
|
|
|
|
*d = '\0'; /* terminate line in new location */
|
|
}
|
|
|
|
|
|
static void
|
|
inp_change_quotes(char *s)
|
|
{
|
|
bool first_quote = FALSE;
|
|
|
|
for (; *s; s++)
|
|
if (*s == '\'') {
|
|
if (first_quote == FALSE) {
|
|
*s = '{';
|
|
first_quote = TRUE;
|
|
} else {
|
|
*s = '}';
|
|
first_quote = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
add_name(struct names *p, char *name)
|
|
{
|
|
if (p->num_names >= N_SUBCKT_W_PARAMS) {
|
|
fprintf(stderr, "ERROR, N_SUBCKT_W_PARMS overflow\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
p->names[p->num_names++] = name;
|
|
}
|
|
|
|
|
|
static char **
|
|
find_name(struct names *p, char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < p->num_names; i++)
|
|
if (strcmp(p->names[i], name) == 0)
|
|
return & p->names[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static char*
|
|
inp_fix_subckt(struct names *subckt_w_params, char *s)
|
|
{
|
|
struct card *head, *first_param_card, *c;
|
|
char *equal, *beg, *buffer, *ptr1, *ptr2, *new_str;
|
|
|
|
equal = strchr(s, '=');
|
|
if (equal && !strstr(s, "params:")) {
|
|
/* get subckt name (ptr1 will point to name) */
|
|
ptr1 = skip_non_ws(s);
|
|
ptr1 = skip_ws(ptr1);
|
|
for (ptr2 = ptr1; *ptr2 && !isspace_c(*ptr2) && !isquote(*ptr2); ptr2++)
|
|
;
|
|
|
|
add_name(subckt_w_params, copy_substring(ptr1, ptr2));
|
|
|
|
/* go to beginning of first parameter word */
|
|
/* s will contain only subckt definition */
|
|
/* beg will point to start of param list */
|
|
beg = skip_back_ws(equal, s);
|
|
beg = skip_back_non_ws(beg, s);
|
|
beg[-1] = '\0'; /* fixme can be < s */
|
|
|
|
head = insert_new_line(NULL, NULL, 0, 0);
|
|
/* create list of parameters that need to get sorted */
|
|
first_param_card = c = NULL;
|
|
while ((ptr1 = strchr(beg, '=')) != NULL) {
|
|
ptr2 = skip_ws(ptr1 + 1);
|
|
ptr1 = skip_back_ws(ptr1, beg);
|
|
ptr1 = skip_back_non_ws(ptr1, beg);
|
|
/* ptr1 points to beginning of parameter */
|
|
|
|
if (*ptr2 == '{')
|
|
ptr2 = inp_spawn_brace(ptr2);
|
|
else
|
|
ptr2 = skip_non_ws(ptr2);
|
|
|
|
if (!ptr2) {
|
|
fprintf(stderr, "Error: Missing } in line %s\n", s);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
beg = ptr2;
|
|
|
|
c = insert_new_line(c, copy_substring(ptr1, ptr2), 0, 0);
|
|
|
|
if (!first_param_card)
|
|
first_param_card = c;
|
|
}
|
|
/* now sort parameters in order of dependencies */
|
|
inp_sort_params(first_param_card, head, NULL, NULL);
|
|
|
|
/* create new ordered parameter string for subckt call */
|
|
new_str = NULL;
|
|
for (c = head->nextcard; c; c = c->nextcard)
|
|
if (new_str == NULL) {
|
|
new_str = copy(c->line);
|
|
} else {
|
|
char *x = tprintf("%s %s", new_str, c->line);
|
|
tfree(new_str);
|
|
new_str = x;
|
|
}
|
|
|
|
line_free_x(head, TRUE);
|
|
|
|
/* create buffer and insert params: */
|
|
buffer = tprintf("%s params: %s", s, new_str);
|
|
|
|
tfree(s);
|
|
tfree(new_str);
|
|
|
|
s = buffer;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* this function shall:
|
|
* reduce sequences of whitespace to one space
|
|
* and to drop even that if it seems to be at a `safe' place to do so
|
|
* safe place means:
|
|
* before or behind a '='
|
|
* before or behind an operator within a {} expression
|
|
* whereby `operator' is classified by `is_arith_char()'
|
|
* fixme:
|
|
* thats odd and very naive business
|
|
*/
|
|
|
|
static char*
|
|
inp_remove_ws(char *s)
|
|
{
|
|
char *x = s;
|
|
char *d = s;
|
|
|
|
int brace_level = 0;
|
|
|
|
/* preserve at least one whitespace at beginning of line
|
|
* fixme,
|
|
* is this really necessary ?
|
|
* or is this an artefact of original inp_remove_ws() implementation ?
|
|
*/
|
|
if (isspace_c(*s))
|
|
*d++ = *s++;
|
|
|
|
while (*s != '\0') {
|
|
if (*s == '{')
|
|
brace_level++;
|
|
if (*s == '}')
|
|
brace_level--;
|
|
|
|
if (isspace_c(*s)) {
|
|
s = skip_ws(s);
|
|
if (!(*s == '\0' || *s == '=' || ((brace_level > 0) && (is_arith_char(*s) || *s == ','))))
|
|
*d++ = ' ';
|
|
continue;
|
|
}
|
|
|
|
if (*s == '=' || ((brace_level > 0) && (is_arith_char(*s) || *s == ','))) {
|
|
*d++ = *s++;
|
|
s = skip_ws(s);
|
|
continue;
|
|
}
|
|
|
|
*d++ = *s++;
|
|
}
|
|
|
|
*d = '\0';
|
|
|
|
if (d == s)
|
|
return x;
|
|
|
|
s = copy(x);
|
|
tfree(x);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
change quotes from '' to {}
|
|
.subckt name 1 2 3 params: l=1 w=2 --> .subckt name 1 2 3 l=1 w=2
|
|
x1 1 2 3 params: l=1 w=2 --> x1 1 2 3 l=1 w=2
|
|
modify .subckt lines by calling inp_fix_subckt()
|
|
No changes to lines in .control section !
|
|
*/
|
|
|
|
static void
|
|
inp_fix_for_numparam(struct names *subckt_w_params, struct card *c)
|
|
{
|
|
bool found_control = FALSE;
|
|
|
|
for (; c; c = c->nextcard) {
|
|
|
|
if (*(c->line) == '*' || ciprefix(".lib", c->line))
|
|
continue;
|
|
|
|
/* exclude lines between .control and .endc from getting quotes changed */
|
|
if (ciprefix(".control", c->line))
|
|
found_control = TRUE;
|
|
if (ciprefix(".endc", c->line))
|
|
found_control = FALSE;
|
|
|
|
if (found_control)
|
|
continue;
|
|
|
|
inp_change_quotes(c->line);
|
|
|
|
if ((inp_compat_mode == COMPATMODE_ALL) || (inp_compat_mode == COMPATMODE_PS) ||
|
|
(inp_compat_mode == COMPATMODE_PSA) || (inp_compat_mode == COMPATMODE_LTPS) ||
|
|
(inp_compat_mode == COMPATMODE_LTPSA))
|
|
if (ciprefix(".subckt", c->line) || ciprefix("x", c->line)) {
|
|
/* remove params: */
|
|
char *str_ptr = strstr(c->line, "params:");
|
|
if (str_ptr)
|
|
memcpy(str_ptr, " ", 7);
|
|
}
|
|
|
|
if (ciprefix(".subckt", c->line))
|
|
c->line = inp_fix_subckt(subckt_w_params, c->line);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
inp_remove_excess_ws(struct card *c)
|
|
{
|
|
bool found_control = FALSE;
|
|
|
|
for (; c; c = c->nextcard) {
|
|
|
|
if (*c->line == '*')
|
|
continue;
|
|
|
|
/* exclude echo lines between .control and .endc from removing white spaces */
|
|
if (ciprefix(".control", c->line))
|
|
found_control = TRUE;
|
|
if (ciprefix(".endc", c->line))
|
|
found_control = FALSE;
|
|
|
|
if (found_control && ciprefix("echo", c->line))
|
|
continue;
|
|
|
|
c->line = inp_remove_ws(c->line); /* freed in fcn */
|
|
}
|
|
}
|
|
|
|
|
|
static struct card *
|
|
expand_section_ref(struct card *c, char *dir_name)
|
|
{
|
|
char *line = c->line;
|
|
|
|
char *s, *s_e, *y;
|
|
|
|
s = skip_non_ws(line);
|
|
while (isspace_c(*s) || isquote(*s))
|
|
s++;
|
|
for (s_e = s; *s_e && !isspace_c(*s_e) && !isquote(*s_e); s_e++)
|
|
;
|
|
y = s_e;
|
|
while (isspace_c(*y) || isquote(*y))
|
|
y++;
|
|
|
|
if (*y) {
|
|
/* library section reference: `.lib <library-file> <section-name>' */
|
|
|
|
struct card *section_def;
|
|
char keep_char1, keep_char2;
|
|
char *y_e;
|
|
struct library *lib;
|
|
|
|
for (y_e = y; *y_e && !isspace_c(*y_e) && !isquote(*y_e); y_e++)
|
|
;
|
|
keep_char1 = *s_e;
|
|
keep_char2 = *y_e;
|
|
*s_e = '\0';
|
|
*y_e = '\0';
|
|
|
|
lib = read_a_lib(s, dir_name);
|
|
|
|
if (!lib) {
|
|
fprintf(stderr, "ERROR, library file %s not found\n", s);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
section_def = find_section_definition(lib->deck, y);
|
|
|
|
if (!section_def) {
|
|
fprintf(stderr, "ERROR, library file %s, section definition %s not found\n", s, y);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* recursively expand the refered section itself */
|
|
{
|
|
struct card *t = section_def;
|
|
for (; t; t = t->nextcard) {
|
|
if (ciprefix(".endl", t->line))
|
|
break;
|
|
if (ciprefix(".lib", t->line))
|
|
t = expand_section_ref(t, lib->habitat);
|
|
}
|
|
if (!t) {
|
|
fprintf(stderr, "ERROR, .endl not found\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* insert the library section definition into `c' */
|
|
{
|
|
struct card *t = section_def;
|
|
for (; t; t = t->nextcard) {
|
|
c = insert_new_line(c, copy(t->line), t->linenum, t->linenum_orig);
|
|
if (t == section_def) {
|
|
c->line[0] = '*';
|
|
c->line[1] = '<';
|
|
}
|
|
if(ciprefix(".endl", t->line)) {
|
|
c->line[0] = '*';
|
|
c->line[1] = '>';
|
|
break;
|
|
}
|
|
}
|
|
if (!t) {
|
|
fprintf(stderr, "ERROR, .endl not found\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
*line = '*'; /* comment out .lib line */
|
|
*s_e = keep_char1;
|
|
*y_e = keep_char2;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
/*
|
|
* recursively expand library section references,
|
|
* either
|
|
* every library section reference (when the given section_name_ === NULL)
|
|
* or
|
|
* just those references occuring in the given library section definition
|
|
*/
|
|
|
|
static void
|
|
expand_section_references(struct card *c, char *dir_name)
|
|
{
|
|
for (; c; c = c->nextcard)
|
|
if (ciprefix(".lib", c->line))
|
|
c = expand_section_ref(c, dir_name);
|
|
}
|
|
|
|
|
|
static char*
|
|
inp_get_subckt_name(char *s)
|
|
{
|
|
char *subckt_name, *end_ptr = strchr(s, '=');
|
|
|
|
if (end_ptr) {
|
|
end_ptr = skip_back_ws(end_ptr, s);
|
|
end_ptr = skip_back_non_ws(end_ptr, s);
|
|
} else {
|
|
end_ptr = strchr(s, '\0');
|
|
}
|
|
|
|
end_ptr = skip_back_ws(end_ptr, s);
|
|
subckt_name = skip_back_non_ws(end_ptr, s);
|
|
|
|
return copy_substring(subckt_name, end_ptr);
|
|
}
|
|
|
|
|
|
static int
|
|
inp_get_params(char *line, char *param_names[], char *param_values[])
|
|
{
|
|
char *equal_ptr;
|
|
char *end, *name, *value;
|
|
int num_params = 0;
|
|
char keep;
|
|
|
|
while ((equal_ptr = find_assignment(line)) != NULL) {
|
|
|
|
/* get parameter name */
|
|
end = skip_back_ws(equal_ptr, line);
|
|
name = skip_back_non_ws(end, line);
|
|
|
|
if (num_params == NPARAMS) {
|
|
fprintf(stderr, "Error: to many params in a line, max is %d\n", NPARAMS);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
param_names[num_params++] = copy_substring(name, end);
|
|
|
|
/* get parameter value */
|
|
value = skip_ws(equal_ptr + 1);
|
|
|
|
if (*value == '{')
|
|
end = inp_spawn_brace(value);
|
|
else
|
|
end = skip_non_ws(value);
|
|
|
|
if (!end) {
|
|
fprintf(stderr, "Error: Missing } in %s\n", line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
keep = *end;
|
|
*end = '\0';
|
|
|
|
if (*value == '{' ||
|
|
isdigit_c(*value) ||
|
|
(*value == '.' && isdigit_c(value[1])))
|
|
{
|
|
value = copy(value);
|
|
} else {
|
|
value = tprintf("{%s}", value);
|
|
}
|
|
|
|
param_values[num_params-1] = value;
|
|
*end = keep;
|
|
|
|
line = end;
|
|
}
|
|
|
|
return num_params;
|
|
}
|
|
|
|
|
|
static char*
|
|
inp_fix_inst_line(char *inst_line,
|
|
int num_subckt_params, char *subckt_param_names[], char *subckt_param_values[],
|
|
int num_inst_params, char *inst_param_names[], char *inst_param_values[])
|
|
{
|
|
char *end, *inst_name, *inst_name_end;
|
|
char *curr_line = inst_line, *new_line = NULL;
|
|
int i, j;
|
|
|
|
inst_name_end = skip_non_ws(inst_line);
|
|
inst_name = copy_substring(inst_line, inst_name_end);
|
|
|
|
end = strchr(inst_line, '=');
|
|
if (end) {
|
|
end = skip_back_ws(end, inst_line);
|
|
end = skip_back_non_ws(end, inst_line);
|
|
end[-1] = '\0'; /* fixme can be < inst_line */
|
|
}
|
|
|
|
for (i = 0; i < num_subckt_params; i++)
|
|
for (j = 0; j < num_inst_params; j++)
|
|
if (strcmp(subckt_param_names[i], inst_param_names[j]) == 0) {
|
|
tfree(subckt_param_values[i]);
|
|
subckt_param_values[i] = copy(inst_param_values[j]);
|
|
}
|
|
|
|
for (i = 0; i < num_subckt_params; i++) {
|
|
new_line = tprintf("%s %s", curr_line, subckt_param_values[i]);
|
|
|
|
tfree(curr_line);
|
|
tfree(subckt_param_names[i]);
|
|
tfree(subckt_param_values[i]);
|
|
|
|
curr_line = new_line;
|
|
}
|
|
|
|
for (i = 0; i < num_inst_params; i++) {
|
|
tfree(inst_param_names[i]);
|
|
tfree(inst_param_values[i]);
|
|
}
|
|
|
|
tfree(inst_name);
|
|
|
|
return curr_line;
|
|
}
|
|
|
|
|
|
/* If multiplier parameter 'm' is found on a X line, flag is set
|
|
to TRUE.
|
|
Function is called from inp_fix_inst_calls_for_numparam() */
|
|
|
|
static bool
|
|
found_mult_param(int num_params, char *param_names[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_params; i++)
|
|
if (strcmp(param_names[i], "m") == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* If a subcircuit invocation (X-line) is found, which contains the
|
|
multiplier parameter 'm', m is added to all lines inside
|
|
the corresponding subcircuit except of some excluded in the code below
|
|
Function is called from inp_fix_inst_calls_for_numparam() */
|
|
|
|
static int
|
|
inp_fix_subckt_multiplier(struct names *subckt_w_params, struct card *subckt_card,
|
|
int num_subckt_params, char *subckt_param_names[], char *subckt_param_values[])
|
|
{
|
|
struct card *card;
|
|
char *new_str;
|
|
|
|
subckt_param_names[num_subckt_params] = copy("m");
|
|
subckt_param_values[num_subckt_params] = copy("1");
|
|
num_subckt_params ++;
|
|
|
|
if (!strstr(subckt_card->line, "params:")) {
|
|
new_str = tprintf("%s params: m=1", subckt_card->line);
|
|
add_name(subckt_w_params, get_subckt_model_name(subckt_card->line));
|
|
} else {
|
|
new_str = tprintf("%s m=1", subckt_card->line);
|
|
}
|
|
|
|
tfree(subckt_card->line);
|
|
subckt_card->line = new_str;
|
|
|
|
for (card = subckt_card->nextcard;
|
|
card && !ciprefix(".ends", card->line);
|
|
card = card->nextcard) {
|
|
char *curr_line = card->line;
|
|
/* no 'm' for comment line, B, V, E, H and some others that are not using 'm' in their model description */
|
|
if (strchr("*bvehaknopstuwy", curr_line[0]))
|
|
continue;
|
|
/* no 'm' for model cards */
|
|
if (ciprefix(".model", curr_line))
|
|
continue;
|
|
new_str = tprintf("%s m={m}", curr_line);
|
|
|
|
tfree(card->line);
|
|
card->line = new_str;
|
|
}
|
|
|
|
return num_subckt_params;
|
|
}
|
|
|
|
|
|
/* a bogus search for a .subckt with given name,
|
|
* which does not honour scoping rules
|
|
*/
|
|
|
|
static struct card *
|
|
bogus_find_subckt(struct card *d, const char *subckt_name)
|
|
{
|
|
const size_t len = strlen(subckt_name);
|
|
for (; d; d = d->nextcard)
|
|
if (ciprefix(".subckt", d->line)) {
|
|
const char *n = skip_ws(skip_non_ws(d->line));
|
|
if ((strncmp(n, subckt_name, len) == 0) && (!n[len] || isspace_c(n[len])))
|
|
break;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
|
|
static void
|
|
inp_fix_inst_calls_for_numparam(struct names *subckt_w_params, struct card *deck)
|
|
{
|
|
struct card *c;
|
|
char *subckt_param_names[NPARAMS];
|
|
char *subckt_param_values[NPARAMS];
|
|
char *inst_param_names[NPARAMS];
|
|
char *inst_param_values[NPARAMS];
|
|
int i;
|
|
|
|
// first iterate through instances and find occurences where 'm' multiplier needs to be
|
|
// added to the subcircuit -- subsequent instances will then need this parameter as well
|
|
for (c = deck; c; c = c->nextcard) {
|
|
char *inst_line = c->line;
|
|
|
|
if (*inst_line == '*')
|
|
continue;
|
|
|
|
if (ciprefix("x", inst_line)) {
|
|
int num_inst_params = inp_get_params(inst_line, inst_param_names, inst_param_values);
|
|
char *subckt_name = inp_get_subckt_name(inst_line);
|
|
|
|
if (found_mult_param(num_inst_params, inst_param_names)) {
|
|
struct card *d, *p = NULL;
|
|
|
|
// iterate through the deck to find the subckt (last one defined wins)
|
|
for (d = deck; (d = bogus_find_subckt(d, subckt_name)) != NULL; d = d->nextcard)
|
|
p = d;
|
|
|
|
if (p) {
|
|
int num_subckt_params = inp_get_params(p->line, subckt_param_names, subckt_param_values);
|
|
|
|
if (!found_mult_param(num_subckt_params, subckt_param_names))
|
|
inp_fix_subckt_multiplier(subckt_w_params, p, num_subckt_params, subckt_param_names, subckt_param_values);
|
|
|
|
for (i = 0; i < num_subckt_params; i++) {
|
|
tfree(subckt_param_names[i]);
|
|
tfree(subckt_param_values[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
tfree(subckt_name);
|
|
for (i = 0; i < num_inst_params; i++) {
|
|
tfree(inst_param_names[i]);
|
|
tfree(inst_param_values[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (c = deck; c; c = c->nextcard) {
|
|
char *inst_line = c->line;
|
|
|
|
if (*inst_line == '*')
|
|
continue;
|
|
|
|
if (ciprefix("x", inst_line)) {
|
|
|
|
char *subckt_name = inp_get_subckt_name(inst_line);
|
|
|
|
if (find_name(subckt_w_params, subckt_name)) {
|
|
struct card *d;
|
|
|
|
for (d = deck; (d = bogus_find_subckt(d, subckt_name)) != NULL; d = d->nextcard) {
|
|
char *subckt_line = d->line;
|
|
subckt_line = skip_non_ws(subckt_line);
|
|
subckt_line = skip_ws(subckt_line);
|
|
|
|
int num_subckt_params = inp_get_params(subckt_line, subckt_param_names, subckt_param_values);
|
|
int num_inst_params = inp_get_params(inst_line, inst_param_names, inst_param_values);
|
|
|
|
// make sure that if have inst params that one matches subckt
|
|
if (num_inst_params != 0) {
|
|
bool found_param_match = FALSE;
|
|
int j, k;
|
|
|
|
for (j = 0; j < num_inst_params; j++) {
|
|
for (k = 0; k < num_subckt_params; k++)
|
|
if (strcmp(subckt_param_names[k], inst_param_names[j]) == 0) {
|
|
found_param_match = TRUE;
|
|
break;
|
|
}
|
|
if (found_param_match)
|
|
break;
|
|
}
|
|
|
|
if (!found_param_match) {
|
|
// comment out .subckt and continue
|
|
while (d != NULL && !ciprefix(".ends", d->line)) {
|
|
*(d->line) = '*';
|
|
d = d->nextcard;
|
|
}
|
|
*(d->line) = '*';
|
|
continue;
|
|
}
|
|
}
|
|
|
|
c->line = inp_fix_inst_line(inst_line, num_subckt_params, subckt_param_names, subckt_param_values, num_inst_params, inst_param_names, inst_param_values);
|
|
for (i = 0; i < num_subckt_params; i++) {
|
|
tfree(subckt_param_names[i]);
|
|
tfree(subckt_param_values[i]);
|
|
}
|
|
|
|
for (i = 0; i < num_inst_params; i++) {
|
|
tfree(inst_param_names[i]);
|
|
tfree(inst_param_values[i]);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
tfree(subckt_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static struct function *
|
|
new_function(struct function_env *env, char *name)
|
|
{
|
|
struct function *f = TMALLOC(struct function, 1);
|
|
|
|
f -> name = name;
|
|
f -> num_parameters = 0;
|
|
|
|
f -> next = env->functions;
|
|
env -> functions = f;
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
static struct function *
|
|
find_function(struct function_env *env, char *name)
|
|
{
|
|
struct function *f;
|
|
|
|
for (; env; env = env->up)
|
|
for (f = env->functions; f; f = f->next)
|
|
if (strcmp(f->name, name) == 0)
|
|
return f;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
free_function(struct function *fcn)
|
|
{
|
|
int i;
|
|
|
|
tfree(fcn->name);
|
|
tfree(fcn->body);
|
|
tfree(fcn->accept);
|
|
|
|
for (i = 0; i < fcn->num_parameters; i++)
|
|
tfree(fcn->params[i]);
|
|
}
|
|
|
|
|
|
static void
|
|
new_function_parameter(struct function *fcn, char *parameter)
|
|
{
|
|
if (fcn->num_parameters >= N_PARAMS) {
|
|
fprintf(stderr, "ERROR, N_PARAMS overflow\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
fcn->params[fcn->num_parameters++] = parameter;
|
|
}
|
|
|
|
|
|
static bool
|
|
inp_strip_braces(char *s)
|
|
{
|
|
int nesting = 0;
|
|
char *d = s;
|
|
|
|
for (; *s; s++)
|
|
if (*s == '{') {
|
|
nesting++;
|
|
} else if (*s == '}') {
|
|
if (--nesting < 0)
|
|
return FALSE;
|
|
} else if (!isspace_c(*s)) {
|
|
*d++ = *s;
|
|
}
|
|
|
|
*d++ = '\0';
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
inp_get_func_from_line(struct function_env *env, char *line)
|
|
{
|
|
char *end, *orig_line = line;
|
|
struct function *function;
|
|
|
|
/* skip `.func' */
|
|
line = skip_non_ws(line);
|
|
line = skip_ws(line);
|
|
|
|
/* get function name */
|
|
end = line;
|
|
while (*end && !isspace_c(*end) && *end != '(')
|
|
end++;
|
|
|
|
function = new_function(env, copy_substring(line, end));
|
|
|
|
end = skip_ws(end);
|
|
|
|
if (*end != '(')
|
|
goto Lerror;
|
|
|
|
end = skip_ws(end + 1);
|
|
|
|
/* get function parameters */
|
|
for (;;) {
|
|
char *beg = end;
|
|
while (*end && !isspace_c(*end) && *end != ',' && *end != ')')
|
|
end++;
|
|
if (end == beg)
|
|
break;
|
|
new_function_parameter(function, copy_substring(beg, end));
|
|
end = skip_ws(end);
|
|
if (*end != ',')
|
|
break;
|
|
end = skip_ws(end + 1);
|
|
if (*end == ')')
|
|
goto Lerror;
|
|
}
|
|
|
|
if (*end != ')')
|
|
goto Lerror;
|
|
|
|
end = skip_ws(end + 1);
|
|
|
|
// skip an unwanted and non advertised optional '='
|
|
if (*end == '=')
|
|
end = skip_ws(end + 1);
|
|
|
|
function->body = copy(end);
|
|
|
|
if (inp_strip_braces(function->body)) {
|
|
int i;
|
|
|
|
char *accept = TMALLOC(char, function->num_parameters + 1);
|
|
for (i = 0; i < function->num_parameters; i++)
|
|
accept[i] = function->params[i][0];
|
|
accept[i] = '\0';
|
|
|
|
function->accept = accept;
|
|
return;
|
|
}
|
|
|
|
tfree(function->body);
|
|
|
|
Lerror:
|
|
// fixme, free()
|
|
fprintf(stderr, "ERROR: failed to parse .func in: %s\n", orig_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
/*
|
|
* grab functions at the current .subckt nesting level
|
|
*/
|
|
|
|
static void
|
|
inp_grab_func(struct function_env *env, struct card *c)
|
|
{
|
|
int nesting = 0;
|
|
|
|
for (; c; c = c->nextcard) {
|
|
|
|
if (*c->line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".subckt", c->line))
|
|
nesting++;
|
|
if (ciprefix(".ends", c->line))
|
|
nesting--;
|
|
|
|
if (nesting < 0)
|
|
break;
|
|
|
|
if (nesting > 0)
|
|
continue;
|
|
|
|
if (ciprefix(".func", c->line)) {
|
|
inp_get_func_from_line(env, c->line);
|
|
*c->line = '*';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static char*
|
|
search_func_arg(char *str, struct function *fcn, int *which, char *str_begin)
|
|
{
|
|
for (; (str = strpbrk(str, fcn->accept)) != NULL; str++) {
|
|
char before;
|
|
|
|
if (str > str_begin)
|
|
before = str[-1];
|
|
else
|
|
before = '\0';
|
|
|
|
if (is_arith_char(before) || isspace_c(before) || strchr(",=", before)) {
|
|
int i;
|
|
for (i = 0; i < fcn->num_parameters; i++) {
|
|
size_t len = strlen(fcn->params[i]);
|
|
if (strncmp(str, fcn->params[i], len) == 0) {
|
|
char after = str[len];
|
|
if (is_arith_char(after) || isspace_c(after) || strchr(",=", after)) {
|
|
*which = i;
|
|
return str;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static char*
|
|
inp_do_macro_param_replace(struct function *fcn, char *params[])
|
|
{
|
|
char *str = copy(fcn->body);
|
|
int i;
|
|
|
|
char *collect_ptr = NULL;
|
|
char *arg_ptr = str;
|
|
char *rest = str;
|
|
|
|
while ((arg_ptr = search_func_arg(arg_ptr, fcn, &i, str)) != NULL) {
|
|
char *p;
|
|
int is_vi = 0;
|
|
|
|
/* exclude v(nn, parameter), v(parameter, nn), v(parameter),
|
|
and i(parameter) if here 'parameter' is also a node name */
|
|
|
|
/* go backwards from 'parameter' and find '(' */
|
|
for (p = arg_ptr; --p > str; )
|
|
if (*p == '(' || *p == ')') {
|
|
if ((*p == '(') && strchr("vi", p[-1]) &&
|
|
(p - 2 < str || is_arith_char(p[-2]) || isspace_c(p[-2]) || strchr(",=", p[-2])))
|
|
is_vi = 1;
|
|
break;
|
|
}
|
|
|
|
/* if we have a true v( or i( */
|
|
if (is_vi) {
|
|
/* go forward and find closing ')' */
|
|
for (p = arg_ptr + 1; *p; p++)
|
|
if (*p == '(' || *p == ')')
|
|
break;
|
|
/* We have a true v(...) or i(...),
|
|
so skip it, and continue searching for new 'parameter' */
|
|
if (*p == ')') {
|
|
arg_ptr = p;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
size_t collect_ptr_len = collect_ptr ? strlen(collect_ptr) : 0;
|
|
size_t len = strlen(rest) + strlen(params[i]) + 1;
|
|
int prefix_len = (int) (arg_ptr - rest);
|
|
if (str_has_arith_char(params[i])) {
|
|
collect_ptr = TREALLOC(char, collect_ptr, collect_ptr_len + len + 2);
|
|
sprintf(collect_ptr + collect_ptr_len, "%.*s(%s)", prefix_len, rest, params[i]);
|
|
} else {
|
|
collect_ptr = TREALLOC(char, collect_ptr, collect_ptr_len + len);
|
|
sprintf(collect_ptr + collect_ptr_len, "%.*s%s", prefix_len, rest, params[i]);
|
|
}
|
|
}
|
|
|
|
arg_ptr += strlen(fcn->params[i]);
|
|
rest = arg_ptr;
|
|
}
|
|
|
|
if (collect_ptr) {
|
|
char *new_str = tprintf("%s%s", collect_ptr, rest);
|
|
tfree(collect_ptr);
|
|
tfree(str);
|
|
str = new_str;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
static char*
|
|
inp_expand_macro_in_str(struct function_env *env, char *str)
|
|
{
|
|
struct function *function;
|
|
char *open_paren_ptr, *close_paren_ptr, *fcn_name, *params[FCN_PARAMS];
|
|
char *curr_ptr, *macro_str, *curr_str = NULL;
|
|
int num_params, i;
|
|
char *orig_ptr = str, *search_ptr = str, *orig_str = copy(str);
|
|
char keep;
|
|
|
|
// printf("%s: enter(\"%s\")\n", __FUNCTION__, str);
|
|
while ((open_paren_ptr = strchr(search_ptr, '(')) != NULL) {
|
|
|
|
fcn_name = open_paren_ptr;
|
|
while (--fcn_name >= search_ptr)
|
|
/* function name consists of numbers, letters and special characters (VALIDCHARS) */
|
|
if (!isalnum_c(*fcn_name) && !strchr(VALIDCHARS, *fcn_name))
|
|
break;
|
|
fcn_name++;
|
|
|
|
search_ptr = open_paren_ptr + 1;
|
|
if (open_paren_ptr == fcn_name)
|
|
continue;
|
|
|
|
*open_paren_ptr = '\0';
|
|
|
|
function = find_function(env, fcn_name);
|
|
|
|
*open_paren_ptr = '(';
|
|
|
|
if (!function)
|
|
continue;
|
|
|
|
/* find the closing paren */
|
|
{
|
|
int num_parens = 1;
|
|
char *c = open_paren_ptr + 1;
|
|
|
|
for (; *c; c++) {
|
|
if (*c == '(')
|
|
num_parens++;
|
|
if (*c == ')' && --num_parens == 0)
|
|
break;
|
|
}
|
|
|
|
if (num_parens) {
|
|
fprintf(stderr, "ERROR: did not find closing parenthesis for function call in str: %s\n", orig_str);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
close_paren_ptr = c;
|
|
}
|
|
|
|
/*
|
|
* if (ciprefix("v(", curr_ptr)) {
|
|
* // look for any commas and change to ' '
|
|
* char *str_ptr = curr_ptr;
|
|
* while (*str_ptr != '\0' && *str_ptr != ')') {
|
|
* if (*str_ptr == ',' || *str_ptr == '(')
|
|
* *str_ptr = ' '; str_ptr++; }
|
|
* if (*str_ptr == ')')
|
|
* *str_ptr = ' ';
|
|
* }
|
|
*/
|
|
|
|
/* get the parameters */
|
|
curr_ptr = open_paren_ptr + 1;
|
|
|
|
for (num_params = 0; curr_ptr < close_paren_ptr; curr_ptr++) {
|
|
char *beg_parameter;
|
|
int num_parens;
|
|
if (isspace_c(*curr_ptr))
|
|
continue;
|
|
beg_parameter = curr_ptr;
|
|
num_parens = 0;
|
|
for (; curr_ptr < close_paren_ptr; curr_ptr++) {
|
|
if (*curr_ptr == '(')
|
|
num_parens++;
|
|
if (*curr_ptr == ')')
|
|
num_parens--;
|
|
if (*curr_ptr == ',' && num_parens == 0)
|
|
break;
|
|
}
|
|
if (num_params == FCN_PARAMS) {
|
|
fprintf(stderr, "Error: Too many params in fcn, max is %d\n", FCN_PARAMS);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
params[num_params++] =
|
|
inp_expand_macro_in_str(env, copy_substring(beg_parameter, curr_ptr));
|
|
}
|
|
|
|
if (function->num_parameters != num_params) {
|
|
fprintf(stderr, "ERROR: parameter mismatch for function call in str: %s\n", orig_str);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
macro_str = inp_do_macro_param_replace(function, params);
|
|
macro_str = inp_expand_macro_in_str(env, macro_str);
|
|
keep = *fcn_name;
|
|
*fcn_name = '\0';
|
|
{
|
|
size_t curr_str_len = curr_str ? strlen(curr_str) : 0;
|
|
size_t len = strlen(str) + strlen(macro_str) + 3;
|
|
curr_str = TREALLOC(char, curr_str, curr_str_len + len);
|
|
sprintf(curr_str + curr_str_len, "%s(%s)", str, macro_str);
|
|
}
|
|
*fcn_name = keep;
|
|
tfree(macro_str);
|
|
|
|
search_ptr = str = close_paren_ptr + 1;
|
|
|
|
for (i = 0; i < num_params; i++)
|
|
tfree(params[i]);
|
|
}
|
|
|
|
if (curr_str == NULL) {
|
|
curr_str = orig_ptr;
|
|
} else {
|
|
if (str != NULL) {
|
|
size_t curr_str_len = strlen(curr_str);
|
|
size_t len = strlen(str) + 1;
|
|
curr_str = TREALLOC(char, curr_str, curr_str_len + len);
|
|
sprintf(curr_str + curr_str_len, "%s", str);
|
|
}
|
|
tfree(orig_ptr);
|
|
}
|
|
|
|
tfree(orig_str);
|
|
// printf("%s: --> \"%s\"\n", __FUNCTION__, curr_str);
|
|
|
|
return curr_str;
|
|
}
|
|
|
|
|
|
static void
|
|
inp_expand_macros_in_func(struct function_env *env)
|
|
{
|
|
struct function *f;
|
|
|
|
for (f = env->functions; f ; f = f->next)
|
|
f->body = inp_expand_macro_in_str(env, f->body);
|
|
}
|
|
|
|
|
|
static struct function_env *
|
|
new_function_env(struct function_env *up)
|
|
{
|
|
struct function_env *env = TMALLOC(struct function_env, 1);
|
|
|
|
env -> up = up;
|
|
env -> functions = NULL;
|
|
|
|
return env;
|
|
}
|
|
|
|
|
|
static struct function_env *
|
|
delete_function_env(struct function_env *env)
|
|
{
|
|
struct function_env *up = env -> up;
|
|
struct function *f;
|
|
|
|
for (f = env -> functions; f; ) {
|
|
struct function *here = f;
|
|
f = f -> next;
|
|
free_function(here);
|
|
tfree(here);
|
|
}
|
|
|
|
tfree(env);
|
|
|
|
return up;
|
|
}
|
|
|
|
|
|
static struct card *
|
|
inp_expand_macros_in_deck(struct function_env *env, struct card *c)
|
|
{
|
|
env = new_function_env(env);
|
|
|
|
inp_grab_func(env, c);
|
|
|
|
inp_expand_macros_in_func(env);
|
|
|
|
for (; c; c = c->nextcard) {
|
|
|
|
if (*c->line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".subckt", c->line)) {
|
|
struct card *subckt = c;
|
|
c = inp_expand_macros_in_deck(env, c->nextcard);
|
|
if (c)
|
|
continue;
|
|
|
|
fprintf(stderr, "Error: line %d, missing .ends\n %s\n",
|
|
subckt->linenum_orig, subckt->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
|
|
if (ciprefix(".ends", c->line))
|
|
break;
|
|
|
|
c->line = inp_expand_macro_in_str(env, c->line);
|
|
}
|
|
|
|
env = delete_function_env(env);
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
/* Put {} around tokens for handling in numparam.
|
|
Searches for the next '=' in the line to become active.
|
|
Several exceptions (eg. no 'set' or 'b' lines, no .cmodel lines,
|
|
no lines between .control and .endc, no .option lines).
|
|
Special handling of vectors with [] and complex values with < >
|
|
|
|
h_vogt 20 April 2008
|
|
* For xspice and num_pram compatibility .cmodel added
|
|
* .cmodel will be replaced by .model in inp_fix_param_values()
|
|
* and then the entire line is skipped (will not be changed by this function).
|
|
* Usage of numparam requires {} around the parameters in the .cmodel line.
|
|
* May be obsolete?
|
|
*/
|
|
|
|
static void
|
|
inp_fix_param_values(struct card *c)
|
|
{
|
|
char *beg_of_str, *end_of_str, *old_str, *equal_ptr, *new_str;
|
|
char *vec_str, *tmp_str, *natok, *buffer, *newvec, *whereisgt;
|
|
bool control_section = FALSE;
|
|
wordlist *nwl;
|
|
int parens;
|
|
|
|
for (; c; c = c->nextcard) {
|
|
char *line = c->line;
|
|
|
|
if (*line == '*' || (ciprefix(".para", line) && strchr(line, '{')))
|
|
continue;
|
|
|
|
if (ciprefix(".control", line)) {
|
|
control_section = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".endc", line)) {
|
|
control_section = FALSE;
|
|
continue;
|
|
}
|
|
|
|
/* no handling of params in "option" lines */
|
|
if (control_section || ciprefix(".option", line))
|
|
continue;
|
|
|
|
/* no handling of params in "set" lines */
|
|
if (ciprefix("set", line))
|
|
continue;
|
|
|
|
/* no handling of params in B source lines */
|
|
if (*line == 'b')
|
|
continue;
|
|
|
|
/* for xspice .cmodel: replace .cmodel with .model and skip entire line) */
|
|
if (ciprefix(".cmodel", line)) {
|
|
*(++line) = 'm';
|
|
*(++line) = 'o';
|
|
*(++line) = 'd';
|
|
*(++line) = 'e';
|
|
*(++line) = 'l';
|
|
*(++line) = ' ';
|
|
continue;
|
|
}
|
|
|
|
/* exclude CIDER models */
|
|
if (ciprefix(".model", line) && (strstr(line, "numos") || strstr(line, "numd") ||
|
|
strstr(line, "nbjt") || strstr(line, "nbjt2") ||
|
|
strstr(line, "numd2")))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* exclude CIDER devices with ic.file parameter */
|
|
if (strstr(line, "ic.file"))
|
|
continue;
|
|
|
|
while ((equal_ptr = find_assignment(line)) != NULL) {
|
|
|
|
// special case: .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=out_variable3
|
|
// no braces around out_variable3. out_variable3 may be v(...) or i(...)
|
|
if (ciprefix(".meas", line))
|
|
if (((equal_ptr[1] == 'v') || (equal_ptr[1] == 'i')) &&
|
|
(equal_ptr[2] == '('))
|
|
{
|
|
// find closing ')' and skip token v(...) or i(...)
|
|
while (*equal_ptr != ')' && equal_ptr[1] != '\0')
|
|
equal_ptr++;
|
|
line = equal_ptr + 1;
|
|
continue;
|
|
}
|
|
|
|
beg_of_str = skip_ws(equal_ptr + 1);
|
|
/* all cases where no {} have to be put around selected token */
|
|
if (isdigit_c(*beg_of_str) ||
|
|
*beg_of_str == '{' ||
|
|
*beg_of_str == '.' ||
|
|
*beg_of_str == '"' ||
|
|
((*beg_of_str == '-' || *beg_of_str == '+') && isdigit_c(beg_of_str[1])) ||
|
|
((*beg_of_str == '-' || *beg_of_str == '+') && beg_of_str[1] == '.' && isdigit_c(beg_of_str[2])) ||
|
|
ciprefix("true", beg_of_str) ||
|
|
ciprefix("false", beg_of_str))
|
|
{
|
|
line = equal_ptr + 1;
|
|
} else if (*beg_of_str == '[') {
|
|
/* A vector following the '=' token: code to put curly brackets around all params
|
|
inside a pair of square brackets */
|
|
end_of_str = beg_of_str;
|
|
while (*end_of_str != ']')
|
|
end_of_str++;
|
|
/* string xx yyy from vector [xx yyy] */
|
|
tmp_str = vec_str = copy_substring(beg_of_str + 1, end_of_str);
|
|
|
|
/* work on vector elements inside [] */
|
|
nwl = NULL;
|
|
for (;;) {
|
|
natok = gettok(&vec_str);
|
|
if (!natok)
|
|
break;
|
|
|
|
buffer = TMALLOC(char, strlen(natok) + 4);
|
|
if (isdigit_c(*natok) || *natok == '{' || *natok == '.' ||
|
|
*natok == '"' || (*natok == '-' && isdigit_c(natok[1])) ||
|
|
ciprefix("true", natok) || ciprefix("false", natok) ||
|
|
eq(natok, "<") || eq(natok, ">"))
|
|
{
|
|
(void) sprintf(buffer, "%s", natok);
|
|
/* A complex value found inside a vector [< x1 y1> <x2 y2>] */
|
|
/* < xx and yy > have been dealt with before */
|
|
/* <xx */
|
|
} else if (*natok == '<') {
|
|
if (isdigit_c(natok[1]) ||
|
|
(natok[1] == '-' && isdigit_c(natok[2])))
|
|
{
|
|
(void) sprintf(buffer, "%s", natok);
|
|
} else {
|
|
*natok = '{';
|
|
(void) sprintf(buffer, "<%s}", natok);
|
|
}
|
|
/* yy> */
|
|
} else if (strchr(natok, '>')) {
|
|
if (isdigit_c(*natok) || (*natok == '-' && isdigit_c(natok[1]))) {
|
|
(void) sprintf(buffer, "%s", natok);
|
|
} else {
|
|
whereisgt = strchr(natok, '>');
|
|
*whereisgt = '}';
|
|
(void) sprintf(buffer, "{%s>", natok);
|
|
}
|
|
/* all other tokens */
|
|
} else {
|
|
(void) sprintf(buffer, "{%s}", natok);
|
|
}
|
|
tfree(natok);
|
|
nwl = wl_cons(copy(buffer), nwl);
|
|
tfree(buffer);
|
|
}
|
|
tfree(tmp_str);
|
|
nwl = wl_reverse(nwl);
|
|
/* new vector elements */
|
|
newvec = wl_flatten(nwl);
|
|
wl_free(nwl);
|
|
/* insert new vector into actual line */
|
|
*equal_ptr = '\0';
|
|
new_str = tprintf("%s=[%s] %s", c->line, newvec, end_of_str+1);
|
|
tfree(newvec);
|
|
|
|
old_str = c->line;
|
|
c->line = new_str;
|
|
line = new_str + strlen(old_str) + 1;
|
|
tfree(old_str);
|
|
} else if (*beg_of_str == '<') {
|
|
/* A complex value following the '=' token: code to put curly brackets around all params
|
|
inside a pair < > */
|
|
end_of_str = beg_of_str;
|
|
while (*end_of_str != '>')
|
|
end_of_str++;
|
|
/* string xx yyy from vector [xx yyy] */
|
|
vec_str = copy_substring(beg_of_str + 1, end_of_str);
|
|
|
|
/* work on tokens inside <> */
|
|
nwl = NULL;
|
|
for (;;) {
|
|
natok = gettok(&vec_str);
|
|
if (!natok)
|
|
break;
|
|
|
|
buffer = TMALLOC(char, strlen(natok) + 4);
|
|
if (isdigit_c(*natok) || *natok == '{' || *natok == '.' ||
|
|
*natok == '"' || (*natok == '-' && isdigit_c(natok[1])) ||
|
|
ciprefix("true", natok) || ciprefix("false", natok))
|
|
{
|
|
(void) sprintf(buffer, "%s", natok);
|
|
} else {
|
|
(void) sprintf(buffer, "{%s}", natok);
|
|
}
|
|
tfree(natok);
|
|
nwl = wl_cons(copy(buffer), nwl);
|
|
tfree(buffer);
|
|
}
|
|
nwl = wl_reverse(nwl);
|
|
/* new elements of complex variable */
|
|
newvec = wl_flatten(nwl);
|
|
wl_free(nwl);
|
|
/* insert new complex value into actual line */
|
|
*equal_ptr = '\0';
|
|
new_str = tprintf("%s=<%s> %s", c->line, newvec, end_of_str+1);
|
|
tfree(newvec);
|
|
|
|
old_str = c->line;
|
|
c->line = new_str;
|
|
line = new_str + strlen(old_str) + 1;
|
|
tfree(old_str);
|
|
} else {
|
|
/* put {} around token to be accepted as numparam */
|
|
end_of_str = beg_of_str;
|
|
parens = 0;
|
|
while (*end_of_str != '\0' &&
|
|
(!isspace_c(*end_of_str) || (parens > 0)))
|
|
{
|
|
if (*end_of_str == '(')
|
|
parens++;
|
|
if (*end_of_str == ')')
|
|
parens--;
|
|
end_of_str++;
|
|
}
|
|
|
|
*equal_ptr = '\0';
|
|
|
|
if (*end_of_str == '\0') {
|
|
new_str = tprintf("%s={%s}", c->line, beg_of_str);
|
|
|
|
} else {
|
|
*end_of_str = '\0';
|
|
new_str = tprintf("%s={%s} %s", c->line, beg_of_str, end_of_str+1);
|
|
}
|
|
old_str = c->line;
|
|
c->line = new_str;
|
|
|
|
line = new_str + strlen(old_str) + 1;
|
|
tfree(old_str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static char*
|
|
get_param_name(char *line)
|
|
{
|
|
char *beg;
|
|
char *equal_ptr = strchr(line, '=');
|
|
|
|
if (!equal_ptr) {
|
|
fprintf(stderr, "ERROR: could not find '=' on parameter line '%s'!\n", line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
equal_ptr = skip_back_ws(equal_ptr, line);
|
|
|
|
beg = skip_back_non_ws(equal_ptr, line);
|
|
|
|
return copy_substring(beg, equal_ptr);
|
|
}
|
|
|
|
|
|
static char*
|
|
get_param_str(char *line)
|
|
{
|
|
char *equal_ptr = strchr(line, '=');
|
|
|
|
if (equal_ptr)
|
|
return skip_ws(equal_ptr + 1);
|
|
else
|
|
return line;
|
|
}
|
|
|
|
|
|
struct dependency
|
|
{
|
|
int level;
|
|
int skip;
|
|
char *param_name;
|
|
char *param_str;
|
|
char *depends_on[100];
|
|
struct card *card;
|
|
};
|
|
|
|
|
|
static int
|
|
inp_get_param_level(int param_num, struct dependency *deps, int num_params)
|
|
{
|
|
int i, k, l, level = 0;
|
|
|
|
if (deps[param_num].level != -1)
|
|
return deps[param_num].level;
|
|
|
|
for (i = 0; deps[param_num].depends_on[i]; i++) {
|
|
|
|
for (k = 0; k < num_params; k++)
|
|
if (deps[param_num].depends_on[i] == deps[k].param_name)
|
|
break;
|
|
|
|
if (k >= num_params) {
|
|
fprintf(stderr, "ERROR: unable to find dependency parameter for %s!\n", deps[param_num].param_name);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
l = inp_get_param_level(k, deps, num_params) + 1;
|
|
|
|
if (level < l)
|
|
level = l;
|
|
}
|
|
|
|
deps[param_num].level = level;
|
|
|
|
return level;
|
|
}
|
|
|
|
|
|
static int
|
|
get_number_terminals(char *c)
|
|
{
|
|
int i, j, k;
|
|
char *name[12];
|
|
char nam_buf[128];
|
|
bool area_found = FALSE;
|
|
|
|
switch (*c) {
|
|
case 'r':
|
|
case 'c':
|
|
case 'l':
|
|
case 'k':
|
|
case 'f':
|
|
case 'h':
|
|
case 'b':
|
|
case 'v':
|
|
case 'i':
|
|
case 'd':
|
|
return 2;
|
|
break;
|
|
case 'u':
|
|
case 'j':
|
|
case 'w':
|
|
case 'z':
|
|
return 3;
|
|
break;
|
|
case 't':
|
|
case 'o':
|
|
case 'g':
|
|
case 'e':
|
|
case 's':
|
|
case 'y':
|
|
return 4;
|
|
break;
|
|
case 'm': /* recognition of 4, 5, 6, or 7 nodes for SOI devices needed */
|
|
i = 0;
|
|
/* find the first token with "off" or "=" in the line*/
|
|
while ((i < 20) && (*c != '\0')) {
|
|
char *inst = gettok_instance(&c);
|
|
strncpy(nam_buf, inst, sizeof(nam_buf) - 1);
|
|
txfree(inst);
|
|
if (strstr(nam_buf, "off") || strchr(nam_buf, '='))
|
|
break;
|
|
i++;
|
|
}
|
|
return i-2;
|
|
break;
|
|
case 'p': /* recognition of up to 100 cpl nodes */
|
|
i = j = 0;
|
|
/* find the last token in the line*/
|
|
while ((i < 100) && (*c != '\0')) {
|
|
char *tmp_inst = gettok_instance(&c);
|
|
strncpy(nam_buf, tmp_inst, 32);
|
|
tfree(tmp_inst);
|
|
if (strchr(nam_buf, '='))
|
|
j++;
|
|
i++;
|
|
}
|
|
if (i == 100)
|
|
return 0;
|
|
return i - j - 2;
|
|
break;
|
|
case 'q': /* recognition of 3, 4 or 5 terminal bjt's needed */
|
|
/* QXXXXXXX NC NB NE <NS> <NT> MNAME <AREA> <OFF> <IC=VBE, VCE> <TEMP=T> */
|
|
/* 12 tokens maximum */
|
|
i = j = 0;
|
|
while ((i < 12) && (*c != '\0')) {
|
|
char *comma;
|
|
name[i] = gettok_instance(&c);
|
|
if (strstr(name[i], "off") || strchr(name[i], '='))
|
|
j++;
|
|
#ifdef CIDER
|
|
if (strstr(name[i], "save") || strstr(name[i], "print"))
|
|
j++;
|
|
#endif
|
|
/* If we have IC=VBE, VCE instead of IC=VBE,VCE we need to inc j */
|
|
if ((comma = strchr(name[i], ',')) != NULL && (*(++comma) == '\0'))
|
|
j++;
|
|
/* If we have IC=VBE , VCE ("," is a token) we need to inc j */
|
|
if (eq(name[i], ","))
|
|
j++;
|
|
i++;
|
|
}
|
|
i--;
|
|
area_found = FALSE;
|
|
for (k = i; k > i-j-1; k--) {
|
|
bool only_digits = TRUE;
|
|
char *nametmp = name[k];
|
|
/* MNAME has to contain at least one alpha character. AREA may be assumed
|
|
if we have a token with only digits, and where the previous token does not
|
|
end with a ',' */
|
|
while (*nametmp) {
|
|
if (isalpha_c(*nametmp) || (*nametmp == ','))
|
|
only_digits = FALSE;
|
|
nametmp++;
|
|
}
|
|
if (only_digits && (strchr(name[k-1], ',') == NULL))
|
|
area_found = TRUE;
|
|
}
|
|
for (k = i; k >= 0; k--)
|
|
tfree(name[k]);
|
|
if (area_found) {
|
|
return i-j-2;
|
|
} else {
|
|
return i-j-1;
|
|
}
|
|
break;
|
|
default:
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static char *ya_search_identifier(char *str, const char *identifier, char *str_begin);
|
|
|
|
|
|
static void inp_quote_params(struct card *s_c, struct card *e_c, struct dependency *deps, int num_params);
|
|
|
|
/* sort parameters based on parameter dependencies */
|
|
|
|
static void
|
|
inp_sort_params(struct card *param_cards, struct card *card_bf_start, struct card *s_c, struct card *e_c)
|
|
{
|
|
int i, j, num_params, ind = 0, max_level;
|
|
|
|
struct card *c;
|
|
int skipped;
|
|
int arr_size;
|
|
|
|
struct dependency *deps;
|
|
|
|
if (param_cards == NULL)
|
|
return;
|
|
|
|
/* determine the number of lines with .param */
|
|
|
|
arr_size = 0;
|
|
for (c = param_cards; c; c = c->nextcard)
|
|
if (strchr(c->line, '='))
|
|
arr_size ++;
|
|
|
|
deps = TMALLOC(struct dependency, arr_size);
|
|
|
|
num_params = 0;
|
|
for (c = param_cards; c; c = c->nextcard)
|
|
// ignore .param lines without '='
|
|
if (strchr(c->line, '=')) {
|
|
deps[num_params].depends_on[0] = NULL;
|
|
deps[num_params].level = -1;
|
|
deps[num_params].skip = 0;
|
|
deps[num_params].param_name = get_param_name(c->line); /* copy in fcn */
|
|
deps[num_params].param_str = copy(get_param_str(c->line));
|
|
deps[num_params].card = c;
|
|
num_params ++;
|
|
}
|
|
|
|
// look for duplicately defined parameters and mark earlier one to skip
|
|
// param list is ordered as defined in netlist
|
|
|
|
skipped = 0;
|
|
for (i = 0; i < num_params; i++) {
|
|
for (j = i + 1; j < num_params; j++)
|
|
if (strcmp(deps[i].param_name, deps[j].param_name) == 0)
|
|
break;
|
|
if (j < num_params) {
|
|
deps[i].skip = 1;
|
|
skipped++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_params; i++)
|
|
if (!deps[i].skip) {
|
|
char *param = deps[i].param_name;
|
|
for (j = 0; j < num_params; j++)
|
|
if (j != i && search_plain_identifier(deps[j].param_str, param)) {
|
|
for (ind = 0; deps[j].depends_on[ind]; ind++)
|
|
;
|
|
deps[j].depends_on[ind++] = param;
|
|
deps[j].depends_on[ind] = NULL;
|
|
}
|
|
}
|
|
|
|
max_level = 0;
|
|
for (i = 0; i < num_params; i++) {
|
|
deps[i].level = inp_get_param_level(i, deps, num_params);
|
|
if (max_level < deps[i].level)
|
|
max_level = deps[i].level;
|
|
}
|
|
|
|
c = card_bf_start;
|
|
|
|
ind = 0;
|
|
for (i = 0; i <= max_level; i++)
|
|
for (j = 0; j < num_params; j++)
|
|
if (!deps[j].skip && deps[j].level == i) {
|
|
c = insert_deck(c, deps[j].card);
|
|
ind++;
|
|
}
|
|
else if (deps[j].skip) {
|
|
line_free_x(deps[j].card, FALSE);
|
|
deps[j].card = NULL;
|
|
}
|
|
|
|
num_params -= skipped;
|
|
if (ind != num_params) {
|
|
fprintf(stderr, "ERROR: found wrong number of parameters during levelization ( %d instead of %d parameter s)!\n", ind, num_params);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
inp_quote_params(s_c, e_c, deps, num_params);
|
|
|
|
// clean up memory
|
|
for (i = 0; i < arr_size; i++) {
|
|
tfree(deps[i].param_name);
|
|
tfree(deps[i].param_str);
|
|
}
|
|
|
|
tfree(deps);
|
|
}
|
|
|
|
|
|
static void
|
|
inp_add_params_to_subckt(struct names *subckt_w_params, struct card *subckt_card)
|
|
{
|
|
struct card *card = subckt_card->nextcard;
|
|
char *subckt_line = subckt_card->line;
|
|
char *new_line, *param_ptr, *subckt_name, *end_ptr;
|
|
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->line;
|
|
|
|
if (!ciprefix(".para", curr_line))
|
|
break;
|
|
|
|
param_ptr = strchr(curr_line, ' ');
|
|
param_ptr = skip_ws(param_ptr);
|
|
|
|
if (!strstr(subckt_line, "params:")) {
|
|
new_line = tprintf("%s params: %s", subckt_line, param_ptr);
|
|
|
|
subckt_name = skip_non_ws(subckt_line);
|
|
subckt_name = skip_ws(subckt_name);
|
|
end_ptr = skip_non_ws(subckt_name);
|
|
add_name(subckt_w_params, copy_substring(subckt_name, end_ptr));
|
|
} else {
|
|
new_line = tprintf("%s %s", subckt_line, param_ptr);
|
|
}
|
|
|
|
tfree(subckt_line);
|
|
subckt_line = new_line;
|
|
|
|
*curr_line = '*';
|
|
}
|
|
|
|
subckt_card->line = subckt_line;
|
|
}
|
|
|
|
|
|
/*
|
|
* process a sequence of decks
|
|
* starting from a `.suckt' deck
|
|
* upto the corresponding `.ends' deck
|
|
* return a pointer to the terminating `.ends' deck
|
|
*
|
|
* recursivly descend
|
|
* when another `.subckt' is found
|
|
*
|
|
* parameters are removed from the main list
|
|
* and collected into a local list `first_param_card'
|
|
* then processed and reinserted into the main list
|
|
*
|
|
*/
|
|
|
|
static struct card *
|
|
inp_reorder_params_subckt(struct names *subckt_w_params, struct card *subckt_card)
|
|
{
|
|
struct card *first_param_card = NULL;
|
|
struct card *last_param_card = NULL;
|
|
|
|
struct card *prev_card = subckt_card;
|
|
struct card *c = subckt_card->nextcard;
|
|
|
|
/* move .param lines to beginning of deck */
|
|
while (c != NULL) {
|
|
|
|
char *curr_line = c->line;
|
|
|
|
if (*curr_line == '*') {
|
|
prev_card = c;
|
|
c = c->nextcard;
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".subckt", curr_line)) {
|
|
prev_card = inp_reorder_params_subckt(subckt_w_params, c);
|
|
c = prev_card->nextcard;
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".ends", curr_line)) {
|
|
if (first_param_card) {
|
|
inp_sort_params(first_param_card, subckt_card, subckt_card->nextcard, c);
|
|
inp_add_params_to_subckt(subckt_w_params, subckt_card);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
if (ciprefix(".para", curr_line)) {
|
|
prev_card->nextcard = c->nextcard;
|
|
|
|
last_param_card = insert_deck(last_param_card, c);
|
|
|
|
if (!first_param_card)
|
|
first_param_card = last_param_card;
|
|
|
|
c = prev_card->nextcard;
|
|
continue;
|
|
}
|
|
|
|
prev_card = c;
|
|
c = c->nextcard;
|
|
}
|
|
|
|
/* the terminating `.ends' deck wasn't found */
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
static void
|
|
inp_reorder_params(struct names *subckt_w_params, struct card *list_head)
|
|
{
|
|
struct card *first_param_card = NULL;
|
|
struct card *last_param_card = NULL;
|
|
|
|
struct card *prev_card = list_head;
|
|
struct card *c = prev_card->nextcard;
|
|
|
|
/* move .param lines to beginning of deck */
|
|
while (c != NULL) {
|
|
|
|
char *curr_line = c->line;
|
|
|
|
if (*curr_line == '*') {
|
|
prev_card = c;
|
|
c = c->nextcard;
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".subckt", curr_line)) {
|
|
prev_card = inp_reorder_params_subckt(subckt_w_params, c);
|
|
c = prev_card->nextcard;
|
|
continue;
|
|
}
|
|
|
|
/* check for an unexpected extra `.ends' deck */
|
|
if (ciprefix(".ends", curr_line)) {
|
|
fprintf(stderr, "Error: Unexpected extra .ends in line:\n %s.\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (ciprefix(".para", curr_line)) {
|
|
prev_card->nextcard = c->nextcard;
|
|
|
|
last_param_card = insert_deck(last_param_card, c);
|
|
|
|
if (!first_param_card)
|
|
first_param_card = last_param_card;
|
|
|
|
c = prev_card->nextcard;
|
|
continue;
|
|
}
|
|
|
|
prev_card = c;
|
|
c = c->nextcard;
|
|
}
|
|
|
|
inp_sort_params(first_param_card, list_head, list_head->nextcard, NULL);
|
|
}
|
|
|
|
|
|
// iterate through deck and find lines with multiply defined parameters
|
|
//
|
|
// split line up into multiple lines and place those new lines immediately
|
|
// afetr the current multi-param line in the deck
|
|
|
|
static int
|
|
inp_split_multi_param_lines(struct card *card, int line_num)
|
|
{
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->line;
|
|
|
|
if (*curr_line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".para", curr_line)) {
|
|
|
|
char *equal_ptr, **array;
|
|
int i, counter = 0;
|
|
|
|
while ((equal_ptr = find_assignment(curr_line)) != NULL) {
|
|
counter++;
|
|
curr_line = equal_ptr + 1;
|
|
}
|
|
|
|
if (counter <= 1)
|
|
continue;
|
|
|
|
array = TMALLOC(char *, counter);
|
|
|
|
// need to split multi param line
|
|
curr_line = card->line;
|
|
counter = 0;
|
|
while ((equal_ptr = find_assignment(curr_line)) != NULL) {
|
|
|
|
char *beg_param, *end_param;
|
|
|
|
bool get_expression = FALSE;
|
|
bool get_paren_expression = FALSE;
|
|
|
|
beg_param = skip_back_ws(equal_ptr, curr_line);
|
|
beg_param = skip_back_non_ws(beg_param, curr_line);
|
|
end_param = skip_ws(equal_ptr + 1);
|
|
while (*end_param != '\0' && (!isspace_c(*end_param) || get_expression || get_paren_expression)) {
|
|
if (*end_param == '{')
|
|
get_expression = TRUE;
|
|
if (*end_param == '(')
|
|
get_paren_expression = TRUE;
|
|
if (*end_param == '}')
|
|
get_expression = FALSE;
|
|
if (*end_param == ')')
|
|
get_paren_expression = FALSE;
|
|
end_param++;
|
|
}
|
|
|
|
if (end_param[-1] == ',')
|
|
end_param--;
|
|
|
|
array[counter++] = tprintf(".param %.*s", (int) (end_param - beg_param), beg_param);
|
|
|
|
curr_line = end_param;
|
|
}
|
|
|
|
// comment out current multi-param line
|
|
*(card->line) = '*';
|
|
// insert new param lines immediately after current line
|
|
for (i = 0; i < counter; i++)
|
|
card = insert_new_line(card, array[i], line_num++, 0);
|
|
|
|
tfree(array);
|
|
}
|
|
}
|
|
|
|
return line_num;
|
|
}
|
|
|
|
|
|
static int
|
|
identifier_char(char c)
|
|
{
|
|
return (c == '_') || isalnum_c(c);
|
|
}
|
|
|
|
|
|
static bool
|
|
b_transformation_wanted(const char *p)
|
|
{
|
|
const char *start = p;
|
|
|
|
for (p = start; (p = strpbrk(p, "vith")) != NULL; p++) {
|
|
if (p > start && identifier_char(p[-1]))
|
|
continue;
|
|
if (strncmp(p, "v(", 2) == 0 || strncmp(p, "i(", 2) == 0)
|
|
return TRUE;
|
|
if (strncmp(p, "temper", 6) == 0 && !identifier_char(p[6]))
|
|
return TRUE;
|
|
if (strncmp(p, "hertz", 5) == 0 && !identifier_char(p[5]))
|
|
return TRUE;
|
|
if (strncmp(p, "time", 4) == 0 && !identifier_char(p[4]))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
char *
|
|
search_identifier(char *str, const char *identifier, char *str_begin)
|
|
{
|
|
while ((str = strstr(str, identifier)) != NULL) {
|
|
char before;
|
|
|
|
if (str > str_begin)
|
|
before = str[-1];
|
|
else
|
|
before = '\0';
|
|
|
|
if (is_arith_char(before) || isspace_c(before) || strchr("=,{", before)) {
|
|
char after = str[strlen(identifier)];
|
|
if (is_arith_char(after) || isspace_c(after) || strchr(",}", after))
|
|
return str;
|
|
}
|
|
|
|
str++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char *
|
|
ya_search_identifier(char *str, const char *identifier, char *str_begin)
|
|
{
|
|
while ((str = strstr(str, identifier)) != NULL) {
|
|
char before;
|
|
|
|
if (str > str_begin)
|
|
before = str[-1];
|
|
else
|
|
before = '\0';
|
|
|
|
if (is_arith_char(before) || isspace_c(before) || (str <= str_begin)) {
|
|
char after = str[strlen(identifier)];
|
|
if ((is_arith_char(after) || isspace_c(after) || after == '\0'))
|
|
break;
|
|
}
|
|
|
|
str++;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
static char *
|
|
search_plain_identifier(char *str, const char *identifier)
|
|
{
|
|
char *str_begin = str;
|
|
|
|
while ((str = strstr(str, identifier)) != NULL) {
|
|
char before;
|
|
|
|
if (str > str_begin)
|
|
before = str[-1];
|
|
else
|
|
before = '\0';
|
|
|
|
if (!before || !identifier_char(before)) {
|
|
char after = str[strlen(identifier)];
|
|
if (!after || !identifier_char(after))
|
|
return str;
|
|
}
|
|
|
|
str += strlen(identifier);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* ps compatibility:
|
|
ECOMP 3 0 TABLE {V(1,2)} = (-1 0V) (1, 10V)
|
|
-->
|
|
ECOMP 3 0 int3 int0 1
|
|
BECOMP int3 int0 V = pwl(V(1,2), -2, 0, -1, 0 , 1, 10V, 2, 10V)
|
|
|
|
GD16 16 1 TABLE {V(16,1)} ((-100V,-1pV)(0,0)(1m,1u)(2m,1m))
|
|
-->
|
|
GD16 16 1 int_16 int_1 1
|
|
BGD16 int_16 int_1 V = pwl (v(16,1) , -100V , -1pV , 0 , 0 , 1m , 1u , 2m , 1m)
|
|
*/
|
|
|
|
/* hs compatibility:
|
|
Exxx n1 n2 VCVS n3 n4 gain --> Exxx n1 n2 n3 n4 gain
|
|
Gxxx n1 n2 VCCS n3 n4 tr --> Gxxx n1 n2 n3 n4 tr
|
|
|
|
Two step approach to keep the original names for reuse,
|
|
i.e. for current measurements like i(Exxx):
|
|
Exxx n1 n2 VOL = {equation}
|
|
-->
|
|
Exxx n1 n2 int1 0 1
|
|
BExxx int1 0 V = {equation}
|
|
|
|
Gxxx n1 n2 CUR = {equation}
|
|
-->
|
|
Gxxx n1 n2 int1 0 1
|
|
BGxxx int1 0 V = {equation}
|
|
|
|
Do the following transformations only if {equation} contains
|
|
simulation output like v(node), v(node1, node2), i(branch).
|
|
Otherwise let do numparam the substitutions (R=const is handled
|
|
in inp2r.c).
|
|
|
|
Rxxx n1 n2 R = {equation} or Rxxx n1 n2 {equation}
|
|
-->
|
|
BRxxx n1 n2 I = V(n1,n2)/{equation}
|
|
|
|
Unfortunately the capability for ac noise calculation of
|
|
resistance may be lost.
|
|
|
|
Cxxx n1 n2 C = {equation} or Cxxx n1 n2 {equation}
|
|
-->
|
|
Exxx n-aux 0 n1 n2 1
|
|
Cxxx n-aux 0 1
|
|
Bxxx n2 n1 I = i(Exxx) * equation
|
|
|
|
Lxxx n1 n2 L = {equation} or Lxxx n1 n2 {equation}
|
|
-->
|
|
Fxxx n-aux 0 Bxxx -1
|
|
Lxxx n-aux 0 1
|
|
Bxxx n1 n2 V = v(n-aux) * 1e-16
|
|
|
|
*/
|
|
|
|
static void
|
|
inp_compat(struct card *card)
|
|
{
|
|
char *str_ptr, *cut_line, *title_tok, *node1, *node2;
|
|
char *out_ptr, *exp_ptr, *beg_ptr, *end_ptr, *copy_ptr, *del_ptr;
|
|
char *xline, *x2line = NULL, *x3line = NULL, *x4line = NULL;
|
|
size_t xlen, i, pai = 0, paui = 0, ii;
|
|
char *ckt_array[100];
|
|
|
|
int skip_control = 0;
|
|
|
|
char *equation, *tc1_ptr = NULL, *tc2_ptr = NULL;
|
|
double tc1 = 0.0, tc2 = 0.0;
|
|
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->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;
|
|
}
|
|
|
|
if (*curr_line == '*')
|
|
continue;
|
|
|
|
if (*curr_line == 'e') {
|
|
/* Exxx n1 n2 VCVS n3 n4 gain --> Exxx n1 n2 n3 n4 gain
|
|
remove vcvs */
|
|
replace_token(curr_line, "vcvs", 4, 7);
|
|
|
|
/* Exxx n1 n2 value={equation}
|
|
-->
|
|
Exxx n1 n2 vol={equation} */
|
|
if ((str_ptr = search_plain_identifier(curr_line, "value")) != NULL) {
|
|
if (str_ptr[5] == '=')
|
|
*str_ptr++ = ' ';
|
|
strncpy(str_ptr, " vol=", 5);
|
|
}
|
|
/* Exxx n1 n2 TABLE {expression} = (x0, y0) (x1, y1) (x2, y2)
|
|
-->
|
|
Exxx n1 n2 int1 0 1
|
|
BExxx int1 0 V = pwl (expression, x0-(x2-x0)/2, y0, x0, y0, x1, y1, x2, y2, x2+(x2-x0)/2, y2)
|
|
*/
|
|
if ((str_ptr = strstr(curr_line, "table")) != NULL) {
|
|
char *expression, *firstno, *ffirstno, *secondno, *midline, *lastno, *lastlastno;
|
|
double fnumber, lnumber, delta;
|
|
int nerror;
|
|
cut_line = curr_line;
|
|
/* title and nodes */
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
// Exxx n1 n2 int1 0 1
|
|
ckt_array[0] = tprintf("%s %s %s %s_int1 0 1",
|
|
title_tok, node1, node2, title_tok);
|
|
// skip "table"
|
|
cut_line = skip_ws(cut_line);
|
|
if (ciprefix("table", cut_line)) {
|
|
/* a regular TABLE line */
|
|
cut_line += 5;
|
|
// compatibility, allow table = {expr} {pairs}
|
|
if (*cut_line == '=')
|
|
*cut_line++ = ' ';
|
|
// get the expression
|
|
str_ptr = gettok_char(&cut_line, '{', FALSE, FALSE);
|
|
expression = gettok_char(&cut_line, '}', TRUE, TRUE); /* expression */
|
|
if (!expression || !str_ptr) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
tfree(str_ptr);
|
|
/* remove '{' and '}' from expression */
|
|
if ((str_ptr = strchr(expression, '{')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(expression, '}')) != NULL)
|
|
*str_ptr = ' ';
|
|
/* cut_line may now have a '=', if yes, it will have '{' and '}'
|
|
(braces around token after '=') */
|
|
if ((str_ptr = strchr(cut_line, '=')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(cut_line, '{')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(cut_line, '}')) != NULL)
|
|
*str_ptr = ' ';
|
|
/* get first two numbers to establish extrapolation */
|
|
str_ptr = cut_line;
|
|
ffirstno = gettok_node(&cut_line);
|
|
if (!ffirstno) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
firstno = copy(ffirstno);
|
|
fnumber = INPevaluate(&ffirstno, &nerror, TRUE);
|
|
secondno = gettok_node(&cut_line);
|
|
midline = cut_line;
|
|
cut_line = strrchr(str_ptr, '(');
|
|
if (!cut_line) {
|
|
fprintf(stderr, "Error: bad syntax in line %d (missing parentheses)\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
/* replace '(' with ',' and ')' with ' ' */
|
|
for (; *str_ptr; str_ptr++)
|
|
if (*str_ptr == '(')
|
|
*str_ptr = ',';
|
|
else if (*str_ptr == ')')
|
|
*str_ptr = ' ';
|
|
/* scan for last two numbers */
|
|
lastno = gettok_node(&cut_line);
|
|
lnumber = INPevaluate(&lastno, &nerror, FALSE);
|
|
/* check for max-min and take half the difference for delta */
|
|
delta = (lnumber-fnumber)/2.;
|
|
lastlastno = gettok_node(&cut_line);
|
|
if (!secondno || (*midline == '\0') || (delta <= 0.) || !lastlastno) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 v = pwl(%s, %e, %s, %s, %s, %s, %e, %s)",
|
|
title_tok, title_tok, expression, fnumber-delta, secondno, firstno, secondno,
|
|
midline, lnumber + delta, lastlastno);
|
|
|
|
// comment out current variable e line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 2; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(firstno);
|
|
tfree(lastlastno);
|
|
|
|
}
|
|
else {
|
|
/* not used */
|
|
tfree(ckt_array[0]);
|
|
}
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
/* Exxx n1 n2 VOL = {equation}
|
|
-->
|
|
Exxx n1 n2 int1 0 1
|
|
BExxx int1 0 V = {equation}
|
|
*/
|
|
/* search for ' vol=' or ' vol =' */
|
|
if (((str_ptr = strchr(curr_line, '=')) != NULL) && prefix("vol", skip_back_non_ws(skip_back_ws(str_ptr, curr_line), curr_line))) {
|
|
cut_line = curr_line;
|
|
/* title and nodes */
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
/* Find equation, starts with '{', till end of line */
|
|
str_ptr = strchr(cut_line, '{');
|
|
if (str_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed E line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Exxx n1 n2 int1 0 1
|
|
ckt_array[0] = tprintf("%s %s %s %s_int1 0 1",
|
|
title_tok, node1, node2, title_tok);
|
|
// BExxx int1 0 V = {equation}
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 v = %s",
|
|
title_tok, title_tok, str_ptr);
|
|
|
|
// comment out current variable e line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 2; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
} else if (*curr_line == 'g') {
|
|
/* Gxxx n1 n2 VCCS n3 n4 tr --> Gxxx n1 n2 n3 n4 tr
|
|
remove vccs */
|
|
replace_token(curr_line, "vccs", 4, 7);
|
|
|
|
/* Gxxx n1 n2 value={equation}
|
|
-->
|
|
Gxxx n1 n2 cur={equation} */
|
|
if ((str_ptr = search_plain_identifier(curr_line, "value")) != NULL) {
|
|
if (str_ptr[5] == '=')
|
|
*str_ptr++ = ' ';
|
|
strncpy(str_ptr, " cur=", 5);
|
|
}
|
|
|
|
/* Gxxx n1 n2 TABLE {expression} = (x0, y0) (x1, y1) (x2, y2)
|
|
-->
|
|
Gxxx n1 n2 int1 0 1
|
|
BGxxx int1 0 V = pwl (expression, x0-(x2-x0)/2, y0, x0, y0, x1, y1, x2, y2, x2+(x2-x0)/2, y2)
|
|
*/
|
|
if ((str_ptr = strstr(curr_line, "table")) != NULL) {
|
|
char *expression, *firstno, *ffirstno, *ffirstnof, *secondno, *midline, *lastno, *lastnof, *lastlastno;
|
|
char *m_ptr, *m_token;
|
|
double fnumber, lnumber, delta;
|
|
int nerror;
|
|
cut_line = curr_line;
|
|
/* title and nodes */
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
// Gxxx n1 n2 int1 0 1
|
|
// or
|
|
// Gxxx n1 n2 int1 0 m='expr'
|
|
/* find multiplier m at end of line */
|
|
m_ptr = strstr(cut_line, "m=");
|
|
if (m_ptr) {
|
|
m_token = copy(m_ptr + 2); // get only the expression
|
|
*m_ptr = '\0';
|
|
}
|
|
else
|
|
m_token = copy("1");
|
|
ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s",
|
|
title_tok, node1, node2, title_tok, m_token);
|
|
// skip "table"
|
|
cut_line = skip_ws(cut_line);
|
|
if (!ciprefix("table", cut_line)) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
cut_line += 5;
|
|
// compatibility, allow table = {expr} {pairs}
|
|
if (*cut_line == '=')
|
|
*cut_line++ = ' ';
|
|
// get the expression
|
|
str_ptr = gettok_char(&cut_line, '{', FALSE, FALSE);
|
|
expression = gettok_char(&cut_line, '}', TRUE, TRUE); /* expression */
|
|
if (!expression || !str_ptr) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
tfree(str_ptr);
|
|
/* remove '{' and '}' from expression */
|
|
if ((str_ptr = strchr(expression, '{')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(expression, '}')) != NULL)
|
|
*str_ptr = ' ';
|
|
/* cut_line may now have a '=', if yes, it will have '{' and '}'
|
|
(braces around token after '=') */
|
|
if ((str_ptr = strchr(cut_line, '=')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(cut_line, '{')) != NULL)
|
|
*str_ptr = ' ';
|
|
if ((str_ptr = strchr(cut_line, '}')) != NULL)
|
|
*str_ptr = ' ';
|
|
/* get first two numbers to establish extrapolation */
|
|
str_ptr = cut_line;
|
|
ffirstnof = ffirstno = gettok_node(&cut_line);
|
|
if (!ffirstno) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
firstno = copy(ffirstno);
|
|
fnumber = INPevaluate(&ffirstno, &nerror, TRUE);
|
|
secondno = gettok_node(&cut_line);
|
|
midline = cut_line;
|
|
cut_line = strrchr(str_ptr, '(');
|
|
/* replace '(' with ',' and ')' with ' ' */
|
|
for (; *str_ptr; str_ptr++)
|
|
if (*str_ptr == '(')
|
|
*str_ptr = ',';
|
|
else if (*str_ptr == ')')
|
|
*str_ptr = ' ';
|
|
/* scan for last two numbers */
|
|
lastnof = lastno = gettok_node(&cut_line);
|
|
lnumber = INPevaluate(&lastno, &nerror, FALSE);
|
|
/* check for max-min and take half the difference for delta */
|
|
delta = (lnumber-fnumber)/2.;
|
|
lastlastno = gettok_node(&cut_line);
|
|
if (!secondno || (*midline == '\0') || (delta <= 0.) || !lastlastno) {
|
|
fprintf(stderr, "Error: bad syntax in line %d\n %s\n",
|
|
card->linenum_orig, card->line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
/* BGxxx int1 0 V = pwl (expression, x0-(x2-x0)/2, y0, x0, y0, x1, y1, x2, y2, x2+(x2-x0)/2, y2) */
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 v = pwl(%s, %e, %s, %s, %s, %s, %e, %s)",
|
|
title_tok, title_tok, expression, fnumber-delta, secondno, firstno, secondno,
|
|
midline, lnumber + delta, lastlastno);
|
|
|
|
// comment out current variable e line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 2; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(firstno);
|
|
tfree(ffirstnof);
|
|
tfree(secondno);
|
|
tfree(lastnof);
|
|
tfree(lastlastno);
|
|
tfree(expression);
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
tfree(m_token);
|
|
}
|
|
/*
|
|
Gxxx n1 n2 CUR = {equation}
|
|
-->
|
|
Gxxx n1 n2 int1 0 1
|
|
BGxxx int1 0 V = {equation}
|
|
*/
|
|
/* search for ' cur=' or ' cur =' */
|
|
if (((str_ptr = strchr(curr_line, '=')) != NULL) && prefix("cur", skip_back_non_ws(skip_back_ws(str_ptr, curr_line), curr_line))) {
|
|
char *m_ptr, *m_token;
|
|
cut_line = curr_line;
|
|
/* title and nodes */
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
/* Find equation, starts with '{', till end of line */
|
|
str_ptr = strchr(cut_line, '{');
|
|
if (str_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed G line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
/* find multiplier m at end of line */
|
|
m_ptr = strstr(cut_line, "m=");
|
|
if (m_ptr) {
|
|
m_token = copy(m_ptr + 2); //get only the expression
|
|
*m_ptr = '\0';
|
|
}
|
|
else
|
|
m_token = copy("1");
|
|
// Gxxx n1 n2 int1 0 1
|
|
// or
|
|
// Gxxx n1 n2 int1 0 m='expr'
|
|
ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s",
|
|
title_tok, node1, node2, title_tok, m_token);
|
|
// BGxxx int1 0 V = {equation}
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 v = %s",
|
|
title_tok, title_tok, str_ptr);
|
|
|
|
// comment out current variable g line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 2; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(m_token);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
}
|
|
|
|
/* F element compatibility */
|
|
else if (*curr_line == 'f') {
|
|
char *equastr, *vnamstr;
|
|
/* Fxxx n1 n2 CCCS vnam gain --> Fxxx n1 n2 vnam gain
|
|
remove cccs */
|
|
replace_token(curr_line, "cccs", 4, 6);
|
|
|
|
/* Deal with
|
|
Fxxx n1 n2 vnam {equation}
|
|
if equation contains the 'temper' token */
|
|
if (search_identifier(curr_line, "temper", curr_line)) {
|
|
cut_line = curr_line;
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
vnamstr = gettok(&cut_line);
|
|
equastr = gettok(&cut_line);
|
|
/*
|
|
Fxxx n1 n2 vnam {equation}
|
|
-->
|
|
Fxxx n1 n2 vbFxxx -1
|
|
bFxxx int1 0 i = i(vnam)*{equation}
|
|
vbFxxx int1 0 0
|
|
*/
|
|
//Fxxx n1 n2 VBFxxx -1
|
|
ckt_array[0] = tprintf("%s %s %s vb%s -1",
|
|
title_tok, node1, node2, title_tok);
|
|
//BFxxx BFxxx_int1 0 I = I(vnam)*{equation}
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 i = i(%s) * (%s)",
|
|
title_tok, title_tok, vnamstr, equastr);
|
|
//VBFxxx int1 0 0
|
|
ckt_array[2] = tprintf("vb%s %s_int1 0 dc 0",
|
|
title_tok, title_tok);
|
|
// comment out current variable f line
|
|
*(card->line) = '*';
|
|
// insert new three lines immediately after current line
|
|
for (i = 0; i < 3; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(vnamstr);
|
|
tfree(equastr);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
}
|
|
/* H element compatibility */
|
|
else if (*curr_line == 'h') {
|
|
char *equastr, *vnamstr;
|
|
/* Hxxx n1 n2 CCVS vnam transres --> Hxxx n1 n2 vnam transres
|
|
remove cccs */
|
|
replace_token(curr_line, "ccvs", 4, 6);
|
|
|
|
/* Deal with
|
|
Hxxx n1 n2 vnam {equation}
|
|
if equation contains the 'temper' token */
|
|
if (search_identifier(curr_line, "temper", curr_line)) {
|
|
cut_line = curr_line;
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
vnamstr = gettok(&cut_line);
|
|
equastr = gettok(&cut_line);
|
|
/*
|
|
Hxxx n1 n2 vnam {equation}
|
|
-->
|
|
Hxxx n1 n2 vbHxxx -1
|
|
bHxxx int1 0 i = i(vnam)*{equation}
|
|
vbHxxx int1 0 0
|
|
*/
|
|
//Hxxx n1 n2 VBHxxx -1
|
|
ckt_array[0] = tprintf("%s %s %s vb%s -1",
|
|
title_tok, node1, node2, title_tok);
|
|
//BHxxx BHxxx_int1 0 I = I(vnam)*{equation}
|
|
ckt_array[1] = tprintf("b%s %s_int1 0 i = i(%s) * (%s)",
|
|
title_tok, title_tok, vnamstr, equastr);
|
|
//VBHxxx int1 0 0
|
|
ckt_array[2] = tprintf("vb%s %s_int1 0 dc 0",
|
|
title_tok, title_tok);
|
|
// comment out current variable h line
|
|
*(card->line) = '*';
|
|
// insert new three lines immediately after current line
|
|
for (i = 0; i < 3; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(vnamstr);
|
|
tfree(equastr);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
}
|
|
|
|
/* Rxxx n1 n2 R = {equation} or Rxxx n1 n2 {equation}
|
|
-->
|
|
BRxxx pos neg I = V(pos, neg)/{equation}
|
|
*/
|
|
else if (*curr_line == 'r') {
|
|
cut_line = curr_line;
|
|
/* make BRxxx pos neg I = V(pos, neg)/{equation}*/
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
/* check only after skipping Rname and nodes, either may contain time (e.g. Rtime)*/
|
|
if (!b_transformation_wanted(cut_line)) {
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
continue;
|
|
}
|
|
|
|
/* Find equation, starts with '{', till end of line */
|
|
str_ptr = strchr(cut_line, '{');
|
|
if (str_ptr == NULL) {
|
|
/* if not, equation may start with a '(' */
|
|
str_ptr = strchr(cut_line, '(');
|
|
if (str_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed R line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
equation = gettok_char(&str_ptr, ')', TRUE, TRUE);
|
|
}
|
|
else
|
|
equation = gettok_char(&str_ptr, '}', TRUE, TRUE);
|
|
str_ptr = strstr(cut_line, "tc1");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc1=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc1_ptr = strchr(str_ptr, '=');
|
|
if (tc1_ptr) {
|
|
tc1_ptr++;
|
|
int error;
|
|
tc1 = INPevaluate(&tc1_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
str_ptr = strstr(cut_line, "tc2");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc2=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc2_ptr = strchr(str_ptr, '=');
|
|
if (tc2_ptr) {
|
|
tc2_ptr++;
|
|
int error;
|
|
tc2 = INPevaluate(&tc2_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* white noise model by x2line, x3line, x4line
|
|
if instance parameter noisy=1 (or noise=1) is set */
|
|
bool rnoise = FALSE;
|
|
if(strstr(cut_line, "noisy=1") || strstr(cut_line, "noise=1"))
|
|
rnoise = TRUE;
|
|
|
|
if ((tc1_ptr == NULL) && (tc2_ptr == NULL)) {
|
|
xline = tprintf("b%s %s %s i = v(%s, %s)/(%s)", title_tok, node1, node2,
|
|
node1, node2, equation);
|
|
if(rnoise){
|
|
x2line = tprintf("b%s_1 %s %s i = i(v%s_3)/sqrt(%s)",
|
|
title_tok, node1, node2,
|
|
title_tok,
|
|
equation);
|
|
x3line = tprintf("r%s_2 %s_3 0 1.0",
|
|
title_tok, title_tok);
|
|
x4line = tprintf("v%s_3 %s_3 0 0",
|
|
title_tok, title_tok);
|
|
}
|
|
} else if (tc2_ptr == NULL) {
|
|
xline = tprintf("b%s %s %s i = v(%s, %s)/(%s) tc1=%15.8e reciproctc=1", title_tok, node1, node2,
|
|
node1, node2, equation, tc1);
|
|
if(rnoise) {
|
|
x2line = tprintf("b%s_1 %s %s i = i(v%s_3)/sqrt(%s)",
|
|
title_tok, node1, node2,
|
|
title_tok,
|
|
equation);
|
|
x3line = tprintf("r%s_2 %s_3 0 1.0 tc1=%15.8e",
|
|
title_tok, title_tok, tc1);
|
|
x4line = tprintf("v%s_3 %s_3 0 0",
|
|
title_tok, title_tok);
|
|
}
|
|
} else {
|
|
xline = tprintf("b%s %s %s i = v(%s, %s)/(%s) tc1=%15.8e tc2=%15.8e reciproctc=1", title_tok, node1, node2,
|
|
node1, node2, equation, tc1, tc2);
|
|
if(rnoise) {
|
|
x2line = tprintf("b%s_1 %s %s i = i(v%s_3)/sqrt(%s)",
|
|
title_tok, node1, node2,
|
|
title_tok,
|
|
equation);
|
|
x3line = tprintf("r%s_2 %s_3 0 1.0 tc1=%15.8e tc2=%15.8e",
|
|
title_tok, title_tok, tc1, tc2);
|
|
x4line = tprintf("v%s_3 %s_3 0 0",
|
|
title_tok, title_tok);
|
|
}
|
|
}
|
|
tc1_ptr = NULL;
|
|
tc2_ptr = NULL;
|
|
|
|
// comment out current old R line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
card = insert_new_line(card, xline, 0, 0);
|
|
if (rnoise) {
|
|
card = insert_new_line(card, x2line, 0, 0);
|
|
card = insert_new_line(card, x3line, 0, 0);
|
|
card = insert_new_line(card, x4line, 0, 0);
|
|
}
|
|
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
tfree(equation);
|
|
}
|
|
/* Cxxx n1 n2 C = {equation} or Cxxx n1 n2 {equation}
|
|
-->
|
|
Exxx n-aux 0 n1 n2 1
|
|
Cxxx n-aux 0 1
|
|
Bxxx n2 n1 I = i(Exxx) * equation
|
|
*/
|
|
else if (*curr_line == 'c') {
|
|
cut_line = curr_line;
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
/* check only after skipping Cname and nodes, either may contain time (e.g. Ctime)*/
|
|
if (!b_transformation_wanted(cut_line)) {
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
continue;
|
|
}
|
|
|
|
/* Find equation, starts with '{', till end of line */
|
|
str_ptr = strchr(cut_line, '{');
|
|
if (str_ptr == NULL) {
|
|
/* if not, equation may start with a '(' */
|
|
str_ptr = strchr(cut_line, '(');
|
|
if (str_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed C line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
equation = gettok_char(&str_ptr, ')', TRUE, TRUE);
|
|
}
|
|
else
|
|
equation = gettok_char(&str_ptr, '}', TRUE, TRUE);
|
|
str_ptr = strstr(cut_line, "tc1");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc1=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc1_ptr = strchr(str_ptr, '=');
|
|
if (tc1_ptr) {
|
|
tc1_ptr++;
|
|
int error;
|
|
tc1 = INPevaluate(&tc1_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
str_ptr = strstr(cut_line, "tc2");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc2=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc2_ptr = strchr(str_ptr, '=');
|
|
if (tc2_ptr) {
|
|
tc2_ptr++;
|
|
int error;
|
|
tc2 = INPevaluate(&tc2_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
// Exxx n-aux 0 n1 n2 1
|
|
ckt_array[0] = tprintf("e%s %s_int2 0 %s %s 1",
|
|
title_tok, title_tok, node1, node2);
|
|
// Cxxx n-aux 0 1
|
|
ckt_array[1] = tprintf("c%s %s_int2 0 1", title_tok, title_tok);
|
|
// Bxxx n2 n1 I = i(Exxx) * equation
|
|
if ((tc1_ptr == NULL) && (tc2_ptr == NULL)) {
|
|
ckt_array[2] = tprintf("b%s %s %s i = i(e%s) * (%s)",
|
|
title_tok, node2, node1, title_tok, equation);
|
|
} else if (tc2_ptr == NULL) {
|
|
ckt_array[2] = tprintf("b%s %s %s i = i(e%s) * (%s) tc1=%15.8e reciproctc=1",
|
|
title_tok, node2, node1, title_tok, equation, tc1);
|
|
} else {
|
|
ckt_array[2] = tprintf("b%s %s %s i = i(e%s) * (%s) tc1=%15.8e tc2=%15.8e reciproctc=1",
|
|
title_tok, node2, node1, title_tok, equation, tc1, tc2);
|
|
}
|
|
tc1_ptr = NULL;
|
|
tc2_ptr = NULL;
|
|
// comment out current variable capacitor line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 3; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
tfree(equation);
|
|
}
|
|
|
|
/* Lxxx n1 n2 L = {equation} or Lxxx n1 n2 {equation}
|
|
-->
|
|
Fxxx n-aux 0 Bxxx -1
|
|
Lxxx n-aux 0 1
|
|
Bxxx n1 n2 V = v(n-aux) * equation
|
|
*/
|
|
else if (*curr_line == 'l') {
|
|
cut_line = curr_line;
|
|
/* title and nodes */
|
|
title_tok = gettok(&cut_line);
|
|
node1 = gettok(&cut_line);
|
|
node2 = gettok(&cut_line);
|
|
if (!b_transformation_wanted(cut_line)) {
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
continue;
|
|
}
|
|
|
|
/* Find equation, starts with '{', till end of line */
|
|
str_ptr = strchr(cut_line, '{');
|
|
if (str_ptr == NULL) {
|
|
/* if not, equation may start with a '(' */
|
|
str_ptr = strchr(cut_line, '(');
|
|
if (str_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed L line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
equation = gettok_char(&str_ptr, ')', TRUE, TRUE);
|
|
}
|
|
else
|
|
equation = gettok_char(&str_ptr, '}', TRUE, TRUE);
|
|
str_ptr = strstr(cut_line, "tc1");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc1=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc1_ptr = strchr(str_ptr, '=');
|
|
if (tc1_ptr) {
|
|
tc1_ptr++;
|
|
int error;
|
|
tc1 = INPevaluate(&tc1_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
str_ptr = strstr(cut_line, "tc2");
|
|
if (str_ptr) {
|
|
/* We need to have 'tc2=something */
|
|
if (str_ptr[3] && (isspace_c(str_ptr[3]) || (str_ptr[3] == '='))) {
|
|
tc2_ptr = strchr(str_ptr, '=');
|
|
if (tc2_ptr) {
|
|
tc2_ptr++;
|
|
int error;
|
|
tc2 = INPevaluate(&tc2_ptr, &error, 1);
|
|
}
|
|
}
|
|
}
|
|
// Fxxx n-aux 0 Bxxx 1
|
|
ckt_array[0] = tprintf("f%s %s_int2 0 b%s -1",
|
|
title_tok, title_tok, title_tok);
|
|
// Lxxx n-aux 0 1
|
|
ckt_array[1] = tprintf("l%s %s_int2 0 1", title_tok, title_tok);
|
|
// Bxxx n1 n2 V = v(n-aux) * equation
|
|
if ((tc1_ptr == NULL) && (tc2_ptr == NULL)) {
|
|
ckt_array[2] = tprintf("b%s %s %s v = v(%s_int2) * (%s)",
|
|
title_tok, node1, node2, title_tok, equation);
|
|
} else if (tc2_ptr == NULL) {
|
|
ckt_array[2] = tprintf("b%s %s %s v = v(%s_int2) * (%s) tc1=%15.8e reciproctc=0",
|
|
title_tok, node2, node1, title_tok, equation, tc1);
|
|
} else {
|
|
ckt_array[2] = tprintf("b%s %s %s v = v(%s_int2) * (%s) tc1=%15.8e tc2=%15.8e reciproctc=0",
|
|
title_tok, node2, node1, title_tok, equation, tc1, tc2);
|
|
}
|
|
tc1_ptr = NULL;
|
|
tc2_ptr = NULL;
|
|
// comment out current variable inductor line
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (i = 0; i < 3; i++)
|
|
card = insert_new_line(card, ckt_array[i], 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
tfree(equation);
|
|
}
|
|
/* .probe -> .save
|
|
.print, .plot, .save, .four,
|
|
An ouput vector may be replaced by the following:
|
|
myoutput=par('expression')
|
|
.meas
|
|
A vector out_variable may be replaced by
|
|
par('expression')
|
|
*/
|
|
else if (*curr_line == '.') {
|
|
// replace .probe by .save
|
|
if ((str_ptr = strstr(curr_line, ".probe")) != NULL)
|
|
memcpy(str_ptr, ".save ", 6);
|
|
|
|
/* Various formats for measure statement:
|
|
* .MEASURE {DC|AC|TRAN} result WHEN out_variable=val
|
|
* + <TD=td> <FROM=val> <TO=val>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result WHEN out_variable=out_variable2
|
|
* + <TD=td> <FROM=val> <TO=val>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=val
|
|
* + <TD=td> <FROM=val> <TO=val>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN out_variable2=out_variable3
|
|
* + <TD=td>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result FIND out_variable AT=val
|
|
* + <FROM=val> <TO=val>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result {AVG|MIN|MAX|MIN_AT|MAX_AT|PP|RMS} out_variable
|
|
* + <TD=td> <FROM=val> <TO=val>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result INTEG<RAL> out_variable
|
|
* + <TD=td> <FROM=val> <TO=val>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable AT=val
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable WHEN out_variable2=val
|
|
* + <TD=td>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
*
|
|
* .MEASURE {DC|AC|TRAN} result DERIV<ATIVE> out_variable WHEN out_variable2=out_variable3
|
|
* + <TD=td>
|
|
* + <CROSS=# | CROSS=LAST> <RISE=#|RISE=LAST> <FALL=#|FALL=LAST>
|
|
|
|
The user may set any out_variable to par(' expr ').
|
|
We have to replace this by v(pa_xx) and generate a B source line.
|
|
|
|
* ----------------------------------------------------------------- */
|
|
if (ciprefix(".meas", curr_line)) {
|
|
if (strstr(curr_line, "par(") == NULL)
|
|
continue;
|
|
cut_line = curr_line;
|
|
// search for 'par('
|
|
while ((str_ptr = strstr(cut_line, "par(")) != NULL) {
|
|
if (pai > 99) {
|
|
fprintf(stderr, "ERROR: More than 99 function calls to par()\n");
|
|
fprintf(stderr, " Limited to 99 per input file\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// we have ' par({ ... })', the right delimeter is a ' ' or '='
|
|
if (ciprefix(" par({", (str_ptr-1))) {
|
|
// find expression
|
|
beg_ptr = end_ptr = str_ptr + 5;
|
|
while ((*end_ptr != ' ') && (*end_ptr != '=') && (*end_ptr != '\0'))
|
|
end_ptr++;
|
|
exp_ptr = copy_substring(beg_ptr, end_ptr-2);
|
|
cut_line = str_ptr;
|
|
// generate node
|
|
out_ptr = tprintf("pa_%02d", (int)pai);
|
|
// Bout_ptr out_ptr 0 V = v(expr_ptr)
|
|
ckt_array[pai] = tprintf("b%s %s 0 v = %s",
|
|
out_ptr, out_ptr, exp_ptr);
|
|
ckt_array[++pai] = NULL;
|
|
// length of the replacement V(out_ptr)
|
|
del_ptr = copy_ptr = tprintf("v(%s)", out_ptr);
|
|
// length of the replacement part in original line
|
|
xlen = strlen(exp_ptr) + 7;
|
|
// copy the replacement without trailing '\0'
|
|
for (ii = 0; ii < xlen; ii++)
|
|
if (*copy_ptr)
|
|
*cut_line++ = *copy_ptr++;
|
|
else
|
|
*cut_line++ = ' ';
|
|
|
|
tfree(del_ptr);
|
|
tfree(exp_ptr);
|
|
tfree(out_ptr);
|
|
}
|
|
// or we have '={par({ ... })}', the right delimeter is a ' '
|
|
else if (ciprefix("={par({", (str_ptr-2))) {
|
|
// find expression
|
|
beg_ptr = end_ptr = str_ptr + 5;
|
|
while ((*end_ptr != ' ') && (*end_ptr != '\0'))
|
|
end_ptr++;
|
|
exp_ptr = copy_substring(beg_ptr, end_ptr-3);
|
|
// generate node
|
|
out_ptr = tprintf("pa_%02d", (int)pai);
|
|
// Bout_ptr out_ptr 0 V = v(expr_ptr)
|
|
ckt_array[pai] = tprintf("b%s %s 0 v = %s",
|
|
out_ptr, out_ptr, exp_ptr);
|
|
ckt_array[++pai] = NULL;
|
|
// length of the replacement V(out_ptr)
|
|
del_ptr = copy_ptr = tprintf("v(%s)", out_ptr);
|
|
// length of the replacement part in original line
|
|
xlen = strlen(exp_ptr) + 9;
|
|
// skip '='
|
|
cut_line++;
|
|
// copy the replacement without trailing '\0'
|
|
for (ii = 0; ii < xlen; ii++)
|
|
if (*copy_ptr)
|
|
*cut_line++ = *copy_ptr++;
|
|
else *cut_line++ = ' ';
|
|
|
|
tfree(del_ptr);
|
|
tfree(exp_ptr);
|
|
tfree(out_ptr);
|
|
} else {
|
|
// nothing to replace
|
|
cut_line = str_ptr + 1;
|
|
continue;
|
|
}
|
|
|
|
} // while 'par'
|
|
// no replacement done, go to next line
|
|
if (pai == paui)
|
|
continue;
|
|
// remove white spaces
|
|
card->line = inp_remove_ws(curr_line);
|
|
// insert new B source line immediately after current line
|
|
for (ii = paui; ii < pai; ii++)
|
|
card = insert_new_line(card, ckt_array[ii], 0, 0);
|
|
|
|
paui = pai;
|
|
} else if ((ciprefix(".save", curr_line)) ||
|
|
(ciprefix(".four", curr_line)) ||
|
|
(ciprefix(".print", curr_line)) ||
|
|
(ciprefix(".plot", curr_line)))
|
|
{
|
|
if (strstr(curr_line, "par(") == NULL)
|
|
continue;
|
|
cut_line = curr_line;
|
|
// search for 'par('
|
|
while ((str_ptr = strstr(cut_line, "par(")) != NULL) {
|
|
if (pai > 99) {
|
|
fprintf(stderr, "ERROR: More than 99 function calls to par()\n");
|
|
fprintf(stderr, " Limited to 99 per input file\n");
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// we have ' par({ ... })'
|
|
if (ciprefix(" par({", (str_ptr-1))) {
|
|
|
|
// find expression
|
|
beg_ptr = end_ptr = str_ptr + 5;
|
|
while ((*end_ptr != ' ') && (*end_ptr != '\0'))
|
|
end_ptr++;
|
|
exp_ptr = copy_substring(beg_ptr, end_ptr-2);
|
|
cut_line = str_ptr;
|
|
// generate node
|
|
out_ptr = tprintf("pa_%02d", (int)pai);
|
|
// Bout_ptr out_ptr 0 V = v(expr_ptr)
|
|
ckt_array[pai] = tprintf("b%s %s 0 v = %s",
|
|
out_ptr, out_ptr, exp_ptr);
|
|
ckt_array[++pai] = NULL;
|
|
// length of the replacement V(out_ptr)
|
|
del_ptr = copy_ptr = tprintf("%s", out_ptr);
|
|
// length of the replacement part in original line
|
|
xlen = strlen(exp_ptr) + 7;
|
|
// copy the replacement without trailing '\0'
|
|
for (ii = 0; ii < xlen; ii++)
|
|
if (*copy_ptr)
|
|
*cut_line++ = *copy_ptr++;
|
|
else
|
|
*cut_line++ = ' ';
|
|
|
|
tfree(del_ptr);
|
|
tfree(exp_ptr);
|
|
tfree(out_ptr);
|
|
}
|
|
// or we have '={par({ ... })}'
|
|
else if (ciprefix("={par({", (str_ptr-2))) {
|
|
|
|
// find myoutput
|
|
beg_ptr = end_ptr = str_ptr - 2;
|
|
while (*beg_ptr != ' ')
|
|
beg_ptr--;
|
|
out_ptr = copy_substring(beg_ptr + 1, end_ptr);
|
|
cut_line = beg_ptr + 1;
|
|
// find expression
|
|
beg_ptr = end_ptr = str_ptr + 5;
|
|
while ((*end_ptr != ' ') && (*end_ptr != '\0'))
|
|
end_ptr++;
|
|
exp_ptr = copy_substring(beg_ptr, end_ptr-3);
|
|
// Bout_ptr out_ptr 0 V = v(expr_ptr)
|
|
ckt_array[pai] = tprintf("b%s %s 0 v = %s",
|
|
out_ptr, out_ptr, exp_ptr);
|
|
ckt_array[++pai] = NULL;
|
|
// length of the replacement V(out_ptr)
|
|
del_ptr = copy_ptr = tprintf("%s", out_ptr);
|
|
// length of the replacement part in original line
|
|
xlen = strlen(out_ptr) + strlen(exp_ptr) + 10;
|
|
// copy the replacement without trailing '\0'
|
|
for (ii = 0; ii < xlen; ii++)
|
|
if (*copy_ptr)
|
|
*cut_line++ = *copy_ptr++;
|
|
else *cut_line++ = ' ';
|
|
|
|
tfree(del_ptr);
|
|
tfree(exp_ptr);
|
|
tfree(out_ptr);
|
|
}
|
|
// nothing to replace
|
|
else
|
|
cut_line = str_ptr + 1;
|
|
} // while 'par('
|
|
// no replacement done, go to next line
|
|
if (pai == paui)
|
|
continue;
|
|
// remove white spaces
|
|
card->line = inp_remove_ws(curr_line);
|
|
// comment out current variable capacitor line
|
|
// *(ckt_array[0]) = '*';
|
|
// insert new B source line immediately after current line
|
|
for (ii = paui; ii < pai; ii++)
|
|
card = insert_new_line(card, ckt_array[ii], 0, 0);
|
|
|
|
paui = pai;
|
|
// continue;
|
|
} // if .print etc.
|
|
} // if ('.')
|
|
}
|
|
}
|
|
|
|
|
|
/* replace a token (length 4 char) in string by spaces, if it is found
|
|
at the correct position and the total number of tokens is o.k. */
|
|
|
|
static void
|
|
replace_token(char *string, char *token, int wherereplace, int total)
|
|
{
|
|
int count = 0, i;
|
|
char *actstring = string;
|
|
|
|
/* token to be replaced not in string */
|
|
if (strstr(string, token) == NULL)
|
|
return;
|
|
|
|
/* get total number of tokens */
|
|
while (*actstring) {
|
|
actstring = nexttok(actstring);
|
|
count++;
|
|
}
|
|
/* If total number of tokens correct */
|
|
if (count == total) {
|
|
actstring = string;
|
|
for (i = 1; i < wherereplace; i++)
|
|
actstring = nexttok(actstring);
|
|
/* If token to be replaced at right position */
|
|
if (ciprefix(token, actstring)) {
|
|
actstring[0] = ' ';
|
|
actstring[1] = ' ';
|
|
actstring[2] = ' ';
|
|
actstring[3] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* lines for B sources (except for pwl lines): no parsing in numparam code,
|
|
just replacement of parameters. pwl lines are still handled in numparam.
|
|
Parsing for all other B source lines are done in the B source parser.
|
|
To achive this, do the following:
|
|
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 void
|
|
inp_bsource_compat(struct card *card)
|
|
{
|
|
char *equal_ptr, *str_ptr, *new_str, *final_str;
|
|
int skip_control = 0;
|
|
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->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;
|
|
}
|
|
|
|
if (*curr_line == 'b') {
|
|
/* remove white spaces of everything inside {}*/
|
|
card->line = inp_remove_ws(card->line);
|
|
curr_line = card->line;
|
|
/* exclude special pwl lines */
|
|
if (strstr(curr_line, "=pwl("))
|
|
continue;
|
|
/* store starting point for later parsing, beginning of {expression} */
|
|
equal_ptr = strchr(curr_line, '=');
|
|
/* check for errors */
|
|
if (equal_ptr == NULL) {
|
|
fprintf(stderr, "ERROR: mal formed B line: %s\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
/* find the m={m} token and remove it */
|
|
if ((str_ptr = strstr(curr_line, "m={m}")) != NULL)
|
|
memcpy(str_ptr, " ", 5);
|
|
new_str = inp_modify_exp(equal_ptr + 1);
|
|
final_str = tprintf("%.*s %s", (int) (equal_ptr + 1 - curr_line), curr_line, new_str);
|
|
|
|
// comment out current line (old B source line)
|
|
*(card->line) = '*';
|
|
// insert new B source line immediately after current line
|
|
/* Copy old line numbers into new B source line */
|
|
card = insert_new_line(card, final_str, card->linenum, card->linenum_orig);
|
|
|
|
tfree(new_str);
|
|
} /* end of if 'b' */
|
|
} /* end of for loop */
|
|
}
|
|
|
|
|
|
/* Find all expressions 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->line
|
|
*/
|
|
|
|
static bool
|
|
inp_temper_compat(struct card *card)
|
|
{
|
|
int skip_control = 0;
|
|
char *beg_str, *end_str, *beg_tstr, *end_tstr, *exp_str;
|
|
|
|
bool with_temper = FALSE;
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *new_str = NULL;
|
|
char *curr_line = card->line;
|
|
|
|
if (curr_line == NULL)
|
|
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 some elements */
|
|
if (strchr("*vbiegfh", curr_line[0]))
|
|
continue;
|
|
/* exclude all dot commands except .model */
|
|
if (curr_line[0] == '.' && !prefix(".model", curr_line))
|
|
continue;
|
|
/* exclude lines not containing 'temper' */
|
|
if (!strstr(curr_line, "temper"))
|
|
continue;
|
|
/* now start processing of the remaining lines containing 'temper' */
|
|
/* remove white spaces of everything inside {}*/
|
|
card->line = inp_remove_ws(card->line);
|
|
curr_line = card->line;
|
|
|
|
beg_str = beg_tstr = curr_line;
|
|
while ((beg_tstr = search_identifier(beg_tstr, "temper", curr_line)) != NULL) {
|
|
char *modified_exp;
|
|
/* set the global variable */
|
|
with_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 */
|
|
modified_exp = inp_modify_exp(exp_str);
|
|
tfree(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, ' ', modified_exp);
|
|
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->line);
|
|
card->line = inp_remove_ws(new_str);
|
|
}
|
|
return with_temper;
|
|
}
|
|
|
|
|
|
/* 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 *s;
|
|
wordlist *wl = NULL, *wlist = NULL;
|
|
|
|
/* scan the expression and remove all '{' and '}' */
|
|
for (s = expr; *s; s++)
|
|
if ((*s == '{') || (*s == '}'))
|
|
*s = ' ';
|
|
|
|
/* scan the expression */
|
|
s = expr;
|
|
while (*(s = skip_ws(s))) {
|
|
|
|
static bool c_arith_prev = FALSE;
|
|
bool c_arith = FALSE;
|
|
char c_prev = '\0';
|
|
char c = *s;
|
|
|
|
wl_append_word(&wlist, &wl, NULL);
|
|
|
|
if ((c == ',') || (c == '(') || (c == ')') ||
|
|
(c == '*') || (c == '/') || (c == '^') ||
|
|
(c == '+') || (c == '?') || (c == ':') ||
|
|
(c == '-'))
|
|
{
|
|
if ((c == '*') && (s[1] == '*')) {
|
|
wl->wl_word = tprintf("**");
|
|
s += 2;
|
|
} else if (c == '-' && c_arith_prev && c_prev != ')') {
|
|
/* enter whole number string if '-' is a sign */
|
|
int error1;
|
|
/* allow 100p, 5MEG etc. */
|
|
double dvalue = INPevaluate(&s, &error1, 0);
|
|
if(error1) {
|
|
wl->wl_word = tprintf("%c", c);
|
|
s++;
|
|
}
|
|
else {
|
|
wl->wl_word = tprintf("%18.10e", dvalue);
|
|
/* skip the `unit', FIXME INPevaluate() should do this */
|
|
while (isalpha_c(*s))
|
|
s++;
|
|
}
|
|
} else {
|
|
wl->wl_word = tprintf("%c", c);
|
|
s++;
|
|
}
|
|
c_arith = TRUE;
|
|
} else if ((c == '>') || (c == '<') ||
|
|
(c == '!') || (c == '='))
|
|
{
|
|
/* >=, <=, !=, ==, <>, ... */
|
|
char *beg = s++;
|
|
if ((*s == '=') || (*s == '<') || (*s == '>'))
|
|
s++;
|
|
wl->wl_word = copy_substring(beg, s);
|
|
} else if ((c == '|') || (c == '&')) {
|
|
char *beg = s++;
|
|
if ((*s == '|') || (*s == '&'))
|
|
s++;
|
|
wl->wl_word = copy_substring(beg, s);
|
|
} else if (isalpha_c(c) || c == '_') {
|
|
|
|
char buf[512];
|
|
int i = 0;
|
|
|
|
if (((c == 'v') || (c == 'i')) && (s[1] == '(')) {
|
|
while (*s != ')')
|
|
buf[i++] = *s++;
|
|
buf[i++] = *s++;
|
|
buf[i] = '\0';
|
|
wl->wl_word = copy(buf);
|
|
} else {
|
|
while (isalnum_c(*s) ||
|
|
(*s == '!') || (*s == '#') ||
|
|
(*s == '$') || (*s == '%') ||
|
|
(*s == '_') || (*s == '[') ||
|
|
(*s == ']'))
|
|
{
|
|
buf[i++] = *s++;
|
|
}
|
|
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 ((*s == '(') ||
|
|
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"))
|
|
{
|
|
s = skip_ws(s);
|
|
/* no {} around tc1 = or tc2 = , these are temp coeffs. */
|
|
if (s[0] == '=' && s[1] != '=') {
|
|
buf[i++] = '=';
|
|
buf[i] = '\0';
|
|
s++;
|
|
wl->wl_word = copy(buf);
|
|
} else {
|
|
wl->wl_word = tprintf("({%s})", buf);
|
|
}
|
|
/* '-' following the '=' is attached to number as its sign */
|
|
c_arith = TRUE;
|
|
} else {
|
|
/* {} around all other tokens */
|
|
wl->wl_word = tprintf("({%s})", buf);
|
|
}
|
|
}
|
|
} else if (isdigit_c(c) || (c == '.')) { /* allow .5 format too */
|
|
int error1;
|
|
/* allow 100p, 5MEG etc. */
|
|
double dvalue = INPevaluate(&s, &error1, 0);
|
|
wl->wl_word = tprintf("%18.10e", dvalue);
|
|
/* skip the `unit', FIXME INPevaluate() should do this */
|
|
while (isalpha_c(*s))
|
|
s++;
|
|
} else { /* strange char */
|
|
printf("Preparing expression for numparam\nWhat is this?\n%s\n", s);
|
|
wl->wl_word = tprintf("%c", *s++);
|
|
}
|
|
c_prev = c;
|
|
c_arith_prev = c_arith;
|
|
}
|
|
|
|
expr = wl_flatten(wlist);
|
|
wl_free(wlist);
|
|
|
|
return expr;
|
|
}
|
|
|
|
|
|
/*
|
|
* destructively fetch a token from the input string
|
|
* token is either quoted, or a plain nonwhitespace sequence
|
|
* function will return the place from where to continue
|
|
*/
|
|
|
|
static char *
|
|
get_quoted_token(char *string, char **token)
|
|
{
|
|
char *s = skip_ws(string);
|
|
|
|
if (!*s) /* nothing found */
|
|
return string;
|
|
|
|
if (isquote(*s)) {
|
|
|
|
char *t = ++s;
|
|
|
|
while (*t && !isquote(*t))
|
|
t++;
|
|
|
|
if (!*t) { /* teriminator quote not found */
|
|
*token = NULL;
|
|
return string;
|
|
}
|
|
|
|
*t++ = '\0';
|
|
|
|
*token = s;
|
|
return t;
|
|
|
|
} else {
|
|
|
|
char *t = skip_non_ws(s);
|
|
|
|
if (t == s) { /* nothing found */
|
|
*token = NULL;
|
|
return string;
|
|
}
|
|
|
|
if (*t)
|
|
*t++ = '\0';
|
|
|
|
*token = s;
|
|
return t;
|
|
}
|
|
}
|
|
|
|
|
|
/* Option RSERIES=rval
|
|
* Lxxx n1 n2 Lval
|
|
* -->
|
|
* Lxxx n1 n2_intern__ Lval
|
|
* RLxxx_n2_intern__ n2_intern__ n2 rval
|
|
*/
|
|
|
|
static void
|
|
inp_add_series_resistor(struct card *deck)
|
|
{
|
|
int skip_control = 0;
|
|
struct card *card;
|
|
char *rval = NULL;
|
|
|
|
for (card = deck; card; card = card->nextcard) {
|
|
char *curr_line = card->line;
|
|
if (*curr_line != '*' && strstr(curr_line, "option")) {
|
|
char *t = strstr(curr_line, "rseries");
|
|
if (t) {
|
|
tfree(rval);
|
|
|
|
t += 7;
|
|
if (*t++ == '=')
|
|
rval = gettok(&t);
|
|
|
|
/* default to "1e-3" if no value given */
|
|
if (!rval)
|
|
rval = copy("1e-3");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rval)
|
|
return;
|
|
|
|
fprintf(stdout,
|
|
"\nOption rseries given: \n"
|
|
"resistor %s Ohms added in series to each inductor L\n\n", rval);
|
|
|
|
for (card = deck; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
|
|
/* exclude any command inside .control ... .endc */
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control ++;
|
|
continue;
|
|
} else if (ciprefix(".endc", cut_line)) {
|
|
skip_control --;
|
|
continue;
|
|
} else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix("l", cut_line)) {
|
|
|
|
char *title_tok = gettok(&cut_line);
|
|
char *node1 = gettok(&cut_line);
|
|
char *node2 = gettok(&cut_line);
|
|
|
|
/* new L line and new R line */
|
|
char *newL = tprintf("%s %s %s_intern__ %s", title_tok, node1, title_tok, cut_line);
|
|
char *newR = tprintf("R%s_intern__ %s_intern__ %s %s", title_tok, title_tok, node2, rval);
|
|
|
|
// comment out current L line
|
|
*(card->line) = '*';
|
|
|
|
// insert new new L and R lines immediately after current line
|
|
card = insert_new_line(card, newL, 0, 0);
|
|
card = insert_new_line(card, newR, 0, 0);
|
|
|
|
tfree(title_tok);
|
|
tfree(node1);
|
|
tfree(node2);
|
|
}
|
|
}
|
|
|
|
tfree(rval);
|
|
}
|
|
|
|
|
|
/*
|
|
* rewrite
|
|
* .subckt node1 node2 node3 name params: l={x} w={y}
|
|
* to
|
|
* .subckt node1 node2 node3 name
|
|
* .param l={x} w={y}
|
|
*/
|
|
|
|
static void
|
|
subckt_params_to_param(struct card *card)
|
|
{
|
|
for (; card; card = card->nextcard) {
|
|
char *curr_line = card->line;
|
|
if (ciprefix(".subckt", curr_line)) {
|
|
char *cut_line, *new_line;
|
|
cut_line = strstr(curr_line, "params:");
|
|
if (!cut_line)
|
|
continue;
|
|
/* new_line starts with "params: " */
|
|
new_line = copy(cut_line);
|
|
/* replace "params:" by ".param " */
|
|
memcpy(new_line, ".param ", 7);
|
|
/* card->line ends with subcircuit name */
|
|
cut_line[-1] = '\0';
|
|
/* insert new_line after card->line */
|
|
insert_new_line(card, new_line, card->linenum + 1, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* If XSPICE option is not selected, run this function to alert and exit
|
|
if the 'poly' option is found in e, g, f, or h controlled sources. */
|
|
|
|
#ifndef XSPICE
|
|
|
|
static void
|
|
inp_poly_err(struct card *card)
|
|
{
|
|
size_t skip_control = 0;
|
|
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->line;
|
|
|
|
if (*curr_line == '*')
|
|
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;
|
|
}
|
|
|
|
/* get the fourth token in a controlled source line and exit,
|
|
if it is 'poly' */
|
|
if ((ciprefix("e", curr_line)) || (ciprefix("g", curr_line)) ||
|
|
(ciprefix("f", curr_line)) || (ciprefix("h", curr_line)))
|
|
{
|
|
curr_line = nexttok(curr_line);
|
|
curr_line = nexttok(curr_line);
|
|
curr_line = nexttok(curr_line);
|
|
if (ciprefix("poly", curr_line)) {
|
|
fprintf(stderr,
|
|
"\nError: XSPICE is required to run the 'poly' option in line %d\n",
|
|
card->linenum_orig);
|
|
fprintf(stderr, " %s\n", card->line);
|
|
fprintf(stderr, "\nSee manual chapt. 31 for installation instructions\n");
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* Used for debugging. You may add
|
|
* tprint(working);
|
|
* somewhere in function inp_readall() of this file to have
|
|
* a printout of the actual deck written to file "tprint-out.txt" */
|
|
void
|
|
tprint(struct card *t)
|
|
{
|
|
struct card *tmp;
|
|
|
|
/*debug: print into file*/
|
|
FILE *fd = fopen("tprint-out.txt", "w");
|
|
for (tmp = t; tmp; tmp = tmp->nextcard)
|
|
if (*(tmp->line) != '*')
|
|
fprintf(fd, "%6d %6d %s\n", tmp->linenum_orig, tmp->linenum, tmp->line);
|
|
fprintf(fd, "\n*********************************************************************************\n");
|
|
fprintf(fd, "*********************************************************************************\n");
|
|
fprintf(fd, "*********************************************************************************\n\n");
|
|
for (tmp = t; tmp; tmp = tmp->nextcard)
|
|
fprintf(fd, "%6d %6d %s\n", tmp->linenum_orig, tmp->linenum, tmp->line);
|
|
fprintf(fd, "\n*********************************************************************************\n");
|
|
fprintf(fd, "*********************************************************************************\n");
|
|
fprintf(fd, "*********************************************************************************\n\n");
|
|
for (tmp = t; tmp; tmp = tmp->nextcard)
|
|
if (*(tmp->line) != '*')
|
|
fprintf(fd, "%s\n",tmp->line);
|
|
fclose(fd);
|
|
}
|
|
|
|
|
|
/* prepare .if and .elseif for numparam
|
|
.if(expression) --> .if{expression} */
|
|
|
|
static void
|
|
inp_dot_if(struct card *card)
|
|
{
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->line;
|
|
|
|
if (*curr_line == '*')
|
|
continue;
|
|
|
|
if (ciprefix(".if", curr_line) || ciprefix(".elseif", curr_line)) {
|
|
char *firstbr = strchr(curr_line, '(');
|
|
char *lastbr = strrchr(curr_line, ')');
|
|
if ((!firstbr) || (!lastbr)) {
|
|
fprintf(cp_err, "Error in netlist line %d\n", card->linenum_orig);
|
|
fprintf(cp_err, " Bad syntax: %s\n\n", curr_line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
*firstbr = '{';
|
|
*lastbr = '}';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* 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 char *inp_functionalise_identifier(char *curr_line, char *identifier);
|
|
|
|
static void
|
|
inp_fix_temper_in_param(struct card *deck)
|
|
{
|
|
int skip_control = 0, subckt_depth = 0, j, *sub_count;
|
|
char *funcbody, *funcname;
|
|
struct func_temper *f, *funcs = NULL, **funcs_tail_ptr = &funcs;
|
|
struct card *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->nextcard) {
|
|
|
|
char *curr_line = card->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(".para", curr_line)) {
|
|
|
|
char *p, *temper, *equal_ptr, *lhs_b, *lhs_e;
|
|
|
|
temper = search_identifier(curr_line, "temper", curr_line);
|
|
|
|
if (!temper)
|
|
continue;
|
|
|
|
equal_ptr = find_assignment(curr_line);
|
|
|
|
if (!equal_ptr) {
|
|
fprintf(stderr, "ERROR: could not find '=' on parameter line '%s'!\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* .param lines with `,' separated multiple parameters
|
|
* must have been split in inp_split_multi_param_lines()
|
|
*/
|
|
|
|
if (find_assignment(equal_ptr + 1)) {
|
|
fprintf(stderr, "ERROR: internal error on line '%s'!\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
lhs_b = skip_non_ws(curr_line); // eat .param
|
|
lhs_b = skip_ws(lhs_b);
|
|
|
|
lhs_e = skip_back_ws(equal_ptr, curr_line);
|
|
|
|
/* skip if this is a function already */
|
|
p = strpbrk(lhs_b, "(,)");
|
|
if (p && p < lhs_e)
|
|
continue;
|
|
|
|
if (temper < equal_ptr) {
|
|
fprintf(stderr,
|
|
"Error: you cannot assign a value to TEMPER\n"
|
|
" Line no. %d, %s\n",
|
|
card->linenum, curr_line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
|
|
funcname = copy_substring(lhs_b, lhs_e);
|
|
funcbody = copy(equal_ptr + 1);
|
|
|
|
*funcs_tail_ptr =
|
|
inp_new_func(funcname, funcbody, card, sub_count, subckt_depth);
|
|
funcs_tail_ptr = & (*funcs_tail_ptr)->next;
|
|
|
|
tfree(funcbody);
|
|
}
|
|
}
|
|
|
|
/* second pass */
|
|
/* for each .func entry in `funcs' 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().
|
|
if the replacement is done in a .param line then
|
|
convert it to a .func line and append an entry to `funcs'.
|
|
Continue up to the very end of `funcs'.
|
|
*/
|
|
|
|
for (f = funcs; f; f = f->next) {
|
|
|
|
for(j = 0; j < 16; j++)
|
|
sub_count[j] = 0;
|
|
|
|
card = deck;
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *new_str = NULL; /* string we assemble here */
|
|
char *curr_line = card->line;
|
|
char *firsttok_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 f */
|
|
if (subckt_depth != f->subckt_depth)
|
|
continue;
|
|
if (sub_count[subckt_depth] != f->subckt_count)
|
|
continue;
|
|
|
|
/* remove first token, ignore it here, restore it later */
|
|
firsttok_str = gettok(&curr_line);
|
|
if (*curr_line == '\0') {
|
|
tfree(firsttok_str);
|
|
continue;
|
|
}
|
|
|
|
new_str = inp_functionalise_identifier(curr_line, f->funcname);
|
|
|
|
if (new_str == curr_line) {
|
|
tfree(firsttok_str);
|
|
continue;
|
|
}
|
|
|
|
/* restore first part of the line */
|
|
new_str = INPstrCat(firsttok_str, ' ', new_str);
|
|
new_str = inp_remove_ws(new_str);
|
|
|
|
/* if we have inserted into a .param line, convert to .func */
|
|
if (prefix(".para", new_str)) {
|
|
char *new_tmp_str = new_str;
|
|
new_tmp_str = nexttok(new_tmp_str);
|
|
funcname = gettok_char(&new_tmp_str, '=', FALSE, FALSE);
|
|
funcbody = copy(new_tmp_str + 1);
|
|
*funcs_tail_ptr =
|
|
inp_new_func(funcname, funcbody, card, sub_count, subckt_depth);
|
|
funcs_tail_ptr = & (*funcs_tail_ptr)->next;
|
|
tfree(new_str);
|
|
tfree(funcbody);
|
|
} else {
|
|
/* Or just enter new line into deck */
|
|
insert_new_line(card, new_str, 0, card->linenum);
|
|
*card->line = '*';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* final memory clearance */
|
|
tfree(sub_count);
|
|
inp_delete_funcs(funcs);
|
|
}
|
|
|
|
|
|
/* Convert .param lines containing function 'agauss' and others
|
|
* (function name handed over by *fcn), into .func lines:
|
|
* .param xxx1 = 'aunif()' ---> .func xxx1() 'aunif()'
|
|
* 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()
|
|
*
|
|
* In a second step, after subcircuits have been expanded, all occurencies
|
|
* of agauss in a b-line are replaced by their suitable value (function
|
|
* eval_agauss() in inp.c).
|
|
*/
|
|
|
|
static void
|
|
inp_fix_agauss_in_param(struct card *deck, char *fcn)
|
|
{
|
|
int skip_control = 0, subckt_depth = 0, j, *sub_count;
|
|
char *funcbody, *funcname;
|
|
struct func_temper *f, *funcs = NULL, **funcs_tail_ptr = &funcs;
|
|
struct card *card;
|
|
|
|
sub_count = TMALLOC(int, 16);
|
|
for (j = 0; j < 16; j++)
|
|
sub_count[j] = 0;
|
|
|
|
/* first pass:
|
|
* determine all .param with agauss inside and replace by .func
|
|
* convert
|
|
* .param xxx1 = 'agauss(x,y,z) * 25'
|
|
* to
|
|
* .func xxx1() 'agauss(x,y,z) * 25'
|
|
*/
|
|
card = deck;
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->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(".para", curr_line)) {
|
|
|
|
char *p, *temper, *equal_ptr, *lhs_b, *lhs_e;
|
|
|
|
temper = search_identifier(curr_line, fcn, curr_line);
|
|
|
|
if (!temper)
|
|
continue;
|
|
|
|
equal_ptr = find_assignment(curr_line);
|
|
|
|
if (!equal_ptr) {
|
|
fprintf(stderr, "ERROR: could not find '=' on parameter line '%s'!\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* .param lines with `,' separated multiple parameters
|
|
* must have been split in inp_split_multi_param_lines()
|
|
*/
|
|
|
|
if (find_assignment(equal_ptr + 1)) {
|
|
fprintf(stderr, "ERROR: internal error on line '%s'!\n", curr_line);
|
|
controlled_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
lhs_b = skip_non_ws(curr_line); // eat .param
|
|
lhs_b = skip_ws(lhs_b);
|
|
|
|
lhs_e = skip_back_ws(equal_ptr, curr_line);
|
|
|
|
/* skip if this is a function already */
|
|
p = strpbrk(lhs_b, "(,)");
|
|
if (p && p < lhs_e)
|
|
continue;
|
|
|
|
if (temper < equal_ptr) {
|
|
fprintf(stderr,
|
|
"Error: you cannot assign a value to %s\n"
|
|
" Line no. %d, %s\n",
|
|
fcn, card->linenum, curr_line);
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
|
|
funcname = copy_substring(lhs_b, lhs_e);
|
|
funcbody = copy(equal_ptr + 1);
|
|
|
|
*funcs_tail_ptr =
|
|
inp_new_func(funcname, funcbody, card, sub_count, subckt_depth);
|
|
funcs_tail_ptr = &(*funcs_tail_ptr)->next;
|
|
|
|
tfree(funcbody);
|
|
}
|
|
}
|
|
|
|
/* second pass:
|
|
* for each .func entry in `funcs' 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().
|
|
*/
|
|
|
|
for (f = funcs; f; f = f->next) {
|
|
|
|
for (j = 0; j < 16; j++)
|
|
sub_count[j] = 0;
|
|
|
|
card = deck;
|
|
for (; card; card = card->nextcard) {
|
|
|
|
char *new_str = NULL; /* string we assemble here */
|
|
char *curr_line = card->line;
|
|
char *firsttok_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;
|
|
}
|
|
|
|
/* if function is not at top level,
|
|
exclude lines which do not have the same subcircuit
|
|
nesting depth and number as found in f */
|
|
if (f->subckt_depth > 0) {
|
|
if (subckt_depth != f->subckt_depth)
|
|
continue;
|
|
if (sub_count[subckt_depth] != f->subckt_count)
|
|
continue;
|
|
}
|
|
|
|
/* remove first token, ignore it here, restore it later */
|
|
firsttok_str = gettok(&curr_line);
|
|
if (*curr_line == '\0') {
|
|
tfree(firsttok_str);
|
|
continue;
|
|
}
|
|
|
|
new_str = inp_functionalise_identifier(curr_line, f->funcname);
|
|
|
|
if (new_str == curr_line) {
|
|
tfree(firsttok_str);
|
|
continue;
|
|
}
|
|
|
|
/* restore first part of the line */
|
|
new_str = INPstrCat(firsttok_str, ' ', new_str);
|
|
new_str = inp_remove_ws(new_str);
|
|
|
|
*card->line = '*';
|
|
/* Enter new line into deck */
|
|
insert_new_line(card, new_str, 0, card->linenum);
|
|
}
|
|
}
|
|
/* final memory clearance */
|
|
tfree(sub_count);
|
|
inp_delete_funcs(funcs);
|
|
}
|
|
|
|
|
|
/* append "()" to each 'identifier' in 'curr_line',
|
|
* unless already there */
|
|
static char *
|
|
inp_functionalise_identifier(char *curr_line, char *identifier)
|
|
{
|
|
size_t len = strlen(identifier);
|
|
char *p, *str = curr_line;
|
|
|
|
for (p = str; (p = search_identifier(p, identifier, str)) != NULL; )
|
|
if (p[len] != '(') {
|
|
int prefix_len = (int) (p + len - str);
|
|
char *x = str;
|
|
str = tprintf("%.*s()%s", prefix_len, str, str + prefix_len);
|
|
if (x != curr_line)
|
|
tfree(x);
|
|
p = str + prefix_len + 2;
|
|
} else {
|
|
p ++;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
/* enter function name, nested .subckt depths, and
|
|
* number of .subckt at given level into struct new_func
|
|
* and add line to deck
|
|
*/
|
|
|
|
static struct func_temper *
|
|
inp_new_func(char *funcname, char *funcbody, struct card *card,
|
|
int *sub_count, int subckt_depth)
|
|
{
|
|
struct func_temper *f;
|
|
char *new_str;
|
|
|
|
f = TMALLOC(struct func_temper, 1);
|
|
f->funcname = funcname;
|
|
f->next = NULL;
|
|
f->subckt_depth = subckt_depth;
|
|
f->subckt_count = sub_count[subckt_depth];
|
|
|
|
/* replace line in deck */
|
|
new_str = tprintf(".func %s() %s", funcname, funcbody);
|
|
|
|
*card->line = '*';
|
|
insert_new_line(card, new_str, 0, card->linenum);
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
static void
|
|
inp_delete_funcs(struct func_temper *f)
|
|
{
|
|
while (f) {
|
|
struct func_temper *f_next = f->next;
|
|
tfree(f->funcname);
|
|
tfree(f);
|
|
f = f_next;
|
|
}
|
|
}
|
|
|
|
|
|
/* look for unquoted parameters and quote them */
|
|
/* FIXME, this function seems to be useless and/or buggy and/or naive */
|
|
static void
|
|
inp_quote_params(struct card *c, struct card *end_c, struct dependency *deps, int num_params)
|
|
{
|
|
bool in_control = FALSE;
|
|
|
|
for (; c && c != end_c; c = c->nextcard) {
|
|
|
|
int i, j, num_terminals;
|
|
|
|
char *curr_line = c->line;
|
|
|
|
if (ciprefix(".control", curr_line)) {
|
|
in_control = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (ciprefix(".endc", curr_line)) {
|
|
in_control = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (in_control || curr_line[0] == '.' || curr_line[0] == '*')
|
|
continue;
|
|
|
|
num_terminals = get_number_terminals(curr_line);
|
|
|
|
if (num_terminals <= 0)
|
|
continue;
|
|
|
|
/* There are devices that should not get quotes around tokens following after the terminals.
|
|
See bug 384 */
|
|
if (curr_line[0] == 'f' || curr_line[0] == 'h')
|
|
num_terminals++;
|
|
|
|
for (i = 0; i < num_params; i++) {
|
|
|
|
char *s = curr_line;
|
|
|
|
for (j = 0; j < num_terminals+1; j++) {
|
|
s = skip_non_ws(s);
|
|
s = skip_ws(s);
|
|
}
|
|
|
|
while ((s = ya_search_identifier(s, deps[i].param_name, curr_line)) != NULL) {
|
|
|
|
char *rest = s + strlen(deps[i].param_name);
|
|
|
|
if (s > curr_line &&
|
|
(isspace_c(s[-1]) || s[-1] == '=') &&
|
|
(isspace_c(*rest) || *rest == '\0' || *rest == ')'))
|
|
{
|
|
int prefix_len;
|
|
|
|
if (isspace_c(s[-1])) {
|
|
s = skip_back_ws(s, curr_line);
|
|
if (s > curr_line && s[-1] == '{')
|
|
s--;
|
|
}
|
|
|
|
if (isspace_c(*rest)) {
|
|
/* possible case: "{ length }" -> {length} */
|
|
rest = skip_ws(rest);
|
|
if (*rest == '}')
|
|
rest++;
|
|
else
|
|
rest--;
|
|
}
|
|
|
|
prefix_len = (int)(s - curr_line);
|
|
|
|
curr_line = tprintf("%.*s {%s}%s", prefix_len, curr_line, deps[i].param_name, rest);
|
|
s = curr_line + prefix_len + strlen(deps[i].param_name) + 3;
|
|
|
|
tfree(c->line);
|
|
c->line = curr_line;
|
|
} else {
|
|
s += strlen(deps[i].param_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* VDMOS special:
|
|
Check for 'vdmos' in .model line.
|
|
check if 'pchan', then add p to vdmos and ignore 'pchan'.
|
|
If no 'pchan' is found, add n to vdmos.
|
|
Ignore annotations on Vds, Ron, Qg, and mfg.
|
|
Assemble all other tokens in a wordlist, and flatten it
|
|
to become the new .model line.
|
|
*/
|
|
static void
|
|
inp_vdmos_model(struct card *deck)
|
|
{
|
|
struct card *card;
|
|
for (card = deck; card; card = card->nextcard) {
|
|
|
|
char *curr_line, *cut_line, *token, *new_line;
|
|
wordlist *wl = NULL, *wlb;
|
|
|
|
curr_line = cut_line = card->line;
|
|
|
|
if (ciprefix(".model", curr_line) && strstr(curr_line, "vdmos")) {
|
|
cut_line = strstr(curr_line, "vdmos");
|
|
wl_append_word(&wl, &wl, copy_substring(curr_line, cut_line));
|
|
wlb = wl;
|
|
if (strstr(cut_line, "pchan")) {
|
|
wl_append_word(NULL, &wl, "vdmosp (");
|
|
}
|
|
else {
|
|
wl_append_word(NULL, &wl, "vdmosn (");
|
|
}
|
|
cut_line = cut_line + 5;
|
|
|
|
cut_line = skip_ws(cut_line);
|
|
if (*cut_line == '(')
|
|
cut_line = cut_line + 1;
|
|
new_line = NULL;
|
|
while (cut_line && *cut_line) {
|
|
token = gettok_model(&cut_line);
|
|
if (!ciprefix("pchan", token) && !ciprefix("ron=", token) && !ciprefix("vds=", token) &&
|
|
!ciprefix("qg=", token) && !ciprefix("mfg=", token) && !ciprefix("nchan", token))
|
|
wl_append_word(NULL, &wl, token);
|
|
if (*cut_line == ')') {
|
|
wl_append_word(NULL, &wl, ")");
|
|
break;
|
|
}
|
|
}
|
|
new_line = wl_flatten(wlb);
|
|
tfree(card->line);
|
|
card->line = new_line;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* storage for devices which get voltage source added */
|
|
struct replace_currm
|
|
{
|
|
struct card *s_start;
|
|
struct card *cline;
|
|
char *rtoken;
|
|
struct replace_currm *next;
|
|
};
|
|
|
|
/* check if fourth token of sname starts with POLY */
|
|
static bool
|
|
is_poly_source(char *sname)
|
|
{
|
|
char *nstr = nexttok(sname);
|
|
nstr = nexttok(nstr);
|
|
nstr = nexttok(nstr);
|
|
if (ciprefix("POLY", nstr))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/* Measure current in node 1 of all devices, e.g. I, B, F, G.
|
|
I(V...) will be ignored, I(E...) and I(H...) will be undone if
|
|
they are simple linear sources, however E nonlinear voltage
|
|
source will be converted later to B source,
|
|
therefore we need to add current measurement here.
|
|
First find all ocurrencies of i(XYZ), store their cards, then
|
|
search for XYZ, but only within respective subcircuit, or if
|
|
all happens at top level. Other hierarchy is ignored for now.
|
|
Replace I(XYZ) bx I(V_XYZ), add voltage source V_XYZ with
|
|
suitable extra nodes.
|
|
*/
|
|
static void
|
|
inp_meas_current(struct card *deck)
|
|
{
|
|
struct card *card, *subc_start = NULL, *subc_prev = NULL;
|
|
struct replace_currm *new_rep, *act_rep = NULL, *rep = NULL;
|
|
char *s, *t, *u, *v, *w;
|
|
int skip_control = 0, subs = 0, sn = 0;
|
|
|
|
/* scan through deck and find i(xyz), replace by i(v_xyz) */
|
|
for (card = deck; card; card = card->nextcard) {
|
|
|
|
char *curr_line = card->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;
|
|
}
|
|
|
|
if (*curr_line == '*')
|
|
continue;
|
|
|
|
if (*curr_line == '.') {
|
|
if (ciprefix(".subckt", curr_line)) {
|
|
subs++;
|
|
subc_prev = subc_start;
|
|
subc_start = card;
|
|
}
|
|
else if (ciprefix(".ends", curr_line)) {
|
|
subs--;
|
|
subc_start = subc_prev;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (!strstr(curr_line, "i("))
|
|
continue;
|
|
|
|
s = v = w = stripWhiteSpacesInsideParens(curr_line);
|
|
while (s) {
|
|
/* i( may occur more than once in a line */
|
|
s = u = strstr(s, "i(");
|
|
/* we have found it, but not (in error) at the beginning of the line */
|
|
if (s && s > v) {
|
|
/* '{' if at beginning of expression, '=' possible in B-line */
|
|
if (is_arith_char(s[-1]) || s[-1] == '{' || s[-1] == '=' || isspace_c(s[-1])) {
|
|
s += 2;
|
|
if (*s == 'v') {
|
|
// printf("i(v...) found in\n%s\n not converted!\n\n", curr_line);
|
|
continue;
|
|
}
|
|
else {
|
|
char *beg_str, *new_str;
|
|
get_r_paren(&u);
|
|
/* token containing name of devices to be measured */
|
|
t = copy_substring(s, --u);
|
|
if (ft_ngdebug)
|
|
printf("i(%s) found in\n%s\n\n", t, v);
|
|
|
|
/* new entry to the end of struct rep */
|
|
new_rep = TMALLOC(struct replace_currm, 1);
|
|
new_rep->s_start = subc_start;
|
|
new_rep->next = NULL;
|
|
new_rep->cline = card;
|
|
new_rep->rtoken = t;
|
|
if (act_rep) {
|
|
act_rep->next = new_rep;
|
|
act_rep = act_rep->next;
|
|
}
|
|
else
|
|
rep = act_rep = new_rep;
|
|
/* change line, convert i(XXX) to i(v_XXX) */
|
|
beg_str = copy_substring(v, s);
|
|
new_str = tprintf("%s%s%s", beg_str, "v_", s);
|
|
if (ft_ngdebug)
|
|
printf("converted to\n%s\n\n", new_str);
|
|
tfree(card->line);
|
|
card->line = s = v = new_str;
|
|
s++;
|
|
tfree(beg_str);
|
|
}
|
|
}
|
|
else
|
|
s++;
|
|
}
|
|
}
|
|
tfree(w);
|
|
}
|
|
|
|
/* return if we did not find any i( */
|
|
if (rep == NULL)
|
|
return;
|
|
|
|
/* scan through all the devices, search for xyz, modify node 1 by adding _vmeas,
|
|
add a line with zero voltage v_xyz, having original node 1 and modified node 1.
|
|
Do this within the top level or the same level of subcircuit only. */
|
|
new_rep = rep;
|
|
for (; rep; rep = rep->next) {
|
|
card = rep->s_start;
|
|
subs = 0;
|
|
if (card)
|
|
card = card->nextcard;
|
|
else
|
|
card = deck;
|
|
for (; card; card = card->nextcard) {
|
|
char *tok, *new_tok, *node1, *new_line;
|
|
char *curr_line = card->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;
|
|
}
|
|
|
|
if (*curr_line == '*')
|
|
continue;
|
|
|
|
if (*curr_line == '.') {
|
|
if (ciprefix(".subckt", curr_line))
|
|
subs++;
|
|
else if (ciprefix(".ends", curr_line))
|
|
subs--;
|
|
else
|
|
continue;
|
|
}
|
|
if (subs > 0)
|
|
continue;
|
|
/* We are at now top level or in top level of subcircuit
|
|
where i(xyz) has been found */
|
|
tok = gettok(&curr_line);
|
|
/* done when end of subcircuit is reached */
|
|
if (eq(".ends", tok) && rep->s_start) {
|
|
tfree(tok);
|
|
break;
|
|
}
|
|
if (eq(rep->rtoken, tok)) {
|
|
/* special treatment if we have an e (VCVS) or h (CCVS) source:
|
|
check if it is a simple linear source, if yes, don't do a
|
|
replacement, instead undo the already done name conversion */
|
|
if (((tok[0] == 'e') || (tok[0] == 'h')) && !strchr(curr_line, '=') && !is_poly_source(card->line)) {
|
|
/* simple linear e source */
|
|
char *searchstr = tprintf("i(v_%s)", tok);
|
|
char *thisline = rep->cline->line;
|
|
char *findstr = strstr(thisline, searchstr);
|
|
while (findstr) {
|
|
if (prefix(searchstr, findstr))
|
|
memcpy(findstr, " i(", 4);
|
|
findstr = strstr(thisline, searchstr);
|
|
if (ft_ngdebug)
|
|
printf("i(%s) moved back to i(%s) in\n%s\n\n", searchstr, tok, rep->cline->line);
|
|
}
|
|
tfree(searchstr);
|
|
tfree(tok);
|
|
continue;
|
|
}
|
|
node1 = gettok(&curr_line);
|
|
/* Add _vmeas only once to first device node.
|
|
Continue if we already have modified device "tok" */
|
|
if (!strstr(node1, "_vmeas")) {
|
|
new_line = tprintf("%s %s_vmeas_%d %s", tok, node1, sn, curr_line);
|
|
tfree(card->line);
|
|
card->line = new_line;
|
|
}
|
|
|
|
new_tok = tprintf("v_%s", tok);
|
|
/* We have already added a line v_xyz to the deck */
|
|
if (!ciprefix(new_tok, card->nextcard->line)) {
|
|
/* add new line */
|
|
new_line = tprintf("%s %s %s_vmeas_%d 0", new_tok, node1, node1, sn);
|
|
/* insert new_line after card->line */
|
|
insert_new_line(card, new_line, card->linenum + 1, 0);
|
|
}
|
|
sn++;
|
|
tfree(new_tok);
|
|
tfree(node1);
|
|
}
|
|
tfree(tok);
|
|
}
|
|
}
|
|
|
|
/* free rep */
|
|
while (new_rep) {
|
|
struct replace_currm *repn = new_rep->next;
|
|
tfree(new_rep->rtoken);
|
|
tfree(new_rep);
|
|
new_rep = repn;
|
|
}
|
|
}
|
|
|
|
/* replace the E source TABLE function by a B source pwl
|
|
(used by ST OpAmps and comparators).
|
|
E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 )*I(VreadIo)}
|
|
will become
|
|
BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 )
|
|
E_RO_3 VB_3 VB_4 VALUE={ V(TABLE_NEW_1)*I(VreadIo)}
|
|
*/
|
|
static void
|
|
replace_table(struct card *startcard)
|
|
{
|
|
struct card *card;
|
|
static int numb = 0;
|
|
for (card = startcard; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
if (*cut_line == 'e') {
|
|
char *valp = strstr(cut_line, "value={");
|
|
if (valp) {
|
|
char *ftablebeg = strstr(cut_line, "table(");
|
|
while (ftablebeg) {
|
|
/* get the beginning of the line */
|
|
char *begline = copy_substring(cut_line, ftablebeg);
|
|
/* get the table function */
|
|
char *tabfun = gettok_char(&ftablebeg, ')', TRUE, TRUE);
|
|
/* the new e line */
|
|
char *neweline = tprintf("%s v(table_new_%d)%s", begline, numb, ftablebeg);
|
|
char *newbline = tprintf("btable_new_%d table_new_%d 0 v=pwl%s", numb, numb, tabfun+5);
|
|
numb++;
|
|
tfree(tabfun);
|
|
tfree(begline);
|
|
tfree(card->line);
|
|
card->line = cut_line = neweline;
|
|
insert_new_line(card, newbline, 0, 0);
|
|
/* read next TABLE function in cut_line */
|
|
ftablebeg = strstr(cut_line, "table(");
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* find the model requested by ako:model and do the replacement */
|
|
static struct card*
|
|
find_model(struct card *startcard, struct card *changecard, char *searchname, char *newmname, char *newmtype, char *endstr)
|
|
{
|
|
struct card *nomod, *returncard = changecard;
|
|
char *origmname, *origmtype;
|
|
char *beginline = startcard->line;
|
|
if (ciprefix(".subckt", beginline))
|
|
startcard = startcard->nextcard;
|
|
|
|
int nesting2 = 0;
|
|
for (nomod = startcard; nomod; nomod = nomod->nextcard) {
|
|
char *origmodline = nomod->line;
|
|
if (ciprefix(".subckt", origmodline))
|
|
nesting2++;
|
|
if (ciprefix(".ends", origmodline))
|
|
nesting2--;
|
|
/* skip any subcircuit */
|
|
if (nesting2 > 0)
|
|
continue;
|
|
if (nesting2 == -1) {
|
|
returncard = changecard;
|
|
break;
|
|
}
|
|
if (ciprefix(".model", origmodline)) {
|
|
origmodline = nexttok(origmodline);
|
|
origmname = gettok(&origmodline);
|
|
origmtype = gettok_noparens(&origmodline);
|
|
if (cieq(origmname, searchname)) {
|
|
if (!eq(origmtype, newmtype)) {
|
|
fprintf(stderr, "Error: Original (%s) and new (%s) type for AKO model disagree\n", origmtype, newmtype);
|
|
controlled_exit(1);
|
|
}
|
|
/* we have got it */
|
|
char *newmodcard = tprintf(".model %s %s %s%s", newmname, newmtype, origmodline, endstr);
|
|
char *tmpstr = strstr(newmodcard, ")(");
|
|
if (tmpstr) {
|
|
tmpstr[0] = ' ';
|
|
tmpstr[1] = ' ';
|
|
}
|
|
tfree(changecard->line);
|
|
changecard->line = newmodcard;
|
|
tfree(origmname);
|
|
tfree(origmtype);
|
|
returncard = NULL;
|
|
break;
|
|
}
|
|
tfree(origmname);
|
|
tfree(origmtype);
|
|
}
|
|
else
|
|
returncard = changecard;
|
|
}
|
|
return returncard;
|
|
}
|
|
|
|
/* do the .model replacement required by ako (a kind of)
|
|
* PSPICE does not support ested .subckt definitions, so
|
|
* a simple structure is needed: search for ako:modelname,
|
|
* then for modelname in the subcircuit or in the top level.
|
|
* .model qorig npn (BF=48 IS=2e-7)
|
|
* .model qbip1 ako:qorig NPN (BF=60 IKF=45m)
|
|
* after the replacement we have
|
|
* .model qbip1 NPN (BF=48 IS=2e-7 BF=60 IKF=45m)
|
|
* and we benefit from the fact that if parameters have
|
|
* doubled, the last entry of a parameter (e.g. BF=60)
|
|
* overwrites the previous one (BF=48).
|
|
*/
|
|
static struct card*
|
|
ako_model(struct card *startcard)
|
|
{
|
|
char *newmname, *newmtype;
|
|
struct card *card, *returncard = NULL, *subcktcard = NULL;
|
|
for (card = startcard; card; card = card->nextcard) {
|
|
char *akostr, *searchname;
|
|
char *cut_line = card->line;
|
|
if (ciprefix(".subckt", cut_line))
|
|
subcktcard = card;
|
|
else if (ciprefix(".ends", cut_line))
|
|
subcktcard = NULL;
|
|
if (ciprefix(".model", cut_line) &&
|
|
((akostr = strstr(cut_line, "ako:")) != NULL) && isspace_c(akostr[-1])) {
|
|
akostr += 4;
|
|
searchname = gettok(&akostr);
|
|
cut_line = nexttok(cut_line);
|
|
newmname = gettok(&cut_line);
|
|
newmtype = gettok_noparens(&akostr);
|
|
/* find the model and do the replacement */
|
|
if (subcktcard)
|
|
returncard = find_model(subcktcard, card, searchname, newmname, newmtype, akostr);
|
|
if(returncard || !subcktcard)
|
|
returncard = find_model(startcard, card, searchname, newmname, newmtype, akostr);
|
|
tfree(searchname);
|
|
tfree(newmname);
|
|
tfree(newmtype);
|
|
/* replacement not possible, bail out */
|
|
if (returncard)
|
|
break;
|
|
}
|
|
}
|
|
return returncard;
|
|
}
|
|
|
|
/* in out
|
|
von cntl_on
|
|
voff cntl_off
|
|
ron r_on
|
|
roff r_off
|
|
*/
|
|
static int
|
|
rep_spar(char *inpar[4])
|
|
{
|
|
int i;
|
|
for (i = 0; i < 4; i++) {
|
|
char *t, *strend;
|
|
char *tok = inpar[i];
|
|
if ((t = strstr(tok, "von")) != NULL) {
|
|
strend = copy(t + 1);
|
|
tfree(inpar[i]);
|
|
inpar[i] = tprintf("cntl_%s", strend);
|
|
tfree(strend);
|
|
}
|
|
else if ((t = strstr(tok, "voff")) != NULL) {
|
|
strend = copy(t + 1);
|
|
tfree(inpar[i]);
|
|
inpar[i] = tprintf("cntl_%s", strend);
|
|
tfree(strend);
|
|
}
|
|
else if ((t = strstr(tok, "ron")) != NULL) {
|
|
strend = copy(t + 1);
|
|
tfree(inpar[i]);
|
|
inpar[i] = tprintf("r_%s", strend);
|
|
tfree(strend);
|
|
}
|
|
else if ((t = strstr(tok, "roff")) != NULL) {
|
|
strend = copy(t + 1);
|
|
tfree(inpar[i]);
|
|
inpar[i] = tprintf("r_%s", strend);
|
|
tfree(strend);
|
|
}
|
|
else {
|
|
fprintf(stderr, "Bad vswitch parameter %s\n", tok);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct vsmodels {
|
|
char *modelname;
|
|
char *subcktline;
|
|
struct vsmodels *nextmodel;
|
|
};
|
|
|
|
/* insert a new model, just behind the given model */
|
|
static struct vsmodels *
|
|
insert_new_model(struct vsmodels *vsmodel, char *name, char *subcktline)
|
|
{
|
|
struct vsmodels *x = TMALLOC(struct vsmodels, 1);
|
|
|
|
x->nextmodel = vsmodel ? vsmodel->nextmodel : NULL;
|
|
x->modelname = copy(name);
|
|
x->subcktline = copy(subcktline);
|
|
if (vsmodel)
|
|
vsmodel->nextmodel = x;
|
|
else
|
|
vsmodel = x;
|
|
|
|
return vsmodel;
|
|
}
|
|
|
|
/* find the model */
|
|
static bool
|
|
find_a_model(struct vsmodels *vsmodel, char *name, char *subcktline)
|
|
{
|
|
struct vsmodels *x;
|
|
for (x = vsmodel; vsmodel; vsmodel = vsmodel->nextmodel)
|
|
if (eq(vsmodel->modelname, name) && eq(vsmodel->subcktline, subcktline))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* delete the vsmodels list */
|
|
static bool
|
|
del_models(struct vsmodels *vsmodel)
|
|
{
|
|
struct vsmodels *x;
|
|
|
|
if (!vsmodel)
|
|
return FALSE;
|
|
|
|
while (vsmodel) {
|
|
x = vsmodel->nextmodel;
|
|
tfree(vsmodel->modelname);
|
|
tfree(vsmodel->subcktline);
|
|
tfree(vsmodel);
|
|
vsmodel = x;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**** PSPICE to ngspice **************
|
|
* .model replacement in ako (a kind of) model descriptions
|
|
* replace the E source TABLE function by a B source pwl
|
|
* add predefined params TEMP, VT, GMIN to beginning of deck
|
|
* add predefined params TEMP, VT, GMIN to beginning of each .subckt call
|
|
* add .functions limit, pwr, pwrs, stp, if, int
|
|
* replace
|
|
S1 D S DG GND SWN
|
|
.MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G })
|
|
* by
|
|
as1 %vd(DG GND) % gd(D S) aswn
|
|
.model aswn aswitch(cntl_off={0.49} cntl_on={0.55} r_off={1G}
|
|
+ r_on={ 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } log = TRUE)
|
|
* replace & by &&
|
|
* replace | by ||
|
|
* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2
|
|
* replace T_ABS by temp and T_REL_GLOBAL by dtemp in .model cards
|
|
* get the area factor for diodes and bipolar devices */
|
|
static struct card *
|
|
pspice_compat(struct card *oldcard)
|
|
{
|
|
struct card *card, *newcard, *nextcard;
|
|
struct vsmodels *modelsfound = NULL;
|
|
int skip_control = 0;
|
|
|
|
/* .model replacement in ako (a kind of) model descriptions
|
|
* in first .subckt and top level only */
|
|
struct card *errcard;
|
|
if ((errcard = ako_model(oldcard)) != NULL) {
|
|
fprintf(stderr, "Error: no model found for %s\n", errcard->line);
|
|
controlled_exit(1);
|
|
}
|
|
|
|
/* replace TABLE function in E source */
|
|
replace_table(oldcard);
|
|
|
|
/* add predefined params TEMP, VT, GMIN to beginning of deck */
|
|
char *new_str = copy(".param temp = 'temper'");
|
|
newcard = insert_new_line(NULL, new_str, 1, 0);
|
|
new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'");
|
|
nextcard = insert_new_line(newcard, new_str, 2, 0);
|
|
new_str = copy(".param gmin = 1e-12");
|
|
nextcard = insert_new_line(nextcard, new_str, 3, 0);
|
|
/* add funcs limit, pwr, pwrs, stp, if, int */
|
|
new_str = copy(".func limit(x, a, b) { min(max(x, a), b) }");
|
|
nextcard = insert_new_line(nextcard, new_str, 4, 0);
|
|
new_str = copy(".func pwr(x, a) { abs(x) ** a }");
|
|
nextcard = insert_new_line(nextcard, new_str, 5, 0);
|
|
new_str = copy(".func pwrs(x, a) { sgn(x) * pwr(x, a) }");
|
|
nextcard = insert_new_line(nextcard, new_str, 6, 0);
|
|
new_str = copy(".func stp(x) { u(x) }");
|
|
nextcard = insert_new_line(nextcard, new_str, 7, 0);
|
|
new_str = copy(".func if(a, b, c) {ternary_fcn( a , b , c )}");
|
|
nextcard = insert_new_line(nextcard, new_str, 8, 0);
|
|
new_str = copy(".func int(x) { sign(x)*floor(abs(x)) }");
|
|
nextcard = insert_new_line(nextcard, new_str, 9, 0);
|
|
nextcard->nextcard = oldcard;
|
|
|
|
/* add predefined parameters TEMP, VT after each subckt call */
|
|
/* FIXME: This should not be necessary if we had a better sense of hierarchy
|
|
during the evaluation of TEMPER */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
if (ciprefix(".subckt", cut_line)) {
|
|
new_str = copy(".param temp = 'temper'");
|
|
nextcard = insert_new_line(card, new_str, 0, 0);
|
|
new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'");
|
|
nextcard = insert_new_line(nextcard, new_str, 1, 0);
|
|
}
|
|
}
|
|
|
|
/* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
|
|
/* exclude any command inside .control ... .endc */
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
|
|
if (*cut_line == 'r' || *cut_line == 'l' || *cut_line == 'c') {
|
|
/* Skip name and two nodes */
|
|
char *ntok = nexttok(cut_line);
|
|
ntok = nexttok(ntok);
|
|
ntok = nexttok(ntok);
|
|
char *tctok = search_plain_identifier(ntok, "tc");
|
|
if (tctok) {
|
|
char *tctok1 = strchr(tctok,'=');
|
|
if (tctok1)
|
|
/* skip '=' */
|
|
tctok1 += 1;
|
|
else
|
|
/* no '=' found, skip 'tc' */
|
|
tctok1 = tctok + 2;
|
|
char *tc1 = gettok_node(&tctok1);
|
|
char *tc2 = gettok_node(&tctok1);
|
|
tctok[-1] = '\0';
|
|
char *newstring;
|
|
if (tc1 && tc2)
|
|
newstring = tprintf("%s tc1=%s tc2=%s", cut_line, tc1, tc2);
|
|
else if (tc1)
|
|
newstring = tprintf("%s tc1=%s", cut_line, tc1);
|
|
else {
|
|
fprintf(stderr, "Warning: tc without parameters removed in line \n %s\n", cut_line);
|
|
continue;
|
|
}
|
|
tfree(card->line);
|
|
card->line = newstring;
|
|
tfree(tc1);
|
|
tfree(tc2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* replace & with && and | with || and *# with * # */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *t;
|
|
char *cut_line = card->line;
|
|
|
|
/* we don't have command lines in a PSPICE model */
|
|
if (ciprefix("*#", cut_line)) {
|
|
char *tmpstr = tprintf("* #%s", cut_line + 2);
|
|
tfree(card->line);
|
|
card->line = tmpstr;
|
|
continue;
|
|
}
|
|
|
|
if (*cut_line == '*')
|
|
continue;
|
|
/* exclude any command inside .control ... .endc */
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if ((t = strstr(card->line, "&")) != NULL) {
|
|
while (t && (t[1] != '&')) {
|
|
char *tt = NULL;
|
|
char *tn = copy(t + 1);/*skip |*/
|
|
char *strbeg = copy_substring(card->line, t);
|
|
tfree(card->line);
|
|
card->line = tprintf("%s&&%s", strbeg, tn);
|
|
tfree(strbeg);
|
|
tfree(tn);
|
|
t = card->line;
|
|
while ((t = strstr(t, "&&")) != NULL)
|
|
tt = t = t + 2;
|
|
if (!tt)
|
|
break;
|
|
else
|
|
t = strstr(tt, "&");
|
|
}
|
|
}
|
|
if ((t = strstr(card->line, "|")) != NULL) {
|
|
while (t && (t[1] != '|')) {
|
|
char *tt = NULL;
|
|
char *tn = copy(t + 1);/*skip |*/
|
|
char *strbeg = copy_substring(card->line, t);
|
|
tfree(card->line);
|
|
card->line = tprintf("%s||%s", strbeg, tn);
|
|
tfree(strbeg);
|
|
tfree(tn);
|
|
t = card->line;
|
|
while ((t = strstr(t, "||")) != NULL)
|
|
tt = t = t + 2;
|
|
if (!tt)
|
|
break;
|
|
else
|
|
t = strstr(tt, "|");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* replace T_ABS by temp and T_REL_GLOBAL by dtemp in .model cards */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
if (ciprefix(".model", cut_line)) {
|
|
char *t_str;
|
|
if((t_str = strstr(cut_line, "t_abs")) != NULL)
|
|
memcpy(t_str, " temp", 5);
|
|
else if((t_str = strstr(cut_line, "t_rel_global")) != NULL)
|
|
memcpy(t_str, " dtemp", 12);
|
|
}
|
|
}
|
|
|
|
/* get the area factor for diodes and bipolar devices
|
|
d1 n1 n2 dmod 7 --> d1 n1 n2 dmod area=7
|
|
q2 n1 n2 n3 [n4] bjtmod 1.35 --> q2 n1 n2 n3 n4 bjtmod area=1.35
|
|
q3 1 2 3 4 bjtmod 1.45 --> q2 1 2 3 4 bjtmod area=1.45
|
|
*/
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
if (*cut_line == '*')
|
|
continue;
|
|
// exclude any command inside .control ... .endc
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if (*cut_line == 'q') {
|
|
cut_line = nexttok(cut_line); //.model
|
|
cut_line = nexttok(cut_line); // node1
|
|
cut_line = nexttok(cut_line); // node2
|
|
cut_line = nexttok(cut_line); // node3
|
|
if (!cut_line) {
|
|
fprintf(stderr, "Line no. %d, %s missing tokens\n",
|
|
card->linenum_orig, card->line);
|
|
continue;
|
|
}
|
|
if (*cut_line == '[') { // node4 not a number
|
|
*cut_line = ' ';
|
|
cut_line = strchr(cut_line, ']');
|
|
*cut_line = ' ';
|
|
cut_line = skip_ws(cut_line);
|
|
cut_line = nexttok(cut_line); // model name
|
|
}
|
|
else { // if an integer number, it is node4
|
|
bool is_node4 = TRUE;
|
|
while (*cut_line && !isspace(*cut_line))
|
|
if (!isdigit(*cut_line++))
|
|
is_node4 = FALSE; // already model name
|
|
if(is_node4)
|
|
cut_line = nexttok(cut_line); // model name
|
|
}
|
|
if (*cut_line && atof(cut_line) > 0.0) { // size of area is a real number
|
|
char *tmpstr1 = copy_substring(card->line, cut_line);
|
|
char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line);
|
|
tfree(tmpstr1);
|
|
tfree(card->line);
|
|
card->line = tmpstr2;
|
|
}
|
|
else if (*cut_line && *(skip_ws(cut_line)) == '{') { // size of area is parametrized inside {}
|
|
char *tmpstr1 = copy_substring(card->line, cut_line);
|
|
char *tmpstr2 = gettok_char(&cut_line, '}', TRUE, TRUE);
|
|
char *tmpstr3 = tprintf("%s area=%s %s", tmpstr1, tmpstr2, cut_line);
|
|
tfree(tmpstr1);
|
|
tfree(tmpstr2);
|
|
tfree(card->line);
|
|
card->line = tmpstr3;
|
|
}
|
|
}
|
|
else if (*cut_line == 'd') {
|
|
cut_line = nexttok(cut_line); //.model
|
|
cut_line = nexttok(cut_line); // node1
|
|
cut_line = nexttok(cut_line); // node2
|
|
if (!cut_line) {
|
|
fprintf(stderr, "Line no. %d, %s missing tokens\n",
|
|
card->linenum_orig, card->line);
|
|
continue;
|
|
}
|
|
cut_line = nexttok(cut_line); // model name
|
|
if (*cut_line && atof(cut_line) > 0.0) { // size of area
|
|
char *tmpstr1 = copy_substring(card->line, cut_line);
|
|
char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line);
|
|
tfree(tmpstr1);
|
|
tfree(card->line);
|
|
card->line = tmpstr2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* replace
|
|
* S1 D S DG GND SWN
|
|
* .MODEL SWN VSWITCH ( VON = {0.55} VOFF = {0.49} RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} )
|
|
* by
|
|
* a1 %v(DG) %gd(D S) swa
|
|
* .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE)
|
|
|
|
* simple hierachy, as nested subcircuits are not allowed in PSPICE */
|
|
|
|
/* first scan: find the vswitch models, transform them and put them into a list */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
char *str;
|
|
static struct card *subcktline = NULL;
|
|
static int nesting = 0;
|
|
char *cut_line = card->line;
|
|
if (ciprefix(".subckt", cut_line)) {
|
|
subcktline = card;
|
|
nesting++;
|
|
}
|
|
if (ciprefix(".ends", cut_line))
|
|
nesting--;
|
|
|
|
if (ciprefix(".model", card->line) && strstr(card->line, "vswitch")) {
|
|
char *modpar[4];
|
|
char *modname;
|
|
int i;
|
|
|
|
card->line = str = inp_remove_ws(card->line);
|
|
str = nexttok(str); /* throw away '.model' */
|
|
INPgetNetTok(&str, &modname, 0); /* model name */
|
|
if (!ciprefix("vswitch", str)) {
|
|
tfree(modname);
|
|
continue;
|
|
}
|
|
/* we have to find 4 parameters, identified by '=', separated by spaces */
|
|
char *equalptr[4];
|
|
equalptr[0] = strstr(str, "=");
|
|
if (!equalptr[0]) {
|
|
fprintf(stderr, "Error: not enough parameters in vswitch model\n %s\n", card->line);
|
|
controlled_exit(1);
|
|
}
|
|
for (i = 1; i < 4; i++) {
|
|
equalptr[i] = strstr(equalptr[i - 1] + 1, "=");
|
|
if (!equalptr[i]) {
|
|
fprintf(stderr, "Error: not enough parameters in vswitch model\n %s\n", card->line);
|
|
controlled_exit(1);
|
|
}
|
|
}
|
|
for (i = 0; i < 4; i++) {
|
|
equalptr[i] = skip_back_ws(equalptr[i], str);
|
|
while (*(equalptr[i]) != '(' && !isspace_c(*(equalptr[i])) && *(equalptr[i]) != ',')
|
|
(equalptr[i])--;
|
|
(equalptr[i])++;
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
modpar[i] = copy_substring(equalptr[i], equalptr[i + 1] - 1);
|
|
if (strrchr(equalptr[3], ')'))
|
|
modpar[3] = copy_substring(equalptr[3], strrchr(equalptr[3], ')'));
|
|
else
|
|
/* vswitch defined without parens */
|
|
modpar[3] = copy(equalptr[3]);
|
|
tfree(card->line);
|
|
/* replace VON by cntl_on, VOFF by cntl_off, RON by r_on, and ROFF by r_off */
|
|
rep_spar(modpar);
|
|
card->line = tprintf(".model a%s aswitch(%s %s %s %s log=TRUE)",
|
|
modname, modpar[0], modpar[1], modpar[2], modpar[3]);
|
|
for (i = 0; i < 4; i++)
|
|
tfree(modpar[i]);
|
|
if (nesting > 0)
|
|
modelsfound = insert_new_model(modelsfound, modname, subcktline->line);
|
|
else
|
|
modelsfound = insert_new_model(modelsfound, modname, "top");
|
|
tfree(modname);
|
|
}
|
|
}
|
|
|
|
/* no need to continue if no vswitch is found */
|
|
if (!modelsfound)
|
|
return newcard;
|
|
|
|
/* second scan: find the switch instances s calling a vswitch model and transform them */
|
|
for (card = newcard; card; card = card->nextcard) {
|
|
static struct card *subcktline = NULL;
|
|
static int nesting = 0;
|
|
char *cut_line = card->line;
|
|
if (*cut_line == '*')
|
|
continue;
|
|
// exclude any command inside .control ... .endc
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if (ciprefix(".subckt", cut_line)) {
|
|
subcktline = card;
|
|
nesting++;
|
|
}
|
|
if (ciprefix(".ends", cut_line))
|
|
nesting--;
|
|
|
|
if (ciprefix("s", cut_line)) {
|
|
/* check for the model name */
|
|
int i;
|
|
char *stoks[6];
|
|
for (i = 0; i < 6; i++)
|
|
stoks[i] = gettok_node(&cut_line);
|
|
/* rewrite s line and replace it if a model is found */
|
|
if ((nesting > 0) && find_a_model(modelsfound, stoks[5], subcktline->line)) {
|
|
tfree(card->line);
|
|
card->line = tprintf("a%s %%vd(%s %s) %%gd(%s %s) a%s",
|
|
stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], stoks[5]);
|
|
}
|
|
/* if model is not within same subcircuit, search at top level */
|
|
else if (find_a_model(modelsfound, stoks[5], "top")) {
|
|
tfree(card->line);
|
|
card->line = tprintf("a%s %%vd(%s %s) %%gd(%s %s) a%s",
|
|
stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], stoks[5]);
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
tfree(stoks[i]);
|
|
}
|
|
}
|
|
del_models(modelsfound);
|
|
|
|
return newcard;
|
|
}
|
|
|
|
/* do not modify oldcard address, insert everything after first line only */
|
|
static void
|
|
pspice_compat_a(struct card *oldcard)
|
|
{
|
|
oldcard->nextcard = pspice_compat(oldcard->nextcard);
|
|
}
|
|
|
|
|
|
/**** LTSPICE to ngspice **************
|
|
* add functions uplim, dnlim
|
|
* Replace
|
|
* D1 A K SDMOD
|
|
* .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7)
|
|
* by
|
|
* ad1 a k asdmod
|
|
* .model asdmod sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7)
|
|
*/
|
|
struct card *
|
|
ltspice_compat(struct card *oldcard)
|
|
{
|
|
struct card *card, *newcard, *nextcard;
|
|
struct vsmodels *modelsfound = NULL;
|
|
int skip_control = 0;
|
|
|
|
/* add funcs uplim, dnlim to beginning of deck */
|
|
char *new_str = copy(".func uplim(x, pos, z) { min(x, pos - z) + (1 - (min(max(0, x - pos + z), 2 * z) / 2 / z - 1)**2)*z }");
|
|
newcard = insert_new_line(NULL, new_str, 1, 0);
|
|
new_str = copy(".func dnlim(x, neg, z) { max(x, neg + z) - (1 - (min(max(0, -x + neg + z), 2 * z) / 2 / z - 1)**2)*z }");
|
|
nextcard = insert_new_line(newcard, new_str, 2, 0);
|
|
new_str = copy(".func uplim_tanh(x, pos, z) { min(x, pos - z) + tanh(max(0, x - pos + z) / z)*z }");
|
|
nextcard = insert_new_line(nextcard, new_str, 3, 0);
|
|
new_str = copy(".func dnlim_tanh(x, neg, z) { max(x, neg + z) - tanh(max(0, neg + z - x) / z)*z }");
|
|
nextcard = insert_new_line(nextcard, new_str, 4, 0);
|
|
nextcard->nextcard = oldcard;
|
|
|
|
/* replace
|
|
* D1 A K SDMOD
|
|
* .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7)
|
|
* by
|
|
* a1 a k SDMOD
|
|
* .model SDMOD sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7)
|
|
* Do this if one of the parameters, which are uncommon to standard diode model, has been found.
|
|
|
|
* simple hierachy, as nested subcircuits are not allowed in PSPICE */
|
|
|
|
/* first scan: find the d models, transform them and put them into a list */
|
|
for (card = nextcard; card; card = card->nextcard) {
|
|
char *str;
|
|
static struct card *subcktline = NULL;
|
|
static int nesting = 0;
|
|
char *cut_line = card->line;
|
|
if (ciprefix(".subckt", cut_line)) {
|
|
subcktline = card;
|
|
nesting++;
|
|
}
|
|
if (ciprefix(".ends", cut_line))
|
|
nesting--;
|
|
|
|
if (ciprefix(".model", card->line) && search_plain_identifier(card->line, "d")) {
|
|
if (search_plain_identifier(card->line, "roff") ||
|
|
search_plain_identifier(card->line, "ron") ||
|
|
search_plain_identifier(card->line, "rrev") ||
|
|
search_plain_identifier(card->line, "vfwd") ||
|
|
search_plain_identifier(card->line, "vrev") ||
|
|
search_plain_identifier(card->line, "revepsilon") ||
|
|
search_plain_identifier(card->line, "epsilon") ||
|
|
search_plain_identifier(card->line, "revilimit") ||
|
|
search_plain_identifier(card->line, "ilimit")
|
|
)
|
|
{
|
|
char *modname;
|
|
|
|
card->line = str = inp_remove_ws(card->line);
|
|
str = nexttok(str); /* throw away '.model' */
|
|
INPgetNetTok(&str, &modname, 0); /* model name */
|
|
if (!ciprefix("d", str)) {
|
|
tfree(modname);
|
|
continue;
|
|
}
|
|
/* skip d */
|
|
str++;
|
|
/* we take all the existing parameters */
|
|
char *newstr = copy(str);
|
|
tfree(card->line);
|
|
card->line = tprintf(".model a%s sidiode%s", modname, newstr);
|
|
if (nesting > 0)
|
|
modelsfound = insert_new_model(modelsfound, modname, subcktline->line);
|
|
else
|
|
modelsfound = insert_new_model(modelsfound, modname, "top");
|
|
tfree(modname);
|
|
tfree(newstr);
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
|
|
/* no need to continue if no d is found */
|
|
if (!modelsfound)
|
|
return newcard;
|
|
|
|
/* second scan: find the diode instances d calling a simple diode model and transform them */
|
|
for (card = nextcard; card; card = card->nextcard) {
|
|
static struct card *subcktline = NULL;
|
|
static int nesting = 0;
|
|
char *cut_line = card->line;
|
|
if (*cut_line == '*')
|
|
continue;
|
|
// exclude any command inside .control ... .endc
|
|
if (ciprefix(".control", cut_line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if (ciprefix(".subckt", cut_line)) {
|
|
subcktline = card;
|
|
nesting++;
|
|
}
|
|
if (ciprefix(".ends", cut_line))
|
|
nesting--;
|
|
|
|
if (ciprefix("d", cut_line)) {
|
|
/* check for the model name */
|
|
int i;
|
|
char *stoks[4];
|
|
for (i = 0; i < 4; i++)
|
|
stoks[i] = gettok_node(&cut_line);
|
|
/* rewrite d line and replace it if a model is found */
|
|
if ((nesting > 0) && find_a_model(modelsfound, stoks[3], subcktline->line)) {
|
|
tfree(card->line);
|
|
card->line = tprintf("a%s %s %s a%s",
|
|
stoks[0], stoks[1], stoks[2], stoks[3]);
|
|
}
|
|
/* if model is not within same subcircuit, search at top level */
|
|
else if (find_a_model(modelsfound, stoks[3], "top")) {
|
|
tfree(card->line);
|
|
card->line = tprintf("a%s %s %s a%s",
|
|
stoks[0], stoks[1], stoks[2], stoks[3]);
|
|
}
|
|
for (i = 0; i < 4; i++)
|
|
tfree(stoks[i]);
|
|
}
|
|
}
|
|
del_models(modelsfound);
|
|
|
|
return newcard;
|
|
}
|
|
|
|
/* do not modify oldcard address, insert everything after first line only */
|
|
static void
|
|
ltspice_compat_a(struct card *oldcard)
|
|
{
|
|
oldcard->nextcard = ltspice_compat(oldcard->nextcard);
|
|
}
|
|
|
|
/* syntax check:
|
|
Check if we have a .control ... .endc pair,
|
|
a .if ... .endif pair, a .suckt ... .ends pair */
|
|
static void inp_check_syntax(struct card *deck)
|
|
{
|
|
struct card *card;
|
|
int check_control = 0, check_subs = 0, check_if = 0;
|
|
|
|
/* will lead to crash in inp.c, fcn inp_spsource */
|
|
if (ciprefix(".param", deck->line) || ciprefix(".meas", deck->line)) {
|
|
fprintf(cp_err, "\nError: title line is missing!\n\n");
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
|
|
for (card = deck; card; card = card->nextcard) {
|
|
char *cut_line = card->line;
|
|
if (*cut_line == '*')
|
|
continue;
|
|
// check for .control ... .endc
|
|
if (ciprefix(".control", cut_line)) {
|
|
if (check_control > 0) {
|
|
fprintf(cp_err, "\nError: Nesting of .control statements is not allowed!\n\n");
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
check_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", cut_line)) {
|
|
check_control--;
|
|
continue;
|
|
}
|
|
// check for .subckt ... .ends
|
|
else if (ciprefix(".subckt", cut_line)) {
|
|
// nesting may be critical if params are involved
|
|
if (check_subs > 0 && strchr(cut_line, '='))
|
|
fprintf(cp_err, "\nWarning: Nesting of subcircuits with parameters is only marginally supported!\n\n");
|
|
check_subs++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".ends", cut_line)) {
|
|
check_subs--;
|
|
continue;
|
|
}
|
|
// check for .if ... .endif
|
|
if (ciprefix(".if", cut_line)) {
|
|
check_if++;
|
|
has_if = TRUE;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endif", cut_line)) {
|
|
check_if--;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (check_control > 0) {
|
|
fprintf(cp_err, "\nWarning: Missing .endc statement!\n");
|
|
fprintf(cp_err, " This may cause subsequent errors.\n\n");
|
|
}
|
|
if (check_control < 0) {
|
|
fprintf(cp_err, "\nWarning: Missing .control statement!\n");
|
|
fprintf(cp_err, " This may cause subsequent errors.\n\n");
|
|
}
|
|
if (check_subs != 0) {
|
|
fprintf(cp_err, "\nError: Mismatch of .subckt ... .ends statements!\n");
|
|
fprintf(cp_err, " This will cause subsequent errors.\n\n");
|
|
controlled_exit(EXIT_BAD);
|
|
}
|
|
if (check_if != 0) {
|
|
fprintf(cp_err, "\nError: Mismatch of .if ... .endif statements!\n");
|
|
fprintf(cp_err, " This may cause subsequent errors.\n\n");
|
|
}
|
|
}
|
|
|
|
/* remove the mfg=mfgname entry from the .model cards */
|
|
static void
|
|
rem_mfg_from_models(struct card *deck)
|
|
{
|
|
struct card *card;
|
|
for (card = deck; card; card = card->nextcard) {
|
|
|
|
char *curr_line, *end, *start;
|
|
|
|
curr_line = start = card->line;
|
|
/* remove mfg=name */
|
|
if (ciprefix(".model", curr_line)){
|
|
start = strstr(curr_line, "mfg=");
|
|
if (start) {
|
|
end = nexttok(start);
|
|
if (*end == '\0')
|
|
*start = '\0';
|
|
else
|
|
while (start < end) {
|
|
*start = ' ';
|
|
start++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
} |