Browse Source

Add support for including analog node changes in VCD file output,

and an option to explicitly set the VCD timestep.  Correct the
output value for high-impedance nodes.
pre-master-46
Giles Atkinson 3 years ago
committed by Holger Vogt
parent
commit
84821a4cf5
  1. 2
      src/frontend/commands.c
  2. 346
      src/xspice/evt/evtprint.c

2
src/frontend/commands.c

@ -259,7 +259,7 @@ struct comm spcp_coms[] = {
{ "eprvcd", EVTprintvcd, FALSE, TRUE, { "eprvcd", EVTprintvcd, FALSE, TRUE,
{ 040000, 040000, 040000, 040000 }, E_BEGINNING, 1, LOTS, { 040000, 040000, 040000, 040000 }, E_BEGINNING, 1, LOTS,
arg_enodes, arg_enodes,
"node node ... : Print event values into vcd file." },
"[-a] [-t timescale] node node ... : Print event values into VCD file." },
{ "edisplay", EVTdisplay, FALSE, TRUE, { "edisplay", EVTdisplay, FALSE, TRUE,
{ 040000, 040000, 040000, 040000 }, E_BEGINNING, 0, 0, { 040000, 040000, 040000, 040000 }, E_BEGINNING, 0, 0,
NULL, NULL,

346
src/xspice/evt/evtprint.c

@ -53,6 +53,8 @@ NON-STANDARD FEATURES
#include "ngspice/evtproto.h" #include "ngspice/evtproto.h"
#include "ngspice/fteext.h"
#include <time.h> #include <time.h>
#include <locale.h> #include <locale.h>
@ -466,10 +468,11 @@ get_vcdval(char *xspiceval, char **newval)
"0z", "1z", "Uz", "0z", "1z", "Uz",
"0u", "1u", "Uu" "0u", "1u", "Uu"
}; };
static char *returnmap[] = { static char *returnmap[] = {
"0", "1", "x", "0", "1", "x",
"0", "1", "x", "0", "1", "x",
"0", "1", "z",
"z", "z", "z",
"0", "1", "x" "0", "1", "x"
}; };
@ -482,7 +485,7 @@ get_vcdval(char *xspiceval, char **newval)
/* is it a real number ? */ /* is it a real number ? */
retval = INPevaluate(&xspiceval, &err, 1); retval = INPevaluate(&xspiceval, &err, 1);
if (err) { if (err) {
*newval = copy("unknown");
*newval = copy(xspiceval); // Assume the node type is coded for this.
return 2; return 2;
} }
*newval = tprintf("%.16g", retval); *newval = tprintf("%.16g", retval);
@ -495,6 +498,73 @@ get_vcdval(char *xspiceval, char **newval)
#define localtime _localtime64 #define localtime _localtime64
#endif #endif
/* Function to return a real value to be written to a VCD file. */
struct reals {
struct dvec *time; // Scale vector
int v_index, last_i;
double factor;
struct dvec *node_vector[EPRINT_MAXARGS]; // For analog nodes
};
static double get_real(int index, double when, struct reals *ctx)
{
struct dvec *dv;
if (index < ctx->last_i) {
/* Starting a new pass. */
if (!ctx->time) {
ctx->v_index = 0;
ctx->time = vec_get("time");
if (!ctx->time) {
if (ctx->last_i == EPRINT_MAXARGS) { // First time
fprintf(cp_err,
"ERROR - No vector 'time' in current plot\n");
}
ctx->node_vector[index] = NULL; // No more calls
return NAN;
}
}
/* Advance the vector index. */
while (ctx->v_index < ctx->time->v_length &&
ctx->time->v_realdata[ctx->v_index++] < when) ;
ctx->v_index--;
/* Calculate interpolation factor. */
if (ctx->v_index + 1 < ctx->time->v_length) {
ctx->factor = (when - ctx->time->v_realdata[ctx->v_index]);
ctx->factor /= (ctx->time->v_realdata[ctx->v_index + 1] -
ctx->time->v_realdata[ctx->v_index]);
if (ctx->factor < 0.0 || ctx->factor >= 1.0)
ctx->factor = 0.0; // Rounding
} else {
ctx->factor = 0.0;
}
}
/* Return interpolated value. */
ctx->last_i = index;
dv = ctx->node_vector[index];
if (ctx->v_index < dv->v_length) {
if (ctx->factor == 0.0) {
return dv->v_realdata[ctx->v_index];
} else {
return dv->v_realdata[ctx->v_index] +
ctx->factor *
(dv->v_realdata[ctx->v_index + 1] -
dv->v_realdata[ctx->v_index]);
}
} else {
ctx->node_vector[index] = NULL; // No more calls
return dv->v_realdata[dv->v_length - 1];
}
}
/* /*
* A simple vcd file printer. * A simple vcd file printer.
* command 'eprvcd a0 a1 a2 b0 b1 b2 clk > myvcd.vcd' * command 'eprvcd a0 a1 a2 b0 b1 b2 clk > myvcd.vcd'
@ -510,16 +580,23 @@ EVTprintvcd(wordlist *wl)
{ {
int i; int i;
int nargs; int nargs;
int timesteps = 0, tspower = -1;
wordlist *w; wordlist *w;
struct reals ctx;
double out_time, last_out_time;
char *node_name[EPRINT_MAXARGS]; char *node_name[EPRINT_MAXARGS];
int node_index[EPRINT_MAXARGS];
int udn_index[EPRINT_MAXARGS];
int node_index[EPRINT_MAXARGS];
int udn_index[EPRINT_MAXARGS];
Evt_Node_t *node_data[EPRINT_MAXARGS]; Evt_Node_t *node_data[EPRINT_MAXARGS];
char *node_value[EPRINT_MAXARGS]; char *node_value[EPRINT_MAXARGS];
char *old_node_value[EPRINT_MAXARGS]; char *old_node_value[EPRINT_MAXARGS];
char node_ident[EPRINT_MAXARGS + 1];
char node_ident[EPRINT_MAXARGS + 1];
char vbuf[24][2][EPRINT_MAXARGS]; // Analog value strings
CKTcircuit *ckt; CKTcircuit *ckt;
@ -527,19 +604,37 @@ EVTprintvcd(wordlist *wl)
Mif_Boolean_t more; Mif_Boolean_t more;
double step = 0.0;
double next_step; double next_step;
double this_step; double this_step;
char *value; char *value;
/* Check for the "-a" option (output analog values at timesteps)
* and "-t nn": specifies the VCD timestep as a power of ten.
*/
while (wl && wl->wl_word[0] == '-') {
if (wl->wl_word[1] == 'a' && !wl->wl_word[2]) {
timesteps = 1;
} else if (wl->wl_word[1] == 't' && !wl->wl_word[2]) {
wl = wl->wl_next;
if (wl)
tspower = atoi(wl->wl_word);
else
break;
} else {
break;
}
wl = wl->wl_next;
}
/* Count the number of arguments to the command */ /* Count the number of arguments to the command */
nargs = 0; nargs = 0;
for (w = wl; w; w = w->wl_next) for (w = wl; w; w = w->wl_next)
nargs++; nargs++;
if (nargs < 1) { if (nargs < 1) {
printf("Usage: eprvcd <node1> <node2> ...\n");
printf("Usage: eprvcd [-a] <node1> <node2> ...\n");
return; return;
} }
if (nargs > EPRINT_MAXARGS) { if (nargs > EPRINT_MAXARGS) {
@ -560,17 +655,41 @@ EVTprintvcd(wordlist *wl)
node_table = ckt->evt->info.node_table; node_table = ckt->evt->info.node_table;
/* Get data for each argument */ /* Get data for each argument */
w = wl; w = wl;
for (i = 0; i < nargs; i++) { for (i = 0; i < nargs; i++) {
node_name[i] = w->wl_word; node_name[i] = w->wl_word;
node_index[i] = get_index(node_name[i]); node_index[i] = get_index(node_name[i]);
if (node_index[i] < 0) {
fprintf(cp_err, "ERROR - Node %s is not an event node.\n", node_name[i]);
return;
}
udn_index[i] = node_table[node_index[i]]->udn_index;
node_data[i] = ckt->evt->data.node->head[node_index[i]];
if (node_index[i] >= 0) {
udn_index[i] = node_table[node_index[i]]->udn_index;
node_data[i] = ckt->evt->data.node->head[node_index[i]];
ctx.node_vector[i] = NULL;
} else {
struct pnode *pn;
struct dvec *dv;
wordlist *save;
/* Is it an analog parameter/node expression?
* The whole expression must be a single word (no spaces).
*/
save = w->wl_next;
w->wl_next = NULL;
pn = ft_getpnames_quotes(w, TRUE);
w->wl_next = save;
if (pn) {
dv = ft_evaluate(pn);
free_pnode(pn);
} else {
dv = NULL;
}
if (!dv) {
fprintf(cp_err, "ERROR - Node %s not parsed.\n", node_name[i]);
return;
}
ctx.node_vector[i] = dv;
}
node_value[i] = ""; node_value[i] = "";
w = w->wl_next; w = w->wl_next;
} }
@ -606,43 +725,95 @@ EVTprintvcd(wordlist *wl)
/* get the sim time resolution based on tstep */ /* get the sim time resolution based on tstep */
char *unit; char *unit;
double scale;
double tstep = ckt->CKTstep;
double scale, tick;
if (tspower >= 0) {
/* VCD timestep set by "-t" option. */
if (tspower == 0) {
unit = "s";
scale = 1.0;
} else if (tspower < 4) {
unit = "ms";
tspower = 3 - tspower;
scale = 1e3 * exp10((double)-tspower);
} else if (tspower < 7) {
unit = "us";
tspower = 6 - tspower;
scale = 1e6 * exp10((double)-tspower);
} else if (tspower < 10) {
unit = "ns";
tspower = 9 - tspower;
scale = 1e9 * exp10((double)-tspower);
} else if (tspower < 13) {
unit = "ps";
tspower = 12 - tspower;
scale = 1e12 * exp10((double)-tspower);
} else if (tspower < 16) {
unit = "fs";
tspower = 15 - tspower;
scale = 1e15 * exp10((double)-tspower);
} else { // 1 fS is the bottom.
unit = "fs";
tspower = 0;
scale = 1e15;
}
out_printf("$timescale %g %s $end\n", exp10((double)tspower), unit);
} else {
double tstep = ckt->CKTstep;
/* if selected time step is down to [ms] then report time at [us] etc., always with one level higher resolution */
if (tstep >= 1e-3) {
unit = "us";
scale = 1e6;
}
else if (tstep >= 1e-6) {
unit = "ns";
scale = 1e9;
}
else if (tstep >= 1e-9) {
unit = "ps";
scale = 1e12;
}
else {
unit = "fs";
scale = 1e15;
/* Use the simulation time step. If the selected time step
* is down to [ms] then report time at [us] etc.,
* always with one level higher resolution.
*/
if (tstep >= 1e-3) {
unit = "us";
scale = 1e6;
}
else if (tstep >= 1e-6) {
unit = "ns";
scale = 1e9;
}
else if (tstep >= 1e-9) {
unit = "ps";
scale = 1e12;
} else {
unit = "fs";
scale = 1e15;
}
out_printf("$timescale 1 %s $end\n", unit);
} }
out_printf("$timescale 1 %s $end\n", unit);
tick = 1.0 / scale;
/* Scan the node data. Go for printing using $dumpvars /* Scan the node data. Go for printing using $dumpvars
for the initial values. Also, determine if there is for the initial values. Also, determine if there is
more data following it and if so, what the next step is. */ more data following it and if so, what the next step is. */
ctx.time = NULL;
ctx.last_i = EPRINT_MAXARGS; // Indicate restart
more = MIF_FALSE; more = MIF_FALSE;
next_step = 1e30; next_step = 1e30;
for (i = 0; i < nargs; i++) { for (i = 0; i < nargs; i++) {
step = node_data[i]->step;
g_evt_udn_info[udn_index[i]]->print_val
(node_data[i]->node_value, "all", &value);
old_node_value[i] = node_value[i] = value;
node_data[i] = node_data[i]->next;
if (node_data[i]) {
more = MIF_TRUE;
if (next_step > node_data[i]->step)
next_step = node_data[i]->step;
if (ctx.node_vector[i]) {
/* Analog node or expression. */
sprintf(vbuf[0][i], "%.16g", get_real(i, 0.0, &ctx));
node_value[i] = vbuf[0][i];
old_node_value[i] = vbuf[1][i];
strcpy(vbuf[1][i], vbuf[0][i]);
} else {
/* This must return a pointer to a statically-allocated string. */
g_evt_udn_info[udn_index[i]]->print_val
(node_data[i]->node_value, "all", &value);
node_data[i] = node_data[i]->next;
old_node_value[i] = node_value[i] = value;
if (node_data[i]) {
more = MIF_TRUE;
if (next_step > node_data[i]->step)
next_step = node_data[i]->step;
}
} }
} }
@ -658,7 +829,6 @@ EVTprintvcd(wordlist *wl)
} }
out_printf("$enddefinitions $end\n"); out_printf("$enddefinitions $end\n");
out_printf("#%lld\n", (unsigned long long)(step * scale));
/* first set of data for initialization /* first set of data for initialization
or if only op has been calculated */ or if only op has been calculated */
@ -676,38 +846,92 @@ EVTprintvcd(wordlist *wl)
out_printf("$end\n"); out_printf("$end\n");
/* While there is more data, get the next values and print */ /* While there is more data, get the next values and print */
while (more) {
more = MIF_FALSE;
last_out_time = 0.0;
while (more ||
(timesteps && ctx.time && ctx.v_index + 1 < ctx.time->v_length)) {
int got_one;
this_step = next_step; this_step = next_step;
next_step = 1e30;
for (i = 0; i < nargs; i++)
if (node_data[i]) {
if (node_data[i]->step == this_step) {
g_evt_udn_info[udn_index[i]]->print_val
(node_data[i]->node_value, "all", &value);
node_value[i] = value;
node_data[i] = node_data[i]->next;
}
if (node_data[i]) {
more = MIF_TRUE;
if (next_step > node_data[i]->step)
next_step = node_data[i]->step;
if (timesteps && ctx.time && ctx.v_index + 1 < ctx.time->v_length &&
(ctx.time->v_realdata[ctx.v_index + 1] < this_step ||
(timesteps && !more))) {
/* Analogue output at each time step, skipping if they would
* appear simulataneous in the output.
*/
out_time = ctx.time->v_realdata[ctx.v_index + 1];
if (out_time - last_out_time < tick) {
++ctx.v_index;
continue;
}
for (i = 0; i < nargs; i++) {
if (ctx.node_vector[i])
sprintf(node_value[i], "%.16g",
get_real(i, out_time, &ctx));
}
} else {
/* Process next event. */
out_time = this_step;
more = MIF_FALSE;
next_step = 1e30;
for (i = 0; i < nargs; i++) {
if (ctx.node_vector[i]) {
/* Analog node or expression. */
sprintf(node_value[i], "%.16g",
get_real(i, this_step, &ctx));
} else if (node_data[i]) {
if (node_data[i]->step == this_step) {
g_evt_udn_info[udn_index[i]]->print_val
(node_data[i]->node_value, "all", &value);
node_value[i] = value;
node_data[i] = node_data[i]->next;
}
if (node_data[i]) {
more = MIF_TRUE;
if (next_step > node_data[i]->step)
next_step = node_data[i]->step;
}
} }
} }
}
/* timestamp */
out_printf("#%lld\n", (unsigned long long)(this_step * scale));
/* print only values that have changed */ /* print only values that have changed */
for (i = 0; i < nargs; i++) {
for (i = 0, got_one = 0; i < nargs; i++) {
if (!eq(old_node_value[i], node_value[i])) { if (!eq(old_node_value[i], node_value[i])) {
char *buf; char *buf;
if (!got_one) {
/* timestamp */
out_printf("#%lld\n",
(unsigned long long)(out_time * scale));
last_out_time = out_time;;
got_one = 1;
}
if (get_vcdval(node_value[i], &buf) == 1) if (get_vcdval(node_value[i], &buf) == 1)
out_printf("r%s %c\n", buf, node_ident[i]); out_printf("r%s %c\n", buf, node_ident[i]);
else else
out_printf("%s%c\n", buf, node_ident[i]); out_printf("%s%c\n", buf, node_ident[i]);
old_node_value[i] = node_value[i];
if (ctx.node_vector[i]) {
char *t;
/* Swap buffers. */
t = old_node_value[i];
old_node_value[i] = node_value[i];
node_value[i] = t;
} else {;
old_node_value[i] = node_value[i];
}
tfree(buf); tfree(buf);
} }
} }

Loading…
Cancel
Save