ngspice/test_cases/node_collapsing/diode.c

832 lines
22 KiB
C
Raw Normal View History

prototype for Verilog-A integration using OSDI and OpenVAF This initial prototype is capable of performing DC, transient and AC analysis. Not all features of OSDI are supported yet and there are still some open questions regarding ngspice integration. However many usecase already work very well and a large amount of CMC models are supported. The biggest missing feature right now is noise analysis. test: test case for diode DC working with SH test: add transient analysis to osdi_diode test test: added docu text to osdi_diode test test: added test case directories fix: bug in osdi_load test: small change to netlist fix: implement DEVunsetup fix: correct behaviour for MODEINITSMSIG test: osdi diode enable all analysis modes removed netlist ignoring test results added the build of the diode shared object to the python test script deleting old stuff and always rebuilding the shared object added diode_va.c to the repo preparing CI Create .gitlab-ci.yml file (testing) add res, cap and multiple devices test feat: use osdi command to load files Previously OSDI shared object files were loaded from fixed directories. This was unreliable, inconvenient and caused conflicts with XSPICE. This commit remove the old loading mechanism and instead introduces the `osdi` command that can load (a list of) osdi object files (like the codemodel command for XSPICE). A typical usecase will use this as a precommand in the netlist: .control pre_osdi foo.osdi .endc If the specified file is a relative path it is first resolved relative to the parent directory of the netlist. If the osdi command is invoked from the interactive prompt the file is resolved relative to the current working directory instead. This commit also moves osdi from the devices folder to the root src folder like xspice. This better reflects the role of the code as users may otherthwise (mistakenly) assume that osdi is just another handwritten model. test: update tests to new command fix: do not ignore first parameter feat: implement log message callback fix: don't generate ddt matrix/rhs in DC sweep fix: missing linker script update to osdi 0.3 (testing) simplify test cases, fix bug (testing) multiple devices test improvement (testig) node collapsing bugfix test: increase tolerance in tests feat: update to newest OSDI header fix: temperature update dt behaviour fix: ignored models fix: compilation script fix: allow hicum/l2 to compile with older c++ compilers fix: set required compiler flags for osdi fix: disable x by default fix: add missing SPICE functions fix: update diode to latest ngspice version feat: implement python CMC test runner doc: Add README_OSDI.md fix: make testing script work with python version before 3.9 fix: free of undefined local variable fix: do not calculate time derivative during tran op update osdi version fixes for compilation on windows
2022-04-20 18:12:10 +02:00
/*
* 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>
*
* This is an exemplary implementation of the OSDI interface for the Verilog-A
* model specified in diode.va. In the future, the OpenVAF compiler shall
* generate an comparable object file. Primary purpose of this is example to
* have a concrete example for the OSDI interface, OpenVAF will generate a more
* optimized implementation.
*
*/
#include "osdi.h"
#include "string.h"
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
// public interface
extern uint32_t OSDI_VERSION_MAJOR;
extern uint32_t OSDI_VERSION_MINOR;
extern uint32_t OSDI_NUM_DESCRIPTORS;
extern OsdiDescriptor OSDI_DESCRIPTORS[1];
// number of nodes and definitions of node ids for nicer syntax in this file
// note: order should be same as "nodes" list defined later
#define NUM_NODES 4
#define A 0
#define C 1
#define TNODE 2
#define CI 3
#define NUM_COLLAPSIBLE 2
// number of matrix entries and definitions for Jacobian entries for nicer
// syntax in this file
#define NUM_MATRIX 14
#define CI_CI 0
#define CI_C 1
#define C_CI 2
#define C_C 3
#define A_A 4
#define A_CI 5
#define CI_A 6
#define A_TNODE 7
#define C_TNODE 8
#define CI_TNODE 9
#define TNODE_TNODE 10
#define TNODE_A 11
#define TNODE_C 12
#define TNODE_CI 13
// The model structure for the diode
typedef struct DiodeModel
{
double Rs;
bool Rs_given;
double Is;
bool Is_given;
double zetars;
bool zetars_given;
double N;
bool N_given;
double Cj0;
bool Cj0_given;
double Vj;
bool Vj_given;
double M;
bool M_given;
double Rth;
bool Rth_given;
double zetarth;
bool zetarth_given;
double zetais;
bool zetais_given;
double Tnom;
bool Tnom_given;
double mfactor; // multiplication factor for parallel devices
bool mfactor_given;
// InitError errors[MAX_ERROR_NUM],
} DiodeModel;
// The instace structure for the diode
typedef struct DiodeInstace
{
double mfactor; // multiplication factor for parallel devices
bool mfactor_given;
double temperature;
double rhs_resist[NUM_NODES];
double rhs_react[NUM_NODES];
double jacobian_resist[NUM_MATRIX];
double jacobian_react[NUM_MATRIX];
bool is_collapsible[NUM_COLLAPSIBLE];
double *jacobian_ptr_resist[NUM_MATRIX];
double *jacobian_ptr_react[NUM_MATRIX];
uint32_t node_off[NUM_NODES];
} DiodeInstace;
#define EXP_LIM 80.0
double limexp(double x)
{
if (x < EXP_LIM)
{
return exp(x);
}
else
{
return exp(EXP_LIM) * (x + 1 - EXP_LIM);
}
}
double dlimexp(double x)
{
if (x < EXP_LIM)
{
return exp(x);
}
else
{
return exp(EXP_LIM);
}
}
// implementation of the access function as defined by the OSDI spec
void *osdi_access(void *inst_, void *model_, uint32_t id, uint32_t flags)
{
DiodeModel *model = (DiodeModel *)model_;
DiodeInstace *inst = (DiodeInstace *)inst_;
bool *given;
void *value;
switch (id) // id of params defined in param_opvar array
{
case 0:
if (flags & ACCESS_FLAG_INSTANCE)
{
value = (void *)&inst->mfactor;
given = &inst->mfactor_given;
}
else
{
value = (void *)&model->mfactor;
given = &model->mfactor_given;
}
break;
case 1:
value = (void *)&model->Rs;
given = &model->Rs_given;
break;
case 2:
value = (void *)&model->Is;
given = &model->Is_given;
break;
case 3:
value = (void *)&model->zetars;
given = &model->zetars_given;
break;
case 4:
value = (void *)&model->N;
given = &model->N_given;
break;
case 5:
value = (void *)&model->Cj0;
given = &model->Cj0_given;
break;
case 6:
value = (void *)&model->Vj;
given = &model->Vj_given;
break;
case 7:
value = (void *)&model->M;
given = &model->M_given;
break;
case 8:
value = &model->Rth;
given = &model->Rth_given;
break;
case 9:
value = (void *)&model->zetarth;
given = &model->zetarth_given;
break;
case 10:
value = (void *)&model->zetais;
given = &model->zetais_given;
break;
case 11:
value = (void *)&model->Tnom;
given = &model->Tnom_given;
break;
default:
return NULL;
}
if (flags & ACCESS_FLAG_SET)
{
*given = true;
}
return value;
}
// implementation of the setup_model function as defined in the OSDI spec
OsdiInitInfo setup_model(void *_handle, void *model_)
{
DiodeModel *model = (DiodeModel *)model_;
// set parameters and check bounds
if (!model->mfactor_given)
{
model->mfactor = 1.0;
}
if (!model->Rs_given)
{
model->Rs = 1e-9;
}
if (!model->Is_given)
{
model->Is = 1e-14;
}
if (!model->zetars_given)
{
model->zetars = 0;
}
if (!model->N_given)
{
model->N = 1;
}
if (!model->Cj0_given)
{
model->Cj0 = 0;
}
if (!model->Vj_given)
{
model->Vj = 1.0;
}
if (!model->M_given)
{
model->M = 0.5;
}
if (!model->Rth_given)
{
model->Rth = 0;
}
if (!model->zetarth_given)
{
model->zetarth = 0;
}
if (!model->zetais_given)
{
model->zetais = 0;
}
if (!model->Tnom_given)
{
model->Tnom = 300;
}
return (OsdiInitInfo){.flags = 0, .num_errors = 0, .errors = NULL};
}
// implementation of the setup_instace function as defined in the OSDI spec
OsdiInitInfo setup_instance(void *_handle, void *inst_, void *model_,
double temperature, uint32_t _num_terminals)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
DiodeModel *model = (DiodeModel *)model_;
// Here the logic for node collapsing ist implemented. The indices in this list must adhere to the "collapsible" List of node pairs.
if (model->Rs<1e-9){ // Rs between Ci C
inst->is_collapsible[0] = true;
}
if (model->Rth<1e-9){ // Rs between Ci C
inst->is_collapsible[1] = true;
}
if (!inst->mfactor_given)
{
if (model->mfactor_given)
{
inst->mfactor = model->mfactor;
}
else
{
inst->mfactor = 1;
}
}
inst->temperature = temperature;
return (OsdiInitInfo){.flags = 0, .num_errors = 0, .errors = NULL};
}
// implementation of the eval function as defined in the OSDI spec
uint32_t eval(void *handle, void *inst_, void *model_, uint32_t flags,
double *prev_solve, OsdiSimParas *sim_params)
{
DiodeModel *model = (DiodeModel *)model_;
DiodeInstace *inst = (DiodeInstace *)inst_;
// get voltages
double va = prev_solve[inst->node_off[A]];
double vc = prev_solve[inst->node_off[C]];
double vci = prev_solve[inst->node_off[CI]];
double vdtj = prev_solve[inst->node_off[TNODE]];
double vcic = vci - vc;
double vaci = va - vci;
double gmin = 1e-12;
for (int i = 0; sim_params->names[i] != NULL; i++)
{
if (strcmp(sim_params->names[i], "gmin") == 0)
{
gmin = sim_params->vals[i];
}
}
////////////////////////////////
// evaluate model equations
////////////////////////////////
// temperature update
double pk = 1.3806503e-23;
double pq = 1.602176462e-19;
double t_dev = inst->temperature + vdtj;
double tdev_tnom = t_dev / model->Tnom;
double rs_t = model->Rs * powf(tdev_tnom, model->zetars);
double rth_t = model->Rth * powf(tdev_tnom, model->zetarth);
double is_t = model->Is * powf(tdev_tnom, model->zetais);
double vt = t_dev * pk / pq;
// derivatives w.r.t. temperature
double rs_dt = model->zetars * model->Rs *
powf(tdev_tnom, model->zetars - 1.0) / model->Tnom;
double rth_dt = model->zetarth * model->Rth *
powf(tdev_tnom, model->zetarth - 1.0) / model->Tnom;
double is_dt = model->zetais * model->Is *
powf(tdev_tnom, model->zetais - 1.0) / model->Tnom;
double vt_tj = pk / pq;
// evaluate model equations and calculate all derivatives
// diode current
double id = is_t * (limexp(vaci / (model->N * vt)) - 1.0);
double gd = is_t / vt * dlimexp(vaci / (model->N * vt));
double gdt = -is_t * dlimexp(vaci / (model->N * vt)) * vaci / model->N / vt /
vt * vt_tj +
1.0 * exp((vaci / (model->N * vt)) - 1.0) * is_dt;
// resistor
double irs = 0;
double g = 0;
double grt = 0;
if (!inst->is_collapsible[0]) {
irs = vcic / rs_t;
g = 1.0 / rs_t;
grt = -irs / rs_t * rs_dt;
}
// thermal resistance
double irth = 0;
double gt = 0;
if (!inst->is_collapsible[1]) {
irth = vdtj / rth_t;
gt = 1.0 / rth_t - irth / rth_t * rth_dt;
}
// charge
double vf = model->Vj * (1.0 - powf(3.04, -1.0 / model->M));
double x = (vf - vaci) / vt;
double x_vt = -x / vt;
double x_dtj = x_vt * vt_tj;
double x_vaci = -1.0 / vt;
double y = sqrt(x * x + 1.92);
double y_x = 0.5 / y * 2.0 * x;
double y_vaci = y_x * x_vaci;
double y_dtj = y_x * x_dtj;
double vd = vf - vt * (x + y) / (2.0);
double vd_x = -vt / 2.0;
double vd_y = -vt / 2.0;
double vd_vt = -(x + y) / (2.0);
double vd_dtj = vd_x * x_dtj + vd_y * y_dtj + vd_vt * vt_tj;
double vd_vaci = vd_x * x_vaci + vd_y * y_vaci;
double qd = model->Cj0 * vaci * model->Vj *
(1.0 - powf(1.0 - vd / model->Vj, 1.0 - model->M)) /
(1.0 - model->M);
double qd_vd = model->Cj0 * model->Vj / (1.0 - model->M) * (1.0 - model->M) *
powf(1.0 - vd / model->Vj, 1.0 - model->M - 1.0) / model->Vj;
double qd_dtj = qd_vd * vd_dtj;
double qd_vaci = qd_vd * vd_vaci;
// thermal power source = current source
double ith = id * vaci ;
double ith_vtj = gdt * vaci ;
double ith_vcic = 0;
double ith_vaci = gd * vaci + id;
if (!inst->is_collapsible[0]) {
ith_vcic = 2.0 * vcic / rs_t;
ith += powf(vcic, 2.0) / rs_t;
ith_vtj -= - powf(vcic, 2.0) / rs_t / rs_t * rs_dt;
}
id += gmin * vaci;
gd += gmin;
double mfactor = inst->mfactor;
////////////////
// write rhs
////////////////
if (flags & CALC_RESIST_RESIDUAL)
{
// write resist rhs
inst->rhs_resist[A] = id * mfactor;
inst->rhs_resist[CI] = -id * mfactor + irs * mfactor;
inst->rhs_resist[C] = -irs * mfactor;
inst->rhs_resist[TNODE] = -ith * mfactor + irth * mfactor;
}
if (flags & CALC_REACT_RESIDUAL)
{
// write react rhs
inst->rhs_react[A] = qd * mfactor;
inst->rhs_react[CI] = -qd * mfactor;
}
//////////////////
// write Jacobian
//////////////////
if (flags & CALC_RESIST_JACOBIAN)
{
// stamp diode (current flowing from Ci into A)
inst->jacobian_resist[A_A] = gd * mfactor;
inst->jacobian_resist[A_CI] = -gd * mfactor;
inst->jacobian_resist[CI_A] = -gd * mfactor;
inst->jacobian_resist[CI_CI] = gd * mfactor;
// diode thermal
inst->jacobian_resist[A_TNODE] = gdt * mfactor;
inst->jacobian_resist[CI_TNODE] = -gdt * mfactor;
// stamp resistor (current flowing from C into CI)
inst->jacobian_resist[CI_CI] += g * mfactor;
inst->jacobian_resist[CI_C] = -g * mfactor;
inst->jacobian_resist[C_CI] = -g * mfactor;
inst->jacobian_resist[C_C] = g * mfactor;
// resistor thermal
inst->jacobian_resist[CI_TNODE] = grt * mfactor;
inst->jacobian_resist[C_TNODE] = -grt * mfactor;
// stamp rth flowing into node dTj
inst->jacobian_resist[TNODE_TNODE] = gt * mfactor;
// stamp ith flowing out of T node
inst->jacobian_resist[TNODE_TNODE] -= ith_vtj * mfactor;
inst->jacobian_resist[TNODE_CI] = (ith_vcic - ith_vaci) * mfactor;
inst->jacobian_resist[TNODE_C] = -ith_vcic * mfactor;
inst->jacobian_resist[TNODE_A] = ith_vaci * mfactor;
}
if (flags & CALC_REACT_JACOBIAN)
{
// write react matrix
// stamp Qd between nodes A and Ci depending also on dT
inst->jacobian_react[A_A] = qd_vaci * mfactor;
inst->jacobian_react[A_CI] = -qd_vaci * mfactor;
inst->jacobian_react[CI_A] = -qd_vaci * mfactor;
inst->jacobian_react[CI_CI] = qd_vaci * mfactor;
inst->jacobian_react[A_TNODE] = qd_dtj * mfactor;
inst->jacobian_react[CI_TNODE] = -qd_dtj * mfactor;
}
return 0;
}
// TODO implementation of the load_noise function as defined in the OSDI spec
void load_noise(void *inst, void *model, double freq, double *noise_dens,
double *ln_noise_dens)
{
// TODO add noise to example
}
#define LOAD_RHS_RESIST(name) \
dst[inst->node_off[name]] += inst->rhs_resist[name];
// implementation of the load_rhs_resist function as defined in the OSDI spec
void load_residual_resist(void *inst_, double *dst)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
LOAD_RHS_RESIST(A)
LOAD_RHS_RESIST(CI)
LOAD_RHS_RESIST(C)
LOAD_RHS_RESIST(TNODE)
}
#define LOAD_RHS_REACT(name) dst[inst->node_off[name]] += inst->rhs_react[name];
// implementation of the load_rhs_react function as defined in the OSDI spec
void load_residual_react(void *inst_, double *dst)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
LOAD_RHS_REACT(A)
LOAD_RHS_REACT(CI)
}
#define LOAD_MATRIX_RESIST(name) \
*inst->jacobian_ptr_resist[name] += inst->jacobian_resist[name];
// implementation of the load_matrix_resist function as defined in the OSDI spec
void load_jacobian_resist(void *inst_)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
LOAD_MATRIX_RESIST(A_A)
LOAD_MATRIX_RESIST(A_CI)
LOAD_MATRIX_RESIST(A_TNODE)
LOAD_MATRIX_RESIST(CI_A)
LOAD_MATRIX_RESIST(CI_CI)
LOAD_MATRIX_RESIST(CI_C)
LOAD_MATRIX_RESIST(CI_TNODE)
LOAD_MATRIX_RESIST(C_CI)
LOAD_MATRIX_RESIST(C_C)
LOAD_MATRIX_RESIST(C_TNODE)
LOAD_MATRIX_RESIST(TNODE_TNODE)
LOAD_MATRIX_RESIST(TNODE_A)
LOAD_MATRIX_RESIST(TNODE_C)
LOAD_MATRIX_RESIST(TNODE_CI)
}
#define LOAD_MATRIX_REACT(name) \
*inst->jacobian_ptr_react[name] += inst->jacobian_react[name] * alpha;
// implementation of the load_matrix_react function as defined in the OSDI spec
void load_jacobian_react(void *inst_, double alpha)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
LOAD_MATRIX_REACT(A_A)
LOAD_MATRIX_REACT(A_CI)
LOAD_MATRIX_REACT(CI_A)
LOAD_MATRIX_REACT(CI_CI)
LOAD_MATRIX_REACT(A_TNODE)
LOAD_MATRIX_REACT(CI_TNODE)
}
#define LOAD_MATRIX_TRAN(name) \
*inst->jacobian_ptr_resist[name] += inst->jacobian_react[name] * alpha;
// implementation of the load_matrix_tran function as defined in the OSDI spec
void load_jacobian_tran(void *inst_, double alpha)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
// set dc stamps
load_jacobian_resist(inst_);
// add reactive contributions
LOAD_MATRIX_TRAN(A_A)
LOAD_MATRIX_TRAN(A_CI)
LOAD_MATRIX_TRAN(CI_A)
LOAD_MATRIX_TRAN(CI_CI)
LOAD_MATRIX_TRAN(A_TNODE)
LOAD_MATRIX_TRAN(CI_TNODE)
}
// implementation of the load_spice_rhs_dc function as defined in the OSDI spec
void load_spice_rhs_dc(void *inst_, double *dst, double *prev_solve)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
double va = prev_solve[inst->node_off[A]];
double vci = prev_solve[inst->node_off[CI]];
double vc = prev_solve[inst->node_off[C]];
double vdtj = prev_solve[inst->node_off[TNODE]];
dst[inst->node_off[A]] += inst->jacobian_resist[A_A] * va +
inst->jacobian_resist[A_TNODE] * vdtj +
inst->jacobian_resist[A_CI] * vci -
inst->rhs_resist[A];
dst[inst->node_off[CI]] += inst->jacobian_resist[CI_A] * va +
inst->jacobian_resist[CI_TNODE] * vdtj +
inst->jacobian_resist[CI_CI] * vci -
inst->rhs_resist[CI];
dst[inst->node_off[C]] += inst->jacobian_resist[C_C] * vc +
inst->jacobian_resist[C_CI] * vci +
inst->jacobian_resist[C_TNODE] * vdtj -
inst->rhs_resist[C];
dst[inst->node_off[TNODE]] += inst->jacobian_resist[TNODE_A] * va +
inst->jacobian_resist[TNODE_C] * vc +
inst->jacobian_resist[TNODE_CI] * vci +
inst->jacobian_resist[TNODE_TNODE] * vdtj -
inst->rhs_resist[TNODE];
}
// implementation of the load_spice_rhs_tran function as defined in the OSDI
// spec
void load_spice_rhs_tran(void *inst_, double *dst, double *prev_solve,
double alpha)
{
DiodeInstace *inst = (DiodeInstace *)inst_;
double va = prev_solve[inst->node_off[A]];
double vci = prev_solve[inst->node_off[CI]];
double vdtj = prev_solve[inst->node_off[TNODE]];
// set DC rhs
load_spice_rhs_dc(inst_, dst, prev_solve);
// add contributions due to reactive elements
dst[inst->node_off[A]] +=
alpha * (inst->jacobian_react[A_A] * va +
inst->jacobian_react[A_CI] * vci +
inst->jacobian_react[A_TNODE] * vdtj);
dst[inst->node_off[CI]] += alpha * (inst->jacobian_react[CI_CI] * vci +
inst->jacobian_react[CI_A] * va +
inst->jacobian_react[CI_TNODE] * vdtj);
}
// structure that provides information of all nodes of the model
OsdiNode nodes[NUM_NODES] = {
{.name = "A", .units = "V", .is_reactive = true},
{.name = "C", .units = "V"},
{.name = "dT", .units = "K"},
{.name = "CI", .units = "V", .is_reactive = true},
};
// boolean array that tells which Jacobian entries are constant. Nothing is
// constant with selfheating, though.
bool const_jacobian_entries[NUM_MATRIX] = {};
// these node pairs specify which entries in the Jacobian must be accounted for
OsdiNodePair jacobian_entries[NUM_MATRIX] = {
{CI, CI},
{CI, C},
{C, CI},
{C, C},
{A, A},
{A, CI},
{CI, A},
{A, TNODE},
{C, TNODE},
{CI, TNODE},
{TNODE, TNODE},
{TNODE, A},
{TNODE, C},
{TNODE, CI},
};
OsdiNodePair collapsible[NUM_COLLAPSIBLE] = {
{CI, C},
{TNODE, NUM_NODES},
};
#define NUM_PARAMS 12
// the model parameters as defined in Verilog-A, bounds and default values are
// stored elsewhere as they may depend on model parameters etc.
OsdiParamOpvar params[NUM_PARAMS] = {
{
.name = (char *[]){"$mfactor"},
.num_alias = 0,
.description = "Verilog-A multiplication factor for parallel devices",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_INST,
.len = 0,
},
{
.name = (char *[]){"Rs"},
.num_alias = 0,
.description = "Ohmic res",
.units = "Ohm",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"Is"},
.num_alias = 0,
.description = "Saturation current",
.units = "A",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"zetars"},
.num_alias = 0,
.description = "Temperature coefficient of ohmic res",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"N"},
.num_alias = 0,
.description = "Emission coefficient",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"Cj0"},
.num_alias = 0,
.description = "Junction capacitance",
.units = "F",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"Vj"},
.num_alias = 0,
.description = "Junction potential",
.units = "V",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"M"},
.num_alias = 0,
.description = "Grading coefficient",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"Rth"},
.num_alias = 0,
.description = "Thermal resistance",
.units = "K/W",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"zetarth"},
.num_alias = 0,
.description = "Temperature coefficient of thermal res",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"zetais"},
.num_alias = 0,
.description = "Temperature coefficient of Is",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
{
.name = (char *[]){"Tnom"},
.num_alias = 0,
.description = "Reference temperature",
.units = "",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
};
// fill exported data
uint32_t OSDI_VERSION_MAJOR = OSDI_VERSION_MAJOR_CURR;
uint32_t OSDI_VERSION_MINOR = OSDI_VERSION_MINOR_CURR;
uint32_t OSDI_NUM_DESCRIPTORS = 1;
// this is the main structure used by simulators, it gives access to all
// information in a model
OsdiDescriptor OSDI_DESCRIPTORS[1] = {{
// metadata
.name = "diode_va",
// nodes
.num_nodes = NUM_NODES,
.num_terminals = 3,
.nodes = (OsdiNode *)&nodes,
// matrix entries
.num_jacobian_entries = NUM_MATRIX,
.jacobian_entries = (OsdiNodePair *)&jacobian_entries,
.const_jacobian_entries = (bool *)&const_jacobian_entries,
// memory
.instance_size = sizeof(DiodeInstace),
.model_size = sizeof(DiodeModel),
.residual_resist_offset = offsetof(DiodeInstace, rhs_resist),
.residual_react_offset = offsetof(DiodeInstace, rhs_react),
.node_mapping_offset = offsetof(DiodeInstace, node_off),
.jacobian_resist_offset = offsetof(DiodeInstace, jacobian_resist),
.jacobian_react_offset = offsetof(DiodeInstace, jacobian_react),
.jacobian_ptr_resist_offset = offsetof(DiodeInstace, jacobian_ptr_resist),
.jacobian_ptr_react_offset = offsetof(DiodeInstace, jacobian_ptr_react),
// node collapsing
.num_collapsible = NUM_COLLAPSIBLE,
.collapsible = collapsible,
.is_collapsible_offset = offsetof(DiodeInstace, is_collapsible),
// noise
.noise_sources = NULL,
.num_noise_src = 0,
// parameters and op vars
.num_params = NUM_PARAMS,
.num_instance_params = 1,
.num_opvars = 0,
.param_opvar = (OsdiParamOpvar *)&params,
// setup
.access = &osdi_access,
.setup_model = &setup_model,
.setup_instance = &setup_instance,
.eval = &eval,
.load_noise = &load_noise,
.load_residual_resist = &load_residual_resist,
.load_residual_react = &load_residual_react,
.load_spice_rhs_dc = &load_spice_rhs_dc,
.load_spice_rhs_tran = &load_spice_rhs_tran,
.load_jacobian_resist = &load_jacobian_resist,
.load_jacobian_react = &load_jacobian_react,
.load_jacobian_tran = &load_jacobian_tran,
}};