2765 lines
97 KiB
C
2765 lines
97 KiB
C
/* sim_frontpanel.c: simulator frontpanel API
|
|
|
|
Copyright (c) 2015, Mark Pizzolato
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Mark Pizzolato shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Mark Pizzolato.
|
|
|
|
05-Feb-15 MP Initial implementation
|
|
01-Apr-15 MP Added register indirect, mem_examine and mem_deposit
|
|
03-Apr-15 MP Added logic to pass simulator startup messages in
|
|
panel error text if the connection to the simulator
|
|
shuts down while it is starting.
|
|
04-Apr-15 MP Added mount and dismount routines to connect and
|
|
disconnect removable media
|
|
|
|
This module provides interface between a front panel application and a simh
|
|
simulator. Facilities provide ways to gather information from and to
|
|
observe and control the state of a simulator.
|
|
|
|
The details of the 'wire protocol' are internal to the API interfaces
|
|
provided here and described in sim_frontpanel.h. These details are subject
|
|
to change from one sim_frontpanel version to the next, while all efforts
|
|
will be made to retain any prior sim_frontpanel API interfaces.
|
|
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include "sim_sock.h"
|
|
|
|
#include "sim_frontpanel.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
|
|
#if defined(_WIN32)
|
|
#include <process.h>
|
|
#include <windows.h>
|
|
#include <winerror.h>
|
|
#define sleep(n) Sleep(n*1000)
|
|
#define msleep(n) Sleep(n)
|
|
#define strtoull _strtoui64
|
|
#define CLOCK_REALTIME 0
|
|
int clock_gettime(int clk_id, struct timespec *tp)
|
|
{
|
|
unsigned long long now, unixbase;
|
|
|
|
unixbase = 116444736;
|
|
unixbase *= 1000000000;
|
|
GetSystemTimeAsFileTime((FILETIME*)&now);
|
|
now -= unixbase;
|
|
tp->tv_sec = (long)(now/10000000);
|
|
tp->tv_nsec = (now%10000000)*100;
|
|
return 0;
|
|
}
|
|
#else /* NOT _WIN32 */
|
|
#include <unistd.h>
|
|
#define msleep(n) usleep(1000*n)
|
|
#include <sys/wait.h>
|
|
#if defined (__APPLE__)
|
|
#define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */
|
|
#endif
|
|
|
|
/* on HP-UX, CLOCK_REALTIME is enum, not preprocessor define */
|
|
#if !defined(CLOCK_REALTIME) && !defined(__hpux)
|
|
#define CLOCK_REALTIME 1
|
|
#define NEED_CLOCK_GETTIME 1
|
|
#if !defined(HAVE_STRUCT_TIMESPEC)
|
|
#define HAVE_STRUCT_TIMESPEC
|
|
#if !defined(_TIMESPEC_DEFINED)
|
|
#define _TIMESPEC_DEFINED
|
|
struct timespec {
|
|
long tv_sec;
|
|
long tv_nsec;
|
|
};
|
|
#endif /* _TIMESPEC_DEFINED */
|
|
#endif /* HAVE_STRUCT_TIMESPEC */
|
|
#if defined(NEED_CLOCK_GETTIME)
|
|
int clock_gettime(int clk_id, struct timespec *tp)
|
|
{
|
|
struct timeval cur;
|
|
struct timezone foo;
|
|
|
|
gettimeofday (&cur, &foo);
|
|
tp->tv_sec = cur.tv_sec;
|
|
tp->tv_nsec = cur.tv_usec*1000;
|
|
return 0;
|
|
}
|
|
#endif /* defined(NEED_CLOCK_GETTIME) */
|
|
#endif /* !defined(CLOCK_REALTIME) && !defined(__hpux) */
|
|
|
|
#endif /* NOT _WIN32 */
|
|
|
|
typedef struct {
|
|
char *name;
|
|
char *device_name;
|
|
void *addr;
|
|
size_t size;
|
|
int indirect;
|
|
size_t element_count;
|
|
int *bits;
|
|
size_t bit_count;
|
|
} REG;
|
|
|
|
struct PANEL {
|
|
PANEL *parent; /* Device Panels can have parent panels */
|
|
char *path; /* simulator path */
|
|
char *config;
|
|
char *device_name; /* device name */
|
|
char *temp_config;
|
|
char hostport[64];
|
|
size_t device_count;
|
|
PANEL **devices;
|
|
SOCKET sock;
|
|
size_t reg_count;
|
|
REG *regs;
|
|
char *reg_query;
|
|
int new_register;
|
|
size_t reg_query_size;
|
|
unsigned long long array_element_data;
|
|
volatile OperationalState State;
|
|
unsigned long long simulation_time;
|
|
unsigned long long simulation_time_base;
|
|
pthread_t io_thread;
|
|
int io_thread_running;
|
|
pthread_mutex_t io_lock;
|
|
pthread_mutex_t io_send_lock;
|
|
pthread_mutex_t io_command_lock;
|
|
int command_count;
|
|
int io_waiting;
|
|
char *io_response;
|
|
char *halt_reason;
|
|
size_t io_response_data;
|
|
size_t io_response_size;
|
|
const char *completion_string;
|
|
pthread_cond_t io_done;
|
|
pthread_cond_t startup_done;
|
|
PANEL_DISPLAY_PCALLBACK callback;
|
|
pthread_t callback_thread;
|
|
int callback_thread_running;
|
|
void *callback_context;
|
|
int usecs_between_callbacks;
|
|
pthread_t debugflush_thread;
|
|
int debugflush_thread_running;
|
|
unsigned int sample_frequency;
|
|
unsigned int sample_dither_pct;
|
|
unsigned int sample_depth;
|
|
int debug;
|
|
char *simulator_version;
|
|
int radix;
|
|
FILE *Debug;
|
|
#if defined(_WIN32)
|
|
HANDLE hProcess;
|
|
DWORD dwProcessId;
|
|
#else
|
|
pid_t pidProcess;
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* Thread synchronization model:
|
|
*
|
|
* Mutex: Role:
|
|
* io_lock Serialize access to panel state variables
|
|
* acquired and released in application threads:
|
|
* _panel_register_query_string,
|
|
* _panel_establish_register_bits_collection,
|
|
* _panel_sendf
|
|
* acquired and released in internal threads:
|
|
* _panel_callback
|
|
* _panel_reader
|
|
* io_send_lock Serializes writes to a panel's sockets so that complete
|
|
* command/request data can be delivered before another
|
|
* thread attempts to write to the socket.
|
|
* acquired and released in: _panel_send
|
|
* io_command_lock To serialize frontpanel application command requests
|
|
* acquired and released in: _panel_get_registers,
|
|
* _panel_sendf_completion
|
|
*
|
|
* Condition Var: Sync Mutex: Purpose & Duration:
|
|
* io_done io_lock
|
|
* startup_done io_lock Indicate background thread setup is complete.
|
|
* Once signaled, it is immediately destroyed.
|
|
*/
|
|
|
|
static const char *sim_prompt = "sim> ";
|
|
static const char *register_repeat_prefix = "repeat every ";
|
|
static const char *register_repeat_stop = "repeat stop";
|
|
static const char *register_repeat_stop_all = "repeat stop all";
|
|
static const char *register_repeat_units = " usecs ";
|
|
static const char *register_get_prefix = "show time";
|
|
static const char *register_collect_prefix = "collect ";
|
|
static const char *register_collect_mid1 = " samples every ";
|
|
static const char *register_collect_mid2 = " cycles dither ";
|
|
static const char *register_collect_mid3 = " percent ";
|
|
static const char *register_get_postfix = "sampleout";
|
|
static const char *register_get_start = "# REGISTERS-START";
|
|
static const char *register_get_end = "# REGISTERS-DONE";
|
|
static const char *register_repeat_start = "# REGISTERS-REPEAT-START";
|
|
static const char *register_repeat_end = "# REGISTERS-REPEAT-DONE";
|
|
static const char *register_dev_echo = "# REGISTERS-FOR-DEVICE:";
|
|
static const char *register_ind_echo = "# REGISTER-INDIRECT:";
|
|
static const char *command_status = "ECHO Status:%STATUS%-%TSTATUS%";
|
|
static const char *command_done_echo = "# COMMAND-DONE";
|
|
static int little_endian;
|
|
static void *_panel_reader(void *arg);
|
|
static void *_panel_callback(void *arg);
|
|
static void *_panel_debugflusher(void *arg);
|
|
static int sim_panel_set_error (PANEL *p, const char *fmt, ...);
|
|
static pthread_key_t panel_thread_id;
|
|
|
|
#define TN_IAC 0xFFu /* -1 */ /* protocol delim */
|
|
#define TN_DONT 0xFEu /* -2 */ /* dont */
|
|
#define TN_DO 0xFDu /* -3 */ /* do */
|
|
#define TN_WONT 0xFCu /* -4 */ /* wont */
|
|
#define TN_WILL 0xFBu /* -5 */ /* will */
|
|
|
|
#define TN_BIN 0 /* bin */
|
|
#define TN_ECHO 1 /* echo */
|
|
#define TN_SGA 3 /* sga */
|
|
#define TN_CR 015 /* carriage return */
|
|
#define TN_LF 012 /* line feed */
|
|
#define TN_LINE 34 /* line mode */
|
|
|
|
static unsigned char mantra[] = {
|
|
TN_IAC, TN_WILL, TN_LINE,
|
|
TN_IAC, TN_WILL, TN_SGA,
|
|
TN_IAC, TN_WILL, TN_ECHO,
|
|
TN_IAC, TN_WILL, TN_BIN,
|
|
TN_IAC, TN_DO, TN_BIN
|
|
};
|
|
|
|
static void *
|
|
_panel_malloc (size_t size)
|
|
{
|
|
void *p = malloc (size);
|
|
|
|
if (p == NULL)
|
|
sim_panel_set_error (NULL, "Out of Memory");
|
|
return p;
|
|
}
|
|
|
|
/* Allow compiler to help validate printf style format arguments */
|
|
#if !defined __GNUC__
|
|
#define GCC_FMT_ATTR(n, m)
|
|
#endif
|
|
#if !defined(GCC_FMT_ATTR)
|
|
#define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m)))
|
|
#endif
|
|
|
|
static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) GCC_FMT_ATTR(3, 6);
|
|
#define _panel_debug(p, dbits, fmt, buf, bufsize, ...) do { if (p && p->Debug && ((dbits) & p->debug)) __panel_debug (p, dbits, fmt, buf, bufsize, ##__VA_ARGS__);} while (0)
|
|
|
|
static void __panel_vdebug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, va_list arglist)
|
|
{
|
|
size_t obufsize = 10240 + 9*bufsize;
|
|
|
|
while (p && p->Debug && (dbits & p->debug)) {
|
|
int i, len;
|
|
struct timespec time_now;
|
|
char timestamp[32];
|
|
char threadname[50];
|
|
char *obuf = (char *)_panel_malloc (obufsize);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &time_now);
|
|
sprintf (timestamp, "%lld.%03d ", (long long)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000));
|
|
sprintf (threadname, "%s:%s ", p->parent ? p->device_name : "CPU", (pthread_getspecific (panel_thread_id)) ? (char *)pthread_getspecific (panel_thread_id) : "");
|
|
|
|
obuf[obufsize - 1] = '\0';
|
|
len = vsnprintf (obuf, obufsize - 1, fmt, arglist);
|
|
if (len < 0)
|
|
return;
|
|
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
|
|
if (len >= (int)(obufsize - 9*bufsize)) {
|
|
obufsize = len + 1 + 9*bufsize;
|
|
free (obuf);
|
|
continue;
|
|
}
|
|
|
|
for (i=0; i<bufsize; ++i) {
|
|
switch ((unsigned char)buf[i]) {
|
|
case TN_CR:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_CR_");
|
|
break;
|
|
case TN_LF:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_LF_");
|
|
break;
|
|
case TN_IAC:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_IAC_");
|
|
switch ((unsigned char)buf[i+1]) {
|
|
case TN_IAC:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_IAC_"); ++i;
|
|
break;
|
|
case TN_DONT:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_DONT_"); ++i;
|
|
break;
|
|
case TN_DO:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_DO_"); ++i;
|
|
break;
|
|
case TN_WONT:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_WONT_"); ++i;
|
|
break;
|
|
case TN_WILL:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_WILL_"); ++i;
|
|
break;
|
|
default:
|
|
sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i;
|
|
break;
|
|
}
|
|
switch ((unsigned char)buf[i+1]) {
|
|
case TN_BIN:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_BIN_"); ++i;
|
|
break;
|
|
case TN_ECHO:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_ECHO_"); ++i;
|
|
break;
|
|
case TN_SGA:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_SGA_"); ++i;
|
|
break;
|
|
case TN_LINE:
|
|
sprintf (&obuf[strlen (obuf)], "_TN_LINE_"); ++i;
|
|
break;
|
|
default:
|
|
sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
if (isprint((u_char)buf[i]))
|
|
sprintf (&obuf[strlen (obuf)], "%c", buf[i]);
|
|
else {
|
|
sprintf (&obuf[strlen (obuf)], "_");
|
|
if ((buf[i] >= 1) && (buf[i] <= 26))
|
|
sprintf (&obuf[strlen (obuf)], "^%c", 'A' + buf[i] - 1);
|
|
else
|
|
sprintf (&obuf[strlen (obuf)], "\\%03o", (u_char)buf[i]);
|
|
sprintf (&obuf[strlen (obuf)], "_");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fprintf(p->Debug, "%s%s%s\n", timestamp, threadname, obuf);
|
|
free (obuf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...)
|
|
{
|
|
va_list arglist;
|
|
|
|
va_start (arglist, bufsize);
|
|
__panel_vdebug (p, dbits, fmt, buf, bufsize, arglist);
|
|
va_end (arglist);
|
|
}
|
|
|
|
void
|
|
sim_panel_debug (PANEL *panel, const char *fmt, ...)
|
|
{
|
|
va_list arglist;
|
|
|
|
va_start (arglist, fmt);
|
|
__panel_vdebug (panel, DBG_APP, fmt, NULL, 0, arglist);
|
|
va_end (arglist);
|
|
}
|
|
|
|
|
|
static void *
|
|
_panel_debugflusher(void *arg)
|
|
{
|
|
PANEL *p = (PANEL*)arg;
|
|
int flush_interval = 15;
|
|
int sleeps = 0;
|
|
|
|
pthread_setspecific (panel_thread_id, "debugflush");
|
|
|
|
pthread_mutex_lock (&p->io_lock);
|
|
p->debugflush_thread_running = 1;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */
|
|
msleep (100);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
while (p->sock != INVALID_SOCKET) {
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
msleep (1000);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
if (0 == (sleeps++)%flush_interval)
|
|
sim_panel_flush_debug (p);
|
|
}
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
pthread_setspecific (panel_thread_id, NULL);
|
|
p->debugflush_thread_running = 0;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
_set_debug_file (PANEL *panel, const char *debug_file)
|
|
{
|
|
if (!panel)
|
|
return;
|
|
panel->Debug = fopen(debug_file, "w");
|
|
if (panel->Debug)
|
|
setvbuf (panel->Debug, NULL, _IOFBF, 65536);
|
|
}
|
|
|
|
void
|
|
sim_panel_set_debug_mode (PANEL *panel, int debug_bits)
|
|
{
|
|
if (panel)
|
|
panel->debug = debug_bits;
|
|
}
|
|
|
|
void
|
|
sim_panel_flush_debug (PANEL *panel)
|
|
{
|
|
if (!panel)
|
|
return;
|
|
if (panel->Debug)
|
|
fflush (panel->Debug);
|
|
}
|
|
|
|
|
|
static int
|
|
_panel_send (PANEL *p, const char *msg, int len)
|
|
{
|
|
int sent = 0;
|
|
|
|
if (p->sock == INVALID_SOCKET)
|
|
return sim_panel_set_error (p, "Invalid Socket for write");
|
|
pthread_mutex_lock (&p->io_send_lock);
|
|
while (len) {
|
|
int bsent = sim_write_sock (p->sock, msg, len);
|
|
if (bsent < 0) {
|
|
pthread_mutex_unlock (&p->io_send_lock);
|
|
return sim_panel_set_error (p, "%s", sim_get_err_sock("Error writing to socket"));
|
|
}
|
|
_panel_debug (p, DBG_XMT, "Sent %d bytes: ", msg, bsent, bsent);
|
|
len -= bsent;
|
|
msg += bsent;
|
|
sent += bsent;
|
|
}
|
|
pthread_mutex_unlock (&p->io_send_lock);
|
|
return sent;
|
|
}
|
|
|
|
static int
|
|
_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...);
|
|
|
|
static int
|
|
_panel_sendf_completion (PANEL *p, char **response, const char *completion, const char *fmt, ...);
|
|
|
|
static int
|
|
_panel_register_query_string (PANEL *panel, char **buf, size_t *buf_size)
|
|
{
|
|
size_t i, j, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0;
|
|
const char *dev;
|
|
|
|
pthread_mutex_lock (&panel->io_lock);
|
|
buf_needed = 3 + 7 + /* EXECUTE */
|
|
strlen (register_get_start) + /* # REGISTERS-START */
|
|
strlen (register_get_prefix); /* SHOW TIME */
|
|
for (i=0; i<panel->reg_count; i++) {
|
|
if (panel->regs[i].bits)
|
|
++bit_reg_count;
|
|
else {
|
|
++reg_count;
|
|
buf_needed += 10 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0);
|
|
if (panel->regs[i].element_count > 0)
|
|
buf_needed += 4 + 6 /* 6 digit register array index */;
|
|
if (panel->regs[i].indirect)
|
|
buf_needed += 12 + strlen (register_ind_echo) + strlen (panel->regs[i].name);
|
|
}
|
|
}
|
|
if (bit_reg_count)
|
|
buf_needed += 2 + strlen (register_get_postfix);
|
|
buf_needed += 10 + strlen (register_get_end); /* # REGISTERS-DONE */
|
|
if (buf_needed > *buf_size) {
|
|
free (*buf);
|
|
*buf = (char *)_panel_malloc (buf_needed);
|
|
if (!*buf) {
|
|
panel->State = Error;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return -1;
|
|
}
|
|
*buf_size = buf_needed;
|
|
}
|
|
buf_data = 0;
|
|
if (reg_count) {
|
|
sprintf (*buf + buf_data, "EXECUTE %s;%s;", register_get_start, register_get_prefix);
|
|
buf_data += strlen (*buf + buf_data);
|
|
}
|
|
dev = "";
|
|
for (i=j=0; i<panel->reg_count; i++) {
|
|
const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : "";
|
|
|
|
if ((panel->regs[i].indirect) || (panel->regs[i].bits))
|
|
continue;
|
|
if (strcmp (dev, reg_dev)) {/* devices are different */
|
|
char *tbuf;
|
|
|
|
buf_needed += 4 + strlen (register_dev_echo) + strlen (reg_dev); /* # REGISTERS-for-DEVICE:XXX */
|
|
tbuf = (char *)_panel_malloc (buf_needed);
|
|
if (tbuf == NULL) {
|
|
panel->State = Error;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return -1;
|
|
}
|
|
strcpy (tbuf, *buf);
|
|
free (*buf);
|
|
*buf = tbuf;
|
|
sprintf (*buf + buf_data, "%s%s%s;", (i == 0)? "" : ";", register_dev_echo, reg_dev);
|
|
buf_data += strlen (*buf + buf_data);
|
|
dev = reg_dev;
|
|
j = 0;
|
|
*buf_size = buf_needed;
|
|
}
|
|
if (panel->regs[i].element_count == 0) {
|
|
if (j == 0)
|
|
sprintf (*buf + buf_data, "E -16 %s %s", dev, panel->regs[i].name);
|
|
else
|
|
sprintf (*buf + buf_data, ",%s", panel->regs[i].name);
|
|
}
|
|
else {
|
|
if (j == 0)
|
|
sprintf (*buf + buf_data, "E -16 %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1));
|
|
else
|
|
sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, (int)(panel->regs[i].element_count-1));
|
|
}
|
|
++j;
|
|
buf_data += strlen (*buf + buf_data);
|
|
}
|
|
if (buf_data && ((*buf)[buf_data-1] != ';')) {
|
|
strcpy (*buf + buf_data, ";");
|
|
buf_data += strlen (*buf + buf_data);
|
|
}
|
|
for (i=j=0; i<panel->reg_count; i++) {
|
|
const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : "";
|
|
|
|
if ((!panel->regs[i].indirect) || (panel->regs[i].bits))
|
|
continue;
|
|
sprintf (*buf + buf_data, "%s%s;E -16 %s %s,$;", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name);
|
|
buf_data += strlen (*buf + buf_data);
|
|
}
|
|
if (bit_reg_count) {
|
|
strcpy (*buf + buf_data, register_get_postfix);
|
|
buf_data += strlen (*buf + buf_data);
|
|
strcpy (*buf + buf_data, ";");
|
|
buf_data += strlen (*buf + buf_data);
|
|
}
|
|
strcpy (*buf + buf_data, register_get_end);
|
|
buf_data += strlen (*buf + buf_data);
|
|
strcpy (*buf + buf_data, "\r");
|
|
buf_data += strlen (*buf + buf_data);
|
|
*buf_size = buf_data;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_panel_establish_register_bits_collection (PANEL *panel)
|
|
{
|
|
size_t i, buf_data, buf_needed = 1, reg_count = 0, bit_reg_count = 0;
|
|
int cmd_stat, bits_count = 0;
|
|
char *buf, *response = NULL;
|
|
|
|
pthread_mutex_lock (&panel->io_lock);
|
|
for (i=0; i<panel->reg_count; i++) {
|
|
if (panel->regs[i].bits)
|
|
buf_needed += 9 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0);
|
|
}
|
|
buf = (char *)_panel_malloc (buf_needed);
|
|
if (!buf) {
|
|
panel->State = Error;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return -1;
|
|
}
|
|
*buf = '\0';
|
|
buf_data = 0;
|
|
for (i=0; i<panel->reg_count; i++) {
|
|
if (panel->regs[i].bits) {
|
|
++bits_count;
|
|
sprintf (buf + buf_data, "%s%s", (bits_count != 1) ? "," : "", panel->regs[i].indirect ? "-I " : "");
|
|
buf_data += strlen (buf + buf_data);
|
|
if (panel->regs[i].device_name) {
|
|
sprintf (buf + buf_data, "%s ", panel->regs[i].device_name);
|
|
buf_data += strlen (buf + buf_data);
|
|
}
|
|
sprintf (buf + buf_data, "%s", panel->regs[i].name);
|
|
buf_data += strlen (buf + buf_data);
|
|
}
|
|
}
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "%s%u%s%u%s%u%s%s\r", register_collect_prefix, panel->sample_depth,
|
|
register_collect_mid1, panel->sample_frequency,
|
|
register_collect_mid2, panel->sample_dither_pct,
|
|
register_collect_mid3, buf)) {
|
|
sim_panel_set_error (NULL, "Error establishing bit data collection:%s", response);
|
|
free (response);
|
|
free (buf);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
free (buf);
|
|
return 0;
|
|
}
|
|
|
|
static PANEL **panels = NULL;
|
|
static int panel_count = 0;
|
|
static char *sim_panel_error_buf = NULL;
|
|
static size_t sim_panel_error_bufsize = 0;
|
|
|
|
|
|
static void
|
|
_panel_cleanup (void)
|
|
{
|
|
while (panel_count)
|
|
sim_panel_destroy (*panels);
|
|
}
|
|
|
|
static void
|
|
_panel_register_panel (PANEL *p)
|
|
{
|
|
if (panel_count == 0)
|
|
pthread_key_create (&panel_thread_id, free);
|
|
if (!pthread_getspecific (panel_thread_id))
|
|
pthread_setspecific (panel_thread_id, p->device_name ? p->device_name : "PanelCreator");
|
|
++panel_count;
|
|
panels = (PANEL **)realloc (panels, sizeof(*panels)*panel_count);
|
|
panels[panel_count-1] = p;
|
|
if (panel_count == 1)
|
|
atexit (&_panel_cleanup);
|
|
}
|
|
|
|
static void
|
|
_panel_deregister_panel (PANEL *p)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<panel_count; i++) {
|
|
if (panels[i] == p) {
|
|
int j;
|
|
for (j=i+1; j<panel_count; j++)
|
|
panels[j-1] = panels[j];
|
|
--panel_count;
|
|
if (panel_count == 0) {
|
|
free (panels);
|
|
panels = NULL;
|
|
pthread_setspecific (panel_thread_id, NULL);
|
|
pthread_key_delete (panel_thread_id);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static PANEL *
|
|
_sim_panel_create (const char *sim_path,
|
|
const char *sim_config,
|
|
size_t device_panel_count,
|
|
PANEL *simulator_panel,
|
|
const char *device_name,
|
|
const char *debug_file)
|
|
{
|
|
PANEL *p = NULL;
|
|
FILE *fIn = NULL;
|
|
FILE *fOut = NULL;
|
|
struct stat statb;
|
|
char *buf = NULL;
|
|
int port;
|
|
int cmd_stat;
|
|
size_t i, device_num;
|
|
char hostport[64];
|
|
union {int i; char c[sizeof (int)]; } end_test;
|
|
|
|
if (sim_panel_error_buf == NULL) { /* Preallocate an error message buffer */
|
|
sim_panel_error_bufsize = 2048;
|
|
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize);
|
|
if (sim_panel_error_buf == NULL) {
|
|
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n";
|
|
sim_panel_error_bufsize = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
if (simulator_panel) {
|
|
for (device_num=0; device_num < simulator_panel->device_count; ++device_num)
|
|
if (simulator_panel->devices[device_num] == NULL)
|
|
break;
|
|
if (device_num == simulator_panel->device_count) {
|
|
sim_panel_set_error (NULL, "No free panel devices slots available %s simulator. All %d slots are used.", simulator_panel->path, (int)simulator_panel->device_count);
|
|
return NULL;
|
|
}
|
|
p = (PANEL *)_panel_malloc (sizeof(*p));
|
|
if (p == NULL)
|
|
goto Error_Return;
|
|
memset (p, 0, sizeof(*p));
|
|
_panel_register_panel (p);
|
|
p->device_name = (char *)_panel_malloc (1 + strlen (device_name));
|
|
if (p->device_name == NULL)
|
|
goto Error_Return;
|
|
strcpy (p->device_name, device_name);
|
|
p->parent = simulator_panel;
|
|
p->Debug = p->parent->Debug;
|
|
strcpy (p->hostport, simulator_panel->hostport);
|
|
p->sock = INVALID_SOCKET;
|
|
}
|
|
else {
|
|
end_test.i = 1; /* test endian-ness */
|
|
little_endian = (end_test.c[0] != 0);
|
|
sim_init_sock ();
|
|
for (port=1024; port < 2048; port++) {
|
|
SOCKET sock;
|
|
|
|
sprintf (hostport, "%d", port);
|
|
sock = sim_connect_sock_ex (NULL, hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING);
|
|
if (sock != INVALID_SOCKET) {
|
|
int sta = 0;
|
|
while (!sta) {
|
|
msleep (10);
|
|
sta = sim_check_conn (sock, 1);
|
|
}
|
|
sim_close_sock (sock);
|
|
if (sta == -1)
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
if (stat (sim_config, &statb) < 0) {
|
|
sim_panel_set_error (NULL, "Can't stat simulator configuration '%s': %s", sim_config, strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
buf = (char *)_panel_malloc (statb.st_size+1);
|
|
if (buf == NULL)
|
|
goto Error_Return;
|
|
buf[statb.st_size] = '\0';
|
|
p = (PANEL *)_panel_malloc (sizeof(*p));
|
|
if (p == NULL)
|
|
goto Error_Return;
|
|
memset (p, 0, sizeof(*p));
|
|
_panel_register_panel (p);
|
|
p->sock = INVALID_SOCKET;
|
|
p->path = (char *)_panel_malloc (strlen (sim_path) + 1);
|
|
if (p->path == NULL)
|
|
goto Error_Return;
|
|
strcpy (p->path, sim_path);
|
|
p->config = (char *)_panel_malloc (strlen (sim_config) + 1);
|
|
if (p->config == NULL)
|
|
goto Error_Return;
|
|
strcpy (p->config, sim_config);
|
|
fIn = fopen (sim_config, "r");
|
|
if (fIn == NULL) {
|
|
sim_panel_set_error (NULL, "Can't open configuration file '%s': %s", sim_config, strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
p->temp_config = (char *)_panel_malloc (strlen (sim_config) + 40);
|
|
if (p->temp_config == NULL)
|
|
goto Error_Return;
|
|
sprintf (p->temp_config, "%s-Panel-%d", sim_config, getpid());
|
|
fOut = fopen (p->temp_config, "w");
|
|
if (fOut == NULL) {
|
|
sim_panel_set_error (NULL, "Can't create temporary configuration file '%s': %s", p->temp_config, strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
fprintf (fOut, "# Temporary FrontPanel generated simh configuration file\n");
|
|
fprintf (fOut, "# Original Configuration File: %s\n", p->config);
|
|
fprintf (fOut, "# Simulator Path: %s\n", sim_path);
|
|
while (fgets (buf, statb.st_size, fIn))
|
|
fputs (buf, fOut);
|
|
free (buf);
|
|
buf = NULL;
|
|
fclose (fIn);
|
|
fIn = NULL;
|
|
fprintf (fOut, "set remote notelnet\n");
|
|
if (device_panel_count)
|
|
fprintf (fOut, "set remote connections=%d\n", (int)device_panel_count+1);
|
|
fprintf (fOut, "set remote -u telnet=%s\n", hostport);
|
|
fprintf (fOut, "set remote master\n");
|
|
fprintf (fOut, "exit\n");
|
|
fclose (fOut);
|
|
fOut = NULL;
|
|
}
|
|
if (debug_file) {
|
|
_set_debug_file (p, debug_file);
|
|
sim_panel_set_debug_mode (p, DBG_XMT|DBG_RCV);
|
|
_panel_debug (p, DBG_XMT|DBG_RCV, "Creating Simulator Process %s\n", NULL, 0, sim_path);
|
|
|
|
if (stat (p->temp_config, &statb) < 0) {
|
|
sim_panel_set_error (NULL, "Can't stat temporary simulator configuration '%s': %s", p->temp_config, strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
buf = (char *)_panel_malloc (statb.st_size+1);
|
|
if (buf == NULL)
|
|
goto Error_Return;
|
|
buf[statb.st_size] = '\0';
|
|
fIn = fopen (p->temp_config, "r");
|
|
if (fIn == NULL) {
|
|
sim_panel_set_error (NULL, "Can't open temporary configuration file '%s': %s", p->temp_config, strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
_panel_debug (p, DBG_XMT|DBG_RCV, "Using Temporary Configuration File '%s' containing:", NULL, 0, p->temp_config);
|
|
i = 0;
|
|
while (fgets (buf, statb.st_size, fIn)) {
|
|
++i;
|
|
buf[strlen(buf) - 1] = '\0';
|
|
_panel_debug (p, DBG_XMT|DBG_RCV, "Line %2d: %s", NULL, 0, (int)i, buf);
|
|
}
|
|
free (buf);
|
|
buf = NULL;
|
|
fclose (fIn);
|
|
fIn = NULL;
|
|
}
|
|
if (!simulator_panel) {
|
|
#if defined(_WIN32)
|
|
char cmd[2048];
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
STARTUPINFO StartupInfo;
|
|
|
|
sprintf(cmd, "%s%s%s %s%s%s", strchr (sim_path, ' ') ? "\"" : "", sim_path, strchr (sim_path, ' ') ? "\"" : "", strchr (p->temp_config, ' ') ? "\"" : "", p->temp_config, strchr (p->temp_config, ' ') ? "\"" : "");
|
|
|
|
memset (&ProcessInfo, 0, sizeof(ProcessInfo));
|
|
memset (&StartupInfo, 0, sizeof(StartupInfo));
|
|
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
|
StartupInfo.hStdInput = INVALID_HANDLE_VALUE;
|
|
StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
|
|
StartupInfo.hStdError = INVALID_HANDLE_VALUE;
|
|
if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, &ProcessInfo)) {
|
|
CloseHandle (ProcessInfo.hThread);
|
|
p->hProcess = ProcessInfo.hProcess;
|
|
p->dwProcessId = ProcessInfo.dwProcessId;
|
|
}
|
|
else { /* Creation Problem */
|
|
sim_panel_set_error (NULL, "CreateProcess Error: %d", GetLastError());
|
|
goto Error_Return;
|
|
}
|
|
#else
|
|
p->pidProcess = fork();
|
|
if (p->pidProcess == 0) {
|
|
close (0); close (1); close (2); /* make sure not to pass the open standard handles */
|
|
if (dup (dup (open ("/dev/null", O_RDWR)))) {}; /* open standard handles to /dev/null */
|
|
if (execlp (sim_path, sim_path, p->temp_config, NULL, NULL)) {
|
|
perror ("execl");
|
|
exit(errno);
|
|
}
|
|
}
|
|
if (p->pidProcess < 0) {
|
|
p->pidProcess = 0;
|
|
sim_panel_set_error (NULL, "fork() Error: %s", strerror(errno));
|
|
goto Error_Return;
|
|
}
|
|
#endif
|
|
strcpy (p->hostport, hostport);
|
|
}
|
|
for (i=0; i<100; i++) { /* Allow up to 10 seconds waiting for simulator to start up */
|
|
p->sock = sim_connect_sock_ex (NULL, p->hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING);
|
|
if (p->sock == INVALID_SOCKET)
|
|
msleep (100);
|
|
else
|
|
break;
|
|
}
|
|
if (p->sock == INVALID_SOCKET) {
|
|
if (simulator_panel) {
|
|
sim_panel_set_error (NULL, "Can't connect to simulator Remote Console on port %s", p->hostport);
|
|
}
|
|
else {
|
|
if (stat (sim_path, &statb) < 0)
|
|
sim_panel_set_error (NULL, "Can't stat simulator '%s': %s", sim_path, strerror(errno));
|
|
else
|
|
sim_panel_set_error (NULL, "Can't connect to the %s simulator Remote Console on port %s, the simulator process may not have started or the simulator binary can't be found", sim_path, p->hostport);
|
|
}
|
|
goto Error_Return;
|
|
}
|
|
_panel_debug (p, DBG_XMT|DBG_RCV, "Connected to simulator on %s after %dms", NULL, 0, p->hostport, (int)i*100);
|
|
pthread_mutex_init (&p->io_lock, NULL);
|
|
pthread_mutex_init (&p->io_send_lock, NULL);
|
|
pthread_mutex_init (&p->io_command_lock, NULL);
|
|
pthread_cond_init (&p->io_done, NULL);
|
|
pthread_cond_init (&p->startup_done, NULL);
|
|
if (sizeof(mantra) != _panel_send (p, (char *)mantra, sizeof(mantra))) {
|
|
sim_panel_set_error (NULL, "Error sending Telnet mantra (options): %s", sim_get_err_sock ("send"));
|
|
goto Error_Return;
|
|
}
|
|
if (1) {
|
|
pthread_attr_t attr;
|
|
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
p->io_thread_running = 0;
|
|
pthread_create (&p->io_thread, &attr, _panel_reader, (void *)p);
|
|
while (!p->io_thread_running)
|
|
pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */
|
|
if ((p->Debug) && (p->parent == NULL)) {
|
|
p->debugflush_thread_running = 0;
|
|
pthread_create (&p->debugflush_thread, &attr, _panel_debugflusher, (void *)p);
|
|
while (!p->debugflush_thread_running)
|
|
pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */
|
|
}
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
pthread_attr_destroy(&attr);
|
|
pthread_cond_destroy (&p->startup_done);
|
|
}
|
|
if (simulator_panel) {
|
|
simulator_panel->devices[device_num] = p;
|
|
}
|
|
else {
|
|
if (device_panel_count) {
|
|
p->devices = (PANEL **)_panel_malloc (device_panel_count*sizeof(*p->devices));
|
|
if (p->devices == NULL)
|
|
goto Error_Return;
|
|
memset (p->devices, 0, device_panel_count*sizeof(*p->devices));
|
|
p->device_count = device_panel_count;
|
|
}
|
|
if (p->State == Error)
|
|
goto Error_Return;
|
|
/* Validate sim_frontpanel API version */
|
|
if (_panel_sendf (p, &cmd_stat, &p->simulator_version, "SHOW VERSION\r"))
|
|
goto Error_Return;
|
|
if (1) {
|
|
int api_version = 0;
|
|
char *c = strstr (p->simulator_version, "FrontPanel API Version");
|
|
|
|
if ((!c) ||
|
|
(1 != sscanf (c, "FrontPanel API Version %d", &api_version)) ||
|
|
(api_version != SIM_FRONTPANEL_VERSION)) {
|
|
sim_panel_set_error (NULL, "Inconsistent sim_frontpanel API version %d in simulator. Version %d needed.-", api_version, SIM_FRONTPANEL_VERSION);
|
|
goto Error_Return;
|
|
}
|
|
}
|
|
if (1) {
|
|
char *radix = NULL;
|
|
|
|
if (_panel_sendf (p, &cmd_stat, &radix, "SHOW %s RADIX\r", p->device_name ? p->device_name : "")) {
|
|
free (radix);
|
|
goto Error_Return;
|
|
}
|
|
sscanf (radix, "Radix=%d", &p->radix);
|
|
free (radix);
|
|
if ((p->radix != 16) && (p->radix != 8)) {
|
|
sim_panel_set_error (NULL, "Unsupported Radix: %d%s%s.", p->radix, p->device_name ? " on device " : "", p->device_name ? p->device_name : "");
|
|
goto Error_Return;
|
|
}
|
|
}
|
|
}
|
|
return p;
|
|
|
|
Error_Return:
|
|
if (fIn)
|
|
fclose (fIn);
|
|
if (fOut) {
|
|
fclose (fOut);
|
|
(void)remove (p->temp_config);
|
|
}
|
|
if (buf)
|
|
free (buf);
|
|
if (1) {
|
|
const char *err = sim_panel_get_error();
|
|
char *errbuf = (char *)_panel_malloc (1 + strlen (err));
|
|
|
|
strcpy (errbuf, err); /* preserve error info while closing */
|
|
sim_panel_destroy (p);
|
|
sim_panel_set_error (NULL, "%s", errbuf);
|
|
free (errbuf);
|
|
}
|
|
if (!simulator_panel)
|
|
sim_cleanup_sock();
|
|
return NULL;
|
|
}
|
|
|
|
PANEL *
|
|
sim_panel_start_simulator_debug (const char *sim_path,
|
|
const char *sim_config,
|
|
size_t device_panel_count,
|
|
const char *debug_file)
|
|
{
|
|
return _sim_panel_create (sim_path, sim_config, device_panel_count, NULL, NULL, debug_file);
|
|
}
|
|
|
|
PANEL *
|
|
sim_panel_start_simulator (const char *sim_path,
|
|
const char *sim_config,
|
|
size_t device_panel_count)
|
|
{
|
|
return sim_panel_start_simulator_debug (sim_path, sim_config, device_panel_count, NULL);
|
|
}
|
|
|
|
PANEL *
|
|
sim_panel_add_device_panel_debug (PANEL *simulator_panel,
|
|
const char *device_name,
|
|
const char *debug_file)
|
|
{
|
|
return _sim_panel_create (NULL, NULL, 0, simulator_panel, device_name, debug_file);
|
|
}
|
|
|
|
PANEL *
|
|
sim_panel_add_device_panel (PANEL *simulator_panel,
|
|
const char *device_name)
|
|
{
|
|
return sim_panel_add_device_panel_debug (simulator_panel, device_name, NULL);
|
|
}
|
|
|
|
int
|
|
sim_panel_destroy (PANEL *panel)
|
|
{
|
|
REG *reg;
|
|
|
|
if (panel) {
|
|
_panel_debug (panel, DBG_XMT|DBG_RCV, "Closing Panel %s", NULL, 0, panel->device_name? panel->device_name : panel->path);
|
|
if (panel->devices) {
|
|
size_t i;
|
|
|
|
for (i=0; i<panel->device_count; i++) {
|
|
if (panel->devices[i]) {
|
|
sim_panel_destroy (panel->devices[i]);
|
|
panel->devices[i] = NULL;
|
|
}
|
|
}
|
|
free (panel->devices);
|
|
panel->devices = NULL;
|
|
}
|
|
|
|
if (panel->sock != INVALID_SOCKET) {
|
|
SOCKET sock = panel->sock;
|
|
int wait_count;
|
|
|
|
/* First, wind down the automatic register queries */
|
|
sim_panel_set_display_callback_interval (panel, NULL, NULL, 0);
|
|
/* Next, attempt a simulator shutdown only with the master panel */
|
|
if (panel->parent == NULL) {
|
|
if (panel->State == Run)
|
|
sim_panel_exec_halt (panel);
|
|
_panel_send (panel, "EXIT\r", 5);
|
|
}
|
|
/* Wait for up to 2 seconds for a graceful shutdown */
|
|
panel->sock = INVALID_SOCKET;
|
|
for (wait_count=0; panel->io_thread_running && (wait_count<20); ++wait_count)
|
|
msleep (100);
|
|
/* Now close the socket which should stop a pending read that hasn't completed */
|
|
sim_close_sock (sock);
|
|
pthread_join (panel->io_thread, NULL);
|
|
}
|
|
if ((panel->Debug) && (panel->parent == NULL))
|
|
pthread_join (panel->debugflush_thread, NULL);
|
|
pthread_mutex_destroy (&panel->io_lock);
|
|
pthread_mutex_destroy (&panel->io_send_lock);
|
|
pthread_mutex_destroy (&panel->io_command_lock);
|
|
pthread_cond_destroy (&panel->io_done);
|
|
#if defined(_WIN32)
|
|
if (panel->hProcess) {
|
|
GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, panel->dwProcessId);
|
|
msleep (200);
|
|
TerminateProcess (panel->hProcess, 0);
|
|
WaitForSingleObject (panel->hProcess, INFINITE);
|
|
CloseHandle (panel->hProcess);
|
|
}
|
|
#else
|
|
if (panel->pidProcess) {
|
|
int status;
|
|
|
|
if (!kill (panel->pidProcess, 0)) {
|
|
kill (panel->pidProcess, SIGTERM);
|
|
msleep (200);
|
|
if (!kill (panel->pidProcess, 0))
|
|
kill (panel->pidProcess, SIGKILL);
|
|
}
|
|
waitpid (panel->pidProcess, &status, 0);
|
|
}
|
|
#endif
|
|
free (panel->path);
|
|
free (panel->device_name);
|
|
free (panel->config);
|
|
if (panel->temp_config)
|
|
(void)remove (panel->temp_config);
|
|
free (panel->temp_config);
|
|
reg = panel->regs;
|
|
while (panel->reg_count--) {
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
reg++;
|
|
}
|
|
free (panel->regs);
|
|
free (panel->reg_query);
|
|
free (panel->io_response);
|
|
free (panel->halt_reason);
|
|
free (panel->simulator_version);
|
|
if ((panel->Debug) && (!panel->parent))
|
|
fclose (panel->Debug);
|
|
if (!panel->parent)
|
|
sim_cleanup_sock ();
|
|
_panel_deregister_panel (panel);
|
|
free (panel);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
OperationalState
|
|
sim_panel_get_state (PANEL *panel)
|
|
{
|
|
if (!panel)
|
|
return Halt;
|
|
return panel->State;
|
|
}
|
|
|
|
static int
|
|
_panel_add_register (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t size,
|
|
void *addr,
|
|
int indirect,
|
|
size_t element_count,
|
|
int *bits,
|
|
size_t bit_count)
|
|
{
|
|
REG *regs, *reg;
|
|
char *response = NULL, *c;
|
|
unsigned long long data;
|
|
size_t i;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if ((bit_count != 0) && (panel->sample_depth == 0)) {
|
|
sim_panel_set_error (NULL, "sim_panel_set_sampling_parameters() must be called first");
|
|
return -1;
|
|
}
|
|
regs = (REG *)_panel_malloc ((1 + panel->reg_count)*sizeof(*regs));
|
|
if (regs == NULL)
|
|
return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n");
|
|
pthread_mutex_lock (&panel->io_lock);
|
|
memcpy (regs, panel->regs, panel->reg_count*sizeof(*regs));
|
|
reg = ®s[panel->reg_count];
|
|
memset (reg, 0, sizeof(*regs));
|
|
reg->name = (char *)_panel_malloc (1 + strlen (name));
|
|
if (reg->name == NULL) {
|
|
panel->State = Error;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n");
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
strcpy (reg->name, name);
|
|
reg->indirect = indirect;
|
|
reg->addr = addr;
|
|
reg->size = size;
|
|
reg->element_count = element_count;
|
|
reg->bits = bits;
|
|
reg->bit_count = bit_count;
|
|
for (i=0; i<strlen (reg->name); i++) {
|
|
if (islower (reg->name[i]))
|
|
reg->name[i] = toupper (reg->name[i]);
|
|
}
|
|
if (device_name) {
|
|
reg->device_name = (char *)_panel_malloc (1 + strlen (device_name));
|
|
if (reg->device_name == NULL) {
|
|
free (reg->name);
|
|
free (regs);
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n");
|
|
}
|
|
strcpy (reg->device_name, device_name);
|
|
for (i=0; i<strlen (reg->device_name); i++) {
|
|
if (islower (reg->device_name[i]))
|
|
reg->device_name[i] = toupper (reg->device_name[i]);
|
|
}
|
|
}
|
|
for (i=0; i<panel->reg_count; i++) {
|
|
char *t1 = (char *)_panel_malloc (2 + strlen (regs[i].name) + (regs[i].device_name? strlen (regs[i].device_name) : 0));
|
|
char *t2 = (char *)_panel_malloc (2 + strlen (reg->name) + (reg->device_name? strlen (reg->device_name) : 0));
|
|
|
|
if ((t1 == NULL) || (t2 == NULL)) {
|
|
free (t1);
|
|
free (t2);
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
panel->State = Error;
|
|
free (regs);
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n");
|
|
}
|
|
sprintf (t1, "%s %s", regs[i].device_name ? regs[i].device_name : "", regs[i].name);
|
|
sprintf (t2, "%s %s", reg->device_name ? reg->device_name : "", reg->name);
|
|
if ((!strcmp (t1, t2)) &&
|
|
(reg->indirect == regs[i].indirect) &&
|
|
((reg->bits == NULL) == (regs[i].bits == NULL))) {
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
sim_panel_set_error (NULL, "Duplicate Register Declaration");
|
|
free (t1);
|
|
free (t2);
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
free (t1);
|
|
free (t2);
|
|
}
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
/* Validate existence of requested register/array */
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s %s%s\r", device_name? device_name : "", name, (element_count > 0) ? "[0]" : "")) {
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
c = strchr (response, ':');
|
|
if ((!strcmp ("Invalid argument\r\n", response)) || (!c)) {
|
|
sim_panel_set_error (NULL, "Invalid Register: %s %s", device_name? device_name : "", name);
|
|
free (response);
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
data = strtoull (c + 1, NULL, 16);
|
|
free (response);
|
|
if (element_count > 0) {
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE %s %s[%d]\r", device_name? device_name : "", name, element_count-1)) {
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
if (!strcmp ("Subscript out of range\r\n", response)) {
|
|
sim_panel_set_error (NULL, "Invalid Register Array Dimension: %s %s[%d]", device_name? device_name : "", name, element_count-1);
|
|
free (response);
|
|
free (reg->name);
|
|
free (reg->device_name);
|
|
free (regs);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
}
|
|
pthread_mutex_lock (&panel->io_lock);
|
|
++panel->reg_count;
|
|
free (panel->regs);
|
|
panel->regs = regs;
|
|
panel->new_register = 1;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
/* Now build the register query string for the whole register list */
|
|
if (_panel_register_query_string (panel, &panel->reg_query, &panel->reg_query_size))
|
|
return -1;
|
|
if (bits) {
|
|
for (i=0; i<bit_count; i++)
|
|
bits[i] = (data & (1LL<<i)) ? panel->sample_depth : 0;
|
|
if (_panel_establish_register_bits_collection (panel))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_add_register (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t size,
|
|
void *addr)
|
|
{
|
|
return _panel_add_register (panel, name, device_name, size, addr, 0, 0, NULL, 0);
|
|
}
|
|
|
|
int
|
|
sim_panel_add_register_bits (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t bit_width,
|
|
int *bits)
|
|
{
|
|
return _panel_add_register (panel, name, device_name, 0, NULL, 0, 0, bits, bit_width);
|
|
}
|
|
|
|
int
|
|
sim_panel_add_register_array (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t element_count,
|
|
size_t size,
|
|
void *addr)
|
|
{
|
|
return _panel_add_register (panel, name, device_name, size, addr, 0, element_count, NULL, 0);
|
|
}
|
|
|
|
|
|
int
|
|
sim_panel_add_register_indirect (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t size,
|
|
void *addr)
|
|
{
|
|
return _panel_add_register (panel, name, device_name, size, addr, 1, 0, NULL, 0);
|
|
}
|
|
|
|
int
|
|
sim_panel_add_register_indirect_bits (PANEL *panel,
|
|
const char *name,
|
|
const char *device_name,
|
|
size_t bit_width,
|
|
int *bits)
|
|
{
|
|
return _panel_add_register (panel, name, device_name, 0, NULL, 1, 0, bits, bit_width);
|
|
}
|
|
|
|
static int
|
|
_panel_get_registers (PANEL *panel, int calledback, unsigned long long *simulation_time)
|
|
{
|
|
if ((!panel) || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if ((!calledback) && (panel->callback)) {
|
|
sim_panel_set_error (NULL, "Callback provides register data");
|
|
return -1;
|
|
}
|
|
if (!panel->reg_count) {
|
|
sim_panel_set_error (NULL, "No registers specified");
|
|
return -1;
|
|
}
|
|
pthread_mutex_lock (&panel->io_command_lock);
|
|
pthread_mutex_lock (&panel->io_lock);
|
|
if (panel->reg_query_size != _panel_send (panel, panel->reg_query, panel->reg_query_size)) {
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
pthread_mutex_unlock (&panel->io_command_lock);
|
|
return -1;
|
|
}
|
|
if (panel->io_response_data)
|
|
_panel_debug (panel, DBG_RCV, "Receive Data Discarded: ", panel->io_response, panel->io_response_data);
|
|
panel->io_response_data = 0;
|
|
panel->io_waiting = 1;
|
|
while (panel->io_waiting)
|
|
pthread_cond_wait (&panel->io_done, &panel->io_lock);
|
|
if (simulation_time)
|
|
*simulation_time = panel->simulation_time;
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
pthread_mutex_unlock (&panel->io_command_lock);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time)
|
|
{
|
|
return _panel_get_registers (panel, (panel->State == Halt), simulation_time);
|
|
}
|
|
|
|
int
|
|
sim_panel_set_display_callback_interval (PANEL *panel,
|
|
PANEL_DISPLAY_PCALLBACK callback,
|
|
void *context,
|
|
int usecs_between_callbacks)
|
|
{
|
|
if (!panel) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
pthread_mutex_lock (&panel->io_lock); /* acquire access */
|
|
panel->callback = callback;
|
|
panel->callback_context = context;
|
|
if (usecs_between_callbacks && (0 == panel->usecs_between_callbacks)) { /* Need to start/enable callbacks */
|
|
pthread_attr_t attr;
|
|
|
|
_panel_debug (panel, DBG_THR, "Starting callback thread, Interval: %d usecs", NULL, 0, usecs_between_callbacks);
|
|
panel->usecs_between_callbacks = usecs_between_callbacks;
|
|
pthread_cond_init (&panel->startup_done, NULL);
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
|
|
pthread_create (&panel->callback_thread, &attr, _panel_callback, (void *)panel);
|
|
pthread_attr_destroy(&attr);
|
|
while (!panel->callback_thread_running)
|
|
pthread_cond_wait (&panel->startup_done, &panel->io_lock); /* Wait for thread to stabilize */
|
|
pthread_cond_destroy (&panel->startup_done);
|
|
}
|
|
if ((usecs_between_callbacks == 0) && panel->usecs_between_callbacks) { /* Need to stop callbacks */
|
|
_panel_debug (panel, DBG_THR, "Shutting down callback thread", NULL, 0);
|
|
panel->usecs_between_callbacks = 0; /* flag disabled */
|
|
pthread_mutex_unlock (&panel->io_lock); /* allow access */
|
|
pthread_join (panel->callback_thread, NULL); /* synchronize with thread rundown */
|
|
pthread_mutex_lock (&panel->io_lock); /* reacquire access */
|
|
}
|
|
pthread_mutex_unlock (&panel->io_lock);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_set_sampling_parameters_ex (PANEL *panel,
|
|
unsigned int sample_frequency,
|
|
unsigned int sample_dither_pct,
|
|
unsigned int sample_depth)
|
|
{
|
|
if (sample_frequency == 0) {
|
|
sim_panel_set_error (NULL, "Invalid sample frequency value: %u", sample_frequency);
|
|
return -1;
|
|
}
|
|
if (sample_dither_pct > 25) {
|
|
sim_panel_set_error (NULL, "Invalid sample dither percentage value: %u", sample_dither_pct);
|
|
return -1;
|
|
}
|
|
if (sample_depth == 0) {
|
|
sim_panel_set_error (NULL, "Invalid sample depth value: %u", sample_depth);
|
|
return -1;
|
|
}
|
|
panel->sample_frequency = sample_frequency;
|
|
panel->sample_dither_pct = sample_dither_pct;
|
|
panel->sample_depth = sample_depth;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_set_sampling_parameters (PANEL *panel,
|
|
unsigned int sample_frequency,
|
|
unsigned int sample_depth)
|
|
{
|
|
return sim_panel_set_sampling_parameters_ex (panel,
|
|
sample_frequency,
|
|
5,
|
|
sample_depth);
|
|
}
|
|
|
|
int
|
|
sim_panel_exec_halt (PANEL *panel)
|
|
{
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't HALT simulator from device front panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
if (_panel_sendf_completion (panel, NULL, sim_prompt, "\005")) {
|
|
_panel_debug (panel, DBG_THR, "Error trying to HALT running simulator: %s", NULL, 0, sim_panel_get_error ());
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
_panel_debug (panel, DBG_THR, "Unable to HALT running simulator", NULL, 0);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
sim_panel_halt_text (PANEL *panel)
|
|
{
|
|
if (!panel || !panel->halt_reason)
|
|
return "";
|
|
return panel->halt_reason;
|
|
}
|
|
|
|
|
|
int
|
|
sim_panel_exec_boot (PANEL *panel, const char *device)
|
|
{
|
|
int cmd_stat;
|
|
char *response, *simtime;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't BOOT simulator from device front panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
/* A BOOT or RUN command will restart the simulator's time base. */
|
|
/* We account for that so that the frontpanel application sees ever */
|
|
/* increasing time values when register data is delivered. */
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r"))
|
|
return -1;
|
|
if ((simtime = strstr (response, "Time:"))) {
|
|
panel->simulation_time = strtoull (simtime + 5, NULL, 10);
|
|
panel->simulation_time_base += panel->simulation_time;
|
|
}
|
|
free (response);
|
|
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "BOOT %s\r", device)) {
|
|
_panel_debug (panel, DBG_THR, "Unable to BOOT simulator: %s", NULL, 0, sim_panel_get_error());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_exec_start (PANEL *panel)
|
|
{
|
|
int cmd_stat;
|
|
char *response, *simtime;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't RUN simulator from device front panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
/* A BOOT or RUN command will restart the simulator's time base. */
|
|
/* We account for that so that the frontpanel application sees ever */
|
|
/* increasing time values when register data is delivered. */
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r")) {
|
|
_panel_debug (panel, DBG_THR, "Unable to send SHOW TIME command while starting simulator: %s", NULL, 0, sim_panel_get_error());
|
|
return -1;
|
|
}
|
|
if ((simtime = strstr (response, "Time:"))) {
|
|
panel->simulation_time = strtoull (simtime + 5, NULL, 10);
|
|
panel->simulation_time_base += panel->simulation_time;
|
|
}
|
|
free (response);
|
|
panel->simulation_time_base += panel->simulation_time;
|
|
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN\r", 5)) {
|
|
_panel_debug (panel, DBG_THR, "Unable to start simulator: %s", NULL, 0, sim_panel_get_error());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_exec_run (PANEL *panel)
|
|
{
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't CONT simulator from device front panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "CONT\r"))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_exec_step (PANEL *panel)
|
|
{
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't STEP simulator from device front panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (_panel_sendf_completion (panel, NULL, sim_prompt, "STEP")) {
|
|
_panel_debug (panel, DBG_THR, "Error trying to STEP running simulator: %s", NULL, 0, sim_panel_get_error ());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_break_set (PANEL *panel, const char *condition)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't establish a breakpoint from device front panel");
|
|
return -1;
|
|
}
|
|
if ((_panel_sendf (panel, &cmd_stat, &response, "BREAK %s\r", condition)) ||
|
|
(*response)) {
|
|
sim_panel_set_error (NULL, "Error establishing breakpoint at '%s': %s", condition, response ? response : "");
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_break_clear (PANEL *panel, const char *condition)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't clear a breakpoint from device front panel");
|
|
return -1;
|
|
}
|
|
if ((_panel_sendf (panel, &cmd_stat, &response, "NOBREAK %s\r", condition)) ||
|
|
(*response)) {
|
|
sim_panel_set_error (NULL, "Error clearing breakpoint at '%s': %s", condition, response ? response : "");
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_break_output_set (PANEL *panel, const char *condition)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't establish an output breakpoint from device front panel");
|
|
return -1;
|
|
}
|
|
if ((_panel_sendf (panel, &cmd_stat, &response, "EXPECT %s\r", condition)) ||
|
|
(*response)) {
|
|
sim_panel_set_error (NULL, "Error establishing output breakpoint for '%s': %s", condition, response ? response : "");
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_break_output_clear (PANEL *panel, const char *condition)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->parent) {
|
|
sim_panel_set_error (NULL, "Can't clear an output breakpoint from device front panel");
|
|
return -1;
|
|
}
|
|
if ((_panel_sendf (panel, &cmd_stat, &response, "NOEXPECT %s\r", condition)) ||
|
|
(*response)) {
|
|
sim_panel_set_error (NULL, "Error clearing output breakpoint for '%s': %s", condition, response ? response : "");
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_gen_examine
|
|
|
|
name_or_addr the name the simulator knows this register by
|
|
size the size (in local storage) of the buffer which will
|
|
receive the data returned when examining the simulator
|
|
value a pointer to the buffer which will be loaded with the
|
|
data returned when examining the simulator
|
|
*/
|
|
|
|
int
|
|
sim_panel_gen_examine (PANEL *panel,
|
|
const char *name_or_addr,
|
|
size_t size,
|
|
void *value)
|
|
{
|
|
char *response = NULL, *c;
|
|
unsigned long long data = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s", name_or_addr)) {
|
|
free (response);
|
|
return -1;
|
|
}
|
|
c = strchr (response, ':');
|
|
if (!c) {
|
|
sim_panel_set_error (NULL, "response: %s", response);
|
|
free (response);
|
|
return -1;
|
|
}
|
|
data = strtoull (c + 1, NULL, 16);
|
|
if (little_endian)
|
|
memcpy (value, &data, size);
|
|
else
|
|
memcpy (value, ((char *)&data) + sizeof(data)-size, size);
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_get_history
|
|
|
|
count the number of instructions to return
|
|
size the size (in local storage) of the buffer which will
|
|
receive the data returned when examining the simulator
|
|
buffer a pointer to the buffer which will be loaded with the
|
|
instruction history returned from the simulator
|
|
*/
|
|
|
|
int
|
|
sim_panel_get_history (PANEL *panel,
|
|
int count,
|
|
size_t size,
|
|
char *buffer)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW HISTORY=%d", count)) {
|
|
free (response);
|
|
return -1;
|
|
}
|
|
strncpy (buffer, response, size);
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_panel_device_debug_mode (PANEL *panel,
|
|
const char *device,
|
|
int set_unset,
|
|
const char *mode_bits)
|
|
{
|
|
char *response = NULL;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if ((device != NULL) &&
|
|
((_panel_sendf (panel, &cmd_stat, &response, "SHOW %s", device) ||
|
|
(cmd_stat)))) {
|
|
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s",
|
|
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response);
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
response = NULL;
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "%sDEBUG %s %s",
|
|
set_unset ? "" : "NO", device ? device : "", mode_bits ? mode_bits : "") ||
|
|
(cmd_stat)) {
|
|
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s",
|
|
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response);
|
|
free (response);
|
|
return -1;
|
|
}
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_gen_deposit
|
|
|
|
name_or_addr the name the simulator knows this register by
|
|
size the size (in local storage) of the buffer which
|
|
contains the data to be deposited into the simulator
|
|
value a pointer to the buffer which contains the data to
|
|
be deposited into the simulator
|
|
*/
|
|
|
|
int
|
|
sim_panel_gen_deposit (PANEL *panel,
|
|
const char *name_or_addr,
|
|
size_t size,
|
|
const void *value)
|
|
{
|
|
unsigned long long data = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (little_endian)
|
|
memcpy (&data, value, size);
|
|
else
|
|
memcpy (((char *)&data) + sizeof(data)-size, value, size);
|
|
if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT -H %s %llx", name_or_addr, data))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_mem_examine
|
|
|
|
addr_size the size (in local storage) of the buffer which
|
|
contains the memory address of the data to be examined
|
|
in the simulator
|
|
addr a pointer to the buffer containing the memory address
|
|
of the data to be examined in the simulator
|
|
value_size the size (in local storage) of the buffer which will
|
|
receive the data returned when examining the simulator
|
|
value a pointer to the buffer which will be loaded with the
|
|
data returned when examining the simulator
|
|
*/
|
|
|
|
int
|
|
sim_panel_mem_examine (PANEL *panel,
|
|
size_t addr_size,
|
|
const void *addr,
|
|
size_t value_size,
|
|
void *value)
|
|
{
|
|
char *response = NULL, *c;
|
|
unsigned long long data = 0, address = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (little_endian)
|
|
memcpy (&address, addr, addr_size);
|
|
else
|
|
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size);
|
|
if (_panel_sendf (panel, &cmd_stat, &response, (panel->radix == 16) ? "EXAMINE -H %llx" : "EXAMINE -H %llo", address)) {
|
|
free (response);
|
|
return -1;
|
|
}
|
|
c = strchr (response, ':');
|
|
if (!c) {
|
|
sim_panel_set_error (NULL, "%s", response);
|
|
free (response);
|
|
return -1;
|
|
}
|
|
data = strtoull (c + 1, NULL, 16);
|
|
if (little_endian)
|
|
memcpy (value, &data, value_size);
|
|
else
|
|
memcpy (value, ((char *)&data) + sizeof(data)-value_size, value_size);
|
|
free (response);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_mem_deposit
|
|
|
|
addr_size the size (in local storage) of the buffer which
|
|
contains the memory address of the data to be deposited
|
|
into the simulator
|
|
addr a pointer to the buffer containing the memory address
|
|
of the data to be deposited into the simulator
|
|
value_size the size (in local storage) of the buffer which will
|
|
contains the data to be deposited into the simulator
|
|
value a pointer to the buffer which contains the data to be
|
|
deposited into the simulator
|
|
*/
|
|
|
|
int
|
|
sim_panel_mem_deposit (PANEL *panel,
|
|
size_t addr_size,
|
|
const void *addr,
|
|
size_t value_size,
|
|
const void *value)
|
|
{
|
|
unsigned long long data = 0, address = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (little_endian) {
|
|
memcpy (&data, value, value_size);
|
|
memcpy (&address, addr, addr_size);
|
|
}
|
|
else {
|
|
memcpy (((char *)&data) + sizeof(data)-value_size, value, value_size);
|
|
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size);
|
|
}
|
|
if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %llx" : "DEPOSIT -H %llo %llx", address, data))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
sim_panel_mem_deposit_instruction
|
|
|
|
addr_size the size (in local storage) of the buffer which
|
|
contains the memory address of the data to be deposited
|
|
into the simulator
|
|
addr a pointer to the buffer containing the memory address
|
|
of the data to be deposited into the simulator
|
|
instruction a pointer to the buffer that contains the mnemonic
|
|
instruction to be deposited at the indicated address
|
|
*/
|
|
|
|
int
|
|
sim_panel_mem_deposit_instruction (PANEL *panel,
|
|
size_t addr_size,
|
|
const void *addr,
|
|
const char *instruction)
|
|
{
|
|
unsigned long long address = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (little_endian)
|
|
memcpy (&address, addr, addr_size);
|
|
else
|
|
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size);
|
|
if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %s" : "DEPOSIT -H %llo %s", address, instruction))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
sim_panel_set_register_value
|
|
|
|
name the name of a simulator register or a memory address
|
|
which is to receive a new value
|
|
value the new value in character string form. The string
|
|
must be in the native/natural radix that the simulator
|
|
uses when referencing that register
|
|
|
|
*/
|
|
|
|
int
|
|
sim_panel_set_register_value (PANEL *panel,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
if (panel->State == Run) {
|
|
sim_panel_set_error (NULL, "Not Halted");
|
|
return -1;
|
|
}
|
|
if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT %s %s", name, value))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
sim_panel_mount
|
|
|
|
device the name of a simulator device/unit
|
|
switches any switches appropriate for the desire attach
|
|
path the path on the local system to be attached
|
|
|
|
*/
|
|
int
|
|
sim_panel_mount (PANEL *panel,
|
|
const char *device,
|
|
const char *switches,
|
|
const char *path)
|
|
{
|
|
char *response = NULL;
|
|
OperationalState OrigState;
|
|
int stat = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
OrigState = panel->State;
|
|
if (OrigState == Run)
|
|
sim_panel_exec_halt (panel);
|
|
do {
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "ATTACH %s %s %s", switches, device, path)) {
|
|
stat = -1;
|
|
break;
|
|
}
|
|
if (cmd_stat) {
|
|
sim_panel_set_error (NULL, response);
|
|
stat = -1;
|
|
}
|
|
} while (0);
|
|
if (OrigState == Run)
|
|
sim_panel_exec_run (panel);
|
|
free (response);
|
|
return stat;
|
|
}
|
|
|
|
/**
|
|
sim_panel_dismount
|
|
|
|
device the name of a simulator device/unit
|
|
|
|
*/
|
|
int
|
|
sim_panel_dismount (PANEL *panel,
|
|
const char *device)
|
|
{
|
|
char *response = NULL;
|
|
OperationalState OrigState;
|
|
int stat = 0;
|
|
int cmd_stat;
|
|
|
|
if (!panel || (panel->State == Error)) {
|
|
sim_panel_set_error (NULL, "Invalid Panel");
|
|
return -1;
|
|
}
|
|
OrigState = panel->State;
|
|
if (OrigState == Run)
|
|
sim_panel_exec_halt (panel);
|
|
do {
|
|
if (_panel_sendf (panel, &cmd_stat, &response, "DETACH %s", device)) {
|
|
stat = -1;
|
|
break;
|
|
}
|
|
if (cmd_stat) {
|
|
sim_panel_set_error (NULL, "%s", response);
|
|
stat = -1;
|
|
}
|
|
} while (0);
|
|
if (OrigState == Run)
|
|
sim_panel_exec_run (panel);
|
|
free (response);
|
|
return stat;
|
|
}
|
|
|
|
|
|
static void *
|
|
_panel_reader(void *arg)
|
|
{
|
|
PANEL *p = (PANEL*)arg;
|
|
REG *r = NULL;
|
|
int sched_policy;
|
|
struct sched_param sched_priority;
|
|
char buf[4096];
|
|
int buf_data = 0;
|
|
int processing_register_output = 0;
|
|
int io_wait_done = 0;
|
|
|
|
/*
|
|
Boost Priority for this response processing thread to quickly digest
|
|
arriving data.
|
|
*/
|
|
pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority);
|
|
++sched_priority.sched_priority;
|
|
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
|
|
pthread_setspecific (panel_thread_id, "reader");
|
|
_panel_debug (p, DBG_THR, "Starting", NULL, 0);
|
|
|
|
buf[buf_data] = '\0';
|
|
pthread_mutex_lock (&p->io_lock);
|
|
if (!p->parent) {
|
|
while (1) {
|
|
int new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1));
|
|
|
|
if (new_data <= 0) {
|
|
sim_panel_set_error (NULL, "%s after reading %d bytes: %s", sim_get_err_sock("Unexpected socket read"), buf_data, buf);
|
|
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
|
|
p->State = Error;
|
|
break;
|
|
}
|
|
_panel_debug (p, DBG_RCV, "Startup receive of %d bytes: ", &buf[buf_data], new_data, new_data);
|
|
buf_data += new_data;
|
|
buf[buf_data] = '\0';
|
|
if (!memcmp (mantra, buf, sizeof (mantra))) { /* strip initial telnet mantra from input stream */
|
|
memmove (buf, buf + sizeof (mantra), 1 + buf_data - sizeof (mantra));
|
|
buf_data -= sizeof (mantra);
|
|
}
|
|
if ((size_t)buf_data < strlen (sim_prompt))
|
|
continue;
|
|
if (!strcmp (sim_prompt, &buf[buf_data - strlen (sim_prompt)])) {
|
|
memmove (buf, &buf[buf_data - strlen (sim_prompt)], strlen (sim_prompt) + 1);
|
|
buf_data = strlen (sim_prompt);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
p->io_thread_running = 1;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */
|
|
msleep (100);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
while ((p->sock != INVALID_SOCKET) &&
|
|
(p->State != Error)) {
|
|
int new_data;
|
|
char *s, *e, *eol;
|
|
|
|
if (NULL == strchr (buf, '\n')) {
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1));
|
|
pthread_mutex_lock (&p->io_lock);
|
|
if (new_data <= 0) {
|
|
sim_panel_set_error (NULL, "%s", sim_get_err_sock("Unexpected socket read"));
|
|
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
|
|
p->State = Error;
|
|
break;
|
|
}
|
|
_panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data);
|
|
buf_data += new_data;
|
|
buf[buf_data] = '\0';
|
|
}
|
|
s = buf;
|
|
while ((eol = strchr (s, '\n'))) {
|
|
/* Line to process */
|
|
*eol++ = '\0';
|
|
while ((*s) && (s[strlen(s)-1] == '\r'))
|
|
s[strlen(s)-1] = '\0';
|
|
if (processing_register_output) {
|
|
e = strchr (s, ':');
|
|
if (e) {
|
|
size_t i;
|
|
char smp_dev[32], smp_reg[32], smp_ind[32];
|
|
unsigned int bit;
|
|
|
|
*e++ = '\0';
|
|
if (!strcmp("Time", s)) {
|
|
p->simulation_time = strtoull (e, NULL, 10);
|
|
s = eol;
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
continue; /* process next line */
|
|
}
|
|
if ((*s == '}') &&
|
|
(3 == sscanf (s, "}%s %s %s", smp_dev, smp_reg, smp_ind))) { /* Register bit Sample Data? */
|
|
r = NULL;
|
|
for (i=0; i<p->reg_count; i++) {
|
|
if (p->regs[i].bits == NULL)
|
|
continue;
|
|
if ((!strcmp (smp_reg, p->regs[i].name)) &&
|
|
((!p->device_name) || (!strcmp (smp_dev, p->device_name)))) {
|
|
r = &p->regs[i];
|
|
break;
|
|
}
|
|
}
|
|
if (r) {
|
|
for (bit = 0; bit < r->bit_count; bit++) {
|
|
int val = (int)strtol (e, &e, 10);
|
|
r->bits[bit] = val;
|
|
if (*e == ',')
|
|
++e;
|
|
else
|
|
break;
|
|
}
|
|
s = eol;
|
|
}
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
r = NULL;
|
|
continue; /* process next line */
|
|
}
|
|
if (!strncmp (s + strlen (sim_prompt), register_ind_echo, strlen (register_ind_echo) - 1)) {
|
|
e = s + strlen (sim_prompt) + strlen (register_ind_echo);
|
|
r = NULL;
|
|
for (i=0; i<p->reg_count; i++) {
|
|
if (p->regs[i].indirect && (!strcmp(p->regs[i].name, e))) {
|
|
r = &p->regs[i];
|
|
break;
|
|
}
|
|
}
|
|
s = eol;
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
if (r)
|
|
continue; /* process next line */
|
|
}
|
|
if (r) {
|
|
if (strcmp (s, r->name)) {
|
|
unsigned long long data;
|
|
|
|
data = strtoull (e, NULL, 16);
|
|
if (little_endian)
|
|
memcpy (r->addr, &data, r->size);
|
|
else
|
|
memcpy (r->addr, ((char *)&data) + sizeof(data)-r->size, r->size);
|
|
r = NULL;
|
|
}
|
|
s = eol;
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
continue; /* process next line */
|
|
}
|
|
for (i=0; i<p->reg_count; i++) {
|
|
if (p->regs[i].element_count == 0) {
|
|
if (!strcmp(p->regs[i].name, s)) {
|
|
unsigned long long data;
|
|
|
|
data = strtoull (e, NULL, 16);
|
|
if (little_endian)
|
|
memcpy (p->regs[i].addr, &data, p->regs[i].size);
|
|
else
|
|
memcpy (p->regs[i].addr, ((char *)&data) + sizeof(data)-p->regs[i].size, p->regs[i].size);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
size_t name_len = strlen (p->regs[i].name);
|
|
|
|
if ((0 == memcmp (p->regs[i].name, s, name_len)) && (s[name_len] == '[')) {
|
|
size_t array_index = (size_t)atoi (s + name_len + 1);
|
|
size_t end_index = array_index;
|
|
char *end = strchr (s + name_len + 1, '[');
|
|
|
|
if (end)
|
|
end_index = (size_t)atoi (end + 1);
|
|
if (strcmp (e, " same as above"))
|
|
p->array_element_data = strtoull (e, NULL, 16);
|
|
while (array_index <= end_index) {
|
|
if (little_endian)
|
|
memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), &p->array_element_data, p->regs[i].size);
|
|
else
|
|
memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), ((char *)&p->array_element_data) + sizeof(p->array_element_data)-p->regs[i].size, p->regs[i].size);
|
|
++array_index;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i != p->reg_count) {
|
|
s = eol;
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
continue;
|
|
}
|
|
--e;
|
|
*e = ':';
|
|
/* Unexpected Register Data Found (or other output containing a : character) */
|
|
}
|
|
}
|
|
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), register_repeat_end))) {
|
|
_panel_debug (p, DBG_RCV, "*Repeat Block Complete (Accumulated Data = %d)", NULL, 0, (int)p->io_response_data);
|
|
if (p->callback) {
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
}
|
|
processing_register_output = 0;
|
|
p->io_response_data = 0;
|
|
p->io_response[p->io_response_data] = '\0';
|
|
goto Start_Next_Line;
|
|
}
|
|
if ((strlen (s) > strlen (sim_prompt)) &&
|
|
((!strcmp (s + strlen (sim_prompt), register_repeat_start)) ||
|
|
(!strcmp (s + strlen (sim_prompt), register_get_start)))) {
|
|
_panel_debug (p, DBG_RCV, "*Repeat/Register Block Starting", NULL, 0);
|
|
processing_register_output = 1;
|
|
goto Start_Next_Line;
|
|
}
|
|
if ((strlen (s) > strlen (sim_prompt)) &&
|
|
(!strcmp (s + strlen (sim_prompt), register_get_end))) {
|
|
_panel_debug (p, DBG_RCV, "*Register Block Complete", NULL, 0);
|
|
p->io_waiting = 0;
|
|
processing_register_output = 0;
|
|
pthread_cond_signal (&p->io_done);
|
|
goto Start_Next_Line;
|
|
}
|
|
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), command_done_echo))) {
|
|
_panel_debug (p, DBG_RCV, "*Received Command Complete", NULL, 0);
|
|
p->io_waiting = 0;
|
|
pthread_cond_signal (&p->io_done);
|
|
goto Start_Next_Line;
|
|
}
|
|
/* Non Register Data Found (echo of EXAMINE or other commands and/or command output) */
|
|
if (p->io_response_data + strlen (s) + 3 > p->io_response_size) {
|
|
char *t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3);
|
|
|
|
if (t == NULL) {
|
|
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
|
|
p->State = Error;
|
|
break;
|
|
}
|
|
memcpy (t, p->io_response, p->io_response_data);
|
|
free (p->io_response);
|
|
p->io_response = t;
|
|
p->io_response_size = p->io_response_data + strlen (s) + 3;
|
|
}
|
|
_panel_debug (p, DBG_RCV, "Receive Data Accumulated: '%s'", NULL, 0, s);
|
|
strcpy (p->io_response + p->io_response_data, s);
|
|
p->io_response_data += strlen(s);
|
|
strcpy (p->io_response + p->io_response_data, "\r\n");
|
|
p->io_response_data += 2;
|
|
if ((!p->parent) &&
|
|
(p->completion_string) &&
|
|
(!memcmp (s, p->completion_string, strlen (p->completion_string)))) {
|
|
_panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string);
|
|
if (eol < &buf[buf_data])
|
|
memset (s + strlen (s), ' ', eol - (s + strlen (s)));
|
|
break;
|
|
}
|
|
Start_Next_Line:
|
|
s = eol;
|
|
while (isspace(0xFF & (*s)))
|
|
++s;
|
|
}
|
|
memmove (buf, s, buf_data - (s - buf) + 1);
|
|
buf_data = strlen (buf);
|
|
if (buf_data)
|
|
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf);
|
|
if ((!p->parent) &&
|
|
(p->completion_string) &&
|
|
(!memcmp (buf, p->completion_string, strlen (p->completion_string)))) {
|
|
_panel_debug (p, DBG_RCV, "*Received Command Complete - Match: '%s'", NULL, 0, p->completion_string);
|
|
io_wait_done = 1;
|
|
}
|
|
if (!memcmp ("Simulator Running...", buf, 20)) {
|
|
_panel_debug (p, DBG_RSP, "State transitioning to Run", NULL, 0);
|
|
p->State = Run;
|
|
buf_data -= 20;
|
|
if (buf_data) {
|
|
memmove (buf, buf + 20, buf_data + 1);
|
|
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf);
|
|
}
|
|
else
|
|
buf[buf_data] = '\0';
|
|
if (io_wait_done) { /* someone waiting for this? */
|
|
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0);
|
|
io_wait_done = 0;
|
|
p->io_waiting = 0;
|
|
p->completion_string = NULL;
|
|
pthread_cond_signal (&p->io_done);
|
|
/* Let this state transition propagate to the interested thread(s) */
|
|
/* before processing remaining buffered data */
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
msleep (100);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
}
|
|
}
|
|
if ((p->State == Run) && (!strcmp (buf, sim_prompt))) {
|
|
_panel_debug (p, DBG_RSP, "State transitioning to Halt: io_wait_done: %d", NULL, 0, io_wait_done);
|
|
p->State = Halt;
|
|
free (p->halt_reason);
|
|
p->halt_reason = (char *)_panel_malloc (1 + strlen (p->io_response));
|
|
if (p->halt_reason == NULL) {
|
|
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
|
|
p->State = Error;
|
|
break;
|
|
}
|
|
strcpy (p->halt_reason, p->io_response);
|
|
}
|
|
if (io_wait_done) {
|
|
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0);
|
|
io_wait_done = 0;
|
|
p->io_waiting = 0;
|
|
p->completion_string = NULL;
|
|
pthread_cond_signal (&p->io_done);
|
|
}
|
|
}
|
|
if (p->io_waiting) {
|
|
_panel_debug (p, DBG_THR, "Receive: restarting waiting thread while exiting", NULL, 0);
|
|
p->io_waiting = 0;
|
|
pthread_cond_signal (&p->io_done);
|
|
}
|
|
_panel_debug (p, DBG_THR, "Exiting", NULL, 0);
|
|
pthread_setspecific (panel_thread_id, NULL);
|
|
p->io_thread_running = 0;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
return NULL;
|
|
}
|
|
|
|
static void *
|
|
_panel_callback(void *arg)
|
|
{
|
|
PANEL *p = (PANEL*)arg;
|
|
int sched_policy;
|
|
struct sched_param sched_priority;
|
|
char *buf = NULL;
|
|
size_t buf_data = 0;
|
|
unsigned int callback_count = 0;
|
|
int cmd_stat;
|
|
|
|
/*
|
|
Boost Priority for timer thread so it doesn't compete
|
|
with compute bound activities.
|
|
*/
|
|
pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority);
|
|
++sched_priority.sched_priority;
|
|
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
|
|
pthread_setspecific (panel_thread_id, "callback");
|
|
_panel_debug (p, DBG_THR, "Starting", NULL, 0);
|
|
|
|
pthread_mutex_lock (&p->io_lock);
|
|
p->callback_thread_running = 1;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */
|
|
msleep (100);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
while ((p->sock != INVALID_SOCKET) &&
|
|
(p->usecs_between_callbacks) &&
|
|
(p->State != Error)) {
|
|
int interval = p->usecs_between_callbacks;
|
|
int new_register = p->new_register;
|
|
|
|
p->new_register = 0;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
|
|
if (new_register) /* need to get and send updated register info */
|
|
_panel_register_query_string (p, &buf, &buf_data);
|
|
|
|
/* twice a second activities: */
|
|
/* 1) update the query string if it has changed */
|
|
/* (only really happens at startup) */
|
|
/* 2) update register state by polling if the simulator is halted */
|
|
msleep (500);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
if (new_register) {
|
|
size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */
|
|
20 + /* max int width */
|
|
strlen (register_repeat_units) + /* units and spacing */
|
|
buf_data + /* command contents */
|
|
1 + /* ; */
|
|
strlen (register_repeat_start) + /* auto repeat begin */
|
|
1 + /* ; */
|
|
strlen (register_repeat_end) + /* auto repeat completion */
|
|
1 + /* carriage return */
|
|
1; /* NUL */
|
|
char *repeat = (char *)malloc (repeat_data);
|
|
char *c;
|
|
|
|
c = strstr (buf, register_get_start); /* remove register_get_start string and anything before it */
|
|
if (c) { /* always true */
|
|
buf_data -= (c - buf) + strlen (register_get_start);
|
|
c += strlen (register_get_start);
|
|
}
|
|
sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix,
|
|
p->usecs_between_callbacks,
|
|
register_repeat_units,
|
|
register_repeat_start,
|
|
(int)buf_data, (int)buf_data, c);
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
c = strstr (repeat, register_get_end); /* remove register_done_echo string and */
|
|
if (c) /* always true */
|
|
strcpy (c, register_repeat_end); /* replace it with the register_repeat_end string */
|
|
if (_panel_sendf (p, &cmd_stat, NULL, "%s", repeat)) {
|
|
pthread_mutex_lock (&p->io_lock);
|
|
free (repeat);
|
|
break;
|
|
}
|
|
pthread_mutex_lock (&p->io_lock);
|
|
free (repeat);
|
|
}
|
|
/* when halted, we directly poll the halted system to get updated */
|
|
/* register state which may have changed due to panel activities */
|
|
if (p->State == Halt) {
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
if (_panel_get_registers (p, 1, NULL)) {
|
|
pthread_mutex_lock (&p->io_lock);
|
|
break;
|
|
}
|
|
if (p->callback)
|
|
p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context);
|
|
pthread_mutex_lock (&p->io_lock);
|
|
}
|
|
}
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
/* stop any established repeating activity in the simulator */
|
|
if (p->parent == NULL) { /* Top level panel? */
|
|
_panel_debug (p, DBG_THR, "Stopping All Repeats before exiting", NULL, 0);
|
|
_panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop_all);
|
|
}
|
|
else {
|
|
_panel_debug (p, DBG_THR, "Stopping Repeats before exiting", NULL, 0);
|
|
_panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop);
|
|
}
|
|
pthread_mutex_lock (&p->io_lock);
|
|
_panel_debug (p, DBG_THR, "Exiting", NULL, 0);
|
|
pthread_setspecific (panel_thread_id, NULL);
|
|
p->callback_thread_running = 0;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
free (buf);
|
|
return NULL;
|
|
}
|
|
|
|
const char *sim_panel_get_error (void)
|
|
{
|
|
return (sim_panel_error_buf ? sim_panel_error_buf : "");
|
|
}
|
|
|
|
void sim_panel_clear_error (void)
|
|
{
|
|
if (sim_panel_error_bufsize)
|
|
free (sim_panel_error_buf);
|
|
sim_panel_error_buf = NULL;
|
|
sim_panel_error_bufsize = 0;
|
|
}
|
|
|
|
#if defined (_WIN32)
|
|
#define vsnprintf _vsnprintf
|
|
#endif
|
|
|
|
static int sim_panel_set_error (PANEL *p, const char *fmt, ...)
|
|
{
|
|
va_list arglist;
|
|
int len;
|
|
|
|
if (p) {
|
|
pthread_mutex_lock (&p->io_lock);
|
|
p->State = Error;
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
}
|
|
if (sim_panel_error_bufsize == 0) {
|
|
sim_panel_error_bufsize = 2048;
|
|
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize);
|
|
if (sim_panel_error_buf == NULL) {
|
|
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n";
|
|
sim_panel_error_bufsize = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0';
|
|
|
|
while (1) { /* format passed string, args */
|
|
va_start (arglist, fmt);
|
|
len = vsnprintf (sim_panel_error_buf, sim_panel_error_bufsize-1, fmt, arglist);
|
|
va_end (arglist);
|
|
|
|
if (len < 0) /* Format encoding error? */
|
|
break;
|
|
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
|
|
if (len >= (int)(sim_panel_error_bufsize-1)) {
|
|
free (sim_panel_error_buf);
|
|
sim_panel_error_bufsize = sim_panel_error_bufsize * 2;
|
|
while ((int)sim_panel_error_bufsize < len + 2)
|
|
sim_panel_error_bufsize = sim_panel_error_bufsize * 2;
|
|
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize);
|
|
if (sim_panel_error_buf == NULL) {
|
|
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n";
|
|
sim_panel_error_bufsize = 0;
|
|
return -1;
|
|
}
|
|
sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0';
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
_panel_vsendf_completion (PANEL *p, int *completion_status, char **response, const char *completion_string, const char *fmt, va_list arglist)
|
|
{
|
|
char stackbuf[1024];
|
|
int bufsize = sizeof(stackbuf);
|
|
char *buf = stackbuf;
|
|
int len, status_echo_len = 0, sent_len;
|
|
int post_fix_len = completion_status ? 7 + sizeof (command_done_echo) + sizeof (command_status) : 1;
|
|
int ret;
|
|
|
|
if (completion_status && completion_string) /* one or the other, but */
|
|
return -1; /* not both */
|
|
|
|
while (1) { /* format passed string, args */
|
|
len = vsnprintf (buf, bufsize-1, fmt, arglist);
|
|
|
|
if (len < 0)
|
|
return sim_panel_set_error (NULL, "Format encoding error while processing '%s'", fmt);
|
|
|
|
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
|
|
if ((len + post_fix_len) >= bufsize-1) {
|
|
if (buf != stackbuf)
|
|
free (buf);
|
|
bufsize = bufsize * 2;
|
|
if (bufsize < (len + post_fix_len + 2))
|
|
bufsize = len + post_fix_len + 2;
|
|
buf = (char *) _panel_malloc (bufsize);
|
|
if (buf == NULL)
|
|
return -1;
|
|
buf[bufsize-1] = '\0';
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (len && (buf[len-1] != '\r')) {
|
|
strcpy (&buf[len], "\r"); /* Make sure command line is terminated */
|
|
++len;
|
|
}
|
|
|
|
pthread_mutex_lock (&p->io_command_lock);
|
|
++p->command_count;
|
|
if (completion_status || completion_string) {
|
|
if (completion_status) {
|
|
sprintf (&buf[len], "%s\r%s\r", command_status, command_done_echo);
|
|
status_echo_len = strlen (&buf[len]);
|
|
}
|
|
pthread_mutex_lock (&p->io_lock);
|
|
p->completion_string = completion_string;
|
|
if (p->io_response_data)
|
|
_panel_debug (p, DBG_RCV, "Receive Data Discarded: ", p->io_response, p->io_response_data);
|
|
p->io_response_data = 0;
|
|
p->io_waiting = 1;
|
|
}
|
|
|
|
_panel_debug (p, DBG_REQ, "Command %d Request%s: %*.*s", NULL, 0, p->command_count, completion_status ? " (with response)" : "", len, len, buf);
|
|
ret = ((len + status_echo_len) == (sent_len = _panel_send (p, buf, len + status_echo_len))) ? 0 : -1;
|
|
|
|
if (completion_status || completion_string) {
|
|
if (ret) { /* Send failed? */
|
|
p->completion_string = NULL;
|
|
p->io_waiting = 0;
|
|
}
|
|
else { /* Sent OK? */
|
|
char *tresponse = NULL;
|
|
|
|
while (p->io_waiting)
|
|
pthread_cond_wait (&p->io_done, &p->io_lock); /* Wait for completion */
|
|
tresponse = (char *)_panel_malloc (p->io_response_data + 1);
|
|
if (0 == memcmp (buf, p->io_response + strlen (sim_prompt), len)) {
|
|
char *eol, *status;
|
|
memcpy (tresponse, p->io_response + strlen (sim_prompt) + len + 1, p->io_response_data + 1 - (strlen (sim_prompt) + len + 1));
|
|
if (completion_status) {
|
|
*completion_status = -1;
|
|
status = strstr (tresponse, command_status);
|
|
if (status) {
|
|
*(status - strlen (sim_prompt)) = '\0';
|
|
status += strlen (command_status) + 2;
|
|
eol = strchr (status, '\r');
|
|
if (eol)
|
|
*eol = '\0';
|
|
sscanf (status, "Status:%08X-", completion_status);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
memcpy (tresponse, p->io_response, p->io_response_data + 1);
|
|
if (response) {
|
|
*response = tresponse;
|
|
if (completion_status)
|
|
_panel_debug (p, DBG_RSP, "Command %d Response(Status=%d): '%s'", NULL, 0, p->command_count, *completion_status, *response);
|
|
else
|
|
_panel_debug (p, DBG_RSP, "Command %d Response - Match '%s': '%s'", NULL, 0, p->command_count, completion_string, *response);
|
|
}
|
|
else {
|
|
free (tresponse);
|
|
if (p->io_response_data) {
|
|
if (completion_status)
|
|
_panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data(Status=%d):", p->io_response, p->io_response_data, p->command_count, *completion_status);
|
|
else
|
|
_panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data - Match '%s':", p->io_response, p->io_response_data, p->command_count, completion_string);
|
|
}
|
|
}
|
|
}
|
|
p->completion_string = NULL;
|
|
p->io_response_data = 0;
|
|
p->io_response[0] = '\0';
|
|
pthread_mutex_unlock (&p->io_lock);
|
|
}
|
|
else {
|
|
if (ret)
|
|
sim_panel_set_error (p, "Unexpected send length: %d, expected: %d", sent_len, len + status_echo_len);
|
|
}
|
|
pthread_mutex_unlock (&p->io_command_lock);
|
|
|
|
if (buf != stackbuf)
|
|
free (buf);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...)
|
|
{
|
|
va_list arglist;
|
|
int status;
|
|
|
|
va_start (arglist, fmt);
|
|
status = _panel_vsendf_completion (p, completion_status, response, NULL, fmt, arglist);
|
|
va_end (arglist);
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
_panel_sendf_completion (PANEL *p, char **response, const char *completion, const char *fmt, ...)
|
|
{
|
|
va_list arglist;
|
|
int status;
|
|
|
|
va_start (arglist, fmt);
|
|
status = _panel_vsendf_completion (p, NULL, response, completion, fmt, arglist);
|
|
va_end (arglist);
|
|
return status;
|
|
}
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|