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.
645 lines
18 KiB
645 lines
18 KiB
/*============================================================================
|
|
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
|
|
|
|
<date> <person name> <nature of 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 <stdio.h>
|
|
#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++;
|
|
}
|
|
}
|
|
}
|