Improve error messages when reading (and discarding) binned models,

e.g. for Skywater PDK with up to 160 bins per model parameter set.
This commit is contained in:
Holger Vogt 2025-04-29 11:32:46 +02:00
parent 78581e3ad4
commit 33f18b485a
2 changed files with 46 additions and 25 deletions

View File

@ -583,11 +583,18 @@ doit(struct card *deck, wordlist *modnames) {
scale = 1; scale = 1;
error = 0; error = 0;
/* Second pass: do the replacements. */ /* Second pass: do the replacements.
Check if binning is used for .model inside of the subcircuit.
Reduce .model lines to the one with appropriate w and l.
(Inspired by Skywater PDK with excessive use of binning (161 bins)
in the subcircuit referencing a MOS device) */
do { /* while (!error && numpasses-- && gotone) */ do { /* while (!error && numpasses-- && gotone) */
struct card *c = deck; struct card *c = deck;
struct card *prev_of_c = NULL; struct card *prev_of_c = NULL;
bool foundmodel = FALSE;
gotone = FALSE; gotone = FALSE;
for (; c; prev_of_c = c, c = c->nextcard) { for (; c; prev_of_c = c, c = c->nextcard) {
if (ciprefix(invoke, c->line)) { /* found reference to .subckt (i.e. component with refdes X) */ if (ciprefix(invoke, c->line)) { /* found reference to .subckt (i.e. component with refdes X) */
@ -597,16 +604,13 @@ doit(struct card *deck, wordlist *modnames) {
gotone = TRUE; gotone = TRUE;
t = tofree = s = copy(c->line); /* s & t hold copy of component line */ t = tofree = s = copy(c->line); /* s & t hold copy of component line */
/* make scname point to first non-whitepace chars after refdes invocation /* scname contains the refdes Xname */
* e.g. if invocation is Xreference, *scname = reference
*/
tofree2 = scname = gettok(&s); tofree2 = scname = gettok(&s);
/*scname += strlen(invoke); */
while ((*scname == ' ') || (*scname == '\t') || (*scname == ':')) while ((*scname == ' ') || (*scname == '\t') || (*scname == ':'))
scname++; scname++;
/* Now set s to point to last non-space chars in line (i.e. /* Now set s to point to last non-space chars in the x line (i.e.
* the name of the model invoked * the name of the model invoked)
*/ */
while (*s) while (*s)
s++; s++;
@ -617,38 +621,47 @@ doit(struct card *deck, wordlist *modnames) {
s--; s--;
s++; s++;
/* iterate through .subckt list and look for .subckt name invoked */ /* Iterate through .subckt list and look for .subckt name
corresponding to the subckt name extracted from the x line */
for (sss = subs; sss; sss = sss->su_next) for (sss = subs; sss; sss = sss->su_next)
if (eq(sss->su_name, s)) if (eq(sss->su_name, s))
break; break;
/* At this point,
/* At this point, sss points to the .subckt invoked, * c is the card with the x line.
* and scname points to the netnames * scname points to the netname of the x line involved.
* involved. * s is the subckt name extracted from the x line.
* sss points to the subcircuit referenced by the x line
* sss->su_def is the contents of the subcircuit.
*/ */
/* If no .subckt is found, don't complain -- this might be an /* If no .subckt is found, don't complain -- this might be an
* instance of a subckt that is defined above at higher level. * instance of a subckt that is defined above at higher level.
*/ */
if (sss) { if (sss) {
// tprint(sss->su_def); // tprint(sss->su_def);
/* copy of the contents between .subckt and .ends */
struct card *su_deck = inp_deckcopy(sss->su_def); struct card *su_deck = inp_deckcopy(sss->su_def);
/* If we have modern PDKs, we have to reduce the amount of memory required. /* If we have modern PDKs, we have to reduce the amount of memory required.
We try to reduce the models to the one really used. We try to reduce the models to the one really used.
Otherwise su_deck is full of unused binning models.*/ Otherwise su_deck is full of unused binning models.
c->w > 0 and c->l > 0 point to an x line with given w and l
(typically a call to a MOS device). */
if ((newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) { if ((newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) {
/* extract wmin, wmax, lmin, lmax */ /* extract wmin, wmax, lmin, lmax */
struct card* new_deck = su_deck; struct card* enter_su_deck = su_deck;
struct card* prev = NULL; struct card* prev = NULL;
while (su_deck) { while (su_deck) {
/* find a .model line */
if (!ciprefix(".model", su_deck->line)) { if (!ciprefix(".model", su_deck->line)) {
prev = su_deck; prev = su_deck;
su_deck = su_deck->nextcard; su_deck = su_deck->nextcard;
continue; continue;
} }
/* check if line contains wmin, wmax, lmin, lmax
if available, extract its values,
if not, go to next line */
char* curr_line = su_deck->line; char* curr_line = su_deck->line;
float fwmin, fwmax, flmin, flmax; float fwmin, fwmax, flmin, flmax;
char *wmin = strstr(curr_line, " wmin="); char *wmin = strstr(curr_line, " wmin=");
@ -716,32 +729,39 @@ doit(struct card *deck, wordlist *modnames) {
su_deck = su_deck->nextcard; su_deck = su_deck->nextcard;
continue; continue;
} }
/* check if x line's w and l are withing the limites of wmin, wmax, lmin, lmax */
float csl = (float)scale * c->l; float csl = (float)scale * c->l;
/* scale by nf */ /* scale by nf */
float csw = (float)scale * c->w / c->nf; float csw = (float)scale * c->w / c->nf;
/*fprintf(stdout, "Debug: nf = %f\n", c->nf);*/ /*fprintf(stdout, "Debug: nf = %f\n", c->nf);*/
if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) { if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) {
/* use the current .model card */ /* if within the limits, use the current .model card */
prev = su_deck; prev = su_deck;
su_deck = su_deck->nextcard; su_deck = su_deck->nextcard;
foundmodel = TRUE;
continue; continue;
} }
else { else {
/* if not within the limits,
delete the .model line not fitting the device */
struct card* tmpcard = su_deck->nextcard; struct card* tmpcard = su_deck->nextcard;
line_free_x(prev->nextcard, FALSE); line_free_x(prev->nextcard, FALSE);
su_deck = prev->nextcard = tmpcard; su_deck = prev->nextcard = tmpcard;
} }
} }
su_deck = new_deck; /* go back to the first card of su_deck */
su_deck = enter_su_deck;
} }
if (!su_deck) { if (!foundmodel && (newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) {
fprintf(stderr, "\nError: Could not find a model for device %s in subcircuit %s\n", fprintf(stderr, "\nError: Could not find a model\n"
scname, sss->su_name); " for device %s in transistor subcircuit %s\n", scname, sss->su_name);
fprintf(stderr, " with w = %.3g and l = %.3g\n\n", c->w, c->l);
controlled_exit(1); controlled_exit(1);
} }
foundmodel = FALSE;
struct card *rest_of_c = c->nextcard; struct card *rest_of_c = c->nextcard;
/* Now we have to replace this line with the /* Now we have to replace this line with the
@ -782,8 +802,8 @@ doit(struct card *deck, wordlist *modnames) {
tfree(tofree); tfree(tofree);
tfree(tofree2); tfree(tofree2);
} } /* if (ciprefix(invoke, c->line)) */
} } /* for (; c; prev_of_c = c, c = c->nextcard) */
} while (!error && numpasses-- && gotone); } while (!error && numpasses-- && gotone);

View File

@ -286,6 +286,7 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
for (modtmp = modtab; modtmp; modtmp = modtmp->INPnextModel) { for (modtmp = modtab; modtmp; modtmp = modtmp->INPnextModel) {
/* exact: 1, with binning extension .[0-9]: 2*/
if (model_name_match(name, modtmp->INPmodName) < 2) if (model_name_match(name, modtmp->INPmodName) < 2)
continue; continue;
@ -328,8 +329,8 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
*model = modtmp; *model = modtmp;
return NULL; return NULL;
} }
fprintf(stderr, "Warning: no model found for w=%.3e and l=%.3e\n", w, l);
} }
return NULL; return NULL;
} }