diff --git a/src/include/ngspice/cmconstants.h b/src/include/ngspice/cmconstants.h index abfd45fa4..73563e14f 100644 --- a/src/include/ngspice/cmconstants.h +++ b/src/include/ngspice/cmconstants.h @@ -52,6 +52,7 @@ NON-STANDARD FEATURES #define ANALOG MIF_ANALOG #define EVENT MIF_EVENT_DRIVEN +#define STEP_PENDING MIF_STEP_PENDING #endif diff --git a/src/include/ngspice/cmproto.h b/src/include/ngspice/cmproto.h index 7ab6ffbd5..17518160c 100644 --- a/src/include/ngspice/cmproto.h +++ b/src/include/ngspice/cmproto.h @@ -97,6 +97,7 @@ int cm_message_printf(const char *fmt, ...); double cm_netlist_get_c(void); double cm_netlist_get_l(void); +void cm_irreversible(unsigned int); const char *cm_get_node_name(const char *, unsigned int); bool cm_probe_node(unsigned int, unsigned int, void *); bool cm_schedule_output(unsigned int, unsigned int, double, void *); diff --git a/src/include/ngspice/dllitf.h b/src/include/ngspice/dllitf.h index 1d9295d81..eaeef3d77 100644 --- a/src/include/ngspice/dllitf.h +++ b/src/include/ngspice/dllitf.h @@ -59,6 +59,7 @@ struct coreInfo_t { int ((*dllitf_cm_message_send)(char *)); double ((*dllitf_cm_netlist_get_c)(void)); double ((*dllitf_cm_netlist_get_l)(void)); + void ((*dllitf_cm_irreversible)(unsigned int)); const char * ((*dllitf_cm_get_node_name)(const char *, unsigned int)); bool ((*dllitf_cm_probe_node)(unsigned int, unsigned int, void *)); diff --git a/src/include/ngspice/evtproto.h b/src/include/ngspice/evtproto.h index 83066409b..ea2bd5f92 100644 --- a/src/include/ngspice/evtproto.h +++ b/src/include/ngspice/evtproto.h @@ -94,6 +94,8 @@ void EVTdequeue(CKTcircuit *ckt, double time); int EVTload(CKTcircuit *ckt, MIFinstance *inst); +int EVTload_with_event(CKTcircuit *ckt, MIFinstance *inst, Mif_Call_Type_t type); + void EVTprint(wordlist *wl); void EVTprintvcd(wordlist *wl); void EVTsave(wordlist *wl); diff --git a/src/include/ngspice/mifdefs.h b/src/include/ngspice/mifdefs.h index 801166f44..c3564e754 100644 --- a/src/include/ngspice/mifdefs.h +++ b/src/include/ngspice/mifdefs.h @@ -80,6 +80,7 @@ struct MIFinstance { Mif_Boolean_t analog; /* true if this inst is analog or hybrid type */ Mif_Boolean_t event_driven; /* true if this inst is event-driven or hybrid type */ + unsigned int irreversible; /* non-zero for special treatment */ int inst_index; /* Index into inst_table in evt struct in ckt */ Mif_Callback_t callback; /* instance callback function */ diff --git a/src/include/ngspice/miftypes.h b/src/include/ngspice/miftypes.h index aa8c6f4b5..532b884e1 100644 --- a/src/include/ngspice/miftypes.h +++ b/src/include/ngspice/miftypes.h @@ -75,6 +75,7 @@ typedef enum { typedef enum { MIF_ANALOG, /* Analog call */ MIF_EVENT_DRIVEN, /* Event-driven call */ + MIF_STEP_PENDING, /* Special event call for irreversible Code Models */ } Mif_Call_Type_t; diff --git a/src/xspice/cm/cm.c b/src/xspice/cm/cm.c index 61ba08819..8a3a283e6 100644 --- a/src/xspice/cm/cm.c +++ b/src/xspice/cm/cm.c @@ -38,6 +38,7 @@ INTERFACES cm_get_path() cm_get_circuit() + cm_irreversible() cm_get_node_name() cm_probe_node() @@ -57,6 +58,7 @@ NON-STANDARD FEATURES #include "ngspice/enh.h" #include "ngspice/mif.h" #include "ngspice/cktdefs.h" +#include "ngspice/cpextern.h" //#include "util.h" @@ -730,6 +732,100 @@ CKTcircuit *cm_get_circuit(void) return(g_mif_info.ckt); } +/* Set the "irreversible" flag on the current instance and shuffle it to the + * requested position among any other irreversibles in the hybrid_index array. + * Array entries are sorted so that non-zero values of instance->irreversible + * are decreasing: an instance with instance->irreversible == 1 is fully + * protected. + */ + +static void duplicate(MIFinstance *instance) +{ + fprintf(cp_err, + "Warning: Duplicate value %d in cm_irreversible() " + "for instance %s.\n", + instance->irreversible, instance->gen.GENname); +} + +void cm_irreversible(unsigned int place) +{ + MIFinstance *instance; + Evt_Ckt_Data_t *evt; + int num_hybrids; + MIFinstance **hybrids; + int old_index, i; + unsigned int value; + + instance = g_mif_info.instance; + if (!g_mif_info.circuit.init) { + fprintf(cp_err, + "%s: Ignoring call to cm_irreversible(): not in INIT\n", + instance->gen.GENname); + return; + } + if (instance->irreversible || place == 0) { + if (instance->irreversible != place) { + fprintf(cp_err, "%s: Ignoring new value %d in cm_irreversible()\n", + instance->gen.GENname, place); + } + return; + } + instance->irreversible = place; + + evt = g_mif_info.ckt->evt; + num_hybrids = evt->counts.num_hybrids; + hybrids = evt->info.hybrids; + + /* Already a hybrid? */ + + for (old_index = 0; old_index < num_hybrids; ++old_index) { + if (hybrids[old_index] == instance) + break; + } + + if (old_index < num_hybrids) { + /* Existing hybrid, move down, shuffling other entries up. */ + + for (i = old_index + 1; i < num_hybrids; ++i) { + value = hybrids[i]->irreversible; + if (value == 0 || value > place) { + hybrids[i - 1] = hybrids[i]; + } else if (value == place) { + duplicate(instance); + break; + } else { + break; + } + } + hybrids[i - 1] = instance; + } else { + /* Instance is not hybrid, add an entry. */ + + num_hybrids++; + hybrids = TREALLOC(MIFinstance *, hybrids, num_hybrids); + evt->counts.num_hybrids = num_hybrids; + evt->info.hybrids = hybrids; + if (hybrids == NULL) { + fprintf(cp_err, "Allocation failed in cm_irreversible()\n"); + abort(); + } + + /* Shuffle entries down. */ + + for (i = num_hybrids - 2; i >= 0; --i) { + value = hybrids[i]->irreversible; + if (value != 0 && value < place) { + hybrids[i + 1] = hybrids[i]; + } else if (value == place) { + duplicate(instance); + } else { + break; + } + } + hybrids[i + 1] = instance; + } +} + /* Get the name of a circuit node connected to a port. */ const char *cm_get_node_name(const char *port_name, unsigned int index) diff --git a/src/xspice/cm/cmexport.c b/src/xspice/cm/cmexport.c index a644cb921..eb7846363 100644 --- a/src/xspice/cm/cmexport.c +++ b/src/xspice/cm/cmexport.c @@ -58,6 +58,7 @@ struct coreInfo_t coreInfo = cm_message_send, cm_netlist_get_c, cm_netlist_get_l, + cm_irreversible, cm_get_node_name, cm_probe_node, cm_schedule_output, diff --git a/src/xspice/evt/evtcall_hybrids.c b/src/xspice/evt/evtcall_hybrids.c index 1427b746d..a45f2506e 100644 --- a/src/xspice/evt/evtcall_hybrids.c +++ b/src/xspice/evt/evtcall_hybrids.c @@ -20,10 +20,12 @@ MODIFICATIONS SUMMARY This file contains function EVTcall_hybrids which calls all models - which have both analog and event-driven ports. It is called following + which have both analog and event-driven ports or have declared + themselves to be irreversible (no back-out). It is called following successful evaluation of an analog iteration attempt to allow events to be scheduled by the hybrid models. The 'CALL_TYPE' is set - to 'EVENT_DRIVEN' when the model is called from this function. + to 'EVENT_DRIVEN' or 'STEP_PENDING' when the model is called + from this function. INTERFACES @@ -71,6 +73,7 @@ void EVTcall_hybrids( hybrids = ckt->evt->info.hybrids; /* Call EVTload for all hybrids */ + for(i = 0; i < num_hybrids; i++) - EVTload(ckt, hybrids[i]); + EVTload_with_event(ckt, hybrids[i], MIF_STEP_PENDING); } diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c index edce5e119..a0b75326a 100644 --- a/src/xspice/evt/evtload.c +++ b/src/xspice/evt/evtload.c @@ -85,6 +85,16 @@ int EVTload( CKTcircuit *ckt, /* The circuit structure */ MIFinstance *inst) /* The instance to call */ { + return EVTload_with_event(ckt, inst, MIF_EVENT_DRIVEN); +} + +/* "Internal" version, also used by EVTcall_hybrids(). */ + +int EVTload_with_event( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *inst, /* The instance to call */ + Mif_Call_Type_t type) /* Type of call (EVENT or STEP_PENDING). */ +{ int i; int j; @@ -104,8 +114,7 @@ int EVTload( /* Prepare the code model inputs */ /* ***************************** */ - /* Get pointer to instance data structure and other data */ - /* needed for fast access */ + /* Get pointer to data structure needed for fast access */ node_data = ckt->evt->data.node; @@ -124,7 +133,14 @@ int EVTload( else cm_data.circuit.time = 0.0; - cm_data.circuit.call_type = MIF_EVENT_DRIVEN; + /* Instances that have declared themselves as irreversible + * are expected to distinguish STEP_PENDING from ordinary events. + */ + + if (type == MIF_STEP_PENDING && inst->irreversible) + cm_data.circuit.call_type = MIF_STEP_PENDING; + else + cm_data.circuit.call_type = MIF_EVENT_DRIVEN; cm_data.circuit.temperature = ckt->CKTtemp - 273.15; /* Setup data needed by cm_... functions */ @@ -141,9 +157,11 @@ int EVTload( /* If after initialization and in transient analysis mode */ - /* create a new state for the instance */ + /* create a new state for the instance, */ + /* except analog-only irreversibles. */ - if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized) + if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized && + inst->inst_index >= 0) EVTcreate_state(ckt, inst->inst_index); /* Loop through all connections on the instance and setup */ diff --git a/src/xspice/icm/dlmain.c b/src/xspice/icm/dlmain.c index 87f6050f9..6c93d4c4b 100644 --- a/src/xspice/icm/dlmain.c +++ b/src/xspice/icm/dlmain.c @@ -340,6 +340,11 @@ double cm_netlist_get_l(void) { return (coreitf->dllitf_cm_netlist_get_l)(); } +void cm_irreversible(unsigned int place) +{ + (coreitf->dllitf_cm_irreversible)(place); +} + const char *cm_get_node_name(const char *port, unsigned int index) { return coreitf->dllitf_cm_get_node_name(port, index); }