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

This commit is contained in:
stefan schippers 2025-07-16 00:12:26 +02:00
parent f5bd459082
commit 5a3c97d64e
22 changed files with 1739 additions and 20 deletions

View File

@ -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

View File

@ -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;i<xctx->wires; ++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;i<xctx->instances; ++i) */

View File

@ -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 */
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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]);

346
src/spectre.awk Executable file
View File

@ -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<lines; i++) {
$0 = line[i]
## /place to insert processing awk hooks
if(xyce == 1) {
## transform ".save" lines into ".print tran" *only* for spice_probe elements, not user code
if(tolower($0) ~/^[ \t]*\.save[ \t]+.*\?-?[0-9]+/) { # .save file=test1.raw format=raw v( ?1 C2 )
$1 = ""
$0 = ".print " $0
}
gsub(/ [mM] *= *1 *$/,"") # xyce does not like m=# fields (multiplicity) removing m=1 is no an issue anyway
}
process()
}
}
## if .param m=10 n=5 is defined and R1[m:n] instance name is present in netlist --> 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;l<nmult+0;l++)
{
ii=(l+nmult*(i-1))%arg_num[j]+1
printf "%s ", arg_name[j,ii]
}
}
}
printf "\n"
}
}
}

611
src/spectre_netlist.c Normal file
View File

@ -0,0 +1,611 @@
/* File: spice_netlist.c
*
* 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
*/
#include "xschem.h"
static Str_hashtable model_table = {NULL, 0}; /* safe even with multiple schematics */
static char *model_name_result = NULL; /* safe even with multiple schematics */
static char *model_name(const char *m)
{
char *m_lower = NULL;
char *modelname = NULL;
char *ptr;
int n;
size_t l = strlen(m) + 1;
my_strdup(_ALLOC_ID_, &m_lower, m);
strtolower(m_lower);
my_realloc(_ALLOC_ID_, &modelname, l);
my_realloc(_ALLOC_ID_, &model_name_result, l);
if((ptr = strstr(m_lower, "subckt"))) {
n = sscanf(ptr, "subckt %s %s", model_name_result, modelname);
} else if((ptr = strstr(m_lower, "model"))) {
n = sscanf(ptr, "model %s %s", model_name_result, modelname);
} else {
n = sscanf(m_lower, " %s %s", model_name_result, modelname);
}
if(n<2) my_strncpy(model_name_result, m_lower, l);
else {
/* build a hash key value with no spaces to make device_model attributes with different spaces equivalent*/
my_strcat(_ALLOC_ID_, &model_name_result, modelname);
}
my_free(_ALLOC_ID_, &modelname);
my_free(_ALLOC_ID_, &m_lower);
return model_name_result;
}
static int spectre_netlist(FILE *fd, int spectre_stop )
{
int err = 0;
int i, flag = 0;
const char *type;
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);
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;i<xctx->instances; ++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;i<xctx->instances; ++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;i<xctx->instances; ++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;i<xctx->instances; ++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;i<npins; ++i) {
int n = pinnumber_list[i].n;
str_tmp = expandlabel ( (xctx->inst[n].lab ? xctx->inst[n].lab : ""), &multip);
/*must handle invalid node names */
fprintf(fd, " %s", str_tmp ? str_tmp : "<NULL>" );
}
}
my_free(_ALLOC_ID_, &pinnumber_list);
fprintf(fd,")\n");
err |= spectre_netlist(fd, 0);
first = 0;
for(i=0;i<xctx->instances; ++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;i<xctx->instances; ++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_, &current_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;i<xctx->symbols; ++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_, &current_dirname_save);
}
/* restore hilight flags from errors found analyzing top level before descending hierarchy */
for(i=0;i<xctx->instances; ++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;i<xctx->instances; ++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;i<model_table.size; ++i) {
model_entry=model_table.table[i];
while(model_entry) {
if(first == 0) fprintf(fd,"**** begin user architecture code\n");
++first;
fprintf(fd, "%s\n", model_entry->value);
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;
}

View File

@ -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);

View File

@ -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;i<no_of_pins; ++i)
{
if(strboolcmp(get_tok_value(xctx->sym[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; i<no_of_pins; ++i) {
prop = xctx->sym[symbol].rect[PINLAYER][i].prop_ptr;
if(!strcmp(get_tok_value(prop, "name",0), token + 2)) break;
}
if(i<no_of_pins && strboolcmp(get_tok_value(prop,"spectre_ignore",0), "true")) {
/* fprintf(fd, "%s ", expandlabel(token+2, &multip)); */
my_mstrcat(_ALLOC_ID_, &result, expandlabel(token+2, &multip), " ", NULL);
}
}
/* reference by pin number instead of pin name, allows faster lookup of the attached net name 20180911 */
else if(token[0]=='@' && token[1]=='#') {
char *pin_attr = NULL;
char *pin_num_or_name = NULL;
get_pin_and_attr(token, &pin_num_or_name, &pin_attr);
pin_number = get_sym_pin_number(symbol, pin_num_or_name);
if(pin_number >= 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;i<no_of_pins; ++i)
{
char *prop = (xctx->inst[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;i<no_of_pins; ++i) {
char *prop = (xctx->inst[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);

View File

@ -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);

View File

@ -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"))

View File

@ -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 {

View File

@ -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);

View File

@ -24,6 +24,7 @@ Options:
-N <file> 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.

View File

@ -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"

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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 {}