From 5a3c97d64eb9403e9d96272a302f82e34d78d53e Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 16 Jul 2025 00:12:26 +0200 Subject: [PATCH] add vccs_limit.sym and vcvs_limit.sym that use the XSPICE limit code model (saturated controlled sources). [WIP] support for spectre netlist format (for the VACASK simulator) - still incomplete --- src/Makefile.in | 4 +- src/actions.c | 36 +- src/callback.c | 4 +- src/editprop.c | 12 + src/in_memory_undo.c | 2 + src/netlist.c | 12 + src/options.c | 4 + src/save.c | 13 +- src/scheduler.c | 19 + src/spectre.awk | 346 +++++++++++++++ src/spectre_netlist.c | 611 ++++++++++++++++++++++++++ src/spice_netlist.c | 2 + src/token.c | 563 +++++++++++++++++++++++- src/verilog_netlist.c | 4 +- src/vhdl_netlist.c | 2 + src/xinit.c | 3 + src/xschem.h | 9 + src/xschem.help | 1 + src/xschem.tcl | 24 +- xschem_library/devices/nmos4.sym | 6 +- xschem_library/devices/vccs_limit.sym | 45 ++ xschem_library/devices/vcvs_limit.sym | 37 ++ 22 files changed, 1739 insertions(+), 20 deletions(-) create mode 100755 src/spectre.awk create mode 100644 src/spectre_netlist.c create mode 100644 xschem_library/devices/vccs_limit.sym create mode 100644 xschem_library/devices/vcvs_limit.sym diff --git a/src/Makefile.in b/src/Makefile.in index 16bf9ac6..b05a32a4 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -3,7 +3,7 @@ put /local/src { icon.c callback.c actions.c move.c check.c clip.c draw.c globals.c main.c netlist.c hash_iterator.c findnet.c scheduler.c store.c xinit.c 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 + hilight.c options.c vhdl_netlist.c svgdraw.c spice_netlist.c spectre_netlist.c tedax_netlist.c verilog_netlist.c parselabel.c expandlabel.c eval_expr.c in_memory_undo.c cairo_jpg.c } @@ -12,7 +12,7 @@ put /local/src { put /local/install_shares { keys.help xschem.help xschem.tcl break.awk convert_to_verilog2001.awk flatten.awk flatten_tedax.awk flatten_savenodes.awk make_sym.awk make_sym_lcc.awk - symgen.awk order_labels.awk sort_labels.awk spice.awk tedax.awk verilog.awk + symgen.awk order_labels.awk sort_labels.awk spice.awk spectre.awk tedax.awk verilog.awk vhdl.awk hspice_backannotate.tcl add_custom_menu.tcl create_graph.tcl add_custom_button.tcl change_index.tcl icon.xpm resources.tcl xschemrc ngspice_backannotate.tcl gschemtoxschem.awk mouse_bindings.tcl cadence_style_rc diff --git a/src/actions.c b/src/actions.c index a6bf8d4d..0569429a 100644 --- a/src/actions.c +++ b/src/actions.c @@ -806,6 +806,12 @@ int set_sym_flags(xSymbol *sym) else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open")) sym->flags |= SPICE_IGNORE; + ptr = get_tok_value(sym->prop_ptr,"spectre_ignore",0); + if(!strcmp(ptr, "short")) + sym->flags |= SPECTRE_SHORT; + else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open")) + sym->flags |= SPECTRE_IGNORE; + ptr = get_tok_value(sym->prop_ptr,"verilog_ignore",0); if(!strcmp(ptr, "short")) sym->flags |= VERILOG_SHORT; @@ -858,6 +864,12 @@ int set_inst_flags(xInstance *inst) inst->flags |= SPICE_SHORT; else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open")) inst->flags |= SPICE_IGNORE; + + ptr = get_tok_value(inst->prop_ptr,"spectre_ignore",0); + if(!strcmp(ptr, "short")) + inst->flags |= SPECTRE_SHORT; + else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open")) + inst->flags |= SPECTRE_IGNORE; ptr = get_tok_value(inst->prop_ptr,"verilog_ignore",0); if(!strcmp(ptr, "short")) @@ -979,9 +991,10 @@ void clear_drawing(void) my_free(_ALLOC_ID_, &xctx->schsymbolprop); my_free(_ALLOC_ID_, &xctx->schprop); my_free(_ALLOC_ID_, &xctx->schvhdlprop); + my_free(_ALLOC_ID_, &xctx->schverilogprop); + my_free(_ALLOC_ID_, &xctx->schspectreprop); my_free(_ALLOC_ID_, &xctx->version_string); if(xctx->header_text) my_free(_ALLOC_ID_, &xctx->header_text); - my_free(_ALLOC_ID_, &xctx->schverilogprop); for(i=0;iwires; ++i) { my_free(_ALLOC_ID_, &xctx->wire[i].prop_ptr); @@ -1977,10 +1990,11 @@ void toggle_ignore(void) int i, n, first = 1; char *attr; int flag = 0; /* 1: spice_ignore=true, 2: spice_ignore=short */ - const char *spice_ignore_str; + const char *ignore_str; if(xctx->netlist_type == CAD_VERILOG_NETLIST) attr="verilog_ignore"; else if(xctx->netlist_type == CAD_VHDL_NETLIST) attr="vhdl_ignore"; else if(xctx->netlist_type == CAD_TEDAX_NETLIST) attr="tedax_ignore"; + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) attr="spectre_ignore"; else if(xctx->netlist_type == CAD_SPICE_NETLIST) attr="spice_ignore"; else attr = NULL; if(attr) { @@ -1993,9 +2007,9 @@ void toggle_ignore(void) first = 0; } flag = 0; - spice_ignore_str = get_tok_value(xctx->inst[i].prop_ptr, attr, 0); - if(!strcmp(spice_ignore_str, "short")) flag = 2; - else if(!strboolcmp(spice_ignore_str, "true")) flag = 1; + ignore_str = get_tok_value(xctx->inst[i].prop_ptr, attr, 0); + if(!strcmp(ignore_str, "short")) flag = 2; + else if(!strboolcmp(ignore_str, "true")) flag = 1; if(flag == 0) flag = 1; else if(flag == 1) flag = 2; @@ -2043,6 +2057,7 @@ void get_additional_symbols(int what) char *spice_sym_def = NULL; char *vhdl_sym_def = NULL; char *verilog_sym_def = NULL; + char *spectre_sym_def = NULL; char *default_schematic = NULL; char *sch = NULL; char symbol_base_sch[PATH_MAX] = ""; @@ -2052,6 +2067,7 @@ void get_additional_symbols(int what) 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_, &spectre_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"spectre_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)); @@ -2099,7 +2115,10 @@ void get_additional_symbols(int what) translate3(spice_sym_def, 1, xctx->inst[i].prop_ptr, symptr->templ, symname_attr, NULL)); - dbg(1, "get_additional_symbols(): spice_sym_def=%s\n", spice_sym_def); + my_strdup(_ALLOC_ID_, &spectre_sym_def, + translate3(spectre_sym_def, 1, xctx->inst[i].prop_ptr, + symptr->templ, + symname_attr, NULL)); 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() */ @@ -2129,6 +2148,10 @@ void get_additional_symbols(int what) my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr, subst_token(xctx->sym[j].prop_ptr, "spice_sym_def", spice_sym_def)); } + if(spectre_sym_def) { + my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr, + subst_token(xctx->sym[j].prop_ptr, "spectre_sym_def", spectre_sym_def)); + } if(verilog_sym_def) { my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr, subst_token(xctx->sym[j].prop_ptr, "verilog_sym_def", verilog_sym_def)); @@ -2146,6 +2169,7 @@ void get_additional_symbols(int what) } /* if(xctx->tok_size && sch[0]) */ my_free(_ALLOC_ID_, &sch); my_free(_ALLOC_ID_, &spice_sym_def); + my_free(_ALLOC_ID_, &spectre_sym_def); my_free(_ALLOC_ID_, &vhdl_sym_def); my_free(_ALLOC_ID_, &verilog_sym_def); } /* for(i=0;iinstances; ++i) */ diff --git a/src/callback.c b/src/callback.c index 05350572..ece8a140 100644 --- a/src/callback.c +++ b/src/callback.c @@ -3145,6 +3145,8 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m err = global_spice_netlist(0, 1); else if(xctx->netlist_type == CAD_VHDL_NETLIST) err = global_vhdl_netlist(0, 1); + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) + err = global_spectre_netlist(0, 1); else if(xctx->netlist_type == CAD_VERILOG_NETLIST) err = global_verilog_netlist(0, 1); else if(xctx->netlist_type == CAD_TEDAX_NETLIST) @@ -3545,7 +3547,7 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m } else if(rstate == ControlMask) { /* toggle spice/vhdl netlist */ xctx->netlist_type++; - if(xctx->netlist_type==6) xctx->netlist_type=1; + if(xctx->netlist_type==7) xctx->netlist_type=1; set_tcl_netlist_type(); draw(); /* needed to ungrey or grey out components due to *_ignore attribute */ } diff --git a/src/editprop.c b/src/editprop.c index 45774052..a0882a53 100644 --- a/src/editprop.c +++ b/src/editprop.c @@ -1947,6 +1947,12 @@ void edit_property(int x) else tclsetvar("retval",""); } + else if(xctx->netlist_type==CAD_SPECTRE_NETLIST) { + if(xctx->schspectreprop!=NULL) + tclsetvar("retval",xctx->schspectreprop); + else + tclsetvar("retval",""); + } else if(xctx->netlist_type==CAD_SPICE_NETLIST) { if(xctx->schprop!=NULL) tclsetvar("retval",xctx->schprop); @@ -1992,6 +1998,12 @@ void edit_property(int x) xctx->push_undo(); my_strdup(_ALLOC_ID_, &xctx->schverilogprop, (char *) tclgetvar("retval")); + } else if(xctx->netlist_type==CAD_SPECTRE_NETLIST && + (!xctx->schspectreprop || strcmp(xctx->schspectreprop, tclgetvar("retval") ) ) ) { + modified = 1; + xctx->push_undo(); + my_strdup(_ALLOC_ID_, &xctx->schspectreprop, (char *) tclgetvar("retval")); + } else if(xctx->netlist_type==CAD_SPICE_NETLIST && (!xctx->schprop || strcmp(xctx->schprop, tclgetvar("retval") ) ) ) { modified = 1; diff --git a/src/in_memory_undo.c b/src/in_memory_undo.c index 313f54e9..64eebee6 100644 --- a/src/in_memory_undo.c +++ b/src/in_memory_undo.c @@ -271,6 +271,7 @@ void mem_push_undo(void) my_strdup(_ALLOC_ID_, &xctx->uslot[slot].gptr, xctx->schvhdlprop); my_strdup(_ALLOC_ID_, &xctx->uslot[slot].vptr, xctx->schverilogprop); my_strdup(_ALLOC_ID_, &xctx->uslot[slot].sptr, xctx->schprop); + my_strdup(_ALLOC_ID_, &xctx->uslot[slot].fptr, xctx->schspectreprop); my_strdup(_ALLOC_ID_, &xctx->uslot[slot].kptr, xctx->schsymbolprop); my_strdup(_ALLOC_ID_, &xctx->uslot[slot].eptr, xctx->schtedaxprop); @@ -435,6 +436,7 @@ void mem_pop_undo(int redo, int set_modify_status) my_strdup(_ALLOC_ID_, &xctx->schvhdlprop, xctx->uslot[slot].gptr); my_strdup(_ALLOC_ID_, &xctx->schverilogprop, xctx->uslot[slot].vptr); + my_strdup(_ALLOC_ID_, &xctx->schspectreprop, xctx->uslot[slot].fptr); my_strdup(_ALLOC_ID_, &xctx->schprop, xctx->uslot[slot].sptr); my_strdup(_ALLOC_ID_, &xctx->schsymbolprop, xctx->uslot[slot].kptr); my_strdup(_ALLOC_ID_, &xctx->schtedaxprop, xctx->uslot[slot].eptr); diff --git a/src/netlist.c b/src/netlist.c index 29844dab..2c161bcf 100644 --- a/src/netlist.c +++ b/src/netlist.c @@ -653,6 +653,8 @@ void set_tcl_netlist_type(void) tclsetvar("netlist_type", "verilog"); } else if(xctx->netlist_type == CAD_VHDL_NETLIST) { tclsetvar("netlist_type", "vhdl"); + } else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) { + tclsetvar("netlist_type", "spectre"); } else if(xctx->netlist_type == CAD_TEDAX_NETLIST) { tclsetvar("netlist_type", "tedax"); } else if(xctx->netlist_type == CAD_SYMBOL_ATTRS) { @@ -719,6 +721,7 @@ int record_global_node(int what, FILE *fp, const char *node) } else if(what == 0) { for(i = 0;i < xctx->max_globals; ++i) { if(xctx->netlist_type == CAD_SPICE_NETLIST) fprintf(fp, ".GLOBAL %s\n", xctx->globals[i]); + if(xctx->netlist_type == CAD_SPECTRE_NETLIST) fprintf(fp, "global %s\n", xctx->globals[i]); /*<<<<*/ if(xctx->netlist_type == CAD_TEDAX_NETLIST) fprintf(fp, "__GLOBAL__ %s\n", xctx->globals[i]); } } else if(what == 2) { @@ -1051,6 +1054,8 @@ int shorted_instance(int i, int lvs_ignore) if((inst[i].flags & SPICE_SHORT) || (sym[inst[i].ptr].flags & SPICE_SHORT) ) shorted = 1; } else if(xctx->netlist_type == CAD_VERILOG_NETLIST) { if((inst[i].flags & VERILOG_SHORT) || (sym[inst[i].ptr].flags & VERILOG_SHORT) ) shorted = 1; + } else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) { + if((inst[i].flags & SPECTRE_SHORT) || (sym[inst[i].ptr].flags & SPECTRE_SHORT) ) shorted = 1; } else if(xctx->netlist_type == CAD_VHDL_NETLIST) { if((inst[i].flags & VHDL_SHORT) || (sym[inst[i].ptr].flags & VHDL_SHORT) ) shorted = 1; } else if(xctx->netlist_type == CAD_TEDAX_NETLIST) @@ -1077,6 +1082,8 @@ int skip_instance(int i, int skip_short, int lvs_ignore) skip = skip_instance2(i, lvs_ignore, (skip_short ? SPICE_SHORT : 0) | SPICE_IGNORE); else if(xctx->netlist_type == CAD_VERILOG_NETLIST) skip = skip_instance2(i, lvs_ignore, (skip_short ? VERILOG_SHORT : 0) | VERILOG_IGNORE); + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) + skip = skip_instance2(i, lvs_ignore, (skip_short ? SPECTRE_SHORT : 0) | SPECTRE_IGNORE); else if(xctx->netlist_type == CAD_VHDL_NETLIST) skip = skip_instance2(i, lvs_ignore, (skip_short ? VHDL_SHORT : 0) | VHDL_IGNORE); else if(xctx->netlist_type == CAD_TEDAX_NETLIST) @@ -1154,6 +1161,10 @@ static int instcheck(int n, int p) ((inst[n].flags & VERILOG_IGNORE) || (k >= 0 && (sym[k].flags & VERILOG_IGNORE))) ) return 0; + if( xctx->netlist_type == CAD_SPECTRE_NETLIST && + ((inst[n].flags & SPECTRE_IGNORE) || + (k >= 0 && (sym[k].flags & SPECTRE_IGNORE))) ) return 0; + if( xctx->netlist_type == CAD_SPICE_NETLIST && ((inst[n].flags & SPICE_IGNORE) || (k >= 0 && (sym[k].flags & SPICE_IGNORE))) ) return 0; @@ -1315,6 +1326,7 @@ static int name_nodes_of_pins_labels_and_propagate() if( xctx->netlist_type == CAD_VERILOG_NETLIST && (inst[i].flags & VERILOG_IGNORE)) continue; if( xctx->netlist_type == CAD_SPICE_NETLIST && (inst[i].flags & SPICE_IGNORE)) continue; if( xctx->netlist_type == CAD_VHDL_NETLIST && (inst[i].flags & VHDL_IGNORE)) continue; + if( xctx->netlist_type == CAD_SPECTRE_NETLIST && (inst[i].flags & SPECTRE_IGNORE)) continue; if( xctx->netlist_type == CAD_TEDAX_NETLIST && (inst[i].flags & TEDAX_IGNORE)) continue; if( netlist_lvs_ignore && (inst[i].flags & LVS_IGNORE_OPEN)) continue; } diff --git a/src/options.c b/src/options.c index 732cc3ed..be8c1e0a 100644 --- a/src/options.c +++ b/src/options.c @@ -129,6 +129,10 @@ static void check_opt(char *opt, char *optval, int type) dbg(1, "process_options(): set netlist type to spice\n"); cli_opt_netlist_type = CAD_SPICE_NETLIST; + } else if( type == LONG && !strcmp("spectre", opt) ) { + dbg(1, "process_options(): set netlist type to spectre\n"); + cli_opt_netlist_type = CAD_SPECTRE_NETLIST; + } else if( (type == SHORT && *opt == 'y') || (type == LONG && !strcmp("symbol", opt)) ) { dbg(1, "process_options(): set netlist type to symbol\n"); cli_opt_netlist_type = CAD_SYMBOL_ATTRS; diff --git a/src/save.c b/src/save.c index fbf6f541..0aae13de 100644 --- a/src/save.c +++ b/src/save.c @@ -2746,6 +2746,9 @@ static void write_xschem_file(FILE *fd) fprintf(fd, "S "); save_ascii_string(xctx->schprop,fd, 1); + fprintf(fd, "F "); + save_ascii_string(xctx->schspectreprop,fd, 1); + fprintf(fd, "E "); save_ascii_string(xctx->schtedaxprop,fd, 1); @@ -3096,8 +3099,8 @@ static void read_xschem_file(FILE *fd) case '#': read_line(fd, 1); break; - case 'F': /* extension for future symbol floater labels */ - read_line(fd, 1); + case 'F': /* spectre global attribute */ + load_ascii_string(&xctx->schspectreprop,fd); break; case 'E': load_ascii_string(&xctx->schtedaxprop,fd); @@ -4362,15 +4365,15 @@ int load_sym_def(const char *name, FILE *embed_fd) case '#': read_line(lcc[level].fd, 1); break; - case 'F': /* extension for future symbol floater labels */ - read_line(lcc[level].fd, 1); - break; case 'E': load_ascii_string(&aux_ptr, lcc[level].fd); break; case 'V': load_ascii_string(&aux_ptr, lcc[level].fd); break; + case 'F': + load_ascii_string(&aux_ptr, lcc[level].fd); + break; case 'S': load_ascii_string(&aux_ptr, lcc[level].fd); break; diff --git a/src/scheduler.c b/src/scheduler.c index 224fc345..331864fa 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1519,6 +1519,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(xctx->netlist_type == CAD_VERILOG_NETLIST) { my_snprintf(f, S(f), "%s.v", get_cell(xctx->current_name, 0)); } + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) { + my_snprintf(f, S(f), "%s.sim", get_cell(xctx->current_name, 0)); + } else if(xctx->netlist_type == CAD_TEDAX_NETLIST) { my_snprintf(f, S(f), "%s.tdx", get_cell(xctx->current_name, 0)); } @@ -1542,6 +1545,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(xctx->netlist_type == CAD_VHDL_NETLIST) { Tcl_SetResult(interp, "vhdl", TCL_STATIC); } + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) { + Tcl_SetResult(interp, "spectre", TCL_STATIC); + } else if(xctx->netlist_type == CAD_VERILOG_NETLIST) { Tcl_SetResult(interp, "verilog", TCL_STATIC); } @@ -1631,6 +1637,10 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg { Tcl_SetResult(interp, xctx->schverilogprop ? xctx->schverilogprop : "", TCL_VOLATILE); } + else if(!strcmp(argv[2], "schspectreprop")) /* get schematic "spectre" global attributes */ + { + Tcl_SetResult(interp, xctx->schspectreprop ? xctx->schspectreprop : "", TCL_VOLATILE); + } else if(!strcmp(argv[2], "schsymbolprop")) /* get schematic "symbol" global attributes */ { Tcl_SetResult(interp, xctx->schsymbolprop ? xctx->schsymbolprop : "", TCL_VOLATILE); @@ -3476,6 +3486,8 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg err = global_vhdl_netlist(hier_netlist, alert); else if(xctx->netlist_type == CAD_VERILOG_NETLIST) err = global_verilog_netlist(hier_netlist, alert); + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) + err = global_spectre_netlist(hier_netlist, alert); else if(xctx->netlist_type == CAD_TEDAX_NETLIST) global_tedax_netlist(hier_netlist, alert); else @@ -5398,6 +5410,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[3], "vhdl")) { xctx->netlist_type=CAD_VHDL_NETLIST; } + else if(!strcmp(argv[3], "spectre")) { + xctx->netlist_type=CAD_SPECTRE_NETLIST; + } else if(!strcmp(argv[3], "verilog")) { xctx->netlist_type=CAD_VERILOG_NETLIST; } @@ -5459,6 +5474,10 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schverilogprop, argv[3]); } + else if(!strcmp(argv[2], "schspectreprop")) { /* set schematic global spectre attribute string */ + if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} + my_strdup(_ALLOC_ID_, &xctx->schspectreprop, argv[3]); + } else if(!strcmp(argv[2], "schvhdlprop")) { /* set schematic global vhdl attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schvhdlprop, argv[3]); diff --git a/src/spectre.awk b/src/spectre.awk new file mode 100755 index 00000000..e80ec34c --- /dev/null +++ b/src/spectre.awk @@ -0,0 +1,346 @@ +#!/usr/bin/awk -f +# +# File: spice.awk +# +# This file is part of XSCHEM, +# a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit +# simulation. +# Copyright (C) 1998-2024 Stefan Frederik Schippers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +BEGIN{ + lines = 0; + first=1 + user_code=0 #20180129 + + # used to handle strange primitives that have a type word before the instance name + special_devs["ymemristor"] = 1 + special_devs["ylin"] = 1 + special_devs["ydelay"] = 1 + special_devs["ytransline"] = 1 + special_devs["ypgbr"] = 1 + special_devs["ypowergridbranch"] = 1 + special_devs["yacc"] = 1 + special_devs[".model"] = 1 + special_devs[".subckt"] = 1 + + while( (ARGV[1] ~ /^[-]/) || (ARGV[1] ~ /^$/) ) { + if(ARGV[1] == "-xyce") { xyce = 1} + for(i=2; i<= ARGC;i++) { + ARGV[i-1] = ARGV[i] + } + ARGC-- + } +} + +# join split lines +{ + + if($0 ~ /^\*\*\*\* begin user (architecture|header) code/) { + user_code = 1 + } + if($0 ~ /^[+]/ && !user_code) { + yy = yy " " substr($0,2) + next + } + else { + zz = yy + yy = $0 + $0 = zz + if(first) { + first=0 + next + } + line[lines++] = $0 + } + if($0 ~ /^\*\*\*\* end user (architecture|header) code/) { + user_code = 0 + } +} + +END{ + user_code = 0 + $0=yy + line[lines++] = $0 + + ## resolve parametric instance name vector multiplicity + substitute_instance_param() + + for(i=0; i R1[10],R1[9],...,R1[5] +## before further processing netlist. +function substitute_instance_param( i, j, name, first, last) +{ + IGNORECASE=1 + for(i = 0;i < lines; i++) { + $0 = line[i] + if($0 ~/^\.param/) { + gsub(/ *= */, "=") + for(j = 2; j <= NF; j++) { + param = value = $j + sub(/=.*/, "", param) + sub(/.*=/,"", value) + par[param] = value + # print "*** " param " = " value + } + } + } + for(i = 0;i < lines; i++) { + $0 = line[i] + if($1 ~ /^[a-zA-Z][^\[\]:]+\[[a-zA-Z_][a-zA-Z0-9_]*:[a-zA-Z_][a-zA-Z0-9_]*\]$/) { + name = first = last = $1 + sub(/\[.*/, "", name) + sub(/^.*\[/, "", first) + sub(/:.*/,"", first) + sub(/^.*:/,"", last) + sub(/\]/,"", last) + if(first in par) first = par[first] + if(last in par) last = par[last] + + n="" + for(j= first;;) { + if(j!=first) n = n "," + n = n name "[" j "]" + if(j == last) break + j+= sign(last - first) + } + # print "*** " n + $1 = n + line[i] = $0 + } + } + IGNORECASE=0 +} + + +function sign(x) +{ + return x<0 ? -1 : x>0 ? 1 : 0 +} + +function process( i,j, iprefix, saveinstr, savetype, saveanalysis) +{ + + if($0 ~/\*\*\*\* end_element/){ + spiceprefix="" + return + } + if($0 ~/\*\*\*\* spice_prefix/){ + spiceprefix=$3 + return + } + if($0 ~/\*\*\*\* begin user header code/){ #20180129 + user_code=1 + return + } + if($0 ~/\*\*\*\* begin user architecture code/){ #20180129 + user_code=1 + print + return + } + if($0 ~/\*\*\*\* end user architecture code/){ #20180129 + user_code=0 + print + return + } + if($0 ~/\*\*\*\* end user header code/){ #20180129 + user_code=0 + return + } + if(user_code) { #20180129 + print + return + } + + # 20181208 do not process commented lines + if($1 ~/^\*/) { + print + return + } + + #20150922 handle the case of pmos elements that netlist also a diode + # format="@name @pinlist @model w=@w l=@l geomod=0 m=@m + # #dx#name 0 @@b dnwell area=... pj=..." + # replace #dx#name with dx prefix added on all @name elements + # example: + # m6[3] GWL WLDECF[3] WL[3] HDD phv w=50u l=10u geomod=0 m=1 + # m6[2] GWL WLDECF[2] WL[2] HDD phv w=50u l=10u geomod=0 m=1 + # m6[1] GWL WLDECF[1] WL[1] HDD phv w=50u l=10u geomod=0 m=1 + # m6[0] GWL WLDECF[0] WL[0] HDD phv w=50u l=10u geomod=0 m=1 + # dxm6[3] 0 HDD dnwell area='(50u + 73u)*(10u + 32u)' pj='2*(50u +73u)+2*(10u +32u)' + # dxm6[2] 0 HDD dnwell area='(50u + 73u)*(10u + 32u)' pj='2*(50u +73u)+2*(10u +32u)' + # dxm6[1] 0 HDD dnwell area='(50u + 73u)*(10u + 32u)' pj='2*(50u +73u)+2*(10u +32u)' + # dxm6[0] 0 HDD dnwell area='(50u + 73u)*(10u + 32u)' pj='2*(50u +73u)+2*(10u +32u)' + #20151027 do this for all fields + for(i=1; i<=NF;i++) { + + if($i ~/^##[a-zA-Z_]+/) { + sub(/^##/, "", $i) + } else + if($i ~/^#[a-zA-Z_0-9]+#[a-zA-Z_]+/) { + iprefix=$i + sub(/^#/,"",iprefix) + sub(/#.*/,"",iprefix) + sub("#" iprefix "#", iprefix,$i) + gsub(/,/, "," iprefix,$i) + ## 20160301 add '?1' if missing in format string + if(i>1 && ( $(i-1) !~/^\?/) ) { + $i = "?1 " $i + } + $0 = $0 # reparse input line + } + } + ## 20140506 do not transform {} of variation groups + ## nmos N { + ## ... + ## } + gsub(/PARAM:/,"") # stefan 20110627 + + if($0 ~/^[gG]/) { + IGNORECASE=1 + sub(/ value=/," cur=") + IGNORECASE=0 + } + if($0 ~/^[eE]/) { + IGNORECASE=1 + sub(/ value=/," vol=") + IGNORECASE=0 + } + if($0 ~/^[rR]/) { + IGNORECASE=1 + sub(/ value=/," r=") + IGNORECASE=0 + } + if($0 ~/^[cC]/) { + IGNORECASE=1 + sub(/ value=/," c=") + IGNORECASE=0 + } + ### ?? too dangerous + # gsub(/ value=/," ") + # gsub(/ VALUE=/," ") + + if($0 ~ /^D/ ) sub(/PERI[ \t]*=/,"PJ=") + + ## .save tran v(?1 GB ) v(?1 SB ) + ## ? may be followed by -1 in some cases + if(tolower($1) ~ /^\.(save|print)$/ && $0 ~/\?-?[0-9]/) { + $0 = tolower($0) + saveinstr = $1 + if(!xyce) { + $1="" + $0 = $0 # reparse line for field splitting + + gsub(/ *\?-?[0-9]+ */, "") # in some cases ?-1 is printed (unknow multiplicity) + gsub(/\( */, "(") + gsub(/ *\)/, ")") + for(i=1; i<=NF; i++) { + savetype=$i; sub(/\(.*/,"", savetype) # v(...) --> v + sub(/^.*\(/,"", $i) + sub(/\).*/,"", $i) + num = split($i, name, ",") + for(j=1; j<= num; j++) { + print saveinstr " " savetype "(" name[j] ")" + } + } + } + + } else if( $1 ~ /^\*\.(ipin|opin|iopin)/ ) { + num=split($2,name,",") + for(i=1;i<=num;i++) print $1 " " name[i] + } else if( tolower($1) ~ /\.subckt/) { + # remove m=.. from subcircuit definition since m= is a multiplier not a param + sub(/ m=[0-9]+/," ",$0) + gsub(","," ",$0) + print $0 + } else { + # handle uncommon primitives that have a prefix before the device name + if(tolower($1) in special_devs) { + devprefix = $1 + num = split($3, name, ",") + $1 = "" + for(i = 0; i < num; i++) { + if(i) $1 = $1 "," + $1 = $1 devprefix + } + num = split($1, name, ",") + $0 = $0 + } else { + num = split($1, name, ",") + } + if(num==0) print "" + + + for(j=2;j<=NF;j+=1) # start from 2 not from 3 20070221 + { + # ............ --> matches ?n and ?-n + # some CDL netlists have this: $SUB=?1 B where B is a node + if($j ~/^(.*=)?\?-?[0-9]+$/) continue # handle the case that $2 not pinlist 20070221 + arg_num[j]=split($j,tmp,",") + for(k=1;k<=arg_num[j]; k++) { + arg_name[j,k]=tmp[k] + } + } + for(i=1;i<=num;i++) + { + printf "%s ", spiceprefix name[i] + + for(j=2;j<=NF;j++) + { + # ............ --> matches ?n and ?-n + # some CDL netlists have this: $SUB=?1 B where B is a node + if($j !~ /^(.*=)?\?-?[0-9]+$/) + { + printf "%s ", $j # if not a node just print it + } + else + { + nmult=$(j) + if(nmult ~ /^(.*=)/) { # some CDL netlists have this: $SUB=?1 B where B is a node + sub(/=.*/, "=", nmult) + printf "%s", nmult + } + nmult=$(j++) + sub(/(.*=)?\?/,"",nmult) + if(nmult+0==-1) nmult=arg_num[j] + for(l=0;lformat, "lvs_format"); + else my_strdup(_ALLOC_ID_, &xctx->format, xctx->custom_format); + if(!spectre_stop) { + dbg(1, "spectre_netlist(): invoke prepare_netlist_structs for %s\n", xctx->current_name); + xctx->prep_net_structs = 0; + err |= prepare_netlist_structs(1); + err |= traverse_node_hash(); /* print all warnings about unconnected floatings etc */ + for(i=0;iinstances; ++i) /* print first ipin/opin defs ... */ + { + if(skip_instance(i, 1, lvs_ignore)) continue; + type = (xctx->inst[i].ptr+ xctx->sym)->type; + if( type && IS_PIN(type) ) { + if(top_sub && !flag) { + fprintf(fd, "*.PININFO "); + flag = 1; + } + if(top_sub) { + int d = 'X'; + if(!strcmp(type, "ipin")) d = 'I'; + if(!strcmp(type, "opin")) d = 'O'; + if(!strcmp(type, "iopin")) d = 'B'; + fprintf(fd, "%s:%c ",xctx->inst[i].lab, d); + } else { + print_spectre_element(fd, i) ; /* this is the element line */ + } + } + } + if(top_sub) fprintf(fd, "\n"); + for(i=0;iinstances; ++i) /* ... then print other lines */ + { + if(skip_instance(i, 1, lvs_ignore)) continue; + type = (xctx->inst[i].ptr+ xctx->sym)->type; + + if( type && !IS_LABEL_OR_PIN(type) ) { + /* already done in global_spectre_netlist */ + if(!strcmp(type,"netlist_commands") && xctx->netlist_count==0) continue; + if(xctx->netlist_count && + !strboolcmp(get_tok_value(xctx->inst[i].prop_ptr, "only_toplevel", 0), "true")) continue; + if(!strcmp(type,"netlist_commands")) { + fprintf(fd,"**** begin user architecture code\n"); + print_spectre_element(fd, i) ; /* this is the element line */ + fprintf(fd,"**** end user architecture code\n"); + } else { + char *val = NULL; + const char *m; + if(print_spectre_element(fd, i)) { + fprintf(fd, "**** end_element\n"); + } + /* hash device_model attribute if any */ + my_strdup2(_ALLOC_ID_, &val, get_tok_value(xctx->inst[i].prop_ptr, "device_model", 2)); + m = val; + if(strchr(val, '@')) m = translate(i, val); + else m = tcl_hook2(m); + if(m[0]) str_hash_lookup(&model_table, model_name(m), m, XINSERT); + else { + my_strdup2(_ALLOC_ID_, &val, + get_tok_value(xctx->sym[xctx->inst[i].ptr].prop_ptr, "device_model", 2)); + m = val; + if(strchr(val, '@')) m = translate(i, val); + else m = tcl_hook2(m); + if(m[0]) str_hash_lookup(&model_table, model_name(m), m, XINSERT); + } + my_free(_ALLOC_ID_, &model_name_result); + my_free(_ALLOC_ID_, &val); + } + } + } + } + if(!spectre_stop && !xctx->netlist_count) redraw_hilights(0); /* draw_hilight_net(1); */ + return err; +} + +/* alert: if set show alert if file missing */ +int global_spectre_netlist(int global, int alert) /* netlister driver */ +{ + int err = 0; + int first; + FILE *fd; + const char *str_tmp; + int multip; + unsigned int *stored_flags; + int i; + const char *type; + char *place=NULL; + char netl_filename[PATH_MAX]; /* overflow safe 20161122 */ + char tcl_cmd_netlist[PATH_MAX + 100]; /* 20081211 overflow safe 20161122 */ + char cellname[PATH_MAX]; /* 20081211 overflow safe 20161122 */ + char *subckt_name; + char *abs_path = NULL; + int split_f; + Str_hashtable subckt_table = {NULL, 0}; + Str_hashentry *model_entry; + int save_prev_mod = xctx->prev_set_modify; + struct stat buf; + char *top_symbol_name = NULL; + /* if top level has a symbol use it for pin ordering + * top_symbol_name == 1: a symbol file matching schematic has been found. + * top_symbol_name == 3: the found symbol has type=subcircuit and has ports */ + int found_top_symbol = 0; + int npins = 0; /* top schematic number of i/o ports */ + Sch_pin_record *pinnumber_list = NULL; /* list of top sch i/o ports ordered wrt sim_pinnumber attr */ + int uppercase_subckt = tclgetboolvar("uppercase_subckt"); + int lvs_netlist = tclgetboolvar("lvs_netlist"); + int top_sub = lvs_netlist || tclgetboolvar("top_is_subckt"); + int lvs_ignore = tclgetboolvar("lvs_ignore"); + + if(lvs_netlist) my_strdup(_ALLOC_ID_, &xctx->format, "lvs_format"); + else my_strdup(_ALLOC_ID_, &xctx->format, xctx->custom_format); + exit_code = 0; /* reset exit code */ + split_f = tclgetboolvar("split_files"); + dbg(1, "global_spectre_netlist(): invoking push_undo()\n"); + xctx->push_undo(); + xctx->netlist_unconn_cnt=0; /* unique count of unconnected pins while netlisting */ + statusmsg("",2); /* clear infowindow */ + str_hash_init(&subckt_table, HASHSIZE); + str_hash_init(&model_table, HASHSIZE); + record_global_node(2, NULL, NULL); /* delete list of global nodes */ + bus_char[0] = bus_char[1] = '\0'; + xctx->hiersep[0]='.'; xctx->hiersep[1]='\0'; + str_tmp = tclgetvar("bus_replacement_char"); + if(str_tmp && str_tmp[0] && str_tmp[1]) { + bus_char[0] = str_tmp[0]; + bus_char[1] = str_tmp[1]; + } + xctx->netlist_count=0; + my_snprintf(netl_filename, S(netl_filename), "%s/.%s_%d", + tclgetvar("netlist_dir"), get_cell(xctx->sch[xctx->currsch], 0), getpid()); + dbg(1, "global_spectre_netlist(): opening %s for writing\n",netl_filename); + fd=fopen(netl_filename, "w"); + if(fd==NULL) { + dbg(0, "global_spectre_netlist(): problems opening netlist file\n"); + return 1; + } + fprintf(fd, "** sch_path: %s\n", xctx->sch[xctx->currsch]); + + if(xctx->netlist_name[0]) { + my_snprintf(cellname, S(cellname), "%s", get_cell_w_ext(xctx->netlist_name, 0)); + } else { + my_snprintf(cellname, S(cellname), "%s.spice", get_cell(xctx->sch[xctx->currsch], 0)); + } + + first = 0; + for(i=0;iinstances; ++i) /* print netlist_commands of top level cell with 'place=header' property */ + { + if(skip_instance(i, 1, lvs_ignore)) continue; + type = (xctx->inst[i].ptr+ xctx->sym)->type; + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->sym[xctx->inst[i].ptr].prop_ptr,"place",0)); + if( type && !strcmp(type,"netlist_commands") ) { + if(!place) { + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->inst[i].prop_ptr,"place",0)); + } + if(place && !strcmp(place, "header" )) { + if(first == 0) fprintf(fd,"**** begin user header code\n"); + ++first; + print_spectre_element(fd, i) ; /* this is the element line */ + } + } + } + if(first) fprintf(fd,"**** end user header code\n"); + + /* netlist_options */ + for(i=0;iinstances; ++i) { + if(skip_instance(i, 1, lvs_ignore)) continue; + if(!(xctx->inst[i].ptr+ xctx->sym)->type) continue; + if( !strcmp((xctx->inst[i].ptr+ xctx->sym)->type,"netlist_options") ) { + netlist_options(i); + } + } + if(!top_sub) fprintf(fd,"**"); + if(uppercase_subckt) + fprintf(fd,"SUBCKT %s ( ", get_cell(xctx->sch[xctx->currsch], 0)); + else + fprintf(fd,"subckt %s ( ", get_cell(xctx->sch[xctx->currsch], 0)); + pinnumber_list = sort_schematic_pins(&npins); /* sort pins according to sim_pinnumber attr */ + + /* print top subckt ipin/opins */ + my_strdup2(_ALLOC_ID_, &top_symbol_name, abs_sym_path(add_ext(xctx->current_name, ".sym"), "")); + if(!stat(top_symbol_name, &buf)) { /* if top level has a symbol use the symbol for pin ordering */ + dbg(1, "found top level symbol %s\n", top_symbol_name); + load_sym_def(top_symbol_name, NULL); + found_top_symbol = 1; + if(xctx->sym[xctx->symbols - 1].type != NULL && + /* only use the symbol if it has pins and is a subcircuit ? */ + /* !strcmp(xctx->sym[xctx->symbols - 1].type, "subcircuit") && */ + xctx->sym[xctx->symbols - 1].rects[PINLAYER] > 0) { + fprintf(fd," "); + print_spectre_subckt_nodes(fd, xctx->symbols - 1); + found_top_symbol = 3; + err |= sym_vs_sch_pins(xctx->symbols - 1); + } + remove_symbol(xctx->symbols - 1); + } + my_free(_ALLOC_ID_, &top_symbol_name); + if(found_top_symbol != 3) { + for(i=0;iinst[n].lab ? xctx->inst[n].lab : ""), &multip); + /*must handle invalid node names */ + fprintf(fd, " %s", str_tmp ? str_tmp : "" ); + } + } + my_free(_ALLOC_ID_, &pinnumber_list); + fprintf(fd,")\n"); + + err |= spectre_netlist(fd, 0); + + first = 0; + for(i=0;iinstances; ++i) /* print netlist_commands of top level cell with no 'place=end' property + and no place=header */ + { + if(skip_instance(i, 1, lvs_ignore)) continue; + type = (xctx->inst[i].ptr+ xctx->sym)->type; + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->sym[xctx->inst[i].ptr].prop_ptr,"place",0)); + if( type && !strcmp(type,"netlist_commands") ) { + if(!place) { + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->inst[i].prop_ptr,"place",0)); + } + if(!place || (strcmp(place, "end") && strcmp(place, "header")) ) { + if(first == 0) fprintf(fd,"**** begin user architecture code\n"); + ++first; + print_spectre_element(fd, i) ; /* this is the element line */ + } + } + } + + xctx->netlist_count++; + + if(xctx->schprop && xctx->schprop[0]) { + if(first == 0) fprintf(fd,"**** begin user architecture code\n"); + ++first; + fprintf(fd, "%s\n", xctx->schprop); + } + if(first) fprintf(fd,"**** end user architecture code\n"); + /* /20100217 */ + + if(!top_sub) fprintf(fd,"**"); + if(uppercase_subckt) + fprintf(fd, "ENDS\n"); + else + fprintf(fd, "ends\n"); + + + if(split_f) { + int save; + fclose(fd); + my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} noshow {%s}", netl_filename, cellname); + save = xctx->netlist_type; + xctx->netlist_type = CAD_SPECTRE_NETLIST; + set_tcl_netlist_type(); + tcleval(tcl_cmd_netlist); + xctx->netlist_type = save; + set_tcl_netlist_type(); + + if(debug_var==0) xunlink(netl_filename); + } + + /* warning if two symbols perfectly overlapped */ + err |= warning_overlapped_symbols(0); + /* preserve current level instance flags before descending hierarchy for netlisting, restore later */ + stored_flags = my_calloc(_ALLOC_ID_, xctx->instances, sizeof(unsigned int)); + for(i=0;iinstances; ++i) stored_flags[i] = xctx->inst[i].color; + + if(global) + { + int saved_hilight_nets = xctx->hilight_nets; + int web_url = is_from_web(xctx->current_dirname); + char *current_dirname_save = NULL; + + my_strdup2(_ALLOC_ID_, ¤t_dirname_save, xctx->current_dirname); + unselect_all(1); + /* ensure all unused symbols purged before descending hierarchy */ + if(!tclgetboolvar("keep_symbols")) remove_symbols(); + /* reload data without popping undo stack, this populates embedded symbols if any */ + dbg(1, "global_spectre_netlist(): invoking pop_undo(2, 0)\n"); + xctx->pop_undo(2, 0); + /* link_symbols_to_instances(-1); */ /* done in xctx->pop_undo() */ + my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], xctx->sch_path[xctx->currsch]); + my_strcat(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], "->netlisting"); + xctx->sch_path_hash[xctx->currsch+1] = 0; + xctx->currsch++; + subckt_name=NULL; + dbg(2, "global_spectre_netlist(): last defined symbol=%d\n",xctx->symbols); + get_additional_symbols(1); + for(i=0;isymbols; ++i) + { + if(xctx->sym[i].flags & (SPICE_IGNORE | SPICE_SHORT)) continue; + 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_, &xctx->hier_attr[xctx->currsch - 1].sym_extra, + get_tok_value(xctx->sym[i].prop_ptr, "extra", 0)); + my_strdup(_ALLOC_ID_, &abs_path, abs_sym_path(xctx->sym[i].name, "")); + if(strcmp(xctx->sym[i].type,"subcircuit")==0 && check_lib(1, abs_path)) + { + if(!web_url) { + tclvareval("get_directory [list ", xctx->sch[xctx->currsch - 1], "]", NULL); + my_strncpy(xctx->current_dirname, tclresult(), S(xctx->current_dirname)); + } + /* xctx->sym can be SCH or SYM, use hash to avoid writing duplicate subckt */ + my_strdup(_ALLOC_ID_, &subckt_name, get_cell(xctx->sym[i].name, 0)); + dbg(1, "global_spectre_netlist(): subckt_name=%s\n", subckt_name); + if (str_hash_lookup(&subckt_table, subckt_name, "", XLOOKUP)==NULL) + { + /* do not insert symbols with default_schematic attribute set to ignore in hash since these symbols + * will not be processed by *_block_netlist() */ + if(strcmp(get_tok_value(xctx->sym[i].prop_ptr, "default_schematic", 0), "ignore")) + str_hash_lookup(&subckt_table, subckt_name, "", XINSERT); + if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"vhdl_netlist",0),"true")==0 ) + err |= vhdl_block_netlist(fd, i, alert); + else if(split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"verilog_netlist",0),"true")==0 ) + err |= verilog_block_netlist(fd, i, alert); + else if(split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_netlist",0),"true")==0 ) + err |= spice_block_netlist(fd, i, alert); + else + if( strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spectre_primitive",0),"true") ) + err |= spectre_block_netlist(fd, i, alert); + } + } + } + if(xctx->hier_attr[xctx->currsch - 1].templ) + my_free(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch - 1].templ); + if(xctx->hier_attr[xctx->currsch - 1].prop_ptr) + my_free(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch - 1].prop_ptr); + if(xctx->hier_attr[xctx->currsch - 1].sym_extra) + my_free(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch - 1].sym_extra); + my_free(_ALLOC_ID_, &abs_path); + /* get_additional_symbols(0); */ + my_free(_ALLOC_ID_, &subckt_name); + /*clear_drawing(); */ + my_free(_ALLOC_ID_, &xctx->sch[xctx->currsch]); + xctx->currsch--; + unselect_all(1); + dbg(1, "global_spectre_netlist(): invoking pop_undo(0, 0)\n"); + /* symbol vs schematic pin check, we do it here since now we have ALL symbols loaded */ + err |= sym_vs_sch_pins(-1); + if(!tclgetboolvar("keep_symbols")) remove_symbols(); + xctx->pop_undo(4, 0); + xctx->prev_set_modify = save_prev_mod; + if(web_url) { + my_strncpy(xctx->current_dirname, current_dirname_save, S(xctx->current_dirname)); + } else { + tclvareval("get_directory [list ", xctx->sch[xctx->currsch], "]", NULL); + my_strncpy(xctx->current_dirname, tclresult(), S(xctx->current_dirname)); + } + my_strncpy(xctx->current_name, rel_sym_path(xctx->sch[xctx->currsch]), S(xctx->current_name)); + dbg(1, "spectre_netlist(): invoke prepare_netlist_structs for %s\n", xctx->current_name); + err |= prepare_netlist_structs(1); /* so 'lab=...' attributes for unnamed nets are set */ + if(!xctx->hilight_nets) xctx->hilight_nets = saved_hilight_nets; + my_free(_ALLOC_ID_, ¤t_dirname_save); + } + /* restore hilight flags from errors found analyzing top level before descending hierarchy */ + for(i=0;iinstances; ++i) if(!xctx->inst[i].color) xctx->inst[i].color = stored_flags[i]; + propagate_hilights(1, 0, XINSERT_NOREPLACE); + draw_hilight_net(1); + my_free(_ALLOC_ID_, &stored_flags); + + /* print globals nodes found in netlist 28032003 */ + if(!split_f) { + record_global_node(0,fd,NULL); + /* record_global_node(2, NULL, NULL); */ /* delete list --> do it in xwin_exit() */ + } + + /* =================================== 20121223 */ + first = 0; + if(!split_f) { + for(i=0;iinstances; ++i) /* print netlist_commands of top level cell with 'place=end' property */ + { + if(skip_instance(i, 1, lvs_ignore)) continue; + type = (xctx->inst[i].ptr+ xctx->sym)->type; + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->sym[xctx->inst[i].ptr].prop_ptr,"place",0)); + if( type && !strcmp(type,"netlist_commands") ) { + if(place && !strcmp(place, "end" )) { + if(first == 0) fprintf(fd,"**** begin user architecture code\n"); + ++first; + print_spectre_element(fd, i) ; + } else { + my_strdup(_ALLOC_ID_, &place,get_tok_value(xctx->inst[i].prop_ptr,"place",0)); + if(place && !strcmp(place, "end" )) { + if(first == 0) fprintf(fd,"**** begin user architecture code\n"); + ++first; + print_spectre_element(fd, i) ; + } + } + } /* netlist_commands */ + } + + } + /* print device_model attributes */ + for(i=0;ivalue); + model_entry = model_entry->next; + } + } + str_hash_free(&model_table); + str_hash_free(&subckt_table); + if(first) fprintf(fd,"**** end user architecture code\n"); + + + /* 20150922 added split_files check */ + if( !top_sub && !split_f) fprintf(fd, ".end\n"); + + dbg(1, "global_spectre_netlist(): starting awk on netlist!\n"); + + + if(!split_f) { + fclose(fd); + if(tclgetboolvar("netlist_show")) { + my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} show {%s}", netl_filename, cellname); + tcleval(tcl_cmd_netlist); + } + else { + my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} noshow {%s}", netl_filename, cellname); + tcleval(tcl_cmd_netlist); + } + if(!debug_var) xunlink(netl_filename); + } + my_free(_ALLOC_ID_, &place); + xctx->netlist_count = 0; + tclvareval("show_infotext ", my_itoa(err), NULL); /* critical error: force ERC window showing */ + exit_code = err ? 10 : 0; + return err; +} + +/* alert: if set show alert if file missing */ +int spectre_block_netlist(FILE *fd, int i, int alert) +{ + int err = 0; + int spectre_stop=0; + char netl_filename[PATH_MAX]; + char tcl_cmd_netlist[PATH_MAX + 100]; + char cellname[PATH_MAX]; + char filename[PATH_MAX]; + /* int j; */ + /* int multip; */ + char *extra=NULL; + int split_f; + char *sym_def = NULL; + char *name = NULL; + const char *default_schematic; + int uppercase_subckt = tclgetboolvar("uppercase_subckt"); + + split_f = tclgetboolvar("split_files"); + + if(!strboolcmp( get_tok_value(xctx->sym[i].prop_ptr,"spectre_stop",0),"true") ) + spectre_stop=1; + else + spectre_stop=0; + if(!strcmp(get_tok_value(xctx->sym[i].prop_ptr, "format", 0), "")) { + return err; + } + get_sch_from_sym(filename, xctx->sym + i, -1, 0); + default_schematic = get_tok_value(xctx->sym[i].prop_ptr, "default_schematic", 0); + if(!strcmp(default_schematic, "ignore")) { + return err; + } + my_strdup(_ALLOC_ID_, &name, tcl_hook2(xctx->sym[i].name)); + + dbg(1, "spectre_block_netlist(): filename=%s\n", filename); + if(split_f) { + my_snprintf(netl_filename, S(netl_filename), "%s/.%s_%d", + tclgetvar("netlist_dir"), get_cell(name, 0), getpid()); + dbg(1, "spectre_block_netlist(): split_files: netl_filename=%s\n", netl_filename); + fd=fopen(netl_filename, "w"); + if(!fd) { + dbg(0, "spectre_block_netlist(): unable to write file %s\n", netl_filename); + err = 1; + goto err; + } + my_snprintf(cellname, S(cellname), "%s.spice", get_cell(name, 0)); + } + fprintf(fd, "\n* expanding symbol: %s # of pins=%d\n", name,xctx->sym[i].rects[PINLAYER] ); + if(xctx->sym[i].base_name) fprintf(fd, "** sym_path: %s\n", abs_sym_path(xctx->sym[i].base_name, "")); + else fprintf(fd, "** sym_path: %s\n", sanitized_abs_sym_path(name, "")); + my_strdup(_ALLOC_ID_, &sym_def, get_tok_value(xctx->sym[i].prop_ptr,"spectre_sym_def",0)); + if(sym_def) { + 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, NULL, NULL); + my_free(_ALLOC_ID_, &symname_attr); + fprintf(fd, "%s\n", translated_sym_def); + my_free(_ALLOC_ID_, &sym_def); + } else { + const char *s = get_cell(sanitize(name), 0); + fprintf(fd, "** sch_path: %s\n", sanitized_abs_sym_path(filename, "")); + if(uppercase_subckt) + fprintf(fd, "SUBCKT %s ( ", s); + else + fprintf(fd, "subckt %s ( ", s); + print_spectre_subckt_nodes(fd, i); + fprintf(fd, ") "); + my_strdup(_ALLOC_ID_, &extra, get_tok_value(xctx->sym[i].prop_ptr,"extra",0) ); + /* this is now done in print_spectre_subckt_nodes */ + /* + * fprintf(fd, "%s ", extra ? extra : "" ); + */ + + /* 20081206 new get_sym_template does not return token=value pairs where token listed in extra */ + fprintf(fd, "\nparameters %s", get_sym_template(xctx->sym[i].templ, extra)); + my_free(_ALLOC_ID_, &extra); + fprintf(fd, "\n"); + + spectre_stop ? load_schematic(0,filename, 0, alert) : load_schematic(1,filename, 0, alert); + get_additional_symbols(1); + err |= spectre_netlist(fd, spectre_stop); /* 20111113 added spectre_stop */ + err |= warning_overlapped_symbols(0); + if(xctx->schprop && xctx->schprop[0]) { + fprintf(fd,"**** begin user architecture code\n"); + fprintf(fd, "%s\n", xctx->schprop); + fprintf(fd,"**** end user architecture code\n"); + } + if(uppercase_subckt) + fprintf(fd, "ENDS\n\n"); + else + fprintf(fd, "ends\n\n"); + } + if(split_f) { + int save; + fclose(fd); + my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} noshow {%s}", netl_filename, cellname); + save = xctx->netlist_type; + xctx->netlist_type = CAD_SPECTRE_NETLIST; + set_tcl_netlist_type(); + tcleval(tcl_cmd_netlist); + xctx->netlist_type = save; + set_tcl_netlist_type(); + if(debug_var==0) xunlink(netl_filename); + } + err: + xctx->netlist_count++; + my_free(_ALLOC_ID_, &name); + return err; +} + diff --git a/src/spice_netlist.c b/src/spice_netlist.c index a985b262..66871ffa 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -496,6 +496,8 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ err |= vhdl_block_netlist(fd, i, alert); else if(split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"verilog_netlist",0),"true")==0 ) err |= verilog_block_netlist(fd, i, alert); + else if(split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spectre_netlist",0),"true")==0 ) + err |= spectre_block_netlist(fd, i, alert); else if( strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_primitive",0),"true") ) err |= spice_block_netlist(fd, i, alert); diff --git a/src/token.c b/src/token.c index 56c1f75f..cd3fa09e 100644 --- a/src/token.c +++ b/src/token.c @@ -2159,6 +2159,163 @@ void print_spice_subckt_nodes(FILE *fd, int symbol) } +void print_spectre_subckt_nodes(FILE *fd, int symbol) +{ + int i=0, multip; + const char *str_ptr=NULL; + register int c, state=TOK_BEGIN, space; + char *format=NULL, *format1 = NULL, *s, *token=NULL; + int pin_number; + size_t sizetok=0; + size_t token_pos=0; + int escape=0; + int no_of_pins=0; + char *result = NULL; + const char *tclres, *fmt_attr = NULL; + + fmt_attr = xctx->format ? xctx->format : "spectre_format"; + my_strdup(_ALLOC_ID_, &format1, get_tok_value(xctx->sym[symbol].prop_ptr, fmt_attr, 2)); + if(!xctx->tok_size && strcmp(fmt_attr, "spectre_format") ) + my_strdup(_ALLOC_ID_, &format1, get_tok_value(xctx->sym[symbol].prop_ptr, "spectre_format", 2)); + dbg(1, "print_spectre_subckt(): format1=%s\n", format1); + + /* can not do this, since @symname is used as a token later in format parser */ + /* my_strdup(_ALLOC_ID_, &format1, + * str_replace(format1, "@symname", get_cell(xctx->sym[symbol].name, 0), '\\', -1)); */ + + if(format1 && strstr(format1, "tcleval(") == format1) { + tclres = tcl_hook2(format1); + if(!strcmp(tclres, "?\n")) { + char *ptr = strrchr(format1 + 8, ')'); + *ptr = '\0'; + my_strdup(_ALLOC_ID_, &format, format1 + 8); + } else my_strdup(_ALLOC_ID_, &format, tclres); + } else { + my_strdup(_ALLOC_ID_, &format, format1); + } + if(format1) my_free(_ALLOC_ID_, &format1); + dbg(1, "print_spectre_subckt(): format=%s\n", format); + if( format==NULL ) { + return; /* no format */ + } + no_of_pins= xctx->sym[symbol].rects[PINLAYER]; + s=format; + + /* begin parsing format string */ + while(1) + { + c=*s++; + if(c=='\\') { + escape=1; + c=*s++; + } + else escape=0; + if(c=='\n' && escape ) c=*s++; /* 20171030 eat escaped newlines */ + space=SPACE(c); + if( state==TOK_BEGIN && (c=='@' || c=='%') && !escape) state=TOK_TOKEN; + else if(state==TOK_TOKEN && token_pos > 1 && + ( + ( (space || c == '%' || c == '@') && !escape ) || + ( (!space && c != '%' && c != '@') && escape ) + ) + ) { + state = TOK_SEP; + } + + STR_ALLOC(&token, token_pos, &sizetok); + if(state==TOK_TOKEN) { + token[token_pos++]=(char)c; + } + else if(state==TOK_SEP) /* got a token */ + { + token[token_pos]='\0'; + token_pos=0; + if(!strcmp(token, "@spiceprefix")) { + /* do nothing */ + } + else if(!strcmp(token, "@name")) { + /* do nothing */ + } + else if(strcmp(token, "@symname")==0) { + break ; + } + else if(strcmp(token, "@pinlist")==0) { + Int_hashtable table = {NULL, 0}; + int_hash_init(&table, 37); + for(i=0;isym[symbol].rect[PINLAYER][i].prop_ptr,"spectre_ignore",0), "true")) { + const char *name = get_tok_value(xctx->sym[symbol].rect[PINLAYER][i].prop_ptr,"name",0); + if(!int_hash_lookup(&table, name, 1, XINSERT_NOREPLACE)) { + str_ptr= expandlabel(name, &multip); + /* fprintf(fd, "%s ", str_ptr); */ + my_mstrcat(_ALLOC_ID_, &result, str_ptr, " ", NULL); + } + } + } + int_hash_free(&table); + } + else if(token[0]=='@' && token[1]=='@') { /* recognize single pins 15112003 */ + char *prop=NULL; + for(i = 0; isym[symbol].rect[PINLAYER][i].prop_ptr; + if(!strcmp(get_tok_value(prop, "name",0), token + 2)) break; + } + if(i= 0 && pin_number < no_of_pins) { + if(strboolcmp(get_tok_value(xctx->sym[symbol].rect[PINLAYER][pin_number].prop_ptr,"spectre_ignore",0), "true")) { + str_ptr = get_tok_value(xctx->sym[symbol].rect[PINLAYER][pin_number].prop_ptr,"name",0); + /* fprintf(fd, "%s ", expandlabel(str_ptr, &multip)); */ + my_mstrcat(_ALLOC_ID_, &result, expandlabel(str_ptr, &multip), " ", NULL); + } + } + my_free(_ALLOC_ID_, &pin_attr); + my_free(_ALLOC_ID_, &pin_num_or_name); + + } + /* this will print the other @parameters, usually "extra" nodes so they will be in the order + * specified by the format string. The 'extra' attribute is no more used to print extra nodes + * in spice_block_netlist(). */ + else if(token[0] == '@') { /* given previous if() conditions not followed by @ or # */ + /* if token not followed by white space it is not an extra node */ + if( ( (space || c == '%' || c == '@') && !escape ) ) { + /* fprintf(fd, "%s ", token + 1); */ + my_mstrcat(_ALLOC_ID_, &result, token + 1, " ", NULL); + } + } + /* if(c!='%' && c!='@' && c!='\0' ) fputc(c,fd); */ + if(c == '@' || c =='%') s--; + state=TOK_BEGIN; + } + /* 20151028 dont print escaping backslashes */ + else if(state==TOK_BEGIN && c!='\0') { + /* do nothing */ + } + if(c=='\0') + { + my_mstrcat(_ALLOC_ID_, &result, "\n", NULL); + break ; + } + } + if(result) { + fprintf(fd, "%s", result); + my_free(_ALLOC_ID_, &result); + } + my_free(_ALLOC_ID_, &format1); + my_free(_ALLOC_ID_, &format); + my_free(_ALLOC_ID_, &token); +} + int has_token(const char *s, const char *tok) { int i = 1; @@ -2572,6 +2729,398 @@ int print_spice_element(FILE *fd, int inst) return 1; } +int print_spectre_element(FILE *fd, int inst) +{ + int i=0, multip, itmp; + const char *str_ptr=NULL; + register int c, state=TOK_BEGIN, space; + char *template=NULL,*format=NULL, *s, *name=NULL, *token=NULL; + const char *lab; + const char *value = NULL; + /* char *translatedvalue = NULL; */ + size_t sizetok=0; + size_t token_pos=0; + int escape=0; + int no_of_pins=0; + char *result = NULL; + size_t size = 0; + char *spiceprefixtag = NULL; + const char *fmt_attr = NULL; + + size = CADCHUNKALLOC; + my_realloc(_ALLOC_ID_, &result, size); + result[0] = '\0'; + + my_strdup(_ALLOC_ID_, &template, (xctx->inst[inst].ptr + xctx->sym)->templ); + my_strdup(_ALLOC_ID_, &name,xctx->inst[inst].instname); + if (!name) my_strdup(_ALLOC_ID_, &name, get_tok_value(template, "name", 0)); + + fmt_attr = xctx->format ? xctx->format : "spectre_format"; + /* allow format string override in instance */ + my_strdup(_ALLOC_ID_, &format, get_tok_value(xctx->inst[inst].prop_ptr, fmt_attr, 2)); + /* get netlist format rule from symbol */ + if(!xctx->tok_size) + my_strdup(_ALLOC_ID_, &format, get_tok_value(xctx->sym[xctx->inst[inst].ptr].prop_ptr, fmt_attr, 2)); + /* allow format string override in instance */ + if(!xctx->tok_size && strcmp(fmt_attr, "spectre_format") ) + my_strdup(_ALLOC_ID_, &format, get_tok_value(xctx->inst[inst].prop_ptr, "spectre_format", 2)); + /* get netlist format rule from symbol */ + if(!xctx->tok_size && strcmp(fmt_attr, "spectre_format")) + my_strdup(_ALLOC_ID_, &format, get_tok_value(xctx->sym[xctx->inst[inst].ptr].prop_ptr, "spectre_format", 2)); + if ((name==NULL) || (format==NULL)) { + my_free(_ALLOC_ID_, &template); + my_free(_ALLOC_ID_, &format); + my_free(_ALLOC_ID_, &name); + my_free(_ALLOC_ID_, &result); + return 0; /* do no netlist unwanted insts(no format) */ + } + no_of_pins= (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]; + s=format; + dbg(1, "print_spectre_element(): name=%s, format=%s xctx->netlist_count=%d\n",name,format, xctx->netlist_count); + /* begin parsing format string */ + while(1) + { + /* always make room for some characters so the single char writes to result do not need reallocs */ + c=*s++; + if(c=='\\') { + escape=1; + c=*s++; + } + else escape=0; + + if (c=='\n' && escape) c=*s++; /* 20171030 eat escaped newlines */ + space=SPACE(c); + if ( state==TOK_BEGIN && (c=='@'|| c=='%') && !escape ) state=TOK_TOKEN; + else if(state==TOK_TOKEN && token_pos > 1 && + ( + ( (space || c == '%' || c == '@') && !escape ) || + ( (!space && c != '%' && c != '@') && escape ) + ) + ) { + dbg(1, "print_spectre_element(): c=%c, space=%d, escape=%d token_pos=%d\n", c, space, escape, token_pos); + state=TOK_SEP; + } + STR_ALLOC(&token, token_pos, &sizetok); + if(state==TOK_TOKEN) { + token[token_pos++]=(char)c; + } + else if (state==TOK_SEP) /* got a token */ + { + char *val = NULL; + size_t token_exists = 0; + token[token_pos]='\0'; + token_pos=0; + + if(strcmp(token,"@symref")==0) + { + const char *s = get_sym_name(inst, 9999, 1, 0); + 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))); + my_mstrcat(_ALLOC_ID_, &result, s, NULL); + } + 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))); + my_mstrcat(_ALLOC_ID_, &result, s, NULL); + } + 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); + my_mstrcat(_ALLOC_ID_, &result, topsch, NULL); + } + else if(strcmp(token,"@schname_ext")==0) /* of course schname must not be present in attributes */ + { + my_mstrcat(_ALLOC_ID_, &result, xctx->current_name, NULL); + } + else if(strcmp(token,"@savecurrent")==0) + { + char *instname = xctx->inst[inst].instname; + + 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")) { + 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); + my_mstrcat(_ALLOC_ID_, &result, schname, NULL); + } + else if(strcmp(token,"@pinlist")==0) /* of course pinlist must not be present in attributes */ + /* print multiplicity */ + { /* and node number: m1 n1 m2 n2 .... */ + Int_hashtable table = {NULL, 0}; + int_hash_init(&table, 37); + for(i=0;iinst[inst].ptr + xctx->sym)->rect[PINLAYER][i].prop_ptr; + int spectre_ignore = !strboolcmp(get_tok_value(prop, "spectre_ignore", 0), "true"); + const char *name = get_tok_value(prop, "name", 0); + if(!spectre_ignore) { + if(!int_hash_lookup(&table, name, 1, XINSERT_NOREPLACE)) { + str_ptr = net_name(inst, i, &multip, 0, 1); + + my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); + } + } + } + int_hash_free(&table); + } + else if(token[0]=='@' && token[1]=='@') { /* recognize single pins 15112003 */ + 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,"spectre_ignore",0), "true")) { + str_ptr = net_name(inst,i, &multip, 0, 1); + + my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); + } + break; + } + } + } + /* reference by pin number instead of pin name, allows faster lookup of the attached net name + * @#0, @#1:net_name, @#2:name, ... */ + else if(token[0]=='@' && token[1]=='#') { + int n; + char *pin_attr = NULL; + char *pin_num_or_name = NULL; + + get_pin_and_attr(token, &pin_num_or_name, &pin_attr); + n = get_inst_pin_number(inst, pin_num_or_name); + if(n>=0 && pin_attr[0] && n < (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]) { + char *pin_attr_value = NULL; + int is_net_name = !strcmp(pin_attr, "net_name"); + /* get pin_attr value from instance: "pinnumber(ENABLE)=5" --> return 5, attr "pinnumber" of pin "ENABLE" + * "pinnumber(3)=6 --> return 6, attr "pinnumber" of 4th pin */ + if(!is_net_name) { + pin_attr_value = get_pin_attr_from_inst(inst, n, pin_attr); + /* get pin_attr from instance pin attribute string */ + if(!pin_attr_value) { + my_strdup(_ALLOC_ID_, &pin_attr_value, + get_tok_value(xctx->sym[xctx->inst[inst].ptr].rect[PINLAYER][n].prop_ptr, pin_attr, 0)); + } + } + /* @#n:net_name attribute (n = pin number or name) will translate to net name attached to pin */ + if(!pin_attr_value && is_net_name) { + prepare_netlist_structs(0); + my_strdup(_ALLOC_ID_, &pin_attr_value, + xctx->inst[inst].node && xctx->inst[inst].node[n] ? xctx->inst[inst].node[n] : "?"); + } + if(!pin_attr_value ) my_strdup(_ALLOC_ID_, &pin_attr_value, "--UNDEF--"); + value = pin_attr_value; + /* recognize slotted devices: instname = "U3:3", value = "a:b:c:d" --> value = "c" */ + if(value[0] && !strcmp(pin_attr, "pinnumber") ) { + char *ss; + int slot; + char *tmpstr = NULL; + tmpstr = my_malloc(_ALLOC_ID_, sizeof(xctx->inst[inst].instname)); + if( (ss=strchr(xctx->inst[inst].instname, ':')) ) { + sscanf(ss+1, "%s", tmpstr); + if(isonlydigit(tmpstr)) { + slot = atoi(tmpstr); + if(strstr(value,":")) value = find_nth(value, ":", "", 0, slot); + } + } + my_free(_ALLOC_ID_, &tmpstr); + } + my_mstrcat(_ALLOC_ID_, &result, value, NULL); + my_free(_ALLOC_ID_, &pin_attr_value); + } + else if(n>=0 && n < (xctx->inst[inst].ptr + xctx->sym)->rects[PINLAYER]) { + const char *si; + char *prop = (xctx->inst[inst].ptr + xctx->sym)->rect[PINLAYER][n].prop_ptr; + si = get_tok_value(prop, "spectre_ignore",0); + if(strboolcmp(si, "true")) { + str_ptr = net_name(inst,n, &multip, 0, 1); + + my_mstrcat(_ALLOC_ID_, &result, "?", my_itoa(multip), " ", str_ptr, " ", NULL); + } + } + my_free(_ALLOC_ID_, &pin_attr); + my_free(_ALLOC_ID_, &pin_num_or_name); + } + else if (!strncmp(token,"@tcleval", 8)) { + size_t s; + char *tclcmd=NULL; + const char *res; + s = token_pos + strlen(name) + strlen(xctx->inst[inst].name) + 100; + tclcmd = my_malloc(_ALLOC_ID_, s); + Tcl_ResetResult(interp); + my_snprintf(tclcmd, s, "tclpropeval {%s} {%s} {%s}", token, name, xctx->inst[inst].name); + dbg(1, "print_spectre_element(): tclpropeval {%s} {%s} {%s}", token, name, xctx->inst[inst].name); + res = tcleval(tclcmd); + + my_mstrcat(_ALLOC_ID_, &result, res, NULL); + my_free(_ALLOC_ID_, &tclcmd); + } + /* 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; + char *parent_prop_ptr = NULL; + char *parent_templ = NULL; + /* char *parent_sym_extra = 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; + /* parent_sym_extra = xctx->hier_attr[xctx->currsch - 1].sym_extra; */ + } + + /* 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, 0, xctx->inst[inst].prop_ptr, NULL, NULL, NULL)); + /* can not put template in above translate3: ---------------------------^^^^ + * if instance has VHI=VHI, format string has VHI=@VHI, and symbol template has VHI=3 + * we do not want token @VHI to resolve to 3, but stop at VHI as specified in instance */ + if(strchr(val, '@')) { + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, parent_prop_ptr, template, NULL)); + } + /* nmos instance format string: @model --> @modeln */ + dbg(1, "print_spectre_element(): 1st round: val: |%s|\n", val); + if(strchr(val, '@')) { + #if 0 + 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 { + #endif + my_strdup2(_ALLOC_ID_, &val, + translate3(val, 0, xctx->inst[inst].prop_ptr, NULL, NULL, NULL)); + dbg(1, "print_spectre_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_spectre_element(): 3nd round: val: |%s|\n", val); + /* normal passgate.sym placement, nmos instance format string: + * @modeln --> nfet_01v8 */ + } + #if 0 + } + #endif + dbg(1, "print_spectre_element(): final: val: |%s|\n", val); + } + /* still unresolved: set to empty */ + if(val[0] == '@') value = ""; + else value = val; + token_exists = xctx->tok_size; + tok_val_len = strlen(value); + /* @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(is_expr(value)) { + value = eval_expr(value); + } + /* 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'; + my_mstrcat(_ALLOC_ID_, &result, str, NULL); + } + /* ... 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'; + my_mstrcat(_ALLOC_ID_, &result, str, NULL); + } + if(c=='\0') + { + char str[2]; + str[0] = '\n'; + str[1] = '\0'; + my_mstrcat(_ALLOC_ID_, &result, str, NULL); + break; + } + } /* while(1) */ + + + /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions + * can be calculated */ + if(result) { + my_strdup(_ALLOC_ID_, &result, tcl_hook2(result)); + } + if(is_expr(result)) { + my_strdup2(_ALLOC_ID_, &result, eval_expr(result)); + } + if(result) fprintf(fd, "%s", result); + dbg(1, "print_spectre_element(): returning |%s|\n", result); + my_free(_ALLOC_ID_, &template); + my_free(_ALLOC_ID_, &format); + my_free(_ALLOC_ID_, &name); + my_free(_ALLOC_ID_, &token); + my_free(_ALLOC_ID_, &result); + if(spiceprefixtag) my_free(_ALLOC_ID_, &spiceprefixtag); + /* my_free(_ALLOC_ID_, &translatedvalue); */ + return 1; +} + void print_tedax_element(FILE *fd, int inst) { int i=0, multip; @@ -3331,7 +3880,8 @@ void print_verilog_element(FILE *fd, int inst) if(strcmp(token, "name") && xctx->tok_size && (!extra || !strstr(extra, token))) { if(value[0] != '\0') /* token has a value */ { - if(strcmp(token,"spice_ignore") && strcmp(token,"vhdl_ignore") && strcmp(token,"tedax_ignore")) { + if(strcmp(token,"spice_ignore") && strcmp(token,"vhdl_ignore") && + strcmp(token,"tedax_ignore") && strcmp(token,"spectre_ignore")) { if(tmp == 0) { fprintf(fd, "#(\n---- start parameters\n"); ++tmp; @@ -4595,7 +5145,15 @@ const char *translate(int inst, const char* s) memcpy(result+result_pos,xctx->schvhdlprop, tmp+1); result_pos+=tmp; } - + + else if(strcmp(token,"@schspectreprop")==0 && xctx->schspectreprop) + { + tmp=strlen(xctx->schspectreprop); + STR_ALLOC(&result, tmp + result_pos, &size); + memcpy(result+result_pos,xctx->schspectreprop, tmp+1); + result_pos+=tmp; + } + else if(strcmp(token,"@schprop")==0 && xctx->schprop) { tmp=strlen(xctx->schprop); @@ -4612,6 +5170,7 @@ const char *translate(int inst, const char* s) memcpy(result+result_pos,xctx->schsymbolprop, tmp+1); result_pos+=tmp; } + else if(strcmp(token,"@schtedaxprop")==0 && xctx->schtedaxprop) { tmp=strlen(xctx->schtedaxprop); diff --git a/src/verilog_netlist.c b/src/verilog_netlist.c index 630acc58..59819edf 100644 --- a/src/verilog_netlist.c +++ b/src/verilog_netlist.c @@ -351,6 +351,8 @@ int global_verilog_netlist(int global, int alert) /* netlister driver */ str_hash_lookup(&subckt_table, subckt_name, "", XINSERT); if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"vhdl_netlist",0),"true")==0 ) err |= vhdl_block_netlist(fd, i, alert); + if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spectre_netlist",0),"true")==0 ) + err |= spectre_block_netlist(fd, i, alert); else if(split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_netlist",0),"true")==0 ) err |= spice_block_netlist(fd, i, alert); else if( strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"verilog_primitive",0), "true")) @@ -451,7 +453,7 @@ int verilog_block_netlist(FILE *fd, int i, int alert) if(split_f) { my_snprintf(netl_filename, S(netl_filename), "%s/.%s_%d", tclgetvar("netlist_dir"), get_cell(name, 0), getpid()); - dbg(1, "global_vhdl_netlist(): split_files: netl_filename=%s\n", netl_filename); + dbg(1, "verilog_block_netlist(): split_files: netl_filename=%s\n", netl_filename); fd=fopen(netl_filename, "w"); if(!fd) { dbg(0, "verilog_block_netlist(): unable to write file %s\n", netl_filename); diff --git a/src/vhdl_netlist.c b/src/vhdl_netlist.c index c7d218c4..ccb7028d 100644 --- a/src/vhdl_netlist.c +++ b/src/vhdl_netlist.c @@ -441,6 +441,8 @@ int global_vhdl_netlist(int global, int alert) /* netlister driver */ str_hash_lookup(&subckt_table, subckt_name, "", XINSERT); if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"verilog_netlist",0),"true")==0 ) err |= verilog_block_netlist(fd, i, alert); + if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spectre_netlist",0),"true")==0 ) + err |= spectre_block_netlist(fd, i, alert); else if( split_f && strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_netlist",0),"true")==0 ) err |= spice_block_netlist(fd, i, alert); else if( strboolcmp(get_tok_value(xctx->sym[i].prop_ptr,"vhdl_primitive",0),"true")) diff --git a/src/xinit.c b/src/xinit.c index 45c679b6..83ae9707 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -2792,6 +2792,7 @@ int Tcl_AppInit(Tcl_Interp *inter) if(!strcmp(n, "spice")) xctx->netlist_type = CAD_SPICE_NETLIST; else if(!strcmp(n, "vhdl")) xctx->netlist_type = CAD_VHDL_NETLIST; else if(!strcmp(n, "verilog")) xctx->netlist_type = CAD_VERILOG_NETLIST; + else if(!strcmp(n, "spectre")) xctx->netlist_type = CAD_SPECTRE_NETLIST; else if(!strcmp(n, "tedax")) xctx->netlist_type = CAD_TEDAX_NETLIST; else if(!strcmp(n, "symbol")) xctx->netlist_type = CAD_SYMBOL_ATTRS; } @@ -3038,6 +3039,8 @@ int Tcl_AppInit(Tcl_Interp *inter) global_vhdl_netlist(1, 1); /* 1 means global netlist */ else if(xctx->netlist_type == CAD_VERILOG_NETLIST) global_verilog_netlist(1, 1); /* 1 means global netlist */ + else if(xctx->netlist_type == CAD_SPECTRE_NETLIST) + global_spectre_netlist(1, 1); /* 1 means global netlist */ else if(xctx->netlist_type == CAD_TEDAX_NETLIST) global_tedax_netlist(1, 1); /* 1 means global netlist */ } else { diff --git a/src/xschem.h b/src/xschem.h index 6afcf066..5bec6162 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -192,6 +192,8 @@ extern char win_temp_dir[PATH_MAX]; #define VERILOG_SHORT 8192 #define VHDL_SHORT 16384 #define TEDAX_SHORT 32768 +#define SPECTRE_SHORT 65536 +#define SPECTRE_IGNORE 131072 #define LVS_IGNORE (LVS_IGNORE_SHORT | LVS_IGNORE_OPEN) #define CADMAXGRIDPOINTS 512 #define CADMAXHIER 40 @@ -212,6 +214,7 @@ extern char win_temp_dir[PATH_MAX]; #define CAD_VERILOG_NETLIST 3 #define CAD_TEDAX_NETLIST 4 #define CAD_SYMBOL_ATTRS 5 +#define CAD_SPECTRE_NETLIST 6 /* possible states, encoded in global 'ui_state' */ #define STARTWIRE 1U @@ -666,6 +669,7 @@ typedef struct char *gptr; char *vptr; char *sptr; + char *fptr; /* spectre global attr */ char *kptr; char *eptr; int *lines; @@ -929,6 +933,7 @@ typedef struct { char *schvhdlprop; char *schsymbolprop; char *schverilogprop; + char *schspectreprop; char *sch[CADMAXHIER]; int currsch; char *version_string; @@ -1495,12 +1500,14 @@ extern void store_arc(int pos, double x, double y, double r, double a, double b, extern void hier_psprint(char **res, int what); extern int global_spice_netlist(int global, int alert); +extern int global_spectre_netlist(int global, int alert); extern int global_tedax_netlist(int global, int alert); extern int global_vhdl_netlist(int global, int alert); extern int global_verilog_netlist(int global, int alert); extern int vhdl_block_netlist(FILE *fd, int i, int alert); extern int verilog_block_netlist(FILE *fd, int i, int alert); extern int spice_block_netlist(FILE *fd, int i, int alert); +extern int spectre_block_netlist(FILE *fd, int i, int alert); extern void remove_symbols(void); extern void remove_symbol(int i); extern void clear_drawing(void); @@ -1636,6 +1643,8 @@ extern const char *translate3(const char* s, int eat_escapes, const char *s1, 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); +extern int print_spectre_element(FILE *fd, int inst); +extern void print_spectre_subckt_nodes(FILE *fd, int symbol); extern void print_tedax_subckt(FILE *fd, int symbol); extern void print_vhdl_element(FILE *fd, int inst); extern void print_verilog_element(FILE *fd, int inst); diff --git a/src/xschem.help b/src/xschem.help index c404aba2..2ee06dd1 100644 --- a/src/xschem.help +++ b/src/xschem.help @@ -24,6 +24,7 @@ Options: -N Set name (only name or full path) of top level netlist file. -t --tedax Set netlist type to tEDAx. -s --spice Set netlist type to SPICE. + --spice Set netlist type to SPECTRE. -y --symbol Set netlist type to SYMBOL (used when drawing symbols) -x --no_x Don't use X (only command mode). -z --rainbow Use a raibow-looking layer color table. diff --git a/src/xschem.tcl b/src/xschem.tcl index 3f7064a0..7fec5a5d 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -1004,6 +1004,15 @@ proc netlist {source_file show netlist_file} { textwindow $dest } } + + if {$netlist_type eq {spectre}} { + set cmd ${XSCHEM_SHAREDIR}/spectre.awk + eval exec {awk -f $cmd -- $source_file > $dest} + if ![string compare $show "show"] { + textwindow $dest + } + } + if {$netlist_type eq {vhdl}} { set cmd $XSCHEM_SHAREDIR/vhdl.awk eval exec {awk -f $cmd $source_file > $dest} @@ -1563,7 +1572,7 @@ proc set_sim_defaults {{reset {}}} { if {( $reset eq {reset} ) || ![info exists sim] || $failure} { if {[info exists sim]} {unset sim} # no simrc, set a reasonable default - set sim(tool_list) {spice spicewave verilog verilogwave vhdl vhdlwave} + set sim(tool_list) {spice spicewave spectre verilog verilogwave vhdl vhdlwave} if {$OS == "Windows"} { set_ne sim(spice,0,cmd) {ngspice -i "$N" -a} } else { @@ -1626,6 +1635,16 @@ proc set_sim_defaults {{reset {}}} { set_ne sim(spicewave,n) 4 set_ne sim(spicewave,default) 0 + ### spectre (vacask simulator) + set_ne sim(spectre,0,cmd) {vacask "$N"} + set sim(spectre,0,name) {Vacask} + set_ne sim(spectre,0,fg) 0 + set_ne sim(spectre,0,st) 1 + + # number of configured spectre simulators, and default one + set_ne sim(spectre,n) 1 + set_ne sim(spectre,default) 0 + ### icarus verilog set_ne sim(verilog,0,cmd) {sh -c "iverilog -o .verilog_object -g2012 '$N' && vvp .verilog_object"} set sim(verilog,0,name) {Icarus verilog} @@ -8996,6 +9015,9 @@ proc build_widgets { {topwin {} } } { } $topwin.menubar.option.netlist add checkbutton -label "Split netlist" -variable split_files \ -selectcolor $selectcolor -accelerator {} + $topwin.menubar.option.netlist add radiobutton -label "Spectre netlist"\ + -background grey60 -variable netlist_type -value spectre -accelerator {Ctrl+Shift+V} \ + -selectcolor $selectcolor -command "xschem set netlist_type spectre; xschem redraw" $topwin.menubar.option.netlist add radiobutton -label "Spice netlist"\ -background grey60 -variable netlist_type -value spice -accelerator {Ctrl+Shift+V} \ -selectcolor $selectcolor -command "xschem set netlist_type spice; xschem redraw" diff --git a/xschem_library/devices/nmos4.sym b/xschem_library/devices/nmos4.sym index db6bbd38..e3fe1496 100644 --- a/xschem_library/devices/nmos4.sym +++ b/xschem_library/devices/nmos4.sym @@ -1,4 +1,4 @@ -v {xschem version=3.4.6 file_version=1.2 +v {xschem version=3.4.8RC file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -23,9 +23,11 @@ G {} K {type=nmos format="@spiceprefix@name @pinlist @model w=@w l=@l @extra m=@m" template="name=M1 model=nmos w=5u l=0.18u del=0 m=1" -verilog_format="nmos #@del @name ( @@d , @@s , @@g );"} +verilog_format="nmos #@del @name ( @@d , @@s , @@g );" +spectre_format="@name ( @pinlist ) @model w=@w l=@l @extra $mfactor=@m"} V {} S {} +F {} E {} L 4 5 -30 5 30 {} L 4 5 -20 20 -20 {} diff --git a/xschem_library/devices/vccs_limit.sym b/xschem_library/devices/vccs_limit.sym new file mode 100644 index 00000000..84f65078 --- /dev/null +++ b/xschem_library/devices/vccs_limit.sym @@ -0,0 +1,45 @@ +v {xschem version=3.4.8RC file_version=1.2} +G {} +K {type=xline +format="@name \\\\%vd( @@CP @@CM ) \\\\%id( @@P @@M ) @name +.model @name limit %( gain=@gain out_lower_limit=@lower_limit out_upper_limit=@upper_limit %)" +template="name=alimit1 gain=1e-6 lower_limit=-1u upper_limit=1u" +} +V {} +S {} +F {} +E {} +L 4 -20 -0 -0 -20 {} +L 4 -20 -0 -0 20 {} +L 4 -0 20 20 0 {} +L 4 0 -20 20 0 {} +L 4 -40 -20 -35 -20 {} +L 4 -30 -20 -25 -20 {} +L 4 -20 -20 -15 -20 {} +L 4 -40 20 -35 20 {} +L 4 -30 20 -25 20 {} +L 4 -20 20 -15 20 {} +L 4 2.5 -22.5 7.5 -22.5 {} +L 4 5 -25 5 -20 {} +L 4 -5 5 0 10 {} +L 4 -5 5 5 5 {} +L 4 0 10 5 5 {} +L 4 0 -5 0 5 {} +L 4 -37.5 -12.5 -32.5 -12.5 {} +L 4 -35 -15 -35 -10 {} +L 4 -37.5 12.5 -32.5 12.5 {} +L 4 0 20 0 30 {} +L 4 0 -30 -0 -20 {} +B 5 -2.5 -32.5 2.5 -27.5 {name=P dir=inout} +B 5 -2.5 27.5 2.5 32.5 {name=M dir=inout} +B 5 -42.5 -22.5 -37.5 -17.5 {name=CP dir=in} +B 5 -42.5 17.5 -37.5 22.5 {name=CM dir=in} +T {@#0:net_name} 5 -42.5 0 0 0.15 0.15 {layer=15 hide=instance} +T {@#1:net_name} 5 32.5 0 0 0.15 0.15 {layer=15 hide=instance} +T {@#2:net_name} -45 -32.5 0 1 0.15 0.15 {layer=15 hide=instance} +T {@#3:net_name} -45 22.5 0 1 0.15 0.15 {layer=15 hide=instance} +T {@name +@symname +gain=@gain +upper_limit=@upper_limit +lower_limit=@lower_limit} 25 -20 0 0 0.15 0.15 {} diff --git a/xschem_library/devices/vcvs_limit.sym b/xschem_library/devices/vcvs_limit.sym new file mode 100644 index 00000000..0dd0b446 --- /dev/null +++ b/xschem_library/devices/vcvs_limit.sym @@ -0,0 +1,37 @@ +v {xschem version=3.4.8RC file_version=1.2} +G {} +K {type=xline +format="@name \\\\%vd( @@CP @@CM ) \\\\%vd( @@P @@M ) @name +.model @name limit %( gain=@gain out_lower_limit=@lower_limit out_upper_limit=@upper_limit %)" +template="name=alimit1 gain=3 lower_limit=-5.0 upper_limit=5.0" +} +V {} +S {} +F {} +E {} +L 4 2.5 -22.5 7.5 -22.5 {} +L 4 5 -25 5 -20 {} +L 4 -0 -30 -0 30 {} +L 4 -20 -0 -0 -20 {} +L 4 -20 -0 -0 20 {} +L 4 -0 20 20 0 {} +L 4 0 -20 20 0 {} +L 4 -40 -20 -35 -20 {} +L 4 -30 -20 -25 -20 {} +L 4 -20 -20 -15 -20 {} +L 4 -40 20 -35 20 {} +L 4 -30 20 -25 20 {} +L 4 -20 20 -15 20 {} +B 5 -2.5 -32.5 2.5 -27.5 {name=P dir=inout} +B 5 -2.5 27.5 2.5 32.5 {name=M dir=inout} +B 5 -42.5 -22.5 -37.5 -17.5 {name=CP dir=in} +B 5 -42.5 17.5 -37.5 22.5 {name=CM dir=in} +T {@#0:net_name} 5 -42.5 0 0 0.15 0.15 {layer=15 hide=instance} +T {@#1:net_name} 5 32.5 0 0 0.15 0.15 {layer=15 hide=instance} +T {@#2:net_name} -45 -32.5 0 1 0.15 0.15 {layer=15 hide=instance} +T {@#3:net_name} -45 22.5 0 1 0.15 0.15 {layer=15 hide=instance} +T {@name +@symname +gain=@gain +upper_limit=@upper_limit +lower_limit=@lower_limit} 25 -20 0 0 0.15 0.15 {}