/* logicexp.c Convert PSpice LOGICEXP logic expressions into XSPICE gates. Extract typical timing delay estimates from PINDLY statements and insert buffers and tristates with these delays. Reference: PSpice A/D Reference Guide version 16.6 */ #include #include #include #include #include #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" static char *get_pindly_instance_name(void); static char *get_inst_name(void); static char *get_logicexp_tmodel_delays( char *out_name, int gate_op, BOOL isnot, DSTRING *mname); /* 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; 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->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 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); tfree(t); } /* 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_last_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_last_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_last_pos = lx->lexer_pos; 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 char *tmodel_gate_name(int c, BOOL not) { /* Returns an XSPICE model name for the case where logicexp does not have a corresponding pindly but does have a UGATE timing model (not d0_gate). */ static char buf[32]; switch (c) { case '&': if (not) sprintf(buf, "dxspice_dly_nand"); else sprintf(buf, "dxspice_dly_and"); break; case '|': if (not) sprintf(buf, "dxspice_dly_nor"); else sprintf(buf, "dxspice_dly_or"); break; case '^': if (not) sprintf(buf, "dxspice_dly_xnor"); else sprintf(buf, "dxspice_dly_xor"); break; case '~': if (not) sprintf(buf, "dxspice_dly_inverter"); else sprintf(buf, "dxspice_dly_buffer"); break; default: return NULL; } 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) { /* Pspice and MicroCap are vague about what defines an identifier */ if (isalnum(c) || c == '_' || c == '/' || c == '-' || c == '+') return c; else return 0; } static void lexer_back_one(LEXER lx) { lx->lexer_pos = lx->lexer_last_pos; } static int lexer_scan(LEXER lx) { int c; lx->lexer_last_pos = lx->lexer_pos; 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; BOOL need_gt1 = FALSE; if (c == '+') { // an identifier does not begin with '+' lx->lexer_buf[0] = (char) c; lx->lexer_buf[1] = '\0'; return LEX_OTHER; } else if (c == '_' || c == '/' || c == '-') { // these need to be followed by at least one more ident need_gt1 = TRUE; } 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 == 1 && need_gt1) { lx->lexer_buf[1] = '\0'; return LEX_OTHER; } 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, slen; if (!str) { return FALSE; } slen = strlen(str); if (slen < 1) { return FALSE; } for (i = 0; i < slen; i++) { if (!isdigit(str[i])) { return FALSE; } } return TRUE; } /* End of lexical scanner */ /* Start of name entries */ typedef struct name_entry *NAME_ENTRY; struct name_entry { char *name; NAME_ENTRY next; }; static NAME_ENTRY new_name_entry(char *name) { NAME_ENTRY newp; newp = TMALLOC(struct name_entry, 1); newp->next = NULL; newp->name = TMALLOC(char, strlen(name) + 1); strcpy(newp->name, name); return newp; } static NAME_ENTRY add_name_entry(char *name, NAME_ENTRY nelist) { NAME_ENTRY newlist = NULL, x = NULL, last = NULL; if (nelist == NULL) { newlist = new_name_entry(name); return newlist; } for (x = nelist; x; x = x->next) { /* No duplicates */ if (eq(x->name, name)) { //printf("\tFound entry %s\n", x->name); return x; } last = x; } x = new_name_entry(name); last->next = x; //printf("\tAdd entry %s\n", x->name); return x; } static void delete_name_entry(NAME_ENTRY entry) { if (!entry) return; if (entry->name) tfree(entry->name); tfree(entry); } static void clear_name_list(NAME_ENTRY nelist) { NAME_ENTRY x = NULL, next = NULL; if (!nelist) { return; } for (x = nelist; x; x = next) { next = x->next; delete_name_entry(x); } } /* End of name entries */ /* Start of infix to posfix */ #define STACK_SIZE 100 #define PUSH_ERROR 1 #define POP_ERROR 2 #define TMP_PREFIX "tmp__" #define TMP_LEN (strlen(TMP_PREFIX)) struct Stack { int top; char *array[STACK_SIZE]; }; struct gate_data { int type; BOOL finished; BOOL is_not; BOOL is_possible; char *outp; NAME_ENTRY ins; NAME_ENTRY last_input; struct gate_data *nxt; struct gate_data *prev; }; static struct gate_data *first_gate = NULL; static struct gate_data *last_gate = NULL; static struct gate_data *new_gate(int c, char *out, char *i1, char *i2) { NAME_ENTRY np; struct gate_data *gdp = TMALLOC(struct gate_data, 1); gdp->type = c; gdp->finished = gdp->is_possible = FALSE; if (c == '~') { gdp->is_not = TRUE; } else { gdp->is_not = FALSE; } gdp->nxt = gdp->prev = NULL; if (out) { gdp->outp = TMALLOC(char, strlen(out) + 1); strcpy(gdp->outp, out); } else { gdp->outp = NULL; } if (i1) { // Only have second input if there is a first np = new_name_entry(i1); gdp->ins = np; if (i2) { assert(c != '~'); // inverters have only one input np = new_name_entry(i2); gdp->ins->next = np; if (strncmp(i1, TMP_PREFIX, TMP_LEN) == 0 && strncmp(i2, TMP_PREFIX, TMP_LEN) != 0) { gdp->is_possible = TRUE; } } gdp->last_input = np; } else { gdp->ins = NULL; gdp->last_input = NULL; } return gdp; } static struct gate_data *insert_gate(struct gate_data *gp) { if (!first_gate) { first_gate = last_gate = gp; gp->nxt = gp->prev = NULL; } else { last_gate->nxt = gp; gp->nxt = NULL; gp->prev = last_gate; last_gate = gp; } return last_gate; } static char *tilde_tail(char *s, DSTRING *ds) { ds_clear(ds); if (strncmp(s, "tilde_", 6) == 0) { ds_cat_printf(ds, "~%s", s + 6); return ds_get_buf(ds); } else { return s; } } static void move_inputs(struct gate_data *curr, struct gate_data *prev) { if (curr == NULL || prev == NULL) return; if (prev->finished) return; delete_name_entry(curr->ins); curr->ins = prev->ins; prev->last_input->next = curr->last_input; prev->ins = prev->last_input = NULL; prev->finished = TRUE; } static void scan_gates(DSTRING *lhs) { struct gate_data *current = NULL, *previous = NULL, *last_curr = NULL; struct gate_data *prev = NULL; current = first_gate; while (current) { int is_gate = (current->type == '&' || current->type == '^' || current->type == '|'); previous = current->prev; if (is_gate && current->is_possible) { if (previous && previous->type == current->type) { if (eq(current->ins->name, previous->outp)) { move_inputs(current, previous); } } } else if (current->type == '~') { if (previous && (previous->type == '&' || previous->type == '|' || previous->type == '^')) { if (strncmp(current->ins->name, TMP_PREFIX, TMP_LEN) == 0 && strncmp(previous->outp, TMP_PREFIX, TMP_LEN) == 0) { if (eq(current->ins->name, previous->outp)) { tfree(previous->outp); previous->outp = TMALLOC(char, strlen(current->outp) + 1); strcpy(previous->outp, current->outp); previous->is_not = TRUE; current->finished = TRUE; } } } } else if (is_gate) { if (current->finished == FALSE && strncmp(current->ins->name, TMP_PREFIX, TMP_LEN) == 0) { prev = current->prev; while (prev) { if (prev->type == current->type && prev->finished == FALSE && strncmp(prev->outp, TMP_PREFIX, TMP_LEN) == 0 && eq(current->ins->name, prev->outp)) { move_inputs(current, prev); break; } prev = prev->prev; } } } last_curr = current; current = current->nxt; } if (ds_get_length(lhs) > 0 && last_curr) { previous = last_curr; while (previous && previous->finished) { previous = previous->prev; } if (previous) { assert(previous->outp != NULL); assert(previous->finished == FALSE); tfree(previous->outp); previous->outp = TMALLOC(char, ds_get_length(lhs) + 1); strcpy(previous->outp, ds_get_buf(lhs)); } } } static void gen_scanned_gates(struct gate_data *gp) { DS_CREATE(instance, 64); DS_CREATE(ds, 32); DS_CREATE(mname, 32); NAME_ENTRY nm = NULL; if (!gp) return; while (gp) { if (gp->finished) { gp = gp->nxt; continue; } ds_clear(&instance); ds_cat_printf(&instance, "%s ", get_inst_name()); (void) get_logicexp_tmodel_delays(gp->outp, gp->type, gp->is_not, &mname); if (gp->type == '&' || gp->type == '^' || gp->type == '|') { nm = gp->ins; ds_cat_str(&instance, "["); while (nm) { ds_cat_printf(&instance, " %s", tilde_tail(nm->name, &ds)); nm = nm->next; } ds_cat_printf(&instance, " ] %s %s", gp->outp, ds_get_buf(&mname)); } else if (gp->type == '~') { ds_cat_printf(&instance, "%s %s %s", tilde_tail(gp->ins->name, &ds), gp->outp, ds_get_buf(&mname)); } u_add_instance(ds_get_buf(&instance)); gp = gp->nxt; } ds_free(&instance); ds_free(&mname); } static void delete_gates(void) { struct gate_data *g1, *g2; NAME_ENTRY n1, n2; g1 = first_gate; while (g1) { g2 = g1; if (g1->outp) tfree(g1->outp); n1 = g1->ins; while (n1) { n2 = n1; n1 = n1->next; delete_name_entry(n2); } g1 = g1->nxt; tfree(g2); } first_gate = last_gate = NULL; } static int get_precedence(char * s) { switch (s[0]) { case '~': return 4; case '&': return 3; case '^': return 2; case '|': return 1; default: return 0; } } static int push(struct Stack* stack, char * item) { if (stack->top == STACK_SIZE - 1) { fprintf(stderr, "ERROR Postfix stack Overflow\n"); return PUSH_ERROR; } stack->array[++stack->top] = item; return 0; } static char * pop(struct Stack* stack, int *status) { if (stack->top == -1) { fprintf(stderr, "ERROR Postfix stack Underflow\n"); *status = POP_ERROR; return ""; } *status = 0; return stack->array[stack->top--]; } static char *makestr(int c) { static char buf[32]; sprintf(buf, "%c", c); return buf; } static int infix_to_postfix(char* infix, DSTRING * postfix_p) { struct Stack stack; int ltok, last_tok = -1; LEXER lx; NAME_ENTRY nlist = NULL, entry = NULL; int status = 0; int lparen_count = 0, rparen_count = 0; lx = new_lexer(infix); stack.top = -1; nlist = add_name_entry("first", NULL); ds_clear(postfix_p); while ( ( ltok = lexer_scan(lx) ) != 0 ) { // start while ltok loop if (ltok == LEX_ID && last_tok == LEX_ID) { fprintf(stderr, "ERROR (1) no gate operator between two identifiers\n"); status = 1; goto err_return; } if (lex_gate_op(ltok)) { if (lex_gate_op(last_tok)) { fprintf(stderr, "ERROR (2) two consecutive gate operators\n"); status = 1; goto err_return; } else if (last_tok != LEX_ID && last_tok != ')') { fprintf(stderr, "ERROR (2a) gate operator not after ID or rparen\n"); status = 1; goto err_return; } } if (last_tok == '~' && ltok != LEX_ID && ltok != '(') { fprintf(stderr, "ERROR (3) \'~\' is not followed by an ID or lparen\n"); status = 1; goto err_return; } if (ltok == '~' && (last_tok == LEX_ID || last_tok == ')')) { fprintf(stderr, "ERROR (4) \'~\' follows an ID or rparen\n"); status = 1; goto err_return; } if (ltok == ')' && (lex_gate_op(last_tok) || last_tok == '~')) { fprintf(stderr, "ERROR (5) incomplete infix sub-expression\n"); status = 1; goto err_return; } last_tok = ltok; if (ltok == LEX_ID) { ds_cat_printf(postfix_p, " %s", lx->lexer_buf); if (strncmp(lx->lexer_buf, TMP_PREFIX, TMP_LEN) == 0) { printf("WARNING potential name collision %s in logicexp\n", lx->lexer_buf); fflush(stdout); } } else if (ltok == '(') { lparen_count++; entry = add_name_entry(makestr(ltok), nlist); status = push(&stack, entry->name); if (status) { goto err_return; } } else if (ltok == ')') { rparen_count++; while ( stack.top != -1 && !eq(stack.array[stack.top], "(") ) { ds_cat_printf(postfix_p, " %s", pop(&stack, &status)); if (status) { goto err_return; } } pop(&stack, &status); if (status) { goto err_return; } } else if (lex_gate_op(ltok) || ltok == '~') { char *tokstr = makestr(ltok); if (ltok == '~') { // change ~ id --> tilde_id and continue int next_tok; next_tok = lexer_scan(lx); if (next_tok == LEX_ID) { ds_cat_printf(postfix_p, " tilde_%s", lx->lexer_buf); last_tok = next_tok; continue; // while ltok loop } else { lexer_back_one(lx); } } while ( stack.top != -1 && !eq(stack.array[stack.top], "(") && get_precedence(stack.array[stack.top]) >= get_precedence(tokstr) ) { ds_cat_printf(postfix_p, " %s", pop(&stack, &status)); if (status) { goto err_return; } } entry = add_name_entry(tokstr, nlist); status = push(&stack, entry->name); if (status) { goto err_return; } } else { fprintf(stderr, "ERROR (6) unexpected infix token %d \'%s\'\n", ltok, lx->lexer_buf); status = 1; goto err_return; } } // end while ltok loop if (lex_gate_op(last_tok) || last_tok == '~') { fprintf(stderr, "ERROR (7) incomplete infix expression\n"); status = 1; goto err_return; } if (lparen_count != rparen_count) { fprintf(stderr, "ERROR (8) mismatched parentheses\n"); status = 1; goto err_return; } while (stack.top != -1) { ds_cat_printf(postfix_p, " %s", pop(&stack, &status)); if (status) { goto err_return; } } err_return: if (status) { fprintf(stderr, "ERROR invalid infix expression: %s\n", infix); } delete_lexer(lx); clear_name_list(nlist); return status; } static int evaluate_postfix(char* postfix) { static int count = 1; struct Stack stack; stack.top = -1; char *operand1, *operand2; char tmp[32]; int ltok, prevtok = 0; LEXER lx; NAME_ENTRY nlist = NULL, entry = NULL; struct gate_data *gp = NULL; int status = 0; int skip = 1; #ifdef PFX_USE_INVERTERS if (getenv("PFX_USE_INVERTERS")) { skip = 0; } else { skip = 1; } #endif lx = new_lexer(postfix); nlist = add_name_entry("first", NULL); tmp[0] = '\0'; while ( ( ltok = lexer_scan(lx) ) != 0 ) { // while ltok loop if (ltok == LEX_ID) { entry = add_name_entry(lx->lexer_buf, nlist); status = push(&stack, entry->name); if (status) { goto err_return; } } else if (ltok == '~') { operand1 = pop(&stack, &status); if (status) { goto err_return; } sprintf(tmp, "%s%d", TMP_PREFIX, count); count++; gp = new_gate('~', tmp, operand1, NULL); gp = insert_gate(gp); entry = add_name_entry(tmp, nlist); status = push(&stack, entry->name); if (status) { goto err_return; } } else { operand2 = pop(&stack, &status); if (status) { goto err_return; } operand1 = pop(&stack, &status); if (status) { goto err_return; } if (lex_gate_op(ltok)) { sprintf(tmp, "%s%d", TMP_PREFIX, count); count++; gp = new_gate(ltok, tmp, operand1, operand2); gp = insert_gate(gp); entry = add_name_entry(tmp, nlist); status = push(&stack, entry->name); if (status) { goto err_return; } } } prevtok = ltok; } // end while ltok loop if (prevtok == LEX_ID) { char *n1 = NULL; DS_CREATE(ds1, 32); sprintf(tmp, "%s%d", TMP_PREFIX, count); count++; n1 = tilde_tail(pop(&stack, &status), &ds1); if (status) { goto err_return; } if (!skip && n1[0] == '~') { gp = new_gate('~', tmp, n1 + 1, NULL); gp->is_not = TRUE; } else { gp = new_gate('~', tmp, n1, NULL); gp->is_not = FALSE; } gp = insert_gate(gp); ds_free(&ds1); } err_return: if (status) { fprintf(stderr, "ERROR invalid postfix expression: %s\n", postfix); } delete_lexer(lx); clear_name_list(nlist); return status; } /* End of infix to posfix */ /* Start of logicexp parser */ static void aerror(char *s); static BOOL amatch(int t); static BOOL bparse(char *line, BOOL new_lexer); static int lookahead = 0; static int number_of_instances = 0; static BOOL use_tmodel_delays = FALSE; static void cleanup_parser(void) { delete_lexer(parse_lexer); parse_lexer = NULL; } static char *get_inst_name(void) { static char name[64]; static int number = 0; number++; (void) sprintf(name, "a_%d", number); number_of_instances++; return name; } static void gen_models(void) { DS_CREATE(model, 64); ds_clear(&model); ds_cat_printf(&model, ".model d_inv_zero_delay d_inverter(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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(inertial_delay=true 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); fflush(stdout); cleanup_parser(); } static BOOL amatch(int t) { if (lookahead == t) { lookahead = lex_scan(); } else { printf("expect = %d lookahead = %d lexer_buf \"%s\"\n", t, lookahead, parse_lexer->lexer_buf); aerror("amatch: syntax error"); return FALSE; } return TRUE; } static BOOL bstmt_postfix(void) { /* A stmt is: output_name_id = '{' expr '}' */ DS_CREATE(lhs, 32); DS_CREATE(postfix, 1024); DS_CREATE(infix, 1024); char *right_bracket = NULL, *rest = NULL; BOOL retval = TRUE; if (lookahead == LEX_ID) { ds_clear(&lhs); ds_cat_str(&lhs, parse_lexer->lexer_buf); if (strncmp(ds_get_buf(&lhs), TMP_PREFIX, TMP_LEN) == 0) { printf("WARNING potential name collision %s in logicexp\n", ds_get_buf(&lhs)); fflush(stdout); } lookahead = lex_scan(); } else { aerror("bstmt_postfix: syntax error"); retval = FALSE; goto bail_out; } if (!amatch(('='))) { retval = FALSE; goto bail_out; } if (lookahead != '{') { printf("ERROR in bstmt_postfix \'{\' was expected\n"); aerror("bstmt_postfix: syntax error"); retval = FALSE; goto bail_out; } rest = parse_lexer->lexer_line + parse_lexer->lexer_pos; right_bracket = strstr(rest, "}"); if (!right_bracket) { printf("ERROR in bstmt_postfix \'}\' was not found\n"); aerror("bstmt_postfix: syntax error"); retval = FALSE; goto bail_out; } ds_clear(&infix); ds_cat_mem(&infix, rest, right_bracket - rest); if (infix_to_postfix(ds_get_buf(&infix), &postfix)) { retval = FALSE; goto bail_out; } if (evaluate_postfix(ds_get_buf(&postfix))) { retval = FALSE; goto bail_out; } scan_gates(&lhs); gen_scanned_gates(first_gate); lookahead = lex_scan(); while (lookahead != '}') { lookahead = lex_scan(); } lookahead = lex_scan(); bail_out: delete_gates(); ds_free(&lhs); ds_free(&postfix); ds_free(&infix); return retval; } static char *get_logicexp_tmodel_delays( char *out_name, int gate_op, BOOL isnot, DSTRING *mname) { ds_clear(mname); if (use_tmodel_delays) { /* This is the case when logicexp has a UGATE timing model (not d0_gate) and no pindly. */ SYM_TAB entry = NULL; char *nm1 = 0; entry = member_sym_tab(out_name, parse_lexer->lexer_sym_tab); if (entry && (entry->attribute & SYM_OUTPUT)) { nm1 = tmodel_gate_name(gate_op, isnot); if (nm1) { ds_cat_str(mname, nm1); } } if (!nm1) { nm1 = lex_gate_name(gate_op, isnot); ds_cat_str(mname, nm1); } } else { ds_cat_str(mname, lex_gate_name(gate_op, isnot)); } return ds_get_buf(mname); } static BOOL bparse(char *line, BOOL new_lexer) { BOOL ret_val = TRUE; DS_CREATE(stmt, LEX_BUF_SZ); if (new_lexer) lex_init(line); if (!parse_lexer) return FALSE; lookahead = lex_set_start("logic:"); lookahead = lex_scan(); // "logic" lookahead = lex_scan(); // ':' lookahead = lex_scan(); while (lookahead != '\0') { ds_clear(&stmt); ds_cat_str(&stmt, parse_lexer->lexer_buf); if (!bstmt_postfix()) { cleanup_parser(); ret_val= FALSE; break; } } if (ret_val) gen_models(); ds_free(&stmt); cleanup_parser(); 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. NOTE: Combinational gates are generated and usually have zero delays. In XSPICE, the shortest delays are 1.0e-12 secs, not actually zero. Timing delays for LOGICEXP come from an associated PINDLY instance when the timing model is d0_gate. Otherwise the timing model is used for the delay estimates (see f_logicexp). The PINDLY statements generate buffers and tristate buffers which drive the primary outputs from the LOGICEXP outputs. These buffers and tristates have estimated typical delays. */ 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) { fprintf(stderr, "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) { fprintf(stderr, "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; char *uname = NULL; 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; uname = (char *)TMALLOC(char, strlen(parse_lexer->lexer_buf) + 1); strcpy(uname, parse_lexer->lexer_buf); /* 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 { fprintf(stderr, "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 { fprintf(stderr, "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); u_remember_pin(parse_lexer->lexer_buf, 1); } /* 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); u_remember_pin(parse_lexer->lexer_buf, 2); } /* timing model */ t = lex_scan(); if (!expect_token(t, LEX_ID, NULL, TRUE, 12)) goto error_return; if (!eq(parse_lexer->lexer_buf, "d0_gate")) { u_add_logicexp_model(parse_lexer->lexer_buf, "d_and", "dxspice_dly_and"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_nand", "dxspice_dly_nand"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_or", "dxspice_dly_or"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_nor", "dxspice_dly_nor"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_xor", "dxspice_dly_xor"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_xnor", "dxspice_dly_xnor"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_buffer", "dxspice_dly_buffer"); u_add_logicexp_model(parse_lexer->lexer_buf, "d_inverter", "dxspice_dly_inverter"); use_tmodel_delays = TRUE; } else { use_tmodel_delays = FALSE; } (void) add_sym_tab_entry(parse_lexer->lexer_buf, SYM_TMODEL, &parse_lexer->lexer_sym_tab); ret_val = bparse(line, FALSE); current_lexer = NULL; if (!ret_val) { fprintf(stderr, "ERROR parsing logicexp\n"); fprintf(stderr, "ERROR in instance %s\n", uname); cleanup_parser(); } if (uname) tfree(uname); return ret_val; error_return: delete_lexer(parse_lexer); current_lexer = NULL; if (uname) tfree(uname); 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); } static PLINE nth_pindly_entry(PINTABLE pint, int n) { /* Entries are 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; 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, DSTRING *pds) { char *tmpmax = NULL, *tmpmin = NULL; float valmin, valmax, average; char *unitsmin, *unitsmax; char *instance = NULL; ds_clear(pds); if (typ && strlen(typ) > 0 && typ[0] != '-') { ds_cat_str(pds, typ); return ds_get_buf(pds); } 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, &unitsmin); valmax = strtof(tmpmax, &unitsmax); if (!eq(unitsmin, unitsmax)) { printf("WARNING typ_estimate units do not match" " min %s max %s", tmpmin, tmpmax); fflush(stdout); if (unitsmin[0] == unitsmax[0]) { average = (valmin + valmax) / (float)2.0; ds_cat_printf(pds, "%.2f%cs", average, unitsmin[0]); } else if (unitsmin[0] == 'p' && unitsmax[0] == 'n') { valmax = (float)1000.0 * valmax; average = (valmin + valmax) / (float)2.0; ds_cat_printf(pds, "%.2fps", average); } else if (unitsmin[0] == 'n' && unitsmax[0] == 'p') { ds_cat_printf(pds, "%.2fns", valmin); } else { ds_cat_printf(pds, "%.2f%s", valmin, unitsmin); } instance = get_pindly_instance_name(); printf(" using delay %s", ds_get_buf(pds)); if (instance) { printf(" pindly %s\n", instance); } else { printf("\n"); } } else { average = (valmin + valmax) / (float)2.0; ds_cat_printf(pds, "%.2f%s", average, unitsmax); } return ds_get_buf(pds); } } else if (tmpmax && strlen(tmpmax) > 0) { ds_cat_str(pds, tmpmax); return ds_get_buf(pds); } else if (tmpmin && strlen(tmpmin) > 0) { ds_cat_str(pds, tmpmin); return ds_get_buf(pds); } else { return NULL; } return NULL; } static char *get_one_estimate(char *s, DSTRING *pds) { ds_clear(pds); if (s && strlen(s) > 0 && s[0] != '-') { ds_cat_str(pds, s); return ds_get_buf(pds); } else { return NULL; } } static char *get_delay_estimate(char *min, char *typ, char *max, DSTRING *pds) { char *one = NULL; struct udevices_info info = u_get_udevices_info(); int delay_type = info.mntymx; if (delay_type == 1) { // min one = get_one_estimate(min, pds); if (one) { return one; } } else if (delay_type == 2) { // max one = get_one_estimate(max, pds); if (one) { return one; } } // typ return get_typ_estimate(min, typ, max, pds); } static char *mntymx_estimate(char *delay_str, DSTRING *pds) { /* Input string (t1,t2,t2) */ int which = 0; size_t i, slen; char *s; DS_CREATE(dmin, 32); DS_CREATE(dtyp, 32); DS_CREATE(dmax, 32); ds_clear(&dmin); ds_clear(&dtyp); ds_clear(&dmax); slen = strlen(delay_str) - 1; for (i = 1; i < slen; 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_delay_estimate(ds_get_buf(&dmin), ds_get_buf(&dtyp), ds_get_buf(&dmax), pds); ds_free(&dmin); ds_free(&dtyp); ds_free(&dmax); return s; } static BOOL extract_delay( LEXER lx, int val, PLINE *pline_arr, int idx, BOOL tri) { /* NOTE: The delays are specified in a DELAY(t1,t2,t3) function. Beware if the format of t1, t2, t3 changes! Expect t1, t2, t3: -1 or x.y[time_unit] or w[time_unit] where the time_unit is ns, ps, etc. and the same for t1, t2, t3; x.y represents a decimal number; w is an integer. Either numbers can have more that one digit. */ BOOL in_delay = FALSE, ret_val = TRUE; int i; BOOL shorter = FALSE, update_val = FALSE; struct udevices_info info = u_get_udevices_info(); float del_max_val = 0.0, del_val = 0.0, del_min_val = FLT_MAX; char *units; shorter = info.shorter_delays; DS_CREATE(dly, 64); DS_CREATE(ddel_str, 16); DS_CREATE(tmp_ds, 128); if (val != '=') { ds_free(&dly); ds_free(&ddel_str); ds_free(&tmp_ds); return FALSE; } val = lexer_scan(lx); if (val != '{') { ds_free(&dly); ds_free(&ddel_str); ds_free(&tmp_ds); return FALSE; } val = lexer_scan(lx); while (val != '}') { if (val == LEX_ID) { if (eq(lx->lexer_buf, "delay")) { 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; ds_clear(&tmp_ds); in_delay = FALSE; tmps = mntymx_estimate(ds_get_buf(&dly), &tmp_ds); if (!tmps) { ret_val = FALSE; ds_clear(&tmp_ds); break; } del_val = strtof(tmps, &units); update_val = FALSE; if (shorter) { if (del_val < del_min_val) { update_val = TRUE; } } else if (del_val > del_max_val) { update_val = TRUE; } if (update_val) { ds_clear(&delay_string); ds_clear(&ddel_str); ds_cat_str(&ddel_str, tmps); if (shorter) { del_min_val = del_val; } else { del_max_val = del_val; } if (ds_get_length(&ddel_str) > 0) { if (tri) { ds_cat_printf(&delay_string, "(inertial_delay=true delay=%s)", ds_get_buf(&ddel_str)); } else { ds_cat_printf(&delay_string, "(inertial_delay=true rise_delay=%s fall_delay=%s)", ds_get_buf(&ddel_str), ds_get_buf(&ddel_str)); } } else { printf("WARNING pindly DELAY not found\n"); fflush(stdout); if (tri) { ds_cat_printf(&delay_string, "(inertial_delay=true delay=10ns)"); } else { ds_cat_printf(&delay_string, "(inertial_delay=true 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 != '}' ds_free(&dly); ds_free(&ddel_str); ds_free(&tmp_ds); return ret_val; } static BOOL new_gen_output_models(LEXER lx) { int val, arrlen = 0, idx = 0, i; BOOL in_pindly = FALSE, in_tristate = FALSE; DS_CREATE(enable_name, 64); DS_CREATE(last_enable, 64); PLINE pline = NULL; PLINE *pline_arr = NULL; arrlen = num_pindly_entries(pindly_tab); if (arrlen <= 0) { ds_free(&enable_name); ds_free(&last_enable); 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, "boolean") || 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) { pline = find_pindly_out_name(pindly_tab, lx->lexer_buf); if (pline) { pline_arr[idx++] = pline; } else { goto err_return; } val = lexer_scan(lx); if (val == ',') { val = lexer_scan(lx); } } if (!extract_delay(lx, val, pline_arr, idx, FALSE)) goto err_return; 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; val = lexer_scan(lx); if (val != '=') { // if there is no '=' it must be an enable id if (val != LEX_ID) { goto err_return; } } else { // enable id follows '=' val = lexer_scan(lx); if (val != LEX_ID) { goto err_return; } } 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) { 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); u_remember_pin(lx->lexer_buf, 3); } else { goto err_return; } val = lexer_scan(lx); if (val == ',') { val = lexer_scan(lx); } } if (!extract_delay(lx, val, pline_arr, idx, TRUE)) goto err_return; 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(&enable_name); ds_free(&last_enable); tfree(pline_arr); return TRUE; err_return: ds_free(&enable_name); ds_free(&last_enable); tfree(pline_arr); return FALSE; } static char *pindly_instance_name = NULL; static void set_pindly_instance_name(char *name) { if (pindly_instance_name) { tfree(pindly_instance_name); pindly_instance_name = NULL; } if (name) { pindly_instance_name = (char *)TMALLOC(char, strlen(name) + 1); strcpy(pindly_instance_name, name); } } static char *get_pindly_instance_name(void) { return pindly_instance_name; } 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; set_pindly_instance_name(lxr->lexer_buf); /* 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 { fprintf(stderr, "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 { fprintf(stderr, "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 { fprintf(stderr, "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); u_remember_pin(lxr->lexer_buf, 1); } /* 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; if (i < num_ena) { u_remember_pin(lxr->lexer_buf, 1); } } /* 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); u_remember_pin(lxr->lexer_buf, 2); } if (!new_gen_output_models(lxr)) { char *i_name = get_pindly_instance_name(); fprintf(stderr, "ERROR generating models for pindly\n"); if (i_name) { fprintf(stderr, "ERROR in instance %s\n", i_name); } goto error_return;; } gen_pindly_buffers(); delete_lexer(lxr); cleanup_pindly_tab(); current_lexer = NULL; set_pindly_instance_name(NULL); return TRUE; error_return: delete_lexer(lxr); cleanup_pindly_tab(); current_lexer = NULL; set_pindly_instance_name(NULL); return FALSE; }