1 /* -*- tab-width: 4 -*-
2 *
3 * Electric(tm) VLSI Design System
4 *
5 * File: dbcreate.c
6 * Database general manipulation routines
7 * Written by: Steven M. Rubin, Static Free Software
8 *
9 * Copyright (c) 2000 Static Free Software.
10 *
11 * Electric(tm) is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * Electric(tm) is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Electric(tm); see the file COPYING. If not, write to
23 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24 * Boston, Mass 02111-1307, USA.
25 *
26 * Static Free Software
27 * 4119 Alpine Road
28 * Portola Valley, California 94028
29 * info@staticfreesoft.com
30 */
31
32 #include "global.h"
33 #include "egraphics.h"
34 #include "database.h"
35
36 /* prototypes for local routines */
37 static ARCPROTO *db_allocarcproto(CLUSTER*);
38 static INTBIG db_checkshortening(NODEINST*, PORTPROTO*);
39 static void db_killportproto(PORTPROTO*);
40
41 /************************* NODES *************************/
42
43 /*
44 * Routine to allocate a nodeinst from memory cluster "cluster".
45 * The routine returns NONODEINST if allocation fails.
46 */
allocnodeinst(CLUSTER * cluster)47 NODEINST *allocnodeinst(CLUSTER *cluster)
48 {
49 REGISTER NODEINST *ni;
50
51 ni = (NODEINST *)emalloc((sizeof (NODEINST)), cluster);
52 if (ni == 0) return(NONODEINST);
53 ni->lowx = ni->highx = ni->lowy = ni->highy = 0;
54 ni->transpose = 0;
55 ni->rotation = 0;
56 ni->proto = NONODEPROTO;
57 ni->parent = NONODEPROTO;
58 ni->prevnodeinst = NONODEINST;
59 ni->nextnodeinst = NONODEINST;
60 ni->geom = NOGEOM;
61 ni->previnst = NONODEINST;
62 ni->nextinst = NONODEINST;
63 ni->firstportarcinst = NOPORTARCINST;
64 ni->firstportexpinst = NOPORTEXPINST;
65 TDCLEAR(ni->textdescript);
66 defaulttextsize(5, ni->textdescript);
67 TDSETPOS(ni->textdescript, VTPOSBOXED);
68 ni->arraysize = 0;
69 ni->changeaddr = (CHAR *)NOCHANGE;
70 ni->changed = 0;
71 ni->userbits = ni->temp1 = ni->temp2 = 0;
72 ni->firstvar = NOVARIABLE;
73 ni->numvar = 0;
74 return(ni);
75 }
76
77 /*
78 * routine to return nodeinst "ni" to the pool of free nodes
79 */
freenodeinst(NODEINST * ni)80 void freenodeinst(NODEINST *ni)
81 {
82 if (ni == NONODEINST) return;
83 if (ni->numvar != 0) db_freevars(&ni->firstvar, &ni->numvar);
84 efree((CHAR *)ni);
85 }
86
startobjectchange(INTBIG addr,INTBIG type)87 void startobjectchange(INTBIG addr, INTBIG type)
88 {
89 /* handle change control and broadcast */
90 if (!db_donextchangequietly && !db_dochangesquietly)
91 {
92 (void)db_change(addr, OBJECTSTART, type, 0, 0, 0, 0, 0);
93 }
94 db_donextchangequietly = FALSE;
95 }
96
endobjectchange(INTBIG addr,INTBIG type)97 void endobjectchange(INTBIG addr, INTBIG type)
98 {
99 /* handle change control and broadcast */
100 if (!db_donextchangequietly && !db_dochangesquietly)
101 {
102 (void)db_change(addr, OBJECTEND, type, 0, 0, 0, 0, 0);
103 }
104 db_donextchangequietly = FALSE;
105 }
106
107 /*
108 * create a new nodeinst of proto "typ" located at (lx-hx, ly-hy), transposed
109 * if "trans" is nonzero, rotated "angle" tenth-degrees. The nodeinst is located
110 * in cell "parnt". The address of the nodeinst is returned.
111 * If NONODEINST is returned, there is an error creating the nodeinst.
112 */
newnodeinst(NODEPROTO * np,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG trans,INTBIG angle,NODEPROTO * parnt)113 NODEINST *newnodeinst(NODEPROTO *np, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy,
114 INTBIG trans, INTBIG angle, NODEPROTO *parnt)
115 {
116 /* make sure values are correct */
117 if (trans != 0 && trans != 1)
118 {
119 db_donextchangequietly = FALSE;
120 return((NODEINST *)db_error(DBBADTRANS|DBNEWNODEINST));
121 }
122 if (angle < 0 || angle >= 3600)
123 {
124 db_donextchangequietly = FALSE;
125 return((NODEINST *)db_error(DBBADROT|DBNEWNODEINST));
126 }
127 if (np == NONODEPROTO)
128 {
129 db_donextchangequietly = FALSE;
130 return((NODEINST *)db_error(DBBADPROTO|DBNEWNODEINST));
131 }
132 if (parnt == NONODEPROTO)
133 {
134 db_donextchangequietly = FALSE;
135 return((NODEINST *)db_error(DBBADPARENT|DBNEWNODEINST));
136 }
137 if (isachildof(parnt, np))
138 {
139 db_donextchangequietly = FALSE;
140 return((NODEINST *)db_error(DBRECURSIVE|DBNEWNODEINST));
141 }
142
143 /* build the nodeinst */
144 return(db_newnodeinst(np, lx, hx, ly, hy, trans, angle, parnt));
145 }
146
147 /*
148 * internal procedure to create a new nodeinst of proto "np" located at
149 * (lx-hx, ly-hy), transposed if "trans" is nonzero, rotated "angle" tenth-degrees.
150 * The nodeinst is located in cell "parnt". The address of the nodeinst is
151 * returned. NONODEINST is returned upon error.
152 */
db_newnodeinst(NODEPROTO * np,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG trans,INTBIG angle,NODEPROTO * parnt)153 NODEINST *db_newnodeinst(NODEPROTO *np, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy,
154 INTBIG trans, INTBIG angle, NODEPROTO *parnt)
155 {
156 REGISTER NODEINST *ni;
157
158 /* allocate the nodeinst */
159 ni = allocnodeinst(parnt->lib->cluster);
160 if (ni == NONODEINST)
161 {
162 db_donextchangequietly = FALSE;
163 return(NONODEINST);
164 }
165
166 /* initialize the nodeinst */
167 ni->proto = np;
168 ni->rotation = (INTSML)angle; ni->transpose = (INTSML)trans;
169 ni->lowx = lx; ni->highx = hx;
170 ni->lowy = ly; ni->highy = hy;
171 ni->parent = parnt;
172
173 /* create its ports */
174 ni->firstportarcinst = NOPORTARCINST;
175 ni->firstportexpinst = NOPORTEXPINST;
176
177 /* make a geometry module for this nodeinst */
178 ni->geom = allocgeom(parnt->lib->cluster);
179 if (ni->geom == NOGEOM) return(NONODEINST);
180 ni->geom->entryisnode = TRUE; ni->geom->entryaddr.ni = ni;
181
182 /* put it in appropriate lists */
183 db_enternodeinst(ni);
184
185 /* handle change control, constraint, and broadcast */
186 if (!db_donextchangequietly && !db_dochangesquietly)
187 {
188 /* set the change cell environment */
189 db_setchangecell(parnt);
190
191 /* tell all tools about this nodeinst */
192 ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTNEW, 0, 0, 0, 0, 0, 0);
193
194 /* tell constraint system about new node */
195 (*el_curconstraint->newobject)((INTBIG)ni, VNODEINST);
196 }
197 db_donextchangequietly = FALSE;
198
199 /* report nodeinst address */
200 return(ni);
201 }
202
203 /*
204 * routine to enter nodeinst "ni" in the database structure by linking it into
205 * the list of nodes of this proto, putting it in the geometry list,
206 * and announcing the existance of the nodeinst.
207 */
db_enternodeinst(NODEINST * ni)208 void db_enternodeinst(NODEINST *ni)
209 {
210 REGISTER NODEPROTO *np, *par;
211
212 /* link the geometry entry for the nodeinst */
213 par = ni->parent;
214 linkgeom(ni->geom, par);
215
216 /* adjust parent size if this node extends over edge */
217
218 /* put in list of nodes of this proto */
219 np = ni->proto;
220 if (np->firstinst != NONODEINST) np->firstinst->previnst = ni;
221 ni->nextinst = np->firstinst;
222 ni->previnst = NONODEINST;
223 np->firstinst = ni;
224
225 /* put in list of nodes in this cell */
226 if (par->firstnodeinst != NONODEINST) par->firstnodeinst->prevnodeinst = ni;
227 ni->nextnodeinst = par->firstnodeinst;
228 ni->prevnodeinst = NONODEINST;
229 par->firstnodeinst = ni;
230
231 /* mark the nodeinst alive */
232 ni->userbits &= ~DEADN;
233
234 /* mark a change to the database */
235 db_changetimestamp++;
236 }
237
238 /*
239 * free up network nodeinst pointed to by "ni".
240 * Returns true if the nodeinst has arcs still connected to it (an error)
241 */
killnodeinst(NODEINST * ni)242 BOOLEAN killnodeinst(NODEINST *ni)
243 {
244 /* error if nodeinst is not valid */
245 if (ni == NONODEINST)
246 {
247 db_donextchangequietly = FALSE;
248 return(db_error(DBBADINST|DBKILLNODEINST) != 0);
249 }
250
251 /* error if any arcs still connected to this nodeinst */
252 if (ni->firstportarcinst != NOPORTARCINST)
253 {
254 db_donextchangequietly = FALSE;
255 return(db_error(DBHASARCS|DBKILLNODEINST) != 0);
256 }
257
258 /* error if any exports */
259 if (ni->firstportexpinst != NOPORTEXPINST)
260 {
261 db_donextchangequietly = FALSE;
262 return(db_error(DBHASPORTS|DBKILLNODEINST) != 0);
263 }
264
265 /* remove nodeinst from lists */
266 db_killnodeinst(ni);
267
268 /* freeing of the nodeinst and ports will be done later */
269 return(FALSE);
270 }
271
272 /*
273 * routine to remove nodeinst "ni" from the database structure by un-linking
274 * it from the list of nodes of this proto, removing it from the geometry
275 * list, and announcing the deletion of the nodeinst.
276 */
db_killnodeinst(NODEINST * ni)277 void db_killnodeinst(NODEINST *ni)
278 {
279 db_retractnodeinst(ni);
280
281 /* handle change control, constraint, and broadcast */
282 if (!db_donextchangequietly && !db_dochangesquietly)
283 {
284 /* set the change cell environment */
285 db_setchangecell(ni->parent);
286
287 /* tell all tools about this nodeinst */
288 ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTKILL, 0, 0, 0, 0, 0, 0);
289
290 /* tell constraint system about killed node */
291 (*el_curconstraint->killobject)((INTBIG)ni, VNODEINST);
292 } else
293 {
294 /* must deallocate now: no change control */
295 freegeom(ni->geom);
296 freenodeinst(ni);
297 }
298 db_donextchangequietly = FALSE;
299 }
300
301 /*
302 * routine to remove nodeinst "ni" from the database structure by un-linking
303 * it from the list of nodes of this proto and removing it from the geometry list.
304 */
db_retractnodeinst(NODEINST * ni)305 void db_retractnodeinst(NODEINST *ni)
306 {
307 /* remove from list of nodes of this kind */
308 if (ni->nextinst != NONODEINST) ni->nextinst->previnst = ni->previnst;
309 if (ni->previnst != NONODEINST) ni->previnst->nextinst = ni->nextinst; else
310 ni->proto->firstinst = ni->nextinst;
311
312 /* remove from list of nodes in this cell */
313 if (ni->nextnodeinst != NONODEINST) ni->nextnodeinst->prevnodeinst = ni->prevnodeinst;
314 if (ni->prevnodeinst != NONODEINST) ni->prevnodeinst->nextnodeinst = ni->nextnodeinst; else
315 ni->parent->firstnodeinst = ni->nextnodeinst;
316
317 /* remove from R-tree */
318 undogeom(ni->geom, ni->parent);
319
320 /* mark the nodeinst dead */
321 ni->userbits |= DEADN;
322
323 /* mark a change to the database */
324 db_changetimestamp++;
325 }
326
327 /*
328 * Routine to determine the default size of primitive node "np" and return it in
329 * (xs, ys).
330 */
defaultnodesize(NODEPROTO * np,INTBIG * xs,INTBIG * ys)331 void defaultnodesize(NODEPROTO *np, INTBIG *xs, INTBIG *ys)
332 {
333 REGISTER VARIABLE *var;
334
335 /* take default size from the prototype */
336 *xs = np->highx - np->lowx;
337 *ys = np->highy - np->lowy;
338
339 /* cells always use this */
340 if (np->primindex == 0) return;
341
342 /* see if there is an override on the node */
343 var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_node_size_default_key);
344 if (var != NOVARIABLE)
345 {
346 *xs = ((INTBIG *)var->addr)[0] * el_curlib->lambda[np->tech->techindex] / WHOLE;
347 *ys = ((INTBIG *)var->addr)[1] * el_curlib->lambda[np->tech->techindex] / WHOLE;
348 }
349 }
350
351 /*
352 * routine to modify nodeinst "ni" by "deltalx" in low X, "deltaly" in low Y,
353 * "deltahx" in high X, "deltahy" in high Y, "deltarot" in rotation, and
354 * "deltatrans" in transgeometry.
355 */
modifynodeinst(NODEINST * ni,INTBIG deltalx,INTBIG deltaly,INTBIG deltahx,INTBIG deltahy,INTBIG deltarot,INTBIG deltatrans)356 void modifynodeinst(NODEINST *ni, INTBIG deltalx, INTBIG deltaly, INTBIG deltahx,
357 INTBIG deltahy, INTBIG deltarot, INTBIG deltatrans)
358 {
359 /* examine the nature of the changes */
360 deltarot = deltarot % 3600;
361 if (deltarot < 0) deltarot += 3600;
362 if (deltatrans != 0) deltatrans = 1;
363 if (deltalx == 0 && deltahx == 0 && deltaly == 0 && deltahy == 0 && deltarot == 0 &&
364 deltatrans == 0)
365 {
366 db_donextchangequietly = FALSE;
367 return;
368 }
369
370 if (!db_donextchangequietly && !db_dochangesquietly)
371 {
372 /* tell constraint system about modified node */
373 (*el_curconstraint->modifynodeinst)(ni, deltalx, deltaly, deltahx, deltahy, deltarot, deltatrans);
374 } else
375 {
376 /* make the change now: no constraint system */
377 ni->lowx += deltalx; ni->highx += deltahx;
378 ni->lowy += deltaly; ni->highy += deltahy;
379 ni->rotation += (INTSML)deltarot;
380 ni->transpose += (INTSML)deltatrans;
381 }
382 db_donextchangequietly = FALSE;
383
384 /* set the change cell environment */
385 db_setchangecell(ni->parent);
386
387 /* mark a change to the database */
388 db_changetimestamp++;
389 }
390
391 /*
392 * routine to modify "count" nodeinsts "ni" by "deltalx" in low X, "deltaly" in low Y,
393 * "deltahx" in high X, "deltahy" in high Y, "deltarot" in rotation, and
394 * "deltatrans" in transgeometry.
395 */
modifynodeinsts(INTBIG count,NODEINST ** nis,INTBIG * deltalxs,INTBIG * deltalys,INTBIG * deltahxs,INTBIG * deltahys,INTBIG * deltarots,INTBIG * deltatranss)396 void modifynodeinsts(INTBIG count, NODEINST **nis, INTBIG *deltalxs, INTBIG *deltalys,
397 INTBIG *deltahxs, INTBIG *deltahys, INTBIG *deltarots, INTBIG *deltatranss)
398 {
399 REGISTER INTBIG i, deltalx, deltaly, deltahx, deltahy;
400 REGISTER INTBIG deltarot, deltatrans;
401 REGISTER NODEINST *ni;
402
403 if (!db_donextchangequietly && !db_dochangesquietly)
404 {
405 /* tell constraint system about modified node */
406 (*el_curconstraint->modifynodeinsts)(count, nis, deltalxs, deltalys, deltahxs, deltahys, deltarots, deltatranss);
407 for(i=0; i<count; i++)
408 db_setchangecell(nis[i]->parent);
409 } else
410 {
411 for(i=0; i<count; i++)
412 {
413 ni = nis[i];
414 deltalx = deltalxs[i];
415 deltaly = deltalys[i];
416 deltahx = deltahxs[i];
417 deltahy = deltahys[i];
418 deltarot = deltarots[i];
419 deltatrans = deltatranss[i];
420
421 /* examine the nature of the changes */
422 deltarot = deltarot % 3600;
423 if (deltarot < 0) deltarot += 3600;
424 if (deltatrans != 0) deltatrans = 1;
425 if (deltalx == 0 && deltahx == 0 && deltaly == 0 && deltahy == 0 && deltarot == 0 &&
426 deltatrans == 0) continue;
427
428 /* make the change now: no constraint system */
429 ni->lowx += deltalx; ni->highx += deltahx;
430 ni->lowy += deltaly; ni->highy += deltahy;
431 ni->rotation += (INTSML)deltarot;
432 ni->transpose += (INTSML)deltatrans;
433 }
434 }
435
436 /* turn off temp flag for quiet changes */
437 db_donextchangequietly = FALSE;
438
439 /* mark a change to the database */
440 db_changetimestamp++;
441 }
442
443 /*
444 * routine to initialize a nodeinst that it not in the
445 * database (for miscellaneous use)
446 */
initdummynode(NODEINST * ni)447 void initdummynode(NODEINST *ni)
448 {
449 ni->rotation = 0;
450 ni->transpose = 0;
451 ni->proto = el_technologies->firstnodeproto;
452 ni->parent = NONODEPROTO;
453 ni->firstportarcinst = NOPORTARCINST;
454 ni->firstportexpinst = NOPORTEXPINST;
455 ni->userbits = 0;
456 TDCLEAR(ni->textdescript);
457 defaulttextsize(5, ni->textdescript);
458 TDSETPOS(ni->textdescript, VTPOSBOXED);
459 ni->numvar = 0;
460 }
461
462 /************************* ARC PROTOTYPES *************************/
463
464 /*
465 * routine to allocate "count" arcprotos from memory cluster "cluster"
466 * (arcprotos are never freed) and place them in the array at "addresses".
467 * The routine sets an element to NOARCPROTO if allocation fails.
468 */
db_allocarcproto(CLUSTER * cluster)469 ARCPROTO *db_allocarcproto(CLUSTER *cluster)
470 {
471 REGISTER ARCPROTO *ap;
472
473 ap = (ARCPROTO *)emalloc((sizeof (ARCPROTO)), cluster);
474 if (ap == 0) return(NOARCPROTO);
475 ap->userbits = ap->temp1 = ap->temp2 = 0;
476 ap->numvar = 0;
477 ap->protoname = NOSTRING;
478 ap->firstvar = NOVARIABLE;
479 ap->tech = NOTECHNOLOGY;
480 ap->nextarcproto = NOARCPROTO;
481 ap->nominalwidth = 0;
482 ap->arcindex = 0;
483 return(ap);
484 }
485
486 /*
487 * routine to return arcproto "ap" to the pool of free arcs
488 */
db_freearcproto(ARCPROTO * ap)489 void db_freearcproto(ARCPROTO *ap)
490 {
491 if (ap == NOARCPROTO) return;
492 if (ap->numvar != 0) db_freevars(&ap->firstvar, &ap->numvar);
493 efree((CHAR *)ap);
494 }
495
496 /*
497 * routine to create a new arc prototype with name "name" and default wire
498 * width "nomwidth". The arc prototype is placed in technology "tech". The
499 * index of the arc prototype is in "ind" which must be a unique number that
500 * identifies this arcinst proto. The routine returns zero if the new arc
501 * prototype has successfully been created. It returns NOARCPROTO if the arc
502 * prototype cannot be created.
503 */
db_newarcproto(TECHNOLOGY * tech,CHAR * name,INTBIG nomwidth,INTBIG ind)504 ARCPROTO *db_newarcproto(TECHNOLOGY *tech, CHAR *name, INTBIG nomwidth, INTBIG ind)
505 {
506 REGISTER ARCPROTO *ap;
507 REGISTER ARCPROTO *lat, *tat;
508 REGISTER CHAR *pp;
509
510 /* make sure name is valid */
511 for(pp = name; *pp != 0; pp++) if (*pp <= ' ' || *pp >= 0177) return(NOARCPROTO);
512
513 ap = db_allocarcproto(tech->cluster);
514 if (ap == NOARCPROTO) return(NOARCPROTO);
515 for(lat = NOARCPROTO, tat = tech->firstarcproto; tat != NOARCPROTO; tat = tat->nextarcproto)
516 lat = tat;
517 if (lat == NOARCPROTO) tech->firstarcproto = ap; else
518 lat->nextarcproto = ap;
519 ap->nextarcproto = NOARCPROTO;
520 if (allocstring(&ap->protoname, name, tech->cluster)) return(NOARCPROTO);
521 ap->nominalwidth = nomwidth;
522 ap->arcindex = ind;
523 ap->tech = tech;
524 return(ap);
525 }
526
527 /************************* ARC INSTANCES *************************/
528
529 /*
530 * Routine to allocate an arcinst from memory cluster "cluster".
531 * The routine returns NOARCINST if allocation fails.
532 */
allocarcinst(CLUSTER * cluster)533 ARCINST *allocarcinst(CLUSTER *cluster)
534 {
535 REGISTER ARCINST *ai;
536
537 ai = (ARCINST *)emalloc((sizeof (ARCINST)), cluster);
538 if (ai == 0) return(NOARCINST);
539 ai->changed = 0; ai->changeaddr = (CHAR *)NOCHANGE;
540 ai->userbits = ai->temp1 = ai->temp2 = 0;
541 ai->numvar = 0;
542 ai->network = NONETWORK;
543 ai->firstvar = NOVARIABLE;
544 ai->nextarcinst = NOARCINST;
545 ai->geom = NOGEOM;
546 ai->proto = NOARCPROTO;
547 ai->length = 0;
548 ai->width = 0;
549 ai->endshrink = 0;
550 ai->end[0].xpos = ai->end[0].ypos = 0;
551 ai->end[1].xpos = ai->end[1].ypos = 0;
552 ai->end[0].nodeinst = ai->end[1].nodeinst = NONODEINST;
553 ai->end[0].portarcinst = ai->end[1].portarcinst = NOPORTARCINST;
554 ai->prevarcinst = NOARCINST;
555 ai->parent = NONODEPROTO;
556 return(ai);
557 }
558
559 /*
560 * routine to return arcinst "ai" to the pool of free arc instances
561 */
freearcinst(ARCINST * ai)562 void freearcinst(ARCINST *ai)
563 {
564 if (ai == NOARCINST) return;
565 if (ai->numvar != 0) db_freevars(&ai->firstvar, &ai->numvar);
566 efree((CHAR *)ai);
567 }
568
569 /*
570 * Routine to allocate a portarcinst from memory cluster "cluster".
571 * The routine returns NOPORTARCINST if allocation fails.
572 */
allocportarcinst(CLUSTER * cluster)573 PORTARCINST *allocportarcinst(CLUSTER *cluster)
574 {
575 REGISTER PORTARCINST *pi;
576
577 pi = (PORTARCINST *)emalloc((sizeof (PORTARCINST)), cluster);
578 if (pi == 0) return(NOPORTARCINST);
579 pi->firstvar = NOVARIABLE;
580 pi->numvar = 0;
581 pi->nextportarcinst = NOPORTARCINST;
582 pi->proto = NOPORTPROTO;
583 pi->conarcinst = NOARCINST;
584 return(pi);
585 }
586
587 /*
588 * routine to return portarcinst "pi" to the pool of free ports
589 */
freeportarcinst(PORTARCINST * pi)590 void freeportarcinst(PORTARCINST *pi)
591 {
592 if (pi == NOPORTARCINST) return;
593 if (pi->numvar != 0) db_freevars(&pi->firstvar, &pi->numvar);
594 efree((CHAR *)pi);
595 }
596
597 /*
598 * create a new arcinst of proto "typ", width "width", and initial userbits in
599 * "initialbits". One end is connected to portproto "pA" of nodeinst
600 * "nA" and the other end is connected to portproto "pB" of nodeinst "nB".
601 * The arcinst is located in cell "parnt". The address of the arcinst is
602 * returned (NOARCINST if an error is found).
603 */
newarcinst(ARCPROTO * typ,INTBIG wid,INTBIG initialbits,NODEINST * nA,PORTPROTO * pA,INTBIG xA,INTBIG yA,NODEINST * nB,PORTPROTO * pB,INTBIG xB,INTBIG yB,NODEPROTO * parnt)604 ARCINST *newarcinst(ARCPROTO *typ, INTBIG wid, INTBIG initialbits, NODEINST *nA,
605 PORTPROTO *pA, INTBIG xA, INTBIG yA, NODEINST *nB, PORTPROTO *pB, INTBIG xB, INTBIG yB,
606 NODEPROTO *parnt)
607 {
608 REGISTER INTBIG i, lambda;
609 REGISTER PORTPROTO *pp;
610 REGISTER ARCINST *ai;
611 REGISTER POLYGON *poly;
612
613 /* check for missing specifications */
614 if (typ == NOARCPROTO)
615 {
616 db_donextchangequietly = FALSE;
617 return((ARCINST *)db_error(DBBADPROTO|DBNEWARCINST));
618 }
619 if (parnt == NONODEPROTO)
620 {
621 db_donextchangequietly = FALSE;
622 return((ARCINST *)db_error(DBBADPARENT|DBNEWARCINST));
623 }
624 if (wid < 0)
625 {
626 db_donextchangequietly = FALSE;
627 return((ARCINST *)db_error(DBBADWIDTH|DBNEWARCINST));
628 }
629 if (nA == NONODEINST || pA == NOPORTPROTO)
630 {
631 db_donextchangequietly = FALSE;
632 return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
633 }
634 if (nB == NONODEINST || pB == NOPORTPROTO)
635 {
636 db_donextchangequietly = FALSE;
637 return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
638 }
639
640 /* make sure that the node is in the cell */
641 if (nA->parent != parnt)
642 {
643 db_donextchangequietly = FALSE;
644 return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
645 }
646 if (nB->parent != parnt)
647 {
648 db_donextchangequietly = FALSE;
649 return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
650 }
651
652 /* make sure that the port proto is on the node */
653 for(pp = nA->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
654 if (pp == pA) break;
655 if (pp == NOPORTPROTO)
656 {
657 db_donextchangequietly = FALSE;
658 return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
659 }
660 for(pp = nB->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
661 if (pp == pB) break;
662 if (pp == NOPORTPROTO)
663 {
664 db_donextchangequietly = FALSE;
665 return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
666 }
667
668 /* make sure that the arc can connect to the port */
669 for(i=0; pA->connects[i] != NOARCPROTO; i++)
670 if (pA->connects[i] == typ) break;
671 if (pA->connects[i] == NOARCPROTO)
672 {
673 (void)db_error(DBBADENDAC|DBNEWARCINST);
674 if (db_printerrors)
675 ttyputmsg(_("Port %s of node %s in cell %s cannot connect to arc %s"), pA->protoname,
676 describenodeinst(nA), describenodeproto(parnt), describearcproto(typ));
677 db_donextchangequietly = FALSE;
678 return(NOARCINST);
679 }
680 for(i=0; pB->connects[i] != NOARCPROTO; i++)
681 if (pB->connects[i] == typ) break;
682 if (pB->connects[i] == NOARCPROTO)
683 {
684 (void)db_error(DBBADENDBC|DBNEWARCINST);
685 if (db_printerrors)
686 ttyputmsg(_("Port %s of node %s in cell %s cannot connect to arc %s"), pB->protoname,
687 describenodeinst(nB), describenodeproto(parnt), describearcproto(typ));
688 db_donextchangequietly = FALSE;
689 return(NOARCINST);
690 }
691
692 /* check that arcinst ends in proper portinst area */
693 poly = allocpolygon(4, db_cluster);
694 shapeportpoly(nA, pA, poly, FALSE);
695 if (!isinside(xA, yA, poly))
696 {
697 (void)db_error(DBBADENDAP|DBNEWARCINST);
698 if (db_printerrors)
699 {
700 lambda = lambdaofcell(parnt);
701 ttyputmsg(_("Point (%s,%s) not inside port %s of node %s in cell %s"), latoa(xA, lambda),
702 latoa(yA, lambda), pA->protoname, describenodeinst(nA), describenodeproto(parnt));
703 }
704 db_donextchangequietly = FALSE;
705 freepolygon(poly);
706 return(NOARCINST);
707 }
708 shapeportpoly(nB, pB, poly, FALSE);
709 if (!isinside(xB, yB, poly))
710 {
711 (void)db_error(DBBADENDBP|DBNEWARCINST);
712 if (db_printerrors)
713 {
714 lambda = lambdaofcell(parnt);
715 ttyputmsg(_("Point (%s,%s) not inside port %s of node %s in cell %s"), latoa(xB, lambda),
716 latoa(yB, lambda), pB->protoname, describenodeinst(nB), describenodeproto(parnt));
717 }
718 db_donextchangequietly = FALSE;
719 freepolygon(poly);
720 return(NOARCINST);
721 }
722 freepolygon(poly);
723
724 /* mark the start of a change to any cell instances that this arc connects */
725 if (nA->proto->primindex == 0) startobjectchange((INTBIG)nA, VNODEINST);
726 if (nB->proto->primindex == 0) startobjectchange((INTBIG)nB, VNODEINST);
727
728 /* create the arcinst */
729 ai = db_newarcinst(typ, wid, initialbits, nA,pA,xA,yA, nB,pB,xB,yB, parnt);
730
731 /* mark the start of a change to any cell instances that this arc connects */
732 if (nA->proto->primindex == 0) endobjectchange((INTBIG)nA, VNODEINST);
733 if (nB->proto->primindex == 0) endobjectchange((INTBIG)nB, VNODEINST);
734
735 return(ai);
736 }
737
738 /*
739 * internal procedure to create a new arcinst of proto "typ" with width
740 * "width". One end is connected to portproto "pA" of nodeinst "nA" and the
741 * other end is connected to portproto "pB" of nodeinst "nB". The arcinst
742 * is located in cell "parnt". The address of the arcinst is returned.
743 * NOARCINST is returned upon error.
744 */
db_newarcinst(ARCPROTO * typ,INTBIG wid,INTBIG initialbits,NODEINST * nA,PORTPROTO * pA,INTBIG xA,INTBIG yA,NODEINST * nB,PORTPROTO * pB,INTBIG xB,INTBIG yB,NODEPROTO * parnt)745 ARCINST *db_newarcinst(ARCPROTO *typ, INTBIG wid, INTBIG initialbits, NODEINST *nA,
746 PORTPROTO *pA, INTBIG xA, INTBIG yA, NODEINST *nB, PORTPROTO *pB, INTBIG xB, INTBIG yB,
747 NODEPROTO *parnt)
748 {
749 REGISTER ARCINST *ai;
750 REGISTER PORTARCINST *npi;
751
752 ai = allocarcinst(parnt->lib->cluster);
753 if (ai == NOARCINST)
754 {
755 db_donextchangequietly = FALSE;
756 return(NOARCINST);
757 }
758 ai->proto = typ;
759 ai->width = wid;
760 ai->length = computedistance(xA,yA, xB,yB);
761 ai->end[0].xpos = xA; ai->end[0].ypos = yA;
762 ai->end[1].xpos = xB; ai->end[1].ypos = yB;
763 ai->end[0].nodeinst = nA;
764 ai->end[1].nodeinst = nB;
765 ai->userbits = initialbits;
766 determineangle(ai);
767 ai->parent = parnt;
768 ai->endshrink = 0;
769
770 ai->end[0].portarcinst = allocportarcinst(parnt->lib->cluster);
771 npi = ai->end[0].portarcinst;
772 npi->proto = pA;
773 db_addportarcinst(nA, npi);
774
775 ai->end[1].portarcinst = allocportarcinst(parnt->lib->cluster);
776 npi = ai->end[1].portarcinst;
777 npi->proto = pB;
778 db_addportarcinst(nB, npi);
779
780 /* create a geometry module for this arcinst */
781 ai->geom = allocgeom(parnt->lib->cluster);
782 if (ai->geom == NOGEOM) return(NOARCINST);
783 ai->geom->entryisnode = FALSE;
784 ai->geom->entryaddr.ai = ai;
785
786 /* enter arcinst in appropriate lists */
787 db_enterarcinst(ai);
788
789 /* handle change control, constraint, and broadcast */
790 if (!db_donextchangequietly && !db_dochangesquietly)
791 {
792 /* set the change cell environment */
793 db_setchangecell(parnt);
794
795 /* tell all tools about this arcinst */
796 ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTNEW, 0, 0, 0, 0, 0, 0);
797
798 /* tell constraint system about new arc */
799 (*el_curconstraint->newobject)((INTBIG)ai, VARCINST);
800 }
801 db_donextchangequietly = FALSE;
802
803 /* report arcinst address */
804 return(ai);
805 }
806
807 /*
808 * routine to enter arcinst "ai" in the database structure by including it in
809 * the nodes it connects, putting it in the geometry list, and announcing
810 * the existance of the arcinst.
811 */
db_enterarcinst(ARCINST * ai)812 void db_enterarcinst(ARCINST *ai)
813 {
814 REGISTER INTBIG i;
815 REGISTER NODEPROTO *np;
816 REGISTER NODEINST *ni;
817 REGISTER ARCINST *oai;
818 REGISTER PORTARCINST *pi;
819
820 /* place connection in the nodes */
821 for(i=0; i<2; i++) ai->end[i].portarcinst->conarcinst = ai;
822
823 /* put in list of arcs in this cell */
824 np = ai->parent;
825 if (np->firstarcinst != NOARCINST) np->firstarcinst->prevarcinst = ai;
826 ai->nextarcinst = np->firstarcinst;
827 ai->prevarcinst = NOARCINST;
828 np->firstarcinst = ai;
829 (void)setshrinkvalue(ai, TRUE);
830
831 /* link the geometry entry for this arcinst */
832 linkgeom(ai->geom, np);
833
834 /* special case: if connecting nodes that change size with connectivity, update their geometry */
835 if ((ai->end[0].nodeinst->proto->userbits&WIPEON1OR2) != 0)
836 updategeom(ai->end[0].nodeinst->geom, np);
837 if ((ai->end[1].nodeinst->proto->userbits&WIPEON1OR2) != 0)
838 updategeom(ai->end[1].nodeinst->geom, np);
839
840 /* special case: update end shrinkage on connected arcs */
841 for(i=0; i<2; i++)
842 {
843 ni = ai->end[i].nodeinst;
844 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
845 {
846 oai = pi->conarcinst;
847 if (oai == ai) continue;
848 if (setshrinkvalue(oai, TRUE) != 0)
849 updategeom(oai->geom, oai->parent);
850 }
851 }
852
853 /* mark the arcinst alive */
854 ai->userbits &= ~DEADA;
855
856 /* mark a change to the database */
857 db_changetimestamp++;
858 }
859
860 /*
861 * routine to add portarcinst "npi" to the list of portarcinsts on nodeinst
862 * "ni".
863 */
db_addportarcinst(NODEINST * ni,PORTARCINST * npi)864 void db_addportarcinst(NODEINST *ni, PORTARCINST *npi)
865 {
866 REGISTER PORTPROTO *pp, *pr;
867 REGISTER PORTARCINST *pi, *lpi;
868
869 pr = npi->proto;
870 pp = ni->proto->firstportproto;
871 lpi = NOPORTARCINST;
872 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
873 {
874 while (pp != pi->proto && pp != pr) pp = pp->nextportproto;
875 if (pp == pr)
876 {
877 if (lpi == NOPORTARCINST) ni->firstportarcinst = npi; else
878 lpi->nextportarcinst = npi;
879 npi->nextportarcinst = pi;
880 return;
881 }
882 lpi = pi;
883 }
884
885 /* not found inside list: append to end */
886 if (lpi == NOPORTARCINST) ni->firstportarcinst = npi; else
887 lpi->nextportarcinst = npi;
888 npi->nextportarcinst = NOPORTARCINST;
889 }
890
891 /*
892 * routine to remove arcinst "ai" from the database. The routine returns
893 * FALSE if successful, TRUE if an error.
894 */
killarcinst(ARCINST * ai)895 BOOLEAN killarcinst(ARCINST *ai)
896 {
897 REGISTER NODEINST *ni1, *ni2;
898
899 if (ai == NOARCINST)
900 {
901 db_donextchangequietly = FALSE;
902 return(db_error(DBBADINST|DBKILLARCINST) != 0);
903 }
904
905 /* mark the start of a change to any cell instances that this arc connects */
906 ni1 = ai->end[0].nodeinst; ni2 = ai->end[1].nodeinst;
907 if (ni1->proto->primindex == 0) startobjectchange((INTBIG)ni1, VNODEINST);
908 if (ni2->proto->primindex == 0) startobjectchange((INTBIG)ni2, VNODEINST);
909
910 /* remove arcinst from appropriate lists */
911 db_killarcinst(ai);
912
913 /* mark the start of a change to any cell instances that this arc connects */
914 if (ni1->proto->primindex == 0) endobjectchange((INTBIG)ni1, VNODEINST);
915 if (ni2->proto->primindex == 0) endobjectchange((INTBIG)ni2, VNODEINST);
916
917 /* freeing of the arcinst will be done later */
918 return(FALSE);
919 }
920
db_killarcinst(ARCINST * ai)921 void db_killarcinst(ARCINST *ai)
922 {
923 db_retractarcinst(ai);
924
925 /* handle change control, constraint, and broadcast */
926 if (!db_donextchangequietly && !db_dochangesquietly)
927 {
928 /* set the change cell environment */
929 db_setchangecell(ai->parent);
930
931 /* tell all tools about this arcinst */
932 ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTKILL, 0, 0, 0, 0, 0, 0);
933
934 /* tell constraint system about killed arc */
935 (*el_curconstraint->killobject)((INTBIG)ai, VARCINST);
936 } else
937 {
938 /* delete the arc now: no change control */
939 freeportarcinst(ai->end[0].portarcinst);
940 freeportarcinst(ai->end[1].portarcinst);
941 freegeom(ai->geom);
942 freearcinst(ai);
943 }
944 db_donextchangequietly = FALSE;
945 }
946
db_retractarcinst(ARCINST * ai)947 void db_retractarcinst(ARCINST *ai)
948 {
949 REGISTER INTBIG i;
950 REGISTER NODEINST *ni;
951 REGISTER PORTARCINST *pi, *lpi;
952 REGISTER ARCINST *oai;
953
954 /* remove from list of arcs in this cell */
955 if (ai->nextarcinst != NOARCINST) ai->nextarcinst->prevarcinst = ai->prevarcinst;
956 if (ai->prevarcinst != NOARCINST) ai->prevarcinst->nextarcinst = ai->nextarcinst; else
957 ai->parent->firstarcinst = ai->nextarcinst;
958
959 /* remove from R-tree geometric list */
960 undogeom(ai->geom, ai->parent);
961
962 /* now update any nodes touching this arcinst */
963 for(i=0; i<2; i++)
964 {
965 ni = ai->end[i].nodeinst;
966 if (ni == NONODEINST) continue;
967 if ((ni->userbits&DEADN) != 0) continue;
968 lpi = NOPORTARCINST;
969 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
970 {
971 if (pi->conarcinst == ai)
972 {
973 if (lpi == NOPORTARCINST) ni->firstportarcinst = pi->nextportarcinst; else
974 lpi->nextportarcinst = pi->nextportarcinst;
975 }
976 lpi = pi;
977 }
978 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
979 {
980 oai = pi->conarcinst;
981 if (oai == ai) continue;
982 if (setshrinkvalue(oai, TRUE) != 0)
983 updategeom(oai->geom, oai->parent);
984 }
985
986 /* special case: if disconnecting from node that changes size with connectivity, update its geometry */
987 if ((ni->proto->userbits&WIPEON1OR2) != 0)
988 updategeom(ni->geom, ai->parent);
989 }
990
991 /* mark it dead */
992 ai->userbits |= DEADA;
993
994 /* mark a change to the database */
995 db_changetimestamp++;
996 }
997
998 /*
999 * Routine to return the default width of primitive arc "ap".
1000 */
defaultarcwidth(ARCPROTO * ap)1001 INTBIG defaultarcwidth(ARCPROTO *ap)
1002 {
1003 REGISTER INTBIG width;
1004 REGISTER VARIABLE *var;
1005
1006 /* take default width from the prototype */
1007 width = ap->nominalwidth;
1008
1009 /* see if there is an override on the arc */
1010 var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, el_arc_width_default_key);
1011 if (var != NOVARIABLE)
1012 width = var->addr * el_curlib->lambda[ap->tech->techindex] / WHOLE;
1013
1014 return(width);
1015 }
1016
1017 /*
1018 * routine to modify arcinst "ai" by "deltawid" in width, "deltax" in both
1019 * ends X geometry and "deltay" in both ends Y position. The X and Y
1020 * motion must not cause the connecting nodes to move. The routine
1021 * returns true if an error is detected. Null arc motion is allowed
1022 * to queue some other change to the arc.
1023 */
modifyarcinst(ARCINST * ai,INTBIG deltawid,INTBIG deltax1,INTBIG deltay1,INTBIG deltax2,INTBIG deltay2)1024 BOOLEAN modifyarcinst(ARCINST *ai, INTBIG deltawid, INTBIG deltax1, INTBIG deltay1,
1025 INTBIG deltax2, INTBIG deltay2)
1026 {
1027 REGISTER INTBIG oldwid, oldlen, oldx0, oldy0, oldx1, oldy1;
1028 REGISTER BOOLEAN e1, e2;
1029
1030 if (ai == NOARCINST)
1031 {
1032 db_donextchangequietly = FALSE;
1033 return(db_error(DBBADINST|DBMODIFYARCINST) != 0);
1034 }
1035 if (ai->width + deltawid < 0)
1036 {
1037 db_donextchangequietly = FALSE;
1038 return(db_error(DBBADNEWWID|DBMODIFYARCINST) != 0);
1039 }
1040 if (deltax1 != 0 || deltay1 != 0 || deltax2 != 0 || deltay2 != 0)
1041 {
1042 if ((ai->userbits&(FIXED|CANTSLIDE)) != 0)
1043 {
1044 db_donextchangequietly = FALSE;
1045 return(db_error(DBNOSLIDING|DBMODIFYARCINST) != 0);
1046 }
1047
1048 /* temporarily set the width and check for port validity */
1049 ai->width += deltawid;
1050 e1 = db_stillinport(ai, 0, ai->end[0].xpos+deltax1, ai->end[0].ypos+deltay1);
1051 e2 = db_stillinport(ai, 1, ai->end[1].xpos+deltax2, ai->end[1].ypos+deltay2);
1052 ai->width -= deltawid;
1053 if (!e1 || !e2)
1054 {
1055 db_donextchangequietly = FALSE;
1056 return(db_error(DBNOTINPORT|DBMODIFYARCINST) != 0);
1057 }
1058 }
1059
1060 /* change the arcinst */
1061 oldwid = ai->width; oldlen = ai->length;
1062 ai->width += deltawid;
1063 oldx0 = ai->end[0].xpos; ai->end[0].xpos += deltax1;
1064 oldy0 = ai->end[0].ypos; ai->end[0].ypos += deltay1;
1065 oldx1 = ai->end[1].xpos; ai->end[1].xpos += deltax2;
1066 oldy1 = ai->end[1].ypos; ai->end[1].ypos += deltay2;
1067 ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
1068 (void)setshrinkvalue(ai, TRUE);
1069 updategeom(ai->geom, ai->parent);
1070
1071 /* handle change control, constraint, and broadcast */
1072 if (!db_donextchangequietly && !db_dochangesquietly)
1073 {
1074 /* tell all tools about this arcinst */
1075 ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldx0, oldy0, oldx1, oldy1,
1076 oldwid, oldlen);
1077
1078 /* tell constraint system about modified arc */
1079 (*el_curconstraint->modifyarcinst)(ai, oldx0, oldy0, oldx1, oldy1, oldwid, oldlen);
1080
1081 /* set the change cell environment */
1082 db_setchangecell(ai->parent);
1083 }
1084 db_donextchangequietly = FALSE;
1085
1086 /* mark a change to the database */
1087 db_changetimestamp++;
1088
1089 return(FALSE);
1090 }
1091
1092 /*
1093 * Routine to set the "endshrink" value for arcinst "ai". Returns
1094 * nonzero if the value changed.
1095 */
setshrinkvalue(ARCINST * ai,BOOLEAN extend)1096 INTBIG setshrinkvalue(ARCINST *ai, BOOLEAN extend)
1097 {
1098 REGISTER INTBIG i, result, ret;
1099 INTBIG shorter[2];
1100 REGISTER NODEINST *ni;
1101 REGISTER ARCINST *oar;
1102 REGISTER INTBIG newshrink;
1103 REGISTER PORTARCINST *pi;
1104
1105 /* reset bits and compute shortening amount for each end of arcinst */
1106 for(i=0; i<2; i++)
1107 {
1108 if (ai->end[i].nodeinst == NONODEINST) return(0);
1109 ai->end[i].nodeinst->userbits &= ~NSHORT;
1110 if (ai->end[i].portarcinst->proto == NOPORTPROTO) continue;
1111 shorter[i] = db_checkshortening(ai->end[i].nodeinst, ai->end[i].portarcinst->proto);
1112 }
1113
1114 /* reset the arcinst shortening bits */
1115 ai->userbits &= ~ASHORT;
1116
1117 /* set shortening bits if the shortening factor is nonzero */
1118 result = (shorter[1] << 16) | shorter[0];
1119 if (result != 0)
1120 {
1121 ai->userbits |= ASHORT;
1122 for(i=0; i<2; i++) if (shorter[i] != 0)
1123 {
1124 ni = ai->end[i].nodeinst;
1125 if ((ni->proto->userbits&NODESHRINK) != 0) ni->userbits |= NSHORT;
1126 if (!extend) continue;
1127 if ((ni->proto->userbits&ARCSHRINK) != 0)
1128 {
1129
1130 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1131 {
1132 oar = pi->conarcinst;
1133 if (oar == ai) continue;
1134 oar->userbits |= ASHORT;
1135 if (oar->end[0].nodeinst == ni)
1136 newshrink = (oar->endshrink & 0xFFFF0000) | shorter[i]; else
1137 newshrink = (oar->endshrink & 0xFFFF) | (shorter[i] << 16);
1138 if (newshrink != oar->endshrink)
1139 {
1140 oar->endshrink = newshrink;
1141 updategeom(oar->geom, oar->parent);
1142 }
1143 }
1144 }
1145 }
1146 }
1147
1148 /* return the shrink value for this arcinst */
1149 if (ai->endshrink == result) ret = 0; else ret = 1;
1150 ai->endshrink = result;
1151 return(ret);
1152 }
1153
1154 /*
1155 * routine to determine, for the arcinst on portinst "pp" of nodeinst "ni",
1156 * whether the nodeinst and/or the arcs must be shortened to compensate
1157 * for nonmanhattan geometry and if so, by what amount. The
1158 * routine returns the angle between the nonmanhattan objects.
1159 */
1160 #define MAXANGLES 3
db_checkshortening(NODEINST * ni,PORTPROTO * pp)1161 INTBIG db_checkshortening(NODEINST *ni, PORTPROTO *pp)
1162 {
1163 REGISTER PORTARCINST *pi;
1164 REGISTER NODEPROTO *np;
1165 REGISTER ARCINST *ai;
1166 REGISTER INTBIG ang, off90, total;
1167 INTBIG angles[MAXANGLES];
1168
1169 /* quit now if we don't have to worry about this kind of nodeinst */
1170 np = ni->proto;
1171 if (np == NONODEPROTO) return(0);
1172 if ((np->userbits&(NODESHRINK|ARCSHRINK)) == 0) return(0);
1173
1174 /* gather the angles of the nodes/arcs */
1175 total = off90 = 0;
1176 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1177 {
1178 ai = pi->conarcinst;
1179
1180 /* ignore zero-size arcs */
1181 if (ai->width == 0) continue;
1182
1183 /* ignore this arcinst if it is not on the desired port */
1184 if ((np->userbits&ARCSHRINK) == 0 && pi->proto != pp) continue;
1185
1186 /* compute the angle */
1187 ang = (ai->userbits&AANGLE) >> AANGLESH;
1188 if (ai->end[1].portarcinst->proto == pi->proto && ai->end[1].nodeinst == ni) ang += 180;
1189 ang %= 360;
1190 if ((ang%90) != 0) off90++;
1191 if (total < MAXANGLES) angles[total++] = ang; else
1192 break;
1193 }
1194
1195 /* throw in the nodeinst rotation factor if it is important */
1196 if ((np->userbits&NODESHRINK) != 0)
1197 {
1198 ang = (pp->userbits & PORTANGLE) >> PORTANGLESH;
1199 ang += (ni->rotation+5) / 10;
1200 if (ni->transpose != 0) { ang = 270 - ang; if (ang < 0) ang += 360; }
1201 ang = (ang+180)%360;
1202 if ((ang%90) != 0) off90++;
1203 if (total < MAXANGLES) angles[total++] = ang;
1204 }
1205
1206 /* all fine if all manhattan angles involved */
1207 if (off90 == 0) return(0);
1208
1209 /* give up if too many arcinst angles */
1210 if (total != 2) return(0);
1211
1212 /* compute and return factor */
1213 ang = abs(angles[1]-angles[0]);
1214 if (ang > 180) ang = 360 - ang;
1215 if (ang > 90) ang = 180 - ang;
1216 return(ang);
1217 }
1218
1219 /*
1220 * routine to set the angle offset of arcinst "ai". This value is the number
1221 * of degrees counter-clockwise from the right in the range of 0 to 359.
1222 * It is kept in the "userbits" field.
1223 */
determineangle(ARCINST * ai)1224 void determineangle(ARCINST *ai)
1225 {
1226 INTBIG ang;
1227 REGISTER NODEINST *ni;
1228 REGISTER INTBIG cx0, cy0, cx1, cy1;
1229
1230 /* if length is zero, look at nodes */
1231 if (ai->end[0].xpos == ai->end[1].xpos && ai->end[0].ypos == ai->end[1].ypos)
1232 {
1233 ni = ai->end[0].nodeinst;
1234 cx0 = (ni->lowx + ni->highx) / 2;
1235 cy0 = (ni->lowy + ni->highy) / 2;
1236 ni = ai->end[1].nodeinst;
1237 cx1 = (ni->lowx + ni->highx) / 2;
1238 cy1 = (ni->lowy + ni->highy) / 2;
1239 if (abs(cx0-cx1) > abs(cy0-cy1))
1240 {
1241 /* horizontal nodes */
1242 if (cx0 > cx1) ang = 1800; else
1243 ang = 0;
1244 } else
1245 {
1246 if (cy0 > cy1) ang = 900; else
1247 ang = 2700;
1248 }
1249 } else
1250 {
1251 ang = figureangle(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
1252 }
1253 ai->userbits = (ai->userbits & ~AANGLE) | ((ang+5)/10 << AANGLESH);
1254 }
1255
1256 /*
1257 * routine to return the address of an arcinst that it not in the
1258 * database for miscellaneous use
1259 */
initdummyarc(ARCINST * ai)1260 void initdummyarc(ARCINST *ai)
1261 {
1262 static NODEINST node;
1263
1264 ai->proto = el_technologies->firstarcproto;
1265 ai->length = 0;
1266 ai->parent = NONODEPROTO;
1267 ai->width = 0;
1268 ai->endshrink = 0;
1269 initdummynode(&node);
1270 ai->end[0].nodeinst = ai->end[1].nodeinst = &node;
1271 ai->end[0].portarcinst = ai->end[1].portarcinst = NOPORTARCINST;
1272 ai->userbits = 0;
1273 ai->numvar = 0;
1274 }
1275
1276 /******************** PORT PROTOTYPES *************************/
1277
1278 /*
1279 * Routine to allocate a portproto from memory cluster "cluster".
1280 * The routine returns NOPORTPROTO if allocation fails.
1281 */
allocportproto(CLUSTER * cluster)1282 PORTPROTO *allocportproto(CLUSTER *cluster)
1283 {
1284 REGISTER PORTPROTO *pp;
1285
1286 pp = (PORTPROTO *)emalloc((sizeof (PORTPROTO)), cluster);
1287 if (pp == 0) return(NOPORTPROTO);
1288 pp->userbits = pp->temp1 = pp->temp2 = 0;
1289 pp->network = NONETWORK;
1290 pp->connects = 0;
1291 pp->numvar = 0;
1292 pp->changeaddr = (CHAR *)NOCHANGE;
1293 pp->protoname = (CHAR *)-1;
1294 pp->firstvar = NOVARIABLE;
1295 pp->nextportproto = NOPORTPROTO;
1296 pp->parent = NONODEPROTO;
1297 pp->subnodeinst = NONODEINST;
1298 pp->subportexpinst = NOPORTEXPINST;
1299 pp->subportproto = NOPORTPROTO;
1300 TDCLEAR(pp->textdescript);
1301 defaulttextsize(1, pp->textdescript);
1302 TDSETPOS(pp->textdescript, VTPOSBOXED);
1303 pp->cachedequivport = NOPORTPROTO;
1304 return(pp);
1305 }
1306
1307 /*
1308 * routine to return portproto "pp" to the pool of free ports
1309 */
freeportproto(PORTPROTO * pp)1310 void freeportproto(PORTPROTO *pp)
1311 {
1312 if (pp == NOPORTPROTO) return;
1313 if (pp->numvar != 0) db_freevars(&pp->firstvar, &pp->numvar);
1314 efree((CHAR *)pp);
1315 }
1316
1317 /*
1318 * routine to allocate a portexpinst from memory cluster "cluster".
1319 * The routine returns NOPORTEXPINST if allocation fails.
1320 */
allocportexpinst(CLUSTER * cluster)1321 PORTEXPINST *allocportexpinst(CLUSTER *cluster)
1322 {
1323 REGISTER PORTEXPINST *pe;
1324
1325 pe = (PORTEXPINST *)emalloc((sizeof (PORTEXPINST)), cluster);
1326 if (pe == 0) return(NOPORTEXPINST);
1327 pe->numvar = 0;
1328 pe->firstvar = NOVARIABLE;
1329 pe->nextportexpinst = NOPORTEXPINST;
1330 pe->proto = NOPORTPROTO;
1331 pe->exportproto = NOPORTPROTO;
1332 return(pe);
1333 }
1334
1335 /*
1336 * routine to return portexpinst "pe" to the pool of free ports
1337 */
freeportexpinst(PORTEXPINST * pe)1338 void freeportexpinst(PORTEXPINST *pe)
1339 {
1340 if (pe == NOPORTEXPINST) return;
1341 if (pe->numvar != 0) db_freevars(&pe->firstvar, &pe->numvar);
1342 efree((CHAR *)pe);
1343 }
1344
1345 /*
1346 * routine to add a portproto to a cell. The port proto is in cell "np".
1347 * The location of the portproto is based on a sub-nodeinst and portproto on
1348 * that sub-nodeinst: the subnodeinst is "nodeinst" and the subportproto is
1349 * portproto "portaddress". The name of the portproto is in "name". The
1350 * routine returns the address of the portproto if sucessful, NOPORTPROTO
1351 * if the portproto cannot be created.
1352 */
newportproto(NODEPROTO * np,NODEINST * nodeinst,PORTPROTO * portaddress,CHAR * name)1353 PORTPROTO *newportproto(NODEPROTO *np, NODEINST *nodeinst, PORTPROTO *portaddress,
1354 CHAR *name)
1355 {
1356 REGISTER PORTPROTO *pp;
1357 REGISTER CHAR *ptr;
1358
1359 /* error checks */
1360 if (np == NONODEPROTO)
1361 {
1362 db_donextchangequietly = FALSE;
1363 return((PORTPROTO *)db_error(DBBADCELL|DBNEWPORTPROTO));
1364 }
1365 if (*name == 0)
1366 {
1367 db_donextchangequietly = FALSE;
1368 return((PORTPROTO *)db_error(DBBADNAME|DBNEWPORTPROTO));
1369 }
1370 for(ptr = name; *ptr != 0; ptr++) if (*ptr <= ' ' || *ptr >= 0177)
1371 {
1372 db_donextchangequietly = FALSE;
1373 return((PORTPROTO *)db_error(DBBADNAME|DBNEWPORTPROTO));
1374 }
1375
1376 /* check the validity of the sub-nodeinst */
1377 for(pp = nodeinst->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1378 if (pp == portaddress) break;
1379 if (pp == NOPORTPROTO)
1380 {
1381 db_donextchangequietly = FALSE;
1382 return((PORTPROTO *)db_error(DBBADSUBPORT|DBNEWPORTPROTO));
1383 }
1384
1385 /* look for duplicate names */
1386 pp = getportproto(np, name);
1387 if (pp != NOPORTPROTO)
1388 {
1389 db_donextchangequietly = FALSE;
1390 return((PORTPROTO *)db_error(DBDUPLICATE|DBNEWPORTPROTO));
1391 }
1392
1393 return(db_newportproto(np, nodeinst, portaddress, name));
1394 }
1395
1396 /*
1397 * routine to do actual port prototype creation. returns NOPORTPROTO
1398 * upon error
1399 */
db_newportproto(NODEPROTO * np,NODEINST * nodeinst,PORTPROTO * portaddress,CHAR * name)1400 PORTPROTO *db_newportproto(NODEPROTO *np, NODEINST *nodeinst, PORTPROTO *portaddress,
1401 CHAR *name)
1402 {
1403 REGISTER PORTPROTO *pp;
1404
1405 /* allocate a new portproto */
1406 pp = allocportproto(np->lib->cluster);
1407 if (pp == NOPORTPROTO)
1408 {
1409 db_donextchangequietly = FALSE;
1410 return(NOPORTPROTO);
1411 }
1412
1413 /* load up the port prototype */
1414 pp->connects = portaddress->connects;
1415 pp->parent = np;
1416 pp->subnodeinst = nodeinst;
1417 pp->subportproto = portaddress;
1418 pp->userbits = portaddress->userbits;
1419 TDCLEAR(pp->textdescript);
1420 defaulttextdescript(pp->textdescript, nodeinst->geom);
1421 defaulttextsize(1, pp->textdescript);
1422 if (allocstring(&pp->protoname, name, np->lib->cluster))
1423 {
1424 db_donextchangequietly = FALSE;
1425 return(NOPORTPROTO);
1426 }
1427
1428 /* link it in and announce it */
1429 db_enterportproto(pp);
1430
1431 /* handle change control, constraint, and broadcast */
1432 if (!db_donextchangequietly && !db_dochangesquietly)
1433 {
1434 /* set the change cell environment */
1435 db_setchangecell(np);
1436
1437 /* announce it */
1438 pp->changeaddr = (CHAR *)db_change((INTBIG)pp, PORTPROTONEW, 0, 0, 0, 0, 0, 0);
1439
1440 /* tell constraint system about new port */
1441 (*el_curconstraint->newobject)((INTBIG)pp, VPORTPROTO);
1442 }
1443 db_donextchangequietly = FALSE;
1444
1445 return(pp);
1446 }
1447
db_enterportproto(PORTPROTO * pp)1448 void db_enterportproto(PORTPROTO *pp)
1449 {
1450 REGISTER INTBIG i, j;
1451 REGISTER PORTPROTO *p, *last;
1452 REGISTER NODEPROTO *cell;
1453 REGISTER PORTEXPINST *pe;
1454
1455 cell = pp->parent;
1456 for(last = NOPORTPROTO, p = cell->firstportproto; p != NOPORTPROTO; p = p->nextportproto)
1457 last = p;
1458 if (last == NOPORTPROTO) cell->firstportproto = pp; else
1459 last->nextportproto = pp;
1460 pp->nextportproto = NOPORTPROTO;
1461
1462 /* load up this portexpinst description */
1463 pe = allocportexpinst(cell->lib->cluster);
1464 pe->proto = pp->subportproto;
1465 db_addportexpinst(pp->subnodeinst, pe);
1466 pe->exportproto = pp;
1467 pp->subportexpinst = pe;
1468
1469 /* insert into hash table of portproto names */
1470 cell->numportprotos++;
1471 if (cell->numportprotos < cell->portprotohashtablesize/2)
1472 {
1473 i = db_namehash(pp->protoname) % cell->portprotohashtablesize;
1474 for(j=1; j<=cell->portprotohashtablesize; j += 2)
1475 {
1476 if (cell->portprotohashtable[i] == NOPORTPROTO)
1477 {
1478 cell->portprotohashtable[i] = pp;
1479 break;
1480 }
1481 i += j;
1482 if (i >= cell->portprotohashtablesize) i -= cell->portprotohashtablesize;
1483 }
1484 } else
1485 {
1486 db_buildportprotohashtable(cell);
1487 }
1488
1489 /* special case: if port is on node that changes size with connectivity, update its geometry */
1490 if ((pp->subnodeinst->proto->userbits&WIPEON1OR2) != 0)
1491 updategeom(pp->subnodeinst->geom, cell);
1492
1493 /* clear cache of port associations in this cell */
1494 db_clearportcache(cell);
1495
1496 /* mark a change to the database */
1497 db_changetimestamp++;
1498 }
1499
db_clearportcache(NODEPROTO * cell)1500 void db_clearportcache(NODEPROTO *cell)
1501 {
1502 REGISTER NODEPROTO *np, *onp;
1503
1504 FOR_CELLGROUP(np, cell)
1505 {
1506 for(onp = np; onp != NONODEPROTO; onp = onp->prevversion)
1507 onp->cachedequivcell = NONODEPROTO;
1508 }
1509 }
1510
1511 /*
1512 * routine to add portexpinst "npe" to the list of portexpinsts on nodeinst
1513 * "ni".
1514 */
db_addportexpinst(NODEINST * ni,PORTEXPINST * npe)1515 void db_addportexpinst(NODEINST *ni, PORTEXPINST *npe)
1516 {
1517 REGISTER PORTPROTO *pp, *pr;
1518 REGISTER PORTEXPINST *pe, *lpe;
1519
1520 pr = npe->proto;
1521 pp = ni->proto->firstportproto;
1522 lpe = NOPORTEXPINST;
1523 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1524 {
1525 while (pp != pe->proto && pp != pr) pp = pp->nextportproto;
1526 if (pp == pr)
1527 {
1528 if (lpe == NOPORTEXPINST) ni->firstportexpinst = npe; else
1529 lpe->nextportexpinst = npe;
1530 npe->nextportexpinst = pe;
1531 return;
1532 }
1533 lpe = pe;
1534 }
1535
1536 /* not found inside list: append to end */
1537 if (lpe == NOPORTEXPINST) ni->firstportexpinst = npe; else
1538 lpe->nextportexpinst = npe;
1539 npe->nextportexpinst = NOPORTEXPINST;
1540 }
1541
1542 /*
1543 * routine to add a portproto to a primitive nodeproto. The nodeproto is in
1544 * "np" and the portproto can connect to arcs listed in the array "arcs".
1545 * The name of the portproto is in "name". The routine returns the address of
1546 * the portproto if sucessful, NOPORTPROTO if the portproto cannot be created.
1547 */
db_newprimportproto(NODEPROTO * np,ARCPROTO ** arcs,CHAR * name)1548 PORTPROTO *db_newprimportproto(NODEPROTO *np, ARCPROTO **arcs, CHAR *name)
1549 {
1550 REGISTER PORTPROTO *last;
1551 REGISTER PORTPROTO *pp;
1552 REGISTER CHAR *ptr;
1553
1554 /* error checks */
1555 if (np == NONODEPROTO) return(NOPORTPROTO);
1556
1557 /* name must not have blank space in it */
1558 for(ptr = name; *ptr != 0; ptr++) if (*ptr <= ' ' || *ptr >= 0177)
1559 {
1560 ttyputmsg(_("Port '%s' on primitive '%s' has bad name (spaces?)"), name, np->protoname);
1561 return(NOPORTPROTO);
1562 }
1563
1564 /* must be during initialization: no instances of this nodeproto */
1565 if (np->firstinst != NONODEINST) return(NOPORTPROTO);
1566
1567 /* reject if there is already a portproto with this name */
1568 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1569 if (namesame((last=pp)->protoname, name) == 0) return(NOPORTPROTO);
1570
1571 /* create the portproto and link it into the nodeinst proto */
1572 pp = allocportproto(np->tech->cluster);
1573 if (pp == NOPORTPROTO) return(NOPORTPROTO);
1574 if (np->firstportproto == NOPORTPROTO) np->firstportproto = pp; else
1575 last->nextportproto = pp;
1576 pp->nextportproto = NOPORTPROTO;
1577
1578 /* initialize the values in the portproto */
1579 pp->userbits = pp->temp1 = pp->temp2 = 0;
1580 pp->network = NONETWORK;
1581 pp->parent = np; pp->connects = arcs;
1582 pp->subnodeinst = NONODEINST; pp->subportproto = NOPORTPROTO;
1583 if (allocstring(&pp->protoname, name, np->tech->cluster)) return(NOPORTPROTO);
1584
1585 return(pp);
1586 }
1587
1588 /*
1589 * routine to delete portproto "pp" from nodeproto "np". Returns FALSE if
1590 * successful, TRUE if the portproto could not be deleted (does not
1591 * exist). If the portproto has arcs connected to it in instances of the
1592 * cell, the arcs are deleted. If the portproto is an export of
1593 * the cell, those exports are deleted (recursively).
1594 */
killportproto(NODEPROTO * np,PORTPROTO * pp)1595 BOOLEAN killportproto(NODEPROTO *np, PORTPROTO *pp)
1596 {
1597 REGISTER PORTPROTO *spt;
1598
1599 /* look at all portprotos to ensure it exists */
1600 if (np == NONODEPROTO)
1601 {
1602 db_donextchangequietly = FALSE;
1603 return(db_error(DBBADCELL|DBKILLPORTPROTO) != 0);
1604 }
1605 for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
1606 if (spt == pp) break;
1607 if (spt == NOPORTPROTO)
1608 {
1609 db_donextchangequietly = FALSE;
1610 return(db_error(DBBADPROTO|DBKILLPORTPROTO) != 0);
1611 }
1612
1613 /* cannot delete port on primitive nodeproto */
1614 if (pp->subnodeinst == NONODEINST)
1615 {
1616 db_donextchangequietly = FALSE;
1617 return(db_error(DBPRIMITIVE|DBKILLPORTPROTO) != 0);
1618 }
1619
1620 db_killportproto(pp);
1621 return(FALSE);
1622 }
1623
db_killportproto(PORTPROTO * pp)1624 void db_killportproto(PORTPROTO *pp)
1625 {
1626 REGISTER PORTARCINST *pi, *nextpi;
1627 REGISTER PORTEXPINST *pe, *nextpe;
1628 REGISTER NODEINST *ni;
1629 REGISTER NODEPROTO *np;
1630
1631 /* look at all instances of nodeproto for port use */
1632 np = pp->parent;
1633 for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
1634 {
1635 /* see if this port has arcs on it on the higher-level instances */
1636 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = nextpi)
1637 {
1638 nextpi = pi->nextportarcinst;
1639 if (pi->proto == pp)
1640 {
1641 if ((pi->conarcinst->userbits&DEADA) != 0) continue;
1642 startobjectchange((INTBIG)pi->conarcinst, VARCINST);
1643 db_killarcinst(pi->conarcinst);
1644 }
1645 }
1646
1647 /* see if this port is an export */
1648 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = nextpe)
1649 {
1650 nextpe = pe->nextportexpinst;
1651 if (pe->proto != pp) continue;
1652 db_killportproto(pe->exportproto);
1653 db_forcehierarchicalanalysis(np);
1654 }
1655 }
1656
1657 /* remove the port prototype */
1658 db_retractportproto(pp);
1659
1660 /* handle change control, constraint, and broadcast */
1661 if (!db_donextchangequietly && !db_dochangesquietly)
1662 {
1663 /* set the change cell environment */
1664 db_setchangecell(np);
1665
1666 /* report the change */
1667 pp->changeaddr = (CHAR *)db_change((INTBIG)pp, PORTPROTOKILL, 0, 0, 0, 0, 0, 0);
1668
1669 /* tell constraint system about killed port */
1670 (*el_curconstraint->killobject)((INTBIG)pp, VPORTPROTO);
1671 } else
1672 {
1673 /* delete the export now: no change control */
1674 efree(pp->protoname);
1675 freeportproto(pp);
1676 }
1677 db_donextchangequietly = FALSE;
1678 }
1679
db_retractportproto(PORTPROTO * pp)1680 void db_retractportproto(PORTPROTO *pp)
1681 {
1682 REGISTER PORTPROTO *ppo, *lpo;
1683 REGISTER NODEPROTO *np;
1684
1685 /* remove portexpinst linkage from database */
1686 db_removeportexpinst(pp);
1687
1688 /* remove the portproto from the list */
1689 lpo = NOPORTPROTO;
1690 np = pp->parent;
1691 for(ppo = np->firstportproto; ppo != NOPORTPROTO; ppo = ppo->nextportproto)
1692 {
1693 if (ppo == pp)
1694 {
1695 if (lpo == NOPORTPROTO) np->firstportproto = pp->nextportproto; else
1696 lpo->nextportproto = pp->nextportproto;
1697 break;
1698 }
1699 lpo = ppo;
1700 }
1701
1702 /* rebuild hash table of cell names */
1703 np->numportprotos--;
1704 db_buildportprotohashtable(np);
1705
1706 /* special case: if port is on node that changes size with connectivity, update its geometry */
1707 if ((pp->subnodeinst->proto->userbits&WIPEON1OR2) != 0)
1708 updategeom(pp->subnodeinst->geom, np);
1709
1710 /* clear cache of port associations in this cell */
1711 db_clearportcache(pp->parent);
1712
1713 /* mark a change to the database */
1714 db_changetimestamp++;
1715 }
1716
1717 /*
1718 * routine to remove the portexpinst corresponding to portproto "pp" and its
1719 * linkage in the nodeinst
1720 */
db_removeportexpinst(PORTPROTO * pp)1721 void db_removeportexpinst(PORTPROTO *pp)
1722 {
1723 REGISTER PORTEXPINST *pe, *lpe;
1724
1725 lpe = NOPORTEXPINST;
1726 for(pe = pp->subnodeinst->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1727 {
1728 if (pe->exportproto == pp)
1729 {
1730 if (lpe == NOPORTEXPINST)
1731 pp->subnodeinst->firstportexpinst = pe->nextportexpinst; else
1732 lpe->nextportexpinst = pe->nextportexpinst;
1733 freeportexpinst(pe);
1734 break;
1735 }
1736 lpe = pe;
1737 }
1738 }
1739
1740 /*
1741 * Routine to rebuild the hash table of cell names in library "lib".
1742 */
db_buildportprotohashtable(NODEPROTO * cell)1743 void db_buildportprotohashtable(NODEPROTO *cell)
1744 {
1745 REGISTER INTBIG i, j, numportprotos, hashtablesize;
1746 REGISTER PORTPROTO *pp;
1747
1748 /* free the former table */
1749 if (cell->portprotohashtablesize > 0)
1750 {
1751 cell->portprotohashtablesize = 0;
1752 efree((CHAR *)cell->portprotohashtable);
1753 }
1754
1755 /* determine the size of the hash table */
1756 numportprotos = 0;
1757 for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) numportprotos++;
1758 if (numportprotos != cell->numportprotos)
1759 {
1760 ttyputmsg(_("Warning: number of ports in cell %s corrected"), describenodeproto(cell));
1761 cell->numportprotos = numportprotos;
1762 }
1763 hashtablesize = pickprime(numportprotos * 4);
1764
1765 /* create the hash table and clear it */
1766 cell->portprotohashtable = (PORTPROTO **)emalloc(hashtablesize * (sizeof (PORTPROTO *)),
1767 cell->lib->cluster);
1768 if (cell->portprotohashtable == 0) return;
1769 for(i=0; i<hashtablesize; i++)
1770 cell->portprotohashtable[i] = NOPORTPROTO;
1771 cell->portprotohashtablesize = hashtablesize;
1772
1773 /* insert all cells into the table */
1774 for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1775 {
1776 i = db_namehash(pp->protoname) % hashtablesize;
1777 for(j=1; j<=hashtablesize; j += 2)
1778 {
1779 if (cell->portprotohashtable[i] == NOPORTPROTO)
1780 {
1781 cell->portprotohashtable[i] = pp;
1782 break;
1783 }
1784 i += j;
1785 if (i >= hashtablesize) i -= hashtablesize;
1786 }
1787 }
1788 }
1789