/* File: check.c * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit * simulation. * Copyright (C) 1998-2021 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 int check_includes(double x1a, double y1a, double x2a, double y2a, double x1b, double y1b, double x2b, double y2b) { if( x1b >= x1a && x2b <= x2a && y1b >= y1a && y2b <= y2a && ( (x2a-x1a)*(y2b-y1b) == (x2b-x1b)*(y2a-y1a) ) /* parallel */ ) { return 1; } return 0; } static int check_breaks(double x1, double y1, double x2, double y2, double x, double y) { if( ( (x > x1 && x < x2) || (y > y1 && y < y2) ) && ( (x2-x1)*(y-y1) == (x-x1)*(y2-y1) ) /* parallel */ ) { return 1; } return 0; } void update_conn_cues(int draw_cues, int dr_win) { int k, i, l, sqx, sqy, save_draw; struct wireentry *wptr; double x0, y0; double x1, y1, x2, y2; struct wireentry *wireptr; xWire * const wire = xctx->wire; struct iterator_ctx ctx; hash_wires(); /* must be done also if wires==0 to clear wiretable */ if(!xctx->wires) return; if(!draw_dots) return; if(cadhalfdotsize*xctx->mooz<0.7) return; x1 = X_TO_XSCHEM(xctx->areax1); y1 = Y_TO_XSCHEM(xctx->areay1); x2 = X_TO_XSCHEM(xctx->areax2); y2 = Y_TO_XSCHEM(xctx->areay2); for(init_wire_iterator(&ctx, x1, y1, x2, y2); ( wireptr = wire_iterator_next(&ctx) ) ;) { k=wireptr->n; /* optimization when editing small areas (detailed zoom) of a huge schematic */ if(LINE_OUTSIDE(wire[k].x1, wire[k].y1, wire[k].x2, wire[k].y2, x1, y1, x2, y2)) continue; for(l = 0;l < 2;l++) { if(l==0 ) { if(wire[k].end1 !=-1) continue; wire[k].end1=0; x0 = wire[k].x1; y0 = wire[k].y1; } else { if(wire[k].end2 !=-1) continue; wire[k].end2=0; x0 = wire[k].x2; y0 = wire[k].y2; } get_square(x0, y0, &sqx, &sqy); for(wptr = xctx->wiretable[sqx][sqy] ; wptr ; wptr = wptr->next) { i = wptr->n; if(i == k) { continue; /* no check wire against itself */ } if( touch(wire[i].x1, wire[i].y1, wire[i].x2, wire[i].y2, x0,y0) ) { if( (x0 != wire[i].x1 && x0 != wire[i].x2) || (y0 != wire[i].y1 && y0 != wire[i].y2) ) { if(l == 0) wire[k].end1 += 2; else wire[k].end2 += 2; } else { if(l == 0) wire[k].end1 += 1; else wire[k].end2 += 1; } } } } } dbg(3, "update_conn_cues(): check3\n"); if(draw_cues) { save_draw = draw_window; draw_window = dr_win; for(init_wire_iterator(&ctx, x1, y1, x2, y2); ( wireptr = wire_iterator_next(&ctx) ) ;) { i = wireptr->n; /* optimization when editing small areas (detailed zoom) of a huge schematic */ if(LINE_OUTSIDE(wire[i].x1, wire[i].y1, wire[i].x2, wire[i].y2, x1, y1, x2, y2)) continue; if( wire[i].end1 >1 ) { /* 20150331 draw_dots */ filledarc(WIRELAYER, ADD, wire[i].x1, wire[i].y1, cadhalfdotsize, 0, 360); } if( wire[i].end2 >1 ) { /* 20150331 draw_dots */ filledarc(WIRELAYER, ADD, wire[i].x2, wire[i].y2, cadhalfdotsize, 0, 360); } } filledarc(WIRELAYER, END, 0.0, 0.0, 0.0, 0.0, 0.0); draw_window = save_draw; } } /* start = 0: initialize timer * start = 1: return elapsed time since previous call * start = 2: return total time from initialize */ double timer(int start) { static double st, cur, lap; if(start == 0) return lap = st = (double) clock() / CLOCKS_PER_SEC; else if(start == 1) { double prevlap = lap; lap = cur = (double) clock() / CLOCKS_PER_SEC; return cur - prevlap; } else { cur = (double) clock() / CLOCKS_PER_SEC; return cur - st; } } void trim_wires(void) { int k, sqx, sqy, doloops; double x0, y0; int j, i, changed; int includes, breaks; struct wireentry *wptr; static unsigned short *wireflag=NULL; doloops = 0; xctx->prep_hash_wires = 0; timer(0); do { dbg(1, "trim_wires(): start: %g\n", timer(1)); changed = 0; doloops++; hash_wires(); /* end1 and end2 reset to -1 */ dbg(1, "trim_wires(): hash_wires_1: %g\n", timer(1)); /* break all wires */ for(i=0;iwires;i++) { int hashloopcnt = 0; x0 = xctx->wire[i].x1; y0 = xctx->wire[i].y1; get_square(x0, y0, &sqx, &sqy); k=1; for(wptr = xctx->wiretable[sqx][sqy] ; ; wptr = wptr->next) { if(!wptr) { if(k == 1) { x0 = xctx->wire[i].x2; y0 = xctx->wire[i].y2; get_square(x0, y0, &sqx, &sqy); wptr = xctx->wiretable[sqx][sqy]; k = 2; if(!wptr) break; } else break; } j = wptr->n; if(i == j) continue; hashloopcnt++; breaks = check_breaks(xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2, x0, y0); if(breaks) { /* wire[i] breaks wire[j] */ dbg(2, "trim_wires(): %d (%g %g %g %g) breaks %d (%g %g %g %g) in (%g, %g)\n", i, xctx->wire[i].x1, xctx->wire[i].y1, xctx->wire[i].x2, xctx->wire[i].y2, j, xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2, x0, y0 ); check_wire_storage(); xctx->wire[xctx->wires].x1=xctx->wire[j].x1; xctx->wire[xctx->wires].y1=xctx->wire[j].y1; xctx->wire[xctx->wires].x2=x0; xctx->wire[xctx->wires].y2=y0; xctx->wire[xctx->wires].sel=0; xctx->wire[xctx->wires].prop_ptr=NULL; my_strdup(27, &xctx->wire[xctx->wires].prop_ptr, xctx->wire[j].prop_ptr); if(!strcmp(get_tok_value(xctx->wire[xctx->wires].prop_ptr,"bus",0), "true")) xctx->wire[xctx->wires].bus=1; else xctx->wire[xctx->wires].bus=0; xctx->wire[xctx->wires].node=NULL; my_strdup(28, &xctx->wire[xctx->wires].node, xctx->wire[j].node); xctx->wire[j].x1 = x0; xctx->wire[j].y1 = y0; hash_wire(XINSERT, xctx->wires, 0); i--; /* redo current i iteration, since we break the 'j' loop due to changed wire hash table */ hash_wire(XDELETE, j, 0); /* rehash since endpoint x1, y1 changed */ hash_wire(XINSERT, j, 0); xctx->wires++; changed = 1; break; } } dbg(2, "trim_wires(): hashloopcnt = %d, wires = %d\n", hashloopcnt, xctx->wires); } dbg(1, "trim_wires(): break: %g\n", timer(1)); /* reduce included wires */ my_realloc(29, &wireflag, xctx->wires*sizeof(unsigned short)); memset(wireflag, 0, xctx->wires*sizeof(unsigned short)); for(i=0;iwires;i++) { if(wireflag[i]) continue; x0 = xctx->wire[i].x1; y0 = xctx->wire[i].y1; get_square(x0, y0, &sqx, &sqy); k=1; for(wptr = xctx->wiretable[sqx][sqy] ; ; wptr = wptr->next) { if(!wptr) { if(k == 1) { x0 = xctx->wire[i].x2; y0 = xctx->wire[i].y2; get_square(x0, y0, &sqx, &sqy); wptr = xctx->wiretable[sqx][sqy]; k = 2; if(!wptr) break; } else break; } j = wptr->n; if(i == j || wireflag[j]) continue; includes = check_includes(xctx->wire[i].x1, xctx->wire[i].y1, xctx->wire[i].x2, xctx->wire[i].y2, xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2); if(includes) { dbg(2, "trim_wires(): %d (%g %g %g %g) include %d (%g %g %g %g)\n", i, xctx->wire[i].x1, xctx->wire[i].y1, xctx->wire[i].x2, xctx->wire[i].y2, j, xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2 ); wireflag[j] = 1; } } } dbg(1, "trim_wires(): included: %g\n", timer(1)); /* delete wires */ j = 0; for(i=0;iwires;i++) { if(wireflag[i]) { j++; /* hash_wire(XDELETE, i, 0);*/ /* can not be done since wire deletions change wire idexes in array */ my_free(114, &xctx->wire[i].prop_ptr); my_free(368, &xctx->wire[i].node); continue; } if(j) { xctx->wire[i-j] = xctx->wire[i]; } } xctx->wires -= j; if(j) { xctx->prep_hash_wires=0; changed = 1; } dbg(1, "trim_wires(): delete_1: %g\n", timer(1)); /* after wire deletions full rehash is needed */ hash_wires(); my_realloc(30, &wireflag, xctx->wires*sizeof(unsigned short)); memset(wireflag, 0, xctx->wires*sizeof(unsigned short)); dbg(1, "trim_wires(): hash_wires_2: %g\n", timer(1)); /* update endpoint (end1, end2) connection counters */ for(i=0;iwires;i++) { x0 = xctx->wire[i].x1; y0 = xctx->wire[i].y1; xctx->wire[i].end1 = xctx->wire[i].end2 = 0; get_square(x0, y0, &sqx, &sqy); k=1; for(wptr = xctx->wiretable[sqx][sqy] ; ; wptr = wptr->next) { if(!wptr) { if(k == 1) { x0 = xctx->wire[i].x2; y0 = xctx->wire[i].y2; get_square(x0, y0, &sqx, &sqy); wptr = xctx->wiretable[sqx][sqy]; k = 2; if(!wptr) break; } else break; } j = wptr->n; if(i == j) continue; if( touch(xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2, x0,y0) ) { /* not parallel */ if( (xctx->wire[i].x2 - xctx->wire[i].x1) * (xctx->wire[j].y2 - xctx->wire[j].y1) != (xctx->wire[j].x2 - xctx->wire[j].x1) * (xctx->wire[i].y2 - xctx->wire[i].y1)) { /* wire[i] touches wire[j] in an inner point, not at edge */ if( (x0 != xctx->wire[j].x1 && x0 != xctx->wire[j].x2) || (y0 != xctx->wire[j].y1 && y0 != xctx->wire[j].y2) ) { if(k == 1) xctx->wire[i].end1 += 2; else xctx->wire[i].end2 += 2; } else { if(k == 1) xctx->wire[i].end1 += 1; else xctx->wire[i].end2 += 1; } } } } } dbg(1, "trim_wires(): endpoints: %g\n", timer(1)); /* merge parallel touching (in wire[i].x2, wire[i].y2) wires */ for(i=0;iwires;i++) { if(wireflag[i]) continue; x0 = xctx->wire[i].x2; y0 = xctx->wire[i].y2; get_square(x0, y0, &sqx, &sqy); for(wptr = xctx->wiretable[sqx][sqy] ; wptr ; wptr = wptr->next) { j = wptr->n; if(i == j || wireflag[j]) continue; if( touch(xctx->wire[j].x1, xctx->wire[j].y1, xctx->wire[j].x2, xctx->wire[j].y2, x0,y0) && /* parallel */ (xctx->wire[i].x2 - xctx->wire[i].x1) * (xctx->wire[j].y2 - xctx->wire[j].y1) == (xctx->wire[j].x2 - xctx->wire[j].x1) * (xctx->wire[i].y2 - xctx->wire[i].y1) && /* touch in wire[j].x1, wire[j].y1 */ xctx->wire[j].x1 == x0 && xctx->wire[j].y1 == y0 && /* no other connecting wires */ xctx->wire[i].end2 == 0 && xctx->wire[j].end1 == 0 ) { dbg(2, "trim_wires(): i=%d merged with j=%d\n", i, j); xctx->wire[i].x2 = xctx->wire[j].x2; xctx->wire[i].y2 = xctx->wire[j].y2; wireflag[j] = 1; break; } } } dbg(1, "trim_wires(): merge: %g\n", timer(1)); /* delete wires */ j = 0; for(i=0;iwires;i++) { xctx->wire[i].end1 = xctx->wire[i].end2 = -1; /* reset all endpoints we recalculate all at end */ if(wireflag[i]) { j++; /* hash_wire(XDELETE, i, 0);*/ /* can not be done since wire deletions change wire idexes in array */ my_free(116, &xctx->wire[i].prop_ptr); my_free(331, &xctx->wire[i].node); continue; } if(j) { xctx->wire[i-j] = xctx->wire[i]; } } xctx->wires -= j; if(j) { xctx->prep_hash_wires=0; /* after wire deletions full rehash is needed */ changed = 1; } dbg(1, "trim_wires(): delete_2: %g\n", timer(1)); if(changed) { xctx->need_reb_sel_arr = 1; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; set_modify(1); } } while(changed); dbg(1, "trim_wires(): doloops=%d changed=%d\n", doloops, changed); my_free(115, &wireflag); update_conn_cues(0, 0); } void break_wires_at_pins(void) { int k, i, j, r, rects, sqx, sqy; short rot, flip; struct wireentry *wptr; xRect *rct; double x0, y0, rx1, ry1; int changed=0; hash_wires(); xctx->need_reb_sel_arr=1; rebuild_selected_array(); /* for(k=0;kinstances;k++) */ for(j=0;jlastsel;j++) if(xctx->sel_array[j].type==ELEMENT) { k = xctx->sel_array[j].n; if( (rects = (xctx->inst[k].ptr+ xctx->sym)->rects[PINLAYER]) > 0 ) { for(r=0;rinst[k].ptr+ xctx->sym)->rect[PINLAYER]; x0=(rct[r].x1+rct[r].x2)/2; y0=(rct[r].y1+rct[r].y2)/2; rot=xctx->inst[k].rot; flip=xctx->inst[k].flip; ROTATION(rot, flip, 0.0,0.0,x0,y0,rx1,ry1); x0=xctx->inst[k].x0+rx1; y0=xctx->inst[k].y0+ry1; get_square(x0, y0, &sqx, &sqy); for(wptr=xctx->wiretable[sqx][sqy]; wptr; wptr=wptr->next) { i = wptr->n; if( touch(xctx->wire[i].x1, xctx->wire[i].y1, xctx->wire[i].x2, xctx->wire[i].y2, x0,y0) ) { if( (x0!=xctx->wire[i].x1 && x0!=xctx->wire[i].x2) || (y0!=xctx->wire[i].y1 && y0!=xctx->wire[i].y2) ) { if(!changed) { push_undo(); changed=1;} check_wire_storage(); xctx->wire[xctx->wires].x1=xctx->wire[i].x1; xctx->wire[xctx->wires].y1=xctx->wire[i].y1; xctx->wire[xctx->wires].x2=x0; xctx->wire[xctx->wires].y2=y0; xctx->wire[xctx->wires].sel=SELECTED; xctx->wire[xctx->wires].prop_ptr=NULL; my_strdup(31, &xctx->wire[xctx->wires].prop_ptr, xctx->wire[i].prop_ptr); if(!strcmp(get_tok_value(xctx->wire[xctx->wires].prop_ptr,"bus",0), "true")) xctx->wire[xctx->wires].bus=1; else xctx->wire[xctx->wires].bus=0; xctx->wire[xctx->wires].node=NULL; hash_wire(XINSERT, xctx->wires, 0); /* insertion happens at beginning of list */ my_strdup(32, &xctx->wire[xctx->wires].node, xctx->wire[i].node); xctx->need_reb_sel_arr=1; xctx->wires++; xctx->wire[i].x1 = x0; xctx->wire[i].y1 = y0; } } } } } } /* xctx->prep_hash_wires=0; */ /* hash_wires(); */ rebuild_selected_array(); for(j=0;jlastsel;j++) if(xctx->sel_array[j].type==WIRE) { /* for(k=0; k < xctx->wires; k++) { */ int l; k = xctx->sel_array[j].n; for(l=0;l<2;l++) { if(l==0 ) { x0 = xctx->wire[k].x1; y0 = xctx->wire[k].y1; } else { x0 = xctx->wire[k].x2; y0 = xctx->wire[k].y2; } get_square(x0, y0, &sqx, &sqy); /* printf(" k=%d, x0=%g, y0=%g\n", k, x0, y0); */ for(wptr=xctx->wiretable[sqx][sqy] ; wptr ; wptr = wptr->next) { i = wptr->n; /* printf("check wire %d to wire %d\n", k, i); */ if(i==k) { continue; /* no check wire against itself */ } if( touch(xctx->wire[i].x1, xctx->wire[i].y1, xctx->wire[i].x2, xctx->wire[i].y2, x0,y0) ) { if( (x0!=xctx->wire[i].x1 && x0!=xctx->wire[i].x2) || (y0!=xctx->wire[i].y1 && y0!=xctx->wire[i].y2) ) { /* printf("touch in mid point: %d\n", l+1); */ if(!changed) { push_undo(); changed=1;} check_wire_storage(); xctx->wire[xctx->wires].x1=xctx->wire[i].x1; xctx->wire[xctx->wires].y1=xctx->wire[i].y1; xctx->wire[xctx->wires].x2=x0; xctx->wire[xctx->wires].y2=y0; xctx->wire[xctx->wires].sel=SELECTED; xctx->wire[xctx->wires].prop_ptr=NULL; my_strdup(33, &xctx->wire[xctx->wires].prop_ptr, xctx->wire[i].prop_ptr); if(!strcmp(get_tok_value(xctx->wire[xctx->wires].prop_ptr,"bus",0), "true")) xctx->wire[xctx->wires].bus=1; else xctx->wire[xctx->wires].bus=0; xctx->wire[xctx->wires].node=NULL; hash_wire(XINSERT, xctx->wires, 0); /* insertion happens at beginning of list */ xctx->need_reb_sel_arr=1; xctx->wires++; xctx->wire[i].x1 = x0; xctx->wire[i].y1 = y0; } } } } } set_modify(1); xctx->prep_net_structs=0; xctx->prep_hi_structs=0; xctx->prep_hash_wires=0; /*update_conn_cues(0, 0); */ }