|
|
|
@ -28,11 +28,10 @@ char *sim_params[NUM_SIM_PARAMS + 1] = { |
|
|
|
"gdev", "gmin", "tnom", "simulatorVersion", "sourceScaleFactor", NULL}; |
|
|
|
char *sim_params_str[1] = {NULL}; |
|
|
|
|
|
|
|
double sim_param_vals[NUM_SIM_PARAMS] = {0,0,0,0,0}; |
|
|
|
double sim_param_vals[NUM_SIM_PARAMS] = {0, 0, 0, 0, 0}; |
|
|
|
|
|
|
|
/* values returned by $simparam*/ |
|
|
|
OsdiSimParas get_simparams(const CKTcircuit *ckt) { |
|
|
|
|
|
|
|
double simulatorVersion = strtod(PACKAGE_VERSION, NULL); |
|
|
|
double gdev = ckt->CKTgmin; |
|
|
|
double sourceScaleFactor = ckt->CKTsrcFact; |
|
|
|
@ -48,6 +47,71 @@ OsdiSimParas get_simparams(const CKTcircuit *ckt) { |
|
|
|
return sim_params_; |
|
|
|
} |
|
|
|
|
|
|
|
static void eval(const OsdiDescriptor *descr, const GENinstance *gen_inst, |
|
|
|
void *inst, OsdiExtraInstData *extra_inst_data, |
|
|
|
const void *model, const OsdiSimInfo *sim_info) { |
|
|
|
|
|
|
|
OsdiNgspiceHandle handle = |
|
|
|
(OsdiNgspiceHandle){.kind = 3, .name = gen_inst->GENname}; |
|
|
|
/* TODO initial conditions? */ |
|
|
|
extra_inst_data->eval_flags = descr->eval(&handle, inst, model, sim_info); |
|
|
|
} |
|
|
|
|
|
|
|
static void load(CKTcircuit *ckt, const GENinstance *gen_inst, void *model, |
|
|
|
void *inst, OsdiExtraInstData *extra_inst_data, bool is_tran, |
|
|
|
bool is_init_tran, const OsdiDescriptor *descr) { |
|
|
|
double dump; |
|
|
|
if (is_tran) { |
|
|
|
/* load dc matrix and capacitances (charge derivative multiplied with |
|
|
|
* CKTag[0]) */ |
|
|
|
descr->load_jacobian_tran(inst, model, ckt->CKTag[0]); |
|
|
|
|
|
|
|
/* load static rhs and dynamic linearized rhs (SUM Vb * dIa/dVb)*/ |
|
|
|
descr->load_spice_rhs_tran(inst, model, ckt->CKTrhs, ckt->CKTrhsOld, |
|
|
|
ckt->CKTag[0]); |
|
|
|
|
|
|
|
uint32_t *node_mapping = |
|
|
|
(uint32_t *)(((char *)inst) + descr->node_mapping_offset); |
|
|
|
|
|
|
|
/* use numeric integration to obtain the remainer of the RHS*/ |
|
|
|
int state = gen_inst->GENstate + (int)descr->num_states; |
|
|
|
for (uint32_t i = 0; i < descr->num_nodes; i++) { |
|
|
|
if (descr->nodes[i].react_residual_off != UINT32_MAX) { |
|
|
|
|
|
|
|
double residual_react = |
|
|
|
*((double *)(((char *)inst) + descr->nodes[i].react_residual_off)); |
|
|
|
|
|
|
|
/* store charges in state vector*/ |
|
|
|
ckt->CKTstate0[state] = residual_react; |
|
|
|
if (is_init_tran) { |
|
|
|
ckt->CKTstate1[state] = residual_react; |
|
|
|
} |
|
|
|
|
|
|
|
/* we only care about the numeric integration itself not ceq/geq |
|
|
|
because those are already calculated by load_jacobian_tran and |
|
|
|
load_spice_rhs_tran*/ |
|
|
|
NIintegrate(ckt, &dump, &dump, 0, state); |
|
|
|
|
|
|
|
/* add the numeric derivative to the rhs */ |
|
|
|
ckt->CKTrhs[node_mapping[i]] -= ckt->CKTstate0[state + 1]; |
|
|
|
|
|
|
|
if (is_init_tran) { |
|
|
|
ckt->CKTstate1[state + 1] = ckt->CKTstate0[state + 1]; |
|
|
|
} |
|
|
|
|
|
|
|
state += 2; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
/* copy internal derivatives into global matrix */ |
|
|
|
descr->load_jacobian_resist(inst, model); |
|
|
|
|
|
|
|
/* calculate spice RHS from internal currents and store into global RHS |
|
|
|
*/ |
|
|
|
descr->load_spice_rhs_dc(inst, model, ckt->CKTrhs, ckt->CKTrhsOld); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
extern int OSDIload(GENmodel *inModel, CKTcircuit *ckt) { |
|
|
|
OsdiNgspiceHandle handle; |
|
|
|
GENmodel *gen_model; |
|
|
|
@ -113,6 +177,35 @@ extern int OSDIload(GENmodel *inModel, CKTcircuit *ckt) { |
|
|
|
|
|
|
|
OsdiRegistryEntry *entry = osdi_reg_entry_model(inModel); |
|
|
|
const OsdiDescriptor *descr = entry->descriptor; |
|
|
|
uint32_t eval_flags = 0; |
|
|
|
|
|
|
|
#ifdef USE_OMP |
|
|
|
/* use openmp 3.0 tasks to parallelize linked list transveral */ |
|
|
|
#pragma omp parallel |
|
|
|
#pragma omp single |
|
|
|
{ |
|
|
|
for (gen_model = inModel; gen_model; gen_model = gen_model->GENnextModel) { |
|
|
|
void *model = osdi_model_data(gen_model); |
|
|
|
|
|
|
|
for (gen_inst = gen_model->GENinstances; gen_inst; |
|
|
|
gen_inst = gen_inst->GENnextInstance) { |
|
|
|
|
|
|
|
void *inst = osdi_instance_data(entry, gen_inst); |
|
|
|
|
|
|
|
OsdiExtraInstData *extra_inst_data = |
|
|
|
osdi_extra_instance_data(entry, gen_inst); |
|
|
|
|
|
|
|
#pragma omp task firstprivate(gen_inst, inst, extra_inst_data, model) |
|
|
|
eval(descr, gen_inst, inst, extra_inst_data, model, &sim_info); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* init small signal analysis does not require loading values into |
|
|
|
* matrix/rhs*/ |
|
|
|
if (is_init_smsig) { |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
for (gen_model = inModel; gen_model; gen_model = gen_model->GENnextModel) { |
|
|
|
void *model = osdi_model_data(gen_model); |
|
|
|
@ -120,92 +213,49 @@ extern int OSDIload(GENmodel *inModel, CKTcircuit *ckt) { |
|
|
|
for (gen_inst = gen_model->GENinstances; gen_inst; |
|
|
|
gen_inst = gen_inst->GENnextInstance) { |
|
|
|
void *inst = osdi_instance_data(entry, gen_inst); |
|
|
|
OsdiExtraInstData *extra_inst_data = |
|
|
|
osdi_extra_instance_data(entry, gen_inst); |
|
|
|
load(ckt, gen_inst, model, inst, extra_inst_data, is_tran, is_init_tran, |
|
|
|
descr); |
|
|
|
eval_flags |= extra_inst_data->eval_flags; |
|
|
|
} |
|
|
|
} |
|
|
|
#else |
|
|
|
for (gen_model = inModel; gen_model; gen_model = gen_model->GENnextModel) { |
|
|
|
void *model = osdi_model_data(gen_model); |
|
|
|
|
|
|
|
/* hpyothetically this could run in parallel we do not write any shared |
|
|
|
data here*/ |
|
|
|
handle = (OsdiNgspiceHandle){.kind = 3, .name = gen_inst->GENname}; |
|
|
|
/* TODO initial conditions? */ |
|
|
|
uint32_t ret_flags = descr->eval(&handle, inst, model, &sim_info); |
|
|
|
|
|
|
|
/* call to $fatal in Verilog-A abort!*/ |
|
|
|
if (ret_flags & EVAL_RET_FLAG_FATAL) { |
|
|
|
return E_PANIC; |
|
|
|
} |
|
|
|
|
|
|
|
/* init small signal analysis does not require loading values into |
|
|
|
* matrix/rhs*/ |
|
|
|
if (is_init_smsig) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
for (gen_inst = gen_model->GENinstances; gen_inst; |
|
|
|
gen_inst = gen_inst->GENnextInstance) { |
|
|
|
void *inst = osdi_instance_data(entry, gen_inst); |
|
|
|
|
|
|
|
/* handle calls to $finish, $limit, $stop |
|
|
|
* TODO actually do something with extra_inst_data->finish and |
|
|
|
* extra_inst_data->limt |
|
|
|
* */ |
|
|
|
OsdiExtraInstData *extra_inst_data = |
|
|
|
osdi_extra_instance_data(entry, gen_inst); |
|
|
|
if (ret_flags & EVAL_RET_FLAG_FINISH) { |
|
|
|
extra_inst_data->finish = true; |
|
|
|
} |
|
|
|
if (ret_flags & EVAL_RET_FLAG_LIM) { |
|
|
|
ckt->CKTnoncon++; |
|
|
|
ckt->CKTtroubleElt = gen_inst; |
|
|
|
eval(descr, gen_inst, inst, extra_inst_data, model, &sim_info); |
|
|
|
|
|
|
|
/* init small signal analysis does not require loading values into |
|
|
|
* matrix/rhs*/ |
|
|
|
if (!is_init_smsig) { |
|
|
|
load(ckt, gen_inst, model, inst, extra_inst_data, is_tran, is_init_tran, |
|
|
|
descr); |
|
|
|
eval_flags |= extra_inst_data->eval_flags; |
|
|
|
} |
|
|
|
if (ret_flags & EVAL_RET_FLAG_STOP) { |
|
|
|
ret = (E_PAUSE); |
|
|
|
} |
|
|
|
|
|
|
|
if (is_tran) { |
|
|
|
/* load dc matrix and capacitances (charge derivative multiplied with |
|
|
|
* CKTag[0]) */ |
|
|
|
descr->load_jacobian_tran(inst, model, ckt->CKTag[0]); |
|
|
|
|
|
|
|
/* load static rhs and dynamic linearized rhs (SUM Vb * dIa/dVb)*/ |
|
|
|
descr->load_spice_rhs_tran(inst, model, ckt->CKTrhs, ckt->CKTrhsOld, |
|
|
|
ckt->CKTag[0]); |
|
|
|
|
|
|
|
uint32_t *node_mapping = |
|
|
|
(uint32_t *)(((char *)inst) + descr->node_mapping_offset); |
|
|
|
|
|
|
|
/* use numeric integration to obtain the remainer of the RHS*/ |
|
|
|
int state = gen_inst->GENstate + (int) descr->num_states; |
|
|
|
for (uint32_t i = 0; i < descr->num_nodes; i++) { |
|
|
|
if (descr->nodes[i].react_residual_off != UINT32_MAX) { |
|
|
|
|
|
|
|
double residual_react = *(( |
|
|
|
double *)(((char *)inst) + descr->nodes[i].react_residual_off)); |
|
|
|
|
|
|
|
/* store charges in state vector*/ |
|
|
|
ckt->CKTstate0[state] = residual_react; |
|
|
|
if (is_init_tran) { |
|
|
|
ckt->CKTstate1[state] = residual_react; |
|
|
|
} |
|
|
|
|
|
|
|
/* we only care about the numeric integration itself not ceq/geq |
|
|
|
because those are already calculated by load_jacobian_tran and |
|
|
|
load_spice_rhs_tran*/ |
|
|
|
NIintegrate(ckt, &dump, &dump, 0, state); |
|
|
|
|
|
|
|
/* add the numeric derivative to the rhs */ |
|
|
|
ckt->CKTrhs[node_mapping[i]] -= ckt->CKTstate0[state + 1]; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
if (is_init_tran) { |
|
|
|
ckt->CKTstate1[state + 1] = ckt->CKTstate0[state + 1]; |
|
|
|
} |
|
|
|
/* call to $fatal in Verilog-A abort simulation!*/ |
|
|
|
if (eval_flags & EVAL_RET_FLAG_FATAL) { |
|
|
|
return E_PANIC; |
|
|
|
} |
|
|
|
|
|
|
|
state += 2; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
/* copy internal derivatives into global matrix */ |
|
|
|
descr->load_jacobian_resist(inst, model); |
|
|
|
if (eval_flags & EVAL_RET_FLAG_LIM) { |
|
|
|
ckt->CKTnoncon++; |
|
|
|
ckt->CKTtroubleElt = gen_inst; |
|
|
|
} |
|
|
|
|
|
|
|
/* calculate spice RHS from internal currents and store into global RHS |
|
|
|
*/ |
|
|
|
descr->load_spice_rhs_dc(inst, model, ckt->CKTrhs, ckt->CKTrhsOld); |
|
|
|
} |
|
|
|
} |
|
|
|
if (eval_flags & EVAL_RET_FLAG_STOP) { |
|
|
|
return E_PAUSE; |
|
|
|
} |
|
|
|
return ret; |
|
|
|
|
|
|
|
return OK; |
|
|
|
} |