Reduce the memory requirements if there is a PDK with

a lot of binning models.
This is a hack and needs testing!
inpcom.c: If an x line, add w and l to the netlist card,
if available.
subckt.c: select a suitable model bin, discard the rest
for each subcircuit, depending on w and l from above.
inpgmod.c: less restrictive equal for real numbers,
allow both min and max boundaries (problem of equating
real numbers), when the selected device has w or l on
the boundary between two model bins.
This commit is contained in:
Holger Vogt 2020-12-10 14:13:33 +01:00
parent 06ce137741
commit 777cfcf477
6 changed files with 280 additions and 16 deletions

View File

@ -41,7 +41,7 @@ Author: 1985 Wayne A. Christopher
#include "numparam/numpaif.h"
#include "ngspice/stringskip.h"
#include "ngspice/randnumb.h"
#include "ngspice/compatmode.h"
#define line_free(line, flag) \
do { \
@ -749,6 +749,28 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
for (ii = 0; ii < 5; ii++)
eval_agauss(deck, statfcn[ii]);
}
/* If we have large PDK deck, search for scale option and set
the variable 'scale'*/
if (newcompat.hs || newcompat.spe) {
struct card* scan;
double dscale = 1;
struct card* scoptions = line_reverse(line_nconc(options, inp_deckcopy(com_options)));
for (scan = scoptions; scan; scan = scan->nextcard) {
char* tmpscale = strstr(scan->line, "scale=");
if (tmpscale) {
int err;
tmpscale = tmpscale + 6;
dscale = INPevaluate(&tmpscale, &err, 1);
if (err == 0)
cp_vset("scale", CP_REAL, &dscale);
else
fprintf(stderr, "\nError: Could not set 'scale' variable\n");
break;
}
}
}
/* Now expand subcircuit macros and substitute numparams.*/
if (!cp_getvar("nosubckt", CP_BOOL, NULL, 0))
if ((deck->nextcard = inp_subcktexpand(deck->nextcard)) == NULL) {

View File

@ -180,6 +180,7 @@ static struct card *ltspice_compat(struct card *oldcard);
static void ltspice_compat_a(struct card *oldcard);
static void inp_repair_dc_ps(struct card* oldcard);
static void inp_get_w_l_x(struct card* oldcard);
#ifndef EXT_ASC
static void utf8_syntax_check(struct card *deck);
@ -563,6 +564,66 @@ static void new_compat_mode(void)
}
}
/* We check x lines for w= and l= and fill in their values.
To be used when expanding subcircuits with binned model cards. */
void inp_get_w_l_x(struct card* card) {
for (; card; card = card->nextcard) {
char* curr_line = card->line;
int skip_control = 0;
char* w = NULL, * l = NULL;
/* exclude any command inside .control ... .endc */
if (ciprefix(".control", curr_line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", curr_line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
/* only subcircuit invocations */
if (*curr_line != 'x' && !newcompat.hs && !newcompat.spe) {
card->w = card->l = 0;
continue;
}
w = strstr(curr_line, " w=");
if (w) {
int err;
w = w + 3;
card->w = (float)INPevaluate(&w, &err, 0);
if(err) {
card->w = card->l = 0;
continue;
}
}
else {
card->w = card->l = 0;
continue;
}
l = strstr(curr_line, " l=");
if (l) {
int err;
l = l + 3;
card->l = (float)INPevaluate(&l, &err, 0);
if(err) {
card->w = card->l = 0;
continue;
}
}
else {
card->w = card->l = 0;
continue;
}
}
}
/*-------------------------------------------------------------------------
Read the entire input file and return a pointer to the first line of
the linked list of 'card' records in data. The pointer is stored in
@ -680,6 +741,10 @@ struct card *inp_readall(FILE *fp, const char *dir_name,
inp_reorder_params(subckt_w_params, cc);
/* Special handling for large PDKs: We need to know W and L of
transistor subcircuits by checking their x invokcation */
inp_get_w_l_x(working);
inp_fix_inst_calls_for_numparam(subckt_w_params, working);
// tprint(working);
delete_names(subckt_w_params);
@ -2107,7 +2172,6 @@ static const char *nlist_find(const struct nlist *nlist, const char *name)
return NULL;
}
#if 0
static const char *nlist_model_find(
const struct nlist *nlist, const char *name)
{
@ -2117,7 +2181,6 @@ static const char *nlist_model_find(
return nlist->names[i];
return NULL;
}
#endif
static void nlist_adjoin(struct nlist *nlist, char *name)
{
@ -2247,7 +2310,7 @@ static void comment_out_unused_subckt_models(struct card *start_card)
char *line = card->line;
/* no models embedded in these lines */
if (strchr("*vibefghkt", *line))
if (strchr(".*vibefghkt", *line))
continue;
/* there is no .subckt, .model or .param inside .control ... .endc */
@ -2323,26 +2386,34 @@ static void comment_out_unused_subckt_models(struct card *start_card)
if (remove_subckt)
*line = '*'; /* make line a comment */
#if 0
else if (has_models &&
(ciprefix(".model", line) || ciprefix(".cmodel", line))) {
char *model_type = get_model_type(line);
char *model_name = get_subckt_model_name(line);
}
/* comment out any unused models */
for (card = start_card; card; card = card->nextcard) {
char* line = card->line;
if (*line == '*')
continue;
if (has_models &&
(ciprefix(".model", line) || ciprefix(".cmodel", line))) {
char* model_type = get_model_type(line);
char* model_name = get_subckt_model_name(line);
/* keep R, L, C models because in addition to no. of terminals the
value may be given, as in RE1 1 2 800 newres dtemp=5, so model
name may be token no. 4 or 5, and, if 5, will not be detected
by get_subckt_model_name()*/
if (!cieq(model_type, "c") && !cieq(model_type, "l") &&
!cieq(model_type, "r") &&
!nlist_model_find(used_models, model_name)) {
!cieq(model_type, "r") &&
!nlist_model_find(used_models, model_name)) {
*line = '*';
}
tfree(model_type);
tfree(model_name);
}
#endif
}
nlist_destroy(used_subckts);
@ -8058,6 +8129,9 @@ static struct card *pspice_compat(struct card *oldcard)
card->line = tprintf(
".model a%s aswitch(%s %s %s %s log=TRUE limit=TRUE)", modname,
modpar[0], modpar[1], modpar[2], modpar[3]);
// card->line = tprintf(
// ".model a%s pswitch(%s %s %s %s log=TRUE)", modname,
// modpar[0], modpar[1], modpar[2], modpar[3]);
}
for (i = 0; i < 4; i++)
tfree(modpar[i]);

View File

@ -60,6 +60,7 @@ Modified: 2000 AlansFixes
#include "ngspice/ftedefs.h"
#include "ngspice/fteinp.h"
#include "ngspice/stringskip.h"
#include "ngspice/compatmode.h"
#include <stdarg.h>
@ -557,6 +558,10 @@ doit(struct card *deck, wordlist *modnames) {
}
#endif
double scale;
if (!cp_getvar("scale", CP_REAL, &scale, 0))
scale = 1;
error = 0;
/* Second pass: do the replacements. */
do { /* while (!error && numpasses-- && gotone) */
@ -608,8 +613,114 @@ doit(struct card *deck, wordlist *modnames) {
* instance of a subckt that is defined above at higher level.
*/
if (sss) {
// tprint(sss->su_def);
struct card *su_deck = inp_deckcopy_ln(sss->su_def);
/* 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.
Otherwise su_deck is full of unused binning models.*/
// struct card* su_deck = NULL;
if ((newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) {
/* extract wmin, wmax, lmin, lmax */
struct card* new_deck = su_deck;
struct card* prev = NULL;
while (su_deck) {
if (!ciprefix(".model", su_deck->line)) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* curr_line = su_deck->line;
float fwmin, fwmax, flmin, flmax;
char *wmin = strstr(curr_line, " wmin=");
if (wmin) {
int err;
wmin = wmin + 6;
fwmin = (float)INPevaluate(&wmin, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char *wmax = strstr(curr_line, " wmax=");
if (wmax) {
int err;
wmax = wmax + 6;
fwmax = (float)INPevaluate(&wmax, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* lmin = strstr(curr_line, " lmin=");
if (lmin) {
int err;
lmin = lmin + 6;
flmin = (float)INPevaluate(&lmin, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* lmax = strstr(curr_line, " lmax=");
if (lmax) {
int err;
lmax = lmax + 6;
flmax = (float)INPevaluate(&lmax, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
float csl = (float)scale * c->l;
float csw = (float)scale * c->w;
if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) {
/* use the current .model card */
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
else {
struct card* tmpcard = su_deck->nextcard;
line_free_x(prev->nextcard, FALSE);
su_deck = prev->nextcard = tmpcard;
}
}
su_deck = new_deck;
}
if (!su_deck) {
fprintf(stderr, "\nError: Could not find a model for device %s in subcircuit %s\n",
scname, sss->su_name);
controlled_exit(1);
}
struct card *su_deck = inp_deckcopy(sss->su_def);
struct card *rest_of_c = c->nextcard;
/* Now we have to replace this line with the
@ -711,6 +822,8 @@ struct card * inp_deckcopy(struct card *deck) {
nd = d = TMALLOC(struct card, 1);
}
d->linenum = deck->linenum;
d->w = deck->w;
d->l = deck->l;
d->line = copy(deck->line);
if (deck->error)
d->error = copy(deck->error);
@ -720,7 +833,6 @@ struct card * inp_deckcopy(struct card *deck) {
return (nd);
}
/*
* Copy a deck, without the ->actualLine lines, without comment lines, and
* without .control section(s).
@ -754,6 +866,8 @@ struct card *inp_deckcopy_oc(struct card * deck)
else { /* This is the first card */
nd = d = TMALLOC(struct card, 1);
}
d->w = deck->w;
d->l = deck->l;
d->linenum_orig = deck->linenum;
d->linenum = i++;
d->line = copy(deck->line);
@ -770,6 +884,57 @@ struct card *inp_deckcopy_oc(struct card * deck)
return nd;
} /* end of function inp_deckcopy_oc */
/*
* Copy a deck, without the ->actualLine lines, without comment lines, and
* without .control section(s).
* First line is always copied (except being .control).
* Keep the line numbers.
*/
struct card* inp_deckcopy_ln(struct card* deck)
{
struct card* d = NULL, * nd = NULL;
int skip_control = 0;
while (deck) {
/* exclude any command inside .control ... .endc */
if (ciprefix(".control", deck->line)) {
skip_control++;
deck = deck->nextcard;
continue;
}
else if (ciprefix(".endc", deck->line)) {
skip_control--;
deck = deck->nextcard;
continue;
}
else if (skip_control > 0) {
deck = deck->nextcard;
continue;
}
if (nd) { /* First card already found */
/* d is the card at the end of the deck */
d = d->nextcard = TMALLOC(struct card, 1);
}
else { /* This is the first card */
nd = d = TMALLOC(struct card, 1);
}
d->w = deck->w;
d->l = deck->l;
d->linenum_orig = deck->linenum_orig;
d->linenum = deck->linenum;
d->line = copy(deck->line);
if (deck->error) {
d->error = copy(deck->error);
}
d->actualLine = NULL;
deck = deck->nextcard;
while (deck && *(deck->line) == '*') { /* skip comments */
deck = deck->nextcard;
}
} /* end of loop over cards in the source deck */
return nd;
} /* end of function inp_deckcopy_ln */
/*-------------------------------------------------------------------

View File

@ -9,5 +9,6 @@
struct card *inp_subcktexpand(struct card *deck);
struct card *inp_deckcopy(struct card *deck);
struct card *inp_deckcopy_oc(struct card *deck);
struct card *inp_deckcopy_ln(struct card *deck);
#endif

View File

@ -82,6 +82,8 @@ struct card {
struct card *nextcard;
struct card *actualLine;
struct nscope *level;
float w;
float l;
};
/* structure used to save models in after they are read during pass 1 */

View File

@ -208,7 +208,7 @@ parse_line(char *line, char *tokens[], int num_tokens, double values[], bool fou
static bool
is_equal(double result, double expectedResult)
{
return fabs(result - expectedResult) < 1e-15;
return fabs(result - expectedResult) < 1e-9;
}
@ -216,7 +216,7 @@ static bool
in_range(double value, double min, double max)
{
/* the standard binning rule is: min <= value < max */
return is_equal(value, min) || (min < value && value < max);
return is_equal(value, min) || is_equal(value, max) || (min < value && value < max);
}