1 /*============================================================================
2 FILE    EVTdump.c
3 
4 MEMBER OF process XSPICE
5 
6 Public Domain
7 
8 Georgia Tech Research Corporation
9 Atlanta, Georgia 30332
10 PROJECT A-8503
11 
12 AUTHORS
13 
14     6/15/92  Bill Kuhn
15 
16 MODIFICATIONS
17 
18     06/05/17  Holger Vogt Shared ngspice additions
19 
20 SUMMARY
21 
22     This file contains functions used
23     to send event-driven node results data to the IPC channel when
24     the simulator is used with CAE software.
25     It also sends data to a caller via callback, if shared ngspice
26     is enabled.
27 
28 INTERFACES
29 
30     EVTdump()
31     EVTshareddump()
32 
33 REFERENCED FILES
34 
35     None.
36 
37 NON-STANDARD FEATURES
38 
39     None.
40 
41 ============================================================================*/
42 
43 
44 #include "ngspice/ngspice.h"
45 //#include "misc.h"
46 
47 #include "ngspice/cktdefs.h"
48 //#include "util.h"
49 #include "ngspice/sperror.h"
50 
51 #include "ngspice/mif.h"
52 #include "ngspice/evt.h"
53 #include "ngspice/evtproto.h"
54 #include "ngspice/evtudn.h"
55 
56 #include "ngspice/ipc.h"
57 #include "ngspice/ipctiein.h"
58 #include "ngspice/ipcproto.h"
59 
60 #ifdef SHARED_MODULE
61 /* global flag, TRUE if callback is used */
62 extern bool wantevtdata;
63 extern void shared_send_event(int, double, double, char *, void *, int, int);
64 extern void shared_send_dict(int, int, char*, char*);
65 static void EVTshareddump(
66     CKTcircuit    *ckt,  /* The circuit structure */
67     Ipc_Anal_t    mode,  /* The analysis mode for this call */
68     double        step);  /* The sweep step for a DCTRCURVE analysis, or */
69                          /* 0.0 for DCOP and TRAN */
70 
71 static void EVTsharedsend_line(
72     int         ipc_index,         /* The index used in the dictionary */
73     double      step,              /* The analysis step */
74     void        *node_value,       /* The node value */
75     int         udn_index,         /* The user-defined node index */
76     int         mode);             /* mode (op, dc, tran) we are in */
77 #endif
78 
79 static void EVTsend_line(
80     int         ipc_index,         /* The index used in the dictionary */
81     double      step,              /* The analysis step */
82     void        *node_value,       /* The node value */
83     int         udn_index);        /* The user-defined node index */
84 
85 
86 
87 /*
88 EVTdump
89 
90 This function is called to send event-driven node data to the IPC
91 channel.  A ``mode'' argument determines how the data is located in
92 the event data structure and what data is sent.
93 
94 If the mode is DCOP, then this is necessarily the first call to
95 the function.  In this case, the set of event-driven nodes is
96 scanned to determine which should be sent.  Only nodes that are
97 not inside subcircuits are sent.  Next, the function sends
98 a ``dictionary'' of node names/types vs. node indexes.
99 Finally, the function sends the DC operating point solutions
100 for the event-driven nodes in the dictionary.
101 
102 If the mode is DCTRCURVE, it is assumed that the function has
103 already been called with mode = DCOP.  The function scans the solution
104 vector and sends data for any nodes that have changed.
105 
106 If the mode is TRAN, it is assumed that the function has already
107 been called once with mode = DCOP.  The function scans the
108 event data for nodes that have changed since the last accepted
109 analog timepoint and sends the new data.
110 
111 Note:  This function must be called BEFORE calling EVTop_save or
112 EVTaccept() so that the state of the node data structure will allow
113 it to determine what has changed.
114 */
115 
116 
117 typedef struct evtdump_s {
118     Mif_Boolean_t  send;             /* True if this node should be sent */
119     int            ipc_index;        /* Index for this node in dict sent to CAE system */
120     char           *node_name_str;   /* Node name */
121     char           *udn_type_str;    /* UDN type */
122 } evtdump_dict_t;
123 
124 
125 
EVTdump(CKTcircuit * ckt,Ipc_Anal_t mode,double step)126 void EVTdump(
127     CKTcircuit    *ckt,  /* The circuit structure */
128     Ipc_Anal_t    mode,  /* The analysis mode for this call */
129     double        step)  /* The sweep step for a DCTRCURVE analysis, or */
130                          /* 0.0 for DCOP and TRAN */
131 {
132     static evtdump_dict_t *node_dict = NULL;
133     static int            num_send_nodes;
134 
135     int         i;
136     int         j;
137     int         num_nodes;
138     int         num_modified;
139     int         index;
140 
141     char        *name;
142     int         name_len;
143 
144     Mif_Boolean_t       firstcall;
145 
146     Evt_Node_Data_t     *node_data;
147 
148     Evt_Node_t          *rhsold;
149     Evt_Node_t          **head;
150     Evt_Node_t          *here;
151 
152     Evt_Node_Info_t     **node_table;
153 
154     char                buff[10000];
155 
156     Mif_Boolean_t       equal;
157 
158 #ifdef SHARED_MODULE
159     if((! g_ipc.enabled) && (!wantevtdata))
160         return;
161     if ((!g_ipc.enabled) && (wantevtdata)) {
162         EVTshareddump(ckt, mode, step);
163         return;
164     }
165 #else
166     /* Return immediately if IPC is not enabled */
167     if(! g_ipc.enabled)
168         return;
169 #endif
170     /* Get number of event-driven nodes */
171     num_nodes = ckt->evt->counts.num_nodes;
172 
173     /* Exit immediately if no event-driven nodes in circuit */
174     if(num_nodes <= 0)
175         return;
176 
177 
178     /* Get pointers for fast access to event data */
179     node_data = ckt->evt->data.node;
180     node_table = ckt->evt->info.node_table;
181     rhsold = node_data->rhsold;
182     head = node_data->head;
183 
184 
185     /* Determine if this is the first call */
186     if(node_dict == NULL)
187         firstcall = MIF_TRUE;
188     else
189         firstcall = MIF_FALSE;
190 
191 
192     /* If this is the first call, get the dictionary info */
193     if(firstcall) {
194 
195         /* Allocate local data structure used to process nodes */
196         node_dict = TMALLOC(evtdump_dict_t, num_nodes);
197 
198         /* Loop through all nodes to determine which nodes should be sent. */
199         /* Only nodes not within subcircuits qualify. */
200 
201         num_send_nodes = 0;
202         for(i = 0; i < num_nodes; i++) {
203 
204             /* Get the name of the node. */
205             name = node_table[i]->name;
206 
207             /* If name is in a subcircuit, mark that node should not be sent */
208             /* and continue to next node. */
209             name_len = (int) strlen(name);
210             for(j = 0; j < name_len; j++) {
211                 if(name[j] == ':')
212                     break;
213             }
214             if(j < name_len) {
215                 node_dict[i].send = MIF_FALSE;
216                 continue;
217             }
218 
219             /* Otherwise, fill in info in dictionary. */
220             node_dict[i].send = MIF_TRUE;
221             node_dict[i].ipc_index = num_send_nodes;
222             node_dict[i].node_name_str = name;
223             node_dict[i].udn_type_str = g_evt_udn_info[node_table[i]->udn_index]->name;
224 
225             /* Increment the count of nodes to be sent. */
226             num_send_nodes++;
227         } /* end for */
228     } /* end if first call */
229 
230 
231     /* Exit if there are no nodes to be sent */
232     if(num_send_nodes <= 0)
233         return;
234 
235 
236     /* If this is the first call, send the dictionary */
237     if(firstcall) {
238         ipc_send_evtdict_prefix();
239         for(i = 0; i < num_nodes; i++) {
240             if(node_dict[i].send) {
241                 sprintf(buff, "%d %s %s", node_dict[i].ipc_index,
242                                           node_dict[i].node_name_str,
243                                           node_dict[i].udn_type_str);
244                 ipc_send_line(buff);
245             }
246         }
247         ipc_send_evtdict_suffix();
248     }
249 
250     /* If this is the first call, send the operating point solution */
251     /* and return. */
252     if(firstcall) {
253         ipc_send_evtdata_prefix();
254         for(i = 0; i < num_nodes; i++) {
255             if(node_dict[i].send) {
256                 EVTsend_line(node_dict[i].ipc_index,
257                              step,
258                              rhsold[i].node_value,
259                              node_table[i]->udn_index);
260             }
261         }
262         ipc_send_evtdata_suffix();
263         return;
264     }
265 
266     /* Otherwise, this must be DCTRCURVE or TRAN mode and we need to */
267     /* send only stuff that has changed since the last call. */
268     /* The determination of what to send is modeled after code in */
269     /* EVTop_save() for DCTRCURVE and EVTaccept() for TRAN. */
270 
271     if(mode == IPC_ANAL_DCTRCURVE) {
272         /* Send data prefix */
273         ipc_send_evtdata_prefix();
274         /* Loop through event nodes */
275         for(i = 0; i < num_nodes; i++) {
276             /* If dictionary indicates this node should be sent */
277             if(node_dict[i].send) {
278                 /* Locate end of node data */
279                 here = head[i];
280                 for(;;) {
281                     if(here->next)
282                         here = here->next;
283                     else
284                         break;
285                 }
286                 /* Compare entry at end of list to rhsold */
287                 g_evt_udn_info[node_table[i]->udn_index]->compare (
288                           rhsold[i].node_value,
289                           here->node_value,
290                           &equal);
291                 /* If value in rhsold is different, send it */
292                 if(!equal) {
293                     EVTsend_line(node_dict[i].ipc_index,
294                                  step,
295                                  rhsold[i].node_value,
296                                  node_table[i]->udn_index);
297                 }
298             }
299         }
300         /* Send data suffix and return */
301         ipc_send_evtdata_suffix();
302         return;
303     }
304 
305 
306     if(mode == IPC_ANAL_TRAN) {
307         /* Send data prefix */
308         ipc_send_evtdata_prefix();
309         /* Loop through list of nodes modified since last time */
310         num_modified = node_data->num_modified;
311         for(i = 0; i < num_modified; i++) {
312             /* Get the index of the node modified */
313             index = node_data->modified_index[i];
314             /* If dictionary indicates this node should be sent */
315             if(node_dict[index].send) {
316                 /* Scan through new events and send the data for each event */
317                 here = *(node_data->last_step[index]);
318                 while((here = here->next) != NULL) {
319                     EVTsend_line(node_dict[index].ipc_index,
320                                  here->step,
321                                  here->node_value,
322                                  node_table[index]->udn_index);
323                 }
324             }
325         }
326         /* Send data suffix and return */
327         ipc_send_evtdata_suffix();
328         return;
329     }
330 
331 }
332 
333 
334 
335 /*
336 EVTsend_line
337 
338 This function formats the event node data and sends it to the IPC channel.
339 */
340 
341 
EVTsend_line(int ipc_index,double step,void * node_value,int udn_index)342 static void EVTsend_line(
343     int         ipc_index,         /* The index used in the dictionary */
344     double      step,              /* The analysis step */
345     void        *node_value,       /* The node value */
346     int         udn_index)         /* The user-defined node index */
347 {
348     double dvalue;
349     char   *svalue;
350     void   *pvalue;
351     int    len;
352 
353     /* Get the data to send */
354     if(g_evt_udn_info[udn_index]->plot_val)
355         g_evt_udn_info[udn_index]->plot_val (node_value, "", &dvalue);
356     else
357         dvalue = 0.0;
358 
359     if(g_evt_udn_info[udn_index]->print_val)
360         g_evt_udn_info[udn_index]->print_val (node_value, "", &svalue);
361     else
362         svalue = "";
363 
364     if(g_evt_udn_info[udn_index]->ipc_val)
365         g_evt_udn_info[udn_index]->ipc_val (node_value, &pvalue, &len);
366     else {
367         pvalue = NULL;
368         len = 0;
369     }
370 
371     /* Send it to the IPC channel */
372     ipc_send_event(ipc_index, step, dvalue, svalue, pvalue, len);
373 }
374 
375 
376 #ifdef SHARED_MODULE
EVTshareddump(CKTcircuit * ckt,Ipc_Anal_t mode,double step)377 static void EVTshareddump(
378     CKTcircuit    *ckt,  /* The circuit structure */
379     Ipc_Anal_t    mode,  /* The analysis mode for this call */
380     double        step)  /* The sweep step for a DCTRCURVE analysis, or */
381                          /* 0.0 for DCOP and TRAN */
382 {
383     static evtdump_dict_t *node_dict = NULL;
384     static int            num_send_nodes;
385 
386     int         i;
387     int         j;
388     int         num_nodes;
389     int         num_modified;
390     int         index;
391 
392     char        *name;
393     int         name_len;
394 
395     Mif_Boolean_t       firstcall;
396 
397     Evt_Node_Data_t     *node_data;
398 
399     Evt_Node_t          *rhsold;
400     Evt_Node_t          **head;
401     Evt_Node_t          *here;
402 
403     Evt_Node_Info_t     **node_table;
404 
405     Mif_Boolean_t       equal;
406 
407     /* Get number of event-driven nodes */
408     num_nodes = ckt->evt->counts.num_nodes;
409 
410     /* Exit immediately if no event-driven nodes in circuit */
411     if (num_nodes <= 0)
412         return;
413 
414 
415     /* Get pointers for fast access to event data */
416     node_data = ckt->evt->data.node;
417     node_table = ckt->evt->info.node_table;
418     rhsold = node_data->rhsold;
419     head = node_data->head;
420 
421 
422     /* Determine if this is the first call */
423     if (node_dict == NULL)
424         firstcall = MIF_TRUE;
425     else
426         firstcall = MIF_FALSE;
427 
428 
429     /* If this is the first call, get the dictionary info */
430     if (firstcall) {
431 
432         /* Allocate local data structure used to process nodes */
433         node_dict = TMALLOC(evtdump_dict_t, num_nodes);
434 
435         /* Loop through all nodes to determine which nodes should be sent. */
436         /* Only nodes not within subcircuits qualify. */
437 
438         num_send_nodes = 0;
439         for (i = 0; i < num_nodes; i++) {
440 
441             /* Get the name of the node. */
442             name = node_table[i]->name;
443 
444             /* If name is in a subcircuit, mark that node should not be sent */
445             /* and continue to next node. */
446             name_len = (int)strlen(name);
447             for (j = 0; j < name_len; j++) {
448                 if (name[j] == ':')
449                     break;
450             }
451             if (j < name_len) {
452                 node_dict[i].send = MIF_FALSE;
453                 continue;
454             }
455 
456             /* Otherwise, fill in info in dictionary. */
457             node_dict[i].send = MIF_TRUE;
458             node_dict[i].ipc_index = num_send_nodes;
459             node_dict[i].node_name_str = name;
460             node_dict[i].udn_type_str = g_evt_udn_info[node_table[i]->udn_index]->name;
461 
462             /* Increment the count of nodes to be sent. */
463             num_send_nodes++;
464         } /* end for */
465     } /* end if first call */
466 
467         /* Exit if there are no nodes to be sent */
468     if (num_send_nodes <= 0)
469         return;
470 
471     /* If this is the first call, send the dictionary (the list of event nodes, line by line) */
472     if (firstcall) {
473         for (i = 0; i < num_nodes; i++) {
474             if (node_dict[i].send)
475                 shared_send_dict(node_dict[i].ipc_index, num_nodes, node_dict[i].node_name_str, node_dict[i].udn_type_str);
476         }
477     }
478 
479     /* If this is the first call, send the operating point solution */
480     /* and return. */
481     if (firstcall) {
482         for (i = 0; i < num_nodes; i++) {
483             if (node_dict[i].send) {
484                 EVTsharedsend_line(node_dict[i].ipc_index,
485                     step,
486                     rhsold[i].node_value,
487                     node_table[i]->udn_index,
488                     mode);
489             }
490         }
491         return;
492     }
493 
494     /* Otherwise, this must be DCTRCURVE or TRAN mode and we need to */
495     /* send only stuff that has changed since the last call. */
496     /* The determination of what to send is modeled after code in */
497     /* EVTop_save() for DCTRCURVE and EVTaccept() for TRAN. */
498 
499     if (mode == IPC_ANAL_DCTRCURVE) {
500         /* Send data prefix */
501         /* Loop through event nodes */
502         for (i = 0; i < num_nodes; i++) {
503             /* If dictionary indicates this node should be sent */
504             if (node_dict[i].send) {
505                 /* Locate end of node data */
506                 here = head[i];
507                 for (;;) {
508                     if (here->next)
509                         here = here->next;
510                     else
511                         break;
512                 }
513                 /* Compare entry at end of list to rhsold */
514                 g_evt_udn_info[node_table[i]->udn_index]->compare(
515                     rhsold[i].node_value,
516                     here->node_value,
517                     &equal);
518                 /* If value in rhsold is different, send it */
519                 if (!equal) {
520                     EVTsharedsend_line(node_dict[i].ipc_index,
521                         step,
522                         rhsold[i].node_value,
523                         node_table[i]->udn_index,
524                         mode);
525                 }
526             }
527         }
528         return;
529     }
530 
531 
532     if (mode == IPC_ANAL_TRAN) {
533         /* Loop through list of nodes modified since last time */
534         num_modified = node_data->num_modified;
535         for (i = 0; i < num_modified; i++) {
536             /* Get the index of the node modified */
537             index = node_data->modified_index[i];
538             /* If dictionary indicates this node should be sent */
539             if (node_dict[index].send) {
540                 /* Scan through new events and send the data for each event */
541                 here = *(node_data->last_step[index]);
542                 while ((here = here->next) != NULL) {
543                     EVTsharedsend_line(node_dict[index].ipc_index,
544                         here->step,
545                         here->node_value,
546                         node_table[index]->udn_index,
547                         mode);
548                 }
549             }
550         }
551         return;
552     }
553 
554 }
555 
556 /*
557 EVTsharedsend_line
558 
559 This function formats the event node data and sends it to the caller via sharedspice.c.
560 */
561 
562 
EVTsharedsend_line(int dict_index,double step,void * node_value,int udn_index,int mode)563 static void EVTsharedsend_line(
564     int         dict_index,        /* The index used in the dictionary */
565     double      step,              /* The analysis step */
566     void        *node_value,       /* The node value */
567     int         udn_index,         /* The user-defined node index */
568     int         mode)              /* the mode (op, dc, tran) we are in */
569 {
570     double dvalue;
571     char   *svalue;
572     void   *pvalue;
573     int    len;
574 
575     /* Get the data to send */
576     if (g_evt_udn_info[udn_index]->plot_val)
577         g_evt_udn_info[udn_index]->plot_val(node_value, "", &dvalue);
578     else
579         dvalue = 0.0;
580 
581     if (g_evt_udn_info[udn_index]->print_val)
582         g_evt_udn_info[udn_index]->print_val(node_value, "", &svalue);
583     else
584         svalue = "";
585 
586     if (g_evt_udn_info[udn_index]->ipc_val)
587         g_evt_udn_info[udn_index]->ipc_val(node_value, &pvalue, &len);
588     else {
589         pvalue = NULL;
590         len = 0;
591     }
592 
593     /* Send it to sharedspice.c */
594     shared_send_event(dict_index, step, dvalue, svalue, pvalue, len, mode);
595 }
596 
597 
598 #endif
599