From 05d61b3987b6fd0b9d9772b912ba5c4f0d8db5e2 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 2 Feb 2025 23:59:41 +0100 Subject: [PATCH 01/27] optimization: replace some sumbol "template" token lookups with existing cached sym[].templ field --- src/actions.c | 8 ++------ src/save.c | 2 +- src/spice_netlist.c | 7 +------ src/token.c | 5 +---- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/actions.c b/src/actions.c index 7bcc6d42..6cd692be 100644 --- a/src/actions.c +++ b/src/actions.c @@ -2010,7 +2010,6 @@ void get_additional_symbols(int what) if(xctx->tok_size && sch[0]) { /* "schematic" token exists and a schematic is specified */ int j; char *sym = NULL; - char *templ = NULL; char *symname_attr = NULL; int ignore_schematic = 0; xSymbol *symptr = xctx->inst[i].ptr + xctx->sym; @@ -2030,15 +2029,13 @@ void get_additional_symbols(int what) my_strdup2(_ALLOC_ID_, &sym, add_ext(rel_sym_path(sch), ".sym")); } - my_strdup2(_ALLOC_ID_, &templ, get_tok_value(symptr->prop_ptr, "template", 0)); my_mstrcat(_ALLOC_ID_, &symname_attr, "symname=", get_cell(sym, 0), NULL); my_mstrcat(_ALLOC_ID_, &symname_attr, " symref=", get_sym_name(i, 9999, 1, 1), NULL); my_strdup(_ALLOC_ID_, &spice_sym_def, translate3(spice_sym_def, 1, xctx->inst[i].prop_ptr, - templ, + symptr->templ, symname_attr)); dbg(1, "get_additional_symbols(): spice_sym_def=%s\n", spice_sym_def); - my_free(_ALLOC_ID_, &templ); my_free(_ALLOC_ID_, &symname_attr); /* if instance symbol has default_schematic set to ignore copy the symbol anyway, since * the base symbol will not be netlisted by *_block_netlist() */ @@ -2395,8 +2392,7 @@ int descend_schematic(int instnumber, int fallback, int alert, int set_title) my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].prop_ptr, xctx->inst[n].prop_ptr); - my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].templ, - get_tok_value(xctx->sym[xctx->inst[n].ptr].prop_ptr, "template", 0)); + my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].templ, xctx->sym[xctx->inst[n].ptr].templ); dbg(1,"descend_schematic(): inst_number=%d\n", inst_number); my_strcat(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], find_nth(str, ",", "", 0, inst_number)); diff --git a/src/save.c b/src/save.c index c088b816..28f135cc 100644 --- a/src/save.c +++ b/src/save.c @@ -5140,7 +5140,7 @@ int descend_symbol(void) my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].prop_ptr, xctx->inst[n].prop_ptr); my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].templ, - get_tok_value(xctx->sym[xctx->inst[n].ptr].prop_ptr, "template", 0)); + xctx->sym[xctx->inst[n].ptr].templ); if(!xctx->inst[n].embed) /* use -1 to keep track we are descending into symbol from instance with no embed attr diff --git a/src/spice_netlist.c b/src/spice_netlist.c index 911289d1..6e17771d 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -641,14 +641,9 @@ int spice_block_netlist(FILE *fd, int i, int alert) my_strdup(_ALLOC_ID_, &sym_def, get_tok_value(xctx->sym[i].prop_ptr,"spice_sym_def",0)); if(sym_def) { char *symname_attr = NULL; - char *templ = NULL; const char *translated_sym_def; - my_strdup2(_ALLOC_ID_, &templ, get_tok_value(xctx->sym[i].prop_ptr, "template", 0)); my_mstrcat(_ALLOC_ID_, &symname_attr, "symname=", get_cell(name, 0), NULL); - translated_sym_def = translate3(sym_def, 1, "", - templ, - symname_attr); - my_free(_ALLOC_ID_, &templ); + translated_sym_def = translate3(sym_def, 1, "", xctx->sym[i].templ, symname_attr); my_free(_ALLOC_ID_, &symname_attr); fprintf(fd, "%s\n", translated_sym_def); my_free(_ALLOC_ID_, &sym_def); diff --git a/src/token.c b/src/token.c index e8e3d855..246f5d08 100644 --- a/src/token.c +++ b/src/token.c @@ -1856,7 +1856,6 @@ static int has_included_subcircuit(int inst, int symbol, char **result) if(xctx->tok_size) { char *symname = NULL; - char *templ = NULL; char *symname_attr = NULL; int no_of_pins = (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; int i; @@ -1865,7 +1864,6 @@ static int has_included_subcircuit(int inst, int symbol, char **result) Str_hashentry *entry; Str_hashtable table = {NULL, 0}; - my_strdup2(_ALLOC_ID_, &templ, get_tok_value(xctx->sym[symbol].prop_ptr, "template", 0)); my_strdup2(_ALLOC_ID_, &symname, get_tok_value(xctx->inst[inst].prop_ptr, "schematic", 0)); if(!symname[0]) { my_strdup2(_ALLOC_ID_, &symname, get_tok_value(xctx->sym[symbol].prop_ptr, "schematic", 0)); @@ -1876,7 +1874,7 @@ static int has_included_subcircuit(int inst, int symbol, char **result) my_mstrcat(_ALLOC_ID_, &symname_attr, "symname=", get_cell(symname, 0), NULL); my_mstrcat(_ALLOC_ID_, &symname_attr, " symref=", get_sym_name(inst, 9999, 1, 1), NULL); translated_sym_def = translate3(spice_sym_def, 1, xctx->inst[inst].prop_ptr, - templ, + xctx->sym[symbol].templ, symname_attr); dbg(1, "has_included_subcircuit(): translated_sym_def=%s\n", translated_sym_def); dbg(1, "has_included_subcircuit(): symname=%s\n", symname); @@ -1915,7 +1913,6 @@ static int has_included_subcircuit(int inst, int symbol, char **result) tclvareval("has_included_subcircuit {", get_cell(symname, 0), "} {", translated_sym_def, "} ", my_itoa(exp_no_of_pins), NULL); - my_free(_ALLOC_ID_, &templ); my_free(_ALLOC_ID_, &symname_attr); if(tclresult()[0]) { /* a valid spice_sym_def netlist was found */ char *subckt_pin, *pin_save; From 795b5557a55878b8c62ba3be9a0592b345f79a1b Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 3 Feb 2025 02:15:41 +0100 Subject: [PATCH 02/27] print_spice_element(), primnt_verilog_primitive(), print_vhdl_primitive(): format string is passed to translate() to do a round of @xxx param substitutions looking up xxx=... in instance attributes. --- src/token.c | 61 +++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/token.c b/src/token.c index 246f5d08..a0aa55d5 100644 --- a/src/token.c +++ b/src/token.c @@ -1174,9 +1174,18 @@ static void print_vhdl_primitive(FILE *fd, int inst) /* netlist primitives, 200 if(c=='\0') { - if(result && strstr(result, "tcleval(")== result) { + /* do one level of substitutions to resolve remaining @params and/or tcl expr/code */ + if(result) { dbg(1, "print_vhdl_primitive(): before translate() result=%s\n", result); - my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + if(!strcmp(xctx->sym[xctx->inst[inst].ptr].type, "netlist_commands")) { + /* since netlist_commands often have @ characters in spice node save / plot commands, do + * not pass through translate, unless a tcleval(...) is present */ + if(strstr(result, "tcleval(")== result) { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } + } else { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } dbg(1, "print_vhdl_primitive(): after translate() result=%s\n", result); } if(result) fprintf(fd, "%s", result); @@ -2470,32 +2479,20 @@ int print_spice_element(FILE *fd, int inst) /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions * can be calculated */ - /* do one level of substitutions to resolve @params and equations*/ - if(result && strstr(result, "tcleval(")== result) { + /* do one level of substitutions to resolve remaining @params and/or tcl expr/code */ + if(result) { dbg(1, "print_spice_element(): before translate() result=%s\n", result); - my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + if(!strcmp(xctx->sym[xctx->inst[inst].ptr].type, "netlist_commands")) { + /* since netlist_commands often have @ characters in spice node save / plot commands, do + * not pass through translate, unless a tcleval(...) is present */ + if(strstr(result, "tcleval(")== result) { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } + } else { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } dbg(1, "print_spice_element(): after translate() result=%s\n", result); } - - - /* can't remember what the f**k this is supposed to do. - why eval( and not tcleval( ? - disable until some regression pops out - */ - #if 0 - * /* do a second round of substitutions, but without calling tcl */ - * if(result && strstr(result, "eval(") == result) { - * char *c = strrchr(result, ')'); - * if(c) while(1) { /* shift following characters back 1 char */ - * *c = (char)c[1]; - * c++; - * if(!*c) break; - * } - * my_strdup2(_ALLOC_ID_, &result, translate(inst, result+5)); - * } - #endif - - if(result) fprintf(fd, "%s", result); my_free(_ALLOC_ID_, &template); my_free(_ALLOC_ID_, &format); @@ -3104,10 +3101,18 @@ static void print_verilog_primitive(FILE *fd, int inst) /* netlist switch level } if(c=='\0') { - /* do one level of substitutions to resolve @params and equations*/ - if(result && strstr(result, "tcleval(")== result) { + /* do one level of substitutions to resolve remaining @params and/or tcl expr/code */ + if(result) { dbg(1, "print_verilog_primitive(): before translate() result=%s\n", result); - my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + if(!strcmp(xctx->sym[xctx->inst[inst].ptr].type, "netlist_commands")) { + /* since netlist_commands often have @ characters in spice node save / plot commands, do + * not pass through translate, unless a tcleval(...) is present */ + if(strstr(result, "tcleval(")== result) { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } + } else { + my_strdup(_ALLOC_ID_, &result, translate(inst, result)); + } dbg(1, "print_verilog_primitive(): after translate() result=%s\n", result); } if(result) fprintf(fd, "%s", result); From 4db384a67133046c00130e4f52d81928a7115b32 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 5 Feb 2025 16:33:23 +0100 Subject: [PATCH 03/27] add eval_expr.y for evaluation of generic math expressions in infix notation --- src/Makefile.in | 7 +- src/eval_expr.y | 208 ++++++++++++++++++++++++++++++++++++++++++++++++ src/scheduler.c | 10 +++ src/xinit.c | 2 + src/xschem.h | 3 + 5 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 src/eval_expr.y diff --git a/src/Makefile.in b/src/Makefile.in index 4eec5b71..ec2d23ef 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -5,7 +5,7 @@ put /local/src { select.c font.c editprop.c save.c paste.c token.c psprint.c node_hash.c hilight.c options.c vhdl_netlist.c svgdraw.c spice_netlist.c tedax_netlist.c verilog_netlist.c parselabel.c expandlabel.c - in_memory_undo.c cairo_jpg.c + eval_expr.c in_memory_undo.c cairo_jpg.c } # list all files that need to be installed in "$(XSHAREDIR)" @@ -49,12 +49,15 @@ parselabel.c: parselabel.l expandlabel.h expandlabel.c expandlabel.h: expandlabel.y bison -d -o expandlabel.c expandlabel.y +eval_expr.c: eval_expr.y + bison -o eval_expr.c eval_expr.y + parselabel.o: expandlabel.h $(OBJ): xschem.h ../config.h Makefile clean: FORCE - rm -rf rawtovcd xschem *.o expandlabel.[ch] parselabel.c + rm -rf rawtovcd xschem *.o eval_expr.c expandlabel.[ch] parselabel.c # Explicit rule for each object: @] diff --git a/src/eval_expr.y b/src/eval_expr.y new file mode 100644 index 00000000..c7d14773 --- /dev/null +++ b/src/eval_expr.y @@ -0,0 +1,208 @@ +%{ +#include +#include /* For math functions, cos(), sin(), etc. */ +#include +#include +#include +#include +#include "xschem.h" + +static const char *str, *strptr; +static char *ret = NULL; + +/* Data type for links in the chain of functions. */ +struct symrec +{ + char *name; /* name of symbol */ + double (*fnctptr)(); /* value of a FNCT */ + struct symrec *next; /* link field */ +}; + +typedef struct symrec symrec; + +/* The symbol table: a chain of `struct symrec'. */ +symrec *sym_table = (symrec *)0; + +static symrec *getsym(char *sym_name); +static symrec *putsym(char *sym_name); +static int kklex(); +static void kkerror(char *s); +static double toint(double x); +static void get_expr(double x); + +struct fn +{ + char *fname; + double (*fnct)(); +}; + +struct fn fn_array[] + = { + {"int" , toint}, + {"sin" , sin}, + {"cos" , cos}, + {"asin", asin}, + {"acos", acos}, + {"atan", atan}, + {"log" , log10}, + {"ln" , log}, + {"exp" , exp}, + {"sqrt", sqrt}, + {0 , 0} + }; +%} + +%define api.prefix {kk} +%union { +double val; /* For returning numbers. */ +symrec *tptr; /* For returning symbol-table pointers */ +} + +%token STREND 0 +%token NUM /* Simple double precision number */ +%token FNCT /* Variable and Function */ +%type exp +%right '=' +%left '-' '+' +%left '*' '/' '%' +%left NEG /* Negation--unary minus */ +%right '^' /* Exponentiation */ + +/* Grammar follows */ +%% + +line: + | exp STREND {get_expr($1);} + | '\'' exp '\'' STREND {get_expr($2);} +; + +exp: NUM { $$ = $1;} + | FNCT '(' exp ')' { $$ = $1 ? (*($1->fnctptr))($3) : 0.0;} + | exp '+' exp { $$ = $1 + $3; } + | exp '-' exp { $$ = $1 - $3;} + | exp '*' exp { $$ = $1 * $3;} + | exp '%' exp { $$ = (int)$1 % (int)$3;} + | exp '/' exp { $$ = $1 / $3;} + | '-' exp %prec NEG { $$ = -$2;} + | exp '^' exp { $$ = pow ($1, $3);} + | '(' exp ')' { $$ = $2;} +; +/* End of grammar */ +%% + +static void get_expr(double x) +{ + char xx[100]; + my_snprintf(xx, S(xx), "%.17g", x); + my_mstrcat(_ALLOC_ID_, &ret, xx, NULL); + strptr = str; +} + +static double toint(double x) +{ + if(x < 0.0) return ceil(x); + return floor(x); +} + +static void kkerror(char *s) /* Called by kkparse on error */ +{ + /* printf("error: |%s|\n\n |%s|\n", s, strptr); */ + my_mstrcat(_ALLOC_ID_, &ret, strptr, NULL); +} + +static symrec *getsym(char *sym_name) +{ + symrec *ptr; + for (ptr = sym_table; ptr; ptr = ptr->next) + if (strcmp (ptr->name,sym_name) == 0) return ptr; + return NULL; +} + +symrec * putsym (char *sym_name) +{ + symrec *ptr; + ptr = (symrec *) my_malloc(_ALLOC_ID_, sizeof (symrec)); + ptr->name = (char *) my_malloc(_ALLOC_ID_, strlen (sym_name) + 1); + strcpy (ptr->name,sym_name); + ptr->next = (struct symrec *)sym_table; + sym_table = ptr; + return ptr; +} + +void eval_expr_init_table(void) /* puts arithmetic functions in table. */ +{ + int i; + symrec *ptr; + for (i = 0; fn_array[i].fname != 0; i++) + { + ptr = putsym (fn_array[i].fname); + ptr->fnctptr = fn_array[i].fnct; + } +} + +void eval_expr_clear_table(void) +{ + symrec *ptr; + for (ptr = sym_table; ptr; ptr = ptr->next) { + symrec *tmp = ptr; + my_free(_ALLOC_ID_, &(tmp->name)); + my_free(_ALLOC_ID_, &tmp); + } +} + +static int kklex() +{ + int c; + + if(!str) { return 0; } + /* Ignore whitespace, get first nonwhite character. */ + while ((c = *str++) == ' ' || c == '\t' || c == '\n'); + if (c == 0) { + str = NULL; + return 0; + } + /* Char starts a number => parse the number. */ + if (c == '.' || isdigit (c)) + { + char s[100] =""; + int rd = 0; + str--; + + sscanf(str, "%99[.0-9a-zA-Z_]%n", s, &rd); + kklval.val = atof_spice(s); + str += rd; + /* printf("yylex: NUM: %s\n", s); */ + return NUM; + } + /* Char starts an identifier => read the name. */ + if(isalpha(c)) { + symrec *s; + int length = 40; /* max length of function names */ + char symbuf[41]; + int i = 0; + /* Initially make the buffer int enough + * for a 40-character symbol name. */ + do + { + symbuf[i++] = (char) c; /* Add this character to the buffer.*/ + c = *str++; /* Get another character.*/ + } + while (c != 0 && isalnum(c) && i < length); + str--; + symbuf[i] = '\0'; + s = getsym (symbuf); + kklval.tptr = s; + /* printf("yylex: FNCT=%s\n", symbuf); */ + return FNCT; + } + /* Any other character is a token by itself. */ + return c; +} + +char *eval_expr(const char *s) +{ + if(ret) my_free(_ALLOC_ID_, &ret); + strptr = str = s; + kkparse(); + return ret; +} diff --git a/src/scheduler.c b/src/scheduler.c index 22322d33..8641ca0f 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1008,6 +1008,16 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } } + + /* eval_expr str + * debug function: evaluate arithmetic expression in str */ + else if(!strcmp(argv[1], "eval_expr")) + { + if(argc > 2) { + Tcl_SetResult(interp, eval_expr(argv[2]), TCL_VOLATILE); + } + } + /* exit [exit_code] [closewindow] [force] * Exit the program, ask for confirm if current file modified. * if exit_code is given exit with its value, otherwise use 0 exit code diff --git a/src/xinit.c b/src/xinit.c index 6d3058f7..deb35b6f 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -952,6 +952,7 @@ static void xwin_exit(void) trim_chars(NULL, ""); /* clear static data in function */ tcl_hook2(NULL); /* clear static data in function */ save_ascii_string(NULL, NULL, 0); /* clear static data in function */ + eval_expr_clear_table(); /* clear expression parser data */ dbg(1, "xwin_exit(): removing font\n"); for(i=0;i<127; ++i) my_free(_ALLOC_ID_, &character[i]); dbg(1, "xwin_exit(): closed display\n"); @@ -2307,6 +2308,7 @@ int Tcl_AppInit(Tcl_Interp *inter) #ifdef __unix__ const char* home_buff; #endif + eval_expr_init_table(); /* get PWD and HOME */ if(!getcwd(pwd_dir, PATH_MAX)) { fprintf(errfp, "Tcl_AppInit(): getcwd() failed\n"); diff --git a/src/xschem.h b/src/xschem.h index 1ddfb2de..b14bec10 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1671,6 +1671,9 @@ extern void check_box_storage(int c); extern void check_arc_storage(int c); extern void check_line_storage(int c); extern void check_polygon_storage(int c); +extern void eval_expr_init_table(void); +extern void eval_expr_clear_table(void); +extern char *eval_expr(const char *s); extern const char *expandlabel(const char *s, int *m); extern void parse(const char *s); extern void clear_expandlabel_data(void); From 70c1e542b2f1f57e2f62d6556308b1105ddf2345 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 5 Feb 2025 18:20:36 +0100 Subject: [PATCH 04/27] attributes within expr(...) where expr() contains an infix arith expression will evaluate if possible. Otherwise the string within expr(...) will be returned unchanged. Changes done in translate() and print_spice_element() --- src/eval_expr.y | 2 +- src/token.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/eval_expr.y b/src/eval_expr.y index c7d14773..8a94444b 100644 --- a/src/eval_expr.y +++ b/src/eval_expr.y @@ -93,7 +93,7 @@ exp: NUM { $$ = $1;} static void get_expr(double x) { char xx[100]; - my_snprintf(xx, S(xx), "%.17g", x); + my_snprintf(xx, S(xx), "%.16g", x); my_mstrcat(_ALLOC_ID_, &ret, xx, NULL); strptr = str; } diff --git a/src/token.c b/src/token.c index a0aa55d5..7eb1576c 100644 --- a/src/token.c +++ b/src/token.c @@ -2246,6 +2246,13 @@ int print_spice_element(FILE *fd, int inst) if (!xctx->tok_size) value=get_tok_value(template, token+1, 0); token_exists = xctx->tok_size; + if(strstr(value, "expr(") == value) { + char *ptr; + my_strdup(_ALLOC_ID_, &val, value); + ptr = strrchr(val + 5, ')'); + *ptr = '\0'; + value = eval_expr(translate3(val + 5, 1, xctx->inst[inst].prop_ptr, template, NULL)); + } if(!strcmp("@savecurrent", token)) { token_exists = 0; /* processed later */ value = NULL; @@ -4479,6 +4486,13 @@ const char *translate(int inst, const char* s) dbg(1, "2 translate(): lcc[%d].prop_ptr=%s, value1=%s\n", i-1, lcc[i-1].prop_ptr, value1); i--; } + if(strstr(value1, "expr(") == value1) { + char *ptr = strrchr(value1 + 5, ')'); + dbg(0, "translate(): expr():%s\n", value1); + *ptr = '\0'; + my_strdup2(_ALLOC_ID_, &value1, eval_expr( + translate3(value1 + 5, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + } tmp=strlen(value1); STR_ALLOC(&result, tmp + result_pos, &size); memcpy(result+result_pos, value1, tmp+1); From 0c4cdb3ea122eba2e0825ef85f8f7ad44657c04b Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 01:11:11 +0100 Subject: [PATCH 05/27] propagate changed key bindings to new opened windows (in no-tabs mode) --- src/callback.c | 46 +++++++++++++++++++++++----------------------- src/globals.c | 2 +- src/scheduler.c | 20 ++++++++++---------- src/xinit.c | 9 +++++---- src/xschem.h | 6 +++--- src/xschem.tcl | 24 ++++++++++++------------ 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/callback.c b/src/callback.c index b91a4076..2f2d4b3b 100644 --- a/src/callback.c +++ b/src/callback.c @@ -2158,7 +2158,7 @@ static void end_shape_point_edit(double c_snap) } #if defined(__unix__) && HAS_CAIRO==1 -static int grabscreen(const char *winpath, int event, int mx, int my, KeySym key, +static int grabscreen(const char *win_path, int event, int mx, int my, KeySym key, int button, int aux, int state) { static int grab_state = 0; @@ -2285,8 +2285,8 @@ static int grabscreen(const char *winpath, int event, int mx, int my, KeySym key /* main window callback */ /* mx and my are set to the mouse coord. relative to window */ -/* winpath: set to .drw or sub windows .x1.drw, .x2.drw, ... */ -int callback(const char *winpath, int event, int mx, int my, KeySym key, +/* win_path: set to .drw or sub windows .x1.drw, .x2.drw, ... */ +int callback(const char *win_path, int event, int mx, int my, KeySym key, int button, int aux, int state) { char str[PATH_MAX + 100]; @@ -2310,9 +2310,9 @@ int rstate; /* (reduced state, without ShiftMask) */ * on such events */ if(fix_mouse_coord) { if(event == KeyPress || event == KeyRelease) { - tclvareval("getmousex ", winpath, NULL); + tclvareval("getmousex ", win_path, NULL); mx = atoi(tclresult()); - tclvareval("getmousey ", winpath, NULL); + tclvareval("getmousey ", win_path, NULL); my = atoi(tclresult()); dbg(1, "mx = %d my=%d\n", mx, my); } @@ -2348,21 +2348,21 @@ int rstate; /* (reduced state, without ShiftMask) */ #if 0 /* exclude Motion and Expose events */ if(event!=6 /* && event!=12 */) { - dbg(0, "callback(): state=%d event=%d, winpath=%s, old_winpath=%s, semaphore=%d\n", - state, event, winpath, old_winpath, xctx->semaphore+1); + dbg(0, "callback(): state=%d event=%d, win_path=%s, old_win_path=%s, semaphore=%d\n", + state, event, win_path, old_win_path, xctx->semaphore+1); } #endif /* Schematic window context switch */ redraw_only =0; - if(strcmp(old_winpath, winpath) ) { + if(strcmp(old_win_path, win_path) ) { if( xctx->semaphore >= 1 || event == Expose) { - dbg(1, "callback(): semaphore >=2 (or Expose) switching window context: %s --> %s\n", old_winpath, winpath); + dbg(1, "callback(): semaphore >=2 (or Expose) switching window context: %s --> %s\n", old_win_path, win_path); redraw_only = 1; - new_schematic("switch_no_tcl_ctx", winpath, "", 1); + new_schematic("switch_no_tcl_ctx", win_path, "", 1); } else { - dbg(1, "callback(): switching window context: %s --> %s, semaphore=%d\n", old_winpath, winpath, xctx->semaphore); - new_schematic("switch", winpath, "", 1); + dbg(1, "callback(): switching window context: %s --> %s, semaphore=%d\n", old_win_path, win_path, xctx->semaphore); + new_schematic("switch", win_path, "", 1); } tclvareval("housekeeping_ctx", NULL); } @@ -2407,7 +2407,7 @@ int rstate; /* (reduced state, without ShiftMask) */ #if defined(__unix__) && HAS_CAIRO==1 if(xctx->ui_state & GRABSCREEN) { - grabscreen(winpath, event, mx, my, key, button, aux, state); + grabscreen(win_path, event, mx, my, key, button, aux, state); } else #endif switch(event) @@ -2450,7 +2450,7 @@ int rstate; /* (reduced state, without ShiftMask) */ break; case Expose: - dbg(1, "callback: Expose, winpath=%s, %dx%d+%d+%d\n", winpath, button, aux, mx, my); + dbg(1, "callback: Expose, win_path=%s, %dx%d+%d+%d\n", win_path, button, aux, mx, my); MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], mx,my,button,aux,mx,my); { XRectangle xr[1]; @@ -2931,10 +2931,10 @@ int rstate; /* (reduced state, without ShiftMask) */ tclvareval("xschem set rectcolor ", n, NULL); if(has_x) { - if(!strcmp(winpath, ".drw")) { + if(!strcmp(win_path, ".drw")) { tclvareval("reconfigure_layers_button {}", NULL); } else { - tclvareval("reconfigure_layers_button [winfo parent ", winpath, "]", NULL); + tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL); } } dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]); @@ -3652,8 +3652,8 @@ int rstate; /* (reduced state, without ShiftMask) */ if(key=='\\' && state==0) /* fullscreen */ { - dbg(1, "callback(): toggle fullscreen, winpath=%s\n", winpath); - toggle_fullscreen(winpath); + dbg(1, "callback(): toggle fullscreen, win_path=%s\n", win_path); + toggle_fullscreen(win_path); break; } if(key=='f' && EQUAL_MODMASK) /* flip objects around their anchor points 20171208 */ @@ -4433,13 +4433,13 @@ int rstate; /* (reduced state, without ShiftMask) */ if(xctx->semaphore > 0) xctx->semaphore--; if(redraw_only) { xctx->semaphore--; /* decrement articially incremented semaphore (see above) */ - dbg(1, "callback(): semaphore >=2 restoring window context: %s <-- %s\n", old_winpath, winpath); - if(old_winpath[0]) new_schematic("switch_no_tcl_ctx", old_winpath, "", 1); + dbg(1, "callback(): semaphore >=2 restoring window context: %s <-- %s\n", old_win_path, win_path); + if(old_win_path[0]) new_schematic("switch_no_tcl_ctx", old_win_path, "", 1); } else - if(strcmp(old_winpath, winpath)) { - if(old_winpath[0]) dbg(1, "callback(): reset old_winpath: %s <- %s\n", old_winpath, winpath); - my_strncpy(old_winpath, winpath, S(old_winpath)); + if(strcmp(old_win_path, win_path)) { + if(old_win_path[0]) dbg(1, "callback(): reset old_win_path: %s <- %s\n", old_win_path, win_path); + my_strncpy(old_win_path, win_path, S(old_win_path)); } return 0; } diff --git a/src/globals.c b/src/globals.c index 36c2984a..7a270d4d 100644 --- a/src/globals.c +++ b/src/globals.c @@ -186,7 +186,7 @@ int yyparse_error = 0; char *xschem_executable=NULL; Tcl_Interp *interp = NULL; double *character[256]; /* array or per-char coordinates of xschem internal vector font */ -char old_winpath[PATH_MAX] = ".drw"; /* previously switched window, used in callback() */ +char old_win_path[PATH_MAX] = ".drw"; /* previously switched window, used in callback() */ #ifndef __unix__ char win_temp_dir[PATH_MAX]=""; const char fopen_read_mode[] = "rb"; diff --git a/src/scheduler.c b/src/scheduler.c index 8641ca0f..8cf1fc2c 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -431,7 +431,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else { cmd_found = 0;} break; case 'c': /*----------------------------------------------*/ - /* callback winpath event mx my key button aux state + /* callback win_path event mx my key button aux state * Invoke the callback event dispatcher with a software event */ if(!strcmp(argv[1], "callback") ) { @@ -3404,22 +3404,22 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } - /* new_schematic create|destroy|destroy_all|switch winpath file [draw] + /* new_schematic create|destroy|destroy_all|switch win_path file [draw] * Open/destroy a new tab or window * create: create new empty window or with 'file' loaded if 'file' given. - * The winpath must be given (even {} is ok). - * non empty winpath ({1}) will avoid warnings if opening the + * The win_path must be given (even {} is ok). + * non empty win_path ({1}) will avoid warnings if opening the * same file multiple times. - * destroy: destroy tab/window identified by winpath. Example: + * destroy: destroy tab/window identified by win_path. Example: * xschem new_schematic destroy .x1.drw * destroy_all: close all tabs/additional windows * if the 'force'argument is given do not issue a warning if modified * tabs are about to be closed. - * switch: switch context to specified 'winpath' window or specified schematic name + * switch: switch context to specified 'win_path' window or specified schematic name * If 'draw' is given and set to 0 do not redraw after switching tab * (only tab i/f) - * Main window/tab has winpath set to .drw, - * Additional windows/tabs have winpath set to .x1.drw, .x2.drw and so on... + * Main window/tab has win_path set to .drw, + * Additional windows/tabs have win_path set to .x1.drw, .x2.drw and so on... */ else if(!strcmp(argv[1], "new_schematic")) { @@ -3623,7 +3623,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg xctx->ui_state2 = MENUSTARTPOLYGON; } - /* preview_window create|draw|destroy|close [winpath] [file] + /* preview_window create|draw|destroy|close [win_path] [file] * destroy: will delete preview schematic data and destroy container window * close: same as destroy but leave the container window. * Used in fileselector to show a schematic preview. @@ -5759,7 +5759,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg /* switch [window_path |schematic_name] * Switch context to indicated window path or schematic name * returns 0 if switch was successfull or 1 in case of errors - * (no tabs/windows present or no matching winpath / schematic name + * (no tabs/windows present or no matching win_path / schematic name * found). */ else if(!strcmp(argv[1], "switch")) diff --git a/src/xinit.c b/src/xinit.c index deb35b6f..9d8438a6 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -187,7 +187,7 @@ static int window_state (Display *disp, Window win, char *arg) {/*{{{*/ /* ----------------------------------------------------------------------- */ /* used to set icon */ -void windowid(const char *winpath) +void windowid(const char *win_path) { #ifdef __unix__ int i; @@ -200,11 +200,11 @@ void windowid(const char *winpath) Window *framewin_child_ptr; unsigned int framewindow_nchildren; - dbg(1, "windowid(): winpath=%s\n", winpath); + dbg(1, "windowid(): win_path=%s\n", win_path); framewindow_nchildren =0; mainwindow=Tk_MainWindow(interp); display = Tk_Display(mainwindow); - tclvareval("winfo id ", winpath, NULL); + tclvareval("winfo id ", win_path, NULL); sscanf(tclresult(), "0x%x", (unsigned int *) &ww); framewin = ww; XQueryTree(display, framewin, &rootwindow, &parent_of_topwindow, &framewin_child_ptr, &framewindow_nchildren); @@ -1443,7 +1443,7 @@ void swap_windows(int dr) new_schematic("switch", wp_j, "", 0); resetwin(1, 1, 1, 0, 0); - my_snprintf(old_winpath, S(old_winpath), ""); + my_snprintf(old_win_path, S(old_win_path), ""); if(dr) draw(); } } @@ -1653,6 +1653,7 @@ static void create_new_window(int *window_count, const char *noconfirm, const ch load_schematic(1, fname, 1, confirm); if(dr) zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); /* draw */ tclvareval("set_bindings ", window_path[n], NULL); + tclvareval("set_replace_key_binding ", window_path[n], NULL); tclvareval("save_ctx ", window_path[n], NULL); /* restore previous context, * because the Expose event after new window creation does a context switch prev win -> new win diff --git a/src/xschem.h b/src/xschem.h index b14bec10..1113aec1 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1212,7 +1212,7 @@ extern int yyparse_error; extern char *xschem_executable; extern Tcl_Interp *interp; extern double *character[256]; -extern char old_winpath[PATH_MAX]; /* previously switched window, used in callback() */ +extern char old_win_path[PATH_MAX]; /* previously switched window, used in callback() */ extern const char fopen_read_mode[]; /* "r" on unix, "rb" on windows */ /*********** Cmdline options (used at xinit, and then not used anymore) ***********/ @@ -1395,7 +1395,7 @@ extern void abort_operation(void); extern void draw_crosshair(int what, int state); extern void backannotate_at_cursor_b_pos(xRect *r, Graph_ctx *gr); /* extern void snapped_wire(double c_snap); */ -extern int callback(const char *winpath, int event, int mx, int my, KeySym key, +extern int callback(const char *win_path, int event, int mx, int my, KeySym key, int button, int aux, int state); extern void resetwin(int create_pixmap, int clear_pixmap, int force, int w, int h); extern Selected find_closest_obj(double mx,double my, int override_lock); @@ -1740,7 +1740,7 @@ extern void print_hilight_net(int show); extern void list_hilights(int all); extern void change_layer(); extern void launcher(); -extern void windowid(const char *winpath); +extern void windowid(const char *win_path); extern int preview_window(const char *what, const char *tk_win_path, const char *fname); extern int new_schematic(const char *what, const char *win_path, const char *fname, int dr); extern void toggle_fullscreen(const char *topwin); diff --git a/src/xschem.tcl b/src/xschem.tcl index 7e08c6e1..ca04c085 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1005,7 +1005,7 @@ proc convert_to_png {filename dest} { # Alt-Key-c # ButtonPress-4 # -proc key_binding { s d { topwin {} } } { +proc key_binding { s d { win_path {.drw} } } { regsub {.*-} $d {} key @@ -1039,14 +1039,14 @@ proc key_binding { s d { topwin {} } } { if { [regexp ButtonPress-3 $d] } { set state [expr {$state +0x400}] } # puts "$state $key <${s}>" if {[regexp ButtonPress- $d]} { - bind $topwin.drw "<${s}>" "xschem callback %W %T %x %y 0 $key 0 $state" + bind $win_path "<${s}>" "xschem callback %W %T %x %y 0 $key 0 $state" } else { if {![string compare $d {} ] } { # puts "bind .drw <${s}> {}" - bind $topwin.drw "<${s}>" {} + bind $win_path "<${s}>" {} } else { # puts "bind .drw <${s}> xschem callback %W %T %x %y $keysym 0 0 $state" - bind $topwin.drw "<${s}>" "xschem callback %W %T %x %y $keysym 0 0 $state" + bind $win_path "<${s}>" "xschem callback %W %T %x %y $keysym 0 0 $state" } } @@ -7831,33 +7831,33 @@ proc housekeeping_ctx {} { } # callback that resets simulate button color at end of simulation -proc set_simulate_button {top_path winpath} { +proc set_simulate_button {top_path win_path} { global simulate_bg execute has_x if {![info exists has_x]} return set current_win [xschem get current_win_path] - set simvar tctx::${winpath}_simulate + set simvar tctx::${win_path}_simulate set sim_button $top_path.menubar # puts "current_win=|$current_win|" # puts "simvar=|$simvar|" - # puts "winpath=|$winpath|" + # puts "win_path=|$win_path|" # puts "top_path=|$top_path|" # puts "sim_button=|$sim_button|" # puts "execute(exitcode,last)=|$execute(exitcode,last)|" if {![info exists execute(exitcode,last)]} { - if { $current_win eq $winpath} { + if { $current_win eq $win_path} { $sim_button entryconfigure Simulate -background $simulate_bg } set $simvar $simulate_bg } elseif { $execute(exitcode,last) == 0} { - if { $current_win eq $winpath} { + if { $current_win eq $win_path} { $sim_button entryconfigure Simulate -background Green } set $simvar Green } else { - if { $current_win eq $winpath} { + if { $current_win eq $win_path} { $sim_button entryconfigure Simulate -background red } set $simvar red @@ -8892,11 +8892,11 @@ proc create_layers_menu { {topwin {} } } { } } -proc set_replace_key_binding {} { +proc set_replace_key_binding { {win_path {.drw}}} { global replace_key if {[array exists replace_key]} { foreach i [array names replace_key] { - key_binding "$i" "$replace_key($i)" + key_binding "$i" "$replace_key($i)" $win_path } } } From 7926a833beedb7ea2b1802a5060dc23c9db94e00 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 04:20:57 +0100 Subject: [PATCH 06/27] added `d` to unselect object under the mouse, `Shift-D` to unselect by area, both work with or without infix_interface, moved delete files to `Ctrl-d`, update keybinding docs --- doc/xschem_man/commands.html | 4 +- src/callback.c | 75 ++++++++++++++++++++++++++++-------- src/keys.help | 4 +- src/xschem.h | 6 ++- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/doc/xschem_man/commands.html b/doc/xschem_man/commands.html index d6a9da20..0a4f589e 100644 --- a/doc/xschem_man/commands.html +++ b/doc/xschem_man/commands.html @@ -156,7 +156,9 @@ ctrl 'c' Save to clipboard shift 'C' Start arc placement shift+ctrl 'C' Start circle placement alt 'C' Toggle dim/brite background with rest of layers -shift 'D' Delete files +ctrl 'd' Delete files +- 'd' Unselect selected object under the mouse pointer +shift 'D' Unselect selected objects by area ctrl 'e' Back to parent schematic - 'e' Descend to schematic alt 'e' Edit selected schematic in a new window diff --git a/src/callback.c b/src/callback.c index 2f2d4b3b..17484caa 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1571,6 +1571,16 @@ static int end_place_move_copy_zoom() return 0; } +static void unselect_at_mouse_pos(int mx, int my) +{ + xctx->last_command = 0; + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + select_object(xctx->mousex, xctx->mousey, 0, 0, NULL); + rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */ +} + void snapped_wire(double c_snap) { double x, y; @@ -1590,11 +1600,22 @@ void snapped_wire(double c_snap) } } -static int check_menu_start_commands(double c_snap) +static int check_menu_start_commands(double c_snap, int mx, int my) { dbg(1, "check_menu_start_commands(): ui_state=%x, ui_state2=%x last_command=%d\n", xctx->ui_state, xctx->ui_state2, xctx->last_command); + if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTDESEL) ) { + if(xctx->ui_state & DESEL_CLICK) { + unselect_at_mouse_pos(mx, my); + } else { /* unselect by area */ + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex; + xctx->my_double_save=xctx->mousey; + xctx->ui_state |= DESEL_AREA; + } + return 1; + } if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTWIRECUT)) { break_wires_at_point(xctx->mousex_snap, xctx->mousey_snap, 1); return 1; @@ -2510,7 +2531,7 @@ int rstate; /* (reduced state, without ShiftMask) */ /* determine direction of a rectangle selection (or unselection with ALT key) */ if(xctx->ui_state & STARTSELECT && !(xctx->ui_state & (PLACE_SYMBOL | STARTPAN | PLACE_TEXT)) ) { /* Unselect by area : determine direction */ - if( (state & Button1Mask) && SET_MODMASK) { + if( ((state & Button1Mask) && SET_MODMASK) || (xctx->ui_state & DESEL_AREA)) { if(mx >= xctx->mx_save) xctx->nl_dir = 0; else xctx->nl_dir = 1; select_rect(enable_stretch, RUBBER,0); @@ -2563,12 +2584,13 @@ int rstate; /* (reduced state, without ShiftMask) */ } } /* Unselect by area */ - if((state & Button1Mask) && (SET_MODMASK) && !(state & ShiftMask) && - !(xctx->ui_state & STARTPAN) && !xctx->shape_point_selected && + if( (((state & Button1Mask) && SET_MODMASK) || (xctx->ui_state & DESEL_AREA)) && + !(state & ShiftMask) && + !(xctx->ui_state & STARTPAN) && + !xctx->shape_point_selected && + !(xctx->ui_state & STARTSELECT) && !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT))) { /* unselect area */ - if( !(xctx->ui_state & STARTSELECT)) { - select_rect(enable_stretch, START,0); - } + select_rect(enable_stretch, START,0); } /* Select by area. Shift pressed */ else if((state&Button1Mask) && (state & ShiftMask) && !(xctx->ui_state & STARTWIRE) && @@ -3979,7 +4001,33 @@ int rstate; /* (reduced state, without ShiftMask) */ draw(); break; } - if(key=='D' && rstate == 0) /* delete files */ + if(key=='d' && rstate == 0) /* unselect object under the mouse */ + { + if(infix_interface) { + unselect_at_mouse_pos(mx, my); + } else { + xctx->ui_state |= (MENUSTART | DESEL_CLICK); + xctx->ui_state2 = MENUSTARTDESEL; + } + break; + } + if(key=='D' && rstate == 0) /* unselect by area */ + { + if( !(xctx->ui_state & STARTPAN) && !xctx->shape_point_selected && + !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT)) && !(xctx->ui_state & STARTSELECT)) { + if(infix_interface) { + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex; + xctx->my_double_save=xctx->mousey; + xctx->ui_state |= DESEL_AREA; + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTDESEL; + } + } + break; + } + if(key=='d' && rstate == ControlMask) /* delete files */ { if(xctx->semaphore >= 2) break; delete_files(); @@ -4120,12 +4168,7 @@ int rstate; /* (reduced state, without ShiftMask) */ } /* Alt - Button1 click to unselect */ else if(button==Button1 && (SET_MODMASK) ) { - xctx->last_command = 0; - xctx->mx_save = mx; xctx->my_save = my; - xctx->mx_double_save=xctx->mousex_snap; - xctx->my_double_save=xctx->mousey_snap; - select_object(xctx->mousex, xctx->mousey, 0, 0, NULL); - rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */ + unselect_at_mouse_pos(mx, my); } /* Middle button press (Button2) will pan the schematic. */ @@ -4165,7 +4208,7 @@ int rstate; /* (reduced state, without ShiftMask) */ break; } /* handle all object insertions started from Tools/Edit menu */ - if(check_menu_start_commands(c_snap)) break; + if(check_menu_start_commands(c_snap, mx, my)) break; /* complete the pending STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */ if(end_place_move_copy_zoom()) break; @@ -4303,6 +4346,7 @@ int rstate; /* (reduced state, without ShiftMask) */ waves_callback(event, mx, my, key, button, aux, state); break; } + xctx->ui_state &= ~DESEL_CLICK; dbg(1, "release: shape_point_selected=%d\n", xctx->shape_point_selected); /* bring up context menu if no pending operation */ if(state == Button3Mask && xctx->semaphore <2) { @@ -4378,6 +4422,7 @@ int rstate; /* (reduced state, without ShiftMask) */ select_rect(enable_stretch, END,-1); } } + xctx->ui_state &= ~DESEL_AREA; rebuild_selected_array(); my_snprintf(str, S(str), "mouse = %.16g %.16g - selected: %d path: %s", xctx->mousex_snap, xctx->mousey_snap, xctx->lastsel, xctx->sch_path[xctx->currsch] ); diff --git a/src/keys.help b/src/keys.help index cc2db919..a73197bf 100644 --- a/src/keys.help +++ b/src/keys.help @@ -96,7 +96,9 @@ ctrl 'c' Save to clipboard shift 'C' Start arc placement shift+ctrl 'C' Start circle placement alt 'C' Toggle dim/brite background with rest of layers -shift 'D' Delete files +ctrl 'd' Delete files +- 'd' Unselect selected object under the mouse pointer +shift 'D' Unselect selected objects by area ctrl 'e' Back to parent schematic - 'e' Descend to schematic alt 'e' Edit selected schematic in a new window diff --git a/src/xschem.h b/src/xschem.h index 1113aec1..34423304 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -229,7 +229,9 @@ extern char win_temp_dir[PATH_MAX]; #define START_SYMPIN 16384U #define GRAPHPAN 32768U /* bit 15 */ #define MENUSTART 65536U /* bit 16 */ -#define GRABSCREEN 131072 /* bit 17 */ +#define GRABSCREEN 131072U /* bit 17 */ +#define DESEL_CLICK 262144U /* bit 18 */ +#define DESEL_AREA 524288U /* bit 19 */ #define SELECTED 1U /* used in the .sel field for selected objs. */ #define SELECTED1 2U /* first point selected... */ @@ -238,6 +240,7 @@ extern char win_temp_dir[PATH_MAX]; #define SELECTED4 16U /* sub states encoded in global ui_state2 to reduce ui_state bits usage */ +/* also used when infix_interface=0 */ #define MENUSTARTWIRE 1U /* start wire invoked from menu */ #define MENUSTARTLINE 2U /* start line invoked from menu */ #define MENUSTARTRECT 4U /* start rect invoked from menu */ @@ -250,6 +253,7 @@ extern char win_temp_dir[PATH_MAX]; #define MENUSTARTWIRECUT 512U #define MENUSTARTWIRECUT2 1024U /* do not align cut point to snap */ #define MENUSTARTCOPY 2048U +#define MENUSTARTDESEL 4096U #define WIRE 1 /* types of defined objects */ #define xRECT 2 From 53fda635c95a2c122545b6ee1a4dad09e16661be Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 10:39:50 +0100 Subject: [PATCH 07/27] remove dbg msg, reduce precision in eval_expr(), add expr() resolution at end of translate() --- src/eval_expr.y | 2 +- src/token.c | 11 +++++++++-- xschem_library/devices/pmos4.sym | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/eval_expr.y b/src/eval_expr.y index 8a94444b..4fa59958 100644 --- a/src/eval_expr.y +++ b/src/eval_expr.y @@ -93,7 +93,7 @@ exp: NUM { $$ = $1;} static void get_expr(double x) { char xx[100]; - my_snprintf(xx, S(xx), "%.16g", x); + my_snprintf(xx, S(xx), "%.15g", x); my_mstrcat(_ALLOC_ID_, &ret, xx, NULL); strptr = str; } diff --git a/src/token.c b/src/token.c index 7eb1576c..b1c2f1c1 100644 --- a/src/token.c +++ b/src/token.c @@ -4488,7 +4488,7 @@ const char *translate(int inst, const char* s) } if(strstr(value1, "expr(") == value1) { char *ptr = strrchr(value1 + 5, ')'); - dbg(0, "translate(): expr():%s\n", value1); + dbg(1, "translate(): expr():%s\n", value1); *ptr = '\0'; my_strdup2(_ALLOC_ID_, &value1, eval_expr( translate3(value1 + 5, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); @@ -4514,11 +4514,18 @@ const char *translate(int inst, const char* s) } /* while(1) */ dbg(2, "translate(): returning %s\n", result); my_free(_ALLOC_ID_, &token); - /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions * can be calculated */ my_strdup2(_ALLOC_ID_, &translated_tok, spice_get_node(tcl_hook2(result))); + if(strstr(translated_tok, "expr(") == translated_tok) { + char *ptr = strrchr(translated_tok + 5, ')'); + dbg(1, "translate(): expr():%s\n", translated_tok); + *ptr = '\0'; + my_strdup2(_ALLOC_ID_, &translated_tok, eval_expr( + translate3(translated_tok + 5, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + } + return translated_tok; } diff --git a/xschem_library/devices/pmos4.sym b/xschem_library/devices/pmos4.sym index c899b2cf..1e417597 100644 --- a/xschem_library/devices/pmos4.sym +++ b/xschem_library/devices/pmos4.sym @@ -59,3 +59,4 @@ T {@#3:net_name} 25 1.25 0 0 0.15 0.15 {layer=15 hide=instance} T {tcleval(vgs=[to_eng \{@#1:spice_get_voltage - @#2:spice_get_voltage \}] vds=[to_eng \{@#0:spice_get_voltage - @#2:spice_get_voltage \}])} 2.5 20 0 1 0.05 0.05 {layer=15 } T {@spice_get_current} 35 20 0 0 0.15 0.15 {layer=17} +T {expr( @w + @l )} 50 -30 0 0 0.2 0.2 {} From a4ce2d1b643e5dff4023d6b404e19e9e85908bdf Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 12:45:21 +0100 Subject: [PATCH 08/27] subst_token(): better token removal (remove space or newline to avoid having many remaining blanks); `netlist_postprocess` xschemrc tcl variable to insert commands after netlist is created --- src/callback.c | 37 +------------------------------------ src/scheduler.c | 1 + src/token.c | 5 ++++- src/xschem.tcl | 9 +++++++++ src/xschemrc | 5 ++++- 5 files changed, 19 insertions(+), 38 deletions(-) diff --git a/src/callback.c b/src/callback.c index 17484caa..3cdb8520 100644 --- a/src/callback.c +++ b/src/callback.c @@ -3836,43 +3836,8 @@ int rstate; /* (reduced state, without ShiftMask) */ } if(key=='n' && rstate==0) /* hierarchical netlist */ { - int err = 0; - yyparse_error = 0; if(xctx->semaphore >= 2) break; - unselect_all(1); - if(set_netlist_dir(0, NULL)) { - dbg(1, "callback(): -------------\n"); - if(xctx->netlist_type == CAD_SPICE_NETLIST) - err = global_spice_netlist(1, 1); - else if(xctx->netlist_type == CAD_VHDL_NETLIST) - err = global_vhdl_netlist(1, 1); - else if(xctx->netlist_type == CAD_VERILOG_NETLIST) - err = global_verilog_netlist(1, 1); - else if(xctx->netlist_type == CAD_TEDAX_NETLIST) - err = global_tedax_netlist(1, 1); - else - tcleval("tk_messageBox -type ok -parent [xschem get topwindow] " - "-message {Please Set netlisting mode (Options menu)}"); - - dbg(1, "callback(): -------------\n"); - } - else { - if(has_x) tcleval("alert_ {Can not write into the netlist directory. Please check} {}"); - else dbg(0, "Can not write into the netlist directory. Please check"); - err = 1; - } - if(err) { - if(has_x) { - tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background red", NULL); - tclvareval("set tctx::", xctx->current_win_path, "_netlist red", NULL); - } - - } else { - if(has_x) { - tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background Green", NULL); - tclvareval("set tctx::", xctx->current_win_path, "_netlist Green", NULL); - } - } + tcleval("xschem netlist -erc"); break; } if(key=='N' && rstate == 0) /* current level only netlist */ diff --git a/src/scheduler.c b/src/scheduler.c index 8cf1fc2c..913a5aac 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -3377,6 +3377,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } } tclsetvar("show_infowindow_after_netlist", saveshow); + tcleval("eval_netlist_postprocess"); set_netlist_dir(1, savedir); if(done_netlist) { if(messages) { diff --git a/src/token.c b/src/token.c index b1c2f1c1..12926482 100644 --- a/src/token.c +++ b/src/token.c @@ -1216,7 +1216,7 @@ const char *subst_token(const char *s, const char *tok, const char *new_val) size_t token_pos=0, result_pos=0, result_save_pos = 0, tmp; int quote=0; int done_subst=0; - int escape=0, matched_tok=0; + int escape=0, matched_tok=0, removed_tok = 0; char *new_val_copy = NULL; size_t new_val_len; @@ -1299,6 +1299,7 @@ const char *subst_token(const char *s, const char *tok, const char *new_val) } else { /* remove token (and value if any) */ result_pos = result_save_pos; done_subst = 1; + removed_tok = 1; } } result_save_pos = result_pos; @@ -1335,11 +1336,13 @@ const char *subst_token(const char *s, const char *tok, const char *new_val) } else { /* remove token (and value if any) */ result_pos = result_save_pos; done_subst = 1; + removed_tok = 1; } } state=TOK_VALUE; } else if( state == TOK_VALUE && space && !quote && !escape) { state=TOK_BEGIN; + if(matched_tok && removed_tok && (c == '\n' || c == ' ') ) continue; } /* state actions */ if(state == TOK_BEGIN) { diff --git a/src/xschem.tcl b/src/xschem.tcl index ca04c085..507471ec 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -8917,6 +8917,15 @@ proc eval_postinit_commands {} { } } +proc eval_netlist_postprocess {} { + global netlist_postprocess + if {[info exists netlist_postprocess]} { + if {[catch {uplevel #0 $netlist_postprocess} res]} { + puts "executing $netlist_postprocess:\n\n$res" + } + } +} + proc setup_tcp_xschem { {port_number {}} } { global xschem_listen_port xschem_server_getdata diff --git a/src/xschemrc b/src/xschemrc index 159c300a..0d1e0bb3 100644 --- a/src/xschemrc +++ b/src/xschemrc @@ -524,7 +524,10 @@ # } # } - +########################################################################### +#### TCL COMMANDS TO BE EXECUTED AFTER GENERATING NETLIST +########################################################################### +# set netlist_postprocess {textfile $netlist_dir/[xschem get netlist_name fallback]} ########################################################################### #### WEB URL DOWNLOAD HELPER APPLICATION From 5dd8ee16eac71fef1cbd3958ee11328b54f1f62f Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 15:18:40 +0100 Subject: [PATCH 09/27] revert a test change on devices/pmos4.sym --- xschem_library/devices/pmos4.sym | 1 - 1 file changed, 1 deletion(-) diff --git a/xschem_library/devices/pmos4.sym b/xschem_library/devices/pmos4.sym index 1e417597..c899b2cf 100644 --- a/xschem_library/devices/pmos4.sym +++ b/xschem_library/devices/pmos4.sym @@ -59,4 +59,3 @@ T {@#3:net_name} 25 1.25 0 0 0.15 0.15 {layer=15 hide=instance} T {tcleval(vgs=[to_eng \{@#1:spice_get_voltage - @#2:spice_get_voltage \}] vds=[to_eng \{@#0:spice_get_voltage - @#2:spice_get_voltage \}])} 2.5 20 0 1 0.05 0.05 {layer=15 } T {@spice_get_current} 35 20 0 0 0.15 0.15 {layer=17} -T {expr( @w + @l )} 50 -30 0 0 0.2 0.2 {} From 1573ea3cad3881c4bcef44573d3391ac50202dce Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Feb 2025 23:57:01 +0100 Subject: [PATCH 10/27] improvements in eval_expr.y, print_spice_element(), translate() to allow multiple expr(...) patterns in strings, they all will be evaluated if possible or returned unevaluated (without `expr(` and `)` ) --- src/eval_expr.y | 107 +++++++++++++++++++++++++++++++++++++++--------- src/token.c | 22 ++++------ 2 files changed, 97 insertions(+), 32 deletions(-) diff --git a/src/eval_expr.y b/src/eval_expr.y index 4fa59958..ba954f26 100644 --- a/src/eval_expr.y +++ b/src/eval_expr.y @@ -10,6 +10,8 @@ static const char *str, *strptr; static char *ret = NULL; +static int dbglev = 1; + /* Data type for links in the chain of functions. */ struct symrec { @@ -29,13 +31,14 @@ static int kklex(); static void kkerror(char *s); static double toint(double x); static void get_expr(double x); +static void get_char(int c); struct fn { char *fname; double (*fnct)(); }; - +static int lex_state = 0; struct fn fn_array[] = { {"int" , toint}, @@ -54,11 +57,14 @@ struct fn fn_array[] %define api.prefix {kk} %union { +int c; double val; /* For returning numbers. */ symrec *tptr; /* For returning symbol-table pointers */ } %token STREND 0 +%token CHAR +%token EXPR /* expr( */ %token NUM /* Simple double precision number */ %token FNCT /* Variable and Function */ %type exp @@ -70,29 +76,45 @@ symrec *tptr; /* For returning symbol-table pointers */ /* Grammar follows */ %% - -line: - | exp STREND {get_expr($1);} - | '\'' exp '\'' STREND {get_expr($2);} +input: + | input line ; -exp: NUM { $$ = $1;} - | FNCT '(' exp ')' { $$ = $1 ? (*($1->fnctptr))($3) : 0.0;} - | exp '+' exp { $$ = $1 + $3; } - | exp '-' exp { $$ = $1 - $3;} - | exp '*' exp { $$ = $1 * $3;} - | exp '%' exp { $$ = (int)$1 % (int)$3;} - | exp '/' exp { $$ = $1 / $3;} - | '-' exp %prec NEG { $$ = -$2;} - | exp '^' exp { $$ = pow ($1, $3);} - | '(' exp ')' { $$ = $2;} +line: + CHAR {get_char($1);} + | EXPR exp ')' {get_expr($2);lex_state = 0;} + | EXPR '\'' exp '\'' ')' {get_expr($3);lex_state = 0;} + | EXPR exp error +; + +exp: NUM {$$ = $1;} + | FNCT '(' exp ')' {$$ = $1 ? (*($1->fnctptr))($3) : 0.0;} + | exp '+' exp {$$ = $1 + $3; } + | exp '-' exp {$$ = $1 - $3;} + | exp '*' exp {$$ = $1 * $3;} + | exp '%' exp {$$ = (int)$1 % (int)$3;} + | exp '/' exp {$$ = $1 / $3;} + | '-' exp %prec NEG {$$ = -$2;} + | exp '^' exp {$$ = pow ($1, $3);} + | '(' exp ')' {$$ = $2;} ; /* End of grammar */ %% +static void get_char(int c) +{ + char s[2]; + dbg(dbglev, "get_char: %c |%s|\n", c, str); + s[0] = (char)c; + s[1] = '\0'; + my_mstrcat(_ALLOC_ID_, &ret, s, NULL); + strptr = str; +} + static void get_expr(double x) { char xx[100]; + dbg(dbglev,"get_expr(): x=%g\n", x); my_snprintf(xx, S(xx), "%.15g", x); my_mstrcat(_ALLOC_ID_, &ret, xx, NULL); strptr = str; @@ -104,10 +126,39 @@ static double toint(double x) return floor(x); } +/* ad=expr(3*xa) pd=expr(2*(3+xa)) --> ad=3*xa pd=2*(3+xa) */ +static void remove_expr(char *s) +{ + char *ptr = s; + int plev = 0; + while(*ptr) { + if(strstr(ptr, "expr(") == ptr) { + ptr += 5; + plev++; + } + if(*ptr == '(') plev++; + if(*ptr == ')') { + plev--; + if(plev == 0) ptr++; + if(!ptr) break; + } + *s = *ptr; + ptr++; + s++; + } + *s = *ptr; +} + + static void kkerror(char *s) /* Called by kkparse on error */ { - /* printf("error: |%s|\n\n |%s|\n", s, strptr); */ - my_mstrcat(_ALLOC_ID_, &ret, strptr, NULL); + char *ss = NULL; + dbg(dbglev, "error: |%s|\n\n |%s|\n", s, str ? str : ""); + my_strdup2(_ALLOC_ID_, &ss, strptr); + remove_expr(ss); + my_mstrcat(_ALLOC_ID_, &ret, ss, NULL); + my_free(_ALLOC_ID_, &ss); + lex_state = 0; } static symrec *getsym(char *sym_name) @@ -155,6 +206,23 @@ static int kklex() int c; if(!str) { return 0; } + if(strstr(str, "expr(") == str) { + lex_state = 1; + str += 5; + dbg(dbglev, "lex(): EXPR\n"); + return EXPR; + } + if(!lex_state) { + c = *str++; + if(c) { + kklval.c = c; + dbg(dbglev, "lex(): CHAR; %c\n", c); + return CHAR; + } else { + dbg(dbglev, "lex(): STREND\n"); + return STREND; + } + } /* Ignore whitespace, get first nonwhite character. */ while ((c = *str++) == ' ' || c == '\t' || c == '\n'); if (c == 0) { @@ -171,7 +239,7 @@ static int kklex() sscanf(str, "%99[.0-9a-zA-Z_]%n", s, &rd); kklval.val = atof_spice(s); str += rd; - /* printf("yylex: NUM: %s\n", s); */ + dbg(dbglev, "lex(): NUM: %s\n", s); return NUM; } /* Char starts an identifier => read the name. */ @@ -192,7 +260,7 @@ static int kklex() symbuf[i] = '\0'; s = getsym (symbuf); kklval.tptr = s; - /* printf("yylex: FNCT=%s\n", symbuf); */ + dbg(dbglev, "ylex: FNCT=%s\n", symbuf); return FNCT; } /* Any other character is a token by itself. */ @@ -201,6 +269,7 @@ static int kklex() char *eval_expr(const char *s) { + lex_state = 0; if(ret) my_free(_ALLOC_ID_, &ret); strptr = str = s; kkparse(); diff --git a/src/token.c b/src/token.c index 12926482..3286ac1d 100644 --- a/src/token.c +++ b/src/token.c @@ -2249,12 +2249,9 @@ int print_spice_element(FILE *fd, int inst) if (!xctx->tok_size) value=get_tok_value(template, token+1, 0); token_exists = xctx->tok_size; - if(strstr(value, "expr(") == value) { - char *ptr; - my_strdup(_ALLOC_ID_, &val, value); - ptr = strrchr(val + 5, ')'); - *ptr = '\0'; - value = eval_expr(translate3(val + 5, 1, xctx->inst[inst].prop_ptr, template, NULL)); + if(strstr(value, "expr(") ) { + my_strdup2(_ALLOC_ID_, &val, value); + value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL)); } if(!strcmp("@savecurrent", token)) { token_exists = 0; /* processed later */ @@ -2503,6 +2500,9 @@ int print_spice_element(FILE *fd, int inst) } dbg(1, "print_spice_element(): after translate() result=%s\n", result); } + if(strstr(result, "expr(") ) { + result = eval_expr(result); + } if(result) fprintf(fd, "%s", result); my_free(_ALLOC_ID_, &template); my_free(_ALLOC_ID_, &format); @@ -4490,11 +4490,9 @@ const char *translate(int inst, const char* s) i--; } if(strstr(value1, "expr(") == value1) { - char *ptr = strrchr(value1 + 5, ')'); dbg(1, "translate(): expr():%s\n", value1); - *ptr = '\0'; my_strdup2(_ALLOC_ID_, &value1, eval_expr( - translate3(value1 + 5, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + translate3(value1, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); } tmp=strlen(value1); STR_ALLOC(&result, tmp + result_pos, &size); @@ -4521,12 +4519,10 @@ const char *translate(int inst, const char* s) * can be calculated */ my_strdup2(_ALLOC_ID_, &translated_tok, spice_get_node(tcl_hook2(result))); - if(strstr(translated_tok, "expr(") == translated_tok) { - char *ptr = strrchr(translated_tok + 5, ')'); + if(strstr(translated_tok, "expr(")) { dbg(1, "translate(): expr():%s\n", translated_tok); - *ptr = '\0'; my_strdup2(_ALLOC_ID_, &translated_tok, eval_expr( - translate3(translated_tok + 5, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + translate3(translated_tok, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); } return translated_tok; From 51ff56357d40bffca79d7a74ae879c20470e9dfa Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Feb 2025 13:22:08 +0100 Subject: [PATCH 11/27] some simplification and refactoring of print_spice_element() and translate() --- src/token.c | 1695 +++++++++++++++++++++++++-------------------------- 1 file changed, 838 insertions(+), 857 deletions(-) diff --git a/src/token.c b/src/token.c index 3286ac1d..0d304135 100644 --- a/src/token.c +++ b/src/token.c @@ -2212,107 +2212,38 @@ int print_spice_element(FILE *fd, int inst) token[token_pos]='\0'; token_pos=0; - /* if spiceprefix==0 and token == @spiceprefix then set empty value */ - if (!tclgetboolvar("spiceprefix") && !strcmp(token, "@spiceprefix")) { - value=NULL; - } else { - size_t tok_val_len; - size_t tok_size; - dbg(1, "print_spice_element(): token: |%s|\n", token); - my_strdup2(_ALLOC_ID_, &val, get_tok_value(xctx->inst[inst].prop_ptr, token+1, 0)); - tok_size = xctx->tok_size; - value = val; - if(strchr(value, '@')) { - /* Symbol format string contains model=@modp, - * resolve @modp looking in instance attributes ... */ - char *parent_prop_ptr = NULL; - char *parent_templ = NULL; - - if(xctx->currsch > 0) { - /* ... also look up modp also in **parent** instance prop_ptr and symbol template attribute */ - parent_prop_ptr = xctx->hier_attr[xctx->currsch - 1].prop_ptr; - parent_templ = xctx->hier_attr[xctx->currsch - 1].templ; - } - dbg(1, "print_spice_element(): before translate3(): value=%s\n", value); - value = translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ); - dbg(1, "print_spice_element(): after translate3(): value=%s\n", value); - } - tok_val_len = strlen(value); - if(!strcmp(token, "@spiceprefix") && value[0]) { - my_realloc(_ALLOC_ID_, &spiceprefixtag, tok_val_len+22); - my_snprintf(spiceprefixtag, tok_val_len+22, "**** spice_prefix %s\n", value); - value = spiceprefixtag; - } - xctx->tok_size = tok_size; - /* xctx->tok_size==0 indicates that token(+1) does not exist in instance attributes */ - - if (!xctx->tok_size) value=get_tok_value(template, token+1, 0); - token_exists = xctx->tok_size; - - if(strstr(value, "expr(") ) { - my_strdup2(_ALLOC_ID_, &val, value); - value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL)); - } - if(!strcmp("@savecurrent", token)) { - token_exists = 0; /* processed later */ - value = NULL; - } - } - if(!token_exists && token[0] =='%') { - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", token + 1); */ - my_mstrcat(_ALLOC_ID_, &result, token + 1, NULL); - /* fputs(token + 1, fd); */ - } else if (value && value[0]!='\0') { - /* instance names (name) and node labels (lab) go thru the expandlabel function. */ - /*if something else must be parsed, put an if here! */ - - if (!(strcmp(token+1,"name") && strcmp(token+1,"lab")) /* expand name/labels */ - && ((lab = expandlabel(value, &itmp)) != NULL)) { - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", lab); */ - my_mstrcat(_ALLOC_ID_, &result, lab, NULL); - /* fputs(lab,fd); */ - - } else { - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", value); */ - my_mstrcat(_ALLOC_ID_, &result, value, NULL); - /* fputs(value,fd); */ - } - } - else if(strcmp(token,"@symref")==0) + + + + + + if(strcmp(token,"@symref")==0) { const char *s = get_sym_name(inst, 9999, 1, 0); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", s); */ my_mstrcat(_ALLOC_ID_, &result, s, NULL); } else if (strcmp(token,"@symname")==0) /* of course symname must not be present in attributes */ { const char *s = sanitize(translate(inst, get_sym_name(inst, 0, 0, 0))); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", s); */ my_mstrcat(_ALLOC_ID_, &result, s, NULL); - /* fputs(s,fd); */ } else if (strcmp(token,"@symname_ext")==0) /* of course symname_ext must not be present in attributes */ { const char *s = sanitize(translate(inst, get_sym_name(inst, 0, 1, 0))); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", s); */ my_mstrcat(_ALLOC_ID_, &result, s, NULL); - /* fputs(s,fd); */ } else if(strcmp(token,"@topschname")==0) /* of course topschname must not be present in attributes */ { const char *topsch; topsch = get_trailing_path(xctx->sch[0], 0, 1); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", topsch); */ my_mstrcat(_ALLOC_ID_, &result, topsch, NULL); } else if(strcmp(token,"@schname_ext")==0) /* of course schname must not be present in attributes */ { - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", xctx->current_name); */ my_mstrcat(_ALLOC_ID_, &result, xctx->current_name, NULL); - /* fputs(xctx->current_name, fd); */ } else if(strcmp(token,"@savecurrent")==0) { @@ -2321,14 +2252,12 @@ int print_spice_element(FILE *fd, int inst) const char *sc = get_tok_value(xctx->inst[inst].prop_ptr, "savecurrent", 0); if(!sc[0]) sc = get_tok_value(template, "savecurrent", 0); if(!strboolcmp(sc , "true")) { - /* result_pos += my_snprintf(result + result_pos, tmp, "\n.save I( ?1 %s )", instname); */ my_mstrcat(_ALLOC_ID_, &result, "\n.save I( ?1 ", instname, " )", NULL); } } else if(strcmp(token,"@schname")==0) /* of course schname must not be present in attributes */ { const char *schname = get_cell(xctx->current_name, 0); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", schname); */ my_mstrcat(_ALLOC_ID_, &result, schname, NULL); } else if(strcmp(token,"@pinlist")==0) /* of course pinlist must not be present in attributes */ @@ -2346,7 +2275,6 @@ int print_spice_element(FILE *fd, int inst) if(!int_hash_lookup(&table, name, 1, XINSERT_NOREPLACE)) { str_ptr = net_name(inst, i, &multip, 0, 1); - /* result_pos += my_snprintf(result + result_pos, tmp, "?%d %s ", multip, str_ptr); */ my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); } } @@ -2361,7 +2289,6 @@ int print_spice_element(FILE *fd, int inst) if(strboolcmp(get_tok_value(prop,"spice_ignore",0), "true")) { str_ptr = net_name(inst,i, &multip, 0, 1); - /* result_pos += my_snprintf(result + result_pos, tmp, "?%d %s ", multip, str_ptr); */ my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); } break; @@ -2413,7 +2340,6 @@ int print_spice_element(FILE *fd, int inst) } my_free(_ALLOC_ID_, &tmpstr); } - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", value); */ my_mstrcat(_ALLOC_ID_, &result, value, NULL); my_free(_ALLOC_ID_, &pin_attr_value); } @@ -2424,7 +2350,6 @@ int print_spice_element(FILE *fd, int inst) if(strboolcmp(si, "true")) { str_ptr = net_name(inst,n, &multip, 0, 1); - /* result_pos += my_snprintf(result + result_pos, tmp, "?%d %s ", multip, str_ptr); */ my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); } } @@ -2442,42 +2367,106 @@ int print_spice_element(FILE *fd, int inst) dbg(1, "print_spice_element(): tclpropeval {%s} {%s} {%s}", token, name, xctx->inst[inst].name); res = tcleval(tclcmd); - /* result_pos += my_snprintf(result + result_pos, tmp, "%s", res); */ my_mstrcat(_ALLOC_ID_, &result, res, NULL); - /* fprintf(fd, "%s", tclresult()); */ my_free(_ALLOC_ID_, &tclcmd); - } /* /20171029 */ + } + /* if spiceprefix==0 and token == @spiceprefix then set empty value */ + else if (!tclgetboolvar("spiceprefix") && !strcmp(token, "@spiceprefix")) { + value=NULL; + /* else tcl var spiceprefix is enabled */ + } + + + else { + /* here a @token in format string will be replaced by value in instance prop_ptr + * or symbol template */ + size_t tok_val_len; + size_t tok_size; + dbg(1, "print_spice_element(): token: |%s|\n", token); + my_strdup2(_ALLOC_ID_, &val, get_tok_value(xctx->inst[inst].prop_ptr, token+1, 0)); + + /* xctx->tok_size==0 indicates that token(+1) does not exist in instance attributes + * so try to get from symbol template */ + if (!xctx->tok_size) { + my_strdup2(_ALLOC_ID_, &val, get_tok_value(template, token+1, 0)); + } + token_exists = xctx->tok_size; + + tok_size = xctx->tok_size; + value = val; + if(strchr(value, '@')) { + /* Symbol format string contains model=@modp, + * resolve @modp looking in instance attributes ... */ + char *parent_prop_ptr = NULL; + char *parent_templ = NULL; + + if(xctx->currsch > 0) { + /* ... look up modp also in **parent** instance prop_ptr and symbol template attribute */ + parent_prop_ptr = xctx->hier_attr[xctx->currsch - 1].prop_ptr; + parent_templ = xctx->hier_attr[xctx->currsch - 1].templ; + } + dbg(1, "print_spice_element(): before translate3(): value=%s\n", value); + value = translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ); + dbg(1, "print_spice_element(): after translate3(): value=%s\n", value); + } + tok_val_len = strlen(value); + xctx->tok_size = tok_size; + + /* @spiceprefix needs a special tag for postprocessing */ + if(!strcmp(token, "@spiceprefix") && value[0]) { + my_realloc(_ALLOC_ID_, &spiceprefixtag, tok_val_len+22); + my_snprintf(spiceprefixtag, tok_val_len+22, "**** spice_prefix %s\n", value); + value = spiceprefixtag; + } + + if(strstr(value, "expr(") ) { + my_strdup2(_ALLOC_ID_, &val, value); + value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL)); + } + /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ + if(!token_exists && token[0] =='%') { + my_mstrcat(_ALLOC_ID_, &result, token + 1, NULL); + } + /* And finally set the value of token into result string */ + else if (value && value[0]!='\0') { + /* instance names (name) and node labels (lab) go thru the expandlabel function. */ + /*if something else must be parsed, put an if here! */ + if (!(strcmp(token+1,"name") && strcmp(token+1,"lab")) /* expand name/labels */ + && ((lab = expandlabel(value, &itmp)) != NULL)) { + my_mstrcat(_ALLOC_ID_, &result, lab, NULL); + } else { + my_mstrcat(_ALLOC_ID_, &result, value, NULL); + } + } + } /* else */ + + /* append token separator to output result ... */ if(c != '%' && c != '@' && c!='\0' ) { char str[2]; str[0] = (unsigned char) c; str[1] = '\0'; - - /* result_pos += my_snprintf(result + result_pos, 2, "%c", c); */ /* no realloc needed */ my_mstrcat(_ALLOC_ID_, &result, str, NULL); - /* fputc(c,fd); */ } + /* ... unless it is the start of another token, so push back to input string */ if(c == '@' || c == '%' ) s--; state=TOK_BEGIN; my_free(_ALLOC_ID_, &val); - } + } /* else if (state==TOK_SEP) */ + else if(state==TOK_BEGIN && c!='\0') { char str[2]; str[0] = (unsigned char) c; str[1] = '\0'; - /* result_pos += my_snprintf(result + result_pos, 2, "%c", c); */ /* no realloc needed */ my_mstrcat(_ALLOC_ID_, &result, str, NULL); - /* fputc(c,fd); */ } if(c=='\0') { char str[2]; str[0] = '\n'; str[1] = '\0'; - /* result_pos += my_snprintf(result + result_pos, 2, "%c", '\n'); */ /* no realloc needed */ my_mstrcat(_ALLOC_ID_, &result, str, NULL); - /* fputc('\n',fd); */ break; } } /* while(1) */ @@ -2509,7 +2498,7 @@ int print_spice_element(FILE *fd, int inst) my_free(_ALLOC_ID_, &name); my_free(_ALLOC_ID_, &token); my_free(_ALLOC_ID_, &result); - my_free(_ALLOC_ID_, &spiceprefixtag); + if(spiceprefixtag) my_free(_ALLOC_ID_, &spiceprefixtag); /* my_free(_ALLOC_ID_, &translatedvalue); */ return 1; } @@ -3750,782 +3739,774 @@ const char *spice_get_node(const char *token) /* if s==NULL return emty string */ const char *translate(int inst, const char* s) { - static regex_t *get_sp_cur = NULL; - static const char *empty=""; - static char *translated_tok = NULL; - static char *result=NULL; /* safe to keep even with multiple schematics */ - size_t size=0; - size_t tmp; - register int c, state=TOK_BEGIN, space; - char *token=NULL; - const char *tmp_sym_name; - size_t sizetok=0; - size_t result_pos=0, token_pos=0; - struct stat time_buf; - struct tm *tm; - char file_name[PATH_MAX]; - const char *value; - int escape=0, engineering = 0; - char date[200]; - int sp_prefix; - int level; - Lcc *lcc; - char *value1 = NULL; - int sim_is_xyce; - char *instname = NULL; - - if(!s && inst == -1) { - if(result) my_free(_ALLOC_ID_, &result); - if(translated_tok) my_free(_ALLOC_ID_, &translated_tok); - if(get_sp_cur) { - regfree(get_sp_cur); - get_sp_cur = NULL; - } - } - - if(!s || !xctx || !xctx->inst) { - return empty; - } - - if(!get_sp_cur) { - get_sp_cur = my_malloc(_ALLOC_ID_, sizeof(regex_t)); - /* @spice_get_current_param(...) or @spice_get_modelparam_param(...) */ - /* @spice_get_current(...) or @spice_get_modelparam(...) */ - /* @spice_get_modelvoltage(...) or @spice_get_modelvoltage_param(...) */ - regcomp(get_sp_cur, - "^@spice_get_(current|modelparam|modelvoltage)(_[a-zA-Z][a-zA-Z0-9_]*)*\\(", REG_NOSUB | REG_EXTENDED); - } - - sp_prefix = tclgetboolvar("spiceprefix"); - - if(inst >= xctx->instances) { - dbg(0, "translate(): instance number out of bounds: %d\n", inst); - return empty; - } - /* if spice_get_* token not processed by tcl use enginering notation (2m, 3u, ...) */ - if(!(strstr(s, "tcleval(") == s)) engineering = 1; - instname = (inst >=0 && xctx->inst[inst].instname) ? xctx->inst[inst].instname : ""; - sim_is_xyce = tcleval("sim_is_xyce")[0] == '1' ? 1 : 0; - level = xctx->currsch; - lcc = xctx->hier_attr; - size=CADCHUNKALLOC; - my_realloc(_ALLOC_ID_, &result,size); - result[0]='\0'; - - dbg(1, "translate(): substituting props in <%s>, instance <%s>\n", s ? s : "" , instname); - - while(1) - { - - c=*s++; - if(c=='\\') { - escape=1; - c=*s++; /* do not remove: breaks translation of format strings in netlists (escaping %) */ + static regex_t *get_sp_cur = NULL; + static const char *empty=""; + static char *result=NULL; /* safe to keep even with multiple schematics */ + size_t size=0; + size_t tmp; + register int c, state=TOK_BEGIN, space; + char *token=NULL; + const char *tmp_sym_name; + size_t sizetok=0; + size_t result_pos=0, token_pos=0; + struct stat time_buf; + struct tm *tm; + char file_name[PATH_MAX]; + const char *value; + int escape=0, engineering = 0; + char date[200]; + int sp_prefix; + int level; + Lcc *lcc; + char *value1 = NULL; + int sim_is_xyce; + char *instname = NULL; + + if(!s && inst == -1) { + if(result) my_free(_ALLOC_ID_, &result); + if(get_sp_cur) { + regfree(get_sp_cur); + get_sp_cur = NULL; + } } - else escape=0; - space=SPACE(c); - if( state==TOK_BEGIN && (c=='@' || c=='%' ) && !escape ) state=TOK_TOKEN; /* 20161210 escape */ - else if(state==TOK_TOKEN && token_pos > 1 && - ( - ( (space || c == '%' || c == '@') && !escape ) || - ( (!space && c != '%' && c != '@') && escape ) - ) - ) state=TOK_SEP; - - STR_ALLOC(&result, result_pos, &size); - STR_ALLOC(&token, token_pos, &sizetok); - if(state==TOK_TOKEN) token[token_pos++]=(char)c; - else if(state==TOK_SEP) + + if(!s || !xctx || !xctx->inst) { + return empty; + } + + if(!get_sp_cur) { + get_sp_cur = my_malloc(_ALLOC_ID_, sizeof(regex_t)); + /* @spice_get_current_param(...) or @spice_get_modelparam_param(...) */ + /* @spice_get_current(...) or @spice_get_modelparam(...) */ + /* @spice_get_modelvoltage(...) or @spice_get_modelvoltage_param(...) */ + regcomp(get_sp_cur, + "^@spice_get_(current|modelparam|modelvoltage)(_[a-zA-Z][a-zA-Z0-9_]*)*\\(", REG_NOSUB | REG_EXTENDED); + } + + sp_prefix = tclgetboolvar("spiceprefix"); + + if(inst >= xctx->instances) { + dbg(0, "translate(): instance number out of bounds: %d\n", inst); + return empty; + } + /* if spice_get_* token not processed by tcl use enginering notation (2m, 3u, ...) */ + if(!(strstr(s, "tcleval(") == s)) engineering = 1; + instname = (inst >=0 && xctx->inst[inst].instname) ? xctx->inst[inst].instname : ""; + sim_is_xyce = tcleval("sim_is_xyce")[0] == '1' ? 1 : 0; + level = xctx->currsch; + lcc = xctx->hier_attr; + size=CADCHUNKALLOC; + my_realloc(_ALLOC_ID_, &result,size); + result[0]='\0'; + + dbg(1, "translate(): substituting props in <%s>, instance <%s>\n", s ? s : "" , instname); + + while(1) { - token[token_pos]='\0'; - if(!strcmp(token, "@name")) { - tmp = strlen(instname); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, instname, tmp+1); - result_pos+=tmp; - } else if(inst >= 0 && strcmp(token,"@symref")==0) { - tmp_sym_name = get_sym_name(inst, 9999, 1, 0); - tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; - tmp=strlen(tmp_sym_name); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,tmp_sym_name, tmp+1); - result_pos+=tmp; - } else if(inst >= 0 && strcmp(token,"@symname")==0) { - tmp_sym_name = get_sym_name(inst, 0, 0, 0); - tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; - tmp=strlen(tmp_sym_name); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,tmp_sym_name, tmp+1); - result_pos+=tmp; - } else if(strcmp(token,"@path")==0) { - const char *path = xctx->sch_path[xctx->currsch] + 1; - int start_level = sch_waves_loaded(), skip = 0; - if(start_level == -1) start_level = 0; - - /* skip path components that are above the level where raw file was loaded */ - while(*path && skip < start_level) { - if(*path == '.') skip++; - ++path; + c=*s++; + if(c=='\\') { + escape=1; + c=*s++; /* do not remove: breaks translation of format strings in netlists (escaping %) */ } - - tmp=strlen(path); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, path, tmp+1); - result_pos+=tmp; - } else if(inst >= 0 && strcmp(token,"@symname_ext")==0) { - tmp_sym_name = get_sym_name(inst, 0, 1, 0); - tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; - tmp=strlen(tmp_sym_name); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,tmp_sym_name, tmp+1); - result_pos+=tmp; - /* recognize single pins 15112003 */ - } else if(inst >= 0 && token[0]=='@' && token[1]=='@' && xctx->inst[inst].ptr >= 0) { - int i, multip; - int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; - prepare_netlist_structs(0); - for(i=0;iinst[inst].ptr + xctx->sym)->rect[PINLAYER][i].prop_ptr; - if (!strcmp( get_tok_value(prop,"name",0), token+2)) { - if(strboolcmp(get_tok_value(prop,"spice_ignore",0), "true")) { - const char *str_ptr = net_name(inst,i, &multip, 0, 0); - tmp = strlen(str_ptr) +100 ; - STR_ALLOC(&result, tmp + result_pos, &size); - result_pos += my_snprintf(result + result_pos, tmp, "%s", str_ptr); - } - break; - } - } - } else if(inst >= 0 && token[0]=='@' && token[1]=='#') { - value = get_pin_attr(token, inst, engineering); - if(value) { - tmp=strlen(value); + else escape=0; + space=SPACE(c); + if( state==TOK_BEGIN && (c=='@' || c=='%' ) && !escape ) state=TOK_TOKEN; /* 20161210 escape */ + else if(state==TOK_TOKEN && token_pos > 1 && + ( + ( (space || c == '%' || c == '@') && !escape ) || + ( (!space && c != '%' && c != '@') && escape ) + ) + ) state=TOK_SEP; + + STR_ALLOC(&result, result_pos, &size); + STR_ALLOC(&token, token_pos, &sizetok); + if(state==TOK_TOKEN) token[token_pos++]=(char)c; + else if(state==TOK_SEP) + { + token[token_pos]='\0'; + if(!strcmp(token, "@name")) { + tmp = strlen(instname); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, instname, tmp+1); + result_pos+=tmp; + } else if(inst >= 0 && strcmp(token,"@symref")==0) { + tmp_sym_name = get_sym_name(inst, 9999, 1, 0); + tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; + tmp=strlen(tmp_sym_name); STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, value, tmp+1); + memcpy(result+result_pos,tmp_sym_name, tmp+1); result_pos+=tmp; - my_free(_ALLOC_ID_, &value); - } - } else if(inst >= 0 && strcmp(token,"@sch_last_modified")==0 && xctx->inst[inst].ptr >= 0) { - - get_sch_from_sym(file_name, xctx->inst[inst].ptr + xctx->sym, inst, 0); - if(!stat(file_name , &time_buf)) { - tm=localtime(&(time_buf.st_mtime) ); - tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, date, tmp+1); - result_pos+=tmp; - } - } else if(inst >= 0 && strcmp(token,"@sym_last_modified")==0) { - my_strncpy(file_name, abs_sym_path(tcl_hook2(xctx->inst[inst].name), ""), S(file_name)); - if(!stat(file_name , &time_buf)) { - tm=localtime(&(time_buf.st_mtime) ); - tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, date, tmp+1); - result_pos+=tmp; - } - } else if(strcmp(token,"@time_last_modified")==0) { - my_strncpy(file_name, abs_sym_path(xctx->sch[xctx->currsch], ""), S(file_name)); - if(!stat(file_name , &time_buf)) { - tm=localtime(&(time_buf.st_mtime) ); - tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, date, tmp+1); - result_pos+=tmp; - } - } else if(strcmp(token,"@schname_ext")==0) { - /* tmp=strlen(xctx->sch[xctx->currsch]);*/ - tmp = strlen(xctx->current_name); - STR_ALLOC(&result, tmp + result_pos, &size); - /* memcpy(result+result_pos,xctx->sch[xctx->currsch], tmp+1); */ - memcpy(result+result_pos, xctx->current_name, tmp+1); - result_pos+=tmp; - } else if(strcmp(token,"@schname")==0) { - const char *schname = get_cell(xctx->current_name, 0); - tmp = strlen(schname); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, schname, tmp+1); - result_pos+=tmp; - } else if(strcmp(token,"@topschname")==0) { - const char *topsch; - topsch = get_trailing_path(xctx->sch[0], 0, 1); - tmp = strlen(topsch); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, topsch, tmp+1); - result_pos+=tmp; - } else if(inst >= 0 && strcmp(token,"@prop_ptr")==0 && xctx->inst[inst].prop_ptr) { - tmp=strlen(xctx->inst[inst].prop_ptr); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->inst[inst].prop_ptr, tmp+1); - result_pos+=tmp; - } - else if(inst >= 0 && strcmp(token,"@spice_get_voltage")==0 && xctx->inst[inst].ptr >= 0) - { - int start_level; /* hierarchy level where waves were loaded */ - int live = tclgetboolvar("live_cursor2_backannotate"); - if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { - int multip; - int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; - if(no_of_pins == 1) { - char *fqnet = NULL; - const char *path = xctx->sch_path[xctx->currsch] + 1; - char *net = NULL; - size_t len; - int idx; - double val = 0.0; - const char *valstr; - if(path) { - prepare_netlist_structs(0); - if(xctx->inst[inst].lab) { - my_strdup2(_ALLOC_ID_, &net, expandlabel(xctx->inst[inst].lab, &multip)); - } - if(net == NULL || net[0] == '\0') { - my_strdup2(_ALLOC_ID_, &net, net_name(inst, 0, &multip, 0, 0)); - } - if(multip == 1 && net && net[0]) { - char *rn; - dbg(1, "translate() @spice_get_voltage: inst=%s\n", instname); - dbg(1, " net=%s\n", net); - rn = resolved_net(net); - if(rn) { - my_strdup2(_ALLOC_ID_, &fqnet, rn); - if(rn) my_free(_ALLOC_ID_, &rn); - strtolower(fqnet); - dbg(1, "translate() @spice_get_voltage: fqnet=%s start_level=%d\n", fqnet, start_level); - idx = get_raw_index(fqnet, NULL); - if(idx >= 0) { - val = xctx->raw->cursor_b_val[idx]; - } - if(!strcmp(fqnet, "0") || !my_strcasecmp(fqnet, "GND")) { - valstr = "0.0"; - xctx->tok_size = 3; - len = 3; - } else if(idx < 0) { - valstr = "-"; - xctx->tok_size = 5; - len = 5; - } else { - valstr = engineering ? dtoa_eng(val) : dtoa(val); - len = xctx->tok_size; - } - if(len) { - STR_ALLOC(&result, len + result_pos, &size); - memcpy(result+result_pos, valstr, len+1); - result_pos += len; - } - dbg(1, "inst %d, net=%s, fqnet=%s idx=%d valstr=%s\n", inst, net, fqnet, idx, valstr); - if(fqnet) my_free(_ALLOC_ID_, &fqnet); - } - } - if(net) my_free(_ALLOC_ID_, &net); - } + } else if(inst >= 0 && strcmp(token,"@symname")==0) { + tmp_sym_name = get_sym_name(inst, 0, 0, 0); + tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; + tmp=strlen(tmp_sym_name); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,tmp_sym_name, tmp+1); + result_pos+=tmp; + } else if(strcmp(token,"@path")==0) { + const char *path = xctx->sch_path[xctx->currsch] + 1; + int start_level = sch_waves_loaded(), skip = 0; + if(start_level == -1) start_level = 0; + + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; } - } - } - - /* copy as is: processed by spice_get_node() later - * the format is "some_text@spice_get_node some_additional_text" - * Examples: - * Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id]) - * will translate to: - * Id=6.6177u - * Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id]) A - * will translate to: - * Id=6.6177uA - * note the required separator spaces around the spice node. Spaces are used here as - * separators since spice nodes don't allow spaces. - * escapes are used for 2 reasons: - * mark a @ as a literal character instead of a the start of a @var token to be substituted - * mark the end of a @var, like for example @var\iable. In this case @var will - * be substituted by xschem instead of @variable - * - * caveats: only one @spice_get_node is allowed in a string - */ - else if(strcmp(token,"@spice_get_node")==0 ) - { - STR_ALLOC(&result, 15 + result_pos, &size); - memcpy(result+result_pos, token, 16); - result_pos += 15; - } - else if(strncmp(token,"@spice_get_voltage(", 19)==0 ) - { - int start_level; /* hierarchy level where waves were loaded */ - int live = tclgetboolvar("live_cursor2_backannotate"); - dbg(1, "--> %s\n", token); - if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { - char *fqnet = NULL; - const char *path = xctx->sch_path[xctx->currsch] + 1; - char *net = NULL; - char *global_net; - size_t len; - int idx, n, multip; - double val = 0.0; - const char *valstr; - tmp = strlen(token) + 1; - if(path) { - int skip = 0; - /* skip path components that are above the level where raw file was loaded */ - while(*path && skip < start_level) { - if(*path == '.') skip++; - ++path; - } - net = my_malloc(_ALLOC_ID_, tmp); - n = sscanf(token + 19, "%[^)]", net); - expandlabel(net, &multip); - if(n == 1 && multip == 1) { - len = strlen(path) + strlen(instname) + strlen(net) + 2; - dbg(1, "net=%s\n", net); - fqnet = my_malloc(_ALLOC_ID_, len); - - - global_net = strrchr(net, '.'); - if(global_net == NULL) global_net = net; - else global_net++; - - if(inst < 0 || record_global_node(3, NULL, global_net)) { - strtolower(net); - my_snprintf(fqnet, len, "%s", global_net); - } else { - strtolower(net); - my_snprintf(fqnet, len, "%s%s.%s", path, instname, net); - } - strtolower(fqnet); - dbg(1, "translate(): inst=%d, net=%s, fqnet=%s start_level=%d\n", inst, net, fqnet, start_level); - idx = get_raw_index(fqnet, NULL); - if(idx >= 0) { - val = xctx->raw->cursor_b_val[idx]; - } - if(!strcmp(fqnet, "0") || !my_strcasecmp(fqnet, "GND")) { - valstr = "0.0"; - xctx->tok_size = 3; - len = 3; - } else if(idx < 0) { - valstr = "-"; - xctx->tok_size = 1; - len = 1; - } else { - /* always use engineering as these tokens are generated from single - * @spice_get_voltage patterns */ - valstr = dtoa_eng(val); - len = xctx->tok_size; - } - if(len) { - STR_ALLOC(&result, len + result_pos, &size); - memcpy(result+result_pos, valstr, len+1); - result_pos += len; - } - dbg(1, "instname %s, net=%s, fqnet=%s idx=%d valstr=%s\n", instname, net, fqnet, idx, valstr); - my_free(_ALLOC_ID_, &fqnet); - } - my_free(_ALLOC_ID_, &net); - } - } - } - /* @spice_get_current(...) or @spice_get_current_param(...) - * @spice_get_modelparam(...) or @spice_get_modelparam_param(...) - * @spice_get_modelvoltage(...) or @spice_get_modelvoltage_param(...) - * - * Only @spice_get_current(...) and @spice_get_current_param(...) are processed - * the other types are ignored */ - else if(!regexec(get_sp_cur, token, 0 , NULL, 0) ) - { - int start_level; /* hierarchy level where waves were loaded */ - int live = tclgetboolvar("live_cursor2_backannotate"); - if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { - char *fqdev = NULL; - const char *path = xctx->sch_path[xctx->currsch] + 1; - char *dev = NULL, *param = NULL; - size_t len; - int idx, n = 0; - double val = 0.0; - const char *valstr; - tmp = strlen(token) + 1; - if(path) { - int skip = 0; - /* skip path components that are above the level where raw file was loaded */ - while(*path && skip < start_level) { - if(*path == '.') skip++; - ++path; - } - dev = my_malloc(_ALLOC_ID_, tmp); - dbg(1, "%s\n", token); - if(!strncmp(token, "@spice_get_current(", 19)) { - n = sscanf(token + 19, "%[^)]", dev); - } else { - param = my_malloc(_ALLOC_ID_, tmp); - n = sscanf(token, "@spice_get_current_%s(%[^)]", param, dev); - if(n < 2) { - my_free(_ALLOC_ID_, ¶m); - n = sscanf(token, "@spice_get_current[^(](%[^)]", dev); - } - } - if(n >= 1) { - strtolower(dev); - len = strlen(path) + strlen(instname) + - strlen(dev) + 21; /* some extra chars for i(..) wrapper */ - dbg(1, "dev=%s\n", dev); - fqdev = my_malloc(_ALLOC_ID_, len); - if(!sim_is_xyce) { - int prefix, vsource; - char *prefix_ptr = strrchr(dev, '.'); /* last '.' in dev */ - if(prefix_ptr) prefix = prefix_ptr[1]; /* character after last '.' */ - else prefix=dev[0]; - dbg(1, "prefix=%c, path=%s\n", prefix, path); - vsource = (prefix == 'v') || (prefix == 'e'); - if(vsource) { - my_snprintf(fqdev, len, "i(%c.%s%s.%s)", prefix, path, instname, dev); - } else if(prefix == 'q') { - my_snprintf(fqdev, len, "i(@%c.%s%s.%s[%s])", prefix, path, instname, dev, param ? param : "ic"); - } else if(prefix == 'd' || prefix == 'm') { - my_snprintf(fqdev, len, "i(@%c.%s%s.%s[%s])", prefix, path, instname, dev, param ? param : "id"); - } else if(prefix == 'i') { - my_snprintf(fqdev, len, "i(@%c.%s%s.%s[current])", prefix, path, instname, dev); - } else { - my_snprintf(fqdev, len, "i(@%c.%s%s.%s[i])", prefix, path, instname, dev); - } - } else { - my_snprintf(fqdev, len, "i(%s%s.%s)", path, instname, dev); - } - strtolower(fqdev); - dbg(1, "fqdev=%s\n", fqdev); - idx = get_raw_index(fqdev, NULL); - if(idx >= 0) { - val = xctx->raw->cursor_b_val[idx]; - } - if(idx < 0) { - valstr = "-"; - xctx->tok_size = 1; - len = 1; - } else { - /* always use engineering as these tokens are generated from single - * @spice_get_voltage patterns */ - valstr = dtoa_eng(val); - len = xctx->tok_size; - } - if(len) { - STR_ALLOC(&result, len + result_pos, &size); - memcpy(result+result_pos, valstr, len+1); - result_pos += len; - } - dbg(1, "instname %s, dev=%s, fqdev=%s idx=%d valstr=%s\n", instname, dev, fqdev, idx, valstr); - my_free(_ALLOC_ID_, &fqdev); - } /* if(n == 1) */ - if(param) my_free(_ALLOC_ID_, ¶m); - my_free(_ALLOC_ID_, &dev); - } /* if(path) */ - } /* if((start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) */ - } - else if(inst >= 0 && strcmp(token,"@spice_get_diff_voltage")==0 && xctx->inst[inst].ptr >= 0) - { - int start_level; /* hierarchy level where waves were loaded */ - int live = tclgetboolvar("live_cursor2_backannotate"); - if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { - int multip; - int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; - if(no_of_pins == 2) { - char *fqnet1 = NULL, *fqnet2 = NULL; - const char *path = xctx->sch_path[xctx->currsch] + 1; - const char *net1, *net2; - size_t len; - int idx1, idx2; - double val = 0.0, val1 = 0.0, val2 = 0.0; - const char *valstr; - if(path) { - int gnd1 = 0, gnd2 = 0; - int skip = 0; - /* skip path components that are above the level where raw file was loaded */ - while(*path && skip < start_level) { - if(*path == '.') skip++; - ++path; - } - prepare_netlist_structs(0); - net1 = net_name(inst, 0, &multip, 0, 0); - len = strlen(path) + strlen(net1) + 1; - dbg(1, "net1=%s\n", net1); - fqnet1 = my_malloc(_ALLOC_ID_, len); - my_snprintf(fqnet1, len, "%s%s", path, net1); - strtolower(fqnet1); - net2 = net_name(inst, 1, &multip, 0, 0); - len = strlen(path) + strlen(net2) + 1; - dbg(1, "net2=%s\n", net2); - fqnet2 = my_malloc(_ALLOC_ID_, len); - my_snprintf(fqnet2, len, "%s%s", path, net2); - strtolower(fqnet2); - dbg(1, "translate(): fqnet1=%s start_level=%d\n", fqnet1, start_level); - dbg(1, "translate(): fqnet2=%s start_level=%d\n", fqnet2, start_level); - if(!strcmp(fqnet1, "0") || !my_strcasecmp(fqnet1, "GND")) gnd1 = 1; - if(!strcmp(fqnet2, "0") || !my_strcasecmp(fqnet2, "GND")) gnd2 = 1; - idx1 = get_raw_index(fqnet1, NULL); - idx2 = get_raw_index(fqnet2, NULL); - if( (!gnd1 && idx1 < 0) || (!gnd2 && idx2 < 0) ) { - valstr = "-"; - xctx->tok_size = 1; - len = 1; - } else { - double val1 = gnd1 ? 0.0 : xctx->raw->cursor_b_val[idx1]; - double val2 = gnd2 ? 0.0 : xctx->raw->cursor_b_val[idx2]; - val = val1 - val2; - valstr = engineering ? dtoa_eng(val) : dtoa(val); - len = xctx->tok_size; - } - if(len) { - STR_ALLOC(&result, len + result_pos, &size); - memcpy(result+result_pos, valstr, len+1); - result_pos += len; - } - dbg(1, "inst %d, fqnet1=%s fqnet2=%s idx1=%d idx2=%d, val1=%g val2=%g valstr=%s\n", - inst, fqnet1, fqnet2, idx1, idx2, val1, val2, valstr); - my_free(_ALLOC_ID_, &fqnet1); - my_free(_ALLOC_ID_, &fqnet2); - } - } - } - } - else if( - strncmp(token,"@spice_get_current", 18)==0 || - strncmp(token,"@spice_get_modelparam", 21)==0 || - strncmp(token,"@spice_get_modelvoltage", 23)==0 - ) - { - int start_level; /* hierarchy level where waves were loaded */ - int live = tclgetboolvar("live_cursor2_backannotate"); - if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { - char *fqdev = NULL; - const char *path = xctx->sch_path[xctx->currsch] + 1; - char *dev = NULL, *param = NULL; - int modelparam = 0; /* 0: current, 1: modelparam, 2: modelvoltage */ - size_t len; - int idx; - int error = 0; - double val = 0.0; - const char *valstr; - if(path) { - int skip = 0; - /* skip path components that are above the level where raw file was loaded */ - while(*path && skip < start_level) { - if(*path == '.') skip++; - ++path; - } - /* token contans _param after @spice_get_current or @spice_get_modelparam - * or @spice_get_modelvoltage */ - if(strcmp(token, "@spice_get_current") && - strcmp(token, "@spice_get_modelparam") && - strcmp(token, "@spice_get_modelvoltage")) { - int n = 0; - param = my_malloc(_ALLOC_ID_, strlen(token) + 1); - n = sscanf(token, "@spice_get_current_%s", param); - if(n == 0) { - n = sscanf(token, "@spice_get_modelparam_%s", param); - modelparam = 1; - } - if(n == 0) { - n = sscanf(token, "@spice_get_modelvoltage_%s", param); - modelparam = 2; - } - if(n == 0) { - my_free(_ALLOC_ID_, ¶m); - error = 1; - } - } - if(!error) { - char *iprefix = modelparam == 0 ? "i(" : modelparam == 1 ? "" : "v("; - char *ipostfix = modelparam == 1 ? "" : ")"; - int prefix; - my_strdup2(_ALLOC_ID_, &dev, instname); - strtolower(dev); - prefix=dev[0]; - len = strlen(path) + strlen(dev) + 40; /* some extra chars for i(..) wrapper */ - dbg(1, "token=%s, dev=%s param=%s\n", token, dev, param ? param : ""); - fqdev = my_malloc(_ALLOC_ID_, len); - if(!sim_is_xyce) { - int vsource = (prefix == 'v') || (prefix == 'e'); - if(path[0]) { - if(vsource) { - my_snprintf(fqdev, len, "i(%c.%s%s)", prefix, path, dev); - } else if(prefix=='q') { - my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", - iprefix, prefix, path, dev, param ? param : "ic", ipostfix); - } else if(prefix=='d' || prefix == 'm') { - my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", - iprefix, prefix, path, dev, param ? param : "id", ipostfix); - } else if(prefix=='i') { - my_snprintf(fqdev, len, "i(@%c.%s%s[current])", prefix, path, dev); - } else { - my_snprintf(fqdev, len, "i(@%c.%s%s[i])", prefix, path, dev); - } - } else { - if(vsource) { - my_snprintf(fqdev, len, "i(%s)", dev); - } else if(prefix == 'q') { - my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "ic", ipostfix); - } else if(prefix == 'd' || prefix == 'm') { - my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "id", ipostfix); - } else if(prefix == 'i') { - my_snprintf(fqdev, len, "i(@%s[current])", dev); - } else { - my_snprintf(fqdev, len, "i(@%s[i])", dev); - } - } - } else { - my_snprintf(fqdev, len, "i(%s%s)", path, dev); - } - if(param) my_free(_ALLOC_ID_, ¶m); - dbg(1, "fqdev=%s\n", fqdev); - strtolower(fqdev); - idx = get_raw_index(fqdev, NULL); - if(idx >= 0) { - val = xctx->raw->cursor_b_val[idx]; - } - /* special handling for resistors that are converted to b sources: - * i(@r.x4.r1[i]) --> i(@b.x4.br1[i]) - */ - if(idx < 0 && !strncmp(fqdev, "i(@r", 4)) { - if(path[0]) { - my_snprintf(fqdev, len, "i(@b.%sb%s[i])", path, dev); - } else { - my_snprintf(fqdev, len, "i(@b%s[i])", dev); - } - dbg(1, "fqdev=%s\n", fqdev); - idx = get_raw_index(fqdev, NULL); - if(idx >= 0) { - val = xctx->raw->cursor_b_val[idx]; - } - } - if(idx < 0) { - valstr = "-"; - xctx->tok_size = 1; - len = 1; - } else { - valstr = engineering ? dtoa_eng(val) : dtoa(val); - len = xctx->tok_size; - } - if(len) { - STR_ALLOC(&result, len + result_pos, &size); - memcpy(result+result_pos, valstr, len+1); - result_pos += len; - } - dbg(1, "instname %s, dev=%s, fqdev=%s idx=%d valstr=%s\n", instname, dev, fqdev, idx, valstr); - my_free(_ALLOC_ID_, &fqdev); - my_free(_ALLOC_ID_, &dev); - } /* if(!error) */ - } /* if(path) */ - } /* (live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) */ - } - else if(strcmp(token,"@schvhdlprop")==0 && xctx->schvhdlprop) - { - tmp=strlen(xctx->schvhdlprop); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->schvhdlprop, tmp+1); - result_pos+=tmp; - } - - else if(strcmp(token,"@schprop")==0 && xctx->schprop) - { - tmp=strlen(xctx->schprop); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->schprop, tmp+1); - result_pos+=tmp; - } - /* /20100217 */ - - else if(strcmp(token,"@schsymbolprop")==0 && xctx->schsymbolprop) - { - tmp=strlen(xctx->schsymbolprop); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->schsymbolprop, tmp+1); - result_pos+=tmp; - } - else if(strcmp(token,"@schtedaxprop")==0 && xctx->schtedaxprop) - { - tmp=strlen(xctx->schtedaxprop); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->schtedaxprop, tmp+1); - result_pos+=tmp; - } - /* /20100217 */ - - else if(strcmp(token,"@schverilogprop")==0 && xctx->schverilogprop) - { - tmp=strlen(xctx->schverilogprop); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos,xctx->schverilogprop, tmp+1); - result_pos+=tmp; - /* if spiceprefix==0 and token == @spiceprefix then set empty value */ - } else if(!sp_prefix && !strcmp(token, "@spiceprefix")) { - /* add nothing */ - } else if(inst >= 0) { - value = get_tok_value(xctx->inst[inst].prop_ptr, token+1, 0); - if(!xctx->tok_size && xctx->inst[inst].ptr >= 0) { - value=get_tok_value(xctx->sym[xctx->inst[inst].ptr].templ, token+1, 0); - } - if(!xctx->tok_size) { /* above lines did not find a value for token */ - if(token[0] =='%') { - /* no definition found -> subst with token without leading % */ - tmp=token_pos -1 ; /* we need token_pos -1 chars, ( strlen(token+1) ) , excluding leading '%' */ + + tmp=strlen(path); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, path, tmp+1); + result_pos+=tmp; + } else if(inst >= 0 && strcmp(token,"@symname_ext")==0) { + tmp_sym_name = get_sym_name(inst, 0, 1, 0); + tmp_sym_name=tmp_sym_name ? tmp_sym_name : ""; + tmp=strlen(tmp_sym_name); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,tmp_sym_name, tmp+1); + result_pos+=tmp; + /* recognize single pins 15112003 */ + } else if(inst >= 0 && token[0]=='@' && token[1]=='@' && xctx->inst[inst].ptr >= 0) { + int i, multip; + int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; + prepare_netlist_structs(0); + for(i=0;iinst[inst].ptr + xctx->sym)->rect[PINLAYER][i].prop_ptr; + if (!strcmp( get_tok_value(prop,"name",0), token+2)) { + if(strboolcmp(get_tok_value(prop,"spice_ignore",0), "true")) { + const char *str_ptr = net_name(inst,i, &multip, 0, 0); + tmp = strlen(str_ptr) +100 ; + STR_ALLOC(&result, tmp + result_pos, &size); + result_pos += my_snprintf(result + result_pos, tmp, "%s", str_ptr); + } + break; + } + } + } else if(inst >= 0 && token[0]=='@' && token[1]=='#') { + value = get_pin_attr(token, inst, engineering); + if(value) { + tmp=strlen(value); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, value, tmp+1); + result_pos+=tmp; + my_free(_ALLOC_ID_, &value); + } + } else if(inst >= 0 && strcmp(token,"@sch_last_modified")==0 && xctx->inst[inst].ptr >= 0) { + + get_sch_from_sym(file_name, xctx->inst[inst].ptr + xctx->sym, inst, 0); + if(!stat(file_name , &time_buf)) { + tm=localtime(&(time_buf.st_mtime) ); + tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); STR_ALLOC(&result, tmp + result_pos, &size); - /* dbg(2, "translate(): token=%s, token_pos = %d\n", token, token_pos); */ - memcpy(result+result_pos, token + 1, tmp+1); + memcpy(result+result_pos, date, tmp+1); result_pos+=tmp; } - } else { - int i = level; - my_strdup2(_ALLOC_ID_, &value1, value); - /* recursive substitution of value using parent level prop_ptr attributes */ - while(i > 0) { - char *v = value1; - const char *tok; - if(v && v[0] == '@') v++; - tok = get_tok_value(lcc[i-1].prop_ptr, v, 0); - if(xctx->tok_size && tok[0]) { - dbg(1, "tok=%s\n", tok); - my_strdup2(_ALLOC_ID_, &value1, tok); - } else { - tok = get_tok_value(lcc[i-1].templ, v, 0); - if(xctx->tok_size && tok[0]) { - dbg(1, "from parent template: tok=%s\n", tok); - my_strdup2(_ALLOC_ID_, &value1, tok); - } - } - dbg(1, "2 translate(): lcc[%d].prop_ptr=%s, value1=%s\n", i-1, lcc[i-1].prop_ptr, value1); - i--; + } else if(inst >= 0 && strcmp(token,"@sym_last_modified")==0) { + my_strncpy(file_name, abs_sym_path(tcl_hook2(xctx->inst[inst].name), ""), S(file_name)); + if(!stat(file_name , &time_buf)) { + tm=localtime(&(time_buf.st_mtime) ); + tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, date, tmp+1); + result_pos+=tmp; } - if(strstr(value1, "expr(") == value1) { - dbg(1, "translate(): expr():%s\n", value1); - my_strdup2(_ALLOC_ID_, &value1, eval_expr( - translate3(value1, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + } else if(strcmp(token,"@time_last_modified")==0) { + my_strncpy(file_name, abs_sym_path(xctx->sch[xctx->currsch], ""), S(file_name)); + if(!stat(file_name , &time_buf)) { + tm=localtime(&(time_buf.st_mtime) ); + tmp=strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", tm); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, date, tmp+1); + result_pos+=tmp; } - tmp=strlen(value1); - STR_ALLOC(&result, tmp + result_pos, &size); - memcpy(result+result_pos, value1, tmp+1); - result_pos+=tmp; - my_free(_ALLOC_ID_, &value1); - } - } - token_pos = 0; - if(c == '@' || c == '%') s--; - else result[result_pos++]=(char)c; - state=TOK_BEGIN; - } /* else if(state==TOK_SEP) */ - else if(state==TOK_BEGIN) result[result_pos++]=(char)c; - if(c=='\0') - { - result[result_pos]='\0'; - break; + } else if(strcmp(token,"@schname_ext")==0) { + /* tmp=strlen(xctx->sch[xctx->currsch]);*/ + tmp = strlen(xctx->current_name); + STR_ALLOC(&result, tmp + result_pos, &size); + /* memcpy(result+result_pos,xctx->sch[xctx->currsch], tmp+1); */ + memcpy(result+result_pos, xctx->current_name, tmp+1); + result_pos+=tmp; + } else if(strcmp(token,"@schname")==0) { + const char *schname = get_cell(xctx->current_name, 0); + tmp = strlen(schname); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, schname, tmp+1); + result_pos+=tmp; + } else if(strcmp(token,"@topschname")==0) { + const char *topsch; + topsch = get_trailing_path(xctx->sch[0], 0, 1); + tmp = strlen(topsch); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, topsch, tmp+1); + result_pos+=tmp; + } else if(inst >= 0 && strcmp(token,"@prop_ptr")==0 && xctx->inst[inst].prop_ptr) { + tmp=strlen(xctx->inst[inst].prop_ptr); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->inst[inst].prop_ptr, tmp+1); + result_pos+=tmp; + } + else if(inst >= 0 && strcmp(token,"@spice_get_voltage")==0 && xctx->inst[inst].ptr >= 0) + { + int start_level; /* hierarchy level where waves were loaded */ + int live = tclgetboolvar("live_cursor2_backannotate"); + if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { + int multip; + int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; + if(no_of_pins == 1) { + char *fqnet = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + char *net = NULL; + size_t len; + int idx; + double val = 0.0; + const char *valstr; + if(path) { + prepare_netlist_structs(0); + if(xctx->inst[inst].lab) { + my_strdup2(_ALLOC_ID_, &net, expandlabel(xctx->inst[inst].lab, &multip)); + } + if(net == NULL || net[0] == '\0') { + my_strdup2(_ALLOC_ID_, &net, net_name(inst, 0, &multip, 0, 0)); + } + if(multip == 1 && net && net[0]) { + char *rn; + dbg(1, "translate() @spice_get_voltage: inst=%s\n", instname); + dbg(1, " net=%s\n", net); + rn = resolved_net(net); + if(rn) { + my_strdup2(_ALLOC_ID_, &fqnet, rn); + if(rn) my_free(_ALLOC_ID_, &rn); + strtolower(fqnet); + dbg(1, "translate() @spice_get_voltage: fqnet=%s start_level=%d\n", fqnet, start_level); + idx = get_raw_index(fqnet, NULL); + if(idx >= 0) { + val = xctx->raw->cursor_b_val[idx]; + } + if(!strcmp(fqnet, "0") || !my_strcasecmp(fqnet, "GND")) { + valstr = "0.0"; + xctx->tok_size = 3; + len = 3; + } else if(idx < 0) { + valstr = "-"; + xctx->tok_size = 5; + len = 5; + } else { + valstr = engineering ? dtoa_eng(val) : dtoa(val); + len = xctx->tok_size; + } + if(len) { + STR_ALLOC(&result, len + result_pos, &size); + memcpy(result+result_pos, valstr, len+1); + result_pos += len; + } + dbg(1, "inst %d, net=%s, fqnet=%s idx=%d valstr=%s\n", inst, net, fqnet, idx, valstr); + if(fqnet) my_free(_ALLOC_ID_, &fqnet); + } + } + if(net) my_free(_ALLOC_ID_, &net); + } + } + } + } + + /* copy as is: processed by spice_get_node() later + * the format is "some_text@spice_get_node some_additional_text" + * Examples: + * Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id]) + * will translate to: + * Id=6.6177u + * Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id]) A + * will translate to: + * Id=6.6177uA + * note the required separator spaces around the spice node. Spaces are used here as + * separators since spice nodes don't allow spaces. + * escapes are used for 2 reasons: + * mark a @ as a literal character instead of a the start of a @var token to be substituted + * mark the end of a @var, like for example @var\iable. In this case @var will + * be substituted by xschem instead of @variable + * + * caveats: only one @spice_get_node is allowed in a string + */ + else if(strcmp(token,"@spice_get_node")==0 ) + { + STR_ALLOC(&result, 15 + result_pos, &size); + memcpy(result+result_pos, token, 16); + result_pos += 15; + } + else if(strncmp(token,"@spice_get_voltage(", 19)==0 ) + { + int start_level; /* hierarchy level where waves were loaded */ + int live = tclgetboolvar("live_cursor2_backannotate"); + dbg(1, "--> %s\n", token); + if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { + char *fqnet = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + char *net = NULL; + char *global_net; + size_t len; + int idx, n, multip; + double val = 0.0; + const char *valstr; + tmp = strlen(token) + 1; + if(path) { + int skip = 0; + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; + } + net = my_malloc(_ALLOC_ID_, tmp); + n = sscanf(token + 19, "%[^)]", net); + expandlabel(net, &multip); + if(n == 1 && multip == 1) { + len = strlen(path) + strlen(instname) + strlen(net) + 2; + dbg(1, "net=%s\n", net); + fqnet = my_malloc(_ALLOC_ID_, len); + + + global_net = strrchr(net, '.'); + if(global_net == NULL) global_net = net; + else global_net++; + + if(inst < 0 || record_global_node(3, NULL, global_net)) { + strtolower(net); + my_snprintf(fqnet, len, "%s", global_net); + } else { + strtolower(net); + my_snprintf(fqnet, len, "%s%s.%s", path, instname, net); + } + strtolower(fqnet); + dbg(1, "translate(): inst=%d, net=%s, fqnet=%s start_level=%d\n", inst, net, fqnet, start_level); + idx = get_raw_index(fqnet, NULL); + if(idx >= 0) { + val = xctx->raw->cursor_b_val[idx]; + } + if(!strcmp(fqnet, "0") || !my_strcasecmp(fqnet, "GND")) { + valstr = "0.0"; + xctx->tok_size = 3; + len = 3; + } else if(idx < 0) { + valstr = "-"; + xctx->tok_size = 1; + len = 1; + } else { + /* always use engineering as these tokens are generated from single + * @spice_get_voltage patterns */ + valstr = dtoa_eng(val); + len = xctx->tok_size; + } + if(len) { + STR_ALLOC(&result, len + result_pos, &size); + memcpy(result+result_pos, valstr, len+1); + result_pos += len; + } + dbg(1, "instname %s, net=%s, fqnet=%s idx=%d valstr=%s\n", instname, net, fqnet, idx, valstr); + my_free(_ALLOC_ID_, &fqnet); + } + my_free(_ALLOC_ID_, &net); + } + } + } + /* @spice_get_current(...) or @spice_get_current_param(...) + * @spice_get_modelparam(...) or @spice_get_modelparam_param(...) + * @spice_get_modelvoltage(...) or @spice_get_modelvoltage_param(...) + * + * Only @spice_get_current(...) and @spice_get_current_param(...) are processed + * the other types are ignored */ + else if(!regexec(get_sp_cur, token, 0 , NULL, 0) ) + { + int start_level; /* hierarchy level where waves were loaded */ + int live = tclgetboolvar("live_cursor2_backannotate"); + if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { + char *fqdev = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + char *dev = NULL, *param = NULL; + size_t len; + int idx, n = 0; + double val = 0.0; + const char *valstr; + tmp = strlen(token) + 1; + if(path) { + int skip = 0; + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; + } + dev = my_malloc(_ALLOC_ID_, tmp); + dbg(1, "%s\n", token); + if(!strncmp(token, "@spice_get_current(", 19)) { + n = sscanf(token + 19, "%[^)]", dev); + } else { + param = my_malloc(_ALLOC_ID_, tmp); + n = sscanf(token, "@spice_get_current_%s(%[^)]", param, dev); + if(n < 2) { + my_free(_ALLOC_ID_, ¶m); + n = sscanf(token, "@spice_get_current[^(](%[^)]", dev); + } + } + if(n >= 1) { + strtolower(dev); + len = strlen(path) + strlen(instname) + + strlen(dev) + 21; /* some extra chars for i(..) wrapper */ + dbg(1, "dev=%s\n", dev); + fqdev = my_malloc(_ALLOC_ID_, len); + if(!sim_is_xyce) { + int prefix, vsource; + char *prefix_ptr = strrchr(dev, '.'); /* last '.' in dev */ + if(prefix_ptr) prefix = prefix_ptr[1]; /* character after last '.' */ + else prefix=dev[0]; + dbg(1, "prefix=%c, path=%s\n", prefix, path); + vsource = (prefix == 'v') || (prefix == 'e'); + if(vsource) { + my_snprintf(fqdev, len, "i(%c.%s%s.%s)", prefix, path, instname, dev); + } else if(prefix == 'q') { + my_snprintf(fqdev, len, "i(@%c.%s%s.%s[%s])", prefix, path, instname, dev, param ? param : "ic"); + } else if(prefix == 'd' || prefix == 'm') { + my_snprintf(fqdev, len, "i(@%c.%s%s.%s[%s])", prefix, path, instname, dev, param ? param : "id"); + } else if(prefix == 'i') { + my_snprintf(fqdev, len, "i(@%c.%s%s.%s[current])", prefix, path, instname, dev); + } else { + my_snprintf(fqdev, len, "i(@%c.%s%s.%s[i])", prefix, path, instname, dev); + } + } else { + my_snprintf(fqdev, len, "i(%s%s.%s)", path, instname, dev); + } + strtolower(fqdev); + dbg(1, "fqdev=%s\n", fqdev); + idx = get_raw_index(fqdev, NULL); + if(idx >= 0) { + val = xctx->raw->cursor_b_val[idx]; + } + if(idx < 0) { + valstr = "-"; + xctx->tok_size = 1; + len = 1; + } else { + /* always use engineering as these tokens are generated from single + * @spice_get_voltage patterns */ + valstr = dtoa_eng(val); + len = xctx->tok_size; + } + if(len) { + STR_ALLOC(&result, len + result_pos, &size); + memcpy(result+result_pos, valstr, len+1); + result_pos += len; + } + dbg(1, "instname %s, dev=%s, fqdev=%s idx=%d valstr=%s\n", instname, dev, fqdev, idx, valstr); + my_free(_ALLOC_ID_, &fqdev); + } /* if(n == 1) */ + if(param) my_free(_ALLOC_ID_, ¶m); + my_free(_ALLOC_ID_, &dev); + } /* if(path) */ + } /* if((start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) */ + } + else if(inst >= 0 && strcmp(token,"@spice_get_diff_voltage")==0 && xctx->inst[inst].ptr >= 0) + { + int start_level; /* hierarchy level where waves were loaded */ + int live = tclgetboolvar("live_cursor2_backannotate"); + if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { + int multip; + int no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; + if(no_of_pins == 2) { + char *fqnet1 = NULL, *fqnet2 = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + const char *net1, *net2; + size_t len; + int idx1, idx2; + double val = 0.0, val1 = 0.0, val2 = 0.0; + const char *valstr; + if(path) { + int gnd1 = 0, gnd2 = 0; + int skip = 0; + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; + } + prepare_netlist_structs(0); + net1 = net_name(inst, 0, &multip, 0, 0); + len = strlen(path) + strlen(net1) + 1; + dbg(1, "net1=%s\n", net1); + fqnet1 = my_malloc(_ALLOC_ID_, len); + my_snprintf(fqnet1, len, "%s%s", path, net1); + strtolower(fqnet1); + net2 = net_name(inst, 1, &multip, 0, 0); + len = strlen(path) + strlen(net2) + 1; + dbg(1, "net2=%s\n", net2); + fqnet2 = my_malloc(_ALLOC_ID_, len); + my_snprintf(fqnet2, len, "%s%s", path, net2); + strtolower(fqnet2); + dbg(1, "translate(): fqnet1=%s start_level=%d\n", fqnet1, start_level); + dbg(1, "translate(): fqnet2=%s start_level=%d\n", fqnet2, start_level); + if(!strcmp(fqnet1, "0") || !my_strcasecmp(fqnet1, "GND")) gnd1 = 1; + if(!strcmp(fqnet2, "0") || !my_strcasecmp(fqnet2, "GND")) gnd2 = 1; + idx1 = get_raw_index(fqnet1, NULL); + idx2 = get_raw_index(fqnet2, NULL); + if( (!gnd1 && idx1 < 0) || (!gnd2 && idx2 < 0) ) { + valstr = "-"; + xctx->tok_size = 1; + len = 1; + } else { + double val1 = gnd1 ? 0.0 : xctx->raw->cursor_b_val[idx1]; + double val2 = gnd2 ? 0.0 : xctx->raw->cursor_b_val[idx2]; + val = val1 - val2; + valstr = engineering ? dtoa_eng(val) : dtoa(val); + len = xctx->tok_size; + } + if(len) { + STR_ALLOC(&result, len + result_pos, &size); + memcpy(result+result_pos, valstr, len+1); + result_pos += len; + } + dbg(1, "inst %d, fqnet1=%s fqnet2=%s idx1=%d idx2=%d, val1=%g val2=%g valstr=%s\n", + inst, fqnet1, fqnet2, idx1, idx2, val1, val2, valstr); + my_free(_ALLOC_ID_, &fqnet1); + my_free(_ALLOC_ID_, &fqnet2); + } + } + } + } + else if( + strncmp(token,"@spice_get_current", 18)==0 || + strncmp(token,"@spice_get_modelparam", 21)==0 || + strncmp(token,"@spice_get_modelvoltage", 23)==0 + ) + { + int start_level; /* hierarchy level where waves were loaded */ + int live = tclgetboolvar("live_cursor2_backannotate"); + if(live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) { + char *fqdev = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + char *dev = NULL, *param = NULL; + int modelparam = 0; /* 0: current, 1: modelparam, 2: modelvoltage */ + size_t len; + int idx; + int error = 0; + double val = 0.0; + const char *valstr; + if(path) { + int skip = 0; + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; + } + /* token contans _param after @spice_get_current or @spice_get_modelparam + * or @spice_get_modelvoltage */ + if(strcmp(token, "@spice_get_current") && + strcmp(token, "@spice_get_modelparam") && + strcmp(token, "@spice_get_modelvoltage")) { + int n = 0; + param = my_malloc(_ALLOC_ID_, strlen(token) + 1); + n = sscanf(token, "@spice_get_current_%s", param); + if(n == 0) { + n = sscanf(token, "@spice_get_modelparam_%s", param); + modelparam = 1; + } + if(n == 0) { + n = sscanf(token, "@spice_get_modelvoltage_%s", param); + modelparam = 2; + } + if(n == 0) { + my_free(_ALLOC_ID_, ¶m); + error = 1; + } + } + if(!error) { + char *iprefix = modelparam == 0 ? "i(" : modelparam == 1 ? "" : "v("; + char *ipostfix = modelparam == 1 ? "" : ")"; + int prefix; + my_strdup2(_ALLOC_ID_, &dev, instname); + strtolower(dev); + prefix=dev[0]; + len = strlen(path) + strlen(dev) + 40; /* some extra chars for i(..) wrapper */ + dbg(1, "token=%s, dev=%s param=%s\n", token, dev, param ? param : ""); + fqdev = my_malloc(_ALLOC_ID_, len); + if(!sim_is_xyce) { + int vsource = (prefix == 'v') || (prefix == 'e'); + if(path[0]) { + if(vsource) { + my_snprintf(fqdev, len, "i(%c.%s%s)", prefix, path, dev); + } else if(prefix=='q') { + my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", + iprefix, prefix, path, dev, param ? param : "ic", ipostfix); + } else if(prefix=='d' || prefix == 'm') { + my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", + iprefix, prefix, path, dev, param ? param : "id", ipostfix); + } else if(prefix=='i') { + my_snprintf(fqdev, len, "i(@%c.%s%s[current])", prefix, path, dev); + } else { + my_snprintf(fqdev, len, "i(@%c.%s%s[i])", prefix, path, dev); + } + } else { + if(vsource) { + my_snprintf(fqdev, len, "i(%s)", dev); + } else if(prefix == 'q') { + my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "ic", ipostfix); + } else if(prefix == 'd' || prefix == 'm') { + my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "id", ipostfix); + } else if(prefix == 'i') { + my_snprintf(fqdev, len, "i(@%s[current])", dev); + } else { + my_snprintf(fqdev, len, "i(@%s[i])", dev); + } + } + } else { + my_snprintf(fqdev, len, "i(%s%s)", path, dev); + } + if(param) my_free(_ALLOC_ID_, ¶m); + dbg(1, "fqdev=%s\n", fqdev); + strtolower(fqdev); + idx = get_raw_index(fqdev, NULL); + if(idx >= 0) { + val = xctx->raw->cursor_b_val[idx]; + } + /* special handling for resistors that are converted to b sources: + * i(@r.x4.r1[i]) --> i(@b.x4.br1[i]) + */ + if(idx < 0 && !strncmp(fqdev, "i(@r", 4)) { + if(path[0]) { + my_snprintf(fqdev, len, "i(@b.%sb%s[i])", path, dev); + } else { + my_snprintf(fqdev, len, "i(@b%s[i])", dev); + } + dbg(1, "fqdev=%s\n", fqdev); + idx = get_raw_index(fqdev, NULL); + if(idx >= 0) { + val = xctx->raw->cursor_b_val[idx]; + } + } + if(idx < 0) { + valstr = "-"; + xctx->tok_size = 1; + len = 1; + } else { + valstr = engineering ? dtoa_eng(val) : dtoa(val); + len = xctx->tok_size; + } + if(len) { + STR_ALLOC(&result, len + result_pos, &size); + memcpy(result+result_pos, valstr, len+1); + result_pos += len; + } + dbg(1, "instname %s, dev=%s, fqdev=%s idx=%d valstr=%s\n", instname, dev, fqdev, idx, valstr); + my_free(_ALLOC_ID_, &fqdev); + my_free(_ALLOC_ID_, &dev); + } /* if(!error) */ + } /* if(path) */ + } /* (live && (start_level = sch_waves_loaded()) >= 0 && xctx->raw->annot_p>=0) */ + } + else if(strcmp(token,"@schvhdlprop")==0 && xctx->schvhdlprop) + { + tmp=strlen(xctx->schvhdlprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schvhdlprop, tmp+1); + result_pos+=tmp; + } + + else if(strcmp(token,"@schprop")==0 && xctx->schprop) + { + tmp=strlen(xctx->schprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schprop, tmp+1); + result_pos+=tmp; + } + /* /20100217 */ + + else if(strcmp(token,"@schsymbolprop")==0 && xctx->schsymbolprop) + { + tmp=strlen(xctx->schsymbolprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schsymbolprop, tmp+1); + result_pos+=tmp; + } + else if(strcmp(token,"@schtedaxprop")==0 && xctx->schtedaxprop) + { + tmp=strlen(xctx->schtedaxprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schtedaxprop, tmp+1); + result_pos+=tmp; + } + /* /20100217 */ + + else if(strcmp(token,"@schverilogprop")==0 && xctx->schverilogprop) + { + tmp=strlen(xctx->schverilogprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schverilogprop, tmp+1); + result_pos+=tmp; + /* if spiceprefix==0 and token == @spiceprefix then set empty value */ + } else if(!sp_prefix && !strcmp(token, "@spiceprefix")) { + /* add nothing */ + } else if(inst >= 0) { + value = get_tok_value(xctx->inst[inst].prop_ptr, token+1, 0); + if(!xctx->tok_size && xctx->inst[inst].ptr >= 0) { + value=get_tok_value(xctx->sym[xctx->inst[inst].ptr].templ, token+1, 0); + } + if(!xctx->tok_size) { /* above lines did not find a value for token */ + if(token[0] =='%') { + /* no definition found -> subst with token without leading % */ + tmp=token_pos -1 ; /* we need token_pos -1 chars, ( strlen(token+1) ) , excluding leading '%' */ + STR_ALLOC(&result, tmp + result_pos, &size); + /* dbg(2, "translate(): token=%s, token_pos = %d\n", token, token_pos); */ + memcpy(result+result_pos, token + 1, tmp+1); + result_pos+=tmp; + } + } else { + int i = level; + my_strdup2(_ALLOC_ID_, &value1, value); + /* recursive substitution of value using parent level prop_ptr attributes */ + while(i > 0) { + char *v = value1; + const char *tok; + if(v && v[0] == '@') v++; + tok = get_tok_value(lcc[i-1].prop_ptr, v, 0); + if(xctx->tok_size && tok[0]) { + dbg(1, "tok=%s\n", tok); + my_strdup2(_ALLOC_ID_, &value1, tok); + } else { + tok = get_tok_value(lcc[i-1].templ, v, 0); + if(xctx->tok_size && tok[0]) { + dbg(1, "from parent template: tok=%s\n", tok); + my_strdup2(_ALLOC_ID_, &value1, tok); + } + } + dbg(1, "2 translate(): lcc[%d].prop_ptr=%s, value1=%s\n", i-1, lcc[i-1].prop_ptr, value1); + i--; + } + tmp=strlen(value1); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos, value1, tmp+1); + result_pos+=tmp; + my_free(_ALLOC_ID_, &value1); + } + } + token_pos = 0; + if(c == '@' || c == '%') s--; + else result[result_pos++]=(char)c; + state=TOK_BEGIN; + } /* else if(state==TOK_SEP) */ + else if(state==TOK_BEGIN) result[result_pos++]=(char)c; + if(c=='\0') + { + result[result_pos]='\0'; + break; + } + } /* while(1) */ + dbg(2, "translate(): returning %s\n", result); + my_free(_ALLOC_ID_, &token); + /* resolve spice_get_node patterns. + * if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions + * can be calculated */ + my_strdup2(_ALLOC_ID_, &result, spice_get_node(tcl_hook2(result))); + + if(strstr(result, "expr(")) { + dbg(1, "translate(): expr():%s\n", result); + my_strdup2(_ALLOC_ID_, &result, eval_expr( + translate3(result, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); } - } /* while(1) */ - dbg(2, "translate(): returning %s\n", result); - my_free(_ALLOC_ID_, &token); - /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions - * can be calculated */ - my_strdup2(_ALLOC_ID_, &translated_tok, spice_get_node(tcl_hook2(result))); - - if(strstr(translated_tok, "expr(")) { - dbg(1, "translate(): expr():%s\n", translated_tok); - my_strdup2(_ALLOC_ID_, &translated_tok, eval_expr( - translate3(translated_tok, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); - } - - return translated_tok; + return result; } const char *translate2(Lcc *lcc, int level, char* s) From 0024dc2759c009b9b7a43a7522a4bd0fa324f781 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Feb 2025 14:23:58 +0100 Subject: [PATCH 12/27] more comments and refactoring in token.c --- src/spice_netlist.c | 2 ++ src/token.c | 18 ++---------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/spice_netlist.c b/src/spice_netlist.c index 6e17771d..7c864c63 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -455,11 +455,13 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ if(lvs_ignore && (xctx->sym[i].flags & LVS_IGNORE)) continue; if(!xctx->sym[i].type) continue; /* store parent symbol template attr (before descending into it) and parent instance prop_ptr + * into xctx->hier_attr[0].templ and xctx->hier_attr[0.prop_ptr, * to resolve subschematic instances with model=@modp in format string, * modp will be first looked up in instance prop_ptr string, and if not found * in parent symbol template string */ my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch - 1].templ, tcl_hook2(xctx->sym[i].templ)); + /* only additional symbols (created with instance schematic=... attr) will have this attribute */ my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch - 1].prop_ptr, tcl_hook2(xctx->sym[i].parent_prop_ptr)); my_strdup(_ALLOC_ID_, &abs_path, abs_sym_path(xctx->sym[i].name, "")); diff --git a/src/token.c b/src/token.c index 0d304135..e0d1482a 100644 --- a/src/token.c +++ b/src/token.c @@ -2212,14 +2212,6 @@ int print_spice_element(FILE *fd, int inst) token[token_pos]='\0'; token_pos=0; - - - - - - - - if(strcmp(token,"@symref")==0) { const char *s = get_sym_name(inst, 9999, 1, 0); @@ -2376,9 +2368,6 @@ int print_spice_element(FILE *fd, int inst) /* else tcl var spiceprefix is enabled */ } - - - else { /* here a @token in format string will be replaced by value in instance prop_ptr * or symbol template */ @@ -4662,7 +4651,6 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, const cha size_t token_pos=0; const char *value; int escape=0; - char *value1 = NULL; const char *escape_pos = NULL; @@ -4684,7 +4672,7 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, const cha } space=SPACE(c); - if( state==TOK_BEGIN && (c=='@' || c=='%' ) && !escape ) state=TOK_TOKEN; /* 20161210 escape */ + if( state==TOK_BEGIN && (c=='@' || c=='%' ) && !escape ) state=TOK_TOKEN; else if(state==TOK_TOKEN && token_pos > 1 && ( ( (space || c == '%' || c == '@') && !escape ) || @@ -4712,9 +4700,7 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, const cha /* no definition found -> keep token */ my_strcat(_ALLOC_ID_, &result, token); } else { - my_strdup2(_ALLOC_ID_, &value1, value); - my_strcat(_ALLOC_ID_, &result, value1); - my_free(_ALLOC_ID_, &value1); + my_strcat(_ALLOC_ID_, &result, value); } token_pos = 0; if(c == '@' || c == '%') s--; /* these token separators are also identifiers for next token: push them back */ From 0b3db90f1986f8aa2442ee1386f62a7eebd7c587 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Feb 2025 14:39:21 +0100 Subject: [PATCH 13/27] one more argument to translate3() --- src/actions.c | 6 +++--- src/draw.c | 4 ++-- src/editprop.c | 2 +- src/scheduler.c | 6 +++--- src/spice_netlist.c | 2 +- src/token.c | 17 +++++++++++------ src/xinit.c | 2 +- src/xschem.h | 3 ++- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/actions.c b/src/actions.c index 6cd692be..303b904a 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1994,7 +1994,7 @@ void get_additional_symbols(int what) /* resolve schematic=generator.tcl( @n ) where n=11 is defined in instance attrs */ my_strdup2(_ALLOC_ID_, &sch, translate3(get_tok_value(xctx->inst[i].prop_ptr,"schematic", 6), 1, - xctx->inst[i].prop_ptr, NULL, NULL)); + xctx->inst[i].prop_ptr, NULL, NULL, NULL)); dbg(1, "sch=%s\n", sch); my_strdup2(_ALLOC_ID_, &sch, tcl_hook2( @@ -2034,7 +2034,7 @@ void get_additional_symbols(int what) my_strdup(_ALLOC_ID_, &spice_sym_def, translate3(spice_sym_def, 1, xctx->inst[i].prop_ptr, symptr->templ, - symname_attr)); + symname_attr, NULL)); dbg(1, "get_additional_symbols(): spice_sym_def=%s\n", spice_sym_def); my_free(_ALLOC_ID_, &symname_attr); /* if instance symbol has default_schematic set to ignore copy the symbol anyway, since @@ -2128,7 +2128,7 @@ void get_sch_from_sym(char *filename, xSymbol *sym, int inst, int fallback) /* resolve schematic=generator.tcl( @n ) where n=11 is defined in instance attrs */ if(inst >=0 ) { my_strdup(_ALLOC_ID_, &str_tmp, translate3(get_tok_value(xctx->inst[inst].prop_ptr,"schematic", 6), - 1, xctx->inst[inst].prop_ptr, NULL, NULL)); + 1, xctx->inst[inst].prop_ptr, NULL, NULL, NULL)); } if(!str_tmp) my_strdup2(_ALLOC_ID_, &str_tmp, get_tok_value(sym->prop_ptr, "schematic", 6)); if(str_tmp[0]) { /* schematic attribute in symbol or instance was given */ diff --git a/src/draw.c b/src/draw.c index b6c5fe6e..f18e089c 100644 --- a/src/draw.c +++ b/src/draw.c @@ -736,7 +736,7 @@ void draw_symbol(int what,int c, int n,int layer,short tmp_flip, short rot, dbg(1, "draw_symbol(): drawing string: str=%s prop=%s\n", txtptr, text.prop_ptr ? text.prop_ptr : ""); my_strdup2(_ALLOC_ID_, &txtptr, translate3(txtptr, 1, xctx->inst[n].prop_ptr, - xctx->sym[xctx->inst[n].ptr].templ, NULL )); + xctx->sym[xctx->inst[n].ptr].templ, NULL, NULL)); dbg(1, "draw_symbol(): after translate3: str=%s\n", txtptr); draw_string(textlayer, what, txtptr, (text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3, @@ -906,7 +906,7 @@ void draw_temp_symbol(int what, GC gc, int n,int layer,short tmp_flip, short rot my_strdup2(_ALLOC_ID_, &txtptr, translate(n, text.txt_ptr)); /* do another round of substitutions if some @var are found, but if not found leave @var as is */ my_strdup2(_ALLOC_ID_, &txtptr, translate3(txtptr, 1, xctx->inst[n].prop_ptr, - xctx->sym[xctx->inst[n].ptr].templ, NULL )); + xctx->sym[xctx->inst[n].ptr].templ, NULL, NULL)); dbg(1, "draw_temp_symbol(): after translate3: str=%s\n", txtptr); if(txtptr[0]) draw_temp_string(gc, what, txtptr, (text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3, diff --git a/src/editprop.c b/src/editprop.c index f40a65a4..44a5d8e0 100644 --- a/src/editprop.c +++ b/src/editprop.c @@ -1475,7 +1475,7 @@ int drc_check(int i) my_strdup(_ALLOC_ID_, &drc, get_tok_value(xctx->sym[xctx->inst[j].ptr].prop_ptr, "drc", 2)); if(drc) { my_strdup(_ALLOC_ID_, &res, translate3(drc, 1, - xctx->inst[j].prop_ptr, xctx->sym[xctx->inst[j].ptr].templ, NULL)); + xctx->inst[j].prop_ptr, xctx->sym[xctx->inst[j].ptr].templ, NULL, NULL)); dbg(1, "drc_check(): res = |%s|, drc=|%s|\n", res, drc); if(res) { const char *result; diff --git a/src/scheduler.c b/src/scheduler.c index 913a5aac..48059963 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -6068,9 +6068,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg int eat_escapes = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) eat_escapes = atoi(argv[3]); - if(argc > 6) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], argv[5], argv[6])); - else if(argc > 5) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], argv[5], NULL)); - else if(argc > 4) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], NULL, NULL)); + if(argc > 6) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], argv[5], argv[6], NULL)); + else if(argc > 5) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], argv[5], NULL, NULL)); + else if(argc > 4) my_strdup2(_ALLOC_ID_, &s, translate3(argv[2], eat_escapes, argv[4], NULL, NULL, NULL)); else { Tcl_SetResult(interp, "xschem translate3: missing arguments", TCL_STATIC); return TCL_ERROR; diff --git a/src/spice_netlist.c b/src/spice_netlist.c index 7c864c63..21d80d05 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -645,7 +645,7 @@ int spice_block_netlist(FILE *fd, int i, int alert) char *symname_attr = NULL; const char *translated_sym_def; my_mstrcat(_ALLOC_ID_, &symname_attr, "symname=", get_cell(name, 0), NULL); - translated_sym_def = translate3(sym_def, 1, "", xctx->sym[i].templ, symname_attr); + translated_sym_def = translate3(sym_def, 1, xctx->sym[i].templ, symname_attr, NULL, NULL); my_free(_ALLOC_ID_, &symname_attr); fprintf(fd, "%s\n", translated_sym_def); my_free(_ALLOC_ID_, &sym_def); diff --git a/src/token.c b/src/token.c index e0d1482a..01f84350 100644 --- a/src/token.c +++ b/src/token.c @@ -1887,7 +1887,7 @@ static int has_included_subcircuit(int inst, int symbol, char **result) my_mstrcat(_ALLOC_ID_, &symname_attr, " symref=", get_sym_name(inst, 9999, 1, 1), NULL); translated_sym_def = translate3(spice_sym_def, 1, xctx->inst[inst].prop_ptr, xctx->sym[symbol].templ, - symname_attr); + symname_attr, NULL); dbg(1, "has_included_subcircuit(): translated_sym_def=%s\n", translated_sym_def); dbg(1, "has_included_subcircuit(): symname=%s\n", symname); @@ -2397,7 +2397,7 @@ int print_spice_element(FILE *fd, int inst) parent_templ = xctx->hier_attr[xctx->currsch - 1].templ; } dbg(1, "print_spice_element(): before translate3(): value=%s\n", value); - value = translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ); + value = translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ, NULL); dbg(1, "print_spice_element(): after translate3(): value=%s\n", value); } tok_val_len = strlen(value); @@ -2412,7 +2412,7 @@ int print_spice_element(FILE *fd, int inst) if(strstr(value, "expr(") ) { my_strdup2(_ALLOC_ID_, &val, value); - value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL)); + value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL, NULL)); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ if(!token_exists && token[0] =='%') { @@ -4493,7 +4493,8 @@ const char *translate(int inst, const char* s) if(strstr(result, "expr(")) { dbg(1, "translate(): expr():%s\n", result); my_strdup2(_ALLOC_ID_, &result, eval_expr( - translate3(result, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, NULL))); + translate3(result, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, + NULL, NULL))); } return result; } @@ -4640,7 +4641,8 @@ const char *translate2(Lcc *lcc, int level, char* s) /* using s1, s2, s3 in turn to resolve @tokens */ /* if no definition for @token is found return @token as is in s */ /* if s==NULL return emty string */ -const char *translate3(const char *s, int eat_escapes, const char *s1, const char *s2, const char *s3) +const char *translate3(const char *s, int eat_escapes, const char *s1, + const char *s2, const char *s3, const char *s4) { static const char *empty=""; static char *translated_tok = NULL; @@ -4659,7 +4661,7 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, const cha my_free(_ALLOC_ID_, &translated_tok); return empty; } - dbg(2, "translate3():\n s=%s\n s1=%s\n s2=%s\n s3=%s\n", s, s1, s2, s3); + dbg(2, "translate3():\n s=%s\n s1=%s\n s2=%s\n s3=%s s4=%s\n", s, s1, s2, s3, s4); my_strdup2(_ALLOC_ID_, &result, ""); while(1) { @@ -4696,6 +4698,9 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, const cha if(!xctx->tok_size && s3) { value=get_tok_value(s3, token+1, 0); } + + + if(!xctx->tok_size) { /* above lines did not find a value for token */ /* no definition found -> keep token */ my_strcat(_ALLOC_ID_, &result, token); diff --git a/src/xinit.c b/src/xinit.c index 9d8438a6..b4cc86a0 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -946,7 +946,7 @@ static void xwin_exit(void) list_tokens(NULL, 0); /* clear static data in function */ translate(-1, NULL); /* clear static data in function */ translate2(NULL, 0, NULL); /* clear static data in function */ - translate3(NULL, 0, NULL, NULL, NULL); /* clear static data in function */ + translate3(NULL, 0, NULL, NULL, NULL, NULL); /* clear static data in function */ subst_token(NULL, NULL, NULL); /* clear static data in function */ find_nth(NULL, "", "", 0, 0); /* clear static data in function */ trim_chars(NULL, ""); /* clear static data in function */ diff --git a/src/xschem.h b/src/xschem.h index 34423304..5f988205 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1609,7 +1609,8 @@ extern int isonlydigit(const char *s); extern const char *spice_get_node(const char *token); extern const char *translate(int inst, const char* s); extern const char* translate2(Lcc *lcc, int level, char* s); -extern const char *translate3(const char* s, int eat_escapes, const char *s1, const char *s2, const char *s3); +extern const char *translate3(const char* s, int eat_escapes, const char *s1, + const char *s2, const char *s3, const char *s4); extern void print_tedax_element(FILE *fd, int inst); extern int print_spice_element(FILE *fd, int inst); extern void print_spice_subckt_nodes(FILE *fd, int symbol); From 188e73cf8b6fc4aed58194b232a7a868eeb32336 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 02:46:45 +0100 Subject: [PATCH 14/27] place_symbol() and proc is_xschem_file: if /path/to/gen.tcl(arg1,arg2,arg3) is given remove (...) argument list before checking existence of generator file. Do not add () to generator filename if argument list already given. Do not include hidden texts in schematic boundbox for full zoom if show hidden_texts is not enabled. draw_graph(): do not wrap graphs using sweepvar_wrap if not dc (this includes: do not wrap multi-point OP sims). print_spice_element(): better @param lookup in instance prop_ptr, symbol template, parent instance prop_ptr (if instance based "additional" symbol) and parent instance symbol template string (use translate3()) --- src/actions.c | 21 ++- src/draw.c | 9 +- src/scheduler.c | 2 +- src/token.c | 172 +++++++++++------- src/xschem.tcl | 1 + xschem_library/devices/simulator_commands.sym | 4 +- .../devices/simulator_commands_shown.sym | 4 +- 7 files changed, 127 insertions(+), 86 deletions(-) diff --git a/src/actions.c b/src/actions.c index 303b904a..e0432572 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1495,7 +1495,9 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot tclvareval("is_xschem_file {", name1, "}", NULL); if(!strcmp(tclresult(), "GENERATOR")) { - my_snprintf(name, S(name), "%s()", name1); + size_t len = strlen(name1); + if( name1[len - 1] != ')') my_snprintf(name, S(name), "%s()", name1); + else my_strncpy(name, name1, S(name)); } else { my_strncpy(name, name1, S(name)); } @@ -1983,31 +1985,32 @@ void get_additional_symbols(int what) char *default_schematic = NULL; char *sch = NULL; char symbol_base_sch[PATH_MAX] = ""; + size_t schematic_token_found = 0; if(xctx->inst[i].ptr < 0) continue; + dbg(1, "get_additional_symbols(): inst=%d (%s) sch=%s\n",i, xctx->inst[i].name, sch); /* copy instance based *_sym_def attributes to symbol */ my_strdup(_ALLOC_ID_, &spice_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"spice_sym_def",6)); my_strdup(_ALLOC_ID_, &verilog_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"verilog_sym_def",4)); my_strdup(_ALLOC_ID_, &vhdl_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"vhdl_sym_def",4)); - dbg(1, "schematic=%s\n", get_tok_value(xctx->inst[i].prop_ptr,"schematic",6)); + dbg(1, "get_additional_symbols(): schematic=%s\n", get_tok_value(xctx->inst[i].prop_ptr,"schematic",6)); /* resolve schematic=generator.tcl( @n ) where n=11 is defined in instance attrs */ - my_strdup2(_ALLOC_ID_, &sch, - translate3(get_tok_value(xctx->inst[i].prop_ptr,"schematic", 6), 1, - xctx->inst[i].prop_ptr, NULL, NULL, NULL)); - dbg(1, "sch=%s\n", sch); + my_strdup2(_ALLOC_ID_, &sch, get_tok_value(xctx->inst[i].prop_ptr,"schematic", 6)); + schematic_token_found = xctx->tok_size; + my_strdup2(_ALLOC_ID_, &sch, translate3(sch, 1, xctx->inst[i].prop_ptr, NULL, NULL, NULL)); + dbg(1, "get_additional_symbols(): sch=%s tok_size= %ld\n", sch, xctx->tok_size); my_strdup2(_ALLOC_ID_, &sch, tcl_hook2( str_replace(sch, "@symname", get_cell(xctx->inst[i].name, 0), '\\', -1))); - dbg(1, "get_additional_symbols(): inst=%d sch=%s\n",i, sch); /* schematic does not exist */ if(sch[0] && stat(abs_sym_path(sch, ""), &buf)) { my_snprintf(symbol_base_sch, PATH_MAX, "%s.sch", get_cell(xctx->sym[xctx->inst[i].ptr].name, 9999)); dbg(1, "get_additional_symbols(): schematic not existing\n"); dbg(1, "using: %s\n", symbol_base_sch); } - if(xctx->tok_size && sch[0]) { /* "schematic" token exists and a schematic is specified */ + if(schematic_token_found && sch[0]) { /* "schematic" token exists and a schematic is specified */ int j; char *sym = NULL; char *symname_attr = NULL; @@ -2685,6 +2688,8 @@ void calc_drawing_bbox(xRect *boundbox, int selected) int no_of_lines; double longest_line; if(selected == 1 && !xctx->text[i].sel) continue; + + if(!xctx->show_hidden_texts && xctx->text[i].flags & (HIDE_TEXT | HIDE_TEXT_INSTANTIATED)) continue; #if HAS_CAIRO==1 customfont = set_text_custom_font(&xctx->text[i]); #endif diff --git a/src/draw.c b/src/draw.c index f18e089c..34d7d8f7 100644 --- a/src/draw.c +++ b/src/draw.c @@ -2543,7 +2543,7 @@ int graph_fullyzoom(xRect *r, Graph_ctx *gr, int graph_dataset) if(gr->logx) xx = mylog10(gv[p]); else xx = gv[p]; if(p == ofs) xx0 = gv0[p]; - wrap = (cnt > 1 && gv0[p] == xx0); + wrap = !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0; if(wrap) { sweepvar_wrap++; cnt = 0; @@ -3568,7 +3568,7 @@ int calc_custom_data_yrange(int sweep_idx, const char *express, Graph_ctx *gr) xx = gv[p]; if(p == ofs) xx0 = gv0[p]; - wrap = ( cnt > 1 && gv0[p] == xx0); + wrap = !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0; if(first != -1) { /* there is something to plot ... */ if(xx > end || xx < start || /* ... and we ran out of graph area ... */ wrap) { /* ... or sweep variable changed direction */ @@ -3713,7 +3713,7 @@ int find_closest_wave(int i, Graph_ctx *gr) if(gr->logy) yy = mylog10(gvy[p]); else yy = gvy[p]; if(p == ofs) xx0 = gv0[p]; - wrap = (cnt > 1 && gv0[p] == xx0); + wrap = !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0; if(first != -1) { if(xx > end || xx < start || wrap) { dbg(1, "find_closest_wave(): last=%d\n", last); @@ -4026,7 +4026,6 @@ void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct) /* optimization: skip unwanted datasets, if no dc no need to detect sweep variable wraps */ if(dataset >= 0 && strcmp(xctx->raw->sim_type, "dc") && dataset != sweepvar_wrap) goto done; - for(p = ofs ; p < ofs_end; p++) { double xxprevious, xxfollowing; @@ -4038,7 +4037,7 @@ void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct) * are simulated and thos no equality test can be done, and any "approx equal" test si going * to do unexpected things (liek in simulations with very dense steps) */ if(p == ofs) xx0 = gv0[p]; /* gv[p];*/ - wrap = cnt > 1 && gv0[p] == xx0; + wrap = !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0; #if 1 /* plot one point before start and one point after end so * waves will extend to whole graph area even if there are few points * but NOT if we are about to wrap (missing 1st/last point in 2-var dc sweeps) */ diff --git a/src/scheduler.c b/src/scheduler.c index 48059963..69413af3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -2742,7 +2742,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg my_free(_ALLOC_ID_, &pins); } - /* is_symgen symbol + /* is_generator symbol * tell if 'symbol' is a generator (symbol(param1,param2,...) */ else if(!strcmp(argv[1], "is_generator")) { diff --git a/src/token.c b/src/token.c index 01f84350..4a067718 100644 --- a/src/token.c +++ b/src/token.c @@ -2372,37 +2372,69 @@ int print_spice_element(FILE *fd, int inst) /* here a @token in format string will be replaced by value in instance prop_ptr * or symbol template */ size_t tok_val_len; - size_t tok_size; + char *parent_prop_ptr = NULL; + char *parent_templ = NULL; + + if(xctx->currsch > 0) { + parent_prop_ptr = xctx->hier_attr[xctx->currsch - 1].prop_ptr; + parent_templ = xctx->hier_attr[xctx->currsch - 1].templ; + } dbg(1, "print_spice_element(): token: |%s|\n", token); - my_strdup2(_ALLOC_ID_, &val, get_tok_value(xctx->inst[inst].prop_ptr, token+1, 0)); - /* xctx->tok_size==0 indicates that token(+1) does not exist in instance attributes - * so try to get from symbol template */ - if (!xctx->tok_size) { - my_strdup2(_ALLOC_ID_, &val, get_tok_value(template, token+1, 0)); - } - token_exists = xctx->tok_size; - - tok_size = xctx->tok_size; - value = val; - if(strchr(value, '@')) { - /* Symbol format string contains model=@modp, - * resolve @modp looking in instance attributes ... */ - char *parent_prop_ptr = NULL; - char *parent_templ = NULL; - - if(xctx->currsch > 0) { - /* ... look up modp also in **parent** instance prop_ptr and symbol template attribute */ - parent_prop_ptr = xctx->hier_attr[xctx->currsch - 1].prop_ptr; - parent_templ = xctx->hier_attr[xctx->currsch - 1].templ; + /* consider this scenario: + * instance of passgate.sym: W_N=5 L_N=0.2 W_P=10 L_P=0.3 m=1 + * instance based schematic (schematic=mypippo attr) will have also modeln=pippon + * passgate.sym: + * format="@name @pinlist @symname W_N=@W_N L_N=@L_N W_P=@W_P L_P=@L_P m=@m" + * template=" ... modeln=nfet_01v8 modelp=pfet_01v8 m=1" + * passgate.sch: + * instance of nmos.sym: L=L_N W=W_N nf=1 m=1 model=@modeln + * nmos.sym: + * format="@name @pinlist @model L=@L W=@W nf=@nf + * + ad=@ad as=@as pd=@pd .... m=@m + * template="name=M1 W=1 L=0.15 m=1 + * ad=\"expr('int((@nf + 1)/2) * @W / @nf * 0.29')\" + * ..." + * model=nfet_01v8 + */ + my_strdup2(_ALLOC_ID_, &val, + translate3(token, 2, xctx->inst[inst].prop_ptr, parent_prop_ptr, template, NULL)); + /* nmos instance format string: @model --> @modeln */ + dbg(1, "print_spice_element(): 1st round: val: |%s|\n", val); + if(strchr(val, '@')) { + if(parent_prop_ptr) { + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ, NULL)); + /* instance based passgate.sym placement, nmos instance format string: @modeln --> pippon */ + /* ad="expr('int((@nf + 1)/2) * @W / @nf * 0.29')" --> ad="expr('int((1 + 1)/2) * W_N / 1 * 0.29')" */ + if(strchr(val, '@')) { + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ, NULL)); + /* ad="expr('int((1 + 1)/2) * W_N / 1 * 0.29')" --> ad="expr('int((1 + 1)/2) * 5 / 1 * 0.29')" */ + } + } else { + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, NULL, NULL, NULL)); + dbg(1, "print_spice_element(): 2nd round: val: |%s|\n", val); + /* normal passgate.sym placement, nmos instance format string: + ad="expr('int((@nf + 1)/2) * @W / @nf * 0.29')" --> ad="expr('int((1 + 1)/2) * W_N/ 1 * 0.29')" */ + if(strchr(val, '@')) { + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, parent_templ, NULL, NULL)); + dbg(1, "print_spice_element(): 3nd round: val: |%s|\n", val); + /* normal passgate.sym placement, nmos instance format string: + * @modeln --> nfet_01v8 */ + } } - dbg(1, "print_spice_element(): before translate3(): value=%s\n", value); - value = translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, parent_templ, NULL); - dbg(1, "print_spice_element(): after translate3(): value=%s\n", value); + dbg(1, "print_spice_element(): final: val: |%s|\n", val); } + value = val; + /* xctx->tok_size==0 (set in translate3()) indicates that token(+1) does not exist + * in instance attributes so try to get from symbol template */ + dbg(1, "print_spice_element(): val: %s\n", val); + token_exists = xctx->tok_size; + value = val; tok_val_len = strlen(value); - xctx->tok_size = tok_size; - /* @spiceprefix needs a special tag for postprocessing */ if(!strcmp(token, "@spiceprefix") && value[0]) { my_realloc(_ALLOC_ID_, &spiceprefixtag, tok_val_len+22); @@ -2411,8 +2443,7 @@ int print_spice_element(FILE *fd, int inst) } if(strstr(value, "expr(") ) { - my_strdup2(_ALLOC_ID_, &val, value); - value = eval_expr(translate3(val, 1, xctx->inst[inst].prop_ptr, template, NULL, NULL)); + value = eval_expr(value); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ if(!token_exists && token[0] =='%') { @@ -2463,25 +2494,14 @@ int print_spice_element(FILE *fd, int inst) /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions * can be calculated */ - - /* do one level of substitutions to resolve remaining @params and/or tcl expr/code */ if(result) { - dbg(1, "print_spice_element(): before translate() result=%s\n", result); - if(!strcmp(xctx->sym[xctx->inst[inst].ptr].type, "netlist_commands")) { - /* since netlist_commands often have @ characters in spice node save / plot commands, do - * not pass through translate, unless a tcleval(...) is present */ - if(strstr(result, "tcleval(")== result) { - my_strdup(_ALLOC_ID_, &result, translate(inst, result)); - } - } else { - my_strdup(_ALLOC_ID_, &result, translate(inst, result)); - } - dbg(1, "print_spice_element(): after translate() result=%s\n", result); + my_strdup(_ALLOC_ID_, &result, tcl_hook2(result)); } if(strstr(result, "expr(") ) { result = eval_expr(result); } if(result) fprintf(fd, "%s", result); + dbg(1, "print_spice_element(): returning |%s|\n", result); my_free(_ALLOC_ID_, &template); my_free(_ALLOC_ID_, &format); my_free(_ALLOC_ID_, &name); @@ -4636,11 +4656,17 @@ const char *translate2(Lcc *lcc, int level, char* s) -/* substitute given tokens in a string with their corresponding values */ -/* ex.: name=@name w=@w l=@l ---> name=m112 w=3e-6 l=0.8e-6 */ -/* using s1, s2, s3 in turn to resolve @tokens */ -/* if no definition for @token is found return @token as is in s */ -/* if s==NULL return emty string */ +/* substitute given tokens in a string with their corresponding values + * ex.: name=@name w=@w l=@l ---> name=m112 w=3e-6 l=0.8e-6 + * using s1, s2, s3 in turn to resolve @tokens + * if no definition for @token is found return @token as is in s + * if s==NULL return emty string + * eat_escapes: + * bit0 == 0 --> keep escapes + * == 1 --> remove escapes + * bit1 == 0 --> return unchanged token if no value found in s* strings + * == 1 --> return empty token if no definition found in s* strings + */ const char *translate3(const char *s, int eat_escapes, const char *s1, const char *s2, const char *s3, const char *s4) { @@ -4652,27 +4678,27 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, size_t sizetok=0; size_t token_pos=0; const char *value; - int escape=0; + int i, escape=0; + size_t found_value = 0; const char *escape_pos = NULL; - + const char *sptr[5]; /* 1...4 used */ if(!s || !xctx) { my_free(_ALLOC_ID_, &result); my_free(_ALLOC_ID_, &translated_tok); return empty; } - dbg(2, "translate3():\n s=%s\n s1=%s\n s2=%s\n s3=%s s4=%s\n", s, s1, s2, s3, s4); + xctx->tok_size = 0; + dbg(1, "---\ntranslate3():\n s=%s\n s1=%s\n s2=%s\n s3=%s\n s4=%s\n---\n", s, s1, s2, s3, s4); my_strdup2(_ALLOC_ID_, &result, ""); - + sptr[1] = s1; sptr[2] = s2; sptr[3] = s3; sptr[4] = s4; while(1) { c=*s; - if(c=='\\') { escape=1; escape_pos = s; - if(eat_escapes) { s++; c=*s; } + if(eat_escapes & 1) { s++; c=*s; } } - space=SPACE(c); if( state==TOK_BEGIN && (c=='@' || c=='%' ) && !escape ) state=TOK_TOKEN; else if(state==TOK_TOKEN && token_pos > 1 && @@ -4681,29 +4707,39 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, ( (!space && c != '%' && c != '@') && escape ) ) ) state=TOK_SEP; - if( s > escape_pos ) escape = 0; - s++; - STR_ALLOC(&token, token_pos, &sizetok); if(state==TOK_TOKEN) token[token_pos++]=(char)c; else if(state==TOK_SEP) { + found_value = 0; token[token_pos]='\0'; dbg(1, "translate3(): token=|%s|\n", token); - value = get_tok_value(s1, token+1, 0); - if(!xctx->tok_size && s2) { - value=get_tok_value(s2, token+1, 0); - } - if(!xctx->tok_size && s3) { - value=get_tok_value(s3, token+1, 0); + value = NULL; + + for(i = 1; i <= 4; i++) { + if(!found_value && sptr[i]) { + value=get_tok_value(sptr[i], token+1, 0); + dbg(1, "translate3(): i=%d, value=%s\n", i, value); + if(xctx->tok_size) found_value = xctx->tok_size; + } + else if( sptr[i]) { + char *v = NULL; + const char *newval; + my_strdup2(_ALLOC_ID_, &v, value); + newval = get_tok_value(sptr[i], v, 0); + if(xctx->tok_size) { + value = newval; + } + my_free(_ALLOC_ID_, &v); + } } - - - if(!xctx->tok_size) { /* above lines did not find a value for token */ - /* no definition found -> keep token */ - my_strcat(_ALLOC_ID_, &result, token); + if(!found_value) { /* above lines did not find a value for token */ + if((eat_escapes & 2) == 0) { + /* no definition found -> keep token */ + my_strcat(_ALLOC_ID_, &result, token); + } } else { my_strcat(_ALLOC_ID_, &result, value); } @@ -4724,7 +4760,6 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, my_strcat(_ALLOC_ID_, &result, ch); } if(c=='\0') { - /* my_strcat(_ALLOC_ID_, &result, ""); */ break; } } /* while(1) */ @@ -4735,6 +4770,7 @@ const char *translate3(const char *s, int eat_escapes, const char *s1, * can be calculated */ dbg(1, "translate3(): result=|%s|\n", result); my_strdup2(_ALLOC_ID_, &translated_tok, tcl_hook2(result)); + xctx->tok_size = found_value; return translated_tok; } diff --git a/src/xschem.tcl b/src/xschem.tcl index 507471ec..9b0a145c 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -3872,6 +3872,7 @@ proc open_sub_schematic {{inst {}} {inst_number 0}} { proc is_xschem_file {f} { + regsub {\(.*} $f {} f ;# remove trailing generator args (gen.tcl(....)) if any if { ![file exists $f] } { return 0 } elseif { [file isdirectory $f] } { return 0 } set a [catch {open "$f" r} fd] diff --git a/xschem_library/devices/simulator_commands.sym b/xschem_library/devices/simulator_commands.sym index ee2879f3..4e227033 100644 --- a/xschem_library/devices/simulator_commands.sym +++ b/xschem_library/devices/simulator_commands.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -31,7 +31,7 @@ value=\\" format="tcleval( [if \{[catch \{sim_is_@simulator \} retval] \} \{ return \{\} \} elseif \{$retval\} \{ return \{ -\\\\@value +@value \} \} else \{ return \{\} \} ])" diff --git a/xschem_library/devices/simulator_commands_shown.sym b/xschem_library/devices/simulator_commands_shown.sym index 52556243..080eb041 100644 --- a/xschem_library/devices/simulator_commands_shown.sym +++ b/xschem_library/devices/simulator_commands_shown.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.5 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -31,7 +31,7 @@ value=\\" format="tcleval( [if \{[catch \{sim_is_@simulator \} retval] \} \{ return \{\} \} elseif \{$retval\} \{ return \{ -\\\\@value +@value \} \} else \{ return \{\} \} ])"} From b625a45dab1d7c5e36c1bef576c7a6aee4ac6fd8 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 03:20:57 +0100 Subject: [PATCH 15/27] place_symbol(): trim leading and trailing whitespace from symbol name given as input --- src/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions.c b/src/actions.c index e0432572..99d2710a 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1482,7 +1482,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot tcleval("load_file_dialog {Choose symbol} *.\\{sym,tcl\\} INITIALINSTDIR"); my_strncpy(name1, tclresult(), S(name1)); } else { - my_strncpy(name1, symbol_name, S(name1)); + my_strncpy(name1, trim_chars(symbol_name, " \t\n"), S(name1)); } dbg(1, "place_symbol(): 1: name1=%s first_call=%d\n",name1, first_call); From 77a6bd2bb88301d73b1828e1a68c17489eb2e23a Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 12:23:51 +0100 Subject: [PATCH 16/27] change order of last statements in hier_psprint() to avoid displaying garbage while moving crosshair cursor and ps->pdf conversion is in progres... --- src/spice_netlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spice_netlist.c b/src/spice_netlist.c index 21d80d05..c341895a 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -133,9 +133,9 @@ void hier_psprint(char **res, int what) /* netlister driver */ xctx->prev_set_modify = save_prev_mod; my_strncpy(xctx->current_name, rel_sym_path(xctx->sch[xctx->currsch]), S(xctx->current_name)); xctx->do_copy_area = save; - if(what & 1) ps_draw(4, 1, 0); /* trailer */ zoom_full(0, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); draw(); + if(what & 1) ps_draw(4, 1, 0); /* trailer */ } static char *model_name_result = NULL; /* safe even with multiple schematics */ From b5a25e5925665f64ac15058eeb85bd99d21a9683 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 13:48:39 +0100 Subject: [PATCH 17/27] fix update not working in proc cellview --- src/xschem.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 9b0a145c..43bcd714 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1986,7 +1986,7 @@ proc cellview { {derived_symbols {}} {upd 0} } { if {$upd} {return} frame .cv.bottom - button .cv.bottom.update -text Update -command "cellview $derived_symbols 1" + button .cv.bottom.update -text Update -command "cellview [list $derived_symbols] 1" pack .cv.bottom.update -side left label .cv.bottom.status -text {STATUS LINE} pack .cv.bottom.status -fill x -expand yes From 8bd9c3d93935c7318ec8721a56ece19e7fd4283c Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 15:27:55 +0100 Subject: [PATCH 18/27] fix bitrots in proc cellview (Update function) --- src/xschem.tcl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 43bcd714..e2fe4ec5 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1799,6 +1799,8 @@ proc cellview_setlabels {w symbol derived_symbol} { if { $sym_spice_sym_def eq {}} { if { ![file exists [abs_sym_path [$w get]]] } { $w configure -bg $missingbg + } elseif {$new_sch ne $default_sch } { + $w configure -bg $symbg } } puts =============== @@ -1814,7 +1816,7 @@ proc cellview_setlabels {w symbol derived_symbol} { xschem set schsymbolprop $newprop xschem set_modify 3 ;# set only modified flag to force a save, do not update window/tab titles xschem save fast - xschem remove_symbols ;# purge all symbols to force a reload from disk + # xschem remove_symbols ;# purge all symbols to force a reload from disk xschem load -keep_symbols -nodraw -noundoreset $current xschem netlist -keep_symbols -noalert;# traverse the hierarchy and retain all encountered symbols puts "get netlist" @@ -1834,7 +1836,6 @@ proc cellview_edit_item {symbol w} { } elseif { $sym_spice_sym_def eq {}} { xschem load_new_window [$w get] } else { - puts $symbol set current [xschem get current_name] set old_sym_def [xschem getprop symbol $symbol spice_sym_def 2] set new_sym_def [editdata $sym_spice_sym_def {Symbol spice_sym_def attribute}] @@ -1906,15 +1907,17 @@ proc cellview { {derived_symbols {}} {upd 0} } { } set syms [join [lsort -index 1 [xschem symbols $derived_symbols]]] + # puts "syms=$syms" foreach {i symbol} $syms { if { [catch {set base_name [xschem symbol_base_name $symbol]}] } { set base_name $symbol } + # puts "i=$i, symbol=$symbol" set derived_symbol 0 if {$base_name ne {}} { set derived_symbol 1 } - if { [catch {set abs_sch [xschem get_sch_from_sym -1 $symbol]} ]} { + if { [catch {xschem get_sch_from_sym -1 $symbol} abs_sch ]} { set abs_sch [abs_sym_path [add_ext $symbol .sch]] } if {$derived_symbol} { @@ -1931,6 +1934,9 @@ proc cellview { {derived_symbols {}} {upd 0} } { } if {$skip} { continue } set sym_sch [rel_sym_path $abs_sch] + if {[catch {xschem getprop symbol $symbol type} type]} { + puts "error: $symbol not found: $type" + } set type [xschem getprop symbol $symbol type] set sym_spice_sym_def [xschem getprop symbol $symbol spice_sym_def 2] if {$type eq {subcircuit}} { From a50b3681c895b7a767b9175f46a6e689e4f2d14e Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 19:44:40 +0100 Subject: [PATCH 19/27] calc_drawing_bbox(): initialize xctx->show_hidden_texts before calculating bbox. --- src/actions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/actions.c b/src/actions.c index 99d2710a..b7a0b00d 100644 --- a/src/actions.c +++ b/src/actions.c @@ -2592,6 +2592,7 @@ void calc_drawing_bbox(xRect *boundbox, int selected) #endif char *estr = NULL; + xctx->show_hidden_texts = tclgetboolvar("show_hidden_texts"); boundbox->x1=-100; boundbox->x2=100; boundbox->y1=-100; From 5e3c27d7eebb8a486e651d221f10d36cd11ebd59 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 9 Feb 2025 20:52:15 +0100 Subject: [PATCH 20/27] load_schematic(): ability to load / preview a generator (adds () to filename) --- src/save.c | 56 ++++++++++++++++++++++++++++++-------------------- src/xschem.tcl | 2 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/save.c b/src/save.c index 28f135cc..05638341 100644 --- a/src/save.c +++ b/src/save.c @@ -3446,6 +3446,7 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler { FILE *fd; char name[PATH_MAX]; + char *ffname = NULL; /*copy of fname so I can change it */ char msg[PATH_MAX+100]; struct stat buf; int i, ret = 1; /* success */ @@ -3454,24 +3455,31 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler xctx->prep_net_structs=0; xctx->prep_hash_inst=0; xctx->prep_hash_wires=0; + my_strdup2(_ALLOC_ID_, &ffname, trim_chars(fname, " \t\n")); if(reset_undo) { xctx->clear_undo(); xctx->prev_set_modify = -1; /* will force set_modify(0) to set window title */ } else xctx->prev_set_modify = 0; /* will prevent set_modify(0) from setting window title */ - if(fname && fname[0]) { + if(ffname && ffname[0]) { int generator = 0; - if(is_generator(fname)) generator = 1; - my_strncpy(name, fname, S(name)); - dbg(1, "load_schematic(): fname=%s\n", fname); + /* if ffname is a generator add () at end of filename if not already present */ + tclvareval("is_xschem_file {", ffname, "}", NULL); + if(!strcmp(tclresult(), "GENERATOR")) { + size_t len = strlen(ffname); + if( ffname[len - 1] != ')') my_strcat(_ALLOC_ID_, &ffname, "()"); + } + if(is_generator(ffname)) generator = 1; + my_strncpy(name, ffname, S(name)); + dbg(1, "load_schematic(): name=%s generator=%d\n", name, generator); /* remote web object specified */ - if(is_from_web(fname) && xschem_web_dirname[0]) { + if(is_from_web(ffname) && xschem_web_dirname[0]) { /* download into ${XSCHEM_TMP_DIR}/xschem_web */ - tclvareval("download_url {", fname, "}", NULL); + tclvareval("download_url {", ffname, "}", NULL); /* build local file name of downloaded object */ - my_snprintf(name, S(name), "%s/%s", xschem_web_dirname, get_cell_w_ext(fname, 0)); + my_snprintf(name, S(name), "%s/%s", xschem_web_dirname, get_cell_w_ext(ffname, 0)); /* build current_dirname by stripping off last filename from url */ - my_snprintf(msg, S(msg), "get_directory {%s}", fname); + my_snprintf(msg, S(msg), "get_directory {%s}", ffname); my_strncpy(xctx->current_dirname, tcleval(msg), S(xctx->current_dirname)); /* local file name */ my_strdup2(_ALLOC_ID_, &xctx->sch[xctx->currsch], name); @@ -3482,29 +3490,32 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler /* ... but not local file from web download --> reset current_dirname */ char sympath[PATH_MAX]; my_snprintf(sympath, S(sympath), "%s", xschem_web_dirname); - /* fname does not begin with $XSCHEM_TMP_DIR/xschem_web and fname does not exist */ + /* ffname does not begin with $XSCHEM_TMP_DIR/xschem_web and ffname does not exist */ - if(strstr(fname, sympath) != fname /* && stat(fname, &buf)*/) { - my_snprintf(msg, S(msg), "get_directory {%s}", fname); + if(strstr(ffname, sympath) != ffname /* && stat(ffname, &buf)*/) { + my_snprintf(msg, S(msg), "get_directory {%s}", ffname); my_strncpy(xctx->current_dirname, tcleval(msg), S(xctx->current_dirname)); } /* local file name */ - my_strdup2(_ALLOC_ID_, &xctx->sch[xctx->currsch], fname); + my_strdup2(_ALLOC_ID_, &xctx->sch[xctx->currsch], ffname); /* local relative reference */ - my_strncpy(xctx->current_name, rel_sym_path(fname), S(xctx->current_name)); + my_strncpy(xctx->current_name, rel_sym_path(ffname), S(xctx->current_name)); } else { /* local file specified and not coming from web url */ /* build current_dirname by stripping off last filename from url */ - my_snprintf(msg, S(msg), "get_directory {%s}", fname); + my_snprintf(msg, S(msg), "get_directory {%s}", ffname); my_strncpy(xctx->current_dirname, tcleval(msg), S(xctx->current_dirname)); /* local file name */ - my_strdup2(_ALLOC_ID_, &xctx->sch[xctx->currsch], fname); + my_strdup2(_ALLOC_ID_, &xctx->sch[xctx->currsch], ffname); /* local relative reference */ - my_strncpy(xctx->current_name, rel_sym_path(fname), S(xctx->current_name)); + my_strncpy(xctx->current_name, rel_sym_path(ffname), S(xctx->current_name)); } - dbg(1, "load_schematic(): opening file for loading:%s, fname=%s\n", name, fname); + dbg(1, "load_schematic(): opening file for loading:%s, ffname=%s\n", name, ffname); dbg(1, "load_schematic(): sch[currsch]=%s\n", xctx->sch[xctx->currsch]); - if(!name[0]) return 0; /* empty filename */ + if(!name[0]) { + my_free(_ALLOC_ID_, &ffname); + return 0; /* empty filename */ + } if(reset_undo) { if(!stat(name, &buf)) { /* file exists */ xctx->time_last_modify = buf.st_mtime; @@ -3515,7 +3526,7 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler } else {xctx->time_last_modify = 0;} /* undefined */ if(generator) { char *cmd; - cmd = get_generator_command(fname); + cmd = get_generator_command(ffname); if(cmd) { fd = popen(cmd, "r"); my_free(_ALLOC_ID_, &cmd); @@ -3526,9 +3537,9 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler size_t len; ret = 0; if(alert) { - fprintf(errfp, "load_schematic(): unable to open file: %s, fname=%s\n", name, fname ); + fprintf(errfp, "load_schematic(): unable to open file: %s, ffname=%s\n", name, ffname ); if(has_x) { - my_snprintf(msg, S(msg), "update; alert_ {Unable to open file: %s}", fname); + my_snprintf(msg, S(msg), "update; alert_ {Unable to open file: %s}", ffname); tcleval(msg); } } @@ -3567,7 +3578,7 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler } } dbg(1, "load_schematic(): %s, returning\n", xctx->sch[xctx->currsch]); - } else { /* fname == NULL or empty */ + } else { /* ffname == NULL or empty */ /* if(reset_undo) xctx->time_last_modify = time(NULL); */ /* no file given, set mtime to current time */ if(reset_undo) xctx->time_last_modify = 0; /* no file given, set mtime to 0 (undefined) */ clear_drawing(); @@ -3602,6 +3613,7 @@ int load_schematic(int load_symbols, const char *fname, int reset_undo, int aler set_netlist_dir(2, NULL); drc_check(-1); } + my_free(_ALLOC_ID_, &ffname); return ret; } diff --git a/src/xschem.tcl b/src/xschem.tcl index e2fe4ec5..1f1ae22b 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -4253,7 +4253,7 @@ proc file_dialog_place_symbol {} { proc file_dialog_display_preview {f} { set type [is_xschem_file $f] - if { $type ne {0} && $type ne {GENERATOR} } { + if { $type ne {0} } { if { [winfo exists .load] } { .load.l.paneright.draw configure -background {} xschem preview_window draw .load.l.paneright.draw "$f" From 724869638e63a6398678d0394f82125e835183e1 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 01:21:22 +0100 Subject: [PATCH 21/27] fix max size of graph bitmap calculation in svg_embedded_graph() and ps_embedded_graph() --- src/draw.c | 7 +++---- src/psprint.c | 6 +++--- src/xinit.c | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/draw.c b/src/draw.c index 34d7d8f7..ea1b9513 100644 --- a/src/draw.c +++ b/src/draw.c @@ -4777,7 +4777,7 @@ void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, cairo_surface_t *png_sfc; int save, save_draw_window, save_draw_grid, rwi, rhi; size_t olength; - const double max_size = 3000.0; + const double max_size = 2500.0; if(!has_x) return; @@ -4792,9 +4792,9 @@ void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, rw = fabs(rx2 -rx1); rh = fabs(ry2 - ry1); scale = 3.0; - if(rw > rh && rw > max_size) { + if(rw > rh && rw * scale > max_size) { scale = max_size / rw; - } else if(rh > max_size) { + } else if(rh * scale > max_size) { scale = max_size / rh; } rwi = (int) (rw * scale + 1.0); @@ -4882,7 +4882,6 @@ void draw(void) #endif dbg(1, "draw()\n"); - if(!xctx || xctx->no_draw) return; cs = tclgetdoublevar("cadsnap"); diff --git a/src/psprint.c b/src/psprint.c index 6ea4281a..3f2f46e7 100644 --- a/src/psprint.c +++ b/src/psprint.c @@ -257,7 +257,7 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl double rw, rh, scale; cairo_surface_t* png_sfc; int save, save_draw_window, save_draw_grid, rwi, rhi; - const double max_size = 3000.0; + const double max_size = 2500.0; int d_c; unsigned char* jpgData = NULL; size_t fileSize = 0; @@ -292,10 +292,10 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl rw = fabs(rx2 - rx1); rh = fabs(ry2 - ry1); scale = 3.0; - if (rw > rh && rw > max_size) { + if (rw > rh && rw * scale > max_size) { scale = max_size / rw; } - else if (rh > max_size) { + else if (rh * scale > max_size) { scale = max_size / rh; } rwi = (int)(rw * scale + 1.0); diff --git a/src/xinit.c b/src/xinit.c index b4cc86a0..0ee4bca2 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -2185,6 +2185,8 @@ void resetwin(int create_pixmap, int clear_pixmap, int force, int w, int h) #else XWindowAttributes wattr; #endif + dbg(1, "resetwin(): create=%d, clear=%d, force=%d, w=%d, h=%d\n", + create_pixmap, clear_pixmap, force, w, h); if(w && h) { width = w; height = h; From 3d972e8e42f917872ec3f75b2d99be0ca45c0365 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 02:43:23 +0100 Subject: [PATCH 22/27] `AUTO SET` button in graph edit dialog box to automatically assign increasing colors to displayed waves --- src/xschem.tcl | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 1f1ae22b..c9f1aadd 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -2961,6 +2961,14 @@ proc touches {sel tag} { return $res } +proc set_graph_default_colors {} { + global graph_selected graph_schname + if { [xschem get schname] ne $graph_schname } return + xschem setprop -fast rect 2 $graph_selected color "4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21" + graph_update_nodelist + xschem draw_graph $graph_selected +} + # change color of selected wave in text widget and redraw graph # OR # change color attribute of wave given as parameter, redraw graph @@ -3352,13 +3360,13 @@ proc graph_edit_properties {n} { grid columnconfig .graphdialog.center.right 5 -weight 0 # bottom frame - button .graphdialog.bottom.cancel -text Cancel -command { + button .graphdialog.bottom.cancel -padx 1 -borderwidth 1 -pady 0 -text Cancel -command { set graph_dialog_default_geometry [winfo geometry .graphdialog] destroy .graphdialog set graph_selected {} set graph_schname {} } - button .graphdialog.bottom.ok -text OK -command { + button .graphdialog.bottom.ok -padx 1 -borderwidth 1 -pady 0 -text OK -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] @@ -3373,7 +3381,7 @@ proc graph_edit_properties {n} { set graph_selected {} set graph_schname {} } - button .graphdialog.bottom.apply -text Apply -command { + button .graphdialog.bottom.apply -padx 1 -borderwidth 1 -pady 0 -text Apply -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] @@ -3390,9 +3398,16 @@ proc graph_edit_properties {n} { pack .graphdialog.bottom.apply -side left pack .graphdialog.bottom.cancel -side left - for {set i 4} {$i < $cadlayers} {incr i} { - radiobutton .graphdialog.bottom.r$i -value $i -background [lindex $tctx::colors $i] \ - -variable graph_sel_color -command graph_change_wave_color -selectcolor white -foreground black + for {set i 4} {$i <= $cadlayers} {incr i} { + if {$i == $cadlayers } { + button .graphdialog.bottom.r$i -padx 1 -borderwidth 1 -pady 0 \ + -command "set_graph_default_colors" \ + -text {AUTO SET} + } else { + radiobutton .graphdialog.bottom.r$i -value $i -background [lindex $tctx::colors $i] \ + -variable graph_sel_color -command graph_change_wave_color \ + -selectcolor white -foreground black + } pack .graphdialog.bottom.r$i -side left } From b5eb81c107614eb7c5deb133d7555d34330f578d Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 13:44:12 +0100 Subject: [PATCH 23/27] updates in proc cellview --- src/callback.c | 4 ---- src/scheduler.c | 2 +- src/xschem.tcl | 22 ++++++++++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/callback.c b/src/callback.c index 3cdb8520..8d1ef6f9 100644 --- a/src/callback.c +++ b/src/callback.c @@ -421,10 +421,6 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int /* determine if mouse pointer is below xaxis or left of yaxis in some graph */ setup_graph_data(i, 0, gr); - - - - /* check if user clicked on a wave label -> draw wave in bold */ if(event == ButtonPress && button == Button3 && edit_wave_attributes(2, i, gr)) { diff --git a/src/scheduler.c b/src/scheduler.c index 69413af3..b5611a46 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -5784,7 +5784,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg char n[100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "derived_symbols")) derived_symbols = 1; - else if(argc > 2 && argv[2][0]) { + else if(argc > 2 && argv[2][0] && isonlydigit(argv[2])) { one_symbol = 1; i = get_symbol(argv[2]); if(i >=0) Tcl_AppendResult(interp, my_itoa(i), " {", xctx->sym[i].name, "}", NULL); diff --git a/src/xschem.tcl b/src/xschem.tcl index c9f1aadd..9093b089 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1771,7 +1771,7 @@ proc simconf_add {tool} { # proc cellview prints symbol bindings (default binding or "schematic" attr in symbol) # of all symbols used in current and sub schematics. proc cellview_setlabels {w symbol derived_symbol} { - global dark_gui_colorscheme + global dark_gui_colorscheme netlist_type if {$dark_gui_colorscheme} { set instfg orange1 set symfg SeaGreen1 @@ -1783,6 +1783,7 @@ proc cellview_setlabels {w symbol derived_symbol} { set symbg SeaGreen1 set missingbg IndianRed1 } + set save_netlist_type [xschem get netlist_type] set current [xschem get current_name] set sym_spice_sym_def [xschem getprop symbol $symbol spice_sym_def 2] set abs_sch [xschem get_sch_from_sym -1 $symbol] @@ -1818,6 +1819,8 @@ proc cellview_setlabels {w symbol derived_symbol} { xschem save fast # xschem remove_symbols ;# purge all symbols to force a reload from disk xschem load -keep_symbols -nodraw -noundoreset $current + set netlist_type $save_netlist_type + xschem set netlist_type $netlist_type xschem netlist -keep_symbols -noalert;# traverse the hierarchy and retain all encountered symbols puts "get netlist" } @@ -1828,6 +1831,8 @@ proc cellview_setlabels {w symbol derived_symbol} { } proc cellview_edit_item {symbol w} { + global netlist_type + set save_netlist_type [xschem get netlist_type] set sym_spice_sym_def [xschem getprop symbol $symbol spice_sym_def 2] if {[xschem is_generator [$w get]]} { set f [$w get] @@ -1848,6 +1853,8 @@ proc cellview_edit_item {symbol w} { xschem save fast puts "$symbol: updated spice_sym_def attribute" xschem load -keep_symbols -nodraw -noundoreset $current + set netlist_type $save_netlist_type + xschem set netlist_type $netlist_type xschem reload_symbols ;# update in-memory symbol data } } @@ -1864,8 +1871,10 @@ proc cellview_edit_sym {w} { xschem load_new_window $sym } -proc cellview { {derived_symbols {}} {upd 0} } { - global keep_symbols nolist_libs dark_gui_colorscheme +proc cellview { {derived_symbols {}} {upd 0}} { + global keep_symbols nolist_libs dark_gui_colorscheme netlist_type + + set save_netlist_type [xschem get netlist_type] if {$dark_gui_colorscheme} { set instfg orange1 @@ -1885,6 +1894,8 @@ proc cellview { {derived_symbols {}} {upd 0} } { } if {!$upd} { + set netlist_type $save_netlist_type + xschem set netlist_type $netlist_type xschem reload_symbols ;# purge unused symbols xschem netlist -keep_symbols -noalert;# traverse the hierarchy and retain all encountered symbols puts "get netlist" @@ -2012,9 +2023,10 @@ proc cellview { {derived_symbols {}} {upd 0} } { ############ traversal proc traversal_setlabels {w parent_sch instname inst_sch sym_sch default_sch inst_spice_sym_def sym_spice_sym_def} { - global traversal dark_gui_colorscheme + global traversal dark_gui_colorscheme netlist_type set sf .trav.center.f.scrl + set save_netlist_type [xschem get netlist_type] # puts "traversal_setlabels: $w parent: |$parent_sch| inst: $instname def: $sym_sch $inst_sch --> [$w get]" # update schematic if {$parent_sch ne {}} { @@ -2032,6 +2044,8 @@ proc traversal_setlabels {w parent_sch instname inst_sch sym_sch default_sch set inst_sch [$w get] # puts "inst_sch set to: $inst_sch" xschem load -undoreset -nodraw $current + set netlist_type $save_netlist_type + xschem set netlist_type $netlist_type } } # /update schematic From 6b090273fbab1f0fd25c4ecf8fa3df61e8b9d2a1 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 15:18:01 +0100 Subject: [PATCH 24/27] lock graphs only if they have the same simulation type loaded. So zooming/panning a tran plot will not affect dc or ac plots. --- src/callback.c | 57 +++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/callback.c b/src/callback.c index 8d1ef6f9..3b976998 100644 --- a/src/callback.c +++ b/src/callback.c @@ -884,7 +884,10 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int } /* loop: after having operated on the master graph do the others */ for(i=0; i< xctx->rects[GRIDLAYER]; ++i) { + int same_sim_type = 0; + char *curr_sim_type = NULL; r = &xctx->rect[GRIDLAYER][i]; + my_strdup2(_ALLOC_ID_, &curr_sim_type, get_tok_value(r->prop_ptr, "sim_type", 0)); need_redraw = 0; if( !(r->flags & 1) ) continue; /* 1: graph; 3: graph_unlocked */ gr->gx1 = gr->master_gx1; @@ -894,6 +897,12 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int if(gr->dataset >= 0 /* && gr->dataset < xctx->raw->datasets */) dataset =gr->dataset; else dataset = -1; + if(!strcmp(curr_sim_type, + get_tok_value(xctx->rect[GRIDLAYER][xctx->graph_master].prop_ptr, "sim_type", 0))) { + same_sim_type = 1; + } + my_free(_ALLOC_ID_, &curr_sim_type); + if(event == MotionNotify && (state & Button1Mask) && !xctx->graph_bottom && !(xctx->graph_flags & (16 | 32 | 512 | 1024))) { double delta; @@ -931,7 +940,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int delta = gr->gw; delta_threshold = 0.01; /* selected or locked or master */ - if( r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if( r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { dbg(1, "moving waves: %d\n", i); if(fabs(xctx->mx_double_save - xctx->mousex_snap) > fabs(gr->cx * delta) * delta_threshold) { xx1 = gr->gx1 + (xctx->mx_double_save - xctx->mousex_snap) / gr->cx; @@ -970,7 +979,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int /* horizontal move of waveforms with mouse wheel */ else { /* selected or locked or master */ - if( r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if( r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { delta = gr->gw; delta_threshold = 0.05; xx1 = gr->gx1 - delta * delta_threshold; @@ -1007,7 +1016,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int /* horizontal move of waveforms with mouse wheel */ else { /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { delta = gr->gw; delta_threshold = 0.05; xx1 = gr->gx1 + delta * delta_threshold; @@ -1048,7 +1057,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int } } else { /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { double var = 0.2 * gr->gw; xx2 = gr->gx2 + var * (1 - zoom_m); xx1 = gr->gx1 - var * zoom_m; @@ -1087,7 +1096,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int } } else { /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { double var = 0.2 * gr->gw; xx2 = gr->gx2 - var * (1 - zoom_m); xx1 = gr->gx1 + var * zoom_m; @@ -1166,13 +1175,15 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int need_redraw = 1; } } else { - delta = gr->gw; - delta_threshold = 0.05; - xx1 = gr->gx1 - delta * delta_threshold; - xx2 = gr->gx2 - delta * delta_threshold; - my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); - my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); - need_redraw = 1; + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 - delta * delta_threshold; + xx2 = gr->gx2 - delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } } } else if(event == KeyPress && key == XK_Right) { @@ -1191,13 +1202,15 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int need_redraw = 1; } } else { - delta = gr->gw; - delta_threshold = 0.05; - xx1 = gr->gx1 + delta * delta_threshold; - xx2 = gr->gx2 + delta * delta_threshold; - my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); - my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); - need_redraw = 1; + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 + delta * delta_threshold; + xx2 = gr->gx2 + delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } } } else if(event == KeyPress && key == XK_Down) { @@ -1234,7 +1247,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int } /* graph_master */ } else { /* not graph_left, full X zoom*/ /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { need_redraw = graph_fullxzoom(i, gr, dataset); } } @@ -1244,7 +1257,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int else if(event == MotionNotify && (state & Button1Mask) && xctx->graph_bottom ) { if(xctx->raw && xctx->raw->values) { /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { /* xx1 and xx2 calculated for master graph above */ my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); @@ -1264,7 +1277,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int else if(button == Button3 && (xctx->ui_state & GRAPHPAN) && !xctx->graph_left && !xctx->graph_top) { /* selected or locked or master */ - if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { if(xctx->mx_double_save != xctx->mousex_snap) { clear_graphpan_at_end = 1; From 88ee9ba0ad5f353fb290393799c9248f635d8c1d Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 17:36:10 +0100 Subject: [PATCH 25/27] fix wrong window size re-setting after a hierarchy ps/pdf printout (due to messing with portrait / landscape) --- src/actions.c | 2 + src/psprint.c | 144 ++++++++++++++++++++++++++------------------------ 2 files changed, 77 insertions(+), 69 deletions(-) diff --git a/src/actions.c b/src/actions.c index b7a0b00d..59afb731 100644 --- a/src/actions.c +++ b/src/actions.c @@ -2845,6 +2845,7 @@ void set_viewport_size(int w, int h, double lw) void save_restore_zoom(int save, Zoom_info *zi) { if(save) { + dbg(1, "save_restore_zoom: save width= %d, height=%d\n", xctx->xrect[0].width, xctx->xrect[0].height); zi->savew = xctx->xrect[0].width; zi->saveh = xctx->xrect[0].height; zi->savelw = xctx->lw; @@ -2856,6 +2857,7 @@ void save_restore_zoom(int save, Zoom_info *zi) xctx->xrect[0].y = 0; xctx->xrect[0].width = (unsigned short)zi->savew; xctx->xrect[0].height = (unsigned short)zi->saveh; + dbg(1, "save_restore_zoom: restore width= %d, height=%d\n", xctx->xrect[0].width, xctx->xrect[0].height); xctx->areax2 = zi->savew+2*INT_WIDTH(zi->savelw); xctx->areay2 = zi->saveh+2*INT_WIDTH(zi->savelw); xctx->areax1 = -2*INT_WIDTH(zi->savelw); diff --git a/src/psprint.c b/src/psprint.c index 3f2f46e7..295ed1ed 100644 --- a/src/psprint.c +++ b/src/psprint.c @@ -301,6 +301,7 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl rwi = (int)(rw * scale + 1.0); rhi = (int)(rh * scale + 1.0); dbg(1, "graph size: %dx%d\n", rwi, rhi); + dbg(1, "ps_embedded_graph: saving zoom\n"); save_restore_zoom(1, &zi); set_viewport_size(rwi, rhi, xctx->lw); @@ -353,8 +354,9 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl cairo_surface_destroy(png_sfc); xctx->draw_pixmap = 1; tclsetboolvar("draw_grid", save_draw_grid); + dbg(1, "ps_embedded_graph: restoring zoom\n"); save_restore_zoom(0, &zi); - resetwin(1, 1, 1, xctx->xrect[0].width, xctx->xrect[0].height); + resetwin(1, 1, 1, 0, 0); change_linewidth(xctx->lw); tclsetboolvar("dark_colorscheme", d_c); build_colors(0, 0); @@ -1178,8 +1180,8 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) fprintf(errfp, "ps_draw(): can not create tmpfile %s\n", *psfile); return; } + setbuf(fd, NULL); /*To prevent buffer errors, still investigating cause. */ } - setbuf(fd, NULL); /*To prevent buffer errors, still investigating cause. */ ps_colors=my_calloc(_ALLOC_ID_, cadlayers, sizeof(Ps_color)); if(ps_colors==NULL){ fprintf(errfp, "create_ps(): calloc error\n"); @@ -1190,78 +1192,83 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) old_grid=tclgetboolvar("draw_grid"); tclsetvar("draw_grid", "0"); - /* xschem window aspect ratio decides if portrait or landscape */ - boundbox.x1 = xctx->areax1; - boundbox.x2 = xctx->areax2; - boundbox.y1 = xctx->areay1; - boundbox.y2 = xctx->areay2; - dx=boundbox.x2-boundbox.x1; - dy=boundbox.y2-boundbox.y1; - - /* xschem drawing bbox decides if portrait or landscape */ - if(fullzoom == 1) { - calc_drawing_bbox(&boundbox, 0); - dx=boundbox.x2-boundbox.x1; - dy=boundbox.y2-boundbox.y1; - } - if(dx >= dy) { - landscape = 1; - } else { - landscape = 0; - } - dbg(1, "dx=%g, dy=%g\n", dx, dy); - if(fullzoom == 1) { - /* save size and zoom factor */ - save_restore_zoom(1, &zi); - /* this zoom only done to reset lw */ - zoom_full(0, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); - /* adjust aspect ratio to paper size */ - if(landscape) - xctx->xrect[0].height = (short unsigned int) (xctx->xrect[0].width * pagey / pagex); - else - xctx->xrect[0].width = (short unsigned int) (xctx->xrect[0].height * pagey / pagex); - dbg(1, "xrect.width=%d, xrect.height=%d\n", xctx->xrect[0].width, xctx->xrect[0].height); - xctx->areax1 = -2*INT_WIDTH(xctx->lw); - xctx->areay1 = -2*INT_WIDTH(xctx->lw); - xctx->areax2 = xctx->xrect[0].width+2*INT_WIDTH(xctx->lw); - xctx->areay2 = xctx->xrect[0].height+2*INT_WIDTH(xctx->lw); - xctx->areaw = xctx->areax2-xctx->areax1; - xctx->areah = xctx->areay2 - xctx->areay1; - dbg(1, "dx=%g, dy=%g\n", dx, dy); - /* fit schematic into adjusted size */ - zoom_full(0, 0, 0 + 2 * tclgetboolvar("zoom_full_center"), 0.97); + if(!(what & 4)) { + /* xschem window aspect ratio decides if portrait or landscape */ boundbox.x1 = xctx->areax1; boundbox.x2 = xctx->areax2; boundbox.y1 = xctx->areay1; boundbox.y2 = xctx->areay2; dx=boundbox.x2-boundbox.x1; dy=boundbox.y2-boundbox.y1; - } - - if(!landscape) { /* decide paper orientation for best schematic fit */ - double tmp; - tmp = pagex; - pagex = pagey; - pagey = tmp; - } - if(fullzoom == 2) { /* set media size to bbox */ - double sc; - my_strncpy(papername, "bbox", S(papername)); - pagex = xctx->xrect[0].width; - pagey = xctx->xrect[0].height; - if(pagex > pagey) { - sc = 842. / pagex; - pagex = my_round(pagex * sc); - pagey = my_round(pagey * sc); - } else { - sc = 842. / pagey; - pagex = my_round(pagex * sc); - pagey = my_round(pagey * sc); + + /* xschem drawing bbox decides if portrait or landscape */ + if(fullzoom == 1) { + calc_drawing_bbox(&boundbox, 0); + dx=boundbox.x2-boundbox.x1; + dy=boundbox.y2-boundbox.y1; } - margin = 0.0; - } + if(dx >= dy) { + landscape = 1; + } else { + landscape = 0; + } + dbg(1, "dx=%g, dy=%g\n", dx, dy); + + + if(fullzoom == 1) { + /* save size and zoom factor */ + dbg(1, "create_ps: saving zoom\n"); + save_restore_zoom(1, &zi); + /* this zoom only done to reset lw */ + zoom_full(0, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); + /* adjust aspect ratio to paper size */ + if(landscape) + xctx->xrect[0].height = (short unsigned int) (xctx->xrect[0].width * pagey / pagex); + else + xctx->xrect[0].width = (short unsigned int) (xctx->xrect[0].height * pagey / pagex); + dbg(1, "xrect.width=%d, xrect.height=%d\n", xctx->xrect[0].width, xctx->xrect[0].height); + xctx->areax1 = -2*INT_WIDTH(xctx->lw); + xctx->areay1 = -2*INT_WIDTH(xctx->lw); + xctx->areax2 = xctx->xrect[0].width+2*INT_WIDTH(xctx->lw); + xctx->areay2 = xctx->xrect[0].height+2*INT_WIDTH(xctx->lw); + xctx->areaw = xctx->areax2-xctx->areax1; + xctx->areah = xctx->areay2 - xctx->areay1; + dbg(1, "dx=%g, dy=%g\n", dx, dy); + /* fit schematic into adjusted size */ + zoom_full(0, 0, 0 + 2 * tclgetboolvar("zoom_full_center"), 0.97); + boundbox.x1 = xctx->areax1; + boundbox.x2 = xctx->areax2; + boundbox.y1 = xctx->areay1; + boundbox.y2 = xctx->areay2; + dx=boundbox.x2-boundbox.x1; + dy=boundbox.y2-boundbox.y1; + } + + if(!landscape) { /* decide paper orientation for best schematic fit */ + double tmp; + tmp = pagex; + pagex = pagey; + pagey = tmp; + } + if(fullzoom == 2) { /* set media size to bbox */ + double sc; + my_strncpy(papername, "bbox", S(papername)); + pagex = xctx->xrect[0].width; + pagey = xctx->xrect[0].height; + if(pagex > pagey) { + sc = 842. / pagex; + pagex = my_round(pagex * sc); + pagey = my_round(pagey * sc); + } else { + sc = 842. / pagey; + pagex = my_round(pagex * sc); + pagey = my_round(pagey * sc); + } + margin = 0.0; + } + } /* if(!(what & 4)) */ if(what & 1) {/* prolog */ dbg(1, "ps_draw(): bbox: x1=%g y1=%g x2=%g y2=%g\n", boundbox.x1, boundbox.y1, boundbox.x2, boundbox.y2); @@ -1334,7 +1341,6 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) fprintf(fd, "%%%%EndProlog\n"); } - if(what & 2) { /* page */ ++numpages; @@ -1491,7 +1497,7 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) dbg(1, "ps_draw(): INT_WIDTH(lw)=%d plotfile=%s\n",INT_WIDTH(xctx->lw), xctx->plotfile); fprintf(fd, "showpage\n\n"); - } + } /* if(what & 2) */ if(what & 4) { /* trailer */ fprintf(fd, "%%%%trailer\n"); fprintf(fd, "%%%%Pages: %d\n", numpages); @@ -1509,10 +1515,10 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) /* restore original size and zoom factor */ - if(fullzoom == 1) { + if(!(what & 4) && fullzoom == 1) { + dbg(1, "create_ps: restoring zoom\n"); save_restore_zoom(0, &zi); } - } int ps_draw(int what, int fullzoom, int eps) From 89a8d8b7195b63e1224448ab9c7fcebd4faa2f58 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 17:44:05 +0100 Subject: [PATCH 26/27] same previous fix for resetwin() restore done in ps_embedded_graph() backported also in svg_embedded_graph() (not necessary here but safer) --- src/draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.c b/src/draw.c index ea1b9513..b7c057dc 100644 --- a/src/draw.c +++ b/src/draw.c @@ -4850,7 +4850,7 @@ void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, xctx->do_copy_area=save; tclsetboolvar("draw_grid", save_draw_grid); save_restore_zoom(0, &zi); - resetwin(1, 1, 1, xctx->xrect[0].width, xctx->xrect[0].height); + resetwin(1, 1, 1, 0, 0); h = fabs(y2 - y1); w = fabs(x2 - x1); From fe8ef8905272dd41c21da16a165d389053911e90 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 10 Feb 2025 18:49:11 +0100 Subject: [PATCH 27/27] update version info in some rom8k symbols. Do a xschem remove_symbols in proc cellview_setlabels to force a reload of changed symbols. --- src/xschem.tcl | 2 +- xschem_library/rom8k/lvnand2.sym | 5 +++-- xschem_library/rom8k/lvnor2.sym | 5 +++-- xschem_library/rom8k/lvnot.sym | 5 +++-- xschem_library/rom8k/rom2_predec1.sym | 5 +++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 9093b089..807cbcd7 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1817,7 +1817,7 @@ proc cellview_setlabels {w symbol derived_symbol} { xschem set schsymbolprop $newprop xschem set_modify 3 ;# set only modified flag to force a save, do not update window/tab titles xschem save fast - # xschem remove_symbols ;# purge all symbols to force a reload from disk + xschem remove_symbols ;# purge all symbols to force a reload from disk xschem load -keep_symbols -nodraw -noundoreset $current set netlist_type $save_netlist_type xschem set netlist_type $netlist_type diff --git a/xschem_library/rom8k/lvnand2.sym b/xschem_library/rom8k/lvnand2.sym index 2fa69bb9..805f94a1 100644 --- a/xschem_library/rom8k/lvnand2.sym +++ b/xschem_library/rom8k/lvnand2.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.4 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -31,7 +31,8 @@ template="name=x1 m=1 + VCCPIN=VCC VSSPIN=VSS" extra="VCCPIN VSSPIN" generic_type="m=integer wna=real lna=real wpa=real lpa=real wnb=real lnb=real wpb=real lpb=real VCCPIN=string VSSPIN=string" -verilog_stop=true} +verilog_stop=true +} V {} S {} E {} diff --git a/xschem_library/rom8k/lvnor2.sym b/xschem_library/rom8k/lvnor2.sym index 28cea362..95672ee9 100644 --- a/xschem_library/rom8k/lvnor2.sym +++ b/xschem_library/rom8k/lvnor2.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.4 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -19,7 +19,8 @@ v {xschem version=3.4.4 file_version=1.2 * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA } -G {type=subcircuit +G {} +K {type=subcircuit vhdl_stop=true verilog_stop=true format="@name @pinlist @VCCPIN @VSSPIN @symname wna=@wna lna=@lna wpa=@wpa lpa=@lpa wnb=@wnb lnb=@lnb wpb=@wpb lpb=@lpb m=@m" diff --git a/xschem_library/rom8k/lvnot.sym b/xschem_library/rom8k/lvnot.sym index 59321d63..2f4980f0 100644 --- a/xschem_library/rom8k/lvnot.sym +++ b/xschem_library/rom8k/lvnot.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.4 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -30,7 +30,8 @@ template="name=x1 m=1 + VCCPIN=VCC VSSPIN=VSS" extra="VCCPIN VSSPIN" generic_type="m=integer wn=real lln=real wp=real lp=real VCCPIN=string VSSPIN=string" -verilog_stop=true} +verilog_stop=true +} V {} S {} E {} diff --git a/xschem_library/rom8k/rom2_predec1.sym b/xschem_library/rom8k/rom2_predec1.sym index 630760ce..524d5cc4 100644 --- a/xschem_library/rom8k/rom2_predec1.sym +++ b/xschem_library/rom8k/rom2_predec1.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.4 file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -19,7 +19,8 @@ v {xschem version=3.4.4 file_version=1.2 * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA } -G {type=subcircuit +G {} +K {type=subcircuit format="@name @pinlist @symname" template="name=x1" }