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:
parent
06ce137741
commit
777cfcf477
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue