2020-08-08 15:47:34 +02:00
|
|
|
/* File: spice_netlist.c
|
2020-10-12 13:13:31 +02:00
|
|
|
*
|
2020-08-08 15:47:34 +02:00
|
|
|
* This file is part of XSCHEM,
|
2020-10-12 13:13:31 +02:00
|
|
|
* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
|
2020-08-08 15:47:34 +02:00
|
|
|
* simulation.
|
|
|
|
|
* Copyright (C) 1998-2020 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 struct hashentry *model_table[HASHSIZE];
|
|
|
|
|
static struct hashentry *model_entry;
|
|
|
|
|
static struct hashentry *subckt_table[HASHSIZE];
|
|
|
|
|
|
|
|
|
|
void global_spice_netlist(int global) /* netlister driver */
|
|
|
|
|
{
|
|
|
|
|
int first;
|
|
|
|
|
FILE *fd;
|
|
|
|
|
const char *str_tmp;
|
|
|
|
|
int mult;
|
|
|
|
|
unsigned int *stored_flags;
|
|
|
|
|
int i, save_ok;
|
|
|
|
|
char *type=NULL;
|
2020-10-12 13:13:31 +02:00
|
|
|
char *place=NULL;
|
2020-08-08 15:47:34 +02:00
|
|
|
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;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-12-02 15:10:47 +01:00
|
|
|
if(xctx->modified) {
|
2020-10-15 17:39:21 +02:00
|
|
|
save_ok = save_schematic(xctx->sch[xctx->currsch]);
|
2020-08-08 15:47:34 +02:00
|
|
|
if(save_ok == -1) return;
|
|
|
|
|
}
|
|
|
|
|
free_hash(subckt_table);
|
|
|
|
|
free_hash(model_table);
|
|
|
|
|
statusmsg("",2); /* clear infowindow */
|
|
|
|
|
record_global_node(2, NULL, NULL); /* delete list of global nodes */
|
|
|
|
|
top_subckt = 0;
|
|
|
|
|
spiceprefix=1;
|
2020-12-02 19:35:42 +01:00
|
|
|
bus_char[0] = bus_char[1] = '\0';
|
2020-11-24 02:54:45 +01:00
|
|
|
hiersep[0]='.'; hiersep[1]='\0';
|
2020-08-08 15:47:34 +02:00
|
|
|
str_tmp = tclgetvar("bus_replacement_char");
|
|
|
|
|
if(str_tmp && str_tmp[0] && str_tmp[1]) {
|
2020-12-02 19:35:42 +01:00
|
|
|
bus_char[0] = str_tmp[0];
|
|
|
|
|
bus_char[1] = str_tmp[1];
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
netlist_count=0;
|
2020-11-22 00:51:24 +01:00
|
|
|
my_snprintf(netl_filename, S(netl_filename), "%s/.%s_%d",
|
|
|
|
|
netlist_dir, skip_dir(xctx->sch[xctx->currsch]), getpid());
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(1, "global_spice_netlist(): opening %s for writing\n",netl_filename);
|
|
|
|
|
fd=fopen(netl_filename, "w");
|
|
|
|
|
|
2020-12-02 15:10:47 +01:00
|
|
|
if(xctx->netlist_name[0]) {
|
|
|
|
|
my_snprintf(cellname, S(cellname), "%s", get_cell(xctx->netlist_name, 0));
|
2020-08-08 15:47:34 +02:00
|
|
|
} else {
|
2020-10-15 17:39:21 +02:00
|
|
|
my_snprintf(cellname, S(cellname), "%s.spice", skip_dir(xctx->sch[xctx->currsch]));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
if(fd==NULL) {
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(0, "global_spice_netlist(): problems opening netlist file\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* netlist_options */
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->instances;i++) {
|
|
|
|
|
if(!(xctx->inst[i].ptr+ xctx->sym)->type) continue;
|
|
|
|
|
if( !strcmp((xctx->inst[i].ptr+ xctx->sym)->type,"netlist_options") ) {
|
2020-08-08 15:47:34 +02:00
|
|
|
netlist_options(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!strcmp(tclgetvar("top_subckt"), "1")) top_subckt = 1;
|
|
|
|
|
if(!strcmp(tclgetvar("spiceprefix"), "0")) spiceprefix = 0;
|
|
|
|
|
|
|
|
|
|
if(!top_subckt) fprintf(fd,"**");
|
2020-10-15 17:39:21 +02:00
|
|
|
fprintf(fd,".subckt %s", skip_dir( xctx->sch[xctx->currsch]) );
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
/* print top subckt ipin/opins */
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->instances;i++) {
|
|
|
|
|
if( strcmp(get_tok_value(xctx->inst[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(xctx->inst[i].ptr<0) continue;
|
|
|
|
|
if(!strcmp(get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "spice_ignore",0 ), "true") ) {
|
2020-08-08 15:47:34 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(380, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(1, "global_spice_netlist(): |%s|\n", type);
|
|
|
|
|
if( type && !strcmp(type,"netlist_options") ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-04 19:53:09 +02:00
|
|
|
if( type && IS_PIN(type)) {
|
2020-10-15 17:39:21 +02:00
|
|
|
str_tmp = expandlabel ( get_tok_value(xctx->inst[i].prop_ptr,"lab",0) ,&mult);
|
2020-10-12 13:13:31 +02:00
|
|
|
dbg(1, "global_spice_netlist(): |%s|\n",
|
2020-10-15 17:39:21 +02:00
|
|
|
get_tok_value(xctx->inst[i].prop_ptr,"lab",0));
|
2020-08-08 15:47:34 +02:00
|
|
|
/*must handle invalid node names */
|
|
|
|
|
fprintf(fd, " %s", str_tmp ? str_tmp : "(NULL)" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fprintf(fd,"\n");
|
|
|
|
|
|
|
|
|
|
spice_netlist(fd, 0);
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
first = 0;
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->instances;i++) /* print netlist_commands of top level cell with no 'place=end' property */
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
if( strcmp(get_tok_value(xctx->inst[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(xctx->inst[i].ptr<0) continue;
|
|
|
|
|
if(!strcmp(get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "spice_ignore",0 ), "true") ) {
|
2020-08-08 15:47:34 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(381, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
|
|
|
|
|
my_strdup(382, &place,get_tok_value((xctx->inst[i].ptr+ xctx->sym)->prop_ptr,"place",0));
|
2020-08-08 15:47:34 +02:00
|
|
|
if( type && !strcmp(type,"netlist_commands") ) {
|
2020-10-12 13:13:31 +02:00
|
|
|
if(!place || strcmp(place, "end" )) {
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(383, &place,get_tok_value(xctx->inst[i].prop_ptr,"place",0));
|
2020-10-12 13:13:31 +02:00
|
|
|
if(!place || strcmp(place, "end" )) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(first == 0) fprintf(fd,"**** begin user architecture code\n");
|
|
|
|
|
first++;
|
|
|
|
|
print_spice_element(fd, i) ; /* this is the element line */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
netlist_count++;
|
|
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
if(xctx->schprop && xctx->schprop[0]) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(first == 0) fprintf(fd,"**** begin user architecture code\n");
|
|
|
|
|
first++;
|
2020-10-15 17:39:21 +02:00
|
|
|
fprintf(fd, "%s\n", xctx->schprop);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
if(first) fprintf(fd,"**** end user architecture code\n");
|
|
|
|
|
/* /20100217 */
|
|
|
|
|
|
|
|
|
|
if(!top_subckt) fprintf(fd,"**");
|
|
|
|
|
fprintf(fd, ".ends\n");
|
|
|
|
|
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
if(split_files) {
|
2020-08-08 15:47:34 +02:00
|
|
|
fclose(fd);
|
|
|
|
|
my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} noshow {%s}", netl_filename, cellname);
|
2020-12-04 12:45:50 +01:00
|
|
|
override_netlist_type(CAD_SPICE_NETLIST);
|
2020-08-08 15:47:34 +02:00
|
|
|
tcleval(tcl_cmd_netlist);
|
2020-12-04 12:45:50 +01:00
|
|
|
override_netlist_type(-1); /* restore to netlist_type default */
|
2020-08-08 15:47:34 +02:00
|
|
|
if(debug_var==0) xunlink(netl_filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* preserve current level instance flags before descending hierarchy for netlisting, restore later */
|
2020-10-15 17:39:21 +02:00
|
|
|
stored_flags = my_calloc(146, xctx->instances, sizeof(unsigned int));
|
|
|
|
|
for(i=0;i<xctx->instances;i++) stored_flags[i] = xctx->inst[i].flags & 4;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(global)
|
|
|
|
|
{
|
|
|
|
|
unselect_all();
|
|
|
|
|
remove_symbols(); /* 20161205 ensure all unused symbols purged before descending hierarchy */
|
2020-10-15 17:39:21 +02:00
|
|
|
load_schematic(1, xctx->sch[xctx->currsch], 0);
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-11-24 17:37:27 +01:00
|
|
|
my_strdup(469, &xctx->sch_path[xctx->currsch+1], xctx->sch_path[xctx->currsch]);
|
|
|
|
|
my_strcat(481, &xctx->sch_path[xctx->currsch+1], "->netlisting");
|
2020-10-15 17:39:21 +02:00
|
|
|
xctx->currsch++;
|
2020-11-24 17:37:27 +01:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
dbg(1, "global_spice_netlist(): last defined symbol=%d\n",xctx->symbols);
|
2020-10-12 13:13:31 +02:00
|
|
|
subckt_name=NULL;
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->symbols;i++)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
if( strcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(!xctx->sym[i].type) continue;
|
|
|
|
|
if(strcmp(xctx->sym[i].type,"subcircuit")==0 && check_lib(xctx->sym[i].name))
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
/* xctx->sym can be SCH or SYM, use hash to avoid writing duplicate subckt */
|
|
|
|
|
my_strdup(391, &subckt_name, get_cell(xctx->sym[i].name, 0));
|
2020-11-03 12:10:55 +01:00
|
|
|
if (str_hash_lookup(subckt_table, subckt_name, "", XLOOKUP)==NULL)
|
2020-10-12 13:13:31 +02:00
|
|
|
{
|
2020-11-03 12:10:55 +01:00
|
|
|
str_hash_lookup(subckt_table, subckt_name, "", XINSERT);
|
2020-10-15 17:39:21 +02:00
|
|
|
if( split_files && strcmp(get_tok_value(xctx->sym[i].prop_ptr,"vhdl_netlist",0),"true")==0 )
|
2020-10-12 13:13:31 +02:00
|
|
|
vhdl_block_netlist(fd, i);
|
2020-10-15 17:39:21 +02:00
|
|
|
else if(split_files && strcmp(get_tok_value(xctx->sym[i].prop_ptr,"verilog_netlist",0),"true")==0 )
|
2020-10-12 13:13:31 +02:00
|
|
|
verilog_block_netlist(fd, i);
|
2020-08-08 15:47:34 +02:00
|
|
|
else
|
2020-10-15 17:39:21 +02:00
|
|
|
if( strcmp(get_tok_value(xctx->sym[i].prop_ptr,"spice_primitive",0),"true") )
|
2020-10-12 13:13:31 +02:00
|
|
|
spice_block_netlist(fd, i);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free_hash(subckt_table);
|
|
|
|
|
my_free(944, &subckt_name);
|
|
|
|
|
/*clear_drawing(); */
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strncpy(xctx->sch[xctx->currsch] , "", S(xctx->sch[xctx->currsch]));
|
|
|
|
|
xctx->currsch--;
|
2020-08-08 15:47:34 +02:00
|
|
|
unselect_all();
|
|
|
|
|
/* remove_symbols(); */
|
2020-10-15 17:39:21 +02:00
|
|
|
load_schematic(1, xctx->sch[xctx->currsch], 0);
|
2020-08-08 15:47:34 +02:00
|
|
|
prepare_netlist_structs(1); /* so 'lab=...' attributes for unnamed nets are set */
|
|
|
|
|
/* symbol vs schematic pin check, we do it here since now we have ALL symbols loaded */
|
|
|
|
|
sym_vs_sch_pins();
|
|
|
|
|
/* restore hilight flags from errors found analyzing top level before descending hierarchy */
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->instances; i++) xctx->inst[i].flags |= stored_flags[i];
|
2020-08-08 15:47:34 +02:00
|
|
|
draw_hilight_net(1);
|
|
|
|
|
}
|
|
|
|
|
my_free(945, &stored_flags);
|
|
|
|
|
|
|
|
|
|
/* print globals nodes found in netlist 28032003 */
|
|
|
|
|
record_global_node(0,fd,NULL);
|
|
|
|
|
|
|
|
|
|
/* =================================== 20121223 */
|
|
|
|
|
first = 0;
|
|
|
|
|
if(!split_files) {
|
2020-10-15 17:39:21 +02:00
|
|
|
for(i=0;i<xctx->instances;i++) /* print netlist_commands of top level cell with 'place=end' property */
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
if( strcmp(get_tok_value(xctx->inst[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(xctx->inst[i].ptr<0) continue;
|
|
|
|
|
if(!strcmp(get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "spice_ignore",0 ), "true") ) {
|
2020-08-08 15:47:34 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(384, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
|
|
|
|
|
my_strdup(385, &place,get_tok_value((xctx->inst[i].ptr+ xctx->sym)->prop_ptr,"place",0));
|
2020-08-08 15:47:34 +02:00
|
|
|
if( type && !strcmp(type,"netlist_commands") ) {
|
2020-10-12 13:13:31 +02:00
|
|
|
if(place && !strcmp(place, "end" )) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(first == 0) fprintf(fd,"**** begin user architecture code\n");
|
|
|
|
|
first++;
|
2020-10-12 13:13:31 +02:00
|
|
|
print_spice_element(fd, i) ;
|
|
|
|
|
} else {
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(386, &place,get_tok_value(xctx->inst[i].prop_ptr,"place",0));
|
2020-10-12 13:13:31 +02:00
|
|
|
if(place && !strcmp(place, "end" )) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(first == 0) fprintf(fd,"**** begin user architecture code\n");
|
|
|
|
|
first++;
|
2020-10-12 13:13:31 +02:00
|
|
|
print_spice_element(fd, i) ;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} /* netlist_commands */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
/* print device_model attributes */
|
|
|
|
|
for(i=0;i<HASHSIZE; i++) {
|
|
|
|
|
model_entry=model_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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free_hash(model_table);
|
|
|
|
|
if(first) fprintf(fd,"**** end user architecture code\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 20150922 added split_files check */
|
2020-10-12 13:13:31 +02:00
|
|
|
if(!split_files) fprintf(fd, ".end\n");
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
dbg(1, "global_spice_netlist(): starting awk on netlist!\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!split_files) {
|
|
|
|
|
fclose(fd);
|
|
|
|
|
if(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(946, &type);
|
|
|
|
|
my_free(947, &place);
|
2020-10-06 16:19:52 +02:00
|
|
|
netlist_count = 0;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *model_name_result = NULL;
|
|
|
|
|
|
|
|
|
|
static char *model_name(const char *m)
|
|
|
|
|
{
|
|
|
|
|
char *m_lower = NULL;
|
|
|
|
|
char *modelname = NULL;
|
2020-10-25 03:03:23 +01:00
|
|
|
int n;
|
|
|
|
|
int l = strlen(m) + 1;
|
2020-08-08 15:47:34 +02:00
|
|
|
my_strdup(255, &m_lower, m);
|
|
|
|
|
strtolower(m_lower);
|
2020-10-25 03:03:23 +01:00
|
|
|
my_realloc(256, &modelname, l);
|
|
|
|
|
my_realloc(257, &model_name_result, l);
|
|
|
|
|
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(296, &model_name_result, modelname);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
my_free(948, &modelname);
|
|
|
|
|
my_free(949, &m_lower);
|
|
|
|
|
return model_name_result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
void spice_block_netlist(FILE *fd, int i)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
int spice_stop=0;
|
2020-08-08 15:47:34 +02:00
|
|
|
char netl_filename[PATH_MAX];
|
2020-10-12 13:13:31 +02:00
|
|
|
char tcl_cmd_netlist[PATH_MAX + 100];
|
|
|
|
|
char cellname[PATH_MAX];
|
2020-08-08 15:47:34 +02:00
|
|
|
char filename[PATH_MAX];
|
|
|
|
|
const char *str_tmp;
|
|
|
|
|
/* int j; */
|
|
|
|
|
/* int mult; */
|
|
|
|
|
char *extra=NULL;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
if(!strcmp( get_tok_value(xctx->sym[i].prop_ptr,"spice_stop",0),"true") )
|
2020-08-08 15:47:34 +02:00
|
|
|
spice_stop=1;
|
|
|
|
|
else
|
|
|
|
|
spice_stop=0;
|
|
|
|
|
|
|
|
|
|
if(split_files) {
|
2020-10-15 17:39:21 +02:00
|
|
|
my_snprintf(netl_filename, S(netl_filename), "%s/.%s_%d", netlist_dir, skip_dir(xctx->sym[i].name), getpid());
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(1, "spice_block_netlist(): split_files: netl_filename=%s\n", netl_filename);
|
|
|
|
|
fd=fopen(netl_filename, "w");
|
2020-10-15 17:39:21 +02:00
|
|
|
my_snprintf(cellname, S(cellname), "%s.spice", skip_dir(xctx->sym[i].name));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
fprintf(fd, "\n* expanding symbol: %s # of pins=%d\n\n",
|
2020-10-15 17:39:21 +02:00
|
|
|
xctx->sym[i].name,xctx->sym[i].rects[PINLAYER] );
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
fprintf(fd, ".subckt %s ",skip_dir(xctx->sym[i].name));
|
2020-08-08 15:47:34 +02:00
|
|
|
print_spice_subckt(fd, i);
|
|
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
my_strdup(387, &extra, get_tok_value(xctx->sym[i].prop_ptr,"extra",0) );
|
2020-10-21 18:18:53 +02:00
|
|
|
/* this is now done in print_spice_subckt */
|
|
|
|
|
/*
|
|
|
|
|
* fprintf(fd, "%s ", extra ? extra : "" );
|
|
|
|
|
*/
|
|
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
/* 20081206 new get_sym_template does not return token=value pairs where token listed in extra */
|
2020-10-15 17:39:21 +02:00
|
|
|
fprintf(fd, "%s", get_sym_template(xctx->sym[i].templ, extra));
|
2020-08-08 15:47:34 +02:00
|
|
|
my_free(950, &extra);
|
|
|
|
|
fprintf(fd, "\n");
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
if((str_tmp = get_tok_value(xctx->sym[i].prop_ptr, "schematic",0 ))[0]) {
|
2020-08-08 15:47:34 +02:00
|
|
|
my_strncpy(filename, abs_sym_path(str_tmp, ""), S(filename));
|
|
|
|
|
load_schematic(1,filename, 0);
|
|
|
|
|
} else {
|
2020-11-22 00:51:24 +01:00
|
|
|
dbg(1, "spice_block_netlist(): loading: %s -> %s\n",
|
|
|
|
|
xctx->sym[i].name, add_ext(abs_sym_path(xctx->sym[i].name, ""), ".sch"));
|
2020-12-03 04:20:05 +01:00
|
|
|
dbg(1, "spice_block_netlist(): current_dirname=%s\n", xctx->current_dirname);
|
2020-10-15 17:39:21 +02:00
|
|
|
load_schematic(1, add_ext(abs_sym_path(xctx->sym[i].name, ""), ".sch") ,0);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
spice_netlist(fd, spice_stop); /* 20111113 added spice_stop */
|
|
|
|
|
netlist_count++;
|
|
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
if(xctx->schprop && xctx->schprop[0]) {
|
2020-08-08 15:47:34 +02:00
|
|
|
fprintf(fd,"**** begin user architecture code\n");
|
2020-10-15 17:39:21 +02:00
|
|
|
fprintf(fd, "%s\n", xctx->schprop);
|
2020-08-08 15:47:34 +02:00
|
|
|
fprintf(fd,"**** end user architecture code\n");
|
|
|
|
|
}
|
|
|
|
|
fprintf(fd, ".ends\n\n");
|
2020-10-12 13:13:31 +02:00
|
|
|
if(split_files) {
|
2020-08-08 15:47:34 +02:00
|
|
|
fclose(fd);
|
|
|
|
|
my_snprintf(tcl_cmd_netlist, S(tcl_cmd_netlist), "netlist {%s} noshow {%s}", netl_filename, cellname);
|
2020-12-04 12:45:50 +01:00
|
|
|
override_netlist_type(CAD_SPICE_NETLIST);
|
2020-08-08 15:47:34 +02:00
|
|
|
tcleval(tcl_cmd_netlist);
|
2020-12-04 12:45:50 +01:00
|
|
|
override_netlist_type(-1); /* restore to netlist_type default */
|
2020-08-08 15:47:34 +02:00
|
|
|
if(debug_var==0) xunlink(netl_filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void spice_netlist(FILE *fd, int spice_stop )
|
|
|
|
|
{
|
2020-11-24 02:54:45 +01:00
|
|
|
int i;
|
|
|
|
|
char *type=NULL;
|
|
|
|
|
|
2020-12-02 15:10:47 +01:00
|
|
|
xctx->prep_net_structs = 0;
|
2020-11-24 02:54:45 +01:00
|
|
|
prepare_netlist_structs(1);
|
|
|
|
|
traverse_node_hash(); /* print all warnings about unconnected floatings etc */
|
|
|
|
|
if(!spice_stop) {
|
|
|
|
|
for(i=0;i<xctx->instances;i++) /* print first ipin/opin defs ... */
|
|
|
|
|
{
|
|
|
|
|
if( strcmp(get_tok_value(xctx->inst[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(xctx->inst[i].ptr<0) continue;
|
|
|
|
|
if(!strcmp(get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "spice_ignore",0 ), "true") ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
my_strdup(388, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
|
|
|
|
|
if( type && IS_PIN(type) ) {
|
|
|
|
|
print_spice_element(fd, i) ; /* this is the element line */
|
|
|
|
|
}
|
2020-10-12 13:13:31 +02:00
|
|
|
}
|
2020-11-24 02:54:45 +01:00
|
|
|
for(i=0;i<xctx->instances;i++) /* ... then print other lines */
|
|
|
|
|
{
|
|
|
|
|
if( strcmp(get_tok_value(xctx->inst[i].prop_ptr,"spice_ignore",0),"true")==0 ) continue;
|
|
|
|
|
if(xctx->inst[i].ptr<0) continue;
|
|
|
|
|
if(!strcmp(get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "spice_ignore",0 ), "true") ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
my_strdup(390, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
|
|
|
|
|
|
|
|
|
|
if( type && !IS_LABEL_OR_PIN(type) ) {
|
2020-12-02 15:10:47 +01:00
|
|
|
/* already done in global_spice_netlist */
|
|
|
|
|
if(!strcmp(type,"netlist_commands") && netlist_count==0) continue;
|
2020-11-24 02:54:45 +01:00
|
|
|
if(netlist_count &&
|
|
|
|
|
!strcmp(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_spice_element(fd, i) ; /* this is the element line */
|
|
|
|
|
fprintf(fd,"**** end user architecture code\n");
|
|
|
|
|
} else {
|
|
|
|
|
const char *m;
|
|
|
|
|
print_spice_element(fd, i) ; /* this is the element line */
|
2020-12-02 17:33:01 +01:00
|
|
|
fprintf(fd, "**** end_element\n");
|
2020-11-24 02:54:45 +01:00
|
|
|
/* hash device_model attribute if any */
|
2020-11-29 01:59:17 +01:00
|
|
|
m = get_tok_value(xctx->inst[i].prop_ptr, "device_model", 0);
|
2020-11-24 02:54:45 +01:00
|
|
|
if(m[0]) str_hash_lookup(model_table, model_name(m), m, XINSERT);
|
|
|
|
|
else {
|
2020-11-29 01:59:17 +01:00
|
|
|
m = get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, "device_model", 0);
|
2020-11-24 02:54:45 +01:00
|
|
|
if(m[0]) str_hash_lookup(model_table, model_name(m), m, XINSERT);
|
|
|
|
|
}
|
|
|
|
|
my_free(951, &model_name_result);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2020-11-24 02:54:45 +01:00
|
|
|
}
|
|
|
|
|
if(!netlist_count) redraw_hilights(); /* draw_hilight_net(1); */
|
|
|
|
|
my_free(952, &type);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* calculate the hash function relative to string s */
|
2020-11-03 12:10:55 +01:00
|
|
|
static unsigned int str_hash(const char *tok)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
while ( (c = *tok++) )
|
|
|
|
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* GENERIC PURPOSE HASH TABLE */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* token value what ... what ...
|
|
|
|
|
* --------------------------------------------------------------------------
|
|
|
|
|
* "whatever" "whatever" XINSERT insert in hash table if not in.
|
2020-10-12 13:13:31 +02:00
|
|
|
* if already present update value if not NULL,
|
2020-08-08 15:47:34 +02:00
|
|
|
* return entry address.
|
2020-09-23 00:58:39 +02:00
|
|
|
* "whatever" "whatever" XINSERT_NOREPLACE same as XINSERT but do not replace existing value
|
2020-08-08 15:47:34 +02:00
|
|
|
* return NULL if not found.
|
|
|
|
|
* "whatever" "whatever" XLOOKUP lookup in hash table,return entry addr.
|
|
|
|
|
* return NULL if not found
|
|
|
|
|
* "whatever" "whatever" XDELETE delete entry if found,return NULL
|
|
|
|
|
*/
|
2020-11-03 12:10:55 +01:00
|
|
|
struct hashentry *str_hash_lookup(struct hashentry **table, const char *token, const char *value, int what)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
unsigned int hashcode, index;
|
|
|
|
|
struct hashentry *entry, *saveptr, **preventry;
|
|
|
|
|
int s ;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
if(token==NULL) return NULL;
|
2020-11-03 12:10:55 +01:00
|
|
|
hashcode=str_hash(token);
|
2020-08-08 15:47:34 +02:00
|
|
|
index=hashcode % HASHSIZE;
|
|
|
|
|
entry=table[index];
|
|
|
|
|
preventry=&table[index];
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
if( !entry ) /* empty slot */
|
|
|
|
|
{
|
2020-09-23 00:58:39 +02:00
|
|
|
if(what==XINSERT || what == XINSERT_NOREPLACE) /* insert data */
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
s=sizeof( struct hashentry );
|
|
|
|
|
entry=(struct hashentry *)my_malloc(313, s);
|
|
|
|
|
entry->next=NULL;
|
|
|
|
|
entry->token=NULL;
|
|
|
|
|
entry->value=NULL;
|
|
|
|
|
my_strdup(297, &entry->token, token);
|
|
|
|
|
my_strdup(307, &entry->value, value);
|
|
|
|
|
entry->hash=hashcode;
|
|
|
|
|
*preventry=entry;
|
|
|
|
|
}
|
|
|
|
|
return NULL; /* if element inserted return NULL since it was not in table */
|
|
|
|
|
}
|
|
|
|
|
if( entry -> hash==hashcode && strcmp(token,entry->token)==0 ) /* found a matching token */
|
|
|
|
|
{
|
|
|
|
|
if(what==XDELETE) /* remove token from the hash table ... */
|
|
|
|
|
{
|
|
|
|
|
saveptr=entry->next;
|
|
|
|
|
my_free(953, &entry->token);
|
|
|
|
|
my_free(954, &entry->value);
|
|
|
|
|
my_free(955, &entry);
|
|
|
|
|
*preventry=saveptr;
|
|
|
|
|
}
|
|
|
|
|
else if(value && what == XINSERT ) {
|
|
|
|
|
my_strdup(308, &entry->value, value);
|
|
|
|
|
}
|
|
|
|
|
return entry; /* found matching entry, return the address */
|
|
|
|
|
}
|
|
|
|
|
preventry=&entry->next; /* descend into the list. */
|
|
|
|
|
entry = entry->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 12:10:55 +01:00
|
|
|
static struct hashentry *str_free_hash_entry(struct hashentry *entry)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
struct hashentry *tmp;
|
|
|
|
|
while( entry ) {
|
|
|
|
|
tmp = entry -> next;
|
|
|
|
|
my_free(956, &(entry->token));
|
|
|
|
|
my_free(957, &(entry->value));
|
|
|
|
|
my_free(958, &entry);
|
|
|
|
|
entry = tmp;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void free_hash(struct hashentry **table)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i=0;i<HASHSIZE;i++)
|
|
|
|
|
{
|
2020-11-03 12:10:55 +01:00
|
|
|
table[i] = str_free_hash_entry( table[i] );
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-27 12:41:36 +02:00
|
|
|
|
|
|
|
|
/* GENERIC PURPOSE INT HASH TABLE */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* token value what ... what ...
|
|
|
|
|
* --------------------------------------------------------------------------
|
|
|
|
|
* "whatever" "whatever" XINSERT insert in hash table if not in.
|
2020-10-12 13:13:31 +02:00
|
|
|
* if already present update value if not NULL,
|
2020-09-27 12:41:36 +02:00
|
|
|
* return entry address.
|
|
|
|
|
* "whatever" "whatever" XINSERT_NOREPLACE same as XINSERT but do not replace existing value
|
|
|
|
|
* return NULL if not found.
|
|
|
|
|
* "whatever" "whatever" XLOOKUP lookup in hash table,return entry addr.
|
|
|
|
|
* return NULL if not found
|
|
|
|
|
* "whatever" "whatever" XDELETE delete entry if found,return NULL
|
|
|
|
|
*/
|
|
|
|
|
struct int_hashentry *int_hash_lookup(struct int_hashentry **table, const char *token, const int value, int what)
|
|
|
|
|
{
|
|
|
|
|
unsigned int hashcode, index;
|
|
|
|
|
struct int_hashentry *entry, *saveptr, **preventry;
|
|
|
|
|
int s ;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-09-27 12:41:36 +02:00
|
|
|
if(token==NULL) return NULL;
|
2020-11-03 12:10:55 +01:00
|
|
|
hashcode=str_hash(token);
|
2020-09-27 12:41:36 +02:00
|
|
|
index=hashcode % HASHSIZE;
|
|
|
|
|
entry=table[index];
|
|
|
|
|
preventry=&table[index];
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
if( !entry ) /* empty slot */
|
|
|
|
|
{
|
|
|
|
|
if(what==XINSERT || what == XINSERT_NOREPLACE) /* insert data */
|
|
|
|
|
{
|
|
|
|
|
s=sizeof( struct int_hashentry );
|
|
|
|
|
entry=(struct int_hashentry *)my_malloc(659, s);
|
|
|
|
|
entry->next=NULL;
|
|
|
|
|
entry->token=NULL;
|
|
|
|
|
my_strdup(658, &entry->token, token);
|
|
|
|
|
entry->value = value;
|
|
|
|
|
entry->hash=hashcode;
|
|
|
|
|
*preventry=entry;
|
|
|
|
|
}
|
|
|
|
|
return NULL; /* if element inserted return NULL since it was not in table */
|
|
|
|
|
}
|
|
|
|
|
if( entry -> hash==hashcode && strcmp(token,entry->token)==0 ) /* found a matching token */
|
|
|
|
|
{
|
|
|
|
|
if(what==XDELETE) /* remove token from the hash table ... */
|
|
|
|
|
{
|
|
|
|
|
saveptr=entry->next;
|
|
|
|
|
my_free(896, &entry->token);
|
|
|
|
|
my_free(897, &entry);
|
|
|
|
|
*preventry=saveptr;
|
|
|
|
|
}
|
|
|
|
|
else if(what == XINSERT ) {
|
|
|
|
|
entry->value = value;
|
|
|
|
|
}
|
|
|
|
|
return entry; /* found matching entry, return the address */
|
|
|
|
|
}
|
|
|
|
|
preventry=&entry->next; /* descend into the list. */
|
|
|
|
|
entry = entry->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct int_hashentry *free_int_hash_entry(struct int_hashentry *entry)
|
|
|
|
|
{
|
|
|
|
|
struct int_hashentry *tmp;
|
|
|
|
|
while( entry ) {
|
|
|
|
|
tmp = entry -> next;
|
|
|
|
|
my_free(1171, &(entry->token));
|
|
|
|
|
my_free(1172, &entry);
|
|
|
|
|
entry = tmp;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void free_int_hash(struct int_hashentry **table)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i=0;i<HASHSIZE;i++)
|
|
|
|
|
{
|
|
|
|
|
table[i] = free_int_hash_entry( table[i] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|