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.
697 lines
19 KiB
697 lines
19 KiB
/* ===========================================================================
|
|
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
|
|
|
|
<date> <person name> <nature of 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;
|
|
}
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
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;
|
|
}
|
|
|