diff --git a/src/include/ngspice/evt.h b/src/include/ngspice/evt.h index 0f01779d5..e5021a39e 100644 --- a/src/include/ngspice/evt.h +++ b/src/include/ngspice/evt.h @@ -78,6 +78,14 @@ struct Evt_Inst_Index { int index; /* the value of the index */ }; +struct Evt_Node_Cb { + struct Evt_Node_Cb *next; + Evt_New_Value_Cb_t fn; /* Function to be called. */ + Evt_Node_Cb_Type_t type; /* Data type to pass to fn. */ + const char *member; /* For event data type's plot fn. */ + void *ctx; +}; + struct Evt_Node_Info { Evt_Node_Info_t *next; /* the next in the linked list */ char *name; /* Name of node in deck */ @@ -88,6 +96,7 @@ struct Evt_Node_Info { int num_outputs; /* Number of outputs connected to this node */ int num_insts; /* The number of insts receiving node as input */ Evt_Inst_Index_t *inst_list; /* Linked list of indexes of these instances */ + Evt_Node_Cb_t *cbs; /* New value callbacks. */ }; struct Evt_Inst_Info { @@ -195,8 +204,6 @@ struct Evt_Queue { /* ************** */ - - struct Evt_Node { Evt_Node_t *next; /* pointer to next in linked list */ Mif_Boolean_t op; /* true if computed from op analysis */ @@ -204,6 +211,7 @@ struct Evt_Node { void **output_value; /* Array of outputs posted to this node */ void *node_value; /* Resultant computed from output values */ void *inverted_value; /* Inverted copy of node_value */ + }; struct Evt_Node_Data { @@ -362,6 +370,4 @@ struct Evt_Ckt_Data { Evt_Option_t options; /* Data input on .options cards */ }; - - #endif diff --git a/src/include/ngspice/evtproto.h b/src/include/ngspice/evtproto.h index ea2bd5f92..033db4226 100644 --- a/src/include/ngspice/evtproto.h +++ b/src/include/ngspice/evtproto.h @@ -136,6 +136,28 @@ bool Evtcheck_nodes( struct INPtables *stab); /* Symbol table. */ struct dvec *EVTfindvec(char *node); + +/* Set and remove call-backs on new node values. */ + +Mif_Boolean_t EVTnew_value_call(const char *node, + Evt_New_Value_Cb_t fn, + Evt_Node_Cb_Type_t type, + void *ctx); + +void EVTcancel_value_call(const char *node, + Evt_New_Value_Cb_t fn, + void *ctx); + +/* Internal utility functions. */ + void Evt_purge_free_outputs(void); +/* Parse a node name with member and find the node index. */ + +struct node_parse { + char *node; + char *member; +}; + +int Evt_Parse_Node(const char *node, struct node_parse *result); #endif diff --git a/src/include/ngspice/evttypes.h b/src/include/ngspice/evttypes.h index c6a59662f..dbcb920e4 100644 --- a/src/include/ngspice/evttypes.h +++ b/src/include/ngspice/evttypes.h @@ -1,6 +1,6 @@ #ifndef ngspice_EVTTYPES_H #define ngspice_EVTTYPES_H - +#include "miftypes.h" typedef struct Evt_Output_Info Evt_Output_Info_t; typedef struct Evt_Port_Info Evt_Port_Info_t; @@ -28,6 +28,11 @@ typedef struct Evt_Limit Evt_Limit_t; typedef struct Evt_Job Evt_Job_t; typedef struct Evt_Option Evt_Option_t; typedef struct Evt_Ckt_Data Evt_Ckt_Data_t; +typedef struct Evt_Node_Cb Evt_Node_Cb_t; +typedef Mif_Boolean_t (*Evt_New_Value_Cb_t)(double when, Mif_Value_t *val_p, + void *ctx, int is_last); + +typedef enum Evt_Node_Cb_Type { Evt_Cbt_Raw, Evt_Cbt_Plot} Evt_Node_Cb_Type_t; #endif diff --git a/src/xspice/evt/evtaccept.c b/src/xspice/evt/evtaccept.c index 0e68c26e6..ef5e637d7 100644 --- a/src/xspice/evt/evtaccept.c +++ b/src/xspice/evt/evtaccept.c @@ -45,6 +45,7 @@ NON-STANDARD FEATURES #include "ngspice/mif.h" #include "ngspice/evt.h" +#include "ngspice/evtudn.h" #include "ngspice/evtproto.h" @@ -160,18 +161,62 @@ void EVTaccept( num_modified = node_data->num_modified; /* Loop through list of items modified since last time */ for(i = 0; i < num_modified; i++) { + Evt_Node_t *this; + Evt_Node_Info_t *node_info; + Evt_Node_Cb_t *cb, **cbpp; + int udn_index; + /* Get the index of the node modified */ index = node_data->modified_index[i]; /* Reset the modified flag */ node_data->modified[index] = MIF_FALSE; + /* Call any value-change functions registered for this node. */ + + node_info = node_table[index]; + udn_index = node_info->udn_index; + + cbpp = &node_info->cbs; + for (;;) { + Mif_Value_t val; + + cb = *cbpp; + if (cb == NULL) + break; + + for (this = *node_data->last_step[index]; + this; + this = this->next) { + switch (cb->type) { + case Evt_Cbt_Raw: + val.pvalue = this->node_value; + break; + case Evt_Cbt_Plot: + g_evt_udn_info[udn_index]->plot_val(this->node_value, + (char *)cb->member, + &val.rvalue); + break; + } + + if ((*cb->fn)(this->step, &val, cb->ctx, !this->next)) { + /* Remove callback from chain. */ + + *cbpp = cb->next; + txfree(cb); + break; + } + } + if (this == NULL) // Normal loop exit. + cbpp = &cb->next; + } + /* Optionally store node values for later examination. * The test of CKTtime here is copied from dctran.c. * CKTinitTime is from the tstart parameter of the "tran" * command or card. */ - if (node_table[index]->save && ckt->CKTtime >= ckt->CKTinitTime && + if (node_info->save && ckt->CKTtime >= ckt->CKTinitTime && (ckt->CKTtime > 0 || !(ckt->CKTmode & MODEUIC))) { /* Update last_step for this index */ node_data->last_step[index] = node_data->tail[index]; @@ -239,3 +284,77 @@ void EVTaccept( } /* EVTaccept */ +/* Functions to set-up and cancel value-changed callbacks. */ + +Mif_Boolean_t EVTnew_value_call(const char *node, + Evt_New_Value_Cb_t fn, + Evt_Node_Cb_Type_t type, + void *ctx) +{ + struct node_parse result; + int index; + + Evt_Ckt_Data_t *evt; + CKTcircuit *ckt; + Evt_Node_Info_t *node_info; + Evt_Node_Cb_t *cb; + + index = Evt_Parse_Node(node, &result); + if (index < 0) + return MIF_FALSE; + ckt = g_mif_info.ckt; + evt = ckt->evt; + node_info = evt->info.node_table[index]; + cb = tmalloc(sizeof *cb); + cb->next = node_info->cbs; + node_info->cbs = cb; + cb->fn = fn; + cb->type = type; + cb->member = copy(result.member); + cb->ctx = ctx; + txfree(result.node); + return MIF_TRUE; +} + +void EVTcancel_value_call(const char *node, + Evt_New_Value_Cb_t fn, + void *ctx) +{ + Evt_Ckt_Data_t *evt; + CKTcircuit *ckt; + Evt_Node_Info_t **node_table, *node_info; + Evt_Node_Cb_t **cbpp, *cb; + int i, num_nodes; + + ckt = g_mif_info.ckt; + if (!ckt) + return; + evt = ckt->evt; + if (!evt) + return; + + /* Look for node name in the event-driven node list */ + + node_table = evt->info.node_table; + num_nodes = evt->counts.num_nodes; + + for (i = 0; i < num_nodes; i++) { + if (cieq(node, node_table[i]->name)) + break; + } + if (i >= num_nodes) + return; + + node_info = node_table[i]; + cbpp = &node_info->cbs; + cb = node_info->cbs; + while (cb) { + if (cb->fn == fn && cb->ctx == ctx) { + *cbpp = cb->next; + tfree(cb); + } else { + cbpp = &cb->next; + } + cb = *cbpp; + } +} diff --git a/src/xspice/evt/evtdest.c b/src/xspice/evt/evtdest.c index 4fa4dfca9..a25632f51 100644 --- a/src/xspice/evt/evtdest.c +++ b/src/xspice/evt/evtdest.c @@ -167,10 +167,12 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data) for (i = 0; i < evt->counts.num_nodes; i++) { Evt_Node_Info_t *info = evt->info.node_table[i]; - Evt_Node_t *node; + Evt_Node_t *node; + node = node_data->head[i]; while (node) { Evt_Node_t *next = node->next; + Evt_Node_destroy(info, node); tfree(node); node = next; @@ -178,6 +180,7 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data) node = node_data->free[i]; while (node) { Evt_Node_t *next = node->next; + Evt_Node_destroy(info, node); tfree(node); node = next; @@ -193,6 +196,7 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data) for (i = 0; i < evt->counts.num_nodes; i++) { Evt_Node_Info_t *info = evt->info.node_table[i]; + Evt_Node_destroy(info, &(node_data->rhs[i])); Evt_Node_destroy(info, &(node_data->rhsold[i])); } @@ -287,6 +291,7 @@ static void Evt_Info_destroy(Evt_Info_t *info) { Evt_Inst_Info_t *inst = info->inst_list; + while (inst) { Evt_Inst_Info_t *next_inst = inst->next; tfree(inst); @@ -295,14 +300,24 @@ Evt_Info_destroy(Evt_Info_t *info) tfree(info->inst_table); Evt_Node_Info_t *nodei = info->node_list; + while (nodei) { Evt_Node_Info_t *next_nodei = nodei->next; + Evt_Node_Cb_t *cb, *cb_next; + tfree(nodei->name); + for (cb = nodei->cbs; cb; cb = cb_next) { + cb_next = cb->next; + if (cb->member) + txfree(cb->member); + tfree(cb); + } Evt_Inst_Index_t *p = nodei->inst_list; + while (p) { Evt_Inst_Index_t *next_p = p->next; - tfree(p); + txfree(p); p = next_p; } diff --git a/src/xspice/evt/evtplot.c b/src/xspice/evt/evtplot.c index a4d17842f..dafce5c7d 100644 --- a/src/xspice/evt/evtplot.c +++ b/src/xspice/evt/evtplot.c @@ -45,15 +45,78 @@ NON-STANDARD FEATURES #include "ngspice/mif.h" #include "ngspice/mifproto.h" -/*saj for output */ #include "ngspice/sim.h" #include "ngspice/dvec.h" -//#include "ftedata.h" -//#include "fteconstant.h" -//#include "util.h" #include "ngspice/cpstd.h" +/* Parse member qualifier from node name and find node index. + * The node name may be qualified by a member name for nodes with + * composite values such as Digital_t, as in "node_name(state)". + */ + +int Evt_Parse_Node(const char *node, struct node_parse *result) +{ + Evt_Ckt_Data_t *evt; + CKTcircuit *ckt; + Evt_Node_Info_t **node_table; + char *name, *ptr; + int i, num_nodes; + + ckt = g_mif_info.ckt; + if (!ckt) + return -1; + evt = ckt->evt; + if (!evt) + return -1; + if (!evt->info.node_table) + return -1; + if (evt->counts.num_nodes == 0) + return -1; + + /* Make a copy of the node name. Do not free this string. */ + + name = MIFcopy((char *)node); + + /* Convert to all lower case */ + + strtolower(name); + + /* Divide into the node name and member name */ + + result->node = name; + for (ptr = name; *ptr != '\0'; ptr++) + if (*ptr == '(') + break; + + if (*ptr == '(') { + *ptr = '\0'; + ptr++; + result->member = ptr; + for( ; *ptr != '\0'; ptr++) + if (*ptr == ')') + break; + *ptr = '\0'; + } else { + result->member = NULL; + } + + /* Look for node name in the event-driven node list */ + + node_table = evt->info.node_table; + num_nodes = evt->counts.num_nodes; + + for (i = 0; i < num_nodes; i++) { + if (cieq(name, node_table[i]->name)) + break; + } + if (i >= num_nodes) { + tfree(name); + return -1; + } + return i; +} + /* EVTfindvec() @@ -79,21 +142,17 @@ keyword "all" is supplied to the plot_val routine for the member name. struct dvec *EVTfindvec( char *node) /* The node name (and optional member name) */ { - char *name; - char *member = "all"; - char *ptr; + char *name; + struct node_parse result; + int index, i; + int udn_index; + int num_events; - int i; - int num_nodes; - int udn_index; - int num_events; - - Mif_Boolean_t found; - Evt_Ckt_Data_t *evt; - CKTcircuit *ckt; - Evt_Node_Info_t **node_table; - Evt_Node_t *head; - Evt_Node_t *event; + Evt_Ckt_Data_t *evt; + CKTcircuit *ckt; + Evt_Node_Info_t *node_info; + Evt_Node_t *head; + Evt_Node_t *event; double *anal_point_vec; double *value_vec; @@ -105,67 +164,26 @@ struct dvec *EVTfindvec( /* Exit immediately if event-driven stuff not allocated yet, */ /* or if number of event nodes is zero. */ + index = Evt_Parse_Node(node, &result); + if (index < 0) + return NULL; + name = result.node; ckt = g_mif_info.ckt; - if(! ckt) - return(NULL); evt = ckt->evt; - if(! evt) - return(NULL); - if(! evt->info.node_table) - return(NULL); - if(evt->counts.num_nodes == 0) - return(NULL); - /* Make a copy of the node name. */ - /* Do not free this string. It is assigned into the dvec structure below. */ - name = MIFcopy(node); - - /* Convert to all lower case */ - strtolower(name); - - /* Divide into the node name and member name */ - for(ptr = name; *ptr != '\0'; ptr++) - if(*ptr == '(') - break; - - if(*ptr == '(') { - *ptr = '\0'; - ptr++; - member = ptr; - for( ; *ptr != '\0'; ptr++) - if(*ptr == ')') - break; - *ptr = '\0'; - } - - /* Look for node name in the event-driven node list */ - num_nodes = evt->counts.num_nodes; - node_table = evt->info.node_table; - - for(i = 0, found = MIF_FALSE; i < num_nodes; i++) { - if(cieq(name, node_table[i]->name)) { - found = MIF_TRUE; - break; - } - } - - if(! found) { + if (!evt->data.node) { tfree(name); - return(NULL); + return NULL; } /* Get the UDN type index */ - udn_index = node_table[i]->udn_index; - if (!evt->data.node) { -// fprintf(stderr, "Warning: No event data available! \n Simulation not yet run?\n"); - tfree(name); - return(NULL); - } + node_info = evt->info.node_table[index]; + udn_index = node_info->udn_index; /* Count the number of events */ - head = evt->data.node->head[i]; + head = evt->data.node->head[index]; for(event = head, num_events = 0; event; event = event->next) num_events++; @@ -187,9 +205,9 @@ struct dvec *EVTfindvec( /* Get the next value by calling the appropriate UDN plot_val function */ value = 0.0; - g_evt_udn_info[udn_index]->plot_val (event->node_value, - member, - &value); + g_evt_udn_info[udn_index]->plot_val(event->node_value, + result.member ? result.member : "all", + &value); /* Put the first value of the horizontal line in the vector */ anal_point_vec[i] = event->step; @@ -206,8 +224,7 @@ struct dvec *EVTfindvec( /* Allocate dvec structures and assign the vectors into them. */ /* See FTE/OUTinterface.c:plotInit() for initialization example. */ - ptr = tprintf("%s_steps", name); - scale = dvec_alloc(ptr, + scale = dvec_alloc(tprintf("%s_steps", name), SV_TIME, (VF_REAL | VF_EVENT_NODE) & ~VF_PERMANENT, i, anal_point_vec); diff --git a/src/xspice/evt/evttermi.c b/src/xspice/evt/evttermi.c index 5f591ccef..8777738c8 100644 --- a/src/xspice/evt/evttermi.c +++ b/src/xspice/evt/evttermi.c @@ -330,6 +330,7 @@ static void EVTnode_insert( node->name = MIFcopy(node_name); node->udn_index = udn_index; node->save = MIF_TRUE; /* Backward compatible behaviour: save all. */ + node->cbs = NULL; index = ckt->evt->counts.num_nodes; (ckt->evt->counts.num_nodes)++; }