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