ngspice/src/frontend/logicexp.c

1602 lines
41 KiB
C
Raw Normal View History

/*
logicexp.c
Convert PSpice LOGICEXP logic expressions into XSPICE gates.
Extract timing delay estimates from PINDLY statements.
*/
#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"
/* #define TRACE */
/* 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);
}
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", 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);
}
/* End of btree symbol table */
/* Start of lexical scanner */
#include <assert.h>
#define LEX_ID 256
#define LEX_OTHER 257
#define LEX_BUF_SZ 512
typedef struct lexer *LEXER;
struct lexer {
char lexer_buf[LEX_BUF_SZ];
char *lexer_line;
int lexer_pos;
int lexer_back;
SYM_TAB lexer_sym_tab;
};
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_buf[0] = '\0';
lx->lexer_sym_tab = NULL;
return lx;
}
static void delete_lexer(LEXER lx)
{
if (!lx)
return;
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 = 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 ':':
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)) {
int i = 0;
while (lex_ident(c)) {
lx->lexer_buf[i] = c;
assert(i < LEX_BUF_SZ);
i++;
c = lexer_getchar(lx);
}
assert(i < LEX_BUF_SZ);
lx->lexer_buf[i] = '\0';
if (c != '\0')
lexer_putback(lx);
return LEX_ID;
} else {
lx->lexer_buf[0] = 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)
{
int 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;
};
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;
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;
}
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;
}
//#define TABLE_PRINT
#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 void 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));
#ifdef TRACE
printf("%s\n", ds_get_buf(&instance));
#endif
u_add_instance(ds_get_buf(&instance));
}
}
ds_free(&instance);
gen_inverters(t->right);
}
static void gen_models(void)
{
DS_CREATE(model, 64);
#ifdef TRACE
printf(".model d_inv_zero_delay d_inverter\n");
printf(".model d__inverter__1 d_inverter\n");
printf(".model d__buffer__1 d_buffer\n");
printf(".model d__nand__1 d_nand\n");
printf(".model d__and__1 d_and\n");
printf(".model d__xnor__1 d_xnor\n");
printf(".model d__xor__1 d_xor\n");
printf(".model d__nor__1 d_nor\n");
printf(".model d__or__1 d_or\n");
#endif
ds_clear(&model);
ds_cat_printf(&model, ".model d_inv_zero_delay d_inverter");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__inverter__1 d_inverter");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__buffer__1 d_buffer");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__nand__1 d_nand");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__and__1 d_and");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__xnor__1 d_xnor");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__xor__1 d_xor");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__nor__1 d_nor");
u_add_instance(ds_get_buf(&model));
ds_clear(&model);
ds_cat_printf(&model, ".model d__or__1 d_or");
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 int bstmt(void)
{
/* A stmt is: output_name = { expr } */
int end_pos;
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();
end_pos = lx->lexer_pos;
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 end_pos;
}
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;
assert(tok_count == 3);
} else if (val == '=') {
assert(tok_count == 2);
}
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);
}
}
ds_free(&alias);
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 {
assert(val != LEX_OTHER);
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
ds_free(&scratch);
ds_free(&non_tmp_name);
ds_free(&tmp_name);
delete_lexer(lxr);
{
int print_it = 0;
if (print_it)
print_sym_tab(alias_tab, FALSE);
}
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;
assert(tok_count == 3);
} else if (val == '=') {
assert(tok_count == 2);
} else if (lex_gate_op(val)) {
if (gate_op != 0)
assert(val == gate_op);
gate_op = val;
} else {
assert(FALSE);
}
val = lexer_scan(lxr);
}
if (in_count == 1) { // buffer or inverter
assert(gate_op == 0);
ds_cat_str(&gate_name, lex_gate_name('~', found_tilde));
} else if (in_count >= 2) { // AND, OR. XOR and inverses
assert(gate_op != 0);
ds_cat_str(&gate_name, lex_gate_name(gate_op, found_tilde));
} else {
assert(FALSE);
}
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);
assert(ent);
assert(ent->attribute & SYM_INVERTER);
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);
}
#ifdef TRACE
printf("%s\n", ds_get_buf(&instance));
#endif
u_add_instance(ds_get_buf(&instance));
}
delete_lexer(lxr);
ds_free(&out_name);
ds_free(&in_names);
ds_free(&gate_name);
ds_free(&instance);
}
/*
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 (strcmp(ds_get_buf(&this), s) == 0) {
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 (strcmp(ds_get_buf(&other), s) == 0) {
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);
}
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;
}
}
}
static void bparse(char *line, BOOL new_lexer)
{
int start_pos = 0, end_pos = 0, stmt_num = 0;
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);
assert(parse_lexer);
lx = parse_lexer;
lookahead = lex_set_start("logic:");
lookahead = lex_scan(); // "logic"
lookahead = lex_scan(); // ':'
lookahead = lex_scan();
while (lookahead != '\0') {
init_parse_tables();
adepth = max_adepth = 0;
stmt_num++;
start_pos = lx->lexer_pos;
ds_clear(&stmt);
ds_cat_str(&stmt, lx->lexer_buf);
end_pos = bstmt();
#ifdef TRACE
ds_cat_mem(&stmt, &lx->lexer_line[start_pos], end_pos - start_pos);
printf("\n* Stmt(%d): %s\n\n", stmt_num, ds_get_buf(&stmt));
#else
(void) start_pos;
(void) end_pos;
#endif
beval_order();
opt_tab1 = optimize_gen_tab(gen_tab);
opt_tab2 = optimize_gen_tab(opt_tab1);
if (opt_tab2) {
gen_gates(opt_tab2, parse_lexer->lexer_sym_tab);
}
delete_parse_table(opt_tab1);
delete_parse_table(opt_tab2);
delete_parse_gen_tables();
}
ds_free(&d_curr_line);
gen_inverters(lx->lexer_sym_tab);
gen_models();
ds_free(&stmt);
#ifdef TRACE
if (!new_lexer)
print_sym_tab(lx->lexer_sym_tab, FALSE);
#endif
delete_lexer(lx);
}
/* End of logicexp parser */
static BOOL expect_token(
int tok, int expected_tok, char *expected_str, BOOL msg)
{
if (tok != expected_tok) {
if (msg) {
printf("ERROR expect_token failed tok %d expected_tok %d\n",
tok, expected_tok);
}
delete_lexer(parse_lexer);
return FALSE;
}
if (tok == LEX_ID) {
if (expected_str) {
if (eq(expected_str, parse_lexer->lexer_buf))
return TRUE;
else {
if (msg) {
printf(
"ERROR expect_token failed lexer_buf %s expected_str %s\n",
parse_lexer->lexer_buf, expected_str);
}
delete_lexer(parse_lexer);
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;
#ifdef TRACE
printf("\nf_logicexp: %s\n", line);
#endif
lex_init(line);
(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)) return FALSE;
/* logicexp ( int , int ) */
t = lex_scan();
if (!expect_token(t, LEX_ID, "logicexp", TRUE)) return FALSE;
t = lex_scan();
if (!expect_token(t, '(', NULL, TRUE)) return FALSE;
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
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");
2022-10-22 00:49:12 +02:00
delete_lexer(parse_lexer);
return FALSE;
}
t = lex_scan();
if (!expect_token(t, ',', NULL, TRUE)) return FALSE;
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
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");
2022-10-22 00:49:12 +02:00
delete_lexer(parse_lexer);
return FALSE;
}
t = lex_scan();
if (!expect_token(t, ')', NULL, TRUE)) return FALSE;
t = lex_scan(); // pwr
if (!expect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
t = lex_scan(); // gnd
if (!expect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
/* num_ins input ids */
for (i = 0; i < num_ins; i++) {
t = lex_scan();
if (!expect_token(t, LEX_ID, NULL, TRUE)) {
return FALSE;
}
(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)) {
return FALSE;
}
(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)) return FALSE;
//printf("TMODEL: %s\n", parse_lexer->lexer_buf);
(void) add_sym_tab_entry(parse_lexer->lexer_buf,
SYM_TMODEL, &parse_lexer->lexer_sym_tab);
bparse(line, FALSE);
return TRUE;
}
/* pindly handling */
static PTABLE ios_in_tab = NULL;
static PTABLE ios_out_tab = NULL;
static void init_ios_tab(void)
{
ios_in_tab = new_parse_table();
ios_out_tab = new_parse_table();
}
static void cleanup_pindly(void)
{
delete_parse_table(ios_in_tab);
delete_parse_table(ios_out_tab);
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
return add_to_parse_table(ios_out_tab, ioname, TRUE);
}
static void print_ios_tabs(void)
{
#ifdef TABLE_PRINT
if (ios_in_tab) {
printf("ios_in_tab\n");
table_print(ios_in_tab->first);
}
if (ios_out_tab) {
printf("ios_out_tab\n");
table_print(ios_out_tab->first);
}
#endif
}
static void gen_output_buffers(void)
{
TLINE tin, tout;
if (ios_in_tab && ios_out_tab) {
if (!ios_in_tab->first || !ios_out_tab->first) {
return;
} else {
DS_CREATE(instance, 128);
tin = ios_in_tab->first;
tout = ios_out_tab->first;
while (tin && tout) {
ds_clear(&instance);
ds_cat_printf(&instance, "%s %s %s d_pindly_buf",
get_inst_name(), tin->line, tout->line);
u_add_instance(ds_get_buf(&instance));
tin = tin->next;
tout = tout->next;
}
u_add_instance(
".model d_pindly_buf d_buffer(rise_delay=10ns fall_delay=10ns)");
ds_free(&instance);
}
}
}
static BOOL pexpect_token(
int tok, int expected_tok, char *expected_str, BOOL msg)
{
BOOL val;
val = expect_token(tok, expected_tok, expected_str, msg);
if (!val)
cleanup_pindly();
return val;
}
BOOL f_pindly(char *line)
{
int t, num_ios = 0, num_refs = 0, num_ena = 0, i;
char *endp;
#ifdef TRACE
printf("\nf_pindly: %s\n", line);
#endif
init_ios_tab();
lex_init(line);
t = lex_scan(); // U*
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
/* pindly ( int , int, int ) */
t = lex_scan();
if (!pexpect_token(t, LEX_ID, "pindly", TRUE)) return FALSE;
t = lex_scan();
if (!pexpect_token(t, '(', NULL, TRUE)) return FALSE;
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
if (lex_all_digits(parse_lexer->lexer_buf)) {
num_ios = (int) strtol(parse_lexer->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly io count is not an integer\n");
delete_lexer(parse_lexer);
cleanup_pindly();
return FALSE;
}
t = lex_scan();
if (!pexpect_token(t, ',', NULL, TRUE)) return FALSE;
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
if (lex_all_digits(parse_lexer->lexer_buf)) {
num_ena = (int) strtol(parse_lexer->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly enable count is not an integer\n");
delete_lexer(parse_lexer);
cleanup_pindly();
return FALSE;
}
if (num_ena != 0) {
delete_lexer(parse_lexer);
cleanup_pindly();
return FALSE;
}
t = lex_scan();
if (!pexpect_token(t, ',', NULL, TRUE)) return FALSE;
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
if (lex_all_digits(parse_lexer->lexer_buf)) {
num_refs = (int) strtol(parse_lexer->lexer_buf, &endp, 10);
} else {
printf("ERROR pindly refs count is not an integer\n");
delete_lexer(parse_lexer);
cleanup_pindly();
return FALSE;
}
t = lex_scan();
if (!pexpect_token(t, ')', NULL, TRUE)) return FALSE;
t = lex_scan(); // pwr
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
t = lex_scan(); // gnd
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) return FALSE;
/* num_ios input ids */
for (i = 0; i < num_ios; i++) {
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) {
return FALSE;
}
(void) ios_tab_add(parse_lexer->lexer_buf, TRUE);
}
/* num_refs reference nodes which are ignored */
for (i = 0; i < num_refs; i++) {
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) {
return FALSE;
}
}
/* num_ios output ids */
for (i = 0; i < num_ios; i++) {
t = lex_scan();
if (!pexpect_token(t, LEX_ID, NULL, TRUE)) {
return FALSE;
}
(void) ios_tab_add(parse_lexer->lexer_buf, FALSE);
}
print_ios_tabs();
gen_output_buffers();
delete_lexer(parse_lexer);
cleanup_pindly();
return TRUE;
}