xschem/src/node_hash.c

401 lines
13 KiB
C

/* File: node_hash.c
*
* This file is part of XSCHEM,
* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
* simulation.
* Copyright (C) 1998-2023 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 Node_hashentry *node_hash_lookup(const char *token, const char *dir,int what,int port,
char *sig_type, char *verilog_type, char *value, char *class, const char *orig_tok)
/* token dir et all what ... action ...
* --------------------------------------------------------------------------
* "whatever" "in"/"out" 0,XINSERT insert in hash table if not in and return NULL
* if already present just return entry address
* and update in/out fields sum up port field
* return NULL otherwise
*
* "whatever" whatever 2,XDELETE delete entry if found return NULL
* "whatever" whatever 1,XLOOKUP only look up element, dont insert */
{
unsigned int hashcode, index;
Node_hashentry **preventry;
Drivers d;
if(token==NULL || token[0]==0 ) return NULL;
dbg(3, "node_hash_lookup(): called with: %s dir=%s what=%d port=%d\n",
token, dir, what, port);
d.in=d.out=d.inout=0;
if(dir) {
if(!strcmp(dir,"in") ) d.in=1;
else if(!strcmp(dir,"out") ) d.out=1;
else if(!strcmp(dir,"inout") ) d.inout=1;
}
d.port=port;
hashcode=str_hash(token);
index=hashcode % HASHSIZE;
preventry=&xctx->node_table[index];
while(1)
{
if( !(*preventry) ) /* empty slot */
{
if( what==XINSERT ) /* insert data */
{
Node_hashentry *entry = (Node_hashentry *)my_malloc(_ALLOC_ID_, sizeof( Node_hashentry ));
entry->next = NULL;
entry->token = entry->sig_type = entry->verilog_type =
entry->value = entry->class = entry->orig_tok = NULL;
my_strdup(_ALLOC_ID_, &(entry->token),token);
if(sig_type &&sig_type[0]) my_strdup(_ALLOC_ID_, &(entry->sig_type), sig_type);
if(verilog_type &&verilog_type[0]) my_strdup(_ALLOC_ID_, &(entry->verilog_type), verilog_type);
if(class && class[0]) my_strdup(_ALLOC_ID_, &(entry->class), class);
if(orig_tok && orig_tok[0]) my_strdup(_ALLOC_ID_, &(entry->orig_tok), orig_tok);
if(value && value[0]) my_strdup(_ALLOC_ID_, &(entry->value), value);
entry->d.port=d.port;
entry->d.in=d.in;
entry->d.out=d.out;
entry->d.inout=d.inout;
entry->hash=hashcode;
*preventry=entry;
dbg(3, "node_hash_lookup(): hashing %s : value=%s\n\n",
entry->token, entry->value? entry->value:"NULL");
dbg(3, "node_hash_lookup(): hashing %s in=%d out=%d inout=%d port=%d\n",
token, d.in, d.out, d.inout, d.port);
}
return NULL; /* whether inserted or not return NULL since it was not in */
}
if( (*preventry) -> hash==hashcode && strcmp(token,(*preventry)->token)==0 ) /* found matching tok */
{
if(what==XDELETE) /* remove token from the hash table ... */
{
Node_hashentry *saveptr=(*preventry)->next, *entry = *preventry;
my_free(_ALLOC_ID_, &entry->token);
my_free(_ALLOC_ID_, &entry->verilog_type);
my_free(_ALLOC_ID_, &entry->sig_type);
my_free(_ALLOC_ID_, &entry->class);
my_free(_ALLOC_ID_, &entry->orig_tok);
my_free(_ALLOC_ID_, &entry->value);
my_free(_ALLOC_ID_, &entry);
*preventry=saveptr;
return NULL;
}
else /* found matching entry, return the address and update in/out count */
{
Node_hashentry *entry = *preventry;
entry->d.port+=port;
entry->d.in+=d.in;
entry->d.out+=d.out;
entry->d.inout+=d.inout;
if(sig_type && sig_type[0] !='\0')
my_strdup(_ALLOC_ID_, &(entry->sig_type), sig_type);
if(verilog_type && verilog_type[0] !='\0')
my_strdup(_ALLOC_ID_, &(entry->verilog_type), verilog_type);
if(value && value[0] !='\0')
my_strdup(_ALLOC_ID_, &(entry->value), value);
dbg(3, "node_hash_lookup(): hashing %s : value=%s\n\n",
entry->token, entry->value? entry->value:"NULL");
return entry;
}
}
preventry=&(*preventry)->next; /* descend into the list. */
}
}
/* wrapper to node_hash_lookup that handles buses */
/* warning, in case of buses return only pointer to first bus element */
Node_hashentry *bus_node_hash_lookup(const char *token, const char *dir, int what, int port,
char *sig_type,char *verilog_type, char *value, char *class)
{
char *start, *string_ptr, c;
int mult;
char *string=NULL;
Node_hashentry *ptr1=NULL, *ptr2=NULL;
if(token==NULL || token[0] == 0) return NULL;
if( token[0] == '#')
{
my_strdup(_ALLOC_ID_, &string, token);
}
else
{
dbg(3, "bus_node_hash_lookup(): expanding node: %s\n", token);
my_strdup(_ALLOC_ID_, &string, expandlabel(token,&mult));
dbg(3, "bus_node_hash_lookup(): done expanding node: %s\n", token);
}
if(string==NULL) return NULL;
string_ptr = start = string;
while(1)
{
c=(*string_ptr);
if(c==','|| c=='\0')
{
*string_ptr='\0'; /* set end string at comma position.... */
/* insert one bus element at a time in hash table */
ptr1=node_hash_lookup(start, dir, what,port, sig_type, verilog_type, value, class, token);
if(!ptr2) ptr2=ptr1;
dbg(3, "bus_node_hash_lookup(): processing node: %s\n", start);
*string_ptr=c; /* ....restore original char */
start=string_ptr+1;
}
if(c==0) break;
string_ptr++;
}
/* if something found return first pointer */
my_free(_ALLOC_ID_, &string);
return ptr2;
}
static void node_hash_free_entry(Node_hashentry *entry)
{
Node_hashentry *tmp;
while(entry) {
tmp = entry->next;
my_free(_ALLOC_ID_, &entry->token);
my_free(_ALLOC_ID_, &entry->verilog_type);
my_free(_ALLOC_ID_, &entry->sig_type);
my_free(_ALLOC_ID_, &entry->class);
my_free(_ALLOC_ID_, &entry->orig_tok);
my_free(_ALLOC_ID_, &entry->value);
my_free(_ALLOC_ID_, &entry);
entry = tmp;
}
}
void node_hash_free(void) /* remove the whole hash table */
{
int i;
dbg(2, "node_hash_free(): removing hash table\n");
for(i=0;i<HASHSIZE; ++i)
{
node_hash_free_entry( xctx->node_table[i] );
xctx->node_table[i] = NULL;
}
}
int traverse_node_hash()
{
int i;
int err = 0;
Node_hashentry *entry;
char str[2048]; /* 20161122 overflow safe */
int incr_hi;
incr_hi = tclgetboolvar("incr_hilight");
for(i=0;i<HASHSIZE; ++i)
{
entry = xctx->node_table[i];
while(entry)
{
if( !record_global_node(3, NULL, entry->token)) {
if(entry->d.out ==0 && entry->d.inout == 0)
{
my_snprintf(str, S(str), "Error: undriven node: %s", entry->token);
if(!xctx->netlist_count) bus_hilight_hash_lookup(entry->token, xctx->hilight_color, XINSERT_NOREPLACE);
if(incr_hi) incr_hilight_color();
statusmsg(str, 2);
err |= 1;
}
else if(entry->d.out + entry->d.inout + entry->d.in == 1)
{
my_snprintf(str, S(str), "Warning: open net: %s", entry->token);
if(!xctx->netlist_count) bus_hilight_hash_lookup(entry->token, xctx->hilight_color, XINSERT_NOREPLACE);
if(incr_hi) incr_hilight_color();
statusmsg(str,2);
}
else if(entry->d.out >=2 && entry->d.port>=0) /* era d.port>=2 03102001 */
{
my_snprintf(str, S(str), "Warning: shorted output node: %s", entry->token);
if(!xctx->netlist_count) bus_hilight_hash_lookup(entry->token, xctx->hilight_color, XINSERT_NOREPLACE);
if(incr_hi) incr_hilight_color();
statusmsg(str,2);
}
else if(entry->d.in ==0 && entry->d.inout == 0)
{
my_snprintf(str, S(str), "Warning: node: %s goes nowhere", entry->token);
if(!xctx->netlist_count) bus_hilight_hash_lookup(entry->token, xctx->hilight_color, XINSERT_NOREPLACE);
if(incr_hi) incr_hilight_color();
statusmsg(str,2);
}
else if(entry->d.out >=2 && entry->d.inout == 0 && entry->d.port>=0) /* era d.port>=2 03102001 */
{
my_snprintf(str, S(str), "Warning: shorted output node: %s", entry->token);
if(!xctx->netlist_count) bus_hilight_hash_lookup(entry->token, xctx->hilight_color, XINSERT_NOREPLACE);
if(incr_hi) incr_hilight_color();
statusmsg(str,2);
}
}
dbg(1, "traverse_node_hash(): node: %s in=%d out=%d inout=%d port=%d\n",
entry->token, entry->d.in, entry->d.out, entry->d.inout, entry->d.port);
entry = entry->next;
}
}
return err;
}
void print_vhdl_signals(FILE *fd)
{
Node_hashentry *ptr;
int i, found;
int mult,j;
char *class=NULL;
found=0;
for(i=0;i<HASHSIZE; ++i) {
ptr = xctx->node_table[i];
while(ptr) {
if(strstr(ptr->token, ".")) {
dbg(2, "print_vhdl_signals(): record field, skipping: %s\n", ptr->token);
ptr = ptr->next;
continue; /* signal is a record field, no declaration */
}
if(ptr->d.port == 0 ) {
found = 1;
if(ptr->token[0]=='#') {
mult=get_unnamed_node(3, 0, atoi((ptr->token)+4) );
}
else {
mult=1;
}
dbg(2, "print_vhdl_signals(): node: %s mult: %d value=%s \n\n",
ptr->token,mult, ptr->value?ptr->value:"NULL");
if( ptr->class && ptr->class[0] )
my_strdup(_ALLOC_ID_, &class, ptr->class);
else
my_strdup(_ALLOC_ID_, &class, "signal");
if(mult>1) {
for(j=mult-1;j>=0;j--) {
fprintf(fd, "%s %s[%d] : ", class, ptr->token[0]=='#' ? ptr->token+1 : ptr->token,j);
if(ptr->sig_type && ptr->sig_type[0]) {
fprintf(fd, "%s", ptr->sig_type);
}
else {
fprintf(fd, "std_logic");
}
if(ptr->value && ptr->value[0]) fprintf(fd, " := %s ", ptr->value);
fprintf(fd, " ; %s\n", ptr->orig_tok);
}
}
else {
fprintf(fd, "%s %s : ", class, ptr->token[0]=='#' ? ptr->token+1 : ptr->token);
if(ptr->sig_type && ptr->sig_type[0]) {
fprintf(fd, "%s", ptr->sig_type);
}
else {
fprintf(fd, "std_logic");
}
if(ptr->value && ptr->value[0]) fprintf(fd, " := %s ", ptr->value);
fprintf(fd, " ; %s\n", ptr->orig_tok);
}
}
ptr = ptr->next;
}
}
if(found) fprintf(fd, "\n" );
my_free(_ALLOC_ID_, &class);
}
void print_verilog_signals(FILE *fd)
{
Node_hashentry *ptr;
int i, found;
int mult,j;
dbg(2, " print_verilog_signals(): entering routine\n");
found=0;
for(i=0;i<HASHSIZE; ++i) {
ptr = xctx->node_table[i];
while(ptr) {
if(ptr->d.port == 0 ) {
found = 1;
if(ptr->token[0]=='#') {
mult=get_unnamed_node(3, 0, atoi((ptr->token)+4) );
}
else {
mult=1;
}
dbg(2, " print_verilog_signals(): node: %s mult: %d value=%s \n\n",
ptr->token,mult, ptr->value?ptr->value:"NULL");
if(mult>1) {
for(j=mult-1;j>=0;j--) {
if(ptr->verilog_type && ptr->verilog_type[0]) {
fprintf(fd, "%s ", ptr->verilog_type);
}
else {
fprintf(fd, "wire ");
}
fprintf(fd, "%s[%d] ", ptr->token[0]=='#' ? ptr->token+1 : ptr->token,j);
if(ptr->value && ptr->value[0]) fprintf(fd, "= %s ", ptr->value);
fprintf(fd, "; // %s\n", ptr->orig_tok);
}
}
else {
if(ptr->verilog_type && ptr->verilog_type[0]) {
fprintf(fd, "%s ", ptr->verilog_type);
} else {
fprintf(fd, "wire ");
}
fprintf(fd, "%s ", ptr->token[0]=='#' ? ptr->token+1 : ptr->token);
if(ptr->value && ptr->value[0]) fprintf(fd, "= %s ", ptr->value);
fprintf(fd, "; // %s\n", ptr->orig_tok);
}
}
ptr = ptr->next;
}
}
if(found) fprintf(fd, "\n" );
}
void list_nets(char **result)
{
Node_hashentry *ptr;
char *type = NULL;
int i;
int netlist_lvs_ignore=tclgetboolvar("lvs_ignore");
prepare_netlist_structs(1);
for(i = 0; i < xctx->instances; i++) {
if(skip_instance(i, 0, netlist_lvs_ignore)) continue;
my_strdup(_ALLOC_ID_, &type,(xctx->inst[i].ptr+ xctx->sym)->type);
if(type && xctx->inst[i].node && IS_PIN(type)) {
/*
* my_mstrcat(_ALLOC_ID_, result,
* "{", get_tok_value(xctx->inst[i].prop_ptr, "lab", 0), " ", type, "}\n", NULL);
*/
my_mstrcat(_ALLOC_ID_, result,
"{", xctx->inst[i].lab, " ", type, "}\n", NULL);
}
}
if(type) my_free(_ALLOC_ID_, &type);
for(i=0;i<HASHSIZE; ++i) {
ptr = xctx->node_table[i];
while(ptr) {
if(!ptr->d.port) {
my_mstrcat(_ALLOC_ID_, result,
"{", ptr->token, " ", "net", "}\n", NULL);
}
ptr = ptr->next;
}
}
}