659 lines
19 KiB
C
659 lines
19 KiB
C
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
|
|
Modified: 2000 AlansFixes
|
|
**********/
|
|
|
|
/*
|
|
* Spice-2 compatibility stuff for .plot, .print, .four, and .width.
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include <assert.h>
|
|
#include "ngspice/cpdefs.h"
|
|
#include "ngspice/ftedefs.h"
|
|
#include "ngspice/dstring.h"
|
|
#include "ngspice/dvec.h"
|
|
#include "ngspice/fteinp.h"
|
|
#include "ngspice/sim.h"
|
|
#include "circuits.h"
|
|
#include "dotcards.h"
|
|
#include "variable.h"
|
|
#include "fourier.h"
|
|
#include "breakp2.h"
|
|
#include "com_measure2.h"
|
|
#include "com_commands.h"
|
|
#include "com_asciiplot.h"
|
|
#include "resource.h"
|
|
#include "postcoms.h"
|
|
|
|
/* Extract all the .save lines */
|
|
|
|
static void fixdotplot(wordlist *wl);
|
|
static void fixdotprint(wordlist *wl);
|
|
static char *fixem(char *string);
|
|
void ft_savemeasure(void);
|
|
|
|
|
|
static struct plot *
|
|
setcplot(char *name)
|
|
{
|
|
struct plot *pl;
|
|
|
|
for (pl = plot_list; pl; pl = pl->pl_next)
|
|
if (ciprefix(name, pl->pl_typename))
|
|
return pl;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* All lines with .width, .plot, .print, .save, .op, .meas, .tf
|
|
have been assembled into a wordlist (wl_first) in inp.c:inp_spsource(),
|
|
and then stored to ci_commands in inp.c:inp_dodeck().
|
|
The .save lines are selected, com_save will put the commands into dbs.
|
|
*/
|
|
|
|
void
|
|
ft_dotsaves(void)
|
|
{
|
|
wordlist *iline, *wl = NULL;
|
|
char *s;
|
|
|
|
if (!ft_curckt) /* Shouldn't happen. */
|
|
return;
|
|
|
|
for (iline = ft_curckt->ci_commands; iline; iline = iline->wl_next)
|
|
if (ciprefix(".save", iline->wl_word)) {
|
|
s = iline->wl_word;
|
|
/* skip .save */
|
|
s = nexttok(s);
|
|
wl = wl_append(wl, gettoks(s));
|
|
}
|
|
|
|
com_save(wl);
|
|
wl_free(wl);
|
|
}
|
|
|
|
|
|
/* Go through the dot lines given and make up a big "save" command with
|
|
* all the node names mentioned. Note that if a node is requested for
|
|
* one analysis, it is saved for all of them.
|
|
*/
|
|
|
|
static char *plot_opts[ ] = {
|
|
"linear",
|
|
"xlog",
|
|
"ylog",
|
|
"loglog"
|
|
};
|
|
|
|
|
|
int
|
|
ft_savedotargs(void)
|
|
{
|
|
wordlist *w, *wl = NULL, *iline, **prev_wl, *w_next;
|
|
char *name;
|
|
char *s;
|
|
int some = 0;
|
|
static wordlist all = { "all", NULL, NULL };
|
|
int isaplot;
|
|
int i;
|
|
int status;
|
|
|
|
if (!ft_curckt) /* Shouldn't happen. */
|
|
return 0;
|
|
|
|
for (iline = ft_curckt->ci_commands; iline; iline = iline->wl_next) {
|
|
s = iline->wl_word;
|
|
if (ciprefix(".plot", s))
|
|
isaplot = 1;
|
|
else
|
|
isaplot = 0;
|
|
|
|
if (isaplot || ciprefix(".print", s)) {
|
|
s = nexttok(s);
|
|
name = gettok(&s);
|
|
|
|
if ((w = gettoks(s)) == NULL) {
|
|
fprintf(cp_err, "Warning: no nodes given: %s\n", iline->wl_word);
|
|
} else {
|
|
if (isaplot) {
|
|
prev_wl = &w;
|
|
for (wl = w; wl; wl = w_next) {
|
|
w_next = wl->wl_next;
|
|
for (i = 0; (size_t) i < NUMELEMS(plot_opts); i++) {
|
|
if (!strcmp(wl->wl_word, plot_opts[i])) {
|
|
/* skip it */
|
|
*prev_wl = w_next;
|
|
tfree(wl);
|
|
break;
|
|
}
|
|
}
|
|
if (i == NUMELEMS(plot_opts))
|
|
prev_wl = &wl->wl_next;
|
|
}
|
|
}
|
|
some = 1;
|
|
com_save2(w, name);
|
|
}
|
|
} else if (ciprefix(".four", s)) {
|
|
s = nexttok(s);
|
|
s = nexttok(s);
|
|
if ((w = gettoks(s)) == NULL) {
|
|
fprintf(cp_err, "Warning: no nodes given: %s\n", iline->wl_word);
|
|
} else {
|
|
some = 1;
|
|
com_save2(w, "TRAN"); /* A hack */
|
|
}
|
|
} else if (ciprefix(".meas", s)) {
|
|
status = measure_extract_variables(s);
|
|
if (!(status)) {
|
|
some = 1;
|
|
}
|
|
} else if (ciprefix(".op", s)) {
|
|
some = 1;
|
|
com_save2(&all, "OP");
|
|
} else if (ciprefix(".tf", s)) {
|
|
some = 1;
|
|
com_save2(&all, "TF");
|
|
}
|
|
}
|
|
return some;
|
|
}
|
|
|
|
|
|
void
|
|
ft_savemeasure(void)
|
|
{
|
|
char *s;
|
|
wordlist *iline;
|
|
|
|
if (!ft_curckt) /* Shouldn't happen. */
|
|
return;
|
|
|
|
for (iline = ft_curckt->ci_commands; iline; iline = iline->wl_next) {
|
|
s = iline->wl_word;
|
|
if (ciprefix(".measure", s)) {
|
|
(void) measure_extract_variables(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Execute the .whatever lines found in the deck, after we are done running.
|
|
* We'll be cheap and use cp_lexer to get the words... This should make us
|
|
* spice-2 compatible. If terse is TRUE then there was a rawfile, so don't
|
|
* print lots of junk.
|
|
*/
|
|
|
|
int
|
|
ft_cktcoms(bool terse)
|
|
{
|
|
wordlist *coms, *command, all;
|
|
char *plottype, *s;
|
|
struct dvec *v;
|
|
static wordlist twl = { "col", NULL, NULL };
|
|
struct plot *pl;
|
|
int i, found;
|
|
char numbuf[BSIZE_SP]; /* For printnum*/
|
|
|
|
all.wl_next = NULL;
|
|
all.wl_word = "all";
|
|
|
|
if (!ft_curckt) {
|
|
return 1;
|
|
}
|
|
|
|
plot_cur = setcplot("op");
|
|
if (!ft_curckt->ci_commands && !plot_cur)
|
|
goto nocmds;
|
|
coms = ft_curckt->ci_commands;
|
|
cp_interactive = FALSE;
|
|
|
|
/* Listing */
|
|
if (ft_listprint) {
|
|
if (terse)
|
|
fprintf(cp_err, ".options: no listing, rawfile was generated.\n");
|
|
else
|
|
inp_list(cp_out, ft_curckt->ci_deck, ft_curckt->ci_options, LS_DECK);
|
|
}
|
|
|
|
/* If there was a .op line, then we have to do the .op output. */
|
|
plot_cur = setcplot("op");
|
|
if (plot_cur != NULL) {
|
|
assert(plot_cur->pl_dvecs != NULL);
|
|
if (plot_cur->pl_dvecs->v_realdata != NULL) {
|
|
if (terse) {
|
|
fprintf(cp_out, "OP information in rawfile.\n");
|
|
} else {
|
|
fprintf(cp_out, "\t%-30s%15s\n", "Node", "Voltage");
|
|
fprintf(cp_out, "\t%-30s%15s\n", "----", "-------");
|
|
fprintf(cp_out, "\t----\t-------\n");
|
|
for (v = plot_cur->pl_dvecs; v; v = v->v_next) {
|
|
if (!isreal(v)) {
|
|
fprintf(cp_err,
|
|
"Internal error: op vector %s not real\n",
|
|
v->v_name);
|
|
continue;
|
|
}
|
|
if ((v->v_type == SV_VOLTAGE) && (*(v->v_name) != '@')) {
|
|
printnum(numbuf, v->v_realdata[0]);
|
|
fprintf(cp_out, "\t%-30s%15s\n", v->v_name, numbuf);
|
|
}
|
|
}
|
|
fprintf(cp_out, "\n\tSource\tCurrent\n");
|
|
fprintf(cp_out, "\t------\t-------\n\n");
|
|
for (v = plot_cur->pl_dvecs; v; v = v->v_next)
|
|
if (v->v_type == SV_CURRENT) {
|
|
printnum(numbuf, v->v_realdata[0]);
|
|
fprintf(cp_out, "\t%-30s%15s\n", v->v_name, numbuf);
|
|
}
|
|
fprintf(cp_out, "\n");
|
|
|
|
if (!ft_nomod) {
|
|
com_showmod(&all);
|
|
}
|
|
com_show(&all);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (pl = plot_list; pl; pl = pl->pl_next)
|
|
if (ciprefix("tf", pl->pl_typename)) {
|
|
if (terse) {
|
|
fprintf(cp_out, "TF information in rawfile.\n");
|
|
break;
|
|
}
|
|
plot_cur = pl;
|
|
fprintf(cp_out, "Transfer function information:\n");
|
|
com_print(&all);
|
|
fprintf(cp_out, "\n");
|
|
}
|
|
|
|
/* Now all the '.' lines */
|
|
while (coms) {
|
|
wordlist* freecom;
|
|
freecom = command = cp_lexer(coms->wl_word);
|
|
if (!command) {
|
|
/* Line not converted to a wordlist */
|
|
goto bad;
|
|
}
|
|
if (command->wl_word == (char*)NULL) {
|
|
/* Line not converted to a wordlist */
|
|
wl_free(freecom);
|
|
goto bad;
|
|
}
|
|
if (eq(command->wl_word, ".width")) {
|
|
do
|
|
command = command->wl_next;
|
|
while (command && !ciprefix("out", command->wl_word));
|
|
if (command) {
|
|
s = strchr(command->wl_word, '=');
|
|
if (!s || !s[1]) {
|
|
fprintf(cp_err, "Error: bad line %s\n", coms->wl_word);
|
|
coms = coms->wl_next;
|
|
wl_free(freecom);
|
|
continue;
|
|
}
|
|
i = atoi(++s);
|
|
cp_vset("width", CP_NUM, &i);
|
|
}
|
|
} else if (eq(command->wl_word, ".print")) {
|
|
if (terse) {
|
|
fprintf(cp_out,
|
|
".print line ignored since rawfile was produced.\n");
|
|
} else {
|
|
command = command->wl_next;
|
|
if (!command) {
|
|
fprintf(cp_err, "Error: bad line %s\n", coms->wl_word);
|
|
coms = coms->wl_next;
|
|
wl_free(freecom);
|
|
continue;
|
|
}
|
|
plottype = command->wl_word;
|
|
command = command->wl_next;
|
|
fixdotprint(command);
|
|
twl.wl_next = command;
|
|
found = 0;
|
|
for (pl = plot_list; pl; pl = pl->pl_next)
|
|
if (ciprefix(plottype, pl->pl_typename)) {
|
|
plot_cur = pl;
|
|
com_print(&twl);
|
|
fprintf(cp_out, "\n");
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
fprintf(cp_err, "Error: .print: no %s analysis found.\n",
|
|
plottype);
|
|
}
|
|
} else if (eq(command->wl_word, ".plot")) {
|
|
if (terse) {
|
|
fprintf(cp_out,
|
|
".plot line ignored since rawfile was produced.\n");
|
|
} else {
|
|
command = command->wl_next;
|
|
if (!command) {
|
|
fprintf(cp_err, "Error: bad line %s\n",
|
|
coms->wl_word);
|
|
coms = coms->wl_next;
|
|
wl_free(freecom);
|
|
continue;
|
|
}
|
|
plottype = command->wl_word;
|
|
command = command->wl_next;
|
|
fixdotplot(command);
|
|
found = 0;
|
|
for (pl = plot_list; pl; pl = pl->pl_next)
|
|
if (ciprefix(plottype, pl->pl_typename)) {
|
|
plot_cur = pl;
|
|
com_asciiplot(command);
|
|
fprintf(cp_out, "\n");
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
fprintf(cp_err, "Error: .plot: no %s analysis found.\n",
|
|
plottype);
|
|
}
|
|
} else if (ciprefix(".four", command->wl_word)) {
|
|
if (terse) {
|
|
fprintf(cp_out,
|
|
".fourier line ignored since rawfile was produced.\n");
|
|
} else {
|
|
int err;
|
|
|
|
plot_cur = setcplot("tran");
|
|
err = fourier(command->wl_next, plot_cur);
|
|
if (!err)
|
|
fprintf(cp_out, "\n\n");
|
|
else
|
|
fprintf(cp_err, "No transient data available for "
|
|
"fourier analysis");
|
|
}
|
|
} else if (!eq(command->wl_word, ".save") &&
|
|
!eq(command->wl_word, ".op") &&
|
|
!ciprefix(".meas", command->wl_word) &&
|
|
!eq(command->wl_word, ".tf")) {
|
|
wl_free(freecom);
|
|
goto bad;
|
|
}
|
|
coms = coms->wl_next; /* go to next line */
|
|
wl_free(freecom);
|
|
} /* end of loop over '.' lines */
|
|
|
|
nocmds:
|
|
/* Now the node table
|
|
if (ft_nodesprint)
|
|
;
|
|
*/
|
|
|
|
/* The options */
|
|
if (ft_optsprint) {
|
|
fprintf(cp_out, "Options:\n\n");
|
|
cp_vprint();
|
|
(void) putc('\n', cp_out);
|
|
}
|
|
|
|
/* And finally the accounting info. */
|
|
if (ft_acctprint) {
|
|
static wordlist ww = { "everything", NULL, NULL };
|
|
com_rusage(&ww);
|
|
} else if ((!ft_noacctprint) && (!ft_acctprint)) {
|
|
com_rusage(NULL);
|
|
}
|
|
/* absolutely no accounting if noacct is given */
|
|
|
|
putc('\n', cp_out);
|
|
return 0;
|
|
|
|
bad:
|
|
fprintf(cp_err, "Internal Error: ft_cktcoms: bad commands\n");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* These routines make sure that the arguments to .plot and .print in
|
|
* spice2 decks are acceptable to spice3. The things we look for are
|
|
* trailing (a,b) in .plot -> xlimit a b
|
|
* vm(x) -> mag(v(x))
|
|
* vp(x) -> ph(v(x))
|
|
* v(x,0) -> v(x)
|
|
* v(0,x) -> -v(x)
|
|
*/
|
|
|
|
static void
|
|
fixdotplot(wordlist *wl)
|
|
{
|
|
/* Create a buffer for printing numbers */
|
|
DS_CREATE(numbuf, 100);
|
|
|
|
while (wl) {
|
|
wl->wl_word = fixem(wl->wl_word);
|
|
|
|
/* Is this a trailing "(a,b)"? Note that we require it to be
|
|
* one word. */
|
|
if (!wl->wl_next && (*wl->wl_word == '(')) {
|
|
double d1, d2;
|
|
char *s = wl->wl_word + 1;
|
|
if (ft_numparse(&s, FALSE, &d1) < 0 ||
|
|
*s != ',') {
|
|
fprintf(cp_err, "Error: bad limits \"%s\"\n",
|
|
wl->wl_word);
|
|
goto EXITPOINT;
|
|
}
|
|
s++; /* step past comma */
|
|
if (ft_numparse(&s, FALSE, &d2) < 0 ||
|
|
*s != ')' || s[1] != '\0') { /* must end with ")" */
|
|
fprintf(cp_err, "Error: bad limits \"%s\"\n",
|
|
wl->wl_word);
|
|
goto EXITPOINT;
|
|
}
|
|
|
|
tfree(wl->wl_word);
|
|
wl->wl_word = copy("xlimit");
|
|
ds_clear(&numbuf);
|
|
if (printnum_ds(&numbuf, d1) != 0) {
|
|
fprintf(cp_err, "Unable to print limit 1: %g\n", d1);
|
|
goto EXITPOINT;
|
|
}
|
|
wl_append_word(NULL, &wl, copy(ds_get_buf(&numbuf)));
|
|
ds_clear(&numbuf);
|
|
if (printnum_ds(&numbuf, d2) != 0) {
|
|
fprintf(cp_err, "Unable to print limit 2: %g\n", d2);
|
|
goto EXITPOINT;
|
|
}
|
|
wl_append_word(NULL, &wl, copy(ds_get_buf(&numbuf)));
|
|
} /* end of case of start of potential (a,b) */
|
|
wl = wl->wl_next;
|
|
} /* end of loop over words */
|
|
|
|
EXITPOINT:
|
|
ds_free(&numbuf); /* Free DSTRING resources */
|
|
} /* end of function fixdotplot */
|
|
|
|
|
|
|
|
static void fixdotprint(wordlist *wl)
|
|
{
|
|
/* Process each word in the wordlist */
|
|
while (wl) {
|
|
wl->wl_word = fixem(wl->wl_word);
|
|
wl = wl->wl_next;
|
|
}
|
|
} /* end of function fixdotprint */
|
|
|
|
|
|
|
|
static char *fixem(char *string)
|
|
{
|
|
char buf[BSIZE_SP], *s, *t;
|
|
char *ss = string; /* save addr of string in case it is freed */
|
|
|
|
if (ciprefix("v(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "v(%s)", string + 2);
|
|
else if (eq(string + 2, "0"))
|
|
(void) sprintf(buf, "-v(%s)", s);
|
|
else
|
|
(void) sprintf(buf, "v(%s)-v(%s)", string + 2, s);
|
|
} else if (ciprefix("vm(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "mag(v(%s))", string + 3);
|
|
else if (eq(string + 3, "0"))
|
|
(void) sprintf(buf, "mag(-v(%s))", s);
|
|
else
|
|
(void) sprintf(buf, "mag(v(%s)-v(%s))", string + 3, s);
|
|
} else if (ciprefix("vp(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "ph(v(%s))", string + 3);
|
|
else if (eq(string + 3, "0"))
|
|
(void) sprintf(buf, "ph(-v(%s))", s);
|
|
else
|
|
(void) sprintf(buf, "ph(v(%s)-v(%s))", string + 3, s);
|
|
} else if (ciprefix("vi(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "imag(v(%s))", string + 3);
|
|
else if (eq(string + 3, "0"))
|
|
(void) sprintf(buf, "imag(-v(%s))", s);
|
|
else
|
|
(void) sprintf(buf, "imag(v(%s)-v(%s))", string + 3, s);
|
|
} else if (ciprefix("vr(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "real(v(%s))", string + 3);
|
|
else if (eq(string + 3, "0"))
|
|
(void) sprintf(buf, "real(-v(%s))", s);
|
|
else
|
|
(void) sprintf(buf, "real(v(%s)-v(%s))", string + 3, s);
|
|
} else if (ciprefix("vdb(", string) &&strchr(string, ',')) {
|
|
for (s = string; *s && (*s != ','); s++)
|
|
;
|
|
*s++ = '\0';
|
|
for (t = s; *t && (*t != ')'); t++)
|
|
;
|
|
*t = '\0';
|
|
if (eq(s, "0"))
|
|
(void) sprintf(buf, "db(v(%s))", string + 4);
|
|
else if (eq(string + 4, "0"))
|
|
(void) sprintf(buf, "db(-v(%s))", s);
|
|
else
|
|
(void) sprintf(buf, "db(v(%s)-v(%s))", string + 4, s);
|
|
} else if (ciprefix("i(", string)) {
|
|
for (s = string; *s && (*s != ')'); s++)
|
|
;
|
|
*s = '\0';
|
|
string += 2;
|
|
(void) sprintf(buf, "%s#branch", string);
|
|
} else {
|
|
return string;
|
|
}
|
|
|
|
txfree(ss);
|
|
string = copy(buf);
|
|
|
|
return string;
|
|
} /* end of function fixem */
|
|
|
|
|
|
|
|
wordlist *
|
|
gettoks(char *s)
|
|
{
|
|
char *t, *s0;
|
|
char *l, *r, *c; /* left, right, center/comma */
|
|
wordlist *wl, *list, **prevp;
|
|
|
|
|
|
list = NULL;
|
|
prevp = &list;
|
|
|
|
/* stripWhite.... uses copy() to return a malloc'ed s, so we have to free it,
|
|
using s0 as its starting address */
|
|
if (strchr(s, '('))
|
|
s0 = s = stripWhiteSpacesInsideParens(s);
|
|
else
|
|
s0 = s = copy(s);
|
|
|
|
while ((t = gettok(&s)) != NULL) {
|
|
if (*t == '(') {
|
|
/* gettok uses copy() to return a malloc'ed t, so we have to free it */
|
|
tfree(t);
|
|
continue;
|
|
}
|
|
l = strrchr(t, '(');
|
|
if (!l) {
|
|
wl = wl_cons(copy(t), NULL);
|
|
*prevp = wl;
|
|
prevp = &wl->wl_next;
|
|
tfree(t);
|
|
continue;
|
|
}
|
|
|
|
r = strchr(t, ')');
|
|
|
|
c = strchr(t, ',');
|
|
if (!c)
|
|
c = r;
|
|
|
|
if (c)
|
|
*c = '\0';
|
|
|
|
wl = wl_cons(NULL, NULL);
|
|
*prevp = wl;
|
|
prevp = &wl->wl_next;
|
|
|
|
if (*(l - 1) == 'i' || *(l - 1) == 'I') {
|
|
char buf[513];
|
|
sprintf(buf, "%s#branch", l + 1);
|
|
wl->wl_word = copy(buf);
|
|
c = r = NULL;
|
|
}
|
|
else {
|
|
wl->wl_word = copy(l + 1);
|
|
}
|
|
|
|
if (c != r) {
|
|
*r = '\0';
|
|
wl = wl_cons(copy(c + 1), NULL);
|
|
*prevp = wl;
|
|
prevp = &wl->wl_next;
|
|
}
|
|
tfree(t);
|
|
} /* end of loop parsing string */
|
|
|
|
txfree(s0);
|
|
return list;
|
|
} /* end of function gettoks */
|
|
|
|
|
|
|