Add two new XSPICE functions: EVTnew_value_call() and

EVTcancel_value_call().  EVTnew_value_call() specifies a function
to be called for each confirmed new value produced on an XSPICE
event node.  To be used to add event node support for iplot.
This commit is contained in:
Giles Atkinson 2025-03-23 16:46:55 +00:00 committed by Holger Vogt
parent f4963b1ada
commit 33f206b916
7 changed files with 266 additions and 81 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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)++;
}