Add XSPICE code model d_cosim, a generic adaptor for digital cosimulation.
This commit is contained in:
parent
12fe7b90c4
commit
566e2938f4
|
|
@ -0,0 +1,71 @@
|
|||
/* Header file for the shim code between d_cosim and a co-simulator. */
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* A value of this type controls how the step() function is called.
|
||||
* The normal method is to call step() to advance to the time of a
|
||||
* queued input, then supply the input, then call step again to
|
||||
* advance to the next input time or the end of the current SPICE
|
||||
* timestep. But Verilator does nothing without an input change,
|
||||
* so step() must be called after input.
|
||||
*/
|
||||
|
||||
typedef enum {Normal, After_input} Cosim_method;
|
||||
|
||||
/* Structure used by Cosim_setup() to pass and return
|
||||
* co-simulation interface information.
|
||||
*/
|
||||
|
||||
struct co_info {
|
||||
/* The co-simulator must set the number of ports in Cosim_setup(). */
|
||||
|
||||
unsigned int in_count;
|
||||
unsigned int out_count;
|
||||
unsigned int inout_count;
|
||||
|
||||
/* The co-simulator may specify a function to be called just before
|
||||
* it is unloaded at the end of a simulation run.
|
||||
*/
|
||||
|
||||
void (*cleanup)(struct co_info *);
|
||||
|
||||
/* Function called by SPICE to advance the co-simulation.
|
||||
* A pointer to this structure is passed, so it has access to its handle
|
||||
* and the target simulation time, vtime. The co-simulator should
|
||||
* pause the step when output is produced and update vtime.
|
||||
*/
|
||||
|
||||
void (*step)(struct co_info *pinfo); // Advance simulation.
|
||||
|
||||
/* Function called by SPICE to pass input to input and inout ports.
|
||||
* (Inouts after inputs.)
|
||||
* Called as:
|
||||
* struct co_info info;
|
||||
* (*in_fn)(&info, bit_number, &value);
|
||||
* Function provided by co-simulator.
|
||||
*/
|
||||
|
||||
void (*in_fn)(struct co_info *, unsigned int, Digital_t *);
|
||||
|
||||
/* Function called by co-simulator to report output on
|
||||
* output and inout ports. (Inouts after outputs.)
|
||||
* Called as:
|
||||
* struct co_info *p_info;
|
||||
* (*out_fn)(p_info, bit_number, &value);
|
||||
* It will usually be called inside a call to step().
|
||||
*/
|
||||
|
||||
void (*out_fn)(struct co_info *, unsigned int, Digital_t *);
|
||||
void *handle; // Co-simulator's private handle
|
||||
double vtime; // Time in the co-simulation.
|
||||
Cosim_method method; // May be set in Cosim_setup;
|
||||
};
|
||||
|
||||
extern void Cosim_setup(struct co_info *pinfo); // This must exist.
|
||||
extern void Cosim_step(struct co_info *pinfo); // Exists for Verilator.
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,578 @@
|
|||
/* Code model d_cosim.
|
||||
*
|
||||
* XSPICE code model for running a co-simulation with no support
|
||||
* for abandoning the current timestep.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined (__MINGW32__) || defined (__CYGWIN__) || defined (_MSC_VER)
|
||||
/* MS WINDOWS. */
|
||||
#undef BOOLEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define dlopen(name, type) LoadLibrary(name)
|
||||
#define dlsym(handle, name) (void *)GetProcAddress(handle, name)
|
||||
#define dlclose(handle) FreeLibrary(handle)
|
||||
|
||||
char *dlerror(void) // Lifted from dev.c.
|
||||
{
|
||||
static const char errstr_fmt[] =
|
||||
"Unable to find message in dlerr(). System code = %lu";
|
||||
static char errstr[256];
|
||||
LPVOID lpMsgBuf;
|
||||
|
||||
DWORD rc = FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (rc == 0) { /* FormatMessage failed */
|
||||
(void) sprintf(errstr, errstr_fmt, (unsigned long) GetLastError());
|
||||
} else {
|
||||
snprintf(errstr, sizeof errstr, lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
return errstr;
|
||||
} /* end of function dlerror */
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include "ngspice/cosim.h"
|
||||
|
||||
/* The argument passed to code model functions. */
|
||||
|
||||
#define XSPICE_ARG mif_private
|
||||
|
||||
#define DBG(...)
|
||||
//#define DBG(...) cm_message_printf(__VA_ARGS__)
|
||||
|
||||
/* Structure used to hold queued inputs. */
|
||||
|
||||
struct pend_in {
|
||||
double when; // Due time.
|
||||
unsigned int which; // Index of input.
|
||||
Digital_t what; // The value.
|
||||
};
|
||||
|
||||
/* Structure to maintain context, pointed to by STATIC VAR cosim_instance. */
|
||||
|
||||
struct instance {
|
||||
struct co_info info; // Co-simulation interface - MUST BE FIRST.
|
||||
int q_index; // Queue index (last active entry).
|
||||
unsigned int q_length; // Size of input queue.
|
||||
struct pend_in *q; // The input queue.
|
||||
unsigned int in_ports; // Number of XSPICE inputs.
|
||||
unsigned int out_ports; // Number of XSPICE outputs.
|
||||
unsigned int inout_ports; // Number of XSPICE inout ports.
|
||||
unsigned int op_pending; // Output is pending.
|
||||
Digital_t *out_vals; // The new output values.
|
||||
double extra; // Margin to extend timestep.
|
||||
void *so_handle; // dlopen() handle to the simulation binary.
|
||||
};
|
||||
|
||||
/* Called at end of simulation run to free memory. */
|
||||
|
||||
static void callback(ARGS, Mif_Callback_Reason_t reason)
|
||||
{
|
||||
struct instance *ip;
|
||||
|
||||
ip = (struct instance *)STATIC_VAR(cosim_instance);
|
||||
if (reason == MIF_CB_DESTROY) {
|
||||
if (!ip)
|
||||
return;
|
||||
if (ip->info.cleanup)
|
||||
(*ip->info.cleanup)(&ip->info);
|
||||
if (ip->so_handle)
|
||||
dlclose(ip->so_handle);
|
||||
if (ip->q)
|
||||
free(ip->q);
|
||||
if (ip->out_vals)
|
||||
free(ip->out_vals);
|
||||
free(ip);
|
||||
STATIC_VAR(cosim_instance) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function called when a co-simulator output changes.
|
||||
* Out-of-range values for bit_num must be ignored.
|
||||
*/
|
||||
|
||||
void accept_output(struct co_info *pinfo, unsigned int bit_num, Digital_t *val)
|
||||
{
|
||||
struct instance *ip = (struct instance *)pinfo; // First member.
|
||||
Digital_t *out_vals; // XSPICE rotating memory.
|
||||
|
||||
if (bit_num >= ip->out_ports + ip->inout_ports)
|
||||
return;
|
||||
out_vals = (Digital_t *)cm_event_get_ptr(1, 0);
|
||||
DBG("Change %s %d/%d->%d/%d vtime %g",
|
||||
cm_get_node_name("d_out", bit_num),
|
||||
out_vals[bit_num].state, out_vals[bit_num].strength,
|
||||
val->state, val->strength,
|
||||
ip->info.vtime);
|
||||
if (ip->op_pending == 0) {
|
||||
/* Prepare pending output. */
|
||||
|
||||
memcpy(ip->out_vals, out_vals, ip->out_ports * sizeof *ip->out_vals);
|
||||
ip->op_pending = 1;
|
||||
}
|
||||
ip->out_vals[bit_num] = *val;
|
||||
}
|
||||
|
||||
/* Push pending outputs, usually sent back from the future.
|
||||
* It is safe to use OUTPUT() here, although it mays seem that this
|
||||
* function may be called twice in a single call to cm_d_cosim().
|
||||
* There will never be any input changes when cm_d_cosim() is called
|
||||
* with pending output, as all input for the shortened time-step has
|
||||
* already been processed.
|
||||
*/
|
||||
|
||||
static void output(struct instance *ip, ARGS)
|
||||
{
|
||||
double delay;
|
||||
Digital_t *out_vals; // XSPICE rotating memory
|
||||
int i, j;
|
||||
|
||||
delay = PARAM(delay) - (TIME - ip->info.vtime);
|
||||
if (delay <= 0) {
|
||||
cm_message_printf("WARNING: output scheduled with impossible "
|
||||
"delay (%g) at %g.", delay, TIME);
|
||||
delay = 1e-12;
|
||||
}
|
||||
out_vals = (Digital_t *)cm_event_get_ptr(1, 0);
|
||||
|
||||
/* Output to d_out. */
|
||||
|
||||
for (i = 0; i < ip->out_ports; ++i) {
|
||||
if (ip->out_vals[i].state != out_vals[i].state ||
|
||||
ip->out_vals[i].strength != out_vals[i].strength) {
|
||||
DBG("%g: OUT %s %d/%d->%d/%d vtime %g with delay %g",
|
||||
TIME, cm_get_node_name("d_out", i),
|
||||
out_vals[i].state, out_vals[i].strength,
|
||||
ip->out_vals[i].state, ip->out_vals[i].strength,
|
||||
ip->info.vtime, delay);
|
||||
*(Digital_t *)OUTPUT(d_out[i]) = out_vals[i] = ip->out_vals[i];
|
||||
OUTPUT_DELAY(d_out[i]) = delay;
|
||||
OUTPUT_CHANGED(d_out[i]) = TRUE;
|
||||
} else {
|
||||
OUTPUT_CHANGED(d_out[i]) = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output to d_inout. */
|
||||
|
||||
for (i = 0, j = ip->out_ports; i < ip->inout_ports; ++i, ++j) {
|
||||
if (ip->out_vals[j].state != out_vals[j].state ||
|
||||
ip->out_vals[j].strength != out_vals[j].strength) {
|
||||
DBG("%g: inOUT %s %d/%d->%d/%d vtime %g with delay %g",
|
||||
TIME, cm_get_node_name("d_inout", i),
|
||||
out_vals[j].state, out_vals[j].strength,
|
||||
ip->out_vals[j].state, ip->out_vals[j].strength,
|
||||
ip->info.vtime, delay);
|
||||
*(Digital_t *)OUTPUT(d_inout[i]) = out_vals[j] = ip->out_vals[j];
|
||||
OUTPUT_DELAY(d_inout[i]) = delay;
|
||||
OUTPUT_CHANGED(d_inout[i]) = TRUE;
|
||||
} else {
|
||||
OUTPUT_CHANGED(d_inout[i]) = FALSE;
|
||||
}
|
||||
}
|
||||
ip->op_pending = 0;
|
||||
}
|
||||
|
||||
/* Run the co-simulation. Return 1 if the timestep was truncated. */
|
||||
|
||||
static int advance(struct instance *ip, ARGS)
|
||||
{
|
||||
/* The co-simulator should advance to the time in ip->info.vtime,
|
||||
* but should pause when output is generated and update vtime.
|
||||
*/
|
||||
|
||||
(*ip->info.step)(&ip->info);
|
||||
|
||||
if (ip->op_pending) {
|
||||
|
||||
/* The co-simulator produced some output. */
|
||||
|
||||
if (TIME - ip->info.vtime <= PARAM(delay)) {
|
||||
#if 1
|
||||
DBG("Direct output with SPICE %.16g CS %.16g",
|
||||
TIME, ip->info.vtime);
|
||||
output(ip, XSPICE_ARG); // Normal output, unlikely.
|
||||
#else
|
||||
cm_event_queue((TIME + ip->info.vtime + PARAM(delay)) / 2.0);
|
||||
#endif
|
||||
} else {
|
||||
|
||||
/* Something changed that may alter the future of the
|
||||
* SPICE simulation. Truncate the current timestep so that
|
||||
* SPICE will see the pending output, which currently occurred
|
||||
* in the past.
|
||||
*/
|
||||
|
||||
DBG("Truncating timestep to %.16g", ip->info.vtime + ip->extra);
|
||||
cm_analog_set_temp_bkpt(ip->info.vtime + ip->extra);
|
||||
|
||||
/* Any remaining input events are in an alternate future. */
|
||||
|
||||
ip->q_index = -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called from the main function to run the co-simulation. */
|
||||
|
||||
static void run(struct instance *ip, ARGS)
|
||||
{
|
||||
struct pend_in *rp;
|
||||
double sim_started;
|
||||
int i;
|
||||
|
||||
if (ip->q_index < 0) {
|
||||
/* No queued input, advance to current TIME. */
|
||||
|
||||
DBG("Advancing vtime without input %.16g -> %.16g",
|
||||
ip->info.vtime , TIME);
|
||||
ip->info.vtime = TIME;
|
||||
advance(ip, XSPICE_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Scan the queue. */
|
||||
|
||||
DBG("%.16g: Running Q with %d entries", TIME, ip->q_index + 1);
|
||||
sim_started = ip->info.vtime;
|
||||
|
||||
for (i = 0; i <= ip->q_index; ++i) {
|
||||
rp = ip->q + i;
|
||||
if (rp->when <= sim_started) {
|
||||
/* Not expected. */
|
||||
|
||||
cm_message_printf("Warning simulated event is in the past:\n"
|
||||
"XSPICE %.16g\n"
|
||||
"Event %.16g\n"
|
||||
"Cosim %.16g",
|
||||
TIME, rp->when, ip->info.vtime);
|
||||
cm_message_printf("i=%d index=%d", i, ip->q_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Step the simulation forward to the input event time. */
|
||||
|
||||
ip->info.vtime = rp->when;
|
||||
if (ip->info.method == Normal && advance(ip, XSPICE_ARG)) {
|
||||
ip->q_index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Pass input change to simulation. */
|
||||
|
||||
(*ip->info.in_fn)(&ip->info, rp->which, &rp->what);
|
||||
while (i < ip->q_index && ip->q[i + 1].when == rp->when) {
|
||||
/* Another change at the same time. */
|
||||
|
||||
++i;
|
||||
rp = ip->q + i;
|
||||
(*ip->info.in_fn)(&ip->info, rp->which, &rp->what);
|
||||
}
|
||||
|
||||
/* Simulator requested to run after input change. */
|
||||
|
||||
if (ip->info.method == After_input && advance(ip, XSPICE_ARG)) {
|
||||
ip->q_index = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* All input was processed. Advance to end of the timestep. */
|
||||
|
||||
ip->q_index = -1;
|
||||
if (ip->info.method == Normal && TIME > ip->info.vtime) {
|
||||
ip->info.vtime = TIME;
|
||||
advance(ip, XSPICE_ARG);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether an input value has changed.
|
||||
* To reduce the number of arguments, a struture pointer is passed.
|
||||
*/
|
||||
|
||||
static bool check_input(struct instance *ip, Digital_t *ovp,
|
||||
struct pend_in *rp)
|
||||
{
|
||||
if (ovp->state != rp->what.state ||
|
||||
ovp->strength != rp->what.strength) {
|
||||
if (++ip->q_index < ip->q_length) {
|
||||
/* Record this event. */
|
||||
|
||||
ip->q[ip->q_index] = *rp;
|
||||
} else {
|
||||
/* Queue is full. Handle that by forcing a shorter timestep. */
|
||||
|
||||
--ip->q_index;
|
||||
while (ip->q_index >= 0 && ip->q[ip->q_index].when >= rp->when)
|
||||
--ip->q_index;
|
||||
if (ip->q_index >= 0) {
|
||||
cm_analog_set_temp_bkpt(
|
||||
(rp->when + ip->q[ip->q_index].when) / 2);
|
||||
} else {
|
||||
/* This should never happen. */
|
||||
|
||||
cm_message_printf("Error: Event queue overflow at %e.",
|
||||
rp->when);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The code model's main function. */
|
||||
|
||||
void ucm_d_cosim(ARGS)
|
||||
{
|
||||
struct instance *ip;
|
||||
Digital_t *in_vals; // XSPICE rotating memory
|
||||
int i, index;
|
||||
|
||||
if (INIT) {
|
||||
int ins, outs, inouts;
|
||||
unsigned int alloc_size;
|
||||
void *handle;
|
||||
void (*ifp)(struct co_info *);
|
||||
char *fn;
|
||||
|
||||
/* Initialise outputs. Done early in case of failure. */
|
||||
|
||||
outs = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out);
|
||||
for (i = 0; i < outs; ++i) {
|
||||
OUTPUT_STATE(d_out[i]) = ZERO;
|
||||
OUTPUT_STRENGTH(d_out[i]) = STRONG;
|
||||
OUTPUT_DELAY(d_out[i]) = PARAM(delay);
|
||||
}
|
||||
|
||||
inouts = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout);
|
||||
for (i = 0; i < inouts; ++i) {
|
||||
OUTPUT_STATE(d_inout[i]) = ZERO;
|
||||
OUTPUT_STRENGTH(d_inout[i]) = STRONG;
|
||||
OUTPUT_DELAY(d_inout[i]) = PARAM(delay);
|
||||
}
|
||||
|
||||
/* Load the shared library containing the co-simulator. */
|
||||
|
||||
fn = PARAM(simulation);
|
||||
handle = dlopen(fn, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle) {
|
||||
cm_message_send("Failed to load simulation binary. "
|
||||
"Try setting LD_LIBRARY_PATH.");
|
||||
cm_message_send(dlerror());
|
||||
return;
|
||||
}
|
||||
ifp = dlsym(handle, "Cosim_setup");
|
||||
if (*ifp == NULL) {
|
||||
cm_message_printf("ERROR: no entry function in %s", fn);
|
||||
cm_message_send(dlerror());
|
||||
dlclose(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the instance data and initialise it. */
|
||||
|
||||
ip = (struct instance *)calloc(1, sizeof *ip);
|
||||
if (!ip)
|
||||
goto no_ip;
|
||||
ip->so_handle = handle;
|
||||
ip->info.vtime = 0.0;
|
||||
ip->info.out_fn = accept_output;
|
||||
CALLBACK = callback;
|
||||
|
||||
/* Store the simulation interface information. */
|
||||
|
||||
(*ifp)(&ip->info);
|
||||
|
||||
/* Check lengths. */
|
||||
|
||||
ins = PORT_NULL(d_in) ? 0 : PORT_SIZE(d_in);
|
||||
if (ins != ip->info.in_count) {
|
||||
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
|
||||
"input counts: %d/%d.",
|
||||
ins, ip->info.in_count);
|
||||
}
|
||||
if (outs != ip->info.out_count) {
|
||||
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
|
||||
"output counts: %d/%d.",
|
||||
outs, ip->info.out_count);
|
||||
}
|
||||
|
||||
if (inouts != ip->info.inout_count) {
|
||||
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
|
||||
"inout counts: %d/%d.",
|
||||
inouts, ip->info.inout_count);
|
||||
}
|
||||
|
||||
/* Create input queue and output buffer. */
|
||||
|
||||
ip->q_index = -1;
|
||||
ip->q_length = PARAM(queue_size);
|
||||
ip->in_ports = ins;
|
||||
ip->out_ports = outs;
|
||||
ip->inout_ports = inouts;
|
||||
if (ins + inouts > ip->q_length) {
|
||||
cm_message_send("WARNING: Input queue size should be greater than "
|
||||
"number of input ports. Size increased.");
|
||||
ip->q_length = ins + inouts + 16;
|
||||
}
|
||||
alloc_size = ip->q_length * sizeof (struct pend_in);
|
||||
ip->q = (struct pend_in *)malloc(alloc_size);
|
||||
if (!ip->q)
|
||||
goto no_q;
|
||||
ip->op_pending = 0;
|
||||
ip->out_vals = (Digital_t *)calloc(outs + inouts, sizeof (Digital_t));
|
||||
if (!ip->out_vals)
|
||||
goto no_out_vals;
|
||||
ip->extra = PARAM(delay) / 3; // FIXME?
|
||||
STATIC_VAR(cosim_instance) = ip;
|
||||
|
||||
/* Allocate XSPICE rotating storage to track changes. */
|
||||
|
||||
cm_event_alloc(0, (ins + inouts) * sizeof (Digital_t));
|
||||
cm_event_alloc(1, (outs + inouts) * sizeof (Digital_t));
|
||||
|
||||
/* Declare irreversible. */
|
||||
|
||||
if (PARAM(irreversible) > 0)
|
||||
cm_irreversible(PARAM(irreversible));
|
||||
return;
|
||||
|
||||
/* Handle malloc failures. */
|
||||
no_out_vals:
|
||||
free(ip->q);
|
||||
no_q:
|
||||
free(ip);
|
||||
no_ip:
|
||||
cm_message_send("No memory!");
|
||||
return;
|
||||
}
|
||||
|
||||
ip = STATIC_VAR(cosim_instance);
|
||||
if (!ip) {
|
||||
int ports;
|
||||
|
||||
/* Error state. Do nothing at all. */
|
||||
|
||||
ports = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out);
|
||||
for (i = 0; i < ports; ++i)
|
||||
OUTPUT_CHANGED(d_out[i]) = FALSE;
|
||||
ports = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout);
|
||||
for (i = 0; i < ports; ++i)
|
||||
OUTPUT_CHANGED(d_inout[i]) = FALSE;
|
||||
return;
|
||||
}
|
||||
in_vals = (Digital_t *)cm_event_get_ptr(0, 0);
|
||||
|
||||
if (TIME == 0.0) {
|
||||
/* Starting, so inputs may be settling. */
|
||||
|
||||
for (i = 0; i < ip->in_ports; ++i) {
|
||||
Digital_t ival;
|
||||
|
||||
ival = *(Digital_t *)INPUT(d_in[i]);
|
||||
(*ip->info.in_fn)(&ip->info, i, &ival);
|
||||
in_vals[i] = ival;
|
||||
}
|
||||
|
||||
for (i = 0; i < ip->out_ports; ++i)
|
||||
OUTPUT_CHANGED(d_out[i]) = FALSE;
|
||||
|
||||
for (i = 0; i < ip->inout_ports; ++i) {
|
||||
Digital_t ival;
|
||||
|
||||
ival = *(Digital_t *)INPUT(d_inout[i]);
|
||||
(*ip->info.in_fn)(&ip->info, i + ip->in_ports, &ival);
|
||||
in_vals[i + ip->in_ports] = ival;
|
||||
OUTPUT_CHANGED(d_inout[i]) = FALSE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (CALL_TYPE == ANALOG) // Belt and braces
|
||||
return;
|
||||
|
||||
/* Check for pending output. */
|
||||
|
||||
if (ip->op_pending) {
|
||||
output(ip, XSPICE_ARG);
|
||||
} else {
|
||||
for (i = 0; i < ip->out_ports; ++i)
|
||||
OUTPUT_CHANGED(d_out[i]) = FALSE;
|
||||
for (i = 0; i < ip->inout_ports; ++i)
|
||||
OUTPUT_CHANGED(d_inout[i]) = FALSE;
|
||||
}
|
||||
|
||||
/* Check TIME as it may have gone backwards after a failed time-step. */
|
||||
|
||||
index = ip->q_index;
|
||||
while (index >= 0 && TIME < ip->q[index].when)
|
||||
--index;
|
||||
ip->q_index = index;
|
||||
|
||||
if (CALL_TYPE == EVENT) {
|
||||
struct pend_in input;
|
||||
unsigned int limit, max;
|
||||
|
||||
/* New input is expected here. */
|
||||
|
||||
input.when = TIME;
|
||||
|
||||
limit = ip->info.in_count + ip->info.inout_count;
|
||||
max = limit < ip->in_ports ? limit : ip->in_ports;
|
||||
limit -= max;
|
||||
|
||||
for (input.which = 0; input.which < max; ++input.which) {
|
||||
input.what = *(Digital_t *)INPUT(d_in[input.which]);
|
||||
if (check_input(ip, in_vals + input.which, &input)) {
|
||||
DBG("%.16g: IN %s %d/%d->%d/%d",
|
||||
TIME, cm_get_node_name("d_in", input.which),
|
||||
in_vals[input.which].state, in_vals[input.which].strength,
|
||||
input.what.state, input.what.strength);
|
||||
in_vals[input.which] = input.what;
|
||||
}
|
||||
}
|
||||
|
||||
if (limit > ip->inout_ports)
|
||||
limit = ip->inout_ports;
|
||||
for (i = 0; i < limit; ++i, ++input.which) {
|
||||
input.what = *(Digital_t *)INPUT(d_inout[i]);
|
||||
if (check_input(ip, in_vals + input.which, &input)) {
|
||||
DBG("%.16g: INout %s %d/%d->%d/%d",
|
||||
TIME, cm_get_node_name("d_inout", i),
|
||||
in_vals[input.which].state, in_vals[input.which].strength,
|
||||
input.what.state, input.what.strength);
|
||||
in_vals[ip->in_ports + i] = input.what;
|
||||
}
|
||||
}
|
||||
} else if (CALL_TYPE == STEP_PENDING) {
|
||||
/* The current timestep succeeded. Run the co-simulator code
|
||||
* forward, replaying any saved input events.
|
||||
*/
|
||||
|
||||
if (TIME <= ip->info.vtime)
|
||||
cm_message_printf("XSPICE time is behind vtime:\n"
|
||||
"XSPICE %.16g\n"
|
||||
"Cosim %.16g",
|
||||
TIME, ip->info.vtime);
|
||||
run(ip, XSPICE_ARG);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/* Copyright 2023 Giles Atkinson
|
||||
SUMMARY
|
||||
|
||||
This file contains the interface specification file for the
|
||||
d_cosim code model for general digital co-simulation.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
NAME_TABLE:
|
||||
|
||||
Spice_Model_Name: d_cosim
|
||||
C_Function_Name: ucm_d_cosim
|
||||
Description: "Bridge to an irreversible digital model"
|
||||
|
||||
PORT_TABLE:
|
||||
|
||||
Port_Name: d_in
|
||||
Description: "digital input"
|
||||
Direction: in
|
||||
Default_Type: d
|
||||
Allowed_Types: [d]
|
||||
Vector: yes
|
||||
Vector_Bounds: [0 -]
|
||||
Null_Allowed: yes
|
||||
|
||||
PORT_TABLE:
|
||||
|
||||
Port_Name: d_out
|
||||
Description: "digital output"
|
||||
Direction: out
|
||||
Default_Type: d
|
||||
Allowed_Types: [d]
|
||||
Vector: yes
|
||||
Vector_Bounds: [0 -]
|
||||
Null_Allowed: yes
|
||||
|
||||
PORT_TABLE:
|
||||
|
||||
Port_Name: d_inout
|
||||
Description: "digital bidirectional port"
|
||||
Direction: inout
|
||||
Default_Type: d
|
||||
Allowed_Types: [d]
|
||||
Vector: yes
|
||||
Vector_Bounds: [0 -]
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: delay
|
||||
Description: "output delay time"
|
||||
Data_Type: real
|
||||
Default_Value: 1.0e-9
|
||||
Limits: [1e-12 -]
|
||||
Vector: no
|
||||
Vector_bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: simulation
|
||||
Description: "A shared library containing a digital model"
|
||||
Data_Type: string
|
||||
Default_Value: -
|
||||
Limits: -
|
||||
Vector: no
|
||||
Vector_Bounds: -
|
||||
Null_Allowed: no
|
||||
|
||||
/* Instances maintain an internal input event queue that should be at least
|
||||
* as large as the number of inputs. Performance with clocked logic may
|
||||
* be improved by making it larger than (2 * F) / MTS, where F is
|
||||
* the clock frequency and MTS is the maximum timestep for .tran.
|
||||
*/
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: queue_size
|
||||
Description: "input queue size"
|
||||
Data_Type: int
|
||||
Default_Value: 128
|
||||
Limits: [1 -]
|
||||
Vector: no
|
||||
Vector_bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: irreversible
|
||||
Description: "Parameter passed to library function cm_irreversible()"
|
||||
Data_Type: int
|
||||
Default_Value: 1
|
||||
Limits: -
|
||||
Vector: no
|
||||
Vector_Bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
STATIC_VAR_TABLE:
|
||||
|
||||
Static_Var_Name: cosim_instance
|
||||
Data_Type: pointer
|
||||
Description: "Per-instance structure"
|
||||
|
|
@ -29,3 +29,4 @@ d_tff
|
|||
d_tristate
|
||||
d_xnor
|
||||
d_xor
|
||||
d_cosim
|
||||
|
|
|
|||
|
|
@ -322,6 +322,10 @@
|
|||
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="icm\digital\d_xor\d_xor-ifspec.c" />
|
||||
<ClCompile Include="icm\digital\d_cosim\d_cosim-ifspec.c" />
|
||||
<ClCompile Include="icm\digital\d_cosim\d_cosim-cfunc.c">
|
||||
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\xspice\icm\dlmain.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue