/**************************************************************************** Copyright (C) 1987-2015 by Jeffery P. Hansen 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. Last edit by hansen on Mon Dec 22 06:31:44 2008 ****************************************************************************/ #include #include #include #include #include "tkgate.h" #define DEBUG_NET 0 static char *vtype_table[] = { "wire", "tri", "wand", "triand", "wor", "trior", "tri0", "tri1", "trireg" }; static int vtype_table_length = sizeof(vtype_table)/sizeof(vtype_table[0]); int ycIsKW(char*); /***************************************************************************** * * Get a string for the "display type" of a net. * * Parameters: * n Net on which to lookup the dtype. * * Returns: String representing the display type. * * Get the string for the net's "dtype". The dtype is the type used to determine what * type of symbol should be displayed next to the net in the list of nets. It * is not necessarily the actual verilog type. * *****************************************************************************/ const char *GNet_getDTypeStr(GNet *n) { /* * Translate type code from HDL modules */ switch (n->n_dtype) { case NT_INPUT : return "input"; case NT_OUTPUT : return "output"; case NT_INOUT : return "inout"; case NT_REG : return "reg"; case NT_WIRE : return "wire"; } if (n->n_ionet) { switch (n->n_ionet->typeinfo->code) { case GC_LOGICIN : return "input"; case GC_LOGICOUT : return "output"; case GC_LOGICTRI : return "inout"; } } return "wire"; } /***************************************************************************** * * Get a string for the "verilog type" of a net. * * Parameters: * n Net on which to lookup the dtype. * * Returns: String representing the verilog type. * *****************************************************************************/ const char *GNet_getVType(GNet *n) { int code = 0; if (n->n_ionet) { switch (n->n_ionet->typeinfo->code) { case GC_SWITCH : case GC_DIP : return "switch"; case GC_LOGICIN : case GC_LOGICOUT : case GC_LOGICTRI : return "port"; } return n->n_ionet->typeinfo->vnames; } code = n->n_vtype; if (code > vtype_table_length) code = 0; return vtype_table[code]; } void GNet_setVType(GNet *n,const char *vtype) { int i; ob_touch(n); for (i = 0;i < vtype_table_length;i++) if (strcmp(vtype_table[i],vtype) == 0) break; if (i >= vtype_table_length) i = 0; n->n_vtype = i; n->n_labelWidth = 0; } /***************************************************************************** * * Create a new net in a module with properties similar to another net * * Parameters: * name Net name hint * rnet Reference net on which to base the new net's properties * M Module in which to create net * *****************************************************************************/ GNet *new_GNet_compatable(const char *name,GNet *rnet,GModuleDef *M) { GNet *net; static unsigned generation = 0; net = (GNet*) ob_malloc(sizeof(GNet),"GNet"); if (M) { char buf[STRMAX]; pickValidName(buf,name,"w",M->m_nets); net->n_signame = ob_strdup(buf); if (name) { net->n_show_name = 1; } else { net->n_show_name = 0; } } else { net->n_signame = 0; net->n_show_name = 0; } net->n_nbits = rnet ? rnet->n_nbits : 1; net->n_dtype = NT_NONE; net->n_vtype = NV_WIRE; net->n_refs = 0; net->n_mark = 0; net->n_ionet = 0; net->n_mod = M; net->n_driver = 0; net->n_next = 0; net->n_finalized = 0; net->n_labelWidth = 0; net->n_generation = generation++; SHash_insert(M->m_nets,net->n_signame,net); if (M && TkGate.circuit->es && M == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) { DoTcl("NetList::add %s -hidden %d -bits %d -type %s",net->n_signame,!net->n_show_name,net->n_nbits,GNet_getDTypeStr(net)); } #if DEBUG_NET printf("new_GNet() -> %x",net); if (net->n_signame) printf(" (%s)\n",net->n_signame); else printf("\n"); #endif return net; } /***************************************************************************** * * Create a new net in a module * * Parameters: * name Net name hint * M Module in which to create net * *****************************************************************************/ GNet *new_GNet(const char *name,GModuleDef *M) { return new_GNet_compatable(name,0,M); } /***************************************************************************** * * Update net in the net list box * * Parameters: * n Target net for operation. * *****************************************************************************/ void net_update(GNet *n) { if (n->n_mod && TkGate.circuit->es && n->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) DoTcl("NetList::replace %s -hidden %d -bits %d -type %s",n->n_signame,!n->n_show_name,n->n_nbits,GNet_getDTypeStr(n)); } void delete_GNet(GNet *net) { ob_touch(net); #if DEBUG_NET printf("GNet::Delete(%s) %x",net->n_signame ?: "",net); #endif if (net->n_signame) { SHash_remove(net->n_mod->m_nets,net->n_signame); if (net->n_mod && TkGate.circuit->es && net->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) { DoTcl("NetList::del %s",net->n_signame); } ob_free(net->n_signame); #if DEBUG_NET printf(" [%d]",r); #endif } #if DEBUG_NET printf("\n"); #endif ob_free(net); } /***************************************************************************** * * Increment the reference count of a net. * * Parameters: * net Target net for operation. * *****************************************************************************/ void net_incref(GNet *net) { ob_touch(net); #if 0 printf("net_incref(%s) [%d]\n",net->n_signame,net->n_refs+1); #endif net->n_refs++; } /* * Decrement the reference count of a net. If the reference * count goes to zero, the net is deleted. */ void net_decref(GNet *net) { ob_touch(net); #if 0 printf("net_decref(%s) [%d]\n",net->n_signame,net->n_refs-1); #endif if (--net->n_refs <= 0) delete_GNet(net); } /* * Select one of the nets 'a' or 'b'. Typically used when joining two wires * to determine which net name will be retained and which will be deleted. */ GNet *net_pickOne(GNet *a,GNet *b,int decref_loser) { GNet *r = 0; /* Net to return */ GNet *l = 0; /* Losing net */ if (!a) r = b; else if (!b) r = a; else if (a == b) { r = a; l = b; } else if (a->n_ionet) { r = a; l = b; } else if (b->n_ionet) { r = b; l = a; } else if (a->n_show_name) { r = a; l = b; } else if (b->n_show_name) { r = b; l = a; } else if (a->n_generation < b->n_generation) { r = a; l = b; } else { r = b; l = a; } if (l && decref_loser) net_decref(l); return r; } /* Check to see if it is OK to connect wires from the specified nets. Return 1 if OK, return 0 and display a status bar message if not OK. */ int net_connectOK(GNet *n1,GNet *n2,int isMidWire) { if (n1 == n2) { message(0,msgLookup("err.badconsame")); /* Connection refused because wires are part of the same net. */ return 0; } if (n1->n_ionet && n2->n_ionet) { message(0,msgLookup("err.badconptsp")); /* Connection refused because both wires are module ports or supply. */ return 0; } if (!isMidWire && n1->n_nbits != n2->n_nbits) { message(0,msgLookup("err.badconbitw")); /* Connection refused because bit widths do not match. */ return 0; } return 1; } /* Unselect all nets. */ void net_unselect(int drawp) { if (TkGate.circuit->nsel) { GNet *n = TkGate.circuit->nsel; if (drawp) GNet_draw(n); TkGate.circuit->nsel = 0; if (drawp) GNet_draw(n); } } /* Make 'n' the selected net. */ void net_select(GNet *n,int drawp) { if (TkGate.circuit->nsel == n) return; net_unselect(drawp); if (!n) return; DoTcl("NetList::shadowselection %s",n->n_signame); ob_touch(TkGate.circuit); if (drawp) GNet_draw(n); TkGate.circuit->nsel = n; if (drawp) GNet_draw(n); } /* Redraw an entire net. */ void GNet_draw(GNet *net) { wire_drawnet(net->n_driver); } void net_setDType(GNet *net,int dtype) { if (net->n_dtype == dtype) return; ob_touch(net); net->n_dtype = dtype; net->n_labelWidth = 0; if (net->n_mod && TkGate.circuit->es && net->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) { DoTcl("NetList::replace %s -hidden %d -bits %d -type %s",net->n_signame,!net->n_show_name,net->n_nbits,GNet_getDTypeStr(net)); } } void net_setSize(GNet *net,int nbits) { if (net->n_nbits == nbits) return; ob_touch(net); net->n_nbits = nbits; if (net->n_mod && TkGate.circuit->es && net->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) { DoTcl("NetList::replace %s -hidden %d -bits %d -type %s",net->n_signame,!net->n_show_name,net->n_nbits,GNet_getDTypeStr(net)); } } void net_editProps(GNet *n,int x,int y) { char *sn = n->n_show_name ? "" :"@"; int ioCode = 0; int wx = ctow_x(x-50); int wy = ctow_y(y-50); const char *result; const char *vtype = GNet_getVType(n); if (n->n_ionet) { ioCode = n->n_ionet->typeinfo->code; if (ioCode != GC_LOGICIN && ioCode != GC_LOGICOUT && ioCode != GC_LOGICTRI) ioCode = 0; /* yappari yameta */ } if (n->n_nbits == 1) DoTcl("tkg_editNet %d %d {%s%s} %d %s\n",wx,wy,n->n_signame,sn,ioCode,vtype); else DoTcl("tkg_editNet %d %d {%s[%d:0]%s} %d %s\n",wx,wy,n->n_signame,n->n_nbits-1,sn,ioCode,vtype); result = Tcl_GetStringResult(TkGate.tcl); if (*result != '0') SetModified(MF_NET); } /* Set the signal name of a wire to s. */ void net_rename(GNet *net,const char *s,int showName) { char *oldName = ob_strdup(net->n_signame); char buf[STRMAX]; if (net->n_signame) { if (net->n_mod && TkGate.circuit->es && net->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) DoTcl("NetList::del %s",net->n_signame); SHash_remove(net->n_mod->m_nets,net->n_signame); ob_free(net->n_signame); } ob_touch(net); net->n_signame = 0; net->n_labelWidth = 0; pickValidName(buf,s,"w",net->n_mod->m_nets); net->n_signame = ob_strdup(buf); if (showName) GNet_showName(net); else GNet_hideName(net); SHash_insert(net->n_mod->m_nets,net->n_signame,net); if (net->n_mod && TkGate.circuit->es && net->n_mod == TkGate.circuit->es->env && editstate_getInterfaceMode() == INTFMODE_NONE) { DoTcl("NetList::replace %s -hidden %d -bits %d -type %s", net->n_signame,!net->n_show_name,net->n_nbits,GNet_getDTypeStr(net)); } ob_free(oldName); } /* * Check 'name' to see if it is a valid name. If 'H' is non-null, then the * name may not appear as a key in the hash table. Returns 0 if the name * is valid. */ int checkValidName(const char *name,SHash *H) { const char *p = name; if (!isalpha(*p) && *p != '_') return -1; for (p++;*p;p++) { if (!isalpha(*p) && !isdigit(*p) && *p != '_') return -1; } if (H && SHash_find(H,name)) return -1; return 0; } /***************************************************************************** * * Pick name for an object based on suggested name. * * Parameters: * buf Return for generated name * name Suggested name * base Type-dependent base name for name generation * H Table of already registered names * * Pick a valid name for an object based on a possible suggested name 'name', * a base name 'base' for objects of a that type, and a hash table 'H' for objects * of that type. One or more of 'name', 'base' and 'H' may be null. The generated * name will be placed in 'buf'. If 'name' is a valid name that does not appear in the * hash table 'H', 'name' will become the selected name. * *****************************************************************************/ void pickValidName(char *buf,const char *name,const char *base,SHash *H) { int reason; if (!base || !*base) base = "n"; reason = 0; if (name) { if (buf != (char*)name) strcpy(buf,name); if (trimName(buf)) { reason = 1; if (!*buf) { if (base) strcpy(buf,base); else strcpy(buf,"name"); } } if (!*buf) sprintf(buf,"%s0",base); if (H && SHash_find(H,buf)) reason = 2; else if (ycIsKW(buf)) reason = 4; else { if (reason) message(0,msgLookup("err.badnetname")); /* "Illegal characters in identifier deleted." */ return; } } else sprintf(buf,"%s0",base); /** @TODO to remove */ /*p = buf+strlen(buf);*/ if ((H && SHash_find(H,buf)) || ycIsKW(buf)) { char *p; int n; p = buf + strlen(buf); while (p > buf && isdigit((int)p[-1])) p--; n = 0; if (*p) sscanf(p,"%d",&n); do { sprintf(p,"%d",n++); } while ((H && SHash_find(H,buf)) || ycIsKW(buf)); } switch (reason) { case 1 : message(0,msgLookup("err.netbcrename"),buf); /* "Identifier renamed to '%s' because of illegal characters." */ break; case 2 : message(0,msgLookup("err.netconfnet"),buf); break; case 3 : message(0,msgLookup("err.netconfgat")); break; case 4 : message(0,msgLookup("err.netconfkw")); break; } } int GWire_pickProbePosition(GWire *w,int *x,int *y) { int orig_x = *x; int orig_y = *y; int best_d = 0x7ffffff; int best_x = 0, best_y = 0; GWireNode *n; w = wire_driver(w); for (n = w->nodes;n && n->out;n = n->out) { int x1 = n->x; int y1 = n->y; int x2 = n->out->x; int y2 = n->out->y; int d = 0x7fffffff; int test_x, test_y; if (x1 == x2) { if (iabs(orig_y-y1) <= iabs(y1-y2) && iabs(orig_y-y2) <= iabs(y1-y2)) { d = iabs(orig_x-x1)*iabs(orig_x-x1); test_x = x1; test_y = orig_y; } } else { if (iabs(orig_x-x1) <= iabs(x1-x2) && iabs(orig_x-x2) <= iabs(x1-x2)) { d = iabs(orig_y-y1)*iabs(orig_y-y1); test_x = orig_x; test_y = y1; } } if (d == 0x7fffffff) { int d1 = iabs(orig_x-x1)*iabs(orig_x-x1) + iabs(orig_y-y1)*iabs(orig_y-y1); int d2 = iabs(orig_x-x2)*iabs(orig_x-x2) + iabs(orig_y-y2)*iabs(orig_y-y2); if (d1 < d2) { d = d1; test_x = x1; test_y = y1; } else { d = d2; test_x = x2; test_y = y2; } } if (d < best_d) { best_d = d; best_x = test_x; best_y = test_y; } } *x = best_x; *y = best_y; return best_d; } int GNet_getDisplayLabel(GNet *net,char label[],int len,int mode) { *label = 0; if (net->n_ionet && mode == DLM_GET_VISIBLE) { switch (net->n_ionet->typeinfo->code) { case GC_LOGICIN : case GC_LOGICOUT : case GC_LOGICTRI : return 0; } } if ((net->n_show_name && net->n_signame) || mode != DLM_GET_VISIBLE) { if (net->n_vtype != NV_WIRE) sprintf(label,"%s(%s)",net->n_signame,GNet_getVType(net)); else sprintf(label,"%s",net->n_signame); } else if (net->n_vtype != NV_WIRE) sprintf(label,"(%s)",GNet_getVType(net)); return strlen(label); } /* Get the position (x,y) and justification p for a signal name on wire w. */ void SignalNamePos(GWireNode *n1,GWireNode *n2,int *x,int *y,int *p,int *q) { *x = n1->x + (n2->x - n1->x)/3; *y = n1->y + (n2->y - n1->y)/3; if (n1->x == n2->x) { *x += 5; if (p) *p = AtLeft|BetweenTopAndBottom; } else { *x -= 5; if (p) *p = BetweenLeftAndRight|AtBottom; } if (q) *q = (n1->x == n2->x) ? (n1->y <= n2->y) : 2 + (n1->x <= n2->x); } int GNet_getWires(GNet *n,GWire **wlist,unsigned which) { return GWire_getNetWires(n->n_driver,wlist,which); } /***************************************************************************** * * Make sure the name on a net is hidden * * Parameter: * net Net on which to make all labels hidden * *****************************************************************************/ void GNet_hideName(GNet *net) { ob_touch(net); net->n_show_name = 0; GNet_checkNameVisibility(net,0); net_update(net); } /***************************************************************************** * * Make sure the name on a net is visible * * Parameter: * net Net on which to make the label visible * *****************************************************************************/ void GNet_showName(GNet *net) { ob_touch(net); net->n_show_name = 1; GNet_checkNameVisibility(net,1); net_update(net); } void GNet_checkNameVisibility(GNet *net,int action) { if (net->n_show_name) { GWire *wlist[GNet_numWires(net)]; GWireNode *n; int count = GNet_getWires(net,wlist,GW_DRIVER); int i; #if 0 printf("checkNameVis %s nw=%d count=%d\n",net->n_signame,GNet_numWires(net),count); #endif /* * Look for a labeled wire, if there is one, then return and do nothing further. */ for (i = 0;i < count;i++) { #if 0 printf(" pad %d\n",i); #endif for (n = wlist[i]->nodes;n;n = n->out) { #if 0 printf(" wire %p islab=%d\n",n,n->isLabeled); #endif if (n->isLabeled) return; } } /* * None of the existing wires were labeled, so pick one and label it. */ for (n = net->n_driver->nodes;n && n->out;n = n->out) if (!n->showSize) break; if (action) { ob_touch(n); //printf("%p: make labeled (1)\n",n); n->isLabeled = 1; n->offset = 50; } else { ob_touch(net); net->n_show_name = 0; } } } int GNet_getLabelWidth(GNet *n) { if (!n->n_labelWidth) { char label[STRMAX]; if (GNet_getDisplayLabel(n, label, STRMAX, DLM_GET_ALWAYS) > 0) { n->n_labelWidth = GKTextWidth(TkGate.stextbXF[TkGate.circuit->zoom_factor],label,strlen(label)); } else n->n_labelWidth = 42; } return n->n_labelWidth; } /***************************************************************************** * * Square of distance from a point to a line segment. * *****************************************************************************/ int segment_distance(int x,int y,int x1,int y1,int x2,int y2) { if (x1 == x2) { /* Veritical segment */ if (y1 > y2) { int t = y1;y1=y2;y2=t;} if (y < y1) { return (y-y1)*(y-y1) + (x-x1)*(x-x1); } else if (y > y2) { return (y-y2)*(y-y2) + (x-x2)*(x-x2); } else { return (x-x1)*(x-x1); } } else { /* Horizontal segment */ if (x1 > x2) { int t = x1;x1=x2;x2=t;} if (x < x1) { return (y-y1)*(y-y1) + (x-x1)*(x-x1); } else if (x > x2) { return (y-y2)*(y-y2) + (x-x2)*(x-x2); } else { return (y-y1)*(y-y1); } } } void GNet_labelClosest(GNet *net,int x,int y) { GWire *wlist[GNet_numWires(net)]; GWireNode *n,*best_n = 0; int best_dist = 0; int count = GNet_getWires(net,wlist,GW_DRIVER); int offset; int side; int i; for (i = 0;i < count;i++) { for (n = wlist[i]->nodes;n && n->out;n = n->out) { int d = segment_distance(x,y,n->x,n->y,n->out->x,n->out->y); if (!best_n || d < best_dist) { best_dist = d; best_n = n; } } } if (best_dist > 50*50) { GNet_checkNameVisibility(net,0); net_update(net); return; } /* * We found the node we wish to label... */ if (best_n) { int x1 = best_n->x; int y1 = best_n->y; int x2 = best_n->out->x; int y2 = best_n->out->y; GrabbedLabel *gl = TkGate.circuit->labelsel; GNet_draw(net); ob_touch(best_n); best_n->isLabeled = 1; if (y1 != y2) { /* Vertical */ int ay = y + gl->oy; if ((gl->position & AtTop)) ay += 12; offset = 100*(ay-y1)/(y2-y1); side = (x < x1); } else if (x1 != x2) { /* Horizontal */ int ax = x + gl->ox; if ((gl->position & AtRight)) ax -= GNet_getLabelWidth(net); offset = 100*(ax-x1)/(x2-x1); side = (y > y1); } else { offset = 50; side = 0; } if (offset < 1) offset = 1; if (offset > 99) offset = 99; best_n->offset = offset; best_n->labelSide = side; GNet_draw(net); } }