ngspice/src/frontend/logicexp.c

2118 lines
58 KiB
C

/*
logicexp.c
Convert PSpice LOGICEXP logic expressions into XSPICE gates.
Extract timing delay estimates from PINDLY statements.
Reference: PSpice A/D Reference Guide version 16.6
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ngspice/memory.h"
#include "ngspice/macros.h"
#include "ngspice/bool.h"
#include "ngspice/ngspice.h"
#include "ngspice/stringskip.h"
#include "ngspice/stringutil.h"
#include "ngspice/dstring.h"
#include "ngspice/logicexp.h"
#include "ngspice/udevices.h"
/* Start of btree symbol table */
#define SYM_INPUT 1
#define SYM_OUTPUT 2
#define SYM_TMODEL 4
#define SYM_KEY_WORD 8
#define SYM_ID 16
#define SYM_GATE_OP 32
#define SYM_INVERTER 64
#define SYM_OTHER 128
typedef struct sym_entry *SYM_TAB;
struct sym_entry {
char *name;
char *alias;
int attribute;
int ref_count; // for inverters
SYM_TAB left;
SYM_TAB right;
};
static SYM_TAB new_sym_entry(char *name, int attr)
{
SYM_TAB newp;
newp = TMALLOC(struct sym_entry, 1);
newp->left = NULL;
newp->right = NULL;
newp->name = TMALLOC(char, strlen(name) + 1);
strcpy(newp->name, name);
newp->alias = NULL;
newp->attribute = attr;
newp->ref_count = 0;
return newp;
}
static SYM_TAB insert_sym_tab(char *name, SYM_TAB t, int attr)
{
int cmp;
if (t == NULL) {
t = new_sym_entry(name, attr);
return t;
}
cmp = strcmp(name, t->name);
if (cmp < 0) {
t->left = insert_sym_tab(name, t->left, attr);
} else if (cmp > 0) {
t->right = insert_sym_tab(name, t->right, attr);
} else {
printf("NOTE insert_sym_tab %s already there\n", name);
}
return t;
}
static SYM_TAB member_sym_tab(char *name, SYM_TAB t)
{
int cmp;
while (t != NULL) {
cmp = strcmp(name, t->name);
if (cmp == 0) {
return t;
} else if (cmp < 0) {
t = t->left;
} else {
t = t->right;
}
}
return NULL;
}
static SYM_TAB add_sym_tab_entry(char *name, int attr, SYM_TAB *stab)
{
SYM_TAB entry = NULL;
entry = member_sym_tab(name, *stab);
if (!entry) {
*stab = insert_sym_tab(name, *stab, attr);
entry = member_sym_tab(name, *stab);
}
return entry;
}
static void alias_sym_tab(char *alias, SYM_TAB t)
{
if (t == NULL) { return; }
if (t->alias)
tfree(t->alias);
t->alias = TMALLOC(char, strlen(alias) + 1);
strcpy(t->alias, alias);
}
static void delete_sym_tab(SYM_TAB t)
{
if (t == NULL) { return; }
delete_sym_tab(t->left);
delete_sym_tab(t->right);
if (t->name)
tfree(t->name);
if (t->alias)
tfree(t->alias);
tfree(t);
}
#ifdef TABLE_PRINT
static void print_sym_tab(SYM_TAB t, BOOL with_addr)
{
if (t == NULL) { return; }
print_sym_tab(t->left, with_addr);
if (with_addr)
printf("%p --> \n", (void *)t);
printf("\"%s\" %d ref_count=%d", t->name, t->attribute, t->ref_count);
if (t->alias)
printf(" alias = \"%s\"", t->alias);
printf("\n");
print_sym_tab(t->right, with_addr);
}
#endif
/* End of btree symbol table */
/* Start of lexical scanner */
#define LEX_ID 256
#define LEX_OTHER 257
#define LEX_BUF_SZ 512
#define LEX_INIT_SZ 128
typedef struct lexer *LEXER;
struct lexer {
char *lexer_buf;
char *lexer_line;
int lexer_pos;
int lexer_back;
SYM_TAB lexer_sym_tab;
size_t lexer_blen;
};
static LEXER parse_lexer = NULL;
static LEXER new_lexer(char *line)
{
LEXER lx;
lx = TMALLOC(struct lexer, 1);
lx->lexer_line = TMALLOC(char, (strlen(line) + 1));
strcpy(lx->lexer_line, line);
lx->lexer_pos = lx->lexer_back = 0;
lx->lexer_blen = LEX_INIT_SZ;
lx->lexer_buf = TMALLOC(char, lx->lexer_blen);
(void) memset(lx->lexer_buf, 0, lx->lexer_blen);
lx->lexer_sym_tab = NULL;
return lx;
}
static void delete_lexer(LEXER lx)
{
if (!lx)
return;
if (lx->lexer_buf)
tfree(lx->lexer_buf);
if (lx->lexer_line)
tfree(lx->lexer_line);
if (lx->lexer_sym_tab)
delete_sym_tab(lx->lexer_sym_tab);
tfree(lx);
}
static void lex_init(char *line)
{
parse_lexer = new_lexer(line);
return;
}
static int lexer_set_start(char *s, LEXER lx)
{
char *pos;
if (!lx)
return -1;
pos = strstr(lx->lexer_line, s);
if (!pos)
return -1;
lx->lexer_pos = (int) (pos - &lx->lexer_line[0]);
lx->lexer_back = lx->lexer_pos;
return lx->lexer_pos;
}
static int lex_set_start(char *s)
{
return lexer_set_start(s, parse_lexer);
}
static int lexer_getchar(LEXER lx)
{
int item = 0;
item = lx->lexer_line[lx->lexer_pos];
lx->lexer_back = lx->lexer_pos;
if (item != 0)
lx->lexer_pos++;
return item;
}
static void lexer_putback(LEXER lx)
{
if (lx->lexer_back >= 0)
lx->lexer_pos = lx->lexer_back;
}
static int lex_punct(int c)
{
switch (c) {
case ',':
case '{':
case '}':
case '(':
case ')':
case ':':
case '.':
return c;
default:
break;
}
return 0;
}
static int lex_oper(int c)
{
switch (c) {
case '~':
case '&':
case '^':
case '|':
case '=':
return c;
default:
break;
}
return 0;
}
static char *lex_gate_name(int c, BOOL not)
{
/* returns an XSPICE gate model name */
static char buf[32];
switch (c) {
case '~':
if (not)
sprintf(buf, "d__inverter__1");
else
sprintf(buf, "d__buffer__1");
break;
case '&':
if (not)
sprintf(buf, "d__nand__1");
else
sprintf(buf, "d__and__1");
break;
case '^':
if (not)
sprintf(buf, "d__xnor__1");
else
sprintf(buf, "d__xor__1");
break;
case '|':
if (not)
sprintf(buf, "d__nor__1");
else
sprintf(buf, "d__or__1");
break;
default:
sprintf(buf, "UNKNOWN");
break;
}
return buf;
}
static int lex_gate_op(int c)
{
switch (c) {
case '&':
case '^':
case '|':
return c;
default:
break;
}
return 0;
}
static int lex_ident(int c)
{
if (isalnum(c) || c == '_' || c == '/' || c == '-')
return c;
else
return 0;
}
static int lexer_scan(LEXER lx)
{
int c;
while (1) {
lx->lexer_buf[0] = '\0';
c = lexer_getchar(lx);
if (c == '\0')
return 0;
else if (isspace(c))
continue;
else if (lex_punct(c))
return c;
else if (lex_oper(c))
return c;
else if (lex_ident(c)) {
size_t i = 0;
while (lex_ident(c)) {
if (i >= lx->lexer_blen) {
lx->lexer_blen *= 2;
lx->lexer_buf =
TREALLOC(char, lx->lexer_buf, lx->lexer_blen);
}
lx->lexer_buf[i] = (char) c;
i++;
c = lexer_getchar(lx);
}
if (i >= lx->lexer_blen) {
lx->lexer_blen *= 2;
lx->lexer_buf =
TREALLOC(char, lx->lexer_buf, lx->lexer_blen);
}
lx->lexer_buf[i] = '\0';
if (c != '\0')
lexer_putback(lx);
return LEX_ID;
} else {
lx->lexer_buf[0] = (char) c;
lx->lexer_buf[1] = '\0';
return LEX_OTHER;
}
}
}
static int lex_scan(void)
{
return lexer_scan(parse_lexer);
}
static BOOL lex_all_digits(char *str)
{
size_t i;
if (!str || strlen(str) < 1)
return FALSE;
for (i = 0; i < strlen(str); i++) {
if (!isdigit(str[i])) return FALSE;
}
return TRUE;
}
/* End of lexical scanner */
/* Start parse table */
typedef struct table_line *TLINE;
struct table_line {
char *line;
TLINE next;
};
typedef struct parse_table *PTABLE;
struct parse_table {
TLINE first;
TLINE last;
unsigned int num_entries;
};
static PTABLE parse_tab = NULL;
static PTABLE gen_tab = NULL;
static PTABLE new_parse_table(void)
{
PTABLE pt;
pt = TMALLOC(struct parse_table, 1);
pt->first = pt->last = NULL;
pt->num_entries = 0;
return pt;
}
static void delete_parse_table(PTABLE pt)
{
TLINE t, next;
if (!pt)
return;
next = pt->first;
while (next) {
t = next;
tfree(t->line);
next = t->next;
tfree(t);
}
tfree(pt);
}
static void delete_parse_gen_tables(void)
{
delete_parse_table(parse_tab);
delete_parse_table(gen_tab);
parse_tab = gen_tab = NULL;
}
static void init_parse_tables(void)
{
parse_tab = new_parse_table();
gen_tab = new_parse_table();
}
static TLINE ptab_new_line(char *line)
{
TLINE t = NULL;
t = TMALLOC(struct table_line, 1);
t->next = NULL;
t->line = TMALLOC(char, (strlen(line) + 1));
strcpy(t->line, line);
return t;
}
static TLINE add_common(char *line, BOOL ignore_blank)
{
if (!line)
return NULL;
if (ignore_blank) {
if (line[0] == '\0') {
return NULL;
} else if (line[0] == '\n' && strlen(line) < 2) {
return NULL;
}
}
return ptab_new_line(line);
}
static TLINE add_to_parse_table(PTABLE pt, char *line, BOOL ignore_blank)
{
TLINE t;
if (!pt)
return NULL;
t = add_common(line, ignore_blank);
if (!t)
return NULL;
t->next = NULL;
if (!pt->first) {
pt->first = pt->last = t;
} else {
pt->last->next = t;
pt->last = t;
}
pt->num_entries++;
return t;
}
static TLINE ptab_add_line(char *line, BOOL ignore_blank)
{
TLINE t;
t = add_to_parse_table(parse_tab, line, ignore_blank);
return t;
}
static TLINE gen_tab_add_line(char *line, BOOL ignore_blank)
{
TLINE t;
t = add_to_parse_table(gen_tab, line, ignore_blank);
return t;
}
static char *get_temp_from_line(char *line, BOOL begin)
{
/* First occurrence of "tmp" on the line */
/* If begin is TRUE then "tmp" must be at the start of line */
static char lbuf[64];
char *p, *q;
int j;
p = strstr(line, "tmp");
if (!p)
return NULL;
if (begin && p != line)
return NULL;
for (q = p, j = 0; isalnum(q[j]) || q[j] == '_'; j++) {
if (j >= 63)
return NULL;
lbuf[j] = q[j];
}
lbuf[j] = '\0';
return lbuf;
}
static char *find_temp_begin(char *line)
{
return get_temp_from_line(line, TRUE);
}
static char *find_temp_anywhere(char *line)
{
return get_temp_from_line(line, FALSE);
}
static int get_temp_depth(char *line)
{
char buf[64];
char *p, *endp;
int depth;
p = find_temp_anywhere(line);
if (p) {
strcpy(buf, p);
p = strstr(buf + strlen("tmp"), "__");
if (p) {
p = p + 2;
depth = (int) strtol(p, &endp, 10);
return depth;
}
}
return -1;
}
static TLINE tab_find(PTABLE pt, char *str, BOOL start_of_line)
{
TLINE t;
size_t len;
if (!pt)
return NULL;
t = pt->first;
len = strlen(str);
while (t) {
if (start_of_line) {
if (strncmp(t->line, str, len) == 0)
return t;
} else {
if (strstr(t->line, str))
return t;
}
t = t->next;
}
return NULL;
}
#ifdef TABLE_PRINT
static void table_print(TLINE first)
{
TLINE t;
if (!first)
return;
t = first;
while (t) {
printf("%s\n", t->line);
t = t->next;
}
}
#endif
/* End parse table */
/* Start of logicexp parser */
static char *get_inst_name(void);
static char *get_inverter_output_name(char *input);
static void gen_inverters(SYM_TAB t);
static void aerror(char *s);
static void amatch(int t);
static void bexpr(void);
static void bfactor(void);
static BOOL bparse(char *line, BOOL new_lexer);
static int lookahead = 0;
static int adepth = 0;
static int max_adepth = 0;
static DSTRING d_curr_line;
static char *get_inst_name(void)
{
static char name[64];
static int number = 0;
number++;
(void) sprintf(name, "a_%d", number);
return name;
}
static char *get_inverter_output_name(char *input)
{
static char buf[LEX_BUF_SZ];
LEXER lx = parse_lexer;
// FIX ME keep this name in the symbol table to ensure uniqueness
(void) sprintf(buf, "inv_out__%s", input);
if (member_sym_tab(buf, lx->lexer_sym_tab))
printf("ERROR %s is already in use\n", buf);
return buf;
}
static char *get_inv_tail(char *str)
{
static char lbuf[64];
char *p = NULL, *q = NULL;
int j;
size_t slen = strlen("inv_out__");
p = strstr(str, "inv_out__");
if (!p)
return NULL;
for (q = p + slen, j = 0; q[j] != '\0' && !isspace(q[j]); j++) {
if (j >= 63)
return NULL;
lbuf[j] = q[j];
}
lbuf[j] = '\0';
return lbuf;
}
static void gen_inverters(SYM_TAB t)
{
DS_CREATE(instance, 128);
if (t == NULL)
return;
gen_inverters(t->left);
if (t->attribute & SYM_INVERTER) {
if (t->ref_count >= 1) {
ds_clear(&instance);
ds_cat_printf(&instance, "%s %s %s d_inv_zero_delay",
get_inst_name(), t->name, get_inverter_output_name(t->name));
u_add_instance(ds_get_buf(&instance));
}
}
ds_free(&instance);
gen_inverters(t->right);
}
static void gen_models(void)
{
DS_CREATE(model, 64);
ds_clear(&model);
ds_cat_printf(&model,
".model d_inv_zero_delay d_inverter(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__inverter__1 d_inverter(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__buffer__1 d_buffer(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__nand__1 d_nand(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__and__1 d_and(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__xnor__1 d_xnor(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__xor__1 d_xor(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__nor__1 d_nor(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model,
".model d__or__1 d_or(rise_delay=1.0e-12 fall_delay=1.0e-12)");
u_add_instance(ds_get_buf(&model));
ds_free(&model);
}
static void aerror(char *s)
{
LEXER lx = parse_lexer;
printf("%s [%s]\n", s, lx->lexer_line + lx->lexer_pos);
exit(1);
}
char *get_temp_name(void)
{
static char name[64];
static int number = 0;
number++;
(void) sprintf(name, "tmp%d", number);
return name;
}
static void amatch(int t)
{
LEXER lx = parse_lexer;
if (lookahead == t) {
lookahead = lex_scan();
} else {
printf("t = '%c' [%d] lookahead = '%c' [%d] lexer_buf %s\n",
t, t, lookahead, lookahead, lx->lexer_buf);
aerror("amatch: syntax error");
}
}
static void bfactor(void)
{
/* factor is : [~] (optional) rest
where rest is: id (input_name) | ( expr ) | error
*/
BOOL is_not = FALSE;
SYM_TAB entry = NULL;
LEXER lx = parse_lexer;
adepth++;
if (lookahead == '~') {
is_not = TRUE;
lookahead = lex_scan();
}
if (lookahead == LEX_ID) {
entry = add_sym_tab_entry(lx->lexer_buf, SYM_ID, &lx->lexer_sym_tab);
if (is_not) {
ds_cat_printf(&d_curr_line, "%s ",
get_inverter_output_name(lx->lexer_buf));
entry->attribute |= SYM_INVERTER;
entry->ref_count++;
} else {
ds_cat_printf(&d_curr_line, "%s ", lx->lexer_buf);
}
lookahead = lex_scan();
} else if (lookahead == '(') {
DS_CREATE(tmpnam, 64);
ds_clear(&tmpnam);
if (adepth > max_adepth)
max_adepth = adepth;
ds_cat_str(&tmpnam, get_temp_name());
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
ds_cat_printf(&d_curr_line, "%s__%d <- ", ds_get_buf(&tmpnam), adepth);
if (is_not) {
ds_cat_printf(&d_curr_line, "~ %c", lookahead);
} else {
ds_cat_printf(&d_curr_line, "%c", lookahead);
}
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
lookahead = lex_scan();
bexpr();
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
ds_cat_printf(&d_curr_line, "%c -> %s__%d", lookahead,
ds_get_buf(&tmpnam), adepth);
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
amatch(')');
ds_free(&tmpnam);
} else {
aerror("bfactor: syntax error");
}
adepth--;
}
static void bexpr(void)
{
/* expr is: factor { gate_op factor } (0 or more times). */
bfactor();
while (lex_gate_op(lookahead)) {
ds_cat_printf(&d_curr_line, "%c ", lookahead);
lookahead = lex_scan();
bfactor();
}
}
static void bstmt(void)
{
/* A stmt is: output_name = { expr } */
SYM_TAB entry = NULL;
LEXER lx = parse_lexer;
DS_CREATE(tname, 64);
DS_CREATE(assign, LEX_BUF_SZ);
if (lookahead == LEX_ID) {
entry = add_sym_tab_entry(lx->lexer_buf, SYM_ID, &lx->lexer_sym_tab);
} else {
aerror("bstmt: syntax error");
}
adepth++;
if (adepth > max_adepth)
max_adepth = adepth;
amatch(LEX_ID);
amatch('=');
ds_clear(&assign);
ds_cat_printf(&assign, "%s =", entry->name);
(void) ptab_add_line(ds_get_buf(&assign), TRUE);
amatch('{');
ds_clear(&tname);
ds_cat_str(&tname, get_temp_name());
ds_cat_printf(&d_curr_line, "%s__%d <- (", ds_get_buf(&tname), adepth);
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
bexpr();
if (ds_get_length(&d_curr_line) > 0) {
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
}
ds_clear(&d_curr_line);
ds_cat_printf(&d_curr_line, ") -> %s__%d", ds_get_buf(&tname), adepth);
(void) ptab_add_line(ds_get_buf(&d_curr_line), TRUE);
ds_clear(&d_curr_line);
amatch('}');
ds_free(&assign);
ds_free(&tname);
adepth--;
return;
}
static PTABLE optimize_gen_tab(PTABLE pt)
{
/* This function compacts the gen_tab, returning a new PTABLE.
Aliases are transformed and removed as described below.
Usually, optimized_gen_tab is called a second time on the
PTABLE created by the first call. The algorithm here will
only transform one level of aliases.
*/
TLINE t = NULL;
LEXER lxr = NULL;
int val, idnum = 0, tok_count = 0;
SYM_TAB entry = NULL, alias_tab = NULL;
BOOL found_tilde = FALSE, starts_with_temp = FALSE;
PTABLE new_gen = NULL;
DS_CREATE(scratch, LEX_BUF_SZ);
DS_CREATE(alias, 64);
DS_CREATE(non_tmp_name, 64);
DS_CREATE(tmp_name, 64);
if (!pt || !pt->first)
return NULL;
t = pt->first;
lxr = new_lexer(t->line);
/* Look for tmp... = another_name
t1 = name1 (alias for t1)
t2 = name2 (alias for t2)
t3 = t1 op t2
during second pass transform
ignore t1, t2
t3 = name1 op name2
*/
while (t) {
idnum = 0;
val = lexer_scan(lxr);
ds_clear(&alias);
entry = NULL;
found_tilde = FALSE;
if (find_temp_begin(t->line))
starts_with_temp = TRUE;
else
starts_with_temp = FALSE;
tok_count = 0;
while (val != '\0') {
tok_count++;
if (val == LEX_ID) {
idnum++;
if (idnum == 1) {
entry = add_sym_tab_entry(lxr->lexer_buf, SYM_ID,
&alias_tab);
} else if (idnum == 2) {
ds_cat_str(&alias, lxr->lexer_buf);
}
} else if (val == '~') {
found_tilde = TRUE;
if (tok_count != 3) {
goto quick_return;
}
} else if (val == '=') {
if (tok_count != 2) {
goto quick_return;
}
}
val = lexer_scan(lxr);
}
if (starts_with_temp && !found_tilde && idnum == 2)
alias_sym_tab(ds_get_buf(&alias), entry);
t = t->next;
if (t) {
delete_lexer(lxr);
lxr = new_lexer(t->line);
}
}
delete_lexer(lxr);
/* Second pass, replace names by their aliases.
Perform transformation as mentioned above.
Transform:
t1 = t2 op t3 {op t4 ...} (t* can also be name*, not just tmps)
lhs = t1 (lhs of original x = { expr } statement)
into:
ignore lhs = t1
lhs = t2 op t3 {op t4...}
NOTE that lhs_= t1 should be the last entry in gen_tab.
lhs = t1 (from stmt lhs = { expr }) is the top-most level
in the parse tree, and is encountered last in the evaluation order.
*/
new_gen = new_parse_table();
ds_clear(&scratch);
t = pt->first;
lxr = new_lexer(t->line);
while (t) { // while (t) second pass
BOOL skip = FALSE;
val = lexer_scan(lxr);
idnum = 0;
entry = NULL;
if (find_temp_begin(t->line))
starts_with_temp = TRUE;
else
starts_with_temp = FALSE;
tok_count = 0;
ds_clear(&scratch);
ds_clear(&non_tmp_name);
ds_clear(&tmp_name);
while (val != '\0' && !skip) {
tok_count++;
if (val == LEX_ID) {
idnum++;
entry = member_sym_tab(lxr->lexer_buf, alias_tab);
if (entry && entry->alias) {
if (idnum > 1) {
ds_cat_printf(&scratch, "%s ", entry->alias);
} else if (idnum == 1) {
if (starts_with_temp) {
skip = TRUE;
}
}
} else {
ds_cat_printf(&scratch, "%s ", lxr->lexer_buf);
if (tok_count == 1) {
ds_clear(&non_tmp_name);
if (!find_temp_begin(lxr->lexer_buf))
ds_cat_str(&non_tmp_name, lxr->lexer_buf);
} else if (tok_count == 3) {
if (ds_get_length(&non_tmp_name) > 0) {
char *str1 = NULL;
str1 = find_temp_begin(lxr->lexer_buf);
if (str1) {
ds_clear(&tmp_name);
ds_cat_str(&tmp_name, lxr->lexer_buf);
}
}
}
}
if (idnum > 2) {
ds_clear(&non_tmp_name);
ds_clear(&tmp_name);
}
} else {
if (val == LEX_OTHER) {
delete_parse_table(new_gen);
new_gen = NULL;
goto quick_return;
}
if (val == '~')
found_tilde = TRUE;
ds_cat_printf(&scratch, "%c ", val);
}
val = lexer_scan(lxr);
}
t = t->next;
if (t) {
delete_lexer(lxr);
lxr = new_lexer(t->line);
}
if (!skip) {
TLINE tnamel = NULL;
char *p = NULL;
DS_CREATE(d_buf, 128);
BOOL ignore_lhs = FALSE;
ds_clear(&d_buf);
if (ds_get_length(&tmp_name) > 0)
tnamel = tab_find(new_gen, ds_get_buf(&tmp_name), TRUE);
if (ds_get_length(&non_tmp_name) > 0 && tnamel) {
ignore_lhs = TRUE;
ds_clear(&d_buf);
p = strstr(tnamel->line, " = ");
if (p) {
ds_cat_str(&d_buf, ds_get_buf(&non_tmp_name));
ds_cat_str(&d_buf, p);
tfree(tnamel->line);
tnamel->line = TMALLOC(char, ds_get_length(&d_buf) + 1);
strcpy(tnamel->line, ds_get_buf(&d_buf));
}
}
if (!ignore_lhs) {
(void) add_to_parse_table(new_gen,
ds_get_buf(&scratch), TRUE);
}
ds_free(&d_buf);
}
} // end of while (t) second pass
quick_return:
ds_free(&alias);
ds_free(&scratch);
ds_free(&non_tmp_name);
ds_free(&tmp_name);
delete_lexer(lxr);
delete_sym_tab(alias_tab);
return new_gen;
}
static void gen_gates(PTABLE gate_tab, SYM_TAB parser_symbols)
{
/* gen_gates is called with PTABLE gate_tab being the final
PTABLE produced by optimize_gen_tab(,..) calls.
If gate tab is the orignal uncompacted gen_tab, then extra
redundant intermediate gates will be created.
*/
TLINE t;
LEXER lxr = NULL;
int val, tok_count = 0, gate_op = 0, idnum = 0, in_count = 0;
BOOL found_tilde = FALSE;
DS_CREATE(out_name, 64);
DS_CREATE(in_names, 64);
DS_CREATE(gate_name, 64);
DS_CREATE(instance, 128);
if (!gate_tab || !gate_tab->first)
return;
t = gate_tab->first;
lxr = new_lexer(t->line);
while (t) {
ds_clear(&out_name);
ds_clear(&in_names);
ds_clear(&gate_name);
ds_clear(&instance);
idnum = 0;
val = lexer_scan(lxr);
found_tilde = FALSE;
tok_count = 0;
gate_op = 0;
in_count = 0;
while (val != '\0') {
tok_count++;
if (val == LEX_ID) {
idnum++;
if (idnum == 1) { //output name
ds_cat_str(&out_name, lxr->lexer_buf);
} else { // input name
in_count++;
ds_cat_printf(&in_names, " %s", lxr->lexer_buf);
}
} else if (val == '~') {
found_tilde = TRUE;
if (tok_count != 3) goto quick_return;
} else if (val == '=') {
if (tok_count != 2) goto quick_return;
} else if (lex_gate_op(val)) {
if (gate_op != 0) {
if (val != gate_op) goto quick_return;
}
gate_op = val;
} else {
goto quick_return;
}
val = lexer_scan(lxr);
}
if (in_count == 1) { // buffer or inverter
if (gate_op != 0) goto quick_return;
ds_cat_str(&gate_name, lex_gate_name('~', found_tilde));
} else if (in_count >= 2) { // AND, OR. XOR and inverses
if (gate_op == 0) goto quick_return;
ds_cat_str(&gate_name, lex_gate_name(gate_op, found_tilde));
} else {
goto quick_return;
}
ds_cat_printf(&instance, "%s ", get_inst_name());
if (in_count == 1) {
/* If the input name is inv_out_<tail> use the <tail>
and instantiate an inverter to avoid an extra buffer.
*/
char *tail = NULL;
SYM_TAB ent;
tail = get_inv_tail(ds_get_buf(&in_names));
if (tail && strlen(tail) > 0) {
ds_clear(&gate_name);
ds_cat_str(&gate_name, lex_gate_name('~', TRUE));
ds_cat_printf(&instance, "%s %s ", tail,
ds_get_buf(&out_name));
ent = member_sym_tab(tail, parser_symbols);
if (!ent) goto quick_return;
if ((ent->attribute & SYM_INVERTER) == 0) goto quick_return;
ent->ref_count--;
} else {
ds_cat_printf(&instance, "%s %s ", ds_get_buf(&in_names),
ds_get_buf(&out_name));
}
} else {
ds_cat_printf(&instance, "[%s ] %s ", ds_get_buf(&in_names),
ds_get_buf(&out_name));
}
ds_cat_printf(&instance, "%s", ds_get_buf(&gate_name));
t = t->next;
if (t) {
delete_lexer(lxr);
lxr = new_lexer(t->line);
}
u_add_instance(ds_get_buf(&instance));
}
quick_return:
delete_lexer(lxr);
ds_free(&out_name);
ds_free(&in_names);
ds_free(&gate_name);
ds_free(&instance);
return;
}
/*
gen_tab lines format:
name1 = [~] name2 [op name3 {op namei}+]
[] means optional, {}+ means zero or more times.
op is gate type (&, |, ^), ~ means invert output.
name1 is the gate output, and name2,... are inputs.
& is AND, | is OR, ^ is XOR.
~ & is NAND, ~ | is NOR, ~ ^ is XNOR.
In any given line, all the op values are the same, and don't change.
AND and OR can have >= 2 inputs, XOR can have only 2 inputs.
If there is only a single input, then the gate is BUF or INV(~).
*/
static void bevaluate(TLINE t, int deep)
{
/* TLINE t is the entry in the parse_tab and deep is the call depth
where the parse_tab is transformed into the gen_tab. The deeper
calls are evaluated first, bottom-up, as determined by beval_order.
The tokens in the parse_tab are reassembled into gen_tab lines
as described above.
*/
char *s;
int down = 0;
DS_CREATE(this, 64);
DS_CREATE(other, 64);
DS_CREATE(new_line, LEX_BUF_SZ);
s = find_temp_begin(t->line);
if (!s)
return;
ds_clear(&other);
ds_clear(&new_line);
ds_clear(&this);
ds_cat_str(&this, s);
if (strstr(t->line + ds_get_length(&this), " ~ ")) {
ds_cat_printf(&new_line, "%s = ~ ", ds_get_buf(&this));
} else {
if (deep == 1)
ds_cat_printf(&new_line, "%s ", parse_tab->first->line);
else
ds_cat_printf(&new_line, "%s = ", ds_get_buf(&this));
}
t = t->next;
while (t) {
s = find_temp_anywhere(t->line);
if (s) {
if (eq(ds_get_buf(&this), s)) {
break;
} else {
if (down == 0) {
s = find_temp_begin(t->line);
ds_clear(&other);
ds_cat_str(&other, s);
down = 1;
ds_cat_printf(&new_line, " %s", ds_get_buf(&other));
} else if (down == 1) {
s = find_temp_anywhere(t->line);
if (eq(ds_get_buf(&other), s)) {
down = 0;
ds_clear(&other);
}
}
}
} else if (down == 0) {
s = find_temp_anywhere(t->line);
if (!s) {
ds_cat_printf(&new_line, " %s", t->line);
}
}
t = t->next;
}
(void) gen_tab_add_line(ds_get_buf(&new_line), TRUE);
ds_free(&this);
ds_free(&other);
ds_free(&new_line);
return;
}
static void beval_order(void)
{
/* The parser is top-down recursive descent. The depth is used
so that the parsed data is evaluated bottom-up. Then the
tmp.. regions can be evaluated before they are referenced.
*/
int i, depth;
TLINE t;
size_t slen;
if (!parse_tab || !parse_tab->first)
return;
slen = strlen("tmp");
for (i = max_adepth; i > 0; i--) {
t = parse_tab->first;
while (t) {
char *q;
int cmp = 0;
cmp = strncmp(t->line, "tmp", slen);
if (cmp == 0 && ((q = strstr(t->line, " <- ")) != NULL)) {
depth = get_temp_depth(t->line);
if (depth >= 0) {
if (i == depth) {
bevaluate(t, i);
}
}
}
t = t->next;
}
}
return;
}
static BOOL bparse(char *line, BOOL new_lexer)
{
int stmt_num = 0;
BOOL ret_val = TRUE;
LEXER lx;
PTABLE opt_tab1 = NULL, opt_tab2 = NULL;
DS_CREATE(stmt, LEX_BUF_SZ);
char *seed_buf;
seed_buf = TMALLOC(char, LEX_BUF_SZ);
(void) memcpy(seed_buf, "seed", strlen("seed"));
ds_init(&d_curr_line, seed_buf, strlen("seed"),
LEX_BUF_SZ, ds_buf_type_heap);
ds_clear(&d_curr_line);
if (new_lexer)
lex_init(line);
if (!parse_lexer) return FALSE;
lx = parse_lexer;
lookahead = lex_set_start("logic:");
lookahead = lex_scan(); // "logic"
lookahead = lex_scan(); // ':'
lookahead = lex_scan();
while (lookahead != '\0') {
unsigned int last_count = 0, curr_count = 0;
init_parse_tables();
adepth = max_adepth = 0;
stmt_num++;
ds_clear(&stmt);
ds_cat_str(&stmt, lx->lexer_buf);
bstmt();
beval_order();
/* generate gates only when optimizations are successful */
last_count = gen_tab->num_entries;
if (last_count == 1) {
gen_gates(gen_tab, lx->lexer_sym_tab);
} else if (last_count > 1) {
opt_tab1 = optimize_gen_tab(gen_tab);
if (opt_tab1) {
curr_count = opt_tab1->num_entries;
opt_tab2 = opt_tab1;
while (curr_count > 1 && curr_count < last_count) {
last_count = curr_count;
opt_tab2 = optimize_gen_tab(opt_tab1);
delete_parse_table(opt_tab1);
if (!opt_tab2) {
ret_val = FALSE;
break;
}
opt_tab1 = opt_tab2;
curr_count = opt_tab2->num_entries;
}
if (opt_tab2) {
gen_gates(opt_tab2, lx->lexer_sym_tab);
delete_parse_table(opt_tab2);
}
} else {
ret_val = FALSE;
}
} else {
ret_val = FALSE;
}
delete_parse_gen_tables();
if (!ret_val) {
break;
}
}
ds_free(&d_curr_line);
gen_inverters(lx->lexer_sym_tab);
gen_models();
ds_free(&stmt);
delete_lexer(lx);
return ret_val;
}
/* End of logicexp parser */
/* Start of f_logicexp which is called from udevices.c
See the PSpice reference which describes the LOGICEXP statement syntax.
Combinational gates are generated and usually have zero delays.
The PINDLY statements generate buffers and tristate buffers
which drive the primary outputs from the LOGICEXP outputs.
These buffers have their delays set.
*/
static LEXER current_lexer = NULL;
static BOOL expect_token(
int tok, int expected_tok, char *expected_str, BOOL msg, int loc)
{
if (tok != expected_tok) {
if (msg) {
printf("ERROR expect_token failed tok %d expected_tok %d loc %d\n",
tok, expected_tok, loc);
}
return FALSE;
}
if (tok == LEX_ID) {
if (expected_str) {
LEXER lx = current_lexer;
if (eq(expected_str, lx->lexer_buf))
return TRUE;
else {
if (msg) {
printf(
"ERROR expect_token failed lexer_buf %s expected_str %s loc %d\n",
lx->lexer_buf, expected_str, loc);
}
return FALSE;
}
} else { // Any LEX_ID string matches
return TRUE;
}
}
return TRUE;
}
BOOL f_logicexp(char *line)
{
int t, num_ins = 0, num_outs = 0, i;
char *endp;
BOOL ret_val = TRUE;
lex_init(line);
current_lexer = parse_lexer;
(void) add_sym_tab_entry("logic", SYM_KEY_WORD,
&parse_lexer->lexer_sym_tab);
t = lex_scan(); // U*
if (!expect_token(t, LEX_ID, NULL, TRUE, 1)) goto error_return;
/* logicexp ( int , int ) */
t = lex_scan();
if (!expect_token(t, LEX_ID, "logicexp", TRUE, 2)) goto error_return;
t = lex_scan();
if (!expect_token(t, '(', NULL, TRUE, 3)) goto error_return;
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE, 4)) goto error_return;
if (lex_all_digits(parse_lexer->lexer_buf)) {
num_ins = (int) strtol(parse_lexer->lexer_buf, &endp, 10);
} else {
printf("ERROR logicexp input count is not an integer\n");
goto error_return;
}
t = lex_scan();
if (!expect_token(t, ',', NULL, TRUE, 5)) goto error_return;
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE, 6)) goto error_return;
if (lex_all_digits(parse_lexer->lexer_buf)) {
num_outs = (int) strtol(parse_lexer->lexer_buf, &endp, 10);
} else {
printf("ERROR logicexp output count is not an integer\n");
goto error_return;
}
t = lex_scan();
if (!expect_token(t, ')', NULL, TRUE, 7)) goto error_return;
t = lex_scan(); // pwr
if (!expect_token(t, LEX_ID, NULL, TRUE, 8)) goto error_return;
t = lex_scan(); // gnd
if (!expect_token(t, LEX_ID, NULL, TRUE, 9)) goto error_return;
/* num_ins input ids */
for (i = 0; i < num_ins; i++) {
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE, 10)) goto error_return;
(void) add_sym_tab_entry(parse_lexer->lexer_buf,
SYM_INPUT, &parse_lexer->lexer_sym_tab);
}
/* num_outs output ids */
for (i = 0; i < num_outs; i++) {
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE, 11)) goto error_return;
(void) add_sym_tab_entry(parse_lexer->lexer_buf,
SYM_OUTPUT, &parse_lexer->lexer_sym_tab);
}
/* timing model */
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE, 12)) goto error_return;
//printf("TMODEL: %s\n", parse_lexer->lexer_buf);
(void) add_sym_tab_entry(parse_lexer->lexer_buf,
SYM_TMODEL, &parse_lexer->lexer_sym_tab);
ret_val = bparse(line, FALSE);
current_lexer = NULL;
return ret_val;
error_return:
delete_lexer(parse_lexer);
current_lexer = NULL;
return FALSE;
}
/* Start of f_pindly which is called from udevices.c
See the PSpice reference which describes the PINDLY statement syntax.
Note that only two sections, PINDLY: and TRISTATE:, are considered.
Typical delays are estimated from the DELAY(...) functions.
XSPICE does not have the variety of delays that PSpice supports.
Output buffers and tristate buffers are generated.
*/
/* C++ with templates would generalize the different TABLEs */
typedef struct pindly_line *PLINE;
struct pindly_line {
char *in_name;
char *out_name;
char *ena_name;
char *delays;
PLINE next;
};
typedef struct pindly_table *PINTABLE;
struct pindly_table {
PLINE first;
PLINE last;
int num_entries;
};
static PINTABLE new_pindly_table(void)
{
PINTABLE pint;
pint = TMALLOC(struct pindly_table, 1);
pint->first = pint->last = NULL;
pint->num_entries = 0;
return pint;
}
static int num_pindly_entries(PINTABLE pint)
{
if (!pint)
return 0;
else
return pint->num_entries;
}
static PLINE new_pindly_line(void)
{
PLINE p = NULL;
p = TMALLOC(struct pindly_line, 1);
p->in_name = p->out_name = p->ena_name = p->delays = NULL;
return p;
}
static PLINE add_new_pindly_line(PINTABLE pint)
{
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;
}
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;
}
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 delete_pindly_table(PINTABLE pint)
{
PLINE p, next;
if (!pint)
return;
next = pint->first;
while (next) {
p = next;
if (p->in_name) tfree(p->in_name);
if (p->out_name) tfree(p->out_name);
if (p->ena_name) tfree(p->ena_name);
if (p->delays) tfree(p->delays);
next = p->next;
tfree(p);
}
tfree(pint);
}
//#define TRACE
#ifdef TRACE
static void print_pindly_table(PINTABLE pint)
{
PLINE p, next;
if (!pint)
return;
next = pint->first;
while (next) {
p = next;
printf("in_name \"%s\"", p->in_name);
printf(" out_name \"%s\"", p->out_name);
printf(" ena_name \"%s\"", p->ena_name);
printf(" delays \"%s\"\n", p->delays);
next = p->next;
}
}
#endif
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 (eq(p->out_name, name)) 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;
#ifdef TRACE
print_pindly_table(pindly_tab);
#endif
pline = pindly_tab->first;
while (pline) {
char *iname = NULL;
ds_clear(&dbuf);
iname = get_inst_name();
if (pline->ena_name && strlen(pline->ena_name) > 0) {
ds_cat_printf(&dbuf, "%s %s %s %s d_tristate_buf_%s", iname,
pline->in_name, pline->ena_name, pline->out_name, iname);
} else {
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);
if (pline->ena_name && strlen(pline->ena_name) > 0) {
ds_cat_printf(&dbuf, ".model d_tristate_buf_%s d_tristate%s",
iname, pline->delays);
} else {
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;
}
ds_free(&dbuf);
}
static char *get_typ_estimate(char *min, char *typ, char *max)
{
char *tmpmax = NULL, *tmpmin = NULL;
float valmin, valmax, average;
char *units1, *units2;
static char tbuf[128];
if (typ && strlen(typ) > 0 && typ[0] != '-') {
strcpy(tbuf, typ);
return tbuf;
}
if (max && strlen(max) > 0 && max[0] != '-') {
tmpmax = max;
}
if (min && strlen(min) > 0 && min[0] != '-') {
tmpmin = min;
}
if (tmpmin && tmpmax) {
if (strlen(tmpmin) > 0 && strlen(tmpmax) > 0) {
valmin = strtof(tmpmin, &units1);
valmax = strtof(tmpmax, &units2);
average = (valmin + valmax) / (float)2.0;
sprintf(tbuf, "%.2f%s", average, units2);
if (!eq(units1, units2)) {
printf("WARNING units do not match\n");
}
return tbuf;
}
} else if (tmpmax && strlen(tmpmax) > 0) {
strcpy(tbuf, tmpmax);
return tbuf;
} else if (tmpmin && strlen(tmpmin) > 0) {
strcpy(tbuf, tmpmin);
return tbuf;
} else {
return NULL;
}
return NULL;
}
static char *typical_estimate(char *delay_str)
{
/* Input string (t1,t2,t2) */
int which = 0;
size_t i;
char *s;
DS_CREATE(dmin, 32);
DS_CREATE(dtyp, 32);
DS_CREATE(dmax, 32);
ds_clear(&dmin);
ds_clear(&dtyp);
ds_clear(&dmax);
for (i = 1; i < strlen(delay_str) - 1; i++) {
if (delay_str[i] == ',') {
which++;
continue;
}
switch (which) {
case 0:
ds_cat_char(&dmin, delay_str[i]);
break;
case 1:
ds_cat_char(&dtyp, delay_str[i]);
break;
case 2:
ds_cat_char(&dmax, delay_str[i]);
break;
default:
break;
}
}
s = get_typ_estimate(ds_get_buf(&dmin), ds_get_buf(&dtyp),
ds_get_buf(&dmax));
ds_free(&dmin);
ds_free(&dtyp);
ds_free(&dmax);
return s;
}
static BOOL new_gen_output_models(LEXER lx)
{
int val, arrlen = 0, idx = 0, i;
BOOL in_pindly = FALSE, in_tristate = FALSE, in_delay = FALSE;
BOOL prit = FALSE;
float typ_max_val = 0.0, typ_val = 0.0;
char *units;
DS_CREATE(dly, 64);
DS_CREATE(enable_name, 64);
DS_CREATE(last_enable, 64);
DS_CREATE(dtyp_max_str, 16);
PLINE pline = NULL;
PLINE *pline_arr = NULL;
arrlen = num_pindly_entries(pindly_tab);
if (arrlen <= 0) {
ds_free(&dly);
ds_free(&enable_name);
ds_free(&last_enable);
ds_free(&dtyp_max_str);
return FALSE;
}
pline_arr = TMALLOC(PLINE, arrlen);
ds_clear(&last_enable);
val = lexer_scan(lx);
while (val != 0) { // Outer while loop
if (val == LEX_ID) {
if (eq(lx->lexer_buf, "pindly")) {
in_pindly = TRUE;
in_tristate = FALSE;
val = lexer_scan(lx);
if (val != ':') {
goto err_return;
}
} else if (eq(lx->lexer_buf, "tristate")) {
in_pindly = FALSE;
in_tristate = TRUE;
val = lexer_scan(lx);
if (val != ':') {
goto err_return;
}
} else if (eq(lx->lexer_buf, "setup_hold")
|| eq(lx->lexer_buf, "width")
|| eq(lx->lexer_buf, "freq")
|| eq(lx->lexer_buf, "general")) {
in_pindly = FALSE;
in_tristate = FALSE;
}
}
if (in_pindly && val == LEX_ID) { // start in_pindly and LEX_ID
while (val == LEX_ID) {
if (prit) printf("pindly out \"%s\"\n", lx->lexer_buf);
pline = find_pindly_out_name(pindly_tab, lx->lexer_buf);
if (pline) {
pline_arr[idx++] = pline;
} else {
goto err_return;
}
val = lexer_scan(lx);
}
typ_max_val = 0.0;
if (val != '=') {
goto err_return;
}
val = lexer_scan(lx);
if (val != '{') {
goto err_return;
}
val = lexer_scan(lx);
while (val != '}') {
if (val == LEX_ID) {
if (eq(lx->lexer_buf, "delay")) {
if (prit) printf("Get pindly delay\n");
in_delay = TRUE;
ds_clear(&dly);
} else {
if (in_delay) {
ds_cat_printf(&dly, "%s", lx->lexer_buf);
}
}
} else {
if (in_delay) {
DS_CREATE(delay_string, 64);
ds_cat_printf(&dly, "%c", val);
if (val == ')') {
char *tmps;
in_delay = FALSE;
if (prit) printf("%s\n", ds_get_buf(&dly));
if (prit) printf("estimate \"%s\"\n",
typical_estimate(ds_get_buf(&dly)));
tmps = typical_estimate(ds_get_buf(&dly));
if (!tmps) {
goto err_return;
}
typ_val = strtof(tmps, &units);
if (typ_val > typ_max_val) {
ds_clear(&delay_string);
ds_clear(&dtyp_max_str);
ds_cat_str(&dtyp_max_str, tmps);
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);
} // end if in_delay
}
val = lexer_scan(lx);
} // end while != '}'
for (i = 0; i < arrlen; i++) {
pline_arr[i] = NULL;
}
idx = 0; // end in_pindly and LEX_ID
} else if (in_tristate && val == LEX_ID) {
// start in_tristate and LEX_ID
if (eq(lx->lexer_buf, "enable")) {
val = lexer_scan(lx);
if (val == LEX_ID && (eq(lx->lexer_buf, "hi")
|| eq(lx->lexer_buf, "lo"))) {
BOOL invert = FALSE;
if (eq(lx->lexer_buf, "lo"))
invert = TRUE;
if (prit) printf("tristate enable %s ", lx->lexer_buf);
val = lexer_scan(lx);
if (val != '=') {
goto err_return;
}
val = lexer_scan(lx);
if (val != LEX_ID) {
goto err_return;
}
if (prit) printf("ena \"%s\"\n", lx->lexer_buf);
ds_clear(&enable_name);
if (invert)
ds_cat_char(&enable_name, '~');
ds_cat_str(&enable_name, lx->lexer_buf);
} else {
goto err_return;
}
ds_clear(&last_enable);
ds_cat_ds(&last_enable, &enable_name);
val = lexer_scan(lx);
if (val != LEX_ID) {
goto err_return;
}
} else if (ds_get_length(&last_enable) > 0) {
ds_clear(&enable_name);
ds_cat_ds(&enable_name, &last_enable);
} else {
goto err_return;
}
while (val == LEX_ID) {
if (prit) printf("tristate out \"%s\"\n", lx->lexer_buf);
pline = find_pindly_out_name(pindly_tab, lx->lexer_buf);
if (pline) {
pline_arr[idx++] = pline;
(void) set_ena_name(ds_get_buf(&enable_name), pline);
} else {
goto err_return;
}
val = lexer_scan(lx);
}
typ_max_val = 0.0;
if (val != '=') {
goto err_return;
}
val = lexer_scan(lx);
if (val != '{') {
goto err_return;
}
val = lexer_scan(lx);
while (val != '}') {
if (val == LEX_ID) {
if (eq(lx->lexer_buf, "delay")) {
if (prit) printf("Get tristate delay\n");
in_delay = TRUE;
ds_clear(&dly);
} else {
if (in_delay) {
ds_cat_printf(&dly, "%s", lx->lexer_buf);
}
}
} else {
if (in_delay) {
DS_CREATE(delay_string, 64);
ds_cat_printf(&dly, "%c", val);
if (val == ')') {
char *tmps;
in_delay = FALSE;
if (prit) printf("%s\n", ds_get_buf(&dly));
if (prit) printf("estimate \"%s\"\n",
typical_estimate(ds_get_buf(&dly)));
tmps = typical_estimate(ds_get_buf(&dly));
if (!tmps) {
goto err_return;
}
typ_val = strtof(tmps, &units);
if (typ_val > typ_max_val) {
ds_clear(&delay_string);
ds_clear(&dtyp_max_str);
ds_cat_str(&dtyp_max_str, tmps);
typ_max_val = typ_val;
if (ds_get_length(&dtyp_max_str) > 0) {
ds_cat_printf(&delay_string,
"(delay=%s)",
ds_get_buf(&dtyp_max_str));
} else {
ds_cat_printf(&delay_string,
"(delay=10ns)");
}
for (i = 0; i < idx; i++) {
(void) set_delays(
ds_get_buf(&delay_string),
pline_arr[i]);
}
}
}
ds_free(&delay_string);
} // end if in_delay
}
val = lexer_scan(lx);
} // end while != '}'
for (i = 0; i < arrlen; i++) {
pline_arr[i] = NULL;
}
idx = 0; // end of in_tristate and LEX_ID
}
val = lexer_scan(lx);
} // end of outer while loop
ds_free(&dly);
ds_free(&dtyp_max_str);
ds_free(&enable_name);
ds_free(&last_enable);
tfree(pline_arr);
return TRUE;
err_return:
printf("ERROR in new_gen_output_models\n");
ds_free(&dly);
ds_free(&dtyp_max_str);
ds_free(&enable_name);
ds_free(&last_enable);
tfree(pline_arr);
return FALSE;
}
BOOL f_pindly(char *line)
{
int t, num_ios = 0, num_ena = 0, num_refs = 0, i;
char *endp;
LEXER lxr;
PLINE pline = NULL;
init_pindly_tab();
lxr = new_lexer(line);
current_lexer = lxr;
t = lexer_scan(lxr); // U*
if (!expect_token(t, LEX_ID, NULL, TRUE, 50)) goto error_return;
/* pindly ( int , int, int ) */
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, "pindly", TRUE, 51)) goto error_return;
t = lexer_scan(lxr);
if (!expect_token(t, '(', NULL, TRUE, 52)) goto error_return;
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 53)) goto error_return;
if (lex_all_digits(lxr->lexer_buf)) {
num_ios = (int) strtol(lxr->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly io count is not an integer\n");
goto error_return;
}
t = lexer_scan(lxr);
if (!expect_token(t, ',', NULL, TRUE, 54)) goto error_return;
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 55)) goto error_return;
if (lex_all_digits(lxr->lexer_buf)) {
num_ena = (int) strtol(lxr->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly enable count is not an integer\n");
goto error_return;
}
t = lexer_scan(lxr);
if (!expect_token(t, ',', NULL, TRUE, 56)) goto error_return;
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 57)) goto error_return;
if (lex_all_digits(lxr->lexer_buf)) {
num_refs = (int) strtol(lxr->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly refs count is not an integer\n");
goto error_return;
}
t = lexer_scan(lxr);
if (!expect_token(t, ')', NULL, TRUE, 58)) goto error_return;
t = lexer_scan(lxr); // pwr
if (!expect_token(t, LEX_ID, NULL, TRUE, 59)) goto error_return;
t = lexer_scan(lxr); // gnd
if (!expect_token(t, LEX_ID, NULL, TRUE, 60)) goto error_return;
/* num_ios input ids */
for (i = 0; i < num_ios; i++) {
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 61)) goto error_return;
pline = add_new_pindly_line(pindly_tab);
(void) set_in_name(lxr->lexer_buf, pline);
}
/* num_ena enable nodes which are ignored */
/* num_refs reference nodes which are ignored */
for (i = 0; i < num_ena + num_refs; i++) {
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 62)) goto error_return;
}
/* num_ios output ids */
pline = NULL;
for (i = 0; i < num_ios; i++) {
t = lexer_scan(lxr);
if (!expect_token(t, LEX_ID, NULL, TRUE, 63)) goto error_return;
if (i == 0)
pline = nth_pindly_entry(pindly_tab, i);
else
pline = pline->next;
(void) set_out_name(lxr->lexer_buf, pline);
}
if (!new_gen_output_models(lxr)) {
printf("ERROR in \"%s\"\n", line);
goto error_return;;
}
gen_pindly_buffers();
delete_lexer(lxr);
cleanup_pindly_tab();
current_lexer = NULL;
return TRUE;
error_return:
delete_lexer(lxr);
cleanup_pindly_tab();
current_lexer = NULL;
return FALSE;
}