From b1341c8e30b7a7dbf8b4ec0f6f4323f2191fe82b Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Fri, 8 Apr 2022 14:00:33 +0100 Subject: [PATCH] Insert XSPICE bridging devices automatically when an analogue node has the same name as an XSPICE event node (including digital nodes). The function Evtcheck_nodes() is replaced by a new version in its own file. --- src/frontend/inp.c | 11 +- src/frontend/inp.h | 6 + src/frontend/inpcom.c | 8 +- src/frontend/inpcom.h | 2 + src/frontend/runcoms2.c | 2 + src/frontend/spiceif.c | 7 +- src/include/ngspice/evtproto.h | 5 + src/include/ngspice/ftedefs.h | 1 + src/xspice/evt/Makefile.am | 3 +- src/xspice/evt/evtcheck_nodes.c | 723 ++++++++++++++++++++++++++++++++ src/xspice/evt/evtinit.c | 59 --- 11 files changed, 754 insertions(+), 73 deletions(-) create mode 100644 src/xspice/evt/evtcheck_nodes.c diff --git a/src/frontend/inp.c b/src/frontend/inp.c index 4d9a8743e..d31f272c3 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -43,13 +43,6 @@ Author: 1985 Wayne A. Christopher #include "ngspice/randnumb.h" #include "ngspice/compatmode.h" -#define line_free(line, flag) \ - do { \ - line_free_x(line, flag); \ - line = NULL; \ - } while(0) - - static struct card *com_options = NULL; static struct card *mc_deck = NULL; static struct card *recent_deck = NULL; @@ -61,7 +54,6 @@ static void dotifeval(struct card *deck); static void eval_agauss(struct card *deck, char *fcn); static wordlist *inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist *controls); -void line_free_x(struct card *deck, bool recurse); static void recifeval(struct card *pdeck); static char *upper(register char *string); static void rem_unused_mos_models(struct card* deck); @@ -188,6 +180,9 @@ com_listing(wordlist *wl) inp_list(cp_out, expand ? ft_curckt->ci_deck : ft_curckt->ci_origdeck, ft_curckt->ci_options, type); + if (expand && ft_curckt->ci_auto && type != LS_RUNNABLE) + inp_list(cp_out, ft_curckt->ci_auto, + ft_curckt->ci_options, type); } } else { fprintf(cp_err, "Error: no circuit loaded.\n"); diff --git a/src/frontend/inp.h b/src/frontend/inp.h index 2c929a05c..52d672cf6 100644 --- a/src/frontend/inp.h +++ b/src/frontend/inp.h @@ -12,5 +12,11 @@ void com_source(wordlist *wl); void com_mc_source(wordlist *wl); void com_circbyline(wordlist *wl); +void line_free_x(struct card *deck, bool recurse); +#define line_free(line, flag) \ + do { \ + line_free_x(line, flag); \ + line = NULL; \ + } while(0) #endif diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 0b67fe533..b76e7681e 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -201,9 +201,6 @@ extern void inp_probe(struct card* card); static void utf8_syntax_check(struct card *deck); #endif -struct card* insert_new_line( - struct card* card, char* line, int linenum, int linenum_orig); - struct inp_read_t { struct card *cc; int line_number; @@ -411,7 +408,10 @@ static int is_cider_model(char *buf) } #endif -/* insert a new card, just behind the given card */ +/* Insert a new card, just behind the given card. + * The new card takes ownership of the memory pointed to by "line". + */ + struct card *insert_new_line( struct card *card, char *line, int linenum, int linenum_orig) { diff --git a/src/frontend/inpcom.h b/src/frontend/inpcom.h index 60d44776d..3344f8fab 100644 --- a/src/frontend/inpcom.h +++ b/src/frontend/inpcom.h @@ -6,5 +6,7 @@ #ifndef ngspice_INPCOM_H #define ngspice_INPCOM_H +struct card *insert_new_line(struct card *card, char *line, + int linenum, int linenum_orig); #endif diff --git a/src/frontend/runcoms2.c b/src/frontend/runcoms2.c index cf91808cc..6d69717e9 100644 --- a/src/frontend/runcoms2.c +++ b/src/frontend/runcoms2.c @@ -233,6 +233,8 @@ com_remcirc(wordlist *wl) line_free(dd, TRUE); dd = ft_curckt->ci_meas; line_free(dd, TRUE); + dd = ft_curckt->ci_auto; + line_free(dd, TRUE); wl_free(ft_curckt->ci_commands); diff --git a/src/frontend/spiceif.c b/src/frontend/spiceif.c index 881532e31..737bce232 100644 --- a/src/frontend/spiceif.c +++ b/src/frontend/spiceif.c @@ -88,7 +88,6 @@ static int finddev(CKTcircuit *ckt, char *name, GENinstance **devptr, GENmodel * /* espice fix integration */ static int finddev_special(CKTcircuit *ckt, char *name, GENinstance **devptr, GENmodel **modptr, int *device_or_model); - /* Input a single deck, and return a pointer to the circuit. */ CKTcircuit * @@ -166,6 +165,12 @@ if_inpdeck(struct card *deck, INPtables **tab) /* Scan through the instance lines and parse the circuit. */ INPpas2(ckt, deck->nextcard, *tab, ft_curckt->ci_defTask); +#ifdef XSPICE + if (!Evtcheck_nodes(ckt, *tab)) { + ft_sperror(E_PRIVATE, "Evtcheck_nodes"); + return NULL; + } +#endif /* If option cshunt is given, add capacitors to each voltage node */ INPpas4(ckt, *tab); diff --git a/src/include/ngspice/evtproto.h b/src/include/ngspice/evtproto.h index 384766369..359133801 100644 --- a/src/include/ngspice/evtproto.h +++ b/src/include/ngspice/evtproto.h @@ -128,6 +128,11 @@ void EVTaccept( CKTcircuit *ckt, /* main circuit struct */ double time); /* time at which analog soln was accepted */ +struct INPtables; +bool Evtcheck_nodes( + CKTcircuit *ckt, /* The circuit structure */ + struct INPtables *stab); /* Symbol table. */ + struct dvec *EVTfindvec(char *node); #endif diff --git a/src/include/ngspice/ftedefs.h b/src/include/ngspice/ftedefs.h index f7d6681a4..471f1c9b2 100644 --- a/src/include/ngspice/ftedefs.h +++ b/src/include/ngspice/ftedefs.h @@ -40,6 +40,7 @@ struct circ { struct card *ci_mcdeck; /* The compacted input deck, used by mc_source */ struct card *ci_options; /* The .option cards from the deck... */ struct card *ci_meas; /* .measure commands to run after simulation */ + struct card *ci_auto ; /* Statements added automatically after parse */ struct card *ci_param; /* .param statements found in deck */ struct variable *ci_vars; /* ... and the parsed versions. */ bool ci_inprogress; /* We are in a break now. */ diff --git a/src/xspice/evt/Makefile.am b/src/xspice/evt/Makefile.am index 8cf641dd9..df3cbe270 100644 --- a/src/xspice/evt/Makefile.am +++ b/src/xspice/evt/Makefile.am @@ -22,7 +22,8 @@ libevtxsp_la_SOURCES = \ evtplot.c \ evtqueue.c \ evttermi.c \ - evtshared.c + evtshared.c \ + evtcheck_nodes.c AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/src/include AM_CFLAGS = $(STATIC) diff --git a/src/xspice/evt/evtcheck_nodes.c b/src/xspice/evt/evtcheck_nodes.c new file mode 100644 index 000000000..ba0fa86f4 --- /dev/null +++ b/src/xspice/evt/evtcheck_nodes.c @@ -0,0 +1,723 @@ +/* Automatic insertion of XSPICE devices bridging event and analogue nodes. + * Giles Atkinson 2022. + * The contents of this file may belong in src/frontend, except that it is + * part of XSPICE. + */ + +#include +#include + +#include "ngspice/ngspice.h" +#include "ngspice/cktdefs.h" +#include "ngspice/devdefs.h" +#include "ngspice/inpdefs.h" +#include "ngspice/sperror.h" +#include "ngspice/wordlist.h" +#include "ngspice/fteext.h" + +#include "ngspice/mif.h" +#include "ngspice/evt.h" +#include "ngspice/evtudn.h" +#include "ngspice/evtproto.h" +#include "ngspice/mif.h" +#include "../../frontend/inpcom.h" +#include "../../frontend/inp.h" +#include "../../frontend/subckt.h" +#include "../../frontend/variable.h" +#include "../../frontend/numparam/numpaif.h" +#include "../../misc/util.h" + +/* Automatic insertion of bridge devices. + +When using XSPICE to simulate digital devices in a mixed-mode simulation, +bridging devices must be included in the circuit to pass signals between the +analogue simulator and XSPICE. Such devices may be included in the netlist, +or they may can be inserted automatically. Different types of automatic +bridge may exist in the same circuit, depending on signal direction and +characteristics of the connected device, for example digital devices +powered by differing voltages. Non-digital XSPICE nodes are supported. + +Bridging may be disabled by setting command variable "auto_bridge" to zero. +Values greater than one enable diagnostic outputs. + +Automatic insertion works by checking node names; if the same name occurs in +both sides of the simulator, it indicates that a connection between +an analogue and a digital device has been specified. At this point no such +connection exists: it is created by adding the bridging device. Devices are +added by generating netlist lines describing them and then performing some +stages of ngspice's normal input processing. + +To add a bridging device, one or two lines of netlist text are created. +Typically, one specifies the device itself (an XSPICE 'A' "device card") +and the other ("setup card") is the required model definition. Alternatively, +the setup card may use .include to read in a subcircuit definition and the +device card is a subcircuit ('X' card). Setup cards are processed once, +device cards, once per bridged node. + +Inserted or included netlist lines use a restricted set of features. There +may be further includes and subcircuits, but nothing else apart from device +and model cards has been tested. Parameter and expression processing are +available, but parameters defined in the main circuit are inaccessible. +Models defined in the circuit may be used, but subcircuits are inaccessible. +These restictions arise from the fact that missing bridges are detected +after the circuit has been built. Previous parameter and subcircuit +definitions are no longer fully available. + +The device card must be a format string for the C library function sprintf() +with one %d and two %s insertion indicators. The %d insertion makes the +device name unique and the string insertions supply a list of node names +that will be bridged. Optionally a final substitution for a double may be +included that will receive the value of internal variable, vcc (see below). +The results of formatting should be an XSPICE device card or a subcircuit call. + +The setup and device cards to be used are determined by examining the +event node that is to be connected. The rules are a little complex, +but are intended to be very flexible, while easy to use in common +cases. If all XSPICE nodes are digital, using 3.3V supply and good +simulation of the device's electrical characteristics is not needed, +then nothing need be done. Other supply voltages can be handled by: + +.param vcc=5 + +or similar, and the parameter may have different values in subcircuits. +Otherwise, the full rules follow. The third rule is intended to be useful +for device libraries whose devices are defined by subcircuits. + +1: The program looks for a command interpreter variable + "auto_bridge_parm_TTTT" where TTTT is the node type string. So + "auto_bridge_parm_d" for digital nodes. The value should be the name of + a circuit parameter (.param card). If not found, ignore the next step, + except in the case of a digital node where the parameter name is assumed + to be "vcc". Initialise an internal variable, vcc, to zero. + +2: Given a parameter name, find the most deeply-nested (in subcircuits) XSPICE + device that is connected to the node. Search upwards through the enclosing + subcircuits for a definition of the parameter. If found, set vcc from + the parameter. + +3: If a command interpreter variable "no_auto_bridge_family": exists, go to + step 5. Search the connected device instances for a parameter, "Family" + with a string value. The first one found will be used. If the first + character of the value is not '*', the setup card will be ".include + FFFFF_DDD_bridge.cir" where FFFFF is the family and DDD is the signal + direction for the XSPICE device: "in", "out" or "inout". The device card + will be "Xauto_bridge%d %s %s bridge_FFFFF_DDD vcc=%g", so a suitably + parameterised subcircuit must be defined in the file. + +4: If the first character was '*', look for a variable + "auto_bridge_FFFFF_TTTT_DDD" where FFFFF is the family, TTTT is the node + type string and DDD is the direction. So this might be + "auto_bridge_74HCT_d_inout" for a digital node. Use the variable's value + in step 6. + +5: Look for a variable "auto_bridge_TTTT_DDD_NNNN" where TTTT and DDD are as + before and NNNN is the integer part of (vcc * 10). + +6: Check and use the variable's value from step 2 or 5. It must be a list + of two or three elements: the setup card, device card and an optional + third value. If present, the third value in the list must be an integer, + specifying the maximum number of nodes handled by a single device. If + not present, or zero, there is no limit. If no variable was found, the + circuit fails, except for digital nodes where defaults are + supplied. Example: + + set auto_bridge_real_out_0 = ( ".model real_output_bridge d_to_real" + "areal_bridge%d [ %s ] null [%s] delay=1e-6" ) + + (This would be on a single line and the spaces are important.) + + For digital the defaults are: + + ( ".model auto_adc adc_bridge(in_low = %g in_high = %g)" + "auto_adc%d [ %s ] [ %s ] auto_adc" ) + + and + + ( ".model auto_dac dac_bridge(out_low = 0 out_high = %g)" + "auto_dac%d [ %s ] [ %s ] auto_dac" ) + + with vcc/2.0 or vcc substituted for %g as appropriate. + + An example of an included subcircuit might be: + + * Subcircuit for digital output buffer with impedance + + .subckt auto_buf dig ana vcc=5 + .model auto_dac dac_bridge(out_low = 0 out_high = {vcc}) + auto_dac [ dig ] [ internal ] auto_dac + rint internal ana 100 + .ends + + and invoked by: + + set auto_bridge_d_out_30 = ( ".include test_sub.cir" + "xauto_buf%d %s %s auto_buf vcc=%g" + 1 ) +*/ + +/* Working information about a type of bridge. */ + +struct bridge { + int udn_index; // Node type. + Mif_Dir_t direction; // IN, OUT or INOUT + double vcc; // A .param value used for matching. + const char *family; // Alternative match parameter. + struct bridge *next; // Linked list + int max, count; // How many nodes/device? + char *setup; // For model or inclusion card. + const char *format; // For device card. + int end_index; // Free buffer space + char held[256]; // Buffer with node names. +}; + +/* Enable/verbosity level */ + +#define AB_OFF 0 +#define AB_SILENT 1 +#define AB_DECK 2 // Show generated deck + +#define BIG 999990000 // For fake linenumbers + +/* Expand .include cards and subcircuits in a deck. */ + +static struct card *expand_deck(struct card *head) +{ + struct card *card, *next; + char **pointers; + int i, dico; + + /* Save the current parameter symbol table. */ + + dico = nupa_add_dicoslist(); + + /* Count the cards, allocate and fill a pointer array. */ + + for (i = 0, card = head; card; ++i, card = card->nextcard) + ; + pointers = TMALLOC(char *, i + 1); + for (i = 0, card = head; card; ++i, card = card->nextcard) + pointers[i] = card->line; + pointers[i] = NULL; + + /* Free the card headers. */ + + for (card = head; card; card = next) { + next = card->nextcard; + tfree(card); + } + + /* The cards are passed to _inp_readall() via a global. */ + + circarray = pointers; + card = inp_readall(NULL, Infile_Path, FALSE, TRUE, NULL); + card = inp_subcktexpand(card); + + /* Destroy the parameter table that was created in subcircuit/parameter + * expansion and restore the previous version. + */ + + nupa_del_dicoS(); + nupa_set_dicoslist(dico); + nupa_rem_dicoslist(dico); + return card; +} + +/* Write a completed bridge instance card. */ + +static struct card *flush_card(struct bridge *bridge, int ln, + struct card *last) +{ + char buff[2 * sizeof bridge->held + 128]; + + bridge->held[bridge->end_index] = '\0'; + snprintf(buff, sizeof buff, bridge->format, + ln, bridge->held, bridge->held, bridge->vcc); + bridge->count = 0; + bridge->end_index = 0; + return insert_new_line(last, copy(buff), BIG + ln, 0); +} + + +/* Release memory used. */ + +static void free_bridges(struct bridge *bridge_list) +{ + struct bridge *bridge; + + while (bridge_list) { + /* Free the structure. Setup has gone. */ + + bridge = bridge_list; + bridge_list = bridge->next; + if (bridge->format) + tfree(bridge->format); + tfree(bridge); + } +} + +/* Find an XSPICE device instance given its index number. Uses link chasing + * as at this point the indexable inst_table does not yet exist. + */ + +static MIFinstance *find_inst(CKTcircuit *ckt, int index) +{ + struct Evt_Inst_Info *chase; + int i; + + for (i = 0, chase = ckt->evt->info.inst_list; + i < index && chase; + ++i, chase = chase->next) + ; + return chase ? chase->inst_ptr : NULL; +} + +/* All ports are declared as outputs, but some may be INOUT. */ + +static Mif_Dir_t scan_ports(Evt_Node_Info_t *event_node, CKTcircuit *ckt) +{ + Evt_Inst_Index_t *inst_list; + Evt_Inst_Info_t **inst_table; + + inst_table = ckt->evt->info.inst_table; + for (inst_list = event_node->inst_list; + inst_list; + inst_list = inst_list->next) { + MIFinstance *inst; + int i; + + inst = find_inst(ckt, inst_list->index); + for (i = 0; i < inst->num_conn; ++i) { + Mif_Conn_Data_t *conn; + int j; + + conn = inst->conn[i]; + if (conn->is_null || !conn->is_input) + continue; + for (j = 0; j < conn->size; ++j) { + Mif_Port_Data_t *port; + + port = conn->port[j]; + if (!strcmp(port->pos_node_str, event_node->name) || + !strcmp(port->neg_node_str, event_node->name)) { + /* An inout connection to this node. */ + + return MIF_INOUT; + } + } + } + } + return MIF_OUT; +} + +/* Examine a device and return its subcircuit nesting depth + * and possibly the value of its "family" parameter, if that exists. + */ + +static int examine_device(MIFinstance *inst, const char **family) +{ + struct MIFmodel *mp; + char *dot; + int i; + + if (!*family) { + Mif_Param_Data_t *pdp; + IFparm *pp; + int type; + + mp = MIFmodPtr(inst); + type = mp->MIFmodType; + pp = ft_sim->devices[type]->modelParms; + for (i = 0; i < inst->num_param; ++i) { + pdp = mp->param[i]; + if (!pdp->is_null && pdp->eltype == IF_STRING && + !strcmp(pp[i].keyword, "family")) { + /* Return value for "family" parameter. */ + + *family = pdp->element->svalue; // May be NULL + if (family && !family[0]) + family = NULL; // Ignore empty string. + break; + } + } + } + + for (i = 0, dot = inst->MIFname; + (dot = strchr(dot, '.')); + dot += 1, ++i) + ; + return i; +} + +/* Scan devices attached to node. Return the name of the deepest-nested + * one and the first "Family" parameter found. + */ + +static const char *scan_devices(Evt_Node_Info_t *event_node, + CKTcircuit *ckt, + const char **family) +{ + Evt_Inst_Index_t *inst_list; + MIFinstance *best_inst = NULL, *inst; + int best_depth = -1, depth, left; + + /* Scan input connections. */ + + for (inst_list = event_node->inst_list; + inst_list; + inst_list = inst_list->next) { + inst = find_inst(ckt, inst_list->index); + depth = examine_device(inst, family); + if (depth > best_depth) { + best_depth = depth; + best_inst = inst; + } + } + + if ((left = event_node->num_outputs)) { + Evt_Node_Info_t *node; + Evt_Output_Info_t *oip; + int i, my_index; + + /* Find the index of this node (not stored). */ + + for (my_index = 0, node = ckt->evt->info.node_list; + node != event_node; + ++my_index, node = node->next) + ; + + /* Scan output connections. */ + + for (i = 0, oip = ckt->evt->info.output_list; + oip; + ++i, oip = oip->next) { + if (oip->node_index == my_index) { + inst = find_inst(ckt, oip->inst_index); + depth = examine_device(inst, family); + if (depth > best_depth) { + best_depth = depth; + best_inst = inst; + } + if (--left == 0) + break; + } + } + } + return best_inst ? best_inst->MIFname : NULL; +} + +/* Can a bridge element be inserted? */ + +static struct bridge *find_bridge(Evt_Node_Info_t *event_node, + CKTcircuit *ckt, + struct bridge **bridge_list_p) +{ + static const char * const dirs[] = {"in", "out", "inout"}; + struct bridge *bridge; + Mif_Dir_t direction; + const char *format = NULL; + const char *type_name, *family, *s_family, *deep; + char *setup, *vcc_parm, *dot; + double vcc; + int max = 0, ok; + struct variable *cvar = NULL; + char buff[256]; + + /* Find the direction for this node. */ + + if (event_node->num_outputs == 0) { + direction = MIF_IN; + } else if (event_node->num_outputs < event_node->num_ports) { + direction = MIF_INOUT; + } else { + direction = scan_ports(event_node, ckt); // Ugly + } + + /* Find the vcc parameter for this node. */ + + type_name = g_evt_udn_info[event_node->udn_index]->name; + snprintf(buff, sizeof buff, "auto_bridge_parm_%s", type_name); + if (cp_getvar(buff, CP_STRING, buff, sizeof buff)) + vcc_parm = buff; + else if (event_node->udn_index == 0) + vcc_parm = "vcc"; + else + vcc_parm = NULL; + + /* Scan attached XSPICE devices for the deepest nested one and + * a device with a "family" parameter. + */ + + family = NULL; + deep = scan_devices(event_node, ckt, &family); + if (family && cp_getvar("no_auto_bridge_family", CP_BOOL, NULL, 0)) + family = NULL; + + /* Look for a real parameter (.param type) in the device's subcircuit, + * and those enclosing it. + */ + + snprintf(buff, sizeof buff, "%s", deep); + while ((dot = strrchr(buff, '.'))) { + snprintf(dot + 1, sizeof buff - (size_t)(dot - buff), vcc_parm); + vcc = nupa_get_param(buff, &ok); + if (ok) + break; + *dot = '\0'; + } + if (dot == NULL) { + vcc = nupa_get_param(vcc_parm, &ok); + if (!ok && event_node->udn_index == 0) // Fallback default for digital. + vcc = 3.3; + } else { + vcc = 0; + } + + if (family && *family == '*') { + s_family = family + 1; + family = NULL; // Not used for matching. + } else { + s_family = NULL; + } + + /* Look for an existing entry. */ + + for (bridge = *bridge_list_p; bridge; bridge = bridge->next) { + if (bridge->udn_index == event_node->udn_index && + bridge->direction == direction) { + if (family) { + if (!strcmp(family, bridge->family)) { + /* Return vcc for formatting: requires bridge->max == 1. */ + + bridge->vcc = vcc; + break; + } + } else if (bridge->vcc == vcc) { // Match vcc. + break; + } + } + } + if (bridge) + return bridge; + + /* Determine if a bridging element exists, starting with the node type. */ + + if (family) { + /* Use standard pattern for known parts family. */ + + snprintf(buff, sizeof buff, ".include %s_%s_bridge.cir", + family, dirs[direction]); + setup = copy(buff); + snprintf(buff, sizeof buff, + "Xauto_bridge%%d %%s %%s bridge_%s_%s vcc=%%g", + family, dirs[direction]); + format = copy(buff); + max = 1; + } else if (s_family) { + /* Family variable lookup. */ + + snprintf(buff, sizeof buff, "auto_bridge_%s_%s_%s", + s_family, type_name, dirs[direction]); + cp_getvar(buff, CP_LIST, &cvar, sizeof cvar); + } + + if (!format && !cvar) { + /* General variable lookup. */ + + snprintf(buff, sizeof buff, "auto_bridge_%s_%s_%d", + type_name, dirs[direction], (int)(vcc * 10)); + cp_getvar(buff, CP_LIST, &cvar, sizeof cvar); + } + if (!format && cvar) { + struct variable *v1, *v2, *v3; + + v1 = cvar->va_vlist; + if (v1 && v1->va_type == CP_STRING) { + v2 = v1->va_next; + if (v2->va_type == CP_STRING && + v2->va_string && v2->va_string[0]) { + format = copy(v2->va_string); + setup = copy(v1->va_string); + + v3 = v2->va_next; + if (v3 && v3->va_type == CP_NUM) + max = v3->va_num; + } else { + cvar = NULL; + } + } else { + cvar = NULL; + } + } + + /* Last and probably most common case, default digital bridges. */ + + if (!format && event_node->udn_index == 0) { + if (direction == MIF_INOUT) { + return NULL; // Abandon hope, for now. + } else { + if (direction == MIF_IN) { + snprintf(buff, sizeof buff, + ".model auto_adc adc_bridge(in_low = %g in_high = %g)", + vcc/2.0, vcc/2.0); + format = copy("auto_adc%d [ %s ] [ %s ] auto_adc"); + } else { // MIF_OUT +#if 0 + snprintf(buff, sizeof buff, ".include test_sub.cir"); + format = copy("xauto_buf%d %s %s auto_buf vcc=%g"); + max = 1; +#else + snprintf(buff, sizeof buff, + ".model auto_dac dac_bridge" + "(out_low = 0 out_high = %g)", + vcc); + format = copy("auto_dac%d [ %s ] [ %s ] auto_dac"); +#endif + } + setup = copy(buff); + } + } + if (!format) + return NULL; + + /* Make a new bridge structure. */ + + bridge = TMALLOC(struct bridge, 1); + bridge->udn_index = event_node->udn_index; + bridge->direction = direction; + bridge->vcc = vcc; + bridge->family = family; + bridge->next = *bridge_list_p; + *bridge_list_p = bridge; + bridge->max = max; + bridge->count = 0; + bridge->setup = setup; + bridge->format = format; + bridge->end_index = 0; + return bridge; +} + +/* Early detection of node type clashes and attempted fix by + * automatic insertion of a bridging device. + */ + +bool Evtcheck_nodes( + CKTcircuit *ckt, /* The circuit structure */ + INPtables *stab) /* Symbol table. */ +{ + struct bridge *bridge_list = NULL, *bridge; + CKTnode *analog_node; + Evt_Node_Info_t *event_node; + struct card *head = NULL, *lastcard = NULL, *card; + int ln = 0, do_expand = 0, show; + + /* Is auto-bridge enabled? */ + + if (!cp_getvar("auto_bridge", CP_NUM, &show, sizeof show)) + show = AB_SILENT; + + /* Try to create joining device if any analog node name matches + * an event node. Failure is fatal. + */ + + for (event_node = ckt->evt->info.node_list; + event_node; + event_node = event_node->next) { + for (analog_node = ckt->CKTnodes; + analog_node; + analog_node = analog_node->next) { + int nl; + + if (strcmp(event_node->name, analog_node->name) == 0) { + if (show == AB_OFF) + return FALSE; // Auto-bridge disabled + bridge = find_bridge(event_node, ckt, &bridge_list); + if (!bridge) { + /* Fatal, circuit cannot run. */ + + errMsg = tprintf("Can not insert bridge for mixed-type " + "node %s\n", + analog_node->name); + free_bridges(bridge_list); + if (head) + line_free(head, TRUE); + return FALSE; + } + + if (bridge->setup) { + /* Model/include card for bridge. + * bridge->setup must be dynamic (for tfree()). + */ + + if (!strncmp(".inc", bridge->setup, 4)) + do_expand = 1; + card = insert_new_line(lastcard, bridge->setup, + BIG + ln++, 0); + if (!lastcard) + head = card; + lastcard = card; + bridge->setup = NULL; // Output just once. + } + + /* Card buffer full? */ + + nl = (int)strlen(analog_node->name); + if ((bridge->max && bridge->count >= bridge->max) || + bridge->end_index + nl + 2 > (int)(sizeof bridge->held)) { + /* Buffer full, flush card. */ + + card = flush_card(bridge, ln++, lastcard); + if (!lastcard) + head = card; + lastcard = card; + } + + /* Copy node name to card contents buffer. */ + + bridge->count++; + if (bridge->end_index) + bridge->held[bridge->end_index++] = ' '; + strcpy(bridge->held + bridge->end_index, analog_node->name); + bridge->end_index += nl; + } + } + } + + /* Flush cards. */ + + for (bridge = bridge_list; bridge; bridge = bridge->next) { + if (bridge->end_index > 0) { + card = flush_card(bridge, ln++, lastcard); + if (!lastcard) + head = card; + lastcard = card; + } + } + + if (!lastcard) + return TRUE; // No success, but also no failures - nothing to bridge. + + if (show >= AB_DECK) { + for (card = head; card; card = card->nextcard) + printf("%d: %s\n", card->linenum, card->line); + } + + /* If there are any .include cards, expand them. */ + + if (do_expand) { + head = expand_deck(head); + if (!head) + return FALSE; + } + + /* Push the cards into the circuit. */ + + INPpas1(ckt, head, stab); + INPpas2(ckt, head, stab, ft_curckt->ci_defTask); + + /* Store them so that they show in "listing e". */ + + ft_curckt->ci_auto = head; + + free_bridges(bridge_list); + return TRUE; +} diff --git a/src/xspice/evt/evtinit.c b/src/xspice/evt/evtinit.c index c619ab4e8..1f2b1a041 100644 --- a/src/xspice/evt/evtinit.c +++ b/src/xspice/evt/evtinit.c @@ -48,8 +48,6 @@ NON-STANDARD FEATURES #include "ngspice/evtproto.h" - -static int EVTcheck_nodes(CKTcircuit *ckt); static int EVTcount_hybrids(CKTcircuit *ckt); static int EVTinit_info(CKTcircuit *ckt); static int EVTinit_queue(CKTcircuit *ckt); @@ -87,9 +85,6 @@ int EVTinit( int err; /* SPICE error return code 0 = OK */ - /* static char *err_no_hybrids = "ERROR - no hybrids found in input deck";*/ - - /* Exit immediately if there are no event-driven instances */ /* but don't complain */ if(ckt->evt->counts.num_insts == 0) @@ -100,20 +95,6 @@ int EVTinit( if(err) return(err); - /* Exit with error if there are no hybrids in the circuit. */ - /* Will probably remove this restriction later... */ -/* - if(ckt->evt->counts.num_hybrids == 0) { - errMsg = TMALLOC(char, strlen(err_no_hybrids) + 1); - strcpy(errMsg, err_no_hybrids); - return(E_PRIVATE); - } -*/ - /* Check that event nodes have not been used as analog nodes also */ - err = EVTcheck_nodes(ckt); - if(err) - return(err); - /* Create info table arrays */ err = EVTinit_info(ckt); if(err) @@ -193,46 +174,6 @@ static int EVTcount_hybrids( } -/* -EVTcheck_nodes - -Report error if any event node name is also used as an analog node. -*/ - - -static int EVTcheck_nodes( - CKTcircuit *ckt) /* The circuit structure */ -{ - - CKTnode *analog_node; - Evt_Node_Info_t *event_node; - - static char *err_prefix = "ERROR - node "; - static char *err_collide = " cannot be both analog and digital"; - - - /* Report error if any analog node name matches any event node name */ - event_node = ckt->evt->info.node_list; - while(event_node) { - analog_node = ckt->CKTnodes; - while(analog_node) { - if(strcmp(event_node->name, analog_node->name) == 0) { - errMsg = tprintf("%s%s%s", err_prefix, - event_node->name, - err_collide); - fprintf(stdout, "%s\n", errMsg); - return(E_PRIVATE); - } - analog_node = analog_node->next; - } - event_node = event_node->next; - } - - /* Return */ - return(OK); -} - - /* EVTinit_info