979 lines
25 KiB
C
979 lines
25 KiB
C
/*============================================================================
|
|
FILE IPC.c
|
|
|
|
MEMBER OF process XSPICE
|
|
|
|
Public Domain
|
|
|
|
Georgia Tech Research Corporation
|
|
Atlanta, Georgia 30332
|
|
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
|
|
|
|
|
|
|
|
============================================================================*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
|
|
#include <assert.h>
|
|
#include "ngspice/memory.h" /* 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 "ngspice/ipc.h"
|
|
#include "ngspice/ipctiein.h"
|
|
#include "ngspice/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
|
|
|
|
/*
|
|
* 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 (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 (
|
|
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_terminate_server (void)
|
|
{
|
|
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 (
|
|
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_c(*tok2)) {
|
|
*tok2 = '\0';
|
|
tok2++;
|
|
break;
|
|
}
|
|
}
|
|
for(tok3 = tok2; *tok3; tok3++) {
|
|
if(isspace_c(*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 (void)
|
|
/*
|
|
* 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], (size_t) (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 (
|
|
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, (size_t) 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 (char *str ) /* The text to send */
|
|
{
|
|
int len;
|
|
int send_len;
|
|
|
|
char *s;
|
|
|
|
Ipc_Status_t status= IPC_STATUS_OK;
|
|
|
|
|
|
len = (int) 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 (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 (void)
|
|
{
|
|
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 (void)
|
|
{
|
|
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 (void)
|
|
{
|
|
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 (void)
|
|
{
|
|
#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 (void)
|
|
{
|
|
#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 (void)
|
|
{
|
|
#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 (void)
|
|
{
|
|
#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(void)
|
|
{
|
|
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(void)
|
|
{
|
|
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 (
|
|
double d1, double 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] = (float)d1;
|
|
if (n > 1) {
|
|
trick.float_val[1] = (float)d2;
|
|
}
|
|
for (i = 0, j = pos; i < n * (int) sizeof(float); j++, i++)
|
|
buf[j] = trick.ch[i];
|
|
buf[0] = (char) ('A' + j - 1);
|
|
return j;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
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 (
|
|
char *tag, /* The node or instance */
|
|
double value ) /* The data value to send */
|
|
{
|
|
int len = 0;
|
|
|
|
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 */
|
|
strtoupper(fmt_buffer);
|
|
|
|
len = stuff_binary_v1 (value, 0.0, 1, fmt_buffer, (int) 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 (
|
|
char *tag, /* The node or instance */
|
|
Ipc_Complex_t value ) /* The data value to send */
|
|
{
|
|
int len=0;
|
|
|
|
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 */
|
|
strtoupper(fmt_buffer);
|
|
|
|
len = stuff_binary_v1 (value.real, value.imag, 2, fmt_buffer,
|
|
(int) 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 (
|
|
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 + (int) 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 = (int) strlen(buff);
|
|
buff_ptr = buff + buff_len;
|
|
fvalue = (float)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 = (float)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 = (char*) 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 += (int) strlen(print_val);
|
|
|
|
/* Send the data to the IPC channel */
|
|
return ipc_send_line_binary(buff, buff_len);
|
|
|
|
#endif
|
|
}
|
|
|
|
|