diff --git a/ChangeLog b/ChangeLog
index fcf98fe22..1b304b7f6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2007-10-8 Paolo Nenzi
+ *src/frontend/{inp.c, inpcom.c, inpcom.h, measure.c, nutimp.c, runcoms.c
+ subckt.c, ftedefs.c, fteext.c, dctran.c, inp2dot.c, inppas2.c}: added
+ several improvements mad by Phil Barker:
+ - .measure statements: trig/targ, max, min, avg, rms, integral, param
+ - autostop option: which causes the simulation to stop if all .measure
+ statements are satisfied; option is ignored if any max, min, avg, rms
+ or integ measurements defined.
+ - fixed some floating point number comparisons where the code was checking for
+ equality with '==' by adding a routine called 'AlmostEqualUlps'.
+ - parametrized subcircuits
+ - added support for ".lib" syntax
+ - added ability to recursively call spice netlist parser; this allows for
+ reading in additional .include and .lib files defined in other netlist files
+ - changed the flattened netlist names created in 'subckt.c' to match other
+ spice simulators
+ - allow for .ic, .nodeset names to be embedded in a subckt;
+ enhanced subckt.c to created appropriate node names for flattened simulation
+ netlist
+
+
2007-10-8 Paolo Nenzi
* src/frontend/{rawfile.c, outitf.c, runcoms.c}, src/include/ftedefs.h:
modified current vectors output amd added struct elements for holding the
diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am
index 7e4bd9239..0a417607d 100644
--- a/src/frontend/Makefile.am
+++ b/src/frontend/Makefile.am
@@ -120,6 +120,7 @@ libfte_a_SOURCES = \
interp.h \
linear.c \
linear.h \
+ measure.c \
misccoms.c \
misccoms.h \
miscvars.c \
@@ -180,6 +181,6 @@ libfte_a_SOURCES = \
# testcommands_LDADD = libfte.a plotting/libplotting.a ../misc/libmisc.a
-INCLUDES = -I$(top_srcdir)/src/include @X_CFLAGS@
+INCLUDES = -I$(top_srcdir)/src/include @X_CFLAGS@
MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/frontend/inp.c b/src/frontend/inp.c
index 0cb4231b8..f60e4f085 100644
--- a/src/frontend/inp.c
+++ b/src/frontend/inp.c
@@ -15,6 +15,8 @@ $Id$
* the listing routines.
*/
+#include
+#include
#include "ngspice.h"
#include "cpdefs.h"
#include "inpdefs.h"
@@ -35,6 +37,10 @@ $Id$
/* gtri - end - 12/12/90 */
#endif
+#ifdef NUMPARAMS
+#include "numparam/numpaif.h"
+#endif
+
#define line_free(line,flag) { line_free_x(line,flag); line = NULL; }
/* static declarations */
@@ -42,47 +48,73 @@ static char * upper(register char *string);
static bool doedit(char *filename);
static void line_free_x(struct line * deck, bool recurse);
-/* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */
+// Initial AlmostEqualULPs version - fast and simple, but
+// some limitations.
+static bool AlmostEqualUlps(float A, float B, int maxUlps)
+{
+ assert(sizeof(float) == sizeof(int));
+ if (A == B)
+ return TRUE;
+
+ int intDiff = abs(*(int*)&A - *(int*)&B);
+
+ if (intDiff <= maxUlps)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */
void
com_listing(wordlist *wl)
{
int type = LS_LOGICAL;
- bool expand = FALSE;
+ bool expand = FALSE, do_param_listing = FALSE;
char *s;
if (ft_curckt) { /* if there is a current circuit . . . . */
while (wl) {
s = wl->wl_word;
- switch (*s) {
- case 'l':
- case 'L':
- type = LS_LOGICAL;
- break;
- case 'p':
- case 'P':
- type = LS_PHYSICAL;
- break;
- case 'd':
- case 'D':
- type = LS_DECK;
- break;
- case 'e':
- case 'E':
- expand = TRUE;
- break;
- default:
- fprintf(cp_err,
- "Error: bad listing type %s\n", s);
- return; /* SJB - don't go on after an error */
- }
+ if ( strcmp( s, "param" ) == 0 ) {
+ do_param_listing = TRUE;
+ }
+ else {
+ switch (*s) {
+ case 'l':
+ case 'L':
+ type = LS_LOGICAL;
+ break;
+ case 'p':
+ case 'P':
+ type = LS_PHYSICAL;
+ break;
+ case 'd':
+ case 'D':
+ type = LS_DECK;
+ break;
+ case 'e':
+ case 'E':
+ expand = TRUE;
+ break;
+ default:
+ fprintf(cp_err,
+ "Error: bad listing type %s\n", s);
+ return; /* SJB - don't go on after an error */
+ }
+ }
wl = wl->wl_next;
}
- if (type != LS_DECK)
+ if ( do_param_listing ) {
+ nupa_list_params(cp_out);
+ }
+ else {
+ if (type != LS_DECK)
fprintf(cp_out, "\t%s\n\n", ft_curckt->ci_name);
- inp_list(cp_out, expand ? ft_curckt->ci_deck :
- ft_curckt->ci_origdeck, ft_curckt->ci_options,
- type);
+ inp_list(cp_out, expand ? ft_curckt->ci_deck :
+ ft_curckt->ci_origdeck, ft_curckt->ci_options,
+ type);
+ }
} else
fprintf(cp_err, "Error: no circuit loaded.\n");
return;
@@ -297,16 +329,20 @@ inp_spsource(FILE *fp, bool comfile, char *filename)
* *filename =
*/
{
- struct line *deck, *dd, *ld;
- struct line *realdeck, *options = NULL;
- char *tt = NULL, name[BSIZE_SP], *s, *t;
+ struct line *deck, *dd, *ld, *prev_param = NULL, *prev_card = NULL;
+ struct line *realdeck, *options = NULL, *curr_meas = NULL;
+ char *tt = NULL, name[BSIZE_SP], *s, *t, *temperature = NULL;
bool nosubckts, commands = FALSE;
wordlist *wl = NULL, *end = NULL, *wl_first = NULL;
wordlist *controls = NULL;
FILE *lastin, *lastout, *lasterr;
+ double temperature_value;
+ bool autostop;
/* read in the deck from a file */
- inp_readall(fp, &deck);
+ char *filename_dup = ( filename == NULL ) ? strdup(".") : strdup(filename);
+ inp_readall(fp, &deck, 0, dirname(filename_dup));
+ tfree(filename_dup);
/* if nothing came back from inp_readall, just close fp and return to caller */
if (!deck) { /* MW. We must close fp always when returning */
@@ -469,6 +505,7 @@ inp_spsource(FILE *fp, bool comfile, char *filename)
if (deck->li_next) {
/* There is something left after the controls. */
fprintf(cp_out, "\nCircuit: %s\n\n", tt);
+ fprintf(stderr, "\nCircuit: %s\n\n", tt);
/* Old location of ENHtranslate_poly. This didn't work, because it
* didn't handle models in .SUBCKTs correctly. Moved to new location below
@@ -485,7 +522,6 @@ inp_spsource(FILE *fp, bool comfile, char *filename)
return;
} /* done expanding subcircuit macros */
-
/* Now handle translation of spice2c6 POLYs. */
/* New location of ENHtranslate_poly. This should handle .SUBCKTs better . . . .
* SDB 4.13.2003. Comments? mailto:sdb@cloud9.net
@@ -504,6 +540,63 @@ inp_spsource(FILE *fp, bool comfile, char *filename)
} /* if (deck->li_next) */
+ /* look for and set temperature; also store param and .meas statements in circuit struct */
+ ft_curckt->ci_param = NULL;
+ ft_curckt->ci_meas = NULL;
+
+ for (dd = deck; dd; dd = dd->li_next) {
+ /* get temp after numparam run on deck */
+ if ( ciprefix(".temp", dd->li_line) ) {
+ s = dd->li_line + 5;
+ while ( isspace(*s) ) s++;
+ if ( temperature != NULL ) {
+ txfree(temperature);
+ }
+ temperature = strdup(s);
+ }
+ /*
+ all parameter lines should be sequentially ordered and placed at
+ beginning of deck
+ */
+ if ( ciprefix( ".param", dd->li_line ) ) {
+ ft_curckt->ci_param = dd;
+ /* find end of .param statements */
+ while ( ciprefix( ".param", dd->li_line ) ) { prev_param = dd; dd = dd->li_next; }
+ prev_card->li_next = dd;
+ prev_param->li_next = NULL;
+ }
+
+ if ( ciprefix( ".meas", dd->li_line ) ) {
+ if ( cp_getvar( "autostop", VT_BOOL, (bool *) &autostop ) ) {
+ if ( strstr( dd->li_line, " max " ) || strstr( dd->li_line, " min " ) || strstr( dd->li_line, " avg " ) ||
+ strstr( dd->li_line, " rms " ) || strstr( dd->li_line, " integ " ) ) {
+ printf( "Warning: .OPTION AUTOSTOP will not be effective because one of 'max|min|avg|rms|integ' is used in .meas\n" );
+ printf( " AUTOSTOP being disabled...\n" );
+ cp_remvar( "autostop" );
+ }
+ }
+
+ if ( curr_meas == NULL ) {
+ curr_meas = ft_curckt->ci_meas = dd;
+ }
+ else {
+ curr_meas->li_next = dd;
+ curr_meas = dd;
+ }
+ prev_card->li_next = dd->li_next;
+ curr_meas->li_next = NULL;
+ dd = prev_card;
+ }
+ prev_card = dd;
+ }
+
+ /* set temperature if defined */
+ if ( temperature != NULL ) {
+ temperature_value = atof(temperature);
+ s = (char *) &temperature_value;
+ cp_vset("temp", VT_REAL, s );
+ txfree(temperature);
+ }
#ifdef TRACE
/* SDB debug statement */
@@ -512,8 +605,8 @@ inp_spsource(FILE *fp, bool comfile, char *filename)
/* Now that the deck is loaded, do the commands, if there are any */
if (controls) {
- for (end = wl = wl_reverse(controls); wl; wl = wl->wl_next)
- cp_evloop(wl->wl_word);
+ for (end = wl = wl_reverse(controls); wl; wl = wl->wl_next)
+ cp_evloop(wl->wl_word);
wl_free(end);
}
@@ -564,6 +657,7 @@ inp_dodeck(struct line *deck, char *tt, wordlist *end, bool reuse,
struct variable *eev = NULL;
wordlist *wl;
bool noparse, ii;
+ double brief = 0, i;
/* First throw away any old error messages there might be and fix
* the case of the lines. */
@@ -586,6 +680,50 @@ inp_dodeck(struct line *deck, char *tt, wordlist *end, bool reuse,
}
cp_getvar("noparse", VT_BOOL, (char *) &noparse);
+ if (!noparse) {
+ for (; options; options = options->li_next) {
+ for (s = options->li_line; *s && !isspace(*s); s++)
+ ;
+
+ ii = cp_interactive;
+ cp_interactive = FALSE;
+ wl = cp_lexer(s);
+ cp_interactive = ii;
+ if (!wl || !wl->wl_word || !*wl->wl_word)
+ continue;
+ if (eev)
+ eev->va_next = cp_setparse(wl);
+ else
+ ct->ci_vars = eev = cp_setparse(wl);
+ while (eev->va_next)
+ eev = eev->va_next;
+ }
+ for (eev = ct->ci_vars; eev; eev = eev->va_next) {
+ static int one = 1;
+ switch (eev->va_type) {
+ case VT_BOOL:
+ if_option(ct->ci_ckt, eev->va_name,
+ eev->va_type, &one);
+ break;
+ case VT_NUM:
+ if_option(ct->ci_ckt, eev->va_name,
+ eev->va_type, (char *) &eev->va_num);
+ break;
+ case VT_REAL:
+ if ( strcmp("brief",eev->va_name)==0 ){
+ cp_vset("brief", VT_REAL, (char*) &eev->va_real );
+ }
+ if_option(ct->ci_ckt, eev->va_name,
+ eev->va_type, (char *) &eev->va_real);
+ break;
+ case VT_STRING:
+ if_option(ct->ci_ckt, eev->va_name,
+ eev->va_type, eev->va_string);
+ break;
+ } /* switch . . . */
+ }
+ } /* if (!noparse) . . . */
+
/*----------------------------------------------------
* Now assuming that we wanna parse this deck, we call
* if_inpdeck which takes the deck and returns a
@@ -611,7 +749,7 @@ inp_dodeck(struct line *deck, char *tt, wordlist *end, bool reuse,
if (dd->li_error) {
char *p, *q;
-
+
#ifdef XSPICE
/* gtri - modify - 12/12/90 - wbk - add setting of ipc syntax error flag */
g_ipc.syntax_error = IPC_TRUE;
@@ -638,6 +776,22 @@ inp_dodeck(struct line *deck, char *tt, wordlist *end, bool reuse,
} /* for (dd = deck; dd; dd = dd->li_next) */
+ if ( cp_getvar( "brief", VT_REAL, (char *) &i ) ) {
+ brief = i;
+ }
+ // only print out netlist if brief == 0
+ if(AlmostEqualUlps(brief,0,3)) {
+ /* output deck */
+ out_printf( "\nProcessed Netlist\n" );
+ out_printf( "=================\n" );
+ int print_listing = 1;
+ for (dd = deck; dd; dd = dd->li_next) {
+ if ( ciprefix(".prot", dd->li_line) ) print_listing = 0;
+ if ( print_listing == 1 ) out_printf( "%s\n", dd->li_line );
+ if ( ciprefix(".unprot", dd->li_line) ) print_listing = 1;
+ }
+ out_printf( "\n" );
+ }
/* Add this circuit to the circuit list. If reuse is TRUE then use
* the ft_curckt structure. */
@@ -669,46 +823,6 @@ inp_dodeck(struct line *deck, char *tt, wordlist *end, bool reuse,
else
ct->ci_filename = NULL;
- if (!noparse) {
- for (; options; options = options->li_next) {
- for (s = options->li_line; *s && !isspace(*s); s++)
- ;
- ii = cp_interactive;
- cp_interactive = FALSE;
- wl = cp_lexer(s);
- cp_interactive = ii;
- if (!wl || !wl->wl_word || !*wl->wl_word)
- continue;
- if (eev)
- eev->va_next = cp_setparse(wl);
- else
- ct->ci_vars = eev = cp_setparse(wl);
- while (eev->va_next)
- eev = eev->va_next;
- }
- for (eev = ct->ci_vars; eev; eev = eev->va_next) {
- static int one = 1;
- switch (eev->va_type) {
- case VT_BOOL:
- if_option(ct->ci_ckt, eev->va_name,
- eev->va_type, &one);
- break;
- case VT_NUM:
- if_option(ct->ci_ckt, eev->va_name,
- eev->va_type, (char *) &eev->va_num);
- break;
- case VT_REAL:
- if_option(ct->ci_ckt, eev->va_name,
- eev->va_type, (char *) &eev->va_real);
- break;
- case VT_STRING:
- if_option(ct->ci_ckt, eev->va_name,
- eev->va_type, eev->va_string);
- break;
- } /* switch . . . */
- }
- } /* if (!noparse) . . . */
-
cp_addkword(CT_CKTNAMES, tt);
return;
}
@@ -867,11 +981,12 @@ com_source(wordlist *wl)
}
/* Don't print the title if this is a spice initialisation file. */
- if (ft_nutmeg || substring(INITSTR, owl->wl_word)
- || substring(ALT_INITSTR, owl->wl_word))
+ if (ft_nutmeg || substring(INITSTR, owl->wl_word) || substring(ALT_INITSTR, owl->wl_word)) {
inp_spsource(fp, TRUE, tempfile ? (char *) NULL : wl->wl_word);
- else
+ }
+ else {
inp_spsource(fp, FALSE, tempfile ? (char *) NULL : wl->wl_word);
+ }
cp_interactive = inter;
if (tempfile)
unlink(tempfile);
diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c
index a39d66b08..5feb3777e 100644
--- a/src/frontend/inpcom.c
+++ b/src/frontend/inpcom.c
@@ -31,6 +31,7 @@ Author: 1985 Wayne A. Christopher
*/
#include
+#include
#include "ngspice.h"
#include "cpdefs.h"
#include "ftedefs.h"
@@ -49,12 +50,26 @@ Author: 1985 Wayne A. Christopher
#endif
/* SJB - Uncomment this line for debug tracing */
-/*#define TRACE */
+/*#define TRACE*/
/* static declarations */
static char * readline(FILE *fd);
+static bool is_arith_char(char c);
+static int get_number_terminals( char c );
static void inp_stripcomments_deck(struct line *deck);
static void inp_stripcomments_line(char * s);
+static void inp_fix_for_numparam(struct line *deck);
+static void inp_remove_excess_ws(struct line *deck);
+static void inp_determine_libraries(struct line *deck, char *lib_name);
+static void inp_init_lib_data();
+static void inp_grab_func(struct line *deck);
+static void inp_fix_inst_calls_for_numparam( struct line *deck);
+static void inp_expand_macros_in_func();
+static void inp_expand_macros_in_deck( struct line *deck );
+static void inp_fix_param_values( struct line *deck );
+static void inp_reorder_params( struct line *deck, struct line *list_head, struct line *end );
+static int inp_split_multi_param_lines( struct line *deck, int line_number );
+static void inp_sort_params( struct line *start_card, struct line *end_card, struct line *card_bf_start, struct line *s_c, struct line *e_c );
/*-------------------------------------------------------------------------*
* This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' *
@@ -142,6 +157,810 @@ inp_pathopen(char *name, char *mode)
return (NULL);
}
+static bool
+isquote( char ch )
+{
+ return ( ch == '\'' || ch == '"' );
+}
+
+static void
+inp_fix_gnd_name( struct line *deck ) {
+ struct line *c = deck;
+ char *gnd;
+
+ while ( c != NULL ) {
+ gnd = c->li_line;
+ if ( *gnd == '*' ) { c = c->li_next; continue; }
+ while ( (gnd = strstr( gnd, "gnd " ) ) ) {
+ if ( isspace(*(gnd-1)) ) {
+ memcpy( gnd, "0 ", 4 );
+ }
+ gnd += 4;
+ }
+ c = c->li_next;
+ }
+}
+
+static struct line*
+create_new_card( char *card_str, int *line_number ) {
+ char *str = strdup(card_str);
+ struct line *newcard = alloc(struct line);
+
+ newcard->li_line = str;
+ newcard->li_linenum = *line_number;
+ newcard->li_error = NULL;
+ newcard->li_actual = NULL;
+
+ *line_number = *line_number + 1;
+
+ return newcard;
+}
+
+static int
+get_comma_seperated_values( char *values[], char *str ) {
+ int count = 0;
+ char *ptr, *comma_ptr, keep;
+
+ while ( ( comma_ptr = strstr( str, "," ) ) ) {
+ ptr = comma_ptr - 1;
+ while ( isspace(*ptr) ) ptr--;
+ ptr++; keep = *ptr; *ptr = '\0';
+ values[count++] = strdup(str);
+ *ptr = keep;
+ str = comma_ptr + 1;
+ while ( isspace(*str) ) str++;
+ }
+ values[count++] = strdup(str);
+ return count;
+}
+
+static void
+inp_chk_for_multi_in_vcvs( struct line *deck, int *line_number ) {
+ struct line *c, *a_card, *model_card, *next_card;
+ char *line, *bool_ptr, *str_ptr1, *str_ptr2, keep, *comma_ptr, *xy_values1[5], *xy_values2[5];
+ char *node_str, *ctrl_node_str, *xy_str1, *model_name, *fcn_name;
+ char big_buf[1000];
+ int xy_count1 = 0, xy_count2 = 0;
+
+ for ( c = deck; c != NULL; c = c->li_next ) {
+ str_ptr1 = line = c->li_line;
+ if ( *line == 'e' ) {
+ if ( (bool_ptr = strstr( line, "nand(" )) ||
+ (bool_ptr = strstr( line, "and(" )) ||
+ (bool_ptr = strstr( line, "nor(" )) ||
+ (bool_ptr = strstr( line, "or(" )) ) {
+ while ( !isspace(*str_ptr1) ) str_ptr1++;
+ keep = *str_ptr1; *str_ptr1 = '\0';
+ model_name = strdup(line); *str_ptr1 = keep;
+
+ str_ptr2 = bool_ptr - 1;
+ while ( isspace(*str_ptr1) ) str_ptr1++;
+ while ( isspace(*str_ptr2) ) str_ptr2--;
+ str_ptr2++;
+ keep = *str_ptr2; *str_ptr2 = '\0';
+ node_str = strdup(str_ptr1); *str_ptr2 = keep;
+
+ str_ptr1 = bool_ptr + 1;
+ while ( *str_ptr1 != '(' ) str_ptr1++;
+ *str_ptr1 = '\0'; fcn_name = strdup(bool_ptr);
+ *str_ptr1 = '(';
+
+ str_ptr1 = strstr( str_ptr1, ")" );
+ str_ptr1++;
+ comma_ptr = str_ptr2 = strstr( line, "," );
+ str_ptr2--;
+ while( isspace(*str_ptr2) ) str_ptr2--;
+
+ while ( isspace(*str_ptr1) ) str_ptr1++;
+ if ( *str_ptr2 == '}' ) {
+ while ( *str_ptr2 != '{' ) str_ptr2--;
+ xy_str1 = str_ptr2; str_ptr2--;
+ while ( isspace(*str_ptr2) ) str_ptr2--;
+ str_ptr2++;
+ } else {
+ while ( !isspace(*str_ptr2) ) str_ptr2--;
+ xy_str1 = str_ptr2 + 1;
+ while ( isspace(*str_ptr2) ) str_ptr2--;
+ str_ptr2++;
+ }
+ keep = *str_ptr2; *str_ptr2 = '\0';
+ ctrl_node_str = strdup(str_ptr1); *str_ptr2 = keep;
+
+ str_ptr1 = comma_ptr + 1;
+ while ( isspace(*str_ptr1) ) str_ptr1++;
+ if ( *str_ptr1 == '{' ) {
+ while ( *str_ptr1 != '}' ) str_ptr1++;
+ str_ptr1++;
+ } else {
+ while ( !isspace(*str_ptr1) ) str_ptr1++;
+ }
+ keep = *str_ptr1; *str_ptr1 = '\0';
+ xy_count1 = get_comma_seperated_values( xy_values1, xy_str1 );
+ *str_ptr1 = keep;
+
+ while ( isspace(*str_ptr1) ) str_ptr1++;
+ xy_count2 = get_comma_seperated_values( xy_values2, str_ptr1 );
+
+ // 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");
+ }
+
+ sprintf( big_buf, "%s %%vd[ %s ] %%vd( %s ) %s", model_name, ctrl_node_str, node_str, model_name );
+ a_card = create_new_card( big_buf, line_number );
+ *a_card->li_line = 'a';
+
+ sprintf( big_buf, ".model %s multi_input_pwl ( x = [%s %s] y = [%s %s] model = \"%s\" )", model_name, xy_values1[0], xy_values2[0],
+ xy_values1[1], xy_values2[1], fcn_name );
+ model_card = create_new_card( big_buf, line_number );
+
+ tfree(model_name); tfree(node_str); tfree(fcn_name); tfree(ctrl_node_str);
+ tfree(xy_values1[0]); tfree(xy_values1[1]); tfree(xy_values2[0]); tfree(xy_values2[1]);
+
+ *c->li_line = '*';
+ next_card = c->li_next;
+ c->li_next = a_card;
+ a_card->li_next = model_card;
+ model_card->li_next = next_card;
+ }
+ }
+ }
+}
+
+static void
+inp_add_control_section( struct line *deck, int *line_number ) {
+ struct line *c, *newcard, *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 != NULL; c = c->li_next ) {
+ if ( *c->li_line == '*' ) continue;
+ if ( ciprefix( ".op ", c->li_line ) ) {
+ *c->li_line = '*';
+ op_line = c->li_line + 1;
+ }
+ if ( ciprefix( ".end", c->li_line ) ) found_end = TRUE;
+ if ( found_control && ciprefix( "run", c->li_line ) ) found_run = TRUE;
+
+ if ( ciprefix( ".control", c->li_line ) ) found_control = TRUE;
+ if ( ciprefix( ".endc", c->li_line ) ) {
+ found_control = FALSE;
+
+ if ( !found_run ) {
+ newcard = create_new_card( "run", line_number );
+ prev_card->li_next = newcard;
+ newcard->li_next = c;
+ prev_card = newcard;
+ found_run = TRUE;
+ }
+ if ( cp_getvar( "rawfile", VT_STRING, rawfile ) ) {
+ line = tmalloc( strlen("write") + strlen(rawfile) + 2 );
+ sprintf(line, "write %s", rawfile);
+ newcard = create_new_card( line, line_number );
+ prev_card->li_next = newcard;
+ newcard->li_next = c;
+ prev_card = newcard;
+ tfree(line);
+ }
+ }
+ prev_card = c;
+ }
+ // check if need to add control section
+ if ( !found_run && found_end ) {
+ prev_card = deck->li_next;
+ newcard = create_new_card( ".endc", line_number );
+ deck->li_next = newcard;
+ newcard->li_next = prev_card;
+
+ if ( cp_getvar( "rawfile", VT_STRING, rawfile ) ) {
+ line = tmalloc( strlen("write") + strlen(rawfile) + 2 );
+ sprintf(line, "write %s", rawfile);
+ prev_card = deck->li_next;
+ newcard = create_new_card( line, line_number );
+ deck->li_next = newcard;
+ newcard->li_next = prev_card;
+ tfree(line);
+ }
+ if ( op_line != NULL ) {
+ prev_card = deck->li_next;
+ newcard = create_new_card( op_line, line_number );
+ deck->li_next = newcard;
+ newcard->li_next = prev_card;
+ }
+
+ prev_card = deck->li_next;
+ newcard = create_new_card( "run", line_number );
+ deck->li_next = newcard;
+ newcard->li_next = prev_card;
+
+ prev_card = deck->li_next;
+ newcard = create_new_card( ".control", line_number );
+ deck->li_next = newcard;
+ newcard->li_next = prev_card;
+ }
+}
+
+// look for shell-style end-of-line continuation '\\'
+static bool
+chk_for_line_continuation( char *line ) {
+ char *ptr = line + strlen(line) - 1;
+
+ if ( *line != '*' && *line != '$' ) {
+ while ( ptr >= line && *ptr && isspace(*ptr) ) ptr--;
+
+ if ( (ptr-1) >= line && *ptr == '\\' && *(ptr-1) && *(ptr-1) == '\\' ) {
+ *ptr = ' '; *(ptr-1) = ' ';
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//
+// change .macro --> .subckt
+// .eom --> .ends
+// .subckt (1 2 3) --> .subckt 1 2 3
+// v(1,11) --> 0
+// x1 (1 2 3) --> x1 1 2 3
+// .param func1(x,y) = {x*y} --> .func func1(x,y) {x*y}
+// agauss( ... ) --> 0 no monte carlo analysis
+//
+static void
+inp_fix_macro_param_func_paren_io( struct line *begin_card ) {
+ struct line *card;
+ char *str_ptr, *new_str, *open_paren_ptr, *search_ptr, *fcn_name, *agauss;
+ bool is_func = FALSE;
+
+ for ( card = begin_card; card != NULL; card = card->li_next ) {
+
+ if ( *card->li_line == '*' ) continue;
+
+ // zero out agauss fcn references
+ search_ptr = card->li_line;
+ while( ( agauss = strstr( search_ptr, "agauss(" ) ) ) {
+ *agauss = '0'; agauss++;
+ while( *agauss != ')' ) { *agauss = ' '; agauss++; }
+ *agauss = ' ';
+ search_ptr = agauss;
+ }
+
+ // zero out any voltage node references on .param lines
+ if ( ciprefix( ".param", card->li_line ) ) {
+ search_ptr = card->li_line;
+ while( ( open_paren_ptr = strstr( search_ptr, "(" ) ) ) {
+ fcn_name = open_paren_ptr - 1;
+ while ( *fcn_name != '\0' && fcn_name != search_ptr && (isalnum(*fcn_name) || *fcn_name == '_' ) ) fcn_name--;
+ if ( fcn_name != search_ptr ) fcn_name++;
+ *open_paren_ptr = '\0';
+ if ( strcmp( fcn_name, "v" ) == 0 ) {
+ *open_paren_ptr = ' ';
+ *fcn_name = '0';
+ fcn_name++;
+ while ( *fcn_name != ')' ) { *fcn_name = ' '; fcn_name++; }
+ *fcn_name = ' ';
+ }
+ else {
+ *open_paren_ptr = '(';
+ }
+ search_ptr = open_paren_ptr + 1;
+ }
+ }
+
+ if ( ciprefix( ".macro", card->li_line ) || ciprefix( ".eom", card->li_line ) ) {
+ str_ptr = card->li_line;
+ while( !isspace(*str_ptr) ) str_ptr++;
+
+ if ( ciprefix( ".macro", card->li_line ) ) {
+ new_str = tmalloc( strlen(".subckt") + strlen(str_ptr) + 1 );
+ sprintf( new_str, ".subckt%s", str_ptr );
+ } else {
+ new_str = tmalloc( strlen(".ends") + strlen(str_ptr) + 1 );
+ sprintf( new_str, ".ends%s", str_ptr );
+ }
+
+ tfree( card->li_line );
+ card->li_line = new_str;
+ }
+ if ( ciprefix( ".subckt", card->li_line ) || ciprefix( "x", card->li_line ) ) {
+ str_ptr = card->li_line;
+ while( !isspace(*str_ptr) ) str_ptr++; // skip over .subckt, instance name
+ while( isspace(*str_ptr) ) str_ptr++;
+ if ( ciprefix( ".subckt", card->li_line ) ) {
+ while( !isspace(*str_ptr) ) str_ptr++; // skip over subckt name
+ while( isspace(*str_ptr) ) str_ptr++;
+ }
+ if ( *str_ptr == '(' ) {
+ *str_ptr = ' ';
+ while ( *str_ptr && *str_ptr != '\0' ) {
+ if ( *str_ptr == ')' ) { *str_ptr = ' '; break; }
+ str_ptr++;
+ }
+ }
+ }
+ is_func = FALSE;
+ if ( ciprefix( ".param", card->li_line ) ) {
+ str_ptr = card->li_line;
+ while ( !isspace( *str_ptr ) ) str_ptr++; // skip over .param
+ while ( isspace( *str_ptr ) ) str_ptr++;
+ while ( !isspace( *str_ptr ) && *str_ptr != '=' ) {
+ if ( *str_ptr == '(' ) is_func = TRUE;
+ str_ptr++;
+ }
+
+ if ( is_func ) {
+ if ( ( str_ptr = strstr( card->li_line, "=" ) ) ) *str_ptr = ' ';
+
+ str_ptr = card->li_line + 1;
+ *str_ptr = 'f'; *(str_ptr+1) = 'u'; *(str_ptr+2) = 'n';
+ *(str_ptr+3) = 'c'; *(str_ptr+4) = ' ';
+ }
+ }
+ }
+}
+
+static char *
+get_instance_subckt( char *line )
+{
+ char *equal_ptr = NULL, *end_ptr = line + strlen(line) - 1, *inst_name_ptr = NULL, *inst_name = NULL;
+ char keep = ' ';
+
+ // see if instance has parameters
+ if ( ( equal_ptr = strstr( line, "=" ) ) ) {
+ end_ptr = equal_ptr - 1;
+ while ( isspace(*end_ptr) ) end_ptr--;
+ while ( !isspace(*end_ptr) ) end_ptr--;
+ while ( isspace(*end_ptr) ) end_ptr--;
+ end_ptr++;
+ keep = *end_ptr;
+ *end_ptr = '\0';
+ }
+ inst_name_ptr = end_ptr;
+ while ( !isspace(*inst_name_ptr) ) inst_name_ptr--;
+ inst_name_ptr++;
+
+ inst_name = strdup(inst_name_ptr);
+
+ if ( equal_ptr ) *end_ptr = keep;
+
+ return inst_name;
+}
+
+static char*
+get_subckt_model_name( char *line )
+{
+ char *name = line, *end_ptr = NULL, *subckt_name;
+ char keep;
+
+ while ( !isspace( *name ) ) name++; // eat .subckt|.model
+ while ( isspace( *name ) ) name++;
+
+ end_ptr = name;
+ while ( !isspace( *end_ptr ) ) end_ptr++;
+ keep = *end_ptr;
+ *end_ptr = '\0';
+
+ subckt_name = strdup(name);
+ *end_ptr = keep;
+
+ return subckt_name;
+}
+
+static char*
+get_model_name( char *line, int num_terminals )
+{
+ char *beg_ptr = line, *end_ptr, keep, *model_name = NULL;
+ int i = 0;
+
+ while ( !isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++; // eat device name
+ while ( isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
+
+ for ( i = 0; i < num_terminals; i++ ) {
+ while ( !isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
+ while ( isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
+ }
+ end_ptr = beg_ptr;
+ while ( *end_ptr != '\0' && !isspace( *end_ptr ) ) end_ptr++;
+ keep = *end_ptr;
+ *end_ptr = '\0';
+
+ model_name = strdup( beg_ptr );
+
+ *end_ptr = keep;
+
+ return model_name;
+}
+
+static char *
+get_adevice_model_name( char *line )
+{
+ char *model_name, *ptr_end = line + strlen(line), *ptr_beg, keep;
+
+ while ( isspace( *(ptr_end-1) ) ) ptr_end--;
+ ptr_beg = ptr_end - 1;
+
+ while ( !isspace(*ptr_beg) ) ptr_beg--;
+ ptr_beg++;
+ keep = *ptr_end; *ptr_end = '\0';
+ model_name = strdup(ptr_beg);
+ *ptr_end = keep;
+
+ return model_name;
+}
+
+static void
+get_subckts_for_subckt( struct line *start_card, char *subckt_name,
+ char *used_subckt_names[], int *num_used_subckt_names,
+ char *used_model_names[], int *num_used_model_names,
+ bool has_models )
+{
+ struct line *card;
+ char *line = NULL, *curr_subckt_name, *inst_subckt_name, *model_name, *new_names[100];
+ bool found_subckt = FALSE, have_subckt = FALSE, found_model = FALSE;
+ int i, num_terminals = 0, tmp_cnt = 0;
+
+ for ( card = start_card; card != NULL; card = card->li_next ) {
+ line = card->li_line;
+
+ if ( *line == '*' ) continue;
+
+ if ( ( ciprefix( ".ends", line ) || ciprefix( ".eom", line ) ) && found_subckt )
+ break;
+
+ if ( ciprefix( ".subckt", line ) || ciprefix( ".macro", line ) ) {
+ 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' ) {
+ inst_subckt_name = get_instance_subckt( line );
+ have_subckt = FALSE;
+ for ( i = 0; i < *num_used_subckt_names; i++ )
+ if ( strcmp( used_subckt_names[i], inst_subckt_name ) == 0 )
+ have_subckt = TRUE;
+ if ( !have_subckt ) {
+ new_names[tmp_cnt++] = used_subckt_names[*num_used_subckt_names] = inst_subckt_name;
+ *num_used_subckt_names = *num_used_subckt_names + 1;
+ }
+ else tfree( inst_subckt_name );
+ }
+ else if ( *line == 'a' ) {
+ model_name = get_adevice_model_name( line );
+ found_model = FALSE;
+ for ( i = 0; i < *num_used_model_names; i++ )
+ if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
+ if ( !found_model ) {
+ used_model_names[*num_used_model_names] = model_name;
+ *num_used_model_names = *num_used_model_names + 1;
+ }
+ else tfree( model_name );
+ }
+ else if ( has_models ) {
+ num_terminals = get_number_terminals( *line );
+
+ if ( num_terminals != 0 ) {
+ model_name = get_model_name( line, num_terminals );
+
+ if ( isalpha( *model_name ) ) {
+ found_model = FALSE;
+ for ( i = 0; i < *num_used_model_names; i++ )
+ if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
+ if ( !found_model ) {
+ used_model_names[*num_used_model_names] = model_name;
+ *num_used_model_names = *num_used_model_names + 1;
+ }
+ else tfree( model_name );
+ }
+ else {
+ tfree( model_name );
+ }
+ }
+ }
+ }
+ }
+ // now make recursive call on instances just found above
+ for ( i = 0; i < tmp_cnt; i++ )
+ get_subckts_for_subckt( start_card, new_names[i], used_subckt_names, num_used_subckt_names,
+ used_model_names, num_used_model_names, has_models );
+}
+
+/*
+ check if current token matches model bin name -- .[0-9]+
+ */
+static bool
+model_bin_match( char* token, char* model_name )
+{
+ char* dot_char;
+ bool flag = FALSE;
+
+ if ( strncmp( model_name, token, strlen(token) ) == 0 ) {
+ if ( (dot_char = strstr( model_name, "." )) ) {
+ flag = TRUE;
+ dot_char++;
+ while( *dot_char != '\0' ) {
+ if ( !isdigit( *dot_char ) ) {
+ flag = FALSE;
+ break;
+ }
+ dot_char++;
+ }
+ }
+ }
+ return flag;
+}
+
+/*
+ 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 line *start_card )
+{
+ struct line *card;
+ char *used_subckt_names[1000], *used_model_names[1000], *line = NULL, *subckt_name, *model_name;
+ int num_used_subckt_names = 0, num_used_model_names = 0, i = 0, num_terminals = 0, tmp_cnt = 0;
+ bool processing_subckt = FALSE, found_subckt = FALSE, remove_subckt = FALSE, found_model = FALSE, has_models = FALSE;
+
+ for ( card = start_card; card != NULL; card = card->li_next ) {
+ if ( ciprefix( ".model", card->li_line ) ) has_models = TRUE;
+ if ( ciprefix( ".param", card->li_line ) && !strstr( card->li_line, "=" ) ) *card->li_line = '*';
+ }
+
+ for ( card = start_card; card != NULL; card = card->li_next ) {
+ line = card->li_line;
+
+ if ( *line == '*' ) 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' ) {
+ subckt_name = get_instance_subckt( line );
+
+ found_subckt = FALSE;
+ for ( i = 0; i < num_used_subckt_names; i++ )
+ if ( strcmp( used_subckt_names[i], subckt_name ) == 0 ) found_subckt = TRUE;
+ if ( !found_subckt ) {
+ used_subckt_names[num_used_subckt_names++] = subckt_name;
+ tmp_cnt++;
+ }
+ else tfree( subckt_name );
+ }
+ else if ( *line == 'a' ) {
+ model_name = get_adevice_model_name( line );
+ found_model = FALSE;
+ for ( i = 0; i < num_used_model_names; i++ )
+ if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
+ if ( !found_model ) used_model_names[num_used_model_names++] = model_name;
+ else tfree( model_name );
+ }
+ else if ( has_models ) {
+ num_terminals = get_number_terminals( *line );
+
+ if ( num_terminals != 0 ) {
+ model_name = get_model_name( line, num_terminals );
+
+ if ( isalpha( *model_name ) ) {
+ found_model = FALSE;
+ for ( i = 0; i < num_used_model_names; i++ )
+ if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
+ if ( !found_model ) used_model_names[num_used_model_names++] = model_name;
+ else tfree( model_name );
+ } else {
+ tfree( model_name );
+ }
+ }
+ }
+ }
+ }
+ for ( i = 0; i < tmp_cnt; i++ )
+ get_subckts_for_subckt( start_card, used_subckt_names[i], used_subckt_names,
+ &num_used_subckt_names, used_model_names, &num_used_model_names, has_models );
+
+ // comment out any unused subckts
+ for ( card = start_card; card != NULL; card = card->li_next ) {
+ line = card->li_line;
+
+ if ( *line == '*' ) continue;
+
+ if ( ciprefix( ".subckt", line ) || ciprefix( ".macro", line ) ) {
+ subckt_name = get_subckt_model_name( line );
+ remove_subckt = TRUE;
+ for ( i = 0; i < num_used_subckt_names; i++ )
+ if ( strcmp( used_subckt_names[i], subckt_name ) == 0 ) remove_subckt = FALSE;
+ tfree(subckt_name);
+ }
+ if ( ciprefix( ".ends", line ) || ciprefix( ".eom", line ) ) {
+ if ( remove_subckt ) *line = '*';
+ remove_subckt = FALSE;
+ }
+ if ( remove_subckt ) *line = '*';
+ else if ( has_models && ciprefix( ".model", line ) ) {
+ model_name = get_subckt_model_name( line );
+
+ found_model = FALSE;
+ for ( i = 0; i < num_used_model_names; i++ )
+ if ( strcmp( used_model_names[i], model_name ) == 0 || model_bin_match( used_model_names[i], model_name ) ) found_model = TRUE;
+ if ( !found_model ) *line = '*';
+ }
+ }
+ for ( i = 0; i < num_used_subckt_names; i++ ) tfree(used_subckt_names[i]);
+ for ( i = 0; i < num_used_model_names; i++ ) tfree(used_model_names[i]);
+}
+
+static char*
+inp_fix_ternary_operator_str( char *line )
+{
+ char *conditional, *if_str, *else_str, *question, *colon, keep, *str_ptr, *str_ptr2, *new_str;
+ char *paren_ptr = NULL, *end_str = NULL, *beg_str = NULL;
+ int count = 0;
+
+ if ( !strstr( line, "?" ) && !strstr( line, ":" ) ) return line;
+
+ str_ptr = line;
+ if ( ciprefix( ".param", line ) || ciprefix( ".func", line ) || ciprefix( ".meas", line ) ) {
+
+ str_ptr = line;
+ if ( ciprefix( ".param", line ) || ciprefix( ".meas", line ) ) str_ptr = strstr( line, "=" );
+ else str_ptr = strstr( line, ")" );
+
+ str_ptr++;
+ while( isspace(*str_ptr) ) str_ptr++;
+ if ( *str_ptr == '{' ) { str_ptr++; while( isspace(*str_ptr) ) str_ptr++; }
+
+ question = strstr( str_ptr, "?" );
+ paren_ptr = strstr( str_ptr, "(" );
+
+ if ( paren_ptr != NULL && paren_ptr < question ) {
+ str_ptr = question;
+ while ( *str_ptr != '(' ) str_ptr--;
+ *str_ptr = '\0';
+ beg_str = strdup(line);
+ *str_ptr = '(';
+ str_ptr++;
+ paren_ptr = NULL;
+ }
+ else {
+ keep = *str_ptr; *str_ptr = '\0';
+ beg_str = strdup(line);
+ *str_ptr = keep;
+ }
+ }
+
+ // get conditional
+ str_ptr2 = question = strstr( str_ptr, "?" );
+ str_ptr2--;
+ while ( isspace(*str_ptr2) ) str_ptr2--;
+ if ( *str_ptr2 == ')' ) {
+ while ( *str_ptr != '(' ) str_ptr--;
+ }
+ str_ptr2++; keep = *str_ptr2; *str_ptr2 = '\0';
+ conditional = strdup(str_ptr);
+ *str_ptr2 = keep;
+
+ // get if
+ str_ptr = question + 1;
+ while ( isspace(*str_ptr) ) str_ptr++;
+ if ( *str_ptr == '(' ) {
+ // find closing paren
+ count = 1;
+ str_ptr2 = str_ptr + 1;
+ while ( count != 0 && *str_ptr2 != '\0' ) {
+ str_ptr2++;
+ if ( *str_ptr2 == '(' ) count++;
+ if ( *str_ptr2 == ')' ) count--;
+ }
+ if ( count != 0 ) {
+ fprintf(stderr, "ERROR: problem parsing 'if' of ternary string %s!\n", line);
+ exit (-1);
+ }
+ colon = str_ptr2 + 1;
+ while ( *colon != ':' && *colon != '\0' ) colon++;
+ if ( *colon != ':' ) {
+ fprintf(stderr,"ERROR: problem parsing ternary string (finding ':') %s!\n", line);
+ exit(-1);
+ }
+ }
+ else if ( ( colon = strstr( str_ptr, ":" ) ) ) {
+ str_ptr2 = colon - 1;
+ while ( isspace(*str_ptr2) ) str_ptr2--;
+ }
+ else {
+ fprintf(stderr,"ERROR: problem parsing ternary string (missing ':') %s!\n", line);
+ exit(-1);
+ }
+ str_ptr2++; keep = *str_ptr2; *str_ptr2 = '\0';
+ if_str = inp_fix_ternary_operator_str(strdup(str_ptr));
+ *str_ptr2 = keep;
+
+ // get else
+ str_ptr = colon + 1;
+ while ( isspace(*str_ptr) ) str_ptr++;
+ if ( paren_ptr != NULL ) {
+ // find end paren ')'
+ bool found_paren = FALSE;
+ count = 0; str_ptr2 = str_ptr;
+ while ( *str_ptr2 != '\0' ) {
+ if ( *str_ptr2 == '(' ) { count++; found_paren = TRUE; }
+ if ( *str_ptr2 == ')' ) count--;
+ str_ptr2++;
+ if ( found_paren && count == 0 ) break;
+ }
+ if ( found_paren && count != 0 ) {
+ fprintf( stderr, "ERROR: problem parsing ternary line %s!\n", line );
+ exit(-1);
+ }
+ keep = *str_ptr2;
+ *str_ptr2 = '\0';
+ else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
+ if ( keep != '}' ) {
+ end_str = inp_fix_ternary_operator_str(strdup(str_ptr2+1));
+ } else {
+ *str_ptr2 = keep;
+ end_str = strdup(str_ptr2);
+ }
+ *str_ptr2 = keep;
+ }
+ else {
+ if ( ( str_ptr2 = strstr( str_ptr, "}" ) ) ) {
+ *str_ptr2 = '\0';
+ else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
+ *str_ptr2 = '}';
+ end_str = strdup(str_ptr2);
+ } else {
+ else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
+ }
+ }
+
+ if ( end_str != NULL ) {
+ if ( beg_str != NULL ) {
+ new_str = tmalloc( strlen(beg_str) + strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + strlen(end_str) + 5 );
+ sprintf( new_str, "%sternary_fcn(%s,%s,%s)%s", beg_str, conditional, if_str, else_str, end_str );
+ } else {
+ new_str = tmalloc( strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + strlen(end_str) + 5 );
+ sprintf( new_str, "ternary_fcn(%s,%s,%s)%s", conditional, if_str, else_str, end_str );
+ }
+ }
+ else {
+ if ( beg_str != NULL ) {
+ new_str = tmalloc( strlen(beg_str) + strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + 5 );
+ sprintf( new_str, "%sternary_fcn(%s,%s,%s)", beg_str, conditional, if_str, else_str );
+ } else {
+ new_str = tmalloc( strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + 5 );
+ sprintf( new_str, "ternary_fcn(%s,%s,%s)", conditional, if_str, else_str );
+ }
+ }
+
+ tfree(line);
+ tfree(conditional); tfree(if_str); tfree(else_str);
+ if ( beg_str != NULL ) tfree(beg_str);
+ if ( end_str != NULL ) tfree(end_str);
+
+ return new_str;
+}
+
+static void
+inp_fix_ternary_operator( struct line *start_card )
+{
+ struct line *card;
+ char *line;
+
+ for ( card = start_card; card != NULL; card = card->li_next ) {
+ line = card->li_line;
+
+ if ( *line == '*' ) continue;
+ if ( strstr( line, "?" ) && strstr( line, ":" ) ) {
+ card->li_line = inp_fix_ternary_operator_str( line );
+ }
+ }
+}
/*-------------------------------------------------------------------------
* Read the entire input file and return a pointer to the first line of
@@ -149,14 +968,19 @@ inp_pathopen(char *name, char *mode)
* *data.
*-------------------------------------------------------------------------*/
void
-inp_readall(FILE *fp, struct line **data)
+inp_readall(FILE *fp, struct line **data, int call_depth, char *dir_name)
{
- struct line *end = NULL, *cc = NULL, *prev = NULL, *working, *newcard;
- char *buffer, *s, *t, c;
+ struct line *end = NULL, *cc = NULL, *prev = NULL, *working, *newcard, *start_lib, *global_card, *tmp_ptr = NULL, *tmp_ptr2 = NULL;
+ char *buffer = NULL, *s, *t, *y, *z, c;
/* segfault fix */
- char *copys=NULL;
+ char *copys=NULL, big_buff[5000], big_buff2[5000];
+ char *global_copy = NULL, keep_char;
int line_number = 1; /* sjb - renamed to avoid confusion with struct line */
+ int line_count = 0;
FILE *newfp;
+ int i, j;
+ bool found_library, found_lib_name, found_end = FALSE, shell_eol_continuation = FALSE;
+ bool dir_name_flag = FALSE;
/* Must set this to NULL or non-tilde includes segfault. -- Tim Molteno */
/* copys = NULL; */ /* This caused a parse error with gcc 2.96. Why??? */
@@ -167,14 +991,32 @@ inp_readall(FILE *fp, struct line **data)
char ipc_buffer[1025]; /* Had better be big enough */
int ipc_len;
+ if ( call_depth == 0 ) {
+ num_subckt_w_params = 0;
+ num_libraries = 0;
+ num_functions = 0;
+ global = NULL;
+ found_end = FALSE;
+ }
+
/* First read in all lines & put them in the struct cc */
while (1) {
/* 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 = tmalloc( strlen(big_buff) + 1 );
+ strcpy(buffer, big_buff);
+ }
+ }
+ else {
buffer = readline(fp);
- if(! buffer)
- break;
+ if(! buffer) {
+ break;
+ }
+ }
}
else {
/* else, get the line from the ipc channel. */
@@ -202,50 +1044,114 @@ inp_readall(FILE *fp, struct line **data)
#ifdef TRACE
/* SDB debug statement */
- printf ("in inp_readall, just read '%s' . . .\n", buffer);
+ printf ("in inp_readall, 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 ( (strcmp(buffer,"\n") == 0) || (strcmp(buffer,"\r\n") == 0) ) {
+ if ( call_depth != 0 || (call_depth == 0 && cc != NULL) ) {
continue;
+ }
}
-
if (*buffer == '@') {
tfree(buffer); /* was allocated by readline() */
break;
}
+ /* now handle .lib statements */
+ if (ciprefix(".lib", buffer)) {
+ for ( s = buffer; *s && !isspace(*s); s++ ); /* skip over .lib */
+ while ( isspace(*s) || isquote(*s) ) s++; /* advance past space chars */
+ if ( !*s ) { /* if at end of line, error */
+ fprintf(cp_err, "Error: .lib filename missing\n");
+ tfree(buffer); /* was allocated by readline() */
+ continue;
+ } /* Now s points to first char after .lib */
+ for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ ); /* skip to end of word */
+ y = t;
+ while ( isspace(*y) || isquote(*y) ) y++; /* advance past space chars */
+ // check if rest of line commented out
+ if ( *y && *y != '$' ) { /* .lib */
+ for ( z = y; *z && !isspace(*z) && !isquote(*z); z++ );
+ c = *t;
+ *t = '\0';
+ *z = '\0';
- /* loop through 'buffer' until end is reached. Then test for
- premature end. If premature end is reached, spew
- error and zap the line. */
- for (s = buffer; *s && (*s != '\n'); s++);
- if (!*s) {
- fprintf(cp_err, "Warning: premature EOF\n");
- }
- *s = '\0'; /* Zap the newline. */
-
- if(*(s-1) == '\r') /* Zop the carriage return under windows */
- *(s-1) = '\0';
+ if ( *s == '~' ) {
+ copys = cp_tildexpand(s); /* allocates memory, but can also return NULL */
+ if( copys != NULL ) {
+ s = copys; /* reuse s, but remember, buffer still points to allocated memory */
+ }
+ }
+
+ /* lower case the file name for later string compares */
+ char *s_ptr, *s_lower = strdup(s);
+ for(s_ptr = s_lower; *s_ptr && (*s_ptr != '\n'); s_ptr++) *s_ptr = tolower(*s_ptr);
+
+ found_library = FALSE;
+ for ( i = 0; i < num_libraries; i++ ) {
+ if ( strcmp( library_file[i], s_lower ) == 0 ) {
+ found_library = TRUE;
+ break;
+ }
+ }
+ if ( found_library ) {
+ if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
+ } else {
+ if ( dir_name != NULL ) sprintf( big_buff2, "%s/%s", dir_name, s );
+ else sprintf( big_buff2, "./%s", s );
+ dir_name_flag = FALSE;
+ if ( !( newfp = inp_pathopen( s, "r" ) ) ) {
+ dir_name_flag = TRUE;
+ if ( !( newfp = inp_pathopen( big_buff2, "r" ) ) ) {
+ perror(s);
+ if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
+ tfree(buffer); /* allocated by readline() above */
+ continue;
+ }
+ }
+ if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
+
+ library_file[num_libraries++] = strdup(s_lower);
+
+ if ( dir_name_flag == FALSE ) {
+ char *s_dup = strdup(s);
+ inp_readall(newfp, &libraries[num_libraries-1], call_depth+1, dirname(s_dup));
+ tfree(s_dup);
+ }
+ else
+ inp_readall(newfp, &libraries[num_libraries-1], call_depth+1, dir_name);
+
+ fclose(newfp);
+ }
+ *t = c;
+ tfree(s_lower);
+
+ /* Make the .lib a comment */
+ *buffer = '*';
+ }
+ } /* end of .lib handling */
/* now handle .include statements */
- if (ciprefix(".include", buffer)) {
+ if (ciprefix(".include", buffer) || ciprefix(".inc", buffer)) {
for (s = buffer; *s && !isspace(*s); s++) /* advance past non-space chars */
;
- while (isspace(*s)) /* now advance past space chars */
+ while (isspace(*s) || isquote(*s)) /* now advance past space chars */
s++;
if (!*s) { /* if at end of line, error */
fprintf(cp_err, "Error: .include filename missing\n");
tfree(buffer); /* was allocated by readline() */
continue;
} /* Now s points to first char after .include */
- for (t = s; *t && !isspace(*t); t++) /* now advance past non-space chars */
+ for (t = s; *t && !isspace(*t) && !isquote(*t); t++) /* now advance past non-space chars */
;
*t = '\0'; /* place \0 and end of file name in buffer */
-
+
if (*s == '~') {
copys = cp_tildexpand(s); /* allocates memory, but can also return NULL */
if(copys != NULL) {
@@ -254,20 +1160,33 @@ inp_readall(FILE *fp, struct line **data)
}
/* open file specified by .include statement */
+ if ( dir_name != NULL ) sprintf( big_buff2, "%s/%s", dir_name, s );
+ else sprintf( big_buff2, "./%s", s );
+ dir_name_flag = FALSE;
if (!(newfp = inp_pathopen(s, "r"))) {
+ dir_name_flag = TRUE;
+ if ( !( newfp = inp_pathopen( big_buff2, "r" ) ) ) {
perror(s);
if(copys) {
- tfree(copys); /* allocated by the cp_tildexpand() above */
+ tfree(copys); /* allocated by the cp_tildexpand() above */
}
tfree(buffer); /* allocated by readline() above */
continue;
+ }
}
if(copys) {
tfree(copys); /* allocated by the cp_tildexpand() above */
}
-
- inp_readall(newfp, &newcard); /* read stuff in include file into netlist */
+
+ if ( dir_name_flag == FALSE ) {
+ char *s_dup = strdup(s);
+ inp_readall(newfp, &newcard, call_depth+1, dirname(s_dup)); /* read stuff in include file into netlist */
+ tfree(s_dup);
+ }
+ else
+ inp_readall(newfp, &newcard, call_depth+1, dir_name); /* read stuff in include file into netlist */
+
(void) fclose(newfp);
/* Make the .include a comment */
@@ -301,31 +1220,180 @@ inp_readall(FILE *fp, struct line **data)
(void) strncpy(buffer + 1, "end of:", 7);
} /* end of .include handling */
+ /* loop through 'buffer' until end is reached. Then test for
+ premature end. If premature end is reached, spew
+ error and zap the line. */
+ if ( !ciprefix( "write", buffer ) ) { // exclude 'write' command so filename case preserved
+ for (s = buffer; *s && (*s != '\n') && (*s != '\0'); s++) *s = tolower(*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';
+ }
+
+ if (ciprefix(".end", buffer) && strlen(buffer) == 4 ) {
+ found_end = TRUE;
+ *buffer = '*';
+ }
+
+ if ( ciprefix( ".global", buffer ) ) {
+ for ( s = buffer; *s && !isspace(*s); s++ );
+
+ if ( global == NULL ) {
+ global = strdup(buffer);
+ } else {
+ global_copy = tmalloc( strlen(global) + strlen(s) + 1 );
+ sprintf( global_copy, "%s%s", global, s );
+ tfree(global);
+ global = global_copy;
+ }
+ *buffer = '*';
+ }
+
+ if ( shell_eol_continuation ) {
+ char *new_buffer = tmalloc( strlen(buffer) + 2);
+ sprintf( new_buffer, "+%s", buffer );
+
+ tfree(buffer);
+ buffer = new_buffer;
+ }
+
+ shell_eol_continuation = chk_for_line_continuation( buffer );
+
/* now check if this is the first pass (i.e. end points to null) */
- if (end) { /* end already exists */
+ if (end) { /* end already exists */
end->li_next = alloc(struct line); /* create next card */
end = end->li_next; /* point to next card */
- } else { /* End doesn't exist. Create it. */
- end = cc = alloc(struct line); /* note that cc points to beginning
- of deck, end to the end */
- }
-
+ } else { /* End doesn't exist. Create it. */
+ end = cc = alloc(struct line); /* note that cc points to beginning
+ of deck, end to the end */
+ }
+
/* now put buffer into li */
- end->li_next = NULL;
- end->li_error = NULL;
- end->li_actual = NULL;
- end->li_line = buffer;
- end->li_linenum = line_number++;
- }
+ end->li_next = NULL;
+ end->li_error = NULL;
+ end->li_actual = NULL;
+ end->li_line = buffer;
+ end->li_linenum = line_number++;
+ } /* end while ((buffer = readline(fp))) */
if (!end) { /* No stuff here */
*data = NULL;
return;
- } /* end while ((buffer = readline(fp))) */
+ }
/* This should be freed because we are done with it. */
/* tfree(buffer); */
+ if ( call_depth == 0 && found_end == TRUE) {
+ if ( global == NULL ) {
+ global = tmalloc( strlen(".global gnd") + 1 );
+ sprintf( global, ".global gnd" );
+ }
+ global_card = alloc(struct line);
+ global_card->li_error = NULL;
+ global_card->li_actual = NULL;
+ global_card->li_line = global;
+ global_card->li_linenum = 1;
+
+ prev = cc->li_next;
+ cc->li_next = global_card;
+ global_card->li_next = prev;
+
+ inp_init_lib_data();
+ inp_determine_libraries(cc, NULL);
+ }
+
+ /*
+ add libraries
+ */
+ found_lib_name = FALSE;
+ if ( call_depth == 0 ) {
+ for( i = 0; i < num_libraries; i++ ) {
+ working = libraries[i];
+ while ( working ) {
+ buffer = working->li_line;
+
+ if ( found_lib_name && ciprefix(".endl", buffer) ) {
+ /* Make the .endl a comment */
+ *buffer = '*';
+ found_lib_name = FALSE;
+
+ /* set pointer and continue to avoid deleting below */
+ tmp_ptr2 = working->li_next;
+ working->li_next = tmp_ptr;
+ working = tmp_ptr2;
+
+ //end = working;
+ //working = working->li_next;
+ //end->li_next = NULL;
+
+ continue;
+ }
+
+ if ( ciprefix(".lib", buffer) ) {
+ if ( found_lib_name == TRUE ) {
+ fprintf( stderr, "ERROR: .lib is missing .endl!\n" );
+ exit(-1);
+ }
+
+ for ( s = buffer; *s && !isspace(*s); s++ ); /* skip over .lib */
+ while ( isspace(*s) || isquote(*s) ) s++; /* advance past space chars */
+ for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ ); /* skip to end of word */
+ keep_char = *t;
+ *t = '\0';
+ /* see if library we want to copy */
+ found_lib_name = FALSE;
+ for( j = 0; j < num_lib_names[i]; j++ ) {
+ if ( strcmp( library_name[i][j], s ) == 0 ) {
+ found_lib_name = TRUE;
+ start_lib = working;
+
+ /* make the .lib a comment */
+ *buffer = '*';
+
+ tmp_ptr = library_ll_ptr[i][j]->li_next;
+ library_ll_ptr[i][j]->li_next = working;
+
+ // renumber lines
+ for ( start_lib = working; !ciprefix(".endl", start_lib->li_line); start_lib = start_lib->li_next )
+ start_lib->li_linenum = line_number++;
+ start_lib->li_linenum = line_number++; // renumber endl line
+
+ //end->li_next = working;
+
+ break;
+ }
+ }
+ *t = keep_char;
+ }
+ prev = working;
+ working = working->li_next;
+
+ if ( found_lib_name == FALSE ) {
+ tfree(prev->li_line);
+ tfree(prev);
+ }
+ } /* end while */
+ }
+ if ( found_end == TRUE ) {
+ end->li_next = alloc(struct line); /* create next card */
+ end = end->li_next; /* point to next card */
+
+ buffer = tmalloc( strlen( ".end" ) + 1 );
+ sprintf( buffer, ".end" );
+
+ /* now put buffer into li */
+ end->li_next = NULL;
+ end->li_error = NULL;
+ end->li_actual = NULL;
+ end->li_line = buffer;
+ end->li_linenum = line_number++;
+ }
+ }
/* Now clean up li: remove comments & stitch together continuation lines. */
working = cc->li_next; /* cc points to head of deck. Start with the
@@ -349,7 +1417,7 @@ inp_readall(FILE *fp, struct line **data)
switch (c) {
case '#':
- case '$':
+ case '$':
case '*':
case '\0':
/* this used to be commented out. Why? */
@@ -398,6 +1466,35 @@ inp_readall(FILE *fp, struct line **data)
}
}
+ working = cc->li_next;
+
+ inp_fix_for_numparam(working);
+ inp_remove_excess_ws(working);
+
+ if ( call_depth == 0 ) {
+ comment_out_unused_subckt_models(working);
+
+ line_number = inp_split_multi_param_lines(working, line_number);
+
+ inp_fix_macro_param_func_paren_io(working);
+ inp_fix_ternary_operator(working);
+ inp_grab_func(working);
+
+ inp_expand_macros_in_func();
+ inp_expand_macros_in_deck(working);
+ inp_fix_param_values(working);
+
+ // get end card as last card in list; end card pntr does not appear to always
+ // be correct at this point
+ for(newcard = working; newcard != NULL; newcard = newcard->li_next)
+ end = newcard;
+
+ inp_reorder_params(working, cc, end);
+ inp_fix_inst_calls_for_numparam(working);
+ inp_fix_gnd_name(working);
+ inp_chk_for_multi_in_vcvs(working, &line_number);
+ inp_add_control_section(working, &line_number);
+ }
*data = cc;
return;
}
@@ -431,15 +1528,14 @@ inp_casefix(char *string)
return;
#endif
}
-
-
+
/* Strip all end-of-line comments from a deck */
static void
inp_stripcomments_deck(struct line *deck)
{
struct line *c=deck;
while( c!=NULL) {
- inp_stripcomments_line(c->li_line);
+ inp_stripcomments_line(c->li_line);
c= c->li_next;
}
}
@@ -504,3 +1600,1437 @@ inp_stripcomments_line(char * s)
*d='\0'; /* terminate line in new location */
}
+
+static void
+inp_change_quotes( char *s )
+{
+ bool first_quote = FALSE;
+
+ while ( *s ) {
+ if ( *s == '\'' ) {
+ if ( first_quote == FALSE ) {
+ *s = '{';
+ first_quote = TRUE;
+ } else {
+ *s = '}';
+ first_quote = FALSE;
+ }
+ }
+ s++;
+ }
+}
+
+static char*
+inp_fix_subckt( char *s )
+{
+ struct line *head=NULL, *newcard=NULL, *start_card=NULL, *end_card=NULL, *prev_card=NULL, *c=NULL;
+ char *equal = strstr( s, "=" );
+ char *beg, *buffer, *ptr1, *ptr2, *str, *new_str = NULL;
+ char keep;
+ int num_params = 0, i = 0;
+
+ if ( !strstr( s, "params:" ) && equal != NULL ) {
+ /* get subckt name (ptr1 will point to name) */
+ for ( ptr1 = s; *ptr1 && !isspace(*ptr1); ptr1++ );
+ while ( isspace(*ptr1) ) ptr1++;
+ for ( ptr2 = ptr1; *ptr2 && !isspace(*ptr2) && !isquote(*ptr2); ptr2++ );
+
+ keep = *ptr2;
+ *ptr2 = '\0';
+
+ subckt_w_params[num_subckt_w_params++] = strdup(ptr1);
+
+ *ptr2 = keep;
+
+ /* go to beginning of first parameter word */
+ /* s will contain only subckt definition */
+ /* beg will point to start of param list */
+ for ( beg = equal-1; *beg && isspace(*beg); beg-- );
+ for ( ; *beg && !isspace(*beg); beg-- );
+ *beg = '\0';
+ beg++;
+
+ head = alloc(struct line);
+ /* create list of parameters that need to get sorted */
+ while ( ( ptr1 = strstr( beg, "=" ) ) ) {
+ ptr2 = ptr1+1;
+ ptr1--;
+ while ( isspace(*ptr1) ) ptr1--;
+ while ( !isspace(*ptr1) && *ptr1 != '\0' ) ptr1--;
+ ptr1++; /* ptr1 points to beginning of parameter */
+
+ while ( isspace(*ptr2) ) ptr2++;
+ while ( *ptr2 && !isspace(*ptr2) ) ptr2++; /* ptr2 points to end of parameter */
+
+ keep = *ptr2;
+ *ptr2 = '\0';
+ beg = ptr2+1;
+
+ newcard = alloc(struct line);
+ str = strdup(ptr1);
+
+ newcard->li_line = str;
+ newcard->li_next = NULL;
+
+ if ( start_card == NULL ) head->li_next = start_card = newcard;
+ else prev_card->li_next = newcard;
+
+ prev_card = end_card = newcard;
+ num_params++;
+ }
+
+ /* now sort parameters in order of dependencies */
+ inp_sort_params( start_card, end_card, head, start_card, end_card );
+
+ /* create new ordered parameter string for subckt call */
+ c=head->li_next;
+ tfree(head);
+ for( i = 0; i < num_params && c!= NULL; i++ ) {
+ if ( new_str == NULL ) new_str = strdup(c->li_line);
+ else {
+ str = new_str;
+ new_str = tmalloc( strlen(str) + strlen(c->li_line) + 2 );
+ sprintf( new_str, "%s %s", str, c->li_line );
+ tfree(str);
+ }
+ tfree(c->li_line);
+ head = c;
+ c = c->li_next;
+ tfree(head);
+ }
+
+ /* create buffer and insert params: */
+ buffer = tmalloc( strlen(s) + 9 + strlen(new_str) + 1 );
+ sprintf( buffer, "%s params: %s", s, new_str );
+
+ tfree(s); tfree(new_str);
+
+ s = buffer;
+ }
+ return s;
+}
+
+static char*
+inp_remove_ws( char *s )
+{
+ char *big_buff = tmalloc( strlen(s) + 1 );
+ int big_buff_index = 0;
+ char *buffer, *curr;
+ bool is_expression = FALSE;
+ curr = s;
+
+ while ( *curr != '\0' ) {
+ if ( *curr == '{' ) is_expression = TRUE;
+ if ( *curr == '}' ) is_expression = FALSE;
+
+ big_buff[big_buff_index++] = *curr;
+ if ( *curr == '=' || (is_expression && (is_arith_char(*curr) || *curr == ',')) ) {
+ curr++;
+ while ( isspace(*curr) ) curr++;
+
+ if ( *curr == '{' ) is_expression = TRUE;
+ if ( *curr == '}' ) is_expression = FALSE;
+
+ big_buff[big_buff_index++] = *curr;
+ }
+ curr++;
+ if ( isspace(*curr) ) {
+ while ( isspace(*curr) ) curr++;
+ if ( is_expression ) {
+ if ( *curr != '=' && !is_arith_char(*curr) && *curr != ',' ) big_buff[big_buff_index++] = ' ';
+ } else {
+ if ( *curr != '=' ) big_buff[big_buff_index++] = ' ';
+ }
+ }
+ }
+ big_buff[big_buff_index++] = *curr;
+
+ buffer = tmalloc( strlen(big_buff) + 1 );
+ sprintf( buffer, "%s", big_buff );
+
+ tfree(s);
+ tfree(big_buff);
+
+ return buffer;
+}
+
+static void
+inp_fix_for_numparam(struct line *deck)
+{
+ struct line *c=deck;
+ while( c!=NULL) {
+ if ( ciprefix( ".modif", c->li_line ) ) *c->li_line = '*';
+ if ( ciprefix( "*lib", c->li_line ) ) {
+ c = c->li_next;
+ continue;
+ }
+
+ if ( !ciprefix( "*lib", c->li_line ) && !ciprefix( "*inc", c->li_line ) )
+ inp_change_quotes(c->li_line);
+
+ if ( ciprefix( ".subckt", c->li_line ) ) {
+ c->li_line = inp_fix_subckt(c->li_line);
+ }
+
+ c= c->li_next;
+ }
+}
+
+static void
+inp_remove_excess_ws(struct line *deck )
+{
+ struct line *c = deck;
+ while ( c != NULL ) {
+ if ( *c->li_line == '*' ) { c = c->li_next; continue; }
+ c->li_line = inp_remove_ws(c->li_line);
+ c = c->li_next;
+ }
+}
+
+static void
+inp_determine_libraries( struct line *deck, char *lib_name )
+{
+ struct line *c = deck;
+ char *line, *s, *t, *y, *z, *copys, keep_char1, keep_char2;
+ int i, j;
+ bool found_lib_name = FALSE;
+ bool read_line = FALSE;
+
+ if ( lib_name == NULL ) read_line = TRUE;
+
+ while ( c != NULL ) {
+ line = c->li_line;
+
+ if ( ciprefix( ".endl", line ) && lib_name != NULL ) read_line = FALSE;
+
+ if ( ciprefix( "*lib", line ) || ciprefix( ".lib", line ) ) {
+ for ( s = line; *s && !isspace(*s); s++);
+ while ( isspace(*s) || isquote(*s) ) s++;
+ for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ );
+ y = t;
+ while ( isspace(*y) || isquote(*y) ) y++;
+
+ /* .lib */
+ if ( !*y ) {
+ keep_char1 = *t;
+ *t = '\0';
+
+ if ( lib_name != NULL && strcmp( lib_name, s ) == 0 ) read_line = TRUE;
+ *t = keep_char1;
+ }
+ /* .lib */
+ else if ( read_line == TRUE ) {
+ for ( z = y; *z && !isspace(*z) && !isquote(*z); z++ );
+ keep_char1 = *t; keep_char2 = *z;
+ *t = '\0'; *z = '\0';
+
+ if ( *s == '~' ) {
+ copys = cp_tildexpand(s);
+ if ( copys != NULL ) {
+ s = copys;
+ }
+ }
+ for ( i = 0; i < num_libraries; i++ )
+ if ( strcmp( library_file[i], s ) == 0 ) {
+ found_lib_name = FALSE;
+ for ( j = 0; j < num_lib_names[i] && found_lib_name == FALSE; j++ )
+ if ( strcmp( library_name[i][j], y ) == 0 ) found_lib_name = TRUE;
+
+ if ( found_lib_name == FALSE ) {
+ library_ll_ptr[i][num_lib_names[i]] = c;
+ library_name[i][num_lib_names[i]++] = strdup(y);
+ /* see if other libraries referenced */
+ inp_determine_libraries( libraries[i], y );
+ }
+ }
+ *line = '*'; /* comment out .lib line */
+ *t = keep_char1; *z = keep_char2;
+ }
+ }
+ c = c->li_next;
+ }
+}
+
+static void
+inp_init_lib_data()
+{
+ int i;
+
+ for ( i = 0; i < num_libraries; i++ )
+ num_lib_names[i] = 0;
+}
+
+static char*
+inp_get_subckt_name( char *s )
+{
+ char *end_ptr = strstr( s, "=" );
+ char *subckt_name, *subckt_name_copy;
+ char keep;
+
+ if ( end_ptr != NULL ) {
+ end_ptr--;
+ while ( isspace(*end_ptr) ) end_ptr--;
+ for( ;*end_ptr && !isspace(*end_ptr); end_ptr--);
+ } else {
+ end_ptr = s + strlen(s);
+ }
+
+ subckt_name = end_ptr;
+ while ( isspace( *subckt_name ) ) subckt_name--;
+ for( ; !isspace(*subckt_name); subckt_name-- );
+ subckt_name++;
+
+ keep = *end_ptr;
+ *end_ptr = '\0';
+
+ subckt_name_copy = strdup( subckt_name );
+
+ *end_ptr = keep;
+
+ return subckt_name_copy;
+}
+
+static int
+inp_get_params( char *line, char *param_names[], char *param_values[] )
+{
+ char *equal_ptr = strstr( line, "=" );
+ char *end, *name, *value;
+ int num_params = 0;
+ char tmp_str[1000];
+ char keep;
+ bool is_expression = FALSE;
+
+ while ( ( equal_ptr = strstr( line, "=" ) ) ) {
+
+ // check for equality '=='
+ if ( *(equal_ptr+1) == '=' ) { line = equal_ptr+2; continue; }
+ // check for '!=', '<=', '>='
+ if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { line = equal_ptr+1; continue; }
+
+ is_expression = FALSE;
+
+ /* get parameter name */
+ name = equal_ptr - 1;
+ while ( *name && isspace(*name) ) name--;
+ end = name + 1;
+ while ( *name && !isspace(*name) ) name--;
+ name++;
+
+ keep = *end;
+ *end = '\0';
+ param_names[num_params++] = strdup(name);
+ *end = keep;
+
+ /* get parameter value */
+ value = equal_ptr + 1;
+ while ( *value && isspace(*value) ) value++;
+
+ if ( *value == '{' ) is_expression = TRUE;
+ end = value;
+ if ( is_expression ) {
+ while ( *end && *end != '}' ) end++;
+ } else {
+ while ( *end && !isspace(*end) ) end++;
+ }
+ if ( is_expression ) end++;
+ keep = *end;
+ *end = '\0';
+
+ if ( *value != '{' &&
+ !( isdigit( *value ) || ( *value == '.' && isdigit(*(value+1)) ) ) ) {
+ sprintf( tmp_str, "{%s}", value );
+ value = tmp_str;
+ }
+ param_values[num_params-1] = strdup(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 = strstr( inst_line, "=" ), *inst_name, *inst_name_end = inst_line;
+ char *curr_line = inst_line, *new_line = NULL;
+ char keep;
+ int i, j;
+
+ while ( !isspace(*inst_name_end) ) inst_name_end++;
+ keep = *inst_name_end;
+ *inst_name_end = '\0';
+ inst_name = strdup( inst_line );
+ *inst_name_end = keep;
+
+ if ( end != NULL ) {
+ end--;
+ while ( isspace( *end ) ) end--;
+ while ( !isspace( *end ) ) end--;
+ *end = '\0';
+ }
+
+ 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] = strdup( inst_param_values[j] );
+ }
+ }
+ }
+
+ for ( i = 0; i < num_subckt_params; i++ ) {
+ new_line = tmalloc( strlen( curr_line ) + strlen( subckt_param_values[i] ) + 2 );
+ sprintf( new_line, "%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;
+}
+
+static bool
+found_mult_param( int num_params, char *param_names[] )
+{
+ bool found_mult = FALSE;
+ int i;
+
+ for ( i = 0; i < num_params; i++ ) {
+ if ( strcmp( param_names[i], "m" ) == 0 ) {
+ found_mult = TRUE;
+ }
+ }
+ return found_mult;
+}
+
+static int
+inp_fix_subckt_multiplier( struct line *subckt_card,
+ int num_subckt_params, char *subckt_param_names[], char *subckt_param_values[] )
+{
+ struct line *card;
+ char *new_str;
+
+ subckt_param_names[num_subckt_params] = strdup("m");
+ subckt_param_values[num_subckt_params] = strdup("1");
+ num_subckt_params = num_subckt_params + 1;
+
+ if ( !strstr( subckt_card->li_line, "params:" ) ) {
+ new_str = tmalloc( strlen( subckt_card->li_line ) + 13 );
+ sprintf( new_str, "%s params: m=1", subckt_card->li_line );
+ subckt_w_params[num_subckt_w_params++] = get_subckt_model_name( subckt_card->li_line );
+ } else {
+ new_str = tmalloc( strlen( subckt_card->li_line ) + 5 );
+ sprintf( new_str, "%s m=1", subckt_card->li_line );
+ }
+
+ tfree( subckt_card->li_line );
+ subckt_card->li_line = new_str;
+
+ for ( card = subckt_card->li_next;
+ card != NULL && !ciprefix( ".ends", card->li_line );
+ card = card->li_next ) {
+ new_str = tmalloc( strlen( card->li_line ) + 7 );
+ sprintf( new_str, "%s m={m}", card->li_line );
+
+ tfree( card->li_line );
+ card->li_line = new_str;
+ }
+
+ return num_subckt_params;
+}
+
+static void
+inp_fix_inst_calls_for_numparam(struct line *deck)
+{
+ struct line *c = deck;
+ struct line *d, *p = NULL;
+ char *inst_line;
+ char *subckt_line;
+ char *subckt_name;
+ char *subckt_param_names[1000];
+ char *subckt_param_values[1000];
+ char *inst_param_names[1000];
+ char *inst_param_values[1000];
+ char name_w_space[1000];
+ int num_subckt_params = 0;
+ int num_inst_params = 0;
+ int i,j,k;
+ bool flag = FALSE;
+
+ // 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 != NULL; c = c->li_next ) {
+ inst_line = c->li_line;
+
+ if ( *inst_line == '*' ) { continue; }
+ if ( ciprefix( "x", inst_line ) ) {
+ num_inst_params = inp_get_params( inst_line, inst_param_names, inst_param_values );
+ subckt_name = inp_get_subckt_name( inst_line );
+
+ if ( found_mult_param( num_inst_params, inst_param_names ) ) {
+ flag = FALSE;
+ // iterate through the deck to find the subckt (last one defined wins)
+ d = deck;
+ while ( d != NULL ) {
+ subckt_line = d->li_line;
+ if ( ciprefix( ".subckt", subckt_line ) ) {
+ for ( ; *subckt_line && !isspace(*subckt_line); subckt_line++ );
+ while ( isspace(*subckt_line) ) subckt_line++;
+
+ sprintf( name_w_space, "%s ", subckt_name );
+ if ( strncmp( subckt_line, name_w_space, strlen(name_w_space) ) == 0 ) {
+ p = d;
+ flag = TRUE;
+ }
+ }
+ d = d->li_next;
+ }
+ if ( flag ) {
+ num_subckt_params = inp_get_params( p->li_line, subckt_param_names, subckt_param_values );
+
+ if ( num_subckt_params == 0 || !found_mult_param( num_subckt_params, subckt_param_names ) ) {
+ inp_fix_subckt_multiplier( p, num_subckt_params, subckt_param_names, subckt_param_values );
+ }
+ }
+ }
+ }
+ }
+
+ c = deck;
+ while ( c != NULL ) {
+ inst_line = c->li_line;
+
+ if ( *inst_line == '*' ) { c = c->li_next; continue; }
+ if ( ciprefix( "x", inst_line ) ) {
+ subckt_name = inp_get_subckt_name( inst_line );
+ for ( i = 0; i < num_subckt_w_params; i++ ) {
+ if ( strcmp( subckt_w_params[i], subckt_name ) == 0 ) {
+ sprintf( name_w_space, "%s ", subckt_name );
+
+ /* find .subckt line */
+ bool found_subckt = FALSE;
+
+ d = deck;
+ while ( d != NULL ) {
+ subckt_line = d->li_line;
+ if ( ciprefix( ".subckt", subckt_line ) ) {
+ for ( ; *subckt_line && !isspace(*subckt_line); subckt_line++ );
+ while ( isspace(*subckt_line) ) subckt_line++;
+
+ if ( strncmp( subckt_line, name_w_space, strlen(name_w_space) ) == 0 ) {
+ num_subckt_params = inp_get_params( subckt_line, subckt_param_names, subckt_param_values );
+ 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
+ bool found_param_match = FALSE;
+ if ( num_inst_params == 0 ) found_param_match = TRUE;
+ else {
+ 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->li_line ) ) { *(d->li_line) = '*'; d = d->li_next; }
+ *(d->li_line) = '*'; d = d->li_next; continue;
+ }
+
+ c->li_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 );
+ found_subckt = TRUE;
+ }
+ }
+ if ( found_subckt ) break;
+ d = d->li_next;
+ }
+ break;
+ }
+ }
+ }
+ c = c->li_next;
+ }
+}
+
+static void
+inp_get_func_from_line( char *line )
+{
+ char *ptr, *end;
+ char keep;
+ char temp_buf[5000];
+ int num_params = 0;
+ int str_len = 0;
+ int i = 0;
+
+ /* get function name */
+ while ( !isspace( *line ) ) line++;
+ while ( isspace( *line ) ) line++;
+ end = line;
+ while ( !isspace( *end ) && *end != '(' ) end++;
+ keep = *end;
+ *end = '\0';
+
+ /* see if already encountered this function */
+ for ( i = 0; i < num_functions; i++ )
+ if ( strcmp( func_names[i], line ) == 0 ) break;
+
+ func_names[num_functions++] = strdup(line);
+ *end = keep;
+
+ num_params = 0;
+
+ /* get function parameters */
+ while ( *end != '(' ) end++;
+ while ( *end != ')' ) {
+ end++;
+ ptr = end;
+ while ( isspace( *ptr ) ) ptr++;
+ end = ptr;
+ while ( !isspace( *end ) && *end != ',' && *end != ')' ) end++;
+ keep = *end;
+ *end = '\0';
+ func_params[num_functions-1][num_params++] = strdup(ptr);
+ *end = keep;
+ }
+ num_parameters[num_functions-1] = num_params;
+
+ /* get function macro */
+ str_len = 0;
+ while ( *end != '{' ) end++;
+ end++;
+ while ( *end != '}' ) {
+ while ( isspace( *end ) ) end++;
+ if ( *end != '}' ) temp_buf[str_len++] = *end;
+ end++;
+ }
+ temp_buf[str_len++] = '\0';
+
+ func_macro[num_functions-1] = strdup(temp_buf);
+}
+
+//
+// only grab global functions; skip subckt functions
+//
+static void
+inp_grab_func( struct line *deck )
+{
+ struct line *c = deck;
+ bool is_subckt = FALSE;
+
+ while ( c != NULL ) {
+ if ( *c->li_line == '*' ) { c = c->li_next; continue; }
+ if ( ciprefix( ".subckt", c->li_line ) ) is_subckt = TRUE;
+ if ( ciprefix( ".ends", c->li_line ) ) is_subckt = FALSE;
+
+ if ( !is_subckt && ciprefix( ".func", c->li_line ) ) {
+ inp_get_func_from_line( c->li_line );
+ *c->li_line = '*';
+ }
+ c = c->li_next;
+ }
+}
+
+static void
+inp_grab_subckt_func( struct line *subckt )
+{
+ struct line *c = subckt;
+
+ while ( !ciprefix( ".ends", c->li_line ) ) {
+ if ( ciprefix( ".func", c->li_line ) ) {
+ inp_get_func_from_line( c->li_line );
+ *c->li_line = '*';
+ }
+ c = c->li_next;
+ }
+}
+
+static bool
+is_arith_char( char c )
+{
+ if ( c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '<' ||
+ c == '>' || c == '?' || c == '|' || c == '&' )
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static bool
+str_has_arith_char( char *s )
+{
+ while ( *s && *s != '\0' ) {
+ if ( is_arith_char(*s) ) return TRUE;
+ s++;
+ }
+ return FALSE;
+}
+
+static char*
+inp_do_macro_param_replace( int fcn_number, char *params[] )
+{
+ char *param_ptr, *curr_ptr, *new_str, *curr_str = NULL, *search_ptr;
+ char keep, before, after;
+ int i;
+
+ for ( i = 0; i < num_parameters[fcn_number]; i++ ) {
+ if ( curr_str == NULL )
+ search_ptr = curr_ptr = func_macro[fcn_number];
+ else {
+ search_ptr = curr_ptr = curr_str;
+ curr_str = NULL;
+ }
+ while ( ( param_ptr = strstr( search_ptr, func_params[fcn_number][i] ) ) ) {
+
+ /* make sure actually have the parameter name */
+ before = *(param_ptr-1);
+ after = *(param_ptr+strlen(func_params[fcn_number][i]));
+ if ( !(is_arith_char(before) || isspace(before) || before == ',' || before == '=' || (param_ptr-1) < curr_ptr ) ||
+ !(is_arith_char(after) || isspace(after) || after == ',' || after == '=' || after == '\0' ) ) {
+ search_ptr = param_ptr + 1;
+ continue;
+ }
+
+ keep = *param_ptr;
+ *param_ptr = '\0';
+
+ if ( curr_str != NULL ) {
+ if ( str_has_arith_char( params[i] ) ) {
+ new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + strlen(params[i]) + 3 );
+ sprintf( new_str, "%s%s(%s)", curr_str, curr_ptr, params[i] );
+ } else {
+ new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + strlen(params[i]) + 1 );
+ sprintf( new_str, "%s%s%s", curr_str, curr_ptr, params[i] );
+ }
+
+ tfree( curr_str );
+ } else {
+ if ( str_has_arith_char( params[i] ) ) {
+ new_str = tmalloc( strlen(curr_ptr) + strlen(params[i]) + 3 );
+ sprintf( new_str, "%s(%s)", curr_ptr, params[i] );
+ } else {
+ new_str = tmalloc( strlen(curr_ptr) + strlen(params[i]) + 1 );
+ sprintf( new_str, "%s%s", curr_ptr, params[i] );
+ }
+ }
+ curr_str = new_str;
+
+ *param_ptr = keep;
+ search_ptr = curr_ptr = param_ptr + strlen(func_params[fcn_number][i]);
+ }
+ if ( param_ptr == NULL ) {
+ if ( curr_str == NULL ) {
+ curr_str = curr_ptr;
+ } else {
+ new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + 1 );
+ sprintf( new_str, "%s%s", curr_str, curr_ptr );
+ tfree(curr_str);
+ curr_str = new_str;
+ }
+ }
+ }
+ return curr_str;
+}
+
+static char*
+inp_expand_macro_in_str( char *str )
+{
+ int i;
+ char *c;
+ char *open_paren_ptr, *close_paren_ptr, *fcn_name, *comma_ptr, *params[1000];
+ char *curr_ptr, *new_str, *macro_str, *curr_str = NULL;
+ int num_parens, num_params;
+ char *orig_ptr = str, *search_ptr = str, *orig_str = strdup(str);;
+ char keep;
+
+ while ( ( open_paren_ptr = strstr( search_ptr, "(" ) ) ) {
+ fcn_name = open_paren_ptr - 1;
+ while ( fcn_name != search_ptr && (isalnum(*fcn_name) || *fcn_name == '_') ) fcn_name--;
+ if ( fcn_name != search_ptr ) fcn_name++;
+
+ *open_paren_ptr = '\0';
+ close_paren_ptr = NULL;
+
+ if ( open_paren_ptr != fcn_name ) {
+ for ( i = 0; i < num_functions; i++ ) {
+ if ( strcmp( func_names[i], fcn_name ) == 0 ) {
+
+ /* find the closing paren */
+ close_paren_ptr = NULL;
+ num_parens = 0;
+ for ( c = open_paren_ptr + 1; *c && *c != '\0'; c++ ) {
+ if ( *c == '(' ) num_parens++;
+ if ( *c == ')' ) {
+ if ( num_parens != 0 ) num_parens--;
+ else {
+ close_paren_ptr = c;
+ break;
+ }
+ }
+ }
+ if ( close_paren_ptr == NULL ) {
+ fprintf( stderr, "ERROR: did not find closing parenthesis for function call in str: %s\n", orig_str );
+ exit( -1 );
+ }
+ *close_paren_ptr = '\0';
+
+ /* get the parameters */
+ curr_ptr = open_paren_ptr+1;
+ while ( isspace(*curr_ptr) ) curr_ptr++;
+ num_params = 0;
+ 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 = ' ';
+ }
+ while ( ( comma_ptr = strstr( curr_ptr, "," ) ) ) {
+ while ( isspace(*curr_ptr) ) curr_ptr++;
+ *comma_ptr = '\0';
+ params[num_params++] = inp_expand_macro_in_str( strdup( curr_ptr ) );
+ *comma_ptr = ',';
+ curr_ptr = comma_ptr+1;
+ }
+ while ( isspace(*curr_ptr) ) curr_ptr++;
+ /* get the last parameter */
+ params[num_params++] = inp_expand_macro_in_str( strdup( curr_ptr ) );
+
+ if ( num_parameters[i] != num_params ) {
+ fprintf( stderr, "ERROR: parameter mismatch for function call in str: %s\n", orig_ptr );
+ exit( -1 );
+ }
+
+ macro_str = inp_do_macro_param_replace( i, params );
+ keep = *fcn_name;
+ *fcn_name = '\0';
+
+ if ( curr_str == NULL ) {
+ new_str = tmalloc( strlen(str) + strlen(macro_str) + strlen(close_paren_ptr+1) + 3 );
+ sprintf( new_str, "%s(%s)", str, macro_str );
+ curr_str = new_str;
+ } else {
+ new_str = tmalloc( strlen(curr_str) + strlen(str) + strlen(macro_str) + strlen(close_paren_ptr+1) + 3 );
+ sprintf( new_str, "%s%s(%s)", curr_str, str, macro_str );
+ tfree(curr_str);
+ curr_str = new_str;
+ }
+
+ *fcn_name = keep;
+ *close_paren_ptr = ')';
+
+ search_ptr = str = close_paren_ptr+1;
+ break;
+ } /* if strcmp */
+ } /* for loop over function names */
+ }
+ *open_paren_ptr = '(';
+ search_ptr = open_paren_ptr + 1;
+ }
+
+ if ( curr_str == NULL ) {
+ curr_str = orig_ptr;
+ }
+ else {
+ if ( str != NULL ) {
+ new_str = tmalloc( strlen(curr_str) + strlen(str) + 1 );
+ sprintf( new_str, "%s%s", curr_str, str );
+ tfree(curr_str);
+ curr_str = new_str;
+ }
+ tfree(orig_ptr);
+ }
+
+ tfree(orig_str);
+
+ return curr_str;
+}
+
+static void
+inp_expand_macros_in_func()
+{
+ int i;
+
+ for ( i = 0; i < num_functions; i++ ) {
+ func_macro[i] = inp_expand_macro_in_str( func_macro[i] );
+ }
+}
+
+static void
+inp_expand_macros_in_deck( struct line *deck )
+{
+ struct line *c = deck;
+ int prev_num_functions = 0, i, j;
+
+ while ( c != NULL ) {
+ if ( *c->li_line == '*' ) { c = c->li_next; continue; }
+ if ( ciprefix( ".subckt", c->li_line ) ) {
+ prev_num_functions = num_functions;
+ inp_grab_subckt_func( c );
+ if ( prev_num_functions != num_functions ) inp_expand_macros_in_func();
+ }
+ if ( ciprefix( ".ends", c->li_line ) ) {
+ if ( prev_num_functions != num_functions ) {
+ for ( i = prev_num_functions; i < num_functions; i++ ) {
+ tfree( func_names[i] );
+ tfree( func_macro[i] );
+ for ( j = 0; j < num_parameters[i]; j++ ) tfree( func_params[i][j] );
+ num_functions = prev_num_functions;
+ }
+ }
+ }
+
+ if ( *c->li_line != '*' ) c->li_line = inp_expand_macro_in_str( c->li_line );
+ c = c->li_next;
+ }
+}
+
+static void
+inp_fix_param_values( struct line *deck )
+{
+ struct line *c = deck;
+ char *line, *beg_of_str, *end_of_str, *old_str, *equal_ptr, *new_str;
+ bool control_section = FALSE, has_paren = FALSE;
+
+ while ( c != NULL ) {
+ line = c->li_line;
+
+ if ( *line == '*' || (ciprefix( ".param", line ) && strstr( line, "{" )) ) { c = c->li_next; continue; }
+
+ if ( ciprefix( ".control", line ) ) { control_section = TRUE; c = c->li_next; continue; }
+ if ( ciprefix( ".endc", line ) ) { control_section = FALSE; c = c->li_next; continue; }
+ if ( control_section || ciprefix( ".option", line ) ) { c = c->li_next; continue; }
+
+ while ( ( equal_ptr = strstr( line, "=" ) ) ) {
+
+ // skip over equality '=='
+ if ( *(equal_ptr+1) == '=' ) { line += 2; continue; }
+ // check for '!=', '<=', '>='
+ if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { line += 1; continue; }
+
+ beg_of_str = equal_ptr + 1;
+ while ( isspace(*beg_of_str) ) beg_of_str++;
+ if ( isdigit(*beg_of_str) || *beg_of_str == '{' || *beg_of_str == '.' ||
+ ( *beg_of_str == '-' && isdigit(*(beg_of_str+1)) ) ) {
+ line = equal_ptr + 1;
+ } else {
+ end_of_str = beg_of_str;
+ while ( *end_of_str != '\0' && (!isspace(*end_of_str) || has_paren) ) {
+ if ( *end_of_str == '(' ) has_paren = TRUE;
+ if ( *end_of_str == ')' ) has_paren = FALSE;
+ end_of_str++;
+ }
+
+ *equal_ptr = '\0';
+
+ if ( *end_of_str == '\0' ) {
+ new_str = tmalloc( strlen(c->li_line) + strlen(beg_of_str) + 4 );
+ sprintf( new_str, "%s={%s}", c->li_line, beg_of_str );
+
+ } else {
+ *end_of_str = '\0';
+
+ new_str = tmalloc( strlen(c->li_line) + strlen(beg_of_str) + strlen(end_of_str+1) + 5 );
+ sprintf( new_str, "%s={%s} %s", c->li_line, beg_of_str, end_of_str+1 );
+ }
+ old_str = c->li_line;
+ c->li_line = new_str;
+
+ line = new_str + strlen(old_str) + 1;
+ tfree(old_str);
+ }
+ }
+ c = c->li_next;
+ }
+}
+
+static char*
+get_param_name( char *line )
+{
+ char *name, *equal_ptr, *beg;
+ char keep;
+
+ if ( ( equal_ptr = strstr( line, "=" ) ) )
+ {
+ equal_ptr--;
+ while ( isspace(*equal_ptr) ) equal_ptr--;
+ equal_ptr++;
+
+ beg = equal_ptr-1;
+ while ( !isspace(*beg) && beg != line ) beg--;
+ if ( beg != line ) beg++;
+ keep = *equal_ptr;
+ *equal_ptr = '\0';
+ name = strdup(beg);
+ *equal_ptr = keep;
+ }
+ else
+ {
+ fprintf( stderr, "ERROR: could not find '=' on parameter line '%s'!\n", line );
+ exit(-1);
+ }
+ return name;
+}
+
+static char*
+get_param_str( char *line )
+{
+ char *equal_ptr;
+
+ if ( ( equal_ptr = strstr( line, "=" ) ) ) {
+ equal_ptr++;
+ while ( isspace(*equal_ptr) ) equal_ptr++;
+ return equal_ptr;
+ }
+ return line;
+}
+
+static int
+inp_get_param_level( int param_num, char *depends_on[12000][100], char *param_names[12000], char *param_strs[12000], int total_params, int *level )
+{
+ int index1 = 0, comp_level = 0, temp_level = 0;
+ int index2 = 0;
+
+ if ( *(level+param_num) != -1 ) return *(level+param_num);
+
+ while ( depends_on[param_num][index1] != NULL )
+ {
+ index2 = 0;
+ while ( index2 <= total_params && param_names[index2] != depends_on[param_num][index1] ) index2++;
+
+ if ( index2 > total_params )
+ {
+ fprintf( stderr, "ERROR: unable to find dependency parameter for %s!\n", param_names[param_num] );
+ exit( -1 );
+ }
+ temp_level = inp_get_param_level( index2, depends_on, param_names, param_strs, total_params, level );
+ temp_level++;
+
+ if ( temp_level > comp_level ) comp_level = temp_level;
+ index1++;
+ }
+ *(level+param_num) = comp_level;
+ return comp_level;
+}
+
+static int
+get_number_terminals( char c )
+{
+ switch (c) {
+ case 'r': case 'c': case 'l': case 'k': case 'f': case 'h': case 'b':
+ case 'v': case 'i': case 'w': case 'd':
+ return 2;
+ break;
+ case 'u': case 'j': case 'z':
+ return 3;
+ break;
+ case 't': case 'o': case 'g': case 'e': case 's': case 'q': case 'm':
+ return 4;
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+/* sort parameters based on parameter dependencies */
+static void
+inp_sort_params( struct line *start_card, struct line *end_card, struct line *card_bf_start, struct line *s_c, struct line *e_c )
+{
+ char *param_name = NULL, *param_str = NULL, *param_ptr = NULL;
+ int i, j, num_params = 0, index = 0, max_level = 0, num_terminals = 0;
+ bool in_control = FALSE;
+ struct line *ptr_array[12000], *ptr_array_ordered[12000], *ptr;
+ char *param_names[12000], *param_strs[12000], *curr_line;
+ char *depends_on[12000][100], *str_ptr, *beg, *end, *new_str;
+ int level[12000], param_skip[12000], skipped = 0;
+
+ if ( start_card == NULL ) return;
+
+ ptr = start_card;
+ while ( ptr != NULL )
+ {
+ if ( strstr( ptr->li_line, "=" ) ) {
+ num_params++;
+ }
+ ptr = ptr->li_next;
+ }
+ num_params = 0;
+
+ ptr = start_card;
+ while ( ptr != NULL )
+ {
+ // ignore .param lines without '='
+ if ( strstr( ptr->li_line, "=" ) ) {
+ depends_on[num_params][0] = NULL;
+ level[num_params] = -1;
+ param_names[num_params] = strdup( get_param_name( ptr->li_line ) );
+ param_strs[num_params] = strdup( get_param_str( ptr->li_line ) );
+
+ ptr_array[num_params++] = ptr;
+ }
+ ptr = ptr->li_next;
+ }
+ // look for duplicately defined parameters and mark earlier one to skip
+ // param list is ordered as defined in netlist
+ for ( i = 0; i < num_params; i++ )
+ param_skip[i] = 0;
+ for ( i = 0; i < num_params; i++ )
+ for ( j = num_params-1; j >= 0; j-- )
+ {
+ if ( i != j && i < j && strcmp( param_names[i], param_names[j] ) == 0 ) {
+ // skip earlier one in list
+ param_skip[i] = 1;
+ skipped++;
+ }
+ }
+
+ for ( i = 0; i < num_params; i++ )
+ {
+ if ( param_skip[i] == 1 ) continue;
+
+ param_name = param_names[i];
+ for ( j = 0; j < num_params; j++ )
+ {
+ if ( j == i ) continue;
+
+ param_str = param_strs[j];
+
+ while ( ( param_ptr = strstr( param_str, param_name ) ) )
+ {
+ if ( !isalnum( *(param_ptr-1) ) && *(param_ptr-1) != '_' &&
+ !isalnum( *(param_ptr+strlen(param_name)) ) && *(param_ptr+strlen(param_name)) != '_' )
+ {
+ index = 0;
+ bool found_in_list = FALSE;
+ while ( depends_on[j][index] != NULL ) {
+ if ( strcmp( param_name, depends_on[j][index] ) == 0 ) { found_in_list = TRUE; break; }
+ index++;
+ }
+ if ( !found_in_list ) {
+ depends_on[j][index++] = param_name;
+ depends_on[j][index] = NULL;
+ }
+ break;
+ }
+ param_str = param_ptr + strlen(param_name);
+ }
+ }
+ }
+
+ for ( i = 0; i < num_params; i++ )
+ {
+ level[i] = inp_get_param_level( i, depends_on, param_names, param_strs, num_params, level );
+ if ( level[i] > max_level ) max_level = level[i];
+ }
+
+ /* look for unquoted parameters and quote them */
+ ptr = s_c;
+ in_control = FALSE;
+ while ( ptr != NULL && ptr != e_c )
+ {
+ curr_line = ptr->li_line;
+
+ if ( ciprefix( ".control", curr_line ) ) { in_control = TRUE; ptr = ptr->li_next; continue; }
+ if ( ciprefix( ".endc", curr_line ) ) { in_control = FALSE; ptr = ptr->li_next; continue; }
+ if ( in_control || curr_line[0] == '.' || curr_line[0] == '*' ) { ptr = ptr->li_next; continue; }
+
+ num_terminals = get_number_terminals( curr_line[0] );
+
+ if ( num_terminals == 0 ) { ptr = ptr->li_next; continue; }
+
+ for ( i = 0; i < num_params; i++ )
+ {
+ str_ptr = curr_line;
+
+ for ( j = 0; j < num_terminals+1; j++ )
+ {
+ while ( !isspace(*str_ptr) && *str_ptr != '\0' ) str_ptr++;
+ while ( isspace(*str_ptr) && *str_ptr != '\0' ) str_ptr++;
+ }
+
+ while ( ( str_ptr = strstr( str_ptr, param_names[i] ) ) )
+ {
+ /* make sure actually have the parameter name */
+ char before = *(str_ptr-1);
+ char after = *(str_ptr+strlen(param_names[i]));
+ if ( !( is_arith_char(before) || isspace(before) || (str_ptr-1) < curr_line ) ||
+ !( is_arith_char(after) || isspace(after) || after == '\0' ) ) {
+ str_ptr = str_ptr + 1;
+ continue;
+ }
+ beg = str_ptr - 1;
+ end = str_ptr + strlen(param_names[i]);
+ if ( ( isspace(*beg) || *beg == '=' ) &&
+ ( isspace(*end) || *end == '\0' ) )
+ {
+ *str_ptr = '\0';
+ if ( *end != '\0' )
+ {
+ new_str = tmalloc( strlen(curr_line) + strlen(param_names[i]) + strlen(end) + 3 );
+ sprintf( new_str, "%s{%s}%s", curr_line, param_names[i], end );
+ }
+ else
+ {
+ new_str = tmalloc( strlen(curr_line) + strlen(param_names[i]) + 3 );
+ sprintf( new_str, "%s{%s}", curr_line, param_names[i] );
+ }
+ str_ptr = new_str + strlen(curr_line) + strlen(param_names[i]);
+
+ tfree(ptr->li_line);
+ curr_line = ptr->li_line = new_str;
+ }
+ str_ptr++;
+ }
+ }
+ ptr = ptr->li_next;
+ }
+
+ index = 0;
+ for ( i = 0; i <= max_level; i++ )
+ for ( j = num_params-1; j >= 0; j-- )
+ {
+ if ( level[j] == i ) {
+ if ( param_skip[j] == 0 ) {
+ ptr_array_ordered[index++] = ptr_array[j];
+ }
+ }
+ }
+
+ num_params -= skipped;
+ if ( index != num_params )
+ {
+ fprintf( stderr, "ERROR: found wrong number of parameters during levelization ( %d instead of %d parameter s)!\n", index, num_params );
+ exit(-1);
+ }
+
+ /* fix next ptrs */
+ ptr = card_bf_start->li_next;
+ card_bf_start->li_next = ptr_array_ordered[0];
+ ptr_array_ordered[num_params-1]->li_next = ptr;
+ for ( i = 0; i < num_params-1; i++ )
+ ptr_array_ordered[i]->li_next = ptr_array_ordered[i+1];
+
+ // clean up memory
+ for ( i = 0; i < num_params; i++ )
+ {
+ tfree( param_names[i] );
+ tfree( param_strs[i] );
+ }
+}
+
+static void
+inp_add_params_to_subckt( struct line *subckt_card )
+{
+ struct line *card = subckt_card->li_next;
+ char *curr_line = card->li_line;
+ char *subckt_line = subckt_card->li_line;
+ char *new_line, *param_ptr, *subckt_name, *end_ptr;
+ char keep;
+
+ while ( card != NULL && ciprefix( ".param", curr_line ) ) {
+ param_ptr = strstr( curr_line, " " );
+ while ( isspace(*param_ptr) ) param_ptr++;
+
+ if ( !strstr( subckt_line, "params:" ) ) {
+ new_line = tmalloc( strlen(subckt_line) + strlen("params: ") + strlen(param_ptr) + 2 );
+ sprintf( new_line, "%s params: %s", subckt_line, param_ptr );
+
+ subckt_name = subckt_card->li_line;
+ while ( !isspace(*subckt_name) ) subckt_name++;
+ while ( isspace(*subckt_name) ) subckt_name++;
+ end_ptr = subckt_name;
+ while ( !isspace(*end_ptr) ) end_ptr++;
+ keep = *end_ptr;
+ *end_ptr = '\0';
+ subckt_w_params[num_subckt_w_params++] = strdup(subckt_name);
+ *end_ptr = keep;
+ } else {
+ new_line = tmalloc( strlen(subckt_line) + strlen(param_ptr) + 2 );
+ sprintf( new_line, "%s %s", subckt_line, param_ptr );
+ }
+
+ tfree( subckt_line );
+ subckt_card->li_line = subckt_line = new_line;
+
+ *curr_line = '*';
+
+ card = card->li_next;
+ curr_line = card->li_line;
+ }
+}
+
+static void
+inp_reorder_params( struct line *deck, struct line *list_head, struct line *end )
+{
+ struct line *c = deck, *subckt_card = NULL, *param_card = NULL, *prev_card = list_head;
+ struct line *subckt_param_card = NULL, *first_param_card = NULL, *first_subckt_param_card = NULL;
+ char *curr_line;
+ bool processing_subckt = FALSE;
+
+ /* move .param lines to beginning of deck */
+ while ( c != NULL ) {
+ curr_line = c->li_line;
+ if ( *curr_line == '*' ) { c = c->li_next; continue; }
+ if ( ciprefix( ".subckt", curr_line ) ) {
+ processing_subckt = TRUE;
+ subckt_card = c;
+ first_subckt_param_card = NULL;
+ }
+ if ( ciprefix( ".ends", curr_line ) && processing_subckt ) {
+ processing_subckt = FALSE;
+ if ( first_subckt_param_card != NULL ) {
+ inp_sort_params( first_subckt_param_card, subckt_param_card, subckt_card, subckt_card, c );
+ inp_add_params_to_subckt( subckt_card );
+ }
+ }
+
+ if ( ciprefix( ".param", curr_line ) ) {
+ if ( !processing_subckt ) {
+ if ( first_param_card == NULL ) {
+ first_param_card = c;
+ } else {
+ param_card->li_next = c;
+ }
+ param_card = c;
+ prev_card->li_next = c->li_next;
+ param_card->li_next = NULL;
+ c = prev_card;
+ } else {
+ if ( first_subckt_param_card == NULL ) {
+ first_subckt_param_card = c;
+ } else {
+ subckt_param_card->li_next = c;
+ }
+ subckt_param_card = c;
+ prev_card->li_next = c->li_next;
+ c = prev_card;
+ subckt_param_card->li_next = NULL;
+ }
+ }
+ prev_card = c;
+ c = c->li_next;
+ }
+ inp_sort_params( first_param_card, param_card, list_head, deck, end );
+}
+
+// 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 line *deck, int line_num )
+{
+ struct line *card = deck, *param_end = NULL, *param_beg = NULL, *prev = NULL, *tmp_ptr;
+ char *curr_line, *equal_ptr, *beg_param, *end_param, *new_line;
+ char *array[5000];
+ int counter = 0, i;
+ bool get_expression = FALSE, get_paren_expression = FALSE;
+ char keep;
+
+ while ( card != NULL )
+ {
+ curr_line = card->li_line;
+
+ if ( *curr_line == '*' ) { card = card->li_next; continue; }
+
+ if ( ciprefix( ".param", curr_line ) )
+ {
+ counter = 0;
+ while ( ( equal_ptr = strstr( curr_line, "=" ) ) ) {
+ // check for equality '=='
+ if ( *(equal_ptr+1) == '=' ) { curr_line = equal_ptr+2; continue; }
+ // check for '!=', '<=', '>='
+ if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { curr_line = equal_ptr+1; continue; }
+ counter++; curr_line = equal_ptr + 1;
+ }
+ if ( counter <= 1 ) { card = card->li_next; continue; }
+
+ // need to split multi param line
+ curr_line = card->li_line;
+ counter = 0;
+ while ( curr_line < card->li_line+strlen(card->li_line) && ( equal_ptr = strstr( curr_line, "=" ) ) )
+ {
+ // check for equality '=='
+ if ( *(equal_ptr+1) == '=' ) { curr_line = equal_ptr+2; continue; }
+ // check for '!=', '<=', '>='
+ if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { curr_line = equal_ptr+1; continue; }
+
+ beg_param = equal_ptr - 1;
+ end_param = equal_ptr + 1;
+ while ( isspace(*beg_param) ) beg_param--;
+ while ( !isspace(*beg_param) ) beg_param--;
+ while ( isspace(*end_param) ) end_param++;
+ while ( *end_param != '\0' && (!isspace(*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++;
+ }
+ beg_param++;
+ keep = *end_param;
+ *end_param = '\0';
+ new_line = tmalloc( strlen(".param ") + strlen(beg_param) + 1 );
+ sprintf( new_line, ".param %s", beg_param );
+ array[counter++] = new_line;
+ *end_param = keep;
+ curr_line = end_param;
+ }
+ tmp_ptr = card->li_next;
+ for ( i = 0; i < counter; i++ )
+ {
+ if ( param_end )
+ {
+ param_end->li_next = alloc(struct line);
+ param_end = param_end->li_next;
+ }
+ else
+ {
+ param_end = param_beg = alloc(struct line);
+ }
+ param_end->li_next = NULL;
+ param_end->li_error = NULL;
+ param_end->li_actual = NULL;
+ param_end->li_line = array[i];
+ param_end->li_linenum = line_num++;
+ }
+ // comment out current multi-param line
+ *(card->li_line) = '*';
+ // insert new param lines immediately after current line
+ tmp_ptr = card->li_next;
+ card->li_next = param_beg;
+ param_end->li_next = tmp_ptr;
+ // point 'card' pointer to last in scalar list
+ card = param_end;
+
+ param_beg = param_end = NULL;
+ } // if ( ciprefix( ".param", curr_line ) )
+ prev = card;
+ card = card->li_next;
+ } // while ( card != NULL )
+ if ( param_end )
+ {
+ prev->li_next = param_beg;
+ prev = param_end;
+ }
+ return line_num;
+}
diff --git a/src/frontend/inpcom.h b/src/frontend/inpcom.h
index 0eb243435..1599bb929 100644
--- a/src/frontend/inpcom.h
+++ b/src/frontend/inpcom.h
@@ -7,7 +7,22 @@
#define INPCOM_H_INCLUDED
FILE * inp_pathopen(char *name, char *mode);
-void inp_readall(FILE *fp, struct line **data);
+void inp_readall(FILE *fp, struct line **data, int, char *dirname);
void inp_casefix(register char *string);
+/* globals -- wanted to avoid complicating inp_readall interface */
+static char *library_file[1000];
+static char *library_name[1000][1000];
+struct line *library_ll_ptr[1000][1000];
+struct line *libraries[1000];
+int num_libraries;
+int num_lib_names[1000];
+static char *global;
+static char *subckt_w_params[1000];
+static int num_subckt_w_params;
+static char *func_names[1000];
+static char *func_params[1000][1000];
+static char *func_macro[5000];
+static int num_functions;
+static int num_parameters[1000];
#endif
diff --git a/src/frontend/measure.c b/src/frontend/measure.c
new file mode 100644
index 000000000..318dccce0
--- /dev/null
+++ b/src/frontend/measure.c
@@ -0,0 +1,611 @@
+#include
+#include
+#include "ngspice.h"
+#include "cpdefs.h"
+#include "ftedefs.h"
+#include "dvec.h"
+
+#include "rawfile.h"
+#include "variable.h"
+#include "numparam/numpaif.h"
+
+static bool measure_valid[20000];
+static bool just_chk_meas;
+static bool measures_passed;
+
+// Initial AlmostEqualULPs version - fast and simple, but
+// some limitations.
+static bool AlmostEqualUlps(float A, float B, int maxUlps)
+{
+ assert(sizeof(float) == sizeof(int));
+
+ if (A == B)
+ return TRUE;
+
+ int intDiff = abs(*(int*)&A - *(int*)&B);
+
+ if (intDiff <= maxUlps)
+ return TRUE;
+
+ return FALSE;
+}
+
+static double
+max( double a, double b ) {
+ if ( a > b ) return a;
+ else return b;
+}
+
+static double
+min( double a, double b ) {
+ if ( a < b ) return a;
+ else return b;
+}
+
+static int
+get_measure_precision()
+{
+ char *env_ptr;
+ int precision = 5;
+
+ if ( ( env_ptr = getenv("NGSPICE_MEAS_PRECISION") ) ) {
+ precision = atoi(env_ptr);
+ }
+
+ return precision;
+}
+
+static double
+interpolate( struct dvec *time, struct dvec *values, int i, int j, double var_value, char x_or_y ) {
+ double slope = (values->v_realdata[j] - values->v_realdata[i])/(time->v_realdata[j] - time->v_realdata[i]);
+ double yint = values->v_realdata[i] - slope*time->v_realdata[i];
+ double result;
+
+ if ( x_or_y == 'x' ) result = (var_value - yint)/slope;
+ else result = slope*var_value + yint;
+
+ return result;
+}
+
+static double
+get_volt_time( struct dvec *time, struct dvec *values, double value, char polarity, int index, bool *failed )
+{
+ int i = 0, count = 0;
+ double comp_time = 0;
+
+ for ( i = 0; i < values->v_length-1; i++ ) {
+ if ( polarity == 'r' ) {
+ if ( values->v_realdata[i] < value && value <= values->v_realdata[i+1] ) {
+ count++;
+ if ( count == index ) comp_time = interpolate( time, values, i, i+1, value, 'x' );
+ }
+ }
+ else if ( polarity == 'f' ) {
+ if ( values->v_realdata[i] >= value && value > values->v_realdata[i+1] ) {
+ count++;
+ if ( count == index ) comp_time = interpolate( time, values, i, i+1, value, 'x' );
+ }
+ }
+ else {
+ if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: unknown signal polarity '%c'; valid types are 'r' or 'f'.\n", polarity );
+ *failed = TRUE;
+ }
+ }
+ if ( AlmostEqualUlps( comp_time, 0, 3 ) ) *failed = TRUE;
+
+ return comp_time;
+}
+
+static bool
+measure( char *trig_name, double trig_value, char trig_polarity, int trig_index,
+ char *targ_name, double targ_value, char targ_polarity, int targ_index, double *result,
+ double *trig_time, double *targ_time ) {
+ struct dvec *time = vec_get("time");
+ struct dvec *trig = vec_get(trig_name);
+ struct dvec *targ = vec_get(targ_name);
+ bool failed = FALSE;
+
+ if ( !time ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: problem accessing vector 'time'!\n" ); return TRUE; }
+ if ( !trig ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: problem accessing vector '%s'!\n", trig_name ); return TRUE; }
+ if ( !targ ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: problem accessing vector '%s'!\n", targ_name ); return TRUE; }
+
+ *trig_time = get_volt_time( time, trig, trig_value, trig_polarity, trig_index, &failed );
+ *targ_time = get_volt_time( time, targ, targ_value, targ_polarity, targ_index, &failed );
+ *result = *targ_time - *trig_time;
+
+ return failed;
+}
+
+/*
+ avg: (average) calculates the area under the out_var divided by the periods of interest
+ rms: (root mean squared) calculates the square root of the area under the out_var^2 curve
+ divided by the period of interest
+ integral: calculate the integral
+*/
+static bool
+measure2( char *meas_type, char *vec_name, char vec_type, double from, double to, double *result, double *result_time ) {
+ struct dvec *time = vec_get("time");
+ struct dvec *vec;
+ int xy_size = 0;
+ double *x, *y, *width, sum1 = 0, sum2 = 0, sum3 = 0;
+ char tmp_vec_name[1000];
+ double prev_result = 0;
+ bool failed = FALSE, first_time = TRUE, constant_y = TRUE;
+ int i;
+
+ if ( to < from ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: (measure2) 'to' time (%e) < 'from' time (%e).\n", to, from ); return TRUE; }
+
+ if ( vec_type == 'i' ) {
+ if ( strstr( vec_name, ".v" ) ) sprintf( tmp_vec_name, "v.%s#branch", vec_name );
+ else sprintf( tmp_vec_name, "%s#branch", vec_name );
+ }
+ else sprintf( tmp_vec_name, "%s", vec_name );
+
+ vec = vec_get( tmp_vec_name );
+
+ if ( !time ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: problem accessing vector 'time'!\n" ); return TRUE; }
+ if ( !vec ) { if ( just_chk_meas != TRUE ) fprintf( stderr, "Error: problem accessing vector '%s'!\n", tmp_vec_name ); return TRUE; }
+
+ if ( strcmp( meas_type, "max" ) == 0 || strcmp( meas_type, "min" ) == 0 ) {
+ for ( i = 0; i < vec->v_length; i++ ) {
+ if ( time->v_realdata[i] >= from && ( i+1 < time->v_length && time->v_realdata[i+1] <= to ) ) {
+ prev_result = *result;
+ if ( first_time ) {
+ first_time = FALSE;
+ *result = vec->v_realdata[i];
+ *result_time = time->v_realdata[i];
+ } else {
+ *result = ( strcmp( meas_type, "max" ) == 0 ) ? max( *result, vec->v_realdata[i] ) : min( *result, vec->v_realdata[i] );
+ if ( !AlmostEqualUlps( prev_result, *result, 3 ) ) *result_time = time->v_realdata[i];
+ }
+ }
+ }
+ }
+ else if ( strcmp( meas_type, "avg" ) == 0 || strcmp( meas_type, "rms" ) == 0 ||
+ strcmp( meas_type, "integral" ) == 0 || strcmp( meas_type, "integ" ) == 0 ) {
+ x = (double *) tmalloc(time->v_length * sizeof(double));
+ y = (double *) tmalloc(time->v_length * sizeof(double));
+ width = (double *) tmalloc(time->v_length * sizeof(double));
+
+ // create new set of values over interval [from, to] -- interpolate if necessary
+ for ( i = 0; i < vec->v_length; i++ ) {
+ if ( time->v_realdata[i] >= from && time->v_realdata[i] <= to ) {
+ *(x+xy_size) = time->v_realdata[i];
+ *(y+xy_size++) = ( strcmp( meas_type, "avg" ) == 0 || ciprefix( "integ", meas_type ) ) ? vec->v_realdata[i] : pow(vec->v_realdata[i],2);
+ }
+ }
+ // evaluate segment width
+ for ( i = 0; i < xy_size-1; i++ ) *(width+i) = *(x+i+1) - *(x+i);
+ *(width+i++) = 0;
+ *(width+i++) = 0;
+
+ // see if y-value constant
+ for ( i = 0; i < xy_size-1; i++ )
+ if ( !AlmostEqualUlps( *(y+i), *(y+i+1), 3 ) ) constant_y = FALSE;
+
+ // Compute Integral (area under curve)
+ i = 0;
+ while ( i < xy_size-1 ) {
+ // Simpson's 3/8 Rule
+ if ( AlmostEqualUlps( *(width+i), *(width+i+1), 3 ) && AlmostEqualUlps( *(width+i), *(width+i+2), 3 ) ) {
+ sum1 += 3*(*(width+i))*(*(y+i) + 3*(*(y+i+1) + *(y+i+2)) + *(y+i+3))/8;
+ i += 3;
+ }
+ // Simpson's 1/3 Rule
+ else if ( AlmostEqualUlps( *(width+i), *(width+i+1), 3 ) ) {
+ sum2 += *(width+i)*(*(y+i) + 4*(*(y+i+1)) + *(y+i+2))/3;
+ i += 2;
+ }
+ // Trapezoidal Rule
+ else if ( !AlmostEqualUlps( *(width+i), *(width+i+1), 3 ) ) {
+ sum3 += *(width+i)*(*(y+i) + *(y+i+1))/2;
+ i++;
+ }
+ }
+
+ if ( !ciprefix( "integ", meas_type ) ) {
+ *result = (sum1 + sum2 + sum3)/(to - from);
+
+ if ( strcmp( meas_type, "rms" ) == 0 ) *result = sqrt(*result);
+ if ( strcmp( meas_type, "avg" ) == 0 && constant_y == TRUE ) *result = *y;
+ }
+ else {
+ *result = ( sum1 + sum2 + sum3 );
+ }
+ txfree(x); txfree(y); txfree(width);
+ }
+ else {
+ if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: (measure2) unknown meas function '%s'.\n", meas_type );
+ return TRUE;
+ }
+ return failed;
+}
+
+static bool
+chkAnalysisType( char *an_type ) {
+ /*
+ if ( strcmp( an_type, "ac" ) != 0 && strcmp( an_type, "dc" ) != 0 &&
+ strcmp( an_type, "noise" ) != 0 && strcmp( an_type, "tran" ) != 0 &&
+ strcmp( an_type, "fft" ) != 0 && strcmp( an_type, "four" ) != 0 )
+ */
+ /* only support tran analysis type for now */
+ if ( strcmp( an_type, "tran" ) != 0 )
+ return FALSE;
+ else return TRUE;
+}
+
+static bool
+get_int_value( char **line, char *name, int *value ) {
+ char *token = gettok(line);
+ bool return_val = TRUE;
+ char *equal_ptr;
+
+ if ( strncmp( token, name, strlen(name) ) != 0 ) {
+ if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; expecting next field to be '%s'.\n", name );
+ return_val = FALSE;
+ } else {
+ /* see if '=' is last char of current token -- implies we need to read value in next token */
+ if ( *(token + strlen(token) - 1) == '=' ) {
+ txfree(token);
+ token = gettok(line);
+ *value = atoi(token);
+ } else {
+ if ( (equal_ptr = strstr( token, "=" )) ) {
+ *value = atoi(equal_ptr+1);
+ } else {
+ if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; missing '='!\n" );
+ return_val = FALSE;
+ }
+ }
+ }
+ txfree(token);
+
+ return return_val;
+}
+
+static bool
+get_double_value( char **line, char *name, double *value ) {
+ char *token = gettok(line);
+ bool return_val = TRUE;
+ char *equal_ptr, *junk;
+ int err;
+
+ if ( strncmp( token, name, strlen(name) ) != 0 ) {
+ if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; expecting next field to be '%s'.\n", name );
+ return_val = FALSE;
+ } else {
+ /* see if '=' is last char of current token -- implies we need to read value in next token */
+ if ( *(token + strlen(token) - 1) == '=' ) {
+ txfree(token);
+ junk = token = gettok(line);
+
+ *value = INPevaluate( &junk, &err, 1 );
+ } else {
+ if ( (equal_ptr = strstr( token, "=" )) ) {
+ equal_ptr += 1;
+ *value = INPevaluate( &equal_ptr, &err, 1 );
+ } else {
+ if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; missing '='!\n" );
+ return_val = FALSE;
+ }
+ }
+ if ( err ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: Bad value.\n" ); return_val = FALSE; }
+ }
+ txfree(token);
+
+ return return_val;
+}
+
+static char*
+get_vector_name( char **line ) {
+ char *token, *name;
+
+ token = name = gettok(line);
+
+ *(name + strlen(name) - 1) = '\0';
+ name = strdup(name); txfree(token);
+
+ return name;
+}
+
+static bool
+do_delay_measurement( char *resname, char *out_line, char *line, char *o_line, int meas_index, double *result ) {
+ char *trig_name, *targ_name, *token;
+ char trig_type, targ_type, trig_polarity, targ_polarity;
+ double targ_value, trig_value;
+ int trig_index, targ_index;
+ double trig_time = 0, targ_time = 0;
+ int precision = get_measure_precision();
+ bool failed;
+
+ measure_valid[meas_index] = FALSE;
+
+ trig_type = *line; line += 2; /* skip over vector type and open paren */
+ trig_name = get_vector_name( &line );
+ if ( trig_type != 'v' && trig_type != 'i' ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unexpected vector type '%c' for .meas!\n", trig_type );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(trig_name); return FALSE;
+ }
+
+ if ( !get_double_value( &line, "val", &trig_value ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); return FALSE; }
+
+ if ( strncmp( line, "rise", 4 ) == 0 ) {
+ trig_polarity = 'r';
+ if ( !get_int_value( &line, "rise", &trig_index ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); return FALSE; }
+ }
+ else if ( strncmp( line, "fall", 4 ) == 0 ) {
+ trig_polarity = 'f';
+ if ( !get_int_value( &line, "fall", &trig_index ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); return FALSE; }
+ }
+ else {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: expecting next token to be rise|fall for measurement!\n" );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(trig_name); return FALSE;
+ }
+
+ token = gettok(&line);
+ if ( strcmp(token, "targ" ) != 0 ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: expected 'targ' as next token in .meas statement!\n" );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(token); txfree(trig_name); return FALSE;
+ }
+ txfree(token);
+
+ targ_type = *line; line += 2; /* skip over vector type and open paren */
+ targ_name = get_vector_name( &line );
+ if ( targ_type != 'v' && targ_type != 'i' ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unexpected vector type '%c' for .meas!\n", targ_type );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(trig_name); txfree(targ_name); return FALSE;
+ }
+
+ if ( !get_double_value( &line, "val", &targ_value ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); txfree(targ_name); return FALSE; }
+
+ if ( strncmp( line, "rise", 4 ) == 0 ) {
+ targ_polarity = 'r';
+ if ( !get_int_value( &line, "rise", &targ_index ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); txfree(targ_name); return FALSE; }
+ }
+ else if ( strncmp( line, "fall", 4 ) == 0 ) {
+ targ_polarity = 'f';
+ if ( !get_int_value( &line, "fall", &targ_index ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(trig_name); txfree(targ_name); return FALSE; }
+ }
+ else {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: expecting next token to be rise|fall for measurement!\n" );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(trig_name); txfree(targ_name); return FALSE;
+ }
+
+ failed = measure( trig_name, trig_value, trig_polarity, trig_index, targ_name, targ_value, targ_polarity,
+ targ_index, result, &trig_time, &targ_time );
+
+ if ( !failed ) {
+ sprintf( out_line, "%-15s= %.*e targ= %.*e trig= %.*e\n", resname, precision, *result, precision, targ_time, precision, trig_time );
+ measure_valid[meas_index] = TRUE;
+ } else {
+ measures_passed = FALSE;
+ sprintf( out_line, "%-15s= failed\n", resname );
+ measure_valid[meas_index] = FALSE;
+ }
+
+ txfree(trig_name); txfree(targ_name);
+
+ return ( failed ) ? FALSE : TRUE;
+}
+
+static bool
+do_other_measurement( char *resname, char *out_line, char *meas_type, char *line, char *o_line, int meas_index, double *result ) {
+
+ char *vec_name;
+ char vec_type;
+ double from, to, result_time = 0;
+ int precision = get_measure_precision();
+ bool failed;
+
+ vec_type = *line; line += 2; /* skip over vector type and open paren */
+ vec_name = get_vector_name( &line );
+ if ( vec_type != 'v' && vec_type != 'i' ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unexpected vector type '%c' for .meas!\n", vec_type );
+ fprintf( cp_err, " %s\n", o_line );
+ }
+ txfree(vec_name); return FALSE;
+ }
+ if ( !get_double_value( &line, "from", &from ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(vec_name); return FALSE; }
+ if ( !get_double_value( &line, "to", &to ) ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, " %s\n", o_line ); txfree(vec_name); return FALSE; }
+
+ failed = measure2( meas_type, vec_name, vec_type, from, to, result, &result_time );
+
+ if ( !failed ) {
+ if ( strcmp( meas_type, "max" ) == 0 || strcmp( meas_type, "min" ) == 0 )
+ sprintf( out_line, "%-15s= %.*e at= %.*e\n", resname, precision, *result, precision, result_time );
+ else
+ sprintf( out_line, "%-15s= %.*e from= %.*e to= %.*e\n", resname, precision, *result, precision, from, precision, to );
+ measure_valid[meas_index] = TRUE;
+ } else {
+ measures_passed = FALSE;
+ sprintf( out_line, "%-15s= failed\n", resname );
+ measure_valid[meas_index] = FALSE;
+ }
+
+ txfree(vec_name);
+
+ return ( failed ) ? FALSE : TRUE;
+}
+
+void
+do_measure( char *what, bool chk_only ) {
+ struct line *meas_card, *meas_results = NULL, *end = NULL, *newcard;
+ char *line, *an_name, *an_type, *resname, *meastype, *str_ptr, out_line[1000];
+ int index = 0, ok = 0;
+ double result = 0;
+ int precision = get_measure_precision();
+ bool first_time = TRUE;
+
+ just_chk_meas = chk_only;
+
+ an_name = strdup( what );
+ strtolower( an_name );
+
+ for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) {
+ line = meas_card->li_line;
+
+ txfree(gettok(&line)); /* discard .meas */
+
+ an_type = gettok(&line); resname = gettok(&line); meastype = gettok(&line);
+
+ if ( chkAnalysisType( an_type ) != TRUE ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum );
+ fprintf( cp_err, " %s\n", meas_card->li_line );
+ }
+
+ txfree(an_type); txfree(resname); txfree(meastype);
+ continue;
+ }
+ else if ( first_time ) {
+ first_time = FALSE;
+
+ if ( just_chk_meas != TRUE && strcmp( an_type, "tran" ) == 0 ) fprintf( stdout, " Transient Analysis\n\n" );
+ }
+
+ /* skip param|expr measurement types for now -- will be done after other measurements */
+ if ( strncmp( meastype, "param", 5 ) == 0 || strncmp( meastype, "expr", 4 ) == 0 ) continue;
+
+ if ( strcmp( an_name, an_type ) != 0 ) {
+ txfree(an_type); txfree(resname); txfree(meastype);
+ continue;
+ }
+
+ if ( strcmp( meastype, "trig" ) == 0 || strcmp( meastype, "delay" ) == 0 ) {
+ if ( do_delay_measurement( resname, out_line, line, meas_card->li_line, index++, &result ) && just_chk_meas != TRUE ) {
+ nupa_add_param( resname, result );
+ }
+ }
+ else if ( strcmp( meastype, "avg" ) == 0 || strcmp( meastype, "mean" ) == 0 ||
+ strcmp( meastype, "max" ) == 0 || strcmp( meastype, "min" ) == 0 ||
+ strcmp( meastype, "rms" ) == 0 || strcmp( meastype, "integ" ) == 0 ||
+ strcmp( meastype, "integral" ) == 0 ) {
+ if ( do_other_measurement( resname, out_line, meastype, line, meas_card->li_line, index++, &result ) && just_chk_meas != TRUE ) {
+ nupa_add_param( resname, result );
+ }
+ }
+ else {
+ measures_passed = FALSE;
+ sprintf( out_line, "%-15s= failed\n", resname );
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unsupported measurement type '%s' on line %d:\n", meastype, meas_card->li_linenum );
+ fprintf( cp_err, " %s\n", meas_card->li_line );
+ }
+ }
+
+ newcard = alloc(struct line);
+ newcard->li_line = strdup(out_line);
+ newcard->li_next = NULL;
+
+ if ( meas_results == NULL ) meas_results = end = newcard;
+ else {
+ end->li_next = newcard;
+ end = newcard;
+ }
+
+ txfree(an_type); txfree(resname); txfree(meastype);
+
+ // see if number of measurements exceeds fixed array size of 20,000
+ if ( index >= 20000 ) {
+ fprintf( stderr, "ERROR: number of measurements exceeds 20,000!\nAborting...\n" );
+ exit(-1);
+ }
+ }
+
+ // now do param|expr .meas statements
+ newcard = meas_results;
+ for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) {
+ line = meas_card->li_line;
+
+ txfree(gettok(&line)); /* discard .meas */
+
+ an_type = gettok(&line); resname = gettok(&line); meastype = gettok(&line);
+
+ if ( chkAnalysisType( an_type ) != TRUE ) {
+ if ( just_chk_meas != TRUE ) {
+ fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum );
+ fprintf( cp_err, " %s\n", meas_card->li_line );
+ }
+
+ txfree(an_type); txfree(resname); txfree(meastype);
+ continue;
+ }
+ if ( strcmp( an_name, an_type ) != 0 ) {
+ txfree(an_type); txfree(resname); txfree(meastype);
+ continue;
+ }
+
+ if ( strncmp( meastype, "param", 5 ) != 0 && strncmp( meastype, "expr", 4 ) != 0 ) {
+
+ if ( just_chk_meas != TRUE ) fprintf( stdout, "%s", newcard->li_line );
+ end = newcard;
+ newcard = newcard->li_next;
+
+ txfree( end->li_line );
+ txfree( end );
+
+ txfree(an_type); txfree(resname); txfree(meastype);
+ continue;
+ }
+
+ if ( just_chk_meas != TRUE ) fprintf( stdout, "%-15s=", resname );
+
+ if ( just_chk_meas != TRUE ) {
+ ok = nupa_eval( meas_card->li_line, meas_card->li_linenum );
+
+ if ( ok ) {
+ str_ptr = strstr( meas_card->li_line, meastype );
+ if ( !get_double_value( &str_ptr, meastype, &result ) ) {
+ if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" );
+ }
+ else {
+ if ( just_chk_meas != TRUE ) fprintf( stdout, " %.*e\n", precision, result );
+ nupa_add_param( resname, result );
+ }
+ }
+ else {
+ if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" );
+ }
+ }
+ txfree(an_type); txfree(resname); txfree(meastype);
+ }
+
+ if ( just_chk_meas != TRUE ) fprintf( stdout, "\n" );
+
+ txfree(an_name);
+
+ fflush( stdout );
+
+ //nupa_list_params();
+}
+
+bool
+check_autostop( char* what ) {
+ bool flag = FALSE;
+ bool autostop;
+
+ measures_passed = TRUE;
+ if ( cp_getvar( "autostop", VT_BOOL, (bool *) &autostop ) ) {
+ do_measure( what, TRUE );
+
+ if ( measures_passed == TRUE ) flag = TRUE;
+ }
+
+ return flag;
+}
diff --git a/src/frontend/nutinp.c b/src/frontend/nutinp.c
index 52fa573a1..6237e1967 100644
--- a/src/frontend/nutinp.c
+++ b/src/frontend/nutinp.c
@@ -33,7 +33,7 @@ inp_nutsource(FILE *fp, bool comfile, char *filename)
wordlist *controls = NULL;
FILE *lastin, *lastout, *lasterr;
- inp_readall(fp, &deck);
+ inp_readall(fp, &deck, 0);
if (!deck)
return;
diff --git a/src/frontend/runcoms.c b/src/frontend/runcoms.c
index c1ed53136..ed1664d17 100644
--- a/src/frontend/runcoms.c
+++ b/src/frontend/runcoms.c
@@ -323,6 +323,9 @@ dosim(char *what, wordlist *wl)
wl->wl_prev = NULL;
tfree(ww);
}
+
+ if ( !err ) do_measure( ft_curckt->ci_last_an, FALSE );
+
return err;
}
diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c
index a5a88772a..2d7e9521d 100644
--- a/src/frontend/subckt.c
+++ b/src/frontend/subckt.c
@@ -64,6 +64,7 @@ $Id$
#ifdef NUMPARAMS
/* Uncomment to turn on tracing for the Numparam */
/*#define TRACE_NUMPARAMS*/
+/*#define TRACE*/
#include "numparam/numpaif.h"
#endif
@@ -170,22 +171,28 @@ inp_subcktexpand(struct line *deck)
#ifdef NUMPARAMS
(void) cp_getvar("numparams", VT_BOOL, (char *) &use_numparams);
+ use_numparams = TRUE;
+
/* deck has .control sections already removed, but not comments */
if ( use_numparams ) {
#ifdef TRACE_NUMPARAMS
- printf("Numparams is processing this deck:\n");
+ fprintf(stderr,"Numparams is processing this deck:\n");
c=deck;
while( c!=NULL) {
- printf("%3d:%s\n",c->li_linenum, c->li_line);
+ fprintf(stderr,"%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next;
}
#endif /* TRACE_NUMPARAMS */
-
+
ok = nupa_signal( NUPADECKCOPY, NULL);
+ /* get the subckt/model names from the deck */
c=deck;
while ( c != NULL) { /* first Numparam pass */
- nupa_scan(c->li_line, c->li_linenum);
+ if ( ciprefix( ".subckt", c->li_line ) )
+ nupa_scan(c->li_line, c->li_linenum, TRUE);
+ if ( ciprefix( ".model", c->li_line ) )
+ nupa_scan(c->li_line, c->li_linenum, FALSE);
c= c->li_next;
}
c=deck;
@@ -193,12 +200,13 @@ inp_subcktexpand(struct line *deck)
c->li_line = nupa_copy(c->li_line, c->li_linenum);
c= c->li_next;
}
+ /* now copy instances */
#ifdef TRACE_NUMPARAMS
- printf("Numparams transformed deck:\n");
+ fprintf(stderr,"Numparams transformed deck:\n");
c=deck;
while( c!=NULL) {
- printf("%3d:%s\n",c->li_linenum, c->li_line);
+ fprintf(stderr,"%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next;
}
#endif /* TRACE_NUMPARAMS */
@@ -328,18 +336,27 @@ inp_subcktexpand(struct line *deck)
ok= ok && nupa_signal(NUPASUBDONE, NULL);
c= ll;
while (c != NULL) {
- ok= ok && nupa_eval( c->li_line, c->li_linenum);
+ // 'param' .meas statements can have dependencies on measurement values
+ // need to skip evaluating here and evaluate after other .meas statements
+ if ( ciprefix( ".meas", c->li_line ) ) {
+ if ( !strstr( c->li_line, "param" ) ) nupa_eval( c->li_line, c->li_linenum);
+ } else {
+ //ok= ok && nupa_eval( c->li_line, c->li_linenum);
+ nupa_eval( c->li_line, c->li_linenum);
+ }
c= c->li_next;
}
#ifdef TRACE_NUMPARAMS
- printf("Numparams converted deck:\n");
+ fprintf(stderr,"Numparams converted deck:\n");
c=ll;
while( c!=NULL) {
- printf("%3d:%s\n",c->li_linenum, c->li_line);
+ fprintf(stderr,"%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next;
}
#endif /* TRACE_NUMPARAMS */
- ok= ok && nupa_signal(NUPAEVALDONE, NULL);
+ //ok= ok && nupa_signal(NUPAEVALDONE, NULL);
+ //nupa_list_params(stdout);
+ nupa_copy_inst_dico();
}
#endif /* NUMPARAMS */
return (ll); /* return the spliced deck. */
@@ -385,6 +402,7 @@ doit(struct line *deck)
#endif
/* First pass: xtract all the .subckts and stick pointers to them into sss. */
for (last = deck, lc = NULL; last; ) {
+
if (ciprefix(sbend, last->li_line)) { /* if line == .ends */
fprintf(cp_err, "Error: misplaced %s line: %s\n", sbend,
last->li_line);
@@ -504,6 +522,7 @@ doit(struct line *deck)
gotone = FALSE;
for (c = deck, lc = NULL; c; ) {
if (ciprefix(invoke, c->li_line)) { /* found reference to .subckt (i.e. component with refdes X) */
+
char *tofree, *tofree2;
gotone = TRUE;
t = tofree = s = copy(c->li_line); /* s & t hold copy of component line */
@@ -512,7 +531,7 @@ doit(struct line *deck)
* e.g. if invocation is Xreference, *scname = reference
*/
tofree2 = scname = gettok(&s);
- scname += strlen(invoke);
+ /*scname += strlen(invoke); */
while ((*scname == ' ') || (*scname == '\t') ||
(*scname == ':'))
scname++;
@@ -659,7 +678,7 @@ inp_deckcopy(struct line *deck)
d->li_linenum = deck->li_linenum;
d->li_line = copy(deck->li_line);
if (deck->li_error)
- d->li_error = copy(deck->li_error);
+ d->li_error = copy(deck->li_error);
d->li_actual = inp_deckcopy(deck->li_actual);
deck = deck->li_next;
}
@@ -684,7 +703,7 @@ static int
translate(struct line *deck, char *formal, char *actual, char *scname, char *subname)
{
struct line *c;
- char *buffer, *next_name, dev_type, *name, *s, *t, ch, *nametofree;
+ char *buffer, *next_name, dev_type, *name, *s, *t, ch, *nametofree, *paren_ptr, *new_str;
int nnodes, i, dim, blen;
int rtn=0;
@@ -705,9 +724,11 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
/* now iterate through the .subckt deck and translate the cards. */
for (c = deck; c; c = c->li_next) {
+ dev_type = *(c->li_line);
+
#ifdef TRACE
/* SDB debug statement */
- printf("\nIn translate, examining line %s \n", c->li_line);
+ printf("\nIn translate, examining line (dev_type: %c, subname: %s, instance: %s) %s \n", dev_type, subname, scname, c->li_line );
#endif
@@ -716,7 +737,7 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
/* Rename the device. */
switch (dev_type) {
case '\0':
- case '*':
+ case '*': case '$':
case '.':
/* Just a pointer to the line into s and then break */
buffer = tmalloc(2000+strlen(c->li_line)); /* DW,VA */
@@ -741,7 +762,7 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
/* maschmann
sprintf(buffer, "%s:%s ", name, scname); */
buffer = (char *)tmalloc((strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer, "a:%s:%s ", scname, name+1 );
+ sprintf(buffer, "a.%s.%s ", scname, name );
@@ -808,11 +829,11 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
if(name[0]=='v' || name[0]=='V') {
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer + blen, "v:%s:%s ", scname, name+1);
+ sprintf(buffer + blen, "v.%s.%s ", scname, name);
} else {
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+3)*sizeof(char));
- sprintf(buffer + blen, "%s:%s ", scname, name);
+ sprintf(buffer + blen, "%s.%s ", scname, name);
}
}
break;
@@ -863,19 +884,9 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
* and stick the translated name into buffer.
*/
ch = *name; /* ch identifies the type of component */
- name++;
- if (*name == ':')
- name++; /* now name point to the rest of the refdes */
-
- if (*name) {
- buffer = (char *)tmalloc((strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer, "%c:%s:%s ", ch, scname, /* F:subcircuitname:refdesname */
- name);
- } else {
- buffer = (char *)tmalloc((strlen(scname)+4)*sizeof(char));
- sprintf(buffer, "%c:%s ", ch, scname); /* F:subcircuitname */
- }
+ buffer = (char *)tmalloc((strlen(scname)+strlen(name)+5)*sizeof(char));
+ sprintf(buffer, "%c.%s.%s ", ch, scname, name);
tfree(t);
@@ -903,7 +914,7 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
*/
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+3)*sizeof(char));
- sprintf(buffer + blen, "%s:%s ", scname, name);
+ sprintf(buffer + blen, "%s.%s ", scname, name);
}
tfree(name);
} /* while (nnodes-- . . . . */
@@ -975,12 +986,10 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
#endif
ch = *name; /* ch is the first char of the token. */
- name++;
- if (*name == ':')
- name++; /* name now points to the remainder of the token */
+
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer + blen, "%c:%s:%s ", ch, scname, name);
+ sprintf(buffer + blen, "%c.%s.%s ", ch, scname, name);
/* From Vsense and Urefdes creates V:Urefdes:sense */
}
else { /* Handle netname */
@@ -1003,7 +1012,7 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
*/
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+3)*sizeof(char));
- sprintf(buffer + blen, "%s:%s ", scname, name);
+ sprintf(buffer + blen, "%s.%s ", scname, name);
/* From netname and Urefdes creates Urefdes:netname */
}
}
@@ -1034,17 +1043,14 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
*/
ch = *name;
- name++;
- if (*name == ':')
- name++;
-
- if (*name) {
+ if ( ch != 'x' ) {
buffer = (char *)tmalloc((strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer, "%c:%s:%s ", ch, scname, name);
+ sprintf(buffer, "%c.%s.%s ", ch, scname, name);
} else {
- buffer = (char *)tmalloc((strlen(scname)+4)*sizeof(char));
- sprintf(buffer, "%c:%s ", ch, scname);
+ buffer = (char *)tmalloc((strlen(scname)+strlen(name)+3)*sizeof(char));
+ sprintf(buffer, "%s.%s ", scname, name);
}
+
tfree(nametofree);
@@ -1071,7 +1077,7 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
*/
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+3)*sizeof(char));
- sprintf(buffer + blen, "%s:%s ", scname, name);
+ sprintf(buffer + blen, "%s.%s ", scname, name);
}
tfree(name);
} /* while (nnodes-- . . . . */
@@ -1089,19 +1095,17 @@ translate(struct line *deck, char *formal, char *actual, char *scname, char *sub
goto quit;
}
ch = *name;
- name++;
- if (*name == ':')
- name++;
- if (*name) {
+ if ( ch != 'x' ) {
blen = strlen(buffer);
buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+5)*sizeof(char));
- sprintf(buffer + blen, "%c:%s:%s ", ch, scname, name);
+ sprintf(buffer + blen, "%c.%s.%s ", ch, scname, name);
} else {
blen = strlen(buffer);
- buffer = (char *)trealloc(buffer, (blen+strlen(scname)+4)*sizeof(char));
- sprintf(buffer + blen, "%c:%s ", ch, scname);
+ buffer = (char *)trealloc(buffer, (blen+strlen(scname)+strlen(name)+2)*sizeof(char));
+ sprintf(buffer + blen, "%s ", scname);
}
+
tfree(t);
} /* while (nnodes--. . . . */
@@ -1204,14 +1208,14 @@ finishLine(char *dst, char *src, char *scname)
if ((which == 'i' || which == 'I') &&
(buf[0] == 'v' || buf[0] == 'V')) {
*dst++ = buf[0];
- *dst++ = ':';
+ *dst++ = '.';
i = 1;
} else {
i = 0;
}
for (s = scname; *s; )
*dst++ = *s++;
- *dst++ = ':';
+ *dst++ = '.';
for (s = buf + i; *s; )
*dst++ = *s++;
}
@@ -1234,7 +1238,7 @@ finishLine(char *dst, char *src, char *scname)
} else {
for (s = scname; *s; )
*dst++ = *s++;
- *dst++ = ':';
+ *dst++ = '.';
for (s = buf; *s; )
*dst++ = *s++;
}
@@ -1870,7 +1874,7 @@ inp_numnodes(char c)
case '\t':
case '.':
case 'x':
- case '*':
+ case '*': case '$':
return (0);
case 'b': return (2);
diff --git a/src/include/ftedefs.h b/src/include/ftedefs.h
index 23f416a6a..8a07ce47c 100644
--- a/src/include/ftedefs.h
+++ b/src/include/ftedefs.h
@@ -31,6 +31,8 @@ struct circ {
struct line *ci_deck; /* The input deck. */
struct line *ci_origdeck;/* The input deck, before subckt expansion. */
struct line *ci_options;/* The .option cards from the deck... */
+ struct line *ci_meas; /* .measure commands to run after simulation */
+ struct line *ci_param; /* .param statements found in deck */
struct variable *ci_vars; /* ... and the parsed versions. */
bool ci_inprogress; /* We are in a break now. */
bool ci_runonce; /* So com_run can to a reset if necessary... */
diff --git a/src/include/fteext.h b/src/include/fteext.h
index ef34514b7..7c9fc15ce 100644
--- a/src/include/fteext.h
+++ b/src/include/fteext.h
@@ -393,6 +393,10 @@ extern int raw_prec;
extern void raw_write(char *name, struct plot *pl, bool app, bool binary);
extern struct plot *raw_read();
+/* meas.c */
+extern void do_measure(char *what, bool chk_only);
+extern bool check_autostop(char *what);
+
/* resource.c */
extern void com_rusage();
diff --git a/src/spicelib/analysis/dctran.c b/src/spicelib/analysis/dctran.c
index fd1c97394..318b7127d 100644
--- a/src/spicelib/analysis/dctran.c
+++ b/src/spicelib/analysis/dctran.c
@@ -7,7 +7,8 @@ Modified: 2000 AlansFixes
/* subroutine to do DC TRANSIENT analysis
--- ONLY, unlike spice2 routine with the same name! */
-#include
+#include
+#include
#include
#include
#include
@@ -35,6 +36,22 @@ Modified: 2000 AlansFixes
void SetAnalyse( char * Analyse, int Percent);
#endif
+// Initial AlmostEqualULPs version - fast and simple, but
+// some limitations.
+static bool AlmostEqualUlps(float A, float B, int maxUlps)
+{
+ assert(sizeof(float) == sizeof(int));
+
+ if (A == B)
+ return TRUE;
+
+ int intDiff = abs(*(int*)&A - *(int*)&B);
+
+ if (intDiff <= maxUlps)
+ return TRUE;
+
+ return FALSE;
+}
int
DCtran(CKTcircuit *ckt,
@@ -458,9 +475,13 @@ nextTime:
/* gtri - end - wbk - Update event queues/data for accepted timepoint */
#endif
ckt->CKTstat->STAToldIter = ckt->CKTstat->STATnumIter;
- if(fabs(ckt->CKTtime - ckt->CKTfinalTime) < ckt->CKTminBreak) {
- /*printf(" done: time is %g, final time is %g, and tol is %g\n",*/
- /*ckt->CKTtime,ckt->CKTfinalTime,ckt->CKTminBreak);*/
+ if(check_autostop("tran") ||
+ fabs(ckt->CKTtime - ckt->CKTfinalTime) < ckt->CKTminBreak ||
+ AlmostEqualUlps( ckt->CKTtime, ckt->CKTfinalTime, 3 ) ) {
+#ifdef STEPDEBUG
+ printf(" done: time is %g, final time is %g, and tol is %g\n",
+ ckt->CKTtime,ckt->CKTfinalTime,ckt->CKTminBreak);
+#endif
(*(SPfrontEnd->OUTendPlot))( (((TRANan*)ckt->CKTcurJob)->TRANplot));
ckt->CKTcurrentAnalysis = 0;
ckt->CKTstat->STATtranTime += (*(SPfrontEnd->IFseconds))()-startTime;
@@ -522,7 +543,8 @@ resume:
MIN(ckt->CKTdelta,ckt->CKTmaxStep);
#ifdef XSPICE
/* gtri - begin - wbk - Cut integration order if first timepoint after breakpoint */
- if(ckt->CKTtime == g_mif_info.breakpoint.last)
+ //if(ckt->CKTtime == g_mif_info.breakpoint.last)
+ if ( AlmostEqualUlps( ckt->CKTtime, g_mif_info.breakpoint.last, 3 ) )
ckt->CKTorder = 1;
/* gtri - end - wbk - Cut integration order if first timepoint after breakpoint */
@@ -530,7 +552,8 @@ resume:
/* are we at a breakpoint, or indistinguishably close? */
- if ((ckt->CKTtime == *(ckt->CKTbreaks)) || (*(ckt->CKTbreaks) -
+ //if ((ckt->CKTtime == *(ckt->CKTbreaks)) || (*(ckt->CKTbreaks) -
+ if ( AlmostEqualUlps( ckt->CKTtime, *(ckt->CKTbreaks), 3 ) || (*(ckt->CKTbreaks) -
(ckt->CKTtime) <= ckt->CKTdelmin)) {
/* first timepoint after a breakpoint - cut integration order */
/* and limit timestep to .1 times minimum of time to next breakpoint,
@@ -581,8 +604,16 @@ resume:
/* gtri - begin - wbk - Modify Breakpoint stuff */
/* Throw out any permanent breakpoint times <= current time */
while(1) {
- if(*(ckt->CKTbreaks) <= (ckt->CKTtime + ckt->CKTminBreak))
+#ifdef STEPDEBUG
+ printf(" brk_pt: %g ckt_time: %g ckt_min_break: %g\n",*(ckt->CKTbreaks), ckt->CKTtime, ckt->CKTminBreak);
+#endif
+ if(AlmostEqualUlps(*(ckt->CKTbreaks),ckt->CKTtime,3) || *(ckt->CKTbreaks) <= (ckt->CKTtime + ckt->CKTminBreak)) {
+#ifdef STEPDEBUG
+ printf("throwing out permanent breakpoint times <= current time (brk pt: %g)\n",*(ckt->CKTbreaks));
+ printf(" ckt_time: %g ckt_min_break: %g\n",ckt->CKTtime, ckt->CKTminBreak);
+#endif
CKTclrBreak(ckt);
+ }
else
break;
}
diff --git a/src/spicelib/parser/inp2dot.c b/src/spicelib/parser/inp2dot.c
index 46e243702..f3e3f395c 100644
--- a/src/spicelib/parser/inp2dot.c
+++ b/src/spicelib/parser/inp2dot.c
@@ -728,6 +728,14 @@ INP2dot(void *ckt, INPtables *tab, card *current, void *task, void *gnode)
LITERR(" Warning: .global not yet implemented - ignored \n");
goto quit;
}
+ /* ignore .meas statements -- these will be handled after analysis */
+ /* also ignore .param statements */
+ /* ignore .prot, .unprot */
+ else if (strcmp(token, ".meas") == 0 || strcmp(token, ".param") == 0 || strcmp(token, ".measure") == 0 ||
+ strcmp(token, ".prot") == 0 || strcmp(token, ".unprot") == 0) {
+ rtn = 0;
+ goto quit;
+ }
LITERR(" unimplemented control card - error \n");
quit:
tfree(token);
diff --git a/src/spicelib/parser/inppas2.c b/src/spicelib/parser/inppas2.c
index 7392b4154..f1f47907a 100644
--- a/src/spicelib/parser/inppas2.c
+++ b/src/spicelib/parser/inppas2.c
@@ -215,7 +215,7 @@ void INPpas2(void *ckt, card * data, INPtables * tab, void *task)
INP2K(ckt, tab, current);
break;
- case '*':
+ case '*': case '$':
/* * - a comment - ignore */
break;