ngspice/src/xspice/evt/evtiter.c

297 lines
8.9 KiB
C

/*============================================================================
FILE EVTiter.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
<date> <person name> <nature of modifications>
SUMMARY
This file contains function EVTiter which iterates through
event-driven outputs and instances until the outputs no longer change.
INTERFACES
int EVTiter(CKTcircuit *ckt)
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/evtudn.h"
#include "ngspice/evtproto.h"
/*
EVTiter
This function iterates through event-driven outputs and instances
until the outputs no longer change. The general algorithm used
is:
Do:
Scan list of changed outputs
Put items on the node queue 'to_eval' list for
each changed output.
Scan list of changed nodes
Resolve nodes with multiple outputs posted.
Create inverted state for nodes with attached
inverted inputs.
Put items on the instance queue 'to_call' list
for each changed node.
If transient analysis, put state of the node
into the node data structure.
Scan instance to_call list
Call EVTload for each instance on list.
While there are changed outputs
*/
int EVTiter(
CKTcircuit *ckt) /* the circuit structure */
{
int i;
int num_changed;
int num_to_eval;
int num_to_call;
int output_index;
/* int output_subindex;*/
int inst_index;
int node_index;
int port_index;
int num_outputs;
int udn_index;
int passes;
Evt_Ckt_Data_t *evt;
Evt_Output_Queue_t *output_queue;
Evt_Node_Queue_t *node_queue;
Evt_Inst_Queue_t *inst_queue;
Evt_Output_Info_t **output_table;
Evt_Node_Info_t **node_table;
Evt_Port_Info_t **port_table;
Evt_Inst_Index_t *inst_list;
Evt_Node_Data_t *node_data;
Evt_Node_t *rhs;
Evt_Node_t *rhsold;
Evt_Node_t *node;
Mif_Boolean_t equal;
char *err_msg;
/* Get temporary pointers for fast access */
evt = ckt->evt;
output_queue = &(evt->queue.output);
node_queue = &(evt->queue.node);
inst_queue = &(evt->queue.inst);
output_table = evt->info.output_table;
node_table = evt->info.node_table;
port_table = evt->info.port_table;
node_data = evt->data.node;
rhs = node_data->rhs;
rhsold = node_data->rhsold;
/* Loop until no more output change, or too many passes through loop */
for(passes = 0; passes < evt->limits.max_event_passes; passes++) {
/* Create list of nodes to evaluate from list of changed outputs */
num_changed = output_queue->num_changed;
for(i = 0; i < num_changed; i++) {
/* Get index of node that output is connected to */
output_index = output_queue->changed_index[i];
node_index = output_table[output_index]->node_index;
/* If not already on list of nodes to evaluate, add it */
if(! node_queue->to_eval[node_index]) {
node_queue->to_eval[node_index] = MIF_TRUE;
node_queue->to_eval_index[(node_queue->num_to_eval)++]
= node_index;
}
/* Reset the changed flag on the output queue */
output_queue->changed[output_index] = MIF_FALSE;
}
output_queue->num_changed = 0;
/* Evaluate nodes and for any which have changed, enter */
/* the instances that receive inputs from them on the list */
/* of instances to call */
num_to_eval = node_queue->num_to_eval;
for(i = 0; i < num_to_eval; i++) {
/* Get the node index, udn index and number of outputs */
node_index = node_queue->to_eval_index[i];
udn_index = node_table[node_index]->udn_index;
num_outputs = node_table[node_index]->num_outputs;
/* Resolve the node value if multiple outputs on it */
/* and test if new node value is different than old value */
if(num_outputs > 1) {
g_evt_udn_info[udn_index]->resolve
(num_outputs,
rhs[node_index].output_value,
rhs[node_index].node_value);
g_evt_udn_info[udn_index]->compare
(rhs[node_index].node_value,
rhsold[node_index].node_value,
&equal);
if(! equal) {
g_evt_udn_info[udn_index]->copy
(rhs[node_index].node_value,
rhsold[node_index].node_value);
}
}
/* Else, load function has already determined that they were */
/* not equal */
else
equal = MIF_FALSE;
/* If not equal, make inverted copy in rhsold if */
/* needed, and place indexes of instances with inputs connected */
/* to the node in the to_call list of inst queue */
if(! equal) {
if(node_table[node_index]->invert) {
g_evt_udn_info[udn_index]->copy
(rhsold[node_index].node_value,
rhsold[node_index].inverted_value);
g_evt_udn_info[udn_index]->invert
(rhsold[node_index].inverted_value);
}
inst_list = node_table[node_index]->inst_list;
while(inst_list) {
inst_index = inst_list->index;
if(! inst_queue->to_call[inst_index]) {
inst_queue->to_call[inst_index] = MIF_TRUE;
inst_queue->to_call_index[(inst_queue->num_to_call)++]
= inst_index;
}
inst_list = inst_list->next;
} /* end while instances with inputs on node */
} /* end if not equal */
/* If transient analysis mode */
/* Save the node data onto the node results list and mark */
/* that it has been modified, even if the */
/* resolved node value has not changed */
if(g_mif_info.circuit.anal_type == MIF_TRAN) {
node = *(node_data->tail[node_index]);
node_data->tail[node_index] = &(node->next);
EVTnode_copy(ckt, node_index, &(rhsold[node_index]), &(node->next));
node->next->step = g_mif_info.circuit.evt_step;
if(! node_data->modified[node_index]) {
node_data->modified[node_index] = MIF_TRUE;
node_data->modified_index[(node_data->num_modified)++] = node_index;
}
}
/* Reset the to_eval flag on the node queue */
node_queue->to_eval[node_index] = MIF_FALSE;
} /* end for number of nodes to evaluate */
node_queue->num_to_eval = 0;
/* Call the instances with inputs on nodes that have changed */
num_to_call = inst_queue->num_to_call;
for(i = 0; i < num_to_call; i++) {
inst_index = inst_queue->to_call_index[i];
inst_queue->to_call[inst_index] = MIF_FALSE;
EVTload(ckt, inst_index);
}
inst_queue->num_to_call = 0;
/* Record statistics */
if(g_mif_info.circuit.anal_type == MIF_DC)
(ckt->evt->data.statistics->op_event_passes)++;
/* If no outputs changed, iteration is over, so return with success! */
if(output_queue->num_changed == 0)
return(0);
} /* end for */
/* Too many passes through loop, report problems and exit with error */
err_msg = TMALLOC(char, 10000);
for(i = 0; i < output_queue->num_changed; i++) {
output_index = output_queue->changed_index[i];
port_index = output_table[output_index]->port_index;
sprintf(err_msg, "\n Instance: %s\n Connection: %s\n Port: %d",
port_table[port_index]->inst_name,
port_table[port_index]->conn_name,
port_table[port_index]->port_num);
ENHreport_conv_prob(ENH_EVENT_NODE,
port_table[port_index]->node_name,
err_msg);
}
FREE(err_msg);
SPfrontEnd->IFerrorf (ERR_WARNING,
"Too many iteration passes in event-driven circuits");
return(E_ITERLIM);
}