diff --git a/src/frontend/breakp.c b/src/frontend/breakp.c index 83eab7771..a02fb40cd 100644 --- a/src/frontend/breakp.c +++ b/src/frontend/breakp.c @@ -10,6 +10,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "ngspice/ngspice.h" #include "ngspice/cpdefs.h" #include "ngspice/ftedefs.h" +#include "ngspice/cktdefs.h" #include "ngspice/dvec.h" #include "ngspice/ftedebug.h" #include "breakp.h" @@ -209,7 +210,10 @@ com_trce(wordlist *wl) } -/* Incrementally plot a value. This is just like trace. */ +/* Incrementally plot values. This is just like trace. + * Nodes may be specified with an offset, as name+number, as that is useful + * for separating the graphs of digital nodes. It will be ignored for analogue. + */ void com_iplot(wordlist *wl) @@ -224,12 +228,13 @@ com_iplot(wordlist *wl) return; } - /* settrace(wl, VF_PLOT); */ - struct dbcomm *d, *td, *currentdb = NULL; double window = 0.0; - int initial_steps = IPOINTMIN; +#ifdef XSPICE + int event_auto_incr = 0; +#endif char *s; + int initial_steps = IPOINTMIN; /* Look for "-w window-size" at the front, indicating a windowed iplot * or "-d steps" to set the initial delay before the window appears. @@ -254,6 +259,12 @@ com_iplot(wordlist *wl) wl = wl->wl_next; if (wl) initial_steps = atoi(wl->wl_word); +#ifdef XSPICE + } else if (wl->wl_word[1] == 'o' && !wl->wl_word[2]) { + /* Automatically offset traces for event nodes. */ + + event_auto_incr = 1; +#endif } else { break; } @@ -269,8 +280,10 @@ com_iplot(wordlist *wl) d = TMALLOC(struct dbcomm, 1); d->db_analysis = NULL; d->db_number = debugnumber++; - d->db_op = initial_steps; // Field re-use - d->db_value1 = window; // Field re-use + d->db_iteration = event_auto_incr ? DB_AUTO_OFFSET : DB_NORMAL; + d->db_op = initial_steps; // Field re-use + d->db_value1 = window; // Field re-use + if (eq(s, "all")) { d->db_type = DB_IPLOTALL; } else { @@ -278,8 +291,14 @@ com_iplot(wordlist *wl) d->db_nodename1 = copy(s); } tfree(s);/*DG: avoid memory leak */ - d->db_also = currentdb; - currentdb = d; + + /* Chain in expected order. */ + + if (currentdb) + td->db_also = d; + else + currentdb = d; + td = d; wl = wl->wl_next; } @@ -388,7 +407,10 @@ void dbfree1(struct dbcomm *d) { tfree(d->db_nodename1); - tfree(d->db_nodename2); + if (d->db_type != DB_IPLOT && d->db_type != DB_IPLOTALL && + d->db_type != DB_DEADIPLOT) { + tfree(d->db_nodename2); + } if (d->db_also) dbfree(d->db_also); tfree(d); diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 41671950c..3de1ba6fb 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -428,7 +428,7 @@ struct comm spcp_coms[] = { { "iplot", com_iplot, TRUE, TRUE, { 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS, NULL, - "[-w width] [-s initial_steps] [all] [node ...] : Incrementally plot nodes." } , + "[-w width] [-d initial_steps] [-o] [all] [node ...] : Incrementally plot nodes." } , { "status", com_sttus, TRUE, FALSE, { 0, 0, 0, 0 }, E_DEFHMASK, 0, 0, NULL, @@ -897,10 +897,6 @@ struct comm nutcp_coms[] = { { 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS, NULL, "[all] [node ...] : Save a spice output." } , - { "iplot", NULL, TRUE, TRUE, - { 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS, - NULL, - "[all] [node ...] : Incrementally plot a node." } , { "status", NULL, TRUE, FALSE, { 0, 0, 0, 0 }, E_DEFHMASK, 0, 0, NULL, diff --git a/src/frontend/plotting/graf.c b/src/frontend/plotting/graf.c index 396a27a23..3fbfcb237 100644 --- a/src/frontend/plotting/graf.c +++ b/src/frontend/plotting/graf.c @@ -162,7 +162,6 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ /* restore background color from previous graph, e.g. for zooming, it will be used in NewViewport(graph) */ - if (prevgraph > 0) { pgraph = FindGraph(prevgraph); if (pgraph) @@ -958,7 +957,7 @@ static int iplot(struct plot *pl, struct dbcomm *db) for (v = pl->pl_dvecs; v; v = v->v_next) { if (v->v_flags & VF_PLOT) { gr_start_internal(v, FALSE); - ft_graf(v, xs, TRUE); + ft_graf(v, v->v_scale ? v->v_scale : xs, TRUE); } } inited = 1; @@ -1119,7 +1118,7 @@ static int iplot(struct plot *pl, struct dbcomm *db) last = v->v_length - 1; if (len > 2 && - v->v_scale->v_realdata[last] < + v->v_scale->v_realdata[last] <= xs->v_realdata[len - 1]) { /* No recent additions to this vector: * draw/extend horizontal line showing last value. @@ -1202,16 +1201,22 @@ static void setflag(struct plot *plot, struct dbcomm *db, } for (dc = db; dc; dc = dc->db_also) { - if (dc->db_nodename1 == NULL) - continue; - v = vec_fromplot(dc->db_nodename1, plot); - if (!v || v->v_plot != plot) { - if (!eq(dc->db_nodename1, "0") && value) { - fprintf(cp_err, "Warning: node %s non-existent in %s.\n", - dc->db_nodename1, plot->pl_name); - /* note: XXX remove it from dbs, so won't get further errors */ + if (db->db_type == DB_IPLOT) { /* Vector cached in db_nodename2. */ + v = (struct dvec *)dc->db_nodename2; + if (!v) + continue; + } else { + if (dc->db_nodename1 == NULL) + continue; + v = vec_fromplot(dc->db_nodename1, plot); + if (!v || v->v_plot != plot) { + if (!eq(dc->db_nodename1, "0") && value) { + fprintf(cp_err, "Warning: node %s non-existent in %s.\n", + dc->db_nodename1, plot->pl_name); + /* note: XXX remove it from dbs, so no further errors. */ + } + continue; } - continue; } if (value) v->v_flags |= mode; @@ -1258,13 +1263,12 @@ static Mif_Boolean_t new_event(double when, Mif_Value_t *val, { struct dbcomm *db = (struct dbcomm *)ctx; struct dvec *v; + double value; int last; if (db->db_type == DB_DEADIPLOT) return MIF_TRUE; - v = vec_fromplot(db->db_nodename1, plot_cur); - if (!v || !v->v_scale) - return MIF_TRUE; + v = (struct dvec *)db->db_nodename2; // Struct member re-use. /* Extend vectors. */ @@ -1272,7 +1276,8 @@ static Mif_Boolean_t new_event(double when, Mif_Value_t *val, AddRealValueToVector(v->v_scale, when); AddRealValueToVector(v->v_scale, when); AddRealValueToVector(v, v->v_realdata[last]); - AddRealValueToVector(v, val->rvalue); + value = val->rvalue + db->db_value2; // Apply any plotting offset. + AddRealValueToVector(v, value); if (db->db_graphid) { GRAPH *gr; @@ -1286,20 +1291,21 @@ static Mif_Boolean_t new_event(double when, Mif_Value_t *val, gr_point(v, when, v->v_realdata[last], v->v_scale->v_realdata[last], v->v_realdata[last], last > 0); - gr_point(v, when, val->rvalue, when, v->v_realdata[last], 1); + gr_point(v, when, value, when, v->v_realdata[last], 1); if (is_last) { struct dvec *xs; /* Extend horizontally to the end of the time-step. */ xs = v->v_plot->pl_scale; - gr_point(v, xs->v_realdata[xs->v_length - 1], val->rvalue, - when, val->rvalue, 1); + gr_point(v, xs->v_realdata[xs->v_length - 1], value, + when, value, 1); } LC_flush(); #ifdef LINE_COMPRESSION_CHECKS LC.dv = NULL; // ... and suppress warnings. #endif + DevUpdate(); PopGraphContext(); } } @@ -1324,18 +1330,35 @@ void gr_iplot(struct plot *plot) for (db = dbs; db; db = db->db_next) { if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) { #ifdef XSPICE - if (db->db_iteration == 0) { + if (db->db_iteration > 0) { + double event_node_offset = 0, event_node_spacing; struct dvec *v; - /* First call: find any XSPICE event nodes in the node + /* First call: set up event nodes spacing. */ + + if (db->db_iteration == DB_AUTO_OFFSET) { + /* Magic value: automatically offset traces for + *event nodes. + */ + + if (!cp_getvar("event_node_spacing", CP_REAL, + &event_node_spacing, 0)) { + event_node_spacing = 1.5; + } + } else { + event_node_spacing = 0; + } + + /* Find any XSPICE event nodes in the node * list and set up plotting. There is a parallel path * for pushing new event values into their corresponding * vectors and plotting them. */ for (dc = db; dc; dc = dc->db_also) { - struct dbcomm *dd; - int dup = 0; + struct dbcomm *dd; + char *offp, save_sign; + int dup = 0; if (dc->db_nodename1 == NULL) continue; @@ -1351,14 +1374,62 @@ void gr_iplot(struct plot *plot) if (dup) continue; + /* Check for a nodename that is an expression. */ + + offp = strchr(dc->db_nodename1, '+'); + if (!offp) + offp = strchr(dc->db_nodename1, '-'); + if (offp > dc->db_nodename1) { + save_sign = *offp; + *offp = '\0'; // Trim to bare name. + } + v = vec_fromplot(dc->db_nodename1, plot); - if (!v) { + if (v) { + dc->db_nodename2 = (char *)v; // Save link to vector. + } else { fprintf(cp_err, "Warning: node %s non-existent in %s.\n", dc->db_nodename1, plot->pl_name); } if (v && (v->v_flags & VF_EVENT_NODE)) { + /* Ask event simulator to call back with new values. */ + + EVTnew_value_call(dc->db_nodename1, new_event, + Evt_Cbt_Plot, dc); + + if (offp > dc->db_nodename1) { + *offp = save_sign; + dc->db_value2 = atof(offp); // Offset to value. + event_node_offset = // New auto-offset. + dc->db_value2 + event_node_spacing; + } else if (event_node_spacing) { + char new_name[256]; + + /* Add offset to vector names. + * Ugly, but only here can event nodes + * be identified. + */ + + snprintf(new_name, sizeof new_name, "%s+%g", + dc->db_nodename1, event_node_offset); + tfree(v->v_name); + v->v_name = copy(new_name); + + dc->db_value2 = event_node_offset; + event_node_offset += event_node_spacing; + } + + if (dc->db_value2) { + int i; + + /* Adjust existing values. */ + + for (i = 0; i < v->v_length; ++i) + v->v_realdata[i] += dc->db_value2; + } + if ((v->v_flags & VF_PERMANENT) == 0) { vec_new(v); v->v_flags |= VF_PERMANENT; // Make it findable. @@ -1367,11 +1438,13 @@ void gr_iplot(struct plot *plot) vec_new(v->v_scale); } } - EVTnew_value_call(dc->db_nodename1, new_event, - Evt_Cbt_Plot, dc); + } else if (offp) { + fprintf(stderr, + "Offset (%s) ignored for analog node %s\n", + offp, dc->db_nodename1); } } - db->db_iteration = 1; + db->db_iteration = 0; } #endif if (db->db_graphid) { @@ -1486,6 +1559,7 @@ void gr_end_iplot(void) prev = NULL; for (db = dbs; db; prev = db, db = next) { + db->db_nodename2 = NULL; // Forget link. next = db->db_next; if (db->db_type == DB_DEADIPLOT) { if (db->db_graphid) { @@ -1498,12 +1572,12 @@ void gr_end_iplot(void) } } else if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) { - db->db_iteration = 0; // Reset XSPICE event plotting + db->db_iteration = DB_NORMAL; // Reset XSPICE event plotting if (db->db_graphid) { /* get private copy of dvecs */ - graph = FindGraph(db->db_graphid); + link = graph->plotdata; while (link) { diff --git a/src/frontend/plotting/plotcurv.c b/src/frontend/plotting/plotcurv.c index acd07a53d..f8b98a8af 100644 --- a/src/frontend/plotting/plotcurv.c +++ b/src/frontend/plotting/plotcurv.c @@ -20,7 +20,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group static void plotinterval(struct dvec *v, double lo, double hi, register double *coeffs, int degree, bool rotated); -static int get_xdirection(struct dvec *xs, int len, bool mn); +static int get_xdirection(struct dvec *xs, int len, bool mn, bool analog); /* Plot the vector v, with scale xs. If we are doing curve-fitting, then * do some tricky stuff. @@ -90,11 +90,11 @@ ft_graf(struct dvec *v, struct dvec *xs, bool nostart) if (xs) { /* Check vector lengths. */ - if (v->v_length != xs->v_length) { + if (v->v_length != xs->v_length && !v->v_scale) { fprintf(stderr, - "Warning: length of vector %s and its scale %s do " - "not match, plot may be truncated!\n", - v->v_name, xs->v_name); + "Warning: length of vector %s (%d) and its scale %s (%d) " + "do not match, plot may be truncated!\n", + v->v_name, v->v_length, xs->v_name, xs->v_length); } length = MIN(v->v_length, xs->v_length); } else { @@ -146,7 +146,8 @@ ft_graf(struct dvec *v, struct dvec *xs, bool nostart) Then everything is plotted. */ bool mono = (currentgraph->plottype != PLOT_RETLIN); - int dir = get_xdirection(xs, length, mono); + int dir = get_xdirection(xs, length, mono, + !(v->v_flags & VF_EVENT_NODE)); for (i = 0; i < length; i++) { dx = isreal(xs) ? xs->v_realdata[i] : realpart(xs->v_compdata[i]); @@ -361,7 +362,7 @@ plotinterval(struct dvec *v, double lo, double hi, register double *coeffs, int If more than 10% of the data points deviate from the majority direction, issue a warning, if 'retraceplot' is not set. */ -static int get_xdirection(struct dvec* xs, int len, bool mn) { +static int get_xdirection(struct dvec* xs, int len, bool mn, bool analog) { int i, dir = 1, inc = 0, dec = 0; double dx, lx; static bool msgsent = FALSE; @@ -379,17 +380,32 @@ static int get_xdirection(struct dvec* xs, int len, bool mn) { lx = dx; } - if (inc < 2 && dec < 2) - fprintf(stderr, "Warning, (new) x axis seems to have one data point only\n"); + /* Event nodes may never change, so no advance is OK. Similarly, vertical + * edges may falsely suggest that "retrace" is needed. + */ - if (mn && !msgsent && (((double)inc / len > 0.1 && inc < dec) || ((double)dec / len > 0.1 && inc > dec))) { - fprintf(stderr, "Warning, more than 10%% of scale vector %s data points are not monotonic.\n", xs->v_name); - fprintf(stderr, " Please consider using the 'retraceplot' flag to the plot command to plot all data.\n"); - msgsent = TRUE; + if (analog) { + if ((inc + dec) == 0) { + fprintf(stderr, + "Warning, (new) x axis (%s) seems to have only one " + "data point: %d points %d increasing %d decreasing.\n", + xs->v_name, xs->v_length, inc, dec); + } + + if (mn && !msgsent && (((double)inc / len > 0.1 && inc < dec) || + ((double)dec / len > 0.1 && inc > dec))) { + fprintf(stderr, + "Warning, more than 10%% of scale vector %s data points " + "are not monotonic.\n", + xs->v_name); + fprintf(stderr, + " Please consider using the 'retraceplot' " + "flag to the plot command to plot all data.\n"); + msgsent = TRUE; + } } if (inc < dec) dir = -1; - return dir; } diff --git a/src/frontend/plotting/plotit.c b/src/frontend/plotting/plotit.c index 449b0fec1..c54d81f3f 100644 --- a/src/frontend/plotting/plotit.c +++ b/src/frontend/plotting/plotit.c @@ -854,11 +854,15 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) /* Add n * spacing (e.g. 1.5) to digital event node based vectors */ if (digitop) { - double spacing = 1.5; + double spacing; double nn = 0.; int ii = 0, jj = 0; - for (d = vecs; d; d = d->v_link2) { + if (!cp_getvar("plot_auto_spacing", CP_REAL, &spacing, 0) || + spacing < 0.0) { + spacing = 1.5; + } + for (d = vecs; d; d = d->v_link2) { if ((d->v_flags & VF_EVENT_NODE) && !(d->v_flags & VF_PERMANENT) && d->v_scale && (d->v_scale->v_flags & VF_EVENT_NODE) && @@ -878,11 +882,11 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) if (!ylim) { ylim = TMALLOC(double, 2); ylim[0] = 0; - /* make ylim[1] a multiple of 2*1.5 */ + /* make ylim[1] a multiple of 2 * spacing. */ if (jj % 2 == 0) ylim[1] = nn; else - ylim[1] = nn + 1.5; + ylim[1] = nn + spacing; } /* re-scaled plot */ else { diff --git a/src/include/ngspice/ftedebug.h b/src/include/ngspice/ftedebug.h index 34c215904..7543e1c6a 100644 --- a/src/include/ngspice/ftedebug.h +++ b/src/include/ngspice/ftedebug.h @@ -34,7 +34,13 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #define DBC_GTE 5 /* >= (ge) */ #define DBC_LTE 6 /* <= (le) */ -/* Below, members db_op and db_value1 are re-purposed by iplot options. */ +/* Below, members db_nodename2, db_op, db_iteration and db_value1/2 + * are re-purposed by iplot optionsf. + * These definitions are used for db_iteration: + */ + +#define DB_NORMAL 1 +#define DB_AUTO_OFFSET 2 struct dbcomm { int db_number; /* The number of this debugging command. */