Browse Source
Add XSPICE code model d_cosim, a generic adaptor for digital cosimulation.
pre-master-46
Add XSPICE code model d_cosim, a generic adaptor for digital cosimulation.
pre-master-46
5 changed files with 756 additions and 0 deletions
-
71src/include/ngspice/cosim.h
-
578src/xspice/icm/digital/d_cosim/cfunc.mod
-
102src/xspice/icm/digital/d_cosim/ifspec.ifs
-
1src/xspice/icm/digital/modpath.lst
-
4visualc/xspice/digital.vcxproj
@ -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" |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue