261 lines
8.0 KiB
C
261 lines
8.0 KiB
C
/*
|
|
* This file is part of the OSDI component of NGSPICE.
|
|
* Copyright© 2022 SemiMod GmbH.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Author: Pascal Kuthe <pascal.kuthe@semimod.de>
|
|
*/
|
|
|
|
#include "ngspice/iferrmsg.h"
|
|
#include "ngspice/memory.h"
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/typedefs.h"
|
|
|
|
#include "osdi.h"
|
|
#include "osdidefs.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#define NUM_SIM_PARAMS 5
|
|
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};
|
|
|
|
/* values returned by $simparam*/
|
|
OsdiSimParas get_simparams(const CKTcircuit *ckt) {
|
|
double simulatorVersion = strtod(PACKAGE_VERSION, NULL);
|
|
double gdev = ckt->CKTgmin;
|
|
double sourceScaleFactor = ckt->CKTsrcFact;
|
|
double gmin = ((ckt->CKTgmin) > (ckt->CKTdiagGmin)) ? (ckt->CKTgmin)
|
|
: (ckt->CKTdiagGmin);
|
|
double sim_param_vals_[NUM_SIM_PARAMS] = {
|
|
gdev, gmin, ckt->CKTnomTemp, simulatorVersion, sourceScaleFactor};
|
|
memcpy(&sim_param_vals, &sim_param_vals_, sizeof(double) * NUM_SIM_PARAMS);
|
|
OsdiSimParas sim_params_ = {.names = sim_params,
|
|
.vals = (double *)&sim_param_vals,
|
|
.names_str = sim_params_str,
|
|
.vals_str = NULL};
|
|
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) {
|
|
|
|
NG_IGNORE(extra_inst_data);
|
|
|
|
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) {
|
|
GENmodel *gen_model;
|
|
GENinstance *gen_inst;
|
|
|
|
bool is_init_smsig = ckt->CKTmode & MODEINITSMSIG;
|
|
bool is_sweep = ckt->CKTmode & MODEDCTRANCURVE;
|
|
bool is_dc = ckt->CKTmode & (MODEDCOP | MODEDCTRANCURVE);
|
|
bool is_ac = ckt->CKTmode & (MODEAC | MODEINITSMSIG);
|
|
bool is_tran = ckt->CKTmode & (MODETRAN);
|
|
bool is_tran_op = ckt->CKTmode & (MODETRANOP);
|
|
bool is_init_tran = ckt->CKTmode & MODEINITTRAN;
|
|
bool is_init_junc = ckt->CKTmode & MODEINITJCT;
|
|
|
|
OsdiSimInfo sim_info = {
|
|
.paras = get_simparams(ckt),
|
|
.abstime = is_tran ? ckt->CKTtime : 0.0,
|
|
.prev_solve = ckt->CKTrhsOld,
|
|
.prev_state = ckt->CKTstates[0],
|
|
.next_state = ckt->CKTstates[0],
|
|
.flags = CALC_RESIST_JACOBIAN,
|
|
};
|
|
|
|
sim_info.flags |= CALC_OP;
|
|
|
|
if (is_dc) {
|
|
sim_info.flags |= ANALYSIS_DC | ANALYSIS_STATIC;
|
|
}
|
|
|
|
if (!is_init_smsig) {
|
|
sim_info.flags |= CALC_RESIST_RESIDUAL | ENABLE_LIM | CALC_RESIST_LIM_RHS;
|
|
}
|
|
|
|
if (is_tran) {
|
|
sim_info.flags |= CALC_REACT_JACOBIAN | CALC_REACT_RESIDUAL |
|
|
CALC_REACT_LIM_RHS | ANALYSIS_TRAN;
|
|
}
|
|
|
|
if (is_tran_op) {
|
|
sim_info.flags |= ANALYSIS_TRAN;
|
|
}
|
|
|
|
if (is_ac) {
|
|
sim_info.flags |= CALC_REACT_JACOBIAN | ANALYSIS_AC;
|
|
}
|
|
|
|
if (is_init_tran) {
|
|
sim_info.flags |= ANALYSIS_IC | ANALYSIS_STATIC;
|
|
}
|
|
|
|
if (is_init_junc) {
|
|
sim_info.flags |= INIT_LIM;
|
|
}
|
|
|
|
if (ckt->CKTmode & MODEACNOISE) {
|
|
sim_info.flags |= CALC_NOISE | ANALYSIS_NOISE;
|
|
}
|
|
|
|
OsdiRegistryEntry *entry = osdi_reg_entry_model(inModel);
|
|
const OsdiDescriptor *descr = entry->descriptor;
|
|
uint32_t eval_flags = 0;
|
|
|
|
#ifdef USE_OMP
|
|
int ret = OK;
|
|
|
|
/* 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);
|
|
|
|
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);
|
|
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* call to $fatal in Verilog-A abort simulation!*/
|
|
if (eval_flags & EVAL_RET_FLAG_FATAL) {
|
|
return E_PANIC;
|
|
}
|
|
|
|
if (eval_flags & EVAL_RET_FLAG_LIM) {
|
|
ckt->CKTnoncon++;
|
|
ckt->CKTtroubleElt = gen_inst;
|
|
}
|
|
|
|
if (eval_flags & EVAL_RET_FLAG_STOP) {
|
|
return E_PAUSE;
|
|
}
|
|
|
|
return OK;
|
|
}
|