diff --git a/src/frontend/com_dl.c b/src/frontend/com_dl.c new file mode 100755 index 000000000..b219393d5 --- /dev/null +++ b/src/frontend/com_dl.c @@ -0,0 +1,24 @@ +#include /* for wl */ +#include "ftedefs.h" +#include /* solve deps in dev.h*/ +#include <../spicelib/devices/dev.h> /*for load library commands*/ + +#ifdef XSPICE +void com_codemodel(wordlist *wl){ + wordlist *ww; + for(ww = wl;ww;ww = ww->wl_next) + if(load_opus(wl->wl_word)) + fprintf(cp_err,"Error: Library %s couldn't be loaded!\n",ww->wl_word); + return; +} +#endif +#ifdef DEVLIB +void com_use(wordlist *wl){ + wordlist *ww; + for(ww = wl;ww;ww = ww->wl_next) + if(load_dev(wl->wl_word)) + fprintf(cp_err,"Error: Library %s couldn't be loaded!\n",ww->wl_word); + return; +} +#endif + diff --git a/src/frontend/com_dl.h b/src/frontend/com_dl.h new file mode 100755 index 000000000..e8210cefd --- /dev/null +++ b/src/frontend/com_dl.h @@ -0,0 +1,12 @@ +#ifndef _COM_DL_H +#define _COM_DL_H 1 + +#ifdef XSPICE +void com_codemodel(wordlist *wl); +#endif + +#ifdef DEVLIB +void com_use(wordlist *wl); +#endif + +#endif diff --git a/src/include/cluster.h b/src/include/cluster.h new file mode 100755 index 000000000..6e4dde2d6 --- /dev/null +++ b/src/include/cluster.h @@ -0,0 +1,24 @@ +#ifndef _CLUSTER_H_ +#define _CLUSTER_H_ +#include + +/* Cluster definitions */ +#define PORT 1234 +#define TIME_PORT 1235 +#define DOMAIN_NAME "cluster.multigig" +#define CLUSTER_WIDTH 4 +#define TIME_HOST "time.cluster.multigig" +/* does all the setups */ +extern int CLUsetup(CKTcircuit *ckt); + +/* reads input pipes and sets voltages*/ +/* call each time the present time is changed, ie just before NIinter*/ +extern int CLUinput(CKTcircuit *ckt); + +/* call after each accepted timestep, ie CKTdump */ +extern int CLUoutput(CKTcircuit *ckt); + + +/* the time step control */ +extern int CLUsync(double time,double *delta, int error); +#endif diff --git a/src/include/cm.h b/src/include/cm.h new file mode 100755 index 000000000..8590f50b9 --- /dev/null +++ b/src/include/cm.h @@ -0,0 +1,49 @@ +#ifndef CM_DEFINED +#define CM_DEFINED + +/* =========================================================================== +FILE CM.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file is includes all include data in the CM package. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "cmtypes.h" +#include "cmconstants.h" // K.A. wrong name +//#include "Cmconsta.h" +#include "cmproto.h" +#include "mifcmdat.h" + + +#endif /* CM_DEFINED */ diff --git a/src/include/cmconstants.h b/src/include/cmconstants.h new file mode 100755 index 000000000..13d9146be --- /dev/null +++ b/src/include/cmconstants.h @@ -0,0 +1,57 @@ +#ifndef CMCONSTANTS_DEFINED +#define CMCONSTANTS_DEFINED + +/* =========================================================================== +FILE CMconstants.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains constants used by code models. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "miftypes.h" + +/***** Define Constants *******************************************/ + +#define FALSE 0 +#define TRUE 1 + +#define DC MIF_DC +#define AC MIF_AC +#define TRANSIENT MIF_TRAN + +#define ANALOG MIF_ANALOG +#define EVENT MIF_EVENT_DRIVEN + + +#endif /* CMCONSTANTS_DEFINED */ diff --git a/src/include/cmproto.h b/src/include/cmproto.h new file mode 100755 index 000000000..9f8baf5ed --- /dev/null +++ b/src/include/cmproto.h @@ -0,0 +1,95 @@ +#ifndef CMPROTO_DEFINED +#define CMPROTO_DEFINED + +/* =========================================================================== +FILE CMproto.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Jeff Murray, Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains ANSI C function prototypes for cm_xxx functions + called by code models. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* Prototypes for functions used by internal code models */ +/* The actual functions reside in ../ICM/CMutil.c */ +/* 12/17/90 */ + + +#include "cmtypes.h" + + +void cm_climit_fcn(double in, double in_offset, double cntl_upper, + double cntl_lower, double lower_delta, + double upper_delta, double limit_range, + double gain, int percent, double *out_final, + double *pout_pin_final, double *pout_pcntl_lower_final, + double *pout_pcntl_upper_final); + + + +void cm_smooth_corner(double x_input, double x_center, double y_center, + double domain, double lower_slope, double upper_slope, + double *y_output, double *dy_dx); +void cm_smooth_discontinuity(double x_input, double x_lower, double y_lower, + double x_upper, double y_upper, + double *y_output, double *dy_dx); +double cm_smooth_pwl(double x_input, double *x, double *y, int size, + double input_domain, double *dout_din); + +double cm_analog_ramp_factor(void); +void *cm_analog_alloc(int tag, int bytes); +void *cm_analog_get_ptr(int tag, int timepoint); +int cm_analog_integrate(double integrand, double *integral, double *partial); +int cm_analog_converge(double *state); +int cm_analog_set_temp_bkpt(double time); +int cm_analog_set_perm_bkpt(double time); +void cm_analog_not_converged(void); +void cm_analog_auto_partial(void); + +void *cm_event_alloc(int tag, int bytes); +void *cm_event_get_ptr(int tag, int timepoint); +int cm_event_queue(double time); + +char *cm_message_get_errmsg(void); +int cm_message_send(char *msg); + +double cm_netlist_get_c(void); +double cm_netlist_get_l(void); + +Complex_t cm_complex_set(double real, double imag); +Complex_t cm_complex_add(Complex_t x, Complex_t y); +Complex_t cm_complex_subtract(Complex_t x, Complex_t y); +Complex_t cm_complex_multiply(Complex_t x, Complex_t y); +Complex_t cm_complex_divide(Complex_t x, Complex_t y); + +#endif /* CMPROTO_DEFINED */ diff --git a/src/include/cmtypes.h b/src/include/cmtypes.h new file mode 100755 index 000000000..372e0aa04 --- /dev/null +++ b/src/include/cmtypes.h @@ -0,0 +1,71 @@ +#ifndef CMTYPES_DEFINED +#define CMTYPES_DEFINED + +/* =========================================================================== +FILE CMtypes.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Jeff Murray, Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains type definitions used by code models. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "miftypes.h" + +/***** Define Typedefs ********************************************/ + +typedef int Boolean_t; + +typedef Mif_Complex_t Complex_t; + + +typedef enum { + ZERO, /* Normally referenced as 0 */ + ONE, /* Normally referenced as 1 */ + UNKNOWN, /* Unknown */ +} Digital_State_t; + +typedef enum { + STRONG, /* strong */ + RESISTIVE, /* resistive */ + HI_IMPEDANCE, /* high impedance */ + UNDETERMINED, /* unknown strength */ +} Digital_Strength_t; + +typedef struct { + Digital_State_t state; + Digital_Strength_t strength; +} Digital_t; + + + +#endif /* CMTYPES_DEFINED */ diff --git a/src/include/dllitf.h b/src/include/dllitf.h new file mode 100755 index 000000000..716b13da6 --- /dev/null +++ b/src/include/dllitf.h @@ -0,0 +1,79 @@ +/* + DLL load interface + (c)2000 Arpad Buermen +*/ + +#ifndef __DLLITF_H +#define __DLLITF_H + +#include "mifproto.h" +#include "cmproto.h" + + +// This structure contains pointers to core SPICE OPUS functions used in CMs and UDNs. +// A pointer to this structure is passed to the dll when the dll is loaded. + +struct coreInfo_t { + // MIF stuff + void ((*dllitf_MIF_INP2A)(void *, INPtables *, card *)); + char * ((*dllitf_MIFgetMod)(void *, char *, INPmodel **, INPtables *)); + IFvalue * ((*dllitf_MIFgetValue)(void *, char **, int, INPtables *, char **)); + int ((*dllitf_MIFsetup)(SMPmatrix *, GENmodel *, CKTcircuit *, int *)); + int ((*dllitf_MIFunsetup)(GENmodel *, CKTcircuit *)); + int ((*dllitf_MIFload)(GENmodel *, CKTcircuit *)); + int ((*dllitf_MIFmParam)(int, IFvalue *, GENmodel *)); + int ((*dllitf_MIFask)(CKTcircuit *, GENinstance *, int, IFvalue *, IFvalue *)); + int ((*dllitf_MIFmAsk)(CKTcircuit *, GENmodel *, int, IFvalue *)); + int ((*dllitf_MIFtrunc)(GENmodel *, CKTcircuit *, double *)); + int ((*dllitf_MIFconvTest)(GENmodel *, CKTcircuit *)); + int ((*dllitf_MIFdelete)(GENmodel *, IFuid, GENinstance **)); + int ((*dllitf_MIFmDelete)(GENmodel **, IFuid, GENmodel *)); + void ((*dllitf_MIFdestroy)(GENmodel **)); + char * ((*dllitf_MIFgettok)(char **)); + char * ((*dllitf_MIFget_token)(char **, Mif_Token_Type_t *)); + Mif_Cntl_Src_Type_t ((*dllitf_MIFget_cntl_src_type)(Mif_Port_Type_t, Mif_Port_Type_t)); + char * ((*dllitf_MIFcopy)(char *)); + // CM stuff + void ((*dllitf_cm_climit_fcn)(double, double, double, double, double, double, + double, double, int, double *, double *, double *, + double *)); + void ((*dllitf_cm_smooth_corner)(double, double, double, double, double, double, + double *, double *)); + void ((*dllitf_cm_smooth_discontinuity)(double, double, double, double, double, + double *, double *)); + double ((*dllitf_cm_smooth_pwl)(double, double *, double *, int, double, double *)); + double ((*dllitf_cm_analog_ramp_factor)(void)); + void * ((*dllitf_cm_analog_alloc)(int, int)); + void * ((*dllitf_cm_analog_get_ptr)(int, int)); + int ((*dllitf_cm_analog_integrate)(double, double *, double *)); + int ((*dllitf_cm_analog_converge)(double *)); + int ((*dllitf_cm_analog_set_temp_bkpt)(double)); + int ((*dllitf_cm_analog_set_perm_bkpt)(double)); + void ((*dllitf_cm_analog_not_converged)(void)); + void ((*dllitf_cm_analog_auto_partial)(void)); + void * ((*dllitf_cm_event_alloc)(int, int)); + void * ((*dllitf_cm_event_get_ptr)(int, int)); + int ((*dllitf_cm_event_queue)(double)); + char * ((*dllitf_cm_message_get_errmsg)(void)); + int ((*dllitf_cm_message_send)(char *)); + double ((*dllitf_cm_netlist_get_c)(void)); + double ((*dllitf_cm_netlist_get_l)(void)); + Complex_t ((*dllitf_cm_complex_set)(double, double)); + Complex_t ((*dllitf_cm_complex_add)(Complex_t, Complex_t)); + Complex_t ((*dllitf_cm_complex_subtract)(Complex_t, Complex_t)); + Complex_t ((*dllitf_cm_complex_multiply)(Complex_t, Complex_t)); + Complex_t ((*dllitf_cm_complex_divide)(Complex_t, Complex_t)); + FILE * ((*dllitf_cm_stream_out)(void)); + FILE * ((*dllitf_cm_stream_in)(void)); + FILE * ((*dllitf_cm_stream_err)(void)); + /*Other stuff*/ + void * ((*dllitf_malloc_pj)(size_t)); + void * ((*dllitf_calloc_pj)(size_t, size_t)); + void * ((*dllitf_realloc_pj)(void *, size_t)); + void ((*dllitf_free_pj)(void *)); + char * ((*dllitf_tmalloc)(int)); + char * ((*dllitf_trealloc)(char *, int)); + void ((*dllitf_txfree)(char *)); +}; + +#endif diff --git a/src/include/enh.h b/src/include/enh.h new file mode 100755 index 000000000..18108d4be --- /dev/null +++ b/src/include/enh.h @@ -0,0 +1,109 @@ +#ifndef ENH_HEADER +#define ENH_HEADER x + +/* =========================================================================== +FILE ENH.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains typedefs used by the event-driven algorithm. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "miftypes.h" +#include "fteinp.h" + +/* +The following data is used in implementing various enhancements made to the +simulator. The main struct is dynamically allocated in ckt so that incremental additions +can be made more easily without the need to recompile multiple modules. +Allocation and initialization is done in CKTinit.c which should be the only +module needed to recompile after additions are made here. +*/ + + +typedef enum { + ENH_ANALOG_NODE, /* An analog node */ + ENH_EVENT_NODE, /* An event-driven node */ + ENH_ANALOG_BRANCH, /* A branch current */ + ENH_ANALOG_INSTANCE, /* An analog instance */ + ENH_EVENT_INSTANCE, /* An event-driven instance */ + ENH_HYBRID_INSTANCE, /* A hybrid (analog/event-driven) instance */ +} Enh_Conv_Source_t; + + +typedef struct { + double current; /* The current dynamic breakpoint time */ + double last; /* The last used dynamic breakpoint time */ +} Enh_Bkpt_t; + +typedef struct { + double ramptime; /* supply ramping time specified on .options */ +} Enh_Ramp_t; + +typedef struct { + Mif_Boolean_t last_NIiter_call; /* True if this is the last call to NIiter() */ + Mif_Boolean_t report_conv_probs; /* True if conv test functions should send debug info */ +} Enh_Conv_Debug_t; + + +typedef struct { + Mif_Boolean_t enabled; /* True if convergence limiting enabled on code models */ + double abs_step; /* Minimum limiting step size */ + double step; /* Fractional step amount */ +} Enh_Conv_Limit_t; + + +typedef struct { + Mif_Boolean_t enabled; /* True if rshunt option used */ + double gshunt; /* 1.0 / rshunt */ + int num_nodes; /* Number of nodes in matrix */ + double **diag; /* Pointers to matrix diagonals */ +} Enh_Rshunt_t; + + +typedef struct { + Enh_Bkpt_t breakpoint; /* Data used by dynamic breakpoints */ + Enh_Ramp_t ramp; /* New options added to simulator */ + Enh_Conv_Debug_t conv_debug; /* Convergence debug info dumping data */ + Enh_Conv_Limit_t conv_limit; /* Convergence limiting info */ + Enh_Rshunt_t rshunt_data; /* Shunt conductance from nodes to ground */ +} Enh_Ckt_Data_t; + + + +void ENHreport_conv_prob(Enh_Conv_Source_t type, char *name, char *msg); +struct line *ENHtranslate_poly(struct line *deck); + + +#endif /* ENH_HEADER */ diff --git a/src/include/evt.h b/src/include/evt.h new file mode 100755 index 000000000..1a5da1008 --- /dev/null +++ b/src/include/evt.h @@ -0,0 +1,371 @@ +#ifndef EVT_HEADER +#define EVT_HEADER x + +/* =========================================================================== +FILE EVT.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of the evt data structure and all + its substructures. The single evt structure is housed inside of + the main 3C1 circuit structure 'ckt' and contains virtually all + information about the event-driven simulation. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "mifdefs.h" +#include "mifcmdat.h" +#include "miftypes.h" + + + +/* ************** */ +/* Info structure */ +/* ************** */ + + +typedef struct Evt_Output_Info_s { + struct Evt_Output_Info_s *next; /* the next in the linked list */ + int node_index; /* index into node info struct for this output */ + int output_subindex; /* index into output data in node data struct */ + int inst_index; /* Index of instance the port is on */ + int port_index; /* Index of port the output corresponds to */ +} Evt_Output_Info_t; + +typedef struct Evt_Port_Info_s { + struct Evt_Port_Info_s *next; /* the next in the linked list of node info */ + int inst_index; /* Index of instance the port is on */ + int node_index; /* index of node the port is connected to */ + char *node_name; /* name of node port is connected to */ + char *inst_name; /* instance name */ + char *conn_name; /* connection name on instance */ + int port_num; /* port number of instance connector */ +} Evt_Port_Info_t; + +typedef struct Evt_Inst_Index_s { + struct Evt_Inst_Index_s *next; /* the next in the linked list */ + int index; /* the value of the index */ +} Evt_Inst_Index_t; + +typedef struct Evt_Node_Info_s { + struct Evt_Node_Info_s *next; /* the next in the linked list */ + char *name; /* Name of node in deck */ + int udn_index; /* Index of the node type */ + Mif_Boolean_t invert; /* True if need to make inverted copy */ + int num_ports; /* Number of ports connected to this node */ + int num_outputs; /* Number of outputs connected to this node */ + int num_insts; /* The number of insts receiving node as input */ + Evt_Inst_Index_t *inst_list; /* Linked list of indexes of these instances */ +} Evt_Node_Info_t; + +typedef struct Evt_Inst_Info_s { + struct Evt_Inst_Info_s *next; /* the next in the linked list of node info */ + MIFinstance *inst_ptr; /* Pointer to MIFinstance struct for this instance */ +} Evt_Inst_Info_t; + +typedef struct { + Evt_Inst_Info_t *inst_list; /* static info about event/hybrid instances */ + Evt_Node_Info_t *node_list; /* static info about event nodes */ + Evt_Port_Info_t *port_list; /* static info about event ports */ + Evt_Output_Info_t *output_list; /* static info about event outputs */ + int *hybrid_index; /* vector of inst indexs for hybrids */ + Evt_Inst_Info_t **inst_table; /* vector of pointers to elements in inst_list */ + Evt_Node_Info_t **node_table; /* vector of pointers to elements in node_list */ + Evt_Port_Info_t **port_table; /* vector of pointers to elements in port_list */ + Evt_Output_Info_t **output_table; /* vector of pointers to elements in output_list */ +} Evt_Info_t; + + + + + + + + +/* *************** */ +/* Queue structure */ +/* *************** */ + + + +typedef struct Evt_Inst_Event_s { + struct Evt_Inst_Event_s *next; /* the next in the linked list */ + double event_time; /* Time for this event to happen */ + double posted_time; /* Time at which event was entered in queue */ +} Evt_Inst_Event_t; + +typedef struct { + Evt_Inst_Event_t **head; /* Beginning of linked lists */ + Evt_Inst_Event_t ***current; /* Beginning of pending events */ + Evt_Inst_Event_t ***last_step; /* Values of 'current' at last accepted timepoint */ + Evt_Inst_Event_t **free; /* Linked lists of items freed by backups */ + double last_time; /* Time at which last_step was set */ + double next_time; /* Earliest next event time in queue */ + int num_modified; /* Number modified since last accepted timepoint */ + int *modified_index; /* Indexes of modified instances */ + Mif_Boolean_t *modified; /* Flags used to prevent multiple entries */ + int num_pending; /* Count of number of pending events in lists */ + int *pending_index; /* Indexes of pending events */ + Mif_Boolean_t *pending; /* Flags used to prevent multiple entries */ + int num_to_call; /* Count of number of instances that need to be called */ + int *to_call_index; /* Indexes of instances to be called */ + Mif_Boolean_t *to_call; /* Flags used to prevent multiple entries */ +} Evt_Inst_Queue_t; + + + + +typedef struct { + int num_to_eval; /* Count of number of nodes that need to be evaluated */ + int *to_eval_index; /* Indexes of nodes to be evaluated */ + Mif_Boolean_t *to_eval; /* Flags used to prevent multiple entries */ + int num_changed; /* Count of number of nodes that changed */ + int *changed_index; /* Indexes of nodes that changed */ + Mif_Boolean_t *changed; /* Flags used to prevent multiple entries */ +} Evt_Node_Queue_t; + + + + +typedef struct Evt_Output_Event_s { + struct Evt_Output_Event_s *next; /* the next in the linked list */ + double event_time; /* Time for this event to happen */ + double posted_time; /* Time at which event was entered in queue */ + Mif_Boolean_t removed; /* True if event has been deactivated */ + double removed_time; /* Time at which event was deactivated */ + void *value; /* The delayed value sent to this output */ +} Evt_Output_Event_t; + +typedef struct { + Evt_Output_Event_t **head; /* Beginning of linked lists */ + Evt_Output_Event_t ***current; /* Beginning of pending events */ + Evt_Output_Event_t ***last_step; /* Values of 'current' at last accepted timepoint */ + Evt_Output_Event_t **free; /* Linked lists of items freed by backups */ + double last_time; /* Time at which last_step was set */ + double next_time; /* Earliest next event time in queue */ + int num_modified; /* Number modified since last accepted timepoint */ + int *modified_index; /* Indexes of modified outputs */ + Mif_Boolean_t *modified; /* Flags used to prevent multiple entries */ + int num_pending; /* Count of number of pending events in lists */ + int *pending_index; /* Indexes of pending events */ + Mif_Boolean_t *pending; /* Flags used to prevent multiple entries */ + int num_changed; /* Count of number of outputs that changed */ + int *changed_index; /* Indexes of outputs that changed */ + Mif_Boolean_t *changed; /* Flags used to prevent multiple entries */ +} Evt_Output_Queue_t; + + + + +typedef struct { + Evt_Inst_Queue_t inst; /* dynamic queue for instances */ + Evt_Node_Queue_t node; /* dynamic queue of changing nodes */ + Evt_Output_Queue_t output; /* dynamic queue of delayed outputs */ +} Evt_Queue_t; + + + + +/* ************** */ +/* Data structure */ +/* ************** */ + + + + +typedef struct Evt_Node_s { + struct Evt_Node_s *next; /* pointer to next in linked list */ + Mif_Boolean_t op; /* true if computed from op analysis */ + double step; /* DC step or time at which data was computed */ + void **output_value; /* Array of outputs posted to this node */ + void *node_value; /* Resultant computed from output values */ + void *inverted_value; /* Inverted copy of node_value */ +} Evt_Node_t; + +typedef struct { + Evt_Node_t **head; /* Beginning of linked lists */ + Evt_Node_t ***tail; /* Location of last item added to list */ + Evt_Node_t ***last_step; /* 'tail' at last accepted timepoint */ + Evt_Node_t **free; /* Linked lists of items freed by backups */ + int num_modified; /* Number modified since last accepted timepoint */ + int *modified_index; /* Indexes of modified nodes */ + Mif_Boolean_t *modified; /* Flags used to prevent multiple entries */ + Evt_Node_t *rhs; /* Location where model outputs are placed */ + Evt_Node_t *rhsold; /* Location where model inputs are retrieved */ + double *total_load; /* Location where total load inputs are retrieved */ +} Evt_Node_Data_t; + + + + +typedef struct Evt_State_s { + struct Evt_State_s *next; /* Pointer to next state */ + struct Evt_State_s *prev; /* Pointer to previous state */ + double step; /* Time at which state was assigned (0 for DC) */ + void *block; /* Block of memory holding all states on inst */ +} Evt_State_t; + + +typedef struct Evt_State_Desc_s { + struct Evt_State_Desc_s *next; /* Pointer to next description */ + int tag; /* Tag for this state */ + int size; /* Size of this state */ + int offset; /* Offset of this state into the state block */ +} Evt_State_Desc_t; + + +typedef struct { + Evt_State_t **head; /* Beginning of linked lists */ + Evt_State_t ***tail; /* Location of last item added to list */ + Evt_State_t ***last_step; /* 'tail' at last accepted timepoint */ + Evt_State_t **free; /* Linked lists of items freed by backups */ + int num_modified; /* Number modified since last accepted timepoint */ + int *modified_index; /* List of indexes modified */ + Mif_Boolean_t *modified; /* Flags used to prevent multiple entries */ + int *total_size; /* Total bytes for all states allocated */ + Evt_State_Desc_t **desc; /* Lists of description structures */ +} Evt_State_Data_t; + + + + +typedef struct Evt_Msg_s { + struct Evt_Msg_s *next; /* Pointer to next state */ + Mif_Boolean_t op; /* true if output from op analysis */ + double step; /* DC step or time at which message was output */ + char *text; /* The value of the message text */ + int port_index; /* The index of the port from which the message came */ +} Evt_Msg_t; + + +typedef struct { + Evt_Msg_t **head; /* Beginning of linked lists */ + Evt_Msg_t ***tail; /* Location of last item added to list */ + Evt_Msg_t ***last_step; /* 'tail' at last accepted timepoint */ + Evt_Msg_t **free; /* Linked lists of items freed by backups */ + int num_modified; /* Number modified since last accepted timepoint */ + int *modified_index; /* List of indexes modified */ + Mif_Boolean_t *modified; /* Flags used to prevent multiple entries */ +} Evt_Msg_Data_t; + + +typedef struct { + int op_alternations; /* Total alternations between event and analog */ + int op_load_calls; /* Total load calls in DCOP analysis */ + int op_event_passes; /* Total passes through event iteration loop */ + int tran_load_calls; /* Total inst calls in transient analysis */ + int tran_time_backups; /* Number of transient timestep cuts */ +} Evt_Statistic_t; + + + + +typedef struct { + Evt_Node_Data_t *node; /* dynamic event solution vector */ + Evt_State_Data_t *state; /* dynamic event instance state data */ + Evt_Msg_Data_t *msg; /* dynamic event message data */ + Evt_Statistic_t *statistics; /* Statistics for events, etc. */ +} Evt_Data_t; + + + +/* **************** */ +/* Counts structure */ +/* **************** */ + + +typedef struct { + int num_insts; /* number of event/hybrid instances parsed */ + int num_hybrids; /* number of hybrids parsed */ + int num_hybrid_outputs; /* number of outputs on all hybrids parsed */ + int num_nodes; /* number of event nodes parsed */ + int num_ports; /* number of event ports parsed */ + int num_outputs; /* number of event outputs parsed */ +} Evt_Count_t; + + + +/* **************** */ +/* Limits structure */ +/* **************** */ + + +typedef struct { + int max_event_passes; /* maximum loops in attempting convergence of event nodes */ + int max_op_alternations; /* maximum loops through event/analog alternation */ +} Evt_Limit_t; + + +/* ************** */ +/* Jobs structure */ +/* ************** */ + + +typedef struct { + int num_jobs; /* Number of jobs run */ + char **job_name; /* Names of different jobs */ + Evt_Node_Data_t **node_data; /* node_data for different jobs */ + Evt_State_Data_t **state_data; /* state_data for different jobs */ + Evt_Msg_Data_t **msg_data; /* messages for different jobs */ + Evt_Statistic_t **statistics; /* Statistics for different jobs */ +} Evt_Job_t; + + + +/* ***************** */ +/* Options structure */ +/* ***************** */ + + +typedef struct { + Mif_Boolean_t op_alternate; /* Alternate analog/event solutions in OP analysis */ +} Evt_Option_t; + + +/* ****************** */ +/* Main evt structure */ +/* ****************** */ + +typedef struct { + Evt_Count_t counts; /* Number of insts, nodes, etc. */ + Evt_Info_t info; /* Static info about insts, etc. */ + Evt_Queue_t queue; /* Dynamic queued events */ + Evt_Data_t data; /* Results and state data */ + Evt_Limit_t limits; /* Iteration limits, etc. */ + Evt_Job_t jobs; /* Data held from multiple job runs */ + Evt_Option_t options; /* Data input on .options cards */ +} Evt_Ckt_Data_t; + + + +#endif /* EVT_HEADER */ diff --git a/src/include/evtproto.h b/src/include/evtproto.h new file mode 100755 index 000000000..104814472 --- /dev/null +++ b/src/include/evtproto.h @@ -0,0 +1,124 @@ +#ifndef EVTPROTO_HEADER +#define EVTPROTO_HEADER x + +/* =========================================================================== +FILE EVTproto.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains ANSI C function prototypes for functions + in the event-driven simulation algorithm package. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "cktdefs.h" +#include "cpstd.h" +#include "mifdefs.h" +#include "ipc.h" + + +/* ******************* */ +/* Function Prototypes */ +/* ******************* */ + + +int EVTinit(CKTcircuit *ckt); +/*int EVTinit2(CKTcircuit *ckt);*/ + +void EVTtermInsert( + CKTcircuit *ckt, + MIFinstance *fast, + char *node_name, + char *type_name, + int conn_num, + int port_num, + char **err_msg); + +int EVTsetup(CKTcircuit *ckt); + +int EVTiter(CKTcircuit *ckt); + +void EVTbackup(CKTcircuit *ckt, double new_time); + +double EVTnext_time(CKTcircuit *ckt); + +void EVTqueue_output( + CKTcircuit *ckt, + int output_index, + int udn_index, + Evt_Output_Event_t *new_event, + double posted_time, + double event_time); + + +void EVTqueue_inst( + CKTcircuit *ckt, + int inst_index, + double posted_time, + double event_time); + +void EVTdequeue(CKTcircuit *ckt, double time); + +int EVTload(CKTcircuit *ckt, int inst_index); + +void EVTprint(wordlist *wl); + +int EVTop( + CKTcircuit *ckt, + long firstmode, + long continuemode, + int max_iter, + Mif_Boolean_t first_call); + +void EVTop_save( + CKTcircuit *ckt, + Mif_Boolean_t op, + double step); + +void EVTnode_copy( + CKTcircuit *ckt, + int node_index, + Evt_Node_t *from, + Evt_Node_t **to); + +void EVTcall_hybrids(CKTcircuit *ckt); + +void EVTdump( + CKTcircuit *ckt, + Ipc_Anal_t mode, + double step); + +void EVTaccept( + CKTcircuit *ckt, /* main circuit struct */ + double time); /* time at which analog soln was accepted */ + +#endif /* EVTPROTO_HEADER */ diff --git a/src/include/evtudn.h b/src/include/evtudn.h new file mode 100755 index 000000000..20f9b7d94 --- /dev/null +++ b/src/include/evtudn.h @@ -0,0 +1,123 @@ +#ifndef EVTUDN_HEADER +#define EVTUDN_HEADER x + +/* =========================================================================== +FILE EVTudn.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of "User-Defined Nodes". + These nodes are integrated into the simulator similar to the + way models are tied into SPICE 3C1, so that new node types + can be relatively easily added. The functions (required and + optional) are listed below. For optional functions, the + function can be left undefined and the pointer placed into the + Evt_Udn_Info_t structure can be specified as NULL. + + Required functions: + create - allocate data structure used as inputs and outputs to code models + initialize - set structure to appropriate initial value for first use as model input + copy - make a copy of the contents into created but possibly uninitialized structure + compare - determine if two structures are equal in value + + Optional functions: + dismantle - free allocations _inside_ structure (but not structure itself) + invert - invert logical value of structure + resolve - determine the resultant when multiple outputs are connected to a node + plot_val - output a real value for specified structure component for plotting purposes + print_val - output a string value for specified structure component for printing + ipc_val - output a binary data structure and size of the structure for IPC + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "miftypes.h" /* for Mif_Boolean_t used in udn_..._compare */ + +#define MALLOCED_PTR (*evt_struct_ptr) +#define STRUCT_PTR evt_struct_ptr +#define STRUCT_PTR_1 evt_struct_ptr_1 +#define STRUCT_PTR_2 evt_struct_ptr_2 +#define EQUAL (*evt_equal) +#define INPUT_STRUCT_PTR evt_input_struct_ptr +#define OUTPUT_STRUCT_PTR evt_output_struct_ptr +#define INPUT_STRUCT_PTR_ARRAY evt_input_struct_ptr_array +#define INPUT_STRUCT_PTR_ARRAY_SIZE evt_input_struct_ptr_array_size +#define STRUCT_MEMBER_ID evt_struct_member_id +#define PLOT_VAL (*evt_plot_val) +#define PRINT_VAL (*evt_print_val) +#define IPC_VAL (*evt_ipc_val) +#define IPC_VAL_SIZE (*evt_ipc_val_size) + +#define CREATE_ARGS void **evt_struct_ptr +#define INITIALIZE_ARGS void *evt_struct_ptr +#define COMPARE_ARGS void *evt_struct_ptr_1, \ + void *evt_struct_ptr_2, \ + Mif_Boolean_t *evt_equal +#define COPY_ARGS void *evt_input_struct_ptr, \ + void *evt_output_struct_ptr +#define DISMANTLE_ARGS void *evt_struct_ptr +#define INVERT_ARGS void *evt_struct_ptr +#define RESOLVE_ARGS int evt_input_struct_ptr_array_size, \ + void **evt_input_struct_ptr_array, \ + void *evt_output_struct_ptr +#define PLOT_VAL_ARGS void *evt_struct_ptr, \ + char *evt_struct_member_id, \ + double *evt_plot_val +#define PRINT_VAL_ARGS void *evt_struct_ptr, \ + char *evt_struct_member_id, \ + char **evt_print_val +#define IPC_VAL_ARGS void *evt_struct_ptr, \ + void **evt_ipc_val, \ + int *evt_ipc_val_size + + +typedef struct { + char *name; + char *description; + void ((*create)(CREATE_ARGS)); + void ((*dismantle)(DISMANTLE_ARGS)); + void ((*initialize)(INITIALIZE_ARGS)); + void ((*invert)(INVERT_ARGS)); + void ((*copy)(COPY_ARGS)); + void ((*resolve)(RESOLVE_ARGS)); + void ((*compare)(COMPARE_ARGS)); + void ((*plot_val)(PLOT_VAL_ARGS)); + void ((*print_val)(PRINT_VAL_ARGS)); + void ((*ipc_val)(IPC_VAL_ARGS)); +} Evt_Udn_Info_t; + + +extern int g_evt_num_udn_types; +extern Evt_Udn_Info_t **g_evt_udn_info; + + +#endif /* EVTUDN_HEADER */ diff --git a/src/include/ipc.h b/src/include/ipc.h new file mode 100755 index 000000000..a75c5d078 --- /dev/null +++ b/src/include/ipc.h @@ -0,0 +1,122 @@ +/* $Id$ + * + */ +/*============================================================================ +FILE IPC.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + Provides compatibility for the new SPICE simulator to both the MSPICE user + interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE + v.2 Simulator Interface and BCP (via Bsd Sockets). + +INTERFACES + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#ifndef IPC_DEFINED +#define IPC_DEFINED + + +#define IPC_MAX_LINE_LEN 80 +#define IPC_MAX_PATH_LEN 2048 + +/* Known socket port for server and client to communicate: */ +#define SOCKET_PORT 1064 + +/* Recognition character for Beginning Of Line of message: */ +#define BOL_CHAR '\\' + +/* Length (in bytes) of a socket message header: */ +#define SOCK_MSG_HDR_LEN 5 + + +typedef int Ipc_Boolean_t; + +#define IPC_FALSE 0 +#define IPC_TRUE 1 + +typedef struct { /* Don't change this type! It is cast elsewhere */ + double real; + double imag; +} Ipc_Complex_t; + +/*---------------------------------------------------------------------------*/ +typedef enum { + IPC_STATUS_OK, + IPC_STATUS_NO_DATA, + IPC_STATUS_END_OF_DECK, + IPC_STATUS_EOF, + IPC_STATUS_ERROR, +} Ipc_Status_t; + +#if 0 +/*---------------------------------------------------------------------------*/ +typedef void* Ipc_Connection_t; +/* + * A connection is an `opaque' type - the user has no access to the details of + * the implementation. Indeed the details are different depending on whether + * underlying transport mechanism is AEGIS Mailboxes or Bsd Sockets (or + * something else...) + */ +#endif + +/*---------------------------------------------------------------------------*/ +typedef enum { + IPC_WAIT, + IPC_NO_WAIT, +} Ipc_Wait_t; + +/*---------------------------------------------------------------------------*/ +typedef enum { + IPC_PROTOCOL_V1, /* >DATAB records in ATESSE v.1 format + * Handles v.1 style logfile name passing protocol + */ + IPC_PROTOCOL_V2, /* >DATAB records in ATESSE v.2 format + */ +} Ipc_Protocol_t; + +/*---------------------------------------------------------------------------*/ +typedef enum { + IPC_MODE_BATCH, + IPC_MODE_INTERACTIVE, +} Ipc_Mode_t; + + +/*---------------------------------------------------------------------------*/ +typedef enum { + IPC_ANAL_DCOP, + IPC_ANAL_DCTRCURVE, + IPC_ANAL_AC, + IPC_ANAL_TRAN, +} Ipc_Anal_t; + + + +#endif /* IPC_DEFINED */ diff --git a/src/include/ipcproto.h b/src/include/ipcproto.h new file mode 100755 index 000000000..b211baf6b --- /dev/null +++ b/src/include/ipcproto.h @@ -0,0 +1,52 @@ + +/* IPC.c */ +Ipc_Boolean_t kw_match (char *keyword , char *str ); +Ipc_Status_t ipc_initialize_server (char *server_name , Ipc_Mode_t m , Ipc_Protocol_t p ); +Ipc_Status_t ipc_terminate_server (void ); +Ipc_Status_t ipc_get_line (char *str , int *len , Ipc_Wait_t wait ); +Ipc_Status_t ipc_flush (void ); +Ipc_Status_t ipc_send_line_binary (char *str , int len ); +Ipc_Status_t ipc_send_line (char *str ); +Ipc_Status_t ipc_send_data_prefix (double time ); +Ipc_Status_t ipc_send_dcop_prefix (void ); +Ipc_Status_t ipc_send_data_suffix (void ); +Ipc_Status_t ipc_send_dcop_suffix (void ); +Ipc_Status_t ipc_send_errchk (void ); +Ipc_Status_t ipc_send_end (void ); +int stuff_binary_v1 (double d1 , double d2 , int n , char *buf , int pos ); +Ipc_Status_t ipc_send_double (char *tag , double value ); +Ipc_Status_t ipc_send_complex (char *tag , Ipc_Complex_t value ); +Ipc_Status_t ipc_send_int (char *tag , int value ); +Ipc_Status_t ipc_send_boolean (char *tag , Ipc_Boolean_t value ); +Ipc_Status_t ipc_send_string (char *tag , char *value ); +Ipc_Status_t ipc_send_int_array (char *tag , int array_len , int *value ); +Ipc_Status_t ipc_send_double_array (char *tag , int array_len , double *value ); +Ipc_Status_t ipc_send_complex_array (char *tag , int array_len , Ipc_Complex_t *value ); +Ipc_Status_t ipc_send_boolean_array (char *tag , int array_len , Ipc_Boolean_t *value ); +Ipc_Status_t ipc_send_string_array (char *tag , int array_len , char **value ); +Ipc_Status_t ipc_send_evtdict_prefix (); +Ipc_Status_t ipc_send_evtdict_suffix (); +Ipc_Status_t ipc_send_evtdata_prefix (); +Ipc_Status_t ipc_send_evtdata_suffix (); +Ipc_Status_t ipc_send_event(int, double, double, char *, void *, int); + +/* IPCtiein.c */ +void ipc_handle_stop (void ); +void ipc_handle_returni (void ); +void ipc_handle_mintime (double time ); +void ipc_handle_vtrans (char *vsrc , char *dev ); +void ipc_send_stdout (void ); +void ipc_send_stderr (void ); +Ipc_Status_t ipc_send_std_files (void ); +Ipc_Boolean_t ipc_screen_name (char *name , char *mapped_name ); +int ipc_get_devices (void *circuit , char *device , char ***names , double **modtypes ); +void ipc_free_devices (int num_items , char **names , double *modtypes ); +void ipc_check_pause_stop (void ); + +/* IPCaegis.c */ +Ipc_Status_t ipc_transport_initialize_server (char *server_name , Ipc_Mode_t m , Ipc_Protocol_t p , char *batch_filename ); +Ipc_Status_t extract_msg (char *str , int *len ); +Ipc_Status_t ipc_transport_get_line (char *str , int *len , Ipc_Wait_t wait ); +Ipc_Status_t ipc_transport_terminate_server (void ); +Ipc_Status_t ipc_transport_send_line (char *str , int len ); + diff --git a/src/include/ipctiein.h b/src/include/ipctiein.h new file mode 100755 index 000000000..4a3670bf0 --- /dev/null +++ b/src/include/ipctiein.h @@ -0,0 +1,96 @@ +/*============================================================================ +FILE IPCtiein.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + Provides a protocol independent interface between the simulator + and the IPC method used to interface to CAE packages. + +INTERFACES + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#ifndef IPC_TIEIN_DEFINED +#define IPC_TIEIN_DEFINED + + +#include "ipc.h" +#include "ipcproto.h" + + +#define IPC_STDOUT_FILE_NAME "/usr/tmp/atesse_xspice.out" +#define IPC_STDERR_FILE_NAME "/usr/tmp/atesse_xspice.err" + + +/* +Ipc_Vtrans_t is used by functions that return results to translate +voltage source names to the names of the devices they monitor. +This table is built from #VTRANS cards in the incoming deck and +is provided for ATESSE 1.0 compatibility. +*/ + +typedef struct { + int size; /* Size of arrays */ + char **vsrc_name; /* Array of voltage source name prefixes */ + char **device_name; /* Array of device names the vsources map to */ +} Ipc_Vtrans_t; + + +/* +Ipc_Tiein_t is used by the SPICE mods that take care of interprocess communications +activities. +*/ + +typedef struct { + + Ipc_Boolean_t enabled; /* True if we are using IPC */ + Ipc_Mode_t mode; /* INTERACTIVE or BATCH mode */ + Ipc_Anal_t anal_type; /* DCOP, AC, ... mode */ + Ipc_Boolean_t syntax_error; /* True if error occurred during parsing */ + Ipc_Boolean_t run_error; /* True if error occurred during simulation */ + Ipc_Boolean_t errchk_sent; /* True if #ERRCHK has been sent */ + Ipc_Boolean_t returni; /* True if simulator should return currents */ + double mintime; /* Minimum time between timepoints returned */ + double last_time; /* Last timepoint returned */ + double cpu_time; /* CPU time used during simulation */ + Ipc_Boolean_t *send; /* Used by OUTinterface to determine what to send */ + char *log_file; /* Path to write log file */ + Ipc_Vtrans_t vtrans; /* Used by OUTinterface to translate v sources */ + Ipc_Boolean_t stop_analysis; /* True if analysis should be terminated */ + +} Ipc_Tiein_t; + + + +extern Ipc_Tiein_t g_ipc; + + +#endif /* IPC_TIEIN_DEFINED */ + diff --git a/src/include/mif.h b/src/include/mif.h new file mode 100755 index 000000000..106412299 --- /dev/null +++ b/src/include/mif.h @@ -0,0 +1,84 @@ +#ifndef MIF +#define MIF + +/* =========================================================================== +FILE MIF.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file structure definitions global data used with the MIF package. + The global data structure is used to circumvent the need to modify + argument lists in existing SPICE 3C1 functions. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "miftypes.h" +#include "mifdefs.h" +#include "cktdefs.h" + + +typedef struct { + Mif_Boolean_t init; /* TRUE if first call to model */ + Mif_Boolean_t anal_init; /* TRUE if first call for this analysis type */ + Mif_Analysis_t anal_type; /* The type of analysis being performed */ + Mif_Call_Type_t call_type; /* Type of call to code model - analog or event-driven */ + double evt_step; /* The current DC step or time in event analysis */ +} Mif_Circuit_Info_t; + + +typedef struct { + double current; /* The current dynamic breakpoint time */ + double last; /* The last used dynamic breakpoint time */ +} Mif_Bkpt_Info_t; + + +typedef struct { + Mif_Boolean_t global; /* Set by .option to force all models to use auto */ + Mif_Boolean_t local; /* Set by individual model to request auto partials */ +} Mif_Auto_Partial_t; + + +typedef struct { + Mif_Circuit_Info_t circuit; /* Circuit data that will be needed by MIFload */ + MIFinstance *instance; /* Current instance struct */ + CKTcircuit *ckt; /* The ckt struct for the circuit */ + char *errmsg; /* An error msg from a cm_... function */ + Mif_Bkpt_Info_t breakpoint; /* Data used by dynamic breakpoints */ + Mif_Auto_Partial_t auto_partial; /* Flags to enable auto partial computations */ +} Mif_Info_t; + + + +extern Mif_Info_t g_mif_info; + + +#endif /* MIF */ diff --git a/src/include/mifcmdat.h b/src/include/mifcmdat.h new file mode 100755 index 000000000..ea877455d --- /dev/null +++ b/src/include/mifcmdat.h @@ -0,0 +1,373 @@ +#ifndef MIFCMDAT +#define MIFCMDAT + +/* =========================================================================== +FILE MIFcmdat.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the data structure definitions used by + code model and the associated MIF package. + + A special preprocessor (cmpp) is used on models written by a + user to turn items like INPUT() into the appropriate structure + reference. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "miftypes.h" + + +/* ************************************************************************** */ + + +/* + * Pointers into matrix for a voltage input, voltage output partial + */ + +typedef struct Mif_E_Ptr_s { + + double *branch_poscntl; /* Branch row, positive controlling column */ + double *branch_negcntl; /* Branch row, negative controlling column */ + +} Mif_E_Ptr_t; + + + +/* + * Pointers into matrix for a current input, current output partial + */ + +typedef struct Mif_F_Ptr_s { + + double *pos_ibranchcntl; /* Positive row, controlling branch column */ + double *neg_ibranchcntl; /* Negative row, controlling branch column */ + +} Mif_F_Ptr_t; + + + +/* + * Pointers into matrix for a voltage input, current output partial + */ + +typedef struct Mif_G_Ptr_s { + + double *pos_poscntl; /* Positive row, positive controlling column */ + double *pos_negcntl; /* Positive row, negative controlling column */ + double *neg_poscntl; /* Negative row, positive controlling column */ + double *neg_negcntl; /* Negative row, negative controlling column */ + +} Mif_G_Ptr_t; + + +/* + * Pointers into matrix for a current input, voltage output partial + */ + +typedef struct Mif_H_Ptr_s { + + double *branch_ibranchcntl; /* Branch row, controlling branch column */ + +} Mif_H_Ptr_t; + + + + +/* + * Matrix pointers associated with a particular port (of a particular type) + */ + + +typedef union Mif_Port_Ptr_u { + + Mif_E_Ptr_t e; /* Pointers for voltage input, voltage output */ + Mif_F_Ptr_t f; /* Pointers for current input, current output */ + Mif_G_Ptr_t g; /* Pointers for voltage input, current output */ + Mif_H_Ptr_t h; /* Pointers for current input, voltage output */ + +} Mif_Port_Ptr_t; + + + +/* + * Array of matrix data pointers for particular ports in a connection + */ + +typedef struct Mif_Conn_Ptr_s { + + Mif_Port_Ptr_t *port; /* Data for a particular port */ + +} Mif_Conn_Ptr_t; + + + +/* + * Row numbers and matrix entry pointers for loading the matrix and RHS with + * data appropriate for the particular output port and input ports. + */ + +typedef struct Mif_Smp_Ptr_s { + + /* Data at this level is for this connection. The Mif_Conn_Ptr_t */ + /* subtree is used only if this connection is an output. It supplies */ + /* the matrix pointers required for loading the partials from each */ + /* input. */ + + /* node connection equation numbers */ + int pos_node; /* Row associated with positive node */ + int neg_node; /* Row associated with negative node */ + + /* V source branch equation numbers */ + int branch; /* Row associated with V output branch */ + int ibranch; /* Row associated with I input branch */ + + /* matrix pointers for V source output */ + double *pos_branch; /* Positive node row, branch column */ + double *neg_branch; /* Negative node row, branch column */ + double *branch_pos; /* Branch row, positive node column */ + double *branch_neg; /* Branch row, negative node column */ + + /* matrix pointers for the zero-valued V source associated with an I input */ + double *pos_ibranch; /* Positive node row, branch column */ + double *neg_ibranch; /* Negative node row, branch column */ + double *ibranch_pos; /* Branch row, positive node column */ + double *ibranch_neg; /* Branch row, negative node column */ + + /* array of pointer info required for putting partials into the matrix */ + Mif_Conn_Ptr_t *input; /* Matrix pointers associated with inputs */ + +} Mif_Smp_Ptr_t; + + + + +/* ******************************************************************** */ + + + +/* + * Partial derivatives wrt ports of a particular input connection + */ + +typedef struct Mif_Partial_s { + + double *port; /* Partial wrt this port */ + +} Mif_Partial_t; + + +/* + * AC gains wrt ports of a particular input connection + */ + +typedef struct Mif_AC_Gain_s { + + Mif_Complex_t *port; /* AC gain wrt this port */ + +} Mif_AC_Gain_t; + + +/* + * Data used to access information in event struct in CKTcircuit struct ckt + */ + +typedef struct { + int node_index; /* Index of node in event-driven structures */ + int output_subindex; /* Subindex of output on node */ + int port_index; /* Index of port in event-driven structures */ + int output_index; /* Index of output in event-driven structures */ +} Mif_Evt_Data_t; + + +/* + * Information about individual port(s) of a connection. + */ + +typedef struct Mif_Port_Data_s { + + Mif_Port_Type_t type; /* Port type - e.g. MIF_VOLTAGE, ... */ + char *type_str; /* Port type in string form */ + char *pos_node_str; /* Positive node identifier */ + char *neg_node_str; /* Negative node identifier */ + char *vsource_str; /* Voltage source identifier */ + + Mif_Boolean_t is_null; /* Set to true if null in SPICE deck */ + Mif_Value_t input; /* The input value */ + Mif_Value_t output; /* The output value */ + Mif_Partial_t *partial; /* Partials for this port wrt inputs */ + Mif_AC_Gain_t *ac_gain; /* AC gains for this port wrt inputs */ + int old_input; /* Index into CKTstate for old input */ + + Mif_Boolean_t invert; /* True if state should be inverted */ + Mif_Boolean_t changed; /* A new output has been assigned */ + double load; /* Load factor output to this port */ + double total_load; /* Total load for this port */ + double delay; /* Digital delay for this output port */ + char *msg; /* Message string output to port */ + + Mif_Smp_Ptr_t smp_data; /* Pointers used to load matrix/rhs */ + Mif_Evt_Data_t evt_data; /* Data used to access evt struct */ + + double nominal_output; /* Saved output when doing auto partial */ + +} Mif_Port_Data_t; + + +/* ******************************************************************** */ + +/* + * Information in MIFinstance struct used by cm_.. support functions. + */ + + +typedef struct Mif_State_s { /* for cm_analog_alloc() */ + + int tag; /* Tag identifying this particular state */ + int index; /* Index into ckt->CKTstate[i] vector */ + int doubles; /* Number of doubles allocated for this state */ + int bytes; /* Actual number of bytes requested by cm_analog_alloc() */ + +} Mif_State_t; + + +typedef struct Mif_Intgr_s { /* for cm_analog_integrate() */ + + int byte_index; /* Byte offset into state array */ + +} Mif_Intgr_t; + + +typedef struct Mif_Conv_s { /* for cm_analog_converge() */ + + int byte_index; /* Byte offset into state array */ + double last_value; /* Value at last iteration */ + +} Mif_Conv_t; + + + +/* ******************************************************************** */ + + + +/* + * Information about the circuit in which this model is simulating. + */ + +typedef struct Mif_Circ_Data_s { + + Mif_Boolean_t init; /* True if first call to model - a setup pass */ + Mif_Analysis_t anal_type; /* Current analysis type */ + Mif_Boolean_t anal_init; /* True if first call in this analysis type */ + Mif_Call_Type_t call_type; /* Analog or event type call */ + double time; /* Current analysis time */ + double frequency; /* Current analysis frequency */ + double temperature; /* Current analysis temperature */ + double t[8]; /* History of last 8 analysis times t[0]=time */ + +} Mif_Circ_Data_t; + + + +/* + * The structure associated with a named "connection" on the model. + */ + +typedef struct Mif_Conn_Data_s { + + char *name; /* Name of this connection - currently unused */ + char *description; /* Description of this connection - unused */ + Mif_Boolean_t is_null; /* Set to true if null in SPICE deck */ + Mif_Boolean_t is_input; /* Set to true if connection is an input */ + Mif_Boolean_t is_output; /* Set to true if connection is an output */ + int size; /* The size of an array (1 if scalar) */ + Mif_Port_Data_t **port; /* Pointer(s) to port(s) for this connection */ + +} Mif_Conn_Data_t; + + + +/* + * Values for model parameters + */ + +typedef struct Mif_Param_Data_s { + + Mif_Boolean_t is_null; /* True if no value given on .model card */ + int size; /* Size of array (1 if scalar) */ + Mif_Value_t *element; /* Value of parameter(s) */ + +} Mif_Param_Data_t; + + + + +/* + * Values for instance variables + */ + +typedef struct Mif_Inst_Var_Data_s { + + int size; /* Size of array (1 if scalar) */ + Mif_Value_t *element; /* Value of instance variables(s) */ + +} Mif_Inst_Var_Data_t; + + + + +/* ************************************************************************* */ + + + +/* + * HERE IT IS!!! + * The top level data structure passed to code models. + */ + +typedef struct Mif_Private_s { + + Mif_Circ_Data_t circuit; /* Information about the circuit */ + int num_conn; /* Number of connections on this model */ + Mif_Conn_Data_t **conn; /* Information about each connection */ + int num_param; /* Number of parameters on this model */ + Mif_Param_Data_t **param; /* Information about each parameter */ + int num_inst_var; /* Number of instance variables */ + Mif_Inst_Var_Data_t **inst_var; /* Information about each inst variable */ + +} Mif_Private_t; + + + +#endif /* MIFCMDAT */ diff --git a/src/include/mifdefs.h b/src/include/mifdefs.h new file mode 100755 index 000000000..51daf991e --- /dev/null +++ b/src/include/mifdefs.h @@ -0,0 +1,111 @@ +#ifndef MIFDEFS +#define MIFDEFS + +/* =========================================================================== +FILE MIFdefs.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains (augmented) SPICE 3C1 compatible typedefs for use + with code models. These typedefs define the data structures that are + used internally to describe instances and models in the circuit + description linked lists. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "mifcmdat.h" +#include "ifsim.h" + + +/* The per-instance data structure */ + +typedef struct sMIFinstance { + + struct sMIFmodel *MIFmodPtr; /* backpointer to model */ + struct sMIFinstance *MIFnextInstance; /* pointer to next instance of current model */ + IFuid MIFname; /* pointer to character string naming this instance */ + + int num_conn; /* number of connections on the code model */ + Mif_Conn_Data_t **conn; /* array of data structures for each connection */ + + int num_inst_var; /* number of instance variables on the code model */ + Mif_Inst_Var_Data_t **inst_var; /* array of structs for each instance var */ + + int num_param; /* number of parameters on the code model */ + Mif_Param_Data_t **param; /* array of structs for each parameter */ + + int num_state; /* Number of state tags used for this inst */ + Mif_State_t *state; /* Info about states */ + + int num_intgr; /* Number of integrals */ + Mif_Intgr_t *intgr; /* Info for integrals */ + + int num_conv; /* Number of things to be converged */ + Mif_Conv_t *conv; /* Info for convergence things */ + + Mif_Boolean_t initialized; /* True if model called once already */ + + 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 */ + + int inst_index; /* Index into inst_table in evt struct in ckt */ + +} MIFinstance ; + + + +/* The per model data structure */ + +typedef struct sMIFmodel { + + int MIFmodType; /* type index of this device type */ + struct sMIFmodel *MIFnextModel; /* pointer to next possible model in linked list */ + MIFinstance *MIFinstances; /* pointer to list of instances that have this model */ + IFuid MIFmodName; /* pointer to character string naming this model */ + + int num_param; /* number of parameters on the code model */ + Mif_Param_Data_t **param; /* array of structs for each parameter */ + + Mif_Boolean_t analog; /* true if this model is analog or hybrid type */ + Mif_Boolean_t event_driven; /* true if this model is event-driven or hybrid type */ + +} MIFmodel; + + + +/* NOTE: There are no device parameter tags, since the ask, mAsk, ... */ +/* functions for code models work out of the generic code model structure */ + + + +#endif /* MIFDEFS */ diff --git a/src/include/mifparse.h b/src/include/mifparse.h new file mode 100755 index 000000000..fbfc19944 --- /dev/null +++ b/src/include/mifparse.h @@ -0,0 +1,120 @@ +#ifndef MIFPARSE +#define MIFPARSE + +/* =========================================================================== +FILE MIFparse.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the information structure definitions used by the + code model parser to check for valid connections and parameters. + + Structures of these types are created by the code model preprocessor + (cmpp) from the user created ifspec.ifs file. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + +#include "miftypes.h" + + +/* + * Information about a connection used by the parser to error check input + */ + + +typedef struct Mif_Conn_Info_s { + + char *name; /* Name of this connection */ + char *description; /* Description of this connection */ + Mif_Dir_t direction; /* Is this connection an input, output, or both? */ + Mif_Port_Type_t default_port_type; /* The default port type */ + char *default_type; /* The default type in string form */ + int num_allowed_types; /* The size of the allowed type arrays */ + Mif_Port_Type_t *allowed_type; /* The allowed types */ + char **allowed_type_str; /* The allowed types in string form */ + Mif_Boolean_t is_array; /* True if connection is an array */ + Mif_Boolean_t has_lower_bound; /* True if there is an array size lower bound */ + int lower_bound; /* Array size lower bound */ + Mif_Boolean_t has_upper_bound; /* True if there is an array size upper bound */ + int upper_bound; /* Array size upper bound */ + Mif_Boolean_t null_allowed; /* True if null is allowed for this connection */ + +} Mif_Conn_Info_t; + + + + +/* + * Information about a parameter used by the parser to error check input + */ + +typedef struct Mif_Param_Info_s { + + char *name; /* Name of this parameter */ + char *description; /* Description of this parameter */ + Mif_Data_Type_t type; /* Is this a real, boolean, string, ... */ + Mif_Boolean_t has_default; /* True if there is a default value */ + Mif_Parse_Value_t default_value; /* The default value */ + Mif_Boolean_t has_lower_limit; /* True if there is a lower limit */ + Mif_Parse_Value_t lower_limit; /* The lower limit for this parameter */ + Mif_Boolean_t has_upper_limit; /* True if there is a upper limit */ + Mif_Parse_Value_t upper_limit; /* The upper limit for this parameter */ + Mif_Boolean_t is_array; /* True if parameter is an array */ + Mif_Boolean_t has_conn_ref; /* True if parameter is associated with a connector */ + int conn_ref; /* The subscript of the associated connector */ + Mif_Boolean_t has_lower_bound; /* True if there is an array size lower bound */ + int lower_bound; /* Array size lower bound */ + Mif_Boolean_t has_upper_bound; /* True if there is an array size upper bound */ + int upper_bound; /* Array size upper bound */ + Mif_Boolean_t null_allowed; /* True if null is allowed for this parameter */ + +} Mif_Param_Info_t; + + + + +/* + * Information about an instance parameter used by the parser to error check input + */ + +typedef struct Mif_Inst_Var_Info_s { + + char *name; /* Name of this instance var */ + char *description; /* Description of this instance var */ + Mif_Data_Type_t type; /* Is this a real, boolean, string, ... */ + Mif_Boolean_t is_array; /* True if instance var is an array */ + +} Mif_Inst_Var_Info_t; + + +#endif /* MIFPARSE */ diff --git a/src/include/mifproto.h b/src/include/mifproto.h new file mode 100755 index 000000000..924cbed5f --- /dev/null +++ b/src/include/mifproto.h @@ -0,0 +1,157 @@ +#ifndef MIFPROTO +#define MIFPROTO + +/* =========================================================================== +FILE MIFproto.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains ANSI C function prototypes for functions in the + MIF package. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + + +#include "ifsim.h" +#include "inpdefs.h" +#include "smpdefs.h" +#include "cktdefs.h" +#include "miftypes.h" + + + +extern void MIF_INP2A( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current /* the card we are to parse */ +); + + +extern char * MIFgetMod( + void *ckt, + char *name, + INPmodel **model, + INPtables *tab +); + + +extern IFvalue * MIFgetValue( + void *ckt, + char **line, + int type, + INPtables *tab, + char **err +); + + +extern int MIFsetup( + SMPmatrix *matrix, + GENmodel *inModel, + CKTcircuit *ckt, + int *state +); + +extern int MIFload( + GENmodel *inModel, + CKTcircuit *ckt +); + + +extern int MIFmParam( + int param_index, + IFvalue *value, + GENmodel *inModel +); + +extern int MIFask( + CKTcircuit *ckt, + GENinstance *inst, + int param_index, + IFvalue *value, + IFvalue *select +); + +extern int MIFmAsk( + CKTcircuit *ckt, + GENmodel *inModel, + int param_index, + IFvalue *value +); + +extern int MIFtrunc( + GENmodel *inModel, + CKTcircuit *ckt, + double *timeStep +); + +extern int MIFconvTest( + GENmodel *inModel, + CKTcircuit *ckt +); + +extern int MIFdelete( + GENmodel *inModel, + IFuid name, + GENinstance **inst +); + +extern int MIFmDelete( + GENmodel **inModel, + IFuid modname, + GENmodel *model +); + +extern void MIFdestroy( + GENmodel **inModel +); + +extern char *MIFgettok( + char **s +); + + +extern char *MIFget_token( + char **s, + Mif_Token_Type_t *type +); + + +extern Mif_Cntl_Src_Type_t MIFget_cntl_src_type( + Mif_Port_Type_t in_port_type, + Mif_Port_Type_t out_port_type +); + +extern char *MIFcopy(char *); + + +#endif /* MIFPROTO */ diff --git a/src/include/miftypes.h b/src/include/miftypes.h new file mode 100755 index 000000000..95c5ab5a6 --- /dev/null +++ b/src/include/miftypes.h @@ -0,0 +1,226 @@ +#ifndef MIFTYPES +#define MIFTYPES + +/* =========================================================================== +FILE MIFtypes.h + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains typedefs shared by several header files in + the MIF package. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + + + + +/* ***************************************************************************** */ + + +typedef int Mif_Boolean_t; + +#define MIF_FALSE 0 +#define MIF_TRUE 1 + + +typedef int Mif_Status_t; + +#define MIF_OK 0 +#define MIF_ERROR 1 + +/* +typedef enum { + MIF_OK, + MIF_ERROR, +} Mif_Status_t; +*/ + + +/* ***************************************************************************** */ + + +/* + * The type of call to a code model - analog or event-driven + */ + +typedef enum { + MIF_ANALOG, /* Analog call */ + MIF_EVENT_DRIVEN, /* Event-driven call */ +} Mif_Call_Type_t; + + + +/* + * Analysis type enumerations + */ + +typedef enum { + MIF_DC, /* A DC or DCOP analysis */ + MIF_AC, /* A swept AC analysis */ + MIF_TRAN, /* A transient analysis */ +} Mif_Analysis_t; + + + +/* + * Port type enumerations + */ + +typedef enum { + MIF_VOLTAGE, /* v - Single-ended voltage */ + MIF_DIFF_VOLTAGE, /* vd - Differential voltage */ + MIF_CURRENT, /* i - Single-ended current */ + MIF_DIFF_CURRENT, /* id - Differential current */ + MIF_VSOURCE_CURRENT, /* vnam - Voltage source current */ + MIF_CONDUCTANCE, /* g - Single-ended VCIS */ + MIF_DIFF_CONDUCTANCE, /* gd - Differential VCIS */ + MIF_RESISTANCE, /* h - Single-ended ICVS */ + MIF_DIFF_RESISTANCE, /* hd - Differential ICVS */ + MIF_DIGITAL, /* d - Digital */ + MIF_USER_DEFINED, /* - Any user defined type */ +} Mif_Port_Type_t; + + + +/* + * The direction of a connector + */ + +typedef enum { + MIF_IN, /* Input only */ + MIF_OUT, /* Output only */ + MIF_INOUT, /* Input and output (e.g. g or h type) */ +} Mif_Dir_t; + + + +/* + * The type of a parameter + */ + +typedef enum { + + MIF_BOOLEAN, + MIF_INTEGER, + MIF_REAL, + MIF_COMPLEX, + MIF_STRING, + +} Mif_Data_Type_t; + + + +/* + * The type of a token + */ + +typedef enum { + + MIF_LARRAY_TOK, + MIF_RARRAY_TOK, + MIF_LCOMPLEX_TOK, + MIF_RCOMPLEX_TOK, + MIF_PERCENT_TOK, + MIF_TILDE_TOK, + MIF_STRING_TOK, + MIF_NULL_TOK, + MIF_NO_TOK, + +} Mif_Token_Type_t; + + + +/* + * Type of controlled source + */ + +typedef enum { + MIF_VCVS, + MIF_VCIS, + MIF_ICVS, + MIF_ICIS, +} Mif_Cntl_Src_Type_t; + + +/* ***************************************************************************** */ + + +/* + * Complex numbers + */ + +typedef struct { + + double real; + double imag; + +} Mif_Complex_t; + + + +/* + * Values of different types used by the load, ... routines + */ + +typedef union { + + Mif_Boolean_t bvalue; /* For digital node value */ + int ivalue; /* For integer parameters */ + double rvalue; /* For spice node values and real parameters */ + Mif_Complex_t cvalue; /* For complex parameters */ + char *svalue; /* For string parameters */ + void *pvalue; /* For user defined nodes */ + +} Mif_Value_t; + + + + +/* + * Values of different types used by the parser. Note that this is a structure + * instead of a union because we need to do initializations in the ifspec.c files for + * the models and unions cannot be initialized in any useful way in C + * + */ + +typedef struct { + + Mif_Boolean_t bvalue; /* For boolean values */ + int ivalue; /* For integer values */ + double rvalue; /* For real values */ + Mif_Complex_t cvalue; /* For complex values */ + char *svalue; /* For string values */ + +} Mif_Parse_Value_t; + + +#endif /* MIFTYPES */ diff --git a/src/include/multi_line.h b/src/include/multi_line.h new file mode 100644 index 000000000..7edf9dbab --- /dev/null +++ b/src/include/multi_line.h @@ -0,0 +1,66 @@ +/* + * project.h + * + * Diagonalization by Successive Rotations Method + * (The Jacobi Method) + * + * Date: October 4, 1991 + * + * Author: Shen Lin + * + * Copyright (C) University of California, Berkeley + * + */ + +/************************************************************ + * + * Macros + * + ************************************************************/ + +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef ABS +#define ABS(x) ((x) >= 0 ? (x) : (-(x))) +#endif +#ifndef SGN +#define SGN(x) ((x) >= 0 ? (1.0) : (-1.0)) +#endif + +/************************************************************ + * + * Defines + * + ************************************************************/ + +#define MAX_DIM 16 +#define Title "Diagonalization of a Symmetric matrix A (A = S^-1 D S)\n" +#define Left_deg 7 /* should be greater than or equal to 6 */ +#define Right_deg 2 + + +/************************************************************ + * + * Data Structure Definitions + * + ************************************************************/ + +typedef struct linked_list_of_max_entry{ + struct linked_list_of_max_entry *next; + int row, col; + float value; +} MAXE, *MAXE_PTR; + +typedef struct { + double *Poly[MAX_DIM]; + double C_0[MAX_DIM]; +} Mult_Out; + +typedef struct { + double *Poly; + double C_0; +} Single_Out; diff --git a/src/include/swec.h b/src/include/swec.h new file mode 100644 index 000000000..532741dfb --- /dev/null +++ b/src/include/swec.h @@ -0,0 +1,396 @@ +/* + * project.h + * + * Timing Simulator (ESWEC) + * + * Date: October 5, 1990 + * + * Author: Shen Lin + * + * Copyright (C) University of California, Berkeley + * + */ +#ifndef _SWEC_H_ +#define _SWEC_H_ + +/************************************************************ + * + * Defines + * + ************************************************************/ + +#define MainTitle " Timing Simulator\n" +#define MAXDEVICE 4 +#define MAXMOS 31500 /* suggested value */ +#define MAXDD 256 /* suggested value */ +#define MAXVCCS 128 /* suggested value */ +#define MAXTIME 1000000 +#define MAXNODE 136 + +#define TAB_SIZE 8192 /* originally 2048 */ +#define NUM_STEPS_PER_MICRON 10 /* 0.1 micron is the smallest step */ +#define MAX_FET_SIZE 80 /* largest fet in microns */ +#define Vol_Step 1.0e-3 /* voltage resolution */ +#define SCL 1000.0 /* voltage scaler (1V/3mv) */ +#define MAX_CP_TX_LINES 4 /* max number of coupled lines in + a multiconductor line system */ + +/************************************************************ + * + * Macro + * + ************************************************************/ +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef ABS +#define ABS(x) ((x) >= 0 ? (x) : (-(x))) +#endif + +/************************************************************ + * + * Data Structure Definitions + * + ************************************************************/ + +typedef struct reglist REGLIST; +typedef struct node NODE; +typedef struct mosfet MOSFET; +typedef struct emosfet EMOSFET; +typedef struct diode DIODE; +typedef struct ediode EDIODE; +typedef struct vccs VCCS; +typedef struct evccs EVCCS; +typedef struct i_cap I_CAP; +typedef struct ei_cap EI_CAP; +typedef struct resistor RESISTOR; +typedef struct eresistor ERESISTOR; +typedef struct rline RLINE; +typedef struct erline ERLINE; +typedef struct txline TXLine; +typedef struct etxline ETXLine; +typedef struct cpline CPLine; +typedef struct ecpline ECPLine; +typedef struct bqueue BQUEUE; +typedef struct pqueue PQUEUE; +typedef struct ms_device MS_DEVICE; +typedef struct bp_device BP_DEVICE; +typedef struct dd_device DD_DEVICE; + +struct mosfet{ + int type; /* 1 : NMOS, 2 : PMOS */ + MS_DEVICE *device; /* NULL if the nominal device model */ + NODE *out_node; + NODE *in_node; + float Cs, Cd; + MOSFET *nx; + int time; /* instantaneous information */ + float voltage, dvg; /* instantaneous information */ + float vgN_1; /* gate voltage at previous event point */ + float G; /* effective conductance at t(n) */ + float effective; /* W over effective L */ + int tabW; /* width in ns/um */ + REGLIST *region; /* region associated with this mos */ + /* NULL if driven by the node of the same region */ +}; + +struct diode{ + float Is; /* saturation current */ + float Vj; /* junction potential */ + double G; + NODE *in_node; + NODE *out_node; + DIODE *nx; +}; + +struct vccs{ + float Is; /* saturation current */ + NODE *in_node; + NODE *out_node; + NODE *pcv_node; + NODE *ncv_node; + DIODE *nx; +}; + +typedef struct { + char name[10]; /* device name */ + int type; /* 1 : NMOS, 2 : PMOS */ + int device_id; /* device id */ +} DEVICENAME; + +struct ms_device{ + char name[10]; + int used; /* device used in circuit flag */ + float rho; /* device vsat denom param */ + float alpha; /* device vsat denom vgg param */ + float vt; /* device zero bias threshold voltage in mv*/ + float gamma; /* device backgate bias vt param */ + float fermi; /* device fermi potential in mv */ + float theta; /* device backgate bias vt width param */ + float mu; /* device vt width param */ + float eta; /* device saturation slope */ + float eta5; /* eta - 0.5 */ + int pzld; /* positive lambda */ + float lambda; /* channel-length modulation */ + float kp; /* device conductance parameter */ + float cgs0; /* gate-source overlap capacitance + per meter channel width */ + float cgd0; /* gate-drain overlap capacitance + per meter channel width */ + float cox; /* oxide-field capacitance + per square meter of gate area */ + float cjsw; /* zero-biased junction sidewall capacitace + per meter of junction perimeter */ + float cj0; /* zero-biased junction bottom capacitace + per square meter of junction area */ + float keq; /* abrupt junction parameter */ + + float ld; /* lateral diffusion */ + float *thresh; + float *sat; + float *dsat; + float *body; + float *gammod; +}; + +struct bp_device{ + char name[10]; + int type; /* 1 : NPN; 2 : PNP */ + float rc; /* collector resistance */ + float re; /* emitter resistance */ + float rb; /* zero bias base resistance */ + float Is; /* transport saturation current */ + float Af; /* ideal maximum forward alpha */ + float Ar; /* ideal maximum reverse alpha */ + float Vje; /* B-E built-in potential */ + float Vjc; /* B-C built-in potential */ +}; + +struct dd_device{ + char name[10]; + float Is; /* saturation current */ + float rs; /* ohmic resistance */ + float Vj; /* junction potential */ +}; + +typedef struct linked_lists_of_Bpoint{ + struct linked_lists_of_Bpoint *next; + int time; + float voltage; + float slope; +} BPOINT, *BPOINTPTR; + +typedef struct linked_lists_of_nodeName{ + char id[24]; + struct linked_lists_of_nodeName *left, *right; + NODE *nd; +} NDname, *NDnamePt; + +struct node { + NDnamePt name; + EMOSFET *mptr; /* pointer to head of src/drn MOSFET list */ + EMOSFET *gptr; /* pointer to head of gate MOSFET list */ + EI_CAP *cptr; /* pointer to head of internodal cap list */ + ERESISTOR *rptr; /* pointer to head of internodal resistor list */ + ERLINE *rlptr; /* pointer to head of internodal TX line list */ + ETXLine *tptr; /* pointer to head of transmission line list */ + ECPLine *cplptr; /* pointer to head of coupled lines list */ + EDIODE *ddptr; /* pointer to head of diode list */ + EVCCS *vccsptr; /* pointer to head of VCCS list */ + EVCCS *cvccsptr;/* pointer to head of controlled VCCS list */ + NODE *next; /* pointer to next node */ + REGLIST *region; /* region associated with this node */ + NODE *base_ptr; /* group src/drn nodes into region */ + /* charles 2,2 1/18/93 + float V; + float dv; voltage at t(n-1) and slope at t(n) + */ + double V; + double dv; + float CL; /* grounded capacitance in F */ + double gsum; /*^ sum of the equivalent conductance */ + double cgsum; /*^ sum of the constant conductance */ + double is; /*^ equivalent Is */ + int tag; /* -2 : Vdd, -3 : Vss, -1 : initial value */ + int flag; /*^ flag to show some features of the node */ + PQUEUE *qptr; /*^ pointer to the entry in the queue or waiting list */ + FILE *ofile; /* output file for the signal at this node */ + /* NULL if not for print */ + int dvtag; +}; + +struct reglist{ + REGLIST *rnxt; /* pointer to next region */ + NODE *nlist; /* node list */ + MOSFET *mos; + I_CAP *cap; + RESISTOR *res; + TXLine *txl; + CPLine *cpl; + struct linked_lists_of_Bpoint *Bpoint; /* break points at primary inputs */ + struct linked_lists_of_Bpoint *head; /* header of the break points at primary inputs */ + int eTime; /* time when this region previously evaluated */ + int DCvalue; + /* 1, 0, 2 : unknown, 3 : unchangeable 1, 4 : unchangeable 0 */ + BQUEUE *prediction; +}; + + +struct bqueue{ + int key; /* time for the event to be fired, or DC weight */ + BQUEUE *left; + BQUEUE *right; + BQUEUE *pred; + BQUEUE *pool; + REGLIST *region; /* region id */ +}; + +struct pqueue { + NODE *node; + PQUEUE *next; + PQUEUE *prev; +}; + +struct i_cap { + NODE *in_node; + NODE *out_node; + float cap; + I_CAP *nx; +}; + +struct resistor { + NODE *in_node; + NODE *out_node; + float g; /* conductance */ + int ifF; /* whether floating */ + float g1; /* conductance for floating resistor */ + RESISTOR *nx; +}; + +struct rline { + NODE *in_node; + NODE *out_node; + float g; /* conductance */ + RLINE *nx; +}; + +typedef struct linked_lists_of_vi_txl{ + struct linked_lists_of_vi_txl *next; + struct linked_lists_of_vi_txl *pool; + int time; + /* charles 2,2 + float v_i, v_o; + float i_i, i_o; + */ + double v_i, v_o; + double i_i, i_o; +} VI_list_txl; + +typedef struct linked_lists_of_vi{ + struct linked_lists_of_vi *next; + struct linked_lists_of_vi *pool; + int time; + float v_i[MAX_CP_TX_LINES], v_o[MAX_CP_TX_LINES]; + float i_i[MAX_CP_TX_LINES], i_o[MAX_CP_TX_LINES]; +} VI_list; + +typedef struct { + double c, x; + double cnv_i, cnv_o; +} TERM; + +typedef struct { + int ifImg; + double aten; + TERM tm[3]; +} TMS; + +struct cpline { + int noL; + int ext; + float ratio[MAX_CP_TX_LINES]; + float taul[MAX_CP_TX_LINES]; + TMS *h1t[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + TMS *h2t[MAX_CP_TX_LINES][MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + TMS *h3t[MAX_CP_TX_LINES][MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double h1C[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double h2C[MAX_CP_TX_LINES][MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double h3C[MAX_CP_TX_LINES][MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double h1e[MAX_CP_TX_LINES][MAX_CP_TX_LINES][3]; + NODE *in_node[MAX_CP_TX_LINES]; + NODE *out_node[MAX_CP_TX_LINES]; + int tag_i[MAX_CP_TX_LINES], tag_o[MAX_CP_TX_LINES]; + CPLine *nx; + struct linked_lists_of_vi *vi_head; + struct linked_lists_of_vi *vi_tail; + float dc1[MAX_CP_TX_LINES], dc2[MAX_CP_TX_LINES]; +}; + +struct txline { + int lsl; /* 1 if the line is lossless, otherwise 0 */ + int ext; /* a flag, set if time step is greater than tau */ + float ratio; + float taul; + double sqtCdL; + double h2_aten; + double h3_aten; + double h1C; + double h1e[3]; + int ifImg; + NODE *in_node; + NODE *out_node; + int tag_i, tag_o; + TERM h1_term[3]; + TERM h2_term[3]; + TERM h3_term[6]; + TXLine *nx; + struct linked_lists_of_vi_txl *vi_head; + struct linked_lists_of_vi_txl *vi_tail; + float dc1, dc2; + int newtp; /* flag indicating new time point */ +}; + +struct evccs { + VCCS *vccs; + EVCCS *link; +}; + +struct ediode { + DIODE *dd; + EDIODE *link; +}; + +struct emosfet { + MOSFET *mos; + EMOSFET *link; +}; + +struct ei_cap { + I_CAP *cap; + EI_CAP *link; +}; + +struct eresistor { + RESISTOR *res; + ERESISTOR *link; +}; + +struct erline { + RLINE *rl; + ERLINE *link; +}; + +struct etxline { + TXLine *line; + ETXLine *link; +}; + +struct ecpline { + CPLine *line; + ECPLine *link; +}; + +#endif diff --git a/src/include/tclspice.h b/src/include/tclspice.h new file mode 100755 index 000000000..e25e1363d --- /dev/null +++ b/src/include/tclspice.h @@ -0,0 +1,33 @@ +/*Include file to allow spice to export certain data */ +#ifndef TCLSPICE_H +#define TCLSPICE_H + +extern int steps_completed; +extern void blt_init(void *run); +extern void blt_add(int index,double value); +extern void blt_relink(int index, void* v); +extern void blt_lockvec(int index); + +/* For things to do per loop */ +int Tcl_ExecutePerLoop(); + +/* For tk ploting */ +extern int sp_Tk_Init(void); +#include +extern int sp_Tk_NewViewport(GRAPH *graph); +extern int sp_Tk_Close(void); +extern int sp_Tk_Clear(void); +extern int sp_Tk_DrawLine(int x1, int y1, int x2, int y2); +extern int sp_Tk_Arc(int x0, int y0, int radius, double theta1, double theta2); +extern int sp_Tk_Text(char *text, int x, int y); +extern int sp_Tk_DefineColor(int colorid, double red, double green, double blue); +extern int sp_Tk_DefineLinestyle(int linestyleid, int mask); +extern int sp_Tk_SetLinestyle(int linestyleid); +extern int sp_Tk_SetColor(int colorid); +extern int sp_Tk_Update(void); + +/* The blt callback method */ +#include +extern int blt_plot(struct dvec *y,struct dvec *x); + +#endif diff --git a/src/pkgIndex.tcl.in b/src/pkgIndex.tcl.in new file mode 100755 index 000000000..a7b2b00b0 --- /dev/null +++ b/src/pkgIndex.tcl.in @@ -0,0 +1,3028 @@ +#Tcl package index file, version 0.1 +#Tcl package index file, version %VERSION% +set ::spicewishversion "0.1" + + + +#set ::plot_background \#004040 +set ::plot_background white + +proc Loadspice { version dir } { + + package require BLT + + package require tclreadline + + package require Tclx ;# for bsearch + + set suffix [info sharedlibextension] + + set library spice${suffix} + + + global tcl_platform + if { $tcl_platform(platform) == "unix" } { + set library [file join $dir lib${library}] + } + load $library spice + + blt::vector create ::spice::X_Data + blt::vector create ::spice::Y_Data + + namespace eval spice { + namespace export ac help save alias history sens alter altermod iplot setcirc asciiplot jobs setplot aspice setscale bg let settype linearize shell bug listing shift show cdump maxstep showmod compose newhelp noise spec cross oldhelp spice dc op spice_data define spice_header deftype plot state delete plot_datapoints status delta plot_date step plot_get_value stop diff plot_name strcmp display plot_nvars tf disto plot_title dowhile plot_variables tran dump print transpose echo pz tutorial edit quit unalias else rehash undefine end repeat unlet reset fourier reshape version spicetoblt resume where get_output rspice get_param run write goto running xgraph hardcopy rusage steps_completed blt_vnum codemodel halt + + } + + # Callback functions for the plot command + # Warning: if any of these functions return an error then + # spice will probably segfault as tcl/tk will overflow somewhere + # Note: color is actually spelt COLOUR, which looks much better + # Note: they don't work in namespace so have to make global + + proc spice_gr_NewViewport { } { + set width 1000 + set height 400 + set fontwidth 12 + set fontheight 24 + canvas .c -width $width -height $height -background white + pack .c + return "$width $height $fontwidth $fontheight" + } + proc spice_gr_Close { } { + } + proc spice_gr_Clear { } { + } + proc spice_gr_DrawLine { x1 y1 x2 y2 } { + puts "draw" + .c create line [expr $x1 + 25] [expr 375 - $y1] [expr $x2 + 25] [expr 375 - $y2] + } + proc spice_gr_Arc { x0 y0 radius theta1 theta2 } { + .c create arc [expr $x0 - $radius + 25] [expr 375 - $y0 - $radius] \ + [expr $x1 + $radius + 25 ] [expr 375 - $y1 + $radius] \ + -start $theta1 -extent $theta2 + } + proc spice_gr_Text {text x y} { + .c create text [expr $x + 25] [expr 375 - $y] -text $text + } + + proc spice_gr_SetLinestyle {linestyleid} { + puts "SetLinestyle $linestyleid" + } + proc spice_gr_SetColor {colorid } { + puts "SetColor $colorid $color" + } + + proc spice_gr_Update { } { + } + + + # These seem to never be called /* + proc spice_gr_DefineColor {colorid red green blue} { + puts "DefineColor $colorid $red $green $blue" + } + proc spice_gr_DefineLinestyle {linestyleid mask} { + puts "DefineLinestyle $linestyleid $mask" + } + + + ::tclreadline::readline customcompleter "plot_command_readline_completer" ;#- see routine above + +} + + +#------------------------------------------------ +# nutmeg extensions (eqivalent functions to nutmeg) +# (requires readline, + + +#toplevel counters +set ::toplevel_count 1 + +#Colour schemes. +# - Colours allocated on a rotating colour sheme ::trace_index_counter counts the traces issued +# -OR- if a particular hash exists for a trace, this is used + +set ::linecolour(default) "grey" +set ::linecolour(0) "red" +set ::linecolour(1) "blue" +set ::linecolour(2) "orange" +set ::linecolour(3) "green" +set ::linecolour(4) "magenta" +set ::linecolour(5) "brown" + + + +#-alternate line dashes +set ::dashlist(default) "" ;#- solid by default +set ::dashlist(0) "2 2"; #- fine dots +set ::dashlist(1) "5 5"; #- large dahes +set ::dashlist(2) "10 2"; #- large dahes, small gap +set ::dashlist(3) "2 10"; #- +set ::dashlist(4) "3 1 2"; #- +set ::dashlist(5) "20 5"; #- + +#-------------------------------------------------------------------------------------------------------------------------------------- +proc q { } { exit } + +#--------------------------------------------------------------------------------------------------------------------------------------- +proc run { } { spice::run } + +#--------------------------------------------------------------------------------------------------------------------------------------- +# plot command, replacement of nutmeg's plot command, mapped onto +# the new code + +proc plot { args } { + + global disp_zoom_level disp_dx + + #make a new toplevel + set tl [toplevel ".tclspice$::toplevel_count"] + wm title $tl "tclspice plot #$::toplevel_count" + set .tclspice$::toplevel_count.dx 0 + set disp_dx 0 + set gr_n ".tclspice$::toplevel_count" + + # create drop down menu + pack [ frame .tclspice$::toplevel_count.f ] -side top -fill both + + # create trace and place slot info bar - bottom + pack [ frame .tclspice$::toplevel_count.tracePlaceInfoBar -background grey ] -side bottom -fill x + + # create frame for graph & trace and place + pack [frame $tl.scope ] -side bottom -expand 1 -fill both + pack [ frame $tl.scope.tp -background grey ] -side left -fill both + + # create blt graph + set ::graph_to_use [blt::graph $tl.scope.g -width 1000 -height 200] ;#- .g + $::graph_to_use grid configure -hide no -dashes { 4 4 } ;#- Add a grid + $::graph_to_use crosshairs on ;# -crosshairs + pack $::graph_to_use -expand 1 -fill both -side right ;#- show it + + #graph axis config + $::graph_to_use axis configure x \ + -command graph_Y_axis_callback \ + -subdivisions 2 + + # graph legend config + $::graph_to_use legend configure -activerelief raised + + # graph legend active on button 3 + $::graph_to_use legend bind all { %W legend activate [%W legend get current] } + $::graph_to_use legend bind all { %W legend deactivate [%W legend get current]} + + # creates the drag and drop packet for each of the plots in the legend + blt::drag&drop source $::graph_to_use -packagecmd {make_package %t %W } -button 1 + set token [blt::drag&drop token $::graph_to_use -activebackground blue ] + pack [ label $token.label -text "" ] + + # highlights the nearest traces to the cursors legend + $::graph_to_use element bind all { + %W legend activate [%W element get current] + TracePlace_set_scale_y2 %W [%W element get current] + } + $::graph_to_use element bind all { %W legend deactivate [%W element get current] } + + + # zoom controls + #set ::zoom_selected($tl.scope.g) 0 + #bind $tl { set ::zoom_selected(%W) 1 ; puts "zoom on $::zoom_selected(%W)" } + #bind $tl { set ::zoom_selected(%W) 0 ; puts "zoom off $::zoom_selected(%W)"} + + # zoom controls + ZoomStack $::graph_to_use Shift-Button-1 Shift-Button-3 + + + #make a bindng for mouse motion etc + # bind $::graph_to_use { tclspice_button_handler 1 %W %x %y } + # bind $::graph_to_use { tclspice_button_handler 2 %W %x %y } + bind $::graph_to_use { tclspice_button_handler 3 %W %x %y } + bind $::graph_to_use { tclspice_motion_handler %W %x %y } + + + # button 2 pop up menu + bind $::graph_to_use { + tclspice_button_handler 2 %W %x %y + } + + # + $::graph_to_use legend bind all { %W legend activate [%W legend get current] } + $::graph_to_use legend bind all { %W legend deactivate [%W legend get current]} + + set ::trace_place_selected($tl.scope.g) 0 + + # -- menu [ options ] + pack [ menubutton .tclspice$::toplevel_count.f.options -text "options" -menu .tclspice$::toplevel_count.f.options.m ] -side left + set options_menu [menu .tclspice$::toplevel_count.f.options.m -tearoff 0] + $options_menu add command -label "rename graph" -command "entry_selection_box .tclspice$::toplevel_count graph" + $options_menu add command -label "clear graph" -command "spice_gr_cleargraph $tl.scope.g" + $options_menu add command -label "save to postscript" -command "entry_selection_box .tclspice$::toplevel_count save_to_postscript" + $options_menu add checkbutton -label "Trace Place" -onvalue 1 -offvalue 0 -variable ::trace_place_selected($tl.scope.g) -command "TracePlace $tl" + $options_menu add command -label "close" -command { exit } + + # -- menu [ traces ] + pack [ menubutton .tclspice$::toplevel_count.f.traces -text "traces" -menu .tclspice$::toplevel_count.f.traces.m ] -side left + set traces_menu [menu .tclspice$::toplevel_count.f.traces.m -tearoff 0 ] + $traces_menu add command -label "Add/remove traces" -command "trace_selection_box update $tl.scope.g" + $traces_menu add cascade -label "Remove trace" -menu $traces_menu.remove_plot + $traces_menu add command -label "Update traces" -command "spice_update_traces $tl.scope.g" + menu $traces_menu.remove_plot -tearoff 0 -postcommand "create_remove_plot_menu $tl $traces_menu" + + + #------------------------------------------------------------------------------------------------------------- + proc create_remove_plot_menu {tl traces_menu } { + $traces_menu.remove_plot delete 0 last ;# - clear the old list + # add the current traces + + # repalce the traces in alphabetical order + set trace_list [ lsort -dictionary [$tl.scope.g element names] ] + + foreach trace $trace_list { + $traces_menu.remove_plot add command -label $trace -command "$tl.scope.g element delete $trace" + } + } + #-------------------------------------------------------------------------------------------------------------- + + + set ::graph_dx($tl) 0.00000000000000 + set ::graph_dy($tl) 0.00000000000000 + set ::graph_x1($tl) 0.00000000000000 + set ::graph_y1($tl) 0.00000000000000 + set ::graph_x2($tl) 0.00000000000000 + set ::graph_y2($tl) 0.00000000000000 + + # measurements + pack [ label $tl.f.dyv -textvariable graph_dy($tl) -background "lightgrey" -width 16 ] -side right + pack [ label $tl.f.dy -text "dy :" -background "lightgrey" ] -side right + bind $tl.f.dyv { regexp {(.[0-9A-z]+)} %W temp; puts "dy : $::graph_dy($temp)"} + + pack [ label $tl.f.dxv -textvariable graph_dx($tl) -background "grey" -width 16 ] -side right + pack [ label $tl.f.dx -text "dx : " -background "grey" ] -side right + bind $tl.f.dxv { regexp {(.[0-9A-z]+)} %W temp; puts "dx : $::graph_dx($temp)"} + + pack [ label $tl.f.y2v -textvariable graph_y2($tl) -background "lightgrey" -width 16 ] -side right + pack [ label $tl.f.y2 -text "y2 :" -background "lightgrey"] -side right + bind $tl.f.y2v { regexp {(.[0-9A-z]+)} %W temp; puts "y2 : $::graph_y2($temp)"} + + pack [ label $tl.f.y1v -textvariable graph_y1($tl) -background "grey" -width 16] -side right + pack [ label $tl.f.y1 -text "y1 :" -background "grey" ] -side right + bind $tl.f.y1v { regexp {(.[0-9A-z]+)} %W temp; puts "y1 : $::graph_y1($temp)"} + + pack [ label $tl.f.x2v -textvariable graph_x2($tl) -background "lightgrey" -width 16 ] -side right + pack [ label $tl.f.x2 -text "x2 :" -background "lightgrey" ] -side right + bind $tl.f.x2v { regexp {(.[0-9A-z]+)} %W temp; puts "x2 : $::graph_x2($temp)"} + + pack [ label $tl.f.x1v -textvariable graph_x1($tl) -background "grey" -width 16 ] -side right + pack [ label $tl.f.x1 -text "x1 :" -background "grey" ] -side right + bind $tl.f.x1v { regexp {(.[0-9A-z]+)} %W temp; puts "x1 : $::graph_x1($temp)"} + + + # clear out old stuff + spice_gr_cleargraph $::graph_to_use ;#- zap the old text and re-initialise the + + add_traces $::graph_to_use $args + + #save the plot line in a variable so it can be run again - e.g. when gui restarted + set "::${::graph_to_use}_plotlist" [subst $args] ;#- expand as argument + ## no good becasue traces can be added and removed + # XXXXXXXXXx + + # used to speed up y2axis, not recalcing the same slot twice + set ::trace_place_y2axis_slot($::graph_to_use) "" + set ::trace_place_y2axis_zoom($::graph_to_use) "" + + incr ::toplevel_count ;#- move to next toplevel + +} + +#--------------------------------------------------------------------------------------------------------------------------- + +proc spice_update_traces { graph { mode "" } { trace_list ""} } { + + # -- mode -- + # - "remove" - removes traces in list from scope + # - "add" - add traces in list to scope + # - "update" - replaces the traces displayed with given list + # - "" - just update the current traces in the graph + + + set scope_trace_list [ $graph element names ] + + # traces are been 'added' or 'removed' + if { ( $trace_list != "") && ($mode != "") } { + + if { $mode == "remove" } { + foreach trace $trace_list { + if { [ $graph element exists $trace ] } { $graph element delete $trace } + } + } + + if { $mode == "add" } { + foreach trace $trace_list { + if { [ $graph element exists $trace ] == 0 } { $graph element create $trace } + } + } + + # new scope list + set scope_trace_list [ $graph element names ] + + if { $mode == "update" } { + # this just replaces the plot list with list passed + set scope_trace_list { } + set scope_trace_list $trace_list + } + + } + + # removes all the traces + foreach trace [ $graph element names ] { $graph element delete $trace } + + # repalce the traces in alphabetical order + set trace_list [ lsort -dictionary $scope_trace_list ] + + foreach trace $trace_list { $graph element create $trace } + + #----------------------------------------------------------------- + proc spice_trace_list_position { graph trace } { + + set trace_list [ lsort -dictionary [ $graph element names ] ] + set list_position [lsearch $trace_list $trace] + + if { $list_position == -1 } { return -1 } + + return $list_position + } + #----------------------------------------------------------------- + + # colour selection not correct + + # delete all the old vectors - replace with new values + foreach vector_name [ blt::vector names ::$graph\_trace_* ] { blt::vector destroy $vector_name } + + # retrieves the time vector + blt::vector create _time + spice::spicetoblt time _time 0 [ expr \$::spice::steps_completed - 1] + + set index 0 + foreach trace_name [ $graph element names ] { + + # if a saved trace that has been pre run + if { [ regexp {_OLD} $trace_name ] } { + + set saved_plots_file [ open "$::spicefilename.gui_plots.tcl" "r"] + set trace_name_old "$trace_name\_OLD" + gets $saved_plots_file string + + while { [eof $saved_plots_file ] == 0} { + if { [ regexp $trace_name $string] } { + foreach xy { x y } { + + set start [ string first " : " $string ] + set string [ string range $string [ expr $start + 3 ] end ] + + if { $xy == "x" } { _time set "$string "} + if { $xy == "y" } { + blt::vector create _vector + _vector set "$string" + } + gets $saved_plots_file string + } + } + gets $saved_plots_file string + } + + close $saved_plots_file + + set symbol "" + + } else { + + # get the new trace values + blt::vector create _vector + spice::spicetoblt $trace_name _vector 0 [ expr \$::spice::steps_completed - 1] + + set symbol "circle" + } + + #create an element on the graph called $Yname using the X and Y vectors + #-choose colour etc + if { [info exists ::linecolour($trace_name)] } { + set colour $::linecolour($trace_name) ;#- if specific colour assigned to the trace + } elseif { [info exists ::linecolour($index)] } { + set colour $::linecolour($index) ;#- if within number of traces + } else { + + # set colour $::linecolour(default) + # creates a random colour + set randred [format "%03x" [expr {int (rand() * 4095)}]] + set randgreen [format "%03x" [expr {int (rand() * 4095)}]] + set randblue [format "%03x" [expr {int (rand() * 4095)}]] + set colour "#$randred$randgreen$randblue" + } + + set ::linecolour($trace_name) $colour ;# saves the traces colour + + $graph element configure $trace_name \ + -ydata "$_vector(:)" \ + -xdata "$_time(:)" \ + -symbol $symbol \ + -linewidth 2 \ + -pixels 3 \ + -color $colour + + blt::vector create $graph\_trace_Y[ spice_trace_list_position $graph $trace_name ] + blt::vector create $graph\_trace_X[ spice_trace_list_position $graph $trace_name ] + + _time dup $graph\_trace_X[ spice_trace_list_position $graph $trace_name ] + _vector dup $graph\_trace_Y[ spice_trace_list_position $graph $trace_name ] + + blt::vector destroy _vector + + incr index + } + + # update the trace and place grid + TracePlace_update $graph + +} + +#----------------------------------------------------------------------------------------------------------------------------- +# creates temp vectors for 'trace' + +proc spice_return_trace_vectors { graph trace } { + + # delete any old temp value created by this procedure + foreach vector [ blt::vector names temp_vector_* ] { blt::vector destroy $vector } + + # valid trace + if { [ $graph element exists $trace ] != 1 } { return -1 } + + set list_position [ spice_trace_list_position $graph $trace ] + + $graph\_trace_X$list_position dup temp_vector_X + $graph\_trace_Y$list_position dup temp_vector_Y + + return 1 + +} + +#------------------------------------------------------------------------------------------------------------------ +# called from pop up menu, when mouse over active trace + +proc TracePlace_remove_trace_from_slot { window } { + + # called from pop up menu when mouse over a trace on the screen "remove a1_x1_y0" + + regexp {(.[0-9A-z]+)} $window window_clean + set graph "$window_clean.scope.g" + set tracePlace_w "$window_clean.scope.tp.ft.tracePlaceGrid" + + # exits if none of the legends are active + if { [$graph legend activate] == "" } { return } + + # exits if trace and place is not active + if { $::trace_place_selected($graph) == 0 } { return } + + # get the current trace + set trace [ $graph legend activate ] + + set slot_col -1 + + # runs through all the slot lists and removes trace + for {set col_cnt 0 } {$col_cnt <= $::columns} {incr col_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + + if { [ lcontain $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) $trace ] } { + + set slot_col $col_cnt + set slot_row $row_cnt + + set string_start [ string first $trace $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) ] + set string_finish [ expr $string_start + [ string length $trace ] - 1] + set ::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) [ string replace $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) $string_start $string_finish "" ] + + } + } + } + + # exits if trace is not in any list + if { $slot_col == -1 } { return } + + # removes the trace from scope , drops the legend relief + $graph element configure $trace -labelrelief flat + $graph element configure $trace -hide 1 + + # removes trace from traceplace info bar bottom + if { [$window_clean.tracePlaceInfoBar.fakeGraph element exists $trace ] } { $window_clean.tracePlaceInfoBar.fakeGraph element delete $trace } + + # update the slots canvas markers + TracePlace_update_slot_canvas_markers $tracePlace_w $slot_col $slot_row +} + +#------------------------------------------------------------------------------------------------------------- +# creates a drag and drop package + +proc make_package {token graph { args ""} } { + + regexp {(.[0-9A-z]+)} $graph scope_name + set scope_name "$scope_name.scope.g" + + + # exits if trace and place is not active + if { $::trace_place_selected($scope_name) == 0 } { return } + + # case where a trace place slot is selected - for all slots traces to be moved to another slot + if { $args == "tracePlaceGrid" } { + set list $::slot_trace_list($graph) + set list_length [llength $list] + + if { $list_length == 0 } { return } + + if { $list_length == 1 } { + + # removes all the " " at the end of the string + while { [ string range $list [ expr [ string length $list] -1] [ expr [ string length $list] -1] ] == " " } { + set list [ string range $list 0 [ expr [ string length $list] - 2] ] + } + + # removes all the " " at the start of the string + while { [ string range $list 0 0 ] == " " } { + set list [ string range $list 1 [ expr [ string length $list] - 1] ] + } + + $token.label configure -text $list -background [ $scope_name element cget $list -color ] -height 1 + + } else { + # more than one trace in the box + $token.label configure -text "Traces" -background green -height 1 + } + return [list $list] + } + + # exits if none of the legends are active + if { [$graph legend activate] == "" } { return } + + $token.label configure -text [$graph legend activate] -background [ $scope_name element cget [$graph legend activate ] -color ] -height 1 + + return [ $graph legend activate ] +} + + +#------------------------------------------------------------------------------------ +#BOTH buttons 1 & 2 +proc tclspice_button_handler { button widget x y } { + + #get realcoords from pix + set xreal [$widget axis invtransform x $x] + set yreal [$widget axis invtransform y $y] + + # pop up box for markers + if {$button == 2} { + pop_up_menu $widget $x $y + } + +} + + +#-------------------------------------------------------------------------------------- +# +proc add_traces { graph args } { + + regexp {(.[0-9A-z]+)} $graph window_name + set graph_w "$window_name.scope.g" + + while { [ string range $args 0 0 ] == "\{" } { + set args [ string range $args 1 [ expr [ string length $args] - 1] ] + } + + while { [ string range $args [ expr [ string length $args] - 1] [ expr [ string length $args] - 1] ] == "\}" } { + set args [ string range $args 0 [ expr [ string length $args] - 2] ] + } + + puts "list add : $args" + + # blt plots need re work so that there are more than 6 colours then grey + # spice::bltplot $args + spice_update_traces $graph_w add $args + +} + + + +#----------------------------------------------------------------------------------------- + +proc update_traces { widget trace_list } { + spice_update_traces $widget update $trace_list +} + +#----------------------------------------------------------------------------------------------- +proc remove_traces {widget trace } { + spice_update_traces $widget remove $args +} + +#----------------------------------------------------------------------------------------------------- +set ::pop_up_menu "normal_mode" +set ::disp_zoom_level 0 + +#------------------------------------------------------------------------------------------------------- +# creates the pop up menu + +proc pop_up_menu { widget x y } { + global disp_zoom_level + + regexp {(.[0-9A-z]+)} $widget window_name + + # returns if not over trace + if { [ $window_name.scope.g legend activate] == "" } { return } + + set xreal $x + set yreal $y + + catch {destroy .markerEntry } ;# destroy old entry box + catch {destroy .m} ;# destroy old pop up + menu .m -tearoff 0 + + .m add command -label "Remove [ $window_name.scope.g legend activate]" -command "TracePlace_remove_trace_from_slot $window_name" + + .m add command -label "scale" -command "TracePlace_set_scale_y2 $window_name.scope.g [ $window_name.scope.g legend activate]" + + .m add command -label "triggerpoints" + + scan [wm geometry $window_name ] "%dx%d+%d+%d" width height xpos ypos + set info_bar_y [ winfo height $window_name.f ] + set trace_place_offset_x [ winfo x $window_name.scope.g ] + + tk_popup .m [expr $xpos + $x + $trace_place_offset_x] [expr $ypos + $y + $info_bar_y + $info_bar_y] "" + +} + +#------------------------------------------------------------------------------------------------------------------------ +# set up the y axis when using trace and place +# + +proc TracePlace_set_scale_y2 {graph { trace ""}} { + + # makes sure that a trace is passed + if { $trace == "" } { puts "no trace passed" ; return } + + # exits if trace and place is not active + if { $::trace_place_selected($graph) == 0 } { return } + + regexp {(.[0-9A-z]+)} $graph window_clean + set tracePlace_w "$window_clean.scope.tp.ft.tracePlaceGrid" + + # searches to which list the trace is from + set slot_col -1 + for {set col_cnt 0 } {$col_cnt <= $::columns} {incr col_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + if { [ lcontain $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) $trace ] } { + set slot_col $col_cnt + set slot_row $row_cnt + } + } + } + if { $slot_col == -1 } { return } + + # global variable that keeps track of which slot the scale is based on + # should speed up cutting out the resizeing below + + if { ($::trace_place_y2axis_slot($window_clean.scope.g) == "$slot_col\_$slot_row") + && + ($::trace_place_y2axis_zoom($window_clean.scope.g) == "[ $graph yaxis cget -min ]_[ $graph yaxis cget -max ]") } { return } + + set ::trace_place_y2axis_slot($window_clean.scope.g) "$slot_col\_$slot_row" + # set ::trace_place_y2axis_zoom($window_clean.scope.g) "$yaxis_min_$yaxis_max" + + + # calculates maximum and minimum value of trace + set trace_max_value 0 + set trace_min_value 0 + + # runs through all traces on screen within the slot to find make + foreach trace_m $::slot_trace_list($tracePlace_w.f_$slot_col\_$slot_row.b) { + + # trace is not displayed on screen + if { [ $window_clean.scope.g element cget $trace_m -hide ] == 1 } { continue } + + spice_return_trace_vectors $window_clean.scope.g $trace_m + + #$window_clean.scope.g_$trace_m dup temp + #set data_list_length [$window_clean.scope.g_$trace_m length] + set data_list_length [temp_vector_Y length] + + for { set i 0 } { $i < $data_list_length } { incr i } { + + if { [ temp_vector_Y index $i ] > $trace_max_value } { set trace_max_value [ temp_vector_Y index $i ] } + if { [ temp_vector_Y index $i ] < $trace_min_value } { set trace_min_value [ temp_vector_Y index $i ] } + } + + } + + # $window_clean.scope.g_$trace dup temp + # set data_list_length [$window_clean.scope.g_$trace length] + # needed ??????????????? + spice_return_trace_vectors $window_clean.scope.g $trace + set data_list_length [temp_vector_Y length] + + # slots start and finish position + set dimensions [ TracePlace_slot_dimension $graph $slot_col $slot_row ] + set slot_start_position [lindex $dimensions 0] + set slot_finish_position [lindex $dimensions 1] + + # displays second axis + $graph y2axis configure -hide 0 + + set yaxis_min [ $graph yaxis cget -min ] + set yaxis_max [ $graph yaxis cget -max ] + + # calculates the axis scale + #if { $trace_min_value < 0 } { set temp_min [ expr $trace_min_value * -1 ] } else { set temp_min $trace_min_value } + set trace_min_value $trace_min_value + set scale_factor [ expr ( $trace_max_value - $trace_min_value) / ( $slot_finish_position - $slot_start_position )] + if { $trace_min_value == 0 } { set trace_min_value 0.000001 } + set scale_min [ expr $trace_min_value - ( $slot_start_position * $scale_factor)] + set scale_max [ expr $trace_max_value + ( (100 - $slot_finish_position ) * $scale_factor) ] + + $graph y2axis configure -min $scale_min -max $scale_max + + # need to take into consideration also when zoomed in + set yaxis_min [ $graph yaxis cget -min ] + set yaxis_max [ $graph yaxis cget -max ] + + # zoom level 1:1 + if { [ expr int ($yaxis_max - $yaxis_min) ] ==100 } { return } + + set ::trace_place_y2axis_zoom($window_clean.scope.g) "$yaxis_min\_$yaxis_max" + + set y2axis_min [ $graph y2axis cget -min ] + set y2axis_max [ $graph y2axis cget -max ] + set scale_factor [ expr 100 / ( $y2axis_max - $y2axis_min ) ] + set scale_min [ expr $y2axis_min + ( $yaxis_min / $scale_factor )] + set scale_max [ expr $y2axis_min + ( $yaxis_max / $scale_factor ) ] + + $graph y2axis configure -min $scale_min -max $scale_max + +} +#----------------------------------------------------------------------------------------------------- +# creates a widget to enter names 'rename graph' & 'name of postscript file' + +proc entry_selection_box {window_name mode {xreal "" } {yreal ""} } { + + scan [wm geometry $window_name ] "%dx%d+%d+%d" width height xpos ypos + catch {destroy .markerEntry } + toplevel .markerEntry + wm geometry .markerEntry 300x70+[expr $xpos + ($width / 2 ) - 150]+[expr $ypos + ($height /2) - 35] ;# position in centre + + set ::temp_window_name $window_name + + #----- + if {$mode == "graph" } { + + wm title .markerEntry "Rename Graph" + + pack [ label .markerEntry.l -text "Enter text:" ] -fill x + pack [ entry .markerEntry.e -textvariable ::marker_entry_text -background white ] -fill x + set ::marker_entry_text [wm title $window_name] + + pack [ button .markerEntry.b -text "OK" -command {wm title $::temp_window_name $marker_entry_text ; catch {destroy .markerEntry } } ] + + bind .markerEntry.e {wm title $::temp_window_name $marker_entry_text ; catch {destroy .markerEntry } } + + focus -force .markerEntry.e + } + + #----- + if {$mode == "save_to_postscript" } { + + wm title .markerEntry "Save to postscript" + + pack [ label .markerEntry.l -text "Enter file name:" ] -fill x + pack [ entry .markerEntry.e -textvariable ::marker_entry_text -background white ] -fill x + set ::marker_entry_text "temp.ps" + + pack [ button .markerEntry.b -text "OK" -command { postscript_bw $::temp_window_name.scope.g $marker_entry_text ; catch {destroy .markerEntry } } ] + + bind .markerEntry.e { postscript_bw $::temp_window_name.g $marker_entry_text ; catch {destroy .markerEntry } } + focus -force .markerEntry.e + } + +} + +#----------------------------------------------------------------------------------------------- + +proc tclspice_motion_handler { widget x y } { + + # puts $widget + # puts $x + # puts $y + # puts [$widget axis invtransform x $x] + # [%W axis invtransform x %x] +} + +#----------------------------------------------------------------------------------------------------------- +#blt calls this back when doing the ticks --- replace the floating value with an engineering value +proc graph_Y_axis_callback { widget value } { + return [ float_eng_spice $value] +} + +#------------------------------------------------------------------------------------------------------------- + + +######################################################################## +# Postscript +# +######################################################################## + +#Changes to b/w, removes marker points etc +proc postscript_bw { graph_name filename } { + + #make bw with dashes instead of colours + postscript_make_bw $graph_name + postscript_add_dashes $graph_name + + #postscript + $graph_name postscript output $filename + + foreach trace_bw [ $graph_name element names ] { + $graph_name line configure $trace_bw -symbol {} ;#- turn off symbols + $graph_name line configure $trace_bw -color black ;#- turn to black + } +} + +#---------------------------------------------------------------------------------------------- +#replaces colours with black + +proc postscript_make_bw { graph_name } { + + foreach trace_bw [ $graph_name element names ] { + $graph_name line configure $trace_bw -symbol {} ;#- turn off symbols + $graph_name line configure $trace_bw -color black ;#- turn to black + } +} + +#--------------------------------------------------------------------------------------------------- +#replaces colours with dashes + +proc postscript_add_dashes { graph_name } { + + set tracecnt 0 + + #scan through traces on the graph + foreach trace_bw [ $graph_name element names ] { + + #-dashlist etc - either a specific one, or the incremental one + if { [info exists ::dashlist($trace_bw)] } { + set dashes $::dashlist($trace_bw) ;#- if specific dash assigned to the trace + } elseif { [info exists ::dashlist($tracecnt)] } { + set dashes $::dashlist($tracecnt) ;#- if within number of traces + } else { set dashes $::dashlist(default) } + + $graph_name line configure $trace_bw -dashes $dashes + + incr tracecnt + } +} + + + + + +#-------------------------------------------------------------------------------------------------------- +#Routine to clear out the graph ready for re-plotting +proc spice_gr_cleargraph { graph_name } { + + global disp_zoom_level + set ::trace_index_counter 0 ;#- clear the count + + # returns graph to zoom 1:1 + if {$disp_zoom_level >0} { + Zoom1:1 $graph_name + } + + #remove old traces + foreach trace_to_zap [ $graph_name element names ] { + $graph_name element delete $trace_to_zap + } + + #remove old markers + foreach trace_to_zap [ $graph_name marker names ] { + $graph_name marker delete $trace_to_zap + } + + # remove vectors associated with traces + +} + +#----------------------------------------------------------------------------------------------------- + +# spice::bltplot callback handler --- comes here with each pair of vectors, can +# +# On entry:- +# +# ::graph_to_use - is the destination blt::graph - set before issuing the spice::bltplot command +# ::linecolour("tracename") - hash for colour lookup e.g linecolour(A0) might be red +# +# ?name - name of trace +# ?type - type e.g. voltage current time +# ?units - e.g. amp second +# +# ::spice::X_data - X axis data - for this pair of values +# ::spice::Y_data - Y axis data +# +# On return:- +# +# global blt vectors "::${graph_to_use}_X0...?" has the trace number in for X, similar for Y. +# e.g. ::.g_Y0 etc exist +# +# global list of spice vectors "::${graph_to_use}_plotlist gets e.g. Q0 Q1 etc on it +# - used to do re-plot when gui restored +# +# blt graph $::graph_to_use will have elements of the names of the Y trace of spice + + + +# not used +proc spice_gr_Plot { Xname Xtype Xunits Yname Ytype Yunits } { + + puts "$Xname $Xtype $Xunits $Yname $Ytype $Yunits " + + #set vector names + set xvect_name "::${::graph_to_use}_X$::trace_index_counter" + set yvect_name "::${::graph_to_use}_Y$::trace_index_counter" + + + #copy global to new vectors + #- creates e.g. ::vect_time for time + blt::vector create $xvect_name + $xvect_name set $::spice::X_Data(:) + + #- creates e.g. ::vect_a0 for a0 + blt::vector create $yvect_name + $yvect_name set $::spice::Y_Data(:) + + #create an element on the graph called $Yname using the X and Y vectors + #-choose colour etc + if { [info exists ::linecolour($Yname)] } { + set colour $::linecolour($Yname) ;#- if specific colour assigned to the trace + } elseif { [info exists ::linecolour($::trace_index_counter)] } { + set colour $::linecolour($::trace_index_counter) ;#- if within number of traces + } else { + # set colour $::linecolour(default) + + # creates a random colour + set randred [format "%03x" [expr {int (rand() * 4095)}]] + set randgreen [format "%03x" [expr {int (rand() * 4095)}]] + set randblue [format "%03x" [expr {int (rand() * 4095)}]] + set colour "#$randred$randgreen$randblue" + } + + #-add line + $::graph_to_use element create $Yname -xdata "$xvect_name" -ydata "$yvect_name" \ + -symbol "circle" \ + -linewidth 2 \ + -pixels 3 + + $::graph_to_use line configure $Yname -color $colour + + #-move to next colour + incr ::trace_index_counter +} + +#------------------------------------------------------------------------------------------------------------ +#handler for tclreadline - does command completion for finding the plot traces +# -see main code where this is set + +proc plot_command_readline_completer { word start end line } { + + set match {} ;#- no match found yet + set shortest_string_len 1000 ;#- will always get shorter + set matches 0 ;#- totaliser for matches + + #check for "plot" command line + if { [ regexp {\s+plot} " $line " ] } { + #found plot near the start of the line + + #check if punter is asking for a variable + if { $start >= 5 } { + + #-find the last item typed so far + set arglist [split $line " "] + set lastarg [lindex $arglist end] + + #-search against the possible spice variables + set possibility_list {} + + set varlist [spice::plot_variables 0] ;#- get current spice traces + + foreach possibility $varlist { + #check for exact match + if {$lastarg == $possibility} { + set possibility_list $possibility + break + } + + #check for close match + if { [string match "${lastarg}*" $possibility] } { + lappend possibility_list $possibility ;#- add to list + incr matches ;#-add another match + #update shortest len + if { [string length $possibility] < $shortest_string_len } { + set shortest_string_len [string length $possibility] + } + } + } + + #check for exact match + if { $lastarg == $possibility } { set match $lastarg } else { set match "" } + + #for multiple matches, find the longest common bit of string to return readline style + #-loop for each character + set done 0 + for {set charindex 0} { $charindex < $shortest_string_len } { incr charindex } { + + #-get char from one string + set char [string index [lindex $possibility_list 0] $charindex] + + #check in each of other strings for same character + for {set cnt 1} {$cnt < $matches} {incr cnt} { + + set char_alt [string index [lindex $possibility_list $cnt] $charindex] + + if {$char != $char_alt } { + #found a mismatch, so exit now + set done 1 + break + } + if { $done == 1} { break } + } + if { $done == 1} { break } + } + + #return the longest common portion which matches + set common_bit [string range [lindex $possibility_list 0] 0 [expr $charindex-1] ] + + #sort the return values into alphabetic order + set possibility_list [lsort -dictionary $possibility_list] + + #return parameters for the readline + return [list $common_bit $possibility_list] ;# + + } ;#- end of if starting at the right place + + } ;#- end of if got plot in the line + + ::tclreadline::ScriptCompleter $word $start $end $line +} + +#---------------------------------------------------------------------------------------------------- + +#Eng units stuff +set ::significant_digits 4 ;#- mininum signficant digits to show - might show more e.g. 125 will be shown no matter what + +#lookup hashes +set ::table_eng_float_MSC(T) 1e+12 +set ::table_eng_float_MSC(G) 1e+9 +set ::table_eng_float_MSC(M) 1e+6 +set ::table_eng_float_MSC(K) 1e+3 +set ::table_eng_float_MSC(\n) 1; # Will need to change? +set ::table_eng_float_MSC(m) 1e-3 +set ::table_eng_float_MSC(u) 1e-6 +set ::table_eng_float_MSC(\u03BC) 1e-6 +set ::table_eng_float_MSC(n) 1e-9 +set ::table_eng_float_MSC(p) 1e-12 +set ::table_eng_float_MSC(f) 1e-15 +set ::table_eng_float_MSC(a) 1e-18 + +#inverse hash +set ::table_float_eng_MSC(1e+12)" T" +set ::table_float_eng_MSC(1e+9) " G" +set ::table_float_eng_MSC(1e+6) " M" +set ::table_float_eng_MSC(1e+3) " K" +set ::table_float_eng_MSC(1) " " +set ::table_float_eng_MSC(1e-3) " m" +set ::table_float_eng_MSC(1e-6) " \u03BC" +set ::table_float_eng_MSC(1e-9) " n" +set ::table_float_eng_MSC(1e-12) " p" +set ::table_float_eng_MSC(1e-15) " f" +set ::table_float_eng_MSC(1e-18) " a" + +#----------------------------------------------------------------------------------------------------------- +# Returns best abbreviated number from a real input +proc float_eng_spice {num {forceunit ""} } { + + #On entry, forceunit can be u,p whatever as an option to force the result + # into that unit rather than the best one + # Or, can be "1" + + + #30/5/02 - force the unit and return no decimal places if force is on + if { $forceunit != "" } { + + if { $forceunit == "1" } { + + return [format %.0f $num ] ;#- just return the number + } else { + #-fixed suffix + set multiplier $::table_eng_float_MSC($forceunit) ;#- look up number from the suffix + } + + if { $forceunit == "u" } { set forceunit "\u03BC" } ;#- use the nicer u figure + + #zero decimal places + set returnval "[format %.0f [expr $num / $multiplier]] $forceunit"; + return $returnval + } + #end 30/5/02 + + #Normal operation... + + set indexes [lsort -real -decreasing "[array names ::table_float_eng_MSC]"] + + set out {}; # If no suffix found, return number as is + + if "$num != 0" { + foreach index $indexes { + + set newnum "[expr {$num / $index}]" + set whole 0 + set fraction 0; # In case of integer input + + # regexp {^(\d+)} $newnum whole; # Messy! + # Uses stupid variable to save having to do two regexp's + regexp {^(\d+)\.*(\d*)} $newnum stupid whole fraction + + if {[expr {[string match {*e*} $newnum] == 0}] && \ + [expr {$whole >0 }] } { + set suf "$::table_float_eng_MSC($index)" + + #work out how many decimal places to show - fixed to 4 significant digits + set decimal_length_to_show [ expr ( ($::significant_digits - 1 ) - [ string length $whole ] ) ] + if { $decimal_length_to_show >= 0 } { + + if { $fraction == "" } { + set newnm $whole + } else { + #need some decimal places to give the resolution required + set newnum "$whole.[string range $fraction 0 [ expr ($decimal_length_to_show)] ]" + } + + } else { + #no decimal places + set newnum $whole ;#- just use the whole, no decimals + } + + set out "$newnum$suf" + return "$out" + break + } else { + #puts "[expr {fmod($newnum,1)}] is not big enough" + } + } + } + if {[string equal $out {}]} { + #puts {Appropriate suffix not found} + return $num + } +} + + +#------------------------------------------------------------------------------------------------------------ + +set ::spice_run 0 ;#- not yet run +set ::spicefilename "" ;#- complete name of spice file + +#------------------------------------------------------------------------------------------------------------- +#Initialises the gui +proc spice_init_gui { spicefile } { + + #load spice file if given + if { $spicefile != "" } { + + # test if 'spicefile' is a project + if { [regexp {.swp} $spicefile] } { + + # unpacks the project + project_open $spicefile + + # generates list of project files + set file_list [ project_file_list $spicefile ] + + for { set i 0 } { $i < [ llength $file_list ] } { incr i } { + + set file_name [ lrange $file_list $i $i ] + + # searches for the circuit file + if { [ string range $file_name end-3 end] == ".cir" } { + set ::spicefilename $file_name ;#- save name + } + } + + # no spice file found + if { $::spicefilename == ""} { error "no spice file found in project"} + + + + } else { + + set ::spicefilename $spicefile ;#- save name + } + + spice::source $::spicefilename + wm title . "spicewish - $spicefile" + } + + #pack the gui + pack [ frame .control_butts] -side left -anchor n + + pack [ button .control_butts.b_stop -text "STOP " -command { spice::stop }] -fill x + + pack [ button .control_butts.b_go -text "GO" -command { + if { $spice_run == 0 } { + spice::bg run + set spice_run 1 ;#- has now run once, so next time resume + } else { spice::bg resume } + }] -fill x + + pack [ button .control_butts.b_plot -text "Plot" -command "trace_selection_box"] -fill x + + # pack [ button .control_butts.b_savegui -text "Savegui" -command { save_all_nutmeg_windows }] -fill x + + # pack [ button .control_butts.b_restgui -text "Restorgui" -command { restore_all_nutmeg_windows } ] -fill x + + pack [ button .control_butts.b_notes -text "Edit" -command {edit_window } ] -fill x + + pack [ button .control_butts.b_saveProject -text "Save Pro" -command { project_save $::spicefilename }] -fill x + + pack [ button .control_butts.b_quit -text "Quit" -command { exit }] -fill x + + # replots scopes if opening a project file + if { [regexp {.swp} $spicefile] } { restore_all_nutmeg_windows } + +} + +###################################################################### +# editing - spice file - project notes +# +###################################################################### + +proc edit_window { } { + + if { [ winfo exists .edit] } { + pack .edit + } else { + + pack [ frame .edit ] -expand 1 -fill both + blt::tabnotebook .edit.tnb + + set count 0 + foreach tab { "spice_file" "project_notes"} { + + .edit.tnb insert end -text [ string totitle $tab ] + .edit.tnb configure -tearoff 0 + + pack [ frame .edit.tnb.$tab ] -expand 1 -fill both + .edit.tnb tab configure $count -window .edit.tnb.$tab -fill both + + pack [ frame .edit.tnb.$tab.buttons ] -side bottom + + if { $tab == "spice_file" } { + set ::text_search 0 + set ::text_string "" + pack [ entry .edit.tnb.$tab.buttons.search_entry -background white -textvariable ::text_string] -side left + pack [ checkbutton .edit.tnb.$tab.buttons.search_select -variable ::text_search -command edit_text_search ] -side left + } + + pack [ button .edit.tnb.$tab.buttons.b_cancel -text "Close" -command "pack forget .edit"] -side left + pack [ button .edit.tnb.$tab.buttons.b_save -text "Save" -command "save_file_from_text .edit.tnb.$tab.text $tab"] -side left + + if { $tab == "spice_file" } { + pack [ button .edit.tnb.$tab.buttons.b_reRun -text "Re Run" -command reRun ] -side left + } + + #----------------------------------------------- + proc reRun { } { + + save_all_nutmeg_windows reRun + spice::halt + spice::stop + spice::reset + spice::source $::spicefilename + + after 100 + + set ::spice_run 0 + restore_all_nutmeg_windows + } + #------------------------------------------------ + + pack [ scrollbar .edit.tnb.$tab.scroll -command ".edit.tnb.$tab.text yview" ] -side right -fill y + pack [ text .edit.tnb.$tab.text -background white -yscrollcommand ".edit.tnb.$tab.scroll set"] -expand 1 -fill both ;# pack last due top resizing + + + # load the text file + if { $tab == "spice_file" } { + load_file_to_text .edit.tnb.$tab.text $::spicefilename + .edit.tnb.$tab.text configure -background LemonChiffon + edit_highlight_spicevariables .edit.tnb.$tab.text + } + + if { $tab == "project_notes" } { + load_file_to_text .edit.tnb.$tab.text $::spicefilename.project_notes.txt + } + + incr count + } + pack .edit.tnb -fill both -expand 1 + } +} + +#------------------------------------------------------------------------------------------------------ +# saves text widget to file +proc save_file_from_text { w tab } { + + if {$tab == "project_notes" } {set filename "$::spicefilename.project_notes.txt" } + if {$tab == "spice_file" } {set filename "$::spicefilename" } + + # saves text window to .txt file + set data [$w get 1.0 {end -1c}] + set fileid [open $filename w] + puts -nonewline $fileid $data + close $fileid +} + +#----------------------------------------------------------------------------------------------------------- +# loads files into a text widget +proc load_file_to_text {w file} { + + # test if file exsists + if { [ file exists $file ] == 0 } { return } + + set f [open $file] + $w delete 1.0 end + while {![eof $f]} { $w insert end [read $f 10000] } + close $f +} + +#------------------------------------------------------------------------------------ +# highlight text in entry field + +proc edit_text_search { } { + + set w ".edit.tnb.spice_file.text" + + if { $::text_search == 0 } { + $w tag delete search + return + } + + $w tag remove search 0.0 end + if {$::text_string == ""} { return } + set cur 1.0 + while 1 { + set cur [$w search -count length $::text_string $cur end] + if {$cur == ""} { break } + + $w tag add search $cur "$cur + $length char" + set cur [$w index "$cur + $length char"] + + $w tag configure search -foreground white + $w tag configure search -background red + } +} + +#------------------------------------------------------------------------------------------- +# searches through the text highlighting 'strings' with passed 'colour' + +proc text_search {w string colour} { + + # string passes the starting character eg "c" for capacitors + + set tag $string + $w tag remove search 0.0 end + + if {$string == ""} { return } + + set start 1.0 + while 1 { + set start [$w search -count length $string $start end] + if {$start != "" } { + + # returns the position of the next " " + set finish [$w search -count length " " $start [expr $start + 1 ]] + + if {( $start == "") || ($finish =="")} { + break + + } else { + + # ad call + # doesn't seem to work for the L becuase they go overf two lines this crashes this + # need to test if the start anf finish are on the same line + + regexp {([0-9]+).([0-9]+)} $finish finish_match finish_row finish_col + + set valid 1 + + # if start character not at start of line + + regexp {([0-9]+).([0-9]+)} $start start_match start_row start_col + + if { $start_match == $start } { + if { $start_col != 0 } { + + if { [ $w get "$start_row.[ expr $start_col - 1]" ] != " " } { set valid 0 } + } + } + + # test that start and finish are on the same line + if { $start_row != $finish_row } { set valid 0 } + + if { $valid == 1 } { + # set text passed colour between + $w tag add $tag $start $finish + $w tag configure $tag -foreground $colour + } + } + + + } else { break } + + + # error trap + # previous character must == "" // beging of line f + + set start [$w index "$start + $length char"] + + } +} + +#-------------------------------------------------------------------------------------- + +proc edit_highlight_spicevariables { w } { + text_search $w L blue + text_search $w V red + text_search $w R green + +} + +#-------------------------------------------------------------------------------------- + +########################################################################### +# selecting traces +# +########################################################################### + +proc trace_selection_box { {args ""} {graph_name "" } } { + + + regexp {(.[0-9A-z]+)} $graph_name window_name + #set scope_name "$scope_name.scope.g" + + # error if spice not been run + if { $::spice_run == 0 } { + set error_mesage [tk_messageBox -message "Simulation not run" -type okcancel -icon error -parent . ] + return + } + + catch {destroy .trace_selection_box } + toplevel .trace_selection_box + wm title .trace_selection_box "Select Traces" + + blt::tabnotebook .trace_selection_box.tnb + pack .trace_selection_box.tnb -fill both + + set count 0 + foreach tabs { voltage current } { + + .trace_selection_box.tnb insert end -text [ string totitle $tabs] + .trace_selection_box.tnb configure -tearoff 0 + + frame .trace_selection_box.tnb.$tabs + + .trace_selection_box.tnb tab configure $count -window .trace_selection_box.tnb.$tabs + + pack [ frame .trace_selection_box.tnb.$tabs.f ] + + eval "blt::hierbox .trace_selection_box.tnb.$tabs.f.hierbox -hideroot true \ + -yscrollcommand {.trace_selection_box.tnb.$tabs.f.vert set} -xscrollcommand {.trace_selection_box.tnb.$tabs.f.vert set} -background white" + + eval "scrollbar .trace_selection_box.tnb.$tabs.f.vert -orient vertical -command {.trace_selection_box.tnb.$tabs.f.hierbox yview} -width 20 " + + .trace_selection_box.tnb.$tabs.f.hierbox insert 1 $tabs -labelfont {times 10} + .trace_selection_box.tnb.$tabs.f.hierbox toggle 1 + + pack .trace_selection_box.tnb.$tabs.f.hierbox -side left + pack .trace_selection_box.tnb.$tabs.f.vert -side left -fill y + + # bind button 1/2 to move through the tabs + .trace_selection_box.tnb.$tabs.f.hierbox bind all { .trace_selection_box.tnb select left } + .trace_selection_box.tnb.$tabs.f.hierbox bind all { .trace_selection_box.tnb select right } + + # bind button 1 to the select the traces from the list + .trace_selection_box.tnb.$tabs.f.hierbox bind all { + set hierbox_path %W + set _index [ $hierbox_path index current] + + if { [ $hierbox_path entry cget $_index -labelfont] == {times 10}} { + $hierbox_path entry configure $_index -labelfont {times 12 bold} + } else { + $hierbox_path entry configure $_index -labelfont {times 10} + } + } + + # load all the traces + foreach trace [spice::spice_data] { + if {[lindex $trace 1] == $tabs} { + + # traces from spice + .trace_selection_box.tnb.$tabs.f.hierbox insert -at 1 end [lindex $trace 0] -labelfont {times 10} + + # saved plots + if { ( $args == "update" ) || ($graph_name != "" ) } { + if { [ info exists ::OLD_plotList($window_name)] } { + set temp "[lindex $trace 0]\_OLD" + if { [lcontain $::OLD_plotList($window_name) $temp ] } { + .trace_selection_box.tnb.$tabs.f.hierbox insert -at 1 end $temp -labelfont {times 10} + } + } + } + + } + } + + + + if { ( $args == "update" ) || ($graph_name != "" ) } { + # updating the traces from an existing scope + set pre_selected [$graph_name element names] + foreach trace $pre_selected { + + # Nasty and slow -quick fix + for {set i 0} { $i < [ .trace_selection_box.tnb.$tabs.f.hierbox index end ] } {incr i} { + if { [ .trace_selection_box.tnb.$tabs.f.hierbox entry cget $i -label ] == $trace } { + .trace_selection_box.tnb.$tabs.f.hierbox entry configure $i -labelfont {times 12 bold} + } + } + } + + pack [button .trace_selection_box.tnb.$tabs.b -text "Update" -command "load_plots_from_selection_box update $graph_name " ] -fill x + } else { + pack [button .trace_selection_box.tnb.$tabs.b -text "Plot" -command "load_plots_from_selection_box normal" ] -fill x + } + + incr count + } +} + +#----------------------------------------------------------------------------------------------------------------- +proc load_plots_from_selection_box {mode { graph_name "" } } { + + set list {} + foreach tabs { voltage current } { + for {set i 0} { $i < [ .trace_selection_box.tnb.$tabs.f.hierbox index end ] } {incr i} { + + if {[ .trace_selection_box.tnb.$tabs.f.hierbox entry cget $i -labelfont] == {times 12 bold}} { + lappend list [.trace_selection_box.tnb.$tabs.f.hierbox get $i ] + } + } + } + + # error no traces seleted + if { [ llength $list ] == 0 } { + set temp [tk_messageBox -message "No traces seleted" -type okcancel -icon error -parent .trace_selection_box] + if {$temp == "cancel" } { destroy .trace_selection_box } + return + } + + # stops spice if running + if { [spice::running] } { spice::stop } + + if {$mode == "normal" } { + # creates a new plot + plot $list + } + if {$mode == "update" } { + # updates the exsisting plots + update_traces $graph_name $list + } + + destroy .trace_selection_box +} + +#--------------------------------------------------------------------------------------------------------- +#set version "0.2.5" +#set libdir "/usr/lib" +set version "%VERSION%" +set libdir "%LIB_DIR%" + +package ifneeded spice $version [list Loadspice $version $libdir] + +#----------------------------------------------------------------------------------------------------------------- +# window management routines + +proc save_all_nutmeg_windows { { mode ""} } { + + # mode ?? + # - reRun - re runs the simulation saving the current traces vectors, and plotting them against the re run simulation + # - save - simply saves all the current traces vectors to redisplay later + + + #open file ???.cir.gui_settings.tcl + set fh_settings [open "${::spicefilename}.gui_settings.tcl" w] + puts $fh_settings "\# tclspice gui settings file - evaluated by tcl to restore the previous 'nutmeg' session" + + # simulation steps + puts $fh_settings "\# saved_plots_sim_steps $spice::steps_completed " + + puts $fh_settings "\# spicewish version : $::spicewishversion" + + #save all window positions as tcl statements + foreach winname [all_nutmeg_toplevels] { + + puts $fh_settings "\n\n\#DATA FOR WINDOW $winname" + + #get toplevelnum + regexp {.tclspice([0-9]+)} $winname match toplevelnum + + #save to restore the toplevel counter + puts $fh_settings "set ::toplevel_count \$::toplevel_count " + + # don't save traces with "_OLD" + + # at moment saving the curent none _OLD traces, and making _OLD plot for that trace + # this re work if to save graph with out the re plots + set trace_list [ .tclspice$toplevelnum\.scope.g element names ] + set clean_trace_list {} + + foreach trace $trace_list { + puts " trace : $trace" + + if { $mode == "reRun" } { + if { [ regexp {_OLD} $trace ] != 1 } { + lappend clean_trace_list $trace + lappend clean_trace_list $trace\_OLD + } + } + + if {$mode == "save" } { + lappend clean_trace_list $trace + } + + } + puts $fh_settings "plot { $clean_trace_list }" + + # save the old plot list to a global variable, so that it can be read when the trace selection box is loaded + set OLD_plotList { } + foreach trace $clean_trace_list { + if { [ regexp {_OLD} $trace ] } { + lappend OLD_plotList $trace + } + } + + puts $fh_settings "set ::OLD_plotList(.tclspice\[expr \$::toplevel_count - 1\]) { $OLD_plotList } " + + # save title of graph + set graph_name [wm title $winname] + + if { $mode == "reRun" } { + if { [regexp {rerun([0-9]+)} $graph_name match rerun_value] == 1 } { + set reRun_number [ expr $rerun_value + 1] + set start [ string first " - " $graph_name ] + set graph_name [ string range $graph_name [ expr $start + 3] end ] + set graph_name "rerun$reRun_number - $graph_name " + } else { + set reRun_number 1 + set graph_name "rerun$reRun_number - $graph_name " + } + } + + # use [expr \$::toplevel_count - 1\] because procedure plot increments the toplevel_count at the end + + puts $fh_settings "wm title .tclspice\[expr \$::toplevel_count - 1\] \" $graph_name\"" + + # save the measurment + puts $fh_settings "set ::graph_dx(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_dx(.tclspice$toplevelnum)" + puts $fh_settings "set ::graph_dy(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_dy(.tclspice$toplevelnum)" + puts $fh_settings "set ::graph_x1(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_x1(.tclspice$toplevelnum)" + puts $fh_settings "set ::graph_y1(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_y1(.tclspice$toplevelnum)" + puts $fh_settings "set ::graph_x2(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_x2(.tclspice$toplevelnum)" + puts $fh_settings "set ::graph_y2(.tclspice\[expr \$::toplevel_count - 1\]) $::graph_y2(.tclspice$toplevelnum)" + + # save trace place suff + puts $fh_settings "" + puts $fh_settings "set ::trace_place_selected(.tclspice\[expr \$::toplevel_count - 1\]\.scope.g) $::trace_place_selected(.tclspice$toplevelnum\.scope.g) " + + # not quite right because if trace place turned on then off again values not saved to file + + # if trace place been used + if { $::trace_place_selected(.tclspice$toplevelnum\.scope.g) } { + + puts $fh_settings "TracePlace .tclspice\[expr \$::toplevel_count - 1\]" + + # save the list of traces for each slot + for {set col_cnt 0 } {$col_cnt <= $::columns} {incr col_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + if { $::slot_trace_list(.tclspice$toplevelnum\.scope.tp.ft.tracePlaceGrid.f_$col_cnt\_$row_cnt.b) != "" } { + puts $fh_settings "set ::slot_trace_list(.tclspice\[expr \$::toplevel_count - 1\]\.scope.tp.ft.tracePlaceGrid.f_$col_cnt\_$row_cnt\.b) \"$::slot_trace_list(.tclspice$toplevelnum\.scope.tp.ft.tracePlaceGrid.f_$col_cnt\_$row_cnt.b)\" " + } + } + } + + + # saves the dimensions of each slot + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + puts $fh_settings "\grid rowconfigure .tclspice\[expr \$::toplevel_count - 1\]\.scope.tp.ft.tracePlaceGrid $row_cnt -minsize [grid rowconfigure .tclspice$toplevelnum\.scope.tp.ft.tracePlaceGrid $row_cnt -minsize ] " + } + + # enables the selected slot + set slot_location [ TracePlace_active_slot .tclspice$toplevelnum\.scope.tp.ft.tracePlaceGrid ] + if { $slot_location != "" } { + puts $fh_settings "\TracePlace_selected_slot .tclspice\[expr \$::toplevel_count - 1\]\.scope.tp.ft.tracePlaceGrid.f_[lindex $slot_location 0]\_[lindex $slot_location 1].b " + } + } + + # saves the geometry of the window + set geom [winfo geometry $winname] + puts $fh_settings "wm geometry .tclspice\[expr \$::toplevel_count - 1\] $geom" + + # save all the markers + set markerlist [$winname.scope.g marker names] + foreach marker $markerlist { + puts $fh_settings ".tclspice\[expr \$::toplevel_count - 1\].scope.g marker create text -name $marker \ + -coords \{ [$winname.scope.g marker cget $marker -coords]\} \ + -text \"[$winname.scope.g marker cget $marker -text]\" \ + -anchor [$winname.scope.g marker cget $marker -anchor] \ + -background [$winname.scope.g marker cget $marker -background]" + } + + # set up a list of traces append here - rest of routine needs to be after + set index 0 + set tracelist [$winname.scope.g element names] + foreach trace $tracelist { + + if { [regexp {_OLD} $trace ] } { continue } ;# - first time simulation re run + + spice_return_trace_vectors .tclspice$toplevelnum\.scope.g $trace + + blt::vector create saved_plot_X$index + blt::vector create saved_plot_Y$index + + saved_plot_X$index set temp_vector_X + saved_plot_Y$index set temp_vector_Y + + incr index + } + + # need this to be after loop for all graphs + # writing same things opening the file twice + + set fh_oldplot [open "${::spicefilename}.gui_plots.tcl" w] + + #save all the current traces from graph to _OLD + set tracelist [$winname.scope.g element names] + set index 0 + foreach trace $tracelist { + + if { [regexp {_OLD} $trace ] } { continue } ;# - first time simulation re run + + puts $fh_oldplot " " + + blt::vector create tempSaveX + blt::vector create tempSaveY + + tempSaveX set saved_plot_X$index + tempSaveY set saved_plot_Y$index + + puts $fh_oldplot "#$trace\_OLD_X : $tempSaveX(:) " + puts $fh_oldplot "#$trace\_OLD_Y : $tempSaveY(:) " + + blt::vector destroy tempSaveX + blt::vector destroy tempSaveY + + blt::vector destroy saved_plot_X$index + blt::vector destroy saved_plot_Y$index + + incr index + } + } + close $fh_settings + close $fh_oldplot +} + +#-------------------------------------------------------------------------------------------------- + + +#------------------------------------------------------------------------------------------------------ +# creates a progress bar + +proc progress_bar { w {value "" } } { + + if {$value == ""} { + pack [canvas $w -width 100 -height 15 -relief sunken] + $w create rectangle 1 1 5 15 -fill green -tags {bar} + } else { + $w coords bar 1 1 $value 15 + } +} + +#------------------------------------------------------------------------------------------------ +# proc passed number of setps to run before halting + +proc spice_run_steps {pre_run_steps { progressBar "" } } { + + # starts spice + if {$::spice_run == 1} { + if { [$spice::steps_completed] > $pre_run_steps } { return } + spice::bg resume + } else { + set ::spice_run 1 + spice::bg run + } + + # progress bar + if {$progressBar != "" } { + + # check if already packed + if {[ winfo exists .control_butts.progressBar] } { + pack .control_butts.progressBar + progress_bar .control_butts.progressBar.bar 5 + + } else { + pack [frame .control_butts.progressBar ] + pack [label .control_butts.progressBar.label -text "re simulating"] + progress_bar .control_butts.progressBar.bar + update + } + } + + set time 0 + + while { $time < $pre_run_steps } { + sleep 1 + set time $spice::steps_completed + if {$progressBar != "" } { progress_bar .control_butts.progressBar.bar [ expr (($time + 0.001) / $pre_run_steps) * 100 ] } + update + } + spice::halt + pack forget .control_butts.progressBar + +} + +#-------------------------------------------------------------------------------------------------------- +# loads all the scopes that are saved to file + +proc restore_all_nutmeg_windows { } { + + # previous simulations run time + set sim_time [open "${::spicefilename}.gui_settings.tcl" r] + gets $sim_time re_simulation_time + gets $sim_time re_simulation_time + close $sim_time + regexp { ([0-9\.\-e]+)} $re_simulation_time re_simulation_time + + # kills all exsisting scope windows + foreach window [ all_nutmeg_toplevels ] { + destroy $window + + # need to kill vectors as well + } + + spice_run_steps $re_simulation_time progress + + # spice_run_untill $re_simulation_time progress + + source "${::spicefilename}.gui_settings.tcl" + source "${::spicefilename}.gui_plots.tcl" + +} + +#------------------------------------------------------------------------------------------- +#- returns all the "tclspice??" toplevel window names + +proc all_nutmeg_toplevels { } { + + set returnlist "" + + foreach winname [winfo children .] { + + if { [ regexp {.tclspice[0-9]+} $winname ] } { + #found nutmeg type toplevel + lappend returnlist $winname + } + } + + return $returnlist +} + +#--------------------------------------------------------------------------------------------------------------- + +########################################################################## +# zoom controls +# +# Filched from the BLT Demos. May re-write for speed +########################################################################## + +proc y_axis_control { start finish window } { + global zoomInfo + + regexp {.tclspice([0-9]+)} $window match toplevelnum + set graph ".tclspice$toplevelnum\.scope.g" + + Zoom1:1 $graph no_label + + # saves current settings + set cmd {} + foreach margin { xaxis yaxis x2axis y2axis } { + foreach axis [$graph $margin use] { + set min [$graph axis cget $axis -min] + set max [$graph axis cget $axis -max] + set c [list $graph axis configure $axis -min $min -max $max] + append cmd "$c\n" + } + } + set zoomInfo($graph,stack) [linsert $zoomInfo($graph,stack) 0 $cmd] + + set Y_limits [ $graph axis limits y ] + set Y_limits_bot [lindex $Y_limits 0] + set Y_limits_top [lindex $Y_limits 1] + + if { $Y_limits_bot < 0 } { + set Y_limits_bot_t [ expr ( $Y_limits_bot * -1) ] + } else { + set Y_limits_bot_t $Y_limits_bot + } + + set scale [ expr ( $Y_limits_top + $Y_limits_bot_t ) /100.0] + + $graph yaxis configure -min [ expr $Y_limits_bot + ($start * $scale) ] + $graph yaxis configure -max [ expr $Y_limits_bot + ($finish * $scale) ] +} + +#--------------------- + +proc InitStack { graph } { + + global zoomInfo + set zoomInfo($graph,interval) 100 + set zoomInfo($graph,afterId) 0 + set zoomInfo($graph,A,x) {} + set zoomInfo($graph,A,y) {} + set zoomInfo($graph,B,x) {} + set zoomInfo($graph,B,y) {} + set zoomInfo($graph,stack) {} + set zoomInfo($graph,corner) A +} + +#-------------------- +proc ZoomStack { graph {start "ButtonPress-1"} {reset "ButtonRelease-3"} } { + + global zoomInfo zoomMod + + InitStack $graph + + if { [info exists zoomMod] } { + set modifier $zoomMod + } else { + set modifier "" + } + bind bltZoomGraph <${modifier}${start}> { + SetZoomPoint %W %x %y + } + + bind bltZoomGraph <${modifier}${reset}> { + if { [%W inside %x %y] } { + ResetZoom %W + } + } + AddBindTag $graph bltZoomGraph +} + +#-------------------- +proc DestroyZoomTitle { graph } { + global zoomInfo + + if { $zoomInfo($graph,corner) == "A" } { + catch { $graph marker delete "zoomTitle" } + } +} + +#-------------------- +proc Zoom1:1 { graph {noTitle ""}} { + global zoomInfo disp_zoom_level + + $graph axis configure x -min {} -max {} + $graph axis configure y -min {} -max {} + $graph axis configure x2 -min {} -max {} + $graph axis configure x2 -min {} -max {} + InitStack $graph + set disp_zoom_level 0 + if { $noTitle == "" } {ZoomTitleLast $graph } + update + if { $noTitle == "" } { after 2000 "DestroyZoomTitle $graph" } +} + +#-------------------- +proc PopZoom { graph } { + global zoomInfo disp_zoom_level + + set zoomStack $zoomInfo($graph,stack) + + if { [llength $zoomStack] > 0 } { + set cmd [lindex $zoomStack 0] + set zoomInfo($graph,stack) [lrange $zoomStack 1 end] + eval $cmd + ZoomTitleLast $graph +# busy hold $graph + update + after 2000 "DestroyZoomTitle $graph" +# busy release $graph + } else { + catch { $graph marker delete "zoomTitle" } + + } + set disp_zoom_level [expr [llength $zoomInfo($graph,stack)] ] +} + +# Push the old axis limits on the stack and set the new ones + +#----------------------------------------------------------------------- + +proc PushZoom { graph } { + global zoomInfo disp_zoom_level + + eval $graph marker delete [$graph marker names "zoom*"] + if { [info exists zoomInfo($graph,afterId)] } { + after cancel $zoomInfo($graph,afterId) + } + + set x1 $zoomInfo($graph,A,x) + set y1 $zoomInfo($graph,A,y) + set x2 $zoomInfo($graph,B,x) + set y2 $zoomInfo($graph,B,y) + + if { ($x1 == $x2) || ($y1 == $y2) } { + # No delta, revert to start + return + } + + # saves current settings + set cmd {} + foreach margin { xaxis yaxis x2axis y2axis } { + + foreach axis [$graph $margin use] { + set min [$graph axis cget $axis -min] + set max [$graph axis cget $axis -max] + set c [list $graph axis configure $axis -min $min -max $max] + append cmd "$c\n" + } + } + set zoomInfo($graph,stack) [linsert $zoomInfo($graph,stack) 0 $cmd] + + # busy hold $graph + # This update lets the busy cursor take effect. + update + + foreach margin { xaxis x2axis } { + foreach axis [$graph $margin use] { + set min [$graph axis invtransform $axis $x1] + set max [$graph axis invtransform $axis $x2] + if { $min > $max } { + $graph axis configure $axis -min $max -max $min + } else { + $graph axis configure $axis -min $min -max $max + } + } + } + foreach margin { yaxis y2axis } { + foreach axis [$graph $margin use] { + set min [$graph axis invtransform $axis $y1] + set max [$graph axis invtransform $axis $y2] + if { $min > $max } { + $graph axis configure $axis -min $max -max $min + } else { + $graph axis configure $axis -min $min -max $max + } + } + } + # This "update" forces the graph to be redrawn + set disp_zoom_level [expr [llength $zoomInfo($graph,stack)] ] + update + + # busy release $graph +} + +#-------------------- + +# +# This routine terminates either an existing zoom, or pops back to +# the previous zoom level (if no zoom is in progress). +# + +#-------------------- +proc ResetZoom { graph } { + global zoomInfo + + if { ![info exists zoomInfo($graph,corner)] } { + InitStack $graph + } + eval $graph marker delete [$graph marker names "zoom*"] + + if { $zoomInfo($graph,corner) == "A" } { + # Reset the whole axis + PopZoom $graph + + + } else { + global zoomMod + + if { [info exists zoomMod] } { + set modifier $zoomMod + } else { + set modifier "Any-" + } + set zoomInfo($graph,corner) A + bind $graph <${modifier}Motion> { } + } +} + +#-------------------- + +option add *zoomTitle.font -*-helvetica-medium-R-*-*-18-*-*-*-*-*-*-* +option add *zoomTitle.shadow yellow4 +option add *zoomTitle.foreground yellow1 +option add *zoomTitle.coords "-Inf Inf" + +#-------------------- +proc ZoomTitleNext { graph } { + global zoomInfo disp_zoom_level + set level [expr [llength $zoomInfo($graph,stack)] + 1] + + if { [$graph cget -invertxy] } { + set coords "-Inf -Inf" + } else { + set coords "-Inf Inf" + } + $graph marker create text -name "zoomTitle" -text "Zoom #$level" \ + -coords $coords -bindtags "" -anchor nw +} + +#-------------------- +proc ZoomTitleLast { graph } { + global zoomInfo + + set level [llength $zoomInfo($graph,stack)] + if { $level > 0 } { + $graph marker create text -name "zoomTitle" -anchor nw \ + -text "Zoom #$level" + } + if { $level == 0 } { + $graph marker create text -name "zoomTitle" -anchor nw \ + -text "Zoom 1:1" + } +} + +#-------------------- +proc SetZoomPoint { graph x y } { + global zoomInfo zoomMod + if { ![info exists zoomInfo($graph,corner)] } { + InitStack $graph + } + GetCoords $graph $x $y $zoomInfo($graph,corner) + if { [info exists zoomMod] } { + set modifier $zoomMod + } else { + set modifier "Any-" + } + if { $zoomInfo($graph,corner) == "A" } { + if { ![$graph inside $x $y] } { + return + } + # First corner selected, start watching motion events + + #MarkPoint $graph A + ZoomTitleNext $graph + + bind $graph <${modifier}Motion> { + GetCoords %W %x %y B + #MarkPoint $graph B + Box %W + } + set zoomInfo($graph,corner) B + } else { + # Delete the modal binding + bind $graph <${modifier}Motion> { } + PushZoom $graph + set zoomInfo($graph,corner) A + } +} + +#-------------------- + +option add *zoomOutline.dashes 4 +option add *zoomTitle.anchor nw +option add *zoomOutline.lineWidth 2 +option add *zoomOutline.xor yes + +#-------------------- +proc MarchingAnts { graph offset } { + global zoomInfo + + incr offset + if { [$graph marker exists zoomOutline] } { + $graph marker configure zoomOutline -dashoffset $offset + set interval $zoomInfo($graph,interval) + set id [after $interval [list MarchingAnts $graph $offset]] + set zoomInfo($graph,afterId) $id + } +} + +#-------------------- +proc GetCoords { graph x y index } { + global zoomInfo + if { [$graph cget -invertxy] } { + set zoomInfo($graph,$index,x) $y + set zoomInfo($graph,$index,y) $x + + } else { + set zoomInfo($graph,$index,x) $x + set zoomInfo($graph,$index,y) $y + } +} + +#-------------------- +proc AddBindTag { graph name } { + set oldtags [bindtags $graph] + if { [lsearch $oldtags $name] < 0 } { + bindtags $graph [concat $name $oldtags] + } +} + +#-------------------- +proc Box { graph } { + global zoomInfo + + # select which yaxis to take the measurments + # !!!!!!!!!!!!!! need a global variable for when the trace place is selected but returned to normal graph + if { $::trace_place_selected($graph) } { + # trace place grid active + set m_yaxis "y2axis" + } else { + set m_yaxis "yaxis" + } + + + if { $zoomInfo($graph,A,x) > $zoomInfo($graph,B,x) } { + set x1 [$graph xaxis invtransform $zoomInfo($graph,B,x)] + set y1 [$graph yaxis invtransform $zoomInfo($graph,B,y)] + set x2 [$graph xaxis invtransform $zoomInfo($graph,A,x)] + set y2 [$graph yaxis invtransform $zoomInfo($graph,A,y)] + + set my1 [$graph $m_yaxis invtransform $zoomInfo($graph,B,y)] + set my2 [$graph $m_yaxis invtransform $zoomInfo($graph,A,y)] + + } else { + set x1 [$graph xaxis invtransform $zoomInfo($graph,A,x)] + set y1 [$graph yaxis invtransform $zoomInfo($graph,A,y)] + set x2 [$graph xaxis invtransform $zoomInfo($graph,B,x)] + set y2 [$graph yaxis invtransform $zoomInfo($graph,B,y)] + + set my1 [$graph $m_yaxis invtransform $zoomInfo($graph,A,y)] + set my2 [$graph $m_yaxis invtransform $zoomInfo($graph,B,y)] + } + set coords { $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1 } + if { [$graph marker exists "zoomOutline"] } { + $graph marker configure "zoomOutline" -coords $coords + } else { + set X [lindex [$graph xaxis use] 0] + set Y [lindex [$graph yaxis use] 0] + $graph marker create line -coords $coords -name "zoomOutline" \ + -mapx $X -mapy $Y + set interval $zoomInfo($graph,interval) + set id [after $interval [list MarchingAnts $graph 0]] + set zoomInfo($graph,afterId) $id + } + + regexp {(.[0-9A-z]+)} $graph window_name + set ::graph_dx($window_name) [expr ($x2 - $x1)] + set ::graph_dy($window_name) [expr ($my2 - $my1)] + set ::graph_x1($window_name) $x1 + set ::graph_y1($window_name) $my1 + set ::graph_x2($window_name) $x2 + set ::graph_y2($window_name) $my2 +} + +### End of software theft + + + +########################################################################## +# trace and place +# +# +########################################################################## + + +# procedure that calls traceplace form menu pass the %w +# then calls procedure active button if this is passed a value then this one will become active + +#------------------------------------------------------------------------------------------ +# returns to normal graph from trace place grid + +proc NormalGraph { window } { + + # un packs the trace and place widget + pack forget $window.scope.tp.l + pack forget $window.scope.tp.ft + $window.scope.tp configure -width 1 + + # un packs the trace and place information bar at bottom + pack forget $window.tracePlaceInfoBar.fakeGraph + pack forget $window.tracePlaceInfoBar.legends + $window.tracePlaceInfoBar configure -height 1 + + # turns off all the legenfs relief + foreach trace [ $window.scope.g element names ] { + $window.scope.g element configure $trace -labelrelief flat + $window.scope.g element configure $trace -hide 0 + } + + $window.scope.g yaxis configure -min "" -max "" -hide 0 + $window.scope.g y2axis configure -hide 1 + + # turn off the + spice_update_traces $window.scope.g + +} + +#----------------------------------------------------------------------------------------------------------------- +# creates the trace and place grid +# re packs if already created + +proc TracePlace { window } { + global columns rows max_traces_per_slot total_hei desired_width desired_height + + # unpacks the trace place grid + if {$::trace_place_selected($window.scope.g) != 1} { + NormalGraph $window + $window.scope.g y2axis configure -hide 1 + $window.scope.g yaxis configure -hide 0 + return + } + + set window_clean $window + set window "$window\.scope.tp" + $window_clean.scope.g yaxis configure -hide 1 + $window_clean.scope.g y2axis configure -hide 0 + #trace place info bar , bottom. if already created once, then just re pack + if { [ winfo exists $window\.l ] == 1 } { + + # re packs the trace and place slots widget + pack $window\.l -side bottom -fill both + pack $window\.ft -side top -expand 1 -fill y + + # re packs the trace and place information bar at bottom + pack $window_clean.tracePlaceInfoBar.fakeGraph -side left + pack $window_clean.tracePlaceInfoBar.legends -side left + + # refreshes tarce and place grid + TracePlace_update $window_clean + + return + } + + + #---- + #-------- + # construction in progress + + # creates a fake graph that is never seen, so that its legend can be used for the trace and place information bar + set fakeGraph_w [ blt::graph $window_clean.tracePlaceInfoBar.fakeGraph ] + $fakeGraph_w configure -height 20 -width 20 -plotrelief flat + $fakeGraph_w xaxis configure -hide 1 + $fakeGraph_w yaxis configure -hide 1 + $fakeGraph_w legend configure -position $window_clean.tracePlaceInfoBar.legends -columns 1 + + pack $fakeGraph_w -side left + pack $window_clean.tracePlaceInfoBar.legends -side left + + # graph legend active on button 3 + $fakeGraph_w legend bind all { %W legend activate [%W legend get current] } + $fakeGraph_w legend bind all { %W legend deactivate [%W legend get current]} + + # creates the drag and drop packet for each of the plots in the trace place info bar + blt::drag&drop source $window_clean.tracePlaceInfoBar.legends -packagecmd {make_package %t %W } -button 1 + set token [blt::drag&drop token $window_clean.tracePlaceInfoBar.legends -activebackground blue ] + pack [ label $token.label -text "" ] + + $window_clean.tracePlaceInfoBar.fakeGraph element create "" -symbol "" + + #---------- + #------- + + set columns 3 ;#- will give 2^rows of total choices for trace positons. + set rows [expr pow(2, $columns) ] ;# -this many rows (see above) + set min_row_hei 1 ; #- cant click a single element smaller than this (but still can get smaller than this is squashed) + set max_traces_per_slot 8 + + + #- call procedure to get the wm height of the graph + #- wm geometry .tclspice 1 + #- perform scan to get height - minus 24 for title plus fotter included padding of graph?? + #- set desired height to this + + # global columns rows + pack [ label $window.l -text "" -borderwidth 4 ] -side bottom -fill both + pack [ frame $window.ft -width 50 ] -side top -expand 1 -fill y + pack [ frame $window.ft.tracePlaceGrid -borderwidth 4 ] -side left -expand 1 -fill y + + + # set desired_height 70 + # set desired_width 20 ; #-initial width of the panel - can still dynamically resize the width + + scan [winfo geometry $window.ft.tracePlaceGrid ] "%dx%d+%d+%d" desired_width desired_height xpos ypos + set total_hei [expr int( ( $desired_height / $rows)* $rows )] + + set window "$window.ft.tracePlaceGrid" + set ::window $window + + for {set columns_cnt 0 } {$columns_cnt <= $columns} {incr columns_cnt } { + + set row_span_holder [ expr pow(2, $columns) / pow(2, $columns_cnt) ] + set row_span_holder [ expr int($row_span_holder) ] + + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $columns_cnt) - 1) ] } {incr row_cnt } { + + # set slotnum [ slotnum_FROM_column_gridrow $columns_cnt $row_cnt $row_span_holder] + set slotnum [ expr int (int ( [expr $row_cnt* $row_span_holder] / [expr pow(2, ($columns - $columns_cnt)) ]) + [expr int (pow(2, $columns_cnt))]) ] + + # create a frame for each slot to hold the button and the canvas + set slot_frame [ frame $window.f_$columns_cnt\_$row_cnt ] + + + # creates button/slot + set button_path [button $window.f_$columns_cnt\_$row_cnt.b -padx 3 -pady 0 -activebackground lightgrey -command "TracePlace_selected_slot $window.f_$columns_cnt\_$row_cnt.b "] + + # creates a clean trace list, for all the traces associated with that slot + set ::slot_trace_list($button_path) {} + + # sets button to be a target for drag and drop packet + # - when active adds trace to slots list + blt::drag&drop target $button_path handler string { + append ::slot_trace_list(%W) "%v " + TracePlace_selected_slot %W update + } + + + # creates a drag drop packet so that all the slots traces can be moved + blt::drag&drop source $button_path -packagecmd { make_package %t %W tracePlaceGrid } -button 1 + set token2 [blt::drag&drop token $button_path -activebackground blue ] + pack [ label $token2.label -text "" ] + + + # button bindings + bind $button_path { regexp {(.[0-9A-z]+)} %W temp; TracePlace_wheel_move up $temp.scope.tp.ft.tracePlaceGrid} + bind $button_path { regexp {(.[0-9A-z]+)} %W temp; TracePlace_wheel_move up $temp.scope.tp.ft.tracePlaceGrid } + bind $button_path { regexp {(.[0-9A-z]+)} %W temp; TracePlace_wheel_move "down" $temp.scope.tp.ft.tracePlaceGrid } + bind $button_path { regexp {(.[0-9A-z]+)} %W temp; TracePlace_wheel_move "down" $temp.scope.tp.ft.tracePlaceGrid } + + + # create a canvas in slot to display included traces + set canvas_path [ canvas $window.f_$columns_cnt\_$row_cnt.c -background grey -width 2 -height 10 ] + + grid configure $window.f_$columns_cnt\_$row_cnt -row [ expr $row_cnt * $row_span_holder ] -column [ expr ($columns - $columns_cnt) * $max_traces_per_slot ] -sticky "nsew" -rowspan $row_span_holder + + # packs the button into the slots frame on + pack $button_path -side right -fill both -expand 1 + + # packs the canvas into the slot + pack $canvas_path -side left -fill y + + grid columnconfigure $window [expr $columns_cnt * $max_traces_per_slot] -weight 1 + grid rowconfigure $window $row_cnt -weight 1 + } + } + + # can not set the default size here because winfo can not detect the size of gid widget because of delay on creation + + + # Fill in all row sizes to the default + for {set rows_cnt 0 } { $rows_cnt < $rows } { incr rows_cnt} { + set row_heights($rows_cnt) 10 ;#[expr $total_hei / $rows] + grid rowconfigure $window $rows_cnt -minsize $row_heights($rows_cnt) + } + + # error when the trace is first made scales out of the window size + # but if resize window stays within limits + scan [wm geometry $window_clean ] "%dx%d+%d+%d" width total_hei xpos ypos + wm geometry $window_clean "[expr $width+1]x$total_hei\+$xpos\+$ypos" + +} + +#------------------------------------------------------------------------------------------------------------------ +# refereshes the trace and place grid + +proc TracePlace_update { window } { + + # updates all the traces + # called when the traces on screen are updated added / removed + + # retrieve the name of the fake graph + regexp {(.[0-9A-z]+)} $window window_clean + set graph_w "$window_clean.scope.g" + set tracePlace_w "$window_clean.scope.tp.ft.tracePlaceGrid" + + # returns if trace place grid not active + if { $::trace_place_selected($graph_w) == 0 } { return } + + set slot [TracePlace_active_slot $tracePlace_w ] + if { $slot == "" } { return } + + TracePlace_selected_slot $tracePlace_w\.f_[lindex $slot 0]\_[lindex $slot 1]\.b + +} + + +#---------------------------------------------------------------------------------------------------------- +# + +proc TracePlace_selected_slot { window {args ""} } { + + # retrieve the name of the fake graph + regexp {(.[0-9A-z]+)} $window window_clean + set fakeGraph_w "$window_clean.tracePlaceInfoBar.fakeGraph" + set tracePlace_w "$window_clean.scope.tp.ft.tracePlaceGrid" + + regexp {Grid.f_([0-9]+)_([0-9]+)} $window match active_col active_row + + set dropped_slot_row $active_row + set dropped_slot_col $active_col + + # highlight the selected button green turn the previously selected button back to grey + for {set columns_cnt 0 } {$columns_cnt <= $::columns} {incr columns_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $columns_cnt) -1) ] } {incr row_cnt } { + + if { [$tracePlace_w.f_$columns_cnt\_$row_cnt.b cget -background] == "green" } { + + # case where a packet is been dropped on a slot that is not active, so exits not displaying list + if { ($args == "update") && + ( ($active_col != $columns_cnt) || ($active_row != $row_cnt)) } { + + # case where packet is moved from current slot to another, need to redisplay new slot list + set active_col $columns_cnt + set active_row $row_cnt + continue + } + + # turns off the highlight 'green' colour + $tracePlace_w.f_$columns_cnt\_$row_cnt.b configure -background \#dcdcdc -activebackground lightgrey + continue + } + + } + } + + + # search through all the other slot lists to make sure that the traces are not duplicated + # - error trapping when a trace is moved from one slot to another + # - restores the slot to its default colour + for {set columns_cnt 0 } {$columns_cnt <= $::columns} {incr columns_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $columns_cnt) -1) ] } {incr row_cnt } { + + # returns all the slots to there default grey colours + $tracePlace_w.f_$columns_cnt\_$row_cnt.b configure -background \#dcdcdc -activebackground lightgrey + + # continues if current slot + if { ($dropped_slot_row == $row_cnt) && ($dropped_slot_col == $columns_cnt) } { continue } + foreach trace $::slot_trace_list($tracePlace_w.f_$dropped_slot_col\_$dropped_slot_row.b) { + + if { [ lcontain $::slot_trace_list($tracePlace_w.f_$columns_cnt\_$row_cnt.b) $trace ] } { + + # -- nasty hack just need command that delete element form a string--- + set cleanList {} + foreach list $::slot_trace_list($tracePlace_w.f_$columns_cnt\_$row_cnt.b) { + if { $trace != $list } { append cleanList "$list "} + } + set ::slot_trace_list($tracePlace_w.f_$columns_cnt\_$row_cnt.b) {} + set ::slot_trace_list($tracePlace_w.f_$columns_cnt\_$row_cnt.b) $cleanList + #------------------------------------------------------------------------------------------ + } + } + + } + } + + # highlights all the decendants of the selected slot blue, produces list of slots + set activated_slots_list { } + set tr $active_row + for {set col_cnt 0 } {$col_cnt <= [ expr $::columns - $active_col] } {incr col_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + + append activated_slots_list "[ expr $col_cnt + $active_col]\_[ expr $tr + $row_cnt ] " + $tracePlace_w.f_[ expr $col_cnt + $active_col]\_[ expr $tr + $row_cnt ].b configure -background lightblue -activebackground lightgrey + } + set tr [ expr $tr * 2 ] + } + + # highlights the selected slot green + $tracePlace_w.f_$active_col\_$active_row.b configure -background green -activebackground lightgreen + + # re packs the trace and place information bar at bottom, just incase previously forget + pack $window_clean.tracePlaceInfoBar.fakeGraph -side left + pack $window_clean.tracePlaceInfoBar.legends -side left + + # clears the trace and place list at bottom of screen + foreach trace_to_zap [ $fakeGraph_w element names ] { $fakeGraph_w element delete $trace_to_zap } + + # makes sure that all the real legend labels reliefs are flat + # hides all the traces on the scope + foreach trace [ $window_clean.scope.g element names ] { + $window_clean.scope.g element configure $trace -labelrelief flat + $window_clean.scope.g element configure $trace -hide 1 + } + + + # display the slots list of selected traces at the bottom of screen, and in the real graphs legend + set clean_trace_list {} + foreach trace $::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b) { + + # makes sure that the trace has not been removed from the real graph + if { [$window_clean.scope.g element exists $trace] == 0 } { continue } + + # makes sure that there is no dupliaction of traces in the list + if { [$fakeGraph_w element exists $trace] } { continue } + + # add the trace to the list at the bottom of the screen + $fakeGraph_w element create $trace -color [$window_clean.scope.g element cget $trace -color ] + + # highlight by relief in real legend + $window_clean.scope.g element configure $trace -labelrelief raised + + append clean_trace_list "$trace " + } + + # saves the cleaned up plot list + set ::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b) {} + set ::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b) $clean_trace_list + + + # if no traces in the slot list, puts a blank plot due to packing/update problems with fake graph legend + if { [ llength $::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b)] == 0 } {$fakeGraph_w element create "" -symbol ""} + + + + # hides all the traces on the scope + # foreach scope_trace [ $window_clean.scope.g element names ] { $window_clean.scope.g element configure $scope_trace -hide 1} + + + # loops through all the activated slots and displays there traces + # resizes the traces so that they are relative to there slots size and position + foreach activated_slot $activated_slots_list { + + regexp {([0-9]+)_([0-9]+)} $activated_slot match activated_slot_col activated_slot_row + set active_row $activated_slot_row + set active_col $activated_slot_col + + # resets the y axis to 0 -100 and hides the scale + $window_clean.scope.g axis configure y -min 0 -max 100 ;#-hide 1 ;# - set up the y axis + # should only call above if list length > 1 + + # displays traces within slots plot list on scope, and rasies it legend + set max 0 + set min 0 + foreach scope_trace [ $window_clean.scope.g element names ] { + if { [ lcontain $::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b) "$scope_trace" ]} { + + $window_clean.scope.g element configure $scope_trace -labelrelief raised + $window_clean.scope.g element configure $scope_trace -hide 0 + + spice_return_trace_vectors $window_clean.scope.g $scope_trace + + set data_list_length [ temp_vector_Y length ] + for { set i 0 } { $i < $data_list_length } { incr i } { + if { [ temp_vector_Y index $i ] > $max } { set max [ temp_vector_Y index $i ] } + if { [ temp_vector_Y index $i ] < $min } { set min [ temp_vector_Y index $i ] } + } + } + } + + + foreach scope_trace [ $window_clean.scope.g element names ] { + if { [ lcontain $::slot_trace_list($tracePlace_w.f_$active_col\_$active_row.b) "$scope_trace" ]} { + + spice_return_trace_vectors $window_clean.scope.g $scope_trace + set data_list_length [ temp_vector_Y length ] + set scale_factor [ expr 100 / ($max - $min ) ] + + if { $min < 0 } { + set offset [ expr $min * -1 ] + } else { + set offset 0 + } + + # retrives the size of the selected slot + set dimensions [ TracePlace_slot_dimension $window $active_col $active_row ] + set slot_start_position [lindex $dimensions 0] + set slot_finish_position [lindex $dimensions 1] + set slot_scale_factor [ expr 100 / (($slot_finish_position) - ( $slot_start_position)) ] + + for { set i 0 } { $i < $data_list_length } { incr i } { + + #scales against 0 -100 + temp_vector_Y index $i [ expr ( [ temp_vector_Y index $i ] * $scale_factor) ] + + # offset traces < 0 + temp_vector_Y index $i [ expr [ temp_vector_Y index $i ] + ( $offset * $scale_factor )] + + # scales against the slot dimensions + temp_vector_Y index $i [ expr ( [temp_vector_Y index $i] / $slot_scale_factor ) + $slot_start_position ] + + } + + temp_vector_Y dup temp2 + $window_clean.scope.g element configure $scope_trace -ydata $temp2(:) + } + } + } + + # runs through all the slots and draws markers into the canvases + for {set col_cnt 0 } {$col_cnt <= $::columns} {incr col_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $col_cnt) -1) ] } {incr row_cnt } { + TracePlace_update_slot_canvas_markers $tracePlace_w $col_cnt $row_cnt + } + } + +} + +#-------------------------------------------------------------------------------------------------------------------------------------- + +proc TracePlace_update_slot_canvas_markers { tracePlace_w col_cnt row_cnt } { + + regexp {(.[0-9A-z]+)} $tracePlace_w window_clean + + # clean old markers + $tracePlace_w.f_$col_cnt\_$row_cnt.c delete tracePlaceMarker + + # length of list + set list_length [ llength $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b)] + + # the height of the column + set slot_height [ winfo height $tracePlace_w.f_$col_cnt\_$row_cnt.c ] + + if { $list_length == 0 } { + set rectangle_height $slot_height + } else { + set rectangle_height [expr $slot_height / $list_length ] + } + + # run through the list display ing rectanlgles + set rect_start_y 0 + set rect_finish_y 0 + + foreach scope_trace $::slot_trace_list($tracePlace_w.f_$col_cnt\_$row_cnt.b) { + set rect_finish_y [expr $rect_finish_y + $rectangle_height ] + # places retcangle on canvas + $tracePlace_w.f_$col_cnt\_$row_cnt.c create rectangle 0 $rect_start_y 10 $rect_finish_y -tags tracePlaceMarker -fill [ $window_clean.scope.g element cget $scope_trace -color ] + set rect_start_y [expr $rect_start_y + $rectangle_height ] + } +} + + +#---------------------------------------------------------------------------------------------------------------------------------- +# returns the start and finish dimensions for a given slot on trace and place + +proc TracePlace_slot_dimension { window active_col active_row } { + + regexp {(.[0-9A-z]+)} $window window_clean + set fakeGraph_w "$window_clean.tracePlaceInfoBar.fakeGraph" + set tracePlace_w "$window_clean.scope.tp.ft.tracePlaceGrid" + + set grid_info [grid info $tracePlace_w.f_$active_col\_0] + + set count 0 + set temp_row 0 + set calc_height 0 + set row_span_value 0 + + # loops down the row calculating the size of each slot + for {set rows_cnt [expr ( int ($::rows) / [lindex $grid_info 9]) - 1 ] } { $rows_cnt >= 0 } { set rows_cnt [expr $rows_cnt - 1]} { + # retrieves the configs of the slot + set rows_cnt [ expr int($rows_cnt) ] + set grid_info [grid info $tracePlace_w.f_$active_col\_$rows_cnt] + + # save the start height of the selected slot + if { $count == $active_row } { set temp_finish $row_span_value} ;# start + + # calculates the size of the slot + for {set row_span_cnt $temp_row } {$row_span_cnt < ( [lindex $grid_info 9] + $temp_row )} {incr row_span_cnt } { + set row_span_value [expr $row_span_value + [grid rowconfigure $tracePlace_w $row_span_cnt -minsize ] ] + } + set temp_row $row_span_cnt + set calc_height $row_span_value + incr count + + # save the finish height of the selected slot + if { $count == [ expr $active_row + 1 ]} { set temp_start $row_span_value} ;# finish + } + + return "[expr 100 - ( $temp_start / [expr $calc_height /100.0] ) ] [expr 100 - ( $temp_finish / [expr $calc_height /100.0] ) ]" +} + + +#--------------------------------------------------------------------------------------------------------------------------------- +proc TracePlace_active_button {window } { + global columns + + # detects which button is active, and returns its grid position + for {set columns_cnt 0 } {$columns_cnt <= $columns} {incr columns_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $columns_cnt) -1) ] } {incr row_cnt } { + + if { [$window.f_$columns_cnt\_$row_cnt.b cget -state] == "active" } { + return "$columns_cnt $row_cnt" + } + } + } +} + +#---------------------------------------------------------------------------------------------------------------------------------- +proc TracePlace_active_slot {window } { + global columns + + # detects which button is active, and returns its grid position + for {set columns_cnt 0 } {$columns_cnt <= $columns} {incr columns_cnt } { + for {set row_cnt 0} { $row_cnt <= [ expr (pow(2, $columns_cnt) -1) ] } {incr row_cnt } { + + if { [$window.f_$columns_cnt\_$row_cnt.b cget -background] == "green" } { + return "$columns_cnt $row_cnt" + } + } + } +} + +#---------------------------------------------------------------------------------------------------------------------------------- + +set ::trace_place_update_time 500 + +#---------------------------------------------------------------------------------------------------------------------------------- +# resizes the slot on the the trace and place grid + +proc TracePlace_wheel_move { direction window } { + global columns min_row_hei total_hei rows + + # update the scope traces after "trace_place_update_delay" , the delay is reset each time this proc is called + foreach after_script [ after info ] {after cancel $after_script } ; # kills all the afters that are active + + set ::delayed_update_widget $window + + after $::trace_place_update_time { + set slot_location [ TracePlace_active_slot $::delayed_update_widget ] + if { $slot_location != "" } { TracePlace_selected_slot $::delayed_update_widget.f_[lindex $slot_location 0]\_[lindex $slot_location 1].b update } ;# update if there is an active (green) slot + } + + scan [winfo geometry $window ] "%dx%d+%d+%d" width total_hei xpos ypos + + # detects which button is active + set button_location [ TracePlace_active_button $window] + set col_cnt [lindex $button_location 0] + set row_cnt [lindex $button_location 1] + + set grid_info [grid info $window.f_$col_cnt\_$row_cnt] + set col_info [lindex $grid_info 3] + set row_span_info [lindex $grid_info 9] + set row_info [lindex $grid_info 5] + + # stores current slot sizes + for {set rows_cnt 0 } { $rows_cnt < $rows } { incr rows_cnt} { + set row_heights($rows_cnt) [ grid rowconfigure $window $rows_cnt -minsize ] + if {$row_heights($rows_cnt) < 10} {set row_heights($rows_cnt) 10} + } + + set allocated_height 0 + + # increases / decreases selected slots + for { set rows_cnt $row_info} {$rows_cnt < [expr ($row_info + $row_span_info) ] } {incr rows_cnt } { + + set valholder $row_heights($rows_cnt) + + if {$direction == "up" } { + set valholder [expr $valholder * 1.1] ;#- scale up + } else { + set valholder [expr $valholder / 1.1] ;#- scale down + } + + set row_heights_holder($rows_cnt) $valholder ; #- store back in working holder + set allocated_height [expr $allocated_height + $valholder] ; #- add the height + } + + # Return if this would exceed the screen size limit by itself (even without makeing all the other rows 0 height) + if {$allocated_height >= $total_hei } {puts "hit limit $allocated_height"; return } + + # recalculates the slots sizes + for {set rows_cnt_outer 0 } { $rows_cnt_outer < $rows} {incr rows_cnt_outer } { + + if { [catch {set temp $row_heights_holder($rows_cnt_outer)} ] } { + + # Find unallocated heights total (i.e. the total of the elements not yet decided) + set unallocated_height 0 + set allocated_height 0 + + for { set rows_cnt 0 } { $rows_cnt < $rows } { incr rows_cnt} { + if { [catch {set temp $row_heights_holder($rows_cnt)} ] } { + set unallocated_height [ expr $unallocated_height + $row_heights($rows_cnt) ] + } else { + set allocated_height [ expr $allocated_height + $row_heights_holder($rows_cnt) ] + # case where a slot has been given a value above + } + } + set scale_to_apply [ expr ($total_hei - $allocated_height) / $unallocated_height ] + set row_heights_holder($rows_cnt_outer) [expr ( $row_heights($rows_cnt_outer) * $scale_to_apply) ] + } + } + + + # if the first row + # need to be if all the rows are less than the miniumum allowed then set to minimum + if { ( $row_info == 0 ) } { + # loop through all the rows checking if they have all dropped below the minumum + set temp 0 + for { set temp_cnt 0 } { $temp_cnt < $rows } { incr temp_cnt } { + if { $row_heights_holder($temp_cnt) < [expr $total_hei / $rows ] } { incr temp } + } + if { $temp == $rows } { + for { set temp_cnt 0 } { $temp_cnt < $rows } { incr temp_cnt } { + set row_heights_holder($temp_cnt) [expr $total_hei / $rows ] + } + } + } + + # reapply's the new slot sizes + for {set rows_cnt_outer 0 } { $rows_cnt_outer < $rows} {incr rows_cnt_outer } { + grid rowconfigure $window $rows_cnt_outer -minsize $row_heights_holder($rows_cnt_outer) + } + +} + + + + + +################################################################### +# saving project +# +################################################################### + +set ::saved_projects_extension "swp" + +proc project_save { project_name } { + + save_all_nutmeg_windows save + + # creates list of project files + set file_list [ glob $project_name* ] + + set clean_file_list {} + + # run's through the list to clean up un needed files + for { set i 0 } { $i < [ llength $file_list ] } { incr i } { + + set file [ lrange $file_list $i $i ] + + # removes any tar balls + if { [ regexp {.tar.gz} $file ] == 1 } { continue } ; + + # removes any project extension + if { [ regexp {.swp} $file ] == 1 } { continue } ; + + # removes any saved files + if { [ regexp {~} $file ] == 1 } { continue } ; + + append clean_file_list "$file " + } + + # delete's if project already exsists + if { [ file exists $project_name.$::saved_projects_extension ] } {puts ""; puts "deleted old project"; file delete -force $project_name.$::saved_projects_extension } + + # saving file + if { [ system tar -zcf $project_name.$::saved_projects_extension $clean_file_list] == 0 } { + puts " " + puts "project \" $project_name.$::saved_projects_extension \" saved " + foreach file $file_list { + puts "-- $file" + } + puts "" + } +} + +#--------------------------------------------------------------------------------------------------------------- +# untars the project +proc project_open { project_name } { + + if { [ system tar -zxf $project_name ] == 0 } { puts "project '$project_name' opened ok " } +} + +#-------------------------------------------------------------------------------------------- +# returns contents list of the project file + +proc project_file_list { project_name } { + + return [ exec tar tzf $project_name ] +} diff --git a/src/spicelib/analysis/cluster.c b/src/spicelib/analysis/cluster.c new file mode 100755 index 000000000..d2c090a93 --- /dev/null +++ b/src/spicelib/analysis/cluster.c @@ -0,0 +1,426 @@ +/* Spice hooks */ +#include +#ifdef CLUSTER +#include +#include "cluster.h" +#include +#include + +/* Misc stuff */ +#include +#include + +/*Network stuff*/ +#include +#include +#include +#include +#include + + +struct input_pipe { + /*the names of the local and remote nodes*/ + char remote[32]; + char local[32]; + int fd; + FILE *stream; + /* the data recieved */ + double time; + double data; + /*resistance of this link*/ + double res; + /* The value controled */ + double *currentPtr; + /*The output it is linked to*/ + struct output_pipe *link; + struct input_pipe *next; +}; + +struct output_pipe { + int fd; + FILE *stream; + /*the names of the local and remote nodes*/ + char local[32]; + char remote[32]; + /* The index of the local node value in the ckt->CKTrhsOld array */ + int outIndex; + /*Last values sent*/ + double time,data; + struct input_pipe *link; + struct output_pipe *next; +}; + +static double lastTimeSent=0; + +static int time_sock=0; +static FILE *time_outfile=NULL; +static FILE *time_infile=NULL; + +static struct input_pipe* input_pipes=NULL; +static struct output_pipe* output_pipes=NULL; + +/* sets up deamon which waits for connections + *and sets up input pipes as it recieves them */ +static void *start_listener(void *); + +/* Setup the output pipes*/ +static int setup_output(CKTcircuit *ckt); +static int setup_input(CKTcircuit *ckt); +static int setup_time(); + +int CLUsetup(CKTcircuit *ckt){ + pthread_t tid; + struct input_pipe *curr; + int i, connections=0; + GENmodel *mod; + GENinstance *inst; + + /* count the number of connections expected */ + i = INPtypelook("Isource"); + for(mod = (GENmodel *)ckt->CKThead[i]; + mod != NULL; + mod = mod->GENnextModel) + for (inst = mod->GENinstances; + inst != NULL; + inst = inst->GENnextInstance) + if(strncmp("ipcx",inst->GENname,4) == 0) + connections++; + + /* allocate the input connections */ + for(i=0;inext = input_pipes; + else + curr->next = NULL; + input_pipes = curr; + } + + pthread_create(&tid,NULL,start_listener,(void *)&connections); + setup_output(ckt); + pthread_join(tid,NULL); + setup_input(ckt); + setup_time(); + return 0; +} + +#include "../devices/isrc/isrcdefs.h" +/*Connect to remote machine and find the data*/ +static int setup_output(CKTcircuit *ckt){ + int type; + GENmodel *mod; + GENinstance *inst; + char hostname[64]; + + lastTimeSent = 0; + type = INPtypelook("Isource"); + + for(mod = (GENmodel *)ckt->CKThead[type]; + mod != NULL; + mod = mod->GENnextModel) + + for (inst = mod->GENinstances; + inst != NULL; + inst = inst->GENnextInstance) + + if(strncmp("ipcx",inst->GENname,4) == 0){ + ISRCinstance *isrc = (ISRCinstance *)inst; + CKTnode *node; + struct output_pipe *curr; + struct sockaddr_in address; + struct hostent *host=NULL; + int sock,nodeNum,i; + + /*Create the struct*/ + curr = (struct output_pipe *)tmalloc(sizeof(struct output_pipe)); + if(output_pipes) + curr->next = output_pipes; + else + curr->next = NULL; + output_pipes = curr; + + /* The node names */ + strcpy(curr->local,CKTnodName(ckt,isrc->ISRCnegNode));/*weird*/ + strcpy(curr->remote,isrc->ISRCname); + + /* extract remote node number */ + nodeNum = /*Xcoord*/(curr->remote[4] - '0') * CLUSTER_WIDTH + + /*Ycoord*/(curr->remote[9] - '0'); + sprintf(hostname,"n%d."DOMAIN_NAME,nodeNum); + + /* network stuff */ + host = gethostbyname(hostname); + if(!host){ + printf("Host not found in setup_output\n"); + exit(0); + } + + if((sock = socket(PF_INET,SOCK_STREAM,0)) < 0){ + printf("Socket open in setup_output\n"); + exit(0); + } + + address.sin_family = AF_INET; + address.sin_port = htons(PORT); + memcpy(&address.sin_addr,host->h_addr_list[0], + sizeof(address.sin_addr)); + + printf("connecting to %s ...... ",hostname); + fflush(stdout); + + while(connect(sock,(struct sockaddr *)&address,sizeof(address))){ + usleep(500);/*wait for the sever to start*/ + } + + printf("connected\n"); + + curr->fd = sock; + + /* send stuff */ + /* buffer */ + i = (strlen(curr->remote) + strlen(curr->local) + 2)*sizeof(char); + setsockopt(sock,SOL_SOCKET,SO_SNDBUF,&i,sizeof(i)); + + curr->stream = fdopen(curr->fd,"w"); + + fwrite(curr->remote,sizeof(char),strlen(curr->remote),curr->stream); + fputc('\0',curr->stream); + fwrite(curr->local,sizeof(char),strlen(curr->local),curr->stream); + fputc('\0',curr->stream); + fflush(curr->stream); + + /* buffer, what is done per time point */ + i = sizeof(double)*2; + setsockopt(sock,SOL_SOCKET,SO_SNDBUF,&i,sizeof(i)); + + /* find the index in ckt->rhsOld which contains the local node */ + i = 0; + for(node = ckt->CKTnodes->next;node;node = node->next){ + i++; + if(strcmp(node->name,curr->local)==0){ + curr->outIndex = i; + goto next; + } + } + printf("Local node %s not found\n",curr->local); + exit(0); + next: + + } + return 0; +} + +/*Processes the connections recieved by start_listener*/ +static int setup_input(CKTcircuit *ckt){ + int type; + GENmodel *mod; + GENinstance *inst; + struct input_pipe *input; + type = INPtypelook("Isource"); + + for(input = input_pipes;input;input = input->next){ + int i; + + input->stream = fdopen(input->fd,"r"); + + /*Get the local and remote node names*/ + i=0; + do { + while(fread(&input->local[i],sizeof(char),1,input->stream) != 1); + }while(input->local[i++] != '\0'); + + i=0; + do { + while(fread(&input->remote[i],sizeof(char),1,input->stream) != 1); + }while(input->remote[i++] != '\0'); + + /* initilise */ + input->time = -1; + + /*Find the Isource to control*/ + for(mod = (GENmodel *)ckt->CKThead[type]; + mod != NULL; + mod = mod->GENnextModel) + + for (inst = mod->GENinstances; + inst != NULL; + inst = inst->GENnextInstance) + + if(strcmp(input->remote,&inst->GENname[11]) == 0){ + + ISRCinstance *isrc = (ISRCinstance *)inst; + input->res = isrc->ISRCdcValue; + isrc->ISRCdcValue = 0; + input->currentPtr = &isrc->ISRCdcValue; + goto next; + } + /* We get here if no Isource matches */ + printf("Current source %s not found\n",input->remote); + exit(0); + + next: + + /* Now find the corresponding output */ + { + struct output_pipe *output; + for(output = output_pipes;output;output = output->next) + if(strcmp(&input->local[11],output->local)==0){ + input->link = output; + output->link = input; + goto next2; + } + printf("Parent to %s not found\n",&input->local[11]); + exit(0); + next2: + } + } + return 0; +} + +/* This starts a server and waits for connections, number given by argument*/ +static void *start_listener(void *v){ + int *connections = (int *)v; + int count=0; + struct sockaddr_in address; + int sock, conn,i; + size_t addrLength = sizeof(struct sockaddr_in); + struct input_pipe *curr; + + if((sock = socket(PF_INET,SOCK_STREAM,0)) < 0){ + printf("socket in start_listener\n"); + exit(0); + } + + /* Allow reuse of the socket */ + i = 1; + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i)); + + /* port, inferface ..*/ + address.sin_family = AF_INET; + address.sin_port = htons(PORT); + memset(&address.sin_addr,0,sizeof(address.sin_addr)); + + if(bind(sock, (struct sockaddr *)&address,sizeof(address))){ + printf("bind in start_listener\n"); + exit(0); + } + if(listen(sock,5)){ + printf("listen in start_listener\n"); + exit(0); + } + + /* Loop till recieved all connections */ + curr = input_pipes; + while (count < *connections){ + if((conn = accept(sock, (struct sockaddr *)&address,&addrLength)) < 0){ + printf("accept in start_listener\n"); + exit(0); + } + + curr->fd = conn; + /* will fill rest of structure later in setup_input*/ + count ++; + curr = curr->next; + } + + close(sock); + + return NULL; +} + +/*Writes data to remote computer*/ +int CLUoutput(CKTcircuit *ckt){ + struct output_pipe *output; + lastTimeSent = ckt->CKTtime; + for(output = output_pipes; + output; + output = output->next){ + output->time = ckt->CKTtime; + output->data = ckt->CKTrhsOld[output->outIndex]; + fwrite(&output->time,sizeof(double),1,output->stream); + fwrite(&output->data, + sizeof(double),1,output->stream); + fflush(output->stream); + } + return 0; +} + +/*Maniputates the local circuit based on the links*/ +int CLUinput(CKTcircuit *ckt){ + struct input_pipe *input; + double tmp; + for(input= input_pipes;input;input = input->next){ + /*recieve data till we get a good time point*/ + while (input->time < lastTimeSent){ + while(fread(&input->time, sizeof(double), 1, input->stream) != 1){} + while(fread(&input->data, sizeof(double), 1, input->stream) != 1){} + } + tmp = (input->link->data - input->data) / input->res; + + /*dampen out large currents*/ + if(tmp > 0) + *input->currentPtr = 0.2 * (1 - exp(-tmp/0.2)); + else + *input->currentPtr = -0.2 * (1 - exp(tmp/0.2)); + + /*GND is the posNode, local node is the negNode*/ + } + return 0; +} + +static int setup_time(){ + struct sockaddr_in address; + struct hostent *host=NULL; + char *hostname = TIME_HOST; + int sock,i; + + /* network stuff */ + host = gethostbyname(hostname); + if(!host){ + printf("Host not found in setup_time\n"); + exit(0); + } + if((sock = socket(PF_INET,SOCK_STREAM,0)) < 0){ + printf("Socket open in setup_time\n"); + exit(0); + } + + i = sizeof(double)*2; + setsockopt(sock,SOL_SOCKET,SO_SNDBUF ,&i,sizeof(i)); + + address.sin_family = AF_INET; + address.sin_port = htons(TIME_PORT); + memcpy(&address.sin_addr,host->h_addr_list[0], + sizeof(address.sin_addr)); + + + while(connect(sock,(struct sockaddr *)&address,sizeof(address))){ + usleep(500);/*wait for the sever to start*/ + } + time_sock=sock; + time_outfile=fdopen(sock,"w"); + time_infile=fdopen(sock,"r"); + + return 0; +} + + +int CLUsync(double time,double *delta, int error){ + double tmp; + if(error) + tmp = *delta * (-1); + else + tmp = *delta; + fwrite(&time,sizeof(double),1,time_outfile); + fwrite(&tmp,sizeof(double),1,time_outfile); + fflush(time_outfile); + while(fread(&tmp,sizeof(double),1,time_infile) != 1); + if(tmp < 0){ + *delta = -tmp; + return 0; + } else { + *delta = tmp; + return 1; + } +} +#endif diff --git a/src/spicelib/devices/cpl/Makefile.am b/src/spicelib/devices/cpl/Makefile.am new file mode 100755 index 000000000..28161c144 --- /dev/null +++ b/src/spicelib/devices/cpl/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in + +pkglib_LTLIBRARIES = libcpl.la + +libcpl_la_SOURCES = \ + cpl.c \ + cpldest.c \ + cplmdel.c \ + cplparam.c \ + cpldel.c \ + cplload.c \ + cplmpar.c \ + cplsetup.c \ + cplinit.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/spicelib/devices/cpl/cpl.c b/src/spicelib/devices/cpl/cpl.c new file mode 100644 index 000000000..4bb1aaf46 --- /dev/null +++ b/src/spicelib/devices/cpl/cpl.c @@ -0,0 +1,39 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "cpldefs.h" +#include "devdefs.h" +#include "ifsim.h" +#include "suffix.h" + +IFparm CPLpTable[] = { + IOP("pos_nodes", CPL_POS_NODE, IF_VECTOR|IF_STRING, "in nodes"), + IOP("neg_nodes", CPL_NEG_NODE, IF_VECTOR|IF_STRING, "out nodes"), + IOP("dimension", CPL_DIM, IF_INTEGER, "number of coupled lines"), + IOP("length", CPL_LENGTH, IF_REAL, "length of lines"), +}; + +IFparm CPLmPTable[] = { /* model parameters */ + IOP( "r", CPL_R, IF_REALVEC,"resistance per length"), + IOP( "l", CPL_L, IF_REALVEC,"inductance per length"), + IOP( "c", CPL_C, IF_REALVEC,"capacitance per length"), + IOP( "g", CPL_G, IF_REALVEC,"conductance per length"), + IOP( "length", CPL_length, IF_REAL,"length"), + IP( "cpl", CPL_MOD_R, IF_FLAG,"Device is a cpl model"), +}; + +char *CPLnames[] = { + "P+", + "P-" +}; + +int CPLnSize = NUMELEMS(CPLnames); +int CPLiSize = sizeof(CPLinstance); +int CPLmSize = sizeof(CPLmodel); +int CPLmPTSize = NUMELEMS(CPLmPTable); +int CPLpTSize = NUMELEMS(CPLpTable); diff --git a/src/spicelib/devices/cpl/cpldefs.h b/src/spicelib/devices/cpl/cpldefs.h new file mode 100644 index 000000000..81aa5a0b3 --- /dev/null +++ b/src/spicelib/devices/cpl/cpldefs.h @@ -0,0 +1,97 @@ +#ifndef CPL +#define CPL + +#include "ifsim.h" +#include "cktdefs.h" +#include "gendefs.h" +#include "complex.h" +#include "noisedef.h" +#include "swec.h" + +/* information used to describe a single instance */ + +typedef struct sCPLinstance { + struct sCPLmodel *CPLmodPtr; /* backpointer to model */ + struct sCPLinstance *CPLnextInstance; /* pointer to next instance of + * current model*/ + + IFuid CPLname; /* pointer to character string naming this instance */ + + int dimension; + int *CPLposNodes; + int *CPLnegNodes; + double CPLlength; + int *CPLibr1; + int *CPLibr2; + CPLine *cplines; /* pointer to SWEC cplines type */ + CPLine *cplines2; /* temporary pointer to SWEC cplines type */ + + char **in_node_names; + char **out_node_names; + + double **CPLibr1Ibr1; + double **CPLibr2Ibr2; + double **CPLposIbr1; + double **CPLnegIbr2; + /* trial */ + double **CPLposPos; + double **CPLnegNeg; + double **CPLposNeg; + double **CPLnegPos; + + double ***CPLibr1Pos; + double ***CPLibr2Neg; + double ***CPLibr1Neg; + double ***CPLibr2Pos; + double ***CPLibr1Ibr2; + double ***CPLibr2Ibr1; + + unsigned CPLibr1Given : 1; + unsigned CPLibr2Given : 1; + unsigned CPLdcGiven : 1; + unsigned CPLlengthgiven : 1; + +} CPLinstance ; + + +/* per model data */ + +typedef struct sCPLmodel { /* model structure for a cpl */ + int CPLmodType; /* type index of this device type */ + struct sCPLmodel *CPLnextModel; /* pointer to next possible model in + * linked list */ + CPLinstance * CPLinstances; /* pointer to list of instances that have this + * model */ + IFuid CPLmodName; /* pointer to character string naming this model */ + + double *Rm; + double *Gm; + double *Lm; + double *Cm; + double length; + unsigned Rmgiven : 1; + unsigned Lmgiven : 1; + unsigned Gmgiven : 1; + unsigned Cmgiven : 1; + unsigned lengthgiven : 1; + +} CPLmodel; + +/* instance parameters */ +#define CPL_POS_NODE 1 +#define CPL_NEG_NODE 2 +#define CPL_DIM 3 +#define CPL_LENGTH 4 + +/* model parameters */ +#define CPL_R 101 +#define CPL_C 102 +#define CPL_G 103 +#define CPL_L 104 +#define CPL_length 105 +#define CPL_MOD_R 106 + +#include "cplext.h" +extern VI_list *pool_vi; + +#endif /*CPL*/ diff --git a/src/spicelib/devices/cpl/cpldel.c b/src/spicelib/devices/cpl/cpldel.c new file mode 100644 index 000000000..746811eaf --- /dev/null +++ b/src/spicelib/devices/cpl/cpldel.c @@ -0,0 +1,38 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +CPLdelete(inModel,name,inst) + GENmodel *inModel; + IFuid name; + GENinstance **inst; +{ + CPLmodel *model = (CPLmodel *)inModel; + CPLinstance **fast = (CPLinstance **)inst; + CPLinstance **prev = NULL; + CPLinstance *here; + + for( ; model ; model = model->CPLnextModel) { + prev = &(model->CPLinstances); + for(here = *prev; here ; here = *prev) { + if(here->CPLname == name || (fast && here==*fast) ) { + *prev= here->CPLnextInstance; + FREE(here); + return(OK); + } + prev = &(here->CPLnextInstance); + } + } + return(E_NODEV); +} diff --git a/src/spicelib/devices/cpl/cpldest.c b/src/spicelib/devices/cpl/cpldest.c new file mode 100644 index 000000000..e0c6b46f6 --- /dev/null +++ b/src/spicelib/devices/cpl/cpldest.c @@ -0,0 +1,34 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "cpldefs.h" +#include "suffix.h" + +void +CPLdestroy(inModel) + GENmodel **inModel; +{ + CPLmodel **model = (CPLmodel **)inModel; + CPLinstance *here; + CPLinstance *prev = NULL; + CPLmodel *mod = *model; + CPLmodel *oldmod = NULL; + + for( ; mod ; mod = mod->CPLnextModel) { + if(oldmod) FREE(oldmod); + oldmod = mod; + prev = (CPLinstance *)NULL; + for(here = mod->CPLinstances ; here ; here = here->CPLnextInstance) { + if(prev) FREE(prev); + prev = here; + } + if(prev) FREE(prev); + } + if(oldmod) FREE(oldmod); + *model = NULL; +} diff --git a/src/spicelib/devices/cpl/cplext.h b/src/spicelib/devices/cpl/cplext.h new file mode 100644 index 000000000..bc96eb61b --- /dev/null +++ b/src/spicelib/devices/cpl/cplext.h @@ -0,0 +1,19 @@ +#ifdef __STDC__ +/* extern int CPLaccept(CKTcircuit*,GENmodel*); */ +extern int CPLdelete(GENmodel*,IFuid,GENinstance**); +extern void CPLdestroy(GENmodel**); +extern int CPLload(GENmodel*,CKTcircuit*); +extern int CPLmDelete(GENmodel**,IFuid,GENmodel*); +extern int CPLmParam(int,IFvalue*,GENmodel*); +extern int CPLparam(int,IFvalue*,GENinstance*,IFvalue*); +extern int CPLsetup(SMPmatrix*,GENmodel*,CKTcircuit*,int*); +#else /* stdc */ +/* extern int CPLaccept(); */ +extern int CPLdelete(); +extern void CPLdestroy(); +extern int CPLload(); +extern int CPLmDelete(); +extern int CPLmParam(); +extern int CPLparam(); +extern int CPLsetup(); +#endif /* stdc */ diff --git a/src/spicelib/devices/cpl/cplinit.c b/src/spicelib/devices/cpl/cplinit.c new file mode 100644 index 000000000..8e45b21f0 --- /dev/null +++ b/src/spicelib/devices/cpl/cplinit.c @@ -0,0 +1,68 @@ +#include + +#include + +#include "cplitf.h" +#include "cplext.h" +#include "cplinit.h" + +SPICEdev CPLinfo = { + { + "CplLines", + "Simple Coupled Multiconductor Lines", + + &CPLnSize, + &CPLnSize, + CPLnames, + + &CPLpTSize, + CPLpTable, + + &CPLmPTSize, + CPLmPTable, + 0 + }, + + CPLparam, + CPLmParam, + CPLload, + CPLsetup, + NULL, + NULL, + NULL, + NULL, + NULL, /* CPLfindBranch, */ + NULL, + NULL, + CPLdestroy, +#ifdef DELETES + CPLmDelete, + CPLdelete, +#else /* DELETES */ + NULL, + NULL, +#endif /* DELETES */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + &CPLiSize, + &CPLmSize + +}; + +SPICEdev * +get_cpl_info(void) +{ + return &CPLinfo; +} diff --git a/src/spicelib/devices/cpl/cplinit.h b/src/spicelib/devices/cpl/cplinit.h new file mode 100644 index 000000000..17f4a5b24 --- /dev/null +++ b/src/spicelib/devices/cpl/cplinit.h @@ -0,0 +1,13 @@ +#ifndef _CPLINIT_H +#define _CPLINIT_H + +extern IFparm CPLpTable[ ]; +extern IFparm CPLmPTable[ ]; +extern int CPLmPTSize; +extern int CPLpTSize; +extern char *CPLnames[ ]; +extern int CPLiSize; +extern int CPLmSize; +extern int CPLnSize; + +#endif diff --git a/src/spicelib/devices/cpl/cplitf.h b/src/spicelib/devices/cpl/cplitf.h new file mode 100644 index 000000000..4a690f024 --- /dev/null +++ b/src/spicelib/devices/cpl/cplitf.h @@ -0,0 +1,6 @@ +#ifndef DEV_CPL +#define DEV_CPL + +SPICEdev *get_cpl_info(void); + +#endif diff --git a/src/spicelib/devices/cpl/cplload.c b/src/spicelib/devices/cpl/cplload.c new file mode 100644 index 000000000..5b502fe5e --- /dev/null +++ b/src/spicelib/devices/cpl/cplload.c @@ -0,0 +1,890 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + +VI_list *pool_vi; +static double ratio[MAX_CP_TX_LINES]; +static VI_list *new_vi(); +static void free_vi(); +static int get_pvs_vi(); +static int update_cnv(); +static int add_new_vi(); +static int right_consts(); +static int update_delayed_cnv(); +static int multC(); +static int expC(); +static int divC(); +static void update_cnv_a(); +static void copy_cp(); + + +/*ARGSUSED*/ +int +CPLload(inModel,ckt) + GENmodel *inModel; + CKTcircuit *ckt; +{ + register CPLmodel *model = (CPLmodel *)inModel; + register CPLinstance *here; + CPLine *cp, *cp2; + int *k, *l; + int time, time2; + double h, h1, f; + int hint; + float hf; + NODE *nd; + double v, v1, g; + int cond1, i; + int noL, m, p, q; + CKTnode *node; + VI_list *vi, *vi_before; + int before, delta; + int resindex; + + + h = ckt->CKTdelta; + h1 = 0.5 * h; + time2 = (int) (ckt->CKTtime * 1e12); + hint = (int)(h * 1e12); + hf = (float)(h * 1e12); + time = (int) ((ckt->CKTtime - ckt->CKTdelta) * 1e12); + + cond1= ckt->CKTmode & MODEDC; + + for( ; model != NULL; model = model->CPLnextModel ) { + for (here = model->CPLinstances; here != NULL ; + here=here->CPLnextInstance) { + + cp = here->cplines; + if (cond1 || cp->vi_head == NULL) continue; + + noL = cp->noL = here->dimension; + if (cp->vi_tail->time > time) { + time = cp->vi_tail->time; + hint = time2 - time; + } + + before = cp->vi_tail->time; + vi_before = cp->vi_tail; + + if (time > cp->vi_tail->time) { + + copy_cp(cp, here->cplines2); + add_new_vi(here, ckt, time); + delta = time - before; + + for (m = 0; m < noL; m++) { + nd = cp->in_node[m]; + v = vi_before->v_i[m]; + v1 = nd->V = cp->vi_tail->v_i[m]; + nd->dv = (v1 - v) / delta; + } + for (m = 0; m < noL; m++) { + nd = cp->out_node[m]; + v = vi_before->v_o[m]; + v1 = nd->V = cp->vi_tail->v_o[m]; + nd->dv = (v1 - v) / delta; + } + + update_cnv(cp, (float)delta); + if (cp->ext) update_delayed_cnv(cp, (float)delta); + } + } + } + + model = (CPLmodel *)inModel; + /* loop through all the models */ + for( ; model != NULL; model = model->CPLnextModel ) { + + /* loop through all the instances of the model */ + for (here = model->CPLinstances; here != NULL ; + here=here->CPLnextInstance) { + + double mintaul = 123456789.0; + + cp = here->cplines; + cp2 = here->cplines2; + + for (i = 0; i < cp->noL; i++) { + if (mintaul > cp->taul[i]) mintaul = cp->taul[i]; + } + if (mintaul < hf) { + + fprintf(stderr, "your time step is too large for tau.\n"); + fprintf(stderr, "please decrease max time step in .tran card.\n"); + fprintf(stderr, ".tran tstep tstop tstart tmax.\n"); + fprintf(stderr, "make tmax smaller than %e and try again.\n", + mintaul * 1e-12); + + return (1111); + + } + + noL = cp->noL = here->dimension; + if (cond1) { + resindex = 0; + for (m = 0; m < noL; m++) { + if (here->CPLlengthgiven) + g = model->Rm[resindex] * here->CPLlength; + else g = model->Rm[resindex] * here->CPLmodPtr->length; + *(here->CPLposIbr1[m]) += 1.0; + *(here->CPLnegIbr2[m]) += 1.0; + *(here->CPLibr1Ibr1[m]) += 1.0; + *(here->CPLibr1Ibr2[m][m]) += 1.0; + *(here->CPLibr2Pos[m][m]) += 1.0; + *(here->CPLibr2Neg[m][m]) -= 1.0; + *(here->CPLibr2Ibr1[m][m]) -= g; + resindex = resindex + noL - m; + } + continue; + } + + /* dc setup */ + if (here->CPLdcGiven == 0 && !cond1) { + for (i = 0; i < cp->noL; i++) { + nd = cp->in_node[i]; + for(node = ckt->CKTnodes;node; node = node->next) { + if (strcmp(nd->name->id, node->name) == 0) { + cp->dc1[i] = ckt->CKTrhsOld[node->number]; + cp2->dc1[i] = nd->V = cp->dc1[i]; + break; + } + } + nd = cp->out_node[i]; + for(node = ckt->CKTnodes;node; node = node->next) { + if (strcmp(nd->name->id, node->name) == 0) { + cp->dc2[i] = ckt->CKTrhsOld[node->number]; + cp2->dc2[i] = nd->V = cp->dc2[i]; + break; + } + } + } + here->CPLdcGiven = 1; + + vi = new_vi(); + vi->time = 0; + { + int i, j, k, l; + for (i = 0; i < cp->noL; i++) { + for (j = 0; j < cp->noL; j++) { + TMS *tms; + double a, b; + + tms = cp->h1t[i][j]; + if (tms->ifImg) { + tms->tm[0].cnv_i = - cp->dc1[j] * + tms->tm[0].c / tms->tm[0].x; + tms->tm[0].cnv_o = - cp->dc2[j] * + tms->tm[0].c / tms->tm[0].x; + divC(tms->tm[1].c, tms->tm[2].c, + tms->tm[1].x, tms->tm[2].x, &a, &b); + tms->tm[1].cnv_i = - cp->dc1[j] * a; + tms->tm[1].cnv_o = - cp->dc2[j] * a; + tms->tm[2].cnv_i = - cp->dc1[j] * b; + tms->tm[2].cnv_o = - cp->dc2[j] * b; + } else + for (k = 0; k < 3; k++) { + tms->tm[k].cnv_i = - cp->dc1[j] * + tms->tm[k].c / tms->tm[k].x; + tms->tm[k].cnv_o = - cp->dc2[j] * + tms->tm[k].c / tms->tm[k].x; + } + + for (l = 0; l < cp->noL; l++) { + tms = cp->h2t[i][j][l]; + for (k = 0; k < 3; k++) { + tms->tm[k].cnv_i = 0.0; + tms->tm[k].cnv_o = 0.0; + } + } + for (l = 0; l < cp->noL; l++) { + tms = cp->h3t[i][j][l]; + if (tms->ifImg) { + tms->tm[0].cnv_i = - cp->dc1[j] * + tms->tm[0].c / tms->tm[0].x; + tms->tm[0].cnv_o = - cp->dc2[j] * + tms->tm[0].c / tms->tm[0].x; + divC(tms->tm[1].c, tms->tm[2].c, + tms->tm[1].x, tms->tm[2].x, &a, &b); + tms->tm[1].cnv_i = - cp->dc1[j] * a; + tms->tm[1].cnv_o = - cp->dc2[j] * a; + tms->tm[2].cnv_i = - cp->dc1[j] * b; + tms->tm[2].cnv_o = - cp->dc2[j] * b; + } else + for (k = 0; k < 3; k++) { + tms->tm[k].cnv_i = - cp->dc1[j] * + tms->tm[k].c / tms->tm[k].x; + tms->tm[k].cnv_o = - cp->dc2[j] * + tms->tm[k].c / tms->tm[k].x; + } + } + } + + for (i = 0; i < cp->noL; i++) { + vi->i_i[i] = vi->i_o[i] = 0.0; + vi->v_i[i] = cp->dc1[i]; + vi->v_o[i] = cp->dc2[i]; + } + } + + vi->next = NULL; + cp->vi_tail = vi; + cp->vi_head = vi; + cp2->vi_tail = vi; + cp2->vi_head = vi; + + } + } + + for (m = 0; m < noL; m++) { + *(here->CPLibr1Ibr1[m]) = -1.0; + *(here->CPLibr2Ibr2[m]) = -1.0; + } + + for (m = 0; m < noL; m++) { + *(here->CPLposIbr1[m]) = 1.0; + *(here->CPLnegIbr2[m]) = 1.0; + } + + for (m = 0; m < noL; m++) { + for (p = 0; p < noL; p++) { + *(here->CPLibr1Pos[m][p]) = + cp->h1t[m][p]->aten + h1 * cp->h1C[m][p]; + *(here->CPLibr2Neg[m][p]) = + cp->h1t[m][p]->aten + h1 * cp->h1C[m][p]; + } + } + + k = here->CPLibr1; + l = here->CPLibr2; + + copy_cp(cp2, cp); + + if (right_consts(here,cp2, time,time2,h,h1,k,l,ckt)) { + cp2->ext = 1; + for (q = 0; q < noL; q++) { + cp->ratio[q] = ratio[q]; + if (ratio[q] > 0.0) { + for (m = 0; m < noL; m++) { + for (p = 0; p < noL; p++) { + + + if (cp->h3t[m][p][q]) { + f = ratio[q] * (h1 * cp->h3C[m][p][q] + + cp->h3t[m][p][q]->aten); + *(here->CPLibr1Neg[m][p]) = -f; + *(here->CPLibr2Pos[m][p]) = -f; + } + if (cp->h2t[m][p][q]) { + f = ratio[q] * (h1 * cp->h2C[m][p][q] + + cp->h2t[m][p][q]->aten); + *(here->CPLibr1Ibr2[m][p]) = -f; + *(here->CPLibr2Ibr1[m][p]) = -f; + } + + } + } + } + } + } + else cp->ext = 0; + } + } + + return(OK); +} + +static void +copy_cp(new, old) +CPLine *new, *old; +{ + int i, j, k, l, m; + VI_list *temp; + + new->noL = m = old->noL; + new->ext = old->ext; + for (i = 0; i < m; i++) { + new->ratio[i] = old->ratio[i]; + new->taul[i] = old->taul[i]; + + for (j = 0; j < m; j++) { + if (new->h1t[i][j] == NULL) + new->h1t[i][j] = (TMS *) malloc(sizeof (TMS)); + new->h1t[i][j]->ifImg = old->h1t[i][j]->ifImg; + new->h1t[i][j]->aten = old->h1t[i][j]->aten; + new->h1C[i][j] = old->h1C[i][j]; + + for (k = 0; k < 3; k++) { + new->h1t[i][j]->tm[k].c = old->h1t[i][j]->tm[k].c; + new->h1t[i][j]->tm[k].x = old->h1t[i][j]->tm[k].x; + new->h1t[i][j]->tm[k].cnv_i = old->h1t[i][j]->tm[k].cnv_i; + new->h1t[i][j]->tm[k].cnv_o = old->h1t[i][j]->tm[k].cnv_o; + new->h1e[i][j][k] = old->h1e[i][j][k]; + } + for (l = 0; l < m; l++) { + if (new->h2t[i][j][l] == NULL) + new->h2t[i][j][l] = (TMS *) malloc(sizeof (TMS)); + new->h2t[i][j][l]->ifImg = old->h2t[i][j][l]->ifImg; + new->h2t[i][j][l]->aten = old->h2t[i][j][l]->aten; + new->h2C[i][j][l] = old->h2C[i][j][l]; + new->h3C[i][j][l] = old->h3C[i][j][l]; + for (k = 0; k < 3; k++) { + new->h2t[i][j][l]->tm[k].c = old->h2t[i][j][l]->tm[k].c; + new->h2t[i][j][l]->tm[k].x = old->h2t[i][j][l]->tm[k].x; + new->h2t[i][j][l]->tm[k].cnv_i + = old->h2t[i][j][l]->tm[k].cnv_i; + new->h2t[i][j][l]->tm[k].cnv_o + = old->h2t[i][j][l]->tm[k].cnv_o; + } + + if (new->h3t[i][j][l] == NULL) + new->h3t[i][j][l] = (TMS *) malloc(sizeof (TMS)); + new->h3t[i][j][l]->ifImg = old->h3t[i][j][l]->ifImg; + new->h3t[i][j][l]->aten = old->h3t[i][j][l]->aten; + for (k = 0; k < 3; k++) { + new->h3t[i][j][l]->tm[k].c = old->h3t[i][j][l]->tm[k].c; + new->h3t[i][j][l]->tm[k].x = old->h3t[i][j][l]->tm[k].x; + new->h3t[i][j][l]->tm[k].cnv_i + = old->h3t[i][j][l]->tm[k].cnv_i; + new->h3t[i][j][l]->tm[k].cnv_o + = old->h3t[i][j][l]->tm[k].cnv_o; + } + } + } + } + + while (new->vi_head->time < old->vi_head->time) { + temp = new->vi_head; + new->vi_head = new->vi_head->next; + free_vi(temp); + } +} + + +static int +right_consts(here, cp, t, time, h, h1, l1, l2, ckt) +CPLinstance *here; +CPLine *cp; +int t, time; +double h, h1; +int *l1, *l2; +CKTcircuit *ckt; +{ + int i, j, k, l; + double e; + double ff[MAX_CP_TX_LINES], gg[MAX_CP_TX_LINES]; + double v1_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v2_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i1_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i2_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v1_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v2_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i1_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i2_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + int ext; + register int noL; + + noL = cp->noL; + + for (j = 0; j < noL; j++) { + register double ff1; + + ff[j] = 0.0; + gg[j] = 0.0; + for (k = 0; k < noL; k++) + if (cp->h1t[j][k]) { + if (cp->h1t[j][k]->ifImg) { + double er, ei, a, b, a1, b1; + TMS *tms; + tms = cp->h1t[j][k]; + cp->h1e[j][k][0] = e = exp((double) tms->tm[0].x * h); + expC(tms->tm[1].x, tms->tm[2].x, h, &er, &ei); + cp->h1e[j][k][1] = er; + cp->h1e[j][k][2] = ei; + + ff1 = tms->tm[0].c * e * h1; + ff[j] -= tms->tm[0].cnv_i * e; + gg[j] -= tms->tm[0].cnv_o * e; + ff[j] -= ff1 * cp->in_node[k]->V; + gg[j] -= ff1 * cp->out_node[k]->V; + + multC(tms->tm[1].c, tms->tm[2].c, er, ei, &a1, &b1); + multC(tms->tm[1].cnv_i, tms->tm[2].cnv_i, er, ei, &a, &b); + ff[j] -= 2.0 * (a1 * h1 * cp->in_node[k]->V + a); + multC(tms->tm[1].cnv_o, tms->tm[2].cnv_o, er, ei, &a, &b); + gg[j] -= 2.0 * (a1 * h1 * cp->out_node[k]->V + a); + } else { + ff1 = 0.0; + for (i = 0; i < 3; i++) { + cp->h1e[j][k][i] = e = exp((double) cp->h1t[j][k]->tm[i].x * h); + ff1 -= cp->h1t[j][k]->tm[i].c * e; + ff[j] -= cp->h1t[j][k]->tm[i].cnv_i * e; + gg[j] -= cp->h1t[j][k]->tm[i].cnv_o * e; + } + ff[j] += ff1 * h1 * cp->in_node[k]->V; + gg[j] += ff1 * h1 * cp->out_node[k]->V; + } + } + } + + ext = get_pvs_vi(t, time, cp, v1_i, v2_i, i1_i, i2_i, + v1_o, v2_o, i1_o, i2_o); + + for (j = 0; j < noL; j++) { /** current eqn **/ + register TERM *tm; + + for (k = 0; k < noL; k++) /** node voltage **/ + for (l = 0; l < noL; l++) /** different mode **/ + if (cp->h3t[j][k][l]) { + if (cp->h3t[j][k][l]->ifImg) { + double er, ei, a, b, a1, b1, a2, b2; + register TMS *tms; + tms = cp->h3t[j][k][l]; + expC(tms->tm[1].x, tms->tm[2].x, h, &er, &ei); + a2 = h1 * tms->tm[1].c; + b2 = h1 * tms->tm[2].c; + a = tms->tm[1].cnv_i; + b = tms->tm[2].cnv_i; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, (double) v1_i[l][k] * er + v2_i[l][k], (double) v1_i[l][k] * ei, &a1, &b1); + tms->tm[1].cnv_i = a + a1; + tms->tm[2].cnv_i = b + b1; + a = tms->tm[1].cnv_o; + b = tms->tm[2].cnv_o; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, (double) v1_o[l][k] * er + v2_o[l][k], (double) v1_o[l][k] * ei, &a1, &b1); + tms->tm[1].cnv_o = a + a1; + tms->tm[2].cnv_o = b + b1; + tm = &(tms->tm[0]); + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * + (v1_i[l][k] * e + v2_i[l][k]); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * + (v1_o[l][k] * e + v2_o[l][k]); + ff[j] += tms->aten * v2_o[l][k] + tm->cnv_o + + 2.0 * tms->tm[1].cnv_o; + gg[j] += tms->aten * v2_i[l][k] + tm->cnv_i + + 2.0 * tms->tm[1].cnv_i; + } else { + for (i = 0; i < 3; i++) { /** 3 poles **/ + tm = &(cp->h3t[j][k][l]->tm[i]); + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (v1_i[l][k] * e + v2_i[l][k]); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (v1_o[l][k] * e + v2_o[l][k]); + ff[j] += tm->cnv_o; + gg[j] += tm->cnv_i; + } + + ff[j] += cp->h3t[j][k][l]->aten * v2_o[l][k]; + gg[j] += cp->h3t[j][k][l]->aten * v2_i[l][k]; + } + } + for (k = 0; k < noL; k++) /** line current **/ + for (l = 0; l < noL; l++) /** different mode **/ + if (cp->h2t[j][k][l]) { + if (cp->h2t[j][k][l]->ifImg) { + double er, ei, a, b, a1, b1, a2, b2; + register TMS *tms; + tms = cp->h2t[j][k][l]; + expC(tms->tm[1].x, tms->tm[2].x, h, &er, &ei); + a2 = h1 * tms->tm[1].c; + b2 = h1 * tms->tm[2].c; + a = tms->tm[1].cnv_i; + b = tms->tm[2].cnv_i; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, (double) i1_i[l][k] * er + i2_i[l][k], (double) i1_i[l][k] * ei, &a1, &b1); + tms->tm[1].cnv_i = a + a1; + tms->tm[2].cnv_i = b + b1; + a = tms->tm[1].cnv_o; + b = tms->tm[2].cnv_o; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, (double) i1_o[l][k] * er + i2_o[l][k], (double) i1_o[l][k] * ei, &a1, &b1); + tms->tm[1].cnv_o = a + a1; + tms->tm[2].cnv_o = b + b1; + tm = &(tms->tm[0]); + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * + (i1_i[l][k] * e + i2_i[l][k]); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * + (i1_o[l][k] * e + i2_o[l][k]); + ff[j] += tms->aten * i2_o[l][k] + tm->cnv_o + + 2.0 * tms->tm[1].cnv_o; + gg[j] += tms->aten * i2_i[l][k] + tm->cnv_i + + 2.0 * tms->tm[1].cnv_i; + } else { + for (i = 0; i < 3; i++) { /** 3 poles **/ + tm = &(cp->h2t[j][k][l]->tm[i]); + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (i1_i[l][k] * e + i2_i[l][k]); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (i1_o[l][k] * e + i2_o[l][k]); + ff[j] += tm->cnv_o; + gg[j] += tm->cnv_i; + } + + ff[j] += cp->h2t[j][k][l]->aten * i2_o[l][k]; + gg[j] += cp->h2t[j][k][l]->aten * i2_i[l][k]; + } + } + } + + for (i = 0; i < noL; i++) { + *(ckt->CKTrhs + l1[i]) = ff[i]; + *(ckt->CKTrhs + l2[i]) = gg[i]; + } + + return(ext); +} + +static int +update_cnv(cp, h) +CPLine *cp; +float h; +{ + int i, j, k; + register int noL; + double ai, bi, ao, bo; + double e, t; + register TMS *tms; + register TERM *tm; + + noL = cp->noL; + for (j = 0; j < noL; j++) + for (k = 0; k < noL; k++) { + ai = cp->in_node[k]->V; + ao = cp->out_node[k]->V; + bi = cp->in_node[k]->dv; + bo = cp->out_node[k]->dv; + + if (cp->h1t[j][k]) { + if (cp->h1t[j][k]->ifImg) { + tms = cp->h1t[j][k]; + if (tms == NULL) + continue; + tm = &(tms->tm[0]); + e = cp->h1e[j][k][0]; + t = tm->c / tm->x; + update_cnv_a(tms, h, ai, ao, ai - bi * h, ao - bo * h, + cp->h1e[j][k][1], cp->h1e[j][k][2]); + bi *= t; + bo *= t; + tm->cnv_i = (tm->cnv_i - bi*h) * e + (e - 1.0)*(ai*t + + 1.0e+12*bi/tm->x); + tm->cnv_o = (tm->cnv_o - bo*h) * e + (e - 1.0)*(ao*t + + 1.0e+12*bo/tm->x); + } else + for (i = 0; i < 3; i++) { + tm = &(cp->h1t[j][k]->tm[i]); + + e = cp->h1e[j][k][i]; + + t = tm->c / tm->x; + bi *= t; + bo *= t; + + tm->cnv_i = (tm->cnv_i - bi*h) * e + (e - 1.0)*(ai*t + 1.0e+12*bi/tm->x); + tm->cnv_o = (tm->cnv_o - bo*h) * e + (e - 1.0)*(ao*t + 1.0e+12*bo/tm->x); + } + } + } + return 0; +} + +static VI_list +*new_vi() +{ + VI_list *q; + + if (pool_vi) { + q = pool_vi; + pool_vi = pool_vi->pool; + return(q); + } else return((VI_list *) malloc (sizeof (VI_list))); +} + +static void +free_vi(q) +VI_list *q; +{ + q->pool = pool_vi; + pool_vi = q; +} + + +static int +add_new_vi(here, ckt, time) + CPLinstance *here; + CKTcircuit *ckt; + int time; +{ + VI_list *vi; + register int i, noL; + CPLine *cp, *cp2; + + cp = here->cplines; + cp2 = here->cplines2; + + vi = new_vi(); + vi->time = time; + noL = cp->noL; + for (i = 0; i < noL; i++) { + /* + vi->v_i[i] = cp->in_node[i]->V; + vi->v_o[i] = cp->out_node[i]->V; + */ + vi->v_i[i] = *(ckt->CKTrhsOld + here->CPLposNodes[i]); + vi->v_o[i] = *(ckt->CKTrhsOld + here->CPLnegNodes[i]); + vi->i_i[i] = *(ckt->CKTrhsOld + here->CPLibr1[i]); + vi->i_o[i] = *(ckt->CKTrhsOld + here->CPLibr2[i]); + } + cp->vi_tail->next = vi; + cp2->vi_tail->next = vi; + vi->next = NULL; + cp->vi_tail = vi; + cp2->vi_tail = vi; + + return(1); +} + + +static int +get_pvs_vi(t1, t2, cp, v1_i, v2_i, i1_i, i2_i, v1_o, v2_o, i1_o, i2_o) + CPLine *cp; + int t1, t2; + double v1_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v2_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i1_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i2_i[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v1_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double v2_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i1_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; + double i2_o[MAX_CP_TX_LINES][MAX_CP_TX_LINES]; +{ + double ta[MAX_CP_TX_LINES], tb[MAX_CP_TX_LINES]; + register VI_list *vi, *vi1; + register double f; + register int i, j; + int mini = -1; + double minta = 123456789.0; + int ext = 0; + register int noL; + + noL = cp->noL; + + for (i = 0; i < noL; i++) { + ta[i] = t1 - cp->taul[i]; + tb[i] = t2 - cp->taul[i]; + if (ta[i] < minta) { + minta = ta[i]; + mini = i; + } + } + + for (i = 0; i < noL; i++) { + + ratio[i] = 0.0; + + if (tb[i] <= 0) { + for (j = 0; j < noL; j++) { + i1_i[i][j] = i2_i[i][j] = i1_o[i][j] = i2_o[i][j] = 0.0; + v1_i[i][j] = v2_i[i][j] = cp->dc1[j]; + v1_o[i][j] = v2_o[i][j] = cp->dc2[j]; + } + } else { + if (ta[i] <= 0) { + for (j = 0; j < noL; j++) { + i1_i[i][j] = i1_o[i][j] = 0.0; + v1_i[i][j] = cp->dc1[j]; + v1_o[i][j] = cp->dc2[j]; + } + vi1 = cp->vi_head; + vi = vi1->next; + } else { + vi1 = cp->vi_head; + for (vi = vi1->next; vi->time < ta[i]; ) { + /* if (i == mini) + free_vi(vi1); */ + vi1 = vi; + + /* new */ + vi = vi->next; + if (vi == NULL) goto errordetect; + } + f = (ta[i] - vi1->time) / (vi->time - vi1->time); + for (j = 0; j < noL; j++) { + v1_i[i][j] = vi1->v_i[j] + f * (vi->v_i[j] - vi1->v_i[j]); + v1_o[i][j] = vi1->v_o[j] + f * (vi->v_o[j] - vi1->v_o[j]); + i1_i[i][j] = vi1->i_i[j] + f * (vi->i_i[j] - vi1->i_i[j]); + i1_o[i][j] = vi1->i_o[j] + f * (vi->i_o[j] - vi1->i_o[j]); + } + if (i == mini) + cp->vi_head = vi1; + } + + if (tb[i] > t1) { + /* + fprintf(stderr, "pvs: time = %d\n", t2); + */ + ext = 1; + + ratio[i] = f = (tb[i] - t1) / (t2 - t1); + + if (vi) + for (; vi->next; vi = vi->next); + else + vi = vi1; + f = 1 - f; + for (j = 0; j < noL; j++) { + v2_i[i][j] = vi->v_i[j] * f; + v2_o[i][j] = vi->v_o[j] * f; + i2_i[i][j] = vi->i_i[j] * f; + i2_o[i][j] = vi->i_o[j] * f; + } + } else { + for (; vi->time < tb[i];) { + vi1 = vi; + + /* new */ + vi = vi->next; + if (vi == NULL) goto errordetect; + } + + f = (tb[i] - vi1->time) / (vi->time - vi1->time); + for (j = 0; j < noL; j++) { + v2_i[i][j] = vi1->v_i[j] + f * (vi->v_i[j] - vi1->v_i[j]); + v2_o[i][j] = vi1->v_o[j] + f * (vi->v_o[j] - vi1->v_o[j]); + i2_i[i][j] = vi1->i_i[j] + f * (vi->i_i[j] - vi1->i_i[j]); + i2_o[i][j] = vi1->i_o[j] + f * (vi->i_o[j] - vi1->i_o[j]); + } + } + } + } + + return(ext); + +errordetect: + fprintf(stderr, "your maximum time step is too large for tau.\n"); + fprintf(stderr, "decrease max time step in .tran card and try again\n"); + exit(0); +} + + +static int +update_delayed_cnv(cp, h) + CPLine *cp; + float h; +{ + int i, j, k; + float *ratio; + register double f; + register VI_list *vi; + register TMS *tms; + register int noL; + + h *= 0.5e-12; + ratio = cp->ratio; + vi = cp->vi_tail; + noL = cp->noL; + + for (k = 0; k < noL; k++) /* mode */ + if (ratio[k] > 0.0) + for (i = 0; i < noL; i++) /* current eqn */ + for (j = 0; j < noL; j++) { + tms = cp->h3t[i][j][k]; + if (tms == NULL) + continue; + f = h * ratio[k] * vi->v_i[j]; + tms->tm[0].cnv_i += f * tms->tm[0].c; + tms->tm[1].cnv_i += f * tms->tm[1].c; + tms->tm[2].cnv_i += f * tms->tm[2].c; + + f = h * ratio[k] * vi->v_o[j]; + tms->tm[0].cnv_o += f * tms->tm[0].c; + tms->tm[1].cnv_o += f * tms->tm[1].c; + tms->tm[2].cnv_o += f * tms->tm[2].c; + + tms = cp->h2t[i][j][k]; + f = h * ratio[k] * vi->i_i[j]; + tms->tm[0].cnv_i += f * tms->tm[0].c; + tms->tm[1].cnv_i += f * tms->tm[1].c; + tms->tm[2].cnv_i += f * tms->tm[2].c; + + f = h * ratio[k] * vi->i_o[j]; + tms->tm[0].cnv_o += f * tms->tm[0].c; + tms->tm[1].cnv_o += f * tms->tm[1].c; + tms->tm[2].cnv_o += f * tms->tm[2].c; + } + return(1); +} + + +static int expC(ar, ai, h, cr, ci) +double ar, ai, *cr, *ci; +double h; +{ + double e, cs, si; + + e = exp((double) ar * h); + cs = cos((double) ai * h); + si = sin((double) ai * h); + *cr = e * cs; + *ci = e * si; + + return(1); +} + +static int multC(ar, ai, br, bi, cr, ci) +double ar, ai, br, bi; +double *cr, *ci; +{ + register double tp; + + tp = ar*br - ai*bi; + *ci = ar*bi+ai*br; + *cr = tp; + + return (1); + +} + +static void +update_cnv_a(tms, h, ai, ao, bi, bo, er, ei) + TMS *tms; + float h; + double ai, bi, ao, bo; + double er, ei; +{ + double a, b, a1, b1; + + h *= 0.5e-12; + multC(tms->tm[1].c, tms->tm[2].c, er, ei, &a1, &b1); + multC(tms->tm[1].cnv_i, tms->tm[2].cnv_i, er, ei, &a, &b); + tms->tm[1].cnv_i = a + h * (a1 * bi + ai * tms->tm[1].c); + tms->tm[2].cnv_i = b + h * (b1 * bi + ai * tms->tm[2].c); + + multC(tms->tm[1].cnv_o, tms->tm[2].cnv_o, er, ei, &a, &b); + tms->tm[1].cnv_o = a + h * (a1 * bo + ao * tms->tm[1].c); + tms->tm[2].cnv_o = b + h * (b1 * bo + ao * tms->tm[2].c); +} + +static int divC(ar, ai, br, bi, cr, ci) +double ar, ai, br, bi; +double *cr, *ci; +{ + double t; + + t = br*br + bi*bi; + *cr = (ar*br + ai*bi) / t; + *ci = (ai*br - ar*bi) / t; + + return(1); +} + diff --git a/src/spicelib/devices/cpl/cplmdel.c b/src/spicelib/devices/cpl/cplmdel.c new file mode 100644 index 000000000..0697dc890 --- /dev/null +++ b/src/spicelib/devices/cpl/cplmdel.c @@ -0,0 +1,45 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +CPLmDelete(inModel,modname,kill) + GENmodel **inModel; + IFuid modname; + GENmodel *kill; +{ + CPLmodel **model = (CPLmodel **)inModel; + CPLmodel *modfast = (CPLmodel *)kill; + CPLinstance *here; + CPLinstance *prev = NULL; + CPLmodel **oldmod; + oldmod = model; + + for( ; *model ; model = &((*model)->CPLnextModel)) { + if( (*model)->CPLmodName == modname || + (modfast && *model == modfast) ) goto delgot; + oldmod = model; + } + return(E_NOMOD); + +delgot: + *oldmod = (*model)->CPLnextModel; /* cut deleted device out of list */ + for(here = (*model)->CPLinstances ; here ; here = here->CPLnextInstance) { + if(prev) FREE(prev); + prev = here; + } + if(prev) FREE(prev); + FREE(*model); + return(OK); + +} diff --git a/src/spicelib/devices/cpl/cplmpar.c b/src/spicelib/devices/cpl/cplmpar.c new file mode 100644 index 000000000..03cef866f --- /dev/null +++ b/src/spicelib/devices/cpl/cplmpar.c @@ -0,0 +1,51 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "const.h" +#include "ifsim.h" +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +CPLmParam(param,value,inModel) + int param; + IFvalue *value; + GENmodel *inModel; +{ + register CPLmodel *model = (CPLmodel *)inModel; + switch(param) { + case CPL_R: + model->Rm = value->v.vec.rVec; + model->Rmgiven = TRUE; + break; + case CPL_L: + model->Lm = value->v.vec.rVec; + model->Lmgiven = TRUE; + break; + case CPL_G: + model->Gm = value->v.vec.rVec; + model->Gmgiven = TRUE; + break; + case CPL_C: + model->Cm = value->v.vec.rVec; + model->Cmgiven = TRUE; + break; + case CPL_length: + model->length = value->rValue; + model->lengthgiven = TRUE; + break; + case CPL_MOD_R: + break; + default: + return(E_BADPARM); + } + return(OK); +} diff --git a/src/spicelib/devices/cpl/cplparam.c b/src/spicelib/devices/cpl/cplparam.c new file mode 100644 index 000000000..98dd7eaec --- /dev/null +++ b/src/spicelib/devices/cpl/cplparam.c @@ -0,0 +1,45 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "const.h" +#include "ifsim.h" +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + + +/* ARGSUSED */ +int +CPLparam(param,value,inst,select) + int param; + IFvalue *value; + GENinstance *inst; + IFvalue *select; +{ + CPLinstance *here = (CPLinstance *)inst; + switch(param) { + case CPL_POS_NODE: + here->in_node_names = value->v.vec.sVec; + break; + case CPL_NEG_NODE: + here->out_node_names = value->v.vec.sVec; + break; + case CPL_DIM: + here->dimension = value->iValue; + break; + case CPL_LENGTH: + here->CPLlength = value->rValue; + here->CPLlengthgiven = TRUE; + break; + + default: + return(E_BADPARM); + } + return(OK); +} diff --git a/src/spicelib/devices/cpl/cplsetup.c b/src/spicelib/devices/cpl/cplsetup.c new file mode 100644 index 000000000..f5bb8337c --- /dev/null +++ b/src/spicelib/devices/cpl/cplsetup.c @@ -0,0 +1,2101 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "smpdefs.h" +#include "cpldefs.h" +#include "sperror.h" +#include "suffix.h" + +#include "../cap/capdefs.h" +#include "multi_line.h" + +#define VECTOR_ALLOC(vec, n) { \ + int i; \ + vec = (double **) malloc(n * sizeof(double *)); \ + for (i = 0; i < n; i++) { \ + vec[i] = (double *) malloc(sizeof(double)); \ + } \ +} +#define MATRIX_ALLOC(mat, m, j) { \ + int k; \ + mat = (double ***) malloc(m * sizeof(double **)); \ + for (k = 0; k < m; k++) { \ + VECTOR_ALLOC(mat[k], j); \ + } \ +} + +#define MAX_DEG 8 +#define epsilon 1.0e-88 +#define ABS(x) ((x) >= 0 ? (x) : (-(x))) +#define MAX_STRING 128 + +static double ZY[MAX_DIM][MAX_DIM]; +static double Sv[MAX_DIM][MAX_DIM]; +static double D[MAX_DIM]; +static double Y5[MAX_DIM][MAX_DIM]; +static double Y5_1[MAX_DIM][MAX_DIM]; +static double Sv_1[MAX_DIM][MAX_DIM]; + +static double R_m[MAX_DIM][MAX_DIM]; +static double G_m[MAX_DIM][MAX_DIM]; +static double L_m[MAX_DIM][MAX_DIM]; +static double C_m[MAX_DIM][MAX_DIM]; +static double length; +static double TAU[MAX_DIM]; + +static double A[MAX_DIM][2*MAX_DIM]; + +static double frequency[MAX_DEG]; + +static double Si[MAX_DIM][MAX_DIM]; +static double Si_1[MAX_DIM][MAX_DIM]; + +/* MacLaurin Series */ +static double *SiSv_1[MAX_DIM][MAX_DIM]; +static double *Sip[MAX_DIM][MAX_DIM]; +static double *Si_1p[MAX_DIM][MAX_DIM]; +static double *Sv_1p[MAX_DIM][MAX_DIM]; +static double *W[MAX_DIM]; + +static Mult_Out IWI[MAX_DIM][MAX_DIM]; +static Mult_Out IWV[MAX_DIM][MAX_DIM]; +static Single_Out SIV[MAX_DIM][MAX_DIM]; +static double At[4][4]; +static double Scaling_F; +static double Scaling_F2; + +/* misc.c match */ +static void new_memory(); +static double *vector(); +static void free_vector(); +static void polint(); +/*static int match_x();*/ +static int match(); +static int Gaussian_Elimination2(); +static void eval_Si_Si_1(); +static void loop_ZY(); +static void poly_matrix(); +/*static int checkW();*/ +static void poly_W(); +static void eval_frequency(); +static void store(); +static void store_SiSv_1(); +/*static int check();*/ +static int coupled(); +static int generate_out(); +static int ReadCpL(); +/*static int divC();*/ + +/* mult */ +static void mult_p(); +static void matrix_p_mult(); +static double approx_mode(); +static double eval2(); +static int get_c(); +static int Pade_apx(); +static int Gaussian_Elimination(); +static double root3(); +static int div3(); +static int find_roots(); + +static NODE* insert_node(); +static NDnamePt insert_ND(); +static NODE* NEW_node(); +static NDnamePt ndn; +static NODE *node_tab; +#define epsi_mult 1e-28 + +/* diag */ +static MAXE_PTR sort(); +static void ordering(); +static MAXE_PTR delete_1(); +static void reordering(); +static void diag(); +static int rotate(); + +#define epsi 1.0e-16 +static char *message = "tau of coupled lines is larger than max time step"; + +/* ARGSUSED */ +int +CPLsetup(matrix,inModel,ckt,state) + register SMPmatrix *matrix; + GENmodel *inModel; + CKTcircuit*ckt; + int *state; +{ + register CPLmodel *model = (CPLmodel *)inModel; + register CPLinstance *here; + CKTnode *tmp, *node; + int error, m, p; + char **branchname; + int noL; + + /* loop through all the models */ + for( ; model != NULL; model = model->CPLnextModel ) { + + /* loop through all the instances of the model */ + for (here = model->CPLinstances; here != NULL ; + here=here->CPLnextInstance) { + +/* macro to make elements with built in test for out of memory */ +#define TSTALLOC(ptr,first,second) \ +if((here->ptr = SMPmakeElt(matrix,here->first,here->second))==(double *)NULL){\ + return(E_NOMEM);\ +} + noL = here->dimension; + + here->CPLposNodes = (int *) malloc(noL * sizeof(int)); + here->CPLnegNodes = (int *) malloc(noL * sizeof(int)); + here->CPLibr1 = (int *) malloc(noL * sizeof(int)); + here->CPLibr2 = (int *) malloc(noL * sizeof(int)); + + VECTOR_ALLOC(here->CPLibr1Ibr1, noL); + VECTOR_ALLOC(here->CPLibr2Ibr2, noL); + VECTOR_ALLOC(here->CPLposIbr1, noL); + VECTOR_ALLOC(here->CPLnegIbr2, noL); + VECTOR_ALLOC(here->CPLposPos, noL); + VECTOR_ALLOC(here->CPLnegNeg, noL); + VECTOR_ALLOC(here->CPLnegPos, noL); + VECTOR_ALLOC(here->CPLposNeg, noL); + + MATRIX_ALLOC(here->CPLibr1Pos, noL, noL); + MATRIX_ALLOC(here->CPLibr2Neg, noL, noL); + MATRIX_ALLOC(here->CPLibr1Neg, noL, noL); + MATRIX_ALLOC(here->CPLibr2Pos, noL, noL); + MATRIX_ALLOC(here->CPLibr1Ibr2, noL, noL); + MATRIX_ALLOC(here->CPLibr2Ibr1, noL, noL); + + branchname = (char **) malloc(sizeof(char *) * here->dimension); + if (! here->CPLibr1Given) { + for (m = 0; m < here->dimension; m++) { + branchname[m] = malloc(MAX_STRING); + sprintf(branchname[m], "branch1_%d", m); + error = + CKTmkCur(ckt, &tmp, here->CPLname, branchname[m]); + if (error) return (error); + here->CPLibr1[m] = tmp->number; + } + here->CPLibr1Given = 1; + } + free(branchname); + branchname = (char **) malloc(sizeof(char *) * here->dimension); + if (! here->CPLibr2Given) { + for (m = 0; m < here->dimension; m++) { + branchname[m] = malloc(MAX_STRING); + sprintf(branchname[m], "branch2_%d", m); + error = + CKTmkCur(ckt, &tmp, here->CPLname, branchname[m]); + if (error) return (error); + here->CPLibr2[m] = tmp->number; + } + here->CPLibr2Given = 1; + } + free(branchname); + + for (m = 0; m < here->dimension; m++) { + for (node = ckt->CKTnodes; node; node = node->next) { + if (strcmp(here->in_node_names[m], + node->name) == 0){ + here->CPLposNodes[m] = node->number; + } + } + } + for (m = 0; m < here->dimension; m++) { + for (node = ckt->CKTnodes; node; node = node->next) { + if (strcmp(here->out_node_names[m], + node->name) == 0){ + here->CPLnegNodes[m] = node->number; + } + } + } + + for (m = 0; m < here->dimension; m++) { + TSTALLOC(CPLibr1Ibr1[m],CPLibr1[m],CPLibr1[m]); + TSTALLOC(CPLibr2Ibr2[m],CPLibr2[m],CPLibr2[m]); + TSTALLOC(CPLposIbr1[m],CPLposNodes[m],CPLibr1[m]); + TSTALLOC(CPLnegIbr2[m],CPLnegNodes[m],CPLibr2[m]); + TSTALLOC(CPLposPos[m],CPLposNodes[m],CPLposNodes[m]); + TSTALLOC(CPLnegNeg[m],CPLnegNodes[m],CPLnegNodes[m]); + TSTALLOC(CPLnegPos[m],CPLnegNodes[m],CPLposNodes[m]); + TSTALLOC(CPLposNeg[m],CPLposNodes[m],CPLnegNodes[m]); + + for (p = 0; p < here->dimension; p++) { + + TSTALLOC(CPLibr1Pos[m][p],CPLibr1[m],CPLposNodes[p]); + TSTALLOC(CPLibr2Neg[m][p],CPLibr2[m],CPLnegNodes[p]); + TSTALLOC(CPLibr1Neg[m][p],CPLibr1[m],CPLnegNodes[p]); + TSTALLOC(CPLibr2Pos[m][p],CPLibr2[m],CPLposNodes[p]); + TSTALLOC(CPLibr1Ibr2[m][p],CPLibr1[m],CPLibr2[p]); + TSTALLOC(CPLibr2Ibr1[m][p],CPLibr2[m],CPLibr1[p]); + + } + } + + ReadCpL(here, ckt); + + } + } + + return(OK); +} + + +static int +ReadCpL(here, ckt) +CPLinstance *here; +CKTcircuit *ckt; +{ + int i, j, noL, counter; + float f; + char *name; + CPLine *c, *c2; + ECPLine *ec; + NODE *nd; + RLINE *lines[MAX_CP_TX_LINES]; + ERLINE *er; + + c = (CPLine *) malloc(sizeof (CPLine)); + c2 = (CPLine *) malloc(sizeof (CPLine)); + c->vi_head = c->vi_tail = NULL; + noL = c->noL = here->dimension; + here->cplines = c; + here->cplines2 = c2; + + for (i = 0; i < noL; i++) { + ec = (ECPLine *) malloc(sizeof (ECPLine)); + name = here->in_node_names[i]; + nd = insert_node(name); + ec->link = nd->cplptr; + nd->cplptr = ec; + ec->line = c; + c->in_node[i] = nd; + c2->in_node[i] = nd; + + er = (ERLINE *) malloc(sizeof (ERLINE)); + er->link = nd->rlptr; + nd->rlptr = er; + er->rl = lines[i] = (RLINE *) malloc(sizeof (RLINE)); + er->rl->in_node = nd; + + c->dc1[i] = c->dc2[i] = 0.0; + } + + for (i = 0; i < noL; i++) { + ec = (ECPLine *) malloc(sizeof (ECPLine)); + name = here->out_node_names[i]; + nd = insert_node(name); + ec->link = nd->cplptr; + nd->cplptr = ec; + ec->line = c; + c->out_node[i] = nd; + c2->out_node[i] = nd; + + er = (ERLINE *) malloc(sizeof (ERLINE)); + er->link = nd->rlptr; + nd->rlptr = er; + er->rl = lines[i]; + er->rl->out_node = nd; + } + + + counter = 0; + for (i = 0; i < noL; i++) { + for (j = 0; j < noL; j++) { + if (i > j) { + R_m[i][j] = R_m[j][i]; + G_m[i][j] = G_m[j][i]; + C_m[i][j] = C_m[j][i]; + L_m[i][j] = L_m[j][i]; + } + else { + f = here->CPLmodPtr->Rm[counter]; + R_m[i][j] =here->CPLmodPtr->Rm[counter]= MAX(f, 1.0e-4); + G_m[i][j] = here->CPLmodPtr->Gm[counter]; + L_m[i][j] = here->CPLmodPtr->Lm[counter]; + C_m[i][j] = here->CPLmodPtr->Cm[counter]; + counter++; + } + } + } + if (here->CPLlengthgiven) + length = here->CPLlength; + else length = here->CPLmodPtr->length; + + for (i = 0; i < noL; i++) + lines[i]->g = 1.0 / (R_m[i][i] * length); + + coupled(noL); + + for (i = 0; i < noL; i++) { + double d, t; + int k; + + c->taul[i] = TAU[i] * 1.0e+12; + for (j = 0; j < noL; j++) { + if (SIV[i][j].C_0 == 0.0) + c->h1t[i][j] = NULL; + else { + c->h1t[i][j] = (TMS *) malloc(sizeof (TMS)); + d = c->h1t[i][j]->aten = SIV[i][j].C_0; + c->h1t[i][j]->ifImg = (int) SIV[i][j].Poly[6] - 1.0; + /* since originally 2 for img 1 for noimg */ + c->h1t[i][j]->tm[0].c = SIV[i][j].Poly[0] * d; + c->h1t[i][j]->tm[1].c = SIV[i][j].Poly[1] * d; + c->h1t[i][j]->tm[2].c = SIV[i][j].Poly[2] * d; + c->h1t[i][j]->tm[0].x = SIV[i][j].Poly[3]; + c->h1t[i][j]->tm[1].x = SIV[i][j].Poly[4]; + c->h1t[i][j]->tm[2].x = SIV[i][j].Poly[5]; + if (c->h1t[i][j]->ifImg) + c->h1C[i][j] = c->h1t[i][j]->tm[0].c + 2.0 * c->h1t[i][j]->tm[1].c; + else { + t = 0.0; + for (k = 0; k < 3; k++) + t += c->h1t[i][j]->tm[k].c; + c->h1C[i][j] = t; + } + } + + for (k = 0; k < noL; k++) { + if (IWI[i][j].C_0[k] == 0.0) + c->h2t[i][j][k] = NULL; + else { + c->h2t[i][j][k] = (TMS *) malloc(sizeof (TMS)); + d = c->h2t[i][j][k]->aten = IWI[i][j].C_0[k]; + c->h2t[i][j][k]->ifImg = (int) IWI[i][j].Poly[k][6] - 1.0; + /* since originally 2 for img 1 for noimg */ + c->h2t[i][j][k]->tm[0].c = IWI[i][j].Poly[k][0] * d; + c->h2t[i][j][k]->tm[1].c = IWI[i][j].Poly[k][1] * d; + c->h2t[i][j][k]->tm[2].c = IWI[i][j].Poly[k][2] * d; + c->h2t[i][j][k]->tm[0].x = IWI[i][j].Poly[k][3]; + c->h2t[i][j][k]->tm[1].x = IWI[i][j].Poly[k][4]; + c->h2t[i][j][k]->tm[2].x = IWI[i][j].Poly[k][5]; + if (c->h2t[i][j][k]->ifImg) + c->h2C[i][j][k] = c->h2t[i][j][k]->tm[0].c + 2.0 * + c->h2t[i][j][k]->tm[1].c; + else + c->h2C[i][j][k] = c->h2t[i][j][k]->tm[0].c + + c->h2t[i][j][k]->tm[1].c + + c->h2t[i][j][k]->tm[2].c; + } + if (IWV[i][j].C_0[k] == 0.0) + c->h3t[i][j][k] = NULL; + else { + c->h3t[i][j][k] = (TMS *) malloc(sizeof (TMS)); + d = c->h3t[i][j][k]->aten = IWV[i][j].C_0[k]; + c->h3t[i][j][k]->ifImg = (int) IWV[i][j].Poly[k][6] - 1.0; + /* since originally 2 for img 1 for noimg */ + c->h3t[i][j][k]->tm[0].c = IWV[i][j].Poly[k][0] * d; + c->h3t[i][j][k]->tm[1].c = IWV[i][j].Poly[k][1] * d; + c->h3t[i][j][k]->tm[2].c = IWV[i][j].Poly[k][2] * d; + c->h3t[i][j][k]->tm[0].x = IWV[i][j].Poly[k][3]; + c->h3t[i][j][k]->tm[1].x = IWV[i][j].Poly[k][4]; + c->h3t[i][j][k]->tm[2].x = IWV[i][j].Poly[k][5]; + if (c->h3t[i][j][k]->ifImg) + c->h3C[i][j][k] = c->h3t[i][j][k]->tm[0].c + 2.0 * + c->h3t[i][j][k]->tm[1].c; + else + c->h3C[i][j][k] = c->h3t[i][j][k]->tm[0].c + + c->h3t[i][j][k]->tm[1].c + + c->h3t[i][j][k]->tm[2].c; + } + } + } + } + + for (i = 0; i < noL; i++) { + if (c->taul[i] < ckt->CKTmaxStep) { + errMsg = MALLOC(strlen(message)+1); + strcpy(errMsg,message); + return(-1); + } + } + + return(1); +} + + +/**************************************************************** + misc.c Miscellaneous procedures for simulation of + coupled transmission lines. + ****************************************************************/ + + +static void +new_memory(dim, deg, deg_o) + int dim, deg, deg_o; +{ + int i, j; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + SiSv_1[i][j] = (double *) calloc(deg_o+1, sizeof(double)); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Sip[i][j] = (double *) calloc(deg_o+1, sizeof(double)); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Si_1p[i][j] = (double *) calloc(deg_o+1, sizeof(double)); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Sv_1p[i][j] = (double *) calloc(deg_o+1, sizeof(double)); + + for (i = 0; i < dim; i++) + W[i] = (double *) calloc(MAX_DEG, sizeof(double)); +} + +/*** + ***/ + +/**************************************************************** + match Create a polynomial matching given data points + ****************************************************************/ + + +static double +*vector(nl, nh) + int nl, nh; +{ + double *v; + + v = (double *) malloc((unsigned) (nh - nl +1) * sizeof(double)); + if (!v) { + fprintf(stderr, "Memory Allocation Error by malloc in vector().\n"); + fprintf(stderr, "...now exiting to system ...\n"); + exit(0); + } + return v-nl; +} + +static void +free_vector(v, nl, nh) + double *v; + int nl, nh; +{ + free((char*) (v +nl)); +} + +static void +polint(xa, ya, n, x, y, dy) +/* + Given arrays xa[1..n] and ya[1..n], and given a value x, this routine + returns a value y, and an error estimate dy. If P(x) is the + polynomial of degree n-1 such that P(xa) = ya, then the returned + value y = P(x) + */ + double xa[], ya[], x, *y, *dy; + int n; +{ + int i, m, ns = 1; + double den, dif, dift, ho, hp, w; + double *c, *d, *vector(); + void free_vector(); + + dif = ABS(x - xa[1]); + c = vector(1, n); + d = vector(1, n); + for (i = 1; i <= n; i++) { + if ((dift = ABS(x - xa[i])) < dif) { + ns = i; + dif = dift; + } + c[i] = ya[i]; + d[i] = ya[i]; + } + *y = ya[ns--]; + for (m = 1; m < n; m++) { + for (i = 1; i <= n-m; i++) { + ho = xa[i]-x; + hp = xa[i+m]-x; + w = c[i+1]-d[i]; + if ((den=ho-hp) == 0.0) { + fprintf(stderr, "(Error) in routine POLINT\n"); + fprintf(stderr, "...now exiting to system ...\n"); + exit(0); + } + den = w/den; + d[i] = hp * den; + c[i] = ho * den; + } + *y += (*dy = (2*ns < (n-m) ? c[ns+1] : d[ns--])); + } + free_vector(d, 1, n); + free_vector(c, 1, n); +} + +static int +match(n, cof, xa, ya) + double xa[], ya[], cof[]; + int n; +/* + Given arrays xa[0..n] and ya[0..n] containing a tabulated function + ya = f(xa), this routine returns an array of coefficients cof[0..n], + such that ya[i] = sum_j {cof[j]*xa[i]**j}. + */ +{ + int k, j, i; + double xmin, dy, *x, *y, *xx, *vector(); + void polint(), free_vector(); + + x = vector(0, n); + y = vector(0, n); + xx = vector(0, n); + for (j = 0; j <= n; j++) { + x[j] = xa[j]; + xx[j] = y[j] = ya[j]; + } + for (j = 0; j <= n; j++) { + polint(x-1, y-1, n+1-j, (double) 0.0, &cof[j], &dy); + xmin = 1.0e38; + k = -1; + for (i = 0; i <= n-j; i++) { + if (ABS(x[i]) < xmin) { + xmin = ABS(x[i]); + k = i; + } + if (x[i]) y[i] = (y[i] - cof[j]) / x[i]; + } + for (i = k+1; i <= n-j; i++) { + y[i-1] = y[i]; + x[i-1] = x[i]; + } + } + free_vector(y, 0, n); + free_vector(x, 0, n); + + /**** check ****/ + /* + for (i = 0; i <= n; i++) { + xmin = xa[i]; + dy = cof[0]; + for (j = 1; j <= n; j++) { + dy += xmin * cof[j]; + xmin *= xa[i]; + } + printf("*** real x = %e y = %e\n", xa[i], xx[i]); + printf("*** calculated y = %e\n", dy); + printf("*** error = %e \% \n", (dy-xx[i])/xx[i]); + } + */ + return 0; +} + +/*** + ***/ +/*** +static int +match_x(dim, Alfa, X, Y) + int dim; + double X[]; + double Y[]; + double Alfa[]; +{ + int i, j; + double f; + double scale; + + **** check **** + double xx[16]; + for (i = 0; i <= dim; i++) + xx[i] = Y[i]; + + if (Y[1] == Y[0]) + scale = 1.0; + else + scale = X[1] / (Y[1] - Y[0]); + for (i = 0; i < dim; i++) { + f = X[i+1]; + for (j = dim-1; j >= 0; j--) { + A[i][j] = f; + f *= X[i+1]; + } + A[i][dim] = (Y[i+1] - Y[0])*scale; + } + Gaussian_Elimination2(dim, 1); + Alfa[0] = Y[0]; + for (i = 1; i <= dim; i++) + Alfa[i] = A[dim-i][dim] / scale; + + **** check **** + * + for (i = 0; i <= dim; i++) { + f = X[i]; + scale = Alfa[0]; + for (j = 1; j <= dim; j++) { + scale += f * Alfa[j]; + f *= X[i]; + } + printf("*** real x = %e y = %e\n", X[i], xx[i]); + printf("*** calculated y = %e\n", scale); + printf("*** error = %e \% \n", (scale-xx[i])/xx[i]); + } + * + + return(1); +} +***/ +/*** + ***/ + +static int +Gaussian_Elimination2(dims, type) + /* type = 1 : to solve a linear system + -1 : to inverse a matrix */ + int dims; + int type; +{ + register int i, j, k, dim; + register double f; + double max; + int imax; + + if (type == -1) + dim = 2 * dims; + else + dim = dims; + + for (i = 0; i < dims; i++) { + imax = i; + max = ABS(A[i][i]); + for (j = i+1; j < dim; j++) + if (ABS(A[j][i]) > max) { + imax = j; + max = ABS(A[j][i]); + } + if (max < epsilon) { + fprintf(stderr, " can not choose a pivot (misc)\n"); + exit(0); + } + if (imax != i) + for (k = i; k <= dim; k++) { + f = A[i][k]; + A[i][k] = A[imax][k]; + A[imax][k] = f; + } + + f = 1.0 / A[i][i]; + A[i][i] = 1.0; + + for (j = i+1; j <= dim; j++) + A[i][j] *= f; + + for (j = 0; j < dims ; j++) { + if (i == j) + continue; + f = A[j][i]; + A[j][i] = 0.0; + for (k = i+1; k <= dim; k++) + A[j][k] -= f * A[i][k]; + } + } + + return(1); +} + +/*** + +static void +eval_Si_Si_1(dim, y) + int dim; + double y; +{ + int i, j, k; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Si_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + if (k == j) + Si_1[i][j] += Sv_1[i][k] * + (y * R_m[k][j] + Scaling_F * L_m[k][j]); + else + Si_1[i][j] += Sv_1[i][k] * L_m[k][j] * Scaling_F; + / + Si_1[i][j] *= Scaling_F; + / + } + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Si_1[i][j] /= sqrt((double) D[i]); + + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) + A[i][j] = Si_1[i][j]; + for (j = dim; j < 2* dim; j++) + A[i][j] = 0.0; + A[i][i+dim] = 1.0; + } + Gaussian_Elimination2(dim, -1); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Si[i][j] = A[i][j+dim]; +} + +***/ + +static void +eval_Si_Si_1(dim, y) + int dim; + double y; +{ + int i, j, k; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Si_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Si_1[i][j] += Sv_1[i][k] * (y * R_m[k][j] + Scaling_F * L_m[k][j]); + /* + else + Si_1[i][j] += Sv_1[i][k] * L_m[k][j] * Scaling_F; + Si_1[i][j] *= Scaling_F; + */ + } + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Si_1[i][j] /= sqrt((double) D[i]); + + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) + A[i][j] = Si_1[i][j]; + for (j = dim; j < 2* dim; j++) + A[i][j] = 0.0; + A[i][i+dim] = 1.0; + } + Gaussian_Elimination2(dim, -1); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Si[i][j] = A[i][j+dim]; +} + +/*** + +static void +loop_ZY(dim, y) + int dim; + double y; +{ + int i, j, k; + double fmin, fmin1; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + if (i == j) + ZY[i][j] = Scaling_F * C_m[i][i] + G_m[i] * y; + else + ZY[i][j] = Scaling_F * C_m[i][j]; + diag(dim); + fmin = D[0]; + for (i = 1; i < dim; i++) + if (D[i] < fmin) + fmin = D[i]; + if (fmin < 0) { + fprintf(stderr, "(Error) The capacitance matrix of the multiconductor system is not positive definite.\n"); + exit(0); + } else { + fmin = sqrt(fmin); + fmin1 = 1 / fmin; + } + for (i = 0; i < dim; i++) + D[i] = sqrt((double) D[i]); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Y5[i][j] = D[i] * Sv[j][i]; + Y5_1[i][j] = Sv[j][i] / D[i]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[i][k] * Y5[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Y5[i][j] = Sv_1[i][j]; + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[i][k] * Y5_1[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Y5_1[i][j] = Sv_1[i][j]; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + ZY[i][j] = 0.0; + for (k = 0; k < dim; k++) + if (k == i) + ZY[i][j] += (Scaling_F * L_m[i][i] + R_m[i] * y) * + Y5[k][j]; + else + ZY[i][j] += L_m[i][k] * Y5[k][j] * Scaling_F; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Y5[i][k] * ZY[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + ZY[i][j] = Sv_1[i][j]; + + diag(dim); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[k][i] * Y5[k][j]; + Sv_1[i][j] *= fmin1; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + ZY[i][j] = 0.0; + for (k = 0; k < dim; k++) + ZY[i][j] += Y5_1[i][k] * Sv[k][j]; + ZY[i][j] *= fmin; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Sv[i][j] = ZY[i][j]; + +} +***/ + +static void +loop_ZY(dim, y) + int dim; + double y; +{ + int i, j, k; + double fmin, fmin1; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + ZY[i][j] = Scaling_F * C_m[i][j] + G_m[i][j] * y; + /* + else + ZY[i][j] = Scaling_F * C_m[i][j]; + */ + diag(dim); + fmin = D[0]; + for (i = 1; i < dim; i++) + if (D[i] < fmin) + fmin = D[i]; + if (fmin < 0) { + fprintf(stderr, "(Error) The capacitance matrix of the multiconductor system is not positive definite.\n"); + exit(0); + } else { + fmin = sqrt(fmin); + fmin1 = 1 / fmin; + } + for (i = 0; i < dim; i++) + D[i] = sqrt((double) D[i]); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Y5[i][j] = D[i] * Sv[j][i]; + Y5_1[i][j] = Sv[j][i] / D[i]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[i][k] * Y5[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Y5[i][j] = Sv_1[i][j]; + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[i][k] * Y5_1[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Y5_1[i][j] = Sv_1[i][j]; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + ZY[i][j] = 0.0; + for (k = 0; k < dim; k++) + ZY[i][j] += (Scaling_F * L_m[i][k] + R_m[i][k] * y) * Y5[k][j]; + /* + else + ZY[i][j] += L_m[i][k] * Y5[k][j] * Scaling_F; + */ + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Y5[i][k] * ZY[k][j]; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + ZY[i][j] = Sv_1[i][j]; + + diag(dim); + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + Sv_1[i][j] = 0.0; + for (k = 0; k < dim; k++) + Sv_1[i][j] += Sv[k][i] * Y5[k][j]; + Sv_1[i][j] *= fmin1; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + ZY[i][j] = 0.0; + for (k = 0; k < dim; k++) + ZY[i][j] += Y5_1[i][k] * Sv[k][j]; + ZY[i][j] *= fmin; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + Sv[i][j] = ZY[i][j]; + +} + + +/*** + ***/ + +static void +poly_matrix(A, dim, deg) + double *A[MAX_DIM][MAX_DIM]; + int dim, deg; +{ + int i, j; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + match(deg, A[i][j], frequency, A[i][j]); +} + +/*** + ***/ +/*** +static int +checkW(W, d) + double W[], d; +{ + double f, y; + float y1; + int k; + + printf("(W)y ="); + scanf("%f", &y1); + + f = W[0]; + y = y1; + f += y * W[1]; + for (k = 2; k < 6; k++) { + y *= y1; + f += y * W[k]; + } + printf("W[i]= %e\n ", f*exp((double)-d/y1)); + + return(1); +} +***/ +/*** + ***/ + +static void +poly_W(dim, deg) + int dim, deg; +{ + int i; + extern double approx_mode(); + + for (i = 0; i < dim; i++) { + match(deg, W[i], frequency, W[i]); + TAU[i] = approx_mode(W[i], W[i], length); + /* + checkW(W[i], TAU[i]); + */ + } +} + +/*** + ***/ + +static void +eval_frequency(dim, deg_o) + int deg_o; +{ + int i, im; + double min; + + min = D[0]; + im = 0; + + for (i = 1; i < dim; i++) + if (D[i] < min) { + min = D[i]; + im = i; + } + + if (min <= 0) { + fprintf(stderr, "A mode frequency is not positive. Abort!\n"); + exit(0); + } + + Scaling_F2 = 1.0 / min; + Scaling_F = sqrt(Scaling_F2); + min = length * 8.0; + /* + min *= 1.0e18; + min = sqrt(min)*1.0e-9*length/8.0; + */ + + frequency[0] = 0.0; + + for (i = 1; i <= deg_o; i++) + frequency[i] = frequency[i-1] + min; + + for (i = 0; i < dim; i++) + D[i] *= Scaling_F2; +} + +/*** + ***/ + +static void +store(dim, ind) + int ind; +{ + int i, j; + + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) { + /* store_Sip */ + Sip[i][j][ind] = Si[i][j]; + /* store_Si_1p */ + Si_1p[i][j][ind] = Si_1[i][j]; + /* store_Sv_1p */ + Sv_1p[i][j][ind] = Sv_1[i][j]; + } + /* store_W */ + W[i][ind] = D[i]; + } +} + +/*** + ***/ + +static void +store_SiSv_1(dim, ind) +{ + int i, j, k; + double temp; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + temp = 0.0; + for (k = 0; k < dim; k++) + temp += Si[i][k] * Sv_1[k][j]; + SiSv_1[i][j][ind] = temp; + } +} + +/*** + ***/ +/*** +static int +check(Sip, Si_1p, Sv_1p, SiSv_1p) + double *Sip[MAX_DIM][MAX_DIM]; + double *Si_1p[MAX_DIM][MAX_DIM]; + double *Sv_1p[MAX_DIM][MAX_DIM]; + double *SiSv_1p[MAX_DIM][MAX_DIM]; +{ + double f, y; + float y1; + int i, j, k; + + printf("y ="); + scanf("%f", &y1); + + printf("\n"); + printf("Si =\n"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + f = Sip[i][j][0]; + y = y1; + f += y * Sip[i][j][1]; + for (k = 2; k < 8; k++) { + y *= y1; + f += y * Sip[i][j][k]; + } + printf("%e ", f); + } + printf("\n"); + } + printf("\n"); + printf("Si_1 =\n"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + f = Si_1p[i][j][0]; + y = y1; + f += y * Si_1p[i][j][1]; + for (k = 2; k < 8; k++) { + y *= y1; + f += y * Si_1p[i][j][k]; + } + printf("%e ", f); + } + printf("\n"); + } + printf("\n"); + printf("Sv_1 =\n"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + f = Sv_1p[i][j][0]; + y = y1; + f += y * Sv_1p[i][j][1]; + for (k = 2; k < 8; k++) { + y *= y1; + f += y * Sv_1p[i][j][k]; + } + printf("%e ", f); + } + printf("\n"); + } + printf("\n"); + printf("SiSv_1 =\n"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + f = SiSv_1p[i][j][0]; + y = y1; + f += y * SiSv_1p[i][j][1]; + for (k = 2; k < 8; k++) { + y *= y1; + f += y * SiSv_1p[i][j][k]; + } + printf("%e ", f); + } + printf("\n"); + } + return(1); +} +***/ +/*** + ***/ + +static int +coupled(dim) + int dim; +{ + int deg, deg_o; + int i; + + deg = Right_deg; + deg_o = Left_deg; + new_memory(dim, deg, deg_o); + + Scaling_F = Scaling_F2 = 1.0; + + /*** y = 0 : ZY = LC ***/ + loop_ZY(dim, (double) 0.0); + eval_frequency(dim, deg_o); + eval_Si_Si_1(dim, (double) 0.0); + store_SiSv_1(dim, (int) 0); + store(dim, (int) 0); + + /*** Step 1 ***/ + /*** Step 2 ***/ + for (i = 1; i <= deg_o; i++) { + loop_ZY(dim, frequency[i]); + eval_Si_Si_1(dim, frequency[i]); + store_SiSv_1(dim, i); + store(dim, i); + } + poly_matrix(Sip, dim, deg_o); + poly_matrix(Si_1p, dim, deg_o); + poly_matrix(Sv_1p, dim, deg_o); + poly_W(dim, deg_o); + matrix_p_mult(Sip, W, Si_1p, dim, deg_o, deg_o, IWI); + matrix_p_mult(Sip, W, Sv_1p, dim, deg_o, deg_o, IWV); + + poly_matrix(SiSv_1, dim, deg_o); + + /*** + check(Sip, Si_1p, Sv_1p, SiSv_1); + ***/ + + generate_out(dim, deg_o); + + return(1); +} + +/*** + ***/ + +static int +generate_out(dim, deg_o) + int dim, deg_o; +{ + int i, j, k, rtv; + double C; + double *p; + double c1, c2, c3, x1, x2, x3; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + p = SiSv_1[i][j]; + SIV[i][j].C_0 = C = p[0]; + if (C == 0.0) + continue; + for (k = 0; k <= deg_o; k++) + p[k] /= C; + if (i == j) { + rtv = Pade_apx((double) sqrt((double) G_m[i][i] / R_m[i][i]) / C, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + SIV[i][j].C_0 = 0.0; + printf("SIV\n"); + continue; + } + } else { + rtv = Pade_apx((double) 0.0, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + SIV[i][j].C_0 = 0.0; + printf("SIV\n"); + continue; + } + } + p = SIV[i][j].Poly = (double *) calloc(7, sizeof(double)); + p[0] = c1; + p[1] = c2; + p[2] = c3; + p[3] = x1; + p[4] = x2; + p[5] = x3; + p[6] = (double) rtv; + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + for (k = 0; k < dim; k++) { + p = IWI[i][j].Poly[k]; + C = IWI[i][j].C_0[k]; + if (C == 0.0) + continue; + if (i == j && k == i) { + rtv = Pade_apx((double) + exp(- sqrt((double) G_m[i][i] * R_m[i][i]) * length) / C, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + IWI[i][j].C_0[k] = 0.0; + printf("IWI %d %d %d\n", i, j, k); + continue; + } + } else { + rtv = Pade_apx((double) 0.0, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + IWI[i][j].C_0[k] = 0.0; + printf("IWI %d %d %d\n", i, j, k); + continue; + } + } + p[0] = c1; + p[1] = c2; + p[2] = c3; + p[3] = x1; + p[4] = x2; + p[5] = x3; + p[6] = (double) rtv; + } + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + for (k = 0; k < dim; k++) { + p = IWV[i][j].Poly[k]; + C = IWV[i][j].C_0[k]; + if (C == 0.0) + continue; + if (i == j && k == i) { + rtv = Pade_apx((double) sqrt((double) G_m[i][i] / R_m[i][i]) * + exp(- sqrt((double) G_m[i][i] * R_m[i][i]) * length) / C, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + IWV[i][j].C_0[k] = 0.0; + printf("IWV %d %d %d\n", i, j, k); + continue; + } + } else { + rtv = Pade_apx((double) 0.0, + p, &c1, &c2, &c3, &x1, &x2, &x3); + if (rtv == 0) { + IWV[i][j].C_0[k] = 0.0; + printf("IWV %d %d %d\n", i, j, k); + continue; + } + } + p[0] = c1; + p[1] = c2; + p[2] = c3; + p[3] = x1; + p[4] = x2; + p[5] = x3; + p[6] = (double) rtv; + } + return(1); +} + +/**************************************************************** + mult.c Multiplication for Matrix of Polynomial + X(y) = A(y) D(y) B(y), + where D(y) is a diagonal matrix with each + diagonal entry of the form + e^{-a_i s}d(y), for which s = 1/y + and i = 1..N. + Each entry of X(y) will be of the form + \sum_{i=1}^N c_i e^{-a_i s} b_i(y), where + b_i(0) = 1; therefore, those + b_i(y)'s will be each entry's output. + ****************************************************************/ + +static void +mult_p(p1, p2, p3, d1, d2, d3) +/* p3 = p1 * p2 */ + double *p1, *p2, *p3; + int d1, d2, d3; +{ + int i, j, k; + + for (i = 0; i <= d3; i++) + p3[i] = 0.0; + for (i = 0; i <= d1; i++) + for (j = i, k = 0; k <= d2; j++, k++) { + if (j > d3) + break; + p3[j] += p1[i] * p2[k]; + } +} + + +static void +matrix_p_mult(A, D, B, dim, deg, deg_o, X) + double *A[MAX_DIM][MAX_DIM]; + double *D[MAX_DIM]; + double *B[MAX_DIM][MAX_DIM]; + int dim, deg, deg_o; + Mult_Out X[MAX_DIM][MAX_DIM]; +{ + int i, j, k, l; + double *p; + double *T[MAX_DIM][MAX_DIM]; + double t1; + + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + p = T[i][j] = (double *) calloc(deg_o+1, sizeof(double)); + mult_p(B[i][j], D[i], p, deg, deg_o, deg_o); + } + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) + for (k = 0; k < dim; k++) { + p = X[i][j].Poly[k] = + (double *) calloc(deg_o+1, sizeof(double)); + mult_p(A[i][k], T[k][j], p, deg, deg_o, deg_o); + t1 = X[i][j].C_0[k] = p[0]; + if (t1 != 0.0) { + p[0] = 1.0; + for (l = 1; l <= deg_o; l++) + p[l] /= t1; + } + } + + /********** + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + for (k = 0; k < dim; k++) { + fprintf(outFile, "(%5.3f)", X[i][j].C_0[k]); + p = X[i][j].Poly[k]; + for (l = 0; l <= deg_o; l++) + fprintf(outFile, "%5.3f ", p[l]); + fprintf(outFile, "\n"); + } + fprintf(outFile, "\n"); + } + ***********/ +} + +/**************************************************************** + mode approximation + + ****************************************************************/ + +/*** + ***/ + +static double +approx_mode(X, b, length) + double X[], b[], length; +{ + double w0, w1, w2, w3, w4, w5; + double a[8]; + double delay, atten; + double y1, y2, y3, y4, y5, y6; + int i, j; + + w0 = X[0]; + w1 = X[1] / w0; /* a */ + w2 = X[2] / w0; /* b */ + w3 = X[3] / w0; /* c */ + w4 = X[4] / w0; /* d */ + w5 = X[5] / w0; /* e */ + + y1 = 0.5 * w1; + y2 = w2 - y1 * y1; + y3 = 3 * w3 - 3.0 * y1 * y2; + y4 = 12.0 * w4 - 3.0 * y2 * y2 - 4.0 * y1 * y3; + y5 = 60.0 * w5 - 5.0 * y1 * y4 -10.0 * y2 * y3; + y6 = -10.0 * y3 * y3 - 15.0 * y2 * y4 - 6.0 * y1 * y5; + + delay = sqrt(w0) * length / Scaling_F; + atten = exp((double) - delay * y1); + + a[1] = y2 / 2.0; + a[2] = y3 / 6.0; + a[3] = y4 / 24.0; + a[4] = y5 / 120.0; + a[5] = y6 / 720.0; + + a[1] *= -delay; + a[2] *= -delay; + a[3] *= -delay; + a[4] *= -delay; + a[5] *= -delay; + + b[0] = 1.0; + b[1] = a[1]; + for (i = 2; i <= 5; i++) { + b[i] = 0.0; + for (j = 1; j <= i; j++) + b[i] += j * a[j] * b[i-j]; + b[i] = b[i] / (double) i; + } + + for (i = 0; i <= 5; i++) + b[i] *= atten; + + return(delay); +} + +/*** + ***/ + +static double +eval2(a, b, c, x) + double a, b, c, x; +{ + return(a*x*x + b*x + c); +} + +/*** + ***/ + +static int +get_c(q1, q2, q3, p1, p2, a, b, cr, ci) + double q1, q2, q3, p1, p2, a, b; + double *cr, *ci; +{ + double d, n; + + d = (3.0*(a*a-b*b)+2.0*p1*a+p2)*(3.0*(a*a-b*b)+2.0*p1*a+p2); + d += (6.0*a*b+2.0*p1*b)*(6.0*a*b+2.0*p1*b); + n = -(q1*(a*a-b*b)+q2*a+q3)*(6.0*a*b+2.0*p1*b); + n += (2.0*q1*a*b+q2*b)*(3.0*(a*a-b*b)+2.0*p1*a+p2); + *ci = n/d; + n = (3.0*(a*a-b*b)+2.0*p1*a+p2)*(q1*(a*a-b*b)+q2*a+q3); + n += (6.0*a*b+2.0*p1*b)*(2.0*q1*a*b+q2*b); + *cr = n/d; + + return(1); +} + + +static int +Pade_apx(a_b, b, c1, c2, c3, x1, x2, x3) +/* + b[0] + b[1]*y + b[2]*y^2 + ... + b[5]*y^5 + ... + = (q3*y^3 + q2*y^2 + q1*y + 1) / (p3*y^3 + p2*y^2 + p1*y + 1) + + where b[0] is always equal to 1.0 and neglected, + and y = 1/s. + + (q3*y^3 + q2*y^2 + q1*y + 1) / (p3*y^3 + p2*y^2 + p1*y + 1) + = (s^3 + q1*s^2 + q2*s + q3) / (s^3 + p1*s^2 + p2*s + p3) + = c1 / (s - x1) + c2 / (s - x2) + c3 / (s - x3) + 1.0 + */ + double a_b; + double b[]; + double *c1, *c2, *c3; + double *x1, *x2, *x3; +{ + double p1, p2, p3, q1, q2, q3; + + At[0][0] = 1.0 - a_b; + At[0][1] = b[1]; + At[0][2] = b[2]; + At[0][3] = -b[3]; + + At[1][0] = b[1]; + At[1][1] = b[2]; + At[1][2] = b[3]; + At[1][3] = -b[4]; + + At[2][0] = b[2]; + At[2][1] = b[3]; + At[2][2] = b[4]; + At[2][3] = -b[5]; + + Gaussian_Elimination(3); + + p3 = At[0][3]; + p2 = At[1][3]; + p1 = At[2][3]; + /* + if (p3 < 0.0 || p2 < 0.0 || p1 < 0.0 || p1*p2 <= p3) + return(0); + */ + q1 = p1 + b[1]; + q2 = b[1] * p1 + p2 + b[2]; + q3 = p3 * a_b; + + if (find_roots(p1, p2, p3, x1, x2, x3)) { + /* + printf("complex roots : %e %e %e \n", *x1, *x2, *x3); + */ + *c1 = eval2(q1 - p1, q2 - p2, q3 - p3, *x1) / + eval2((double) 3.0, (double) 2.0 * p1, p2, *x1); + get_c(q1 - p1, q2 - p2, q3 - p3, p1, p2, *x2, *x3, c2, c3); + return(2); + } else { + /* new + printf("roots are %e %e %e \n", *x1, *x2, *x3); + */ + *c1 = eval2(q1 - p1, q2 - p2, q3 - p3, *x1) / + eval2((double) 3.0, (double) 2.0 * p1, p2, *x1); + *c2 = eval2(q1 - p1, q2 - p2, q3 - p3, *x2) / + eval2((double) 3.0, (double) 2.0 * p1, p2, *x2); + *c3 = eval2(q1 - p1, q2 - p2, q3 - p3, *x3) / + eval2((double) 3.0, (double) 2.0 * p1, p2, *x3); + return(1); + } +} + +static int +Gaussian_Elimination(dims) + int dims; +{ + register int i, j, k, dim; + register double f; + double max; + int imax; + + dim = dims; + + for (i = 0; i < dim; i++) { + imax = i; + max = ABS(At[i][i]); + for (j = i+1; j < dim; j++) + if (ABS(At[j][i]) > max) { + imax = j; + max = ABS(At[j][i]); + } + if (max < epsi_mult) { + fprintf(stderr, " can not choose a pivot (mult)\n"); + exit(0); + } + if (imax != i) + for (k = i; k <= dim; k++) { + f = At[i][k]; + At[i][k] = At[imax][k]; + At[imax][k] = f; + } + + f = 1.0 / At[i][i]; + At[i][i] = 1.0; + + for (j = i+1; j <= dim; j++) + At[i][j] *= f; + + for (j = 0; j < dim ; j++) { + if (i == j) + continue; + f = At[j][i]; + At[j][i] = 0.0; + for (k = i+1; k <= dim; k++) + At[j][k] -= f * At[i][k]; + } + } + return(1); +} + +static double +root3(a1, a2, a3, x) + double x; + double a1, a2, a3; +{ + double t1, t2; + + t1 = x * (x * (x + a1) + a2) + a3; + t2 = x * (2.0*a1 + 3.0*x) + a2; + + return(x - t1 / t2); +} + +static int +div3(a1, a2, a3, x, p1, p2) + double x; + double a1, a2, a3; + double *p1, *p2; +{ + *p1 = a1 + x; + + /* *p2 = a2 + (a1 + x) * x; */ + + *p2 = - a3 / x; + + return(1); +} + + +static int +find_roots(a1, a2, a3, x1, x2, x3) + double a1, a2, a3; + double *x1, *x2, *x3; +{ + double x, t; + double p, q; + + /*********************************************** + double m,n; + p = a1*a1/3.0 - a2; + q = a1*a2/3.0 - a3 - 2.0*a1*a1*a1/27.0; + p = p*p*p/27.0; + t = q*q - 4.0*p; + if (t < 0.0) { + if (q != 0.0) { + t = atan(sqrt((double)-t)/q); + if (t < 0.0) + t += 3.141592654; + t /= 3.0; + x = 2.0 * pow(p, 0.16666667) * cos(t) - a1 / 3.0; + } else { + t /= -4.0; + x = pow(t, 0.16666667) * 1.732 - a1 / 3.0; + } + } else { + t = sqrt(t); + m = 0.5*(q - t); + n = 0.5*(q + t); + if (m < 0.0) + m = -pow((double) -m, (double) 0.3333333); + else + m = pow((double) m, (double) 0.3333333); + if (n < 0.0) + n = -pow((double) -n, (double) 0.3333333); + else + n = pow((double) n, (double) 0.3333333); + x = m + n - a1 / 3.0; + } + ************************************************/ + q = (a1*a1-3.0*a2) / 9.0; + p = (2.0*a1*a1*a1-9.0*a1*a2+27.0*a3) / 54.0; + t = q*q*q - p*p; + if (t >= 0.0) { + t = acos((double) p /(q * sqrt(q))); + x = -2.0*sqrt(q)*cos(t / 3.0) - a1/3.0; + } else { + if (p > 0.0) { + t = pow(sqrt(-t)+p, (double) 1.0 / 3.0); + x = -(t + q / t) - a1/3.0; + } else if (p == 0.0) { + x = -a1/3.0; + } else { + t = pow(sqrt(-t)-p, (double) 1.0 / 3.0); + x = (t + q / t) - a1/3.0; + } + } + /* + fprintf(stderr, "..1.. %e\n", x*x*x+a1*x*x+a2*x+a3); + */ + { + double x1; + int i = 0; + x1 = x; + for (t = root3(a1, a2, a3, x); ABS(t-x) > 5.0e-4; + t = root3(a1, a2, a3, x)) + if (++i == 32) { + x = x1; + break; + } else + x = t; + } + /* + fprintf(stderr, "..2.. %e\n", x*x*x+a1*x*x+a2*x+a3); + */ + + + *x1 = x; + div3(a1, a2, a3, x, &a1, &a2); + + t = a1 * a1 - 4.0 * a2; + if (t < 0) { + /* + fprintf(stderr, "***** Two Imaginary Roots.\n Update.\n"); + *x2 = -0.5 * a1; + *x3 = a2 / *x2; + */ + *x3 = 0.5 * sqrt(-t); + *x2 = -0.5 * a1; + return(1); + } else { + t = sqrt(t); + if (a1 >= 0.0) + *x2 = t = -0.5 * (a1 + t); + else + *x2 = t = -0.5 * (a1 - t); + *x3 = a2 / t; + return(0); + } +} + + +static NDnamePt +insert_ND(name, ndn) + char *name; + NDnamePt *ndn; +{ + int cmp; + NDnamePt p; + + if (*ndn == NULL) { + p = *ndn = (NDnamePt) malloc(sizeof (NDname)); + p->nd = NULL; + p->right = p->left = NULL; + strcpy(p->id, name); + return(p); + } + cmp = strcmp((*ndn)->id, name); + if (cmp == 0) + return(*ndn); + else { + if (cmp < 0) + return(insert_ND(name, &((*ndn)->left))); + else + return(insert_ND(name, &((*ndn)->right))); + } +} + +static NODE * +insert_node(name) + char *name; +{ + NDnamePt n; + NODE *p; + + n = insert_ND(name, &ndn); + if (n->nd == NULL) { + p = NEW_node(); + p->name = n; + n->nd = p; + p->next = node_tab; + node_tab = p; + return(p); + } else + return(n->nd); +} +/*** +static int divC(ar, ai, br, bi, cr, ci) +double ar, ai, br, bi; +double *cr, *ci; +{ + double t; + + t = br*br + bi*bi; + *cr = (ar*br + ai*bi) / t; + *ci = (ai*br - ar*bi) / t; + + return(1); +} +***/ + +static NODE +*NEW_node() +{ + /* + char *malloc(); + */ + NODE *n; + + n = (NODE *) malloc (sizeof (NODE)); + n->mptr = NULL; + n->gptr = NULL; + n->cptr = NULL; + n->rptr = NULL; + n->tptr = NULL; + n->cplptr = NULL; + n->rlptr = NULL; + n->ddptr = NULL; + n->cvccsptr = NULL; + n->vccsptr = NULL; + n->CL = 0.001; + n->V = n->dv = 0.0; + n->gsum = n->cgsum = 0; + n->is = 0; + n->tag = 0; + n->flag = 0; + n->region = NULL; + n->ofile = NULL; + n->dvtag = 0; + + return(n); +} + + + +/**************************************************************** + diag.c This file contains the main(). + ****************************************************************/ + +#define epsi2 1.0e-8 + +static int dim; +static MAXE_PTR row; + +static MAXE_PTR +sort(list, val, r, c, e) + MAXE_PTR list, e; + float val; + int r, c; +{ + if (list == NULL || list->value < val) { + e->row = r; + e->col = c; + e->value = val; + e->next = list; + return(e); + } else { + list->next = sort(list->next, val, r, c, e); + return(list); + } +} + + +static void +ordering() +{ + MAXE_PTR e; + int i, j, m; + float mv; + + for (i = 0; i < dim-1; i++) { + m = i+1; + mv = ABS(ZY[i][m]); + for (j = m+1; j < dim; j++) + if ((int)(ABS(ZY[i][j]) * 1e7) > (int) (1e7 *mv)) { + + mv = ABS(ZY[i][j]); + m = j; + } + e = (MAXE_PTR) malloc(sizeof (MAXE)); + row = sort(row, mv, i, m, e); + } +} + + +static MAXE_PTR +delete_1(list, rc) + MAXE_PTR *list; + int rc; +{ + MAXE_PTR list1, e; + + list1 = *list; + if ((*list)->row == rc) { + *list = (*list)->next; + return(list1); + } + for (e = list1->next; e->row != rc; e = e->next) + list1 = e; + list1->next = e->next; + return(e); +} + + +static void +reordering(p, q) + int p, q; +{ + MAXE_PTR e; + int j, m; + float mv; + + m = p+1; + mv = ABS(ZY[p][m]); + for (j = m+1; j < dim; j++) + if ((int)(ABS(ZY[p][j]) * 1e7) > (int) (1e7 *mv)) { + mv = ABS(ZY[p][j]); + m = j; + } + e = delete_1(&row, p); + row = sort(row, mv, p, m, e); + + m = q+1; + if (m != dim) { + mv = ABS(ZY[q][m]); + for (j = m+1; j < dim; j++) + if ((int)(ABS(ZY[q][j]) * 1e7) > (int) (1e7 *mv)) { + + mv = ABS(ZY[q][j]); + m = j; + } + e = delete_1(&row, q); + row = sort(row, mv, q, m, e); + } + +} + +static void +diag(dims) + int dims; +{ + int i, j, c; + double fmin, fmax; + + dim = dims; + row = NULL; + + fmin = fmax = ABS(ZY[0][0]); + for (i = 0; i < dim; i++) + for (j = i; j < dim; j++) + if (ABS(ZY[i][j]) > fmax) + fmax = ABS(ZY[i][j]); + else if (ABS(ZY[i][j]) < fmin) + fmin = ABS(ZY[i][j]); + fmin = 2.0 / (fmin + fmax); + for (i = 0; i < dim; i++) + for (j = i; j < dim; j++) + ZY[i][j] *= fmin; + + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) + if (i == j) + Sv[i][i] = 1.0; + else + Sv[i][j] = 0.0; + } + + ordering(); + + if (row) + for (c = 0; row->value > epsi2; c++) { + int p, q; + + p = row->row; + q = row->col; + + rotate(dim, p, q); + reordering(p, q); + } + + for (i = 0; i < dim; i++) + D[i] = ZY[i][i] / fmin; +} + +/**************************************************************** + rotate() rotation of the Jacobi's method + ****************************************************************/ + +static int +rotate(dim, p, q) + int p, q, dim; +{ + int j; + double co, si; + double ve, mu, ld; + double T[MAX_DIM]; + double t; + + ld = - ZY[p][q]; + mu = 0.5 * (ZY[p][p] - ZY[q][q]); + ve = sqrt((double) ld*ld + mu*mu); + co = sqrt((double) (ve + ABS(mu)) / (2.0 * ve)); + si = SGN(mu) * ld / (2.0 * ve * co); + + for (j = p+1; j < dim; j++) + T[j] = ZY[p][j]; + for (j = 0; j < p; j++) + T[j] = ZY[j][p]; + + for (j = p+1; j < dim; j++) { + if (j == q) + continue; + if (j > q) + ZY[p][j] = T[j] * co - ZY[q][j] * si; + else + ZY[p][j] = T[j] * co - ZY[j][q] * si; + } + for (j = q+1; j < dim; j++) { + if (j == p) + continue; + ZY[q][j] = T[j] * si + ZY[q][j] * co; + } + for (j = 0; j < p; j++) { + if (j == q) + continue; + ZY[j][p] = T[j] * co - ZY[j][q] * si; + } + for (j = 0; j < q; j++) { + if (j == p) + continue; + ZY[j][q] = T[j] * si + ZY[j][q] * co; + } + + t = ZY[p][p]; + ZY[p][p] = t * co * co + ZY[q][q] * si * si - 2.0 * ZY[p][q] * si * co; + ZY[q][q] = t * si * si + ZY[q][q] * co * co + 2.0 * ZY[p][q] * si * co; + + ZY[p][q] = 0.0; + + { + double R[MAX_DIM]; + + for (j = 0; j < dim; j++) { + T[j] = Sv[j][p]; + R[j] = Sv[j][q]; + } + + for (j = 0; j < dim; j++) { + Sv[j][p] = T[j] * co - R[j] * si; + Sv[j][q] = T[j] * si + R[j] * co; + } + } + + return(1); + +} + diff --git a/src/spicelib/devices/txl/Makefile.am b/src/spicelib/devices/txl/Makefile.am new file mode 100755 index 000000000..19f634b5c --- /dev/null +++ b/src/spicelib/devices/txl/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in + +pkglib_LTLIBRARIES = libtxl.la + +libtxl_la_SOURCES = \ + txl.c \ + txlask.c \ + txldest.c \ + txlload.c \ + txlmdel.c \ + txlparam.c \ + txlacct.c \ + txldel.c \ + txlmask.c \ + txlmpar.c \ + txlsetup.c \ + txlinit.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/spicelib/devices/txl/txl.c b/src/spicelib/devices/txl/txl.c new file mode 100644 index 000000000..c1b3d2a8c --- /dev/null +++ b/src/spicelib/devices/txl/txl.c @@ -0,0 +1,38 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "txldefs.h" +#include "devdefs.h" +#include "ifsim.h" +#include "suffix.h" + +IFparm TXLpTable[] = { + IP("pos_node", TXL_IN_NODE, IF_INTEGER,"Positive node of txl"), + IP("neg_node", TXL_OUT_NODE, IF_INTEGER,"Negative node of txl"), + IOP("length", TXL_LENGTH, IF_REAL,"length of line"), +}; + +IFparm TXLmPTable[] = { /* model parameters */ + IOP( "r", TXL_R, IF_REAL,"resistance per length"), + IOP( "l", TXL_L, IF_REAL,"inductance per length"), + IOP( "c", TXL_C, IF_REAL,"capacitance per length"), + IOP( "g", TXL_G, IF_REAL,"conductance per length"), + IOP( "length", TXL_length, IF_REAL,"length"), + IP( "txl", TXL_MOD_R, IF_FLAG,"Device is a txl model"), +}; + +char *TXLnames[] = { + "Y+", + "Y-" +}; + +int TXLnSize = NUMELEMS(TXLnames); +int TXLiSize = sizeof(TXLinstance); +int TXLmSize = sizeof(TXLmodel); +int TXLmPTSize = NUMELEMS(TXLmPTable); +int TXLpTSize = NUMELEMS(TXLpTable); diff --git a/src/spicelib/devices/txl/txlacct.c b/src/spicelib/devices/txl/txlacct.c new file mode 100644 index 000000000..bc4c979f6 --- /dev/null +++ b/src/spicelib/devices/txl/txlacct.c @@ -0,0 +1,76 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include +#include "cktdefs.h" +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +TXLaccept(ckt,inModel) + register CKTcircuit *ckt; + GENmodel *inModel; + /* set up the breakpoint table. + */ +{ + register TXLmodel *model = (TXLmodel *)inModel; + register TXLinstance *here; + int hint; + double h, v, v1; + NODE *nd; + TXLine *tx; + + /* loop through all the voltage source models */ + for( ; model != NULL; model = model->TXLnextModel ) { + + /* loop through all the instances of the model */ + for (here = model->TXLinstances; here != NULL ; + here=here->TXLnextInstance) { + + h = ckt->CKTdelta; + hint = (int) (h * 1e12); + if (hint != 0) { + tx = here->txline; + nd = tx->in_node; + if (nd->dvtag == 0) { + v = nd->V; + v1 = nd->V = *(ckt->CKTrhs + here->TXLposNode); + nd->dv = (v1 - v) / hint; + nd->dvtag = 1; + } + nd = tx->out_node; + if (nd->dvtag == 0) { + v = nd->V; + v1 = nd->V = *(ckt->CKTrhs + here->TXLnegNode); + nd->dv = (v1 - v) / hint; + nd->dvtag = 1; + } + } + else { + /* can't happen. */ + printf("zero h detected\n"); + exit(1); + } + } + } + model = (TXLmodel *)inModel; + for( ; model != NULL; model = model->TXLnextModel ) { + for (here = model->TXLinstances; here != NULL ; + here=here->TXLnextInstance) { + nd = here->txline->in_node; + nd->dvtag = 0; + nd = here->txline->out_node; + nd->dvtag = 0; + } + } + + return(OK); +} diff --git a/src/spicelib/devices/txl/txlask.c b/src/spicelib/devices/txl/txlask.c new file mode 100644 index 000000000..2fd2dde5f --- /dev/null +++ b/src/spicelib/devices/txl/txlask.c @@ -0,0 +1,43 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include +#include "const.h" +#include "txldefs.h" +#include "ifsim.h" +#include "cktdefs.h" +#include "sperror.h" +#include "suffix.h" + + +/*ARGSUSED*/ +int +TXLask(ckt,inst,which,value,select) + CKTcircuit *ckt; + GENinstance *inst; + int which; + IFvalue *value; + IFvalue *select; +{ + TXLinstance *fast = (TXLinstance *)inst; + switch(which) { + case TXL_OUT_NODE: + value->iValue = fast->TXLnegNode; + return(OK); + case TXL_IN_NODE: + value->iValue = fast->TXLposNode; + return(OK); + case TXL_LENGTH: + value->rValue = fast->TXLlength; + return(OK); + default: + return(E_BADPARM); + } + /* NOTREACHED */ +} diff --git a/src/spicelib/devices/txl/txldefs.h b/src/spicelib/devices/txl/txldefs.h new file mode 100644 index 000000000..3bb520f10 --- /dev/null +++ b/src/spicelib/devices/txl/txldefs.h @@ -0,0 +1,92 @@ +#ifndef TXL +#define TXL + +#include "ifsim.h" +#include "cktdefs.h" +#include "gendefs.h" +#include "complex.h" +#include "noisedef.h" +#include "swec.h" + +/* information used to describe a single instance */ + +typedef struct sTXLinstance { + struct sTXLmodel *TXLmodPtr; /* backpointer to model */ + struct sTXLinstance *TXLnextInstance; /* pointer to next instance of + * current model*/ + + IFuid TXLname; /* pointer to character string naming this instance */ + + int TXLposNode; + int TXLnegNode; + double TXLlength; + int TXLibr1; + int TXLibr2; + TXLine *txline; /* pointer to SWEC txline type */ + TXLine *txline2; /* pointer to SWEC txline type. temporary storage */ + char *in_node_name; + char *out_node_name; + + double *TXLposPosptr; + double *TXLposNegptr; + double *TXLnegPosptr; + double *TXLnegNegptr; + double *TXLibr1Posptr; + double *TXLibr2Negptr; + double *TXLposIbr1ptr; + double *TXLnegIbr2ptr; + double *TXLibr1Negptr; + double *TXLibr2Posptr; + double *TXLibr1Ibr1ptr; + double *TXLibr2Ibr2ptr; + double *TXLibr1Ibr2ptr; + double *TXLibr2Ibr1ptr; + + unsigned TXLibr1Given : 1; + unsigned TXLibr2Given : 1; + unsigned TXLdcGiven : 1; + unsigned TXLlengthgiven : 1; /* flag to indicate C was specified */ + +} TXLinstance ; + + +/* per model data */ + +typedef struct sTXLmodel { /* model structure for a txl */ + int TXLmodType; /* type index of this device type */ + struct sTXLmodel *TXLnextModel; /* pointer to next possible model in + * linked list */ + TXLinstance * TXLinstances; /* pointer to list of instances that have this + * model */ + IFuid TXLmodName; /* pointer to character string naming this model */ + + double R; + double L; + double G; + double C; + double length; + unsigned Rgiven : 1; /* flag to indicate R was specified */ + unsigned Lgiven : 1; /* flag to indicate L was specified */ + unsigned Ggiven : 1; /* flag to indicate G was specified */ + unsigned Cgiven : 1; /* flag to indicate C was specified */ + unsigned lengthgiven : 1; /* flag to indicate C was specified */ + +} TXLmodel; + +/* instance parameters */ +#define TXL_IN_NODE 1 +#define TXL_OUT_NODE 2 +#define TXL_LENGTH 3 + +/* model parameters */ +#define TXL_R 101 +#define TXL_C 102 +#define TXL_G 103 +#define TXL_L 104 +#define TXL_length 105 +#define TXL_MOD_R 106 + +#include "txlext.h" +extern VI_list_txl *pool_vi_txl; + +#endif /*TXL*/ diff --git a/src/spicelib/devices/txl/txldel.c b/src/spicelib/devices/txl/txldel.c new file mode 100644 index 000000000..3817a3f65 --- /dev/null +++ b/src/spicelib/devices/txl/txldel.c @@ -0,0 +1,38 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +TXLdelete(inModel,name,inst) + GENmodel *inModel; + IFuid name; + GENinstance **inst; +{ + TXLmodel *model = (TXLmodel *)inModel; + TXLinstance **fast = (TXLinstance **)inst; + TXLinstance **prev = NULL; + TXLinstance *here; + + for( ; model ; model = model->TXLnextModel) { + prev = &(model->TXLinstances); + for(here = *prev; here ; here = *prev) { + if(here->TXLname == name || (fast && here==*fast) ) { + *prev= here->TXLnextInstance; + FREE(here); + return(OK); + } + prev = &(here->TXLnextInstance); + } + } + return(E_NODEV); +} diff --git a/src/spicelib/devices/txl/txldest.c b/src/spicelib/devices/txl/txldest.c new file mode 100644 index 000000000..6c371f9fe --- /dev/null +++ b/src/spicelib/devices/txl/txldest.c @@ -0,0 +1,36 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "txldefs.h" +#include "suffix.h" + + +void +TXLdestroy(inModel) + GENmodel **inModel; +{ + TXLmodel **model = (TXLmodel **)inModel; + TXLinstance *here; + TXLinstance *prev = NULL; + TXLmodel *mod = *model; + TXLmodel *oldmod = NULL; + + for( ; mod ; mod = mod->TXLnextModel) { + if(oldmod) FREE(oldmod); + oldmod = mod; + prev = (TXLinstance *)NULL; + for(here = mod->TXLinstances ; here ; here = here->TXLnextInstance) { + if(prev) FREE(prev); + prev = here; + } + if(prev) FREE(prev); + } + if(oldmod) FREE(oldmod); + *model = NULL; +} diff --git a/src/spicelib/devices/txl/txlext.h b/src/spicelib/devices/txl/txlext.h new file mode 100644 index 000000000..2ad036a7f --- /dev/null +++ b/src/spicelib/devices/txl/txlext.h @@ -0,0 +1,19 @@ +#ifdef __STDC__ +/* extern int TXLaccept(CKTcircuit*,GENmodel*); */ +extern int TXLdelete(GENmodel*,IFuid,GENinstance**); +extern void TXLdestroy(GENmodel**); +extern int TXLload(GENmodel*,CKTcircuit*); +extern int TXLmDelete(GENmodel**,IFuid,GENmodel*); +extern int TXLmParam(int,IFvalue*,GENmodel*); +extern int TXLparam(int,IFvalue*,GENinstance*,IFvalue*); +extern int TXLsetup(SMPmatrix*,GENmodel*,CKTcircuit*,int*); +#else /* stdc */ +/* extern int TXLaccept(); */ +extern int TXLdelete(); +extern void TXLdestroy(); +extern int TXLload(); +extern int TXLmDelete(); +extern int TXLmParam(); +extern int TXLparam(); +extern int TXLsetup(); +#endif /* stdc */ diff --git a/src/spicelib/devices/txl/txlfbr.c b/src/spicelib/devices/txl/txlfbr.c new file mode 100644 index 000000000..07d9a5b8f --- /dev/null +++ b/src/spicelib/devices/txl/txlfbr.c @@ -0,0 +1,41 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "cktdefs.h" +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +TXLfindBr(ckt,inModel,name) + register CKTcircuit *ckt; + GENmodel *inModel; + register IFuid name; +{ + register TXLmodel *model = (TXLmodel *)inModel; + register TXLinstance *here; + int error; + CKTnode *tmp; + + for( ; model != NULL; model = model->TXLnextModel) { + for (here = model->TXLinstances; here != NULL; + here = here->TXLnextInstance) { + if(here->TXLname == name) { + if(here->TXLbranch == 0) { + error = CKTmkCur(ckt,&tmp,here->TXLname,"branch"); + if(error) return(error); + here->TXLbranch = tmp->number; + } + return(here->TXLbranch); + } + } + } + return(0); +} diff --git a/src/spicelib/devices/txl/txlinit.c b/src/spicelib/devices/txl/txlinit.c new file mode 100644 index 000000000..ffbd446f2 --- /dev/null +++ b/src/spicelib/devices/txl/txlinit.c @@ -0,0 +1,73 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ +#include + +#include + +#include "txlitf.h" +#include "txlext.h" +#include "txlinit.h" + + +SPICEdev TXLinfo = { + { + "TransLine", + "Simple Lossy Transmission Line", + + &TXLnSize, + &TXLnSize, + TXLnames, + + &TXLpTSize, + TXLpTable, + + &TXLmPTSize, + TXLmPTable, + }, + + TXLparam, + TXLmParam, + TXLload, + TXLsetup, + NULL, + NULL, + NULL, + NULL, + NULL, /* TXLfindBranch, */ + TXLload, /* ac load */ + NULL, + TXLdestroy, +#ifdef DELETES + TXLmDelete, + TXLdelete, +#else /* DELETES */ + NULL, + NULL, +#endif /* DELETES */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + &TXLiSize, + &TXLmSize + +}; + +SPICEdev * +get_txl_info(void) +{ + return &TXLinfo; +} diff --git a/src/spicelib/devices/txl/txlinit.h b/src/spicelib/devices/txl/txlinit.h new file mode 100644 index 000000000..7a64d3706 --- /dev/null +++ b/src/spicelib/devices/txl/txlinit.h @@ -0,0 +1,18 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ +#ifndef _TXLINIT_H +#define _TXLINIT_H + +extern IFparm TXLpTable[ ]; +extern IFparm TXLmPTable[ ]; +extern int TXLmPTSize; +extern int TXLpTSize; +extern char *TXLnames[ ]; +extern int TXLiSize; +extern int TXLmSize; +extern int TXLnSize; + +#endif diff --git a/src/spicelib/devices/txl/txlitf.h b/src/spicelib/devices/txl/txlitf.h new file mode 100644 index 000000000..b4e306300 --- /dev/null +++ b/src/spicelib/devices/txl/txlitf.h @@ -0,0 +1,12 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#ifndef DEV_TXL +#define DEV_TXL + +SPICEdev *get_txl_info(void); + +#endif diff --git a/src/spicelib/devices/txl/txlload.c b/src/spicelib/devices/txl/txlload.c new file mode 100644 index 000000000..0c73ceec0 --- /dev/null +++ b/src/spicelib/devices/txl/txlload.c @@ -0,0 +1,689 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + +VI_list_txl *pool_vi_txl; +static double ratio[MAX_CP_TX_LINES]; +static int update_cnv_txl(); +static VI_list_txl *new_vi_txl(); +static void free_vi_txl(); +static int add_new_vi_txl(); +static int get_pvs_vi_txl(); +static int right_consts_txl(); +static int update_delayed_cnv_txl(); +static int multC(); +static int expC(); +static void copy_tx(); +/*static char *message = "tau of txl line is larger than max time step";*/ + +/*ARGSUSED*/ +int +TXLload(inModel,ckt) + GENmodel *inModel; + CKTcircuit *ckt; +{ + register TXLmodel *model = (TXLmodel *)inModel; + register TXLinstance *here; + TXLine *tx, *tx2; + int k, l; + int time, time2; + double h, h1, f; + int hint; + float hf; + NODE *nd; + double v, v1, g; + int cond1; + CKTnode *node; + VI_list_txl *vi, *vi_before; + int i, before, delta; + + + /* debug + printf("before txlload\n"); + SMPprint(ckt->CKTmatrix, stdout); + */ + + h = ckt->CKTdelta; + h1 = 0.5 * h; + time2 = (int) (ckt->CKTtime * 1e12); + hint = (int)(h * 1e12); + hf = (float)(h * 1e12); + time = (int) ((ckt->CKTtime - ckt->CKTdelta) * 1e12); + + cond1= ckt->CKTmode & MODEDC; + + for( ; model != NULL; model = model->TXLnextModel ) { + for (here = model->TXLinstances; here != NULL ; + here=here->TXLnextInstance) { + + tx = here->txline; + if (cond1 || tx->vi_head == NULL) continue; + + if (time < tx->vi_tail->time) { + time = tx->vi_tail->time; + hint = time2 - time; + } + + vi_before = tx->vi_tail; + before = tx->vi_tail->time; + + if (time > tx->vi_tail->time) { + + copy_tx(tx, here->txline2); + add_new_vi_txl(here, ckt, time); + + delta = time - before; + + nd = tx->in_node; + v = vi_before->v_i; + nd->V = tx->vi_tail->v_i; + v1 = nd->V; + nd->dv = (v1 - v) / delta; + + nd = tx->out_node; + v = vi_before->v_o; + v1 = nd->V = tx->vi_tail->v_o; + nd->dv = (v1 - v) / delta; + + if (tx->lsl) continue; + update_cnv_txl(tx, (float) delta); + if (tx->ext) update_delayed_cnv_txl(tx, (float) delta); + } + } + } + + model = (TXLmodel *)inModel; + for( ; model != NULL; model = model->TXLnextModel ) { + for (here = model->TXLinstances; here != NULL ; + here=here->TXLnextInstance) { + + tx = here->txline; + tx2 = here->txline2; + + if (!tx->lsl && hf > tx->taul) { + + fprintf(stderr, "your time step is too large for tau.\n"); + fprintf(stderr, "please decrease max time step in .tran card.\n"); + fprintf(stderr, ".tran tstep tstop tstart tmax.\n"); + fprintf(stderr, "make tmax smaller than %e and try again.\n", + tx->taul * 1e-12); + + return (1111); + + + } + + if (cond1) { + if (here->TXLlengthgiven) + g = model->R * here->TXLlength; + else g = model->R * here->TXLmodPtr->length; + *(here->TXLposIbr1ptr) += 1.0; + *(here->TXLnegIbr2ptr) += 1.0; + *(here->TXLibr1Ibr1ptr) += 1.0; + *(here->TXLibr1Ibr2ptr) += 1.0; + *(here->TXLibr2Posptr) += 1.0; + *(here->TXLibr2Negptr) -= 1.0; + *(here->TXLibr2Ibr1ptr) -= g; + + continue; + + } + + /* dc setup */ + if (here->TXLdcGiven == 0 && !cond1) { + nd = tx->in_node; + for (node = ckt->CKTnodes;node; node = node->next) { + if (strcmp(nd->name->id, node->name) == 0) { + tx->dc1 = tx2->dc1 = ckt->CKTrhsOld[node->number]; + nd->V = tx->dc1; + break; + } + } + nd = tx->out_node; + for (node = ckt->CKTnodes;node; node = node->next) { + if (strcmp(nd->name->id, node->name) == 0) { + tx->dc2 = tx2->dc2 = ckt->CKTrhsOld[node->number]; + nd->V = tx->dc2; + break; + } + } + here->TXLdcGiven = 1; + + vi = new_vi_txl(); + vi->time = 0; + + vi->i_i = *(ckt->CKTrhsOld + here->TXLibr1); + vi->i_o = *(ckt->CKTrhsOld + here->TXLibr2); + + vi->v_i = tx->dc1; + vi->v_o = tx->dc2; + + for (i = 0; i < 3; i++) { + tx->h1_term[i].cnv_i = + - tx->dc1 * tx->h1_term[i].c / tx->h1_term[i].x; + tx->h1_term[i].cnv_o = + - tx->dc2 * tx->h1_term[i].c / tx->h1_term[i].x; + } + for (i = 0; i < 3; i++) { + tx->h2_term[i].cnv_i = 0.0; + tx->h2_term[i].cnv_o = 0.0; + } + for (i = 0; i < 6; i++) { + tx->h3_term[i].cnv_i = + - tx->dc1 * tx->h3_term[i].c / tx->h3_term[i].x; + tx->h3_term[i].cnv_o = + - tx->dc2 * tx->h3_term[i].c / tx->h3_term[i].x; + } + vi->next = NULL; + tx->vi_tail = vi; + tx->vi_head = vi; + here->txline2->vi_tail = vi; + here->txline2->vi_head = vi; + + } + + /* change 6,6 1/18/93 + *(here->TXLibr1Ibr1ptr) -= 1.0; + *(here->TXLibr2Ibr2ptr) -= 1.0; + *(here->TXLposIbr1ptr) += 1.0; + *(here->TXLnegIbr2ptr) += 1.0; + *(here->TXLibr1Posptr) += tx->sqtCdL + h1 * tx->h1C; + *(here->TXLibr2Negptr) += tx->sqtCdL + h1 * tx->h1C; + */ + *(here->TXLibr1Ibr1ptr) = -1.0; + *(here->TXLibr2Ibr2ptr) = -1.0; + *(here->TXLposIbr1ptr) = 1.0; + *(here->TXLnegIbr2ptr) = 1.0; + *(here->TXLibr1Posptr) = tx->sqtCdL + h1 * tx->h1C; + *(here->TXLibr2Negptr) = tx->sqtCdL + h1 * tx->h1C; + + k = here->TXLibr1; + l = here->TXLibr2; + + copy_tx(tx2, tx); + + if (right_consts_txl(tx2, time, time2, h, h1, k, l, ckt)) { + if (tx->lsl) { + f = ratio[0] * tx->h3_aten; + *(here->TXLibr1Negptr) = -f; + *(here->TXLibr2Posptr) = -f; + f = ratio[0] * tx->h2_aten; + *(here->TXLibr1Ibr2ptr) = -f; + *(here->TXLibr2Ibr1ptr) = -f; + } + else { + tx->ext = 1; + tx->ratio = ratio[0]; + if (ratio[0] > 0.0) { + f = ratio[0] * (h1 * (tx->h3_term[0].c + + tx->h3_term[1].c + tx->h3_term[2].c + + tx->h3_term[3].c + tx->h3_term[4].c + + tx->h3_term[5].c ) + tx->h3_aten); + *(here->TXLibr1Negptr) = -f; + *(here->TXLibr2Posptr) = -f; + f = ratio[0] * (h1 * ( tx->h2_term[0].c + + tx->h2_term[1].c + tx->h2_term[2].c ) + + tx->h2_aten); + *(here->TXLibr1Ibr2ptr) = -f; + *(here->TXLibr2Ibr1ptr) = -f; + } + } + } + else tx->ext = 0; + } + } + + if (cond1) return (OK); + + /* debug + printf("after txlload\n"); + SMPprint(ckt->CKTmatrix, stdout); + */ + + return(OK); +} + +static void copy_tx(new, old) +TXLine *new, *old; +{ + int i; + VI_list_txl *temp; + + new->lsl = old->lsl; + new->ext = old->ext; + new->ratio = old->ratio; + new->taul = old->taul; + new->sqtCdL = old->sqtCdL; + new->h2_aten = old->h2_aten; + new->h3_aten = old->h3_aten; + new->h1C = old->h1C; + for (i= 0; i < 3; i++) { + new->h1e[i] = old->h1e[i]; + + new->h1_term[i].c = old->h1_term[i].c; + new->h1_term[i].x = old->h1_term[i].x; + new->h1_term[i].cnv_i = old->h1_term[i].cnv_i; + new->h1_term[i].cnv_o = old->h1_term[i].cnv_o; + + new->h2_term[i].c = old->h2_term[i].c; + new->h2_term[i].x = old->h2_term[i].x; + new->h2_term[i].cnv_i = old->h2_term[i].cnv_i; + new->h2_term[i].cnv_o = old->h2_term[i].cnv_o; + } + for (i= 0; i < 6; i++) { + new->h3_term[i].c = old->h3_term[i].c; + new->h3_term[i].x = old->h3_term[i].x; + new->h3_term[i].cnv_i = old->h3_term[i].cnv_i; + new->h3_term[i].cnv_o = old->h3_term[i].cnv_o; + } + + new->ifImg = old->ifImg; + if (new->vi_tail != old->vi_tail) { + /* someting wrong */ + exit (0); + } + + while (new->vi_head->time < old->vi_head->time) { + temp = new->vi_head; + new->vi_head = new->vi_head->next; + free_vi_txl(temp); + } + +} + + +static int update_cnv_txl(tx, h) + TXLine *tx; + float h; +{ + int i; + + double ai, bi, ao, bo; + register double e, t; + + ai = tx->in_node->V; + ao = tx->out_node->V; + bi = tx->in_node->dv; + bo = tx->out_node->dv; + + for (i = 0; i < 3; i++) { + register TERM *tm; + tm = &(tx->h1_term[i]); + + e = tx->h1e[i]; + + t = tm->c / tm->x; + bi *= t; + bo *= t; + + tm->cnv_i = (tm->cnv_i - bi*h) * e + (e - 1.0)*(ai*t + 1.0e+12*bi/tm->x); + tm->cnv_o = (tm->cnv_o - bo*h) * e + (e - 1.0)*(ao*t + 1.0e+12*bo/tm->x); + } + return (1); +} + + +static VI_list_txl +*new_vi_txl() +{ + VI_list_txl *q; + + if (pool_vi_txl) { + q = pool_vi_txl; + pool_vi_txl = pool_vi_txl->pool; + return(q); + } else + return((VI_list_txl *) malloc (sizeof (VI_list_txl))); +} + +static void +free_vi_txl(q) + VI_list_txl *q; +{ + q->pool = pool_vi_txl; + pool_vi_txl = q; +} + + +static int add_new_vi_txl(here, ckt, time) +TXLinstance *here; +CKTcircuit *ckt; +int time; +{ + VI_list_txl *vi; + TXLine *tx, *tx2; + + tx = here->txline; + tx2 = here->txline2; + + vi = new_vi_txl(); + vi->time = time; + tx->vi_tail->next = vi; + tx2->vi_tail->next = vi; + vi->next = NULL; + tx->vi_tail = vi; + tx2->vi_tail = vi; + + vi->v_i = *(ckt->CKTrhsOld + here->TXLposNode); + vi->v_o = *(ckt->CKTrhsOld + here->TXLnegNode); + vi->i_i = *(ckt->CKTrhsOld + here->TXLibr1); + vi->i_o = *(ckt->CKTrhsOld + here->TXLibr2); + return(1); +} + + +static int +get_pvs_vi_txl(t1, t2, tx, v1_i, v2_i, i1_i, i2_i, v1_o, v2_o, i1_o, i2_o) + TXLine *tx; + int t1, t2; + double *v1_i, *v2_i, *i1_i, *i2_i, *v1_o, *v2_o, *i1_o, *i2_o; +{ + double ta, tb; + register VI_list_txl *vi, *vi1; + register double f; + int ext = 0; + + ta = t1 - tx->taul; + tb = t2 - tx->taul; + if (tb <= 0) { + *v1_i = *v2_i = tx->dc1; + *v2_o = *v1_o = tx->dc2; + *i1_i = *i2_i = *i1_o = *i2_o = 0; + return(ext); + } + + if (ta <= 0) { + *i1_i = *i1_o = 0.0; + *v1_i = tx->dc1; + *v1_o = tx->dc2; + vi1 = tx->vi_head; + vi = vi1->next; + } else { + vi1 = tx->vi_head; + for (vi = vi1->next; vi->time < ta; vi = vi->next) { + /* free_vi_txl(vi1); */ + vi1 = vi; + } + f = (ta - vi1->time) / (vi->time - vi1->time); + *v1_i = vi1->v_i + f * (vi->v_i - vi1->v_i); + *v1_o = vi1->v_o + f * (vi->v_o - vi1->v_o); + *i1_i = vi1->i_i + f * (vi->i_i - vi1->i_i); + *i1_o = vi1->i_o + f * (vi->i_o - vi1->i_o); + tx->vi_head = vi1; + } + + if (tb > t1) { + + /* fprintf(stderr, "pvs: time = %d\n", t2); */ + ext = 1; + /* + f = tb - t1; + *v2_i = tx->in_node->V + tx->in_node->dv * f; + *v2_o = tx->out_node->V + tx->out_node->dv * f; + + if (vi) { + for (; vi->time != t1; vi = vi->next) + vi1 = vi; + + f /= (double) (t1 - vi1->time); + *i2_i = vi->i_i + f * (vi->i_i - vi1->i_i); + *i2_o = vi->i_o + f * (vi->i_o - vi1->i_o); + } else { + *i2_i = vi1->i_i; + *i2_o = vi1->i_o; + } + */ + ratio[0] = f = (tb - t1) / (t2 - t1); + if (vi) + for (; vi->time != t1; vi = vi->next); + else + vi = vi1; + f = 1 - f; + *v2_i = vi->v_i * f; + *v2_o = vi->v_o * f; + *i2_i = vi->i_i * f; + *i2_o = vi->i_o * f; + } else { + for (; vi->time < tb; vi = vi->next) + vi1 = vi; + + f = (tb - vi1->time) / (vi->time - vi1->time); + *v2_i = vi1->v_i + f * (vi->v_i - vi1->v_i); + *v2_o = vi1->v_o + f * (vi->v_o - vi1->v_o); + *i2_i = vi1->i_i + f * (vi->i_i - vi1->i_i); + *i2_o = vi1->i_o + f * (vi->i_o - vi1->i_o); + } + + return(ext); +} + + +static int +right_consts_txl(tx, t, time, h, h1, l1, l2, ckt) +TXLine *tx; +int t, time; +double h, h1; /*** h1 = 0.5 * h ***/ +int l1, l2; +CKTcircuit *ckt; +{ + int i; + register double ff=0.0, gg=0.0, e; + double v1_i, v2_i, i1_i, i2_i; + double v1_o, v2_o, i1_o, i2_o; + int ext; + + if (! tx->lsl) { + register double ff1=0.0; + for (i = 0; i < 3; i++) { + tx->h1e[i] = e = exp((double) tx->h1_term[i].x * h); + ff1 -= tx->h1_term[i].c * e; + ff -= tx->h1_term[i].cnv_i * e; + gg -= tx->h1_term[i].cnv_o * e; + } + ff += ff1 * h1 * tx->in_node->V; + gg += ff1 * h1 * tx->out_node->V; + } + + ext = get_pvs_vi_txl(t, time, tx, &v1_i, &v2_i, &i1_i, &i2_i, &v1_o, &v2_o, &i1_o, &i2_o); + + if (tx->lsl) { + ff = tx->h3_aten * v2_o + tx->h2_aten * i2_o; + gg = tx->h3_aten * v2_i + tx->h2_aten * i2_i; + } else { + if (tx->ifImg) { + double a, b, er, ei, a1, b1, a2, b2; + + for (i = 0; i < 4; i++) { + register TERM *tm; + tm = &(tx->h3_term[i]); + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (v1_i * e + v2_i); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (v1_o * e + v2_o); + } + expC(tx->h3_term[4].x, tx->h3_term[5].x, h, &er, &ei); + a2 = h1 * tx->h3_term[4].c; + b2 = h1 * tx->h3_term[5].c; + + a = tx->h3_term[4].cnv_i; + b = tx->h3_term[5].cnv_i; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, v1_i * er + v2_i, v1_i * ei, &a1, &b1); + tx->h3_term[4].cnv_i = a + a1; + tx->h3_term[5].cnv_i = b + b1; + + a = tx->h3_term[4].cnv_o; + b = tx->h3_term[5].cnv_o; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, v1_o * er + v2_o, v1_o * ei, &a1, &b1); + tx->h3_term[4].cnv_o = a + a1; + tx->h3_term[5].cnv_o = b + b1; + + ff += tx->h3_aten * v2_o; + gg += tx->h3_aten * v2_i; + + for (i = 0; i < 5; i++) { + ff += tx->h3_term[i].cnv_o; + gg += tx->h3_term[i].cnv_i; + } + ff += tx->h3_term[4].cnv_o; + gg += tx->h3_term[4].cnv_i; + + { + register TERM *tm; + tm = &(tx->h2_term[0]); + + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (i1_i * e + i2_i); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (i1_o * e + i2_o); + } + expC(tx->h2_term[1].x, tx->h2_term[2].x, h, &er, &ei); + a2 = h1 * tx->h2_term[1].c; + b2 = h1 * tx->h2_term[2].c; + + a = tx->h2_term[1].cnv_i; + b = tx->h2_term[2].cnv_i; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, i1_i * er + i2_i, i1_i * ei, &a1, &b1); + tx->h2_term[1].cnv_i = a + a1; + tx->h2_term[2].cnv_i = b + b1; + + a = tx->h2_term[1].cnv_o; + b = tx->h2_term[2].cnv_o; + multC(a, b, er, ei, &a, &b); + multC(a2, b2, i1_o * er + i2_o, i1_o * ei, &a1, &b1); + tx->h2_term[1].cnv_o = a + a1; + tx->h2_term[2].cnv_o = b + b1; + + ff += tx->h2_aten * i2_o + tx->h2_term[0].cnv_o + + 2.0 * tx->h2_term[1].cnv_o; + gg += tx->h2_aten * i2_i + tx->h2_term[0].cnv_i + + 2.0 * tx->h2_term[1].cnv_i; + } else { + for (i = 0; i < 6; i++) { + register TERM *tm; + tm = &(tx->h3_term[i]); + + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (v1_i * e + v2_i); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (v1_o * e + v2_o); + } + + ff += tx->h3_aten * v2_o; + gg += tx->h3_aten * v2_i; + + for (i = 0; i < 6; i++) { + ff += tx->h3_term[i].cnv_o; + gg += tx->h3_term[i].cnv_i; + } + + for (i = 0; i < 3; i++) { + register TERM *tm; + tm = &(tx->h2_term[i]); + + e = exp((double) tm->x * h); + tm->cnv_i = tm->cnv_i * e + h1 * tm->c * (i1_i * e + i2_i); + tm->cnv_o = tm->cnv_o * e + h1 * tm->c * (i1_o * e + i2_o); + } + + ff += tx->h2_aten * i2_o; + gg += tx->h2_aten * i2_i; + + for (i = 0; i < 3; i++) { + ff += tx->h2_term[i].cnv_o; + gg += tx->h2_term[i].cnv_i; + } + } + } + + *(ckt->CKTrhs + l1) = ff; + *(ckt->CKTrhs + l2) = gg; + + return(ext); +} + + +static int +update_delayed_cnv_txl(tx, h) + TXLine *tx; + float h; +{ + float ratio; + register double f; + register VI_list_txl *vi; + register TERM *tms; + + h *= 0.5e-12; + ratio = tx->ratio; + vi = tx->vi_tail; + + if (ratio > 0.0) { + tms = tx->h3_term; + f = h * ratio * vi->v_i; + tms[0].cnv_i += f * tms[0].c; + tms[1].cnv_i += f * tms[1].c; + tms[2].cnv_i += f * tms[2].c; + tms[3].cnv_i += f * tms[3].c; + tms[4].cnv_i += f * tms[4].c; + tms[5].cnv_i += f * tms[5].c; + + f = h * ratio * vi->v_o; + tms[0].cnv_o += f * tms[0].c; + tms[1].cnv_o += f * tms[1].c; + tms[2].cnv_o += f * tms[2].c; + tms[3].cnv_o += f * tms[3].c; + tms[4].cnv_o += f * tms[4].c; + tms[5].cnv_o += f * tms[5].c; + + tms = tx->h2_term; + f = h * ratio * vi->i_i; + tms[0].cnv_i += f * tms[0].c; + tms[1].cnv_i += f * tms[1].c; + tms[2].cnv_i += f * tms[2].c; + + f = h * ratio * vi->i_o; + tms[0].cnv_o += f * tms[0].c; + tms[1].cnv_o += f * tms[1].c; + tms[2].cnv_o += f * tms[2].c; + } + + return(1); +} + +static int expC(ar, ai, h, cr, ci) + double ar, ai, *cr, *ci; + float h; +{ + double e, cs, si; + + e = exp((double) ar * h); + cs = cos((double) ai * h); + si = sin((double) ai * h); + *cr = e * cs; + *ci = e * si; + + return(1); +} + +static int multC(ar, ai, br, bi, cr, ci) + double ar, ai, br, bi; + double *cr, *ci; +{ + register double tp; + + tp = ar*br - ai*bi; + *ci = ar*bi + ai*br; + *cr = tp; + + return (1); + +} + diff --git a/src/spicelib/devices/txl/txlmask.c b/src/spicelib/devices/txl/txlmask.c new file mode 100644 index 000000000..995a8bf74 --- /dev/null +++ b/src/spicelib/devices/txl/txlmask.c @@ -0,0 +1,49 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "const.h" +#include "cktdefs.h" +#include "ifsim.h" +#include "txldefs.h" +#include "sperror.h" +#include "devdefs.h" +#include "suffix.h" +#include "swec.h" + + +/* ARGSUSED */ +int +TXLmodAsk(ckt,inModel,which,value) + CKTcircuit *ckt; + GENmodel *inModel; + int which; + IFvalue *value; +{ + TXLmodel *model = (TXLmodel *)inModel; + switch(which) { + case TXL_R: + value->rValue = model->R; + return(OK); + case TXL_C: + value->rValue = model->C; + return(OK); + case TXL_G: + value->rValue = model->G; + return(OK); + case TXL_L: + value->rValue = model->L; + return(OK); + case TXL_length: + value->rValue = model->length; + return(OK); + default: + return(E_BADPARM); + } +} + diff --git a/src/spicelib/devices/txl/txlmdel.c b/src/spicelib/devices/txl/txlmdel.c new file mode 100644 index 000000000..434f03543 --- /dev/null +++ b/src/spicelib/devices/txl/txlmdel.c @@ -0,0 +1,45 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +TXLmDelete(inModel,modname,kill) + GENmodel **inModel; + IFuid modname; + GENmodel *kill; +{ + TXLmodel **model = (TXLmodel **)inModel; + TXLmodel *modfast = (TXLmodel *)kill; + TXLinstance *here; + TXLinstance *prev = NULL; + TXLmodel **oldmod; + oldmod = model; + + for( ; *model ; model = &((*model)->TXLnextModel)) { + if( (*model)->TXLmodName == modname || + (modfast && *model == modfast) ) goto delgot; + oldmod = model; + } + return(E_NOMOD); + +delgot: + *oldmod = (*model)->TXLnextModel; /* cut deleted device out of list */ + for(here = (*model)->TXLinstances ; here ; here = here->TXLnextInstance) { + if(prev) FREE(prev); + prev = here; + } + if(prev) FREE(prev); + FREE(*model); + return(OK); + +} diff --git a/src/spicelib/devices/txl/txlmpar.c b/src/spicelib/devices/txl/txlmpar.c new file mode 100644 index 000000000..f3346bf55 --- /dev/null +++ b/src/spicelib/devices/txl/txlmpar.c @@ -0,0 +1,51 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "const.h" +#include "ifsim.h" +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +int +TXLmParam(param,value,inModel) + int param; + IFvalue *value; + GENmodel *inModel; +{ + register TXLmodel *model = (TXLmodel *)inModel; + switch(param) { + case TXL_R: + model->R = value->rValue; + model->Rgiven = TRUE; + break; + case TXL_L: + model->L = value->rValue; + model->Lgiven = TRUE; + break; + case TXL_G: + model->G = value->rValue; + model->Ggiven = TRUE; + break; + case TXL_C: + model->C = value->rValue; + model->Cgiven = TRUE; + break; + case TXL_length: + model->length = value->rValue; + model->lengthgiven = TRUE; + break; + case TXL_MOD_R: + break; + default: + return(E_BADPARM); + } + return(OK); +} diff --git a/src/spicelib/devices/txl/txlparam.c b/src/spicelib/devices/txl/txlparam.c new file mode 100644 index 000000000..83e9aaf93 --- /dev/null +++ b/src/spicelib/devices/txl/txlparam.c @@ -0,0 +1,40 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "const.h" +#include "ifsim.h" +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + + +/* ARGSUSED */ +int +TXLparam(param,value,inst,select) + int param; + IFvalue *value; + GENinstance *inst; + IFvalue *select; +{ + TXLinstance *here = (TXLinstance *)inst; + switch(param) { + case TXL_IN_NODE: + here->TXLposNode = value->iValue; + break; + case TXL_OUT_NODE: + here->TXLnegNode = value->iValue; + break; + case TXL_LENGTH: + here->TXLlength = value->rValue; + here->TXLlengthgiven = TRUE; + break; + default: + return(E_BADPARM); + } + return(OK); +} diff --git a/src/spicelib/devices/txl/txlsetup.c b/src/spicelib/devices/txl/txlsetup.c new file mode 100644 index 000000000..f9f728a42 --- /dev/null +++ b/src/spicelib/devices/txl/txlsetup.c @@ -0,0 +1,1140 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + + +#include "ngspice.h" +#include +#include "smpdefs.h" +#include "txldefs.h" +#include "sperror.h" +#include "suffix.h" + +#include "../cap/capdefs.h" + +static int ReadTxL(); +/*static int multC();*/ +static int main_pade(); +static int mac(); +/*static int divC();*/ +static int div_C(); +static int div3(); +/*static double approx1();*/ +/*static double approx2();*/ +static int find_roots(); +/*static double f3();*/ +/*static double f2();*/ +/*static int expC();*/ +/*static double exp_approx1();*/ +/*static double exp_approx2();*/ +static int exp_pade(); +/*static int exp_div3();*/ +static int exp_find_roots(); +static double eval2(); +static int get_c(); +static int get_h3(); +static int Gaussian_Elimination2(); +static int Gaussian_Elimination1(); +static int pade(); +static int update_h1C_c(); +static void y_pade(); +static double root3(); +static NDnamePt insert_ND(); +static NODE *insert_node(); +static NODE *NEW_node(); +/*static VI_list_txl *new_vi_txl();*/ + +NODE *node_tab = NULL; +NDnamePt ndn = NULL; +VI_list_txl *pool_vi_txl = NULL; + +/* pade.c */ +/** +static double xx1, xx2, xx3, xx4, xx5, xx6; +static double cc1, cc2, cc3, cc4, cc5, cc6; +**/ + +/* y.c */ +static double sqtCdL; +static double b1, b2, b3, b4, b5; +static double p1, p2, p3, q1, q2, q3; +static double c1, c2, c3, x1, x2, x3; +static double A[3][4]; + +/* exp.c */ +static double RdL, GdC, RG, tau, RC, GL; +static double a0, a1, a2, a3, a4, a5; +static double ep1, ep2, ep3, eq1, eq2, eq3; +static double ec1, ec2, ec3, ex1, ex2, ex3; +static int ifImg; +static double AA[3][4]; + +#define epsi 1.0e-16 +#define epsi2 1.0e-28 + +/* ARGSUSED */ +int +TXLsetup(matrix,inModel,ckt,state) + register SMPmatrix *matrix; + GENmodel *inModel; + CKTcircuit*ckt; + int *state; +{ + register TXLmodel *model = (TXLmodel *)inModel; + register TXLinstance *here; + CKTnode *tmp; + int error; + + /* loop through all the models */ + for( ; model != NULL; model = model->TXLnextModel ) { + + /* loop through all the instances of the model */ + for (here = model->TXLinstances; here != NULL ; + here=here->TXLnextInstance) { + +/* macro to make elements with built in test for out of memory */ +#define TSTALLOC(ptr,first,second) \ +if((here->ptr = SMPmakeElt(matrix,here->first,here->second))==(double *)NULL){\ + return(E_NOMEM);\ +} + + if (! here->TXLibr1Given) { + error = CKTmkCur(ckt, &tmp, here->TXLname, "branch1"); + if (error) return (error); + here->TXLibr1 = tmp->number; + } + if (! here->TXLibr2Given) { + error = CKTmkCur(ckt, &tmp, here->TXLname, "branch2"); + if (error) return (error); + here->TXLibr2 = tmp->number; + } + + TSTALLOC(TXLposPosptr, TXLposNode, TXLposNode); + TSTALLOC(TXLposNegptr, TXLposNode, TXLnegNode); + TSTALLOC(TXLnegPosptr, TXLnegNode, TXLposNode); + TSTALLOC(TXLnegNegptr, TXLnegNode, TXLnegNode); + TSTALLOC(TXLibr1Posptr, TXLibr1, TXLposNode); + TSTALLOC(TXLibr2Negptr, TXLibr2, TXLnegNode); + TSTALLOC(TXLnegIbr2ptr, TXLnegNode, TXLibr2); + TSTALLOC(TXLposIbr1ptr, TXLposNode, TXLibr1); + TSTALLOC(TXLibr1Ibr1ptr, TXLibr1, TXLibr1); + TSTALLOC(TXLibr2Ibr2ptr, TXLibr2, TXLibr2); + TSTALLOC(TXLibr1Negptr, TXLibr1, TXLnegNode); + TSTALLOC(TXLibr2Posptr, TXLibr2, TXLposNode); + TSTALLOC(TXLibr1Ibr2ptr, TXLibr1, TXLibr2); + TSTALLOC(TXLibr2Ibr1ptr, TXLibr2, TXLibr1); + + here->in_node_name = CKTnodName(ckt,here->TXLposNode); + here->out_node_name = CKTnodName(ckt,here->TXLnegNode); + ReadTxL(here, ckt); + + } + } + + return(OK); +} + +/*** +static VI_list_txl +*new_vi_txl() +{ + VI_list_txl *q; + + if (pool_vi_txl) { + q = pool_vi_txl; + pool_vi_txl = pool_vi_txl->pool; + return(q); + } else + return((VI_list_txl *) malloc (sizeof (VI_list_txl))); +} +***/ + +static int ReadTxL(tx, ckt) +TXLinstance *tx; +CKTcircuit *ckt; +{ + double R, L, G, C, l; + char *p, *n; + NODE *nd; + ETXLine *et; + TXLine *t, *t2; + RLINE *line; + ERLINE *er; + double LL = 1e-12; + + p = tx->in_node_name; + n = tx->out_node_name; + + line = (RLINE *) malloc(sizeof (RLINE)); + er = (ERLINE *) malloc(sizeof (ERLINE)); + et = (ETXLine *) malloc(sizeof (ETXLine)); + t = (TXLine *) malloc(sizeof (TXLine)); + t2 = (TXLine *) malloc(sizeof (TXLine)); + tx->txline = t; + tx->txline2 = t2; + t->newtp = 0; + t2->newtp = 0; + t->vi_head = t->vi_tail = NULL; + nd = insert_node(p); + et->link = nd->tptr; + nd->tptr = et; + et->line = t; + t->in_node = nd; + t2->in_node = nd; + er->link = nd->rlptr; + nd->rlptr = er; + er->rl = line; + line->in_node = nd; + et = (ETXLine *) malloc(sizeof (ETXLine)); + nd = insert_node(n); + et->link = nd->tptr; + nd->tptr = et; + et->line = t; + t->out_node = nd; + t2->out_node = nd; + er = (ERLINE *) malloc(sizeof (ERLINE)); + er->link = nd->rlptr; + nd->rlptr = er; + er->rl = line; + line->out_node = nd; + t->dc1 = t->dc2 = 0.0; + t2->dc1 = t2->dc2 = 0.0; + t->lsl = 0; + t2->lsl = 0; + l = 0.0; + + R = tx->TXLmodPtr->R; + L = tx->TXLmodPtr->L; + L = MAX(L, LL); + C = tx->TXLmodPtr->C; + G = tx->TXLmodPtr->G; + if (tx->TXLlengthgiven == TRUE) + l = tx->TXLlength; + else l = tx->TXLmodPtr->length; + + + if (l == 0.0) { + fprintf(stderr, "(Error) transmission line of zero length\n"); + exit(0); + } + else { + if (R / L < 5.0e+5) { + line->g = 1.0e+2; + if (G < 1.0e-2) { + t->lsl = 1; /* lossless line */ + t->taul = sqrt((double) C * L) * l * 1.0e12; + t->h3_aten = t->sqtCdL = sqrt((double) C / L); + t->h2_aten = 1.0; + t->h1C = 0.0; + } + } + else line->g = 1.0 / (R * l); + } + + if (! t->lsl) + main_pade(R, L, G, C, l, t); + + return(1); +} + + +/**************************************************************** + pade.c : Calculate the Pade Approxximation of Y(s) + ****************************************************************/ + + +static int main_pade(R, L, G, C, l, h) + double R, L, G, C, l; + TXLine *h; +{ + y_pade(R, L, G, C, h); + h->ifImg = exp_pade(R, L, G, C, l, h); + get_h3(h); + h->taul *= 1.0e12; + update_h1C_c(h); + + return(1); +} + +static int div_C(ar, ai, br, bi, cr, ci) + double ar, ai, br, bi; + double *cr, *ci; +{ + *cr = ar * br + ai * bi; + *ci = - ar * bi + ai * br; + *cr = *cr / (br * br + bi * bi); + *ci = *ci / (br * br + bi * bi); + return (1); +} + +/*** +static int expC(ar, ai, h, cr, ci) + double ar, ai, *cr, *ci; + float h; +{ + double e, cs, si; + + e = exp((double) ar * h); + cs = cos((double) ai * h); + si = sin((double) ai * h); + *cr = e * cs; + *ci = e * si; + + return(1); +} +***/ + +/*** +static int multC(ar, ai, br, bi, cr, ci) + double ar, ai, br, bi; + double *cr, *ci; +{ + *cr = ar*br - ai*bi; + *ci = ar*bi + ai*br; + + return(1); +} +***/ + +/*** +static int divC(ar, ai, br, bi, cr, ci) + double ar, ai, br, bi; + double *cr, *ci; +{ + double t; + t = br*br + bi*bi; + *cr = (ar*br + ai*bi) / t; + *ci = (ai*br - ar*bi) / t; + + return(1); +} +***/ + +static int get_h3(h) + TXLine *h; +{ + double cc1, cc2, cc3, cc4, cc5, cc6; + double xx1, xx2, xx3, xx4, xx5, xx6; + + h->h3_aten = h->h2_aten * h->sqtCdL; + h->h3_term[0].x = xx1 = h->h1_term[0].x; + h->h3_term[1].x = xx2 = h->h1_term[1].x; + h->h3_term[2].x = xx3 = h->h1_term[2].x; + h->h3_term[3].x = xx4 = h->h2_term[0].x; + h->h3_term[4].x = xx5 = h->h2_term[1].x; + h->h3_term[5].x = xx6 = h->h2_term[2].x; + cc1 = h->h1_term[0].c; + cc2 = h->h1_term[1].c; + cc3 = h->h1_term[2].c; + cc4 = h->h2_term[0].c; + cc5 = h->h2_term[1].c; + cc6 = h->h2_term[2].c; + + if (h->ifImg) { + double r, i; + + h->h3_term[0].c = cc1 + cc1 * (cc4/(xx1-xx4) + + 2.0*(cc5*xx1-xx6*cc6-xx5*cc5)/(xx1*xx1-2.0*xx5*xx1+xx5*xx5+xx6*xx6)); + h->h3_term[1].c = cc2 + cc2 * (cc4/(xx2-xx4) + + 2.0*(cc5*xx2-xx6*cc6-xx5*cc5)/(xx2*xx2-2.0*xx5*xx2+xx5*xx5+xx6*xx6)); + h->h3_term[2].c = cc3 + cc3 * (cc4/(xx3-xx4) + + 2.0*(cc5*xx3-xx6*cc6-xx5*cc5)/(xx3*xx3-2.0*xx5*xx3+xx5*xx5+xx6*xx6)); + + h->h3_term[3].c = cc4 + cc4 * (cc1/(xx4-xx1) + cc2/(xx4-xx2) + cc3/(xx4-xx3)); + + h->h3_term[4].c = cc5; + h->h3_term[5].c = cc6; + div_C(cc5, cc6, xx5-xx1, xx6, &r, &i); + h->h3_term[4].c += r * cc1; + h->h3_term[5].c += i * cc1; + div_C(cc5, cc6, xx5-xx2, xx6, &r, &i); + h->h3_term[4].c += r * cc2; + h->h3_term[5].c += i * cc2; + div_C(cc5, cc6, xx5-xx3, xx6, &r, &i); + h->h3_term[4].c += r * cc3; + h->h3_term[5].c += i * cc3; + } else { + h->h3_term[0].c = cc1 + cc1 * (cc4/(xx1-xx4) + cc5/(xx1-xx5) + cc6/(xx1-xx6)); + h->h3_term[1].c = cc2 + cc2 * (cc4/(xx2-xx4) + cc5/(xx2-xx5) + cc6/(xx2-xx6)); + h->h3_term[2].c = cc3 + cc3 * (cc4/(xx3-xx4) + cc5/(xx3-xx5) + cc6/(xx3-xx6)); + + h->h3_term[3].c = cc4 + cc4 * (cc1/(xx4-xx1) + cc2/(xx4-xx2) + cc3/(xx4-xx3)); + h->h3_term[4].c = cc5 + cc5 * (cc1/(xx5-xx1) + cc2/(xx5-xx2) + cc3/(xx5-xx3)); + h->h3_term[5].c = cc6 + cc6 * (cc1/(xx6-xx1) + cc2/(xx6-xx2) + cc3/(xx6-xx3)); + } + + return(1); +} + +static int update_h1C_c(h) + TXLine *h; +{ + int i; + double d = 0; + + for (i = 0; i < 3; i++) { + h->h1_term[i].c *= h->sqtCdL; + d += h->h1_term[i].c; + } + h->h1C = d; + + for (i = 0; i < 3; i++) + h->h2_term[i].c *= h->h2_aten; + + for (i = 0; i < 6; i++) + h->h3_term[i].c *= h->h3_aten; + + return(1); +} +/**************************************************************** + y.c : Calculate the Pade Approximation of Y(s) + ****************************************************************/ + + +static double eval2(a, b, c, x) + double a, b, c, x; +{ + return(a*x*x + b*x + c); +} + +/*** +static double approx1(st) + double st; +{ + double s3, s2, s1; + + s1 = st; + s2 = s1 * s1; + s3 = s2 * s1; + + return((s3 + q1*s2 + q2*s1 + q3) / (s3 + p1*s2 + p2*s1 + p3)); +} +***/ +/*** +static double approx2(st) + double st; +{ + return(1.0 + c1/(st - x1) + c2/(st - x2) + c3/(st - x3)); +} +***/ + +static void y_pade(R, L, G, C, h) + double R, L, G, C; + TXLine *h; +{ + + /* float RdL, GdC; */ + double RdL, GdC; + + sqtCdL = sqrt((double) C / L); + RdL = R / L; + GdC = G / C; + + mac(GdC, RdL, &b1, &b2, &b3, &b4, &b5); + + A[0][0] = 1.0 - sqrt((double) (GdC / RdL)); + A[0][1] = b1; + A[0][2] = b2; + A[0][3] = -b3; + + A[1][0] = b1; + A[1][1] = b2; + A[1][2] = b3; + A[1][3] = -b4; + + A[2][0] = b2; + A[2][1] = b3; + A[2][2] = b4; + A[2][3] = -b5; + + Gaussian_Elimination1(3); + + p3 = A[0][3]; + p2 = A[1][3]; + p1 = A[2][3]; + + q1 = p1 + b1; + q2 = b1 * p1 + p2 + b2; + q3 = p3 * sqrt((double) (GdC / RdL)); + + find_roots(p1, p2, p3, &x1, &x2, &x3); + c1 = eval2(q1 - p1, q2 - p2, q3 - p3, x1) / + eval2((double) 3.0, (double) 2.0 * p1, p2, x1); + c2 = eval2(q1 - p1, q2 - p2, q3 - p3, x2) / + eval2((double) 3.0, (double) 2.0 * p1, p2, x2); + c3 = eval2(q1 - p1, q2 - p2, q3 - p3, x3) / + eval2((double) 3.0, (double) 2.0 * p1, p2, x3); + + h->sqtCdL = sqtCdL; + h->h1_term[0].c = c1; + h->h1_term[1].c = c2; + h->h1_term[2].c = c3; + h->h1_term[0].x = x1; + h->h1_term[1].x = x2; + h->h1_term[2].x = x3; + +} + +static int Gaussian_Elimination1(dims) + int dims; +{ + register int i, j, k, dim; + register double f; + int imax; + double max; + + dim = dims; + + for (i = 0; i < dim; i++) { + imax = i; + max = ABS(A[i][i]); + for (j = i+1; j < dim; j++) + if (ABS(A[j][i]) > max) { + imax = j; + max = ABS(A[j][i]); + } + if (max < epsi) { + fprintf(stderr, " can not choose a pivot \n"); + exit(0); + } + if (imax != i) + for (k = i; k <= dim; k++) { + f = A[i][k]; + A[i][k] = A[imax][k]; + A[imax][k] = f; + } + + f = 1.0 / A[i][i]; + A[i][i] = 1.0; + + for (j = i+1; j <= dim; j++) + A[i][j] *= f; + + for (j = 0; j < dim ; j++) { + if (i == j) + continue; + f = A[j][i]; + A[j][i] = 0.0; + for (k = i+1; k <= dim; k++) + A[j][k] -= f * A[i][k]; + } + } + return(1); +} + +static double root3(a1, a2, a3, x) + double x; + double a1, a2, a3; +{ + double t1, t2; + + t1 = x*x*x + a1*x*x + a2*x + a3; + t2 = 3.0*x*x + 2.0*a1*x + a2; + + return(x - t1 / t2); +} + +static int div3(a1, a2, a3, x, p1, p2) + double x; + double a1, a2, a3; + double *p1, *p2; +{ + *p1 = a1 + x; + *p2 = - a3 / x; + + return(1); +} + + +/**************************************************************** + Calculate the Maclaurin series of F(z) + + F(z) = sqrt((1+az) / (1+bz)) + = 1 + b1 z + b2 z^2 + b3 z^3 + b4 z^4 + b5 z^5 + ****************************************************************/ + +/*** +static double f3(a, b, z) + double a, b, z; +{ + double t4, t3, t2, t1; + double t14, t13, t12, t11; + double sqt11; + + t1 = 1 / (1.0 + b * z); + t2 = t1 * t1; + t3 = t2 * t1; + t4 = t3 * t1; + + t11 = (1.0 + a * z) * t1; + t12 = (1.0 + a * z) * t2; + t13 = (1.0 + a * z) * t3; + t14 = (1.0 + a * z) * t4; + + sqt11 = sqrt(t11); + + + return( + -0.5 * (-2.0*a*b*t2 + 2.0*b*b*t13) * (a*t1 - b*t12) / (t11*sqt11) + +3.0/8.0 * (a*t1-b*t12)*(a*t1-b*t12)*(a*t1-b*t12) / (t11*t11*sqt11) + +0.5 * (4.0*a*b*b*t3 + 2.0*a*b*b*t3 - 6.0*b*b*b*t14) / sqt11 + -0.25 * (-2.0*a*b*t2 + 2.0*b*b*t13) * (a*t1-b*(1.0+a*z)) / + (t11*sqt11) + ); +} +***/ + +/*** +static double f2(a, b, z) + double a, b, z; +{ + double t3, t2, t1; + double t13, t12, t11; + double sqt11; + + t1 = 1 / (1.0 + b * z); + t2 = t1 * t1; + t3 = t2 * t1; + + t11 = (1.0 + a * z) * t1; + t12 = (1.0 + a * z) * t2; + t13 = (1.0 + a * z) * t3; + + sqt11 = sqrt(t11); + + return( + -0.25 * (a*t1-b*t12) * (a*t1-b*t12) / (t11*sqt11) + +0.5 * (-2.0*a*b*t2 + 2.0*b*b*t13) / sqt11 + ); +} +***/ + +static int mac(at, bt, b1, b2, b3, b4, b5) + /* float at, bt; */ + double at, bt; + double *b1, *b2, *b3, *b4, *b5; +{ + double a, b; + double y1, y2, y3, y4, y5; + + a = at; + b = bt; + + y1 = *b1 = 0.5 * (a - b); + y2 = 0.5 * (3.0 * b * b - 2.0 * a * b - a * a) * y1 / (a - b); + y3 = ((3.0 * b * b + a * a) * y1 * y1 + 0.5 * (3.0 * b * b + - 2.0 * a * b - a * a) * y2) / (a - b); + y4 = ((3.0 * b * b - 3.0 * a * a) * y1 * y1 * y1 + (9.0 * b * b + + 3.0 * a * a) * y1 * y2 + 0.5 * (3.0 * b * b - 2.0 * a * b + - a * a) * y3) / (a - b); + y5 = (12.0 * a * a * y1 * y1 * y1 * y1 + y1 * y1 * y2 * ( + 18.0 * b * b - 18.0 * a * a) + (9.0 * b * b + 3.0 * a * a) * + (y2 * y2 + y1 * y3) + (3.0 * b * b + a * a) * y1 * y3 + + 0.5 * (3.0 * b * b - 2.0 * a * b - a * a) * y4) / (a - b); + + *b2 = y2 / 2.0; + *b3 = y3 / 6.0; + *b4 = y4 / 24.0; + *b5 = y5 / 120.0; + + return(1); +} + + +/**************************************************** + exp.c + ****************************************************/ + +/*** +static double exp_approx1(st) + double st; +{ + double s3, s2, s1; + + s1 = st; + s2 = s1 * s1; + s3 = s2 * s1; + + return(exp((double) - st * tau - a0) * + (s3 + eq1*s2 + eq2*s1 + eq3) / (s3 + ep1*s2 + ep2*s1 + ep3)); +} +***/ + +static int get_c(eq1, eq2, eq3, ep1, ep2, a, b, cr, ci) + double eq1, eq2, eq3, ep1, ep2, a, b; + double *cr, *ci; +{ + double d, n; + + d = (3.0*(a*a-b*b)+2.0*ep1*a+ep2)*(3.0*(a*a-b*b)+2.0*ep1*a+ep2); + d += (6.0*a*b+2.0*ep1*b)*(6.0*a*b+2.0*ep1*b); + n = -(eq1*(a*a-b*b)+eq2*a+eq3)*(6.0*a*b+2.0*ep1*b); + n += (2.0*eq1*a*b+eq2*b)*(3.0*(a*a-b*b)+2.0*ep1*a+ep2); + *ci = n/d; + n = (3.0*(a*a-b*b)+2.0*ep1*a+ep2)*(eq1*(a*a-b*b)+eq2*a+eq3); + n += (6.0*a*b+2.0*ep1*b)*(2.0*eq1*a*b+eq2*b); + *cr = n/d; + + return(1); +} + +/*** +static double exp_approx2(st) + double st; +{ + if (ifImg) + return(1.0 + ec1/(st - ex1) + 2.0*(ec2*(st-ex2)-ec3*ex3) / + ((st-ex2)*(st-ex2) + ex3*ex3)); + else + return(1.0 + ec1/(st - ex1) + ec2/(st - ex2) + ec3/(st - ex3)); +} +***/ + +static int exp_pade(R, L, G, C, l, h) + float R, L, G, C, l; + TXLine *h; +{ + + tau = sqrt((double) L*C); + RdL = R / L; + GdC = G / C; + RG = R * G; + RC = R * C; + GL = G * L; + + { + double a, b, t; + double y1, y2, y3, y4, y5, y6; + + a = RdL; + b = GdC; + t = tau; + + /* + y1 = 0.5 * (a + b); + y2 = a * b - y1 * y1; + y3 = - a * b * y1 - 2.0 * y1 * y2 + y1 * y1 * y1; + y4 = 2.0 * a * b * y1 * y1 - a * b * y2 - 2.0 * y2 * y2 + - 2.0 * y1 * y3 + 5.0 * y1 * y1 * y2 + - 2.0 * y1 * y1 * y1 * y1; + y5 = 6.0 * a * b * (y1 * y2 - y1 * y1 * y1) - a * b * y3 + - 2.0 * y1 * y4 + - 6.0 * y2 * y3 + 12.0 * y2 * y2 * y1 + 7.0 * y1 * y1 * y3 + -10.0 * y1 * y1 * y1 * y2 - 8.0 * y1 * y1 * y1 * y2 + + 6.0 * y1 * y1 * y1 * y1 * y1; + y6 = 24.0 * a * b * y1 * y1 * y1 * y1 - 36.0 * a * b * y1 * y1 * y2 + + 6.0 * a * b * y2 * y2 + 8.0 * a * b * y1 * y3 - 2.0 * y2 * y4 + - 2.0 * y1 * y5 + 2.0 * y1 * y1 * y4 - a * b * y4 -6.0 * y3 * y3 + + 44.0 * y1 * y2 * y3 + 60.0 * y1 * y1 * y1 * y1 * y2 + -24.0 * y1 * y1 * y1 * y1 * y1 * y1 + 12.0 * y2 * y2 * y2 + -54.0 * y1 * y1 * y2 * y2 + 7.0 * y1 * y1 * y4 + -24.0 * y1 * y1 * y1 * y3 - 24.0 * y1 * y1 * y2 * y2 + -8.0 * y1 * y1 * y1 * y3 + 24.0 * y1 * y1 * y1 * y1 * y2 + - 6.0 * y2 * y4; + */ + + y1 = 0.5 * (a + b); + y2 = a * b - y1 * y1; + y3 = -3.0 * y1 * y2; + y4 = -3.0 * y2 * y2 - 4.0 * y1 * y3; + y5 = - 5.0 * y1 * y4 -10.0 * y2 * y3; + y6 = -10.0 * y3 * y3 - 15.0 * y2 * y4 - 6.0 * y1 * y5; + + a0 = y1 * t; + a1 = y2 * t * t / 2.0; + a2 = y3 * t * t * t / 6.0; + a3 = y4 * t * t * t * t / 24.0; + a4 = y5 * t * t * t * t * t / 120.0; + a5 = y6 * t * t * t * t * t * t / 720.0; + + } + + a0 *= l; + a1 *= l; + a2 *= l; + a3 *= l; + a4 *= l; + a5 *= l; + + pade(l); + + h->taul = tau * l; + h->h2_aten = exp(- a0); + h->h2_term[0].c = ec1; + h->h2_term[1].c = ec2; + h->h2_term[2].c = ec3; + h->h2_term[0].x = ex1; + h->h2_term[1].x = ex2; + h->h2_term[2].x = ex3; + + return(ifImg); +} + +static int pade(l) + float l; +{ + int i, j; + double a[6]; + double b[6]; + + a[1] = -a1; + a[2] = -a2; + a[3] = -a3; + a[4] = -a4; + a[5] = -a5; + + b[0] = 1.0; + b[1] = a[1]; + for (i = 2; i <= 5; i++) { + b[i] = 0.0; + for (j = 1; j <= i; j++) + b[i] += j * a[j] * b[i-j]; + b[i] = b[i] / (double) i; + } + + AA[0][0] = 1.0 - exp((double) a0 - l * sqrt(RG)); + AA[0][1] = b[1]; + AA[0][2] = b[2]; + AA[0][3] = -b[3]; + + AA[1][0] = b[1]; + AA[1][1] = b[2]; + AA[1][2] = b[3]; + AA[1][3] = -b[4]; + + AA[2][0] = b[2]; + AA[2][1] = b[3]; + AA[2][2] = b[4]; + AA[2][3] = -b[5]; + + Gaussian_Elimination2(3); + + ep3 = AA[0][3]; + ep2 = AA[1][3]; + ep1 = AA[2][3]; + + eq1 = ep1 + b[1]; + eq2 = b[1] * ep1 + ep2 + b[2]; + eq3 = ep3 * exp((double) a0 - l * sqrt(RG)); + + ep3 = ep3 / (tau*tau*tau); + ep2 = ep2 / (tau*tau); + ep1 = ep1 / tau; + eq3 = eq3 / (tau*tau*tau); + eq2 = eq2 / (tau*tau); + eq1 = eq1 / tau; + /* + printf("factor = %e\n", exp(-a0)); + printf("ep1 = %e ep2 = %e ep3 = %e\n", ep1, ep2, ep3); + */ + exp_find_roots(ep1, ep2, ep3, &ex1, &ex2, &ex3); + /* + printf("roots are %e %e %e \n", ex1, ex2, ex3); + */ + ec1 = eval2(eq1 - ep1, eq2 - ep2, eq3 - ep3, ex1) / + eval2((double) 3.0, (double) 2.0 * ep1, ep2, ex1); + if (ifImg) + get_c(eq1 - ep1, eq2 - ep2, eq3 - ep3, ep1, ep2, ex2, ex3, &ec2, &ec3); + else { + ec2 = eval2(eq1 - ep1, eq2 - ep2, eq3 - ep3, ex2) / + eval2((double) 3.0, (double) 2.0 * ep1, ep2, ex2); + ec3 = eval2(eq1 - ep1, eq2 - ep2, eq3 - ep3, ex3) / + eval2((double) 3.0, (double) 2.0 * ep1, ep2, ex3); + } + return (1); +} + +static int Gaussian_Elimination2(dims) + int dims; +{ + register int i, j, k, dim; + register double f; + double max; + int imax; + + dim = dims; + + for (i = 0; i < dim; i++) { + imax = i; + max = ABS(AA[i][i]); + for (j = i+1; j < dim; j++) + if (ABS(AA[j][i]) > max) { + imax = j; + max = ABS(AA[j][i]); + } + if (max < epsi2) { + fprintf(stderr, " can not choose a pivot \n"); + exit(0); + } + if (imax != i) + for (k = i; k <= dim; k++) { + f = AA[i][k]; + AA[i][k] = AA[imax][k]; + AA[imax][k] = f; + } + + f = 1.0 / AA[i][i]; + AA[i][i] = 1.0; + + for (j = i+1; j <= dim; j++) + AA[i][j] *= f; + + for (j = 0; j < dim ; j++) { + if (i == j) + continue; + f = AA[j][i]; + AA[j][i] = 0.0; + for (k = i+1; k <= dim; k++) + AA[j][k] -= f * AA[i][k]; + } + } + return(1); +} + +/*** +static int exp_div3(a1, a2, a3, x, p1, p2) + double x; + double a1, a2, a3; + double *p1, *p2; + { + *p1 = a1 + x; + *p2 = - a3 / x; + + return(1); +} +***/ + +/*** + ***/ + +static int exp_find_roots(a1, a2, a3, ex1, ex2, ex3) + double a1, a2, a3; + double *ex1, *ex2, *ex3; +{ + double x, t; + double p, q; + + q = (a1*a1-3.0*a2) / 9.0; + p = (2.0*a1*a1*a1-9.0*a1*a2+27.0*a3) / 54.0; + t = q*q*q - p*p; + if (t >= 0.0) { + t = acos((double) p /(q * sqrt(q))); + x = -2.0*sqrt(q)*cos(t / 3.0) - a1/3.0; + } else { + if (p > 0.0) { + t = pow(sqrt(-t)+p, (double) 1.0 / 3.0); + x = -(t + q / t) - a1/3.0; + } else if (p == 0.0) { + x = -a1/3.0; + } else { + t = pow(sqrt(-t)-p, (double) 1.0 / 3.0); + x = (t + q / t) - a1/3.0; + } + } + { + double ex1; + int i = 0; + ex1 = x; + for (t = root3(a1, a2, a3, x); ABS(t-x) > 5.0e-4; + t = root3(a1, a2, a3, x)) + if (++i == 32) { + x = ex1; + break; + } else + x = t; + } + /*** + x = a1; + for (t = root3(a1, a2, a3, x); ABS(t-x) > epsi2; + t = root3(a1, a2, a3, x)) { + x = t; + i++; + if (i > 1000) { + x = 0.5 * (x + root3(a1, a2, a3, x)); + j++; + if (j == 3) + break; + i = 0; + } + } + ***/ + *ex1 = x; + div3(a1, a2, a3, x, &a1, &a2); + + t = a1 * a1 - 4.0 * a2; + if (t < 0) { + ifImg = 1; + printf("***** Two Imaginary Roots.\n"); + *ex3 = 0.5 * sqrt(-t); + *ex2 = -0.5 * a1; + } else { + ifImg = 0; + t *= 1.0e-16; + t = sqrt(t)*1.0e8; + if (a1 >= 0.0) + *ex2 = t = -0.5 * (a1 + t); + else + *ex2 = t = -0.5 * (a1 - t); + *ex3 = a2 / t; + /* + *ex2 = 0.5 * (-a1 + t); + *ex3 = 0.5 * (-a1 - t); + */ + } + + return(1); +} +static NDnamePt +insert_ND(name, ndn) + char *name; + NDnamePt *ndn; +{ + int cmp; + NDnamePt p; + + if (*ndn == NULL) { + p = *ndn = (NDnamePt) malloc(sizeof (NDname)); + p->nd = NULL; + p->right = p->left = NULL; + strcpy(p->id, name); + return(p); + } + cmp = strcmp((*ndn)->id, name); + if (cmp == 0) + return(*ndn); + else { + if (cmp < 0) + return(insert_ND(name, &((*ndn)->left))); + else + return(insert_ND(name, &((*ndn)->right))); + } +} + + +static NODE +*insert_node(name) + char *name; +{ + NDnamePt n; + NODE *p; + + n = insert_ND(name, &ndn); + if (n->nd == NULL) { + p = NEW_node(); + p->name = n; + n->nd = p; + p->next = node_tab; + node_tab = p; + return(p); + } else + return(n->nd); +} + +static NODE +*NEW_node() +{ + NODE *n; + + n = (NODE *) malloc (sizeof (NODE)); + n->mptr = NULL; + n->gptr = NULL; + n->cptr = NULL; + n->rptr = NULL; + n->tptr = NULL; + n->cplptr = NULL; + n->rlptr = NULL; + n->ddptr = NULL; + n->cvccsptr = NULL; + n->vccsptr = NULL; + n->CL = 0.0; + n->V = n->dv = 0.0; + n->gsum = n->cgsum = 0; + n->is = 0; + n->tag = 0; + n->flag = 0; + n->region = NULL; + n->ofile = NULL; + n->dvtag = 0; + + return(n); +} + +static int find_roots(a1, a2, a3, x1, x2, x3) + double a1, a2, a3; + double *x1, *x2, *x3; +{ + double x, t; + double p, q; + + q = (a1*a1-3.0*a2) / 9.0; + p = (2.0*a1*a1*a1-9.0*a1*a2+27.0*a3) / 54.0; + t = q*q*q - p*p; + if (t >= 0.0) { + t = acos((double) p /(q * sqrt(q))); + x = -2.0*sqrt(q)*cos(t / 3.0) - a1/3.0; + } else { + if (p > 0.0) { + t = pow(sqrt(-t)+p, (double) 1.0 / 3.0); + x = -(t + q / t) - a1/3.0; + } else if (p == 0.0) { + x = -a1/3.0; + } else { + t = pow(sqrt(-t)-p, (double) 1.0 / 3.0); + x = (t + q / t) - a1/3.0; + } + } + { + double x1; + int i = 0; + x1 = x; + for (t = root3(a1, a2, a3, x); ABS(t-x) > 5.0e-4; + t = root3(a1, a2, a3, x)) + if (++i == 32) { + x = x1; + break; + } else + x = t; + } + /* + x = a1; + i = 0; + j = 0; + for (t = root3(a1, a2, a3, x); ABS(t-x) > epsi; + t = root3(a1, a2, a3, x)) { + x = t; + i++; + if (i > 1000) { + x = 0.5 * (x + root3(a1, a2, a3, x)); + j++; + if (j == 3) + break; + i = 0; + } + } + */ + + *x1 = x; + div3(a1, a2, a3, x, &a1, &a2); + + t = a1 * a1 - 4.0 * a2; + if (t < 0) { + printf("***** Two Imaginary Roots in Characteristic Admittance.\n"); + exit(0); + } + + t *= 1.0e-18; + t = sqrt(t) * 1.0e9; + if (a1 >= 0.0) + *x2 = t = -0.5 * (a1 + t); + else + *x2 = t = -0.5 * (a1 - t); + *x3 = a2 / t; + /* + *x2 = 0.5 * (-a1 + t); + *x3 = 0.5 * (-a1 - t); + */ + return(1); +} + diff --git a/src/spicelib/parser/inp2p.c b/src/spicelib/parser/inp2p.c new file mode 100644 index 000000000..e6977c5bd --- /dev/null +++ b/src/spicelib/parser/inp2p.c @@ -0,0 +1,131 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "ifsim.h" +#include "inpdefs.h" +#include "inpmacs.h" +#include "fteext.h" +#include "inp.h" + +void +INP2P(ckt,tab,current) + void *ckt; + INPtables *tab; + card *current; + +{ + +int mytype; /* the type we determine cpls are */ +int type; /* the type the model says it is */ +char *line; /* the part of the current line left to parse */ +char *name, *tempname; /* the cpl's name */ +char *model; /* the name of the cpl's model */ +char **nname1; /* the first node's name */ +char **nname2; /* the second node's name */ +char *ground; +void **node1; /* the first node's node pointer */ +void **node2; /* the second node's node pointer */ +void *groundnode; +int error; /* error code temporary */ +int error1; /* secondary error code temporary */ +INPmodel *thismodel; /* pointer to model structure describing our model */ +void *mdfast; /* pointer to the actual model */ +void *fast; /* pointer to the actual instance */ +IFvalue ptemp; /* a value structure to package cpl into */ +IFuid uid; /* uid for default model */ +double lenval = 0; +int lenvalgiven = 0; +int num, i; + + mytype = INPtypelook("CplLines"); + if(mytype < 0 ) { + LITERR("Device type CplLines not supported by this binary\n") + return; + } + line = current->line; + INPgetTok(&line,&name,1); + INPinsert(&name,tab); + /* num = (int) INPevaluate(&line,&error1,1); */ + num = 0; + + /* first pass to determine the dimension */ + while (*line != '\0') { + INPgetTok(&line, &tempname,1); + if (strcmp(tempname, "len") == 0) break; + num ++; + } + num = (num - 2) / 2; + line = current->line; + INPgetTok(&line,&name,1); + + nname1 = (char **) malloc(num * sizeof(char *)); + nname2 = (char **) malloc(num * sizeof(char *)); + node1 = (void **) malloc(num * sizeof(void *)); + node2 = (void **) malloc(num * sizeof(void *)); + + + for (i = 0; i < num; i++) { + INPgetTok(&line,&(nname1[i]),1); + INPtermInsert(ckt,&(nname1[i]),tab,&(node1[i])); + } + INPgetTok(&line,&ground,1); + INPtermInsert(ckt,&ground,tab,&groundnode); + for (i = 0; i < num; i++) { + INPgetTok(&line,&(nname2[i]),1); + INPtermInsert(ckt,&(nname2[i]),tab,&(node2[i])); + } + INPgetTok(&line,&ground,1); + INPtermInsert(ckt,&ground,tab,&groundnode); + + INPgetTok(&line,&model,1); + if (strcmp(model, "len") == 0) { + lenval = INPevaluate(&line,&error1,1); + lenvalgiven = 1; + INPgetTok(&line,&model,1); + } + if(*model) { /* token isn't null */ + INPinsert(&model,tab); + thismodel = (INPmodel *)NULL; + current->error = INPgetMod(ckt,model,&thismodel,tab); + if(thismodel != NULL) { + if(mytype != thismodel->INPmodType) { + LITERR("incorrect model type") + return; + } + mdfast = thismodel->INPmodfast; + type = thismodel->INPmodType; + } else { + type = mytype; + if(!tab->defPmod) { + /* create default P model */ + IFnewUid(ckt,&uid,(IFuid)NULL,"P",UID_MODEL,(void**)NULL); + IFC(newModel, (ckt,type,&(tab->defPmod),uid)) + } + mdfast = tab->defPmod; + } + IFC(newInstance,(ckt,mdfast,&fast,name)) + } else { + LITERR("model name is not found") + return; + } + + /* IFC(bindNode,(ckt,fast,1,fakename)) */ + + ptemp.iValue = num; + GCA(INPpName,("dimension", &ptemp,ckt,type,fast)) + ptemp.v.vec.sVec = nname1; + GCA(INPpName,("pos_nodes", &ptemp,ckt,type,fast)) + ptemp.v.vec.sVec = nname2; + GCA(INPpName,("neg_nodes", &ptemp,ckt,type,fast)) + if (error1 == 0 && lenvalgiven) { + ptemp.rValue = lenval; + GCA(INPpName,("length",&ptemp,ckt,type,fast)) + } + + return; +} diff --git a/src/spicelib/parser/inp2y.c b/src/spicelib/parser/inp2y.c new file mode 100644 index 000000000..84a547405 --- /dev/null +++ b/src/spicelib/parser/inp2y.c @@ -0,0 +1,236 @@ +/********** +Copyright 1992 Regents of the University of California. All rights +reserved. +Author: 1992 Charles Hough +**********/ + +#include "ngspice.h" +#include +#include "ifsim.h" +#include "inpdefs.h" +#include "inpmacs.h" +#include "fteext.h" +#include "inp.h" + +void +INP2Y(ckt,tab,current) + void *ckt; + INPtables *tab; + card *current; + +{ +/* parse a txl card */ +/* Yxxxx node1 node2 name */ + +int mytype; /* the type to determine txl */ +int mytype2; /* the type to determine cpl */ +int type; /* the type the model says it is */ +char *line; /* the part of the current line left to parse */ +char *name; /* the resistor's name */ +char *buf; /* temporary buffer for parsing */ +char *model; /* the name of the resistor's model */ +char *nname1; /* the first node's name */ +char *nname2; /* the second node's name */ +char rname1[10], rname2[10], rname3[10]; +char cname1[10], cname2[10], cname3[10], cname4[10]; +char *internal1, *internal2; +char *ground1, *ground2; +void *node1; /* the first node's node pointer */ +void *node2; /* the second node's node pointer */ +void *gnode1, *gnode2, *inode1, *inode2; +int error; /* error code temporary */ +int error1; /* secondary error code temporary */ +INPmodel *thismodel; /* pointer to model structure describing our model */ +void *mdfast; /* pointer to the actual model */ +void *fast; /* pointer to the actual instance */ +void *mdfast2, *mdfast3, *mdfast4, *mdfast5, *mdfast6; +void *fast2, *fast3, *fast4, *fast5, *fast6; +IFuid uid; /* uid for default model */ +GENinstance *txl; +IFvalue ptemp; /* a value structure to package into */ +double lval=0, rval=0, cval=0, lenval=0; +int lenvalgiven = 0; + + mytype = INPtypelook("TransLine"); + mytype2 = INPtypelook("CplLines"); + + if(mytype < 0 ) { + LITERR("Device type TransLine not supported by this binary\n") + return; + } + line = current->line; + INPgetTok(&line,&name,1); + INPinsert(&name,tab); + INPgetTok(&line,&nname1,1); + INPtermInsert(ckt,&nname1,tab,&node1); + INPgetTok(&line,&ground1,1); + INPtermInsert(ckt,&ground1,tab,&gnode1); + INPgetTok(&line,&nname2,1); + INPtermInsert(ckt,&nname2,tab,&node2); + INPgetTok(&line,&ground2,1); + INPtermInsert(ckt,&ground2,tab,&gnode2); + + INPgetTok(&line,&model,1); + if (strcmp(model, "len") == 0) { + lenval = INPevaluate(&line,&error1,1); + lenvalgiven = 1; + INPgetTok(&line,&model,1); + } + if(*model) { /* token isn't null */ + INPinsert(&model,tab); + thismodel = (INPmodel *)NULL; + current->error = INPgetMod(ckt,model,&thismodel,tab); + if(thismodel != NULL) { + if (thismodel->INPmodType == mytype2) { + INP2P(ckt,tab,current); + return; + } + else if (mytype != thismodel->INPmodType) { + LITERR("incorrect model type") + return; + } + line = thismodel->INPmodLine->line; + INPgetTok(&line,&buf,1); /* throw out .model */ + INPgetTok(&line,&buf,1); /* throw out model name */ + INPgetTok(&line,&buf,1); /* throw out txl */ + INPgetTok(&line,&buf,1); + while (*line != '\0') { + if (*buf == 'R' || *buf == 'r') { + INPgetTok(&line,&buf,1); + rval = atof(buf); + } + if ((strcmp(buf,"L") == 0) || (strcmp(buf,"l") == 0)) { + INPgetTok(&line,&buf,1); + lval = atof(buf); + } + if ((strcmp(buf,"C") == 0) || (strcmp(buf,"c") == 0)) { + INPgetTok(&line,&buf,1); + cval = atof(buf); + } + if (lenvalgiven == 0) { + if (strcmp(buf,"length")== 0) { + INPgetTok(&line,&buf,1); + lenval = atof(buf); + } + } + INPgetTok(&line,&buf,1); + } + if (lenval && rval && lval && rval/lval > 1.6e10) { + /* use 3-pi model */ + rval = 3.0 / (rval * lenval); + cval = cval * lenval / 6.0; + + type = INPtypelook("Resistor"); + + /* resistor between node1 and internal1 */ + internal1 = (char *) malloc (10); + strcpy(internal1, "txlnd1"); + INPtermInsert(ckt, &internal1, tab, &inode1); + if(!tab->defRmod) { + /* create default R model */ + IFnewUid(ckt,&uid,(IFuid)NULL,"R",UID_MODEL,(void**)NULL); + IFC(newModel, (ckt,type,&(tab->defRmod),uid)) + } + mdfast = tab->defRmod; + strcpy(rname1, "txlres1"); + IFC(newInstance,(ckt,mdfast,&fast,rname1)) + IFC(bindNode,(ckt,fast,1,node1)) + IFC(bindNode,(ckt,fast,2,inode1)) + ptemp.rValue = rval; + GCA(INPpName,("resistance",&ptemp,ckt,type,fast)) + + /* resistor between internal1 and internal2 */ + internal2 = (char *) malloc (10); + strcpy(internal2, "txlnd2"); + INPtermInsert(ckt, &internal2, tab, &inode2); + strcpy(rname2, "txlres2"); + mdfast2 = tab->defRmod; + IFC(newInstance,(ckt,mdfast2,&fast2,rname2)) + IFC(bindNode,(ckt,fast2,1,inode1)) + IFC(bindNode,(ckt,fast2,2,inode2)) + ptemp.rValue = rval; + GCA(INPpName,("resistance",&ptemp,ckt,type,fast2)) + + /* resistor between internal2 and node2 */ + strcpy(rname3, "txlres3"); + mdfast3 = tab->defRmod; + IFC(newInstance,(ckt,mdfast3,&fast3,rname3)) + IFC(bindNode,(ckt,fast3,1,inode2)) + IFC(bindNode,(ckt,fast3,2,node2)) + ptemp.rValue = rval; + GCA(INPpName,("resistance",&ptemp,ckt,type,fast3)) + + /* capacitor on node1 */ + type = INPtypelook("Capacitor"); + if(!tab->defCmod) { + IFnewUid(ckt,&uid,(IFuid)NULL,"C",UID_MODEL,(void**)NULL); + IFC(newModel,(ckt,type,&(tab->defCmod),uid)) + } + mdfast4 = tab->defCmod; + strcpy(cname1, "txlcap1"); + IFC(newInstance,(ckt,mdfast4,&fast4,cname1)) + IFC(bindNode,(ckt,fast4,1,node1)) + IFC(bindNode,(ckt,fast4,2,gnode1)) + ptemp.rValue = cval; + GCA(INPpName,("capacitance",&ptemp,ckt,type,fast4)) + + /* capacitor on internal1 */ + strcpy(cname2, "txlcap2"); + mdfast4 = tab->defCmod; + IFC(newInstance,(ckt,mdfast4,&fast4,cname2)) + IFC(bindNode,(ckt,fast4,1,inode1)) + IFC(bindNode,(ckt,fast4,2,gnode1)) + ptemp.rValue = cval * 2; + GCA(INPpName,("capacitance",&ptemp,ckt,type,fast4)) + + /* capacitor on internal2 */ + strcpy(cname3, "txlcap3"); + mdfast5 = tab->defCmod; + IFC(newInstance,(ckt,mdfast5,&fast5,cname3)) + IFC(bindNode,(ckt,fast5,1,inode2)) + IFC(bindNode,(ckt,fast5,2,gnode1)) + ptemp.rValue = cval * 2; + GCA(INPpName,("capacitance",&ptemp,ckt,type,fast5)) + + /* capacitor on node2 */ + strcpy(cname4, "txlcap4"); + mdfast6 = tab->defCmod; + IFC(newInstance,(ckt,mdfast6,&fast6,cname4)) + IFC(bindNode,(ckt,fast6,1,node2)) + IFC(bindNode,(ckt,fast6,2,gnode1)) + ptemp.rValue = cval; + GCA(INPpName,("capacitance",&ptemp,ckt,type,fast6)) + return; + + } + + /* use regular txl model */ + mdfast = thismodel->INPmodfast; + type = thismodel->INPmodType; + } else { + type = mytype; + if(!tab->defYmod) { + /* create default Y model */ + IFnewUid(ckt,&uid,(IFuid)NULL,"Y",UID_MODEL,(void**)NULL); + IFC(newModel, (ckt,type,&(tab->defYmod),uid)) + } + mdfast = tab->defYmod; + } + IFC(newInstance,(ckt,mdfast,&fast,name)) + } else { + LITERR("model name is not found") + return; + } + + if (error1 == 0 && lenvalgiven) { + ptemp.rValue = lenval; + GCA(INPpName,("length",&ptemp,ckt,type,fast)) + } + + IFC(bindNode,(ckt,fast,1,node1)) + IFC(bindNode,(ckt,fast,2,node2)) + + txl = (GENinstance *)fast; + + return; +} diff --git a/src/tcl/ChangeLog b/src/tcl/ChangeLog new file mode 100755 index 000000000..a7de44579 --- /dev/null +++ b/src/tcl/ChangeLog @@ -0,0 +1,34 @@ +25/07/2002: + * Modified configure/Makefiles so use --enable-tcl to make module + * Added option --enable-cluster to configure for cluster version + * Swig no longer needed to compile +23/07/2002: + * Added display device Tk plot + * Added function bltplot +17/04/2002: + * Merged a version of cluster spice. +26/03/2002: + * New TCL function lastVector. +19/02/2002: + * User nodes and codemodels now work in DCtrans analysis, + calculated results are now correct. + * Fixed bug where the first timestep always seems to fail to converge + after a resume or a step, thus cutting delta. +30/01/2002: + * Precompiled Opus/XSpice models are now supported with + --enable-xspice flag, load them using codemodel "opus spice module" +22/01/2002: + * Fixed parameters being ignored when mosfet model is not found +16/01/2002: + * Fixed crash when a mosfet model is not found +In the mists of time: + * Changed spicelib/parser/inpgtok.c to allow / * ^ in node names (/ needed for ext2spice) + * Modifed files in + spicelib/analysis/(acan.c, dctran.c, dctrcurv.c, noisean.c), frontend/outitf.c, + frontend/runcoms.c, frontend/runcoms2.c + so rawfiles continue to be written to after a pause then a resume + * Small modification to main.c to make the struct nutmeginfo global, so tclspice can use it. + * Added ifdefed code to frontend/outitf.c which links output to tclspice. + * Modified main.c and frontend/inp.c so .save lines are always processed + * ifdefed code in misc/alloc.c so the functions use Tcl_Mutex to stop memory allocation conflicts. + * Fixed crash in com_resume() when run without circuit loaded diff --git a/src/tcl/README b/src/tcl/README new file mode 100755 index 000000000..a471ab4b8 --- /dev/null +++ b/src/tcl/README @@ -0,0 +1,170 @@ +Spice Tcl module +$Id$ +DESCRIPTION + +Contained in this package is the source code for a spice tcl module and ng-spice-rework-14. +It allows you run spice commands in tcl and run the spice simulator in the background +so you can manipulate the results while spice is running. + +THANKS: + +Ng-Spice at http://ngspice.sourceforge.net for the spice simulator + +ScriptEDA ( http://www-cad.eecs.berkeley.edu/~pinhong/scriptEDA ) for the idea and examples. + +INSTALLATION + +Requirements: + ngspice, included (only tested with ng-spice-rework-13 and 14, should work with all rework versions) + tcl WITH thread support compiled in (tested with tcl-8.3.3-65 and tcl8.3.4) + BTL for tcl (tested with blt2.4y) + pthreads (most modern OS's have this) + +Installation: + This package contains the ng-spice-rework-14 source code, which has been slightly modifed, and the tcl module source. + The spice source is contained in the ng-spice-rework directory. + Tcl modules's documentation is in the ng-spice-rework/src/tcl directory + + Please hide libgc.so (the garbage collector library), if you have it. You can put it back afterwards! + This is because you don't want tclspice to compile with it included! + + Also if you want to run spice in the background you need to recompile + tcl and tk to enable thread support if they haven't got it enabled already (redhat packages haven't). + If you don't then tclspice will crash frequently!!!! + + The install commands are: + From the root directory of the source: + ./configure --enable-tcl --enable-experimental --enable-xspice --disable-shared + make tcl + And as root: + make tcl_install + +USAGE + +example: + +$tclsh + +%package require spice + (to load the module) + +Then you can run commands either by typing spice command directly or by typing spice +For example: + %spice::version + %spice::source ./example.cir + %spice::bg run + %spice::halt + %spice::show + %spice::plot_nvars 0 + %spice::plot_variables 0 + %spice::plot_datapoints 0 + %spice::bg resume + %spice::halt + %exit + +Commands availiabe: + + spice has it's own namespace (spice::) which can be imported. + Although there are a few conflicts, so not all commands are imported. + + + spice::spice + runs the spicified spice command in the foreground + spice::bg + runs the spice command in the background + All spice commands should be recognised + spice::halt + Stops(or attempts to) a spice background command (like run) (it simulates a ^C) + + spice::running + returns 1 if a background process is running, 0 otherwise + + spice::get_output script ?stderr? + runs the tcl script "script", catching std_out and maping it to the return value. + It can also catch the std_err and put it into the stderr variable. + + spice::get_param device param + Returns the parameter of the device + + spice::spice_data + Returns the names and types of avaliable spice + variables + {name type} {name type} ... {name type} + + spice::spice_header + Returns the current run's title, analysis name, date, and the number of signals as follows: + {title ??} {name ???} {date ????} {variables ???} + + spice::delta ?value? + Sets the value of delta to the given value, if given. + It returns the current setting of delta + + spice::maxstep ?value? + Same as spice::delta but for the maximum stepsize allowed + + variables: + + spice::steps_completed + The number of simulation steps done so far. + +The following concern the BLT vectors generated; + + functions: + spice::spicetoblt vecName signal ?start? ?end? + Sets the blt vector vecName with the contents of the spice signal, signal, + using the optional start and end index. + + spice::lastVector vecName + Sets the blt vector vecName to contain the last spice state vector + + variables: + spice::blt_vnum + The number of signals avalible + +The following only work if there is a plot stored; + + spice::plot_variables plot + returns a list of variables in plot "plot" + plot is from 0-numofplots, newest plot being 0. + + spice::plot_get_value name plot index + returns value of varable "name" in plot "plot", at position "index" + + spice::plot_datapoints plot + returns number of time steps saved so far + + spice::plot_title plot + returns plot title + + spice::plot_date plot + returns date string + + spice::plot_name plot + returns plot name + + spice::plot_nvars plot + returns number of variables in the plot + +Plotting functions; + + spice::plot + The standard plot function has been wrapped to a Tk canvas ".c" See Tcl source in + pkgIndex.tcl The callback functions are named gr_*, modify/override at will. + spice::bltplot + Instead of plotting: For each pair of vectors to plot spice::gr_Plot is called, + "proc spice_gr_Plot { Xname Xtype Xunits Yname Ytype Yunits }" which you can override + With the static Blt vectors "spice::X_Data" and "spice::Y_Data" containing the plot data. + WARNING: + If any of the Tcl callback functions cause an error then a crash may occour as tcl + overwrites random data in the spice code. Not sure why. + +TODO & BUGS + +1) plot and iplot don't work: window opens then tclsh stops, Xserver doesn't respond to spice requests + They aren't really needed anymore, as you now have the blt vectors to play with, + and the Tk plot output and bltplot output. +2) Spice prints to stdout, unavoidable, sorry if it mucks up your commandline +3) tclreadline doesn't like this module very much + +Stefan Jones + diff --git a/src/tcl/example.cir b/src/tcl/example.cir new file mode 100755 index 000000000..b31bb0993 --- /dev/null +++ b/src/tcl/example.cir @@ -0,0 +1,587 @@ +TITLE: proj1.cir.cir - Rotary Traveling Wave Oscillator +.control +set filetype=ascii +.endc + +VDD0 VDD0 VDD DC 0 +VSS0 VSS0 0 DC 0 +VDD_A0 VDD_A0 VDD0 DC 0 +VSS_A0 VSS_A0 VSS0 DC 0 +VDD_B0 VDD_B0 VDD0 DC 0 +VSS_B0 VSS_B0 VSS0 DC 0 +LA0 A0 LCA0 3.69030941553353e-11 +RA0 LCA0 A1 0.266535044422507 +LB0 B0 LCB0 3.69030941553353e-11 +RB0 LCB0 B1 0.266535044422507 +C0 A1 B1 2.50418376625721e-14 +MNA0 B0 A0 VSS_B0 VSS_B0 Nmod L=2.53696435353243e-07 W= ++4.24857778403814e-05 AD=3.125e-11 AS=3.125e-11 PD= ++8.49715556807627e-05 PS=8.49715556807627e-05 NQSMOD=1 +MPA0 B0 A0 VDD_B0 VDD_B0 Pmod L=2.55343565546106e-07 W= ++0.000101772203908557 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000203544407817114 PS=0.000203544407817114 NQSMOD=1 +MNB0 A0 B0 VSS_A0 VSS_A0 Nmod L=2.53941602497219e-07 W= ++4.10652659629401e-05 AD=3.125e-11 AS=3.125e-11 PD= ++8.21305319258802e-05 PS=8.21305319258802e-05 NQSMOD=1 +MPB0 A0 B0 VDD_A0 VDD_A0 Pmod L=2.52010168145607e-07 W= ++0.000103533977891464 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000207067955782928 PS=0.000207067955782928 NQSMOD=1 +LA1 A1 LCA1 3.69030941553353e-11 +RA1 LCA1 A2 0.266535044422507 +LB1 B1 LCB1 3.69030941553353e-11 +RB1 LCB1 B2 0.266535044422507 +C1 A2 B2 2.50418376625721e-14 +MNA1 B1 A1 0 0 Nmod L=2.52370578161099e-07 W=4.12246995102289e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.24493990204578e-05 PS= ++8.24493990204578e-05 NQSMOD=1 +MPA1 B1 A1 VDD VDD Pmod L=2.45709468983316e-07 W=0.000103710764679465 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000207421529358929 PS= ++0.000207421529358929 NQSMOD=1 +MNB1 A1 B1 0 0 Nmod L=2.48115895523017e-07 W=4.26306134285554e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.52612268571108e-05 PS= ++8.52612268571108e-05 NQSMOD=1 +MPB1 A1 B1 VDD VDD Pmod L=2.55265156192223e-07 W=0.000102043840486507 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000204087680973014 PS= ++0.000204087680973014 NQSMOD=1 +LA2 A2 LCA2 3.69030941553353e-11 +RA2 LCA2 A3 0.266535044422507 +LB2 B2 LCB2 3.69030941553353e-11 +RB2 LCB2 B3 0.266535044422507 +C2 A3 B3 2.50418376625721e-14 +MNA2 B2 A2 0 0 Nmod L=2.53484220592882e-07 W=4.16915225420459e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.33830450840917e-05 PS= ++8.33830450840917e-05 NQSMOD=1 +MPA2 B2 A2 VDD VDD Pmod L=2.44256748076514e-07 W=0.00010549295960702 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000210985919214039 PS= ++0.000210985919214039 NQSMOD=1 +MNB2 A2 B2 0 0 Nmod L=2.48805892712811e-07 W=4.15734692647458e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.31469385294916e-05 PS= ++8.31469385294916e-05 NQSMOD=1 +MPB2 A2 B2 VDD VDD Pmod L=2.54004987710956e-07 W=0.00010229621219808 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.00020459242439616 PS= ++0.00020459242439616 NQSMOD=1 +LA3 A3 LCA3 3.69030941553353e-11 +RA3 LCA3 A4 0.266535044422507 +LB3 B3 LCB3 3.69030941553353e-11 +RB3 LCB3 B4 0.266535044422507 +C3 A4 B4 2.50418376625721e-14 +MNA3 B3 A3 0 0 Nmod L=2.54307430347219e-07 W=4.11339076756089e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.22678153512179e-05 PS= ++8.22678153512179e-05 NQSMOD=1 +MPA3 B3 A3 VDD VDD Pmod L=2.52369109463781e-07 W=0.000103371681055656 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000206743362111311 PS= ++0.000206743362111311 NQSMOD=1 +MNB3 A3 B3 0 0 Nmod L=2.4960708801709e-07 W=4.21794611046917e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.43589222093833e-05 PS= ++8.43589222093833e-05 NQSMOD=1 +MPB3 A3 B3 VDD VDD Pmod L=2.53834779766428e-07 W=0.000105556314711602 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000211112629423204 PS= ++0.000211112629423204 NQSMOD=1 +LA4 A4 LCA4 3.69030941553353e-11 +RA4 LCA4 A5 0.266535044422507 +LB4 B4 LCB4 3.69030941553353e-11 +RB4 LCB4 B5 0.266535044422507 +C4 A5 B5 2.50418376625721e-14 +MNA4 B4 A4 0 0 Nmod L=2.48091656083177e-07 W=4.11207568141106e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.22415136282211e-05 PS= ++8.22415136282211e-05 NQSMOD=1 +MPA4 B4 A4 VDD VDD Pmod L=2.47723605289033e-07 W=0.000103463392309261 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000206926784618522 PS= ++0.000206926784618522 NQSMOD=1 +MNB4 A4 B4 0 0 Nmod L=2.49254771880382e-07 W=4.25122425012226e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.50244850024452e-05 PS= ++8.50244850024452e-05 NQSMOD=1 +MPB4 A4 B4 VDD VDD Pmod L=2.49689766979065e-07 W=0.000103227993619608 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000206455987239216 PS= ++0.000206455987239216 NQSMOD=1 +LA5 A5 LCA5 3.69030941553353e-11 +RA5 LCA5 A6 0.266535044422507 +LB5 B5 LCB5 3.69030941553353e-11 +RB5 LCB5 B6 0.266535044422507 +C5 A6 B6 2.50418376625721e-14 +MNA5 B5 A5 0 0 Nmod L=2.53960031106522e-07 W=4.1129961792588e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.22599235851759e-05 PS= ++8.22599235851759e-05 NQSMOD=1 +MPA5 B5 A5 VDD VDD Pmod L=2.47418707088064e-07 W=0.000101621693062467 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000203243386124935 PS= ++0.000203243386124935 NQSMOD=1 +MNB5 A5 B5 0 0 Nmod L=2.49659687529522e-07 W=4.2524931640785e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.50498632815701e-05 PS= ++8.50498632815701e-05 NQSMOD=1 +MPB5 A5 B5 VDD VDD Pmod L=2.46328059754468e-07 W=0.000102061546065548 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000204123092131096 PS= ++0.000204123092131096 NQSMOD=1 +LA6 A6 LCA6 3.69030941553353e-11 +RA6 LCA6 A7 0.266535044422507 +LB6 B6 LCB6 3.69030941553353e-11 +RB6 LCB6 B7 0.266535044422507 +C6 A7 B7 2.50418376625721e-14 +MNA6 B6 A6 0 0 Nmod L=2.54326804653788e-07 W=4.17332976706085e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.34665953412171e-05 PS= ++8.34665953412171e-05 NQSMOD=1 +MPA6 B6 A6 VDD VDD Pmod L=2.48727427835127e-07 W=0.000103244611103459 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000206489222206918 PS= ++0.000206489222206918 NQSMOD=1 +MNB6 A6 B6 0 0 Nmod L=2.49697035135609e-07 W=4.23570035518e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.47140071036001e-05 PS= ++8.47140071036001e-05 NQSMOD=1 +MPB6 A6 B6 VDD VDD Pmod L=2.48995485890626e-07 W=0.000103695454759978 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000207390909519956 PS= ++0.000207390909519956 NQSMOD=1 +LA7 A7 LCA7 3.69030941553353e-11 +RA7 LCA7 A8 0.266535044422507 +LB7 B7 LCB7 3.69030941553353e-11 +RB7 LCB7 B8 0.266535044422507 +C7 A8 B8 2.50418376625721e-14 +MNA7 B7 A7 0 0 Nmod L=2.53418975114981e-07 W=4.06421756574473e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.12843513148946e-05 PS= ++8.12843513148946e-05 NQSMOD=1 +MPA7 B7 A7 VDD VDD Pmod L=2.4471861043622e-07 W=0.000104600862141835 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.00020920172428367 PS= ++0.00020920172428367 NQSMOD=1 +MNB7 A7 B7 0 0 Nmod L=2.50159056393584e-07 W=4.06845582724173e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.13691165448345e-05 PS= ++8.13691165448345e-05 NQSMOD=1 +MPB7 A7 B7 VDD VDD Pmod L=2.55032245177227e-07 W=0.000106482118141681 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000212964236283363 PS= ++0.000212964236283363 NQSMOD=1 +LA8 A8 LCA8 3.69030941553353e-11 +RA8 LCA8 A9 0.266535044422507 +LB8 B8 LCB8 3.69030941553353e-11 +RB8 LCB8 B9 0.266535044422507 +C8 A9 B9 2.50418376625721e-14 +MNA8 B8 A8 0 0 Nmod L=2.45729547191971e-07 W=4.18266198665335e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.3653239733067e-05 PS=8.3653239733067e-05 ++NQSMOD=1 +MPA8 B8 A8 VDD VDD Pmod L=2.45156004861421e-07 W=0.000101593205477244 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000203186410954489 PS= ++0.000203186410954489 NQSMOD=1 +MNB8 A8 B8 0 0 Nmod L=2.49014342219656e-07 W=4.14314219478801e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.28628438957603e-05 PS= ++8.28628438957603e-05 NQSMOD=1 +MPB8 A8 B8 VDD VDD Pmod L=2.50396673007567e-07 W=0.000103029640740115 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.00020605928148023 PS= ++0.00020605928148023 NQSMOD=1 +LA9 A9 LCA9 3.69030941553353e-11 +RA9 LCA9 A10 0.266535044422507 +LB9 B9 LCB9 3.69030941553353e-11 +RB9 LCB9 B10 0.266535044422507 +C9 A10 B10 2.50418376625721e-14 +MNA9 B9 A9 0 0 Nmod L=2.51066307645916e-07 W=4.17044186844862e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.34088373689724e-05 PS= ++8.34088373689724e-05 NQSMOD=1 +MPA9 B9 A9 VDD VDD Pmod L=2.4945438501494e-07 W=0.000104601836030031 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000209203672060063 PS= ++0.000209203672060063 NQSMOD=1 +MNB9 A9 B9 0 0 Nmod L=2.56178041422984e-07 W=4.17990098329256e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.35980196658511e-05 PS= ++8.35980196658511e-05 NQSMOD=1 +MPB9 A9 B9 VDD VDD Pmod L=2.5274010042983e-07 W=0.000103578149162769 ++AD=7.8125e-11 AS=7.8125e-11 PD=0.000207156298325537 PS= ++0.000207156298325537 NQSMOD=1 +LA10 A10 LCA10 3.69030941553353e-11 +RA10 LCA10 A11 0.266535044422507 +LB10 B10 LCB10 3.69030941553353e-11 +RB10 LCB10 B11 0.266535044422507 +C10 A11 B11 2.50418376625721e-14 +MNA10 B10 A10 0 0 Nmod L=2.45772611943267e-07 W=4.24345922952649e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.48691845905299e-05 PS= ++8.48691845905299e-05 NQSMOD=1 +MPA10 B10 A10 VDD VDD Pmod L=2.55544710347746e-07 W= ++0.000105625826497323 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000211251652994645 PS=0.000211251652994645 NQSMOD=1 +MNB10 A10 B10 0 0 Nmod L=2.55886308364338e-07 W=4.0850956346516e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.17019126930321e-05 PS= ++8.17019126930321e-05 NQSMOD=1 +MPB10 A10 B10 VDD VDD Pmod L=2.44778614470725e-07 W= ++0.000105511594248206 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000211023188496411 PS=0.000211023188496411 NQSMOD=1 +LA11 A11 LCA11 3.69030941553353e-11 +RA11 LCA11 A12 0.266535044422507 +LB11 B11 LCB11 3.69030941553353e-11 +RB11 LCB11 B12 0.266535044422507 +C11 A12 B12 2.50418376625721e-14 +MNA11 B11 A11 0 0 Nmod L=2.4768739676619e-07 W=4.14724461551725e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.29448923103449e-05 PS= ++8.29448923103449e-05 NQSMOD=1 +MPA11 B11 A11 VDD VDD Pmod L=2.46276216123912e-07 W= ++0.000101782633723501 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000203565267447002 PS=0.000203565267447002 NQSMOD=1 +MNB11 A11 B11 0 0 Nmod L=2.54985612770668e-07 W=4.24608643314108e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.49217286628216e-05 PS= ++8.49217286628216e-05 NQSMOD=1 +MPB11 A11 B11 VDD VDD Pmod L=2.45772463970764e-07 W= ++0.000106109588792745 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.00021221917758549 PS=0.00021221917758549 NQSMOD=1 +LA12 A12 LCA12 3.69030941553353e-11 +RA12 LCA12 A13 0.266535044422507 +LB12 B12 LCB12 3.69030941553353e-11 +RB12 LCB12 B13 0.266535044422507 +C12 A13 B13 2.50418376625721e-14 +MNA12 B12 A12 0 0 Nmod L=2.45480481009462e-07 W=4.20858793029857e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.41717586059714e-05 PS= ++8.41717586059714e-05 NQSMOD=1 +MPA12 B12 A12 VDD VDD Pmod L=2.48462320158069e-07 W= ++0.00010650127744954 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.00021300255489908 PS=0.00021300255489908 NQSMOD=1 +MNB12 A12 B12 0 0 Nmod L=2.51992945030792e-07 W=4.17981435096244e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.35962870192489e-05 PS= ++8.35962870192489e-05 NQSMOD=1 +MPB12 A12 B12 VDD VDD Pmod L=2.49941922576661e-07 W= ++0.0001044851795426 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000208970359085199 PS=0.000208970359085199 NQSMOD=1 +LA13 A13 LCA13 3.69030941553353e-11 +RA13 LCA13 A14 0.266535044422507 +LB13 B13 LCB13 3.69030941553353e-11 +RB13 LCB13 B14 0.266535044422507 +C13 A14 B14 2.50418376625721e-14 +MNA13 B13 A13 0 0 Nmod L=2.51146190542173e-07 W=4.23276196447018e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.46552392894035e-05 PS= ++8.46552392894035e-05 NQSMOD=1 +MPA13 B13 A13 VDD VDD Pmod L=2.46359362747576e-07 W= ++0.00010249565409785 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.0002049913081957 PS=0.0002049913081957 NQSMOD=1 +MNB13 A13 B13 0 0 Nmod L=2.44656485453628e-07 W=4.11044634633624e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.22089269267248e-05 PS= ++8.22089269267248e-05 NQSMOD=1 +MPB13 A13 B13 VDD VDD Pmod L=2.56119611942636e-07 W= ++0.0001064085618438 AD=7.8125e-11 AS=7.8125e-11 PD=0.0002128171236876 ++PS=0.0002128171236876 NQSMOD=1 +LA14 A14 LCA14 3.69030941553353e-11 +RA14 LCA14 A15 0.266535044422507 +LB14 B14 LCB14 3.69030941553353e-11 +RB14 LCB14 B15 0.266535044422507 +C14 A15 B15 2.50418376625721e-14 +MNA14 B14 A14 0 0 Nmod L=2.47663439668801e-07 W=4.20889991075918e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.41779982151835e-05 PS= ++8.41779982151835e-05 NQSMOD=1 +MPA14 B14 A14 VDD VDD Pmod L=2.51252450429323e-07 W= ++0.000103622229824819 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000207244459649638 PS=0.000207244459649638 NQSMOD=1 +MNB14 A14 B14 0 0 Nmod L=2.5044796612668e-07 W=4.24685059762319e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.49370119524638e-05 PS= ++8.49370119524638e-05 NQSMOD=1 +MPB14 A14 B14 VDD VDD Pmod L=2.4786360459861e-07 W= ++0.000104258615459431 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000208517230918863 PS=0.000208517230918863 NQSMOD=1 +LA15 A15 LCA15 3.69030941553353e-11 +RA15 LCA15 A16 0.266535044422507 +LB15 B15 LCB15 3.69030941553353e-11 +RB15 LCB15 B16 0.266535044422507 +C15 A16 B16 2.50418376625721e-14 +MNA15 B15 A15 0 0 Nmod L=2.5213362488047e-07 W=4.26610931467994e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.53221862935989e-05 PS= ++8.53221862935989e-05 NQSMOD=1 +MPA15 B15 A15 VDD VDD Pmod L=2.44411053097269e-07 W= ++0.000104344399065411 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000208688798130821 PS=0.000208688798130821 NQSMOD=1 +MNB15 A15 B15 0 0 Nmod L=2.44947739168727e-07 W=4.18061319080677e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.36122638161354e-05 PS= ++8.36122638161354e-05 NQSMOD=1 +MPB15 A15 B15 VDD VDD Pmod L=2.45133067349567e-07 W= ++0.000103673770597555 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.00020734754119511 PS=0.00020734754119511 NQSMOD=1 +LA16 A16 LCA16 3.69030941553353e-11 +RA16 LCA16 A17 0.266535044422507 +LB16 B16 LCB16 3.69030941553353e-11 +RB16 LCB16 B17 0.266535044422507 +C16 A17 B17 2.50418376625721e-14 +MNA16 B16 A16 0 0 Nmod L=2.5558903414348e-07 W=4.23651981762607e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.47303963525215e-05 PS= ++8.47303963525215e-05 NQSMOD=1 +MPA16 B16 A16 VDD VDD Pmod L=2.46968507923118e-07 W= ++0.000101577430536373 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000203154861072746 PS=0.000203154861072746 NQSMOD=1 +MNB16 A16 B16 0 0 Nmod L=2.52441475326891e-07 W=4.0963445615255e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.192689123051e-05 PS= ++8.192689123051e-05 NQSMOD=1 +MPB16 A16 B16 VDD VDD Pmod L=2.49958772476576e-07 W= ++0.000102341104143712 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000204682208287424 PS=0.000204682208287424 NQSMOD=1 +LA17 A17 LCA17 3.69030941553353e-11 +RA17 LCA17 A18 0.266535044422507 +LB17 B17 LCB17 3.69030941553353e-11 +RB17 LCB17 B18 0.266535044422507 +C17 A18 B18 2.50418376625721e-14 +MNA17 B17 A17 0 0 Nmod L=2.46623947628415e-07 W=4.07033737509309e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.14067475018618e-05 PS= ++8.14067475018618e-05 NQSMOD=1 +MPA17 B17 A17 VDD VDD Pmod L=2.52274212428759e-07 W= ++0.000105423152156798 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000210846304313596 PS=0.000210846304313596 NQSMOD=1 +MNB17 A17 B17 0 0 Nmod L=2.51233452024547e-07 W=4.12815452669714e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.25630905339429e-05 PS= ++8.25630905339429e-05 NQSMOD=1 +MPB17 A17 B17 VDD VDD Pmod L=2.49324495416238e-07 W= ++0.000104142717459091 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000208285434918183 PS=0.000208285434918183 NQSMOD=1 +LA18 A18 LCA18 3.69030941553353e-11 +RA18 LCA18 A19 0.266535044422507 +LB18 B18 LCB18 3.69030941553353e-11 +RB18 LCB18 B19 0.266535044422507 +C18 A19 B19 2.50418376625721e-14 +MNA18 B18 A18 0 0 Nmod L=2.52038203439398e-07 W=4.17044670825126e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.34089341650252e-05 PS= ++8.34089341650252e-05 NQSMOD=1 +MPA18 B18 A18 VDD VDD Pmod L=2.46465042965348e-07 W= ++0.000102305682920291 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000204611365840582 PS=0.000204611365840582 NQSMOD=1 +MNB18 A18 B18 0 0 Nmod L=2.45695550122768e-07 W=4.2510656912981e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.50213138259621e-05 PS= ++8.50213138259621e-05 NQSMOD=1 +MPB18 A18 B18 VDD VDD Pmod L=2.56212134001568e-07 W= ++0.000101696358889307 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000203392717778614 PS=0.000203392717778614 NQSMOD=1 +LA19 A19 LCA19 3.69030941553353e-11 +RA19 LCA19 A20 0.266535044422507 +LB19 B19 LCB19 3.69030941553353e-11 +RB19 LCB19 B20 0.266535044422507 +C19 A20 B20 2.50418376625721e-14 +MNA19 B19 A19 0 0 Nmod L=2.46298724559332e-07 W=4.26183323927543e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.52366647855085e-05 PS= ++8.52366647855085e-05 NQSMOD=1 +MPA19 B19 A19 VDD VDD Pmod L=2.53903413760174e-07 W= ++0.000103580270078538 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000207160540157077 PS=0.000207160540157077 NQSMOD=1 +MNB19 A19 B19 0 0 Nmod L=2.4541336381424e-07 W=4.1471197163819e-05 AD= ++3.125e-11 AS=3.125e-11 PD=8.2942394327638e-05 PS=8.2942394327638e-05 ++NQSMOD=1 +MPB19 A19 B19 VDD VDD Pmod L=2.51953325753565e-07 W= ++0.0001019745929959 AD=7.8125e-11 AS=7.8125e-11 PD=0.0002039491859918 ++PS=0.0002039491859918 NQSMOD=1 +LA20 A20 LCA20 3.69030941553353e-11 +RA20 LCA20 A21 0.266535044422507 +LB20 B20 LCB20 3.69030941553353e-11 +RB20 LCB20 B21 0.266535044422507 +C20 A21 B21 2.50418376625721e-14 +MNA20 B20 A20 0 0 Nmod L=2.55318350883171e-07 W=4.2257523363596e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.4515046727192e-05 PS= ++8.4515046727192e-05 NQSMOD=1 +MPA20 B20 A20 VDD VDD Pmod L=2.50733395598687e-07 W= ++0.000105848300738233 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000211696601476467 PS=0.000211696601476467 NQSMOD=1 +MNB20 A20 B20 0 0 Nmod L=2.53961238224852e-07 W=4.07467605160825e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.1493521032165e-05 PS= ++8.1493521032165e-05 NQSMOD=1 +MPB20 A20 B20 VDD VDD Pmod L=2.52173406118976e-07 W= ++0.000104205251139887 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000208410502279773 PS=0.000208410502279773 NQSMOD=1 +LA21 A21 LCA21 3.69030941553353e-11 +RA21 LCA21 A22 0.266535044422507 +LB21 B21 LCB21 3.69030941553353e-11 +RB21 LCB21 B22 0.266535044422507 +C21 A22 B22 2.50418376625721e-14 +MNA21 B21 A21 0 0 Nmod L=2.46323292867561e-07 W=4.1601573531982e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.32031470639639e-05 PS= ++8.32031470639639e-05 NQSMOD=1 +MPA21 B21 A21 VDD VDD Pmod L=2.54613442115316e-07 W= ++0.000104931161465525 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.00020986232293105 PS=0.00020986232293105 NQSMOD=1 +MNB21 A21 B21 0 0 Nmod L=2.55836584454404e-07 W=4.10424160274173e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.20848320548346e-05 PS= ++8.20848320548346e-05 NQSMOD=1 +MPB21 A21 B21 VDD VDD Pmod L=2.54709741956022e-07 W= ++0.000102062091080516 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000204124182161031 PS=0.000204124182161031 NQSMOD=1 +LA22 A22 LCA22 3.69030941553353e-11 +RA22 LCA22 A23 0.266535044422507 +LB22 B22 LCB22 3.69030941553353e-11 +RB22 LCB22 B23 0.266535044422507 +C22 A23 B23 2.50418376625721e-14 +MNA22 B22 A22 0 0 Nmod L=2.54430620981417e-07 W=4.19307535657001e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.38615071314001e-05 PS= ++8.38615071314001e-05 NQSMOD=1 +MPA22 B22 A22 VDD VDD Pmod L=2.46694525572975e-07 W= ++0.000103108817734331 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000206217635468663 PS=0.000206217635468663 NQSMOD=1 +MNB22 A22 B22 0 0 Nmod L=2.4991048194413e-07 W=4.12343575509987e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.24687151019974e-05 PS= ++8.24687151019974e-05 NQSMOD=1 +MPB22 A22 B22 VDD VDD Pmod L=2.53435790976082e-07 W= ++0.000105540213369592 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000211080426739185 PS=0.000211080426739185 NQSMOD=1 +LA23 A23 LCA23 3.69030941553353e-11 +RA23 LCA23 A24 0.266535044422507 +LB23 B23 LCB23 3.69030941553353e-11 +RB23 LCB23 B24 0.266535044422507 +C23 A24 B24 2.50418376625721e-14 +MNA23 B23 A23 0 0 Nmod L=2.47985427798248e-07 W=4.14939059451511e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.29878118903023e-05 PS= ++8.29878118903023e-05 NQSMOD=1 +MPA23 B23 A23 VDD VDD Pmod L=2.50625862917368e-07 W= ++0.000104857443713713 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000209714887427425 PS=0.000209714887427425 NQSMOD=1 +MNB23 A23 B23 0 0 Nmod L=2.48906363214973e-07 W=4.09072154111792e-05 ++AD=3.125e-11 AS=3.125e-11 PD=8.18144308223583e-05 PS= ++8.18144308223583e-05 NQSMOD=1 +MPB23 A23 B23 VDD VDD Pmod L=2.544246792556e-07 W= ++0.000106667496188909 AD=7.8125e-11 AS=7.8125e-11 PD= ++0.000213334992377817 PS=0.000213334992377817 NQSMOD=1 +RCROSS1 A0 B24 0.001 +RCROSS2 B0 A24 0.001 + +.SAVE VDD0 +.SAVE VSS0 +.SAVE VDD_A0 +.SAVE VSS_A0 +.SAVE VDD_B0 +.SAVE VSS_B0 +.SAVE A0 B0 LA0#branch LB0#branch A1 B1 LA1#branch LB1#branch A2 B2 ++LA2#branch LB2#branch A3 B3 LA3#branch LB3#branch A4 B4 LA4#branch ++LB4#branch A5 B5 LA5#branch LB5#branch A6 B6 LA6#branch LB6#branch ++A7 B7 LA7#branch LB7#branch A8 B8 LA8#branch LB8#branch A9 B9 ++LA9#branch LB9#branch A10 B10 LA10#branch LB10#branch A11 B11 ++LA11#branch LB11#branch A12 B12 LA12#branch LB12#branch A13 B13 ++LA13#branch LB13#branch A14 B14 LA14#branch LB14#branch A15 B15 ++LA15#branch LB15#branch A16 B16 LA16#branch LB16#branch A17 B17 ++LA17#branch LB17#branch A18 B18 LA18#branch LB18#branch A19 B19 ++LA19#branch LB19#branch A20 B20 LA20#branch LB20#branch A21 B21 ++LA21#branch LB21#branch A22 B22 LA22#branch LB22#branch A23 B23 ++LA23#branch LB23#branch + + +** +**INCLUDING FILE: ./proj1/process.models.... +* +* Typical N Typical P - from process corners (taken from tsmc025_corners.bsim3 fron NCSU) +* +* TSMC 0.25u 5M 1P process. 2.5V transistor models + + +.MODEL Nmod NMOS LEVEL=8 ++TNOM = 25 ++VERSION = 3.2.2 TOX = 5.8e-9 ++XJ = 1E-07 NCH = 2.354946E+17 LLN = 1 ++LWN = 1 WLN = 1 WWN = 1 ++LINT = 1.76E-08 WINT = 6.75E-09 MOBMOD = 1 ++BINUNIT = 2 DWG = 0 DWB = 0 ++VTH0 = 0.4321336 LVTH0 = 2.081814E-08 WVTH0 = -5.470342E-11 ++PVTH0 = -6.721795E-16 K1 = 0.3281252 LK1 = 9.238362E-08 ++WK1 = 2.878255E-08 PK1 = -2.426481E-14 K2 = 0.0402824 ++LK2 = -3.208392E-08 WK2 = -1.154091E-08 PK2 = 9.192045E-15 ++K3 = 0 DVT0 = 0 DVT1 = 0 ++DVT2 = 0 DVT0W = 0 DVT1W = 0 ++DVT2W = 0 NLX = 0 W0 = 0 ++K3B = 0 VSAT = 7.586954E+04 LVSAT = 3.094656E-03 ++WVSAT = -1.747416E-03 PVSAT = 8.820956E-10 UA = 8.924498E-10 ++LUA = -1.511745E-16 WUA = -3.509821E-17 PUA = -3.08778E-23 ++UB = 8.928832E-21 LUB = -1.655745E-27 WUB = -2.03282E-27 ++PUB = 3.4578E-34 UC = -1.364265E-11 LUC = 1.170473E-17 ++WUC = -1.256705E-18 PUC = -6.249644E-24 RDSW = 447.8871 ++PRWB = 0 PRWG = 0 WR = 0.99 ++U0 = 0.06005258 LU0 = -6.31976E-09 WU0 = -8.819531E-09 ++PU0 = 3.57209E-15 A0 = -1.468837 LA0 = 6.419548E-07 ++WA0 = 5.512414E-07 PA0 = -9.222928E-14 KETA = -0.04922795 ++LKETA = 2.360844E-08 WKETA = 1.560385E-08 PKETA = -5.98377E-15 ++A1 = 0.02659908 LA1 = -6.511454E-09 A2 = 1 ++AGS = -4.01637 LAGS = 1.090294E-06 WAGS = 1.162021E-06 ++PAGS = -3.108579E-13 B0 = 0 B1 = 0 ++VOFF = -0.1829426 LVOFF = 9.941631E-09 WVOFF = 1.568082E-08 ++PVOFF = -2.832958E-15 NFACTOR = 0.6790636 LNFACTOR= 3.454948E-08 ++WNFACTOR= 1.501016E-07 PNFACTOR= -2.955591E-14 CIT = 2.218499E-04 ++LCIT = -1.076934E-10 WCIT = -3.286884E-10 PCIT = 1.658928E-16 ++CDSC = 0 CDSCB = 0 CDSCD = 0 ++ETA0 = 1.215578E-04 LETA0 = -1.037758E-11 WETA0 = -3.030225E-11 ++PETA0 = 1.529658E-17 ETAB = 3.548681E-03 LETAB = -1.791374E-09 ++WETAB = -6.897268E-10 PETAB = 3.481742E-16 DSUB = 0 ++PCLM = 3.583838 PDIBLC1 = 0 PDIBLC2 = 5.379674E-03 ++LPDIBLC2= 7.808481E-09 WPDIBLC2= 5.516945E-10 PPDIBLC2= -2.784957E-16 ++PDIBLCB = -0.1229374 LPDIBLCB= 4.956215E-08 WPDIBLCB= 3.299946E-08 ++PPDIBLCB= -9.624918E-15 DROUT = 0 PSCBE1 = 4.472639E+08 ++LPSCBE1 = 28.64041 WPSCBE1 = 15.7154 PPSCBE1 = -7.933138E-06 ++PSCBE2 = 1.842585E-06 LPSCBE2 = 2.871008E-12 WPSCBE2 = 2.579183E-12 ++PPSCBE2 = -1.301972E-18 PVAG = -2.015254E-03 LPVAG = 1.017757E-09 ++WPVAG = 3.07622E-10 PPVAG = -1.55418E-16 DELTA = -0.02862256 ++LDELTA = 1.492454E-08 WDELTA = -6.71663E-09 PDELTA = 3.407521E-15 ++ALPHA0 = 0 BETA0 = 30 KT1 = -0.2579945 ++LKT1 = -1.664895E-08 WKT1 = -1.633463E-08 PKT1 = 3.755864E-15 ++KT2 = -0.05347481 LKT2 = 8.244731E-09 WKT2 = 1.13705E-09 ++PKT2 = -1.240924E-15 AT = -1.132632E+04 LAT = 6.469047E-03 ++WAT = 6.829220E-04 PAT = -4.154249E-10 UTE = -2.309089 ++LUTE = 1.662427E-07 WUTE = 1.244801E-07 PUTE = -5.627924E-14 ++UA1 = -3.461758E-10 LUA1 = 1.747495E-16 WUA1 = -1.42065E-16 ++PUA1 = 7.171442E-23 UB1 = 0 UC1 = -2.38157E-12 ++LUC1 = -2.895726E-18 WUC1 = -1.990052E-17 PUC1 = 1.004497E-23 ++KT1L = 0 PRT = -1E-18 CJ = 2.024128E-3 ++MJ = 0.4960069 PB = 0.9173808 CJSW = 2.751528E-10 ++MJSW = 0.443145 PBSW = 0.9173808 CJSWG = 2.135064E-10 ++MJSWG = 0.443145 PBSWG = 0.9173808 ++RSH = 4.5 ++XTI = 3 ++CGDO = 3.11E-10 CGSO = 3.11E-10 CAPMOD = 2 ++XPART = 1 CF = 0 ++JS = 1E-06 ++JSW = 5E-11 + + + +.MODEL Pmod PMOS LEVEL=8 ++VERSION = 3.2.2 ++TNOM = 25 TOX = 5.8e-9 ++XJ = 1E-7 NCH = 4.1589E17 ++LLN = 1 LWN = 1 WLN = 1 ++WWN = 1 LINT = 1.2365E-8 WINT = 7.8E-9 ++MOBMOD = 1 BINUNIT = 2 DWG = 0 ++DWB = 0 VTH0 = -0.6236538 LVTH0 = 2.649834E-8 ++WVTH0 = 3.214189E-8 PVTH0 = -3.22268E-15 K1 = 0.4198155 ++LK1 = 5.770498E-8 WK1 = 5.577151E-8 PK1 = -2.81684E-14 ++K2 = 0.0429467 LK2 = -2.296405E-8 WK2 = -1.355302E-8 ++PK2 = 6.848271E-15 K3 = 0 DVT0 = 0 ++DVT1 = 0 DVT2 = 0 DVT0W = 0 ++DVT1W = 0 DVT2W = 0 NLX = 0 ++W0 = 0 K3B = 0 VSAT = 1.443912E5 ++LVSAT = -7.688012E-4 WVSAT = -6.083648E-3 PVSAT = 2.186471E-10 ++UA = 1.846811E-9 LUA = -3.27694E-16 WUA = -2.82106E-16 ++PUA = 7.180233E-23 UB = -7.84535E-19 LUB = 4.772849E-25 ++WUB = 2.599205E-25 PUB = -1.46530E-31 UC = -1.75560E-10 ++LUC = 3.360832E-17 WUC = 1.504425E-17 PUC = -1.30556E-23 ++RDSW = 1.03E3 PRWB = 0 PRWG = 0 ++WR = 1 U0 = 0.0136443 LU0 = -7.22084E-10 ++WU0 = -1.088554E-9 PU0 = 2.730854E-16 A0 = 0.1071803 ++LA0 = 4.64252E-7 WA0 = 5.383179E-7 PA0 = -1.32033E-13 ++KETA = -4.943762E-3 LKETA = -3.565304E-9 WKETA = -5.226247E-9 ++PKETA = 2.640665E-15 A1 = 0 A2 = 0.4 ++AGS = 0.1664005 LAGS = 1.19106E-7 WAGS = 5.29237E-8 ++PAGS = -2.67304E-14 B0 = 0 B1 = 0 ++VOFF = -0.0592623 LVOFF = -1.96686E-8 WVOFF = -1.486398E-8 ++PVOFF = 7.510321E-15 NFACTOR = 0.8588103 LNFACTOR= -1.158881E-7 ++WNFACTOR= 1.210664E-8 PNFACTOR= -6.11712E-15 CIT = 6.439495E-5 ++LCIT = 2.916437E-10 WCIT = -3.11284E-11 PCIT = 1.572825E-17 ++CDSC = 0 CDSCB = 0 CDSCD = 0 ++ETA0 = -3.819468E-3 LETA0 = 2.155422E-9 WETA0 = 8.235612E-10 ++PETA0 = -4.16037E-16 ETAB = 1.334637E-3 LETAB = -7.93631E-10 ++WETAB = 5.284657E-11 PETAB = -2.68353E-17 DSUB = 0 ++PCLM = 0.1098002 LPCLM = 6.874263E-7 WPCLM = 6.724724E-7 ++PPCLM = -1.97766E-13 PDIBLC1 = 0 PDIBLC2 = 5.801323E-3 ++LPDIBLC2= -1.81964E-9 WPDIBLC2= -5.853396E-9 PPDIBLC2= 2.957545E-15 ++PDIBLCB = 0.1921199 DROUT = 0 PSCBE1 = 7.19E8 ++PSCBE2 = 1E-20 PVAG = 0 DELTA = 0.01 ++ALPHA0 = 0 BETA0 = 30 KT1 = -0.3248987 ++LKT1 = -1.160393E-8 WKT1 = 4.153356E-8 PKT1 = -4.62347E-15 ++KT2 = -0.0367632 AT = 1E4 UTE = -1.04 ++UA1 = 3.992421E-10 UB1 = -9.23294E-19 LUB1 = -5.28718E-26 ++WUB1 = -6.13069E-26 PUB1 = 1.503674E-32 UC1 = -1.00699E-12 ++KT1L = 0 PRT = 0 CJ = 1.931092e-3 ++MJ = 0.4812153 PB = 0.9134669 CJSW = 2.232277e-10 ++MJSW = 0.3237595 PBSW = 0.9134669 CJSWG = 1.607088e-10 ++MJSWG = 0.3237595 PBSWG = 0.9134669 ++RSH = 3.5 ++CGDO = 2.68e-10 CGSO = 2.68e-10 ++CAPMOD = 2 ++XPART = 1 ++CF = 0 XTI = 3 ++JS = 3E-7 ++JSW = 5E-12 + +**.... FINISHED INCLUDING: ./proj1/process.models +** +.OPTIONS TEMP=25 +VSLEW_CONTROL VSLEW 0 (PULSE 0 1 0 1e-09) +EVLOGIC VRAMP 0 VSLEW 0 2.5 +VDDPOWER VDD VRAMP DC 0 +VARACTOR_V VARACTOR_V 0 DC 2.5 +.SAVE vddpower#branch +.SAVE vdd +.SAVE varactor_v +.TRAN 0.02n 3000n 0n 0.5n +.END diff --git a/src/tcl/plot.tcl b/src/tcl/plot.tcl new file mode 100755 index 000000000..810aa418d --- /dev/null +++ b/src/tcl/plot.tcl @@ -0,0 +1,12 @@ +#!/bin/sh +# WishFix \ + exec wish -f "$0" ${1+"$@"} +### + +package require spice + +spice::source example.cir +spice::step 100 + +spice::plot a0 vs b0 +spice::bltplot a0 diff --git a/src/tcl/vector_test.tcl b/src/tcl/vector_test.tcl new file mode 100755 index 000000000..9fc3be419 --- /dev/null +++ b/src/tcl/vector_test.tcl @@ -0,0 +1,50 @@ +#!/bin/sh +# WishFix \ + exec wish -f "$0" ${1+"$@"} +### + +package require BLT +namespace import blt::* + +package require spice + +wm title . "Vector Test script" +wm geometry . 800x600+40+40 +pack propagate . false + +stripchart .chart +pack .chart -side top -fill both -expand true +.chart axis configure x -title "Time" + + +# Create a vector (and call it $vector) +#vector create v1 +spice::source example.cir +spice::bg run + +after 1000 + +vector create a0 +vector create b0 +vector create a1 +vector create b1 +vector create stime +proc bltupdate {} { + spice::spicetoblt a0 a0 + spice::spicetoblt b0 b0 + spice::spicetoblt a1 a1 + spice::spicetoblt b1 b1 + spice::spicetoblt time stime + #puts $spice::lastitercount + after 100 bltupdate +} +bltupdate + + + +.chart element create a0 -color red -xdata stime -ydata a0 +.chart element create b0 -color blue -xdata stime -ydata b0 +.chart element create a1 -color yellow -xdata stime -ydata a1 +.chart element create b1 -color black -xdata stime -ydata b1 + + diff --git a/src/tclspice.c b/src/tclspice.c new file mode 100755 index 000000000..1772e1508 --- /dev/null +++ b/src/tclspice.c @@ -0,0 +1,1323 @@ +/* Copied and written by Stefan Jones (stefan.jones@multigig.com) at Multigig Ltd + * Under GPL licence + * Code based on and copied from ScriptEDA ( http://www-cad.eecs.berkeley.edu/~pinhong/scriptEDA ) + * $Id$ + */ + +/*******************/ +/* Defines */ +/*******************/ + +#define TCLSPICE_name "spice" +#define TCLSPICE_prefix "spice::" +#define TCLSPICE_namespace "spice" + +/**********************************************************************/ +/* Header files for C functions */ +/**********************************************************************/ + +/* Copied from main.c in ngspice*/ +#include +#include +#ifdef HAVE_STRING_H +#include +#endif /* HAVE_STRING_H */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef HAVE_GETRUSAGE +#ifdef HAVE_FTIME +#include +#endif +#endif + +/*Included for the module to access data*/ +#include +#include +#include +#include +#include + +/*For get_output*/ +#include +#include + +/* run spicein background */ +#include + +extern IFfrontEnd nutmeginfo; + +extern struct comm spcp_coms[ ]; + +extern int SIMinit(IFfrontEnd *frontEnd, IFsimulator **simulator); + +/*For blt spice to use*/ +typedef struct { + char *name; + pthread_mutex_t mutex;/*lock for this vector*/ + double *data;/* vector data*/ + int size;/*data it can store*/ + int length;/*actual amount of data*/ +} vector; + +/*The current run (to get variable names, etc)*/ +static runDesc *cur_run = NULL; + +static vector *vectors = NULL; + +/* save this each time called */ +static Tcl_Interp *spice_interp=NULL; +#define save_interp() spice_interp = interp; + + +/****************************************************************************/ +/* BLT and data routines */ +/****************************************************************************/ + +/*this holds the number of time points done (altered by spice)*/ +int steps_completed = 0; +/* number of bltvectors*/ +static int blt_vnum = 0; + + +/*Native Tcl functions */ + +static int spice_header(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + char buf[256]; + char *date, *name, *title; + if (args != 1) { + Tcl_SetResult(interp, "Wrong # args. spice::spice_header",TCL_STATIC); + return TCL_ERROR; + } + if(cur_run){ + Tcl_ResetResult(interp); + date = datestring(); + title = cur_run->name; + name = cur_run->type; + sprintf(buf,"{title \"%s\"} {name \"%s\"} {date \"%s\"} {variables %u}",title,name,date,cur_run->numData); + Tcl_AppendResult(interp, (char *)buf,TCL_STATIC); + return TCL_OK; + }else return TCL_ERROR; +} + + +static int spice_data(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + char buf[256]; + int i, type; + char *name; + if (args != 1) { + Tcl_SetResult(interp, "Wrong # args. spice::spice_data",TCL_STATIC); + return TCL_ERROR; + } + if(blt_vnum){ + Tcl_ResetResult(interp); + for(i = 0; i < blt_vnum;i++){ + name = vectors[i].name; + if (substring("#branch", name)) + type = SV_CURRENT; + else if (cieq(name, "time")) + type = SV_TIME; + else if (cieq(name, "frequency")) + type = SV_FREQUENCY; + else + type = SV_VOLTAGE; + sprintf(buf,"{%s %s} ",name, + ft_typenames(type)); + Tcl_AppendResult(interp, (char *)buf, TCL_STATIC); + } + return TCL_OK; + }else return TCL_ERROR; +} + +static int resetTriggers(); + +/*Creates and registers the blt vectors, used by spice*/ +void blt_init(void *tmp){ + int i; + runDesc *run = cur_run; + cur_run = NULL; + /* reset varaibles and free*/ + if(vectors){ + resetTriggers(); + for(i = blt_vnum, blt_vnum = 0/*stops vector access*/;i > 0;i--){ + if(run->writeOut) + FREE(vectors[i].data); + FREE(vectors[i].name); + pthread_mutex_destroy(&vectors[i].mutex); + } + FREE(vectors); + } + + + /* initilise */ + cur_run = (runDesc *)tmp;; + vectors = (vector *)MALLOC(cur_run->numData*sizeof(vector)); + for(i = 0;i < cur_run->numData;i++){ + vectors[i].name = copy((cur_run->data[i]).name); + pthread_mutex_init(&vectors[i].mutex,NULL); + vectors[i].data = NULL; + vectors[i].size = 0; + vectors[i].length = 0; + } + blt_vnum = i;/*allows access to vectors*/ + return; +} + +/*Adds data to the stored vector*/ +void blt_add(int index,double value){ + vector *v; + v = &vectors[index]; + pthread_mutex_lock(&vectors[index].mutex); + if(!(v->length < v->size)){ + v->size += 100; + v->data = (double *)REALLOC(v->data,sizeof(double)*v->size); + } + v->data[v->length] = value; + v->length ++; + pthread_mutex_unlock(&vectors[index].mutex); + return; +} + +/* Locks the vector data to stop conflicts*/ +void blt_lockvec(int index){ + pthread_mutex_lock(&vectors[index].mutex); + return; +} + + +/*links a dvec to a blt vector, used to stop duplication of data when writing to a plot, + but makes BLT vectors more unsafe */ +void blt_relink(int index,void *tmp){ + struct dvec *v = (struct dvec *)tmp; + vectors[index].data = v->v_realdata; + vectors[index].length = v->v_length; + vectors[index].size = v->v_length;/*silly spice doesn't use v_rlength*/ + pthread_mutex_unlock(&vectors[index].mutex); + return; +} + + + + +/* Tcl functions to access spice data */ + +/* This copys the last Spice state vector to the given blt_vector + * arg1: blt_vector + */ +static int lastVector(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + Blt_Vector *vec; + char *blt; + int i; + double *V; + if (args != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::lastVector vecName",TCL_STATIC); + return TCL_ERROR; + } + blt = argv[1]; + if(Blt_GetVector(interp,blt,&vec)){ + Tcl_SetResult(interp, "Bad blt vector ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)blt, TCL_STATIC); + return TCL_ERROR; + } + if(!(V = (double *)MALLOC(sizeof(double)*blt_vnum))) { + Tcl_SetResult(interp, "Out of Memory",TCL_STATIC); + return TCL_ERROR; + } + + for(i=0;i < blt_vnum;i++){ + pthread_mutex_lock(&vectors[i].mutex); + V[i] = vectors[i].data[vectors[i].length-1]; + pthread_mutex_unlock(&vectors[i].mutex); + } + Blt_ResetVector(vec,V,blt_vnum, + blt_vnum,TCL_VOLATILE); + txfree(V); + return TCL_OK; +} + +/*agr1: spice variable name + *arg2: blt_vector + *arg3: start copy index, optional + *arg4: end copy index. optional + */ +static int spicetoblt(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + Blt_Vector *vec; + int index, i; + char *blt, *var; + int start=0,end=-1,len; + + if (args < 3 || args > 5) { + Tcl_SetResult(interp, "Wrong # args. spice::spicetoblt spice_variable vecName ?start? ?end?",TCL_STATIC); + return TCL_ERROR; + } + + var = argv[1]; + blt = argv[2]; + + for(i=0;i < blt_vnum && strcmp(var,vectors[i].name);i++); + + if(i == blt_vnum) { + Tcl_SetResult(interp, "Bad spice variable ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)var, TCL_STATIC); + return TCL_ERROR; + } else index = i; + + if(Blt_GetVector(interp,blt,&vec)){ + Tcl_SetResult(interp, "Bad blt vector ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)blt, TCL_STATIC); + return TCL_ERROR; + } + + + if(args >= 4) + start = atoi(argv[3]); + if(args == 5) + end = atoi(argv[4]); + if(vectors[index].length) { + pthread_mutex_lock(&vectors[index].mutex); + + len = vectors[index].length; + + if(start){ + start = start % len; + if(start < 0) + start += len; + } + + end = end % len; + if(end < 0) + end += len; + + len = abs(end - start + 1); + + Blt_ResetVector(vec,(vectors[index].data + start),len, + len,TCL_VOLATILE); + + pthread_mutex_unlock(&vectors[index].mutex); + } + return TCL_OK; +} + + +/******************************************************************/ +/* Main spice command executions and thread control */ +/*****************************************************************/ + +static pthread_t tid; +static bool fl_running = FALSE; +static bool fl_exited = TRUE; + + +static void *_thread_run(void *string){ + fl_exited = FALSE; + cp_evloop((char *)string); + FREE(string); + fl_exited = TRUE; + return 0; +} + +/*Stops a running thread, hopefully */ +static int _thread_stop(){ + int timeout = 0; + if(fl_running) { + while(!fl_exited && timeout < 100){ + ft_intrpt = TRUE; + timeout++; + sleep(1); + } + if(!fl_exited) { + fprintf(stderr,"couldn't stop tclspice\n"); + return TCL_ERROR; + } + pthread_join(tid, NULL); + fl_running = FALSE; + ft_intrpt = FALSE; + return TCL_OK; + }else { + fprintf(stderr,"spice not running\n"); + } + return TCL_OK; +} + +static int _run(int args,char **argv){ + char buf[1024] = "", *string; + int i; + bool fl_bg = FALSE; + /* run task in background if preceeded by "bg"*/ + if(!strcmp(argv[0],"bg")) { + args--; + argv = &argv[1]; + fl_bg = TRUE; + } + + /*build a char * to pass to cp_evloop */ + for(i=0;i 1) + cp_evloop(buf); + else{ + _thread_stop(); + cp_evloop(buf); + } + else { + /* cannot do anything if spice is running in the bg*/ + if(fl_running){ + if(fl_exited){ + _thread_stop(); + cp_evloop(buf); + }else + fprintf(stderr,"type \"spice stop\" first\n"); + }else{ + /*do the command*/ + cp_evloop(buf); + } + } + return TCL_OK; +} + +static int _tcl_dispatch(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + int i; + save_interp(); + /* Looks backwards through the first command and strips the :: part */ + for(i = strlen(argv[0])-1;i > 0;i--) + if(argv[0][i] == *":") + argv[0] += i + 1; + return _run(args,argv); +} + + +/* Runs the spice command given in spice */ +static int _spice_dispatch(ClientData clientData, Tcl_Interp *interp, + int args, char **argv){ + save_interp(); + if(args == 1) return TCL_OK; + return _run(args-1,&argv[1]); +} + +/*Checks if spice is runnuing in the background */ +static int running(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + Tcl_SetObjResult(interp,Tcl_NewIntObj((long) (fl_running && !fl_exited))); + return TCL_OK; +} + + +/**************************************/ +/* plot manipulation functions */ +/* only usefull if plots are saved */ +/**************************************/ + +/*helper function*/ +inline static struct plot * get_plot(int plot){ + struct plot *pl; + pl = plot_list; + for(;0 < plot;plot--){ + pl = pl->pl_next; + if(!pl) + return (struct plot *)NULL; + } + return pl; +} + +/*Outputs the names of all variables in the plot */ +static int plot_variables(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + int plot; + struct dvec *v; + + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_variables plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp,"bad plot given",TCL_STATIC); + return TCL_ERROR; + } + + for(v = pl->pl_dvecs;v;v = v->v_next){ + Tcl_AppendElement(interp,v->v_name); + } + return TCL_OK; +} + +/*returns the value of a variable */ +static int plot_get_value(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + struct dvec *v; + char *name; + int plot,index; + + if (argc != 4) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_get_value name plot index",TCL_STATIC); + return TCL_ERROR; + } + + name = argv[1]; + plot = atoi(argv[2]); + index = atoi(argv[3]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot",TCL_STATIC); + return TCL_ERROR; + } + for(v = pl->pl_dvecs;v;v = v->v_next) + if(!strcmp(v->v_name,name)){ + if (index < v->v_length) { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj((double) v->v_realdata[index])); + return TCL_OK; + } else { + Tcl_SetResult(interp, "bad index",TCL_STATIC); + return TCL_ERROR; + } + } + Tcl_SetResult(interp, "variable not found",TCL_STATIC); + return TCL_ERROR; +} + + +/*The length of the first vector in a plot*/ +static int plot_datapoints(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + struct dvec *v; + int plot; + + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_datapoints plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot", TCL_STATIC); + return TCL_ERROR; + } + + v = pl->pl_dvecs; + + Tcl_SetObjResult(interp,Tcl_NewIntObj((long) v->v_length));// could be very dangeous + return TCL_OK; +} + +/*These functions give you infomation about a plot*/ + +static int plot_title(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + int plot; + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_nvars plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot", TCL_STATIC); + return TCL_ERROR; + } + Tcl_SetObjResult(interp,Tcl_NewStringObj(pl->pl_title,-1)); + return TCL_OK; +} + +static int plot_date(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + + int plot; + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_nvars plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot", TCL_STATIC); + return TCL_ERROR; + } + Tcl_SetObjResult(interp,Tcl_NewStringObj(pl->pl_date,-1)); + return TCL_OK; +} + +static int plot_name(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + int plot; + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_nvars plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot", TCL_STATIC); + return TCL_ERROR; + } + Tcl_SetObjResult(interp,Tcl_NewStringObj(pl->pl_name,-1)); + return TCL_OK; +} + +/*number of variables in a plot*/ + +static int plot_nvars(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct plot *pl; + struct dvec *v; + int plot; + int i=0; + + if (argc != 2) { + Tcl_SetResult(interp, "Wrong # args. spice::plot_nvars plot",TCL_STATIC); + return TCL_ERROR; + } + + plot = atoi(argv[1]); + + if(!(pl = get_plot(plot))){ + Tcl_SetResult(interp, "bad plot",TCL_STATIC); + return TCL_ERROR; + } + for(v = pl->pl_dvecs;v;v = v->v_next) + i++; + Tcl_SetObjResult(interp,Tcl_NewIntObj((long) i)); + return TCL_OK; +} + + +/*******************************************/ +/* Misc functions */ +/*******************************************/ + +/*Runs a tcl script and returns the output*/ +static int get_output(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { + FILE *pipein; + int tmp_1,tmp_2=0; + char buf[1024]; + int outfd,outfd2=0; + save_interp(); + if ((argc < 2) || (argc > 3)) { + Tcl_SetResult(interp, "Wrong # args. spice::get_output script ?var_for_stderr?",TCL_STATIC); + return TCL_ERROR; + } + tmp_1=dup(1); + outfd=open("/tmp/tclspice.tmp_out",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU); + if(argc==3){ + tmp_2=dup(2); + outfd2=open("/tmp/tclspice.tmp_err",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU); + } + freopen("/tmp/tclspice.tmp_out","w",stdout); + if(argc==3)freopen("/tmp/tclspice.tmp_err","w",stderr); + dup2(outfd,1); + if(argc==3)dup2(outfd2,2); + + Tcl_Eval(interp,argv[1]); + + fclose(stdout); + close(outfd); + if(argc==3){ + fclose(stderr); + close(outfd2); + } + dup2(tmp_1, 1); + close(tmp_1); + if(argc==3){ + dup2(tmp_2, 2); + close(tmp_2); + } + freopen("/dev/fd/1","w",stdout); + if(argc==3)freopen("/dev/fd/2","w",stderr); + pipein=fopen("/tmp/tclspice.tmp_out","r"); + if(pipein==NULL){ + fprintf(stderr,"pipein==NULL\n"); + } + Tcl_ResetResult(interp); + while(fgets(buf,1024,pipein)!=NULL){ + Tcl_AppendResult(interp, (char *)buf, TCL_STATIC); + } + fclose(pipein); + if(argc==3){ + pipein=fopen("/tmp/tclspice.tmp_err","r"); + Tcl_SetVar(interp,argv[2],"",0); + while(fgets(buf,1024,pipein)!=NULL){ + Tcl_SetVar(interp,argv[2],buf,TCL_APPEND_VALUE); + } + fclose(pipein); + } + return TCL_OK; +} + +/* Returns the current value of a parameter + * has lots of memory leaks + */ +static int get_param(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + wordlist *wl= NULL; + char *device, *param; + struct variable *v; + char buf[128]; + if (argc != 3) { + Tcl_SetResult(interp, "Wrong # args. spice::get_param device param",TCL_STATIC); + return TCL_ERROR; + } + if (!ft_curckt) { + Tcl_SetResult(interp, "No circuit loaded ",TCL_STATIC); + return TCL_ERROR; + } + + device = argv[1]; + param = argv[2]; + /* copied from old_show(wordlist *) */ + v = (*if_getparam)(ft_curckt->ci_ckt, + &device, param, 0, 0); + if (!v) + v = (*if_getparam)(ft_curckt->ci_ckt, + &device, param, 0, 1); + if (v) { + wl = cp_varwl(v); + Tcl_SetResult(interp,wl->wl_word,TCL_VOLATILE); + wl_free(wl); + tfree(v); + return TCL_OK; + + } else { + sprintf(buf,"%s in %s not found",param, device); + Tcl_AppendResult(interp,buf,TCL_STATIC); + } + return TCL_ERROR; + +} + +/* Direct control over the step size + * Spice will still adjust it to keep accuracy wuithin reltol and abstol + */ +static int delta(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + if (argc < 1 ||argc > 2) { + Tcl_SetResult(interp, "Wrong # args. spice::delta ?value?",TCL_STATIC); + return TCL_ERROR; + } + if (!ft_curckt) { + Tcl_SetResult(interp, "No circuit loaded ",TCL_STATIC); + return TCL_ERROR; + } + + if(argc == 2) { + ((CKTcircuit *)ft_curckt->ci_ckt)->CKTdelta = atof(argv[1]); + } + /*Ok, as log as string less than 200 chars*/ + sprintf(interp->result,"%G",((CKTcircuit *)ft_curckt->ci_ckt)->CKTdelta); + return TCL_OK; +} + +#include +/* Direct control over the maximum stepsize + * Spice will still adjust it to keep accuracy wuithin reltol and abstol + */ +static int maxstep(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + TRANan *job; + if (argc < 1 ||argc > 2) { + Tcl_SetResult(interp, "Wrong # args. spice::maxstep ?value?",TCL_STATIC); + return TCL_ERROR; + } + if (!ft_curckt) { + Tcl_SetResult(interp, "No circuit loaded ",TCL_STATIC); + return TCL_ERROR; + } + + job = (TRANan*)((CKTcircuit *)ft_curckt->ci_ckt)->CKTcurJob; + if(argc == 2) { + job->TRANmaxStep = atof(argv[1]); + } + /*Ok, as log as string less than 200 chars*/ + sprintf(interp->result,"%G",job->TRANmaxStep); + return TCL_OK; + +} + + +/****************************************/ +/* The Tk frontend for plot */ +/****************************************/ + +/* Use Tcl_GetStringResult to get canvas size etc. from Tcl */ +#include +int sp_Tk_Init(void) { + /* This is hard coded in C at the mo, use X11 values */ + dispdev->numlinestyles = 8; + dispdev->numcolors = 20; + dispdev->width = 1280; + dispdev->height = 1024; + + return 0; +} + +#include +int sp_Tk_NewViewport(GRAPH *graph) { + char *result; + int width, height, fontwidth, fontheight; + graph->devdep = (char *) NULL; + + if(Tcl_GlobalEval(spice_interp,"spice_gr_NewViewport") != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + + result = Tcl_GetStringResult(spice_interp); + if(sscanf(result,"%i %i %i %i",&width,&height,&fontwidth, &fontheight) != 4) { + Tcl_ResetResult(spice_interp); + return 1; + } + graph->absolute.xpos = 0; /* these always seem sensible, let Tcl adjust coods */ + graph->absolute.ypos = 0; + graph->absolute.width = width; + graph->absolute.height = height; + graph->fontwidth = fontwidth; + graph->fontheight = fontheight; + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_Close(void) { + if(Tcl_Eval(spice_interp,"spice_gr_Close") != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_Clear(void) { + if(Tcl_Eval(spice_interp,"spice_gr_Clear") != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_DrawLine(int x1, int y1, int x2, int y2) { + char buf[1024]; + sprintf(buf,"spice_gr_DrawLine %i %i %i %i",x1, y1, x2, y2); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_Arc(int x0, int y0, int radius, double theta1, double theta2) { + char buf[1024]; + sprintf(buf,"spice_gr_Arc %i %i %i %f %f", x0, y0, radius, theta1, theta2); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_Text(char *text, int x, int y) { + char buf[1024]; + sprintf(buf,"spice_gr_Text \"%s\" %i %i",text,x,y); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_DefineColor(int colorid, double red, double green, double blue) { + char buf[1024]; + sprintf(buf,"spice_gr_DefineColor %i %g %g %g",colorid, red, green, blue); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_DefineLinestyle(int linestyleid, int mask) { + char buf[1024]; + sprintf(buf,"spice_gr_DefineLinestyle %i %i", linestyleid, mask); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_SetLinestyle(int linestyleid) { + char buf[1024]; + sprintf(buf,"spice_gr_SetLinestyle %i", linestyleid); + if(Tcl_Eval(spice_interp, buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_SetColor(int colorid) { + char buf[1024]; + sprintf(buf,"spice_gr_SetColor %i", colorid); + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + +int sp_Tk_Update(void) { + if(Tcl_Eval(spice_interp,"spice_gr_Update") != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + Tcl_ResetResult(spice_interp); + return 0; +} + + +/********************************************************/ +/* The Blt method for plotting */ +/********************************************************/ + +int blt_plot(struct dvec *y,struct dvec *x){ + Blt_Vector *X_Data=NULL, *Y_Data=NULL; + char buf[1024]; + + /* A bug in these functions? , crashes if used so make vectors in Tcl + Blt_CreateVector(spice_interp,"spice::X_Data",1,&X_Data); + Blt_CreateVector(spice_interp,"spice::Y_Data",1,&Y_Data); + */ + Blt_GetVector(spice_interp,"spice::X_Data",&X_Data); + Blt_GetVector(spice_interp,"spice::Y_Data",&Y_Data); + + if(!X_Data || !Y_Data) { + fprintf(stderr,"Error: Blt vector X_Data or Y_Data not created\n"); + return 1; + } + + Blt_ResetVector (Y_Data, y->v_realdata ,y->v_length, y->v_length, TCL_VOLATILE); + + if (x) { + Blt_ResetVector (X_Data, x->v_realdata, x->v_length, x->v_length, TCL_VOLATILE); + } else { + x = y; + /*TODO: handle complex data properly */ + Blt_ResetVector (X_Data, y->v_realdata, y->v_length, y->v_length, TCL_VOLATILE); + } + + sprintf(buf,"spice_gr_Plot %s %s %s %s %s %s", + x->v_name, ft_typenames(x->v_type), ft_typabbrev(x->v_type), + y->v_name, ft_typenames(y->v_type), ft_typabbrev(y->v_type)); + + if(Tcl_Eval(spice_interp,buf) != TCL_OK) { + Tcl_ResetResult(spice_interp); + return 1; + } + + Tcl_ResetResult(spice_interp); + + return 0; +} + +/********************************************************/ +/* Triggering stuff */ +/********************************************************/ + +struct triggerEvent { + struct triggerEvent *next; + int vector; + int type; + int stepNumber; + double time; +}; + + +struct triggerEvent *eventQueue=NULL; +struct triggerEvent *eventQueueEnd=NULL; + +pthread_mutex_t triggerMutex; + +struct watch { + struct watch *next; + int vector;/* index of vector to watch */ + int type; /* +ive or -ive trigger */ + int state; /* pretriggered or not */ + double Vmin; /* the boundaries */ + double Vmax; +}; + +struct watch *watches=NULL; + +int Tcl_ExecutePerLoop() { + + struct watch *current; + + pthread_mutex_lock(&vectors[0].mutex); + pthread_mutex_lock(&triggerMutex); + + for(current=watches;current;current = current->next) { + vector *v; + v = &vectors[current->vector]; + pthread_mutex_lock(&v->mutex); + + if((current->type > 0 && current->state && v->data[v->length-1] > current->Vmax) || + (current->type < 0 && current->state && v->data[v->length-1] < current->Vmin) ) { + struct triggerEvent *tmp = (struct triggerEvent *)MALLOC(sizeof(struct triggerEvent)); + + tmp->next = NULL; + + if(eventQueue) { + eventQueueEnd->next = tmp; + eventQueueEnd = tmp; + } else { + eventQueue = tmp; + } + + eventQueueEnd = tmp; + + tmp->vector = current->vector; + tmp->type = current->type; + tmp->stepNumber = vectors[0].length; + tmp->time = vectors[0].data[vectors[0].length-1]; + + current->state = 0; + + } else + if((current->type > 0 && v->data[v->length-1] < current->Vmin) || + (current->type < 0 && v->data[v->length-1] > current->Vmax)) + current->state = 1; + pthread_mutex_unlock(&v->mutex); + } + + pthread_mutex_unlock(&triggerMutex); + + pthread_mutex_unlock(&vectors[0].mutex); + + return 0; +} + +static int resetTriggers() { + pthread_mutex_lock(&triggerMutex); + + while(watches) { + struct watch *tmp = watches; + watches = tmp->next; + FREE(tmp); + } + + while(eventQueue) { + struct triggerEvent *tmp = eventQueue; + eventQueue = tmp->next; + FREE(tmp); + } + + eventQueueEnd = NULL; + + pthread_mutex_unlock(&triggerMutex); + return 0; +} + + +/* Registers a watch for a trigger + *arg0: Vector Name to watch + *arg1: Vmin + *arg2: Vmax + *arg3: 1 / -1 for +ive(voltage goes +ive) or -ive trigger + */ +static int registerTrigger(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + int i, index; + char *var; + struct watch *tmp; + + if (argc != 4 && argc != 5) { + Tcl_SetResult(interp, "Wrong # args. spice::registerTrigger vecName Vmin Vmax ?type?",TCL_STATIC); + return TCL_ERROR; + } + + var = argv[1]; + + for(i=0;i < blt_vnum && strcmp(var,vectors[i].name);i++); + + if(i == blt_vnum) { + Tcl_SetResult(interp, "Bad spice variable ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)var, TCL_STATIC); + return TCL_ERROR; + } else index = i; + + pthread_mutex_lock(&triggerMutex); + + tmp = (struct watch *)MALLOC(sizeof(struct watch)); + + tmp->next = watches; + watches = tmp; + + watches->vector = index; + if(argc == 5) + watches->type = atoi(argv[4]); + else + watches->type = 1; + + watches->state = 0; + watches->Vmin = atof(argv[2]); + watches->Vmax = atof(argv[3]); + + pthread_mutex_unlock(&triggerMutex); + + return TCL_OK; +} +/*unregisters a trigger + *arg0: Vector name + *arg1: type + */ +static int unregisterTrigger(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + int i, index, type; + char *var; + struct watch *tmp; + struct watch **cut; + + if (argc != 2 && argc != 3) { + Tcl_SetResult(interp, "Wrong # args. spice::unregisterTrigger vecName ?type?",TCL_STATIC); + return TCL_ERROR; + } + + var = argv[1]; + + for(i=0;i < blt_vnum && strcmp(var,vectors[i].name);i++); + + if(i == blt_vnum) { + Tcl_SetResult(interp, "Bad spice variable ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)var, TCL_STATIC); + return TCL_ERROR; + } else index = i; + + if(argc == 3) + type = atoi(argv[4]); + else + type = 1; + + pthread_mutex_lock(&triggerMutex); + + cut = &watches; + + tmp = watches; + + while(tmp) + if(tmp->vector == index && tmp->type == type) { + struct watch *t = tmp; + *cut = tmp->next; + tmp = tmp->next; + FREE(t); + } else { + cut = &tmp->next; + tmp = tmp->next; + } + + pthread_mutex_unlock(&triggerMutex); + + return TCL_OK; +} + +/* returns: +"vecName" "time" "stepNumber" "type" +*/ +static int popTriggerEvent(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + + if (argc != 1) { + Tcl_SetResult(interp, "Wrong # args. spice::popTriggerEvent",TCL_STATIC); + return TCL_ERROR; + } + + if(eventQueue) { + struct triggerEvent *popedEvent; + Tcl_Obj *list; + + pthread_mutex_lock(&triggerMutex); + + popedEvent = eventQueue; + + eventQueue = popedEvent->next; + + + list = Tcl_NewListObj(0,NULL); + + Tcl_ListObjAppendElement(interp,list,Tcl_NewStringObj(vectors[popedEvent->vector].name,strlen(vectors[popedEvent->vector].name))); + + Tcl_ListObjAppendElement(interp,list,Tcl_NewDoubleObj(popedEvent->time)); + + Tcl_ListObjAppendElement(interp,list,Tcl_NewIntObj(popedEvent->stepNumber)); + + Tcl_ListObjAppendElement(interp,list,Tcl_NewIntObj(popedEvent->type)); + + Tcl_SetObjResult(interp,list); + + FREE(popedEvent); + + pthread_mutex_unlock(&triggerMutex); + } + + return TCL_OK; +} + +static int listTriggers(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]){ + struct watch *tmp; + Tcl_Obj *list; + + if (argc != 1) { + Tcl_SetResult(interp, "Wrong # args. spice::listTriggers",TCL_STATIC); + return TCL_ERROR; + } + + list = Tcl_NewListObj(0,NULL); + + pthread_mutex_lock(&triggerMutex); + + for(tmp=watches;tmp;tmp=tmp->next) + Tcl_ListObjAppendElement(interp,list,Tcl_NewStringObj(vectors[tmp->vector].name,strlen(vectors[tmp->vector].name))); + + pthread_mutex_unlock(&triggerMutex); + + Tcl_SetObjResult(interp,list); + + return TCL_OK; +} + + +/*******************************************************/ +/* Initialise spice and setup native methods */ +/*******************************************************/ + +#ifdef __MINGW32__ +__declspec(dllexport) +#endif +int Spice_Init(Tcl_Interp *interp) { + + if (interp == 0) return TCL_ERROR; + +#ifdef USE_TCL_STUBS + if(Tcl_InitStubs(interp, (char *)"8.1",0) == NULL) + return TCL_ERROR; +#endif + + Tcl_PkgProvide(interp, (char*)TCLSPICE_name, (char*)TCLSPICE_version); + + Tcl_Eval(interp, "namespace eval " TCLSPICE_namespace " { }"); + + { + extern void DevInit(); + int i; + char *key; + Tcl_CmdInfo infoPtr; + char buf[256]; + + ft_rawfile = NULL; + ivars( ); + + cp_in = stdin; + cp_out = stdout; + cp_err = stderr; + + /*timer*/ + init_time( ); + + /*IFsimulator struct initilised*/ + SIMinit(&nutmeginfo, &ft_sim); + + /* program name*/ + cp_program = ft_sim->simulator; + + srandom(getpid()); + + /*parameter fetcher, used in show*/ + if_getparam = spif_getparam; + + /*Command prompt stuff */ + ft_cpinit(); + + /* initilise Tk display */ + DevInit(); + + /*parrallel arch support, or not */ + ARCHme = 0; + ARCHsize = 1; + + /* init the mutex */ + pthread_mutex_init(&triggerMutex,NULL); + + /*register functions*/ + for (i = 0;(key = cp_coms[i].co_comname); i++) { + sprintf(buf,"%s%s",TCLSPICE_prefix,key); + if(Tcl_GetCommandInfo(interp,buf, &infoPtr)!= 0){ + printf("Command '%s' can not be registered!\n", buf); + }else{ + Tcl_CreateCommand(interp,buf, _tcl_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + } + } + + Tcl_CreateCommand(interp, TCLSPICE_prefix "spice_header", spice_header, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "spice_data", spice_data, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "spicetoblt", spicetoblt, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "lastVector", lastVector, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "spice", _spice_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "get_output", get_output, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "bg", _tcl_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "halt", _tcl_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "get_param", get_param, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "delta", delta, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "maxstep", maxstep, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "running", running, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_variables", plot_variables, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_get_value", plot_get_value, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_datapoints", plot_datapoints, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_title", plot_title, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_date", plot_date, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_name", plot_name, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_nvars", plot_nvars, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "registerTrigger", registerTrigger, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "popTriggerEvent", popTriggerEvent, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "unregisterTrigger", unregisterTrigger, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "listTriggers", listTriggers, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + + Tcl_LinkVar(interp, TCLSPICE_prefix "steps_completed", (char *)&steps_completed, TCL_LINK_READ_ONLY|TCL_LINK_INT); + Tcl_LinkVar(interp, TCLSPICE_prefix "blt_vnum", (char *)&blt_vnum, TCL_LINK_READ_ONLY|TCL_LINK_INT); + } + return TCL_OK; +} diff --git a/src/xspice/Makefile.am b/src/xspice/Makefile.am new file mode 100755 index 000000000..e0e1c6a10 --- /dev/null +++ b/src/xspice/Makefile.am @@ -0,0 +1,16 @@ +# Process this file with automake +CFLAGS = -g -O2 -Wall +CC = gcc +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + +EXTRA_DIST = README + +SUBDIRS = mif cm enh evt ipc idn + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir) + +MAINTAINERCLEANFILES = Makefile.in + +all: xspice.o +xspice.o: + $(COMPILE) -c xspice.c diff --git a/src/xspice/README b/src/xspice/README new file mode 100755 index 000000000..29701bd34 --- /dev/null +++ b/src/xspice/README @@ -0,0 +1,24 @@ +Spice Opus / XSpice code model support. +-------------------------------------- + +Use configure flag --enable-xspice to compile the support in, +when you run the ./configure script. +This creates a new command, "codemodel", which you can +use to load a codemodel. + +Some codemodels are included in the xspice/lib directory +with some examples in xspice/examples, compiled for linux glibc. + +Make sure the the library dir, xspice/lib, is in your LD_LIBRARY_PATH +enviromental variable, otherwise the libs will not be found! + +To create codemodels go to http://www.fe.uni-lj.si/spice/welcome.html +and download their trial version of spice opus for the codemodel toolkit! + +TODO: + Intergrate the ipc stuff from XSpice. + Create ng-spice capacity to create codemodels (a perl script) + Ngspice crashes when you try to plot a digital node + +Stefan Jones + 19/2/2002 diff --git a/src/xspice/cm/Makefile.am b/src/xspice/cm/Makefile.am new file mode 100755 index 000000000..41bcc96fe --- /dev/null +++ b/src/xspice/cm/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libcmxsp.a + +libcmxsp_a_SOURCES = \ +cm.c \ +cmevt.c \ +cmmeters.c \ +cmutil.c + + + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/spicelib/devices + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/cm/cm.c b/src/xspice/cm/cm.c new file mode 100755 index 000000000..6c5df7380 --- /dev/null +++ b/src/xspice/cm/cm.c @@ -0,0 +1,701 @@ +/* =========================================================================== +FILE CM.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models. + +INTERFACES + + cm_analog_alloc() + cm_analog_get_ptr() + cm_analog_integrate() + cm_analog_converge() + cm_analog_set_temp_bkpt() + cm_analog_set_perm_bkpt() + cm_analog_ramp_factor() + cm_analog_not_converged() + cm_analog_auto_partial() + + cm_message_get_errmsg() + cm_message_send() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include "ngspice.h" +#include "cm.h" +#include "mif.h" +#include "cktdefs.h" +//#include "util.h" + + + + + +static void cm_static_integrate(int byte_index, + double integrand, + double *integral, + double *partial); + +/* + +cm_analog_alloc() + +This function is called from code model C functions to allocate +state storage for a particular instance. It computes the number +of doubles that need to be allocated in SPICE's state storage +vectors from the number of bytes specified in it's argument and +then allocates space for the states. An index into the SPICE +state-vectors is stored in the instance's data structure along +with a ``tag'' variable supplied by the caller so that the location +of the state storage area can be found by cm_analog_get_ptr(). + +*/ + +void *cm_analog_alloc( + int tag, /* The user-specified tag for this block of memory */ + int bytes) /* The number of bytes to allocate */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_State_t *state; + + int doubles_needed; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Scan states in instance struct and see if tag has already been used */ + for(i = 0; i < here->num_state; i++) { + if(tag == here->state[i].tag) { + g_mif_info.errmsg = "ERROR - cm_analog_alloc() - Tag already used in previous call\n"; + return(NULL); + } + } + + /* Compute number of doubles needed and allocate space in ckt->CKTstates[i] */ + doubles_needed = bytes / sizeof(double) + 1; + + /* Allocate space in instance struct for this state descriptor */ + if(here->num_state == 0) { + here->num_state = 1; + here->state = (void *) MALLOC(sizeof(Mif_State_t)); + } + else { + here->num_state++; + here->state = (void *) REALLOC(here->state, + here->num_state * sizeof(Mif_State_t)); + } + + /* Fill in the members of the state descriptor struct */ + state = &(here->state[here->num_state - 1]); + state->tag = tag; + state->index = ckt->CKTnumStates; + state->doubles = doubles_needed; + state->bytes = bytes; + + + /* Add the states to the ckt->CKTstates vectors */ + ckt->CKTnumStates += doubles_needed; + for(i=0;i<=ckt->CKTmaxOrder+1;i++) { + if(ckt->CKTnumStates == doubles_needed) + ckt->CKTstates[i] = (double *) MALLOC(ckt->CKTnumStates * sizeof(double)); + else + ckt->CKTstates[i] = (double *) REALLOC(ckt->CKTstates[i], + ckt->CKTnumStates * sizeof(double)); + } + + /* Return pointer to the allocated space in state 0 */ + return( (void *) (ckt->CKTstates[0] + (ckt->CKTnumStates - doubles_needed))); + +} + + +/* +cm_analog_get_ptr() + +This function is called from code model C functions to return a +pointer to state storage allocated with cm_analog_alloc(). A tag +specified in its argument list is used to locate the state in +question. A second argument specifies whether the desired state +is for the current timestep or from a preceding timestep. The +location of the state in memory is then computed and returned. +*/ + +void *cm_analog_get_ptr( + int tag, /* The user-specified tag for this block of memory */ + int timepoint) /* The timepoint of interest - 0=current 1=previous */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_State_t *state=NULL; + + Mif_Boolean_t got_tag; + + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Scan states in instance struct and see if tag exists */ + for(got_tag = MIF_FALSE, i = 0; i < here->num_state; i++) { + if(tag == here->state[i].tag) { + state = &(here->state[i]); + got_tag = MIF_TRUE; + break; + } + } + + /* Return error if tag not found */ + if(! got_tag) { + g_mif_info.errmsg = "ERROR - cm_analog_get_ptr() - Bad tag\n"; + return(NULL); + } + + /* Return error if timepoint is not 0 or 1 */ + if((timepoint < 0) || (timepoint > 1)) { + g_mif_info.errmsg = "ERROR - cm_analog_get_ptr() - Bad timepoint\n"; + return(NULL); + } + + /* Return address of requested state in ckt->CKTstates[timepoint] vector */ + return( (void *) (ckt->CKTstates[timepoint] + state->index) ); + +} + + +/* +cm_analog_integrate() + +This function performs a numerical integration on the state +supplied in its argument list according to the integrand also +supplied in the argument list. The next value of the integral +and the partial derivative with respect to the integrand input is +returned. The integral argument must be a pointer to memory +previously allocated through a call to cm_analog_alloc(). If this is +the first call to cm_analog_integrate(), information is entered into the +instance structure to mark that the integral should be processed +by MIFtrunc and MIFconvTest. +*/ + +int cm_analog_integrate( + double integrand, /* The integrand */ + double *integral, /* The current and returned value of integral */ + double *partial) /* The partial derivative of integral wrt integrand */ +{ + + MIFinstance *here; + CKTcircuit *ckt; + + Mif_Intgr_t *intgr; + Mif_Boolean_t got_index; + + char *char_state0; + char *char_state; + + int byte_index; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Check to be sure we're in transient analysis */ + if(g_mif_info.circuit.anal_type != MIF_TRAN) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Called in non-transient analysis\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Preliminary check to be sure argument was allocated by cm_analog_alloc() */ + if(ckt->CKTnumStates <= 0) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Integral must be memory allocated by cm_analog_alloc()\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Compute byte offset from start of state0 vector */ + char_state0 = (char *) ckt->CKTstate0; + char_state = (char *) integral; + byte_index = char_state - char_state0; + + /* Check to be sure argument address is in range of state0 vector */ + if((byte_index < 0) || + (byte_index > ((ckt->CKTnumStates - 1) * sizeof(double)) ) ) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Argument must be in state vector 0\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Scan the intgr array in the instance struct to see if already exists */ + for(got_index = MIF_FALSE, i = 0; i < here->num_intgr; i++) { + if(here->intgr[i].byte_index == byte_index) { + got_index = MIF_TRUE; + } + } + + /* Report error if not found and this is not the first load pass in tran analysis */ + if((! got_index) && (! g_mif_info.circuit.anal_init)) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - New integral and not initialization pass\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* If new integral state, allocate space in instance */ + /* struct for this intgr descriptor and register it with */ + /* the cm_analog_converge() function */ + if(! got_index) { + if(here->num_intgr == 0) { + here->num_intgr = 1; + here->intgr = (void *) MALLOC(sizeof(Mif_Intgr_t)); + } + else { + here->num_intgr++; + here->intgr = (void *) REALLOC(here->intgr, + here->num_intgr * sizeof(Mif_Intgr_t)); + } + intgr = &(here->intgr[here->num_intgr - 1]); + intgr->byte_index = byte_index; + if(cm_analog_converge(integral)) { + printf("%s\n",g_mif_info.errmsg); + g_mif_info.errmsg = "ERROR - cm_analog_integrate() - Failure in cm_analog_converge() call\n"; + return(MIF_ERROR); + } + } + + /* Compute the new integral and the partial */ + cm_static_integrate(byte_index, integrand, integral, partial); + + return(MIF_OK); +} + + +/* +cm_analog_converge() + +This function registers a state variable allocated with +cm_analog_alloc() to be subjected to a convergence test at the end of +each iteration. The state variable must be a double. +Information is entered into the instance structure to mark that +the state variable should be processed by MIFconvTest. +*/ + +int cm_analog_converge( + double *state) /* The state to be converged */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_Conv_t *conv; + + char *char_state0; + char *char_state; + + int byte_index; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Preliminary check to be sure argument was allocated by cm_analog_alloc() */ + if(ckt->CKTnumStates <= 0) { + g_mif_info.errmsg = + "ERROR - cm_analog_converge() - Argument must be memory allocated by cm_analog_alloc()\n"; + return(MIF_ERROR); + } + + /* Compute byte offset from start of state0 vector */ + char_state0 = (char *) ckt->CKTstate0; + char_state = (char *) state; + byte_index = char_state - char_state0; + + /* Check to be sure argument address is in range of state0 vector */ + if((byte_index < 0) || + (byte_index > ((ckt->CKTnumStates - 1) * sizeof(double)) ) ) { + g_mif_info.errmsg = + "ERROR - cm_analog_converge() - Argument must be in state vector 0\n"; + return(MIF_ERROR); + } + + /* Scan the conv array in the instance struct to see if already registered */ + /* If so, do nothing, just return */ + for(i = 0; i < here->num_conv; i++) { + if(here->conv[i].byte_index == byte_index) + return(MIF_OK); + } + + /* Allocate space in instance struct for this conv descriptor */ + if(here->num_conv == 0) { + here->num_conv = 1; + here->conv = (void *) MALLOC(sizeof(Mif_Conv_t)); + } + else { + here->num_conv++; + here->conv = (void *) REALLOC(here->conv, + here->num_conv * sizeof(Mif_Conv_t)); + } + + /* Fill in the conv descriptor data */ + conv = &(here->conv[here->num_conv - 1]); + conv->byte_index = byte_index; + conv->last_value = 1.0e30; /* There should be a better way ... */ + + return(MIF_OK); +} + + + +/* +cm_message_get_errmsg() + +This function returns the address of an error message string set +by a call to some code model support function. +*/ + +char *cm_message_get_errmsg(void) +{ + return(g_mif_info.errmsg); +} + + + + +/* +cm_analog_set_temp_bkpt() + +This function is called by a code model C function to set a +temporary breakpoint. These temporary breakpoints remain in +effect only until the next timestep is taken. A temporary +breakpoint added with a time less than the current time, but +greater than the last successful timestep causes the simulator to +abandon the current timestep and decrease the timestep to hit the +breakpoint. A temporary breakpoint with a time greater than the +current time causes the simulator to make the breakpoint the next +timepoint if the next timestep would produce a time greater than +that of the breakpoint. +*/ + + +int cm_analog_set_temp_bkpt( + double time) /* The time of the breakpoint to be set */ +{ + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Make sure breakpoint is not prior to last accepted timepoint */ + if(time < ((ckt->CKTtime - ckt->CKTdelta) + ckt->CKTminBreak)) { + g_mif_info.errmsg = + "ERROR - cm_analog_set_temp_bkpt() - Time < last accepted timepoint\n"; + return(MIF_ERROR); + } + + /* If too close to a permanent breakpoint or the current time, discard it */ + if( (fabs(time - ckt->CKTbreaks[0]) < ckt->CKTminBreak) || + (fabs(time - ckt->CKTbreaks[1]) < ckt->CKTminBreak) || + (fabs(time - ckt->CKTtime) < ckt->CKTminBreak) ) + return(MIF_OK); + + /* If < current dynamic breakpoint, make it the current breakpoint */ + if( time < g_mif_info.breakpoint.current) + g_mif_info.breakpoint.current = time; + + return(MIF_OK); +} + + + + +/* +cm_analog_set_perm_bkpt() + +This function is called by a code model C function to set a +permanent breakpoint. These permanent breakpoints remain in +effect from the time they are introduced until the simulation +time equals or exceeds the breakpoint time. A permanent +breakpoint added with a time less than the current time, but +greater than the last successful timestep causes the simulator to +abandon the current timestep and decrease the timestep to hit the +breakpoint. A permanent breakpoint with a time greater than the +current time causes the simulator to make the breakpoint the next +timepoint if the next timestep would produce a time greater than +that of the breakpoint. +*/ + + +int cm_analog_set_perm_bkpt( + double time) /* The time of the breakpoint to be set */ +{ + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Call cm_analog_set_temp_bkpt() to force backup if less than current time */ + if(time < (ckt->CKTtime + ckt->CKTminBreak)) + return(cm_analog_set_temp_bkpt(time)); + else + CKTsetBreak(ckt,time); + + return(MIF_OK); +} + + +/* +cm_analog_ramp_factor() + +This function returns the current value of the ramp factor +associated with the ``ramptime'' option. For this option +to work best, models with analog outputs that may be non-zero at +time zero should call this function and scale their outputs +and partials by the ramp factor. +*/ + + +double cm_analog_ramp_factor(void) +{ + + CKTcircuit *ckt; + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + + /* if ramptime == 0.0, no ramptime option given, so return 1.0 */ + /* this is the most common case, so it goes first */ + if(ckt->enh->ramp.ramptime == 0.0) + return(1.0); + + /* else if not transient analysis, return 1.0 */ + else if( (!(ckt->CKTmode & MODETRANOP)) && (!(ckt->CKTmode & MODETRAN)) ) + return(1.0); + + /* else if time >= ramptime, return 1.0 */ + else if(ckt->CKTtime >= ckt->enh->ramp.ramptime) + return(1.0); + + /* else time < end of ramp, so compute and return factor based on time */ + else + return(ckt->CKTtime / ckt->enh->ramp.ramptime); +} + + +/* ************************************************************ */ + + +/* + * Copyright (c) 1985 Thomas L. Quarles + * + * This is a modified version of the function NIintegrate() + * + * Modifications are Copyright 1991 Georgia Tech Research Institute + * + */ + +static void cm_static_integrate(int byte_index, + double integrand, + double *integral, + double *partial) +{ + CKTcircuit *ckt; + + double intgr[7]; + double cur=0; + double *double_ptr; + + double ceq; + double geq; + + char *char_ptr; + + int i; + + + /* Get the address of the ckt struct from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Get integral values from current and previous timesteps */ + for(i = 0; i <= ckt->CKTorder; i++) { + char_ptr = (char *) ckt->CKTstates[i]; + char_ptr += byte_index; + double_ptr = (double *) char_ptr; + intgr[i] = *double_ptr; + } + + + /* Do what SPICE3C1 does for its implicit integration */ + + switch(ckt->CKTintegrateMethod) { + + case TRAPEZOIDAL: + + switch(ckt->CKTorder) { + + case 1: + cur = ckt->CKTag[1] * intgr[1]; + break; + + case 2: + /* WARNING - This code needs to be redone. */ + /* The correct code should rely on one previous value */ + /* of cur as done in NIintegrate() */ + cur = -0.5 * ckt->CKTag[0] * intgr[1]; + break; + } + + break; + + case GEAR: + cur = 0.0; + + switch(ckt->CKTorder) { + + case 6: + cur += ckt->CKTag[6] * intgr[6]; + /* fall through */ + case 5: + cur += ckt->CKTag[5] * intgr[5]; + /* fall through */ + case 4: + cur += ckt->CKTag[4] * intgr[4]; + /* fall through */ + case 3: + cur += ckt->CKTag[3] * intgr[3]; + /* fall through */ + case 2: + cur += ckt->CKTag[2] * intgr[2]; + /* fall through */ + case 1: + cur += ckt->CKTag[1] * intgr[1]; + break; + + } + break; + + } + + ceq = cur; + geq = ckt->CKTag[0]; + + /* WARNING: Take this out when the case 2: above is fixed */ + if((ckt->CKTintegrateMethod == TRAPEZOIDAL) && + (ckt->CKTorder == 2)) + geq *= 0.5; + + + /* The following code is equivalent to */ + /* the solution of one matrix iteration to produce the */ + /* integral value. */ + + *integral = (integrand - ceq) / geq; + *partial = 1.0 / geq; + +} + + + + + +/* +cm_analog_not_converged() + +This function tells the simulator not to allow the current +iteration to be the final iteration. It is called when +a code model performs internal limiting on one or more of +its inputs to assist convergence. +*/ + +void cm_analog_not_converged(void) +{ + (g_mif_info.ckt->CKTnoncon)++; +} + + + + +/* +cm_message_send() + +This function prints a message output from a code model, prepending +the instance name. +*/ + + +int cm_message_send( + char *msg) /* The message to output. */ +{ + MIFinstance *here; + + /* Get the address of the instance struct from g_mif_info */ + here = g_mif_info.instance; + + /* Print the name of the instance and the message */ + printf("\nInstance: %s Message: %s\n", (char *) here->MIFname, msg); + + return(0); +} + + + + + + +/* +cm_analog_auto_partial() + +This function tells the simulator to automatically compute +approximations of partial derivatives of analog outputs +with respect to analog inputs. When called from a code +model, it sets a flag in the g_mif_info structure +which tells function MIFload() and it's associated +MIFauto_partial() function to perform the necessary +calculations. +*/ + + +void cm_analog_auto_partial(void) +{ + g_mif_info.auto_partial.local = MIF_TRUE; +} + diff --git a/src/xspice/cm/cmevt.c b/src/xspice/cm/cmevt.c new file mode 100755 index 000000000..7854fa946 --- /dev/null +++ b/src/xspice/cm/cmevt.c @@ -0,0 +1,267 @@ +/* =========================================================================== +FILE CMevt.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models + that are associated with the event-driven algorithm. + +INTERFACES + + cm_event_alloc() + cm_event_get_ptr() + cm_event_queue() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "cm.h" +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + + + +/* +cm_event_alloc() + +This function is called from code model C functions to allocate +state storage for a particular event-driven +instance. It is similar to the +function cm_analog_alloc() used by analog models, but allocates states +that are rotated during event-driven 'timesteps' instead of analog +timesteps. +*/ + + +void *cm_event_alloc( + int tag, /* The user-specified tag for the memory block */ + int bytes) /* The number of bytes to be allocated */ +{ + + int inst_index; + int num_tags; + + MIFinstance *here; + CKTcircuit *ckt; + + void *ptr; + + Evt_State_Desc_t **desc_ptr; + Evt_State_Desc_t *desc; + + Evt_State_Data_t *state_data; + Evt_State_t *state; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + + /* If not initialization pass, return error */ + if(here->initialized) { + g_mif_info.errmsg = + "ERROR - cm_event_alloc() - Cannot alloc when not initialization pass\n"; + return(NULL); + } + + + /* Get pointers for fast access */ + inst_index = here->inst_index; + state_data = ckt->evt->data.state; + + + /* Scan state descriptor list to determine if tag is present and to */ + /* find the end of the list. Report error if duplicate tag */ + desc_ptr = &(state_data->desc[inst_index]); + desc = *desc_ptr; + num_tags = 1; + while(desc) { + if(desc->tag == tag) { + g_mif_info.errmsg = + "ERROR - cm_event_alloc() - Duplicate tag\n"; + return(NULL); + } + desc_ptr = &(desc->next); + desc = *desc_ptr; + num_tags++; + } + + /* Create a new state description structure at end of list */ + /* and fill in the data and update the total size */ + *desc_ptr = (void *) MALLOC(sizeof(Evt_State_Desc_t)); + desc = *desc_ptr; + desc->tag = tag; + desc->size = bytes; + desc->offset = state_data->total_size[inst_index]; + state_data->total_size[inst_index] += bytes; + + /* Create a new state structure if list starting at head is null */ + state = state_data->head[inst_index]; + if(state == NULL) { + state = (void *) MALLOC(sizeof(Evt_State_t)); + state_data->head[inst_index] = state; + } + + /* Create or enlarge the block and set the time */ + if(num_tags == 1) + state->block = MALLOC(state_data->total_size[inst_index]); + else + state->block = REALLOC(state->block, + state_data->total_size[inst_index]); + + state->step = g_mif_info.circuit.evt_step; + + + /* Return allocated memory */ + ptr = ((char *)state->block) + desc->offset; + return(ptr); +} + + + + + +/* +cm_event_get_ptr() + +This function is called from code model C functions to return a +pointer to state storage allocated with cm_event_alloc(). A tag +specified in its argument list is used to locate the state in +question. A second argument specifies whether the desired state +is for the current timestep or from a preceding timestep. The +location of the state in memory is then computed and returned. +*/ + + +void *cm_event_get_ptr( + int tag, /* The user-specified tag for the memory block */ + int timepoint) /* The timepoint - 0=current, 1=previous */ +{ + + int i; + int inst_index; + + MIFinstance *here; + CKTcircuit *ckt; + + void *ptr; + + Evt_State_Desc_t *desc; + + Evt_State_Data_t *state_data; + Evt_State_t *state; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + + /* If initialization pass, return error */ + if((! here->initialized) && (timepoint > 0)) { + g_mif_info.errmsg = + "ERROR - cm_event_get_ptr() - Cannot get_ptr(tag,1) during initialization pass\n"; + return(NULL); + } + + /* Get pointers for fast access */ + inst_index = here->inst_index; + state_data = ckt->evt->data.state; + + /* Scan state descriptor list to find the descriptor for this tag. */ + /* Report error if tag not found */ + desc = state_data->desc[inst_index]; + while(desc) { + if(desc->tag == tag) + break; + desc = desc->next; + } + + if(desc == NULL) { + g_mif_info.errmsg = + "ERROR - cm_event_get_ptr() - Specified tag not found\n"; + return(NULL); + } + + /* Get the state pointer from the current array */ + state = *(state_data->tail[inst_index]); + + /* Backup the specified number of timesteps */ + for(i = 0; i < timepoint; i++) + if(state->prev) + state = state->prev; + + /* Return pointer */ + ptr = ((char *) state->block) + desc->offset; + return(ptr); +} + + + + +/* +cm_event_queue() + +This function queues an event for an instance participating +in the event-driven algorithm. +*/ + + +int cm_event_queue( + double time) /* The time of the event to be queued */ +{ + + MIFinstance *here; + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* If breakpoint time <= current event time, return error */ + if(time <= g_mif_info.circuit.evt_step) { + g_mif_info.errmsg = + "ERROR - cm_event_queue() - Event time cannot be <= current time\n"; + return(MIF_ERROR); + } + + /* Add the event time to the inst queue */ + EVTqueue_inst(ckt, here->inst_index, g_mif_info.circuit.evt_step, + time); + + return(MIF_OK); +} diff --git a/src/xspice/cm/cmmeters.c b/src/xspice/cm/cmmeters.c new file mode 100755 index 000000000..d49ed794a --- /dev/null +++ b/src/xspice/cm/cmmeters.c @@ -0,0 +1,314 @@ +/* =========================================================================== +FILE CMmeters.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from code models. + These functions are primarily designed for use by the + "cmeter" and "lmeter" models provided in the XSPICE + code model library. + +INTERFACES + + cm_netlist_get_c() + cm_netlist_get_l() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include "ngspice.h" +#include "cm.h" +#include "mif.h" + +#include "cktdefs.h" + +#include "mifdefs.h" +#include "cap/capdefs.h" +#include "ind/inddefs.h" +#include "vsrc/vsrcdefs.h" +#include "inpdefs.h" + + + +/* +cm_netlist_get_c() + +This is a special function designed for use with the c_meter +model. It returns the parallel combination of the capacitance +connected to the first port on the instance. +*/ + +double cm_netlist_get_c() +{ + CKTcircuit *ckt; + + MIFinstance *cmeter_inst; + CAPinstance *cap_inst; + VSRCinstance *vsrc_inst; + + CAPmodel *cap_head; + CAPmodel *cap_model; + VSRCmodel *vsrc_head; + VSRCmodel *vsrc_model; + + int cap_type; + int vsrc_type; + + int cmeter_node; + int vsrc_node; + + double c; + + + /* Get the circuit data structure and current instance */ + ckt = g_mif_info.ckt; + cmeter_inst = g_mif_info.instance; + + /* Get internal node number for positive node of cmeter input */ + cmeter_node = cmeter_inst->conn[0]->port[0]->smp_data.pos_node; + + /* Initialize total capacitance value to zero */ + c = 0.0; + + + /* ****************************************************** */ + /* Look for capacitors connected directly to cmeter input */ + /* ****************************************************** */ + + /* Get the head of the list of capacitor models in the circuit */ + cap_type = INPtypelook("Capacitor"); + if(cap_type < 0) { + printf("\nERROR - Capacitor type not supported in this binary\n"); + return(0); + } + cap_head = (CAPmodel *) ckt->CKThead[cap_type]; + + /* Scan through all capacitor instances and add in values */ + /* of any capacitors connected to cmeter input */ + + for(cap_model = cap_head; cap_model; cap_model = cap_model->CAPnextModel) { + for(cap_inst = cap_model->CAPinstances; + cap_inst; + cap_inst = cap_inst->CAPnextInstance) { + if((cmeter_node == cap_inst->CAPposNode) || + (cmeter_node == cap_inst->CAPnegNode)) { + c += cap_inst->CAPcapac; + } + } + } + + + /* ***************************************************************** */ + /* Look for capacitors connected through zero-valued voltage sources */ + /* ***************************************************************** */ + + /* Get the head of the list of voltage source models in the circuit */ + vsrc_type = INPtypelook("Vsource"); + if(vsrc_type < 0) { + printf("\nERROR - Vsource type not supported in this binary\n"); + return(0); + } + vsrc_head = (VSRCmodel *) ckt->CKThead[vsrc_type]; + + /* Scan through all voltage source instances and add in values */ + /* of any capacitors connected to cmeter input through voltage source */ + + for(vsrc_model = vsrc_head; vsrc_model; vsrc_model = vsrc_model->VSRCnextModel) { + for(vsrc_inst = vsrc_model->VSRCinstances; + vsrc_inst; + vsrc_inst = vsrc_inst->VSRCnextInstance) { + + /* Skip to next if not DC source with value = 0.0 */ + if((vsrc_inst->VSRCfunctionType != 0) || + (vsrc_inst->VSRCdcValue != 0.0)) + continue; + + /* See if voltage source is connected to cmeter input */ + /* If so, get other node voltage source is connected to */ + /* If not, skip to next source */ + if(cmeter_node == vsrc_inst->VSRCposNode) + vsrc_node = vsrc_inst->VSRCnegNode; + else if(cmeter_node == vsrc_inst->VSRCnegNode) + vsrc_node = vsrc_inst->VSRCposNode; + else + continue; + + + /* Scan through all capacitor instances and add in values */ + /* of any capacitors connected to the voltage source node */ + + for(cap_model = cap_head; cap_model; cap_model = cap_model->CAPnextModel) { + for(cap_inst = cap_model->CAPinstances; + cap_inst; + cap_inst = cap_inst->CAPnextInstance) { + if((vsrc_node == cap_inst->CAPposNode) || + (vsrc_node == cap_inst->CAPnegNode)) { + c += cap_inst->CAPcapac; + } + } + } + + + } /* end for all vsrc instances */ + } /* end for all vsrc models */ + + + /* Return the total capacitance value */ + return(c); +} + + + + +/* +cm_netlist_get_l() + +This is a special function designed for use with the l_meter +model. It returns the equivalent value of inductance +connected to the first port on the instance. +*/ + + +double cm_netlist_get_l() +{ + CKTcircuit *ckt; + + MIFinstance *lmeter_inst; + INDinstance *ind_inst; + VSRCinstance *vsrc_inst; + + INDmodel *ind_head; + INDmodel *ind_model; + VSRCmodel *vsrc_head; + VSRCmodel *vsrc_model; + + int ind_type; + int vsrc_type; + + int lmeter_node; + int vsrc_node; + + double l; + + + /* Get the circuit data structure and current instance */ + ckt = g_mif_info.ckt; + lmeter_inst = g_mif_info.instance; + + /* Get internal node number for positive node of lmeter input */ + lmeter_node = lmeter_inst->conn[0]->port[0]->smp_data.pos_node; + + /* Initialize total inductance to infinity */ + l = 1.0e12; + + + /* ****************************************************** */ + /* Look for inductors connected directly to lmeter input */ + /* ****************************************************** */ + + /* Get the head of the list of inductor models in the circuit */ + ind_type = INPtypelook("Inductor"); + if(ind_type < 0) { + printf("\nERROR - Inductor type not supported in this binary\n"); + return(0); + } + ind_head = (INDmodel *) ckt->CKThead[ind_type]; + + /* Scan through all inductor instances and add in values */ + /* of any inductors connected to lmeter input */ + + for(ind_model = ind_head; ind_model; ind_model = ind_model->INDnextModel) { + for(ind_inst = ind_model->INDinstances; + ind_inst; + ind_inst = ind_inst->INDnextInstance) { + if((lmeter_node == ind_inst->INDposNode) || + (lmeter_node == ind_inst->INDnegNode)) { + l = 1.0 / ( (1.0 / l) + (1.0 / ind_inst->INDinduct) ); + } + } + } + + + /* ***************************************************************** */ + /* Look for inductors connected through zero-valued voltage sources */ + /* ***************************************************************** */ + + /* Get the head of the list of voltage source models in the circuit */ + vsrc_type = INPtypelook("Vsource"); + if(vsrc_type < 0) { + printf("\nERROR - Vsource type not supported in this binary\n"); + return(0); + } + vsrc_head = (VSRCmodel *) ckt->CKThead[vsrc_type]; + + /* Scan through all voltage source instances and add in values */ + /* of any inductors connected to lmeter input through voltage source */ + + for(vsrc_model = vsrc_head; vsrc_model; vsrc_model = vsrc_model->VSRCnextModel) { + for(vsrc_inst = vsrc_model->VSRCinstances; + vsrc_inst; + vsrc_inst = vsrc_inst->VSRCnextInstance) { + + /* Skip to next if not DC source with value = 0.0 */ + if((vsrc_inst->VSRCfunctionType != 0) || + (vsrc_inst->VSRCdcValue != 0.0)) + continue; + + /* See if voltage source is connected to lmeter input */ + /* If so, get other node voltage source is connected to */ + /* If not, skip to next source */ + if(lmeter_node == vsrc_inst->VSRCposNode) + vsrc_node = vsrc_inst->VSRCnegNode; + else if(lmeter_node == vsrc_inst->VSRCnegNode) + vsrc_node = vsrc_inst->VSRCposNode; + else + continue; + + + /* Scan through all inductor instances and add in values */ + /* of any inductors connected to the voltage source node */ + + for(ind_model = ind_head; ind_model; ind_model = ind_model->INDnextModel) { + for(ind_inst = ind_model->INDinstances; + ind_inst; + ind_inst = ind_inst->INDnextInstance) { + if((vsrc_node == ind_inst->INDposNode) || + (vsrc_node == ind_inst->INDnegNode)) { + l = 1.0 / ( (1.0 / l) + (1.0 / ind_inst->INDinduct) ); + } + } + } + + + } /* end for all vsrc instances */ + } /* end for all vsrc models */ + + + /* Return the total capacitance value */ + return(l); +} + + diff --git a/src/xspice/cm/cmutil.c b/src/xspice/cm/cmutil.c new file mode 100755 index 000000000..f0cfe8303 --- /dev/null +++ b/src/xspice/cm/cmutil.c @@ -0,0 +1,523 @@ +/* =========================================================================== +FILE CMutil.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Jeff Murray + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models. + These functions were written to support code models in the + XSPICE library, but may be useful in general. + +INTERFACES + + cm_smooth_corner() + cm_smooth_discontinuity() + cm_smooth_pwl() + + cm_climit_fcn() + + cm_complex_set() + cm_complex_add() + cm_complex_subtract() + cm_complex_multiply() + cm_complex_divide() + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include +#include +#include "cm.h" + +/* Corner Smoothing Function ************************************ +* * +* The following function smooths the transition between two * +* slopes into a quadratic (parabolic) curve. The calling * +* function passes an x,y coordinate representing the * +* "breakpoint", a smoothing domain value (d), and the slopes at * +* both endpoints, and the x value itself. The situation is * +* shown below: B C * +* A |<-d->| ^ y * +* ---------*-----* | | * +* lower_slope-^ |<-d->|\ | | * +* \ | | * +* \ | *------>x * +* At Ax * +*****************************************************************/ + +void cm_smooth_discontinuity( + double x_input, /* The x value at which to compute y */ + double x_lower, /* The x value of the lower corner */ + double y_lower, /* The y value of the lower corner */ + double x_upper, /* The x value of the upper corner */ + double y_upper, /* The y value of the upper corner */ + double *y_output, /* The computed smoothed y value */ + double *dy_dx) /* The partial of y wrt x */ +{ + double x_center,y_center,a,b,c,center_slope; + + + /* Derive x_center, y_center & center_slope values */ + x_center = (x_upper + x_lower) / 2.0; + y_center = (y_upper + y_lower) / 2.0; + center_slope = 2.0 * (y_upper - y_lower) / (x_upper - x_lower); + + + if (x_input < x_lower) { /* x_input @ lower level */ + *y_output = y_lower; + *dy_dx = 0.0; + } + else { + if (x_input < x_center) { /* x_input in lower transition */ + a = center_slope / (x_upper - x_lower); + b = center_slope - 2.0 * a * x_center; + c = y_center - a * x_center * x_center - b * x_center; + *y_output = a * x_input * x_input + b * x_input + c; + *dy_dx = 2.0 * a * x_input + b; + } + else { /* x_input in upper transition */ + if (x_input < x_upper) { + a = -center_slope / (x_upper - x_lower); + b = -2.0 * a * x_upper; + c = y_upper - a * x_upper * x_upper - b * x_upper; + *y_output = a * x_input * x_input + b * x_input + c; + *dy_dx = 2.0 * a * x_input + b; + } + else { /* x_input @ upper level */ + *y_output = y_upper; + *dy_dx = 0.0; + } + } + } +} + + + +/* Controlled Limiter Function (modified CLIMIT) */ + +/* +This is a special function created for use with the CLIMIT +controlled limiter model. +*/ + +void cm_climit_fcn( + double in, /* The input value */ + double in_offset, /* The input offset */ + double cntl_upper, /* The upper control input value */ + double cntl_lower, /* The lower control input value */ + double lower_delta, /* The delta from control to limit value */ + double upper_delta, /* The delta from control to limit value */ + double limit_range, /* The limiting range */ + double gain, /* The gain from input to output */ + int percent, /* The fraction vs. absolute range flag */ + double *out_final, /* The output value */ + double *pout_pin_final, /* The partial of output wrt input */ + double *pout_pcntl_lower_final, /* The partial of output wrt lower control input */ + double *pout_pcntl_upper_final) /* The partial of output wrt upper control input */ +{ + +/* Define error message string constants */ + +char *climit_range_error = "\n**** ERROR ****\n* CLIMIT function linear range less than zero. *\n"; + + +double threshold_upper,threshold_lower,linear_range, + out_lower_limit,out_upper_limit,limited_out, + out,pout_pin,pout_pcntl_lower,pout_pcntl_upper,junk; + + /* Find Upper & Lower Limits */ + + out_lower_limit = cntl_lower + lower_delta; + out_upper_limit = cntl_upper - upper_delta; + + + if (percent == TRUE) /* Set range to absolute value */ + limit_range = limit_range * + (out_upper_limit - out_lower_limit); + + + + threshold_upper = out_upper_limit - /* Set Upper Threshold */ + limit_range; + threshold_lower = out_lower_limit + /* Set Lower Threshold */ + limit_range; + linear_range = threshold_upper - threshold_lower; + + + /* Test the linear region & make sure there IS one... */ + if (linear_range < 0.0) { + printf("%s\n",climit_range_error); +/* limited_out = 0.0; + pout_pin = 0.0; + pout_pcntl_lower = 0.0; + pout_pcntl_upper = 0.0; + return; +*/ } + + /* Compute Un-Limited Output */ + out = gain * (in_offset + in); + + + if (out < threshold_lower) { /* Limit Out @ Lower Bound */ + + pout_pcntl_upper= 0.0; + + if (out > (out_lower_limit - limit_range)) { /* Parabolic */ + cm_smooth_corner(out,out_lower_limit,out_lower_limit, + limit_range,0.0,1.0,&limited_out, + &pout_pin); + pout_pin = gain * pout_pin; + cm_smooth_discontinuity(out,out_lower_limit,1.0,threshold_lower, + 0.0,&pout_pcntl_lower,&junk); + } + else { /* Hard-Limited Region */ + limited_out = out_lower_limit; + pout_pin = 0.0; + pout_pcntl_lower = 1.0; + } + } + else { + if (out > threshold_upper) { /* Limit Out @ Upper Bound */ + + pout_pcntl_lower= 0.0; + + if (out < (out_upper_limit+limit_range)) { /* Parabolic */ + cm_smooth_corner(out,out_upper_limit,out_upper_limit, + limit_range,1.0,0.0,&limited_out, + &pout_pin); + pout_pin = gain * pout_pin; + cm_smooth_discontinuity(out,threshold_upper,0.0,out_upper_limit, + 1.0,&pout_pcntl_upper,&junk); + } + else { /* Hard-Limited Region */ + limited_out = out_upper_limit; + pout_pin = 0.0; + pout_pcntl_upper = 1.0; + } + } + else { /* No Limiting Needed */ + limited_out = out; + pout_pin = gain; + pout_pcntl_lower = 0.0; + pout_pcntl_upper = 0.0; + } + } + + + *out_final = limited_out; + *pout_pin_final = pout_pin; + *pout_pcntl_lower_final = pout_pcntl_lower; + *pout_pcntl_upper_final = pout_pcntl_upper; + +} + +/**** End Controlled Limiter Function ****/ + +/*=============================================================================*/ + + +/* Piecewise Linear Smoothing Function ********************* +* The following is a transfer curve function which * +* accepts as input an "x" value, and returns a "y" * +* value. The transfer characteristic is a smoothed * +* piece-wise linear curve described by *x and *y array * +* coordinate pairs. * +* * +* Created 8/14/91 * +* Last Modified 8/14/91 J.P.Murray * +***********************************************************/ + +/*********************************************************** +* * +* ^ x[4] * +* x[1] | * * +* | midpoint /|\ * +* | | / \ * +* | V | / | \ * +* *----*----* \ * +* midpoint /| | | \ * +* | / || * <- midpoint * +* V/ | |x[3] \ * +* <-----------*------------O------------\-------------> * +* | / | \ | | * +* / | \ * +* |/ | \| | * +* * | *-----*---> * +* /| | x[5] x[6] * +* / | * +* / x[0] | * +* / | * +* / | * +* / | * +* V * +* * +***********************************************************/ + +/*********************************************************** +* * +* Note that for the cm_smooth_pwl function, the arguments * +* are as listed below: * +* * +* * +* double x_input; input * * +* double *x; pointer to the x-coordinate * +* array * * +* double *y; pointer to the y-coordinate * +* array * * +* int size; size of the arrays * +* * +* double input_domain; smoothing range * * +* double dout_din; partial derivative of the * +* output w.r.t. the input * * +* * +***********************************************************/ + + +double cm_smooth_pwl(double x_input, double *x, double *y, int size, + double input_domain, double *dout_din) +{ + + int i; /* generic loop counter index */ + + double lower_seg; /* x segment below which input resides */ + double upper_seg; /* x segment above which the input resides */ + double lower_slope; /* slope of the lower segment */ + double upper_slope; /* slope of the upper segment */ + double out; /* output */ + double threshold_lower; /* value below which the output begins smoothing */ + double threshold_upper; /* value above which the output begins smoothing */ + + + /* char *limit_error="\n***ERROR***\nViolation of 50% rule in breakpoints!\n";*/ + + + + + + /* Determine segment boundaries within which x_input resides */ + + if (x_input <= (*(x+1) + *x)/2.0) {/*** x_input below lowest midpoint ***/ + *dout_din = (*(y+1) - *y)/(*(x+1) - *x); + out = *y + (x_input - *x) * *dout_din; + } + else { + if (x_input >= (*(x+size-2) + *(x+size-1))/2.0) { + /*** x_input above highest midpoint ***/ + *dout_din = (*(y+size-1) - *(y+size-2)) / + (*(x+size-1) - *(x+size-2)); + out = *(y+size-1) + (x_input - *(x+size-1)) * *dout_din; + } + else { /*** x_input within bounds of end midpoints... ***/ + /*** must determine position progressively & then ***/ + /*** calculate required output. ***/ + + for (i=1; i + +SUMMARY + + This file contains routines used for general enhancements made + to the Berkeley SPICE3 core. + +INTERFACES + + ENHreport_conv_prob() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/*=== INCLUDE FILES ===*/ + + +#include +#include "enh.h" + +/* +ENHreport_conv_prob() + +Report convergence problem messages from nodes, branch currents, +or instances. This function is setup to allow providing the SI +with information identifying the type of convergence problem. +For now, it simply writes to stdout. +*/ + + +void ENHreport_conv_prob( + Enh_Conv_Source_t type, /* node, branch, or instance */ + char *name, /* the name of the node/branch/instance */ + char *msg) /* an optional message */ +{ + + char *type_str; + char *msg_str; + + /* Convert the type enum to a string for printing */ + switch(type) { + + case ENH_ANALOG_NODE: + case ENH_EVENT_NODE: + type_str = "node"; + break; + + case ENH_ANALOG_BRANCH: + type_str = "branch current"; + break; + + case ENH_ANALOG_INSTANCE: + case ENH_EVENT_INSTANCE: + case ENH_HYBRID_INSTANCE: + type_str = "instance"; + break; + + default: + printf("\nERROR: Internal error in ENHreport_conv_prob - impossible type\n"); + return; + } + + /* Check for msg == NULL and turn into null string */ + if(msg) + msg_str = msg; + else + msg_str = ""; + + /* Print the convergence problem report */ + printf("\nWARNING: Convergence problems at %s (%s). %s\n", + type_str, name, msg_str); + +} /* ENHreport_conv_prob */ + diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c new file mode 100755 index 000000000..a76ce2ea0 --- /dev/null +++ b/src/xspice/enh/enhtrans.c @@ -0,0 +1,437 @@ +/* =========================================================================== +FILE ENHtranslate_poly.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used by the simulator in + calling the internal "poly" code model to substitute for + SPICE 2G6 style poly sources found in the input deck. + +INTERFACES + + ENHtranslate_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/*=== FUNCTION PROTOTYPES ===*/ + +//void free(void *); //ka removed compiler error +/* int atoi(char *); */ + + +/*=== INCLUDE FILES ===*/ + + +/* #include "prefix.h" */ + +#include "ngspice.h" +//#include "misc.h" + +#include "fteinp.h" +#include "enh.h" +#include "cpdefs.h" +#include "ftedefs.h" +#include "mifproto.h" + +/* #include "suffix.h" */ + + +/*=== FUNCTION PROTOTYPES ===*/ + +static int needs_translating(char *card); +static int count_tokens(char *card); +static char *translate(char *orig_card, char **inst_card, + char **mod_card); +static int get_poly_dimension(char *card); + +// added as a quick bug fix, a lot of standard models have a linear poly(1) which +// fails in this code, Kevin Aylward April 15th 2000 +char * (*FPConvertSpicePoly1ToBsource)(char *card); // this is so I can use the MFC class libary +/* +ENHtranslate_poly() + +Translate all 2G6 style polynomial controlled sources in the deck +to new polynomial controlled source code model syntax. +*/ + + +struct line * ENHtranslate_poly( + struct line *deck) /* Linked list of lines in input deck */ +{ + struct line *d; + struct line *l1; + struct line *l2; + + char *card; + int poly_dimension; + char *buff; + + + /* Iterate through each card in the deck and translate as needed */ + for(d = deck; d; d = d->li_next) + { + + /* If doesn't need to be translated, continue to next card */ + if(! needs_translating(d->li_line)) + continue; + +// Start added as a quick fix to a xspice translation bug in poly(1) code +// Kevin Aylward April 15th 2000, fuck knows where it is + poly_dimension = get_poly_dimension(d->li_line); + + if(poly_dimension == 1)// + { + buff = (FPConvertSpicePoly1ToBsource)(d->li_line); + + if(buff) + { + FREE(d->li_line); + + d->li_line = buff; + } + + continue; + } +// End added as a quick fix to a xspice translation bug in poly(1) code +// Kevin Aylward April 15th 2000 + + + /* Create two new line structs and splice into deck */ +/* l1 = alloc(line); */ /* jgroves */ +/* l2 = alloc(line); */ /* jgroves */ + l1 = alloc(struct line); + l2 = alloc(struct line); + l2->li_next = d->li_next; + l1->li_next = l2; + d->li_next = l1; + + /* Create the translated cards */ + d->li_error = translate(d->li_line, &(l1->li_line), &(l2->li_line)); + + /* Comment out the original line */ + card = (void *) MALLOC(strlen(d->li_line) + 2); + strcpy(card,"*"); + strcat(card, d->li_line); + d->li_line = card; + + /* Advance deck pointer to last line added */ + d = l2; + } + + /* Return head of deck */ + return(deck); + +} /* ENHtranslate_poly */ + + + +/* +needs_translating() + +Test to see if card needs translating. Return true if card defines +an e,f,g, or h controlled source and has too many tokens to be +a simple linear dependent source. Otherwise return false. +*/ + + +static int needs_translating( + char *card) /* the card text to check */ +{ + + switch(*card) { + + case 'e': + case 'g': + if(count_tokens(card) <=6) + return(0); + else + return(1); + + case 'f': + case 'h': + if(count_tokens(card) <= 5) + return(0); + else + return(1); + + default: + return(0); + } + +} /* needs_translating */ + + + + +/* +count_tokens() + +Count and return the number of tokens on the card. +*/ + + +static int count_tokens( + char *card) /* the card text on which to count tokens */ +{ + int i; + + /* Get and count tokens until end of line reached */ + for(i = 0; *card != '\0'; i++) + txfree(MIFgettok(&card)); + + return(i); + +} /* count_tokens */ + + + +/* +translate() + +Do the syntax translation of the 2G6 source to the new code model syntax. +*/ + +static char *translate( + char *orig_card, /* the original untranslated card */ + char **inst_card, /* the instance card created by the translation */ + char **mod_card) /* the model card created by the translation */ +{ + int dim; + int num_tokens; + + int num_conns; + int num_coefs; + int inst_card_len; + int mod_card_len; + + int i; + + char type; + + char *name; + char **out_conn; + char **in_conn; + char **coef; + + char *card; + + + /* Get the first character into local storage for checking type */ + type = *orig_card; + + /* Count the number of tokens for use in parsing */ + num_tokens = count_tokens(orig_card); + + /* Determine the dimension of the poly source */ + dim = get_poly_dimension(orig_card); + if(dim <= 0) + return("ERROR - Argument to poly() is not an integer\n"); + + /* Compute number of input connections based on type and dimension */ + switch(type) { + case 'e': + case 'g': + num_conns = 2 * dim; + break; + + default: + num_conns = dim; + } + + /* Compute number of coefficients. Return error if less than one. */ + if(dim == 1) + num_coefs = num_tokens - num_conns - 3; + else + num_coefs = num_tokens - num_conns - 5; + + if(num_coefs < 1) + return("ERROR - Number of connections differs from poly dimension\n"); + + /* Split card into name, output connections, input connections, */ + /* and coefficients */ + + card = orig_card; + name = MIFgettok(&card); + + out_conn = (void *) MALLOC(2 * sizeof(char *)); + for(i = 0; i < 2; i++) + out_conn[i] = MIFgettok(&card); + + if(dim > 1) + for(i = 0; i < 2; i++) + txfree(MIFgettok(&card)); + + in_conn = (void *) MALLOC(num_conns * sizeof(char *)); + for(i = 0; i < num_conns; i++) + in_conn[i] = MIFgettok(&card); + + coef = (void *) MALLOC(num_coefs * sizeof(char *)); + for(i = 0; i < num_coefs; i++) + coef[i] = MIFgettok(&card); + + /* Compute the size needed for the new cards to be created */ + /* Allow a fair amount of extra space for connection types, etc. */ + /* to be safe... */ + + inst_card_len = 50; + inst_card_len += 2 * (strlen(name) + 1); + for(i = 0; i < 2; i++) + inst_card_len += strlen(out_conn[i]) + 1; + for(i = 0; i < num_conns; i++) + inst_card_len += strlen(in_conn[i]) + 1; + + mod_card_len = 50; + mod_card_len += strlen(name) + 1; + for(i = 0; i < num_coefs; i++) + mod_card_len += strlen(coef[i]) + 1; + + /* Allocate space for the cards and write them into the strings */ + + *inst_card = (void *) MALLOC(inst_card_len); + *mod_card = (void *) MALLOC(mod_card_len); + + strcpy(*inst_card, "a$poly$"); + sprintf(*inst_card + strlen(*inst_card), "%s ", name); + + if((type == 'e') || (type == 'g')) + sprintf(*inst_card + strlen(*inst_card), "%%vd [ "); + else + sprintf(*inst_card + strlen(*inst_card), "%%vnam [ "); + + for(i = 0; i < num_conns; i++) + sprintf(*inst_card + strlen(*inst_card), "%s ", in_conn[i]); + + sprintf(*inst_card + strlen(*inst_card), "] "); + + if((type == 'e') || (type == 'h')) + sprintf(*inst_card + strlen(*inst_card), "%%vd "); + else + sprintf(*inst_card + strlen(*inst_card), "%%id "); + + for(i = 0; i < 2; i++) + sprintf(*inst_card + strlen(*inst_card), "%s ", out_conn[i]); + + sprintf(*inst_card + strlen(*inst_card), "a$poly$%s", name); + + + sprintf(*mod_card, ".model a$poly$%s poly coef = [ ", name); + for(i = 0; i < num_coefs; i++) + sprintf(*mod_card + strlen(*mod_card), "%s ", coef[i]); + sprintf(*mod_card + strlen(*mod_card), "]"); + + + /* Free the temporary space */ + FREE(name); + name = NULL; + + for(i = 0; i < 2; i++) + { + FREE(out_conn[i]); + + out_conn[i] = NULL; + } + + FREE(out_conn); + + out_conn = NULL; + + for(i = 0; i < num_conns; i++) + { + FREE(in_conn[i]); + + in_conn[i] = NULL; + } + + FREE(in_conn); + + in_conn = NULL; + + for(i = 0; i < num_coefs; i++) + { + FREE(coef[i]); + coef[i] = NULL; + } + + FREE(coef); + + coef = NULL; + + /* Return NULL to indicate no error */ + return(NULL); + +} /* translate */ + + +/* +get_poly_dimension() + +Get the poly source dimension from the token immediately following +the 'poly' if any. If 'poly' is not present, return 1. If poly is +present and token following is a valid integer, return it. Else +return 0. +*/ + + +static int get_poly_dimension( + char *card) /* the card text */ +{ + + int i; + int dim; + char *tok; + + + /* Skip over name and output connections */ + for(i = 0; i < 3; i++) + txfree(MIFgettok(&card)); + + /* Check the next token to see if it is "poly" */ + /* If not, return a dimension of 1 */ + tok = MIFgettok(&card); + if(strcmp(tok, "poly")) + { + FREE(tok); + + tok = NULL; + + return(1); + } + + FREE(tok); + + /* Must have been "poly", so next line must be a number */ + /* Try to convert it. If successful, return the number */ + /* else, return 0 to indicate an error... */ + tok = MIFgettok(&card); + dim = atoi(tok); + FREE(tok); + + return(dim); + +} /* get_poly_dimension */ + diff --git a/src/xspice/evt/Makefile.am b/src/xspice/evt/Makefile.am new file mode 100755 index 000000000..d03788259 --- /dev/null +++ b/src/xspice/evt/Makefile.am @@ -0,0 +1,27 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libevtxsp.a + +libevtxsp_a_SOURCES = \ +evtaccept.c \ +evtcall_hybrids.c \ +evtdump.c \ +evtiter.c \ +evtnext_time.c \ +evtop.c \ +evtprint.c \ +evtsetup.c \ +evtbackup.c \ +evtdeque.c \ +evtinit.c \ +evtload.c \ +evtnode_copy.c \ +evtplot.c \ +evtqueue.c \ +evttermi.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/evt/evtaccept.c b/src/xspice/evt/evtaccept.c new file mode 100755 index 000000000..16973ed44 --- /dev/null +++ b/src/xspice/evt/evtaccept.c @@ -0,0 +1,170 @@ +/*============================================================================ +FILE EVTaccept.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function called at the end of a + successful (accepted) analog timepoint. It saves pointers + to the states of the queues and data at this accepted time. + +INTERFACES + + void EVTaccept(CKTcircuit *ckt, double time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/*=== INCLUDE FILES ===*/ + +#include +#include +#include "cktdefs.h" + +#include "mif.h" +#include "evt.h" + + + +/* +EVTaccept() + +This function is called at the end of a successful (accepted) +analog timepoint. It saves pointers to the states of the +queues and data at this accepted time. +*/ + + + +void EVTaccept( + CKTcircuit *ckt, /* main circuit struct */ + double time) /* time at which analog soln was accepted */ +{ + + int i; + int index; + int num_modified; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Node_Data_t *node_data; + Evt_State_Data_t *state_data; + Evt_Msg_Data_t *msg_data; + + + /* Exit if no event instances */ + if(ckt->evt->counts.num_insts == 0) + return; + + /* Get often used pointers */ + inst_queue = &(ckt->evt->queue.inst); + output_queue = &(ckt->evt->queue.output); + + node_data = ckt->evt->data.node; + state_data = ckt->evt->data.state; + msg_data = ckt->evt->data.msg; + + + /* Process the inst queue */ + num_modified = inst_queue->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the inst modified */ + index = inst_queue->modified_index[i]; + /* Update last_step for this index */ + inst_queue->last_step[index] = inst_queue->current[index]; + /* Reset the modified flag */ + inst_queue->modified[index] = MIF_FALSE; + } + /* Record the new last_time and reset number modified to zero */ + inst_queue->last_time = time; + inst_queue->num_modified = 0; + + + /* Process the output queue */ + num_modified = output_queue->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the output modified */ + index = output_queue->modified_index[i]; + /* Update last_step for this index */ + output_queue->last_step[index] = output_queue->current[index]; + /* Reset the modified flag */ + output_queue->modified[index] = MIF_FALSE; + } + /* Record the new last_time and reset number modified to zero */ + output_queue->last_time = time; + output_queue->num_modified = 0; + + + /* Process the node data */ + num_modified = node_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the node modified */ + index = node_data->modified_index[i]; + /* Update last_step for this index */ + node_data->last_step[index] = node_data->tail[index]; + /* Reset the modified flag */ + node_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + node_data->num_modified = 0; + + + /* Process the state data */ + num_modified = state_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the state modified */ + index = state_data->modified_index[i]; + /* Update last_step for this index */ + state_data->last_step[index] = state_data->tail[index]; + /* Reset the modified flag */ + state_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + state_data->num_modified = 0; + + + /* Process the msg data */ + num_modified = msg_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the msg modified */ + index = msg_data->modified_index[i]; + /* Update last_step for this index */ + msg_data->last_step[index] = msg_data->tail[index]; + /* Reset the modified flag */ + msg_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + msg_data->num_modified = 0; + +} /* EVTaccept */ + + diff --git a/src/xspice/evt/evtbackup.c b/src/xspice/evt/evtbackup.c new file mode 100755 index 000000000..6ddc24cc6 --- /dev/null +++ b/src/xspice/evt/evtbackup.c @@ -0,0 +1,645 @@ +/*============================================================================ +FILE EVTbackup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function that resets the queues and data + structures to their state at the new analog simulation time specified + following the rejection of an analog timestep by the DCtran routine. + +INTERFACES + + void EVTbackup(CKTcircuit *ckt, double new_time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/*=== INCLUDE FILES ===*/ +#include +#include "ngspice.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + +/*=== FUNCTION PROTOTYPES ===*/ + + +static void EVTbackup_node_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_state_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_msg_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_inst_queue(CKTcircuit *ckt, double new_time); +static void EVTbackup_output_queue(CKTcircuit *ckt, double new_time); + + + +/* +EVTbackup() + +This function resets the queues and data structures to their state +at the new analog simulation time specified. The algorithms in this file +assume the following timestep coordination between +analog and event-driven algorithms: + + while(not end of analysis) { + + while (next event time <= next analog time) { + do event solution with call_type = event_driven + if any instance set analog breakpoint < next analog time + set next analog time to breakpoint + } + + do analog timestep solution with call_type = analog + call all hybrid models with call_type = event_driven + + if(analog solution doesn't converge) + Call EVTbackup + else + Call EVTaccept + } +*/ + + +void EVTbackup( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + + /* Backup the node data */ + EVTbackup_node_data(ckt, new_time); + + /* Backup the state data */ + EVTbackup_state_data(ckt, new_time); + + /* Backup the msg data */ + EVTbackup_msg_data(ckt, new_time); + + /* Backup the inst queue */ + EVTbackup_inst_queue(ckt, new_time); + + /* Backup the output queue */ + EVTbackup_output_queue(ckt, new_time); + + /* Record statistics */ + (ckt->evt->data.statistics->tran_time_backups)++; + +} /* EVTbackup */ + + + + +/* +EVTbackup_node_data() + +Reset the node structure data. +*/ + + +static void EVTbackup_node_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int node_index; + + Evt_Node_Info_t **node_table; + Evt_Node_Data_t *node_data; + Evt_Node_t **node_ptr; + Evt_Node_t *node; + Evt_Node_t *from_node; + Evt_Node_t *to_node; + Evt_Node_t *head; + Evt_Node_t *tail; + Evt_Node_t *free_head; + + /* Get pointers for quick access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = node_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the needed node and udn indexes */ + node_index = node_data->modified_index[i]; + + /* Scan data for this node from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + node_ptr = node_data->last_step[node_index]; + node = *node_ptr; + while(1) { + if((node->next == NULL) || (node->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = node->next; + if(head) { + tail = *(node_data->tail[node_index]); + free_head = node_data->free[node_index]; + node_data->free[node_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + node_data->tail[node_index] = node_ptr; + node->next = NULL; + + break; + } + node_ptr = &(node->next); + node = node->next; + } + + /* Copy data from the location at tail to rhs and rhsold */ + from_node = *(node_data->tail[node_index]); + to_node = &(node_data->rhs[node_index]); + EVTnode_copy(ckt, node_index, from_node, &to_node); + to_node = &(node_data->rhsold[node_index]); + EVTnode_copy(ckt, node_index, from_node, &to_node); + + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + node_index = node_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(node_data->last_step[node_index]))->next == NULL) { + node_data->modified[node_index] = MIF_FALSE; + (node_data->num_modified)--; + } + /* else, keep the index */ + else { + node_data->modified_index[j] = node_data->modified_index[i]; + j++; + } + } + +} /* EVTbackup_node_data */ + + + +/* +EVTbackup_state_data() + +Reset the state structure data. +*/ + + +static void EVTbackup_state_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + int i; + int j; + + int num_modified; + int inst_index; + + Evt_State_Data_t *state_data; + + Evt_State_t **state_ptr; + Evt_State_t *state; + Evt_State_t *head; + Evt_State_t *tail; + Evt_State_t *free_head; + + /* Get pointers for quick access */ + state_data = ckt->evt->data.state; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = state_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the inst index */ + inst_index = state_data->modified_index[i]; + + /* Scan data for this inst from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + state_ptr = state_data->last_step[inst_index]; + state = *state_ptr; + while(1) { + if((state->next == NULL) || (state->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = state->next; + if(head) { + tail = *(state_data->tail[inst_index]); + free_head = state_data->free[inst_index]; + state_data->free[inst_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + state_data->tail[inst_index] = state_ptr; + state->next = NULL; + + break; + } + state_ptr = &(state->next); + state = state->next; + } + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + inst_index = state_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(state_data->last_step[inst_index]))->next == NULL) { + state_data->modified[inst_index] = MIF_FALSE; + (state_data->num_modified)--; + } + /* else, keep the index */ + else { + state_data->modified_index[j] = state_data->modified_index[i]; + j++; + } + } + +} /* EVTbackup_state_data */ + + + +/* +EVTbackup_msg_data() + +Backup the message data. +*/ + + +static void EVTbackup_msg_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + int i; + int j; + + int num_modified; + int port_index; + + Evt_Msg_Data_t *msg_data; + + Evt_Msg_t **msg_ptr; + Evt_Msg_t *msg; + Evt_Msg_t *head; + Evt_Msg_t *tail; + Evt_Msg_t *free_head; + + /* Get pointers for quick access */ + msg_data = ckt->evt->data.msg; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = msg_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the port index */ + port_index = msg_data->modified_index[i]; + + /* Scan data for this port from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + msg_ptr = msg_data->last_step[port_index]; + msg = *msg_ptr; + while(1) { + if((msg->next == NULL) || (msg->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = msg->next; + if(head) { + tail = *(msg_data->tail[port_index]); + free_head = msg_data->free[port_index]; + msg_data->free[port_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + msg_data->tail[port_index] = msg_ptr; + msg->next = NULL; + + break; + } + msg_ptr = &(msg->next); + msg = msg->next; + } + + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + port_index = msg_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(msg_data->last_step[port_index]))->next == NULL) { + msg_data->modified[port_index] = MIF_FALSE; + (msg_data->num_modified)--; + } + /* else, keep the index */ + else { + msg_data->modified_index[j] = msg_data->modified_index[i]; + j++; + } + } +} + + + +/* +EVTbackup_inst_queue() + +Backup data in inst queue. +*/ + + +static void EVTbackup_inst_queue( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int num_pending; + int inst_index; + + Evt_Inst_Queue_t *inst_queue; + + Evt_Inst_Event_t **inst_ptr; + Evt_Inst_Event_t *inst; + + double next_time; + double event_time; + + + /* Get pointers for quick access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Loop through list of indexes modified since last accepted timepoint */ + /* and remove events with posted time > new_time */ + num_modified = inst_queue->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the inst index */ + inst_index = inst_queue->modified_index[i]; + + /* Scan forward from last_step and cut out data with posted time */ + /* > new_time and add it to the free list */ + + inst_ptr = inst_queue->last_step[inst_index]; + inst = *inst_ptr; + + while(inst) { + if(inst->posted_time > new_time) { + *inst_ptr = inst->next; + inst->next = inst_queue->free[inst_index]; + inst_queue->free[inst_index] = inst; + inst = *inst_ptr; + } + else { + inst_ptr = &(inst->next); + inst = *inst_ptr; + } + } + + /* Scan forward from last_step and set current to first */ + /* event with event_time > new_time */ + + inst_ptr = inst_queue->last_step[inst_index]; + inst = *inst_ptr; + + while(inst) { + if(inst->event_time > new_time) + break; + inst_ptr = &((*inst_ptr)->next); + inst = *inst_ptr; + } + inst_queue->current[inst_index] = inst_ptr; + } + + /* Add set of items modified to set of items pending before updating the */ + /* pending list because things may have been pulled from the pending list */ + for(i = 0; i < num_modified; i++) { + j = inst_queue->modified_index[i]; + if(! inst_queue->pending[j]) { + inst_queue->pending[j] = MIF_TRUE; + inst_queue->pending_index[(inst_queue->num_pending)++] = j; + } + } + + /* Update the pending list and the next time by seeing if there */ + /* is anything at the location pointed to by current */ + next_time = 1e30; + num_pending = inst_queue->num_pending; + for(i = 0, j = 0; i < num_pending; i++) { + inst_index = inst_queue->pending_index[i]; + inst = *(inst_queue->current[inst_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! inst) { + inst_queue->pending[inst_index] = MIF_FALSE; + (inst_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + inst_queue->pending_index[j] = inst_queue->pending_index[i]; + j++; + event_time = inst->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + inst_queue->next_time = next_time; + + /* Update the modified list by looking for any queued events */ + /* with posted time > last_time */ + for(i = 0, j = 0; i < num_modified; i++) { + + inst_index = inst_queue->modified_index[i]; + inst = *(inst_queue->last_step[inst_index]); + + while(inst) { + if(inst->posted_time > inst_queue->last_time) + break; + inst = inst->next; + } + + if(! inst) { + inst_queue->modified[inst_index] = MIF_FALSE; + (inst_queue->num_modified)--; + } + else { + inst_queue->modified_index[j] = inst_queue->modified_index[i]; + j++; + } + } +} + + + + + +/* +EVTbackup_output_queue() + +Backup data in output queue. +*/ + + + +static void EVTbackup_output_queue( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int num_pending; + + int output_index; + + Evt_Output_Queue_t *output_queue; + + Evt_Output_Event_t **output_ptr; + Evt_Output_Event_t *output; + + double next_time; + double event_time; + + + /* Get pointers for quick access */ + output_queue = &(ckt->evt->queue.output); + + /* Loop through list of indexes modified since last accepted timepoint */ + /* and remove events with posted time > new_time */ + num_modified = output_queue->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the output index */ + output_index = output_queue->modified_index[i]; + + /* Scan forward from last_step and cut out data with posted time */ + /* > new_time and add it to the free list */ + /* Also, unremove anything with removed time > new_time */ + + output_ptr = output_queue->last_step[output_index]; + output = *output_ptr; + + while(output) { + if(output->posted_time > new_time) { + *output_ptr = output->next; + output->next = output_queue->free[output_index]; + output_queue->free[output_index] = output; + output = *output_ptr; + } + else { + if(output->removed && (output->removed_time > new_time)) + output->removed = MIF_FALSE; + output_ptr = &(output->next); + output = *output_ptr; + } + } + + /* Scan forward from last_step and set current to first */ + /* event with event_time > new_time */ + + output_ptr = output_queue->last_step[output_index]; + output = *output_ptr; + + while(output) { + if(output->event_time > new_time) + break; + output_ptr = &((*output_ptr)->next); + output = *output_ptr; + } + output_queue->current[output_index] = output_ptr; + } + + /* Add set of items modified to set of items pending before updating the */ + /* pending list because things may have been pulled from the pending list */ + for(i = 0; i < num_modified; i++) { + j = output_queue->modified_index[i]; + if(! output_queue->pending[j]) { + output_queue->pending[j] = MIF_TRUE; + output_queue->pending_index[(output_queue->num_pending)++] = j; + } + } + + /* Update the pending list and the next time by seeing if there */ + /* is anything at the location pointed to by current */ + next_time = 1e30; + num_pending = output_queue->num_pending; + for(i = 0, j = 0; i < num_pending; i++) { + output_index = output_queue->pending_index[i]; + output = *(output_queue->current[output_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! output) { + output_queue->pending[output_index] = MIF_FALSE; + (output_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + output_queue->pending_index[j] = output_queue->pending_index[i]; + j++; + event_time = output->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + output_queue->next_time = next_time; + + /* Update the modified list by looking for any queued events */ + /* with posted time > last_time */ + for(i = 0, j = 0; i < num_modified; i++) { + + output_index = output_queue->modified_index[i]; + output = *(output_queue->last_step[output_index]); + + while(output) { + if(output->posted_time > output_queue->last_time) + break; + output = output->next; + } + + if(! output) { + output_queue->modified[output_index] = MIF_FALSE; + (output_queue->num_modified)--; + } + else { + output_queue->modified_index[j] = output_queue->modified_index[i]; + j++; + } + } +} diff --git a/src/xspice/evt/evtcall_hybrids.c b/src/xspice/evt/evtcall_hybrids.c new file mode 100755 index 000000000..f44146090 --- /dev/null +++ b/src/xspice/evt/evtcall_hybrids.c @@ -0,0 +1,78 @@ +/*============================================================================ +FILE EVTcall_hybrids.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTcall_hybrids which calls all models + which have both analog and event-driven ports. 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. + +INTERFACES + + void EVTcall_hybrids(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include "ngspice.h" +#include "cktdefs.h" + +#include "evt.h" + +#include "evtproto.h" + + +/* +EVTcall_hybrids + +This function calls all the hybrid instances. It is called following +the successful evaluation of an analog iteration. +*/ + + +void EVTcall_hybrids( + CKTcircuit *ckt) /* the main circuit structure */ +{ + + int i; + int num_hybrids; + + int *hybrid_index; + + + /* Get needed data for fast access */ + num_hybrids = ckt->evt->counts.num_hybrids; + hybrid_index = ckt->evt->info.hybrid_index; + + /* Call EVTload for all hybrids */ + for(i = 0; i < num_hybrids; i++) + EVTload(ckt, hybrid_index[i]); + +} diff --git a/src/xspice/evt/evtdeque.c b/src/xspice/evt/evtdeque.c new file mode 100755 index 000000000..3f70ce18b --- /dev/null +++ b/src/xspice/evt/evtdeque.c @@ -0,0 +1,366 @@ +/*============================================================================ +FILE EVTdequeue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTdequeue which removes any items on the + output and instance queues with event times matching the specified + simulation time. + +INTERFACES + + void EVTdequeue(CKTcircuit *ckt, double time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + +static void EVTdequeue_output(CKTcircuit *ckt, double time); +static void EVTdequeue_inst(CKTcircuit *ckt, double time); + +static void EVTprocess_output( + CKTcircuit *ckt, + int output_index, + void *value); + + +/* +EVTdequeue + +This function removes any items on the output and instance queues +with event times matching the specified simulation time. EVTiter +is then called to determine which instances need to be called. + +*/ + + +void EVTdequeue( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + /* Take all items on output queue with matching time */ + /* and set changed flags in output queue */ + EVTdequeue_output(ckt, time); + + /* Take all items on inst queue with matching time */ + /* and set to_call flags in inst queue */ + EVTdequeue_inst(ckt, time); + +} + + +/* +EVTdequeue_output + +This function de-queues output events with times matching the +specified time. +*/ + +static void EVTdequeue_output( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + int i; + int j; + + int num_pending; + int index; + int output_index; + + double next_time; + double event_time; + + Evt_Output_Queue_t *output_queue; + + Evt_Output_Event_t *output; + Evt_Output_Event_t **output_ptr; + + + /* Get pointers for fast access */ + output_queue = &(ckt->evt->queue.output); + + /* Exit if nothing pending on output queue or if next_time */ + /* != specified time */ + if(output_queue->num_pending == 0) + return; + if(output_queue->next_time != time) + return; + + /* Scan the list of outputs pending */ + num_pending = output_queue->num_pending; + for(i = 0; i < num_pending; i++) { + + /* Get the index of the output */ + index = output_queue->pending_index[i]; + + /* Get pointer to next event in queue at this index */ + output = *(output_queue->current[index]); + + /* If event time does not match current time, skip */ + if(output->event_time != time) + continue; + + /* It must match, so pull the event from the queue and process it */ + EVTprocess_output(ckt, index, output->value); + + /* Move current to point to next non-removed item in list */ + output_ptr = &(output->next); + output = *output_ptr; + while(output) { + if(! output->removed) + break; + output_ptr = &(output->next); + output = *output_ptr; + } + output_queue->current[index] = output_ptr; + + /* Mark that this index in the queue has been modified */ + if(! output_queue->modified[index]) { + output_queue->modified[index] = MIF_TRUE; + output_queue->modified_index[(output_queue->num_modified)++] = index; + } + } + + + /* Update/compact the pending list and update the next_time */ + next_time = 1e30; + for(i = 0, j = 0; i < num_pending; i++) { + output_index = output_queue->pending_index[i]; + output = *(output_queue->current[output_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! output) { + output_queue->pending[output_index] = MIF_FALSE; + (output_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + output_queue->pending_index[j] = output_queue->pending_index[i]; + j++; + event_time = output->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + output_queue->next_time = next_time; + + +} + + + +/* +EVTdequeue_inst + +This function de-queues instance events with times matching the +specified time. +*/ + + +void EVTdequeue_inst( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + int i; + int j; + + int num_pending; + int index; + int inst_index; + + double next_time; + double event_time; + + Evt_Inst_Queue_t *inst_queue; + + Evt_Inst_Event_t *inst; + + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Exit if nothing pending on inst queue or if next_time */ + /* != specified time */ + if(inst_queue->num_pending == 0) + return; + if(inst_queue->next_time != time) + return; + + /* Scan the list of insts pending */ + num_pending = inst_queue->num_pending; + for(i = 0; i < num_pending; i++) { + + /* Get the index of the inst */ + index = inst_queue->pending_index[i]; + + /* Get pointer to next event in queue at this index */ + inst = *(inst_queue->current[index]); + + /* If event time does not match current time, skip */ + if(inst->event_time != time) + continue; + + /* It must match, so pull the event from the queue and process it */ + if(! inst_queue->to_call[index]) { + inst_queue->to_call[index] = MIF_TRUE; + inst_queue->to_call_index[(inst_queue->num_to_call)++] = + index; + } + + /* Move current to point to next item in list */ + inst_queue->current[index] = &(inst->next); + + /* Mark that this index in the queue has been modified */ + if(! inst_queue->modified[index]) { + inst_queue->modified[index] = MIF_TRUE; + inst_queue->modified_index[(inst_queue->num_modified)++] = index; + } + } + + + /* Update/compact the pending list and update the next_time */ + next_time = 1e30; + for(i = 0, j = 0; i < num_pending; i++) { + inst_index = inst_queue->pending_index[i]; + inst = *(inst_queue->current[inst_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! inst) { + inst_queue->pending[inst_index] = MIF_FALSE; + (inst_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + inst_queue->pending_index[j] = inst_queue->pending_index[i]; + j++; + event_time = inst->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + inst_queue->next_time = next_time; + + + +} + + + +/* +EVTprocess_output + +This function processes a specified output after it is pulled +from the queue. +*/ + + +static void EVTprocess_output( + CKTcircuit *ckt, /* The circuit structure */ + int output_index, /* The index of the output to process */ + void *value) /* The output value */ +{ + + int num_outputs; + int node_index; + int udn_index; + int output_subindex; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Output_Queue_t *output_queue; + + Mif_Boolean_t equal; + + + output_table = ckt->evt->info.output_table; + node_table = ckt->evt->info.node_table; + + node_index = output_table[output_index]->node_index; + num_outputs = node_table[node_index]->num_outputs; + udn_index = node_table[node_index]->udn_index; + + rhs = ckt->evt->data.node->rhs; + rhsold = ckt->evt->data.node->rhsold; + + /* Determine if output is different from rhsold value */ + /* and copy it to rhs AND rhsold if so */ + /* This is somewhat inefficient, but that's the way */ + /* we have setup the structures (rhs and rhsold must match)... */ + if(num_outputs > 1) { + output_subindex = output_table[output_index]->output_subindex; + (*(g_evt_udn_info[udn_index]->compare)) + (value, + rhsold[node_index].output_value[output_subindex], + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhs[node_index].output_value[output_subindex]); + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhsold[node_index].output_value[output_subindex]); + } + } + else { + (*(g_evt_udn_info[udn_index]->compare)) + (value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhsold[node_index].node_value); + } + } + + /* If different, put in changed list of output queue */ + if(! equal) { + output_queue = &(ckt->evt->queue.output); + if(! output_queue->changed[output_index]) { + output_queue->changed[output_index] = MIF_TRUE; + output_queue->changed_index[(output_queue->num_changed)++] = + output_index; + } + } +} diff --git a/src/xspice/evt/evtdump.c b/src/xspice/evt/evtdump.c new file mode 100755 index 000000000..6bb016791 --- /dev/null +++ b/src/xspice/evt/evtdump.c @@ -0,0 +1,350 @@ +/*============================================================================ +FILE EVTdump.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 6/15/92 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used + to send event-driven node results data to the IPC channel when + the simulator is used with CAE software. + +INTERFACES + + EVTdump() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtproto.h" +#include "evtudn.h" + +#include "ipc.h" +#include "ipctiein.h" +#include "ipcproto.h" + + + +static void EVTsend_line( + int ipc_index, /* The index used in the dictionary */ + double step, /* The analysis step */ + void *node_value, /* The node value */ + int udn_index); /* The user-defined node index */ + + + +/* +EVTdump + +This function is called to send event-driven node data to the IPC +channel. A ``mode'' argument determines how the data is located in +the event data structure and what data is sent. + +If the mode is DCOP, then this is necessarily the first call to +the function. In this case, the set of event-driven nodes is +scanned to determine which should be sent. Only nodes that are +not inside subcircuits are sent. Next, the function sends +a ``dictionary'' of node names/types vs. node indexes. +Finally, the function sends the DC operating point solutions +for the event-driven nodes in the dictionary. + +If the mode is DCTRCURVE, it is assumed that the function has +already been called with mode = DCOP. The function scans the solution +vector and sends data for any nodes that have changed. + +If the mode is TRAN, it is assumed that the function has already +been called once with mode = DCOP. The function scans the +event data for nodes that have changed since the last accepted +analog timepoint and sends the new data. + +Note: This function must be called BEFORE calling EVTop_save or +EVTaccept() so that the state of the node data structure will allow +it to determine what has changed. +*/ + + +typedef struct evtdump_s { + Mif_Boolean_t send; /* True if this node should be sent */ + int ipc_index; /* Index for this node in dict sent to CAE system */ + char *node_name_str; /* Node name */ + char *udn_type_str; /* UDN type */ +} evtdump_dict_t; + + + +void EVTdump( + CKTcircuit *ckt, /* The circuit structure */ + Ipc_Anal_t mode, /* The analysis mode for this call */ + double step) /* The sweep step for a DCTRCURVE analysis, or */ + /* 0.0 for DCOP and TRAN */ +{ + static evtdump_dict_t *node_dict = NULL; + static int num_send_nodes; + + int i; + int j; + int num_nodes; + int num_modified; + int index; + + char *name; + int name_len; + + Mif_Boolean_t firstcall; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhsold; + Evt_Node_t **head; + Evt_Node_t *here; + + Evt_Node_Info_t **node_table; + + char buff[10000]; + + Mif_Boolean_t equal; + + + /* Return immediately if IPC is not enabled */ + if(! g_ipc.enabled) + return; + + /* Get number of event-driven nodes */ + num_nodes = ckt->evt->counts.num_nodes; + + /* Exit immediately if no event-driven nodes in circuit */ + if(num_nodes <= 0) + return; + + + /* Get pointers for fast access to event data */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + rhsold = node_data->rhsold; + head = node_data->head; + + + /* Determine if this is the first call */ + if(node_dict == NULL) + firstcall = MIF_TRUE; + else + firstcall = MIF_FALSE; + + + /* If this is the first call, get the dictionary info */ + if(firstcall) { + + /* Allocate local data structure used to process nodes */ + node_dict = (void *) MALLOC(num_nodes * sizeof(evtdump_dict_t)); + + /* Loop through all nodes to determine which nodes should be sent. */ + /* Only nodes not within subcircuits qualify. */ + + num_send_nodes = 0; + for(i = 0; i < num_nodes; i++) { + + /* Get the name of the node. */ + name = node_table[i]->name; + + /* If name is in a subcircuit, mark that node should not be sent */ + /* and continue to next node. */ + name_len = strlen(name); + for(j = 0; j < name_len; j++) { + if(name[j] == ':') + break; + } + if(j < name_len) { + node_dict[i].send = MIF_FALSE; + continue; + } + + /* Otherwise, fill in info in dictionary. */ + node_dict[i].send = MIF_TRUE; + node_dict[i].ipc_index = num_send_nodes; + node_dict[i].node_name_str = name; + node_dict[i].udn_type_str = g_evt_udn_info[node_table[i]->udn_index]->name; + + /* Increment the count of nodes to be sent. */ + num_send_nodes++; + } /* end for */ + } /* end if first call */ + + + /* Exit if there are no nodes to be sent */ + if(num_send_nodes <= 0) + return; + + + /* If this is the first call, send the dictionary */ + if(firstcall) { + ipc_send_evtdict_prefix(); + for(i = 0; i < num_nodes; i++) { + if(node_dict[i].send) { + sprintf(buff, "%d %s %s", node_dict[i].ipc_index, + node_dict[i].node_name_str, + node_dict[i].udn_type_str); + ipc_send_line(buff); + } + } + ipc_send_evtdict_suffix(); + } + + /* If this is the first call, send the operating point solution */ + /* and return. */ + if(firstcall) { + ipc_send_evtdata_prefix(); + for(i = 0; i < num_nodes; i++) { + if(node_dict[i].send) { + EVTsend_line(node_dict[i].ipc_index, + step, + rhsold[i].node_value, + node_table[i]->udn_index); + } + } + ipc_send_evtdata_suffix(); + return; + } + + /* Otherwise, this must be DCTRCURVE or TRAN mode and we need to */ + /* send only stuff that has changed since the last call. */ + /* The determination of what to send is modeled after code in */ + /* EVTop_save() for DCTRCURVE and EVTaccept() for TRAN. */ + + if(mode == IPC_ANAL_DCTRCURVE) { + /* Send data prefix */ + ipc_send_evtdata_prefix(); + /* Loop through event nodes */ + for(i = 0; i < num_nodes; i++) { + /* If dictionary indicates this node should be sent */ + if(node_dict[i].send) { + /* Locate end of node data */ + here = head[i]; + for(;;) { + if(here->next) + here = here->next; + else + break; + } + /* Compare entry at end of list to rhsold */ + (*(g_evt_udn_info[node_table[i]->udn_index]->compare)) ( + rhsold[i].node_value, + here->node_value, + &equal); + /* If value in rhsold is different, send it */ + if(!equal) { + EVTsend_line(node_dict[i].ipc_index, + step, + rhsold[i].node_value, + node_table[i]->udn_index); + } + } + } + /* Send data suffix and return */ + ipc_send_evtdata_suffix(); + return; + } + + + if(mode == IPC_ANAL_TRAN) { + /* Send data prefix */ + ipc_send_evtdata_prefix(); + /* Loop through list of nodes modified since last time */ + num_modified = node_data->num_modified; + for(i = 0; i < num_modified; i++) { + /* Get the index of the node modified */ + index = node_data->modified_index[i]; + /* If dictionary indicates this node should be sent */ + if(node_dict[index].send) { + /* Scan through new events and send the data for each event */ + here = *(node_data->last_step[index]); + while((here = here->next)) { + EVTsend_line(node_dict[index].ipc_index, + here->step, + here->node_value, + node_table[index]->udn_index); + } + } + } + /* Send data suffix and return */ + ipc_send_evtdata_suffix(); + return; + } + +} + + + +/* +EVTsend_line + +This function formats the event node data and sends it to the IPC channel. +*/ + + +static void EVTsend_line( + int ipc_index, /* The index used in the dictionary */ + double step, /* The analysis step */ + void *node_value, /* The node value */ + int udn_index) /* The user-defined node index */ +{ + double dvalue; + char *svalue; + void *pvalue; + int len; + + /* Get the data to send */ + if(g_evt_udn_info[udn_index]->plot_val) + (*(g_evt_udn_info[udn_index]->plot_val)) (node_value, "", &dvalue); + else + dvalue = 0.0; + + if(g_evt_udn_info[udn_index]->print_val) + (*(g_evt_udn_info[udn_index]->print_val)) (node_value, "", &svalue); + else + svalue = ""; + + if(g_evt_udn_info[udn_index]->ipc_val) + (*(g_evt_udn_info[udn_index]->ipc_val)) (node_value, &pvalue, &len); + else { + pvalue = NULL; + len = 0; + } + + /* Send it to the IPC channel */ + ipc_send_event(ipc_index, step, dvalue, svalue, pvalue, len); +} diff --git a/src/xspice/evt/evtinit.c b/src/xspice/evt/evtinit.c new file mode 100755 index 000000000..1c59e01b1 --- /dev/null +++ b/src/xspice/evt/evtinit.c @@ -0,0 +1,437 @@ +/*============================================================================ +FILE EVTinit.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTinit which allocates and initializes + evt structure elements after the number of instances, nodes, etc. + have been determined in parsing during INPpas2. EVTinit also checks + to be sure no nodes have been used for both analog and event-driven + algorithms simultaneously. + +INTERFACES + + int EVTinit(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "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); +static int EVTinit_limits(CKTcircuit *ckt); + + + +/* Allocation macro with built-in check for out-of-memory */ +/* Adapted from SPICE 3C1 code in CKTsetup.c */ +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + + +/* +EVTinit + +Allocate and initialize additional evt structure elements now that +we can determine the number of instances, nodes, etc. + +Also check to be sure that no nodes have been used in both event-driven +and analog domains. + +In this version, we also report an error if there are no hybrids in the +circuit. This restriction may be removed in the future to allow the +simulator to be used with digital only circuits... +*/ + + +int EVTinit( + CKTcircuit *ckt) /* the circuit structure */ +{ + + 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) + return(OK); + + /* Count the number of hybrids and hybrid outputs */ + err = EVTcount_hybrids(ckt); + 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 = MALLOC(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) + return(err); + + /* Setup queues */ + err = EVTinit_queue(ckt); + if(err) + return(err); + + /* Initialize limits */ + err = EVTinit_limits(ckt); + if(err) + return(err); + + /* Note: Options were initialized in CKTinit so that INPpas2 */ + /* could set values according to .options cards in deck. The */ + /* structure 'jobs' will be setup immediately prior to each */ + /* simulation job. The results data structure is also */ + /* allocated immediately prior to each simulation job. */ + + /* Return */ + return(OK); +} + + +/* +EVTcount_hybrids + +Count the number of hybrids and the number of outputs on all hybrids. +*/ + +static int EVTcount_hybrids( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int j; + + int num_hybrids; + int num_hybrid_outputs; + int num_conn; + int num_port; + + MIFinstance *fast; + + Evt_Inst_Info_t *inst; + + + /* Count number of hybrids and hybrid outputs in the inst list */ + /* created during parsing. Note: other counts */ + /* are created during parsing, but these were */ + /* too difficult to do until now... */ + num_hybrids = 0; + num_hybrid_outputs = 0; + inst = ckt->evt->info.inst_list; + while(inst) { + fast = inst->inst_ptr; + if(fast->analog && fast->event_driven) { + num_hybrids++; + num_conn = fast->num_conn; + for(i = 0; i < num_conn; i++) { + if((! fast->conn[i]->is_null) && (fast->conn[i]->is_output)) { + num_port = fast->conn[i]->size; + for(j = 0; j < num_port; j++) + if(! fast->conn[i]->port[j]->is_null) + num_hybrid_outputs++; + } + } + } + inst = inst->next; + } + ckt->evt->counts.num_hybrids = num_hybrids; + ckt->evt->counts.num_hybrid_outputs = num_hybrid_outputs; + + return(OK); +} + + +/* +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 = MALLOC(strlen(err_prefix) + strlen(event_node->name) + + strlen(err_collide) + 1); + sprintf(errMsg, "%s%s%s", err_prefix, + event_node->name, + err_collide); + + fprintf(stdout,errMsg); + + return(E_PRIVATE); + } + analog_node = analog_node->next; + } + event_node = event_node->next; + } + + /* Return */ + return(OK); +} + + +/* +EVTinit_info + +This function creates the ``info'' pointer tables used in the +event-driven circuit representation. These arrays allow faster +access to data associated with instances, nodes, ports, and +outputs than could be provided by having to scan the linked-list +representations created during parsing. +*/ + + +static int EVTinit_info( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int i; + int j; + + int num_insts; + int num_nodes; + int num_ports; + int num_outputs; + + Evt_Inst_Info_t *inst; + Evt_Node_Info_t *node; + Evt_Port_Info_t *port; + Evt_Output_Info_t *output; + + Evt_Inst_Info_t **inst_table; + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + Evt_Output_Info_t **output_table; + + int *hybrid_index; + + int num_hybrids; + + + /* Allocate and initialize table of inst pointers */ + num_insts = ckt->evt->counts.num_insts; + CKALLOC(inst_table, num_insts, void *) + inst = ckt->evt->info.inst_list; + for(i = 0; i < num_insts; i++) { + inst_table[i] = inst; + inst = inst->next; + } + ckt->evt->info.inst_table = inst_table; + + /* Allocate and initialize table of node pointers */ + num_nodes = ckt->evt->counts.num_nodes; + CKALLOC(node_table, num_nodes, void *) + node = ckt->evt->info.node_list; + for(i = 0; i < num_nodes; i++) { + node_table[i] = node; + node = node->next; + } + ckt->evt->info.node_table = node_table; + + /* Allocate and initialize table of port pointers */ + num_ports = ckt->evt->counts.num_ports; + CKALLOC(port_table, num_ports, void *) + port = ckt->evt->info.port_list; + for(i = 0; i < num_ports; i++) { + port_table[i] = port; + port = port->next; + } + ckt->evt->info.port_table = port_table; + + /* Allocate and initialize table of output pointers */ + num_outputs = ckt->evt->counts.num_outputs; + CKALLOC(output_table, num_outputs, void *) + output = ckt->evt->info.output_list; + for(i = 0; i < num_outputs; i++) { + output_table[i] = output; + output = output->next; + } + ckt->evt->info.output_table = output_table; + + + /* Allocate and create table of indexes into inst_table for hybrids */ + num_hybrids = ckt->evt->counts.num_hybrids; + CKALLOC(hybrid_index, num_hybrids, int) + for(i = 0, j = 0; i < num_insts; i++) { + if(inst_table[i]->inst_ptr->analog) + hybrid_index[j++] = i; + } + ckt->evt->info.hybrid_index = hybrid_index; + + + /* Return */ + return(OK); +} + + + +/* +EVTinit_queue + +This function prepares the event-driven queues for simulation. +*/ + + +static int EVTinit_queue( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int num_insts; + int num_nodes; + int num_outputs; + + Evt_Inst_Queue_t *inst_queue; + Evt_Node_Queue_t *node_queue; + Evt_Output_Queue_t *output_queue; + + + /* Allocate elements in the inst queue */ + + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + CKALLOC(inst_queue->head, num_insts, void *) + CKALLOC(inst_queue->current, num_insts, void *) + CKALLOC(inst_queue->last_step, num_insts, void *) + CKALLOC(inst_queue->free, num_insts, void *) + CKALLOC(inst_queue->modified_index, num_insts, int) + CKALLOC(inst_queue->modified, num_insts, Mif_Boolean_t) + CKALLOC(inst_queue->pending_index, num_insts, int) + CKALLOC(inst_queue->pending, num_insts, Mif_Boolean_t) + CKALLOC(inst_queue->to_call_index, num_insts, int) + CKALLOC(inst_queue->to_call, num_insts, Mif_Boolean_t) + + + /* Allocate elements in the node queue */ + + num_nodes = ckt->evt->counts.num_nodes; + node_queue = &(ckt->evt->queue.node); + + CKALLOC(node_queue->to_eval_index, num_nodes, int) + CKALLOC(node_queue->to_eval, num_nodes, Mif_Boolean_t) + CKALLOC(node_queue->changed_index, num_nodes, int) + CKALLOC(node_queue->changed, num_nodes, Mif_Boolean_t) + + + /* Allocate elements in the output queue */ + + num_outputs = ckt->evt->counts.num_outputs; + output_queue = &(ckt->evt->queue.output); + + CKALLOC(output_queue->head, num_outputs, void *) + CKALLOC(output_queue->current, num_outputs, void *) + CKALLOC(output_queue->last_step, num_outputs, void *) + CKALLOC(output_queue->free, num_outputs, void *) + CKALLOC(output_queue->modified_index, num_outputs, int) + CKALLOC(output_queue->modified, num_outputs, Mif_Boolean_t) + CKALLOC(output_queue->pending_index, num_outputs, int) + CKALLOC(output_queue->pending, num_outputs, Mif_Boolean_t) + CKALLOC(output_queue->changed_index, num_outputs, int) + CKALLOC(output_queue->changed, num_outputs, Mif_Boolean_t) + + + /* Return */ + return(OK); +} + + + + +/* +EVTinit_limits + +This function initializes the iteration limits applicable to the +event-driven algorithm. +*/ + + +static int EVTinit_limits( + CKTcircuit *ckt) /* the circuit structure */ +{ + + /* Set maximum number of event load calls within a single event iteration */ + /* to the number of event outputs. This should allow for the */ + /* maximum possible number of events that can trickle through any */ + /* circuit that does not contain loops. */ + + ckt->evt->limits.max_event_passes = ckt->evt->counts.num_outputs + 1; + + + /* Set maximum number of alternations between analog and event-driven */ + /* iterations to the number of event outputs on hybrids. */ + + ckt->evt->limits.max_op_alternations = ckt->evt->counts.num_hybrid_outputs + 1; + + + /* Return */ + return(OK); +} diff --git a/src/xspice/evt/evtiter.c b/src/xspice/evt/evtiter.c new file mode 100755 index 000000000..d53f0a651 --- /dev/null +++ b/src/xspice/evt/evtiter.c @@ -0,0 +1,300 @@ +/*============================================================================ +FILE EVTiter.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTiter which iterates through + event-driven outputs and instances until the outputs no longer change. + + +INTERFACES + + int EVTiter(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + +/* +EVTiter + +This function iterates through event-driven outputs and instances +until the outputs no longer change. The general algorithm used +is: + +Do: + + Scan list of changed outputs + Put items on the node queue 'to_eval' list for + each changed output. + + Scan list of changed nodes + Resolve nodes with multiple outputs posted. + Create inverted state for nodes with attached + inverted inputs. + Put items on the instance queue 'to_call' list + for each changed node. + If transient analysis, put state of the node + into the node data structure. + + Scan instance to_call list + Call EVTload for each instance on list. + +While there are changed outputs + +*/ + + +int EVTiter( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int i; + int num_changed; + + int num_to_eval; + int num_to_call; + + int output_index; + /* int output_subindex;*/ + int inst_index; + int node_index; + int port_index; + + int num_outputs; + int udn_index; + + int passes; + + Evt_Ckt_Data_t *evt; + + Evt_Output_Queue_t *output_queue; + Evt_Node_Queue_t *node_queue; + Evt_Inst_Queue_t *inst_queue; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + + Evt_Inst_Index_t *inst_list; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Node_t *node; + + Mif_Boolean_t equal; + + char *err_msg; + + + /* Get temporary pointers for fast access */ + evt = ckt->evt; + + output_queue = &(evt->queue.output); + node_queue = &(evt->queue.node); + inst_queue = &(evt->queue.inst); + + output_table = evt->info.output_table; + node_table = evt->info.node_table; + port_table = evt->info.port_table; + + node_data = evt->data.node; + rhs = node_data->rhs; + rhsold = node_data->rhsold; + + + /* Loop until no more output change, or too many passes through loop */ + for(passes = 0; passes < evt->limits.max_event_passes; passes++) { + + + /* Create list of nodes to evaluate from list of changed outputs */ + num_changed = output_queue->num_changed; + for(i = 0; i < num_changed; i++) { + + /* Get index of node that output is connected to */ + output_index = output_queue->changed_index[i]; + node_index = output_table[output_index]->node_index; + + /* If not already on list of nodes to evaluate, add it */ + if(! node_queue->to_eval[node_index]) { + node_queue->to_eval[node_index] = MIF_TRUE; + node_queue->to_eval_index[(node_queue->num_to_eval)++] + = node_index; + } + + /* Reset the changed flag on the output queue */ + output_queue->changed[output_index] = MIF_FALSE; + + } + output_queue->num_changed = 0; + + + + /* Evaluate nodes and for any which have changed, enter */ + /* the instances that receive inputs from them on the list */ + /* of instances to call */ + + num_to_eval = node_queue->num_to_eval; + for(i = 0; i < num_to_eval; i++) { + + /* Get the node index, udn index and number of outputs */ + node_index = node_queue->to_eval_index[i]; + udn_index = node_table[node_index]->udn_index; + num_outputs = node_table[node_index]->num_outputs; + + /* Resolve the node value if multiple outputs on it */ + /* and test if new node value is different than old value */ + if(num_outputs > 1) { + (*(g_evt_udn_info[udn_index]->resolve)) + (num_outputs, + rhs[node_index].output_value, + rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].node_value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].node_value, + rhsold[node_index].node_value); + } + } + /* Else, load function has already determined that they were */ + /* not equal */ + else + equal = MIF_FALSE; + + /* If not equal, make inverted copy in rhsold if */ + /* needed, and place indexes of instances with inputs connected */ + /* to the node in the to_call list of inst queue */ + if(! equal) { + if(node_table[node_index]->invert) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhsold[node_index].node_value, + rhsold[node_index].inverted_value); + (*(g_evt_udn_info[udn_index]->invert)) + (rhsold[node_index].inverted_value); + } + inst_list = node_table[node_index]->inst_list; + while(inst_list) { + inst_index = inst_list->index; + if(! inst_queue->to_call[inst_index]) { + inst_queue->to_call[inst_index] = MIF_TRUE; + inst_queue->to_call_index[(inst_queue->num_to_call)++] + = inst_index; + } + inst_list = inst_list->next; + } /* end while instances with inputs on node */ + } /* end if not equal */ + + /* If transient analysis mode */ + /* Save the node data onto the node results list and mark */ + /* that it has been modified, even if the */ + /* resolved node value has not changed */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + + node = *(node_data->tail[node_index]); + node_data->tail[node_index] = &(node->next); + EVTnode_copy(ckt, node_index, &(rhsold[node_index]), &(node->next)); + node->next->step = g_mif_info.circuit.evt_step; + + if(! node_data->modified[node_index]) { + node_data->modified[node_index] = MIF_TRUE; + node_data->modified_index[(node_data->num_modified)++] = node_index; + } + } + + /* Reset the to_eval flag on the node queue */ + node_queue->to_eval[node_index] = MIF_FALSE; + + } /* end for number of nodes to evaluate */ + node_queue->num_to_eval = 0; + + + + /* Call the instances with inputs on nodes that have changed */ + num_to_call = inst_queue->num_to_call; + for(i = 0; i < num_to_call; i++) { + inst_index = inst_queue->to_call_index[i]; + inst_queue->to_call[inst_index] = MIF_FALSE; + EVTload(ckt, inst_index); + } + inst_queue->num_to_call = 0; + + + /* Record statistics */ + if(g_mif_info.circuit.anal_type == MIF_DC) + (ckt->evt->data.statistics->op_event_passes)++; + + + /* If no outputs changed, iteration is over, so return with success! */ + if(output_queue->num_changed == 0) + return(0); + + } /* end for */ + + + /* Too many passes through loop, report problems and exit with error */ + + err_msg = MALLOC(10000); + for(i = 0; i < output_queue->num_changed; i++) { + output_index = output_queue->changed_index[i]; + port_index = output_table[output_index]->port_index; + sprintf(err_msg, "\n Instance: %s\n Connection: %s\n Port: %d", + port_table[port_index]->inst_name, + port_table[port_index]->conn_name, + port_table[port_index]->port_num); + ENHreport_conv_prob(ENH_EVENT_NODE, + port_table[port_index]->node_name, + err_msg); + } + FREE(err_msg); + + (*(SPfrontEnd->IFerror)) (ERR_WARNING, + "Too many iteration passes in event-driven circuits", + (IFuid *) NULL); + return(E_ITERLIM); + +} diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c new file mode 100755 index 000000000..606ec4cf2 --- /dev/null +++ b/src/xspice/evt/evtload.c @@ -0,0 +1,613 @@ +/*============================================================================ +FILE EVTload.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTload which is used to call a + specified event-driven or hybrid code model during an event-driven + iteration. The 'CALL_TYPE' is set to 'EVENT_DRIVEN' when the + model is called from this function. + +INTERFACES + + int EVTload(CKTcircuit *ckt, int inst_index) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" + + + + +extern SPICEdev **DEVices; + + + +static void EVTcreate_state( + CKTcircuit *ckt, + int inst_index); + +static void EVTadd_msg( + CKTcircuit *ckt, + int port_index, + char *msg_text); + +static void EVTcreate_output_event( + CKTcircuit *ckt, + int node_index, + int output_index, + void **value_ptr); + +static void EVTprocess_output( + CKTcircuit *ckt, + Mif_Boolean_t changed, + int output_index, + Mif_Boolean_t invert, + double delay); + + + +/* +EVTload + +This function calls the code model function for the specified +instance with CALL_TYPE set to EVENT_DRIVEN. Event outputs, +messages, etc. are processed on return from the code model. +Analog outputs, partials, etc. should not be computed by the +code model when the call type is event-driven and are +ignored. +*/ + +int EVTload( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index) /* The instance to call code model for */ +{ + + int i; + int j; + + int num_conn; + int num_port; + int mod_type; + + Mif_Conn_Data_t *conn; + Mif_Port_Data_t *port; + + Mif_Private_t cm_data; + + MIFinstance *inst; + + Evt_Node_Data_t *node_data; + + void *value_ptr; + + + /* ***************************** */ + /* Prepare the code model inputs */ + /* ***************************** */ + + /* Get pointer to instance data structure and other data */ + /* needed for fast access */ + inst = ckt->evt->info.inst_table[inst_index]->inst_ptr; + node_data = ckt->evt->data.node; + + /* Setup circuit data in struct to be passed to code model function */ + + if(inst->initialized) + cm_data.circuit.init = MIF_FALSE; + else + cm_data.circuit.init = MIF_TRUE; + + cm_data.circuit.anal_init = MIF_FALSE; + cm_data.circuit.anal_type = g_mif_info.circuit.anal_type; + + if(g_mif_info.circuit.anal_type == MIF_TRAN) + cm_data.circuit.time = g_mif_info.circuit.evt_step; + else + cm_data.circuit.time = 0.0; + + cm_data.circuit.call_type = MIF_EVENT_DRIVEN; + cm_data.circuit.temperature = ckt->CKTtemp - 273.15; + + + /* Setup data needed by cm_... functions */ + + g_mif_info.ckt = ckt; + g_mif_info.instance = inst; + g_mif_info.errmsg = ""; + g_mif_info.circuit.call_type = MIF_EVENT_DRIVEN; + + if(inst->initialized) + g_mif_info.circuit.init = MIF_FALSE; + else + g_mif_info.circuit.init = MIF_TRUE; + + + /* If after initialization and in transient analysis mode */ + /* create a new state for the instance */ + + if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized) + EVTcreate_state(ckt, inst_index); + + + /* Loop through all connections on the instance and setup */ + /* load, total_load, and msg on all ports, and changed flag */ + /* and output pointer on all outputs */ + + num_conn = inst->num_conn; + for(i = 0; i < num_conn; i++) { + + conn = inst->conn[i]; + + /* if connection is null, continue to next */ + if(conn->is_null) + continue; + + /* Loop through each port on the connection */ + num_port = conn->size; + for(j = 0; j < num_port; j++) { + + port = conn->port[j]; + + /* Skip if port is null */ + if(port->is_null) + continue; + + /* If port type is Digital or User-Defined */ + if((port->type == MIF_DIGITAL) || (port->type == MIF_USER_DEFINED)) { + + /* Initialize the msg pointer on the port to NULL, */ + /* initialize the load value to zero, and get the total load */ + port->msg = NULL; + port->load = 0.0; + port->total_load = node_data->total_load[port->evt_data.node_index]; + + /* If connection is an output, initialize changed to true */ + /* and create a new output event object in the free list */ + /* if transient analysis mode */ + if(conn->is_output) { + port->changed = MIF_TRUE; + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + EVTcreate_output_event(ckt, + port->evt_data.node_index, + port->evt_data.output_index, + &value_ptr); + port->output.pvalue = value_ptr; + } + } + } + else { + /* Get the analog input value. All we need to do is */ + /* set it to zero if mode is INITJCT. Otherwise, value */ + /* should still be around from last successful analog call */ + if(ckt->CKTmode & MODEINITJCT) + port->input.rvalue = 0.0; + } + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* Prepare the structure to be passed to the code model */ + cm_data.num_conn = inst->num_conn; + cm_data.conn = inst->conn; + cm_data.num_param = inst->num_param; + cm_data.param = inst->param; + cm_data.num_inst_var = inst->num_inst_var; + cm_data.inst_var = inst->inst_var; + + + /* ******************* */ + /* Call the code model */ + /* ******************* */ + + mod_type = inst->MIFmodPtr->MIFmodType; + (*(DEVices[mod_type]->DEVpublic.cm_func)) (&cm_data); + + + /* ****************************** */ + /* Process the code model outputs */ + /* ****************************** */ + + /* Loop through all connections and ports and process the msgs */ + /* and event outputs */ + + num_conn = inst->num_conn; + for(i = 0; i < num_conn; i++) { + + conn = inst->conn[i]; + if(conn->is_null) + continue; + + /* Loop through each port on the connection */ + num_port = conn->size; + for(j = 0; j < num_port; j++) { + + port = conn->port[j]; + + /* Skip if port is null */ + if(port->is_null) + continue; + + /* Process the message if any */ + if(port->msg) + EVTadd_msg(ckt, port->evt_data.port_index, port->msg); + + /* If this is the initialization pass, process the load factor */ + if(! inst->initialized) { + node_data->total_load[port->evt_data.node_index] += + port->load; + } + + /* If connection is not an event output, continue to next port */ + if(! conn->is_output) + continue; + if((port->type != MIF_DIGITAL) && (port->type != MIF_USER_DEFINED)) + continue; + + /* If output changed, process it */ + EVTprocess_output(ckt, port->changed, + port->evt_data.output_index, + port->invert, port->delay); + + /* And prevent erroneous models from overwriting it during */ + /* analog iterations */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) + port->output.pvalue = NULL; + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* Record statistics */ + if(g_mif_info.circuit.anal_type == MIF_DC) + (ckt->evt->data.statistics->op_load_calls)++; + else if(g_mif_info.circuit.anal_type == MIF_TRAN) + (ckt->evt->data.statistics->tran_load_calls)++; + + /* Mark that the instance has been called once */ + inst->initialized = MIF_TRUE; + + return(OK); +} + + + +/* +EVTcreate_state + +This function creates a new state storage area for a particular instance +during an event-driven simulation. New states must be created so +that old states are saved and can be accessed by code models in the +future. The new state is initialized to the previous state value. +*/ + + +static void EVTcreate_state( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index) /* The instance to create state for */ +{ + int i; + int total_size; + + Evt_State_Data_t *state_data; + + Evt_State_t *new_state; + Evt_State_t *prev_state; + + char *from; + char *to; + + + /* Get variables for fast access */ + state_data = ckt->evt->data.state; + + /* Exit immediately if no states on this instance */ + if(state_data->desc[inst_index] == NULL) + return; + + /* Get size of state block to be allocated */ + total_size = state_data->total_size[inst_index]; + + /* Allocate a new state for the instance */ + if(state_data->free[inst_index]) + { + new_state = state_data->free[inst_index]; + state_data->free[inst_index] = new_state->next; + } + else + { + + new_state = (void *) MALLOC(sizeof(Evt_State_t)); + new_state->block = (void *) MALLOC(total_size); + + } + + /* Splice the new state into the state data linked list */ + /* and update the tail pointer */ + prev_state = *(state_data->tail[inst_index]); + prev_state->next = new_state; + new_state->prev = prev_state; + state_data->tail[inst_index] = &(prev_state->next); + + /* Copy the old state to the new state and set the step */ + from = prev_state->block; + to = new_state->block; + for(i = 0; i < total_size; i++) + to[i] = from[i]; + new_state->step = g_mif_info.circuit.evt_step; + + /* Mark that the state data on the instance has been modified */ + if(! state_data->modified[inst_index]) { + state_data->modified[inst_index] = MIF_TRUE; + state_data->modified_index[(state_data->num_modified)++] = inst_index; + } +} + + +/* +EVTcreate_output_event + +This function creates a new output event. +*/ + +static void EVTcreate_output_event( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The node type port is on */ + int output_index, /* The output index for this port */ + void **value_ptr) /* The event created */ +{ + int udn_index; + Evt_Node_Info_t **node_table; + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t *event; + + + /* Check the output queue free list and use the structure */ + /* at the head of the list if non-null. Otherwise, create a new one. */ + output_queue = &(ckt->evt->queue.output); + if(output_queue->free[output_index]) { + *value_ptr = output_queue->free[output_index]->value; + } + else { + /* Create a new event */ + event = (void *) MALLOC(sizeof(Evt_Output_Event_t)); + event->next = NULL; + + /* Initialize the value */ + node_table = ckt->evt->info.node_table; + udn_index = node_table[node_index]->udn_index; + (*(g_evt_udn_info[udn_index]->create)) (&(event->value)); + + /* Put the event onto the free list and return the value pointer */ + output_queue->free[output_index] = event; + *value_ptr = event->value; + } +} + + + +/* +EVTadd_msg + +This function records a message output by a code model into the +message results data structure. +*/ + + +static void EVTadd_msg( + CKTcircuit *ckt, /* The circuit structure */ + int port_index, /* The port to add message to */ + char *msg_text) /* The message text */ +{ + + Evt_Msg_Data_t *msg_data; + + Evt_Msg_t **msg_ptr; + Evt_Msg_t *msg; + + + /* Get pointers for fast access */ + msg_data = ckt->evt->data.msg; + msg_ptr = msg_data->tail[port_index]; + + /* Set pointer to location at which to add, and update tail */ + if(*msg_ptr != NULL) { + msg_ptr = &((*msg_ptr)->next); + msg_data->tail[port_index] = msg_ptr; + } + + /* Add a new entry in the list of messages for this port */ + if(msg_data->free[port_index]) { + *msg_ptr = msg_data->free[port_index]; + msg_data->free[port_index] = msg_data->free[port_index]->next; + } + else { + *msg_ptr = (void *) MALLOC(sizeof(Evt_Msg_t)); + } + + /* Fill in the values */ + msg = *msg_ptr; + msg->next = NULL; + if((ckt->CKTmode & MODEDCOP) == MODEDCOP) + msg->op = MIF_TRUE; + else + msg->step = g_mif_info.circuit.evt_step; + msg->text = MIFcopy(msg_text); + + /* Update the modified indexes */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + if(! msg_data->modified[port_index]) { + msg_data->modified[port_index] = MIF_TRUE; + msg_data->modified_index[(msg_data->num_modified)++] = port_index; + } + } + +} + + +/* +EVTprocess_output + +This function processes an event-driven output produced by a code +model. If transient analysis mode, the event is placed into the +output queue according to its (non-zero) delay. If DC analysis, +the event is processed immediately. +*/ + + +static void EVTprocess_output( + CKTcircuit *ckt, /* The circuit structure */ + Mif_Boolean_t changed, /* Has output changed? */ + int output_index, /* The output of interest */ + Mif_Boolean_t invert, /* Does output need to be inverted? */ + double delay) /* The output delay in transient analysis */ +{ + + int num_outputs; + int node_index; + int udn_index; + int output_subindex; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t *output_event; + + Mif_Boolean_t equal; + + + output_queue = &(ckt->evt->queue.output); + output_table = ckt->evt->info.output_table; + node_table = ckt->evt->info.node_table; + + node_index = output_table[output_index]->node_index; + udn_index = node_table[node_index]->udn_index; + + + /* if transient analysis, just put the output event on the queue */ + /* to be processed at a later time */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + /* If model signaled that output was not posted, */ + /* leave the event struct on the free list and return */ + if((! changed) || (delay <= 0.0)) { + if(changed && (delay <= 0.0)) + printf("\nERROR - Output delay <= 0 not allowed - output ignored!\n"); + return; + } + /* Remove the (now used) struct from the head of the free list */ + output_event = output_queue->free[output_index]; + output_queue->free[output_index] = output_event->next; + /* Invert the output value if necessary */ + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (output_event->value); + /* Add it to the queue */ + EVTqueue_output(ckt, output_index, udn_index, output_event, + g_mif_info.circuit.evt_step, + g_mif_info.circuit.evt_step + delay); + return; + } + /* If not transient analysis, process immediately. */ + /* Determine if output has changed from rhsold value */ + /* and put entry in output queue changed list if so */ + else { + + /* If model signaled that output was not posted, */ + /* just return */ + if(! changed) + return; +/* + if(delay > 0.0) + printf("\nWARNING - Non-zero output delay not allowed in DCOP - delay ignored!\n"); +*/ + rhs = ckt->evt->data.node->rhs; + rhsold = ckt->evt->data.node->rhsold; + + /* Determine if changed */ + num_outputs = node_table[node_index]->num_outputs; + if(num_outputs > 1) { + output_subindex = output_table[output_index]->output_subindex; + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (rhs[node_index].output_value[output_subindex]); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].output_value[output_subindex], + rhsold[node_index].output_value[output_subindex], + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].output_value[output_subindex], + rhsold[node_index].output_value[output_subindex]); + } + } + else { + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].node_value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].node_value, + rhsold[node_index].node_value); + } + } + + /* If changed, put in changed list of output queue */ + if(! equal) { + if(! output_queue->changed[output_index]) { + output_queue->changed[output_index] = MIF_TRUE; + output_queue->changed_index[(output_queue->num_changed)++] = + output_index; + } + } + + return; + + } /* end else process immediately */ +} diff --git a/src/xspice/evt/evtnext_time.c b/src/xspice/evt/evtnext_time.c new file mode 100755 index 000000000..b2f3c35e9 --- /dev/null +++ b/src/xspice/evt/evtnext_time.c @@ -0,0 +1,93 @@ +/*============================================================================ +FILE EVTnext_time.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTnext_time which determines and + returns the time of the next scheduled event on the inst and output + queues. + +INTERFACES + + double EVTnext_time(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + +/* +EVTnext_time + +Get the next event time as the minimum of the next times +in the inst and output queues. If no next time in either, +return machine infinity. +*/ + + +double EVTnext_time( + CKTcircuit *ckt) /* The circuit structure */ +{ + + double next_time; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + + /* Initialize next time to machine infinity */ + next_time = 1e30; + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + output_queue = &(ckt->evt->queue.output); + + /* If anything pending in inst queue, set next time */ + /* to minimum of itself and the inst queue next time */ + if(inst_queue->num_pending) + if(inst_queue->next_time < next_time) + next_time = inst_queue->next_time; + + /* If anything pending in output queue, set next time */ + /* to minimum of itself and the output queue next time */ + if(output_queue->num_pending) + if(output_queue->next_time < next_time) + next_time = output_queue->next_time; + + return(next_time); +} diff --git a/src/xspice/evt/evtnode_copy.c b/src/xspice/evt/evtnode_copy.c new file mode 100755 index 000000000..6690b9224 --- /dev/null +++ b/src/xspice/evt/evtnode_copy.c @@ -0,0 +1,159 @@ +/*============================================================================ +FILE EVTnode_copy.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTnode_copy which copies the state + of a node structure. + +INTERFACES + + void EVTnode_copy(CKTcircuit *ckt, int node_index, Evt_Node_t *from, + Evt_Node_t **to) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" +#include "cm.h" + +/* +EVTnode_copy + +This function copies the state of a node structure. + +If the destination is NULL, it is allocated before the copy. This is the +case when EVTiter copies a node during a transient analysis to +save the state of an element of rhsold into the node data structure +lists. + +If the destination is non-NULL, only the internal elements of the node +structure are copied. This is the case when EVTbackup restores that state +of nodes that existed at a certain timestep back into rhs and rhsold. +*/ + + +void EVTnode_copy( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The node to copy */ + Evt_Node_t *from, /* Location to copy from */ + Evt_Node_t **to) /* Location to copy to */ +{ + + int i; + + int udn_index; + int num_outputs; + Mif_Boolean_t invert; + + Evt_Node_Data_t *node_data; + Evt_Node_Info_t **node_table; + + Evt_Node_t *here; + + /* Digital_t *dummy;*/ + + /* char buff[128];*/ + + /* Get data for fast access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + + udn_index = node_table[node_index]->udn_index; + num_outputs = node_table[node_index]->num_outputs; + invert = node_table[node_index]->invert; + + + /* If destination is not allocated, allocate it */ + /* otherwise we just copy into the node struct */ + here = *to; + + if(here == NULL) + { + /* Use allocated structure on free list if available */ + /* Otherwise, allocate a new one */ + here = node_data->free[node_index]; + if(here) + { + *to = here; + node_data->free[node_index] = here->next; + here->next = NULL; + } + else + { + here = (void *) MALLOC(sizeof(Evt_Node_t)); + *to = here; + /* Allocate/initialize the data in the new node struct */ + if(num_outputs > 1) + { + here->output_value = (void *) MALLOC(num_outputs * sizeof(void *)); + + for(i = 0; i < num_outputs; i++) + { + (*(g_evt_udn_info[udn_index]->create)) + ( &(here->output_value[i]) ); + } + } + + here->node_value = NULL; + + (*(g_evt_udn_info[udn_index]->create)) ( &(here->node_value) ); + + if(invert) + (*(g_evt_udn_info[udn_index]->create)) ( &(here->inverted_value) ); + + + } + } + + /* Copy the node data */ + here->op = from->op; + here->step = from->step; + if(num_outputs > 1) + { + for(i = 0; i < num_outputs; i++) + { + (*(g_evt_udn_info[udn_index]->copy)) (from->output_value[i], + here->output_value[i]); + } + } + (*(g_evt_udn_info[udn_index]->copy)) (from->node_value, here->node_value); + if(invert) + { + (*(g_evt_udn_info[udn_index]->copy)) (from->inverted_value, + here->inverted_value); + } +} diff --git a/src/xspice/evt/evtop.c b/src/xspice/evt/evtop.c new file mode 100755 index 000000000..fa431ecd8 --- /dev/null +++ b/src/xspice/evt/evtop.c @@ -0,0 +1,321 @@ +/*============================================================================ +FILE EVTop.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTop which is used to perform an + operating point analysis in place of CKTop when there are + event-driven instances in the circuit. It alternates between doing + event-driven iterations with EVTiter and doing analog iterations with + NIiter/CKTop until no more event-driven outputs change. + +INTERFACES + + EVTop() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtproto.h" +#include "evtudn.h" + + +static void EVTnode_compare( + CKTcircuit *ckt, + int node_index, + Evt_Node_t *node1, + Evt_Node_t *node2, + Mif_Boolean_t *equal); + + + +/* +EVTop + +This function is used to perform an operating point analysis +in place of CKTop when there are event-driven instances in the +circuit. It alternates between doing event-driven iterations +with EVTiter and doing analog iterations with NIiter/CKTop +until no more event-driven outputs change. +*/ + + +int EVTop( + CKTcircuit *ckt, /* The circuit structure */ + long firstmode, /* The SPICE 3C1 CKTop() firstmode parameter */ + long continuemode, /* The SPICE 3C1 CKTop() continuemode paramter */ + int max_iter, /* The SPICE 3C1 CKTop() max iteration parameter */ + Mif_Boolean_t first_call) /* Is this the first time through? */ +{ + + int i; + int num_insts; + int converged; + int output_index; + int port_index; + + char *err_msg; + + Mif_Boolean_t firstime; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Output_Info_t **output_table; + Evt_Port_Info_t **port_table; + + + /* get data to local storage for fast access */ + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + /* Initialize to_call entries in event inst queue */ + /* to force calling all event/hybrid instance the first */ + /* time through */ + + if(first_call) { + for(i = 0; i < num_insts; i++) { + inst_queue->to_call[i] = MIF_TRUE; + inst_queue->to_call_index[i] = i; + } + inst_queue->num_to_call = num_insts; + } + + + /* Alternate between event-driven and analog solutions until */ + /* there are no changed event-driven outputs */ + + firstime = MIF_TRUE; + for(;;) { + + /* Call EVTiter to establish initial outputs from */ + /* event/hybrid instances with states (e.g. flip-flops) */ + + ckt->CKTmode = firstmode; + converged = EVTiter(ckt); + if(converged != 0) + return(converged); + + /* Now do analog solution for current state of hybrid outputs */ + + /* If first analog solution, call CKTop */ + if(firstime) { + firstime = MIF_FALSE; + converged = CKTop(ckt, + firstmode, + continuemode, + max_iter); + if(converged != 0) + return(converged); + } + /* Otherwise attempt to converge with mode = continuemode */ + else { + ckt->CKTmode = continuemode; + converged = NIiter(ckt,max_iter); + if(converged != 0) { + converged = CKTop(ckt, + firstmode, + continuemode, + max_iter); + if(converged != 0) + return(converged); + } + } + + /* Call all hybrids to allow new event outputs to be posted */ + EVTcall_hybrids(ckt); + + /* Increment count of successful alternations */ + (ckt->evt->data.statistics->op_alternations)++; + + /* If .option card specified not to alternate solutions, exit */ + /* immediately with this first pass solution */ + if(! ckt->evt->options.op_alternate) + return(0); + + /* If no hybrid instances produced different event outputs, */ + /* alternation is completed, so exit */ + if(ckt->evt->queue.output.num_changed == 0) + return(0); + + /* If too many alternations, exit with error */ + if(ckt->evt->data.statistics->op_alternations >= + ckt->evt->limits.max_op_alternations) { + + (*(SPfrontEnd->IFerror)) (ERR_WARNING, + "Too many analog/event-driven solution alternations", + (IFuid *) NULL); + + err_msg = MALLOC(10000); + output_queue = &(ckt->evt->queue.output); + output_table = ckt->evt->info.output_table; + port_table = ckt->evt->info.port_table; + + for(i = 0; i < output_queue->num_changed; i++) { + output_index = output_queue->changed_index[i]; + port_index = output_table[output_index]->port_index; + sprintf(err_msg, "\n Instance: %s\n Connection: %s\n Port: %d", + port_table[port_index]->inst_name, + port_table[port_index]->conn_name, + port_table[port_index]->port_num); + ENHreport_conv_prob(ENH_EVENT_NODE, + port_table[port_index]->node_name, + err_msg); + } + FREE(err_msg); + + return(E_ITERLIM); + } + + } /* end forever */ +} + + + +/* +EVTop_save + +Save result from operating point iteration into the node data area. +*/ + +void EVTop_save( + CKTcircuit *ckt, /* The circuit structure */ + Mif_Boolean_t op, /* True if from a DCOP analysis, false if TRANOP, etc. */ + double step) +{ + + int i; + int num_nodes; + + Mif_Boolean_t equal; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhsold; + Evt_Node_t **head; + Evt_Node_t **here; + + /* char buff[128];*/ + + + /* Get pointers for fast access */ + node_data = ckt->evt->data.node; + rhsold = node_data->rhsold; + head = node_data->head; + + /* For number of event nodes, copy rhsold to node data */ + /* and set the op member if appropriate */ + num_nodes = ckt->evt->counts.num_nodes; + + for(i = 0; i < num_nodes; i++) + { + /* if head is null, just copy there */ + if(head[i] == NULL) + { + EVTnode_copy(ckt, i, &(rhsold[i]), &(head[i])); + + head[i]->op = op; + head[i]->step = step; + + } + /* Otherwise, add to the end of the list */ + else + { + /* Locate end of list */ + here = &(head[i]); + for(;;) + { + if((*here)->next) + here = &((*here)->next); + else + break; + } + /* Compare entry at end of list to rhsold */ + + EVTnode_compare(ckt, i, &(rhsold[i]), *here, &equal); + + /* If new value in rhsold is different, add it to the list */ + if(!equal) + { + here = &((*here)->next); + EVTnode_copy(ckt, i, &(rhsold[i]), here); + (*here)->op = op; + (*here)->step = step; + } + } /* end else add to end of list */ + } /* end for number of nodes */ + +} + + + +/* ************************************************************ */ + + +/* +EVTnode_compare + +This function compares the resolved values of the old and +new states on a node. The actual comparison is done by +calling the appropriate user-defined node compare function. +*/ + + +static void EVTnode_compare( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The index for the node in question */ + Evt_Node_t *node1, /* The first value */ + Evt_Node_t *node2, /* The second value */ + Mif_Boolean_t *equal) /* The computed result */ +{ + + Evt_Node_Data_t *node_data; + Evt_Node_Info_t **node_table; + + int udn_index; + + + /* Get data for fast access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + udn_index = node_table[node_index]->udn_index; + + + /* Do compare based on changes in resolved node value only */ + (*(g_evt_udn_info[udn_index]->compare)) ( + node1->node_value, + node2->node_value, + equal); +} diff --git a/src/xspice/evt/evtplot.c b/src/xspice/evt/evtplot.c new file mode 100755 index 000000000..60d47fefc --- /dev/null +++ b/src/xspice/evt/evtplot.c @@ -0,0 +1,218 @@ +/*============================================================================ +FILE EVTplot.c + +MEMBER OF process XSPICE + +Copyright 1992 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 5/7/92 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTplot which is used to provide basic + plotting of event driven nodes through SPICE3's 'plot' command. + +INTERFACES + + void EVTplot() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//nclude "misc.h" +#include "evt.h" +#include "evtudn.h" +#include "evtproto.h" +#include "mif.h" +#include "mifproto.h" + +/*saj for output */ +#include "sim.h" +#include "dvec.h" +//#include "ftedata.h" +//#include "fteconstant.h" +//#include "util.h" +#include "cpstd.h" + + +/* + +EVTfindvec() + +This function is called from FTE/vectors.c:findvec() when a node specified +for plotting cannot be located in the analog plot data. It scans the +event driven data structures looking for the node, and if found, returns +a new 'dvec' structure holding the data to be plotted. The dvec struct +is created with it's own v_scale member holding the event time vector +for this node since the time vector is not aligned with the analog data +points or with other event vectors. + +The node name supplied as argument can either be a simple node name, or a +name of the form (), where is the member of +the event-driven structure to be plotted. These member names are defined +by the individual "user-defined node" plot_val routines for the node +type in question. If the simple node name form is used, the special +keyword "all" is supplied to the plot_val routine for the member name. + +*/ + + +struct dvec *EVTfindvec( + char *node) /* The node name (and optional member name) */ +{ + char *name; + char *member = "all"; + char *ptr; + + int i; + int len; + int num_nodes; + int udn_index; + int num_events; + + Mif_Boolean_t found; + Evt_Node_Info_t **node_table; + Evt_Node_t *head; + Evt_Node_t *event; + + double *anal_point_vec; + double *value_vec; + double value; + + struct dvec *d; + struct dvec *scale; + + /* Exit immediately if event-driven stuff not allocated yet, */ + /* or if number of event nodes is zero. */ + if(! g_mif_info.ckt) + return(NULL); + if(! g_mif_info.ckt->evt) + return(NULL); + if(g_mif_info.ckt->evt->counts.num_nodes == 0) + return(NULL); + + /* Make a copy of the node name. */ + /* Do not free this string. It is assigned into the dvec structure below. */ + name = MIFcopy(node); + + /* Convert to all lower case */ + len = strlen(name); + for(i = 0; i < len; i++) + if(isupper(name[i])) + name[i] = tolower(name[i]); + + /* Divide into the node name and member name */ + for(ptr = name; *ptr != '\0'; ptr++) + if(*ptr == '(') + break; + + if(*ptr == '(') { + *ptr = '\0'; + ptr++; + member = ptr; + for( ; *ptr != '\0'; ptr++) + if(*ptr == ')') + break; + *ptr = '\0'; + } + + /* Look for node name in the event-driven node list */ + num_nodes = g_mif_info.ckt->evt->counts.num_nodes; + node_table = g_mif_info.ckt->evt->info.node_table; + + for(i = 0, found = MIF_FALSE; i < num_nodes; i++) { + if(cieq(name, node_table[i]->name)) { + found = MIF_TRUE; + break; + } + } + + if(! found) + return(NULL); + + /* Get the UDN type index */ + udn_index = node_table[i]->udn_index; + + /* Count the number of events */ + head = g_mif_info.ckt->evt->data.node->head[i]; + + for(event = head, num_events = 0; event; event = event->next) + num_events++; + + /* Allocate arrays to hold the analysis point and node value vectors */ + anal_point_vec = (double *) MALLOC(2 * (num_events + 2) * sizeof(double)); + value_vec = (double *) MALLOC(2 * (num_events + 2) * sizeof(double)); + + /* Iterate through the events and fill the arrays. */ + /* Note that we create vertical segments every time an event occurs. */ + /* Need to modify this in the future to complete the vector out to the */ + /* last analysis point... */ + for(i = 0, event = head; event; event = event->next) { + + /* If not first point, put the second value of the horizontal line in the vectors */ + if(i > 0) { + anal_point_vec[i] = event->step; + value_vec[i] = value; + i++; + } + + /* Get the next value by calling the appropriate UDN plot_val function */ + value = 0.0; + (*(g_evt_udn_info[udn_index]->plot_val)) (event->node_value, + member, + &value); + + /* Put the first value of the horizontal line in the vector */ + anal_point_vec[i] = event->step; + value_vec[i] = value; + i++; + + } + + /* Allocate dvec structures and assign the vectors into them. */ + /* See FTE/OUTinterface.c:plotInit() for initialization example. */ + + scale = (void *) MALLOC(sizeof(struct dvec)); + + scale->v_name = MIFcopy("step"); + scale->v_type = SV_TIME; + scale->v_flags = VF_REAL & ~VF_PERMANENT; + scale->v_length = i; + scale->v_realdata = anal_point_vec; + scale->v_scale = NULL; + + d = (void *) MALLOC(sizeof(struct dvec)); + + d->v_name = name; + d->v_type = SV_VOLTAGE; + d->v_flags = VF_REAL & ~VF_PERMANENT; + d->v_length = i; + d->v_realdata = value_vec; + d->v_scale = scale; + + + /* Return the dvec */ + return(d); +} diff --git a/src/xspice/evt/evtprint.c b/src/xspice/evt/evtprint.c new file mode 100755 index 000000000..2c82c2ddb --- /dev/null +++ b/src/xspice/evt/evtprint.c @@ -0,0 +1,369 @@ +/*============================================================================ +FILE EVTprint.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTprint which is used to provide a simple + tabular output of event-driven node data. This printout is invoked + through a new nutmeg command called 'eprint' which takes event-driven + node names as argument. + +INTERFACES + + void EVTprint(wordlist *wl) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cpstd.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + + +static int get_index(char *node_name); + +static void print_data( + Mif_Boolean_t dcop, + double step, + char **node_value, + int nargs); + +/* +EVTprint + +This function implements the 'eprint' command used to print +event-driven node values and messages from the latest simulation. + +This is a simple prototype implementation of the +eprint command for testing purposes. It is currently lacking +in the following areas: + +1) It accepts only up to 16 nodes. + +2) It does not support the selected printing of different + members of a user-defined data struct. + +3) It is dumb in its output formatting - just tries to print + everything on a line with 4 space separators. + +4) It works only for the latest simulation - i.e. it does not + use the evt jobs structure to find old results. + +5) It does not allow a range of timesteps to be selected. + +*/ + + + +#define EPRINT_MAXARGS 16 + + +void EVTprint( + wordlist *wl) /* The command line entered by user */ +{ + + int i; + int nargs; + int num_ports; + + wordlist *w; + + char *node_name[EPRINT_MAXARGS]; + int node_index[EPRINT_MAXARGS]; + int udn_index[EPRINT_MAXARGS]; + Evt_Node_t *node_data[EPRINT_MAXARGS]; + char *node_value[EPRINT_MAXARGS]; + + CKTcircuit *ckt; + + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + + Mif_Boolean_t more; + Mif_Boolean_t dcop; + + double step; + double next_step; + double this_step; + + char *value; + + Evt_Msg_t *msg_data; + Evt_Statistic_t *statistics; + + + /* Count the number of arguments to the command */ + nargs = 0; + w = wl; + while(w) { + nargs++; + w = w->wl_next; + } + + if(nargs < 1) { + printf("Usage: eprint ...\n"); + return; + } + if(nargs > EPRINT_MAXARGS) { + printf("ERROR - eprint currently limited to 16 arguments\n"); + return; + } + + /* Get needed pointers */ + ckt = g_mif_info.ckt; + node_table = ckt->evt->info.node_table; + + /* Get data for each argument */ + w = wl; + for(i = 0; i < nargs; i++) { + node_name[i] = w->wl_word; + node_index[i] = get_index(node_name[i]); + if(node_index[i] < 0) { + printf("ERROR - Node %s is not an event node.\n", node_name[i]); + return; + } + udn_index[i] = node_table[node_index[i]]->udn_index; + node_data[i] = ckt->evt->data.node->head[node_index[i]]; + node_value[i] = ""; + w = w->wl_next; + } + + + /* Print results data */ + printf("\n**** Results Data ****\n\n"); + + /* Print the column identifiers */ + printf("Time or Step\n"); + for(i = 0; i < nargs; i++) + printf("%s\n",node_name[i]); + printf("\n\n"); + + /* Scan the node data and determine if the first vector */ + /* is for a DCOP analysis or the first step in a swept DC */ + /* or transient analysis. Also, determine if there is */ + /* more data following it and if so, what the next step */ + /* is. */ + more = MIF_FALSE; + dcop = MIF_FALSE; + next_step = 1e30; + for(i = 0; i < nargs; i++) { + if(node_data[i]->op) + dcop = MIF_TRUE; + else + step = node_data[i]->step; + (*(g_evt_udn_info[udn_index[i]]->print_val)) + (node_data[i]->node_value, "all", &value); + node_value[i] = value; + node_data[i] = node_data[i]->next; + if(node_data[i]) { + more = MIF_TRUE; + if(node_data[i]->step < next_step) + next_step = node_data[i]->step; + } + } + + /* Print the data */ + print_data(dcop, step, node_value, nargs); + + /* While there is more data, get the next values and print */ + while(more) { + + more = MIF_FALSE; + this_step = next_step; + next_step = 1e30; + + for(i = 0; i < nargs; i++) { + + if(node_data[i]) { + if(node_data[i]->step == this_step) { + (*(g_evt_udn_info[udn_index[i]]->print_val)) + (node_data[i]->node_value, "all", &value); + node_value[i] = value; + node_data[i] = node_data[i]->next; + } + if(node_data[i]) { + more = MIF_TRUE; + if(node_data[i]->step < next_step) + next_step = node_data[i]->step; + } + + } /* end if node_data not NULL */ + + } /* end for number of args */ + + print_data(MIF_FALSE, this_step, node_value, nargs); + + } /* end while there is more data */ + printf("\n\n"); + + + /* Print messages for all ports */ + printf("\n**** Messages ****\n\n"); + + num_ports = ckt->evt->counts.num_ports; + port_table = ckt->evt->info.port_table; + + for(i = 0; i < num_ports; i++) { + + /* Get pointer to messages for this port */ + msg_data = ckt->evt->data.msg->head[i]; + + /* If no messages on this port, skip */ + if(! msg_data) + continue; + + /* Print the port description */ + printf("Node: %s Inst: %s Conn: %s Port: %d\n\n", + port_table[i]->node_name, + port_table[i]->inst_name, + port_table[i]->conn_name, + port_table[i]->port_num); + + /* Print the messages on this port */ + while(msg_data) { + if(msg_data->op) + printf("DCOP "); + else + printf("%-16.9e", msg_data->step); + printf("%s\n", msg_data->text); + msg_data = msg_data->next; + } + printf("\n\n"); + + } /* end for number of ports */ + + + /* Print statistics */ + printf("\n**** Statistics ****\n\n"); + + statistics = ckt->evt->data.statistics; + printf("Operating point analog/event alternations: %d\n", + statistics->op_alternations); + printf("Operating point load calls: %d\n", + statistics->op_load_calls); + printf("Operating point event passes: %d\n", + statistics->op_event_passes); + printf("Transient analysis load calls: %d\n", + statistics->tran_load_calls); + printf("Transient analysis timestep backups: %d\n", + statistics->tran_time_backups); + + printf("\n\n"); +} + + + + +/* +get_index + +This function determines the index of a specified event-driven node. +*/ + + +static int get_index( + char *node_name /* The name of the node to search for */ +) +{ + + /* Get the event-driven node index for the specified name */ + + int index; + + Mif_Boolean_t found; + Evt_Node_Info_t *node; + CKTcircuit *ckt; + + + /* Scan list of nodes in event structure to see if there */ + + found = MIF_FALSE; + index = 0; + + ckt = g_mif_info.ckt; + node = ckt->evt->info.node_list; + + while(node) { + if(strcmp(node_name, node->name) == 0) { + found = MIF_TRUE; + break; + } + else { + index++; + node = node->next; + } + } + + /* Return the index or -1 if not found */ + if(! found) + return(-1); + else + return(index); +} + + + + +/* +print_data + +This function prints the values of one or more nodes to +standard output. +*/ + + +static void print_data( + Mif_Boolean_t dcop, /* Is this the operating point data */ + double step, /* The analysis step if dcop */ + char **node_value, /* The array of values to be printed */ + int nargs) /* The size of the value array */ +{ + + int i; + char step_str[100]; + + if(dcop) + strcpy(step_str, "DCOP "); + else + sprintf(step_str, "%-16.9e", step); + + printf("%s", step_str); + for(i = 0; i < nargs; i++) + printf(" %s", node_value[i]); + printf("\n"); +} diff --git a/src/xspice/evt/evtqueue.c b/src/xspice/evt/evtqueue.c new file mode 100755 index 000000000..1de0b9937 --- /dev/null +++ b/src/xspice/evt/evtqueue.c @@ -0,0 +1,252 @@ +/*============================================================================ +FILE EVTqueue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions that place new events into the output and + instance queues. + +INTERFACES + + void EVTqueue_output( + CKTcircuit *ckt, + int output_index, + int udn_index, + Evt_Output_Event_t *new_event, + double posted_time, + double event_time) + + void EVTqueue_inst( + CKTcircuit *ckt, + int inst_index, + double posted_time, + double event_time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + + +/* +EVTqueue_output + +This function places the specified output event onto the output +queue. It is called by EVTload during a transient analysis. + +The linked list in the queue for the specified output is +searched beginning at the current head of the pending events +to find the location at which to insert the new event. The +events are ordered in the list by event_time. If the event +is placed before the end of the list, subsequent events are +removed from the list by marking them as 'removed' and +recording the time of removal. This allows efficient backup +of the state of the queue if a subsequent analog timestep +fails. +*/ + + +void EVTqueue_output( + CKTcircuit *ckt, /* The circuit structure */ + int output_index, /* The output in question */ + int udn_index, /* The associated user-defined node type */ + Evt_Output_Event_t *new_event, /* The event to queue */ + double posted_time, /* The current time */ + double event_time) /* The time the event should happen */ +{ + + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t **here; + Evt_Output_Event_t *next; + + Mif_Boolean_t splice; + + + /* Get pointers for fast access */ + output_queue = &(ckt->evt->queue.output); + + /* Put the times into the event struct */ + new_event->event_time = event_time; + new_event->posted_time = posted_time; + new_event->removed = MIF_FALSE; + + /* Update next_time in output queue */ + if((output_queue->num_pending <= 0) || + (event_time < output_queue->next_time)) + output_queue->next_time = event_time; + + /* Find location at which to insert event */ + splice = MIF_FALSE; + here = output_queue->current[output_index]; + while(*here) { + if(event_time <= (*here)->event_time) { + splice = MIF_TRUE; + break; + } + here = &((*here)->next); + } + + /* If needs to be spliced into middle of existing list */ + if(splice) { + /* splice it in */ + next = *here; + *here = new_event; + new_event->next = next; + /* mark later events as removed */ + while(next) { + if(! next->removed) { + next->removed = MIF_TRUE; + next->removed_time = posted_time; + } + next = next->next; + } + } + /* else, just put it at the end */ + else { + *here = new_event; + new_event->next = NULL; + } + + /* Add to the list of outputs modified since last accepted timestep */ + if(! output_queue->modified[output_index]) { + output_queue->modified[output_index] = MIF_TRUE; + output_queue->modified_index[(output_queue->num_modified)++] = + output_index; + } + + /* Add to the list of outputs with events pending */ + if(! output_queue->pending[output_index]) { + output_queue->pending[output_index] = MIF_TRUE; + output_queue->pending_index[(output_queue->num_pending)++] = + output_index; + } +} + + + +/* +EVTqueue_inst + +This function places the specified inst event onto the inst +queue. + +The linked list in the queue for the specified inst is +searched beginning at the current head of the pending events +to find the location at which to insert the new event. The +events are ordered in the list by event_time. +*/ + + + + +void EVTqueue_inst( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index, /* The instance in question */ + double posted_time, /* The current time */ + double event_time) /* The time the event should happen */ +{ + + Evt_Inst_Queue_t *inst_queue; + Evt_Inst_Event_t **here; + Evt_Inst_Event_t *new_event; + Evt_Inst_Event_t *next; + + Mif_Boolean_t splice; + + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Update next_time in inst queue */ + if((inst_queue->num_pending <= 0) || + (event_time < inst_queue->next_time)) + inst_queue->next_time = event_time; + + /* Create a new event or get one from the free list and copy in data */ + if(inst_queue->free[inst_index]) { + new_event = inst_queue->free[inst_index]; + inst_queue->free[inst_index] = new_event->next; + } + else { + new_event = (void *) MALLOC(sizeof(Evt_Inst_Event_t)); + } + new_event->event_time = event_time; + new_event->posted_time = posted_time; + + /* Find location at which to insert event */ + splice = MIF_FALSE; + here = inst_queue->current[inst_index]; + while(*here) { + /* If there's an event with the same time, don't duplicate it */ + if(event_time == (*here)->event_time) + return; + else if(event_time < (*here)->event_time) { + splice = MIF_TRUE; + break; + } + here = &((*here)->next); + } + + /* If needs to be spliced into middle of existing list */ + if(splice) { + /* splice it in */ + next = *here; + *here = new_event; + new_event->next = next; + } + /* else, just put it at the end */ + else { + *here = new_event; + new_event->next = NULL; + } + + /* Add to the list of insts modified since last accepted timestep */ + if(! inst_queue->modified[inst_index]) { + inst_queue->modified[inst_index] = MIF_TRUE; + inst_queue->modified_index[(inst_queue->num_modified)++] = + inst_index; + } + + /* Add to the list of insts with events pending */ + if(! inst_queue->pending[inst_index]) { + inst_queue->pending[inst_index] = MIF_TRUE; + inst_queue->pending_index[(inst_queue->num_pending)++] = + inst_index; + } +} diff --git a/src/xspice/evt/evtsetup.c b/src/xspice/evt/evtsetup.c new file mode 100755 index 000000000..59aef7343 --- /dev/null +++ b/src/xspice/evt/evtsetup.c @@ -0,0 +1,578 @@ +/*============================================================================ +FILE EVTsetup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTsetup which clears/allocates the + event-driven queues and data structures immediately prior to a new + analysis. In addition, it places entries in the job list so that + results data from multiple analysis can be retrieved similar to + SPICE3C1 saving multiple 'plots'. + +INTERFACES + + int EVTsetup(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +#include "sperror.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" +#include "mifproto.h" +#include "evtproto.h" + + + + +static int EVTsetup_queues(CKTcircuit *ckt); +static int EVTsetup_data(CKTcircuit *ckt); +static int EVTsetup_jobs(CKTcircuit *ckt); +static int EVTsetup_load_ptrs(CKTcircuit *ckt); + + + + +/* Allocation macros with built-in check for out-of-memory */ +/* Adapted from SPICE 3C1 code in CKTsetup.c */ + +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + +#define CKREALLOC(var,size,type) \ + if((size) == 1) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } else if((size) > 1) { \ + if(!(var = (void *) REALLOC((void *) (var), (size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + + +/* +EVTsetup + +This function clears/allocates the event-driven queues and data structures +immediately prior to a new analysis. In addition, it places entries +in the job list so that results data from multiple analysis can be +retrieved similar to SPICE3C1 saving multiple 'plots'. +*/ + + +int EVTsetup( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int err; + + + /* Exit immediately if no event-driven instances in circuit */ + if(ckt->evt->counts.num_insts == 0) + return(OK); + + /* Clear the inst, node, and output queues, and initialize the to_call */ + /* elements in the instance queue to call all event-driven instances */ + err = EVTsetup_queues(ckt); + if(err) + return(err); + + /* Allocate and initialize the node, state, message, and statistics data */ + err = EVTsetup_data(ckt); + if(err) + return(err); + + /* Set the job pointers to the allocated results, states, messages, */ + /* and statistics so that data will be accessable after run */ + err = EVTsetup_jobs(ckt); + if(err) + return(err); + + /* Setup the pointers in the MIFinstance structure for inputs, outputs, */ + /* and total loads */ + err = EVTsetup_load_ptrs(ckt); + if(err) + return(err); + + /* Initialize additional event data */ + g_mif_info.circuit.evt_step = 0.0; + + /* Return OK */ + return(OK); +} + + + + +/* +EVTsetup_queues + +This function clears the event-driven queues in preparation for +a new simulation. +*/ + + +static int EVTsetup_queues( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int num_insts; + int num_nodes; + int num_outputs; + + Evt_Inst_Queue_t *inst_queue; + Evt_Node_Queue_t *node_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Inst_Event_t *inst_event; + Evt_Output_Event_t *output_event; + + void *ptr; + + /* ************************ */ + /* Clear the instance queue */ + /* ************************ */ + + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + for(i = 0; i < num_insts; i++) { + inst_event = inst_queue->head[i]; + while(inst_event) { + ptr = inst_event; + inst_event = inst_event->next; + FREE(ptr); + } + inst_event = inst_queue->free[i]; + while(inst_event) { + ptr = inst_event; + inst_event = inst_event->next; + FREE(ptr); + } + inst_queue->head[i] = NULL; + inst_queue->current[i] = &(inst_queue->head[i]); + inst_queue->last_step[i] = &(inst_queue->head[i]); + inst_queue->free[i] = NULL; + } + + inst_queue->next_time = 0.0; + inst_queue->last_time = 0.0; + + inst_queue->num_modified = 0; + inst_queue->num_pending = 0; + inst_queue->num_to_call = 0; + + for(i = 0; i < num_insts; i++) { + inst_queue->modified[i] = MIF_FALSE; + inst_queue->pending[i] = MIF_FALSE; + inst_queue->to_call[i] = MIF_FALSE; + } + + + /* ******************** */ + /* Clear the node queue */ + /* ******************** */ + + num_nodes = ckt->evt->counts.num_nodes; + node_queue = &(ckt->evt->queue.node); + + node_queue->num_changed = 0; + node_queue->num_to_eval = 0; + + for(i = 0; i < num_nodes; i++) { + node_queue->changed[i] = MIF_FALSE; + node_queue->to_eval[i] = MIF_FALSE; + } + + /* ********************** */ + /* Clear the output queue */ + /* ********************** */ + + num_outputs = ckt->evt->counts.num_outputs; + output_queue = &(ckt->evt->queue.output); + + for(i = 0; i < num_outputs; i++) { + output_event = output_queue->head[i]; + while(output_event) { + ptr = output_event; + output_event = output_event->next; + FREE(ptr); + } + output_event = output_queue->free[i]; + while(output_event) { + ptr = output_event; + output_event = output_event->next; + FREE(ptr); + } + output_queue->head[i] = NULL; + output_queue->current[i] = &(output_queue->head[i]); + output_queue->last_step[i] = &(output_queue->head[i]); + output_queue->free[i] = NULL; + } + + output_queue->next_time = 0.0; + output_queue->last_time = 0.0; + + output_queue->num_modified = 0; + output_queue->num_pending = 0; + output_queue->num_changed = 0; + + for(i = 0; i < num_outputs; i++) { + output_queue->modified[i] = MIF_FALSE; + output_queue->pending[i] = MIF_FALSE; + output_queue->changed[i] = MIF_FALSE; + } + + return(OK); +} + + + +/* +EVTsetup_data + +This function sets up the event-driven node, state, and +message data runtime structures in preparation for +a new simulation. +*/ + + +static int EVTsetup_data( + CKTcircuit *ckt) /* The circuit structure */ +{ + + Evt_Data_t *data; + + int i; + int j; + + int num_insts; + int num_ports; + int num_nodes; + + int udn_index; + int num_outputs; + + Mif_Boolean_t invert; + + Evt_Node_Data_t *node_data; + Evt_State_Data_t *state_data; + Evt_Msg_Data_t *msg_data; + /* Evt_Statistic_t *statistics_data;*/ + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Node_Info_t *node_info; + + + /* Allocate main substructures of data */ + /* Note that we don't free any old structures */ + /* since they are pointed to by jobs and need */ + /* to be maintained so that results from multiple */ + /* jobs are kept around like SPICE does */ + + data = &(ckt->evt->data); + CKALLOC(data->node, 1, Evt_Node_Data_t) + CKALLOC(data->state, 1, Evt_State_Data_t) + CKALLOC(data->msg, 1, Evt_Msg_Data_t) + CKALLOC(data->statistics, 1, Evt_Statistic_t) + + /* Allocate node data */ + + num_nodes = ckt->evt->counts.num_nodes; + node_data = data->node; + + CKALLOC(node_data->head, num_nodes, void *) + CKALLOC(node_data->tail, num_nodes, void *) + CKALLOC(node_data->last_step, num_nodes, void *) + CKALLOC(node_data->free, num_nodes, void *) + CKALLOC(node_data->modified_index, num_nodes, int) + CKALLOC(node_data->modified, num_nodes, Mif_Boolean_t) + CKALLOC(node_data->rhs, num_nodes, Evt_Node_t) + CKALLOC(node_data->rhsold, num_nodes, Evt_Node_t) + CKALLOC(node_data->total_load, num_nodes, double) + + /* Initialize the node data */ + + for(i = 0; i < num_nodes; i++) { + node_data->tail[i] = &(node_data->head[i]); + node_data->last_step[i] = &(node_data->head[i]); + } + + for(i = 0; i < num_nodes; i++) { + + /* Get pointers to rhs & rhsold, the user-defined node type index, */ + /* the number of outputs on the node and the invert flag */ + rhs = &(node_data->rhs[i]); + rhsold = &(node_data->rhsold[i]); + node_info = ckt->evt->info.node_table[i]; + udn_index = node_info->udn_index; + num_outputs = node_info->num_outputs; + invert = node_info->invert; + + /* Initialize the elements within rhs and rhsold */ + rhs->step = 0.0; + rhsold->step = 0.0; + if(num_outputs > 1) { + CKALLOC(rhs->output_value, num_outputs, void *) + CKALLOC(rhsold->output_value, num_outputs, void *) + for(j = 0; j < num_outputs; j++) { + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->output_value[j])); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->output_value[j]); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->output_value[j])); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->output_value[j]); + } + } + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->node_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->node_value); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->node_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->node_value); + if(invert) { + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->inverted_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->inverted_value); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->inverted_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->inverted_value); + } + + /* Initialize the total load value to zero */ + node_data->total_load[i] = 0.0; + } + + + /* Allocate and initialize state data */ + + num_insts = ckt->evt->counts.num_insts; + state_data = data->state; + + CKALLOC(state_data->head, num_insts, void *) + CKALLOC(state_data->tail, num_insts, void *) + CKALLOC(state_data->last_step, num_insts, void *) + CKALLOC(state_data->free, num_insts, void *) + CKALLOC(state_data->modified_index, num_insts, int) + CKALLOC(state_data->modified, num_insts, Mif_Boolean_t) + CKALLOC(state_data->total_size, num_insts, int) + CKALLOC(state_data->desc, num_insts, void *) + + for(i = 0; i < num_insts; i++) { + state_data->tail[i] = &(state_data->head[i]); + state_data->last_step[i] = &(state_data->head[i]); + } + + + /* Allocate and initialize msg data */ + + num_ports = ckt->evt->counts.num_ports; + msg_data = data->msg; + + CKALLOC(msg_data->head, num_ports, void *) + CKALLOC(msg_data->tail, num_ports, void *) + CKALLOC(msg_data->last_step, num_ports, void *) + CKALLOC(msg_data->free, num_ports, void *) + CKALLOC(msg_data->modified_index, num_ports, int) + CKALLOC(msg_data->modified, num_ports, Mif_Boolean_t) + + for(i = 0; i < num_ports; i++) { + msg_data->tail[i] = &(msg_data->head[i]); + msg_data->last_step[i] = &(msg_data->head[i]); + } + + /* Don't need to initialize statistics since they were */ + /* calloc'ed above */ + + return(OK); +} + + + + +/* +EVTsetup_jobs + +This function prepares the jobs data for a new simulation. +*/ + + +static int EVTsetup_jobs( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int num_jobs; + + Evt_Job_t *jobs; + Evt_Data_t *data; + + + jobs = &(ckt->evt->jobs); + data = &(ckt->evt->data); + + /* Increment the number of jobs */ + num_jobs = ++(jobs->num_jobs); + + /* Allocate/reallocate necessary pointers */ + CKREALLOC(jobs->job_name, num_jobs, void *) + CKREALLOC(jobs->node_data, num_jobs, void *) + CKREALLOC(jobs->state_data, num_jobs, void *) + CKREALLOC(jobs->msg_data, num_jobs, void *) + CKREALLOC(jobs->statistics, num_jobs, void *) + + /* Fill in the pointers, etc. for this new job */ + i = num_jobs - 1; + jobs->job_name[i] = MIFcopy((char *) ckt->CKTcurJob->JOBname); + jobs->node_data[i] = data->node; + jobs->state_data[i] = data->state; + jobs->msg_data[i] = data->msg; + jobs->statistics[i] = data->statistics; + + return(OK); +} + + + +/* +EVTsetup_load_ptrs + +This function setups up the required data in the MIFinstance +structure of event-driven and hybrid instances. +*/ + + +static int EVTsetup_load_ptrs( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int j; + int k; + + int num_insts; + int num_conn; + int num_port; + int num_outputs; + + int node_index; + int output_subindex; + + MIFinstance *fast; + + Mif_Conn_Data_t *conn; + + Mif_Port_Type_t type; + Mif_Port_Data_t *port; + + Evt_Node_Data_t *node_data; + + + /* This function setups up the required data in the MIFinstance */ + /* structure of event-driven and hybrid instances */ + + /* Loop through all event-driven and hybrid instances */ + num_insts = ckt->evt->counts.num_insts; + for(i = 0; i < num_insts; i++) { + + /* Get the MIFinstance pointer */ + fast = ckt->evt->info.inst_table[i]->inst_ptr; + + /* Loop through all connections */ + num_conn = fast->num_conn; + for(j = 0; j < num_conn; j++) { + + /* Skip if connection is null */ + if(fast->conn[j]->is_null) + continue; + + conn = fast->conn[j]; + + /* Loop through all ports */ + num_port = conn->size; + for(k = 0; k < num_port; k++) { + + /* Get port data pointer for quick access */ + port = conn->port[k]; + + if(port->is_null) + continue; + + /* Skip if port is not digital or user-defined type */ + type = port->type; + if((type != MIF_DIGITAL) && (type != MIF_USER_DEFINED)) + continue; + + /* Set input.pvalue to point to rhsold.node_value or to */ + /* rhsold.inverted_value as appropriate */ + node_index = port->evt_data.node_index; + node_data = ckt->evt->data.node; + if(conn->is_input) { + if(port->invert) { + port->input.pvalue = node_data->rhsold[node_index]. + inverted_value; + } + else { + port->input.pvalue = node_data->rhsold[node_index]. + node_value; + } + } + + /* Set output.pvalue to point to rhs.node_value or rhs.output_value[i] */ + /* where i is given by the output_subindex in output info */ + /* depending on whether more than one output is connected to the node. */ + /* Note that this is only for the DCOP analysis. During a transient */ + /* analysis, new structures will be created and the pointers will */ + /* be set by EVTload */ + if(conn->is_output) { + num_outputs = ckt->evt->info.node_table[node_index]->num_outputs; + if(num_outputs <= 1) { + port->output.pvalue = node_data->rhs[node_index]. + node_value; + } + else { + output_subindex = port->evt_data.output_subindex; + port->output.pvalue = node_data->rhs[node_index]. + output_value[output_subindex]; + } + } + + } /* end for number of ports */ + } /* end for number of connections */ + } /* end for number of insts */ + + return(OK); +} diff --git a/src/xspice/evt/evttermi.c b/src/xspice/evt/evttermi.c new file mode 100755 index 000000000..7922ad817 --- /dev/null +++ b/src/xspice/evt/evttermi.c @@ -0,0 +1,512 @@ +/*============================================================================ +FILE EVTtermInsert.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTtermInsert which is called by + MIF_INP2A during the parsing of the input deck. EVTtermInsert is + similar to SPICE3's INPtermInsert except that it is used when the node + type is event-driven. Calls to this function build the info lists + for instances, nodes, outputs, and ports. The completion of the info + struct is carried out by EVTinit following the parsing of all + instances in the deck. + +INTERFACES + + void EVTtermInsert( + CKTcircuit *ckt, + MIFinstance *fast, + char *node_name, + char *type_name, + int conn_num, + int port_num, + char **err_msg) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" + + +static void EVTinst_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int *inst_index, + char **err_msg); + +static void EVTnode_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + char *node_name, + char *type_name, + int conn_num, + int port_num, + int *node_index, + int *output_subindex, + char **err_msg); + +static void EVTport_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + int node_index, + char *node_name, + int conn_num, + int port_num, + int *port_index, + char **err_msg); + +static void EVToutput_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + int node_index, + int port_index, + int output_subindex, + int conn_num, + int port_num, + char **err_msg); + + +/* +EVTtermInsert + +This function is called by MIF_INP2A during the parsing of the input +deck. EVTtermInsert is similar to 3C1's INPtermInsert except that it is +used when the node type is event-driven. Calls to this function build +the info lists for instances, nodes, outputs, and ports. The +completion of the info struct is carried out by EVTinit following +the parsing of all instances in the deck. +*/ + + +void EVTtermInsert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + char *node_name, /* The node name */ + char *type_name, /* The type of node */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 for scalar ports */ + char **err_msg) /* Returned error message if any */ +{ + + int inst_index; + int node_index; + int port_index; + + int output_subindex; + + + /* Get the instance index and create new entry in inst */ + /* info list if this is a new instance. */ + EVTinst_insert(ckt, fast, &inst_index, err_msg); + if(*err_msg) + return; + + /* Get the node index and create new entry in node info */ + /* list if this is a new node */ + EVTnode_insert(ckt, fast, inst_index, node_name, type_name, + conn_num, port_num, &node_index, &output_subindex, + err_msg); + if(*err_msg) + return; + + /* Create new entry in port info list and return port index */ + EVTport_insert(ckt, fast, inst_index, node_index, node_name, conn_num, + port_num, &port_index, err_msg); + if(*err_msg) + return; + + /* Create new entry in output info list if appropriate */ + if(fast->conn[conn_num]->is_output) { + EVToutput_insert(ckt, fast, inst_index, node_index, port_index, + output_subindex, conn_num, port_num, err_msg); + if(*err_msg) + return; + } +} + + + + +/* +EVTinst_insert + +This function locates or creates a new entry for the specified +instance in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTinst_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int *inst_index, /* The index found or added */ + char **err_msg) /* Error message if any */ +{ + + Mif_Boolean_t found; + int index; + + Evt_Inst_Info_t *inst; + Evt_Inst_Info_t **inst_ptr; + + + /* Scan list of instances in event structure to see if already there */ + /* and get the index */ + found = MIF_FALSE; + index = 0; + inst = ckt->evt->info.inst_list; + inst_ptr = &(ckt->evt->info.inst_list); + + while(inst) { + if(inst->inst_ptr == fast) { + found = MIF_TRUE; + break; + } + else { + index++; + inst_ptr = &(inst->next); + inst = inst->next; + } + } + + + /* If not found, create a new entry in list and increment the */ + /* instance count in the event structure */ + if(! found) { + *inst_ptr = (void *) MALLOC(sizeof(Evt_Inst_Info_t)); + inst = *inst_ptr; + inst->next = NULL; + inst->inst_ptr = fast; + index = ckt->evt->counts.num_insts; + (ckt->evt->counts.num_insts)++; + } + + /* Record the inst index in the MIFinstance structure and return it */ + fast->inst_index = index; + *inst_index = index; +} + + + +/* +EVTnode_insert + +This function locates or creates a new entry for the specified +node in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTnode_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + char *node_name, /* The node name */ + char *type_name, /* The node type specified */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + int *node_index, /* The node index found or added */ + int *output_subindex, /* The output number on this node */ + char **err_msg) /* Error message text if any */ +{ + + int i; + int udn_index=0; + Mif_Boolean_t found; + + int index; + + Evt_Node_Info_t *node; + Evt_Node_Info_t **node_ptr; + + Evt_Inst_Index_t *inst; + Evt_Inst_Index_t **inst_ptr; + + + /* *************************************** */ + /* Get and check the node type information */ + /* *************************************** */ + + /* Scan the list of user-defined node types and get the index */ + found = MIF_FALSE; + for(i = 0; i < g_evt_num_udn_types; i++) { + if(strcmp(type_name, g_evt_udn_info[i]->name) == 0) { + udn_index = i; + found = MIF_TRUE; + break; + } + } + + /* Report error if not recognized */ + if(! found) { + *err_msg = "Unrecognized connection type"; + return; + } + + /* If inverted, check to be sure invert function exists for type */ + if(fast->conn[conn_num]->port[port_num]->invert) { + if(g_evt_udn_info[udn_index]->invert == NULL) { + *err_msg = "Connection type cannot be inverted"; + return; + } + } + + + /* ******************************************* */ + /* Find/create entry in event-driven node list */ + /* ******************************************* */ + + /* Scan list of nodes in event structure to see if already there */ + /* and get the index */ + found = MIF_FALSE; + index = 0; + node = ckt->evt->info.node_list; + node_ptr = &(ckt->evt->info.node_list); + + while(node) { + if(strcmp(node_name, node->name) == 0) { + found = MIF_TRUE; + break; + } + else { + index++; + node_ptr = &(node->next); + node = node->next; + } + } + + + /* If found, verify that connection type is same as type of node */ + if(found) { + if(udn_index != node->udn_index) { + *err_msg = "Node cannot have two different types"; + return; + } + } + + /* If not found, create a new entry in list and increment the */ + /* node count in the event structure */ + if(! found) { + *node_ptr = (void *) MALLOC(sizeof(Evt_Node_Info_t)); + node = *node_ptr; + node->next = NULL; + node->name = MIFcopy(node_name); + node->udn_index = udn_index; + index = ckt->evt->counts.num_nodes; + (ckt->evt->counts.num_nodes)++; + } + + + /* ******************************************* */ + /* Update remaining items in node list struct */ + /* ******************************************* */ + + /* Update flag on node that indicates if inversion is used by any */ + /* instance inputs */ + if(fast->conn[conn_num]->is_input) + if(! node->invert) + node->invert = fast->conn[conn_num]->port[port_num]->invert; + + /* Increment counts of ports, outputs connected to node */ + (node->num_ports)++; + if(fast->conn[conn_num]->is_output) + (node->num_outputs)++; + + /* If this is an input, add instance to list if not already there */ + if(fast->conn[conn_num]->is_input) { + + found = MIF_FALSE; + inst = node->inst_list; + inst_ptr = &(node->inst_list); + + while(inst) { + if(inst_index == inst->index) { + found = MIF_TRUE; + break; + } + else { + inst_ptr = &(inst->next); + inst = inst->next; + } + } + + if(! found) { + (node->num_insts)++; + *inst_ptr = (void *) MALLOC(sizeof(Evt_Inst_Index_t)); + inst = *inst_ptr; + inst->next = NULL; + inst->index = inst_index; + } + } + + /* Record the node index in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.node_index = index; + + /* Return the node index */ + *node_index = index; + if(fast->conn[conn_num]->is_output) + *output_subindex = node->num_outputs - 1; + else + *output_subindex = 0; /* just for safety - shouldn't need this */ +} + + + +/* +EVTport_insert + +This function locates or creates a new entry for the specified +port in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTport_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + int node_index, /* The index of the node in evt structures */ + char *node_name, /* The node name */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + int *port_index, /* The port index found or added */ + char **err_msg) /* Error message text if any */ +{ + + Evt_Port_Info_t *port; + Evt_Port_Info_t **port_ptr; + + int index; + + /* Find the end of the port info list */ + port = ckt->evt->info.port_list; + port_ptr = &(ckt->evt->info.port_list); + + index = 0; + while(port) { + port_ptr = &(port->next); + port = port->next; + index++; + } + + + /* Update the port count and create a new entry in the list */ + + (ckt->evt->counts.num_ports)++; + + *port_ptr = (void *) MALLOC(sizeof(Evt_Port_Info_t)); + port = *port_ptr; + + /* Fill in the elements */ + port->next = NULL; + port->inst_index = inst_index; + port->node_index = node_index; + port->node_name = MIFcopy(node_name); + port->inst_name = MIFcopy((char *) fast->MIFname); + port->conn_name = MIFcopy((char *) fast->conn[conn_num]->name); + port->port_num = port_num; + + /* Record the port index in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.port_index = index; + + /* Return the port index */ + *port_index = index; +} + + + + +/* +EVToutput_insert + +This function locates or creates a new entry for the specified +output in the event-driven ``info'' structures during parsing. +*/ + + +static void EVToutput_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + int node_index, /* The index of the node in evt structures */ + int port_index, /* The index of the port in the evt structures */ + int output_subindex, /* The output on this node */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + char **err_msg) /* Error message text if any */ +{ + Evt_Output_Info_t *output; + Evt_Output_Info_t **output_ptr; + + int index; + + /* Find the end of the port info list */ + output = ckt->evt->info.output_list; + output_ptr = &(ckt->evt->info.output_list); + + index = 0; + while(output) { + output_ptr = &(output->next); + output = output->next; + index++; + } + + + /* Update the port count and create a new entry in the list */ + + (ckt->evt->counts.num_outputs)++; + + *output_ptr = (void *) MALLOC(sizeof(Evt_Output_Info_t)); + output = *output_ptr; + + /* Fill in the elements */ + output->next = NULL; + output->inst_index = inst_index; + output->node_index = node_index; + output->port_index = port_index; + output->output_subindex = output_subindex; + + /* Record the output index and subindex in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.output_index = index; + fast->conn[conn_num]->port[port_num]->evt_data.output_subindex + = output_subindex; + +} diff --git a/src/xspice/examples/analog_models1_ac.deck b/src/xspice/examples/analog_models1_ac.deck new file mode 100755 index 000000000..6467c2bd2 --- /dev/null +++ b/src/xspice/examples/analog_models1_ac.deck @@ -0,0 +1,67 @@ +Code Model Test - AC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 1.0 AC 1.0 0.0 +* +v3 3 0 DC 2.0 +* +v4 4 0 0.5 AC 0.5 0.0 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 3] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** divider block *** +a4 1 3 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 4 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[-1.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +r4 4 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_dc.deck b/src/xspice/examples/analog_models1_dc.deck new file mode 100755 index 000000000..4ad6f0c83 --- /dev/null +++ b/src/xspice/examples/analog_models1_dc.deck @@ -0,0 +1,61 @@ +Code Model Test - DC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_swept_dc.deck b/src/xspice/examples/analog_models1_swept_dc.deck new file mode 100755 index 000000000..e211325bd --- /dev/null +++ b/src/xspice/examples/analog_models1_swept_dc.deck @@ -0,0 +1,61 @@ +Code Model Test - Swept DC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.dc v1 .1 10 .1 +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_transient.deck b/src/xspice/examples/analog_models1_transient.deck new file mode 100755 index 000000000..a4967d275 --- /dev/null +++ b/src/xspice/examples/analog_models1_transient.deck @@ -0,0 +1,62 @@ +Code Model Test - Transient: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.tran .1s 10s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 10 10) +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=0.1 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_ac.deck b/src/xspice/examples/analog_models2_ac.deck new file mode 100755 index 000000000..0b450c809 --- /dev/null +++ b/src/xspice/examples/analog_models2_ac.deck @@ -0,0 +1,66 @@ +Code Model Test - Swept DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=628.0) +* +* +* +*** magnetic core & inductive coupling *** +v40 45 46 0.0 +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 46) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1e12 +r20 20 0 1e12 +r30 30 0 1e12 +r40 40 0 1e12 +r45 45 0 1e12 +r50 50 0 1e12 +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_dc.deck b/src/xspice/examples/analog_models2_dc.deck new file mode 100755 index 000000000..66a612e46 --- /dev/null +++ b/src/xspice/examples/analog_models2_dc.deck @@ -0,0 +1,63 @@ +Code Model Test - DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +* +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.425625 1.516203] int_ic=[0.0 0.0] ++ denormalized_freq=6283.2) +* +* +*** magnetic core & inductive coupling *** +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 45) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_swept_dc.deck b/src/xspice/examples/analog_models2_swept_dc.deck new file mode 100755 index 000000000..470d11c81 --- /dev/null +++ b/src/xspice/examples/analog_models2_swept_dc.deck @@ -0,0 +1,64 @@ +Code Model Test - Swept DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.dc v1 .1 10 .5 +* +*** input sources *** +v1 1 0 DC 2 +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=1.0) +* +* +* +*** magnetic core & inductive coupling *** +v40 45 46 0.0 +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 46) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1e12 +r20 20 0 1e12 +r30 30 0 1e12 +r40 40 0 1e12 +r45 45 0 1e12 +r50 50 0 1e12 +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_transient.deck b/src/xspice/examples/analog_models2_transient.deck new file mode 100755 index 000000000..c9efac726 --- /dev/null +++ b/src/xspice/examples/analog_models2_transient.deck @@ -0,0 +1,67 @@ +Code Model Test - Transient: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.tran .1s 10s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 10 10) +* +v2 2 0 DC PWL(0 0 0.1 0.1 0.2 0.9 0.3 1.0 10 1.0) +* +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 2 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=1.0) +* +* +* +*** magnetic core & inductive coupling *** +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 45) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_ac.deck b/src/xspice/examples/analog_models3_ac.deck new file mode 100755 index 000000000..3c4d7fa96 --- /dev/null +++ b/src/xspice/examples/analog_models3_ac.deck @@ -0,0 +1,81 @@ +Code Model Test - AC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_dc.deck b/src/xspice/examples/analog_models3_dc.deck new file mode 100755 index 000000000..cff617cdc --- /dev/null +++ b/src/xspice/examples/analog_models3_dc.deck @@ -0,0 +1,79 @@ +Code Model Test - DC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-3 i_sink_range=1.0e-3 ++ r_out_domain=1.0e-3) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_swept_dc.deck b/src/xspice/examples/analog_models3_swept_dc.deck new file mode 100755 index 000000000..394e7aac1 --- /dev/null +++ b/src/xspice/examples/analog_models3_swept_dc.deck @@ -0,0 +1,79 @@ +Code Model Test - DC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.dc v1 .1 15 .5 +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_transient.deck b/src/xspice/examples/analog_models3_transient.deck new file mode 100755 index 000000000..c92ffff96 --- /dev/null +++ b/src/xspice/examples/analog_models3_transient.deck @@ -0,0 +1,80 @@ +Code Model Test - Transient: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.tran .1s 15s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 15 15) +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_ac.deck b/src/xspice/examples/analog_models4_ac.deck new file mode 100755 index 000000000..70413e1ad --- /dev/null +++ b/src/xspice/examples/analog_models4_ac.deck @@ -0,0 +1,78 @@ +Code Model Test - DC: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 10K +a4 (40 0) zener1 +.model zener1 zener (v_breakdown=10.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_dc.deck b/src/xspice/examples/analog_models4_dc.deck new file mode 100755 index 000000000..b3254ea5e --- /dev/null +++ b/src/xspice/examples/analog_models4_dc.deck @@ -0,0 +1,74 @@ +Code Model Test - DC: sine triangle aswitch zener oneshot +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 10K +a4 (40 0) zener1 +.model zener1 zener (v_breakdown=10.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_swept_dc.deck b/src/xspice/examples/analog_models4_swept_dc.deck new file mode 100755 index 000000000..d06fe05f8 --- /dev/null +++ b/src/xspice/examples/analog_models4_swept_dc.deck @@ -0,0 +1,72 @@ +Code Model Test - Swept DC: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.dc v1 .1 15 .5 +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 100 +a4 (0 40) zener1 +.model zener1 zener (v_breakdown=8.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + diff --git a/src/xspice/examples/analog_models4_transient.deck b/src/xspice/examples/analog_models4_transient.deck new file mode 100755 index 000000000..9ad60efee --- /dev/null +++ b/src/xspice/examples/analog_models4_transient.deck @@ -0,0 +1,76 @@ +Code Model Test - Transient: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.tran .01ms 2ms +* +*** input sources *** +* +v1 1 0 DC 0.0 PWL(0 0 2e-3 2) +* +v2 2 0 DC 0.0 PWL(0 0 2e-3 10) +* +v3 3 0 DC 0.0 PWL(0 0.0 0.9e-3 0.0 1e-3 1.0 1.9e-3 1.0 2e-3 0.0 2.9e-3 0.0) +* +v4 4 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 10.0 20.0] ++ freq_array=[500 500 2000 2000] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 10.0 20.0] ++ freq_array=[500 500 10000 10000] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.5) +* +* +*** aswitch block *** +a3 1 (4 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +rzener 2 40 100 +a4 (0 40) zener1 +.model zener1 zener (v_breakdown=9.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 0 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[2.0e-3 2.0e-3 0.1e-3 0.1e-3] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +r4 4 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + diff --git a/src/xspice/examples/arbitrary_phase.deck b/src/xspice/examples/arbitrary_phase.deck new file mode 100755 index 000000000..3ef68bc29 --- /dev/null +++ b/src/xspice/examples/arbitrary_phase.deck @@ -0,0 +1,17 @@ +Arbitrary Phase SIN and PULSE Sources +* +* This circuit generates two cycles of sine and square waves +* beginning at +45 degrees. +* +* Phase shift is specified after Berkeley defined parameters +* on the independent source cards. +* +.tran 2e-5 2e-3 +* +v1 1 0 0.0 sin(0 1 1k 0 0 45.0) +r1 1 0 1k +* +v2 2 0 0.0 pulse(-1 1 0 1e-5 1e-5 5e-4 1e-3 45.0) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_io.deck b/src/xspice/examples/bad_io.deck new file mode 100755 index 000000000..74678115c --- /dev/null +++ b/src/xspice/examples/bad_io.deck @@ -0,0 +1,17 @@ +Invalid number of inputs/outputs +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the number of +* connections on the code model is incorrect. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 3 gain_block +a2 1 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_io_type.deck b/src/xspice/examples/bad_io_type.deck new file mode 100755 index 000000000..41e64ad59 --- /dev/null +++ b/src/xspice/examples/bad_io_type.deck @@ -0,0 +1,25 @@ +Invalid input/output type +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if an invalid type +* is used with the code model connections. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +* Both connections on the gain block must be analog, but +* the second is specified as digital +* +a1 1 %d 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +* Node 1 below should be a digital node, but is an analog node +* +a2 [1] [3] dac +.model dac dac_bridge +r3 3 0 1k +* +.end diff --git a/src/xspice/examples/bad_name.deck b/src/xspice/examples/bad_name.deck new file mode 100755 index 000000000..0380cac2e --- /dev/null +++ b/src/xspice/examples/bad_name.deck @@ -0,0 +1,16 @@ +Unknown code model name +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the code model name +* is incorrect. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block this_model_doesnt_exist (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_param.deck b/src/xspice/examples/bad_param.deck new file mode 100755 index 000000000..22a2aa2f0 --- /dev/null +++ b/src/xspice/examples/bad_param.deck @@ -0,0 +1,16 @@ +Unknown code model parameter +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the .model card +* references a parameter that doesn't exist +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (this_parameter_doesnt_exist=2 gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_param_type.deck b/src/xspice/examples/bad_param_type.deck new file mode 100755 index 000000000..f110feda5 --- /dev/null +++ b/src/xspice/examples/bad_param_type.deck @@ -0,0 +1,16 @@ +Invalid parameter type +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the parameter value +* is invalid. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=false) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/d_to_real/Makefile b/src/xspice/examples/d_to_real/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/d_to_real/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/d_to_real/cfunc.mod b/src/xspice/examples/d_to_real/cfunc.mod new file mode 100755 index 000000000..079fc78ca --- /dev/null +++ b/src/xspice/examples/d_to_real/cfunc.mod @@ -0,0 +1,43 @@ +/* $Id$ */ + +void ucm_d_to_real (ARGS) +{ + + Digital_State_t in; + + double *out; + double delay; + double zero; + double one; + double ena; + + + in = INPUT_STATE(in); + if(PORT_NULL(enable)) + ena = 1.0; + else if(INPUT_STATE(enable) == ONE) + ena = 1.0; + else + ena = 0.0; + out = OUTPUT(out); + + zero = PARAM(zero); + one = PARAM(one); + delay = PARAM(delay); + + + if(in == ZERO) + *out = zero * ena; + else if(in == UNKNOWN) + *out = (zero + one) / 2.0 * ena; + else + *out = one * ena; + + if(TIME > 0.0) + OUTPUT_DELAY(out) = delay; + +} + + + + diff --git a/src/xspice/examples/d_to_real/ifspec.ifs b/src/xspice/examples/d_to_real/ifspec.ifs new file mode 100755 index 000000000..eb2300c82 --- /dev/null +++ b/src/xspice/examples/d_to_real/ifspec.ifs @@ -0,0 +1,32 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: d_to_real +C_Function_Name: ucm_d_to_real +Description: "Node bridge from digital to real with enable" + + +PORT_TABLE: + +Port_Name: in enable out +Description: "input" "enable" "output" +Direction: in in out +Default_Type: d d real +Allowed_Types: [d] [d] [real] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: no yes no + + +PARAMETER_TABLE: + +Parameter_Name: zero one delay +Description: "value for 0" "value for 1" "delay" +Data_Type: real real real +Default_Value: 0.0 1.0 1e-9 +Limits: - - [1e-15 -] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: yes yes yes + diff --git a/src/xspice/examples/diffpair.in b/src/xspice/examples/diffpair.in new file mode 100755 index 000000000..da321ce89 --- /dev/null +++ b/src/xspice/examples/diffpair.in @@ -0,0 +1,28 @@ +difpair ckt - simple differential pair +*.width in=72 +.opt acct list node lvlcod=2 +*.tf v(5) vin +*.dc vin -0.25 0.25 0.005 +*.ac dec 10 1 10ghz +.tran 5ns 500ns +vin 1 0 sin(0 0.1 5meg) ac 1 +vcc 8 0 12 +vee 9 0 -12 +q1 4 2 6 qnl +q2 5 3 6 qnl +rs1 1 2 1k +rs2 3 0 1k +rc1 4 8 10k +rc2 5 8 10k +q3 6 7 9 qnl +q4 7 7 9 qnl +rbias 7 8 20k +.model qnl npn(bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf cjc=2pf ++ va=50) +.print dc v(4) v(5) +.plot dc v(5) +.print ac vm(5) vp(5) +.plot ac vm(5) vp(5) +.print tran v(4) v(5) +.plot tran v(5) +.end diff --git a/src/xspice/examples/digital_invert.deck b/src/xspice/examples/digital_invert.deck new file mode 100755 index 000000000..08d7f6e91 --- /dev/null +++ b/src/xspice/examples/digital_invert.deck @@ -0,0 +1,23 @@ +Digital inversions +* +.tran 1e-8 1e-6 +* +v1 1 0 0.0 pulse(0 1 0 1e-8 1e-8 0.25e-6 0.5e-6) +r1 1 0 1k +* +a1 [1] [2] adc +.model adc adc_bridge +* +a2 2 3 inv +a3 2 ~4 inv +a4 ~2 5 inv +a5 ~2 ~6 inv +.model inv d_inverter +* +a6 [2 ~4] 7 nand +.model nand d_nand +* +a8 [2 3 4 5 6 7] [12 13 14 15 16 17] dac +.model dac dac_bridge +* +.end diff --git a/src/xspice/examples/digital_models.deck b/src/xspice/examples/digital_models.deck new file mode 100755 index 000000000..39f737278 --- /dev/null +++ b/src/xspice/examples/digital_models.deck @@ -0,0 +1,20 @@ +Digital models +* +* This circuit contains a nand gate oscillator enabled by +* a pulse input after 20nS. Node 1 is an analog node. +* Nodes 2 and 3 are digital nodes. +* +.tran 1e-8 1e-7 +* +v1 1 0 0.0 pulse(0 1 2e-8 1e-9 1e-9) +* +r1 1 0 1k +* +a1 [1] [2] atod1 +.model atod1 adc_bridge (in_low=0.25 in_high=0.75 ++ rise_delay=1e-9 fall_delay=1e-9) +* +a2 [2 3] 3 nand +.model nand d_nand (rise_delay=1e-9 fall_delay=1e-9) +* +.end diff --git a/src/xspice/examples/digital_models1.deck b/src/xspice/examples/digital_models1.deck new file mode 100755 index 000000000..c78fa45ad --- /dev/null +++ b/src/xspice/examples/digital_models1.deck @@ -0,0 +1,77 @@ +Code Model Test: buffer, inverter, and, nand, or, nor, xor, xnor +* +* +*** analysis type *** +.tran .01s 4s +* +*** input sources *** +* +v2 200 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +v1 100 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge blocks *** +aconverter [200 100] [2 1] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** buffer block *** +a1 1 10 d_buffer1 +.model d_buffer1 d_buffer (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** inverter block *** +a2 1 20 d_inv1 +.model d_inv1 d_inverter (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** and block *** +a3 [1 2] 30 d_and1 +.model d_and1 d_and (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** nand block *** +a4 [1 2] 40 d_nand1 +.model d_nand1 d_nand (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** or block *** +a5 [1 2] 50 d_or1 +.model d_or1 d_or (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** nor block *** +a6 [1 2] 60 d_nor1 +.model d_nor1 d_nor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** xor block *** +a7 [1 2] 70 d_xor1 +.model d_xor1 d_xor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** xnor block *** +a8 [1 2] 80 d_xnor1 +.model d_xnor1 d_xnor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +* +* +* +.end diff --git a/src/xspice/examples/digital_models2.deck b/src/xspice/examples/digital_models2.deck new file mode 100755 index 000000000..9f6244073 --- /dev/null +++ b/src/xspice/examples/digital_models2.deck @@ -0,0 +1,91 @@ +Code Model Test: d flip-flop, jk flip-flop, toggle ff, set-reset ff +* +* +*** analysis type *** +.tran .01s 4s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) (4.0 1.0) ) +* +* +vset 400 0 DC 0.0 +* +* +vreset 500 0 DC PWL( (0 0.0) (3.8 0.0) (3.80000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge blocks *** +aconverter [100 200 300 400 500] [1 2 3 4 5] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** d flip-flop block *** +a1 1 3 4 5 10 11 d_dff1 +.model d_dff1 d_dff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ data_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** jk flip-flop block *** +a2 1 2 3 4 5 20 21 d_jkff1 +.model d_jkff1 d_jkff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ jk_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** toggle flip-flop block *** +a3 1 3 4 5 30 31 d_tff1 +.model d_tff1 d_tff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ t_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** set-reset flip-flop block *** +a4 1 2 3 4 5 40 41 d_srff1 +.model d_srff1 d_srff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ sr_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +r3 300 0 1k +r4 400 0 1k +r5 500 0 1k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/digital_models3.deck b/src/xspice/examples/digital_models3.deck new file mode 100755 index 000000000..b9173ece7 --- /dev/null +++ b/src/xspice/examples/digital_models3.deck @@ -0,0 +1,92 @@ +Code Model Test: d latch, set-reset latch, frequency divider +* +* +*** analysis type *** +.tran .01s 8s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) ++ (4.0 1.0) (4.00000000001 0.0) ++ (4.5 0.0) (4.50000000001 1.0) ++ (5.0 1.0) (5.00000000001 0.0) ++ (5.5 0.0) (5.50000000001 1.0) ++ (6.0 1.0) (6.00000000001 0.0) ++ (6.5 0.0) (6.50000000001 1.0) ++ (7.0 1.0) (7.00000000001 0.0) ++ (7.5 0.0) (7.50000000001 1.0) (4.0 1.0) ) +* +* +vset 400 0 DC 0.0 +* +* +vreset 500 0 DC PWL( (0 0.0) (3.8 0.0) (3.80000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge block *** +aconverter [100 200 300 400 500] [1 2 3 4 5] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** d latch block *** +a1 1 3 4 5 10 11 d_dlatch1 +.model d_dlatch1 d_dlatch (data_delay=1.0e-6 enable_delay=2.0e-6 ++ set_delay=3.0e-6 reset_delay=4.0e-6 ++ ic=0 ++ rise_delay=5.0e-6 fall_delay=6.0e-6 ++ data_load=1.0e-12 enable_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** set-reset latch block *** +a2 1 2 3 4 5 20 21 d_srlatch1 +.model d_srlatch1 d_srlatch (sr_delay=1.0e-6 enable_delay=2.0e-6 ++ set_delay=3.0e-6 reset_delay=4.0e-6 ++ ic=0 ++ rise_delay=5.0e-6 fall_delay=6.0e-6 ++ sr_load=1.0e-12 enable_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** frequency divider block *** +a3 3 30 d_fdiv1 +.model d_fdiv1 d_fdiv (div_factor=3 high_cycles=2 ++ i_count=0 rise_delay=1.0e-6 fall_delay=2.0e-6 ++ freq_in_load=1.0e-12) +* +* +* +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +r3 300 0 1k +r4 400 0 1k +r5 500 0 1k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/digital_models4.deck b/src/xspice/examples/digital_models4.deck new file mode 100755 index 000000000..8a328c922 --- /dev/null +++ b/src/xspice/examples/digital_models4.deck @@ -0,0 +1,91 @@ +Code Model Test: State Machine, RAM +* +* +*** analysis type *** +.tran .01s 8s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ++ (3.5000000001 0.0) (4 0.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) ++ (4.0 1.0) (4.00000000001 0.0) ++ (4.5 0.0) (4.50000000001 1.0) ++ (5.0 1.0) (5.00000000001 0.0) ++ (5.5 0.0) (5.50000000001 1.0) ++ (6.0 1.0) (6.00000000001 0.0) ++ (6.5 0.0) (6.50000000001 1.0) ++ (7.0 1.0) (7.00000000001 0.0) ++ (7.5 0.0) (7.50000000001 1.0) (4.0 1.0) ) +* +vaddr1 400 0 DC 0 +* +* +vaddr2 500 0 DC PWL( (0 0.0) (0.6 0.0) (0.60000000001 1.0) ++ (0.9 1.0) (0.90000000001 0.0) ++ (2.6 0.0) (2.60000000001 1.0) ++ (2.9 1.0) (2.90000000001 0.0) (3.0 0.0) ) +* +* +* +vselect 600 0 DC PWL( (0 0.0) (1.0 0.0) (2.0000000001 1.0) (2 1.0) ) +* +* +* +* +* +*** adc_bridge block *** +aconverter [100 200 300 400 500 600] [1 2 3 4 5 6] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** state machine block *** +a1 [1 2] 3 4 [10 11] d_state1 +.model d_state1 d_state (clk_delay=1.0e-6 reset_delay=2.0e-6 ++ state_file=state.txt reset_state=0 ++ input_load=1.0e-12 clk_load=1.0e-12 ++ reset_load=1.0e-12) +* +* +*** RAM block *** +a2 [1 2] [20 21] [3 4] 5 [6] d_ram1 +.model d_ram1 d_ram (select_value=1 ic=0 ++ read_delay=1.0e-6 data_load=1.0e-12 ++ address_load=1.0e-12 select_load=1.0e-12 ++ enable_load=1.0e-12) +* +* +* +* +* +*** resistors to ground *** +r1 100 0 10k +r2 200 0 10k +r3 300 0 10k +r4 400 0 10k +r5 500 0 10k +r6 600 0 10k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/dot_model_ref.deck b/src/xspice/examples/dot_model_ref.deck new file mode 100755 index 000000000..159d144f8 --- /dev/null +++ b/src/xspice/examples/dot_model_ref.deck @@ -0,0 +1,19 @@ +Model card reference +* +* This circuit contains simple gain blocks that share a +* single .model card. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +r2 2 0 1k +* +a2 1 3 gain_block +r3 3 0 1k +* +.model gain_block gain (in_offset = 1 gain=10) +* +.end diff --git a/src/xspice/examples/hybrid_models1_dc.deck b/src/xspice/examples/hybrid_models1_dc.deck new file mode 100755 index 000000000..190fb0be6 --- /dev/null +++ b/src/xspice/examples/hybrid_models1_dc.deck @@ -0,0 +1,46 @@ +Code Model Test - DC: d_osc, dac_bridge, adc_bridge +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** d_osc block *** +a1 1 10 d_osc1 +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[100 100 1000 1000] ++ duty_cycle=0.5 init_phase=0.0 ++ rise_delay=1.0e-6 fall_delay=2.0e-6) +* +*** dac_bridge block *** +a2 [10] [20] dac_bridge1 +.model dac_bridge1 dac_bridge (out_low=0.5 out_high=4.5 out_undef=1.8 ++ input_load=1.0e-12 ++ t_rise=1.0e-6 t_fall=2.0e-6) +* +* +*** adc_bridge block *** +a3 [2] [30] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.7 in_high=2.4 ++ rise_delay=1.0e-12 fall_delay=2.0e-12) +* +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r20 20 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/hybrid_models1_transient.deck b/src/xspice/examples/hybrid_models1_transient.deck new file mode 100755 index 000000000..e09b15ad4 --- /dev/null +++ b/src/xspice/examples/hybrid_models1_transient.deck @@ -0,0 +1,48 @@ +Code Model Test - Transient: d_osc, dac_bridge, adc_bridge +* +* +*** analysis type *** +.tran .01s 1s +* +*** input sources *** +* +v1 1 0 DC PWL( (0 0.0) (1 1.0) ) +* +v2 2 0 DC PWL( (0 0.0) (1 5.0) ) +* +* +*** d_osc block *** +a1 1 10 d_osc1 +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 8.0 8.0] ++ duty_cycle=0.5 init_phase=0.0 ++ rise_delay=1.0e-6 fall_delay=2.0e-6) +* +*** dac_bridge block *** +a2 [10] [20] dac_bridge1 +.model dac_bridge1 dac_bridge (out_low=0.5 out_high=4.5 out_undef=1.8 ++ input_load=1.0e-12 ++ t_rise=1.0e-6 t_fall=2.0e-6) +* +* +*** adc_bridge block *** +a3 [2] [30] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.7 in_high=2.4 ++ rise_delay=1.0e-12 fall_delay=2.0e-12) +* +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r20 20 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/initial_conditions.deck b/src/xspice/examples/initial_conditions.deck new file mode 100755 index 000000000..610383625 --- /dev/null +++ b/src/xspice/examples/initial_conditions.deck @@ -0,0 +1,19 @@ +Capacitor and inductor with natural initial conditions +* +* This circuit contains a capacitor and an inductor with +* initial conditions on them. Each of the components +* has a parallel resistor so that an exponential decay +* of the initial condition occurs with a time constant of +* 1 second. +* +.tran 0.1 5 +* +a1 1 0 cap +.model cap capacitor (c=1000uf ic=1) +r1 1 0 1k +* +a2 2 0 ind +.model ind inductor (l=1H ic=1) +r2 2 0 1.0 +* +.end diff --git a/src/xspice/examples/io_ordering.deck b/src/xspice/examples/io_ordering.deck new file mode 100755 index 000000000..8cc245526 --- /dev/null +++ b/src/xspice/examples/io_ordering.deck @@ -0,0 +1,17 @@ +IO ordering +* +* This circuit contains a simple gain block. The order of +* the nodes listed on the instance line follows the order +* of the connections defined in the 'ifspec.ifs' file for +* the model. Refer to /atesse-su/src/cml/gain/ifspec.ifs . +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/io_types.deck b/src/xspice/examples/io_types.deck new file mode 100755 index 000000000..0d792ab73 --- /dev/null +++ b/src/xspice/examples/io_types.deck @@ -0,0 +1,34 @@ +IO types +* +* This circuit contains a mix of input output types including +* voltages, currents, digital signals, and user defined +* signals. +* +.tran 1e-6 1e-4 +* +v1 1 0 0.0 pulse(0 1 2e-5) +r1 1 0 1k +* +abridge1 [1] [enable] node_bridge1 +.model node_bridge1 adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +abridge2 clk enable real_node1 node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +again real_node1 real_node2 times10 +.model times10 real_gain (gain=10) +* +abridge3 real_node2 analog_node node_bridge3 +.model node_bridge3 real_to_v +* +rout analog_node 0 1k +* +again %vnam v1 %i i_out gain_block +.model gain_block gain (gain=10) +ri_out i_out 0 1k +* +* +.end diff --git a/src/xspice/examples/long_names.deck b/src/xspice/examples/long_names.deck new file mode 100755 index 000000000..6a0fecada --- /dev/null +++ b/src/xspice/examples/long_names.deck @@ -0,0 +1,19 @@ +Long names +* +* This circuit contains a sine wave source followed by a +* gain block code model with a gain of 10. Long names +* are used for instances, models, and nodes. +* +.tran 1e-5 1e-3 +* +v1_123456789_123456789_1234 1 0 0.0 sin(0 1 2k) +* +r1_123456789_123456789_1234 1 0 1k +* +a1_123456789_123456789_1234 1 out_123456789_123456789_1234 ++ gain_block_123456789_123456789_1234 +* +.model gain_block_123456789_123456789_1234 gain (gain=10) +r2_123456789_123456789_1234 out_123456789_123456789_1234 0 1k +* +.end diff --git a/src/xspice/examples/mixed_case.deck b/src/xspice/examples/mixed_case.deck new file mode 100755 index 000000000..1a1006492 --- /dev/null +++ b/src/xspice/examples/mixed_case.deck @@ -0,0 +1,15 @@ +MiXeD CaSe +* +* This circuit contains a simple gain block to demonstrate +* that the simulator deck parsing code is case-insensitive. +* +.TrAn 1E-5 1e-3 +* +V1 1 0 0.0 sIn(0 1 1k) +r1 1 0 1k +* +A1 1 2 GaIn_BlOcK +.MODel gAiN_bLoCk GAin (gaIN=10) +r2 2 0 1K +* +.eNd diff --git a/src/xspice/examples/mixed_io_size.deck b/src/xspice/examples/mixed_io_size.deck new file mode 100755 index 000000000..824cdeb4b --- /dev/null +++ b/src/xspice/examples/mixed_io_size.deck @@ -0,0 +1,33 @@ +Mixed IO sizes +* +* This circuit contains a collection of digital and analog +* models with saclar and vector inputs of varying sizes. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 pulse(0 1 1e-4) +r1 1 0 1k +* +v2 2 0 0.0 sin(0 1 2k) +r2 2 0 1k +* +abridge1 [1] [enable] atod +.model atod adc_bridge +* +aosc [enable clk] clk nand +.model nand d_nand (rise_delay=1e-4 fall_delay=1e-4) +* +ainv clk clk_bar inv +.model inv d_inverter (rise_delay=1e-5 fall_delay=1e-5) +* +adac [clk clk_bar] [3 4] dac +.model dac dac_bridge (t_rise=1e-5 t_fall=1e-5) +* +asum [1 2 3 4] 5 sum +.model sum summer +* +r3 3 0 1k +r4 4 0 1k +r5 5 0 1k +* +.end diff --git a/src/xspice/examples/mixed_mode.deck b/src/xspice/examples/mixed_mode.deck new file mode 100755 index 000000000..59e4745ba --- /dev/null +++ b/src/xspice/examples/mixed_mode.deck @@ -0,0 +1,98 @@ +Mixed IO types +* +* This circuit contains a mixture of IO types, including +* analog, digital, user-defined (real), and 'null'. +* +* The circuit demonstrates the use of the digital and +* user-defined node capability to model system-level designs +* such as sampled-data filters. The simulated circuit +* contains a digital oscillator enabled after 100us. The +* square wave oscillator output is divided by 8 with a +* ripple counter. The result is passed through a digital +* filter to convert it to a sine wave. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 pulse(0 1 1e-4 1e-6) +r1 1 0 1k +* +abridge1 [1] [enable] atod +.model atod adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +adiv2 div2_out clk NULL NULL NULL div2_out dff +adiv4 div4_out div2_out NULL NULL NULL div4_out dff +adiv8 div8_out div4_out NULL NULL NULL div8_out dff +.model dff d_dff +* +abridge2 div8_out enable filt_in node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +xfilter filt_in clk filt_out dig_filter +* +abridge3 filt_out a_out node_bridge3 +.model node_bridge3 real_to_v +* +rlpf1 a_out oa_minus 10k +* +xlpf 0 oa_minus lpf_out opamp +* +rlpf2 oa_minus lpf_out 10k +clpf lpf_out oa_minus 0.01uF +* +* +.subckt dig_filter filt_in clk filt_out +* +.model n0 real_gain (gain=1.0) +.model n1 real_gain (gain=2.0) +.model n2 real_gain (gain=1.0) +.model g1 real_gain (gain=0.125) +.model zm1 real_delay +.model d0a real_gain (gain=-0.75) +.model d1a real_gain (gain=0.5625) +.model d0b real_gain (gain=-0.3438) +.model d1b real_gain (gain=1.0) +* +an0a filt_in x0a n0 +an1a filt_in x1a n1 +an2a filt_in x2a n2 +* +az0a x0a clk x1a zm1 +az1a x1a clk x2a zm1 +* +ad0a x2a x0a d0a +ad1a x2a x1a d1a +* +az2a x2a filt1_out g1 +az3a filt1_out clk filt2_in zm1 +* +an0b filt2_in x0b n0 +an1b filt2_in x1b n1 +an2b filt2_in x2b n2 +* +az0b x0b clk x1b zm1 +az1b x1b clk x2b zm1 +* +ad0 x2b x0b d0b +ad1 x2b x1b d1b +* +az2b x2b clk filt_out zm1 +* +.ends dig_filter +* +* +.subckt opamp plus minus out +* +r1 plus minus 300k +a1 %vd (plus minus) outint lim +.model lim limit (out_lower_limit = -12 out_upper_limit = 12 ++ fraction = true limit_range = 0.2 gain=300e3) +r3 outint out 50.0 +r2 out 0 1e12 +* +.ends opamp +* +* +.end diff --git a/src/xspice/examples/mixed_ref.deck b/src/xspice/examples/mixed_ref.deck new file mode 100755 index 000000000..475e0f655 --- /dev/null +++ b/src/xspice/examples/mixed_ref.deck @@ -0,0 +1,41 @@ +Mixed references +* +* This circuit demonstrates the use of single-ended and +* differential inputs and outputs. +* +* Note that digital models reference a single node for +* their inputs and output (i.e. they are single-ended) +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 5k) +v2 2 0 0.0 sin(0 1 1k) +* +r1 1 0 1k +r2 2 0 1k +* +* +a1 %v 1 %i 10 times10 +r10 10 0 1k +* +* +a2 %vd (1 2) %id(11 12) times10 +r11 11 0 1k +r12 12 0 1k +r11_12 11 12 1.0 +* +* +r3 2 3 1k +a3 %i 3 %v 13 times10 +r13 13 0 1k +* +a4 [1] [digital_node1] adc +.model adc adc_bridge +* +a5 digital_node1 digital_node2 inv +.model inv d_inverter +* +* +.model times10 gain (gain=10) +* +.end diff --git a/src/xspice/examples/mosamp2.in b/src/xspice/examples/mosamp2.in new file mode 100755 index 000000000..d8029ad77 --- /dev/null +++ b/src/xspice/examples/mosamp2.in @@ -0,0 +1,42 @@ +mosamp2 - mos amplifier - transient +.options acct abstol=10n vntol=10n +.tran 0.1us 10us +m1 15 15 1 32 m w=88.9u l=25.4u +m2 1 1 2 32 m w=12.7u l=266.7u +m3 2 2 30 32 m w=88.9u l=25.4u +m4 15 5 4 32 m w=12.7u l=106.7u +m5 4 4 30 32 m w=88.9u l=12.7u +m6 15 15 5 32 m w=44.5u l=25.4u +m7 5 20 8 32 m w=482.6u l=12.7u +m8 8 2 30 32 m w=88.9u l=25.4u +m9 15 15 6 32 m w=44.5u l=25.4u +m10 6 21 8 32 m w=482.6u l=12.7u +m11 15 6 7 32 m w=12.7u l=106.7u +m12 7 4 30 32 m w=88.9u l=12.7u +m13 15 10 9 32 m w=139.7u l=12.7u +m14 9 11 30 32 m w=139.7u l=12.7u +m15 15 15 12 32 m w=12.7u l=207.8u +m16 12 12 11 32 m w=54.1u l=12.7u +m17 11 11 30 32 m w=54.1u l=12.7u +m18 15 15 10 32 m w=12.7u l=45.2u +m19 10 12 13 32 m w=270.5u l=12.7u +m20 13 7 30 32 m w=270.5u l=12.7u +m21 15 10 14 32 m w=254u l=12.7u +m22 14 11 30 32 m w=241.3u l=12.7u +m23 15 20 16 32 m w=19u l=38.1u +m24 16 14 30 32 m w=406.4u l=12.7u +m25 15 15 20 32 m w=38.1u l=42.7u +m26 20 16 30 32 m w=381u l=25.4u +m27 20 15 66 32 m w=22.9u l=7.6u +cc 7 9 40pf +cl 66 0 70pf +vin 21 0 pulse(0 5 1ns 1ns 1ns 5us 10us) +vccp 15 0 dc +15 +vddn 30 0 dc -15 +vb 32 0 dc -20 +.model m nmos(nsub=2.2e15 uo=575 ucrit=49k uexp=0.1 tox=0.11u xj=2.95u ++ level=2 cgso=1.5n cgdo=1.5n cbd=4.5f cbs=4.5f ld=2.4485u nss=3.2e10 ++ kp=2e-5 phi=0.6 ) +.print tran v(20) v(66) +.plot tran v(20) v(66) +.end diff --git a/src/xspice/examples/mosmem.in b/src/xspice/examples/mosmem.in new file mode 100755 index 000000000..c2803445b --- /dev/null +++ b/src/xspice/examples/mosmem.in @@ -0,0 +1,27 @@ +mosmem - mos memory cell +.width in=72 +.opt abstol=1u +.opt acct list node +.tran 20ns 2us +vdd 9 0 dc 5 +vs 7 0 pulse(2 0 520ns 20ns 20ns 500ns 2000ns) +vw 1 0 pulse(0 2 20ns 20ns 500ns 200ns) +vwb 2 0 pulse(2 0 20ns 20ns 20ns 2000ns 2000ns) +m1 3 1 0 0 mod w=250u l=5u +m2 4 2 0 0 mod w=250u l=5u +m3 9 9 3 0 mod w=5u l=5u +m4 9 9 4 0 mod w=5u l=5u +m5 5 7 3 0 mod w=50u l=5u +m6 6 7 4 0 mod w=50u l=5u +m7 5 6 0 0 mod w=250u l=5u +m8 6 5 0 0 mod w=250u l=5u +m9 9 9 5 0 mod w=5u l=5u +m10 9 9 6 0 mod w=5u l=5u +m11 8 4 0 0 mod w=250u l=5u +m12 9 9 8 0 mod w=5u l=5u +.model mod nmos(vto=0.5 phi=0.7 kp=1.0e-6 gamma=1.83 lambda=0.115 ++ level=1 cgso=1u cgdo=1u cbd=50p cbs=50p) +.print dc v(5) v(6) +.plot dc v(6) +.plot tran v(6) v(5) v(7) v(1) v(2) +.end diff --git a/src/xspice/examples/nco/Makefile b/src/xspice/examples/nco/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/nco/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/nco/cfunc.mod b/src/xspice/examples/nco/cfunc.mod new file mode 100755 index 000000000..993248863 --- /dev/null +++ b/src/xspice/examples/nco/cfunc.mod @@ -0,0 +1,90 @@ +/* $Id$ */ + +void *malloc(unsigned); + +#define OUT_STATE 0 +#define NXT_TIME 1 +#define NUM_NOTES 128 + + +/* A numerically controlled oscillator. Output frequencies */ +/* are determined according to the MIDI note number at input */ + +void ucm_nco (ARGS) +{ + + double *freq; + + int *output_state; + double *next_time; + + int i; + int index; + int scale_factor; + + double half_period; + + + if(INIT) { + + /* Setup storage for the toggled output state */ + output_state = (int *) cm_event_alloc(OUT_STATE, sizeof(int)); + next_time = (double *) cm_event_alloc(NXT_TIME, sizeof(double)); + + /* Allocate storage for frequencies */ + STATIC_VAR(freq) = malloc(NUM_NOTES * sizeof(double)); + freq = STATIC_VAR(freq); + + /* Initialize the frequency array */ + for(i = 0; i < NUM_NOTES; i++) { + if(i == 0) + freq[0] = 8.17578 * PARAM(mult_factor); + else + freq[i] = freq[i-1] * 1.059463094; + } + } + else { + + /* Get old output state */ + output_state = (int *) cm_event_get_ptr(OUT_STATE, 0); + next_time = (double *) cm_event_get_ptr(NXT_TIME, 0); + } + + + /* Convert the input bits to an integer */ + index = 0; + scale_factor = 64; + for(i = 0; i < 7; i++) { + if(INPUT_STATE(in[i]) == ONE) + index += scale_factor; + scale_factor /= 2; + } + + /* Look up the frequency and compute half its period */ + freq = STATIC_VAR(freq); + half_period = 1.0 / freq[index]; + + + /* Queue up events and output the new state */ + if(TIME == 0.0) { + *next_time = half_period; + cm_event_queue(*next_time); + OUTPUT_STATE(out) = *output_state; + } + else { + if(TIME == *next_time) { + *next_time = TIME + half_period; + cm_event_queue(*next_time); + *output_state = 1 - *output_state; + OUTPUT_STATE(out) = *output_state; + OUTPUT_DELAY(out) = PARAM(delay); + } + else + OUTPUT_CHANGED(out) = FALSE; + } + +} + + + + diff --git a/src/xspice/examples/nco/ifspec.ifs b/src/xspice/examples/nco/ifspec.ifs new file mode 100755 index 000000000..30cdba5d7 --- /dev/null +++ b/src/xspice/examples/nco/ifspec.ifs @@ -0,0 +1,38 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: nco +C_Function_Name: ucm_nco +Description: "A simple MIDI numerically controlled oscillator" + + +PORT_TABLE: + +Port_Name: in out +Description: "program input" "oscillator output" +Direction: in out +Default_Type: d d +Allowed_Types: [d] [d] +Vector: yes no +Vector_Bounds: [7 7] - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: delay mult_factor +Description: "output delay" "freq multiplier" +Data_Type: real real +Default_Value: 1e-9 1 +Limits: [1e-15 -] [1e-9 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +STATIC_VAR_TABLE: + +Static_Var_Name: freq +Data_Type: pointer +Description: "frequencies of notes" diff --git a/src/xspice/examples/param_defaults.deck b/src/xspice/examples/param_defaults.deck new file mode 100755 index 000000000..75e3542be --- /dev/null +++ b/src/xspice/examples/param_defaults.deck @@ -0,0 +1,16 @@ +Parameter defaults +* +* This circuit contains a code model with +* parameters of various types, which are all defaulted, +* and prints the default values. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types +* +.end diff --git a/src/xspice/examples/param_types.deck b/src/xspice/examples/param_types.deck new file mode 100755 index 000000000..84a43ef9e --- /dev/null +++ b/src/xspice/examples/param_types.deck @@ -0,0 +1,23 @@ +Parameter types +* +* This circuit contains a code model which accepts several +* parameters of various types and prints them. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types ++ integer=2 ++ real=3.0 ++ complex=<4.0 5.0> ++ string=six ++ integer_array=[7 8] ++ real_array=[9.0 10.0] ++ complex_array=[< 11.0 12.0 > < 13.0 14.0 >] ++ string_array=[fifteen sixteen] +* +.end diff --git a/src/xspice/examples/parsing.deck b/src/xspice/examples/parsing.deck new file mode 100755 index 000000000..b5de5f594 --- /dev/null +++ b/src/xspice/examples/parsing.deck @@ -0,0 +1,16 @@ +Parsing +* +* This circuit contains a simple gain block to demonstrate +* that the simulator parses the syntax used to reference +* code models. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/polarity.deck b/src/xspice/examples/polarity.deck new file mode 100755 index 000000000..7488e182a --- /dev/null +++ b/src/xspice/examples/polarity.deck @@ -0,0 +1,26 @@ +Polarity of voltages and currents +* +* This circuit contains a set of gain blocks to evaluate +* the polarity of voltages and currents on code models +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +* +r1 1 0 1k +* +* +a1 %v 1 %v 10 times10 +r10 10 0 1k +* +r1_2 1 2 1k +a2 %i 2 %v 11 times10 +r11 11 0 1k +* +a3 1 %i 12 times10 +r12 12 0 1k +* +* +.model times10 gain (gain=10) +* +.end diff --git a/src/xspice/examples/print_param_types/Makefile b/src/xspice/examples/print_param_types/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/print_param_types/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/print_param_types/cfunc.mod b/src/xspice/examples/print_param_types/cfunc.mod new file mode 100755 index 000000000..8cf7d02ca --- /dev/null +++ b/src/xspice/examples/print_param_types/cfunc.mod @@ -0,0 +1,33 @@ +/* $Id$ */ + +void ucm_print_param_types (ARGS) +{ + int i; + + if(INIT) { + /* Print scalar parameters */ + printf("\nScalar parameters\n\n"); + printf("integer = %d\n", PARAM(integer)); + printf("real = %e\n", PARAM(real)); + printf("complex = <%e %e>\n", PARAM(complex).real, + PARAM(complex).imag); + printf("string = %s\n", PARAM(string)); + + /* Print vector parameters */ + printf("\nVector parameters\n\n"); + for(i = 0; i < PARAM_SIZE(integer_array); i++) + printf("integer = %d\n", PARAM(integer_array[i])); + for(i = 0; i < PARAM_SIZE(real_array); i++) + printf("real = %e\n", PARAM(real_array[i])); + for(i = 0; i < PARAM_SIZE(complex_array); i++) + printf("complex = <%e %e>\n", PARAM(complex_array[i]).real, + PARAM(complex_array[i]).imag); + for(i = 0; i < PARAM_SIZE(string_array); i++) + printf("string = %s\n", PARAM(string_array[i])); + + } +} + + + + diff --git a/src/xspice/examples/print_param_types/ifspec.ifs b/src/xspice/examples/print_param_types/ifspec.ifs new file mode 100755 index 000000000..e836df007 --- /dev/null +++ b/src/xspice/examples/print_param_types/ifspec.ifs @@ -0,0 +1,112 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: print_param_types +C_Function_Name: ucm_print_param_types +Description: "ignores its input, but prints its parameters" + + +PORT_TABLE: + + +Port_Name: in +Description: "input" +Direction: in +Default_Type: v +Allowed_Types: [v,vd,i,id,vnam] +Vector: yes +Vector_Bounds: - +Null_Allowed: no + + + +PARAMETER_TABLE: + +Parameter_Name: integer +Description: "integer parameter" +Data_Type: int +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: real +Description: "real parameter" +Data_Type: real +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: complex +Description: "complex parameter" +Data_Type: complex +Default_Value: <1.0, 1.0> +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: string +Description: "string parameter" +Data_Type: string +Default_Value: "one" +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: integer_array +Description: "integer array parameter" +Data_Type: int +Default_Value: 1 +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: real_array +Description: "real array parameter" +Data_Type: real +Default_Value: 1 +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: complex_array +Description: "complex array parameter" +Data_Type: complex +Default_Value: <1.0 1.0> +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: string_array +Description: "string array parameter" +Data_Type: string +Default_Value: "one" +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + + diff --git a/src/xspice/examples/rca3040.in b/src/xspice/examples/rca3040.in new file mode 100755 index 000000000..6b6e52048 --- /dev/null +++ b/src/xspice/examples/rca3040.in @@ -0,0 +1,33 @@ +rca3040 ckt - rca 3040 wideband amplifier +.ac dec 10 1 10ghz +.dc vin -0.25 0.25 0.005 +.tran 2.0ns 200ns +vin 1 0 sin(0 0.1 50meg 0.5ns) ac 1 +vcc 2 0 15.0 +vee 3 0 -15.0 +rs1 30 1 1k +rs2 31 0 1k +r1 5 3 4.8k +r2 6 3 4.8k +r3 9 3 811 +r4 8 3 2.17k +r5 8 0 820 +r6 2 14 1.32k +r7 2 12 4.5k +r8 2 15 1.32k +r9 16 0 5.25k +r10 17 0 5.25k +q1 2 30 5 qnl +q2 2 31 6 qnl +q3 10 5 7 qnl +q4 11 6 7 qnl +q5 14 12 10 qnl +q6 15 12 11 qnl +q7 12 12 13 qnl +q8 13 13 0 qnl +q9 7 8 9 qnl +q10 2 15 16 qnl +q11 2 14 17 qnl +.model qnl npn bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf ++ cjc=2pf va 50 +.end diff --git a/src/xspice/examples/real_delay/Makefile b/src/xspice/examples/real_delay/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_delay/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_delay/cfunc.mod b/src/xspice/examples/real_delay/cfunc.mod new file mode 100755 index 000000000..5f3c41cbe --- /dev/null +++ b/src/xspice/examples/real_delay/cfunc.mod @@ -0,0 +1,46 @@ +/* $Id$ */ + + +#define CLK_STATE 0 + + +void ucm_real_delay (ARGS) +{ + + double *in; + double *out; + + Digital_State_t *state; + Digital_State_t *old_state; + + + if(INIT) { + state = (void *) cm_event_alloc(CLK_STATE, sizeof(Digital_State_t)); + old_state = state; + *state = INPUT_STATE(clk); + } + else { + state = (void *) cm_event_get_ptr(CLK_STATE, 0); + old_state = (void *) cm_event_get_ptr(CLK_STATE, 1); + } + + if(ANALYSIS != TRANSIENT) + OUTPUT_CHANGED(out) = FALSE; + else { + *state = INPUT_STATE(clk); + if(*state == *old_state) + OUTPUT_CHANGED(out) = FALSE; + else if(*state != ONE) + OUTPUT_CHANGED(out) = FALSE; + else { + in = INPUT(in); + out = OUTPUT(out); + *out = *in; + OUTPUT_DELAY(out) = PARAM(delay); + } + } +} + + + + diff --git a/src/xspice/examples/real_delay/ifspec.ifs b/src/xspice/examples/real_delay/ifspec.ifs new file mode 100755 index 000000000..603bbee27 --- /dev/null +++ b/src/xspice/examples/real_delay/ifspec.ifs @@ -0,0 +1,33 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_delay +C_Function_Name: ucm_real_delay +Description: "A Z ** -1 block working on real data" + + +PORT_TABLE: + +Port_Name: in clk out +Description: "input" "clock" "output" +Direction: in in out +Default_Type: real d real +Allowed_Types: [real] [d] [real] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: no no no + + +PARAMETER_TABLE: + +Parameter_Name: delay +Description: "delay from clk to out" +Data_Type: real +Default_Value: 1e-9 +Limits: [1e-15 -] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/examples/real_gain/Makefile b/src/xspice/examples/real_gain/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_gain/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_gain/cfunc.mod b/src/xspice/examples/real_gain/cfunc.mod new file mode 100755 index 000000000..f6934ccc3 --- /dev/null +++ b/src/xspice/examples/real_gain/cfunc.mod @@ -0,0 +1,39 @@ +/* $Id$ */ + +void ucm_real_gain (ARGS) +{ + double *in; + double *out; + + double in_offset; + double gain; + double out_offset; + double delay; + double ic; + + + /* Get the input and output pointers */ + in = INPUT(in); + out = OUTPUT(out); + + /* Get the parameters */ + in_offset = PARAM(in_offset); + gain = PARAM(gain); + out_offset = PARAM(out_offset); + delay = PARAM(delay); + ic = PARAM(ic); + + + /* Assign the output and delay */ + if(ANALYSIS == DC) { + *out = ic; + if(INIT) + cm_event_queue(delay); + } + else { + *out = gain * (*in + in_offset) + out_offset; + OUTPUT_DELAY(out) = delay; + } +} + + diff --git a/src/xspice/examples/real_gain/ifspec.ifs b/src/xspice/examples/real_gain/ifspec.ifs new file mode 100755 index 000000000..886acfdcb --- /dev/null +++ b/src/xspice/examples/real_gain/ifspec.ifs @@ -0,0 +1,45 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_gain +C_Function_Name: ucm_real_gain +Description: "A gain block for event-driven real data" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: real real +Allowed_Types: [real] [real] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: in_offset gain out_offset +Description: "input offset" "gain" "output offset" +Data_Type: real real real +Default_Value: 0.0 1.0 0.0 +Limits: - - - +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: yes yes yes + + +PARAMETER_TABLE: + +Parameter_Name: delay ic +Description: "delay" "initial condition" +Data_Type: real real +Default_Value: 1.0e-9 0.0 +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + diff --git a/src/xspice/examples/real_to_v/Makefile b/src/xspice/examples/real_to_v/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_to_v/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_to_v/cfunc.mod b/src/xspice/examples/real_to_v/cfunc.mod new file mode 100755 index 000000000..f6b699391 --- /dev/null +++ b/src/xspice/examples/real_to_v/cfunc.mod @@ -0,0 +1,75 @@ +/* $Id$ */ + + +#define TS 0 +#define VS 1 + + +void ucm_real_to_v (ARGS) +{ + + double *t, *v; + double *in; + + double out; + + + in = INPUT(in); + + if(INIT) { + t = (void *) cm_event_alloc(TS, 2 * sizeof(double)); + v = (void *) cm_event_alloc(VS, 2 * sizeof(double)); + t[0] = -2.0; + t[1] = -1.0; + v[0] = *in; + v[1] = *in; + } + else { + t = (void *) cm_event_get_ptr(TS, 0); + v = (void *) cm_event_get_ptr(VS, 0); + } + + switch(CALL_TYPE) { + + case ANALOG: + if(TIME == 0.0) { + OUTPUT(out) = *in; + v[0] = *in; + v[1] = *in; + } + else { + if(TIME <= t[0]) + OUTPUT(out) = v[0]; + else if(TIME >= t[1]) + OUTPUT(out) = v[1]; + else { + OUTPUT(out) = v[0] + (v[1] - v[0]) * + (TIME - t[0]) / (t[1] - t[0]); + } + } + break; + + case EVENT: + if(TIME == 0.0) + return; + if(TIME >= t[1]) { + v[0] = v[1]; + v[1] = *in; + t[0] = TIME; + t[1] = TIME + PARAM(transition_time); + } + else { + v[0] = v[0] + (v[1] - v[0]) * + (TIME - t[0]) / (t[1] - t[0]); + v[1] = *in; + t[0] = TIME; + t[1] = TIME + PARAM(transition_time); + } + break; + + } +} + + + + diff --git a/src/xspice/examples/real_to_v/ifspec.ifs b/src/xspice/examples/real_to_v/ifspec.ifs new file mode 100755 index 000000000..9b06bf137 --- /dev/null +++ b/src/xspice/examples/real_to_v/ifspec.ifs @@ -0,0 +1,33 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_to_v +C_Function_Name: ucm_real_to_v +Description: "Node bridge from real to analog voltage" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: real v +Allowed_Types: [real] [v, vd, i, id] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: gain transition_time +Description: "gain" "output transition time" +Data_Type: real real +Default_Value: 1.0 1e-9 +Limits: - [1e-15 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + diff --git a/src/xspice/examples/rtlinv.in b/src/xspice/examples/rtlinv.in new file mode 100755 index 000000000..8632a17e8 --- /dev/null +++ b/src/xspice/examples/rtlinv.in @@ -0,0 +1,20 @@ +rtlinv ckt - cascaded rtl inverters +.width in=72 +.opt acct list node lvlcod=2 +.dc vin 0.0 2.5 0.025 +.tran 2ns 200ns +vcc 6 0 5 +vin 1 0 pulse(0 5 2ns 2ns 2ns 80ns) +rb1 1 2 10k +rc1 6 3 1k +q1 3 2 0 qnd +rb2 3 4 10k +q2 5 4 0 qnd +rc2 6 5 1k +.model qnd npn(bf=50 rb=70 rc=40 ccs=2pf tf=0.1ns tr=10ns cje=0.9pf ++ cjc=1.5pf pc=0.85 va=50) +.print dc v(3) v(5) +.plot dc v(3) +.print tran v(3) v(5) +.plot tran v(3) v(5) v(1) +.end diff --git a/src/xspice/examples/schmitt.in b/src/xspice/examples/schmitt.in new file mode 100755 index 000000000..6a7732027 --- /dev/null +++ b/src/xspice/examples/schmitt.in @@ -0,0 +1,24 @@ +schmitt ckt - ecl compatible schmitt trigger +.width in=72 +.opt acct list node lvlcod=2 +.tran 10ns 1000ns +vin 1 0 pulse(-1.6 -1.2 10ns 400ns 400ns 100ns 10000ns) +vee 8 0 -5 +rin 1 2 50 +rc1 0 3 50 +r1 3 5 185 +r2 5 8 760 +rc2 0 6 100 +re 4 8 260 +rth1 7 8 125 +rth2 7 0 85 +cload 7 0 5pf +q1 3 2 4 qstd off +q2 6 5 4 qstd +q3 0 6 7 qstd +q4 0 6 7 qstd +.model qstd npn(is=1.0e-16 bf=50 br=0.1 rb=50 rc=10 tf=0.12ns tr=5ns ++ cje=0.4pf pe=0.8 me=0.4 cjc=0.5pf pc=0.8 mc=0.333 ccs=1pf va=50) +.print tran v(1) v(3) v(5) v(6) +.plot tran v(3) v(5) v(6) v(1) +.end diff --git a/src/xspice/examples/spice3.deck b/src/xspice/examples/spice3.deck new file mode 100755 index 000000000..1055c69b8 --- /dev/null +++ b/src/xspice/examples/spice3.deck @@ -0,0 +1,25 @@ +A Berkeley SPICE3 compatible circuit +* +* This circuit contains only Berkeley SPICE3 components. +* +* The circuit is an AC coupled transistor amplifier with +* a sinewave input at node "1", a gain of approximately -3.9, +* and output on node "coll". +* +.tran 1e-5 2e-3 +* +vcc vcc 0 12.0 +vin 1 0 0.0 ac 1.0 sin(0 1 1k) +* +ccouple 1 base 10uF +* +rbias1 vcc base 100k +rbias2 base 0 24k +* +q1 coll base emit generic +.model generic npn +* +rcollector vcc coll 3.9k +remitter emit 0 1k +* +.end diff --git a/src/xspice/examples/suffixes.deck b/src/xspice/examples/suffixes.deck new file mode 100755 index 000000000..81b9bec75 --- /dev/null +++ b/src/xspice/examples/suffixes.deck @@ -0,0 +1,25 @@ +Engineering suffixes +* +* This circuit contains a code model which accepts several +* parameters of various types and prints them. The values +* specified on the .model card use engineering suffixes on +* the numeric parameters. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types ++ integer=2k ++ real=3.0u ++ complex=< 4.0f 5.0mil > ++ string=six ++ integer_array=[7meg 8] ++ real_array=[9.0n 10.0p] ++ complex_array=[< 11.0t 12.0g > < 13.0m 14.0 >] ++ string_array=[fifteen sixteen] +* +.end diff --git a/src/xspice/examples/supply_ramping.deck b/src/xspice/examples/supply_ramping.deck new file mode 100755 index 000000000..c2efd8050 --- /dev/null +++ b/src/xspice/examples/supply_ramping.deck @@ -0,0 +1,30 @@ +Supply ramping option +* +* This circuit demonstrates the use of the option +* "ramptime" which ramps independent sources and the +* capacitor and inductor initial conditions from +* zero to their final value during the time period +* specified. +* +* +.tran 0.1 5 +.option ramptime=0.2 +* +a1 1 0 cap +.model cap capacitor (c=1000uf ic=1) +r1 1 0 1k +* +a2 2 0 ind +.model ind inductor (l=1H ic=1) +r2 2 0 1.0 +* +v1 3 0 1.0 +r3 3 0 1k +* +i1 4 0 1e-3 +r4 4 0 1k +* +v2 5 0 0.0 sin(0 1 0.3 0 0 45.0) +r5 5 0 1k +* +.end diff --git a/src/xspice/examples/user_defined_nodes.deck b/src/xspice/examples/user_defined_nodes.deck new file mode 100755 index 000000000..9fb9ca1a4 --- /dev/null +++ b/src/xspice/examples/user_defined_nodes.deck @@ -0,0 +1,30 @@ +User defined nodes +* +* This circuit contains a mix of node types including +* two 'real' type user-defined nodes and associated +* node bridges. +* +.tran 1e-6 1e-4 +* +v1 1 0 0.0 pulse(0 1 2e-5) +r1 1 0 1k +* +abridge1 [1] [enable] node_bridge1 +.model node_bridge1 adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +abridge2 clk enable real_node1 node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +again real_node1 real_node2 times10 +.model times10 real_gain (gain=10) +* +abridge3 real_node2 analog_node node_bridge3 +.model node_bridge3 real_to_v +* +rout analog_node 0 1k +* +* +.end diff --git a/src/xspice/examples/xspice.deck b/src/xspice/examples/xspice.deck new file mode 100755 index 000000000..121ef20b8 --- /dev/null +++ b/src/xspice/examples/xspice.deck @@ -0,0 +1,22 @@ +A simple XSPICE amplifier circuit +* +* This uses an XSPICE "gain" code model to substitute for +* the transistor amplifier circuit in spice3.deck. +* +.tran 1e-5 2e-3 +* +vin 1 0 0.0 ac 1.0 sin(0 1 1k) +* +ccouple 1 in 10uF +* +* +rzin in 0 19.35k +* +aamp in coll gain_block +.model gain_block gain (gain = -3.9 out_offset = 7.003) +* +rzout out coll 3.9k +rbig coll 0 1e12 +* +* +.end diff --git a/src/xspice/icm/Makefile.am b/src/xspice/icm/Makefile.am new file mode 100755 index 000000000..1d1ebc2f9 --- /dev/null +++ b/src/xspice/icm/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libidnxsp.a + +libidnxsp_a_SOURCES = \ + idndig.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/icm/poly/Makefile.am b/src/xspice/icm/poly/Makefile.am new file mode 100755 index 000000000..2490dc745 --- /dev/null +++ b/src/xspice/icm/poly/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libidnxsp.a + +libidnxsp_a_SOURCES = \ + ifspec.c \ + cfunc.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in + +ifspec.c: + cmpp -ifs +cfunc.c: + cmpp -mod cfunc.mod diff --git a/src/xspice/icm/poly/cfunc.c b/src/xspice/icm/poly/cfunc.c new file mode 100755 index 000000000..d0484d9da --- /dev/null +++ b/src/xspice/icm/poly/cfunc.c @@ -0,0 +1,305 @@ +#line 1 "cfunc.mod" +#include "cm.h" +#line 1 "cfunc.mod" +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + icm_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void icm_poly (Mif_Private_t *private) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + + /* Get number of input values */ + + num_inputs = private->conn[0]->size; + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(private->circuit.init) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + private->inst_var[0]->element[0].pvalue = acgains; + } + else + acgains = private->inst_var[0]->element[0].pvalue; + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(private->circuit.anal_type == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = private->inst_var[0]->element[0].pvalue; + private->conn[1]->port[0]->ac_gain[0].port[i].real = acgains[i]; + private->conn[1]->port[0]->ac_gain[0].port[i].imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = private->conn[0]->port[i]->input.rvalue; + + num_coefs = private->param[0]->size; + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = private->param[0]->element[i].rvalue; + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + private->conn[1]->port[0]->output.rvalue = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + private->conn[1]->port[0]->partial[0].port[i] = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(private->circuit.anal_type == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/poly/cfunc.mod b/src/xspice/icm/poly/cfunc.mod new file mode 100755 index 000000000..d7ec837ed --- /dev/null +++ b/src/xspice/icm/poly/cfunc.mod @@ -0,0 +1,302 @@ +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + icm_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void icm_poly (ARGS) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + + /* Get number of input values */ + + num_inputs = PORT_SIZE(in); + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(INIT) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + STATIC_VAR(acgains) = acgains; + } + else + acgains = STATIC_VAR(acgains); + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(ANALYSIS == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = STATIC_VAR(acgains); + AC_GAIN(out,in[i]).real = acgains[i]; + AC_GAIN(out,in[i]).imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = INPUT(in[i]); + + num_coefs = PARAM_SIZE(coef); + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = PARAM(coef[i]); + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + OUTPUT(out) = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + PARTIAL(out,in[i]) = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(ANALYSIS == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/poly/ifspec.c b/src/xspice/icm/poly/ifspec.c new file mode 100755 index 000000000..707859a58 --- /dev/null +++ b/src/xspice/icm/poly/ifspec.c @@ -0,0 +1,194 @@ + +/* + * Structures for model: poly + * + * Automatically generated by cmpp preprocessor + * + * !!! DO NOT EDIT !!! + * + */ + + +// #include "prefix.h" +#include +#include "spice.h" +#include "devdefs.h" +#include "ifsim.h" +#include "mifdefs.h" +#include "mifproto.h" +#include "mifparse.h" +// #include "suffix.h" + + +static IFparm MIFmPTable[] = { + IOP("coef", 0, (IF_REAL|IF_VECTOR), "2g6 compatible spice card coefficient list"), +}; + + +static IFparm MIFpTable[] = { + OP("acgains", 1, IF_STRING, "partial derivatives from dc analysis used for ac gains"), +}; + + +static Mif_Port_Type_t MIFportEnum0[] = { + MIF_VOLTAGE, + MIF_DIFF_VOLTAGE, + MIF_CURRENT, + MIF_DIFF_CURRENT, + MIF_VSOURCE_CURRENT, +}; + + +static char *MIFportStr0[] = { + "v", + "vd", + "i", + "id", + "vnam", +}; + + +static Mif_Port_Type_t MIFportEnum1[] = { + MIF_VOLTAGE, + MIF_DIFF_VOLTAGE, + MIF_CURRENT, + MIF_DIFF_CURRENT, +}; + + +static char *MIFportStr1[] = { + "v", + "vd", + "i", + "id", +}; + + +static Mif_Conn_Info_t MIFconnTable[] = { + { + "in", + "input", + MIF_IN, + MIF_VOLTAGE, + "v", + 5, + MIFportEnum0, + MIFportStr0, + MIF_TRUE, + MIF_TRUE, + 1, + MIF_FALSE, + 0, + MIF_FALSE, + }, + { + "out", + "output", + MIF_OUT, + MIF_VOLTAGE, + "v", + 4, + MIFportEnum1, + MIFportStr1, + MIF_FALSE, + MIF_FALSE, + 0, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Param_Info_t MIFparamTable[] = { + { + "coef", + "2g6 compatible spice card coefficient list", + MIF_REAL, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_TRUE, + MIF_FALSE, + 0, + MIF_TRUE, + 2, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Inst_Var_Info_t MIFinst_varTable[] = { + { + "acgains", + "partial derivatives from dc analysis used for ac gains", + MIF_STRING, + MIF_FALSE, + }, +}; + + +extern void icm_poly(Mif_Private_t *); + +static int val_terms = 0; +static int val_numNames = 0; +static int val_numInstanceParms = 1; +static int val_numModelParms = 1; +static int val_sizeofMIFinstance = sizeof(MIFinstance); +static int val_sizeofMIFmodel = sizeof(MIFmodel); + +SPICEdev icm_poly_info = { + { "poly", + "2g6 compatible polynomial controlled source", + &val_terms, + &val_numNames, + NULL, + &val_numInstanceParms, + MIFpTable, + &val_numModelParms, + MIFmPTable, + icm_poly, + 2, + MIFconnTable, + 1, + MIFparamTable, + 1, + MIFinst_varTable, + }, +NULL, +MIFmParam, +MIFload, +MIFsetup, +MIFunsetup, +NULL, +NULL, +MIFtrunc, +NULL, +MIFload, +NULL, +MIFdestroy, +MIFmDelete, +MIFdelete, +NULL, +MIFask, +MIFmAsk, +NULL, +MIFconvTest, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +&val_sizeofMIFinstance, +&val_sizeofMIFmodel, + +}; + diff --git a/src/xspice/icm/poly/ifspec.ifs b/src/xspice/icm/poly/ifspec.ifs new file mode 100755 index 000000000..39a2aaef8 --- /dev/null +++ b/src/xspice/icm/poly/ifspec.ifs @@ -0,0 +1,75 @@ +/* =========================================================================== +FILE ifspec.ifs + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +NAME_TABLE: + +Spice_Model_Name: poly +C_Function_Name: icm_poly +Description: "2G6 compatible polynomial controlled source" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: v v +Allowed_Types: [v,vd,i,id,vnam] [v,vd,i,id] +Vector: yes no +Vector_Bounds: [1 -] - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: coef +Description: "2G6 compatible spice card coefficient list" +Data_Type: real +Default_Value: - +Limits: - +Vector: yes +Vector_Bounds: [2 -] +Null_Allowed: no + + +STATIC_VAR_TABLE: + +Static_Var_Name: acgains +Data_Type: pointer +Description: "Partial derivatives from DC analysis used for AC gains" diff --git a/src/xspice/icm/poly/make.bat b/src/xspice/icm/poly/make.bat new file mode 100755 index 000000000..19a341b81 --- /dev/null +++ b/src/xspice/icm/poly/make.bat @@ -0,0 +1,3 @@ +#!/bin/sh +cmpp -mod cfunc.mod +cmpp -ifs diff --git a/src/xspice/idn/Makefile.am b/src/xspice/idn/Makefile.am new file mode 100755 index 000000000..1d1ebc2f9 --- /dev/null +++ b/src/xspice/idn/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libidnxsp.a + +libidnxsp_a_SOURCES = \ + idndig.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/idn/idndig.c b/src/xspice/idn/idndig.c new file mode 100755 index 000000000..3fc7701c5 --- /dev/null +++ b/src/xspice/idn/idndig.c @@ -0,0 +1,349 @@ +/*============================================================================ +FILE IDNdig.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of the 'digital' node type + used by 12-state digital models in the code model library. + These functions are called exclusively through function + pointers in an Evt_Udn_Info_t data structure. + +INTERFACES + + Evt_Udn_Info_t idn_digital_info + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "cm.h" + +#include "evtudn.h" + +void *tmalloc(size_t); + + +/* ************************************************************************ */ + +void idn_digital_create(void **evt_struct) +{ + /* Malloc space for a digital struct */ + + *evt_struct = tmalloc(sizeof(Digital_t)); +} + + +/* ************************************************************************ */ + +void idn_digital_dismantle(void *evt_struct) +{ + /* Do nothing. There are no internally malloc'ed things to dismantle */ +} + + +/* ************************************************************************ */ + +void idn_digital_initialize(void *evt_struct) +{ + Digital_t *dig_struct = evt_struct; + + + /* Initialize to unknown state and strength */ + dig_struct->state = ZERO; + dig_struct->strength = UNDETERMINED; +} + + +/* ************************************************************************ */ + +void idn_digital_invert(void *evt_struct) +{ + Digital_t *dig_struct = evt_struct; + + + /* Invert the state */ + switch(dig_struct->state) { + + case ZERO: + dig_struct->state = ONE; + return; + + case ONE: + dig_struct->state = ZERO; + return; + + default: + return; + } + +} + + +/* ************************************************************************ */ + +void idn_digital_copy(void *evt_from_struct, void *evt_to_struct) +{ + Digital_t *dig_from_struct = evt_from_struct; + Digital_t *dig_to_struct = evt_to_struct; + + /* Copy the structure */ + dig_to_struct->state = dig_from_struct->state; + dig_to_struct->strength = dig_from_struct->strength; +} + + +/* ************************************************************************ */ + +void idn_digital_resolve(int num_struct, + void **evt_struct_array, void *evt_struct) +{ + Digital_t **dig_struct_array; + Digital_t *dig_struct; + + static int map[12][12] = { + { 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2}, + { 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + { 0, 1, 2, 3, 5, 5, 3, 3, 3, 9, 11, 11}, + { 0, 1, 2, 5, 4, 5, 4, 4, 4, 11, 10, 11}, + { 0, 1, 2, 5, 5, 5, 5, 5, 5, 10, 11, 11}, + { 0, 1, 2, 3, 4, 5, 6, 8, 8, 9, 11, 11}, + { 0, 1, 2, 3, 4, 5, 8, 7, 8, 11, 10, 11}, + { 0, 1, 2, 3, 4, 5, 8, 8, 8, 11, 11, 11}, + { 0, 2, 2, 9, 11, 11, 9, 11, 11, 9, 11, 11}, + { 2, 1, 2, 11, 10, 11, 11, 10, 11, 11, 10, 11}, + { 2, 1, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11} }; + + int i; + + int index1; + int index2; + + /* Cast the input void pointers to pointers of the digital type */ + dig_struct = evt_struct; + dig_struct_array = evt_struct_array; + + /* Copy the first member of the array directly to the output */ + dig_struct->state = dig_struct_array[0]->state; + dig_struct->strength = dig_struct_array[0]->strength; + + /* Convert struct to index into map */ + index1 = dig_struct->state + ((int)dig_struct->strength) * 3; + + + /* For the remaining members, perform the resolution algorithm */ + for(i = 1; i < num_struct; i++) { + + /* Convert struct to index into map */ + index2 = dig_struct_array[i]->state + + ((int)dig_struct_array[i]->strength) * 3; + + /* Compute the result */ + index1 = map[index1][index2]; + } + + /* Convert result back to state and strength */ + dig_struct->state = index1 % 3; + dig_struct->strength = index1 / 3; +} + + +/* ************************************************************************ */ + +void idn_digital_compare(void *evt_struct1, void *evt_struct2, + Boolean_t *equal) +{ + Digital_t *dig_struct1 = evt_struct1; + Digital_t *dig_struct2 = evt_struct2; + + /* Compare the structures in order of most likely differences */ + if(dig_struct1->state != dig_struct2->state) + *equal = FALSE; + else if(dig_struct1->strength != dig_struct2->strength) + *equal = FALSE; + else + *equal = TRUE; +} + + +/* ************************************************************************ */ + +void idn_digital_plot_val(void *evt_struct, char *member, double *val) +{ + Digital_t *dig_struct = evt_struct; + + + /* Output a value for the requested member of the digital struct */ + if(strcmp(member,"strength") == 0) { + + /* Choose values that will not make plots lie on state plots */ + switch(dig_struct->strength) { + + case STRONG: + *val = 0.1; + return; + + case RESISTIVE: + *val = 0.6; + return; + + case HI_IMPEDANCE: + *val = 1.1; + return; + + case UNDETERMINED: + *val = -0.4; + return; + } + } + else { + /* member = "state" or anything else */ + + /* Pick reasonable values */ + switch(dig_struct->state) { + + case ZERO: + *val = 0.0; + return; + + case ONE: + *val = 1.0; + return; + + case UNKNOWN: + *val = 0.5; + return; + } + } +} + + +/* ************************************************************************ */ + +void idn_digital_print_val(void *evt_struct, char *member, char **val) +{ + Digital_t *dig_struct = evt_struct; + + int index; + + static char *map[] = { "0s", "1s", "Us", + "0r", "1r", "Ur", + "0z", "1z", "Uz", + "0u", "1u", "Uu" }; + + + /* Output a value for the requested member of the digital struct */ + + if(strcmp(member,"state") == 0) { + + /* Pick reasonable values */ + switch(dig_struct->state) { + + case ZERO: + *val = "0"; + return; + + case ONE: + *val = "1"; + return; + + case UNKNOWN: + *val = "U"; + return; + + default: + *val = "?"; + return; + } + } + else if(strcmp(member,"strength") == 0) { + + /* Choose values that will not make plots lie on state plots */ + switch(dig_struct->strength) { + + case STRONG: + *val = "s"; + return; + + case RESISTIVE: + *val = "r"; + return; + + case HI_IMPEDANCE: + *val = "z"; + return; + + case UNDETERMINED: + *val = "u"; + return; + + default: + *val = "?"; + return; + } + } + else { + index = dig_struct->state + ((int)dig_struct->strength) * 3; + if((index < 0) || (index > 11)) + *val = "??"; + else + *val = map[index]; + return; + } +} + + + +/* ************************************************************************ */ + +void idn_digital_ipc_val(void *evt_struct, void **ipc_val, int *ipc_val_size) +{ + /* Return the digital data structure and its size */ + *ipc_val = evt_struct; + *ipc_val_size = sizeof(Digital_t); +} + + + +Evt_Udn_Info_t idn_digital_info = { + +"d", +"12 state digital data", +idn_digital_create, +idn_digital_dismantle, +idn_digital_initialize, +idn_digital_invert, +idn_digital_copy, +idn_digital_resolve, +idn_digital_compare, +idn_digital_plot_val, +idn_digital_print_val, +idn_digital_ipc_val + +}; + diff --git a/src/xspice/ipc/Makefile.am b/src/xspice/ipc/Makefile.am new file mode 100755 index 000000000..dc19a55e9 --- /dev/null +++ b/src/xspice/ipc/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libipcxsp.a + +libipcxsp_a_SOURCES = \ +ipcaegis.c \ +ipc.c \ +ipcsockets.c \ +ipcstdio.c \ +ipctiein.c + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/spicelib/devices + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/ipc/ipc.c b/src/xspice/ipc/ipc.c new file mode 100755 index 000000000..03be29fb3 --- /dev/null +++ b/src/xspice/ipc/ipc.c @@ -0,0 +1,987 @@ +/*============================================================================ +FILE IPC.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + 6/13/92 Bill Kuhn Added some comments + +SUMMARY + + Provides compatibility for the new SPICE simulator to both the MSPICE user + interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE + v.2 Simulator Interface and BCP (via Bsd Sockets). + + The Interprocess Communications package provides functions + called to receive XSPICE decks from the ATESSE Simulator Interface + or Batch Control processes, and to return results to those + processes. Functions callable from the simulator packages include: + + ipc_initialize_server + ipc_terminate_server + ipc_get_line + ipc_send_line + ipc_send_data_prefix + ipc_send_data_suffix + ipc_send_dcop_prefix + ipc_send_dcop_suffix + ipc_send_evtdict_prefix + ipc_send_evtdict_suffix + ipc_send_evtdata_prefix + ipc_send_evtdata_suffix + ipc_send_errchk + ipc_send_end + ipc_send_boolean + ipc_send_int + ipc_send_double + ipc_send_complex + ipc_send_event + ipc_flush + + These functions communicate with a set of transport-level functions + that implement the interprocess communications under one of + the following protocol types determined by a compile-time option: + + BSD UNIX Sockets + HP/Apollo Mailboxes + + For each transport protocol, the following functions are written: + + ipc_transport_initialize_server + ipc_transport_get_line + ipc_transport_terminate_server + ipc_transport_send_line + + + +============================================================================*/ + +#ifndef NDEBUG +#include +#endif +#include /* Specific to BSD - Use sys/fcntl.h for sys5 */ +#include +#include +#include + +#include +#include +#include +#include /* NOTE: I think this is a Sys5ism (there is not man + * page for it under Bsd, but it's in /usr/include + * and it has a BSD copyright header. Go figure. + */ + +#include "ipc.h" +#include "ipctiein.h" +#include "ipcproto.h" + + +/* + * Conditional compilation sanity check: + */ + +/* +#if !defined (IPC_AEGIS_MAILBOXES) && !defined (IPC_UNIX_SOCKETS)\ + && !defined (IPC_DEBUG_VIA_STDIO) +" compiler error - must specify a transport mechanism"; +#endif + +*///ka removed + +/* + * static 'globals' + */ + +/*typedef unsigned char Buffer_Char_t;*/ +typedef char Buffer_Char_t; + +#define OUT_BUFFER_SIZE 1000 +#define MAX_NUM_RECORDS 200 +static int end_of_record_index [MAX_NUM_RECORDS]; +static int num_records; +static Buffer_Char_t out_buffer [OUT_BUFFER_SIZE]; +static int fill_count; + +static Ipc_Mode_t mode; +static Ipc_Protocol_t protocol; +static Ipc_Boolean_t end_of_deck_seen; +static int batch_fd; + +#define FMT_BUFFER_SIZE 80 +static char fmt_buffer [FMT_BUFFER_SIZE]; + +/*---------------------------------------------------------------------------*/ +Ipc_Boolean_t kw_match (keyword, str) + char *keyword; + char *str; + /* + * returns IPC_TRUE if the first `strlen(keyword)' characters of `str' match + * the ones in `keyword' - case sensitive + */ +{ + char *k = keyword; + char *s = str; + + /* + * quit if we run off the end of either string: + */ + while (*s && *k) { + if (*s != *k) { + return IPC_FALSE; + } + s++; + k++; + } + /* + * if we get this far, it sould be because we ran off the end of the + * keyword else we didn't match: + */ + return (*k == '\0'); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_initialize_server + +This function creates the interprocess communication channel +server mailbox or socket. +*/ + + +Ipc_Status_t ipc_initialize_server (server_name, m, p) + char *server_name; /* Mailbox path or host/portnumber pair */ + Ipc_Mode_t m; /* Interactive or batch */ + Ipc_Protocol_t p; /* Type of IPC protocol */ + /* + * For mailboxes, `server_name' would be the mailbox pathname; for + * sockets, this needs to be a host/portnumber pair. Maybe this should be + * automatically generated by the routine... + */ +{ + Ipc_Status_t status; + char batch_filename [1025]; + + mode = m; + protocol = p; + end_of_deck_seen = IPC_FALSE; + + num_records = 0; + fill_count = 0; + + status = ipc_transport_initialize_server (server_name, m, p, + batch_filename); + + if (status != IPC_STATUS_OK) { + fprintf (stderr, "ERROR: IPC: error initializing server\n"); + return IPC_STATUS_ERROR; + } + + if (mode == IPC_MODE_BATCH) { +#ifdef IPC_AEGIS_MAILBOXES + strcat (batch_filename, ".log"); +#endif + batch_fd = open (batch_filename, O_WRONLY | O_CREAT, 0666); + if (batch_fd < 0) { + // fprintf (stderr, "ERROR: IPC: Error opening batch output file: %s\n",batch_filename); + perror ("IPC"); + return IPC_STATUS_ERROR; + } + } + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_terminate_server + +This function deallocates the interprocess communication channel +mailbox or socket. +*/ + +Ipc_Status_t ipc_transport_terminate_server (); + +Ipc_Status_t ipc_terminate_server () +{ + return ipc_transport_terminate_server (); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_get_line + +This function gets a SPICE deck input line from the interprocess +communication channel. Any special control commands in the deck +beginning with a ``>'' or ``#'' character are processed internally by +this function and not returned to SPICE. +*/ + +Ipc_Status_t ipc_get_line (str, len, wait) + char *str; /* Text retrieved from IPC channel */ + int *len; /* Length of text string */ + Ipc_Wait_t wait; /* Select blocking or non-blocking */ + /* + * Reads one SPICE line from the connection. Strips any control lines + * which cannot be interpretted by the simulator (e.g. >INQCON) and + * processes them. If such a line is read, it is processed and the next + * line is read. `ipc_get_line' does not return until a non-interceptable + * line is read or end of file. + * + * If `wait' is IPC_NO_WAIT and there is no data available on the + * connection, `ipc_get_line' returns IPC_STATUS_NO_DATA. If `wait' is + * IPC_WAIT, `ipc_get_line' will not return until there is data available + * or and end of file condition is reached or an error occurs. + * + * Intercepts and processes the following commands: + * #RETURNI, #MINTIME, #VTRANS, + * >PAUSE, >CONT, >STOP, >INQCON, >NETLIST, >ENDNET + * Other > records are silently ignored. + * + * Intercepts old-style .TEMP card generated by MSPICE + * + * Returns: + * IPC_STATUS_OK - for successful reads + * IPC_STATUS_NO_DATA - when NO_WAIT and no data available + * IPC_STATUS_END_OF_DECK - at end of deck (>ENDNET seen) + * IPC_STATUS_ERROR - otherwise + */ +{ + Ipc_Status_t status; + Ipc_Boolean_t need_another = IPC_TRUE; + + do { + + status = ipc_transport_get_line (str, len, wait); + + switch (status) { + case IPC_STATUS_NO_DATA: + case IPC_STATUS_ERROR: + need_another = IPC_FALSE; + break; + case IPC_STATUS_END_OF_DECK: + assert (0); /* should never get this from the low-level get-line */ + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + break; + case IPC_STATUS_OK: + /* + * Got a good line - check to see if it's one of the ones we need to + * intercept + */ + if (str[0] == '>') { + if (kw_match (">STOP", str)) { + ipc_handle_stop(); + } else if (kw_match (">PAUSE", str)) { + /* assert (need_another); */ + /* + * once more around the loop to do a blocking wait for the >CONT + */ + need_another = IPC_TRUE; + wait = IPC_WAIT; + } else if (kw_match (">INQCON", str)) { + ipc_send_line (">ABRTABL"); + ipc_send_line (">PAUSABL"); + ipc_send_line (">KEEPABL"); + status = ipc_flush (); + if (IPC_STATUS_OK != status) { + need_another = IPC_FALSE; + } + } else if (kw_match (">ENDNET", str)) { + end_of_deck_seen = IPC_TRUE; + need_another = IPC_FALSE; + status = IPC_STATUS_END_OF_DECK; + } else { + /* silently ignore */ + } + } else if (str[0] == '#') { + if (kw_match ("#RETURNI", str)) { + ipc_handle_returni (); + } else if (kw_match ("#MINTIME", str)) { + double d1/*,d2*/; + if (1 != sscanf (&str[8], "%lg", &d1)) { + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + } else { + ipc_handle_mintime (d1); + } + } else if (kw_match ("#VTRANS", str)) { + char *tok1; + char *tok2; + char *tok3; + + tok1 = &str[8]; + for (tok2 = tok1; *tok2; tok2++) { + if (isspace(*tok2)) { + *tok2 = '\0'; + tok2++; + break; + } + } + for(tok3 = tok2; *tok3; tok3++) { + if(isspace(*tok3)) { + *tok3 = '\0'; + break; + } + } + ipc_handle_vtrans (tok1, tok2); + } else { + /* silently ignore */ + } + } else if (str[0] == '.') { + if (kw_match (".TEMP", str)) { + /* don't pass .TEMP card to caller */ + printf("Old-style .TEMP card found - ignored\n"); + } + else { + /* pass all other . cards to the caller */ + need_another = IPC_FALSE; + } + } else { + /* + * Not a '>' or '#' record - let the caller deal with it + */ + need_another = IPC_FALSE; + } + break; + default: + /* + * some unknown status value! + */ + assert (0); + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + break; + } + } while (need_another); + + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_flush + +This function flushes the interprocess communication channel +buffer contents. +*/ + +Ipc_Status_t ipc_flush () + /* + * Flush all buffered messages out the connection. + */ +{ + Ipc_Status_t status; + int last = 0; + /*int bytes;*/ + int i; + + /* if batch mode */ + if (mode == IPC_MODE_BATCH) { + + assert (batch_fd >= 0); + + /* for number of records in buffer */ + for (i = 0; i < num_records; i++) { + + /* write the records to the .log file */ + if ((end_of_record_index [i] - last) != + write (batch_fd, &out_buffer[last], end_of_record_index [i] - last)) { + // fprintf (stderr,"ERROR: IPC: Error writing to batch output file\n"); + perror ("IPC"); + return IPC_STATUS_ERROR; + } + + /* If the record is one of the batch simulation status messages, */ + /* send it over the ipc channel too */ + if( kw_match("#ERRCHK", &out_buffer[last]) || + kw_match(">ENDANAL", &out_buffer[last]) || + kw_match(">ABORTED", &out_buffer[last]) ) { + + status = ipc_transport_send_line (&out_buffer[last], + end_of_record_index [i] - last); + if (IPC_STATUS_OK != status) { + return status; + } + } + last = end_of_record_index [i]; + } + + /* else, must be interactive mode */ + } else { + /* send the full buffer over the ipc channel */ + status = ipc_transport_send_line (&out_buffer[0], + end_of_record_index [num_records - 1]); + if (IPC_STATUS_OK != status) { + return status; + } + } + + /* reset counts to zero and return */ + num_records = 0; + fill_count = 0; + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_send_line_binary (str, len) + char *str; + int len; + /* + * Same as `ipc_send_line' except does not expect the str to be null + * terminated. Sends exactly `len' characters. Use this for binary data + * strings that may have embedded nulls. + * + * Modified by wbk to append newlines for compatibility with + * ATESSE 1.0 + * + */ +{ + int length = len + 1; + /*int diff;*/ + Ipc_Status_t status; + + /* + * If we can't add the whole str to the buffer, or if there are no more + * record indices free, flush the buffer: + */ + if (((fill_count + length) >= OUT_BUFFER_SIZE) || + (num_records >= MAX_NUM_RECORDS)) { + status = ipc_flush (); + if (IPC_STATUS_OK != status) { + return status; + } + } + + /* + * make sure that the str will fit: + */ + if (length + fill_count > OUT_BUFFER_SIZE) { + // fprintf (stderr,"ERROR: IPC: String too long to fit in output buffer (> %d bytes) - truncated\n",OUT_BUFFER_SIZE); + length = OUT_BUFFER_SIZE - fill_count; + } + + /* + * finally, concatenate the str to the end of the buffer and add the newline: + */ + memcpy (&out_buffer[fill_count], str, len); + fill_count += len; + + out_buffer[fill_count] = '\n'; + fill_count++; + + end_of_record_index [num_records++] = fill_count; + + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_line + +This function sends a line of text over the interprocess +communication channel. +*/ + + +Ipc_Status_t ipc_send_line (str) + char *str; /* The text to send */ +{ + int len; + int send_len; + + char *s; + + Ipc_Status_t status= IPC_STATUS_OK; + + + len = strlen(str); + + /* if short string, send it immediately */ + if(len < 80) + status = ipc_send_line_binary (str, len); + else { + /* otherwise, we have to send it as multiple strings */ + /* because Mspice cannot handle things longer than 80 chars */ + s = str; + while(len > 0) { + if(len < 80) + send_len = len; + else + send_len = 79; + status = ipc_send_line_binary (str, send_len); + if(status != IPC_STATUS_OK) + break; + s += send_len; + len -= send_len; + } + } + + return(status); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_data_prefix + +This function sends a ``>DATAB'' line over the interprocess +communication channel to signal that this is the beginning of a +results dump for the current analysis point. +*/ + +Ipc_Status_t ipc_send_data_prefix (time) + double time; /* The analysis point for this data set */ +{ + char buffer[40]; + + sprintf (buffer, ">DATAB %.5E", time); + return ipc_send_line (buffer); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_data_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of a results +dump from a particular analysis point. +*/ + + +Ipc_Status_t ipc_send_data_suffix () +{ + Ipc_Status_t status; + + status = ipc_send_line (">ENDDATA"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_dcop_prefix + +This function sends a ``>DCOPB'' line over the interprocess +communication channel to signal that this is the beginning of a +results dump from a DC operating point analysis. +*/ + +Ipc_Status_t ipc_send_dcop_prefix () +{ + return ipc_send_line (">DCOPB"); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_dcop_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of a results +dump from a particular analysis point. +*/ + + +Ipc_Status_t ipc_send_dcop_suffix () +{ + Ipc_Status_t status; + + status = ipc_send_line (">ENDDCOP"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdict_prefix + +This function sends a ``>EVTDICT'' line over the interprocess +communication channel to signal that this is the beginning of an +event-driven node dictionary. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + +Ipc_Status_t ipc_send_evtdict_prefix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + return ipc_send_line (">EVTDICT"); +#endif +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdict_suffix + +This function sends a ``>ENDDICT'' line over the interprocess +communication channel to signal that this is the end of an +event-driven node dictionary. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_evtdict_suffix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + Ipc_Status_t status; + + status = ipc_send_line (">ENDDICT"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdata_prefix + +This function sends a ``>EVTDATA'' line over the interprocess +communication channel to signal that this is the beginning of an +event-driven node data block. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + +Ipc_Status_t ipc_send_evtdata_prefix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + return ipc_send_line (">EVTDATA"); +#endif +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdata_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of an +event-driven node data block. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_evtdata_suffix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + Ipc_Status_t status; + + status = ipc_send_line (">ENDDATA"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_errchk + +This function sends a ``\ERRCHK [GO|NOGO]'' message over the +interprocess communication channel to signal that the initial +parsing of the input deck has been completed and to indicate +whether or not errors were detected. +*/ + + +Ipc_Status_t ipc_send_errchk() +{ + char str[IPC_MAX_LINE_LEN+1]; + Ipc_Status_t status; + + if(g_ipc.errchk_sent) + return(IPC_STATUS_OK); + + if(g_ipc.syntax_error) + sprintf(str, "#ERRCHK NOGO"); + else + sprintf(str, "#ERRCHK GO"); + + g_ipc.errchk_sent = IPC_TRUE; + + status = ipc_send_line(str); + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_end + +This function sends either an ``>ENDANAL'' or an ``>ABORTED'' message +over the interprocess communication channel together with the +total CPU time used to indicate whether or not the simulation +completed normally. +*/ + + +Ipc_Status_t ipc_send_end() +{ + char str[IPC_MAX_LINE_LEN+1]; + Ipc_Status_t status; + + if(g_ipc.syntax_error || g_ipc.run_error) + sprintf(str, ">ABORTED %.4f", g_ipc.cpu_time); + else + sprintf(str, ">ENDANAL %.4f", g_ipc.cpu_time); + + status = ipc_send_line(str); + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + + +/*---------------------------------------------------------------------------*/ +int stuff_binary_v1 (d1, d2, n, buf, pos) + double d1, d2; /* doubles to be stuffed */ + int n; /* how many of d1, d2 ( 1 <= n <= 2 ) */ + char *buf; /* buffer to stuff to */ + int pos; /* index at which to stuff */ +{ + union { + float float_val[2]; + char ch[32]; + } trick; + int i, j; + + assert (protocol == IPC_PROTOCOL_V1); + assert (sizeof(float) == 4); + assert (sizeof(char) == 1); + assert ((n >= 1) && (n <= 2)); + + trick.float_val[0] = d1; + if (n > 1) { + trick.float_val[1] = d2; + } + for (i = 0, j = pos; i < n*sizeof(float); j++, i++) + buf[j] = trick.ch[i]; + i = sizeof(float)*n + pos; + buf[0] = 'A' + i - 1; + return i; +} + +/*---------------------------------------------------------------------------*/ + + +/* +ipc_send_double + +This function sends a double data value over the interprocess +communication channel preceded by a character string that +identifies the simulation variable. +*/ + +Ipc_Status_t ipc_send_double (tag, value) + char *tag; /* The node or instance */ + double value; /* The data value to send */ +{ + int i; + int len = 0; + int fmt_buffer_len; + + switch (protocol) { + case IPC_PROTOCOL_V1: + strcpy (fmt_buffer, " "); /* save room for the length byte */ + strcat (fmt_buffer, tag); + strcat (fmt_buffer, " "); + + /* If talking to Mentor tools, must force upper case for Mspice 7.0 */ + fmt_buffer_len = strlen(fmt_buffer); + for(i = 0; i < fmt_buffer_len; i++) { + if(islower(fmt_buffer[i])) + fmt_buffer[i] = toupper(fmt_buffer[i]); + } + + len = stuff_binary_v1 (value, 0.0, 1, fmt_buffer, strlen(fmt_buffer)); + break; + case IPC_PROTOCOL_V2: + break; + } + return ipc_send_line_binary (fmt_buffer, len); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_complex + +This function sends a complex data value over the interprocess +communication channel preceded by a character string that +identifies the simulation variable. +*/ + + +Ipc_Status_t ipc_send_complex (tag, value) + char *tag; /* The node or instance */ + Ipc_Complex_t value; /* The data value to send */ +{ + int i; + int len=0; + int fmt_buffer_len; + + switch (protocol) { + case IPC_PROTOCOL_V1: + strcpy (fmt_buffer, " "); /* save room for the length byte */ + strcat (fmt_buffer, tag); + strcat (fmt_buffer, " "); + + /* If talking to Mentor tools, must force upper case for Mspice 7.0 */ + fmt_buffer_len = strlen(fmt_buffer); + for(i = 0; i < fmt_buffer_len; i++) { + if(islower(fmt_buffer[i])) + fmt_buffer[i] = toupper(fmt_buffer[i]); + } + + len = stuff_binary_v1 (value.real, value.imag, 2, fmt_buffer, + strlen(fmt_buffer)); + break; + case IPC_PROTOCOL_V2: + break; + } + return ipc_send_line_binary (fmt_buffer, len); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_event + +This function sends data from an event-driven node over the interprocess +communication channel. The data is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_event(ipc_index, step, plot_val, print_val, ipc_val, len) + int ipc_index; /* Index used in EVTDICT */ + double step; /* Analysis point or timestep (0.0 for DC) */ + double plot_val; /* The value for plotting purposes */ + char *print_val; /* The value for printing purposes */ + void *ipc_val; /* The binary representation of the node data */ + int len; /* The length of the binary representation */ +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + char buff[OUT_BUFFER_SIZE]; + int i; + int buff_len; + char *buff_ptr; + char *temp_ptr; + float fvalue; + + /* Report error if size of data is too big for IPC channel block size */ + if((len + strlen(print_val) + 100) >= OUT_BUFFER_SIZE) { + printf("ERROR - Size of event-driven data too large for IPC channel\n"); + return IPC_STATUS_ERROR; + } + + /* Place the index into the buffer with a trailing space */ + sprintf(buff, "%d ", ipc_index); + + assert(sizeof(float) == 4); + assert(sizeof(int) == 4); + + /* Put the analysis step bytes in */ + buff_len = strlen(buff); + buff_ptr = buff + buff_len; + fvalue = step; + temp_ptr = (char *) &fvalue; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the plot value in */ + fvalue = plot_val; + temp_ptr = (char *) &fvalue; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the length of the binary representation in */ + temp_ptr = (char *) &len; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the binary representation bytes in last */ + temp_ptr = ipc_val; + for(i = 0; i < len; i++) + buff_ptr[i] = temp_ptr[i]; + buff_ptr += len; + buff_len += len; + + /* Put the print value in */ + strcpy(buff_ptr, print_val); + buff_ptr += strlen(print_val); + buff_len += strlen(print_val); + + /* Send the data to the IPC channel */ + return ipc_send_line_binary(buff, buff_len); + +#endif +} + + diff --git a/src/xspice/ipc/ipcaegis.c b/src/xspice/ipc/ipcaegis.c new file mode 100755 index 000000000..c86cb7216 --- /dev/null +++ b/src/xspice/ipc/ipcaegis.c @@ -0,0 +1,307 @@ +/*============================================================================ +FILE IPCaegis.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + Provides compatibility for the new XSPICE simulator to both the MSPICE user + interface and BCP via ATESSE v.1 style AEGIS mailboxes. + +INTERFACES + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#ifdef IPC_AEGIS_MAILBOXES + +#include +#include +#include +#include +#include +#include + +#include "ipc.h" + + +typedef unsigned char Buffer_char_t; + +static status_$t status; +typedef enum { + IPC_MBX_UNINITIALIZED, + IPC_MBX_INITIALIZED, + IPC_MBX_CONNECTED_TO_CLIENT, +} Ipc_Mbx_State_t; + +static void *mbx_handle; +static Ipc_Mbx_State_t mbx_state = IPC_MBX_UNINITIALIZED; +static mbx_$server_msg_t mbx_send_msg_buf; +static mbx_$server_msg_t mbx_recieve_msg_buf; +static mbx_$server_msg_t *mbx_ret_ptr; +static int mbx_ret_len; +static short mbx_chan; + +#include "ipcproto.h" + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_initialize_server + +This function creates an Aegis mailbox, and if successful, +calls ipc_get_line to wait for the first record sent which is +assumed to be the batch output filename. +*/ + + + +Ipc_Status_t ipc_transport_initialize_server (server_name, m, p, + batch_filename) + char *server_name; /* The mailbox pathname */ + Ipc_Mode_t m; /* Mode - interactive or batch */ + Ipc_Protocol_t p; /* Protocol type */ + char *batch_filename; /* Batch filename returned */ +{ + int len; + // extern void *malloc(); + + assert (p == IPC_PROTOCOL_V1); + + mbx_$create_server (server_name, strlen (server_name), mbx_$serv_msg_max, + 1, &mbx_handle, &status); + + if (status.all != status_$ok) { + fprintf (stderr, + "ERROR: IPC: Error creating mailbox server \"%s\"\n", + server_name); + error_$print (status); + mbx_state = IPC_MBX_UNINITIALIZED; + return IPC_STATUS_ERROR; + } else { + mbx_state = IPC_MBX_INITIALIZED; + /* + * First record is the name of the batch filename - whether we're in + * batch mode or not: + */ + return ipc_get_line (batch_filename, &len, IPC_WAIT); + } + /* + * shouldn't get here + */ + assert (0); + return IPC_STATUS_ERROR; +} +/*---------------------------------------------------------------------------*/ +Ipc_Status_t extract_msg (str, len) + char *str; + int *len; +{ + *len = mbx_ret_len - mbx_$serv_msg_hdr_len; + assert (*len >= 0); + + /* + * null terminate before copy: + */ + mbx_ret_ptr->data [*len] = '\0'; + strcpy (str, mbx_ret_ptr->data); + + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_get_line + +This function reads data sent by a client over the mailbox +channel. It also handles the initial opening of the +mailbox channel when requested by a client. +*/ + + + +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; /* The string text read from IPC channel */ + int *len; /* The length of str */ + Ipc_Wait_t wait; /* Blocking or non-blocking */ +{ + if (mbx_state == IPC_MBX_UNINITIALIZED) { + fprintf (stderr, + "ERROR: IPC: Attempted to read from non-initialized mailbox\n"); + return IPC_STATUS_ERROR; + } + + assert ((mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) || + (mbx_state == IPC_MBX_INITIALIZED)); + + do { + if (wait == IPC_WAIT) { + mbx_$get_rec (mbx_handle, &mbx_recieve_msg_buf, mbx_$serv_msg_max, + &mbx_ret_ptr, &mbx_ret_len, &status); + } else { + mbx_$get_conditional (mbx_handle, &mbx_recieve_msg_buf, + mbx_$serv_msg_max, &mbx_ret_ptr, &mbx_ret_len, + &status); + if (status.all == mbx_$channel_empty) { + return IPC_STATUS_NO_DATA; + } + } + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error reading from mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + switch (mbx_ret_ptr->mt) { + case mbx_$channel_open_mt: + if (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) { + /* + * we're already connected to a client... refuse the connection + */ + mbx_send_msg_buf.mt = mbx_$reject_open_mt; + } else { + mbx_send_msg_buf.mt = mbx_$accept_open_mt; + mbx_state = IPC_MBX_CONNECTED_TO_CLIENT; + } + mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len; + mbx_chan = mbx_ret_ptr->chan; + mbx_send_msg_buf.chan = mbx_chan; + + mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, mbx_$serv_msg_hdr_len, + &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + /* + * check to see if there was a message buried in the open request: + */ + if (mbx_ret_len > mbx_$serv_msg_hdr_len) { + return extract_msg (str, len); + } + break; + case mbx_$eof_mt: + mbx_chan = mbx_ret_ptr->chan; + mbx_$deallocate(mbx_handle, mbx_chan, &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error deallocating mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + mbx_state = IPC_MBX_INITIALIZED; + return IPC_STATUS_EOF; + break; + case mbx_$data_mt: + assert (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT); + return extract_msg (str, len); + break; + case mbx_$data_partial_mt: + fprintf (stderr, "ERROR: IPC: Recieved partial data message - ignored\n"); + break; + default: + fprintf (stderr, "ERROR: IPC: Bad message type (0x%x) recieved\n", + mbx_ret_ptr->mt); + } + } while (1); + return IPC_STATUS_ERROR; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_terminate_server + +This function calls ipc\_transport\_get\_line until it +receives an EOF from the client, which concludes the +communication. +*/ + + +Ipc_Status_t ipc_transport_terminate_server () +{ + char buffer[300]; + int len; + Ipc_Status_t status; + + do { + status = ipc_transport_get_line (buffer, &len, IPC_WAIT); + } while ((status =! IPC_STATUS_ERROR) && + (status =! IPC_STATUS_EOF)); + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_send_line + +This function sends a message to the current client through +the mailbox channel. +*/ + + +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; /* The bytes to send */ + int len; /* The number of bytes from str to send */ +{ + long cnt; + + if (mbx_state != IPC_MBX_CONNECTED_TO_CLIENT) { + fprintf (stderr, + "ERROR: IPC: Attempted to write to non-open mailbox\n"); + return IPC_STATUS_ERROR; + } + + mbx_send_msg_buf.mt = mbx_$data_mt; + if (mbx_$serv_msg_hdr_len + len > mbx_$serv_msg_max) { + fprintf (stderr, + "ERROR: IPC: send_line message too long - truncating\n"); + len = mbx_$serv_msg_max - mbx_$serv_msg_hdr_len; + } + + mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len + len; + mbx_send_msg_buf.chan = mbx_chan; + memcpy (mbx_send_msg_buf.data, str, len); + + cnt = mbx_send_msg_buf.cnt; + mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, cnt, &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + return IPC_STATUS_OK; +} + +#endif /* IPC_AEGIS_MAILBOXES */ diff --git a/src/xspice/ipc/ipcsockets.c b/src/xspice/ipc/ipcsockets.c new file mode 100755 index 000000000..8c4efeba9 --- /dev/null +++ b/src/xspice/ipc/ipcsockets.c @@ -0,0 +1,744 @@ +/*============================================================================= + + FILE IPCsockets.c + + Copyright 1991 + Georgia Tech Research Corporation, Atlanta, Georgia 30332 + All Rights Reserved + + + PROJECT ATESSE A-8503 + + AUTHOR + Stefan Roth July 1991 + + MODIFICATIONS + none + + SUMMARY + Generic Interprocess Communication module + Provides compatibility for the new SPICE simulator to both the MSPICE user + interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE + v.2 Simulator Interface and BCP (via BSD Sockets). This file contains the + BSD sockets version. + The Simulator is the server, while the SI and BCP will be the clients. + + + INTERFACES + + FILE ROUTINE CALLED + + IPC.c ipc_get_line(); + + + REFERENCED FILES + + Outputs to stderr. + + +=============================================================================*/ + + +/*============================================================================= + + DESCRIPTION OF FUNCTIONALITY: + + Outline of Initialize_Server function: + create socket; + bind name to socket; + getsockname; + listen; + sock_state = IPC_SOCK_INITIALIZED; + return ipc_get_line (); + + + Format of a message line: + bytes description + ----- ------------------- + 0 recognition character for beginning of line; value is BOL_CHAR. + 1-4 message length (not including bytes 0-4); 32 bits in htonl + format; + if value = -1, then EOF and socket should be closed. + 5-N+5 message body of length specified in bytes 1-4. + + The bytes before the message body are the message header. The header + length is specified as SOCK_MSG_HDR_LEN bytes. + + + Outline of Get_Line function: + read 5 characters; + verify that first char is BOL_CHAR; + interpret message length (N) from bytes 1-4; + do error checking on message header bytes; + read N characters as message body; + do error checking on message body read; + + + Outline of Send_Line function: + write BOL_CHAR; + write 4-byte message body length + write message body (N bytes) + do error checking after each write operation + + + Outline of Terminate_Server function: + Continue to read lines (with ipc_transport_get_line) and ignore + them until socket EOF is reached; + Close the socket. + + +=============================================================================*/ + + +/*#ifdef IPC_UNIX_SOCKETS */ + +/*=== INCLUDE FILES ===*/ +#include "ngspice.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipc.h" +#include "ipctiein.h" + + + + +/*=== TYPE DEFINITIONS ===*/ + +typedef enum { + IPC_SOCK_UNINITIALIZED, + IPC_SOCK_INITIALIZED, + IPC_SOCK_CONNECTED_TO_CLIENT +} Ipc_Sock_State_t; + + +/*=== LOCAL VARIABLES ===*/ + +static int sock_desc; /* socket descriptor */ +static int msg_stream; /* socket stream */ +static Ipc_Sock_State_t sock_state = IPC_SOCK_UNINITIALIZED; + + +/*=== INCLUDE FILES ===*/ + +#include "ipcproto.h" + +/*============================================================================= + +FUNCTION ipc_transport_initialize_server + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Creates and opens the BSD socket of the server. Listens for requests + by a client and then reads the first line message. + +INTERFACES + + Called by: (IPC.c) ipc_initialize_server(); + +RETURNED VALUE + + Ipc_Status_t - returns status of the socket connection. + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + +Ipc_Status_t ipc_transport_initialize_server (server_name, mode, protocol, + batch_filename) + char *server_name; /* not used */ + Ipc_Mode_t mode; /* not used */ + Ipc_Protocol_t protocol; /* IN - only used in assert */ + char *batch_filename; /* OUT - returns a value */ + /* Note that unused parameters are required to maintain compatibility */ + /* with version 1 (mailboxes) functions of the same names. */ +{ + struct sockaddr_in server; /* Server specifications for socket*/ + int server_length; /* Size of server structure */ + unsigned int port_num; /* Port number converted from server_name */ + + Ipc_Status_t ipc_get_line (); + + /* assert (protocol == IPC_PROTOCOL_V2); */ /* allow v1 protocol - wbk */ + assert (sock_state == IPC_SOCK_UNINITIALIZED); + + /* convert server_name (from atesse_xspice invocation line) to a port */ + /* number */ + port_num = atoi(server_name); + if((port_num > 0) && (port_num < 1024)) { + /* Reserved port number */ + perror ("ERROR: IPC Port numbers below 1024 are reserved"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + + sock_desc = socket (AF_INET, SOCK_STREAM, 0); + + if (sock_desc < 0) { + /* Unsuccessful socket opening */ + perror ("ERROR: IPC Creating socket"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + /* Socket opened successfully */ + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = port_num; /* SOCKET_PORT; */ + + server_length = sizeof (server); + if (bind (sock_desc, (struct sockaddr *)&server, server_length) + < 0) { + fprintf (stderr, "ERROR: IPC: Bind unsuccessful\n"); + perror ("ERROR: IPC"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + if (getsockname (sock_desc, (struct sockaddr *)&server, &server_length) + < 0) { + fprintf (stderr, "ERROR: IPC: getting socket name\n"); + perror ("ERROR: IPC"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + fprintf (stderr, "Socket port %d.\n", ntohs(server.sin_port)); + + listen (sock_desc, 5); + + sock_state = IPC_SOCK_INITIALIZED; + + /* Socket ok to use now */ + + /* + * First record is the name of the batch filename if we're in batch mode. + */ + + if(g_ipc.mode == IPC_MODE_BATCH) { + int len; + return ipc_get_line (batch_filename, &len, IPC_WAIT); + } + + /* Return success */ + return IPC_STATUS_OK; + +} /* end ipc_transport_initialize_server */ + + + +/*============================================================================= + +FUNCTION bytes_to_integer + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Convert four bytes at START in the string STR + to a 32-bit unsigned integer. The string is assumed + to be in network byte order and the returned value + is converted to host byte order (with ntohl). + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + u_long - unsigned 32 bit integer + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + +static u_long bytes_to_integer (str, start) + char *str; /* IN - string that contains the bytes to convert */ + int start; /* IN - index into string where bytes are */ +{ + u_long u; /* Value to be returned */ + char buff[4]; /* Transfer str into buff to word align reqd data */ + int index; /* Index into str and buff for transfer */ + + /* Get the str+start character and cast it into a u_long and put + the value through the network-to-host-short converter and store + it in the variable u. */ + + index = 0; + while (index < sizeof(u)) { + buff[index] = str[index+start]; + index++; + } + u = ntohl (*((u_long *) buff)); + + return u; +} /* end bytes_to_integer */ + + + +/*============================================================================= + +FUNCTION handle_socket_eof + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Do processing when the socket reaches EOF or when a message from the + client states that EOF has been reached. + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + Ipc_Status_t - always IPC_STATUS_EOF + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +static Ipc_Status_t handle_socket_eof () +{ + close (msg_stream); + close (sock_desc); + + sock_state = IPC_SOCK_UNINITIALIZED; + + return IPC_STATUS_EOF; +} /* handle_socket_eof */ + + + +/*============================================================================= + +FUNCTION read_sock + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Read N bytes from a socket. Only returns when the read had an error, + when 0 bytes (EOF) could be read, or LENGTH bytes are read. + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + int - Returns the total number of bytes read. + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +static int read_sock (stream, buffer, length, wait, flags) + int stream; /* IN - Socket stream */ + char *buffer; /* OUT - buffer to store incoming data */ + int length; /* IN - Number of bytes to be read */ + Ipc_Wait_t wait; /* IN - type of read operation */ + int flags; /* IN - Original socket flags for blocking read */ +{ + int count; /* Number of bytes read with last `read` */ + int totalcount; /* total number of bytes read */ + char *buf2; + +/* count = 0; */ +/* while (count < length) { */ +/* buffer[count] = 'x'; */ +/* count++; */ +/* } */ + count = read (stream, buffer, length); + if (wait == IPC_NO_WAIT) { + fcntl (stream, F_SETFL, flags); /* Revert to blocking read */ + } + if ((count <= 0) || (count == length)) { + /* If error or if read in reqd number of bytes: */ + return count; + } else { + /* Only got some of the bytes requested */ + totalcount = count; + buf2 = &buffer[totalcount]; + length = length - count; + while (length > 0) { + count = read (stream, buf2, length); + if (count <= 0) /* EOF or read error */ + break; + totalcount = totalcount + count; + buf2 = &buffer[totalcount]; + length = length - count; + } + if (length != 0) { + fprintf (stderr, "WARNING: READ_SOCK read %d bytes instead of %d\n", + totalcount, totalcount + length); + } + return totalcount; + } +} /* end read_sock */ + + + +/*============================================================================= + +FUNCTION ipc_transport_get_line + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Main function for reading one line from a socket. Requires that the + socket be open. Lines are mostly SPICE code, but commands may also + be embedded in the socket data and they are interpreted by this function. + Therefore, this function may cause the socket to be closed. + +INTERFACES + + Called by: ipc_transport_terminate_server(); + (IPC.c) ipc_get_line(); + +RETURNED VALUE + + Ipc_Status_t - returns status of the read operation + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; /* returns the result, null terminated */ + int *len; /* length of str passed IN and passed OUT */ + Ipc_Wait_t wait; /* IN - wait or dont wait on incoming msg */ +{ + int count; /* number of bytes read */ + int message_length; /* extracted from message header */ + + if (sock_state == IPC_SOCK_UNINITIALIZED) { + fprintf (stderr, + "ERROR: IPC: Attempted to read from uninitialized socket\n"); + return IPC_STATUS_ERROR; + } + + assert ((sock_state == IPC_SOCK_CONNECTED_TO_CLIENT) || + (sock_state == IPC_SOCK_INITIALIZED)); + + if (sock_state == IPC_SOCK_INITIALIZED) { + /* We have an open socket but have not connected to a client. */ + /* Accept a connection from a client. */ + msg_stream = accept (sock_desc, (struct sockaddr *)0, (int *)0); + + if (msg_stream == -1) { + fprintf (stderr, "ERROR: IPC: Server accepting request\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } + sock_state = IPC_SOCK_CONNECTED_TO_CLIENT; + } + /*-----------------------------------------------------------------------*/ + /* First read in the message header. */ + { + int flags; + flags = fcntl(msg_stream, F_GETFL, NULL); /* Blocking read mode */ + + if (wait == IPC_WAIT) { + /* Block here and wait for the next message */ + count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } + } else if (wait == IPC_NO_WAIT) { + /* Read message, but do not wait if none available. */ + + fcntl (msg_stream, F_SETFL, flags | O_NDELAY); + count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); + + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } else if (count == -1) { + if (errno == EWOULDBLOCK) { + return IPC_STATUS_NO_DATA; + } + } + + } else { + /* Serious problem, since it is not reading anything. */ + fprintf (stderr, + "ERROR: IPC: invalid wait arg to ipc_transport_get_line\n"); + } + } + + /* Do more error checking on the read in values of the message header: */ + if (count == -1) { + fprintf (stderr, "ERROR: IPC: Reading from socket\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } else if (str[0] != BOL_CHAR) { + fprintf (stderr, + "ERROR: IPC: Did not find beginning of message header (%c)\n", + str[0]); + return IPC_STATUS_ERROR; + } else if ((message_length = bytes_to_integer (str, 1)) == -1) { + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } else if (message_length == 0) { + *len = 0; + return IPC_STATUS_NO_DATA; + +/* Invalid test... delete - wbk + } else if (message_length > *len) { + fprintf (stderr, + "ERROR: IPC: Buffer (%d) is too short for message (%d)\n", + *len, message_length); + return IPC_STATUS_ERROR; +*/ + + } + + /*-----------------------------------------------------------------------*/ + /* Now read in the message body. */ + /* Always block here since the message header was already read and */ + /* we must get the body. */ + + *len = message_length; + count = read_sock (msg_stream, str, message_length); + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, */ + /* "WARNING: IPC: Reached eof in message body on socket\n");*/ + return handle_socket_eof (); + } else if (count == -1) { + fprintf (stderr, "ERROR: IPC: reading message body from socket\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } + + /* Looks like we have a valid message here. Put in the string terminator. */ + *len = count; + str[count] = '\0'; + + return IPC_STATUS_OK; + +} /* end ipc_transport_get_line */ + + + +/*============================================================================= + +FUNCTION ipc_transport_send_line + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + Send a line of information. First sends a message header and + then the actual message body. + Error checking is done to make reasonably sure that the data was sent. + + +INTERFACES + + Called by: (IPC.c) ipc_flush (); + +RETURNED VALUE + + Ipc_Status_t - returns status of the send operation (typically + IPC_STATUS_ERROR or IPC_STATUS_OK). + + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; /* IN - String to write */ + int len; /* IN - Number of characters out of STR to write */ +{ + int count; /* Counts how many bytes were actually written */ + u_long u; /* 32-bit placeholder for transmission of LEN */ + char hdr_buff[5]; /* Buffer for building header message in */ + int i; /* Temporary counter */ + char *char_ptr; /* Pointer for int to bytes conversion */ + + if (sock_state != IPC_SOCK_CONNECTED_TO_CLIENT) { + fprintf (stderr, "ERROR: IPC: Attempt to write to non-open socket\n"); + return IPC_STATUS_ERROR; + } + + /* Write message body header with length: */ + hdr_buff[0] = BOL_CHAR; + u = htonl (len); + char_ptr = (char *) &u; + for(i = 0; i < 4; i++) + hdr_buff[i+1] = char_ptr[i]; + + count = write (msg_stream, hdr_buff, 5); + if (count != 5) { + fprintf (stderr, "ERROR: IPC: (%d) send line error 1\n", count); + return IPC_STATUS_ERROR; + } + + /* Write message body: */ + count = write (msg_stream, str, len); + if (count != len) { + fprintf (stderr, "ERROR: IPC: (%d) send line error 2\n", count); + return IPC_STATUS_ERROR; + } + + return IPC_STATUS_OK; +} /* end ipc_transport_send_line */ + + + +/*============================================================================= + +FUNCTION ipc_transport_terminate_server + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + This function reads all pending incoming messages and discards them. + Reading continues until a read error occurs or EOF is reached, at which + time the socket is closed. + Note that this function does not actually close the socket. This is + done in ipc_transport_get_line, which is called in this function. + + In this function, the incoming line length is limited. See buffer below. + +INTERFACES + + Called by: (IPC.c) ipc_terminate_server(); + +RETURNED VALUE + + Ipc_Status_t - returns status of last read operation (always + IPC_STATUS_ERROR or IPC_STATUS_EOF). + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_terminate_server () +{ + char buffer[17000]; /* temp buffer for incoming data */ + int len; /* placeholder var to as arg to function */ + Ipc_Status_t status; /* value to be returned from function */ + int max_size; /* Max length of buffer */ + + max_size = sizeof (buffer); + do { + len = max_size; + status = ipc_transport_get_line (buffer, &len, IPC_WAIT); + } while ((status != IPC_STATUS_ERROR) && + (status != IPC_STATUS_EOF)); + return status; +} + +/*#endif IPC_UNIX_SOCKETS */ diff --git a/src/xspice/ipc/ipcstdio.c b/src/xspice/ipc/ipcstdio.c new file mode 100755 index 000000000..52598b240 --- /dev/null +++ b/src/xspice/ipc/ipcstdio.c @@ -0,0 +1,69 @@ +/* + * Steve Tynor + * + * Generic Interprocess Communication module + * + * Used for debugging in absense of IPC interface. + * + */ + +//#include + +#ifdef IPC_DEBUG_VIA_STDIO + +#include + + +#include "ipc.h" + +#include "ipcproto.h" + +#include /* 12/1/97 jg */ + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_initialize_server (server_name, m, p, + batch_filename) + char *server_name; + Ipc_Mode_t m; + Ipc_Protocol_t p; + char *batch_filename; +{ + assert (m == IPC_MODE_INTERACTIVE); + printf ("INITIALIZE_SERVER\n"); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; + int *len; + Ipc_Wait_t wait; +{ + printf ("GET_LINE\n"); + gets (str); + *len = strlen (str); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; + int len; +{ + int i; + + printf ("SEND_LINE: /"); + for (i = 0; i < len; i++) + putchar (str[i]); + printf ("/\n"); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_terminate_server () +{ +return IPC_STATUS_OK; +} + + +#endif /* IPC_DEBUG_VIA_STDIO */ diff --git a/src/xspice/ipc/ipctiein.c b/src/xspice/ipc/ipctiein.c new file mode 100755 index 000000000..2980241ab --- /dev/null +++ b/src/xspice/ipc/ipctiein.c @@ -0,0 +1,530 @@ +/*============================================================================ +FILE IPCtiein.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + Provides a protocol independent interface between the simulator + and the IPC method used to interface to CAE packages. + +INTERFACES + + g_ipc (global variable) + + ipc_handle_stop() + ipc_handle_returni() + ipc_handle_mintime() + ipc_handle_vtrans() + ipc_send_stdout() + ipc_send_stderr() + ipc_send_std_files() + ipc_screen_name() + ipc_get_devices() + ipc_free_devices() + ipc_check_pause_stop() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + + +#include "ngspice.h" +#include +#include +#include +//#include "util.h" +#include "inpdefs.h" +#include "gendefs.h" +#include "cktdefs.h" +#include "bjt/bjtdefs.h" +#include "jfet/jfetdefs.h" +#include "mos1/mos1defs.h" +#include "mos2/mos2defs.h" +#include "mos3/mos3defs.h" +#include "mifproto.h" +#include "ipc.h" +#include "ipctiein.h" + + + +/* +Global variable g_ipc is used by the SPICE mods that take care of +interprocess communications activities. +*/ + + +Ipc_Tiein_t g_ipc = { + IPC_FALSE, /* enabled */ + IPC_MODE_INTERACTIVE, /* mode */ + IPC_ANAL_DCOP, /* analysis mode */ + IPC_FALSE, /* parse_error */ + IPC_FALSE, /* run_error */ + IPC_FALSE, /* errchk_sent */ + IPC_FALSE, /* returni */ + 0.0, /* mintime */ + 0.0, /* lasttime */ + 0.0, /* cpu time */ + NULL, /* send array */ + NULL, /* log file */ + { /* vtrans struct */ + 0, /* size */ + NULL, /* vsrc_name array */ + NULL, /* device_name array */ + }, + IPC_FALSE, /* stop analysis */ +}; + + + +/* +ipc_handle_stop + +This function sets a flag in the g_ipc variable to signal that +a stop message has been received over the IPC channel. +*/ + +void ipc_handle_stop(void) +{ + g_ipc.stop_analysis = IPC_TRUE; +} + + +/* +ipc_handle_returni + +This function sets a flag in the g_ipc variable to signal that +a message has been received over the IPC channel specifying that +current values are to be returned in the results data sets. +*/ + +void ipc_handle_returni(void) +{ + g_ipc.returni = IPC_TRUE; +} + + +/* +ipc_handle_mintime + +This function sets a value in the g_ipc variable that specifies +how often data is to be returned as it is computed. If the +simulator takes timestep backups, data may still be returned +more often that that specified by 'mintime' so that glitches +are not missed. +*/ + +void ipc_handle_mintime(double time) +{ + g_ipc.mintime = time; +} + + + +/* +ipc_handle_vtrans + +This function processes arguments from a #VTRANS card received over +the IPC channel. The data on the card specifies that a particular +zero-valued voltage source name should be translated to the specified +instance name for which it was setup to monitor currents. +*/ + +void ipc_handle_vtrans( + char *vsrc, /* The name of the voltage source to be translated */ + char *dev) /* The device name the vsource name should be translated to */ +{ + int i; + int size; + + + if(g_ipc.vtrans.size == 0) { + g_ipc.vtrans.size = 1; + g_ipc.vtrans.vsrc_name = (void *) MALLOC(sizeof(char *)); + g_ipc.vtrans.device_name = (void *) MALLOC(sizeof(char *)); + g_ipc.vtrans.vsrc_name[0] = MIFcopy(vsrc); + g_ipc.vtrans.device_name[0] = MIFcopy(dev); + } + else { + g_ipc.vtrans.size++; + + size = g_ipc.vtrans.size; + i = g_ipc.vtrans.size - 1; + + g_ipc.vtrans.vsrc_name = (void *) REALLOC(g_ipc.vtrans.vsrc_name, + size * sizeof(char *)); + g_ipc.vtrans.device_name = (void *) REALLOC(g_ipc.vtrans.device_name, + size * sizeof(char *)); + g_ipc.vtrans.vsrc_name[i] = MIFcopy(vsrc); + g_ipc.vtrans.device_name[i] = MIFcopy(dev); + } +} + + + +/* +ipc_send_stdout + +This function sends the data written to stdout over the IPC channel. +This stream was previously redirected to a temporary file during +the simulation. +*/ + +void ipc_send_stdout(void) +{ + int c; + int len; + + char buf[IPC_MAX_LINE_LEN+1]; + + /* rewind the redirected stdout stream */ + rewind(stdout); + + /* Begin reading from the top of file and send lines */ + /* over the IPC channel. */ + + /* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ + /* we must wrap it because Mspice can't handle it */ + + len = 0; + while( (c=fgetc(stdout)) != EOF) { + if(c != '\n') { + buf[len] = c; + len++; + } + if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { + buf[len] = '\0'; + ipc_send_line(buf); + len = 0; + } + } + if(len > 0) { + buf[len] = '\0'; + ipc_send_line(buf); + } + + /* Finally, rewind file again to discard the data already sent */ + rewind(stdout); +} + + +/* +ipc_send_stderr + +This function sends the data written to stderr over the IPC channel. +This stream was previously redirected to a temporary file during +the simulation. +*/ + +void ipc_send_stderr(void) +{ + int c; + int len; + + char buf[IPC_MAX_LINE_LEN+1]; + + /* rewind the redirected stderr stream */ + rewind(stderr); + + /* Begin reading from the top of file and send lines */ + /* over the IPC channel. */ + + /* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ + /* we must wrap it because Mspice can't handle it */ + + len = 0; + while( (c=fgetc(stderr)) != EOF) { + if(c != '\n') { + buf[len] = c; + len++; + } + if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { + buf[len] = '\0'; + ipc_send_line(buf); + len = 0; + } + } + if(len > 0) { + buf[len] = '\0'; + ipc_send_line(buf); + } + + /* Finally, rewind file again to discard the data already sent */ + rewind(stderr); +} + + +/* +ipc_send_std_files + +This function sends the data written to stdout and stderr over the +IPC channel. These streams were previously redirected to temporary +files during the simulation. +*/ + +Ipc_Status_t ipc_send_std_files() +{ + ipc_send_stdout(); + ipc_send_stderr(); + + return(ipc_flush()); +} + + + +/* +ipc_screen_name + +This function screens names of instances and nodes to limit the +data returned over the IPC channel. +*/ + +Ipc_Boolean_t ipc_screen_name(char *name, char *mapped_name) +{ + char *endp; + int i; + int len; + long l; + + /* Return FALSE if name is in a subcircuit */ + for(i = 0; name[i] != '\0'; i++) { + if(name[i] == ':') + return(IPC_FALSE); + } + + /* Determine if name is numeric and what value is */ + l = strtol(name, &endp, 10); + + /* If numeric */ + if(*endp == '\0') { + /* Return FALSE if >100,000 -> added by ms_server in ATESSE 1.0 */ + if(l >= 100000) + return(IPC_FALSE); + /* Otherwise, copy name to mapped name and return true */ + else { + strcpy(mapped_name,name); + return(IPC_TRUE); + } + } + + /* If node is an internal node from a semiconductor (indicated by a */ + /* trailing #collector, #source, ...), do not return its current. */ + /* Otherwise, map to upper case and eliminate trailing "#branch" if any. */ + for(i = 0; name[i]; i++) { + if(name[i] == '#') { + if(strcmp(name + i, "#branch") == 0) + break; + else + return(IPC_FALSE); + } + else { + if(islower(name[i])) + mapped_name[i] = toupper(name[i]); + else + mapped_name[i] = name[i]; + } + } + mapped_name[i] = '\0'; + len = i; + + /* If len != 8 or 6'th char not equal to $, then doesn't need vtrans */ + /* Otherwise, translate to device name that it monitors */ + if(len != 8) + return(IPC_TRUE); + else if(name[5] != '$') + return(IPC_TRUE); + else { + /* Scan list of prefixes in VTRANS table and convert name */ + for(i = 0; i < g_ipc.vtrans.size; i++) { + if(strncmp(mapped_name, g_ipc.vtrans.vsrc_name[i], 5) == 0) { + strcpy(mapped_name, g_ipc.vtrans.device_name[i]); + return(IPC_TRUE); + } + } + return(IPC_TRUE); + } + + return(IPC_TRUE); +} + + + +/* +ipc_get_devices + +This function is used to setup the OUTinterface data structure that +determines what instances will have data returned over the IPC channel. +*/ + + +int ipc_get_devices( + void *circuit, /* The circuit structure */ + char *device, /* The device name as it appears in the info struct */ + char ***names, /* Array of name strings to be built */ + double **modtypes) /* Array of types to be built */ +{ + int index; + int num_instances; + GENmodel *model; + GENinstance *here; + CKTcircuit *ckt; + char *inst_name; + int inst_name_len; + int i; + + BJTmodel *BJTmod; + JFETmodel *JFETmod; + MOS1model *MOS1mod; + MOS2model *MOS2mod; + MOS3model *MOS3mod; + + /* Initialize local variables */ + ckt = (CKTcircuit *) circuit; + num_instances = 0; + + /* Get the index into the circuit structure linked list of models */ + index = INPtypelook(device); + + /* Iterate through all models of this type */ + for(model = ckt->CKThead[index]; model; model = model->GENnextModel) { + + /* Iterate through all instance of this model */ + for(here = model->GENinstances; here; here = here->GENnextInstance) { + + /* Get the name of the instance */ + inst_name = here->GENname; + inst_name_len = strlen(inst_name); + + /* Skip if it is a inside a subcircuit */ + for(i = 0; i < inst_name_len; i++) + if(inst_name[i] == ':') + break; + if(i < inst_name_len) + continue; + + /* Otherwise, add the name to the list */ + num_instances++; + if(num_instances == 1) + *names = (char **) MALLOC(sizeof(char *)); + else + *names = (char **) REALLOC(*names, num_instances * sizeof(char *)); + (*names)[num_instances-1] = MIFcopy(inst_name); + + /* Then get the type if it is a Q J or M */ + if(num_instances == 1) + *modtypes = (double *) MALLOC(sizeof(double)); + else + *modtypes = (double *) REALLOC((char *) *modtypes, + num_instances * sizeof(double)); + + if(strcmp(device,"BJT") == 0) { + BJTmod = (BJTmodel *) model; + (*modtypes)[num_instances-1] = BJTmod->BJTtype; + } + else if(strcmp(device,"JFET") == 0) { + JFETmod = (JFETmodel *) model; + (*modtypes)[num_instances-1] = JFETmod->JFETtype; + } + else if(strcmp(device,"Mos1") == 0) { + MOS1mod = (MOS1model *) model; + (*modtypes)[num_instances-1] = MOS1mod->MOS1type; + } + else if(strcmp(device,"Mos2") == 0) { + MOS2mod = (MOS2model *) model; + (*modtypes)[num_instances-1] = MOS2mod->MOS2type; + } + else if(strcmp(device,"Mos3") == 0) { + MOS3mod = (MOS3model *) model; + (*modtypes)[num_instances-1] = MOS3mod->MOS3type; + } + else { + (*modtypes)[num_instances-1] = 1.0; + } + + } /* end for all instances */ + } /* end for all models */ + + return(num_instances); +} + + + +/* +ipc_free_devices + +This function frees temporary data created by ipc_get_devices(). +*/ + + +void ipc_free_devices( + int num_items, /* Number of things to free */ + char **names, /* Array of name strings to be built */ + double *modtypes) /* Array of types to be built */ +{ + int i; + + for(i = 0; i < num_items; i++) + { + FREE(names[i]); + names[i] = 0; + } + + if(num_items > 0) + { + FREE(names); + FREE(modtypes); + + names = NULL; + modtypes = NULL; + } +} + + +/* +ipc_check_pause_stop + +This function is called at various times during a simulation to check +for incoming messages of the form >STOP or >PAUSE signaling that +simulation should be stopped or paused. Processing of the messages +is handled by ipc_get_line(). +*/ + +void ipc_check_pause_stop(void) +{ + char buf[1025]; + int len; + + /* If already seen stop analysis, don't call ipc_get_line, just return. */ + /* This is provided so that the function can be called multiple times */ + /* during the process of stopping */ + if(g_ipc.stop_analysis) + return; + + /* Otherwise do a non-blocking call to ipc_get_line() to check for messages. */ + /* We assume that the only possible messages at this point are >PAUSE */ + /* and >STOP, so we don't do anything with the returned text if any */ + ipc_get_line(buf, &len, IPC_NO_WAIT); +} + diff --git a/src/xspice/lib/analog.cm b/src/xspice/lib/analog.cm new file mode 100755 index 000000000..b46966130 Binary files /dev/null and b/src/xspice/lib/analog.cm differ diff --git a/src/xspice/lib/digital.cm b/src/xspice/lib/digital.cm new file mode 100755 index 000000000..383c0b636 Binary files /dev/null and b/src/xspice/lib/digital.cm differ diff --git a/src/xspice/lib/xtradev.cm b/src/xspice/lib/xtradev.cm new file mode 100755 index 000000000..46f308717 Binary files /dev/null and b/src/xspice/lib/xtradev.cm differ diff --git a/src/xspice/lib/xtraevt.cm b/src/xspice/lib/xtraevt.cm new file mode 100755 index 000000000..fa135648d Binary files /dev/null and b/src/xspice/lib/xtraevt.cm differ diff --git a/src/xspice/mif/Makefile.am b/src/xspice/mif/Makefile.am new file mode 100755 index 000000000..757d3dcd8 --- /dev/null +++ b/src/xspice/mif/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libmifxsp.a + +libmifxsp_a_SOURCES = \ + mif_inp2.c \ + mifgetmod.c \ + mifgetvalue.c \ + mifload.c \ + mifmpara.c \ + mifsetup.c \ + mifutil.c \ + mifask.c \ + mifmask.c \ + miftrunc.c \ + mifconvt.c \ + mifdelete.c \ + mifmdelete.c \ + mifdestr.c \ + mif.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/mif/mif.c b/src/xspice/mif/mif.c new file mode 100755 index 000000000..42227c0d4 --- /dev/null +++ b/src/xspice/mif/mif.c @@ -0,0 +1,62 @@ +/*============================================================================ +FILE MIF.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file allocates globals used by various packages, including MIF. + +INTERFACES + + g_mif_info + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include "mif.h" + +/* jgroves */ + +int MIFiSize = sizeof(MIFinstance); /* jgroves */ +int MIFmSize = sizeof(MIFmodel); /* jgroves */ + + +/* Allocate global used to pass info on analysis type, etc. from */ +/* SPICE to the MIF load routine */ + + +/* This must be initialized so that EVTfindvec can check for */ +/* NULL pointer in g_mif_info.ckt */ + +Mif_Info_t g_mif_info = { + { MIF_FALSE, MIF_FALSE, MIF_DC, MIF_ANALOG, 0.0,}, + NULL, + NULL, + NULL, + { 0.0, 0.0,}, + { MIF_FALSE, MIF_FALSE,}, +}; diff --git a/src/xspice/mif/mif_inp2.c b/src/xspice/mif/mif_inp2.c new file mode 100755 index 000000000..7c5f55f2a --- /dev/null +++ b/src/xspice/mif/mif_inp2.c @@ -0,0 +1,885 @@ +/*============================================================================ +FILE MIF_INP2A.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the main routine for parsing code model lines + in the SPICE circuit description input deck. + +INTERFACES + + MIF_INP2A() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ /* jgroves */ +#include "ngspice.h" +#include +//#include "util.h" +#include "ifsim.h" +#include "inpdefs.h" +#include "devdefs.h" +#include "inpmacs.h" +#include "fteext.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +#include "evt.h" +#include "evtproto.h" + +/* #include "suffix.h" */ /* jgroves */ + + + +/*#define NUM_SPICE3_MODELS 40 (was 23 - jgroves Number of Berkeley models in DEVices array. */ + /* See CKT/SPIinit.c */ + +extern int *DEVicesfl; /*flags for the devices */ +extern SPICEdev **DEVices; /* info about all device types */ +extern int DEVmaxnum; /* size of DEVices array */ + + +static void MIFinit_inst(MIFmodel *mdfast, MIFinstance *fast); + +static void MIFget_port_type( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t *port_type, + char **port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + Mif_Status_t *status); + + +static void MIFget_port( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + MIFinstance *fast, /* pointer to instance struct */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t def_port_type, + char *def_port_type_str, + Mif_Conn_Info_t *conn_inf, /* for faster access to conn info struct */ + int conn_num, + int port_num, + Mif_Status_t *status); + + + + +/* ********************************************************************* */ + + + +/* +MIF_INP2A + +This function is called by INPpas2() in SPICE to parse the new +``a'' type element cards and build the required circuit structures +for the associated code model device and instance. It first +checks the model name at the end of the element card to be sure +the model was found in pass 1 of the parser. If so, MIFgetMod is +called to process the .model card, creating the necessary +internal model structure and filling in the parameter value +information. Next, the instance structure is allocated. +Finally, the connections on the element card are scanned and the +connection information is filled-in on the instance structure, +and error checks are performed. +*/ + + +void +MIF_INP2A(ckt,tab,current) + +void *ckt; /* circuit structure to put mod/inst structs in */ +INPtables *tab; /* symbol table for node names, etc. */ +card *current; /* the card we are to parse */ + /* Must be called "current" for compatibility */ + /* with macros */ +{ + +/* parse a code model instance card */ +/* Aname */ + + char *line; /* the text line for this card */ + char *name; /* the name of the instance */ + char *model=NULL; /* the name of the model */ + + char *def_port_type_str; /* The default port type in string form */ + char *next_token; /* a token string */ + + int i; /* a loop counter */ + int j; /* a loop counter */ + int type; /* the type of the model for this instance */ + /* int num_conn; number of connections for this model */ + int error; /* for the IFC macro */ + + MIFmodel *mdfast; /* pointer to model struct */ + MIFinstance *fast; /* pointer to instance struct */ + + INPmodel *thismodel; /* pointer to model struct */ + + Mif_Conn_Info_t *conn_info; /* for faster access to conn info struct */ + Mif_Param_Info_t *param_info; /* for faster access to param info struct */ + Mif_Port_Type_t def_port_type; /* the default port type */ + Mif_Status_t status; /* return status */ + Mif_Token_Type_t next_token_type; /* the type of the next token */ + + + /* get the line text from the card struct */ + + line = current->line; + + + /* get the name of the instance and add it to the symbol table */ + + name = MIFgettok(&line); + INPinsert(&name, tab); + + + /* locate the last token on the line and put it into "model" */ + + while(*line != '\0') + model = MIFgettok(&line); + + if(model == NULL) { + LITERR("Missing model on A type device"); + return; + } + + /* Locate model from pass 1. If it hasn't been processed yet, */ + /* allocate a structure in ckt for it, process its parameters */ + /* and return a pointer to its structure in 'thismodel' */ + + current->error = MIFgetMod(ckt, model, &thismodel, tab); + + if(current->error) { + return; + } + + + /* get the integer index into the DEVices data array for this */ + /* model */ + + type = thismodel->INPmodType; + + if((type >= DEVmaxnum) || DEVicesfl[type] == 0) { + LITERR("Invalid model type for A type device"); + return; + } + + /* create a new structure for this instance in ckt */ + + mdfast = thismodel->INPmodfast; + IFC(newInstance, (ckt, mdfast,(void **)&fast, name)) + + + /* initialize the code model specific elements of the inst struct */ + + MIFinit_inst(mdfast, fast); + + + /* *********************** */ + /* Process the connections */ + /* *********************** */ + + /* get the line text from the card struct */ + /* skipping over the name of the instance */ + /* and reading the first token following */ + + line = current->line; + MIFgettok(&line); + next_token = MIFget_token(&line,&next_token_type); + + + /* loop through the fixed number of connections expected */ + + for(i = 0; i < DEVices[type]->DEVpublic.num_conn; i++) { + + /* there better be at least one more token besides the model name */ + if(*line == '\0') { + LITERR("Missing connections on A device"); + return; + } + + /* prepare a pointer for fast access to info about this connection */ + conn_info = &(DEVices[type]->DEVpublic.conn[i]); + + /* get the default port type for this connection */ + def_port_type = conn_info->default_port_type; + def_port_type_str = conn_info->default_type; + + /* set analog and event_driven flags on instance and model */ + if((def_port_type == MIF_DIGITAL) || (def_port_type == MIF_USER_DEFINED)) { + fast->event_driven = MIF_TRUE; + mdfast->event_driven = MIF_TRUE; + } + else { + fast->analog = MIF_TRUE; + mdfast->analog = MIF_TRUE; + } + + /* check for a null connection and continue to next connection if found */ + + if(next_token_type == MIF_NULL_TOK) { + + /* make sure null is allowed */ + if(! conn_info->null_allowed) { + LITERR("NULL connection found where not allowed"); + return; + } + + /* set the null flag to true */ + fast->conn[i]->is_null = MIF_TRUE; + fast->conn[i]->size = 0; + + /* eat the null token and continue to next connection */ + next_token = MIFget_token(&line,&next_token_type); + continue; + } + else { + /* set the null flag to false */ + fast->conn[i]->is_null = MIF_FALSE; + } + + /* process connection as appropriate for scalar or array */ + + if(! conn_info->is_array) { /* a scalar connection - the simpler case */ + + /* do a couple of error checks */ + if(next_token_type == MIF_LARRAY_TOK) { + LITERR("ERROR - Scalar connection expected, [ found"); + return; + } + if(next_token_type == MIF_RARRAY_TOK) { + LITERR("ERROR - Unexpected ]"); + return; + } + + /* If all OK, get the port data into the instance struct */ + /* allocating the port member of the instance struct as needed */ + MIFget_port(ckt, + tab, + current, + fast, + &line, + &next_token, + &next_token_type, + def_port_type, + def_port_type_str, + conn_info, + i, /* connection index */ + 0, /* port index for scalar connection */ + &status); + + if(status == MIF_ERROR) + return; + + fast->conn[i]->size = 1; + } + else { /* the connection is an array - much to be done ... */ + + /* get the leading port type for the array if any */ + /* it will distribute across all ports inside the braces */ + /* overriding the default type in the interface spec */ + + if(next_token_type == MIF_PERCENT_TOK) { + + /* get the port type identifier and check it for validity */ + next_token = MIFget_token(&line,&next_token_type); + MIFget_port_type(ckt, + tab, + current, + &line, + &next_token, + &next_token_type, + &def_port_type, + &def_port_type_str, + conn_info, + &status); + if(status == MIF_ERROR) + return; + } + + /* check for required leading array delim character and eat it if found */ + if(next_token_type != MIF_LARRAY_TOK) { + LITERR("Missing [, an array connection was expected"); + return; + } + else + next_token = MIFget_token(&line,&next_token_type); + + /* get and process ports until ] is encountered */ + + for(j = 0; + (next_token_type != MIF_RARRAY_TOK) && + (*line != '\0'); + j++) { + + /* First, do some error checks */ + + /* check for required leading array delim character */ + if(next_token_type == MIF_LARRAY_TOK) { + LITERR("ERROR - Unexpected [ - Arrays of arrays not allowed"); + return; + } + + /* If all OK, get the port nodes into the instance struct */ + /* allocating the port member of the instance struct as needed */ + + MIFget_port(ckt, + tab, + current, + fast, + &line, + &next_token, + &next_token_type, + def_port_type, + def_port_type_str, + conn_info, + i, /* connection index */ + j, /* port index */ + &status); + + if(status == MIF_ERROR) + return; + } + + /* make sure we exited because the end of the array connection */ + /* was reached. If so, eat the closing array delimiter */ + if(*line == '\0') { + LITERR("Missing ] in array connection"); + return; + } + else + next_token = MIFget_token(&line,&next_token_type); + + /* record the number of ports found for this connection */ + if(j < 1) { + LITERR("Array connection must have at least one port"); + return; + } + fast->conn[i]->size = j; + + } /* array connection processing */ + + /* be careful about putting stuff here, there is a 'continue' used */ + /* in the processing of NULL connections above */ + + } /* for number of connections */ + + + /* *********************** */ + /* Error Checks */ + /* *********************** */ + + /* check for too many connections */ + + if(*line != '\0') { + LITERR("Too many connections"); + return; + } + + /* check connection constraints */ + + for(i = 0; i < DEVices[type]->DEVpublic.num_conn; i++) { + + conn_info = &(DEVices[type]->DEVpublic.conn[i]); + + if( (fast->conn[i]->is_null) && + (! conn_info->null_allowed) ) { + LITERR("Null found for connection where not allowed"); + return; + } + + if(conn_info->has_lower_bound) { + if(fast->conn[i]->size < conn_info->lower_bound) { + LITERR("Too few ports in connection"); + return; + } + } + + if(conn_info->has_upper_bound) { + if(fast->conn[i]->size > conn_info->upper_bound) { + LITERR("Too many ports in connection"); + return; + } + } + } + + /* check model parameter constraints */ + /* some of these should probably be done in MIFgetMod() */ + /* to prevent multiple error messages */ + + for(i = 0; i < DEVices[type]->DEVpublic.num_param; i++) { + + param_info = &(DEVices[type]->DEVpublic.param[i]); + + if(mdfast->param[i]->is_null) { + if(! param_info->has_default) { + LITERR("Parameter on model has no default"); + return; + } + else if((param_info->is_array) && (! param_info->has_conn_ref)) { + LITERR("Defaulted array parameter must have associated array connection"); + return; + } + } + if((! mdfast->param[i]->is_null) && (param_info->is_array)) { + if(param_info->has_conn_ref) { + if(fast->conn[param_info->conn_ref]->size != fast->param[i]->size) { + LITERR("Array parameter size on model does not match connection size"); + return; + } + } + } + } +} + + +/* ********************************************************************* */ + + + +/* +MIFinit_inst + +This function initializes the code model specific elements of the inst struct. +*/ + + +static void MIFinit_inst( + MIFmodel *mdfast, /* The model the instance is derived from */ + MIFinstance *fast) /* The instance to initialize */ +{ + + int mod_type; /* type of this model */ + + Mif_Conn_Info_t *conn_info; + + int i; + + + /* get an index into the DEVices information structure */ + + mod_type = mdfast->MIFmodType; + + + /* allocate code model connector data in instance struct */ + + fast->num_conn = DEVices[mod_type]->DEVpublic.num_conn; + fast->conn = (void *) tmalloc(fast->num_conn * sizeof(void *)); + + for(i = 0; i < fast->num_conn; i++) + fast->conn[i] = (void *) tmalloc(sizeof(Mif_Conn_Data_t)); + + /* initialize code model connector data */ + for(i = 0; i < fast->num_conn; i++) { + conn_info = &(DEVices[mod_type]->DEVpublic.conn[i]); + fast->conn[i]->name = conn_info->name; + fast->conn[i]->description = conn_info->description; + fast->conn[i]->is_null = MIF_TRUE; + fast->conn[i]->size = 0; + fast->conn[i]->port = NULL; + switch(conn_info->direction) { + case MIF_INOUT: + fast->conn[i]->is_input = MIF_TRUE; + fast->conn[i]->is_output = MIF_TRUE; + break; + case MIF_IN: + fast->conn[i]->is_input = MIF_TRUE; + fast->conn[i]->is_output = MIF_FALSE; + break; + case MIF_OUT: + fast->conn[i]->is_input = MIF_FALSE; + fast->conn[i]->is_output = MIF_TRUE; + break; + default: + printf("\nERROR - Impossible direction type in MIFinit_inst\n"); + exit(1); + } + } + + + /* allocate and copy instance variable data to the instance */ + + fast->num_inst_var = DEVices[mod_type]->DEVpublic.num_inst_var; + fast->inst_var = (void *) tmalloc(fast->num_inst_var * sizeof(void *)); + + for(i = 0; i < fast->num_inst_var; i++) { + + fast->inst_var[i] = (void *) tmalloc(sizeof(Mif_Inst_Var_Data_t)); + + if(DEVices[mod_type]->DEVpublic.inst_var[i].is_array) { + fast->inst_var[i]->size = 0; + fast->inst_var[i]->element = NULL; + /* The code model allocates space for the data and sets the size */ + } + else { + fast->inst_var[i]->size = 1; + fast->inst_var[i]->element = (void *) tmalloc(sizeof(Mif_Value_t)); + } + } + + + /* copy model parameter data to the instance */ + + fast->num_param = mdfast->num_param; + fast->param = mdfast->param; + + /* initialize any additional instance data */ + fast->initialized = MIF_FALSE; + fast->analog = MIF_FALSE; + fast->event_driven = MIF_FALSE; + fast->inst_index = 0; +} + + + +/* ********************************************************************* */ + + + +/* +MIFget_port_type + +This function gets the port type identifier and checks it for validity. +*/ + + +static void +MIFget_port_type( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t *port_type, + char **port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + Mif_Status_t *status) +{ + + Mif_Boolean_t found_type; + char *temp; + + int i; + + if(**line == '\0') { + LITERR("Missing connections on A device"); + *status = MIF_ERROR; + return; + } + + if(*next_token_type != MIF_STRING_TOK) { + LITERR("Invalid port type specifier"); + *status = MIF_ERROR; + return; + } + + /* OK, so get the port type string from the token and read next token */ + + temp = *next_token; + *next_token = MIFget_token(line, next_token_type); + + /* check port type for validity */ + + found_type = MIF_FALSE; + + for(i = 0; i < conn_info->num_allowed_types; i++) { + if(strcmp(temp, conn_info->allowed_type_str[i]) == 0) { + found_type = MIF_TRUE; + *port_type = conn_info->allowed_type[i]; + *port_type_str = temp; + break; + } + } + + if(! found_type) { + LITERR("Port type is invalid"); + *status = MIF_ERROR; + } + else + *status = MIF_OK; +} + + + + +/* ********************************************************************* */ + + +/* +MIFget_port + +This function processes a port being parsed, either single ended, +or both connections of a differential. +*/ + + + + +static void +MIFget_port( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + MIFinstance *fast, /* pointer to instance struct */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t def_port_type, + char *def_port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + int conn_num, + int port_num, + Mif_Status_t *status) + +{ + + + CKTnode *pos_node; /* positive connection node */ + CKTnode *neg_node; /* negative connection node */ + + char *node; + + /* get the leading port type if any */ + + if(*next_token_type == MIF_PERCENT_TOK) { + + /* get the port type identifier and check it for validity */ + *next_token = MIFget_token(line, next_token_type); + MIFget_port_type(ckt, + tab, + current, + line, + next_token, + next_token_type, + &def_port_type, + &def_port_type_str, + conn_info, + status); + + if(*status == MIF_ERROR) { + return; + } + } + + /* allocate space in the instance data struct for this port */ + if(port_num == 0) { + fast->conn[conn_num]->port = (void *) tmalloc(sizeof(void *)); + fast->conn[conn_num]->port[0] = (void *) tmalloc(sizeof(Mif_Port_Data_t)); + } + else { + fast->conn[conn_num]->port = (void *) REALLOC( + fast->conn[conn_num]->port, + ((port_num + 1) * sizeof(void *)) ); + fast->conn[conn_num]->port[port_num] = (void *) tmalloc(sizeof(Mif_Port_Data_t)); + } + + + /* store the port type information in the instance struct */ + fast->conn[conn_num]->port[port_num]->type = def_port_type; + fast->conn[conn_num]->port[port_num]->type_str = def_port_type_str; + + /* check for a leading tilde on digital ports */ + if(*next_token_type == MIF_TILDE_TOK) { + if((def_port_type != MIF_DIGITAL) && (def_port_type != MIF_USER_DEFINED)) { + LITERR("ERROR - Tilde not allowed on analog nodes"); + *status = MIF_ERROR; + return; + } + fast->conn[conn_num]->port[port_num]->invert = MIF_TRUE; + + /* eat the tilde and get the next token */ + *next_token = MIFget_token(line, next_token_type); + if(**line == '\0') { + LITERR("ERROR - Not enough ports"); + *status = MIF_ERROR; + return; + } + } + else + fast->conn[conn_num]->port[port_num]->invert = MIF_FALSE; + + + /* check for null port */ + if(*next_token_type == MIF_NULL_TOK) { + + /* make sure null is allowed */ + if(! conn_info->null_allowed) { + LITERR("NULL connection found where not allowed"); + *status = MIF_ERROR; + return; + } + + /* set the (port specific) null flag to true */ + fast->conn[conn_num]->port[port_num]->is_null = MIF_TRUE; + + /* set input value to zero in case user code model refers to it */ + fast->conn[conn_num]->port[port_num]->input.rvalue = 0.0; + + /* eat the null token and return */ + *next_token = MIFget_token(line, next_token_type); + *status = MIF_OK; + return; + } + else { + /* set the (port specific) null flag to false */ + fast->conn[conn_num]->port[port_num]->is_null = MIF_FALSE; + } + + + /* next token must be a node/instance identifier ... */ + if(*next_token_type != MIF_STRING_TOK) { + LITERR("ERROR - Expected node/instance identifier"); + *status = MIF_ERROR; + return; + } + + /* Get the first connection or the voltage source name */ + + switch(def_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + + /* Call the spice3c1 function to put this node in the node list in ckt */ + INPtermInsert(ckt, next_token, tab,(void **)&pos_node); + + /* store the equation number and node identifier */ + /* This is the equivalent of what CKTbindNode() does in 3C1 */ + fast->conn[conn_num]->port[port_num]->pos_node_str = *next_token; + fast->conn[conn_num]->port[port_num]->smp_data.pos_node = pos_node->number; + + break; + + case MIF_VSOURCE_CURRENT: + + /* Call the spice3c1 function to put this vsource instance name in */ + /* the symbol table */ + INPinsert(next_token, tab); + + /* Now record the name of the vsource instance for processing */ + /* later by MIFsetup. This is equivalent to what INPpName */ + /* does in 3C1. Checking to see if the source is present in */ + /* the circuit is deferred to MIFsetup as is done in 3C1. */ + fast->conn[conn_num]->port[port_num]->vsource_str = *next_token; + + break; + + case MIF_DIGITAL: + case MIF_USER_DEFINED: + /* Insert data into event-driven info structs */ + EVTtermInsert(ckt, + fast, + *next_token, + def_port_type_str, + conn_num, + port_num, + &(current->error)); + if(current->error) { + *status = MIF_ERROR; + return; + } + break; + + default: + + /* impossible connection type */ + LITERR("INTERNAL ERROR - Impossible connection type"); + *status = MIF_ERROR; + return; + } + + /* get the next token */ + *next_token = MIFget_token(line, next_token_type); + + /* get other node if appropriate */ + switch(def_port_type) { + + case MIF_VOLTAGE: + case MIF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_RESISTANCE: + /* These are single ended types, so default other node to ground */ + // This don't work dickhead, INPtermInsert tries to FREE(&node) K.A. Feb 27, 2000 + // which was not allocted + node = (char*)malloc(2);// added by K.A. march 5th 2000 + + *node = '0'; // added by K.A. March 5th 2000 + node[1] ='\0'; // added by K.A. March 5th 2000 +// node = "0"; // deleted by K.A. March 5th 2000, this is incorrect, it creates a new pointer + // that cause a crash in INPtermInsert() + + INPtermInsert(ckt, &node, tab,(void **)&neg_node); + + fast->conn[conn_num]->port[port_num]->neg_node_str = node; + fast->conn[conn_num]->port[port_num]->smp_data.neg_node = neg_node->number; + break; + + case MIF_DIFF_VOLTAGE: + case MIF_DIFF_CURRENT: + case MIF_DIFF_CONDUCTANCE: + case MIF_DIFF_RESISTANCE: + /* These are differential types, so get the other node */ + if((**line == '\0') || (*next_token_type != MIF_STRING_TOK)) { + LITERR("ERROR - Expected node identifier"); + *status = MIF_ERROR; + return; + } + INPtermInsert(ckt, next_token, tab,(void **)&neg_node); + fast->conn[conn_num]->port[port_num]->neg_node_str = *next_token; + fast->conn[conn_num]->port[port_num]->smp_data.neg_node = neg_node->number; + *next_token = MIFget_token(line, next_token_type); + break; + + default: + /* must be vsource name, digital, or user defined, so there is no other node */ + break; + + } + + *status = MIF_OK; + return; +} diff --git a/src/xspice/mif/mifask.c b/src/xspice/mif/mifask.c new file mode 100755 index 000000000..7ff22d171 --- /dev/null +++ b/src/xspice/mif/mifask.c @@ -0,0 +1,231 @@ +/*============================================================================ +FILE MIFask.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used by nutmeg to request the + value of a code model static var. + +INTERFACES + + MIFask() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + + +/* +MIFask + +This function is called by SPICE/Nutmeg to query the value of a +parameter on an instance. It locates the value of the parameter +in the instance structure and converts the value into the IFvalue +structure understood by Nutmeg. For code models, this function +is provided to allow output of Instance Variables defined in the +code model's Interface Specification. +*/ + + +int MIFask( + CKTcircuit *ckt, /* The circuit structure */ + GENinstance *inInst, /* The instance to get the value from */ + int which, /* The parameter to get */ + IFvalue *value, /* The value returned */ + IFvalue *select) /* Unused */ +{ + + MIFinstance *inst; + MIFmodel *model; + int mod_type; + int value_type; + int i; + int size; + + int inst_index; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the instance */ + inst = (MIFinstance *) inInst; + + /* Arrange for access to MIF specific data in the model */ + model = inst->MIFmodPtr; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + /* Adjust parameter tag to actual index into inst info array */ + inst_index = which - model->num_param; + + /* Check instance index for validity */ + if((inst_index < 0) || (inst_index >= inst->num_inst_var)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.instanceParms[inst_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + + /* Transfer the values to the SPICE3C1 value union from the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + value->iValue = inst->inst_var[inst_index]->element[0].bvalue; + break; + + case IF_INTEGER: + value->iValue = inst->inst_var[inst_index]->element[0].ivalue; + break; + + case IF_REAL: + value->rValue = inst->inst_var[inst_index]->element[0].rvalue; + break; + + case IF_STRING: + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->sValue = MIFcopy(inst->inst_var[inst_index]->element[0].svalue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->cValue.real = inst->inst_var[inst_index]->element[0].cvalue.real; + value->cValue.imag = inst->inst_var[inst_index]->element[0].cvalue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + size = inst->inst_var[inst_index]->size; + if(size < 0) + size = 0; + + value->v.numValue = size; + + switch(value_type) { + + /* Note that we malloc space each time this function is called. */ + /* This is what TRAask.c does, so we do it too, even though */ + /* we don't know if it is ever freed... */ + + case IF_FLAGVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = inst->inst_var[inst_index]->element[i].bvalue; + break; + + case IF_INTVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = inst->inst_var[inst_index]->element[i].ivalue; + break; + + case IF_REALVEC: + if(size <= 0) + break; + value->v.vec.rVec = (void *) MALLOC(size * sizeof(double)); + for(i = 0; i < size; i++) + value->v.vec.rVec[i] = inst->inst_var[inst_index]->element[i].rvalue; + break; + + case IF_STRINGVEC: + if(size <= 0) + break; + value->v.vec.sVec = (void *) MALLOC(size * sizeof(char *)); + for(i = 0; i < size; i++) + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->v.vec.sVec[i] = MIFcopy(inst->inst_var[inst_index]->element[i].svalue); + break; + + case IF_CPLXVEC: + if(size <= 0) + break; + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->v.vec.cVec = (void *) MALLOC(size * sizeof(IFcomplex)); + for(i = 0; i < size; i++) { + value->v.vec.cVec[i].real = inst->inst_var[inst_index]->element[i].cvalue.real; + value->v.vec.cVec[i].imag = inst->inst_var[inst_index]->element[i].cvalue.imag; + } + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifconvt.c b/src/xspice/mif/mifconvt.c new file mode 100755 index 000000000..93d483b11 --- /dev/null +++ b/src/xspice/mif/mifconvt.c @@ -0,0 +1,145 @@ +/*============================================================================ +FILE MIFconvTest.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used to check that internal + states of a code model have converged. These internal states + are typically integration states. + +INTERFACES + + MIFconvTest() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "cktdefs.h" +#include "sperror.h" + +//#include "util.h" +#include "devdefs.h" +//#include "CONST.h" +#include "trandefs.h" +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + + +/* +MIFconvTest + +This function is called by the CKTconvTest() driver function to +check convergence of any states owned by instances of a +particular code model type. It loops through all models of that +type and all instances of each model. For each instance, it +looks in the instance structure to determine if any variables +allocated by cm_analog_alloc() have been registered by a call to +cm_analog_converge() to have their convergence tested. If so, the value +of the function at the last iteration is compared with the value +at the current iteration to see if it has converged to within the +same delta amount used in node convergence checks (as defined by +SPICE 3C1). +*/ + + +int MIFconvTest( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt) /* The circuit structure */ +{ + + MIFmodel *model; + MIFinstance *here; + + int i; + + double value; + double last_value; + + char *byte_aligned_double_ptr; + double *double_ptr; + + double tol; + + Mif_Boolean_t gotone = MIF_FALSE; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + + /* loop through all models of this type */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* Loop through all instances of this model */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + /* Loop through all items registered for convergence */ + for(i = 0; i < here->num_conv; i++) { + + /* Get the current value and the last value */ + byte_aligned_double_ptr = (char *) ckt->CKTstate0; + byte_aligned_double_ptr += here->conv[i].byte_index; + double_ptr = (double *) byte_aligned_double_ptr; + value = *double_ptr; + + last_value = here->conv[i].last_value; + + /* If none have failed so far, check convergence */ + if(! gotone) { + + tol = ckt->CKTreltol * MAX(fabs(value), fabs(last_value)) + + ckt->CKTabstol; + if (fabs(value - last_value) > tol) { + if(ckt->enh->conv_debug.report_conv_probs) { + ENHreport_conv_prob(ENH_ANALOG_INSTANCE, + (char *) here->MIFname, + ""); + } + ckt->CKTnoncon++; + gotone = MIF_TRUE; + } + } + + /* Rotate the current value to last_value */ + here->conv[i].last_value = value; + + } /* end for number of conv items */ + } /* end for all instances */ + } /* end for all models of this type */ + + return(OK); +} diff --git a/src/xspice/mif/mifdelete.c b/src/xspice/mif/mifdelete.c new file mode 100755 index 000000000..56db26b32 --- /dev/null +++ b/src/xspice/mif/mifdelete.c @@ -0,0 +1,199 @@ +/*============================================================================ +FILE MIFdelete.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used by SPICE to delete an + instance and its allocated data structures from the internal + circuit description data structures. + +INTERFACES + + MIFdelete() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "ngspice.h" +#include "sperror.h" +#include "gendefs.h" + +#include "mifproto.h" +#include "mifdefs.h" + +#include "suffix.h" + + + +/* +MIFdelete + +This function deletes a particular instance from the linked list +of instance structures, freeing all dynamically allocated memory +used by the instance structure. +*/ + + +int +MIFdelete( + GENmodel *inModel, /* The head of the model list */ + IFuid name, /* The name of the instance to delete */ + GENinstance **inst /* The instance structure to delete */ +) +{ + MIFmodel *model; + MIFinstance **fast; + MIFinstance **prev; + MIFinstance *here=NULL; + + Mif_Boolean_t found; + + int i; + int j; + int k; + + int num_conn; + int num_port; + int num_inst_var; + + + /* Convert generic pointers in arg list to MIF specific pointers */ + model = (MIFmodel *) inModel; + fast = (MIFinstance **) inst; + + /*******************************************/ + /* Cut the instance out of the linked list */ + /*******************************************/ + + /* Loop through all models */ + for(found = MIF_FALSE; model; model = model->MIFnextModel) { + prev = &(model->MIFinstances); + /* Loop through all instances of this model */ + for(here = *prev; here; here = here->MIFnextInstance) { + /* If name or pointer matches, cut it out and mark that its found */ + if(here->MIFname == name || (fast && here == *fast) ) { + *prev= here->MIFnextInstance; + found = MIF_TRUE; + break; + } + prev = &(here->MIFnextInstance); + } + if(found) + break; + } + + /* Return error if not found */ + if(!found) + return(E_NODEV); + + + /*******************************/ + /* Free the instance structure */ + /*******************************/ + + /* Loop through all connections on the instance */ + /* and dismantle the stuff allocated during readin/setup */ + /* in MIFinit_inst, MIFget_port, and MIFsetup */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* If connection never used, skip it */ + if(here->conn[i]->is_null) + continue; + + /* If analog output, lots to free... */ + if(here->conn[i]->is_output && here->analog) { + num_port = here->conn[i]->size; + /* For each port on the connector */ + for(j = 0; j < num_port; j++) { + /* Free the partial/ac_gain/smp stuff allocated in MIFsetup */ + for(k = 0; k < num_conn; k++) { + if((here->conn[k]->is_null) || (! here->conn[k]->is_input) ) + continue; + FREE(here->conn[i]->port[j]->partial[k].port); + FREE(here->conn[i]->port[j]->ac_gain[k].port); + FREE(here->conn[i]->port[j]->smp_data.input[k].port); + } + FREE(here->conn[i]->port[j]->partial); + FREE(here->conn[i]->port[j]->ac_gain); + FREE(here->conn[i]->port[j]->smp_data.input); + /* but don't free strings. They are either not owned */ + /* by the inst or are part of tokens. SPICE3C1 never */ + /* frees tokens, so we don't either... */ + } + } + /* Free the basic port structure allocated in MIFget_port */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) + FREE(here->conn[i]->port[j]); + FREE(here->conn[i]->port); + } + + /* Free the connector stuff allocated in MIFinit_inst */ + /* Don't free name/description! They are not owned */ + /* by the instance */ + for(i = 0; i < num_conn; i++) { + FREE(here->conn[i]); + } + FREE(here->conn); + + /* Loop through all instance variables on the instance */ + /* and free stuff */ + + num_inst_var = here->num_inst_var; + for(i = 0; i < num_inst_var; i++) { + if(here->inst_var[i]->element != NULL) { + FREE(here->inst_var[i]->element); + } + FREE(here->inst_var[i]); + } + FREE(here->inst_var); + + /* ************************************************************* */ + /* Dont free params here. They are not currently implemented on */ + /* a per-instance basis, so their allocated space is owned by */ + /* the parent model, not the instance. Param stuff will be freed */ + /* by MIFmDelete */ + /* ************************************************************* */ + + /* Free the stuff used by the cm_... functions */ + + if(here->num_state && here->state) + FREE(here->state); + if(here->num_intgr && here->intgr) + FREE(here->intgr); + if(here->num_conv && here->conv) + FREE(here->conv); + + + /* Finally, free the instance struct itself */ + FREE(here); + + return(OK); +} diff --git a/src/xspice/mif/mifdestr.c b/src/xspice/mif/mifdestr.c new file mode 100755 index 000000000..c637623ea --- /dev/null +++ b/src/xspice/mif/mifdestr.c @@ -0,0 +1,72 @@ +/*============================================================================ +FILE MIFdestroy.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function that deletes all models of a particular + device (code model) type from the circuit description structures. + +INTERFACES + + MIFdestroy() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include + +#include "mifproto.h" + +/* #include "suffix.h" */ + + + +/* +MIFdestroy + +This function deletes all models and all instances of a specified +device type. It traverses the linked list of model structures +for that type and calls MIFmDelete on each model. +*/ + +void MIFdestroy( + GENmodel **inModel) /* The head of the list of models to delete */ +{ + + /* Free all models of this device type by removing */ + /* models from the head of the linked list until */ + /* the head is null */ + + while(*inModel) { + MIFmDelete(inModel, + (*inModel)->GENmodName, + *inModel); + } + +} diff --git a/src/xspice/mif/mifgetmod.c b/src/xspice/mif/mifgetmod.c new file mode 100755 index 000000000..dda0a3176 --- /dev/null +++ b/src/xspice/mif/mifgetmod.c @@ -0,0 +1,235 @@ +/*============================================================================ +FILE + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + + * + * Copyright (c) 1985 Thomas L. Quarles + * + * NOTE: Portions of this code are Copyright Thomas L. Quarles and University of + * California at Berkeley. Other portions are modified and added by + * the Georgia Tech Research Institute. + * + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the routine that allocates a new model structure and + parses the .model card parameters. + +INTERFACES + + MIFgetMod() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ + +#include "ngspice.h" + + +#include +#include "inpdefs.h" /* maschmann : kleinbuchstaben */ +#include "devdefs.h" /* maschmann : kleinbuchstaben */ +//#include "util.h" +#include "ifsim.h" /* maschmann : kleinbuchstaben */ +#include "cpstd.h" /* maschmann : kleinbuchstaben */ +#include "fteext.h" /* maschmann : kleinbuchstaben */ + +#include "mifproto.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +#include "suffix.h" + + +extern INPmodel *modtab; + +extern SPICEdev **DEVices; /* info about all device types */ + +/* +MIFgetMod + +This function is a modified version of SPICE 3C1 INPgetMod(). +MIFgetMod looks in the table of model information created on the +first pass of the parser to find the text of the .model card. It +then checks to see if the .model card has already been processed +by a previous element card reference. If so, it returns a +pointer to the previously created model structure. If not, it +allocates a new model structure and processes the parameters on +the .model card. Parameter values for parameters not found on +the .model card are not filled in by this function. They are +defaulted later by MIFsetup(). The function returns NULL when +successful, and an error string on failure. +*/ + + +char *MIFgetMod(ckt,name,model,tab) + void *ckt; /* The circuit structure */ + char *name; /* The name of the model to look for */ + INPmodel **model; /* The model found/created */ + INPtables *tab; /* Table of model info from first pass */ +{ + INPmodel *modtmp; + IFvalue * val; + register int j; + char * line; + char *parm; + char *err = NULL; + char *temp; + int error; + + int i; + + char *err1; + char *err2; + + MIFmodel *mdfast; + /* Mif_Param_Info_t *param_info;*/ + + /* locate the named model in the modtab list */ + + + /* maschmann : remove : from name */ + + /* char *pos; + + if((pos=strstr(name,":"))!=NULL) *pos=0; */ + + for (modtmp = modtab; modtmp != NULL; modtmp = ((modtmp)->INPnextModel)) { + + if (strcmp((modtmp)->INPmodName,name) == 0) { + + + + /* found the model in question - now instantiate if necessary */ + /* and return an appropriate pointer to it */ + + /* make sure the type is valid before proceeding */ + if(modtmp->INPmodType<0) { + /* illegal device type, so can't handle */ + *model = NULL; + err = (char *)tmalloc((35+strlen(name)) * sizeof(char)); + sprintf(err, "MIF: Unknown device type for model %s \n",name); + return(err); + } + + /* check to see if this model's parameters have been processed */ + if(! ((modtmp)->INPmodUsed )) { + + /* not already processed, so create data struct */ + error = (*(ft_sim->newModel))( ckt,(modtmp)->INPmodType, + &((modtmp)->INPmodfast), (modtmp)->INPmodName); + if(error) + return(INPerror(error)); + + /* gtri modification: allocate and initialize MIF specific model struct items */ + mdfast = modtmp->INPmodfast; + mdfast->num_param = DEVices[modtmp->INPmodType]->DEVpublic.num_param; + mdfast->param = (void *) tmalloc(mdfast->num_param * sizeof(void *)); + for(i = 0; i < mdfast->num_param; i++) { + mdfast->param[i] = (void *) tmalloc(sizeof(Mif_Param_Data_t)); + mdfast->param[i]->is_null = MIF_TRUE; + mdfast->param[i]->size = 0; + mdfast->param[i]->element = NULL; + } + /* remaining initializations will be done by MIFmParam() and MIFsetup() */ + + /* parameter isolation, identification, binding */ + line = ((modtmp)->INPmodLine)->line; + INPgetTok(&line,&parm,1); /* throw away '.model' */ + INPgetTok(&line,&parm,1); /* throw away 'modname' */ + + /* throw away the modtype - we don't treat it as a parameter */ + /* like SPICE does */ + INPgetTok(&line,&parm,1); /* throw away 'modtype' */ + + while(*line != 0) { + INPgetTok(&line,&parm,1); + for(j=0;j<*((*(ft_sim->devices)[(modtmp)->INPmodType]).numModelParms); j++) { + if (strcmp(parm,((*(ft_sim->devices) [ (modtmp)-> + INPmodType ]).modelParms[j].keyword)) == 0) { + /* gtri modification: call MIFgetValue instead of INPgetValue */ + err1 = NULL; + val = MIFgetValue(ckt,&line, + ((*(ft_sim->devices)[(modtmp)-> + INPmodType ]).modelParms[j]. + dataType),tab,&err1); + if(err1) { + err2 = (void *) tmalloc(25 + strlen(name) + strlen(err1)); + sprintf(err2, "MIF-ERROR - model: %s - %s\n", name, err1); + return(err2); + } + error = (*(ft_sim->setModelParm))(ckt, + ((modtmp)->INPmodfast), + (*(ft_sim->devices)[(modtmp)->INPmodType ]). + modelParms[j].id,val,(IFvalue*)NULL); + if(error) + return(INPerror(error)); + break; + } + } + /* gtri modification: processing of special parameter "level" removed */ + if(j >= *((*(ft_sim->devices)[(modtmp)->INPmodType]).numModelParms)) + { + //err has not been allocated, but free() in INPerrCat() + + // This did not allocate enough memory you wanker, K.A. replaced 5 March 2000 +// temp = (char *)tmalloc((40+strlen(parm)) * sizeof(char)); + temp = (char *)tmalloc((42+strlen(parm)) * sizeof(char));// K.A. replaced 5 March 2000 + + sprintf(temp, "MIF: unrecognized parameter (%s) - ignored\n", parm); + + fprintf(stdout,temp); + err = (char *)tmalloc( (2*strlen(temp) +2)*sizeof(char));// K.A. added 5 March 2000 + + *err = '\0';// K.A. added 5 March 2000 + + err = INPerrCat(err,temp); + } + FREE(parm); + + } /* end while end of line not reached */ + + (modtmp)->INPmodUsed=1; + (modtmp)->INPmodLine->error = err; + + } /* end if model parameters not processed yet */ + + *model = modtmp; + return((char *)NULL); + + } /* end if name matches */ + + } /* end for all models in modtab linked list */ + + + /* didn't find model - ERROR - return NULL model */ + *model = (INPmodel *)NULL; + err = (char *)tmalloc((60+strlen(name)) * sizeof(char)); + sprintf(err, " MIF-ERROR - unable to find definition of model %s\n",name); + + return(err); +} diff --git a/src/xspice/mif/mifgetvalue.c b/src/xspice/mif/mifgetvalue.c new file mode 100755 index 000000000..bd3ad6695 --- /dev/null +++ b/src/xspice/mif/mifgetvalue.c @@ -0,0 +1,368 @@ +/*============================================================================ +FILE MIFgetValue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called to read parameter values from a + .model card. + +INTERFACES + + MIFgetValue() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "ifsim.h" +//#include "util.h" +#include "inpdefs.h" +#include "inpptree.h" + +/* #include */ +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +static int MIFget_boolean(char *token, char **err); + +static int MIFget_integer(char *token, char **err); + +static double MIFget_real(char *token, char **err); + +static char *MIFget_string(char *token, char **err); + +static IFcomplex MIFget_complex(char *token, Mif_Token_Type_t token_type, + char **line, char **err); + + + +/* +MIFgetValue + +This function gets a parameter value from the .model text line +into an IFvalue structure. The parameter type is specified in +the argument list and is used to determine how to parse the text +on the .model line. If the parameter is an array, the entire +array is parsed and placed in the IFvalue structure along with +the number of elements found. +*/ + + +IFvalue * +MIFgetValue(ckt,line,type,tab,err) + void *ckt; /* The circuit structure */ + char **line; /* The text line to read value from */ + int type; /* The type of data to read */ + INPtables *tab; /* Unused */ + char **err; /* Error string text */ +{ + static IFvalue val; + + int btemp; + int itemp; + double rtemp; + char *stemp; + IFcomplex ctemp; + + char *token; + Mif_Token_Type_t token_type; + + int value_type; + int is_array; + + + /* Mask off non-type bits */ + value_type = type & IF_VARTYPES; + + /* Setup array boolean */ + is_array = value_type & IF_VECTOR; + + + /* initialize stuff if array */ + if(is_array) { + token = MIFget_token(line, &token_type); + if(token_type != MIF_LARRAY_TOK) { + *err = "Array parameter expected - No array delimiter found"; + return(NULL); + } + val.v.numValue = 0; + val.v.vec.iVec = (void *) MALLOC(1); /* just so that realloc doesn't bomb */ + } + + + /* now get the values into val */ + + while(1) { + + token = MIFget_token(line, &token_type); + + /* exit if no more tokens */ + if(token_type == MIF_NO_TOK) { + *err = "Unexpected end of model card"; + return(NULL); + } + + /* exit if end of array found */ + if(is_array && (token_type == MIF_RARRAY_TOK)) { + if(val.v.numValue == 0) { + *err = "Array parameter must have at least one value"; + return(NULL); + } + break; + } + + /* process the token to extract a value */ + switch(value_type) { + + case IF_FLAG: + val.iValue = MIFget_boolean(token, err); + break; + + case IF_INTEGER: + val.iValue = MIFget_integer(token, err); + break; + + case IF_REAL: + val.rValue = MIFget_real(token, err); + break; + + case IF_STRING: + val.sValue = MIFget_string(token, err); + break; + + case IF_COMPLEX: + val.cValue = MIFget_complex(token, token_type, line, err); + break; + + + case IF_FLAGVEC: + btemp = MIFget_boolean(token, err); + val.v.vec.iVec = (void *) REALLOC(val.v.vec.iVec, + (val.v.numValue + 1) * sizeof(int)); + val.v.vec.iVec[val.v.numValue] = btemp; + val.v.numValue++; + break; + + case IF_INTVEC: + itemp = MIFget_integer(token, err); + val.v.vec.iVec = (void *) REALLOC(val.v.vec.iVec, + (val.v.numValue + 1) * sizeof(int)); + val.v.vec.iVec[val.v.numValue] = itemp; + val.v.numValue++; + break; + + case IF_REALVEC: + rtemp = MIFget_real(token, err); + val.v.vec.rVec = (void *) REALLOC(val.v.vec.rVec, + (val.v.numValue + 1) * sizeof(double)); + val.v.vec.rVec[val.v.numValue] = rtemp; + val.v.numValue++; + break; + + case IF_STRINGVEC: + stemp = MIFget_string(token, err); + val.v.vec.sVec = (void *) REALLOC(val.v.vec.sVec, + (val.v.numValue + 1) * sizeof(char *)); + val.v.vec.sVec[val.v.numValue] = stemp; + val.v.numValue++; + break; + + case IF_CPLXVEC: + ctemp = MIFget_complex(token, token_type, line, err); + val.v.vec.cVec = (void *) REALLOC(val.v.vec.cVec, + (val.v.numValue + 1) * sizeof(IFcomplex)); + val.v.vec.cVec[val.v.numValue] = ctemp; + val.v.numValue++; + break; + + + default: + *err = "Internal error - unexpected value type in MIFgetValue()"; + return(NULL); + + } + + if(*err) + return(NULL); + + /* exit after this single pass if not array */ + if(! is_array) + break; + + } /* end forever loop */ + + + return(&val); +} + + +/* *************************************************************** */ + + +static int MIFget_boolean(char *token, char **err) +{ + *err = NULL; + + if((strcmp(token, "t") == 0) || (strcmp(token, "true") == 0)) + return(1); + else if((strcmp(token, "f") == 0) || (strcmp(token, "false") == 0)) + return(0); + else + *err = "Bad boolean value"; + + return(-1); +} + + +/* *************************************************************** */ + +static int MIFget_integer(char *token, char **err) +{ + int error; + long l; + double dtemp; + char *endp; +/* long strtol(char *, char **, int); */ + + *err = NULL; + + l = strtol(token, &endp, 0); /* handles base 8, 10, 16 automatically */ + + /* if error, probably caused by engineering suffixes, */ + /* so try parsing with INPevaluate */ + if(errno || (*endp != '\0')) { + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = "Bad integer, octal, or hex value"; + l = 0; + } + else if(dtemp > 0.0) + l = dtemp + 0.5; + else + l = dtemp - 0.5; + } + + return((int) l); +} + + +/* *************************************************************** */ + +static double MIFget_real(char *token, char **err) +{ + double dtemp; + int error; + + *err = NULL; + + dtemp = INPevaluate(&token, &error, 1); + + if(error) + *err = "Bad real value"; + + return(dtemp); +} + + +/* *************************************************************** */ + +static char *MIFget_string(char *token, char **err) +{ + *err = NULL; + + return(token); +} + + +/* *************************************************************** */ + +static IFcomplex MIFget_complex(char *token, Mif_Token_Type_t token_type, + char **line, char **err) +{ + static char *msg = "Bad complex value"; + + IFcomplex ctemp; + + double dtemp; + int error; + + *err = NULL; + + ctemp.real = 0.0; + ctemp.imag = 0.0; + + /* Complex values must be of form < > */ + if(token_type != MIF_LCOMPLEX_TOK) { + *err = msg; + return(ctemp); + } + + /* get the real part */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_STRING_TOK) { + *err = msg; + return(ctemp); + } + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = msg; + return(ctemp); + } + ctemp.real = dtemp; + + /* get the imaginary part */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_STRING_TOK) { + *err = msg; + return(ctemp); + } + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = msg; + return(ctemp); + } + ctemp.imag = dtemp; + + /* eat the closing > delimiter */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_RCOMPLEX_TOK) { + *err = msg; + return(ctemp); + } + + return(ctemp); +} diff --git a/src/xspice/mif/mifload.c b/src/xspice/mif/mifload.c new file mode 100755 index 000000000..8202b1ca4 --- /dev/null +++ b/src/xspice/mif/mifload.c @@ -0,0 +1,898 @@ +/*============================================================================ +FILE MIFload.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the driver function for calling code model evaluation + functions. This is one of the most important, complex, and often called + functions in the model interface package. It iterates through all models + and all instances of a specified code model device type, fills in the + inputs for the model, calls the model, and then uses the outputs and + partials returned by the model to load the matrix. + +INTERFACES + + MIFload() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/* #include "prefix.h" */ +#include "ngspice.h" + +#include +#include + +#include "cktdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" +#include "mif.h" + +#include "enh.h" +#include "cm.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; /* info about all device types */ + + + +static void MIFauto_partial( + MIFinstance *here, + void (*cm_func)(), + Mif_Private_t *cm_data +); + + + + + + +/* +MIFload + +This function is called by the CKTload() driver function to call +the C function for each instance of a code model type. It loops +through all models of that type and all instances of each model. +For each instance, it prepares the structure that is passed to +the code model by filling it with the input values for that +instance. The code model's C function is then called, and the +outputs and partial derivatives computed by the C function are +used to fill the matrix for the next solution attempt. +*/ + + +int +MIFload( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt) /* The circuit structure */ +{ + + MIFmodel *model; + MIFinstance *here; + + Mif_Private_t cm_data; /* data to be passed to/from code model */ + Mif_Port_Type_t type; + Mif_Port_Data_t *fast; + + Mif_Smp_Ptr_t *smp_data_out; + + Mif_Port_Ptr_t *smp_ptr; + + Mif_Port_Type_t in_type; + Mif_Port_Type_t out_type; + + Mif_Boolean_t is_input; + Mif_Boolean_t is_output; + + Mif_Cntl_Src_Type_t cntl_src_type; + + Mif_Analysis_t anal_type; + + Mif_Complex_t czero; + Mif_Complex_t ac_gain; + + int mod_type; + int num_conn; + int num_port; + int num_port_k; + int i; + int j; + int k; + int l; + + /*int tag;*/ + + double *rhs; + double *rhsOld; + double partial; + double temp; + + double *double_ptr0; + double *double_ptr1; + + /*double *input;*/ + /* double *oldinput;*/ + + char *byte_ptr0; + char *byte_ptr1; + + double last_input; + double conv_limit; + + double cntl_input; + + + Evt_Node_Data_t *node_data; + + + /* Prepare a zero complex number for AC gain initializations */ + czero.real = 0.0; + czero.imag = 0.0; + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + mod_type = model->MIFmodType; + + /* Setup pointers for fast access to rhs and rhsOld elements of ckt struct */ + rhs = ckt->CKTrhs; + rhsOld = ckt->CKTrhsOld; + + node_data = ckt->evt->data.node; + + /* *********************************************************************** */ + /* Setup the circuit data in the structure to be passed to the code models */ + /* *********************************************************************** */ + + /* anal_init is set if this is the first iteration at any step in */ + /* an analysis */ + if(!(ckt->CKTmode & MODEINITFLOAT)) + g_mif_info.circuit.anal_init = MIF_TRUE; + cm_data.circuit.anal_init = g_mif_info.circuit.anal_init; + + /* anal_type is determined by CKTload */ + anal_type = g_mif_info.circuit.anal_type; + cm_data.circuit.anal_type = anal_type; + + /* get the analysis freq from the ckt struct if this is an AC analysis */ + /* otherwise, set the freq to zero */ + if(anal_type == MIF_AC) + cm_data.circuit.frequency = ckt->CKTomega; + else + cm_data.circuit.frequency = 0.0; + + /* get the analysis times from the ckt struct if this is a transient analysis */ + /* otherwise, set the times to zero */ + if(anal_type == MIF_TRAN) { + cm_data.circuit.time = ckt->CKTtime; + cm_data.circuit.t[0] = ckt->CKTtime; + for(i = 1; i < 8; i++) { + cm_data.circuit.t[i] = cm_data.circuit.t[i-1] - ckt->CKTdeltaOld[i-1]; + if(cm_data.circuit.t[i] < 0.0) + cm_data.circuit.t[i] = 0.0; + } + } + else { + cm_data.circuit.time = 0.0; + for(i = 0; i < 8; i++) { + cm_data.circuit.t[i] = 0.0; + } + } + + cm_data.circuit.call_type = MIF_ANALOG; + cm_data.circuit.temperature = ckt->CKTtemp - 273.15; + + g_mif_info.circuit.call_type = MIF_ANALOG; + g_mif_info.ckt = ckt; + + + /* ***************************************************************** */ + /* loop through all models of this type */ + /* ***************************************************************** */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* If not an analog or hybrid model, continue to next */ + if(! model->analog) + continue; + + /* ***************************************************************** */ + /* loop through all instances of this model */ + /* ***************************************************************** */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* If not an analog or hybrid instance, continue to next */ + if(! here->analog) + continue; + + /* ***************************************************************** */ + /* Prepare the data needed by the cm_.. functions */ + /* ***************************************************************** */ + g_mif_info.instance = here; + g_mif_info.errmsg = ""; + + if(here->initialized) { + cm_data.circuit.init = MIF_FALSE; + g_mif_info.circuit.init = MIF_FALSE; + } + else { + cm_data.circuit.init = MIF_TRUE; + g_mif_info.circuit.init = MIF_TRUE; + } + + + /* ***************************************************************** */ + /* if tran analysis and anal_init is true, copy state 1 to state 0 */ + /* Otherwise the data in state 0 would be invalid */ + /* ***************************************************************** */ + + if((anal_type == MIF_TRAN) && g_mif_info.circuit.anal_init) { + for(i = 0; i < here->num_state; i++) { + double_ptr0 = ckt->CKTstate0 + here->state[i].index; + double_ptr1 = ckt->CKTstate1 + here->state[i].index; + byte_ptr0 = (char *) double_ptr0; + byte_ptr1 = (char *) double_ptr1; + for(j = 0; j < here->state[i].bytes; j++) + byte_ptr0[j] = byte_ptr1[j]; + } + } + + /* ***************************************************************** */ + /* If not AC analysis, loop through all connections on this instance */ + /* and load the input values for each input port of each connection */ + /* ***************************************************************** */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* If AC analysis, skip getting input values. The input values */ + /* should stay the same as they were at the last iteration of */ + /* the operating point analysis */ + if(anal_type == MIF_AC) + break; + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* if this connection is not an input, skip to next connection */ + if(! here->conn[i]->is_input) + continue; + + /* Get number of ports on this connection */ + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If port type is Digital or User-Defined, we only need */ + /* to get the total load. The input values are pointers */ + /* already set by EVTsetup() */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) { + fast->total_load = + node_data->total_load[fast->evt_data.node_index]; + } + /* otherwise, it is an analog node and we get the input value */ + else { + /* load the input values based on type and mode */ + if(ckt->CKTmode & MODEINITJCT) + /* first iteration step for DC */ + fast->input.rvalue = 0.0; + else if((ckt->CKTmode & MODEINITTRAN) || + (ckt->CKTmode & MODEINITPRED)) + /* first iteration step at timepoint */ + fast->input.rvalue = *(ckt->CKTstate1 + fast->old_input); + else { + /* subsequent iterations */ + + /* record last iteration's input value for convergence limiting */ + last_input = fast->input.rvalue; + + /* get the new input value */ + switch(type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + fast->input.rvalue = rhsOld[fast->smp_data.pos_node] - + rhsOld[fast->smp_data.neg_node]; + break; + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + fast->input.rvalue = rhsOld[fast->smp_data.ibranch]; + break; + case MIF_DIGITAL: + case MIF_USER_DEFINED: + break; + } /* end switch on type of port */ + + /* If convergence limiting enabled, limit maximum input change */ + if(ckt->enh->conv_limit.enabled) { + /* compute the maximum the input is allowed to change */ + conv_limit = fabs(last_input) * ckt->enh->conv_limit.step; + if(conv_limit < ckt->enh->conv_limit.abs_step) + conv_limit = ckt->enh->conv_limit.abs_step; + /* if input has changed too much, limit it and signal not converged */ + if(fabs(fast->input.rvalue - last_input) > conv_limit) { + if((fast->input.rvalue - last_input) > 0.0) + fast->input.rvalue = last_input + conv_limit; + else + fast->input.rvalue = last_input - conv_limit; + (ckt->CKTnoncon)++; + /* report convergence problem if last call */ + if(ckt->enh->conv_debug.report_conv_probs) { + ENHreport_conv_prob(ENH_ANALOG_INSTANCE, + (char *) here->MIFname, ""); + } + } + } + + } /* end else */ + + /* Save value of input for use with MODEINITTRAN */ + *(ckt->CKTstate0 + fast->old_input) = fast->input.rvalue; + + } /* end else analog type */ + } /* end for number of ports */ + } /* end for number of connections */ + + /* ***************************************************************** */ + /* loop through all connections on this instance and zero out all */ + /* outputs/partials/AC gains for each output port of each connection */ + /* ***************************************************************** */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if(here->conn[i]->is_null || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog node, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* initialize the output to zero */ + fast->output.rvalue = 0.0; + + /* loop through all connections and ports that */ + /* could be inputs for this port and zero the partials */ + for(k = 0; k < num_conn; k++) { + if(here->conn[k]->is_null || (! here->conn[k]->is_input)) + continue; + num_port_k = here->conn[k]->size; + for(l = 0; l < num_port_k; l++) { + /* skip if this port is null */ + if(here->conn[k]->port[l]->is_null) + continue; + fast->partial[k].port[l] = 0.0; + fast->ac_gain[k].port[l] = czero; + } /* end for number of ports */ + } /* end for number of connections */ + } /* end for number of ports */ + } /* end for number of connections */ + + + /* ***************************************************************** */ + /* Prepare the structure to be passed to the code model */ + /* ***************************************************************** */ + cm_data.num_conn = here->num_conn; + cm_data.conn = here->conn; + cm_data.num_param = here->num_param; + cm_data.param = here->param; + cm_data.num_inst_var = here->num_inst_var; + cm_data.inst_var = here->inst_var; + + /* Initialize the auto_partial flag to false */ + g_mif_info.auto_partial.local = MIF_FALSE; + + /* ******************* */ + /* Call the code model */ + /* ******************* */ + (*(DEVices[mod_type]->DEVpublic.cm_func)) (&cm_data); + + /* Automatically compute partials if requested by .options auto_partial */ + /* or by model through call to cm_analog_auto_partial() in DC or TRAN analysis */ + if((anal_type != MIF_AC) && + (g_mif_info.auto_partial.global || g_mif_info.auto_partial.local)) + MIFauto_partial(here, DEVices[mod_type]->DEVpublic.cm_func, &cm_data); + + /* ***************************************************************** */ + /* Loop through all connections on this instance and */ + /* load the data into the matrix for each output port */ + /* and for each V source associated with a current input. */ + /* For AC analysis, we only load the +-1s required to satisfy */ + /* KCL and KVL in the matrix equations. */ + /* ***************************************************************** */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* prepare things for convenient access later */ + is_input = here->conn[i]->is_input; + is_output = here->conn[i]->is_output; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog node, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(fast->smp_data); + + /* if it is a current input */ + /* load the matrix data needed for the associated zero-valued V source */ + if(is_input && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) { + *(smp_data_out->pos_ibranch) += 1.0; + *(smp_data_out->neg_ibranch) -= 1.0; + *(smp_data_out->ibranch_pos) += 1.0; + *(smp_data_out->ibranch_neg) -= 1.0; + /* rhs[smp_data_out->ibranch] += 0.0; */ + } /* end if current input */ + + /* if it has a voltage source output, */ + /* load the matrix with the V source output data */ + if( (is_output && (type == MIF_VOLTAGE || type == MIF_DIFF_VOLTAGE)) || + (type == MIF_RESISTANCE || type == MIF_DIFF_RESISTANCE) ) { + *(smp_data_out->pos_branch) += 1.0; + *(smp_data_out->neg_branch) -= 1.0; + *(smp_data_out->branch_pos) += 1.0; + *(smp_data_out->branch_neg) -= 1.0; + if(anal_type != MIF_AC) + rhs[smp_data_out->branch] += fast->output.rvalue; + } /* end if V source output */ + + /* if it has a current source output, */ + /* load the matrix with the V source output data */ + if( (is_output && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) || + (type == MIF_CONDUCTANCE || type == MIF_DIFF_CONDUCTANCE) ) { + if(anal_type != MIF_AC) { + rhs[smp_data_out->pos_node] -= fast->output.rvalue; + rhs[smp_data_out->neg_node] += fast->output.rvalue; + } + } /* end if current output */ + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* ***************************************************************** */ + /* loop through all output connections on this instance and */ + /* load the partials/AC gains into the matrix */ + /* ***************************************************************** */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[i]->is_null) || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this output port */ + out_type = fast->type; + + /* If not an analog node, continue to next port */ + if((out_type == MIF_DIGITAL) || (out_type == MIF_USER_DEFINED)) + continue; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(fast->smp_data); + + /* for this port, loop through all connections */ + /* and all ports to touch on each possible input */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an input */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_input)) + continue; + + num_port_k = here->conn[k]->size; + /* loop through all the ports of this connection */ + for(l = 0; l < num_port_k; l++) { + + /* skip if this port is null */ + if(here->conn[k]->port[l]->is_null) + continue; + + /* determine the type of this input port */ + in_type = here->conn[k]->port[l]->type; + + /* If not an analog node, continue to next port */ + if((in_type == MIF_DIGITAL) || (in_type == MIF_USER_DEFINED)) + continue; + + /* get the partial to local variable for fast access */ + partial = fast->partial[k].port[l]; + ac_gain = fast->ac_gain[k].port[l]; + + /* create a pointer to the matrix pointer data for quick access */ + smp_ptr = &(smp_data_out->input[k].port[l]); + + /* get the input value */ + cntl_input = here->conn[k]->port[l]->input.rvalue; + + /* determine type of controlled source according */ + /* to input and output types */ + cntl_src_type = MIFget_cntl_src_type(in_type, out_type); + + switch(cntl_src_type) { + case MIF_VCVS: + if(anal_type == MIF_AC) { + *(smp_ptr->e.branch_poscntl) -= ac_gain.real; + *(smp_ptr->e.branch_negcntl) += ac_gain.real; + *(smp_ptr->e.branch_poscntl+1) -= ac_gain.imag; + *(smp_ptr->e.branch_negcntl+1) += ac_gain.imag; + } + else { + *(smp_ptr->e.branch_poscntl) -= partial; + *(smp_ptr->e.branch_negcntl) += partial; + rhs[smp_data_out->branch] -= partial * cntl_input; + } + break; + case MIF_ICIS: + if(anal_type == MIF_AC) { + *(smp_ptr->f.pos_ibranchcntl) += ac_gain.real; + *(smp_ptr->f.neg_ibranchcntl) -= ac_gain.real; + *(smp_ptr->f.pos_ibranchcntl+1) += ac_gain.imag; + *(smp_ptr->f.neg_ibranchcntl+1) -= ac_gain.imag; + } + else { + *(smp_ptr->f.pos_ibranchcntl) += partial; + *(smp_ptr->f.neg_ibranchcntl) -= partial; + temp = partial * cntl_input; + rhs[smp_data_out->pos_node] += temp; + rhs[smp_data_out->neg_node] -= temp; + } + break; + case MIF_VCIS: + if(anal_type == MIF_AC) { + *(smp_ptr->g.pos_poscntl) += ac_gain.real; + *(smp_ptr->g.pos_negcntl) -= ac_gain.real; + *(smp_ptr->g.neg_poscntl) -= ac_gain.real; + *(smp_ptr->g.neg_negcntl) += ac_gain.real; + *(smp_ptr->g.pos_poscntl+1) += ac_gain.imag; + *(smp_ptr->g.pos_negcntl+1) -= ac_gain.imag; + *(smp_ptr->g.neg_poscntl+1) -= ac_gain.imag; + *(smp_ptr->g.neg_negcntl+1) += ac_gain.imag; + } + else { + *(smp_ptr->g.pos_poscntl) += partial; + *(smp_ptr->g.pos_negcntl) -= partial; + *(smp_ptr->g.neg_poscntl) -= partial; + *(smp_ptr->g.neg_negcntl) += partial; + temp = partial * cntl_input; + rhs[smp_data_out->pos_node] += temp; + rhs[smp_data_out->neg_node] -= temp; + } + break; + case MIF_ICVS: + if(anal_type == MIF_AC) { + *(smp_ptr->h.branch_ibranchcntl) -= ac_gain.real; + *(smp_ptr->h.branch_ibranchcntl+1) -= ac_gain.imag; + } + else { + *(smp_ptr->h.branch_ibranchcntl) -= partial; + rhs[smp_data_out->branch] -= partial * cntl_input; + } + break; + } /* end switch on controlled source type */ + } /* end for number of input ports */ + } /* end for number of input connections */ + } /* end for number of output ports */ + } /* end for number of output connections */ + + here->initialized = MIF_TRUE; + + } /* end for all instances */ + + } /* end for all models */ + + return(OK); +} + + + + +/* +MIFauto_partial + +This function is called by MIFload() when a code model requests +that partial derivatives be computed automatically. It calls +the code model additional times with an individual input to the +model varied by a small amount at each call. Partial +derivatives of each output with respect to the varied input +are then computed by divided differences. +*/ + + +static void MIFauto_partial( + MIFinstance *here, /* The instance structure */ + void (*cm_func)(), /* The code model function to be called */ + Mif_Private_t *cm_data) /* The data to be passed to the code model */ +{ + + Mif_Port_Data_t *fast; + Mif_Port_Data_t *out_fast; + + Mif_Port_Type_t type; + Mif_Port_Type_t out_type; + + int num_conn; + int num_port; + int num_port_k; + + int i; + int j; + int k; + int l; + + double epsilon; + double nominal_input; + + + /* Reset init and anal_init flags before making additional calls */ + /* to the model */ + cm_data->circuit.init = MIF_FALSE; + g_mif_info.circuit.init = MIF_FALSE; + + cm_data->circuit.anal_init = MIF_FALSE; + g_mif_info.circuit.anal_init = MIF_FALSE; + + + /* *************************** */ + /* Save nominal analog outputs */ + /* *************************** */ + + /* loop through all connections */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if(here->conn[i]->is_null || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog port, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* copy the output for use in computing output deltas */ + fast->nominal_output = fast->output.rvalue; + + } /* end for number of output ports */ + } /* end for number of output connections */ + + + /* ***************************************************************** */ + /* Change each analog input by a small amount and call the model to */ + /* compute new outputs. */ + /* ***************************************************************** */ + + /* loop through all connections */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* if this connection is not an input, skip to next connection */ + if(! here->conn[i]->is_input) + continue; + + /* Get number of ports on this connection */ + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If port type is Digital or User-Defined, skip it */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* otherwise, it is an analog port and we need to perturb it and */ + /* then call the model */ + + /* compute the perturbation amount depending on type of input */ + switch(type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + epsilon = 1.0e-6; + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + epsilon = 1.0e-12; + break; + + default: + printf("INTERNAL ERROR - MIFauto_partial. Invalid port type\n"); + epsilon = 1.0e-30; + break; + } /* end switch on type of port */ + + /* record and perturb input value */ + nominal_input = fast->input.rvalue; + fast->input.rvalue += epsilon; + + + /* call model to compute new outputs */ + (*cm_func)(cm_data); + + + /* ******************************************************* */ + /* Compute the partials of each output with respect to the */ + /* perturbed input by divided differences. */ + /* ******************************************************* */ + + /* loop through all analog output connections */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_output)) + continue; + + /* loop through all the ports of this connection */ + num_port_k = here->conn[k]->size; + for(l = 0; l < num_port_k; l++) { + + /*setup a pointer for out_fast access to port data */ + out_fast = here->conn[k]->port[l]; + + /* skip if this port is null */ + if(out_fast->is_null) + continue; + + /* determine the out_type of this port */ + out_type = out_fast->type; + + /* If port type is Digital or User-Defined, skip it */ + if((out_type == MIF_DIGITAL) || (out_type == MIF_USER_DEFINED)) + continue; + + /* compute partial by divided differences */ + out_fast->partial[i].port[j] = + (out_fast->output.rvalue - out_fast->nominal_output) / epsilon; + + /* zero the output in preparation for next call */ + out_fast->output.rvalue = 0.0; + + } /* end for number of output ports */ + } /* end for number of output connections */ + + /* restore nominal input value */ + fast->input.rvalue = nominal_input; + + } /* end for number of input ports */ + } /* end for number of input connections */ + + + /* *************************************************** */ + /* Call model one last time to recompute nominal case. */ + /* *************************************************** */ + + /* This is needed even though the outputs are recorded, because */ + /* the model may compute other state values that cannot be restored */ + /* to the nominal condition from here */ + + (*cm_func)(cm_data); + +} + + + diff --git a/src/xspice/mif/mifmask.c b/src/xspice/mif/mifmask.c new file mode 100755 index 000000000..3004fd491 --- /dev/null +++ b/src/xspice/mif/mifmask.c @@ -0,0 +1,220 @@ +/*============================================================================ +FILE MIFmAsk.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by nutmeg to get the value + of a specified code model parameter. + +INTERFACES + + MIFmAsk() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + +/* +MIFmAsk + +This function is called by SPICE/Nutmeg to query the value of a +parameter on a model. It is essentially the opposite of +MIFmParam, taking the index of the parameter, locating the value +of the parameter in the model structure, and converting that +value into the IFvalue structure understood by Nutmeg. +*/ + + +int MIFmAsk( + CKTcircuit *ckt, /* The circuit structure */ + GENmodel *inModel, /* The model to get the value from */ + int param_index, /* The parameter to get */ + IFvalue *value) /* The value returned */ +{ + + MIFmodel *model; + int mod_type; + int value_type; + int i; + int size; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the model */ + model = (MIFmodel *) inModel; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + + /* Check parameter index for validity */ + if((param_index < 0) || (param_index >= model->num_param)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.modelParms[param_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + + /* Transfer the values to the SPICE3C1 value union from the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + value->iValue = model->param[param_index]->element[0].bvalue; + break; + + case IF_INTEGER: + value->iValue = model->param[param_index]->element[0].ivalue; + break; + + case IF_REAL: + value->rValue = model->param[param_index]->element[0].rvalue; + break; + + case IF_STRING: + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->sValue = MIFcopy(model->param[param_index]->element[0].svalue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->cValue.real = model->param[param_index]->element[0].cvalue.real; + value->cValue.imag = model->param[param_index]->element[0].cvalue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + size = model->param[param_index]->size; + if(size < 0) + size = 0; + + value->v.numValue = size; + + switch(value_type) { + + /* Note that we malloc space each time this function is called. */ + /* This is what TRAask.c does, so we do it too, even though */ + /* we don't know if it is ever freed... */ + + case IF_FLAGVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = model->param[param_index]->element[i].bvalue; + break; + + case IF_INTVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = model->param[param_index]->element[i].ivalue; + break; + + case IF_REALVEC: + if(size <= 0) + break; + value->v.vec.rVec = (void *) MALLOC(size * sizeof(double)); + for(i = 0; i < size; i++) + value->v.vec.rVec[i] = model->param[param_index]->element[i].rvalue; + break; + + case IF_STRINGVEC: + if(size <= 0) + break; + value->v.vec.sVec = (void *) MALLOC(size * sizeof(char *)); + for(i = 0; i < size; i++) + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->v.vec.sVec[i] = MIFcopy(model->param[param_index]->element[i].svalue); + break; + + case IF_CPLXVEC: + if(size <= 0) + break; + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->v.vec.cVec = (void *) MALLOC(size * sizeof(IFcomplex)); + for(i = 0; i < size; i++) { + value->v.vec.cVec[i].real = model->param[param_index]->element[i].cvalue.real; + value->v.vec.cVec[i].imag = model->param[param_index]->element[i].cvalue.imag; + } + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifmdelete.c b/src/xspice/mif/mifmdelete.c new file mode 100755 index 000000000..57c268b7a --- /dev/null +++ b/src/xspice/mif/mifmdelete.c @@ -0,0 +1,123 @@ +/*============================================================================ +FILE MIFmDelete.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to delete a model + structure and all instances of that model. + +INTERFACES + + MIFmDelete() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "util.h" +#include "sperror.h" +#include "gendefs.h" + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + + +/* +MIFmDelete + +This function deletes a particular model defined by a .model card +from the linked list of model structures of a particular code +model type, freeing all dynamically allocated memory used by the +model structure. It calls MIFdelete as needed to delete all +instances of the specified model. +*/ + + +int MIFmDelete( + GENmodel **inModel, /* The head of the model list */ + IFuid modname, /* The name of the model to delete */ + GENmodel *kill /* The model structure to be deleted */ +) +{ + MIFmodel **model; + MIFmodel *modfast; + MIFmodel **oldmod; + MIFmodel *here=NULL; + + Mif_Boolean_t found; + + int i; + + + /* Convert the generic pointers to MIF specific pointers */ + model = (MIFmodel **) inModel; + modfast = (MIFmodel *) kill; + + /* Locate the model by name or pointer and cut it out of the list */ + oldmod = model; + for(found = MIF_FALSE; *model; model = &((*model)->MIFnextModel)) { + if( (*model)->MIFmodName == modname || + (modfast && *model == modfast) ) { + here = *model; + *oldmod = (*model)->MIFnextModel; + found = MIF_TRUE; + break; + } + oldmod = model; + } + + if(! found) + return(E_NOMOD); + + /* Free the instances under this model if any */ + /* by removing from the head of the linked list */ + /* until the head is null */ + while(here->MIFinstances) { + MIFdelete((GENmodel *) here, + here->MIFinstances->MIFname, + (GENinstance **) &(here->MIFinstances)); + } + + /* Free the model params stuff allocated in MIFget_mod */ + for(i = 0; i < here->num_param; i++) { + if(here->param[i]->element) + FREE(here->param[i]->element); + FREE(here->param[i]); + } + FREE(here->param); + + /* Free the model and return */ + FREE(here); + return(OK); + +} diff --git a/src/xspice/mif/mifmpara.c b/src/xspice/mif/mifmpara.c new file mode 100755 index 000000000..f7bc28b64 --- /dev/null +++ b/src/xspice/mif/mifmpara.c @@ -0,0 +1,211 @@ +/*============================================================================ +FILE MIFmParam.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used to assign the value of a parameter + read from the .model card into the appropriate structure in the model. + +INTERFACES + + MIFmParam() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +//#include "resdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + +/* +MIFmParam + +This function is called by SPICE/Nutmeg to set the value of a +parameter on a model according to information parsed from a +.model card or information supplied interactively by a user. It +takes the value of the parameter input in an IFvalue structure +and sets the parameter on the specified model structure. Unlike +the procedure for SPICE 3C1 devices, MIFmParam does not use +enumerations for identifying the parameter to set. Instead, the +parameter is identified directly by the index value of the +parameter in the SPICEdev.DEVpublic.modelParms array. +*/ + +int MIFmParam( + int param_index, /* The parameter to set */ + IFvalue *value, /* The value of the parameter */ + GENmodel *inModel) /* The model structure on which to set the value */ +{ + + MIFmodel *model; + int mod_type; + int value_type; + int i; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the model */ + model = (MIFmodel *) inModel; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + + /* Check parameter index for validity */ + if((param_index < 0) || (param_index >= model->num_param)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.modelParms[param_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + /* initialize the parameter is_null and size elements and allocate elements */ + model->param[param_index]->is_null = MIF_FALSE; + if(is_array) { + model->param[param_index]->size = value->v.numValue; + model->param[param_index]->element = (void *) MALLOC(value->v.numValue * + sizeof(Mif_Value_t)); + } + else { + model->param[param_index]->size = 1; + model->param[param_index]->element = (void *) MALLOC(sizeof(Mif_Value_t)); + } + + + /* Transfer the values from the SPICE3C1 value union to the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + model->param[param_index]->element[0].bvalue = value->iValue; + break; + + case IF_INTEGER: + model->param[param_index]->element[0].ivalue = value->iValue; + break; + + case IF_REAL: + model->param[param_index]->element[0].rvalue = value->rValue; + break; + + case IF_STRING: + /* we don't trust the caller to keep the string alive, so copy it */ + model->param[param_index]->element[0].svalue = + (void *) MALLOC(1 + strlen(value->sValue)); + strcpy(model->param[param_index]->element[0].svalue, value->sValue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + model->param[param_index]->element[0].cvalue.real = value->cValue.real; + model->param[param_index]->element[0].cvalue.imag = value->cValue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + for(i = 0; i < value->v.numValue; i++) { + + switch(value_type) { + + case IF_FLAGVEC: + model->param[param_index]->element[i].bvalue = value->v.vec.iVec[i]; + break; + + case IF_INTVEC: + model->param[param_index]->element[i].ivalue = value->v.vec.iVec[i]; + break; + + case IF_REALVEC: + model->param[param_index]->element[i].rvalue = value->v.vec.rVec[i]; + break; + + case IF_STRINGVEC: + /* we don't trust the caller to keep the string alive, so copy it */ + model->param[param_index]->element[i].svalue = + (void *) MALLOC(1 + strlen(value->v.vec.sVec[i])); + strcpy(model->param[param_index]->element[i].svalue, value->v.vec.sVec[i]); + break; + + case IF_CPLXVEC: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + model->param[param_index]->element[i].cvalue.real = value->v.vec.cVec[i].real; + model->param[param_index]->element[i].cvalue.imag = value->v.vec.cVec[i].imag; + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end for number of elements of vector */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifsetup.c b/src/xspice/mif/mifsetup.c new file mode 100755 index 000000000..11708aa44 --- /dev/null +++ b/src/xspice/mif/mifsetup.c @@ -0,0 +1,456 @@ +/*============================================================================ +FILE MIFsetup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to setup data structures + of a code model after parsing, but prior to beginning a simulation. The + major responsibilities of this function are to default values for model + parameters not given on the .model card, create equations in the matrix + for any voltage sources, and setup the matrix pointers used during + simulation to load the matrix. + +INTERFACES + + MIFsetup() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "util.h" +#include "smpdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; /* info about all device types */ + + + +/* define macro for easy creation of matrix entries/pointers for outputs */ +#define TSTALLOC(ptr,first,second) \ + if((smp_data_out->ptr = \ + SMPmakeElt(matrix, smp_data_out->first, smp_data_out->second)) == NULL) { \ + return(E_NOMEM); \ + } + +/* define macro for easy creation of matrix entries/pointers for inputs */ +#define CTSTALLOC(ptr,first,second) \ + if((smp_data_out->input[k].port[l].ptr = \ + SMPmakeElt(matrix, smp_data_out->first, smp_data_cntl->second)) == NULL) { \ + return(E_NOMEM); \ + } + + + +/* +MIFsetup + +This function is called by the CKTsetup() driver function to +prepare all code model structures and all code model instance +structures for simulation. It loops through all models of a +particular code model type and provides defaults for any +parameters not specified on a .model card. It loops through all +instances of the model and prepares the instance structures for +simulation. The most important setup task is the creation of +entries in the SPICE matrix and the storage of pointers to +locations of the matrix used by MIFload during a simulation. +*/ + + +int +MIFsetup( + SMPmatrix *matrix, /* The analog simulation matrix structure */ + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt, /* The circuit structure */ + int *states) /* The states vector */ +{ + MIFmodel *model; + MIFinstance *here; + + int mod_type; + int max_size; + int size; + int error; + + int num_conn; + int num_port; + int num_port_k; + int i; + int j; + int k; + int l; + + Mif_Port_Type_t type; + Mif_Port_Type_t in_type; + Mif_Port_Type_t out_type; + + Mif_Cntl_Src_Type_t cntl_src_type; + + Mif_Smp_Ptr_t *smp_data_out; + Mif_Smp_Ptr_t *smp_data_cntl; + + Mif_Param_Info_t *param_info; + /* Mif_Conn_Info_t *conn_info;*/ + + Mif_Boolean_t is_input; + Mif_Boolean_t is_output; + + char *suffix; + CKTnode *tmp; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + mod_type = model->MIFmodType; + + + /* loop through all models of this type */ + + for( ; model != NULL; model = model->MIFnextModel) { + + + /* For each parameter not given explicitly on the .model */ + /* card, default it */ + + for(i = 0; i < model->num_param; i++) { + + if(model->param[i]->is_null) { + + /* setup a pointer for quick access */ + param_info = &(DEVices[mod_type]->DEVpublic.param[i]); + + /* determine the size and allocate the parameter element(s) */ + if(! param_info->is_array) { + model->param[i]->size = 1; + model->param[i]->element = (void *) MALLOC(sizeof(Mif_Value_t)); + } + else { /* parameter is an array */ + /* MIF_INP2A() parser assures that there is an associated array connection */ + /* Since several instances may share this model, we have to create an array */ + /* big enough for the instance with the biggest connection array */ + max_size = 0; + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + size = here->conn[param_info->conn_ref]->size; + if(size > max_size) + max_size = size; + } + model->param[i]->size = max_size; + model->param[i]->element = (void *) MALLOC(max_size * sizeof(Mif_Value_t)); + } /* end if parameter is an array */ + + /* set the parameter element(s) to default value */ + for(j = 0; j < model->param[i]->size; j++) { + + switch(param_info->type) { + + case MIF_BOOLEAN: + model->param[i]->element[j].bvalue = param_info->default_value.bvalue; + break; + + case MIF_INTEGER: + model->param[i]->element[j].ivalue = param_info->default_value.ivalue; + break; + + case MIF_REAL: + model->param[i]->element[j].rvalue = param_info->default_value.rvalue; + break; + + case MIF_COMPLEX: + model->param[i]->element[j].cvalue = param_info->default_value.cvalue; + break; + + case MIF_STRING: + model->param[i]->element[j].svalue = param_info->default_value.svalue; + break; + + default: + return(E_BADPARM); + } + + } /* end for number of elements in param array */ + + } /* end if null */ + + } /* end for number of parameters */ + + + /* For each instance, initialize stuff used by cm_... functions */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + here->num_state = 0; + here->state = NULL; + + here->num_intgr = 0; + here->intgr = NULL; + + here->num_conv = 0; + here->conv = NULL; + } + + + /* For each instance, allocate runtime structs for output connections/ports */ + /* and grab a place in the state vector for all input connections/ports */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* Skip these expensive allocations if the instance is not analog */ + if(! here->analog) + continue; + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + if((here->conn[i]->is_null) || (! here->conn[i]->is_output) ) + continue; + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + here->conn[i]->port[j]->partial = + (void *) MALLOC(num_conn * sizeof(Mif_Partial_t)); + here->conn[i]->port[j]->ac_gain = + (void *) MALLOC(num_conn * sizeof(Mif_AC_Gain_t)); + here->conn[i]->port[j]->smp_data.input = + (void *) MALLOC(num_conn * sizeof(Mif_Conn_Ptr_t)); + for(k = 0; k < num_conn; k++) { + if((here->conn[k]->is_null) || (! here->conn[k]->is_input) ) + continue; + num_port_k = here->conn[k]->size; + here->conn[i]->port[j]->partial[k].port = + (void *) MALLOC(num_port_k * sizeof(double)); + here->conn[i]->port[j]->ac_gain[k].port = + (void *) MALLOC(num_port_k * sizeof(Mif_Complex_t)); + here->conn[i]->port[j]->smp_data.input[k].port = + (void *) MALLOC(num_port_k * sizeof(Mif_Port_Ptr_t)); + } + } + } + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + if((here->conn[i]->is_null) || (! here->conn[i]->is_input) ) + continue; + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + here->conn[i]->port[j]->old_input = *states; + (*states)++; + } + } + } + + + /* Loop through all instances of this model and for each port of each connection */ + /* create current equations, matrix entries, and matrix pointers as necessary. */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* Skip these expensive allocations if the instance is not analog */ + if(! here->analog) + continue; + + num_conn = here->num_conn; + + /* loop through all connections on this instance */ + /* and create matrix data needed for outputs and */ + /* V sources associated with I inputs */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* prepare things for convenient access later */ + is_input = here->conn[i]->is_input; + is_output = here->conn[i]->is_output; + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /* if port is null, skip to next */ + if(here->conn[i]->port[j]->is_null) + continue; + + /* determine the type of this port */ + type = here->conn[i]->port[j]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(here->conn[i]->port[j]->smp_data); + + /* if it has a voltage source output, */ + /* create the matrix data needed */ + if( (is_output && (type == MIF_VOLTAGE || type == MIF_DIFF_VOLTAGE)) || + (type == MIF_RESISTANCE || type == MIF_DIFF_RESISTANCE) ) { + + /* first, make the current equation */ + suffix = (void *) MALLOC(strlen((char *) here->MIFname) + 100); + sprintf(suffix, "branch_%d_%d", i, j); + error = CKTmkCur(ckt, &tmp, here->MIFname, suffix); + FREE(suffix); + if(error) + return(error); + smp_data_out->branch = tmp->number; + + /* ibranch is needed to find the input equation for RESISTANCE type */ + smp_data_out->ibranch = tmp->number; + + /* then make the matrix pointers */ + TSTALLOC(pos_branch, pos_node, branch); + TSTALLOC(neg_branch, neg_node, branch); + TSTALLOC(branch_pos, branch, pos_node); + TSTALLOC(branch_neg, branch, neg_node); + } /* end if current input */ + + /* if it is a current input */ + /* create the matrix data needed for the associated zero-valued V source */ + if(is_input && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) { + + /* first, make the current equation */ + suffix = (void *) MALLOC(strlen((char *) here->MIFname) + 100); + sprintf(suffix, "ibranch_%d_%d", i, j); + error = CKTmkCur(ckt, &tmp, here->MIFname, suffix); + FREE(suffix); + if(error) + return(error); + smp_data_out->ibranch = tmp->number; + + /* then make the matrix pointers */ + TSTALLOC(pos_ibranch, pos_node, ibranch); + TSTALLOC(neg_ibranch, neg_node, ibranch); + TSTALLOC(ibranch_pos, ibranch, pos_node); + TSTALLOC(ibranch_neg, ibranch, neg_node); + } /* end if current input */ + + /* if it is a vsource current input (refers to a vsource elsewhere */ + /* in the circuit), locate the source and get its equation number */ + if(is_input && (type == MIF_VSOURCE_CURRENT)) { + smp_data_out->ibranch = CKTfndBranch(ckt, + here->conn[i]->port[j]->vsource_str); + if(smp_data_out->ibranch == (int)NULL) { + IFuid names[2]; + names[0] = here->MIFname; + names[1] = (IFuid) here->conn[i]->port[j]->vsource_str; + (*(SPfrontEnd->IFerror))(ERR_FATAL, + "%s: unknown controlling source %s",names); + return(E_BADPARM); + } + } /* end if vsource current input */ + + } /* end for number of ports */ + } /* end for number of connections */ + + /* now loop through all connections on the instance and create */ + /* matrix data needed for partial derivatives of outputs */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[i]->is_null) || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /* if port is null, skip to next */ + if(here->conn[i]->port[j]->is_null) + continue; + + /* determine the type of this output port */ + out_type = here->conn[i]->port[j]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(here->conn[i]->port[j]->smp_data); + + /* for this port, loop through all connections */ + /* and all ports to touch on each possible input */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an input */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_input)) + continue; + + num_port_k = here->conn[k]->size; + /* loop through all the ports of this connection */ + for(l = 0; l < num_port_k; l++) { + + /* if port is null, skip to next */ + if(here->conn[k]->port[l]->is_null) + continue; + + /* determine the type of this input port */ + in_type = here->conn[k]->port[l]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_cntl = &(here->conn[k]->port[l]->smp_data); + + /* determine type of controlled source according */ + /* to input and output types */ + cntl_src_type = MIFget_cntl_src_type(in_type, out_type); + + switch(cntl_src_type) { + case MIF_VCVS: + CTSTALLOC(e.branch_poscntl, branch, pos_node); + CTSTALLOC(e.branch_negcntl, branch, neg_node); + break; + case MIF_ICIS: + CTSTALLOC(f.pos_ibranchcntl, pos_node, ibranch); + CTSTALLOC(f.neg_ibranchcntl, neg_node, ibranch); + break; + case MIF_VCIS: + CTSTALLOC(g.pos_poscntl, pos_node, pos_node); + CTSTALLOC(g.pos_negcntl, pos_node, neg_node); + CTSTALLOC(g.neg_poscntl, neg_node, pos_node); + CTSTALLOC(g.neg_negcntl, neg_node, neg_node); + break; + case MIF_ICVS: + CTSTALLOC(h.branch_ibranchcntl, branch, ibranch); + break; + } /* end switch on controlled source type */ + } /* end for number of input ports */ + } /* end for number of input connections */ + } /* end for number of output ports */ + } /* end for number of output connections */ + + } /* end for all instances */ + + + } /* end for all models of this type */ + + return(OK); +} diff --git a/src/xspice/mif/miftrunc.c b/src/xspice/mif/miftrunc.c new file mode 100755 index 000000000..0b03fcaf6 --- /dev/null +++ b/src/xspice/mif/miftrunc.c @@ -0,0 +1,224 @@ +/*============================================================================ +FILE MIFtrunc.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to check truncation + error of an integration state used by a code model. + +INTERFACES + + MIFtrunc() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "cktdefs.h" +#include "sperror.h" + +//#include "util.h" +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +static void MIFterr(Mif_Intgr_t *intgr, CKTcircuit *ckt, double *timeStep); + + + +/* +MIFtrunc + +This function is called by the CKTtrunc() driver function to +check numerical integration truncation error of any integrals +associated with instances of a particular code model type. It +traverses all models of that type and all instances of each +model. For each instance, it looks in the instance structure to +determine if any variables allocated by cm_analog_alloc() have been used +in a call to cm_analog_integrate(). If so, the truncation error of that +integration is computed and used to set the maximum delta allowed +for the current timestep. +*/ + + +int +MIFtrunc( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt, /* The circuit structure */ + double *timeStep) /* The timestep delta */ +{ + + MIFmodel *model; + MIFinstance *here; + + int i; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + + + /* loop through all models of this type */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* Loop through all instances of this model */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + /* Loop through all integration states on the instance */ + for(i = 0; i < here->num_intgr; i++) { + + /* Limit timeStep according to truncation error */ + MIFterr(&(here->intgr[i]), ckt, timeStep); + + } /* end for number of integration states */ + } /* end for all instances */ + } /* end for all models of this type */ + + + return(OK); +} + + + +/* + * Copyright (c) 1985 Thomas L. Quarles + * + * This is a modified version of the function CKTterr(). It limits + * timeStep according to computed truncation error. + * + * Modifications are Copyright 1991 Georgia Tech Research Institute + * + */ + + +static void MIFterr( + Mif_Intgr_t *intgr, + CKTcircuit *ckt, + double *timeStep) +{ + double volttol; + double chargetol; + double tol; + double del; + double diff[8]; + double deltmp[8]; + double factor; + + int i; + int j; + + static double gearCoeff[] = { + .5, + .2222222222, + .1363636364, + .096, + .07299270073, + .05830903790 + }; + static double trapCoeff[] = { + .5, + .08333333333 + }; + + /* Define new local variables. Dimension = number of states in ckt struct */ + char *byte_aligned_state_ptr; + double *state_ptr[8]; + + + /* Set state pointers to the (possibly byte-aligned) states */ + for(i = 0; i < 8; i++) { + byte_aligned_state_ptr = (char *) ckt->CKTstates[i]; + byte_aligned_state_ptr += intgr->byte_index; + state_ptr[i] = (double *) byte_aligned_state_ptr; + } + + /* Modify computation of volttol to not include current from previous timestep */ + /* which is unavailable in this implementation. Note that this makes the */ + /* the overall trunction error timestep smaller (which is better accuracy) */ + + /* Old code */ +/* + volttol = ckt->CKTabstol + ckt->CKTreltol * + MAX( fabs(*(ckt->CKTstate0+ccap)), fabs(*(ckt->CKTstate1+ccap))); +*/ + + /* New code */ + volttol = ckt->CKTabstol + ckt->CKTreltol * fabs(*(state_ptr[0]) - *(state_ptr[1])) + / ckt->CKTdelta; + + /* Modify remaining references to qcap to access byte-aligned MIF state */ + /* Otherwise, remaining code is same as SPICE3C1 ... */ + + chargetol = MAX(fabs(*(state_ptr[0])),fabs(*(state_ptr[1]))); + chargetol = ckt->CKTreltol * MAX(chargetol,ckt->CKTchgtol)/ckt->CKTdelta; + tol = MAX(volttol,chargetol); + /* now divided differences */ + for(i=ckt->CKTorder+1;i>=0;i--) { + diff[i] = *(state_ptr[i]); + } + for(i=0 ; i <= ckt->CKTorder ; i++) { + deltmp[i] = ckt->CKTdeltaOld[i]; + } + j = ckt->CKTorder; + while(1) { + for(i=0;i <= j;i++) { + diff[i] = (diff[i] - diff[i+1])/deltmp[i]; + } + if (--j < 0) break; + for(i=0;i <= j;i++) { + deltmp[i] = deltmp[i+1] + ckt->CKTdeltaOld[i]; + } + } + switch(ckt->CKTintegrateMethod) { + case GEAR: + default: + factor = gearCoeff[ckt->CKTorder-1]; + break; + + case TRAPEZOIDAL: + factor = trapCoeff[ckt->CKTorder - 1] ; + break; + } + del = ckt->CKTtrtol * tol/MAX(ckt->CKTabstol,factor * fabs(diff[0])); + if(ckt->CKTorder == 2) { + del = sqrt(del); + } else if (ckt->CKTorder > 2) { + del = exp(log(del)/ckt->CKTorder); + } + *timeStep = MIN(*timeStep,del); + return; + +} diff --git a/src/xspice/mif/mifutil.c b/src/xspice/mif/mifutil.c new file mode 100755 index 000000000..35ce92f20 --- /dev/null +++ b/src/xspice/mif/mifutil.c @@ -0,0 +1,322 @@ +/*============================================================================ +FILE MIFutil.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains various utility routines used by the MIF package. + +INTERFACES + + MIFgettok() + MIFget_token() + MIFget_cntl_src_type() + MIFcopy() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" + +//#include "util.h" +#include "cpstd.h" +#include +#include "miftypes.h" +#include "mifproto.h" + +/* #include "suffix.h" */ + + + + +/* + +MIFgettok + +Get the next token from the input string. The input string pointer +is advanced to the following token and the token from the input +string is copied to malloced storage and a pointer to that storage +is returned. The original input string is undisturbed. + +*/ + +char *MIFgettok(char **s) +{ + + char *buf; /* temporary storage to copy token into */ + char *ret_str; /* storage for returned string */ + + int i; + + /* allocate space big enough for the whole string */ + + buf = (void *) MALLOC(strlen(*s) + 1); + + /* skip over any white space */ + + while(isspace(**s) || (**s == '=') || + (**s == '(') || (**s == ')') || (**s == ',')) + (*s)++; + + /* isolate the next token */ + + switch(**s) { + + case '\0': + FREE(buf); + return(NULL); + + case '<': + case '>': + case '[': + case ']': + case '~': + case '%': + buf[0] = **s; + buf[1] = '\0'; + (*s)++; + break; + + default: + i = 0; + /* if first character is a quote, read until the closing */ + /* quote, or the end of string, discarding the quotes */ + if(**s == '"') { + (*s)++; + while( (**s != '\0') && (**s != '"') ) { + buf[i] = **s; + i++; + (*s)++; + } + if(**s == '"') + (*s)++; + } + /* else, read until the next delimiter */ + else { + while( (**s != '\0') && + (! ( isspace(**s) || (**s == '=') || (**s == '%') || + (**s == '(') || (**s == ')') || (**s == ',') || + (**s == '[') || (**s == ']') || + (**s == '<') || (**s == '>') || (**s == '~') + ) ) ) { + buf[i] = **s; + i++; + (*s)++; + } + } + + buf[i] = '\0'; + break; + } + + /* skip over white space up to next token */ + + while(isspace(**s) || (**s == '=') || + (**s == '(') || (**s == ')') || (**s == ',')) + (*s)++; + + /* make a copy using only the space needed by the string length */ + + ret_str = copy(buf); + FREE(buf); + + return(ret_str); +} + + + + +/* + +MIFget_token + +Get the next token from the input string together with its type. +The input string pointer +is advanced to the following token and the token from the input +string is copied to malloced storage and a pointer to that storage +is returned. The original input string is undisturbed. + +*/ + +char *MIFget_token( + char **s, /* The text line to get the token from */ + Mif_Token_Type_t *type) /* The type of token found */ +{ + + char *ret_str; /* storage for returned string */ + + /* get the token from the input line */ + + ret_str = MIFgettok(s); + + + /* if no next token, return */ + + if(ret_str == NULL) { + *type = MIF_NO_TOK; + return(NULL); + } + + /* else, determine and return token type */ + + switch(*ret_str) { + + case '[': + *type = MIF_LARRAY_TOK; + break; + + case ']': + *type = MIF_RARRAY_TOK; + break; + + case '<': + *type = MIF_LCOMPLEX_TOK; + break; + + case '>': + *type = MIF_RCOMPLEX_TOK; + break; + + case '%': + *type = MIF_PERCENT_TOK; + break; + + case '~': + *type = MIF_TILDE_TOK; + break; + + default: + if(strcmp(ret_str, "null") == 0) + *type = MIF_NULL_TOK; + else + *type = MIF_STRING_TOK; + break; + + } + + return(ret_str); +} + + + +/* +MIFget_cntl_src_type + +This function takes an input connection/port type and an output +connection/port type (MIF_VOLTAGE, MIF_CURRENT, etc.) and maps +this pair to one of the four controlled source types used in +SPICE (VCVS, VCIS, ICVS, ICIS). +*/ + + +Mif_Cntl_Src_Type_t MIFget_cntl_src_type( + Mif_Port_Type_t in_port_type, /* The type of the input port */ + Mif_Port_Type_t out_port_type) /* The type of the output port */ +{ + + switch(in_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + + switch(out_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + return(MIF_VCVS); + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + return(MIF_VCIS); + break; + + default: + break; + + } + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + + switch(out_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + return(MIF_ICVS); + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + return(MIF_ICIS); + break; + + default: + break; + + } + break; + + default: + break; + + } + + return(-1); +} + + +/* +MIFcopy + +This function allocates a new copy of a string. +*/ + +char *MIFcopy(char *str) +{ + char *temp; + + /* Allocate space for the string and then copy it */ + temp = MALLOC(strlen(str) + 1); + strcpy(temp,str); + + return(temp); +} diff --git a/src/xspice/xspice.c b/src/xspice/xspice.c new file mode 100755 index 000000000..4839e967d --- /dev/null +++ b/src/xspice/xspice.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include + + +/*how annoying!, needed for structure below*/ +void *tcalloc(size_t a, size_t b){ + return tmalloc(a*b); +} +int MIFunsetup(GENmodel *model, CKTcircuit *ckt){ + return 0; +} + +struct coreInfo_t coreInfo = +{ + MIF_INP2A, + MIFgetMod, + MIFgetValue, + MIFsetup, + MIFunsetup, + MIFload, + MIFmParam, + MIFask, + MIFmAsk, + MIFtrunc, + MIFconvTest, + MIFdelete, + MIFmDelete, + MIFdestroy, + MIFgettok, + MIFget_token, + MIFget_cntl_src_type, + MIFcopy, + cm_climit_fcn, + cm_smooth_corner, + cm_smooth_discontinuity, + cm_smooth_pwl, + cm_analog_ramp_factor, + cm_analog_alloc, + cm_analog_get_ptr, + cm_analog_integrate, + cm_analog_converge, + cm_analog_set_temp_bkpt, + cm_analog_set_perm_bkpt, + cm_analog_not_converged, + cm_analog_auto_partial, + cm_event_alloc, + cm_event_get_ptr, + cm_event_queue, + cm_message_get_errmsg, + cm_message_send, + cm_netlist_get_c, + cm_netlist_get_l, + cm_complex_set, + cm_complex_add, + cm_complex_subtract, + cm_complex_multiply, + cm_complex_divide, + NULL, + NULL, + NULL, + tmalloc, + tcalloc, + trealloc, + txfree, + tmalloc, + trealloc, + txfree +};