269 lines
9.3 KiB
C
269 lines
9.3 KiB
C
/* inpdeg.c
|
|
Read and parse the .agemodel parameters of the ngspice netlist
|
|
Store them in a hash table ageparams
|
|
|
|
Copyright Holger Vogt 2025
|
|
License: Modified BSD
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/inpdefs.h"
|
|
#include "ngspice/hash.h"
|
|
#include "ngspice/compatmode.h"
|
|
|
|
#include "inpcom.h"
|
|
|
|
/* maximum number of model parameters */
|
|
#define DEGPARAMAX 64
|
|
/* maximum number of models */
|
|
#define DEGMODMAX 64
|
|
|
|
struct agemod {
|
|
char* devmodel;
|
|
char* simmodel;
|
|
int numparams;
|
|
char *paramnames[DEGPARAMAX];
|
|
char *paramvalstr[DEGPARAMAX];
|
|
double paramvals[DEGPARAMAX];
|
|
bool paramread[DEGPARAMAX];
|
|
NGHASHPTR paramhash;
|
|
} agemods[DEGMODMAX];
|
|
|
|
int readdegparams (struct card *deck) {
|
|
struct card* card;
|
|
int ageindex = 0;
|
|
|
|
for (card = deck; card; card = card->nextcard) {
|
|
if (ciprefix(".agemodel", card->line)) {
|
|
/* comment out .agemodel, if compatmode is not set */
|
|
if (!newcompat.de) {
|
|
*card->line = '*';
|
|
continue;
|
|
}
|
|
int parno = 0;
|
|
|
|
if (ageindex == DEGMODMAX) {
|
|
fprintf(stderr, "Error: Too many agemodels ( > %d)\n", DEGMODMAX);
|
|
*card->line = '*';
|
|
continue;
|
|
}
|
|
|
|
agemods[ageindex].paramhash = nghash_init(64);
|
|
|
|
card->line = inp_remove_ws(card->line);
|
|
|
|
char* cut_line = card->line;
|
|
cut_line = nexttok(cut_line); // skip *agemodel
|
|
char* ftok, *dftok, *f1;
|
|
ftok = dftok = gettok(&cut_line);
|
|
f1 = gettok_char(&ftok, '=', TRUE, FALSE);
|
|
if (f1 && ciprefix("devmodel=", f1))
|
|
agemods[ageindex].devmodel = copy(ftok);
|
|
else {
|
|
fprintf(stderr, "Error: bad .agemodel syntax in line\n %s", card->line);
|
|
controlled_exit(1);
|
|
}
|
|
tfree(dftok);
|
|
tfree(f1);
|
|
ftok = dftok = gettok(&cut_line);
|
|
f1 = gettok_char(&ftok, '=', TRUE, FALSE);
|
|
if (f1 && ciprefix("simmodel=", f1))
|
|
agemods[ageindex].simmodel = copy(ftok);
|
|
else {
|
|
fprintf(stderr, "Error: bad .agemodel syntax in line\n %s", card->line);
|
|
controlled_exit(1);
|
|
}
|
|
tfree(dftok);
|
|
tfree(f1);
|
|
|
|
/* now read all other parameters */
|
|
while (cut_line && *cut_line) {
|
|
if (parno == DEGPARAMAX) {
|
|
fprintf(stderr, "Error: Too many model parameters (> %d) in line\n", DEGPARAMAX);
|
|
fprintf(stderr, " %s\n", card->line);
|
|
*card->line = '*';
|
|
break;
|
|
}
|
|
char* f2 = NULL;
|
|
int err = 0;
|
|
ftok = dftok = gettok(&cut_line);
|
|
/* parameter name */
|
|
f1 = gettok_char(&ftok, '=', FALSE, FALSE);
|
|
if (!f1) {
|
|
fprintf(stderr, "Error: bad .agemodel syntax in line\n % s", card->line);
|
|
controlled_exit(1);
|
|
}
|
|
/* parameter value */
|
|
f2 = copy(ftok + 1);
|
|
agemods[ageindex].paramnames[parno] = f1;
|
|
agemods[ageindex].paramvalstr[parno] = f2;
|
|
char *fp = f2;
|
|
agemods[ageindex].paramvals[parno] = INPevaluate(&fp, &err, 1);
|
|
if (err != 0)
|
|
fprintf(stderr, "\nError: Could not evaluate parameter %s\n", f2);
|
|
else {
|
|
agemods[ageindex].paramread[parno] = FALSE;
|
|
nghash_insert(agemods[ageindex].paramhash, f1, &(agemods[ageindex].paramvals[parno]));
|
|
parno++;
|
|
agemods[ageindex].numparams = parno;
|
|
}
|
|
|
|
tfree(dftok);
|
|
}
|
|
ageindex++;
|
|
*card->line = '*';
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Look for an X line.
|
|
Check if the model in the x line is found in the model list agemodds.
|
|
Create a degradation monitor for each x line if model found.
|
|
Add the x instance name to the degmod name.
|
|
Add degmon line and its model line to the netlist.*/
|
|
int adddegmonitors(struct card* deck) {
|
|
static int degmonno;
|
|
double tfuture = 315336e3; /* 10 years */
|
|
int nodes = 4;
|
|
if (agemods[0].paramhash == NULL)
|
|
return 1;
|
|
for (; deck; deck = deck->nextcard) {
|
|
int skip_control = 0;
|
|
|
|
char* line = deck->line;
|
|
|
|
if (*line == '*') {
|
|
continue;
|
|
}
|
|
/* there is no e source inside .control ... .endc */
|
|
if (ciprefix(".control", line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if (*line == 'x') {
|
|
char *modname, *fournodes, *instname;
|
|
int ii;
|
|
// fprintf(stdout, "%.80s\n", line);
|
|
/* x instance model in subcircuit */
|
|
/* get instance name */
|
|
instname = gettok_instance(&line);
|
|
fournodes = line;
|
|
/* and 4 nodes */
|
|
for (ii = 0; ii < nodes; ii++)
|
|
line = nexttok(line);
|
|
if (!line) {
|
|
/* Must be something else, not a 4-node MOS */
|
|
continue;
|
|
}
|
|
fournodes = copy_substring(fournodes, line);
|
|
modname = gettok(&line);
|
|
|
|
/*check if model is available in agemods */
|
|
for (ii = 0; ii < DEGMODMAX; ii++) {
|
|
if (agemods[ii].devmodel) {
|
|
if (cieq(modname, agemods[ii].devmodel)) {
|
|
// fprintf(stdout, "device model %s found as no. %d\n\n", modname, ii);
|
|
/* get the channel length */
|
|
char* lpos = strstr(line, "l=");
|
|
if (!lpos) {
|
|
fprintf(stderr, "Error, channel length l not found in device %s \n\n", deck->line);
|
|
return 1;
|
|
}
|
|
/* get l=val [m] */
|
|
char* clength = gettok(&lpos);
|
|
/* Now add a degradation monitor like
|
|
adegmon1 %v([z a vss vss]) mon degmon1
|
|
.model degmon1 degmon (tfuture=3153360000 l=0.15e-6 devmod="sg13_lv_nmos")
|
|
*/
|
|
char* aline = tprintf("adegmon%d_%s %%v([%s]) mon%d degmon%d\n",
|
|
degmonno, instname, fournodes, degmonno, degmonno);
|
|
char* mline = tprintf(".model degmon%d degmon (tfuture=%e %s devmod=\"%s\"\n",
|
|
degmonno, tfuture, clength, modname);
|
|
tfree(clength);
|
|
insert_new_line(deck, aline, 0, deck->linenum_orig, deck->linesource);
|
|
insert_new_line(deck, mline, 0, deck->linenum_orig, deck->linesource);
|
|
degmonno++;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// fprintf(stderr, "No model found for device %.80s \n\n", deck->line);
|
|
break;
|
|
}
|
|
}
|
|
tfree(fournodes);
|
|
tfree(modname);
|
|
tfree(instname);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int quote_degmons(struct card* deck) {
|
|
for (; deck; deck = deck->nextcard) {
|
|
int skip_control = 0;
|
|
|
|
char* line = deck->line;
|
|
|
|
if (*line == '*') {
|
|
continue;
|
|
}
|
|
/* there is no e source inside .control ... .endc */
|
|
if (ciprefix(".control", line)) {
|
|
skip_control++;
|
|
continue;
|
|
}
|
|
else if (ciprefix(".endc", line)) {
|
|
skip_control--;
|
|
continue;
|
|
}
|
|
else if (skip_control > 0) {
|
|
continue;
|
|
}
|
|
if (*line == 'a' && strstr(line, "adegmon")) {
|
|
char allnodes[1024];
|
|
allnodes[0] = '\0';
|
|
int ii, nodes = 4;
|
|
char* newnodes, *instname;
|
|
instname = gettok_instance(&line);
|
|
/* skip %v */
|
|
line = nexttok(line);
|
|
char* deltoken;
|
|
char* nodetoken = deltoken = gettok_char(&line, ']', false, true);
|
|
if (!nodetoken)
|
|
break;
|
|
/* go beyond '[' */
|
|
nodetoken++;
|
|
for (ii = 0; ii < nodes; ii++) {
|
|
char* nexttoken = gettok(&nodetoken);
|
|
sprintf(allnodes, "%s \"%s\"", allnodes, nexttoken);
|
|
if (!nexttoken)
|
|
break;
|
|
tfree(nexttoken);
|
|
}
|
|
if (!line || eq(line, "")) {
|
|
/* Must be something else, not a 4-node MOS */
|
|
continue;
|
|
}
|
|
newnodes = tprintf("%s %%v [ %s %s", instname, allnodes, line);
|
|
|
|
tfree(deltoken);
|
|
tfree(instname);
|
|
tfree(deck->line);
|
|
deck->line = newnodes;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|