FILE EVTload.c
MEMBER OF process XSPICE
Public Domain
Georgia Tech Research Corporation
Atlanta, Georgia 30332
PROJECT A-8503
AUTHORS
9/12/91 Bill Kuhn
MODIFICATIONS
<date> <person name> <nature of 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 "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "ngspice/devdefs.h"
#include "ngspice/sperror.h"
#include "ngspice/mif.h"
#include "ngspice/evt.h"
#include "ngspice/evtudn.h"
#include "ngspice/mifproto.h"
#include "ngspice/evtproto.h"
#include "ngspice/cmproto.h"
static void EVTcreate_state(
CKTcircuit *ckt,
int inst_index);
static void EVTadd_msg(
CKTcircuit *ckt,
int port_index,
char *msg_text);
static Evt_Output_Event_t *EVTget_output_event(
CKTcircuit *ckt,
Mif_Port_Data_t *port);
static void EVTprocess_output(
CKTcircuit *ckt,
Mif_Port_Data_t *port);
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,
MIFinstance *inst)
{
return EVTload_with_event(ckt, inst, MIF_EVENT_DRIVEN);
}
int EVTload_with_event(
CKTcircuit *ckt,
MIFinstance *inst,
Mif_Call_Type_t type)
{
int i;
int j;
int num_conn;
int num_port;
int mod_type;
Mif_Conn_Data_t *conn;
Mif_Port_Data_t *port;
Evt_Node_Data_t *node_data;
Mif_Private_t cm_data;
node_data = ckt->evt->data.node;
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;
if (type == MIF_STEP_PENDING) {
cm_data.circuit.t[0] = ckt->CKTtime;
cm_data.circuit.t[1] = ckt->CKTtime - ckt->CKTdeltaOld[0];
} else {
cm_data.circuit.t[0] = ckt->CKTtime + ckt->CKTdelta;
cm_data.circuit.t[1] = ckt->CKTtime;
}
if (cm_data.circuit.t[1] < 0.0)
cm_data.circuit.t[1] = 0.0;
} else {
cm_data.circuit.time = 0.0;
cm_data.circuit.t[0] = cm_data.circuit.t[1] = 0.0;
}
* are expected to distinguish STEP_PENDING from ordinary events.
*/
if (type == MIF_STEP_PENDING && inst->irreversible)
cm_data.circuit.call_type = MIF_STEP_PENDING;
else
cm_data.circuit.call_type = MIF_EVENT_DRIVEN;
cm_data.circuit.temperature = ckt->CKTtemp - 273.15;
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((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized &&
inst->inst_index >= 0)
EVTcreate_state(ckt, inst->inst_index);
num_conn = inst->num_conn;
for(i = 0; i < num_conn; i++) {
conn = inst->conn[i];
if(conn->is_null)
continue;
num_port = conn->size;
for(j = 0; j < num_port; j++) {
port = conn->port[j];
if(port->is_null)
continue;
if((port->type == MIF_DIGITAL) || (port->type == MIF_USER_DEFINED)) {
port->msg = NULL;
port->load = 0.0;
port->total_load = node_data->total_load[port->evt_data.node_index];
* initialize changed to true and ensure an output location.
*/
if(conn->is_output) {
port->changed = MIF_TRUE;
if (g_mif_info.circuit.anal_type == MIF_TRAN) {
if (port->next_event == NULL) {
port->next_event = EVTget_output_event(ckt, port);
}
port->output.pvalue = port->next_event->value;
}
}
}
else {
if(ckt->CKTmode & MODEINITJCT)
port->input.rvalue = 0.0;
}
}
}
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;
cm_data.callback = &(inst->callback);
mod_type = MIFmodPtr(inst)->MIFmodType;
DEVices[mod_type]->DEVpublic.cm_func (&cm_data);
num_conn = inst->num_conn;
for(i = 0; i < num_conn; i++) {
conn = inst->conn[i];
if(conn->is_null)
continue;
num_port = conn->size;
for(j = 0; j < num_port; j++) {
port = conn->port[j];
if(port->is_null)
continue;
if(port->msg)
EVTadd_msg(ckt, port->evt_data.port_index, port->msg);
if(! inst->initialized) {
node_data->total_load[port->evt_data.node_index] +=
port->load;
}
if(! conn->is_output)
continue;
if((port->type != MIF_DIGITAL) && (port->type != MIF_USER_DEFINED))
continue;
if (port->changed)
EVTprocess_output(ckt, port);
if(g_mif_info.circuit.anal_type == MIF_TRAN)
port->output.pvalue = NULL;
}
}
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)++;
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,
int inst_index)
{
size_t total_size;
Evt_State_Data_t *state_data;
Evt_State_t *new_state;
Evt_State_t *prev_state;
state_data = ckt->evt->data.state;
if(state_data->desc[inst_index] == NULL)
return;
total_size = (size_t) state_data->total_size[inst_index];
if(state_data->free[inst_index])
{
new_state = state_data->free[inst_index];
state_data->free[inst_index] = new_state->next;
new_state->next = NULL;
}
else
{
new_state = TMALLOC(Evt_State_t, 1);
new_state->block = tmalloc(total_size);
}
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);
memcpy(new_state->block, prev_state->block, total_size);
new_state->step = g_mif_info.circuit.evt_step;
if(! state_data->modified[inst_index]) {
state_data->modified[inst_index] = MIF_TRUE;
state_data->modified_index[(state_data->num_modified)++] = inst_index;
}
}
EVTget_output_event
This function creates a new output event.
*/
static Evt_Output_Event_t *EVTget_output_event(
CKTcircuit *ckt,
Mif_Port_Data_t *port)
{
int udn_index;
Evt_Node_Info_t **node_table;
Evt_Output_Queue_t *output_queue;
Evt_Output_Event_t *event, **free_list;
output_queue = &(ckt->evt->queue.output);
free_list = output_queue->free_list[port->evt_data.output_index];
if (*free_list) {
event = *free_list;
*free_list = event->next;
} else {
event = TMALLOC(Evt_Output_Event_t, 1);
event->next = NULL;
node_table = ckt->evt->info.node_table;
udn_index = node_table[port->evt_data.node_index]->udn_index;
g_evt_udn_info[udn_index]->create (&(event->value));
}
return event;
}
EVTadd_msg
This function records a message output by a code model into the
message results data structure.
*/
static void EVTadd_msg(
CKTcircuit *ckt,
int port_index,
char *msg_text)
{
Evt_Msg_Data_t *msg_data;
Evt_Msg_t **msg_ptr;
Evt_Msg_t *msg;
msg_data = ckt->evt->data.msg;
msg_ptr = msg_data->tail[port_index];
if(*msg_ptr != NULL) {
msg_ptr = &((*msg_ptr)->next);
msg_data->tail[port_index] = msg_ptr;
}
if(msg_data->free[port_index]) {
*msg_ptr = msg_data->free[port_index];
msg_data->free[port_index] = msg_data->free[port_index]->next;
if ((*msg_ptr)->text)
tfree((*msg_ptr)->text);
}
else {
*msg_ptr = TMALLOC(Evt_Msg_t, 1);
}
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);
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;
}
}
}
* static functions.
*/
bool cm_schedule_output(unsigned int conn_index, unsigned int port_index,
double delay, void *vp)
{
MIFinstance *instance;
Mif_Conn_Data_t *conn;
Mif_Port_Data_t *port;
Evt_Node_Info_t *node_info;
Evt_Output_Event_t *output_event;
int udn_index;
if (delay < 0 || g_mif_info.circuit.anal_type != MIF_TRAN)
return FALSE;
instance = g_mif_info.instance;
if (conn_index >= (unsigned int)instance->num_conn)
return FALSE;
conn = instance->conn[conn_index];
if (port_index >= (unsigned int)conn->size)
return FALSE;
port = conn->port[port_index];
if (port->type != MIF_DIGITAL && port->type != MIF_USER_DEFINED)
return FALSE;
output_event = EVTget_output_event(g_mif_info.ckt, port);
node_info =
g_mif_info.ckt->evt->info.node_table[port->evt_data.node_index];
udn_index = node_info->udn_index;
g_evt_udn_info[node_info->udn_index]->copy(vp, output_event->value);
if (port->invert)
g_evt_udn_info[udn_index]->invert(output_event->value);
EVTqueue_output(g_mif_info.ckt, port->evt_data.output_index,
udn_index, output_event,
g_mif_info.circuit.evt_step,
g_mif_info.circuit.evt_step + delay);
return TRUE;
}
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,
Mif_Port_Data_t *port)
{
int num_outputs;
int node_index;
int udn_index;
int output_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 invert, equal;
double delay;
output_queue = &(ckt->evt->queue.output);
output_table = ckt->evt->info.output_table;
node_table = ckt->evt->info.node_table;
output_index = port->evt_data.output_index;
node_index = output_table[output_index]->node_index;
udn_index = node_table[node_index]->udn_index;
invert = port->invert;
if (g_mif_info.circuit.anal_type == MIF_TRAN) {
delay = port->delay;
if(delay < 0.0) {
printf("\nERROR - Output delay < 0 not allowed - output ignored!\n");
printf(" Instance: %s\n Node: %s\n Time: %f \n",
g_mif_info.instance->MIFname, node_table[node_index]->name,
g_mif_info.ckt->CKTtime);
return;
}
output_event = port->next_event;
port->next_event = NULL;
if(invert)
g_evt_udn_info[udn_index]->invert
(output_event->value);
EVTqueue_output(ckt, output_index, udn_index, output_event,
g_mif_info.circuit.evt_step,
g_mif_info.circuit.evt_step + delay);
return;
} else {
rhs = ckt->evt->data.node->rhs;
rhsold = ckt->evt->data.node->rhsold;
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(! 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;
}
}