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.
362 lines
11 KiB
362 lines
11 KiB
/*============================================================================
|
|
FILE EVTdump.c
|
|
|
|
MEMBER OF process XSPICE
|
|
|
|
Public Domain
|
|
|
|
Georgia Tech Research Corporation
|
|
Atlanta, Georgia 30332
|
|
PROJECT A-8503
|
|
|
|
AUTHORS
|
|
|
|
6/15/92 Bill Kuhn
|
|
|
|
MODIFICATIONS
|
|
|
|
06/05/17 Holger Vogt Shared ngspice additions
|
|
|
|
SUMMARY
|
|
|
|
This file contains functions used
|
|
to send event-driven node results data to the IPC channel when
|
|
the simulator is used with CAE software.
|
|
It also sends data to a caller via callback, if shared ngspice
|
|
is enabled.
|
|
|
|
INTERFACES
|
|
|
|
EVTdump()
|
|
EVTshareddump()
|
|
|
|
REFERENCED FILES
|
|
|
|
None.
|
|
|
|
NON-STANDARD FEATURES
|
|
|
|
None.
|
|
|
|
============================================================================*/
|
|
|
|
|
|
#include "ngspice/ngspice.h"
|
|
//#include "misc.h"
|
|
|
|
#include "ngspice/cktdefs.h"
|
|
//#include "util.h"
|
|
#include "ngspice/sperror.h"
|
|
|
|
#include "ngspice/mif.h"
|
|
#include "ngspice/evt.h"
|
|
#include "ngspice/evtproto.h"
|
|
#include "ngspice/evtudn.h"
|
|
|
|
#ifdef SHARED_MODULE
|
|
/* global flag, TRUE if callback is used */
|
|
extern bool wantevtdata;
|
|
extern void shared_send_event(int, double, double, char *, void *, int, int);
|
|
extern void shared_send_dict(int, int, char*, char*);
|
|
static void EVTshareddump(
|
|
CKTcircuit *ckt, /* The circuit structure */
|
|
Ipc_Anal_t mode, /* The analysis mode for this call */
|
|
double step); /* The sweep step for a DCTRCURVE analysis, or */
|
|
/* 0.0 for DCOP and TRAN */
|
|
|
|
static void EVTsharedsend_line(
|
|
int ipc_index, /* The index used in the dictionary */
|
|
double step, /* The analysis step */
|
|
void *node_value, /* The node value */
|
|
int udn_index, /* The user-defined node index */
|
|
int mode); /* mode (op, dc, tran) we are in */
|
|
#endif
|
|
|
|
|
|
/*
|
|
EVTdump
|
|
|
|
This function is called to send event-driven node data to the IPC
|
|
channel. A ``mode'' argument determines how the data is located in
|
|
the event data structure and what data is sent.
|
|
|
|
If the mode is DCOP, then this is necessarily the first call to
|
|
the function. In this case, the set of event-driven nodes is
|
|
scanned to determine which should be sent. Only nodes that are
|
|
not inside subcircuits are sent. Next, the function sends
|
|
a ``dictionary'' of node names/types vs. node indexes.
|
|
Finally, the function sends the DC operating point solutions
|
|
for the event-driven nodes in the dictionary.
|
|
|
|
If the mode is DCTRCURVE, it is assumed that the function has
|
|
already been called with mode = DCOP. The function scans the solution
|
|
vector and sends data for any nodes that have changed.
|
|
|
|
If the mode is TRAN, it is assumed that the function has already
|
|
been called once with mode = DCOP. The function scans the
|
|
event data for nodes that have changed since the last accepted
|
|
analog timepoint and sends the new data.
|
|
|
|
Note: This function must be called BEFORE calling EVTop_save or
|
|
EVTaccept() so that the state of the node data structure will allow
|
|
it to determine what has changed.
|
|
*/
|
|
|
|
|
|
typedef struct evtdump_s {
|
|
Mif_Boolean_t send; /* True if this node should be sent */
|
|
int ipc_index; /* Index for this node in dict sent to CAE system */
|
|
char *node_name_str; /* Node name */
|
|
char *udn_type_str; /* UDN type */
|
|
} evtdump_dict_t;
|
|
|
|
|
|
|
|
void EVTdump(
|
|
CKTcircuit *ckt, /* The circuit structure */
|
|
Ipc_Anal_t mode, /* The analysis mode for this call */
|
|
double step) /* The sweep step for a DCTRCURVE analysis, or */
|
|
/* 0.0 for DCOP and TRAN */
|
|
{
|
|
|
|
#ifdef SHARED_MODULE
|
|
if (!wantevtdata)
|
|
return;
|
|
else {
|
|
EVTshareddump(ckt, mode, step);
|
|
return;
|
|
}
|
|
#else
|
|
(void)*ckt;
|
|
(void)mode;
|
|
(void)step;
|
|
|
|
/* Return immediately */
|
|
return;
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef SHARED_MODULE
|
|
static void EVTshareddump(
|
|
CKTcircuit *ckt, /* The circuit structure */
|
|
Ipc_Anal_t mode, /* The analysis mode for this call */
|
|
double step) /* The sweep step for a DCTRCURVE analysis, or */
|
|
/* 0.0 for DCOP and TRAN */
|
|
{
|
|
static evtdump_dict_t *node_dict = NULL;
|
|
static int num_send_nodes;
|
|
|
|
int i;
|
|
int j;
|
|
int num_nodes;
|
|
int num_modified;
|
|
int index;
|
|
|
|
char *name;
|
|
int name_len;
|
|
|
|
Mif_Boolean_t firstcall;
|
|
|
|
Evt_Node_Data_t *node_data;
|
|
|
|
Evt_Node_t *rhsold;
|
|
Evt_Node_t **head;
|
|
Evt_Node_t *here;
|
|
|
|
Evt_Node_Info_t **node_table;
|
|
|
|
Mif_Boolean_t equal;
|
|
|
|
/* Get number of event-driven nodes */
|
|
num_nodes = ckt->evt->counts.num_nodes;
|
|
|
|
/* Exit immediately if no event-driven nodes in circuit */
|
|
if (num_nodes <= 0)
|
|
return;
|
|
|
|
|
|
/* Get pointers for fast access to event data */
|
|
node_data = ckt->evt->data.node;
|
|
node_table = ckt->evt->info.node_table;
|
|
rhsold = node_data->rhsold;
|
|
head = node_data->head;
|
|
|
|
|
|
/* Determine if this is the first call */
|
|
if (node_dict == NULL)
|
|
firstcall = MIF_TRUE;
|
|
else
|
|
firstcall = MIF_FALSE;
|
|
|
|
|
|
/* If this is the first call, get the dictionary info */
|
|
if (firstcall) {
|
|
|
|
/* Allocate local data structure used to process nodes */
|
|
node_dict = TMALLOC(evtdump_dict_t, num_nodes);
|
|
|
|
/* Loop through all nodes to determine which nodes should be sent. */
|
|
/* Only nodes not within subcircuits qualify. */
|
|
|
|
num_send_nodes = 0;
|
|
for (i = 0; i < num_nodes; i++) {
|
|
|
|
/* Get the name of the node. */
|
|
name = node_table[i]->name;
|
|
|
|
/* If name is in a subcircuit, mark that node should not be sent */
|
|
/* and continue to next node. */
|
|
name_len = (int)strlen(name);
|
|
for (j = 0; j < name_len; j++) {
|
|
if (name[j] == ':')
|
|
break;
|
|
}
|
|
if (j < name_len) {
|
|
node_dict[i].send = MIF_FALSE;
|
|
continue;
|
|
}
|
|
|
|
/* Otherwise, fill in info in dictionary. */
|
|
node_dict[i].send = MIF_TRUE;
|
|
node_dict[i].ipc_index = num_send_nodes;
|
|
node_dict[i].node_name_str = name;
|
|
node_dict[i].udn_type_str = g_evt_udn_info[node_table[i]->udn_index]->name;
|
|
|
|
/* Increment the count of nodes to be sent. */
|
|
num_send_nodes++;
|
|
} /* end for */
|
|
} /* end if first call */
|
|
|
|
/* Exit if there are no nodes to be sent */
|
|
if (num_send_nodes <= 0)
|
|
return;
|
|
|
|
/* If this is the first call, send the dictionary (the list of event nodes, line by line) */
|
|
if (firstcall) {
|
|
for (i = 0; i < num_nodes; i++) {
|
|
if (node_dict[i].send)
|
|
shared_send_dict(node_dict[i].ipc_index, num_nodes, node_dict[i].node_name_str, node_dict[i].udn_type_str);
|
|
}
|
|
}
|
|
|
|
/* If this is the first call, send the operating point solution */
|
|
/* and return. */
|
|
if (firstcall) {
|
|
for (i = 0; i < num_nodes; i++) {
|
|
if (node_dict[i].send) {
|
|
EVTsharedsend_line(node_dict[i].ipc_index,
|
|
step,
|
|
rhsold[i].node_value,
|
|
node_table[i]->udn_index,
|
|
mode);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Otherwise, this must be DCTRCURVE or TRAN mode and we need to */
|
|
/* send only stuff that has changed since the last call. */
|
|
/* The determination of what to send is modeled after code in */
|
|
/* EVTop_save() for DCTRCURVE and EVTaccept() for TRAN. */
|
|
|
|
if (mode == IPC_ANAL_DCTRCURVE) {
|
|
/* Send data prefix */
|
|
/* Loop through event nodes */
|
|
for (i = 0; i < num_nodes; i++) {
|
|
/* If dictionary indicates this node should be sent */
|
|
if (node_dict[i].send) {
|
|
/* Locate end of node data */
|
|
here = head[i];
|
|
for (;;) {
|
|
if (here->next)
|
|
here = here->next;
|
|
else
|
|
break;
|
|
}
|
|
/* Compare entry at end of list to rhsold */
|
|
g_evt_udn_info[node_table[i]->udn_index]->compare(
|
|
rhsold[i].node_value,
|
|
here->node_value,
|
|
&equal);
|
|
/* If value in rhsold is different, send it */
|
|
if (!equal) {
|
|
EVTsharedsend_line(node_dict[i].ipc_index,
|
|
step,
|
|
rhsold[i].node_value,
|
|
node_table[i]->udn_index,
|
|
mode);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if (mode == IPC_ANAL_TRAN) {
|
|
/* Loop through list of nodes modified since last time */
|
|
num_modified = node_data->num_modified;
|
|
for (i = 0; i < num_modified; i++) {
|
|
/* Get the index of the node modified */
|
|
index = node_data->modified_index[i];
|
|
/* If dictionary indicates this node should be sent */
|
|
if (node_dict[index].send) {
|
|
/* Scan through new events and send the data for each event */
|
|
here = *(node_data->last_step[index]);
|
|
while ((here = here->next) != NULL) {
|
|
EVTsharedsend_line(node_dict[index].ipc_index,
|
|
here->step,
|
|
here->node_value,
|
|
node_table[index]->udn_index,
|
|
mode);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
EVTsharedsend_line
|
|
|
|
This function formats the event node data and sends it to the caller via sharedspice.c.
|
|
*/
|
|
|
|
|
|
static void EVTsharedsend_line(
|
|
int dict_index, /* The index used in the dictionary */
|
|
double step, /* The analysis step */
|
|
void *node_value, /* The node value */
|
|
int udn_index, /* The user-defined node index */
|
|
int mode) /* the mode (op, dc, tran) we are in */
|
|
{
|
|
double dvalue;
|
|
char *svalue;
|
|
void *pvalue;
|
|
int len;
|
|
|
|
/* Get the data to send */
|
|
if (g_evt_udn_info[udn_index]->plot_val)
|
|
g_evt_udn_info[udn_index]->plot_val(node_value, "", &dvalue);
|
|
else
|
|
dvalue = 0.0;
|
|
|
|
if (g_evt_udn_info[udn_index]->print_val)
|
|
g_evt_udn_info[udn_index]->print_val(node_value, "", &svalue);
|
|
else
|
|
svalue = "";
|
|
|
|
if (g_evt_udn_info[udn_index]->ipc_val)
|
|
g_evt_udn_info[udn_index]->ipc_val(node_value, &pvalue, &len);
|
|
else {
|
|
pvalue = NULL;
|
|
len = 0;
|
|
}
|
|
|
|
/* Send it to sharedspice.c */
|
|
shared_send_event(dict_index, step, dvalue, svalue, pvalue, len, mode);
|
|
}
|
|
|
|
|
|
#endif
|