You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
20 KiB
532 lines
20 KiB
/*============================================================================
|
|
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
|
|
|
|
<date> <person name> <nature of 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 "ngspice/ngspice.h"
|
|
#include "ngspice/smpdefs.h"
|
|
#include "ngspice/devdefs.h"
|
|
#include "ngspice/sperror.h"
|
|
|
|
#include "ngspice/mifproto.h"
|
|
#include "ngspice/mifparse.h"
|
|
#include "ngspice/mifdefs.h"
|
|
#include "ngspice/mifcmdat.h"
|
|
|
|
|
|
|
|
/* define macro for easy creation of matrix entries/pointers for outputs */
|
|
#define TSTALLOC(ptr,first,second) \
|
|
do { if((smp_data_out->ptr = \
|
|
SMPmakeElt(matrix, smp_data_out->first, smp_data_out->second)) == NULL) { \
|
|
return(E_NOMEM); \
|
|
} } while(0)
|
|
|
|
/* define macro for easy creation of matrix entries/pointers for inputs */
|
|
#define CTSTALLOC(ptr,first,second) \
|
|
do { if((smp_data_out->input[k].port[l].ptr = \
|
|
SMPmakeElt(matrix, smp_data_out->first, smp_data_cntl->second)) == NULL) { \
|
|
return(E_NOMEM); \
|
|
} } while(0)
|
|
|
|
|
|
|
|
/*
|
|
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 = TMALLOC(Mif_Value_t, 1);
|
|
}
|
|
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 = TMALLOC(Mif_Value_t, max_size);
|
|
} /* 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 =
|
|
TMALLOC(Mif_Partial_t, num_conn);
|
|
here->conn[i]->port[j]->ac_gain =
|
|
TMALLOC(Mif_AC_Gain_t, num_conn);
|
|
here->conn[i]->port[j]->smp_data.input =
|
|
TMALLOC(Mif_Conn_Ptr_t, num_conn);
|
|
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 =
|
|
TMALLOC(double, num_port_k);
|
|
here->conn[i]->port[j]->ac_gain[k].port =
|
|
TMALLOC(Mif_Complex_t, num_port_k);
|
|
here->conn[i]->port[j]->smp_data.input[k].port =
|
|
TMALLOC(Mif_Port_Ptr_t, num_port_k);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = TMALLOC(char, strlen(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 = TMALLOC(char, strlen(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 == 0) {
|
|
IFuid names[2];
|
|
names[0] = here->MIFname;
|
|
names[1] = 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;
|
|
case MIF_minus_one:
|
|
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);
|
|
}
|
|
|
|
int
|
|
MIFunsetup(GENmodel *inModel,CKTcircuit *ckt)
|
|
{
|
|
MIFmodel *model;
|
|
MIFinstance *here;
|
|
Mif_Smp_Ptr_t *smp_data_out;
|
|
/* Mif_Smp_Ptr_t *smp_data_cntl;*/
|
|
/* Mif_Port_Type_t type, in_type, out_type;*/
|
|
Mif_Port_Type_t in_type, out_type;
|
|
Mif_Cntl_Src_Type_t cntl_src_type;
|
|
int num_conn,num_port,i,j,k,l,num_port_k;
|
|
|
|
|
|
for (model = (MIFmodel *)inModel; model != NULL;
|
|
model = model->MIFnextModel)
|
|
{
|
|
for(here = model->MIFinstances; here != NULL;
|
|
here = here->MIFnextInstance)
|
|
{
|
|
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 */
|
|
num_port = here->conn[i]->size;
|
|
|
|
/* loop through all ports on this connection */
|
|
for(j = 0; j < num_port; j++) {
|
|
|
|
/* 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(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;
|
|
/* determine the type of this input port */
|
|
for(l = 0; l < num_port_k; l++) {
|
|
|
|
/* if port is null, skip to next */
|
|
if(here->conn[k]->port[l]->is_null)
|
|
continue;
|
|
in_type = here->conn[i]->port[j]->type;
|
|
cntl_src_type = MIFget_cntl_src_type(in_type, out_type);
|
|
|
|
switch(cntl_src_type)
|
|
{
|
|
case MIF_VCVS:
|
|
case MIF_ICVS:
|
|
case MIF_VCIS:
|
|
case MIF_ICIS:
|
|
case MIF_minus_one: /* FIXME, really ? */
|
|
if(smp_data_out->branch)
|
|
{
|
|
CKTdltNNum(ckt, smp_data_out->branch);
|
|
smp_data_out->branch = 0;
|
|
}
|
|
|
|
if(smp_data_out->ibranch)
|
|
{
|
|
CKTdltNNum(ckt, smp_data_out->ibranch);
|
|
smp_data_out->ibranch = 0;
|
|
}
|
|
here->initialized=MIF_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* printf("MIFunsetup completed.\n");*/
|
|
return OK;
|
|
}
|