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.
This commit is contained in:
parent
07bb809158
commit
b1341c8e30
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue