You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1011 lines
27 KiB
1011 lines
27 KiB
/*============================================================================
|
|
FILE EVTprint.c
|
|
|
|
MEMBER OF process XSPICE
|
|
|
|
Copyright 1991
|
|
Georgia Tech Research Corporation
|
|
Atlanta, Georgia 30332
|
|
All Rights Reserved
|
|
|
|
PROJECT A-8503
|
|
|
|
AUTHORS
|
|
|
|
9/12/91 Bill Kuhn
|
|
|
|
MODIFICATIONS
|
|
|
|
7/11/2012 Holger Vogt Replace printf by out_printf to allow output redirection
|
|
5/21/2017 Holger Vogt Update 'edisplay': add node type and number of events
|
|
5/26/2018 Uros Platise Update 'EVTprintvcd': stepsize based on tstep
|
|
|
|
SUMMARY
|
|
|
|
This file contains function EVTprint which is used to provide a simple
|
|
tabular output of event-driven node data. This printout is invoked
|
|
through a new nutmeg command called 'eprint' which takes event-driven
|
|
node names as argument.
|
|
|
|
INTERFACES
|
|
|
|
void EVTprint(wordlist *wl)
|
|
|
|
REFERENCED FILES
|
|
|
|
None.
|
|
|
|
NON-STANDARD FEATURES
|
|
|
|
None.
|
|
|
|
============================================================================*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
|
|
#include "ngspice/cpstd.h"
|
|
#include "ngspice/cpextern.h"
|
|
#include "ngspice/fteext.h"
|
|
|
|
#include "ngspice/mif.h"
|
|
#include "ngspice/evt.h"
|
|
#include "ngspice/evtudn.h"
|
|
|
|
#include "ngspice/evtproto.h"
|
|
|
|
#include "ngspice/fteext.h"
|
|
|
|
#include <time.h>
|
|
#include <locale.h>
|
|
|
|
|
|
static int get_index(char *node_name);
|
|
|
|
static void print_data(
|
|
Mif_Boolean_t dcop,
|
|
double step,
|
|
char **node_value,
|
|
int nargs);
|
|
|
|
/*
|
|
EVTprint
|
|
|
|
This function implements the 'eprint' command used to print
|
|
event-driven node values and messages from the latest simulation.
|
|
|
|
This is a simple prototype implementation of the
|
|
eprint command for testing purposes. It is currently lacking
|
|
in the following areas:
|
|
|
|
1) It accepts only up to 93 nodes. (EPRINT_MAXARGS)
|
|
|
|
2) It does not support the selected printing of different
|
|
members of a user-defined data struct.
|
|
|
|
3) It is dumb in its output formatting - just tries to print
|
|
everything on a line with 4 space separators.
|
|
|
|
4) It works only for the latest simulation - i.e. it does not
|
|
use the evt jobs structure to find old results.
|
|
|
|
5) It does not allow a range of timesteps to be selected.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define EPRINT_MAXARGS 93
|
|
|
|
|
|
void EVTprint(
|
|
wordlist *wl) /* The command line entered by user */
|
|
{
|
|
|
|
int i;
|
|
int nargs;
|
|
int num_ports;
|
|
|
|
wordlist *w;
|
|
|
|
char *node_name[EPRINT_MAXARGS];
|
|
int node_index[EPRINT_MAXARGS];
|
|
int udn_index[EPRINT_MAXARGS];
|
|
Evt_Node_t *node_data[EPRINT_MAXARGS];
|
|
char *node_value[EPRINT_MAXARGS];
|
|
|
|
CKTcircuit *ckt;
|
|
|
|
Evt_Node_Info_t **node_table;
|
|
Evt_Port_Info_t **port_table;
|
|
|
|
Mif_Boolean_t more;
|
|
Mif_Boolean_t dcop;
|
|
|
|
double step = 0.0;
|
|
double next_step;
|
|
double this_step;
|
|
|
|
char *value;
|
|
|
|
Evt_Msg_t *msg_data;
|
|
Evt_Statistic_t *statistics;
|
|
|
|
|
|
/* Count the number of arguments to the command */
|
|
nargs = 0;
|
|
w = wl;
|
|
while(w) {
|
|
nargs++;
|
|
w = w->wl_next;
|
|
}
|
|
|
|
if(nargs < 1) {
|
|
printf("Usage: eprint <node1> <node2> ...\n");
|
|
return;
|
|
}
|
|
if(nargs > EPRINT_MAXARGS) {
|
|
fprintf(cp_err, "ERROR - eprint currently limited to %d arguments\n", EPRINT_MAXARGS);
|
|
return;
|
|
}
|
|
|
|
/* Get needed pointers */
|
|
ckt = g_mif_info.ckt;
|
|
if (!ckt) {
|
|
fprintf(cp_err, "Error: no circuit loaded.\n");
|
|
return;
|
|
}
|
|
node_table = ckt->evt->info.node_table;
|
|
|
|
/* Get data for each argument */
|
|
w = wl;
|
|
for(i = 0; i < nargs; i++) {
|
|
node_name[i] = w->wl_word;
|
|
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;
|
|
if (ckt->evt->data.node)
|
|
node_data[i] = ckt->evt->data.node->head[node_index[i]];
|
|
else {
|
|
fprintf(cp_err, "ERROR - No node data: simulation not yet run?\n");
|
|
return;
|
|
}
|
|
node_value[i] = "";
|
|
w = w->wl_next;
|
|
}
|
|
|
|
out_init();
|
|
|
|
/* Print results data */
|
|
out_printf("\n**** Results Data ****\n\n");
|
|
|
|
/* Print the column identifiers */
|
|
out_printf("Time or Step\n");
|
|
for(i = 0; i < nargs; i++)
|
|
out_printf("%s\n",node_name[i]);
|
|
out_printf("\n\n");
|
|
|
|
/* Scan the node data and determine if the first vector */
|
|
/* is for a DCOP analysis or the first step in a swept DC */
|
|
/* or transient analysis. Also, determine if there is */
|
|
/* more data following it and if so, what the next step */
|
|
/* is. */
|
|
more = MIF_FALSE;
|
|
dcop = MIF_FALSE;
|
|
next_step = 1e30;
|
|
for(i = 0; i < nargs; i++) {
|
|
if(node_data[i]->op)
|
|
dcop = MIF_TRUE;
|
|
else
|
|
step = node_data[i]->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(node_data[i]->step < next_step)
|
|
next_step = node_data[i]->step;
|
|
}
|
|
}
|
|
|
|
/* Print the data */
|
|
print_data(dcop, step, node_value, nargs);
|
|
|
|
/* While there is more data, get the next values and print */
|
|
while(more) {
|
|
|
|
more = MIF_FALSE;
|
|
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(node_data[i]->step < next_step)
|
|
next_step = node_data[i]->step;
|
|
}
|
|
|
|
} /* end if node_data not NULL */
|
|
|
|
} /* end for number of args */
|
|
|
|
print_data(MIF_FALSE, this_step, node_value, nargs);
|
|
|
|
} /* end while there is more data */
|
|
out_printf("\n\n");
|
|
|
|
|
|
/* Print messages for all ports */
|
|
out_printf("\n**** Messages ****\n\n");
|
|
|
|
num_ports = ckt->evt->counts.num_ports;
|
|
port_table = ckt->evt->info.port_table;
|
|
|
|
for(i = 0; i < num_ports; i++) {
|
|
|
|
/* Get pointer to messages for this port */
|
|
msg_data = ckt->evt->data.msg->head[i];
|
|
|
|
/* If no messages on this port, skip */
|
|
if(! msg_data)
|
|
continue;
|
|
|
|
/* Print the port description */
|
|
out_printf("Node: %s Inst: %s Conn: %s Port: %d\n\n",
|
|
port_table[i]->node_name,
|
|
port_table[i]->inst_name,
|
|
port_table[i]->conn_name,
|
|
port_table[i]->port_num);
|
|
|
|
/* Print the messages on this port */
|
|
while(msg_data) {
|
|
if(msg_data->op)
|
|
printf("DCOP ");
|
|
else
|
|
printf("%-16.9e", msg_data->step);
|
|
printf("%s\n", msg_data->text);
|
|
msg_data = msg_data->next;
|
|
}
|
|
out_printf("\n\n");
|
|
|
|
} /* end for number of ports */
|
|
|
|
|
|
/* Print statistics */
|
|
out_printf("\n**** Statistics ****\n\n");
|
|
|
|
statistics = ckt->evt->data.statistics;
|
|
out_printf("Operating point analog/event alternations: %d\n",
|
|
statistics->op_alternations);
|
|
out_printf("Operating point load calls: %d\n",
|
|
statistics->op_load_calls);
|
|
out_printf("Operating point event passes: %d\n",
|
|
statistics->op_event_passes);
|
|
out_printf("Transient analysis load calls: %d\n",
|
|
statistics->tran_load_calls);
|
|
out_printf("Transient analysis timestep backups: %d\n",
|
|
statistics->tran_time_backups);
|
|
|
|
out_printf("\n\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
get_index
|
|
|
|
This function determines the index of a specified event-driven node.
|
|
*/
|
|
|
|
|
|
static int get_index(
|
|
char *node_name /* The name of the node to search for */
|
|
)
|
|
{
|
|
|
|
/* Get the event-driven node index for the specified name */
|
|
|
|
int index;
|
|
|
|
Mif_Boolean_t found;
|
|
Evt_Node_Info_t *node;
|
|
CKTcircuit *ckt;
|
|
|
|
|
|
/* Scan list of nodes in event structure to see if there */
|
|
|
|
found = MIF_FALSE;
|
|
index = 0;
|
|
|
|
ckt = g_mif_info.ckt;
|
|
if (!ckt) {
|
|
fprintf(cp_err, "Error: no circuit loaded.\n");
|
|
return(-1);
|
|
}
|
|
node = ckt->evt->info.node_list;
|
|
|
|
while(node) {
|
|
if(strcmp(node_name, node->name) == 0) {
|
|
found = MIF_TRUE;
|
|
break;
|
|
}
|
|
else {
|
|
index++;
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
/* Return the index or -1 if not found */
|
|
if(! found)
|
|
return(-1);
|
|
else
|
|
return(index);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
print_data
|
|
|
|
This function prints the values of one or more nodes to
|
|
standard output.
|
|
*/
|
|
|
|
|
|
static void print_data(
|
|
Mif_Boolean_t dcop, /* Is this the operating point data */
|
|
double step, /* The analysis step if dcop */
|
|
char **node_value, /* The array of values to be printed */
|
|
int nargs) /* The size of the value array */
|
|
{
|
|
|
|
int i, preci;
|
|
char step_str[100];
|
|
|
|
/* If option numdgt is set, use it for printout precision. */
|
|
if (cp_numdgt > 0)
|
|
preci = cp_numdgt;
|
|
else
|
|
preci = 9;
|
|
|
|
if(dcop)
|
|
strcpy(step_str, "DCOP ");
|
|
else
|
|
sprintf(step_str, "%.*e", preci, step);
|
|
|
|
out_printf("%s", step_str);
|
|
for(i = 0; i < nargs; i++)
|
|
out_printf(" %s", node_value[i]);
|
|
out_printf("\n");
|
|
}
|
|
|
|
|
|
/* print all event node names */
|
|
void
|
|
EVTdisplay(wordlist *wl)
|
|
{
|
|
Evt_Node_Info_t *node;
|
|
CKTcircuit *ckt;
|
|
int node_index, udn_index;
|
|
Evt_Node_Info_t **node_table;
|
|
|
|
NG_IGNORE(wl);
|
|
ckt = g_mif_info.ckt;
|
|
if (!ckt) {
|
|
fprintf(cp_err, "Error: no circuit loaded.\n");
|
|
return;
|
|
}
|
|
node = ckt->evt->info.node_list;
|
|
node_table = ckt->evt->info.node_table;
|
|
|
|
if (!node || !node_table) {
|
|
out_printf("No event node available!\n");
|
|
return;
|
|
}
|
|
out_init();
|
|
if (ckt->evt->jobs.job_plot) {
|
|
out_printf("\nList of event nodes in plot %s\n",
|
|
ckt->evt->jobs.job_plot[ckt->evt->jobs.cur_job]);
|
|
} else {
|
|
out_printf("\nList of event nodes\n");
|
|
}
|
|
out_printf(" %-20s: %-5s, %s\n\n",
|
|
"node name", "type", "number of events");
|
|
|
|
node_index = 0;
|
|
while (node) {
|
|
Evt_Node_t *node_data = NULL;
|
|
int count = 0;
|
|
char *type;
|
|
|
|
udn_index = node_table[node_index]->udn_index;
|
|
if (ckt->evt->data.node) {
|
|
node_data = ckt->evt->data.node->head[node_index];
|
|
while (node_data) {
|
|
count++;
|
|
node_data = node_data->next;
|
|
}
|
|
}
|
|
type = g_evt_udn_info[udn_index]->name;
|
|
out_printf(" %-20s: %-5s, %5d\n", node->name, type, count);
|
|
|
|
node = node->next;
|
|
node_index++;
|
|
}
|
|
}
|
|
|
|
|
|
/* xspice valid 12-state values (idndig.c):
|
|
* 0s, 1s, Us, 0r, 1r, Ur, 0z, 1z, Uz, 0u, 1u, Uu
|
|
* 0 1 x 0 1 x 0 1 z 0 1 x
|
|
*
|
|
* tentative vcd translation, return value:
|
|
* 0: digital value, 1: real number, 2: unknown
|
|
*/
|
|
|
|
static int
|
|
get_vcdval(char *xspiceval, char **newval)
|
|
{
|
|
int i, err;
|
|
double retval;
|
|
|
|
static char *map[] = {
|
|
"0s", "1s", "Us",
|
|
"0r", "1r", "Ur",
|
|
"0z", "1z", "Uz",
|
|
"0u", "1u", "Uu"
|
|
};
|
|
|
|
static char *returnmap[] = {
|
|
"0", "1", "x",
|
|
"0", "1", "x",
|
|
"z", "z", "z",
|
|
"0", "1", "x"
|
|
};
|
|
|
|
for (i = 0; i < 12; i++)
|
|
if (eq(xspiceval, map[i])) {
|
|
*newval = copy(returnmap[i]);
|
|
return 0;
|
|
}
|
|
|
|
/* is it a real number ? */
|
|
retval = INPevaluate(&xspiceval, &err, 1);
|
|
if (err) {
|
|
*newval = copy(xspiceval); // Assume the node type is coded for this.
|
|
return 2;
|
|
}
|
|
*newval = tprintf("%.16g", retval);
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
#define time _time64
|
|
#define localtime _localtime64
|
|
#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.
|
|
* command 'eprvcd a0 a1 a2 b0 b1 b2 clk > myvcd.vcd'
|
|
* prints the event nodes listed to file myvcd.vcd
|
|
* which then may be viewed with an vcd viewer,
|
|
* for example 'gtkwave'
|
|
* Still missing:
|
|
* hierarchy, vector variables
|
|
*/
|
|
|
|
void
|
|
EVTprintvcd(wordlist *wl)
|
|
{
|
|
int i;
|
|
int nargs;
|
|
int timesteps = 0, tspower = -1;
|
|
|
|
wordlist *w;
|
|
|
|
struct reals ctx;
|
|
|
|
double out_time, last_out_time;
|
|
|
|
|
|
char *node_name[EPRINT_MAXARGS];
|
|
int node_index[EPRINT_MAXARGS];
|
|
int udn_index[EPRINT_MAXARGS];
|
|
Evt_Node_t *node_data[EPRINT_MAXARGS];
|
|
char *node_value[EPRINT_MAXARGS];
|
|
char *old_node_value[EPRINT_MAXARGS];
|
|
char node_ident[EPRINT_MAXARGS + 1];
|
|
char vbuf[24][2][EPRINT_MAXARGS]; // Analog value strings
|
|
|
|
CKTcircuit *ckt;
|
|
|
|
Evt_Node_Info_t **node_table;
|
|
|
|
Mif_Boolean_t more;
|
|
|
|
double next_step;
|
|
double this_step;
|
|
|
|
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 */
|
|
nargs = 0;
|
|
for (w = wl; w; w = w->wl_next)
|
|
nargs++;
|
|
|
|
if (nargs < 1) {
|
|
printf("Usage: eprvcd [-a] <node1> <node2> ...\n");
|
|
return;
|
|
}
|
|
if (nargs > EPRINT_MAXARGS) {
|
|
fprintf(cp_err, "ERROR - eprvcd currently limited to %d arguments\n", EPRINT_MAXARGS);
|
|
return;
|
|
}
|
|
|
|
/* Get needed pointers */
|
|
ckt = g_mif_info.ckt;
|
|
if (!ckt) {
|
|
fprintf(cp_err, "Error: no circuit loaded.\n");
|
|
return;
|
|
}
|
|
if (!ckt->evt->data.node) {
|
|
fprintf(cp_err, "ERROR - No node data: simulation not yet run?\n");
|
|
return;
|
|
}
|
|
node_table = ckt->evt->info.node_table;
|
|
|
|
/* Get data for each argument */
|
|
|
|
w = wl;
|
|
for (i = 0; i < nargs; i++) {
|
|
node_name[i] = w->wl_word;
|
|
node_index[i] = get_index(node_name[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] = "";
|
|
w = w->wl_next;
|
|
}
|
|
|
|
/* generate the vcd identifier code made of the printable
|
|
ASCII character set from ! to ~ (decimal 33 to 126) */
|
|
for (i = 0; i < nargs; i++)
|
|
node_ident[i] = (char) ('!' + i);
|
|
node_ident[i] = '\0';
|
|
|
|
out_init();
|
|
|
|
|
|
/* for gtkwave, avoid e.g. German Umlaute */
|
|
setlocale(LC_TIME, "en_US");
|
|
|
|
/* get actual time */
|
|
time_t ltime;
|
|
char datebuff[80];
|
|
struct tm *my_time;
|
|
|
|
time(<ime);
|
|
/* Obtain the local time: */
|
|
my_time = localtime(<ime);
|
|
/* time output format according to vcd spec */
|
|
strftime(datebuff, sizeof(datebuff), "%B %d, %Y %H:%M:%S", my_time);
|
|
out_printf("$date %s $end\n", datebuff);
|
|
|
|
/* return to what it was before */
|
|
setlocale(LC_TIME, "");
|
|
|
|
out_printf("$version %s %s $end\n", ft_sim->simulator, ft_sim->version);
|
|
|
|
/* get the sim time resolution based on tstep */
|
|
char *unit;
|
|
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 * pow(10, (double)-tspower);
|
|
} else if (tspower < 7) {
|
|
unit = "us";
|
|
tspower = 6 - tspower;
|
|
scale = 1e6 * pow(10, (double)-tspower);
|
|
} else if (tspower < 10) {
|
|
unit = "ns";
|
|
tspower = 9 - tspower;
|
|
scale = 1e9 * pow(10, (double)-tspower);
|
|
} else if (tspower < 13) {
|
|
unit = "ps";
|
|
tspower = 12 - tspower;
|
|
scale = 1e12 * pow(10, (double)-tspower);
|
|
} else if (tspower < 16) {
|
|
unit = "fs";
|
|
tspower = 15 - tspower;
|
|
scale = 1e15 * pow(10, (double)-tspower);
|
|
} else { // 1 fS is the bottom.
|
|
unit = "fs";
|
|
tspower = 0;
|
|
scale = 1e15;
|
|
}
|
|
out_printf("$timescale %g %s $end\n", pow(10, (double)tspower), unit);
|
|
} else {
|
|
double tstep = ckt->CKTstep;
|
|
|
|
/* 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);
|
|
}
|
|
tick = 1.0 / scale;
|
|
|
|
/* Scan the node data. Go for printing using $dumpvars
|
|
for the initial values. Also, determine if there is
|
|
more data following it and if so, what the next step is. */
|
|
|
|
ctx.time = NULL;
|
|
ctx.v_index = 0;
|
|
ctx.last_i = EPRINT_MAXARGS; // Indicate restart
|
|
more = MIF_FALSE;
|
|
next_step = 1e30;
|
|
for (i = 0; i < nargs; i++) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nargs; i++) {
|
|
char *buf;
|
|
if (get_vcdval(node_value[i], &buf) == 1)
|
|
/* real number format */
|
|
out_printf("$var real 1 %c %s $end\n", node_ident[i], node_name[i]);
|
|
else
|
|
/* digital data format */
|
|
out_printf("$var wire 1 %c %s $end\n", node_ident[i], node_name[i]);
|
|
tfree(buf);
|
|
}
|
|
|
|
out_printf("$enddefinitions $end\n");
|
|
|
|
/* first set of data for initialization
|
|
or if only op has been calculated */
|
|
out_printf("$dumpvars\n");
|
|
for (i = 0; i < nargs; i++) {
|
|
char *buf;
|
|
if (get_vcdval(node_value[i], &buf) == 1)
|
|
/* real number format */
|
|
out_printf("r%s %c\n", buf, node_ident[i]);
|
|
else
|
|
/* digital data format */
|
|
out_printf("%s%c\n", buf, node_ident[i]);
|
|
tfree(buf);
|
|
}
|
|
out_printf("$end\n");
|
|
|
|
/* While there is more data, get the next values and print */
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* print only values that have changed */
|
|
|
|
for (i = 0, got_one = 0; i < nargs; i++) {
|
|
if (!eq(old_node_value[i], node_value[i])) {
|
|
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)
|
|
out_printf("r%s %c\n", buf, node_ident[i]);
|
|
else
|
|
out_printf("%s%c\n", buf, node_ident[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);
|
|
}
|
|
}
|
|
} /* end while there is more data */
|
|
|
|
out_printf("\n\n");
|
|
}
|
|
|
|
/* Mark event nodes whose data should be saved for printing.
|
|
* By default, all nodes are saved, so initial "none" clears.
|
|
*/
|
|
|
|
static void set_all(CKTcircuit *ckt, Mif_Boolean_t val)
|
|
{
|
|
int i, count;
|
|
Evt_Node_Info_t **node_table;
|
|
|
|
count = ckt->evt->counts.num_nodes;
|
|
node_table = ckt->evt->info.node_table;
|
|
if (!node_table)
|
|
return;
|
|
for (i = 0; i < count; i++)
|
|
node_table[i]->save = val;
|
|
}
|
|
|
|
void
|
|
EVTsave(wordlist *wl)
|
|
{
|
|
int i;
|
|
wordlist *w;
|
|
CKTcircuit *ckt;
|
|
Evt_Node_Info_t **node_table;
|
|
|
|
if (wl == NULL) {
|
|
printf("Usage: esave all | none | <node1> <node2> ...\n");
|
|
return;
|
|
}
|
|
|
|
/* Get needed pointers. */
|
|
|
|
ckt = g_mif_info.ckt;
|
|
if (!ckt) {
|
|
fprintf(cp_err, "Error: no circuit loaded.\n");
|
|
return;
|
|
}
|
|
|
|
node_table = ckt->evt->info.node_table;
|
|
if (!node_table)
|
|
return;
|
|
|
|
/* Deal with "all" and "none". */
|
|
|
|
if (wl->wl_next == NULL) {
|
|
if (!strcmp("none", wl->wl_word)) {
|
|
set_all(ckt, MIF_FALSE);
|
|
return;
|
|
} else if (!strcmp("all", wl->wl_word)) {
|
|
set_all(ckt, MIF_TRUE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
set_all(ckt, MIF_FALSE); /* Clear previous settings. */
|
|
|
|
/* Set save flag for each argument */
|
|
|
|
for (w = wl; w; w = w->wl_next) {
|
|
i = get_index(w->wl_word);
|
|
if (i < 0) {
|
|
fprintf(cp_err, "ERROR - Node %s is not an event node.\n",
|
|
w->wl_word);
|
|
return;
|
|
}
|
|
node_table[i]->save = MIF_TRUE;
|
|
}
|
|
}
|