mirror of
https://git.code.sf.net/p/ngspice/ngspice
synced 2026-08-22 05:47:42 +02:00
Better estimates of rise/fall delays in PINDLYs with outputs separated by CASE.
This commit is contained in:
+245
-80
@@ -1413,67 +1413,179 @@ error_return:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* pindly handling */
|
/* pindly handling */
|
||||||
static PTABLE ios_in_tab = NULL;
|
/* C++ with templates would generalize the different TABLEs */
|
||||||
static PTABLE ios_out_tab = NULL;
|
typedef struct pindly_line *PLINE;
|
||||||
|
struct pindly_line {
|
||||||
|
char *in_name;
|
||||||
|
char *out_name;
|
||||||
|
char *ena_name;
|
||||||
|
char *delays;
|
||||||
|
PLINE next;
|
||||||
|
};
|
||||||
|
|
||||||
static void init_ios_tab(void)
|
typedef struct pindly_table *PINTABLE;
|
||||||
|
struct pindly_table {
|
||||||
|
PLINE first;
|
||||||
|
PLINE last;
|
||||||
|
int num_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
static PINTABLE new_pindly_table(void)
|
||||||
{
|
{
|
||||||
ios_in_tab = new_parse_table();
|
PINTABLE pint;
|
||||||
ios_out_tab = new_parse_table();
|
pint = TMALLOC(struct pindly_table, 1);
|
||||||
|
pint->first = pint->last = NULL;
|
||||||
|
pint->num_entries = 0;
|
||||||
|
return pint;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_pindly(void)
|
static int num_pindly_entries(PINTABLE pint)
|
||||||
{
|
{
|
||||||
delete_parse_table(ios_in_tab);
|
if (!pint)
|
||||||
delete_parse_table(ios_out_tab);
|
return 0;
|
||||||
ios_in_tab = NULL;
|
|
||||||
ios_out_tab = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static TLINE ios_tab_add(char *ioname, BOOL is_input)
|
|
||||||
{
|
|
||||||
if (is_input)
|
|
||||||
return add_to_parse_table(ios_in_tab, ioname, TRUE);
|
|
||||||
else
|
else
|
||||||
return add_to_parse_table(ios_out_tab, ioname, TRUE);
|
return pint->num_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_ios_tabs(void)
|
static PLINE new_pindly_line(void)
|
||||||
{
|
{
|
||||||
#ifdef TABLE_PRINT
|
PLINE p = NULL;
|
||||||
if (ios_in_tab) {
|
p = TMALLOC(struct pindly_line, 1);
|
||||||
printf("ios_in_tab\n");
|
p->in_name = p->out_name = p->ena_name = p->delays = NULL;
|
||||||
table_print(ios_in_tab->first);
|
return p;
|
||||||
}
|
}
|
||||||
if (ios_out_tab) {
|
|
||||||
printf("ios_out_tab\n");
|
static PLINE add_new_pindly_line(PINTABLE pint)
|
||||||
table_print(ios_out_tab->first);
|
{
|
||||||
|
PLINE p;
|
||||||
|
p = new_pindly_line();
|
||||||
|
p->next = NULL;
|
||||||
|
if (!pint->first) {
|
||||||
|
pint->first = pint->last = p;
|
||||||
|
} else {
|
||||||
|
pint->last->next = p;
|
||||||
|
pint->last = p;
|
||||||
}
|
}
|
||||||
|
pint->num_entries++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLINE set_in_name(char *s, PLINE p)
|
||||||
|
{
|
||||||
|
if (p->in_name) tfree(p->in_name);
|
||||||
|
p->in_name = TMALLOC(char, (strlen(s) + 1));
|
||||||
|
strcpy(p->in_name, s);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLINE set_out_name(char *s, PLINE p)
|
||||||
|
{
|
||||||
|
if (p->out_name) tfree(p->out_name);
|
||||||
|
p->out_name = TMALLOC(char, (strlen(s) + 1));
|
||||||
|
strcpy(p->out_name, s);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static PLINE set_ena_name(char *s, PLINE p)
|
||||||
|
{
|
||||||
|
if (p->ena_name) tfree(p->ena_name);
|
||||||
|
p->ena_name = TMALLOC(char, (strlen(s) + 1));
|
||||||
|
strcpy(p->ena_name, s);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static PLINE set_delays(char *s, PLINE p)
|
||||||
|
{
|
||||||
|
if (p->delays) tfree(p->delays);
|
||||||
|
p->delays = TMALLOC(char, (strlen(s) + 1));
|
||||||
|
strcpy(p->delays, s);
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_output_buffers(void)
|
static void delete_pindly_table(PINTABLE pint)
|
||||||
{
|
{
|
||||||
TLINE tin, tout;
|
PLINE p, next;
|
||||||
if (ios_in_tab && ios_out_tab) {
|
if (!pint)
|
||||||
if (!ios_in_tab->first || !ios_out_tab->first) {
|
return;
|
||||||
return;
|
next = pint->first;
|
||||||
} else {
|
while (next) {
|
||||||
DS_CREATE(instance, 128);
|
p = next;
|
||||||
tin = ios_in_tab->first;
|
if (p->in_name) tfree(p->in_name);
|
||||||
tout = ios_out_tab->first;
|
if (p->out_name) tfree(p->out_name);
|
||||||
while (tin && tout) {
|
if (p->ena_name) tfree(p->ena_name);
|
||||||
ds_clear(&instance);
|
if (p->delays) tfree(p->delays);
|
||||||
ds_cat_printf(&instance, "%s %s %s d_pindly_buf",
|
next = p->next;
|
||||||
get_inst_name(), tin->line, tout->line);
|
tfree(p);
|
||||||
u_add_instance(ds_get_buf(&instance));
|
}
|
||||||
tin = tin->next;
|
tfree(pint);
|
||||||
tout = tout->next;
|
}
|
||||||
}
|
|
||||||
ds_free(&instance);
|
static PLINE nth_pindly_entry(PINTABLE pint, int n)
|
||||||
}
|
{
|
||||||
|
/* Entries ore from 0 to num_entries - 1 */
|
||||||
|
PLINE p, next;
|
||||||
|
int count = 0;
|
||||||
|
if (n < 0) return NULL;
|
||||||
|
if (n > pint->num_entries - 1) return NULL;
|
||||||
|
next = pint->first;
|
||||||
|
while (next) {
|
||||||
|
p = next;
|
||||||
|
if (count == n) return p;
|
||||||
|
count++;
|
||||||
|
next = p->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLINE find_pindly_out_name(PINTABLE pint, char *name)
|
||||||
|
{
|
||||||
|
PLINE p, next;
|
||||||
|
if (!pint) return NULL;
|
||||||
|
next = pint->first;
|
||||||
|
while (next) {
|
||||||
|
p = next;
|
||||||
|
if (strcmp(p->out_name, name) == 0) return p;
|
||||||
|
next = p->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PINTABLE pindly_tab = NULL;
|
||||||
|
|
||||||
|
static void init_pindly_tab(void)
|
||||||
|
{
|
||||||
|
pindly_tab = new_pindly_table();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup_pindly_tab(void)
|
||||||
|
{
|
||||||
|
delete_pindly_table(pindly_tab);
|
||||||
|
pindly_tab = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_pindly_buffers(void)
|
||||||
|
{
|
||||||
|
DS_CREATE(dbuf, 128);
|
||||||
|
PLINE pline = NULL;
|
||||||
|
|
||||||
|
pline = pindly_tab->first;
|
||||||
|
while (pline) {
|
||||||
|
char *iname = NULL;
|
||||||
|
ds_clear(&dbuf);
|
||||||
|
iname = get_inst_name();
|
||||||
|
ds_cat_printf(&dbuf, "%s %s %s d_pindly_buf_%s", iname,
|
||||||
|
pline->in_name, pline->out_name, iname);
|
||||||
|
u_add_instance(ds_get_buf(&dbuf));
|
||||||
|
ds_clear(&dbuf);
|
||||||
|
ds_cat_printf(&dbuf, ".model d_pindly_buf_%s d_buffer%s",
|
||||||
|
iname, pline->delays);
|
||||||
|
u_add_instance(ds_get_buf(&dbuf));
|
||||||
|
pline = pline->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_typ_estimate(char *min, char *typ, char *max)
|
static char *get_typ_estimate(char *min, char *typ, char *max)
|
||||||
{
|
{
|
||||||
char *tmpmax = NULL, *tmpmin = NULL;
|
char *tmpmax = NULL, *tmpmin = NULL;
|
||||||
@@ -1516,35 +1628,73 @@ static char *get_typ_estimate(char *min, char *typ, char *max)
|
|||||||
|
|
||||||
static void gen_output_models(LEXER lx)
|
static void gen_output_models(LEXER lx)
|
||||||
{
|
{
|
||||||
int val, which = 0;
|
int val, which = 0, arrlen = 0, idx = 0, i;
|
||||||
BOOL in_delay = FALSE;
|
BOOL in_delay = FALSE, expect_left = FALSE;
|
||||||
float typ_max_val = 0.0, typ_val = 0.0;
|
float typ_max_val = 0.0, typ_val = 0.0;
|
||||||
char *units;
|
char *units;
|
||||||
|
BOOL tracing = FALSE;
|
||||||
DS_CREATE(dmin, 16);
|
DS_CREATE(dmin, 16);
|
||||||
DS_CREATE(dtyp, 16);
|
DS_CREATE(dtyp, 16);
|
||||||
DS_CREATE(dmax, 16);
|
DS_CREATE(dmax, 16);
|
||||||
DS_CREATE(dtyp_max_str, 16);
|
DS_CREATE(dtyp_max_str, 16);
|
||||||
DSTRINGPTR dsp = NULL;
|
DSTRINGPTR dsp = NULL;
|
||||||
|
PLINE pline = NULL;
|
||||||
|
PLINE *pline_arr = NULL;
|
||||||
|
|
||||||
|
arrlen = num_pindly_entries(pindly_tab);
|
||||||
|
assert(arrlen > 0);
|
||||||
|
pline_arr = TMALLOC(PLINE, arrlen);
|
||||||
val = lexer_set_start("pindly:", lx);
|
val = lexer_set_start("pindly:", lx);
|
||||||
val = lexer_scan(lx); // "pindly"
|
val = lexer_scan(lx); // "pindly"
|
||||||
val = lexer_scan(lx); // ':'
|
val = lexer_scan(lx); // ':'
|
||||||
val = lexer_scan(lx);
|
val = lexer_scan(lx);
|
||||||
while (val != '\0') {
|
idx = 0;
|
||||||
|
while (val != '\0') { /* while val */
|
||||||
if (val == LEX_ID) {
|
if (val == LEX_ID) {
|
||||||
|
expect_left = FALSE;
|
||||||
if (strcmp(lx->lexer_buf, "delay") == 0) {
|
if (strcmp(lx->lexer_buf, "delay") == 0) {
|
||||||
ds_clear(&dmin);
|
ds_clear(&dmin);
|
||||||
ds_clear(&dtyp);
|
ds_clear(&dtyp);
|
||||||
ds_clear(&dmax);
|
ds_clear(&dmax);
|
||||||
in_delay = TRUE;
|
in_delay = TRUE;
|
||||||
|
} else if (strcmp(lx->lexer_buf, "setup_hold") == 0
|
||||||
|
|| strcmp(lx->lexer_buf, "width") == 0
|
||||||
|
|| strcmp(lx->lexer_buf, "freq") == 0
|
||||||
|
|| strcmp(lx->lexer_buf, "general") == 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#ifdef TRACE
|
pline = find_pindly_out_name(pindly_tab, lx->lexer_buf);
|
||||||
printf("ID: \"%s\"\n", lx->lexer_buf);
|
if (pline) {
|
||||||
|
if (tracing) printf("Case for %s comming\n", pline->out_name);
|
||||||
|
pline_arr[idx++] = pline;
|
||||||
|
}
|
||||||
|
if (tracing) printf("ID: \"%s\"\n", lx->lexer_buf);
|
||||||
} else if (val == LEX_OTHER) {
|
} else if (val == LEX_OTHER) {
|
||||||
printf("OTHER: \"%s\"\n", lx->lexer_buf);
|
expect_left = FALSE;
|
||||||
|
if (tracing) printf("OTHER: \"%s\"\n", lx->lexer_buf);
|
||||||
} else {
|
} else {
|
||||||
printf("TOK: %d <%c>\n", val, val);
|
if (tracing) printf("TOK: %d <%c>\n", val, val);
|
||||||
#endif
|
if (val == '{') {
|
||||||
|
if (expect_left) {
|
||||||
|
if (tracing) printf("Ready for delays\n");
|
||||||
|
for (i = 0; i < idx; i++)
|
||||||
|
if (tracing)
|
||||||
|
printf("For output %s\n", pline_arr[i]->out_name);
|
||||||
|
}
|
||||||
|
expect_left = FALSE;
|
||||||
|
} else if (val == '=') {
|
||||||
|
expect_left = TRUE;
|
||||||
|
} else if (val == '}') {
|
||||||
|
for (i = 0; i < idx; i++) {
|
||||||
|
if (tracing) printf("Finished delays for output %s\n",
|
||||||
|
pline_arr[i]->out_name);
|
||||||
|
pline_arr[i] = NULL;
|
||||||
|
}
|
||||||
|
idx = 0;
|
||||||
|
expect_left = FALSE;
|
||||||
|
in_delay = FALSE;
|
||||||
|
typ_max_val = 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (in_delay) {
|
if (in_delay) {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
@@ -1574,21 +1724,36 @@ static void gen_output_models(LEXER lx)
|
|||||||
char *s;
|
char *s;
|
||||||
s = get_typ_estimate(ds_get_buf(&dmin),
|
s = get_typ_estimate(ds_get_buf(&dmin),
|
||||||
ds_get_buf(&dtyp), ds_get_buf(&dmax));
|
ds_get_buf(&dtyp), ds_get_buf(&dmax));
|
||||||
#ifdef TRACE
|
if (tracing) printf("\tMIN: \"%s\"", ds_get_buf(&dmin));
|
||||||
printf("\tMIN: \"%s\"", ds_get_buf(&dmin));
|
if (tracing) printf(" TYP: \"%s\"", ds_get_buf(&dtyp));
|
||||||
printf(" TYP: \"%s\"", ds_get_buf(&dtyp));
|
if (tracing) printf(" MAX: \"%s\"", ds_get_buf(&dmax));
|
||||||
printf(" MAX: \"%s\"", ds_get_buf(&dmax));
|
if (s) {
|
||||||
if (s)
|
if (tracing) printf(" ESTIMATE: \"%s\"\n", s);
|
||||||
printf(" ESTIMATE: \"%s\"\n", s);
|
} else {
|
||||||
else
|
if (tracing) printf(" ESTIMATE: UNKNOWN\n");
|
||||||
printf(" ESTIMATE: UNKNOWN\n");
|
}
|
||||||
#endif
|
|
||||||
if (s) {
|
if (s) {
|
||||||
typ_val = strtof(s, &units);
|
typ_val = strtof(s, &units);
|
||||||
if (typ_val > typ_max_val) {
|
if (typ_val > typ_max_val) {
|
||||||
|
DS_CREATE(delay_string, 64);
|
||||||
|
ds_clear(&delay_string);
|
||||||
ds_clear(&dtyp_max_str);
|
ds_clear(&dtyp_max_str);
|
||||||
ds_cat_str(&dtyp_max_str, s);
|
ds_cat_str(&dtyp_max_str, s);
|
||||||
typ_max_val = typ_val;
|
typ_max_val = typ_val;
|
||||||
|
if (ds_get_length(&dtyp_max_str) > 0) {
|
||||||
|
ds_cat_printf(&delay_string,
|
||||||
|
"(rise_delay=%s fall_delay=%s)",
|
||||||
|
ds_get_buf(&dtyp_max_str),
|
||||||
|
ds_get_buf(&dtyp_max_str));
|
||||||
|
} else {
|
||||||
|
ds_cat_printf(&delay_string,
|
||||||
|
"(rise_delay=10ns fall_delay=10ns)");
|
||||||
|
}
|
||||||
|
for (i = 0; i < idx; i++) {
|
||||||
|
(void) set_delays(ds_get_buf(&delay_string),
|
||||||
|
pline_arr[i]);
|
||||||
|
}
|
||||||
|
ds_free(&delay_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1596,21 +1761,13 @@ static void gen_output_models(LEXER lx)
|
|||||||
which = 0;
|
which = 0;
|
||||||
}
|
}
|
||||||
val = lexer_scan(lx);
|
val = lexer_scan(lx);
|
||||||
}
|
} /* end while val */
|
||||||
if (ds_get_length(&dtyp_max_str) > 0) {
|
|
||||||
ds_clear(&dmax); // dmax was no longer in use
|
|
||||||
ds_cat_printf(&dmax,
|
|
||||||
".model d_pindly_buf d_buffer(rise_delay=%s fall_delay=%s)",
|
|
||||||
ds_get_buf(&dtyp_max_str), ds_get_buf(&dtyp_max_str));
|
|
||||||
u_add_instance(ds_get_buf(&dmax));
|
|
||||||
} else {
|
|
||||||
u_add_instance(
|
|
||||||
".model d_pindly_buf d_buffer(rise_delay=10ns fall_delay=10ns)");
|
|
||||||
}
|
|
||||||
ds_free(&dmin);
|
ds_free(&dmin);
|
||||||
ds_free(&dtyp);
|
ds_free(&dtyp);
|
||||||
ds_free(&dmax);
|
ds_free(&dmax);
|
||||||
ds_free(&dtyp_max_str);
|
ds_free(&dtyp_max_str);
|
||||||
|
tfree(pline_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL f_pindly(char *line)
|
BOOL f_pindly(char *line)
|
||||||
@@ -1619,10 +1776,13 @@ BOOL f_pindly(char *line)
|
|||||||
char *endp;
|
char *endp;
|
||||||
LEXER lxr;
|
LEXER lxr;
|
||||||
|
|
||||||
|
PLINE pline = NULL;
|
||||||
|
|
||||||
#ifdef TRACE
|
#ifdef TRACE
|
||||||
printf("\nf_pindly: %s\n", line);
|
printf("\nf_pindly: %s\n", line);
|
||||||
#endif
|
#endif
|
||||||
init_ios_tab();
|
init_pindly_tab();
|
||||||
|
|
||||||
lxr = new_lexer(line);
|
lxr = new_lexer(line);
|
||||||
current_lexer = lxr;
|
current_lexer = lxr;
|
||||||
t = lexer_scan(lxr); // U*
|
t = lexer_scan(lxr); // U*
|
||||||
@@ -1683,7 +1843,8 @@ BOOL f_pindly(char *line)
|
|||||||
for (i = 0; i < num_ios; i++) {
|
for (i = 0; i < num_ios; i++) {
|
||||||
t = lexer_scan(lxr);
|
t = lexer_scan(lxr);
|
||||||
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
||||||
(void) ios_tab_add(lxr->lexer_buf, TRUE);
|
pline = add_new_pindly_line(pindly_tab);
|
||||||
|
(void) set_in_name(lxr->lexer_buf, pline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* num_refs reference nodes which are ignored */
|
/* num_refs reference nodes which are ignored */
|
||||||
@@ -1692,23 +1853,27 @@ BOOL f_pindly(char *line)
|
|||||||
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
||||||
}
|
}
|
||||||
/* num_ios output ids */
|
/* num_ios output ids */
|
||||||
|
pline = NULL;
|
||||||
for (i = 0; i < num_ios; i++) {
|
for (i = 0; i < num_ios; i++) {
|
||||||
t = lexer_scan(lxr);
|
t = lexer_scan(lxr);
|
||||||
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
if (!expect_token(t, LEX_ID, NULL, TRUE)) goto error_return;
|
||||||
(void) ios_tab_add(lxr->lexer_buf, FALSE);
|
if (i == 0)
|
||||||
|
pline = nth_pindly_entry(pindly_tab, i);
|
||||||
|
else
|
||||||
|
pline = pline->next;
|
||||||
|
(void) set_out_name(lxr->lexer_buf, pline);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_ios_tabs();
|
|
||||||
gen_output_buffers();
|
|
||||||
gen_output_models(lxr);
|
gen_output_models(lxr);
|
||||||
|
gen_pindly_buffers();
|
||||||
delete_lexer(lxr);
|
delete_lexer(lxr);
|
||||||
cleanup_pindly();
|
cleanup_pindly_tab();
|
||||||
current_lexer = NULL;
|
current_lexer = NULL;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
error_return:
|
error_return:
|
||||||
delete_lexer(lxr);
|
delete_lexer(lxr);
|
||||||
cleanup_pindly();
|
cleanup_pindly_tab();
|
||||||
current_lexer = NULL;
|
current_lexer = NULL;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user