ngspice/src/xspice/icm/xtradev/degmonitor/cfunc.mod

439 lines
11 KiB
Modula-2
Raw Normal View History

/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
FILE degmonitor/cfunc.mod
Public Domain
Universty Duisburg-Essen
Duisburg, Germany
Project Flowspace
AUTHORS
15 Aug 2025 Holger Vogt
MODIFICATIONS
SUMMARY
This file contains the model-specific routines used to
functionally describe a degradation monitor code model.
The model has been provided by IIS/EAS, Dresden, Germany,
extracted for the IHP Open PDK and its MOS devoces modelled by PSP.
INTERFACES
FILE ROUTINE CALLED
REFERENCED FILES
Inputs from and outputs to ARGS structure.
NON-STANDARD FEATURES
NONE
===============================================================================*/
/*=== INCLUDE FILES ====================*/
#include "ngspice/mifdefs.h"
#include "ngspice/ngspice.h"
#include "ngspice/hash.h"
#include "ngspice/stringutil.h"
#include <string.h>
/*=== CONSTANTS ========================*/
/* maximum number of model parameters */
#define DEGPARAMAX 64
/* maximum number of models */
#define DEGMODMAX 64
/*=== MACROS ===========================*/
/*=== LOCAL VARIABLES & TYPEDEFS =======*/
struct agemod {
char* devmodel;
char* simmodel;
int numparams;
char *paramnames[DEGPARAMAX];
char *paramvalstr[DEGPARAMAX];
double paramvals[DEGPARAMAX];
bool paramread[DEGPARAMAX];
NGHASHPTR paramhash;
} *agemodptr;
/* This struct is model-specific: We need three data sets for dlt_vth [0],
d_idlin [1], and d_idsat [2] */
typedef struct {
double constfac[3]; /* intermediate factor */
double sintegral[3]; /* intermediate intgral */
double prevtime[3]; /* previous time */
int devtype; /* device type 1: nms, -1: pmos */
double VGS0; /* degradation model parameter */
double A[3]; /* degradation model parameter */
double Ea[3]; /* degradation model parameter */
double b[3]; /* degradation model parameter */
double L1[3]; /* degradation model parameter */
double L2[3]; /* degradation model parameter */
double n[3]; /* degradation model parameter */
double c[3]; /* degradation model parameter */
} degLocal_Data_t;
/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/
static void
cm_degmon_callback(ARGS, Mif_Callback_Reason_t reason)
{
switch (reason) {
case MIF_CB_DESTROY: {
degLocal_Data_t *loc = (degLocal_Data_t *) STATIC_VAR(locdata);
if (loc == (degLocal_Data_t *) NULL) {
break;
}
free(loc);
STATIC_VAR(locdata) = NULL;
break;
}
}
}
/* use agemods as input to set the agemodel model parameters,
e.g. loc->A[3] */
int
getdata(struct agemod *agemodptr, degLocal_Data_t *loc, char *devmod)
{
int no; /* which device model */
int ii, jj;
/* all parameters lower case!
At most 64 parameters are supported! */
static char *names[] = {
"vgs0",
"a_dlt_vth",
"ea_dlt_vth",
"b_dlt_vth",
"c_dlt_vth",
"n_dlt_vth",
"l1_dlt_vth",
"l2_dlt_vth",
"a_d_idlin",
"ea_d_idlin",
"b_d_idlin",
"c_d_idlin",
"n_d_idlin",
"l1_d_idlin",
"l2_d_idlin",
"a_d_idsat",
"ea_d_idsat",
"b_d_idsat",
"c_d_idsat",
"n_d_idsat",
"l1_d_idsat",
"l2_d_idsat"
};
/*
static char *names[] = {
"VGS0",
"A_dlt_vth",
"Ea_dlt_vth",
"B_dlt_vth",
"C_dlt_vth",
"n_dlt_vth",
"L1_dlt_vth",
"L2_dlt_vth",
"A_d_idlin",
"Ea_d_idlin",
"B_d_idlin",
"C_d_idlin",
"n_d_idlin",
"L1_d_idlin",
"L2_d_idlin",
"A_d_idsat",
"Ea_d_idsat",
"B_d_idsat",
"C_d_idsat",
"n_d_idsat",
"L1_d_idsat",
"L2_d_idsat"
};
*/
double pvals[64];
for (no = 0; no < 64; no++) {
if (!agemodptr[no].devmodel){
cm_message_printf("Error: Could not find device model %s in the degradation model data!\n", devmod);
cm_cexit(1);
}
if (!strcasecmp(agemodptr[no].devmodel, devmod))
break;
}
if (no == 64){
cm_message_printf("Error: Could not find device model %s in the degradation model data!\n", devmod);
cm_cexit(1);
}
/* Retrive model parameters from hash table. Sequence in pvals[i] is as set by names[i] above. */
for (ii = 0; ii < agemodptr[no].numparams; ii++) {
double* fval;
fval = (double*)nghash_find(agemodptr[no].paramhash, names[ii]);
if (fval){
pvals[ii] = *fval;
}
else { /* error */
cm_message_printf("Error: Could not retrieve parameter %s from the degradation model data!\n", names[ii]);
cm_cexit(1);
}
}
/* loc->xxx selected according to the sequence given in names[i]. */
loc->VGS0 = pvals[0];
loc->A[0] = pvals[1];
loc->Ea[0] = pvals[2];
loc->b[0] = pvals[3];
loc->c[0] = pvals[4];
loc->n[0] = pvals[5];
loc->L1[0] = pvals[6];
loc->L2[0] = pvals[7];
loc->A[1] = pvals[8];
loc->Ea[1] = pvals[9];
loc->b[1] = pvals[10];
loc->c[1] = pvals[11];
loc->n[1] = pvals[12];
loc->L1[1] = pvals[13];
loc->L2[1] = pvals[14];
loc->A[2] = pvals[15];
loc->Ea[2] = pvals[16];
loc->b[2] = pvals[17];
loc->c[2] = pvals[18];
loc->n[2] = pvals[19];
loc->L1[2] = pvals[20];
loc->L2[2] = pvals[21];
return no;
}
/*==============================================================================
FUNCTION void cm_degmon()
AUTHORS
15 Aug 2025 Holger Vogt
SUMMARY
This function implements the degradation monitor code model.
INTERFACES
FILE ROUTINE CALLED
RETURNED VALUE
Returns inputs and outputs via ARGS structure.
GLOBAL VARIABLES
NONE
NON-STANDARD FEATURES
model source:
IIS EAS, 2025
==============================================================================*/
/*=== CM_SEEGEN ROUTINE ===*/
void cm_degmon(ARGS) /* structure holding parms,
inputs, outputs, etc. */
{
double vd; /* drain voltage */
double vg; /* gate voltage */
double vs; /* source voltage */
double vb; /* bulk voltage */
double L; /* channel length */
double constfac; /* static storage of const factor in model equation */
double tfut;
double tsim;
double deg; /* monitor output */
double sintegrand = 0;
double sintegral;
double prevtime;
double k = 1.38062259e-5; /* Boltzmann */
int devtype;
char *devmod; /* PSP device model */
char *simmod; /* degradation model */
double A; /* degradation model parameter */
double Ea; /* degradation model parameter */
double b; /* degradation model parameter */
double L1; /* degradation model parameter */
double L2; /* degradation model parameter */
double n; /* degradation model parameter */
double c; /* degradation model parameter */
degLocal_Data_t *loc; /* Pointer to local static data, not to be included
in the state vector */
if (ANALYSIS == MIF_AC) {
return;
}
/* Retrieve frequently used parameters... */
devmod = PARAM(devmod);
tfut = PARAM(tfuture);
L = PARAM(L);
tsim = TSTOP;
if (INIT==1) {
double Temp = TEMPERATURE + 273.15;
int err = -1, ii;
struct agemod *agemods = cm_get_deg_params();
if (!agemods || !agemods[0].devmodel) {
cm_message_send("Error: Could not find the degradation model data!\n");
cm_cexit(1);
}
if (PORT_SIZE(nodes) != 4)
{
cm_message_send("Error: only devices with exactly 4 node are currently supported\n");
cm_cexit(1);
}
CALLBACK = cm_degmon_callback;
/*** allocate static storage for *loc ***/
if ((loc = (degLocal_Data_t *) (STATIC_VAR(locdata) = calloc(1,
sizeof(degLocal_Data_t)))) == (degLocal_Data_t *) NULL) {
cm_message_send("Error: Unable to allocate Local_Data_t "
"in code model degmon");
cm_cexit(1);
}
/* use agemods as input to set the agemodel model parameters,
e.g. loc->A[3] */
err = getdata(agemods, loc, devmod);
if (err == -1){
cm_message_send("Error: Could not retrieve the degradation model info!\n");
cm_cexit(1);
}
for (ii=0; ii < 3; ii++) {
loc->constfac[ii] = loc->c[ii] * loc->A[ii] * exp(loc->Ea[ii] / k / Temp)
* (loc->L1[ii] + pow((1 / L / 1e6) , loc->L2[ii]));
loc->sintegral[ii] = 0.;
loc->prevtime[ii] = 0.;
}
if (strstr(devmod, "_nmos"))
loc->devtype = 1;
else if (strstr(devmod, "_pmos"))
loc->devtype = -1;
else {
loc->devtype = 0;
cm_message_send("Error: could not extract device type from model name\n");
cm_cexit(1);
}
2025-08-27 23:55:56 +02:00
/*
cm_message_send(INSTNAME);
cm_message_send(INSTMODNAME);
2025-08-27 23:55:56 +02:00
*/
}
else {
int ii;
if (ANALYSIS == MIF_DC) {
return;
}
/* retrieve previous values */
loc = STATIC_VAR (locdata);
if (!loc)
return;
vd = INPUT(nodes[0]);
vg = INPUT(nodes[1]);
vs = INPUT(nodes[2]);
vb = INPUT(nodes[3]);
devtype = loc->devtype;
if (devtype == -1){
vd *= -1.;
vg *= -1.;
vs *= -1.;
vb *= -1.;
}
for (ii = 0; ii < 3; ii++) {
double x1, x2;
constfac = loc->constfac[ii];
sintegral = loc->sintegral[ii];
prevtime = loc->prevtime[ii];
b = loc->b[ii];
n = loc->n[ii];
c = loc->c[ii];
/* final time step quasi reached */
if (sintegral > 1e90) {
return;
}
if (vd - vs > 0 && prevtime < T(0)) {
/**** model equations 1 ****/
x1 = 1. / (constfac * exp (b / (vd - vs)));
x2 = -1. / n;
sintegrand = pow(x1 , x2);
sintegral = sintegral + sintegrand * (T(0) - T(1));
/***************************/
prevtime = T(0);
}
/* test output */
if(ii == 0)
OUTPUT(mon) = sintegral;
if (T(0) > 0.99999 * tsim) {
/**** model equations 2 ****/
sintegral = sintegral * tfut / tsim;
deg = 1. / (c * (pow(sintegral, -1.* n)));
/***************************/
cm_message_printf("no. %d, Degradation deg = %e\n", ii, deg);
sintegral = 1e99; // flag final time step
}
loc->sintegral[ii] = sintegral;
loc->prevtime[ii] = prevtime;
}
}
}