diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2019-03-29 00:00:20 +0100 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2019-03-29 00:00:20 +0100 |
commit | ef796dfca2a5a4604e071e56ed309e389ec3494e (patch) | |
tree | 78bf5e0f73a9f75096622b5b26bf8ea45ed4e945 | |
parent | 1fb46e1420afd39c1e78cf77bf5f7312906ad634 (diff) | |
parent | 6089b1f3dab7b7d60a8ea4bcec47bdec3560c5b4 (diff) |
Update upstream source from tag 'upstream/1.4.49'
Update to upstream version '1.4.49'
with Debian dir 5c23ac3cd90a32b0d213898d488bfa288b54f063
-rw-r--r-- | Makefile | 148 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | antenna.c | 1308 | ||||
-rw-r--r-- | config.log | 499 | ||||
-rwxr-xr-x | config.status | 942 | ||||
-rw-r--r-- | def.c | 545 | ||||
-rw-r--r-- | def.h | 5 | ||||
-rw-r--r-- | delays.c | 280 | ||||
-rw-r--r-- | graphics.c | 52 | ||||
-rw-r--r-- | lef.c | 1353 | ||||
-rw-r--r-- | lef.h | 65 | ||||
-rw-r--r-- | mask.c | 105 | ||||
-rw-r--r-- | mask.h | 1 | ||||
-rw-r--r-- | maze.c | 504 | ||||
-rw-r--r-- | node.c | 1165 | ||||
-rw-r--r-- | node.h | 4 | ||||
-rw-r--r-- | output.c | 1443 | ||||
-rw-r--r-- | output.h | 2 | ||||
-rw-r--r-- | qconfig.c | 159 | ||||
-rw-r--r-- | qconfig.h | 25 | ||||
-rw-r--r-- | qrouter.c | 442 | ||||
-rw-r--r-- | qrouter.h | 45 | ||||
-rw-r--r--[-rwxr-xr-x] | qrouter.sh.in | 0 | ||||
-rw-r--r-- | qrouter.tcl.in | 11 | ||||
-rw-r--r-- | tclqrouter.c | 418 |
27 files changed, 7680 insertions, 1856 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..01a61f0 --- /dev/null +++ b/Makefile @@ -0,0 +1,148 @@ +# +# qrouter Makefile +# + +# Main compiler arguments +CFLAGS += -g +CPPFLAGS = -m64 -fPIC +DEFS = -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DHAVE_LIBXT=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_MMAN_H=1 -DTCL_QROUTER=1 -DLINUX=1 -DSYSV=1 -DVERSION=\"1.4\" -DREVISION=\"7\" +STUB_DEFS = -DUSE_TCL_STUBS -DUSE_TK_STUBS +LIBS = -lXt +LDFLAGS += +LDDL_FLAGS = -shared -Wl,-soname,$@ -Wl,--version-script=symbol.map +LD_RUN_PATH = +SHLIB_CFLAGS = -fPIC +LIB_SPECS_NOSTUB = -L/usr/lib64 -ltk8.6 -L/usr/lib64 -ltcl8.6 +LIB_SPECS = -L/usr/lib64 -ltkstub8.6 -L/usr/lib64 -ltclstub8.6 +INC_SPECS = +TCL_LIB_DIR = /usr/lib64 +TK_LIB_DIR = /usr/lib64 +EXTRA_LIB_SPECS = -ldl +INSTALL = /usr/bin/install -c +SHDLIB_EXT = .so +EXEEXT = +X_LIBS = +X_EXTRA_LIBS = +X_PRE_LIBS = -lSM -lICE +QROUTER_LIB_DIR = share/qrouter +WISH_EXE = /usr/bin/wish +VERSION = 1.4 +REVISION = 7 +prefix = /usr/local + +INSTALL_TARGET := install-tcl +ALL_TARGET := tcl + +SOURCES = qrouter.c point.c maze.c mask.c node.c output.c qconfig.c lef.c def.c +OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) + +SOURCES2 = graphics.c tclqrouter.c tkSimple.c delays.c antenna.c +OBJECTS2 := $(patsubst %.c,%.o,$(SOURCES2)) + +SOURCES3 = qrouterexec.c +OBJECTS3 := $(patsubst %.c,%.o,$(SOURCES3)) + +SOURCES4 = qrouternullg.c +OBJECTS4 := $(patsubst %.c,%.o,$(SOURCES4)) + +SOURCES5 = main.c +OBJECTS5 := $(patsubst %.c,%.o,$(SOURCES5)) + +BININSTALL = ${prefix}/bin +LIBINSTALL = ${prefix}/${QROUTER_LIB_DIR} +EXTRA_DEFS = -DQROUTER_PATH=\"${LIBINSTALL}\" + +all: $(ALL_TARGET) + +install: $(INSTALL_TARGET) + +nointerp: qrouter$(EXEEXT) + +tcl: qrouter.sh qrouter.tcl qrouter$(SHDLIB_EXT) qrouterexec$(EXEEXT) \ + qrouternullg$(EXEEXT) + +qrouter.tcl: qrouter.tcl.in + sed -e '/LIBDIR/s#LIBDIR#${LIBINSTALL}#' \ + -e '/VERSION/s#VERSION#${VERSION}#' \ + -e '/REVISION/s#REVISION#${REVISION}#' \ + qrouter.tcl.in > $@ + +qrouter.sh: qrouter.sh.in + sed -e '/WISH_EXE/s#WISH_EXE#${WISH_EXE}#' \ + -e '/LIBDIR/s#LIBDIR#${LIBINSTALL}#' \ + qrouter.sh.in > $@ + chmod 0755 $@ + +qrouter$(EXEEXT): $(OBJECTS) $(OBJECTS5) + $(CC) $(LDFLAGS) $(OBJECTS) $(OBJECTS5) -o $@ $(LIBS) -lm + +qrouter$(SHDLIB_EXT): $(OBJECTS) $(OBJECTS2) + $(RM) qrouter$(SHDLIB_EXT) + $(CC) ${CFLAGS} ${STUB_DEFS} ${SHLIB_CFLAGS} -o $@ \ + ${LDDL_FLAGS} $(OBJECTS) $(OBJECTS2) \ + ${LDFLAGS} -lc ${LIBS} ${X_PRE_LIBS} -lX11 ${X_LIBS} \ + ${X_EXTRA_LIBS} ${LIB_SPECS} ${EXTRA_LIB_SPECS} -lm + +qrouterexec$(EXEEXT): $(OBJECTS3) + $(RM) qrouterexec$(EXEEXT) + $(CC) ${CFLAGS} ${CPPFLAGS} ${DEFS} ${EXTRA_DEFS} \ + ${SOURCES3} ${INC_SPECS} -o $@ ${LIB_SPECS_NOSTUB} \ + ${LD_RUN_PATH} ${LDFLAGS} ${X_PRE_LIBS} -lX11 ${X_LIBS} \ + ${X_EXTRA_LIBS} ${LIBS} ${EXTRA_LIB_SPECS} -lm + +qrouternullg$(EXEEXT): $(OBJECTS4) + $(RM) qrouternullg$(EXEEXT) + $(CC) ${CFLAGS} ${CPPFLAGS} ${DEFS} ${EXTRA_DEFS} \ + ${SOURCES4} ${INC_SPECS} -o $@ ${LIB_SPECS_NOSTUB} \ + ${LD_RUN_PATH} ${LDFLAGS} ${LIBS} ${EXTRA_LIB_SPECS} -lm + +install-nointerp: + @echo "Installing qrouter" + $(INSTALL) -d $(DESTDIR)${BININSTALL} + $(INSTALL) qrouter $(DESTDIR)${BININSTALL} + +install-tcl: qrouter.sh qrouter.tcl qrouter$(SHDLIB_EXT) \ + qrouterexec$(EXEEXT) qrouternullg$(EXEEXT) + @echo "Installing qrouter" + $(INSTALL) -d $(DESTDIR)${BININSTALL} + $(INSTALL) -d $(DESTDIR)${LIBINSTALL} + $(INSTALL) qrouter.sh $(DESTDIR)${BININSTALL}/qrouter + $(INSTALL) qrouter$(SHDLIB_EXT) $(DESTDIR)${LIBINSTALL} + $(INSTALL) qrouterexec$(EXEEXT) $(DESTDIR)${LIBINSTALL} + $(INSTALL) qrouternullg$(EXEEXT) $(DESTDIR)${LIBINSTALL} + $(INSTALL) console.tcl $(DESTDIR)${LIBINSTALL} + $(INSTALL) tkcon.tcl $(DESTDIR)${LIBINSTALL} + $(INSTALL) qrouter.tcl $(DESTDIR)${LIBINSTALL} + +uninstall: + $(RM) $(DESTDIR)${BININSTALL}/qrouter + +clean: + $(RM) $(OBJECTS) + $(RM) $(OBJECTS2) + $(RM) $(OBJECTS3) + $(RM) $(OBJECTS4) + $(RM) $(OBJECTS5) + $(RM) qrouterexec$(EXEEXT) + $(RM) qrouternullg$(EXEEXT) + $(RM) qrouter$(EXEEXT) + $(RM) qrouter$(SHDLIB_EXT) + $(RM) qrouter.tcl + $(RM) qrouter.sh + +veryclean: + $(RM) $(OBJECTS) + $(RM) $(OBJECTS2) + $(RM) $(OBJECTS3) + $(RM) $(OBJECTS4) + $(RM) $(OBJECTS5) + $(RM) qrouterexec$(EXEEXT) + $(RM) qrouternullg$(EXEEXT) + $(RM) qrouter$(EXEEXT) + $(RM) qrouter$(SHDLIB_EXT) + $(RM) qrouter.tcl + $(RM) qrouter.sh + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) $(SHLIB_CFLAGS) $(DEFS) $(STUB_DEFS) \ + $(EXTRA_DEFS) $(INC_SPECS) -c $< -o $@ diff --git a/Makefile.in b/Makefile.in index 95ed4d4..a86e268 100644 --- a/Makefile.in +++ b/Makefile.in @@ -36,7 +36,7 @@ ALL_TARGET := @ALL_TARGET@ SOURCES = qrouter.c point.c maze.c mask.c node.c output.c qconfig.c lef.c def.c OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) -SOURCES2 = graphics.c tclqrouter.c tkSimple.c delays.c +SOURCES2 = graphics.c tclqrouter.c tkSimple.c delays.c antenna.c OBJECTS2 := $(patsubst %.c,%.o,$(SOURCES2)) SOURCES3 = qrouterexec.c @@ -1,5 +1,5 @@ ------------------------------------------------- -Qrouter version 1.3 +Qrouter version 1.4 Detail netlist router for ASICs (c) 2017 by Tim Edwards Released under Gnu Public License @@ -9,6 +9,13 @@ Released under Gnu Public License Release notes: ---------------------------------------------- +Version 1.4 +------------ +Branch created April 25, 2017 mainly for +the purpose of making the existing version 1.3 +the stable branch, particularly as version 1.3 +is required for qflow. + Version 1.3 ------------ Branch created September 16, 2014 and slowly @@ -111,7 +118,7 @@ the standard compile and install sequence: Options to configure: - --with-prefix=<prefix> + --prefix=<prefix> overrides the standard install location of /usr/local @@ -1 +1 @@ -1.3.108 +1.4.49 diff --git a/antenna.c b/antenna.c new file mode 100644 index 0000000..a3d0ad5 --- /dev/null +++ b/antenna.c @@ -0,0 +1,1308 @@ +/*--------------------------------------------------------------*/ +/* antenna.c -- Compute the metal area to gate area for all */ +/* routes and determine where antenna violations occur. Then, */ +/* resolve the violations by routing from each violation to an */ +/* antenna tap. */ +/* */ +/* To be done: If there are no antenna cells placed, or if */ +/* the antenna route fails, or if the antenna violation is */ +/* close to the limit, see if the route can be adjusted by */ +/* moving the route to a higher layer near the gate. */ +/*--------------------------------------------------------------*/ +/* Written by Tim Edwards, May 2018 */ +/*--------------------------------------------------------------*/ + +#include <ctype.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <regex.h> + +/* This entire file is dependent on the Tcl/Tk version */ +#ifdef TCL_QROUTER +#include <tk.h> + +#include "qrouter.h" +#include "qconfig.h" +#include "node.h" +#include "lef.h" +#include "def.h" +#include "point.h" + +/* Node Hash Table routines taken from delay.c */ +extern GATE FindGateNode(Tcl_HashTable *, NODE, int *); +extern void FreeNodeTable(Tcl_HashTable *); + +extern int TotalRoutes; + +/* Structure to hold information about an antenna error. */ + +typedef struct antennainfo_ *ANTENNAINFO; + +struct antennainfo_ { + ANTENNAINFO next; /* Next antenna violation in the list. */ + NET net; /* The net violating an antenna rule */ + NODE node; /* A gate-end node that is in violation */ + ROUTE route; /* A route that is part of the antenna */ + int layer; /* Uppermost metal layer of the antenna */ +}; + +/* Keep the list as a global variable so it can be accessed */ +/* from doroute() (in qrouter.c) */ + +ANTENNAINFO AntennaList; + +/*--------------------------------------------------------------*/ +/* Regular expression matching of the given string in */ +/* "antennacell" to the string "strtest". If the regular */ +/* expression matches and the result is in the first character */ +/* position of the string, then return TRUE (match), otherwise */ +/* return FALSE (no match). */ +/*--------------------------------------------------------------*/ + +u_char +string_match(char *antennacell, char *strtest) +{ + regex_t regex; + regmatch_t pmatch; + int reti; + + /* Compile regular expression */ + reti = regcomp(®ex, antennacell, 0); + if (reti) { + /* Assume this is not a regular expression and just run */ + /* a straight string match. */ + if (!strcasecmp(antennacell, strtest)) + return TRUE; + else + return FALSE; + } + + /* Execute regular expression */ + reti = regexec(®ex, strtest, 1, &pmatch, 0); + regfree(®ex); + + if (!reti) { + if (pmatch.rm_so == 0) /* Must match beginning of string */ + return TRUE; + else + return FALSE; + } + else + return FALSE; +} + +/*--------------------------------------------------------------*/ +/* Find free antenna cells, and collect all the antenna taps */ +/* into a single net, much like VDD_NET or GND_NET. */ +/* */ +/* Return the number of free antenna taps available in the */ +/* layout. */ +/* */ +/* If the name of the antennacell ends in '*', then assume a */ +/* wildcard character and match to any string beginning with */ +/* the substring of antennacell. */ +/*--------------------------------------------------------------*/ + +void +find_free_antenna_taps(char *antennacell) +{ + int numtaps; + GATE ginst; + GATE gateginfo; + NODE noderec; + int netnum, i; + + if (antennacell == NULL) { + Fprintf(stderr, "No antenna cell defined!\n"); + return; + } + numtaps = 0; + for (ginst = Nlgates; ginst; ginst = ginst->next) { + gateginfo = ginst->gatetype; + + if (string_match(antennacell, gateginfo->gatename)) { + /* Find an unassigned node. If there is not one, */ + /* this is probably a routed (not free) cell. */ + for (i = 0; i < ginst->nodes; i++) { + netnum = ginst->netnum[i]; + noderec = ginst->noderec[i]; + if ((netnum == 0) && (noderec == NULL)) { + ginst->netnum[i] = ANTENNA_NET; + ginst->noderec[i] = (NODE)calloc(1, sizeof(struct node_)); + ginst->noderec[i]->netnum = ANTENNA_NET; + } + } + } + } +} + +/*--------------------------------------------------------------*/ +/* Similar to the routine above, but just count the free taps. */ +/*--------------------------------------------------------------*/ + +int +count_free_antenna_taps(char *antennacell) +{ + int numtaps; + GATE ginst; + GATE gateginfo; + int netnum, i; + + numtaps = 0; + for (ginst = Nlgates; ginst; ginst = ginst->next) { + gateginfo = ginst->gatetype; + + if (string_match(antennacell, gateginfo->gatename)) { + /* Find an unassigned node. If there is not one, */ + /* this is probably a routed (not free) cell. */ + for (i = 0; i < ginst->nodes; i++) { + netnum = ginst->netnum[i]; + if (netnum == ANTENNA_NET) + numtaps++; + } + } + } + return numtaps; +} + +/*--------------------------------------------------------------*/ +/* After routing, the free antenna taps are all marked with the */ +/* net number of the net just routed. To make them free again, */ +/* change all but the one that was routed back to ANTENNA_NET. */ +/* Identify the unused taps by finding the OBSVAL record with */ +/* net set to netnum but not connected to the same node. */ +/*--------------------------------------------------------------*/ + +void revert_antenna_taps(int netnum, NODE node) +{ + int x, y, lay; + PROUTE *Pr; + NODEINFO lnode = NULL; + + /* Clear all targets except for the one just routed */ + + for (lay = 0; lay < Num_layers; lay++) + for (x = 0; x < NumChannelsX; x++) + for (y = 0; y < NumChannelsY; y++) + if ((OBSVAL(x, y, lay) & NETNUM_MASK) == netnum) { + Pr = &OBS2VAL(x, y, lay); + if (Pr->flags & PR_TARGET) { + lnode = NODEIPTR(x, y, lay); + if ((lnode == NULL) || (lnode->nodesav != node)) { + OBSVAL(x, y, lay) &= ~(NETNUM_MASK | ROUTED_NET); + OBSVAL(x, y, lay) |= ANTENNA_NET; + } + } + } +} + +/*--------------------------------------------------------------*/ +/* States to track nodes as they are processed: */ +/* */ +/* NOT_VISITED : Node has not yet been processed. */ +/* VISITED : Node was counted on this pass. */ +/* PROCESSED : Node was counted on a previous pass. */ +/* ANCHOR : Node is a source/drain connection. */ +/*--------------------------------------------------------------*/ + +enum visit_states {NOT_VISITED = 0, VISITED, PROCESSED, ANCHOR}; + +/* Forward declarations */ +float get_route_area_reverse(NET, ROUTE, int, u_char *, u_char, + Tcl_HashTable *, struct routeinfo_ *); +float get_route_area_forward(NET, ROUTE, int, u_char *, u_char, + Tcl_HashTable *, struct routeinfo_ *); +float get_route_area_reverse_fromseg(NET, ROUTE, SEG, int, u_char *, u_char, + Tcl_HashTable *, struct routeinfo_ *); + +/*--------------------------------------------------------------*/ +/* Determine the amount of metal in the route, starting at the */ +/* route start point, and not moving past any point that is */ +/* above "layer". Check all other unvisited routes in net to */ +/* see if any connect to "rt". If so, check if they connect */ +/* to a point that is part of the subnet below or at "layer". */ +/* If they do, recursively run get_route_area_forward on that */ +/* route. When done, return the total area of the subnet. */ +/*--------------------------------------------------------------*/ + +float +get_route_area_forward_fromseg(NET net, ROUTE rt, SEG nseg, int layer, + u_char *visited, u_char method, Tcl_HashTable *NodeTable, + struct routeinfo_ *iroute) +{ + float area, length, width, thick; + int x, y, l, compat; + SEG seg, iseg, chkseg; + ROUTE rt2; + u_char found; + + if (rt->flags & RT_VISITED) return 0.0; + rt->flags |= RT_VISITED; + area = 0.0; + + /* If nseg is NULL then check from the beginning. */ + if (nseg == NULL) nseg = rt->segments; + + /* Check if the route beginning is a node */ + if (nseg == rt->segments) { + if (rt->flags & RT_START_NODE) { + NODE node; + GATE g; + int i; + + node = rt->start.node; + + if (visited) { + + /* If more than one route is connected to the node, */ + /* then this node may have been visited already. */ + + if (visited[node->nodenum] == NOT_VISITED) { + g = FindGateNode(NodeTable, node, &i); + if (g->area[i] == 0.0) { + /* There's a diffusion diode here! */ + visited[node->nodenum] = ANCHOR; + return 0.0; + } else { + /* Add this node to the list of nodes with gates */ + /* attached to this antenna area. */ + visited[node->nodenum] = VISITED; + } + } + } + else if ((method == ANTENNA_ROUTE) && (iroute != NULL)) { + set_node_to_net(node, PR_SOURCE, iroute->glist[0], iroute->bbox, 0); + } + + /* Walk all other routes that start or end on this node */ + + for (rt2 = net->routes; rt2; rt2 = rt2->next) { + if (rt2->flags & RT_VISITED) continue; + + if ((rt2->flags & RT_START_NODE) && (rt2->start.node == node)) { + /* The start point of rt2 connects to the same node */ + area += get_route_area_forward(net, rt2, layer, visited, + method, NodeTable, NULL); + } + else if ((rt2->flags & RT_END_NODE) && (rt2->end.node == node)) { + /* The end point of rt2 connects to the same node */ + for (iseg = rt2->segments; iseg && iseg->next; iseg = iseg->next); + area += get_route_area_reverse(net, rt2, layer, visited, + method, NodeTable, NULL); + } + } + } + } + + for (seg = rt->segments; seg && (seg != nseg); seg = seg->next); + if (seg == NULL) return 0.0; + + for (; seg; seg = seg->next) { + + /* Once the layer goes above the current check layer, the search stops. */ + if (method != ANTENNA_DISABLE) + if (seg->layer > layer) break; + + /* Vias don't contribute to area, at least for now. */ + if (seg->segtype & ST_VIA) continue; + + /* For non-cumulative methods, only count area for */ + /* those segments which are on the given check layer. */ + + if ((method == CALC_AREA) || (method == CALC_SIDEAREA)) + if (seg->layer != layer) + continue; + + /* method ANTENNA_ROUTE indicates that this routine was */ + /* called as part of antenna routing. So set up this */ + /* part of the route in a manner similar to the */ + /* set_route_to_net() routine. */ + + if ((method == ANTENNA_ROUTE) && (iroute != NULL)) { + PROUTE *Pr; + POINT gpoint; + + l = seg->layer; + x = seg->x1; + y = seg->y1; + while (1) { + Pr = &OBS2VAL(x, y, l); + Pr->flags = PR_SOURCE; + Pr->prdata.cost = 0; + + if (~(Pr->flags & PR_ON_STACK)) { + Pr->flags |= PR_ON_STACK; + gpoint = allocPOINT(); + gpoint->x1 = x; + gpoint->y1 = y; + gpoint->layer = l; + gpoint->next = iroute->glist[0]; + iroute->glist[0] = gpoint; + } + + if (x < iroute->bbox.x1) iroute->bbox.x1 = x; + if (x > iroute->bbox.x2) iroute->bbox.x2 = x; + if (y < iroute->bbox.y1) iroute->bbox.y1 = y; + if (y > iroute->bbox.y2) iroute->bbox.y2 = y; + + // Move to next grid position in the segment + if (x == seg->x2 && y == seg->y2) break; + if (seg->x2 > seg->x1) x++; + else if (seg->x2 < seg->x1) x--; + if (seg->y2 > seg->y1) y++; + else if (seg->y2 < seg->y1) y--; + } + } + else if (method == ANTENNA_DISABLE) { + PROUTE *Pr; + + l = seg->layer; + x = seg->x1; + y = seg->y1; + while (1) { + Pr = &OBS2VAL(x, y, l); + Pr->prdata.net = MAXNETNUM; + Pr->flags &= ~(PR_SOURCE | PR_TARGET | PR_COST); + + // Move to next grid position in the segment + if (x == seg->x2 && y == seg->y2) break; + if (seg->x2 > seg->x1) x++; + else if (seg->x2 < seg->x1) x--; + if (seg->y2 > seg->y1) y++; + else if (seg->y2 < seg->y1) y--; + } + } + if ((method != ANTENNA_ROUTE) && (method != ANTENNA_DISABLE)) { + + /* Note that one of x or y is zero, depending on segment orientation */ + x = (seg->x2 - seg->x1); + y = (seg->y2 - seg->y1); + if (x < 0) x = -x; + if (y < 0) y = -y; + + /* Note that "l" is a unitless grid dimension */ + if (x == 0) + length = (float)y * (float)PitchY; + else + length = (float)x * (float)PitchX; + + /* area is either the total top surface of the metal, */ + /* or the total side surface of the metal (in um^2) */ + + width = LefGetRouteWidth(seg->layer); + if ((method == CALC_AREA) || (method == CALC_AGG_AREA)) + area += (float)(length * width); + else if ((method == CALC_SIDEAREA) || (method == CALC_AGG_SIDEAREA)) { + thick = LefGetRouteThickness(seg->layer); + area += thick * 2.0 * (length + width); + } + } + } + + /* Check other routes for intersection with this route */ + + for (rt2 = net->routes; rt2; rt2 = rt2->next) { + if (rt2->flags & RT_VISITED) continue; + + if (!(rt2->flags & RT_START_NODE) && (rt2->start.route == rt)) { + /* The start point of rt2 connects somewhere on rt */ + iseg = rt2->segments; + x = iseg->x1; + y = iseg->y1; + l = iseg->layer; + if (l > layer) continue; + } + else if (!(rt2->flags & RT_END_NODE) && (rt2->end.route == rt)) { + /* The end point of rt2 connects somewhere on rt */ + for (iseg = rt2->segments; iseg && iseg->next; iseg = iseg->next); + x = iseg->x2; + y = iseg->y2; + l = iseg->layer; + if (l > layer) continue; + } + else + continue; + + /* Must determine if rt2 intersects rt within the antenna area */ + + found = (u_char)0; + for (chkseg = rt->segments; chkseg && chkseg != seg; chkseg = chkseg->next) { + if (chkseg->segtype & ST_WIRE) { + if (iseg->segtype & ST_WIRE) { + compat = (l == chkseg->layer); + } + else { + compat = (l == chkseg->layer) || (l + 1 == chkseg->layer); + } + } + else { + if (iseg->segtype & ST_WIRE) { + compat = (l == chkseg->layer) || (l == chkseg->layer + 1); + } + else { + compat = (l == chkseg->layer) || (l == chkseg->layer + 1) || + (l + 1 == chkseg->layer); + } + } + if (!compat) continue; + + if (chkseg->segtype & ST_VIA) { + if ((chkseg->x1 == x) && (chkseg->y1 == y)) { + found = (u_char)1; + break; + } + } + else if (chkseg->x1 < chkseg->x2) { + if (chkseg->y1 == y) { + if ((chkseg->x1 <= x) && (chkseg->x2 >= x)) { + found = (u_char)1; + break; + } + } + } + else if (chkseg->x1 > chkseg->x2) { + if (chkseg->y1 == y) { + if ((chkseg->x1 >= x) && (chkseg->x2 <= x)) { + found = (u_char)1; + break; + } + } + } + else if (chkseg->y1 < chkseg->y2) { + if (chkseg->x1 == x) { + if ((chkseg->y1 <= y) && (chkseg->y2 >= y)) { + found = (u_char)1; + break; + } + } + } + else if (chkseg->y1 > chkseg->y2) { + if (chkseg->x1 == x) { + if ((chkseg->y1 >= y) && (chkseg->y2 <= y)) { + found = (u_char)1; + break; + } + } + } + } + if (found == (u_char)1) { + if (rt2->start.route == rt) + area += get_route_area_forward(net, rt2, layer, visited, + method, NodeTable, iroute); + else + area += get_route_area_reverse(net, rt2, layer, visited, + method, NodeTable, iroute); + } + } + + /* The end of this route may be a node (so record it in visited) or */ + /* a route (so walk it). */ + + if (seg == NULL) { /* If seg != NULL then we didn't reach the route end */ + if (rt->flags & RT_END_NODE) { + NODE node; + GATE g; + int i; + + node = rt->end.node; + + /* Walk all other routes that start or end on this node */ + + for (rt2 = net->routes; rt2; rt2 = rt2->next) { + if (rt2->flags & RT_VISITED) continue; + + if ((rt2->flags & RT_START_NODE) && (rt2->start.node == node)) { + /* The start point of rt2 connects to the same node */ + area += get_route_area_forward(net, rt2, layer, visited, + method, NodeTable, NULL); + } + else if ((rt2->flags & RT_END_NODE) && (rt2->end.node == node)) { + /* The end point of rt2 connects to the same node */ + for (iseg = rt2->segments; iseg && iseg->next; iseg = iseg->next); + area += get_route_area_reverse(net, rt2, layer, visited, + method, NodeTable, NULL); + } + } + + g = FindGateNode(NodeTable, node, &i); + if (g == NULL) { + /* This should not happen */ + Fprintf(stderr, "Error: net %s route end marked as node, but" + " no node found!\n", net->netname); + return 0.0; + } + if (g->area[i] == 0.0) { + /* There's a diffusion diode here! */ + if (visited) visited[node->nodenum] = ANCHOR; + return 0.0; + } else { + /* Add this node to the list of nodes with gates */ + /* attached to this antenna area. */ + if (visited) visited[node->nodenum] = VISITED; + } + if ((method == ANTENNA_ROUTE) && (iroute != NULL)) { + set_node_to_net(node, PR_SOURCE, iroute->glist[0], iroute->bbox, 0); + } + } + else { + SEG rseg; + + /* Back up seg to point to the last segment of the route */ + for (seg = rt->segments; seg && seg->next; seg = seg->next); + x = seg->x2; + y = seg->y2; + l = seg->layer; + + /* Find where on rt2 the segment lands, then search rt2 for */ + /* antenna area forward and reverse from that segment. */ + + rt2 = rt->end.route; + if (rt2 == NULL) return; /* This should not happen */ + + for (rseg = rt2->segments; rseg; rseg = rseg->next) { + if (rseg->segtype & ST_WIRE) { + if (seg->segtype & ST_WIRE) { + compat = (l == rseg->layer); + } + else { + compat = (l == rseg->layer) || (l + 1 == rseg->layer); + } + } + else { + if (seg->segtype & ST_WIRE) { + compat = (l == rseg->layer) || (l == rseg->layer + 1); + } + else { + compat = (l == rseg->layer) || (l == rseg->layer + 1) || + (l + 1 == rseg->layer); + } + } + if (compat) { + if (rseg->segtype & ST_VIA) { + if ((rseg->x2 == seg->x2) && (rseg->y2 == seg->y2)) + break; + } + else if (rseg->x1 < rseg->x2) { + if (rseg->y2 == seg->y2) { + if ((rseg->x1 <= seg->x2) && (rseg->x2 >= seg->x2)) + break; + } + } + else if (rseg->x1 > rseg->x2) { + if (rseg->y2 == seg->y2) { + if ((rseg->x1 >= seg->x2) && (rseg->x2 <= seg->x2)) + break; + } + } + else if (rseg->y1 < rseg->y2) { + if (rseg->x2 == seg->x2) { + if ((rseg->y1 <= seg->y2) && (rseg->y2 >= seg->y2)) + break; + } + } + else if (rseg->y1 > rseg->y2) { + if (rseg->x2 == seg->x2) { + if ((rseg->y1 >= seg->y2) && (rseg->y2 <= seg->y2)) + break; + } + } + } + } + if (rseg == NULL) return; /* This should not happen */ + + if (rseg->next != NULL) + area += get_route_area_forward_fromseg(net, rt2, rseg->next, + layer, visited, method, NodeTable, iroute); + area += get_route_area_reverse_fromseg(net, rt2, rseg, layer, + visited, method, NodeTable, iroute); + } + } + return area; +} + +/*--------------------------------------------------------------*/ +/* Check route antenna forward from the beginning of the route. */ +/*--------------------------------------------------------------*/ + +float +get_route_area_forward(NET net, ROUTE rt, int layer, u_char *visited, + u_char method, Tcl_HashTable *NodeTable, struct routeinfo_ *iroute) +{ + float area; + + area = get_route_area_forward_fromseg(net, rt, NULL, layer, visited, + method, NodeTable, iroute); + return area; +} + +/*--------------------------------------------------------------*/ +/* This is the same as get_route_area_forward_fromseg, but is */ +/* searching the path from end to beginning, so reverse the */ +/* route first and then call get_route_area_forward_fromseg(). */ +/*--------------------------------------------------------------*/ + +float +get_route_area_reverse_fromseg(NET net, ROUTE rt, SEG nseg, int layer, + u_char *visited, u_char method, Tcl_HashTable *NodeTable, + struct routeinfo_ *iroute) +{ + SEG seg, dseg, newseg, firstseg, saveseg; + NODE savestartnode, saveendnode; + float area; + u_char saveflags; + + firstseg = NULL; + + /* Reverse the route */ + for (seg = rt->segments; seg; seg = seg->next) { + newseg = (SEG)malloc(sizeof(struct seg_)); + newseg->layer = seg->layer; + newseg->x1 = seg->x2; + newseg->x2 = seg->x1; + newseg->y1 = seg->y2; + newseg->y2 = seg->y1; + newseg->segtype = seg->segtype; + newseg->next = firstseg; + firstseg = newseg; + } + + saveseg = rt->segments; + + /* Replace the route segment with the reversed route */ + rt->segments = firstseg; + + /* Reverse the endpoint information */ + savestartnode = rt->start.node; + saveendnode = rt->end.node; + rt->start.node = saveendnode; + rt->end.node = savestartnode; + + /* Reverse the start/end flags */ + saveflags = rt->flags & (RT_START_NODE | RT_END_NODE); + rt->flags &= ~(RT_START_NODE | RT_END_NODE); + if (saveflags & RT_START_NODE) rt->flags |= RT_END_NODE; + if (saveflags & RT_END_NODE) rt->flags |= RT_START_NODE; + + area = get_route_area_forward_fromseg(net, rt, nseg, layer, visited, + method, NodeTable, iroute); + + /* Replace the route segment with the original route */ + rt->segments = saveseg; + + /* Revert the endpoint information */ + rt->start.node = savestartnode; + rt->end.node = saveendnode; + + /* Revert the start/end flags */ + rt->flags &= ~(RT_START_NODE | RT_END_NODE); + rt->flags |= saveflags; + + /* Free the reversed route */ + for (seg = firstseg; seg; ) { + dseg = seg->next; + free(seg); + seg = dseg; + } + return area; +} + +/*--------------------------------------------------------------*/ +/* Walk a route in reverse from end to start. */ +/*--------------------------------------------------------------*/ + +float +get_route_area_reverse(NET net, ROUTE rt, int layer, u_char *visited, + u_char method, Tcl_HashTable *NodeTable, + struct routeinfo_ *iroute) +{ + float area; + area = get_route_area_reverse_fromseg(net, rt, NULL, layer, visited, + method, NodeTable, iroute); + return area; +} + +/*--------------------------------------------------------------*/ +/* Find all antenna violations at a specific metal layer */ +/*--------------------------------------------------------------*/ + +int find_layer_antenna_violations(int layer, Tcl_HashTable *NodeTable) +{ + int numerrors, n, nn, numroutes, i, j, new, neterrors; + u_char *visited, method; + float antenna_ratio, thick; + GATE g; + NET net; + ROUTE rt, saveroute; + NODEINFO nodeptr; + NODE node, tnode; + SEG seg; + ANTENNAINFO newantenna; + float gate_area, metal_area, ratio, save_gate, save_metal, max_ratio; + + numerrors = 0; + + /* Get the metal layer record for this layer and find the metal */ + /* area ratio limit and the method to be used for calculating */ + /* metal area. */ + + method = LefGetRouteAntennaMethod(layer); + if (method == CALC_NONE) return 0; /* No antenna information in tech */ + antenna_ratio = (float)LefGetRouteAreaRatio(layer); + thick = (float)LefGetRouteThickness(layer); + if (((method == CALC_SIDEAREA) || (method == CALC_AGG_SIDEAREA)) && (thick == 0.0)) + return 0; /* Insufficient antenna information in tech */ + + /* Make a pass through all nets to find antenna violations */ + + for (n = 0; n < Numnets; n++) { + net = Nlnets[n]; + + if ((net->netnum == VDD_NET) || (net->netnum == GND_NET) || + (net->netnum == ANTENNA_NET)) continue; + + /* Ignore nets with no routes */ + numroutes = 0; + for (rt = net->routes; rt; rt = rt->next) numroutes++; + if (numroutes == 0) continue; + + /* Consider each terminal as a separate sub-net calculation. */ + /* But if multiple terminals belong to the same sub-net, they */ + /* are marked visited and ignored on subsequent calculations. */ + + visited = (u_char *)malloc(net->numnodes * sizeof(u_char)); + for (node = net->netnodes; node != NULL; node = node->next) { + nn = node->nodenum; + visited[nn] = NOT_VISITED; + } + + /* Make a pass through all nodes of the net. Where they are */ + /* not connected together at "layer", these are individual */ + /* sub-nets. */ + + neterrors = 0; + max_ratio = 0.0; /* For diagnostics only */ + for (node = net->netnodes; node != NULL; node = node->next) { + nn = node->nodenum; + if (visited[nn] >= PROCESSED) continue; /* Already seen */ + + /* Find the gate area of this node */ + g = FindGateNode(NodeTable, node, &i); + metal_area = 0.0; + + if (g->area[i] == 0.0) { + visited[nn] = ANCHOR; /* Mark as S/D connection */ + continue; /* No gate, so no violation */ + } + else + visited[nn] = VISITED; + + /* Clear visited flags for routes */ + + for (rt = net->routes; rt; rt = rt->next) + rt->flags &= ~RT_VISITED; + + /* Find the route or routes that connect to this node */ + + for (rt = net->routes; rt; rt = rt->next) { + if ((rt->flags & RT_START_NODE) && (rt->start.node == node)) { + saveroute = rt; + metal_area += get_route_area_forward(net, rt, layer, visited, + method, NodeTable, NULL); + } + else if ((rt->flags & RT_END_NODE) && (rt->end.node == node)) { + saveroute = rt; + metal_area += get_route_area_reverse(net, rt, layer, visited, + method, NodeTable, NULL); + } + else continue; + } + + /* Gate area is combined area of gates visited */ + + gate_area = 0.0; + for (tnode = net->netnodes; tnode != NULL; tnode = tnode->next) { + j = tnode->nodenum; + if (visited[j] == VISITED) { + g = FindGateNode(NodeTable, tnode, &i); + if (g->area[i] == 0.0) { + visited[j] = ANCHOR; + gate_area = 0.0; + break; + } + else + gate_area += g->area[i]; + } + + } + + if (gate_area > 0.0) { + ratio = metal_area / gate_area; + if (ratio > max_ratio) { + max_ratio = ratio; + save_gate = gate_area; + save_metal = metal_area; + } + + if (ratio > antenna_ratio) { + + /* Record and report the violation */ + + numerrors++; + neterrors++; + if (Verbose > 1) { + Fprintf(stderr, + "Antenna violation on node %d of net %s at metal%d\n", + nn, net->netname, layer + 1); + } + if (Verbose > 2) { + Fprintf(stderr, "Metal area = %f, Gate area = %f, Ratio = %f\n", + metal_area, gate_area, ratio); + } + newantenna = (ANTENNAINFO)malloc(sizeof(struct antennainfo_)); + newantenna->net = net; + newantenna->node = node; + newantenna->layer = layer; + newantenna->route = saveroute; + newantenna->next = AntennaList; + AntennaList = newantenna; + } + } + + /* Mark gates as visited on previous pass */ + for (tnode = net->netnodes; tnode != NULL; tnode = tnode->next) { + j = tnode->nodenum; + if (visited[j] == VISITED) visited[j] = PROCESSED; + } + } + free(visited); + + if (Verbose > 3) { + /* Diagnostic */ + if (neterrors == 0) { + if (max_ratio > 0.0) + Fprintf(stderr, "Worst case: Metal area = %f, Gate area = %f, " + "Ratio = %f\n", save_metal, save_gate, max_ratio); + } + } + + /* Clear route visited flags */ + for (rt = net->routes; rt; rt = rt->next) + rt->flags &= ~RT_VISITED; + } + return numerrors; +} + +/*--------------------------------------------------------------*/ +/* This routine is a combination of set_node_to_net(), */ +/* set_routes_to_net(), and disable_node_nets() (see qrouter.c */ +/* and maze.c), but walks the routes in the same manner used */ +/* for finding the antenna violations. Set the antenna part of */ +/* the net as SOURCE, the free antenna taps as TARGET, and the */ +/* non-antenna portion of the net to an unused net number, */ +/* which can be converted back after routing. */ +/*--------------------------------------------------------------*/ + +int set_antenna_to_net(int newflags, struct routeinfo_ *iroute, + u_char stage, ANTENNAINFO violation, Tcl_HashTable *NodeTable) +{ + int x, y, lay, rval, layer; + PROUTE *Pr; + ROUTE rt, clrrt; + NODE node; + NET net; + + /* Set the node and connected antenna metal routes to PR_SOURCE. */ + + rt = violation->route; + node = violation->node; + net = violation->net; + layer = violation->layer; + + if ((rt->flags & RT_START_NODE) && (rt->start.node == node)) + get_route_area_forward(net, rt, layer, NULL, ANTENNA_ROUTE, NodeTable, + iroute); + else if ((rt->flags & RT_END_NODE) && (rt->end.node == node)) + get_route_area_reverse(net, rt, layer, NULL, ANTENNA_ROUTE, NodeTable, + iroute); + else { + /* This should not happen */ + Fprintf(stderr, "Error: Antenna route and node do not connect!\n"); + return 1; + } + + /* Clear route visited flags for next pass */ + for (clrrt = iroute->net->routes; clrrt; clrrt = clrrt->next) + clrrt->flags &= ~RT_VISITED; + + /* Disable the remainder of the route */ + + if ((rt->flags & RT_START_NODE) && (rt->start.node == node)) + get_route_area_forward(net, rt, layer, NULL, ANTENNA_DISABLE, NodeTable, + iroute); + else if ((rt->flags & RT_END_NODE) && (rt->end.node == node)) + get_route_area_reverse(net, rt, layer, NULL, ANTENNA_DISABLE, NodeTable, + iroute); + else { + /* This should not happen */ + Fprintf(stderr, "Error: Antenna route and node do not connect!\n"); + return 1; + } + + /* Done checking routes; clear route visited flags */ + for (clrrt = iroute->net->routes; clrrt; clrrt = clrrt->next) + clrrt->flags &= ~RT_VISITED; + + /* Set the antenna taps to the net number. */ + /* Routine is similar to set_powerbus_to_net(). */ + + rval = 0; + for (lay = 0; lay < Num_layers; lay++) + for (x = 0; x < NumChannelsX; x++) + for (y = 0; y < NumChannelsY; y++) + if ((OBSVAL(x, y, lay) & NETNUM_MASK) == ANTENNA_NET) { + Pr = &OBS2VAL(x, y, lay); + // Skip locations that have been purposefully disabled + if (!(Pr->flags & PR_COST) && (Pr->prdata.net == MAXNETNUM)) + continue; + else if (!(Pr->flags & PR_SOURCE)) { + Pr->flags |= (PR_TARGET | PR_COST); + Pr->prdata.cost = MAXRT; + rval = 1; + OBSVAL(x, y, lay) &= ~NETNUM_MASK; + OBSVAL(x, y, lay) |= net->netnum; + } + } + + return rval; +} + +/*--------------------------------------------------------------*/ +/* This routine is similar to route_setup() for the normal */ +/* stage routes, with changes for the antenna routing. */ +/* Set the node in the "violation" record to source, and set */ +/* all free antenna taps to destination. Add existing routes */ +/* to the source in the same manner as was used to find the */ +/* antenna violation in the first place (this is a subnet of */ +/* the complete net). Disable the remainder of the net. */ +/* Set all free antenna taps to the net number being routed, */ +/* then route like stage 1 power routing. */ +/*--------------------------------------------------------------*/ + +int antenna_setup(struct routeinfo_ *iroute, ANTENNAINFO violation, + Tcl_HashTable *NodeTable) +{ + int i, j, netnum, rval; + PROUTE *Pr; + + for (i = 0; i < Num_layers; i++) { + for (j = 0; j < NumChannelsX * NumChannelsY; j++) { + netnum = Obs[i][j] & (~BLOCKED_MASK); + Pr = &Obs2[i][j]; + if (netnum != 0) { + Pr->flags = 0; // Clear all flags + if (netnum == DRC_BLOCKAGE) + Pr->prdata.net = netnum; + else + Pr->prdata.net = netnum & NETNUM_MASK; + } else { + Pr->flags = PR_COST; // This location is routable + Pr->prdata.cost = MAXRT; + } + } + } + + // Fill out route information record + + iroute->net = violation->net; + iroute->rt = NULL; + for (i = 0; i < 6; i++) + iroute->glist[i] = NULL; + iroute->nsrc = violation->node; + iroute->nsrctap = iroute->nsrc->taps; + iroute->maxcost = MAXRT; + iroute->do_pwrbus = TRUE; + iroute->pwrbus_src = 0; + + iroute->bbox.x2 = iroute->bbox.y2 = 0; + iroute->bbox.x1 = NumChannelsX; + iroute->bbox.y1 = NumChannelsY; + + rval = set_antenna_to_net(PR_SOURCE, iroute, 0, violation, NodeTable); + + /* Unlikely that MASK_BBOX would be useful, since one does */ + /* not know if an antenna tap is inside the box or not. */ + /* Maybe if bounding box is expanded to encompass some */ + /* number of taps. . . */ + + // if (maskMode == MASK_NONE) + fillMask((u_char)0); + // else if (maskMode == MASK_BBOX) + // createBboxMask(iroute->net, (u_char)Numpasses); + + iroute->maxcost = 20; + return rval; +} + +/*--------------------------------------------------------------*/ +/* The simplest way to fix an antenna violation is to find */ +/* a place in the antenna metal to break the antenna and pull */ +/* it up to a higher level of metal. Depending on the severity */ +/* of the antenna violation, this may need to be done more than */ +/* once. If no place to break the antenna is found, return -1 */ +/* for failure. */ +/*--------------------------------------------------------------*/ + +int simpleantennafix(ANTENNAINFO violation, Tcl_HashTable *NodeTable) +{ + return -1; /* Antenna was not fixed */ +} + +/*--------------------------------------------------------------*/ +/* Route from nets with antenna violations to the nearest */ +/* routable antenna cell tap. */ +/* */ +/* This routine is essentially the same as doroute() but with */ +/* some special handling related to the antenna taps, which */ +/* have much in common with VDD and GND taps but significant */ +/* differences as well. */ +/*--------------------------------------------------------------*/ + +int doantennaroute(ANTENNAINFO violation, Tcl_HashTable *NodeTable) +{ + NET net; + NODE node; + ROUTE rt1, lrt; + int layer, i, result, savelayers; + struct routeinfo_ iroute; + + net = violation->net; + node = violation->node; + layer = violation->layer; + + result = antenna_setup(&iroute, violation, NodeTable); + + rt1 = createemptyroute(); + rt1->netnum = net->netnum; + iroute.rt = rt1; + + /* Force routing to be done at or below the antenna check layer. */ + + savelayers = Num_layers; + Num_layers = violation->layer + 1; + + result = route_segs(&iroute, 0, (u_char)0); + + Num_layers = savelayers; + + if (result < 0) { + /* To do: Handle failures? */ + Fprintf(stderr, "Antenna anchoring route failed.\n"); + free(rt1); + } + else { + TotalRoutes++; + if (net->routes) { + for (lrt = net->routes; lrt->next; lrt = lrt->next); + lrt->next = rt1; + } + else { + /* This should not happen */ + Fprintf(stderr, "Error: Net has no routes!\n"); + net->routes = rt1; + } + } + + /* For power-bus-type routing, glist is not empty after routing */ + free_glist(&iroute); + + /* Put free taps back to ANTENNA_NET */ + revert_antenna_taps(net->netnum, rt1->start.node); + + return result; +} + +/*--------------------------------------------------------------*/ +/* Top level routine called from tclqrouter.c */ +/*--------------------------------------------------------------*/ + +void +resolve_antenna(char *antennacell, u_char do_fix) +{ + FILE *fout; + int numtaps, numerrors, numfixed, result; + int layererrors; + int layer, i, new; + Tcl_HashTable NodeTable; + Tcl_HashEntry *entry; + GATE g; + NET net; + ROUTE rt; + ANTENNAINFO nextviolation, FixedList = NULL, BadList = NULL; + + numtaps = count_free_antenna_taps(antennacell); + if (Verbose > 3) { + Fprintf(stdout, "Number of free antenna taps = %d\n", numtaps); + } + + AntennaList = NULL; + numerrors = 0; + numfixed = 0; + + /* Build a hash table of nodes, so the gate area can be found */ + /* quickly for any node by hash lookup. */ + + Tcl_InitHashTable(&NodeTable, TCL_ONE_WORD_KEYS); + + for (g = Nlgates; g; g = g->next) { + for (i = 0; i < g->nodes; i++) { + GATENODE gn; + gn = (GATENODE)malloc(sizeof(struct gatenode_)); + gn->idx = i; + gn->gate = g; + entry = Tcl_CreateHashEntry(&NodeTable, (char *)(*(g->noderec + i)), &new); + Tcl_SetHashValue(entry, gn); + } + } + + /* Working from the 1nd layer metal to the top, compute */ + /* route metal to gate area ratios. Mark each one when */ + /* done, as an antenna violation that has been fixed at, */ + /* say, metal2 can no longer be a violation on any higer */ + /* layer of metal. */ + + for (layer = 0; layer < Num_layers; layer++) { + layererrors = find_layer_antenna_violations(layer, &NodeTable); + numerrors += layererrors; + if (Verbose > 2) { + Fprintf(stdout, "Number of antenna errors on metal%d = %d\n", + layer + 1, layererrors); + } + + /* Fix the violations found on this layer before moving */ + /* on to the next layer. */ + + while (AntennaList != NULL) { + nextviolation = AntennaList->next; + + if (do_fix) { + result = simpleantennafix(AntennaList, &NodeTable); + if (result == 0) { + /* No antenna cell involved, so no backannotation */ + /* required. Remove the "route" record. */ + AntennaList->route = NULL; + } + else + result = doantennaroute(AntennaList, &NodeTable); + if (result >= 0) numfixed++; + } + + /* Move the error information to either the Fixed or Bad lists */ + if (result >= 0) { + AntennaList->next = FixedList; + FixedList = AntennaList; + if (AntennaList->route != NULL) { + /* Replace the route record with the last route */ + /* of the net, which was the route added to fix. */ + /* If the net requires more than one antenna */ + /* anchor, then routes won't be confused. */ + for (rt = AntennaList->net->routes; rt && rt->next; rt = rt->next); + AntennaList->route = rt; + } + } + else { + AntennaList->next = BadList; + BadList = AntennaList; + } + AntennaList = nextviolation; + } + } + + if (Verbose > 0) { + Fprintf(stdout, "Total number of antenna errors found = %d\n", numerrors); + if (do_fix) + Fprintf(stdout, "Total number of antenna errors fixed = %d\n", numfixed); + } + if (numtaps < numerrors) { + if (numtaps == 0) + Fprintf(stderr, "There are no antenna taps to use to correct " + "antenna errors!\n"); + else { + Fprintf(stderr, "There are not enough antenna taps to use to " + "correct antenna errors!\n"); + Fprintf(stderr, "Number of errors = %d, number of taps = %d\n", + numerrors, numtaps); + Fprintf(stderr, "Increate the amount of unallocated antenna cells" + " in the design.\n"); + } + /* To do: Replace the error message with an ad-hoc solution to */ + /* pull routes up to a higher metal layer near the gate causing */ + /* the error. */ + } + + /* Output the violation lists. The fixed violations need to be */ + /* known so that the additional connection to the net can be added */ + /* to the netlist for verification purposes. The unfixed */ + /* violations need to be reported so they can be tracked down and */ + /* fixed by hand. */ + + if ((FixedList != NULL) || (BadList != NULL)) + fout = fopen("antenna.out", "w"); + + if (FixedList != NULL) { + ROUTE rt; + fprintf(fout, "Revised netlist: New antenna anchor connections\n"); + + for (nextviolation = FixedList; nextviolation; + nextviolation = nextviolation->next) { + // NOTE: nextviolation->route was changed from the route that + // connects to the gate in violation, to the route that fixes + // the antenna error. + g = FindGateNode(&NodeTable, nextviolation->route->start.node, &i); + fprintf(fout, "Net=%s Instance=%s Cell=%s Pin=%s\n", + nextviolation->net->netname, g->gatename, + g->gatetype->gatename, g->gatetype->node[i]); + } + fprintf(fout, "\n"); + } + + if (BadList != NULL) { + fprintf(fout, "Unfixed antenna errors:\n"); + + for (nextviolation = BadList; nextviolation; + nextviolation = nextviolation->next) { + g = FindGateNode(&NodeTable, nextviolation->node, &i); + fprintf(fout, "Net=%s Instance=%s Cell=%s Pin=%s error on Metal%d\n", + nextviolation->net->netname, + g->gatename, g->gatetype->gatename, + g->gatetype->node[i], nextviolation->layer + 1); + } + } + + if ((FixedList != NULL) || (BadList != NULL)) fclose(fout); + + /* Free up the node hash table */ + + FreeNodeTable(&NodeTable); + Tcl_DeleteHashTable(&NodeTable); + + /* Free up the violation lists */ + + while (FixedList != NULL) { + nextviolation = FixedList->next; + free(FixedList); + FixedList = nextviolation; + } + while (BadList != NULL) { + nextviolation = BadList->next; + free(BadList); + BadList = nextviolation; + } +} + +#endif /* TCL_QROUTER */ + +/* end of antenna.c */ diff --git a/config.log b/config.log new file mode 100644 index 0000000..78ff1d3 --- /dev/null +++ b/config.log @@ -0,0 +1,499 @@ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by configure, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ ./configure + +## --------- ## +## Platform. ## +## --------- ## + +hostname = stravinsky +uname -m = x86_64 +uname -r = 4.1.13-100.fc21.x86_64 +uname -s = Linux +uname -v = #1 SMP Tue Nov 10 13:13:20 UTC 2015 + +/usr/bin/uname -p = x86_64 +/bin/uname -X = unknown + +/bin/arch = x86_64 +/usr/bin/arch -k = unknown +/usr/convex/getsysinfo = unknown +/usr/bin/hostinfo = unknown +/bin/machine = unknown +/usr/bin/oslevel = unknown +/bin/universe = unknown + +PATH: . +PATH: /bin +PATH: /sbin +PATH: /usr/bin +PATH: /usr/sbin +PATH: /usr/local/bin + + +## ----------- ## +## Core tests. ## +## ----------- ## + +configure:2206: checking build system type +configure:2220: result: x86_64-unknown-linux-gnu +configure:2240: checking host system type +configure:2253: result: x86_64-unknown-linux-gnu +configure:2273: checking target system type +configure:2286: result: x86_64-unknown-linux-gnu +configure:2380: checking for gcc +configure:2396: found /bin/gcc +configure:2407: result: gcc +configure:2636: checking for C compiler version +configure:2645: gcc --version >&5 +gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6) +Copyright (C) 2014 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +configure:2656: $? = 0 +configure:2645: gcc -v >&5 +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.9.2/lto-wrapper +Target: x86_64-redhat-linux +Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux +Thread model: posix +gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) +configure:2656: $? = 0 +configure:2645: gcc -V >&5 +gcc: error: unrecognized command line option '-V' +gcc: fatal error: no input files +compilation terminated. +configure:2656: $? = 4 +configure:2645: gcc -qversion >&5 +gcc: error: unrecognized command line option '-qversion' +gcc: fatal error: no input files +compilation terminated. +configure:2656: $? = 4 +configure:2676: checking whether the C compiler works +configure:2698: gcc conftest.c >&5 +configure:2702: $? = 0 +configure:2750: result: yes +configure:2753: checking for C compiler default output file name +configure:2755: result: a.out +configure:2761: checking for suffix of executables +configure:2768: gcc -o conftest conftest.c >&5 +configure:2772: $? = 0 +configure:2794: result: +configure:2816: checking whether we are cross compiling +configure:2824: gcc -o conftest conftest.c >&5 +configure:2828: $? = 0 +configure:2835: ./conftest +configure:2839: $? = 0 +configure:2854: result: no +configure:2859: checking for suffix of object files +configure:2881: gcc -c conftest.c >&5 +configure:2885: $? = 0 +configure:2906: result: o +configure:2910: checking whether we are using the GNU C compiler +configure:2929: gcc -c conftest.c >&5 +configure:2929: $? = 0 +configure:2938: result: yes +configure:2947: checking whether gcc accepts -g +configure:2967: gcc -c -g conftest.c >&5 +configure:2967: $? = 0 +configure:3008: result: yes +configure:3025: checking for gcc option to accept ISO C89 +configure:3088: gcc -c -g -O2 conftest.c >&5 +configure:3088: $? = 0 +configure:3101: result: none needed +configure:3126: checking how to run the C preprocessor +configure:3157: gcc -E conftest.c +configure:3157: $? = 0 +configure:3171: gcc -E conftest.c +conftest.c:9:28: fatal error: ac_nonexistent.h: No such file or directory + #include <ac_nonexistent.h> + ^ +compilation terminated. +configure:3171: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "" +| #define PACKAGE_TARNAME "" +| #define PACKAGE_VERSION "" +| #define PACKAGE_STRING "" +| #define PACKAGE_BUGREPORT "" +| #define PACKAGE_URL "" +| /* end confdefs.h. */ +| #include <ac_nonexistent.h> +configure:3196: result: gcc -E +configure:3216: gcc -E conftest.c +configure:3216: $? = 0 +configure:3230: gcc -E conftest.c +conftest.c:9:28: fatal error: ac_nonexistent.h: No such file or directory + #include <ac_nonexistent.h> + ^ +compilation terminated. +configure:3230: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "" +| #define PACKAGE_TARNAME "" +| #define PACKAGE_VERSION "" +| #define PACKAGE_STRING "" +| #define PACKAGE_BUGREPORT "" +| #define PACKAGE_URL "" +| /* end confdefs.h. */ +| #include <ac_nonexistent.h> +configure:3259: checking for library containing strerror +configure:3290: gcc -o conftest -g -O2 conftest.c >&5 +configure:3290: $? = 0 +configure:3307: result: none required +configure:3332: checking for a BSD-compatible install +configure:3400: result: /bin/install -c +configure:3454: checking for ranlib +configure:3470: found /bin/ranlib +configure:3481: result: ranlib +configure:3505: checking for autoconf +configure:3521: found /bin/autoconf +configure:3533: result: autoconf +configure:3543: checking for cp +configure:3559: found /bin/cp +configure:3571: result: cp +configure:3581: checking for rm +configure:3597: found /bin/rm +configure:3609: result: rm +configure:3625: checking for grep that handles long lines and -e +configure:3683: result: /bin/grep +configure:3688: checking for egrep +configure:3750: result: /bin/grep -E +configure:3755: checking for ANSI C header files +configure:3775: gcc -c -g -O2 conftest.c >&5 +configure:3775: $? = 0 +configure:3848: gcc -o conftest -g -O2 conftest.c >&5 +configure:3848: $? = 0 +configure:3848: ./conftest +configure:3848: $? = 0 +configure:3859: result: yes +configure:3870: checking for setenv +configure:3870: gcc -o conftest -g -O2 conftest.c >&5 +configure:3870: $? = 0 +configure:3870: result: yes +configure:3870: checking for putenv +configure:3870: gcc -o conftest -g -O2 conftest.c >&5 +configure:3870: $? = 0 +configure:3870: result: yes +configure:3902: checking for ld used by GCC +configure:3965: result: /bin/ld +configure:3972: checking if the linker (/bin/ld) is GNU ld +GNU ld version 2.24 +configure:3984: result: yes +configure:3990: checking for va_copy +configure:4008: gcc -o conftest -g -O2 conftest.c >&5 +configure:4008: $? = 0 +configure:4017: result: yes +configure:4025: checking for __va_copy +configure:4043: gcc -o conftest -g -O2 conftest.c >&5 +configure:4043: $? = 0 +configure:4052: result: yes +configure:4173: checking for tclConfig.sh +configure:4247: result: /usr/lib64/tclConfig.sh +configure:4263: checking for tkConfig.sh +configure:4338: result: /usr/lib64/tkConfig.sh +configure:4472: checking for wish executable +configure:4499: result: /usr/bin/wish +configure:4586: checking for X +configure:4725: gcc -o conftest -g -O2 conftest.c -lX11 >&5 +configure:4725: $? = 0 +configure:4775: result: libraries , headers +configure:4874: gcc -o conftest -g -O2 conftest.c -lX11 >&5 +configure:4874: $? = 0 +configure:4972: checking for gethostbyname +configure:4972: gcc -o conftest -g -O2 conftest.c >&5 +configure:4972: $? = 0 +configure:4972: result: yes +configure:5069: checking for connect +configure:5069: gcc -o conftest -g -O2 conftest.c >&5 +configure:5069: $? = 0 +configure:5069: result: yes +configure:5118: checking for remove +configure:5118: gcc -o conftest -g -O2 conftest.c >&5 +configure:5118: $? = 0 +configure:5118: result: yes +configure:5167: checking for shmat +configure:5167: gcc -o conftest -g -O2 conftest.c >&5 +configure:5167: $? = 0 +configure:5167: result: yes +configure:5225: checking for IceConnectionNumber in -lICE +configure:5250: gcc -o conftest -g -O2 conftest.c -lICE >&5 +configure:5250: $? = 0 +configure:5259: result: yes +configure:5280: checking for XtToolkitInitialize in -lXt +configure:5305: gcc -o conftest -g -O2 conftest.c -lXt >&5 +configure:5305: $? = 0 +configure:5314: result: yes +configure:5377: checking for sys/types.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for sys/stat.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for stdlib.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for string.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for memory.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for strings.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for inttypes.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for stdint.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5377: checking for unistd.h +configure:5377: gcc -c -g -O2 conftest.c >&5 +configure:5377: $? = 0 +configure:5377: result: yes +configure:5391: checking sys/mman.h usability +configure:5391: gcc -c -g -O2 conftest.c >&5 +configure:5391: $? = 0 +configure:5391: result: yes +configure:5391: checking sys/mman.h presence +configure:5391: gcc -E conftest.c +configure:5391: $? = 0 +configure:5391: result: yes +configure:5391: checking for sys/mman.h +configure:5391: result: yes +configure:5808: creating ./config.status + +## ---------------------- ## +## Running config.status. ## +## ---------------------- ## + +This file was extended by config.status, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = + CONFIG_HEADERS = + CONFIG_LINKS = + CONFIG_COMMANDS = + $ ./config.status + +on stravinsky + +config.status:770: creating Makefile + +## ---------------- ## +## Cache variables. ## +## ---------------- ## + +ac_cv_build=x86_64-unknown-linux-gnu +ac_cv_c___va_copy=yes +ac_cv_c_compiler_gnu=yes +ac_cv_c_va_copy=yes +ac_cv_env_CC_set= +ac_cv_env_CC_value= +ac_cv_env_CFLAGS_set= +ac_cv_env_CFLAGS_value= +ac_cv_env_CPPFLAGS_set= +ac_cv_env_CPPFLAGS_value= +ac_cv_env_CPP_set= +ac_cv_env_CPP_value= +ac_cv_env_LDFLAGS_set= +ac_cv_env_LDFLAGS_value= +ac_cv_env_LIBS_set= +ac_cv_env_LIBS_value= +ac_cv_env_XMKMF_set= +ac_cv_env_XMKMF_value= +ac_cv_env_build_alias_set= +ac_cv_env_build_alias_value= +ac_cv_env_host_alias_set= +ac_cv_env_host_alias_value= +ac_cv_env_target_alias_set= +ac_cv_env_target_alias_value= +ac_cv_func_connect=yes +ac_cv_func_gethostbyname=yes +ac_cv_func_putenv=yes +ac_cv_func_remove=yes +ac_cv_func_setenv=yes +ac_cv_func_shmat=yes +ac_cv_have_x='have_x=yes ac_x_includes='\'''\'' ac_x_libraries='\'''\''' +ac_cv_header_inttypes_h=yes +ac_cv_header_memory_h=yes +ac_cv_header_stdc=yes +ac_cv_header_stdint_h=yes +ac_cv_header_stdlib_h=yes +ac_cv_header_string_h=yes +ac_cv_header_strings_h=yes +ac_cv_header_sys_mman_h=yes +ac_cv_header_sys_stat_h=yes +ac_cv_header_sys_types_h=yes +ac_cv_header_unistd_h=yes +ac_cv_host=x86_64-unknown-linux-gnu +ac_cv_lib_ICE_IceConnectionNumber=yes +ac_cv_lib_Xt_XtToolkitInitialize=yes +ac_cv_objext=o +ac_cv_path_EGREP='/bin/grep -E' +ac_cv_path_GREP=/bin/grep +ac_cv_path_LD=/bin/ld +ac_cv_path_install='/bin/install -c' +ac_cv_prog_AUTOCONF=autoconf +ac_cv_prog_CP=cp +ac_cv_prog_CPP='gcc -E' +ac_cv_prog_RM=rm +ac_cv_prog_ac_ct_CC=gcc +ac_cv_prog_ac_ct_RANLIB=ranlib +ac_cv_prog_cc_c89= +ac_cv_prog_cc_g=yes +ac_cv_prog_gnu_ld=yes +ac_cv_search_strerror='none required' +ac_cv_target=x86_64-unknown-linux-gnu + +## ----------------- ## +## Output variables. ## +## ----------------- ## + +ALL_TARGET='tcl' +AUTOCONF='autoconf' +CC='gcc' +CFLAGS='-g -O2' +CP='cp' +CPP='gcc -E' +CPPFLAGS=' -m64 -fPIC' +DEFS='-DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DHAVE_LIBXT=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_MMAN_H=1 -DTCL_QROUTER=1 -DLINUX=1 -DSYSV=1 -DVERSION=\"1.4\" -DREVISION=\"7\"' +ECHO_C='' +ECHO_N='-n' +ECHO_T='' +EGREP='/bin/grep -E' +EXEEXT='' +EXTRA_LIB_SPECS='-ldl' +GREP='/bin/grep' +INC_SPECS='' +INSTALL_DATA='${INSTALL} -m 644' +INSTALL_PROGRAM='${INSTALL}' +INSTALL_SCRIPT='${INSTALL}' +INSTALL_TARGET='install-tcl' +LD='/bin/ld' +LDDL_FLAGS='-shared -Wl,-soname,$@ -Wl,--version-script=symbol.map' +LDFLAGS='' +LD_RUN_PATH='' +LIBOBJS='' +LIBS='-lXt ' +LIB_SPECS=' -L/usr/lib64 -ltkstub8.6 -L/usr/lib64 -ltclstub8.6' +LIB_SPECS_NOSTUB=' -L/usr/lib64 -ltk8.6 -L/usr/lib64 -ltcl8.6' +LTLIBOBJS='' +OBJEXT='o' +PACKAGE_BUGREPORT='' +PACKAGE_NAME='' +PACKAGE_STRING='' +PACKAGE_TARNAME='' +PACKAGE_URL='' +PACKAGE_VERSION='' +PATH_SEPARATOR=':' +QROUTER_LIB_DIR='share/qrouter' +RANLIB='ranlib' +REVISION='7' +RM='rm' +SHDLIB_EXT='.so' +SHELL='/bin/sh' +SHLIB_CFLAGS='-fPIC' +SHLIB_LD='' +SHLIB_LIB_SPECS='' +STDLIBS='' +TCL_LIB_DIR='/usr/lib64' +TK_LIB_DIR='/usr/lib64' +VERSION='1.4' +WISH_EXE='/usr/bin/wish' +XMKMF='' +X_CFLAGS='' +X_EXTRA_LIBS='' +X_LIBS='' +X_PRE_LIBS=' -lSM -lICE' +ac_ct_CC='gcc' +bindir='${exec_prefix}/bin' +build='x86_64-unknown-linux-gnu' +build_alias='' +build_cpu='x86_64' +build_os='linux-gnu' +build_vendor='unknown' +datadir='${datarootdir}' +datarootdir='${prefix}/share' +docdir='${datarootdir}/doc/${PACKAGE}' +dvidir='${docdir}' +exec_prefix='${prefix}' +host='x86_64-unknown-linux-gnu' +host_alias='' +host_cpu='x86_64' +host_os='linux-gnu' +host_vendor='unknown' +htmldir='${docdir}' +includedir='${prefix}/include' +infodir='${datarootdir}/info' +libdir='${exec_prefix}/lib' +libexecdir='${exec_prefix}/libexec' +localedir='${datarootdir}/locale' +localstatedir='${prefix}/var' +mandir='${datarootdir}/man' +oldincludedir='/usr/include' +pdfdir='${docdir}' +prefix='/usr/local' +program_transform_name='s,x,x,' +psdir='${docdir}' +sbindir='${exec_prefix}/sbin' +sharedstatedir='${prefix}/com' +stub_defs=' -DUSE_TCL_STUBS -DUSE_TK_STUBS' +sysconfdir='${prefix}/etc' +target='x86_64-unknown-linux-gnu' +target_alias='' +target_cpu='x86_64' +target_os='linux-gnu' +target_vendor='unknown' + +## ----------- ## +## confdefs.h. ## +## ----------- ## + +/* confdefs.h */ +#define PACKAGE_NAME "" +#define PACKAGE_TARNAME "" +#define PACKAGE_VERSION "" +#define PACKAGE_STRING "" +#define PACKAGE_BUGREPORT "" +#define PACKAGE_URL "" +#define STDC_HEADERS 1 +#define HAVE_SETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_VA_COPY 1 +#define HAVE___VA_COPY 1 +#define HAVE_LIBXT 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_SYS_MMAN_H 1 +#define TCL_QROUTER 1 +#define LINUX 1 +#define SYSV 1 +#define VERSION "1.4" +#define REVISION "7" + +configure: exit 0 diff --git a/config.status b/config.status new file mode 100755 index 0000000..e52ea93 --- /dev/null +++ b/config.status @@ -0,0 +1,942 @@ +#! /bin/sh +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +# Files that config.status was made for. +config_files=" Makefile" + +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to the package provider." + +ac_cs_config="" +ac_cs_version="\ +config.status +configured by ./configure, generated by GNU Autoconf 2.69, + with options \"$ac_cs_config\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='/home/tim/gitsrc/qrouter-1.4' +srcdir='.' +INSTALL='/bin/install -c' +test -n "$AWK" || AWK=awk +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +if $ac_cs_recheck; then + set X /bin/sh './configure' $ac_configure_extra_args --no-create --no-recursion + shift + $as_echo "running CONFIG_SHELL=/bin/sh $*" >&6 + CONFIG_SHELL='/bin/sh' + export CONFIG_SHELL + exec "$@" +fi + +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +cat >>"$ac_tmp/subs1.awk" <<\_ACAWK && +S["LTLIBOBJS"]="" +S["LIBOBJS"]="" +S["QROUTER_LIB_DIR"]="share/qrouter" +S["INSTALL_TARGET"]="install-tcl" +S["ALL_TARGET"]="tcl" +S["STDLIBS"]="" +S["LD_RUN_PATH"]="" +S["SHLIB_CFLAGS"]="-fPIC" +S["WISH_EXE"]="/usr/bin/wish" +S["TK_LIB_DIR"]="/usr/lib64" +S["TCL_LIB_DIR"]="/usr/lib64" +S["LIB_SPECS_NOSTUB"]=" -L/usr/lib64 -ltk8.6 -L/usr/lib64 -ltcl8.6" +S["LIB_SPECS"]=" -L/usr/lib64 -ltkstub8.6 -L/usr/lib64 -ltclstub8.6" +S["INC_SPECS"]="" +S["EXTRA_LIB_SPECS"]="-ldl" +S["stub_defs"]=" -DUSE_TCL_STUBS -DUSE_TK_STUBS" +S["SHLIB_LIB_SPECS"]="" +S["LDDL_FLAGS"]="-shared -Wl,-soname,$@ -Wl,--version-script=symbol.map" +S["LD"]="/bin/ld" +S["SHLIB_LD"]="" +S["SHDLIB_EXT"]=".so" +S["X_EXTRA_LIBS"]="" +S["X_LIBS"]="" +S["X_PRE_LIBS"]=" -lSM -lICE" +S["X_CFLAGS"]="" +S["XMKMF"]="" +S["EGREP"]="/bin/grep -E" +S["GREP"]="/bin/grep" +S["RM"]="rm" +S["CP"]="cp" +S["AUTOCONF"]="autoconf" +S["RANLIB"]="ranlib" +S["INSTALL_DATA"]="${INSTALL} -m 644" +S["INSTALL_SCRIPT"]="${INSTALL}" +S["INSTALL_PROGRAM"]="${INSTALL}" +S["CPP"]="gcc -E" +S["OBJEXT"]="o" +S["EXEEXT"]="" +S["ac_ct_CC"]="gcc" +S["CPPFLAGS"]=" -m64 -fPIC" +S["LDFLAGS"]="" +S["CFLAGS"]="-g -O2" +S["CC"]="gcc" +S["REVISION"]="7" +S["VERSION"]="1.4" +S["target_os"]="linux-gnu" +S["target_vendor"]="unknown" +S["target_cpu"]="x86_64" +S["target"]="x86_64-unknown-linux-gnu" +S["host_os"]="linux-gnu" +S["host_vendor"]="unknown" +S["host_cpu"]="x86_64" +S["host"]="x86_64-unknown-linux-gnu" +S["build_os"]="linux-gnu" +S["build_vendor"]="unknown" +S["build_cpu"]="x86_64" +S["build"]="x86_64-unknown-linux-gnu" +S["target_alias"]="" +S["host_alias"]="" +S["build_alias"]="" +S["LIBS"]="-lXt " +S["ECHO_T"]="" +S["ECHO_N"]="-n" +S["ECHO_C"]="" +S["DEFS"]="-DPACKAGE_NAME=\\\"\\\" -DPACKAGE_TARNAME=\\\"\\\" -DPACKAGE_VERSION=\\\"\\\" -DPACKAGE_STRING=\\\"\\\" -DPACKAGE_BUGREPORT=\\\"\\\" -DPACKAGE_URL=\\\"\\\" -DSTDC_HEADERS=1"\ +" -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DHAVE_LIBXT=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAV"\ +"E_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_MMAN_H=1 -DTCL_QROUTER=1 -DLINU"\ +"X=1 -DSYSV=1 -DVERSION=\\\"1.4\\\" -DREVISION=\\\"7\\\"" +S["mandir"]="${datarootdir}/man" +S["localedir"]="${datarootdir}/locale" +S["libdir"]="${exec_prefix}/lib" +S["psdir"]="${docdir}" +S["pdfdir"]="${docdir}" +S["dvidir"]="${docdir}" +S["htmldir"]="${docdir}" +S["infodir"]="${datarootdir}/info" +S["docdir"]="${datarootdir}/doc/${PACKAGE}" +S["oldincludedir"]="/usr/include" +S["includedir"]="${prefix}/include" +S["localstatedir"]="${prefix}/var" +S["sharedstatedir"]="${prefix}/com" +S["sysconfdir"]="${prefix}/etc" +S["datadir"]="${datarootdir}" +S["datarootdir"]="${prefix}/share" +S["libexecdir"]="${exec_prefix}/libexec" +S["sbindir"]="${exec_prefix}/sbin" +S["bindir"]="${exec_prefix}/bin" +S["program_transform_name"]="s,x,x," +S["prefix"]="/usr/local" +S["exec_prefix"]="${prefix}" +S["PACKAGE_URL"]="" +S["PACKAGE_BUGREPORT"]="" +S["PACKAGE_STRING"]="" +S["PACKAGE_VERSION"]="" +S["PACKAGE_TARNAME"]="" +S["PACKAGE_NAME"]="" +S["PATH_SEPARATOR"]=":" +S["SHELL"]="/bin/sh" +_ACAWK +cat >>"$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + ac_datarootdir_hack=' + s&@datadir@&${datarootdir}&g + s&@docdir@&${datarootdir}/doc/${PACKAGE}&g + s&@infodir@&${datarootdir}/info&g + s&@localedir@&${datarootdir}/locale&g + s&@mandir@&${datarootdir}/man&g + s&\${datarootdir}&${prefix}/share&g' ;; +esac +ac_sed_extra="/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +} + +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 @@ -37,12 +37,12 @@ int numSpecial = 0; /* Tracks number of specialnets */ #ifndef TCL_QROUTER /* Find an instance in the instance list. If qrouter */ -/* is compiled with Tcl support, then this routine is */ -/* found in tclqrouter.c and uses hash tables, greatly */ -/* speeding up the read-in of large DEF files. */ +/* is compiled with Tcl support, then this routine */ +/* uses Tcl hash tables, greatly speeding up the */ +/* read-in of large DEF files. */ -static GATE -DefFindInstance(char *name) +GATE +DefFindGate(char *name) { GATE ginst; @@ -53,6 +53,25 @@ DefFindInstance(char *name) return NULL; } +/* Find a net in the list of nets. If qrouter is */ +/* compiled with Tcl support, then this routine */ +/* uses Tcl hash tables, greatly speeding up the */ +/* read-in of large DEF files. */ + +NET +DefFindNet(char *name) +{ + int i; + NET net; + + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + if (!strcasecmp(net->netname, name)) + return net; + } + return NULL; +} + /* For the non-Tcl version, these are empty placeholders */ static void @@ -65,13 +84,19 @@ DefHashInstance(GATE gateginfo) { } +static void +DefHashNet(NET net) +{ +} + #else /* The versions using TCL hash tables */ #include <tk.h> -/* This hash table speeds up DEF file reading */ +/* These hash tables speed up DEF file reading */ static Tcl_HashTable InstanceTable; +static Tcl_HashTable NetTable; /*--------------------------------------------------------------*/ /* Cell macro lookup based on the hash table */ @@ -83,10 +108,11 @@ DefHashInit(void) /* Initialize the macro hash table */ Tcl_InitHashTable(&InstanceTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&NetTable, TCL_STRING_KEYS); } -static GATE -DefFindInstance(char *name) +GATE +DefFindGate(char *name) { GATE ginst; Tcl_HashEntry *entry; @@ -96,6 +122,20 @@ DefFindInstance(char *name) return ginst; } +NET +DefFindNet(char *name) +{ + NET net; + Tcl_HashEntry *entry; + + // Guard against calls to find nets before DEF file is read + if (Numnets == 0) return NULL; + + entry = Tcl_FindHashEntry(&NetTable, name); + net = (entry) ? (NET)Tcl_GetHashValue(entry) : NULL; + return net; +} + /*--------------------------------------------------------------*/ /* Cell macro hash table generation */ /* Given an instance record, create an entry in the hash table */ @@ -115,6 +155,24 @@ DefHashInstance(GATE gateginfo) Tcl_SetHashValue(entry, (ClientData)gateginfo); } +/*--------------------------------------------------------------*/ +/* Net hash table generation */ +/* Given a net record, create an entry in the hash table for */ +/* the net name, with the record entry pointing to the net */ +/* record. */ +/*--------------------------------------------------------------*/ + +static void +DefHashNet(NET net) +{ + int new; + Tcl_HashEntry *entry; + + entry = Tcl_CreateHashEntry(&NetTable, net->netname, &new); + if (entry != NULL) + Tcl_SetHashValue(entry, (ClientData)net); +} + #endif /* TCL_QROUTER */ /* @@ -125,7 +183,7 @@ DefHashInstance(GATE gateginfo) * Parse a network route statement from the DEF file. * If "special" is 1, then, add the geometry to the * list of obstructions. If "special" is 0, then read - * the geometry into a route structure for the net. + * the geometry into a route structure for the net. * * Results: * Returns the last token encountered. @@ -145,6 +203,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) DSEG lr, drect; struct point_ refp; char valid = FALSE; /* is there a valid reference point? */ + char noobstruct; char initial = TRUE; struct dseg_ locarea; double x, y, lx, ly, w, hw, s; @@ -158,6 +217,12 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) /* Set pitches and allocate memory for Obs[] if we haven't yet. */ set_num_channels(); + /* Don't create obstructions or routes on routed specialnets inputs */ + /* except for power and ground nets. */ + noobstruct = ((special == (char)1) && (!(net->flags & NET_IGNORED)) && + (net->netnum != VDD_NET) && (net->netnum != GND_NET)) ? + TRUE : FALSE; + while (initial || (token = LefNextToken(f, TRUE)) != NULL) { /* Get next point, token "NEW", or via name */ @@ -174,13 +239,14 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) if (routeLayer < 0) { - LefError("Unknown layer type \"%s\" for NEW route\n", token); + LefError(DEF_ERROR, "Unknown layer type \"%s\" for NEW route\n", token); continue; } else if (routeLayer >= Num_layers) { - LefError("DEF file contains layer \"%s\" which is not allowed " - "by the layer limit setting of %d\n", token, Num_layers); + LefError(DEF_ERROR, "DEF file contains layer \"%s\" which is" + " not allowed by the layer limit setting of %d\n", + token, Num_layers); continue; } paintLayer = routeLayer; @@ -191,7 +257,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) token = LefNextToken(f, TRUE); if (sscanf(token, "%lg", &w) != 1) { - LefError("Bad width in special net\n"); + LefError(DEF_ERROR, "Bad width in special net\n"); continue; } if (w != 0) @@ -224,7 +290,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) else if (valid == FALSE) { - LefError("Route has via name \"%s\" but no points!\n", token); + LefError(DEF_ERROR, "Route has via name \"%s\" but no points!\n", token); continue; } lefl = LefFindLayer(token); @@ -235,14 +301,35 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) if (lefl != NULL) { if (lefl->lefClass == CLASS_VIA) { + + // Note: layers may be defined in any order, metal or cut. + // Check both via.area and via.lr layers, and reject those + // that exceed the number of metal layers (those are cuts). + paintLayer = Num_layers - 1; routeLayer = -1; - lr = lefl->info.via.lr; - while (lr != NULL) { + if (lefl->info.via.area.layer < Num_layers) { + routeLayer = lefl->info.via.area.layer; + if (routeLayer < paintLayer) paintLayer = routeLayer; + if ((routeLayer >= 0) && (special == (char)1) && + (valid == TRUE) && (noobstruct == FALSE)) { + s = LefGetRouteSpacing(routeLayer); + drect = (DSEG)malloc(sizeof(struct dseg_)); + drect->x1 = x + (lefl->info.via.area.x1 / 2.0) - s; + drect->x2 = x + (lefl->info.via.area.x2 / 2.0) + s; + drect->y1 = y + (lefl->info.via.area.y1 / 2.0) - s; + drect->y2 = y + (lefl->info.via.area.y2 / 2.0) + s; + drect->layer = routeLayer; + drect->next = UserObs; + UserObs = drect; + } + } + for (lr = lefl->info.via.lr; lr; lr = lr->next) { + if (lr->layer >= Num_layers) continue; routeLayer = lr->layer; if (routeLayer < paintLayer) paintLayer = routeLayer; if ((routeLayer >= 0) && (special == (char)1) && - (valid == TRUE)) { + (valid == TRUE) && (noobstruct == FALSE)) { s = LefGetRouteSpacing(routeLayer); drect = (DSEG)malloc(sizeof(struct dseg_)); drect->x1 = x + (lr->x1 / 2.0) - s; @@ -253,7 +340,6 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) drect->next = UserObs; UserObs = drect; } - lr = lr->next; } if (routeLayer == -1) paintLayer = lefl->type; } @@ -265,11 +351,11 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) } else { - LefError("Error: Via \"%s\" named but undefined.\n", token); + LefError(DEF_ERROR, "Error: Via \"%s\" named but undefined.\n", token); paintLayer = routeLayer; } if ((special == (char)0) && (paintLayer >= 0) && - (paintLayer < Num_layers)) { + (paintLayer < (Num_layers - 1))) { newRoute = (SEG)malloc(sizeof(struct seg_)); newRoute->segtype = ST_VIA; @@ -294,14 +380,18 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) routednet->segments = newRoute; } else { - if (paintLayer >= Num_layers) - LefError("Via \"%s\" exceeds layer limit setting!\n", token); - else - LefError("Via \"%s\" does not define a metal layer!\n", token); + if (paintLayer >= (Num_layers - 1)) + /* Not necessarily an error to have predefined geometry */ + /* above the route layer limit. */ + LefError(DEF_WARNING, "Via \"%s\" exceeds layer " + "limit setting.\n", token); + else if (special == (char)0) + LefError(DEF_ERROR, "Via \"%s\" does not define a" + " metal layer!\n", token); } } else - LefError("Via name \"%s\" unknown in route.\n", token); + LefError(DEF_ERROR, "Via name \"%s\" unknown in route.\n", token); } else { @@ -320,18 +410,28 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) { if (valid == FALSE) { - LefError("No reference point for \"*\" wildcard\n"); + LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n"); goto endCoord; } } else if (sscanf(token, "%lg", &x) == 1) { x /= oscale; // In microns - refp.x1 = (int)((x - Xlowerbound + EPS) / PitchX[paintLayer]); + /* Note: offsets and stubs are always less than half a pitch, */ + /* so round to the nearest integer grid point. */ + refp.x1 = (int)(0.5 + ((x - Xlowerbound + EPS) / PitchX)); + + /* Flag offsets that are more than 1/3 track pitch, as they */ + /* need careful analyzing (in route_set_connections()) to */ + /* separate the main route from the stub route or offest. */ + if ((special == (char)0) && ABSDIFF((double)refp.x1, + (x - Xlowerbound) / PitchX) > 0.33) { + if (routednet) routednet->flags |= RT_CHECK; + } } else { - LefError("Cannot parse X coordinate.\n"); + LefError(DEF_ERROR, "Cannot parse X coordinate.\n"); goto endCoord; } token = LefNextToken(f, TRUE); /* read Y */ @@ -339,7 +439,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) { if (valid == FALSE) { - LefError("No reference point for \"*\" wildcard\n"); + LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n"); if (newRoute != NULL) { free(newRoute); newRoute = NULL; @@ -350,11 +450,16 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) else if (sscanf(token, "%lg", &y) == 1) { y /= oscale; // In microns - refp.y1 = (int)((y - Ylowerbound + EPS) / PitchY[paintLayer]); + refp.y1 = (int)(0.5 + ((y - Ylowerbound + EPS) / PitchY)); + + if ((special == (u_char)0) && ABSDIFF((double)refp.y1, + (y - Ylowerbound) / PitchY) > 0.33) { + if (routednet) routednet->flags |= RT_CHECK; + } } else { - LefError("Cannot parse Y coordinate.\n"); + LefError(DEF_ERROR, "Cannot parse Y coordinate.\n"); goto endCoord; } @@ -369,7 +474,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) /* Skip over nonmanhattan segments, reset the reference */ /* point, and output a warning. */ - LefError("Can't deal with nonmanhattan geometry in route.\n"); + LefError(DEF_ERROR, "Can't deal with nonmanhattan geometry in route.\n"); locarea.x1 = refp.x1; locarea.y1 = refp.y1; lx = x; @@ -380,8 +485,8 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) locarea.x2 = refp.x1; locarea.y2 = refp.y1; - if (special == (char)1) { - if (valid == TRUE) { + if (special != (char)0) { + if ((valid == TRUE) && (noobstruct == FALSE)) { s = LefGetRouteSpacing(routeLayer); hw = w / 2; drect = (DSEG)malloc(sizeof(struct dseg_)); @@ -417,10 +522,14 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) else if ((paintLayer >= 0) && (paintLayer < Num_layers)) { newRoute = (SEG)malloc(sizeof(struct seg_)); newRoute->segtype = ST_WIRE; - newRoute->x1 = locarea.x1; - newRoute->x2 = locarea.x2; - newRoute->y1 = locarea.y1; - newRoute->y2 = locarea.y2; + // NOTE: Segments are added at the front of the linked + // list, so they are backwards from the entry in the + // DEF file. Therefore the first and second coordinates + // must be swapped, or the segments become disjoint. + newRoute->x1 = locarea.x2; + newRoute->x2 = locarea.x1; + newRoute->y1 = locarea.y2; + newRoute->y2 = locarea.y1; newRoute->layer = paintLayer; if (routednet == NULL) { @@ -438,7 +547,7 @@ DefAddRoutes(FILE *f, float oscale, NET net, char special) routednet->segments = newRoute; } else if (paintLayer >= Num_layers) { - LefError("Route layer exceeds layer limit setting!\n"); + LefError(DEF_ERROR, "Route layer exceeds layer limit setting!\n"); } } @@ -447,14 +556,22 @@ endCoord: while (*token != ')') token = LefNextToken(f, TRUE); } - } - /* Make sure we have allocated memory for nets */ - allocate_obs_array(); - - /* Write the route(s) back into Obs[] */ - writeback_all_routes(net); + /* Remove routes that are less than 1 track long; these are stub */ + /* routes to terminals that did not require a specialnets entry. */ + + if (routednet && (net->routes == routednet) && (routednet->flags & RT_CHECK)) { + int ix, iy; + SEG seg; + seg = routednet->segments; + if (seg && seg->next == NULL) { + ix = ABSDIFF(seg->x1, seg->x2); + iy = ABSDIFF(seg->y1, seg->y2); + if ((ix == 0 && iy == 1) || (ix == 1 && iy == 0)) + remove_top_route(net); + } + } return token; /* Pass back the last token found */ } @@ -484,13 +601,13 @@ DefReadGatePin(NET net, NODE node, char *instname, char *pinname, double *home) int gridx, gridy; DPOINT dp; - g = DefFindInstance(instname); + g = DefFindGate(instname); if (g) { gateginfo = g->gatetype; if (!gateginfo) { - LefError("Endpoint %s/%s of net %s not found\n", + LefError(DEF_ERROR, "Endpoint %s/%s of net %s not found\n", instname, pinname, net->netname); return; } @@ -509,23 +626,21 @@ DefReadGatePin(NET net, NODE node, char *instname, char *pinname, double *home) // but not centered on gridpoints should be marked // in some way, and handled appropriately. - gridx = (int)((drect->x1 - Xlowerbound) / - PitchX[drect->layer]) - 1; + gridx = (int)((drect->x1 - Xlowerbound) / PitchX) - 1; if (gridx < 0) gridx = 0; while (1) { - dx = (gridx * PitchX[drect->layer]) + Xlowerbound; + dx = (gridx * PitchX) + Xlowerbound; if (dx > drect->x2 + home[drect->layer] - EPS) break; if (dx < drect->x1 - home[drect->layer] + EPS) { gridx++; continue; } - gridy = (int)((drect->y1 - Ylowerbound) / - PitchY[drect->layer]) - 1; + gridy = (int)((drect->y1 - Ylowerbound) / PitchY) - 1; if (gridy < 0) gridy = 0; while (1) { - dy = (gridy * PitchY[drect->layer]) + Ylowerbound; + dy = (gridy * PitchY) + Ylowerbound; if (dy > drect->y2 + home[drect->layer] - EPS) break; if (dy < drect->y1 - home[drect->layer] + EPS) { gridy++; @@ -579,7 +694,11 @@ DefReadGatePin(NET net, NODE node, char *instname, char *pinname, double *home) * Read a NETS or SPECIALNETS section from a DEF file. * * Results: - * None. + * Return the total number of fixed or cover nets, + * excluding power and ground nets. This gives the + * base number of nets to be copied verbatim from + * input to output (used only for SPECIALNETS, as + * regular nets are tracked with the NET_IGNORED flag). * * Side Effects: * Many. Networks are created, and geometry may be @@ -594,14 +713,16 @@ enum def_netprop_keys { DEF_NETPROP_COVER, DEF_NETPROP_SHAPE, DEF_NETPROP_SOURCE, DEF_NETPROP_WEIGHT, DEF_NETPROP_PROPERTY}; -static void +static int DefReadNets(FILE *f, char *sname, float oscale, char special, int total) { char *token; int keyword, subkey; int i, processed = 0; int nodeidx; + int fixed = 0; char instname[MAX_NAME_LEN], pinname[MAX_NAME_LEN]; + u_char is_new; NET net; int netidx; @@ -641,6 +762,7 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) } } else { + netidx = Numnets; Nlnets = (NET *)realloc(Nlnets, (Numnets + total) * sizeof(NET)); for (i = Numnets; i < (Numnets + total); i++) Nlnets[i] = NULL; } @@ -650,7 +772,7 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) keyword = Lookup(token, net_keys); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in NET " + LefError(DEF_WARNING, "Unknown keyword \"%s\" in NET " "definition; ignoring.\n", token); LefEndStatement(f); continue; @@ -662,31 +784,40 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) /* Get net name */ token = LefNextToken(f, TRUE); + net = DefFindNet(token); + + if (net == NULL) { + net = (NET)malloc(sizeof(struct net_)); + Nlnets[Numnets++] = net; + net->netorder = 0; + net->numnodes = 0; + net->flags = 0; + net->netname = strdup(token); + net->netnodes = (NODE)NULL; + net->noripup = (NETLIST)NULL; + net->routes = (ROUTE)NULL; + net->xmin = net->ymin = 0; + net->xmax = net->ymax = 0; + + // Net numbers start at MIN_NET_NUMBER for regular nets, + // use VDD_NET and GND_NET for power and ground, and 0 + // is not a valid net number. + + if (vddnet && !strcmp(token, vddnet)) + net->netnum = VDD_NET; + else if (gndnet && !strcmp(token, gndnet)) + net->netnum = GND_NET; + else + net->netnum = netidx++; + DefHashNet(net); - net = (NET)malloc(sizeof(struct net_)); - Nlnets[Numnets++] = net; - net->netorder = 0; - net->numnodes = 0; - net->flags = 0; - net->netname = strdup(token); - net->netnodes = (NODE)NULL; - net->noripup = (NETLIST)NULL; - net->routes = (ROUTE)NULL; - net->xmin = net->ymin = 0; - net->xmax = net->ymax = 0; - - // Net numbers start at MIN_NET_NUMBER for regular nets, - // use VDD_NET and GND_NET for power and ground, and 0 - // is not a valid net number. - - if (vddnet && !strcmp(token, vddnet)) - net->netnum = VDD_NET; - else if (gndnet && !strcmp(token, gndnet)) - net->netnum = GND_NET; - else - net->netnum = netidx++; - - nodeidx = 0; + nodeidx = 0; + is_new = TRUE; + } + else { + nodeidx = net->numnodes; + is_new = FALSE; + } /* Update the record of the number of nets processed */ /* and spit out a message for every 5% finished. */ @@ -732,7 +863,7 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) subkey = Lookup(token, net_property_keys); if (subkey < 0) { - LefError("Unknown net property \"%s\" in " + LefError(DEF_WARNING, "Unknown net property \"%s\" in " "NET definition; ignoring.\n", token); continue; } @@ -745,24 +876,29 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) /* Ignore this too, along with the next keyword */ token = LefNextToken(f, TRUE); break; + case DEF_NETPROP_FIXED: + case DEF_NETPROP_COVER: + /* Read in fixed nets like regular nets but mark + * them as NET_IGNORED. HOWEVER, if the net + * already exists and is not marked NET_IGNORED, + * then don't force it to be ignored. That is + * particularly an issue for a net like power or + * ground, which may need to be routed like a + * regular net but also has fixed portions. */ + if (is_new) { + net->flags |= NET_IGNORED; + fixed++; + } + // fall through case DEF_NETPROP_ROUTED: // Read in the route; qrouter now takes // responsibility for this route. while (token && (*token != ';')) token = DefAddRoutes(f, oscale, net, special); - break; - case DEF_NETPROP_FIXED: - case DEF_NETPROP_COVER: - // Treat fixed nets like specialnets: read them - // in as obstructions, and write them out as-is. - // Use special = 2 so it is treated like a - // specialnet but does not expect the specialnet - // syntax (unless it is, in fact, a specialnet - // entry). - - while (token && (*token != ';')) - token = DefAddRoutes(f, oscale, net, - (special == (char)0) ? (char)2 : special); + // Treat power and ground nets in specialnets as fixed + if (subkey == DEF_NETPROP_ROUTED && special == (char)1) + if (net->netnum == VDD_NET || net->netnum == GND_NET) + fixed++; break; } } @@ -771,7 +907,7 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) case DEF_NET_END: if (!LefParseEndStatement(f, sname)) { - LefError("Net END statement missing.\n"); + LefError(DEF_ERROR, "Net END statement missing.\n"); keyword = -1; } break; @@ -801,8 +937,9 @@ DefReadNets(FILE *f, char *sname, float oscale, char special, int total) (special) ? " special" : ""); } else - LefError("Warning: Number of nets read (%d) does not match " + LefError(DEF_WARNING, "Warning: Number of nets read (%d) does not match " "the number declared (%d).\n", processed, total); + return fixed; } /* @@ -853,7 +990,7 @@ DefReadLocation(gate, f, oscale) keyword = Lookup(token, orientations); if (keyword < 0) { - LefError("Unknown macro orientation \"%s\".\n", token); + LefError(DEF_ERROR, "Unknown macro orientation \"%s\".\n", token); return -1; } @@ -877,7 +1014,7 @@ DefReadLocation(gate, f, oscale) case DEF_WEST: case DEF_FLIPPED_EAST: case DEF_FLIPPED_WEST: - LefError("Error: Cannot handle 90-degree rotated components!\n"); + LefError(DEF_ERROR, "Error: Cannot handle 90-degree rotated components!\n"); break; } @@ -891,7 +1028,7 @@ DefReadLocation(gate, f, oscale) return 0; parse_error: - LefError("Cannot parse location: must be ( X Y ) orient\n"); + LefError(DEF_ERROR, "Cannot parse location: must be ( X Y ) orient\n"); return -1; } @@ -979,7 +1116,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) if (keyword < 0) { - LefError("Unknown keyword \"%s\" in PINS " + LefError(DEF_WARNING, "Unknown keyword \"%s\" in PINS " "definition; ignoring.\n", token); LefEndStatement(f); continue; @@ -997,7 +1134,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) token = LefNextToken(f, TRUE); if (sscanf(token, "%2047s", pinname) != 1) { - LefError("Bad pin statement: Need pin name\n"); + LefError(DEF_ERROR, "Bad pin statement: Need pin name\n"); LefEndStatement(f); break; } @@ -1014,6 +1151,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) gate->taps = (DSEG *)malloc(sizeof(DSEG)); gate->noderec = (NODE *)malloc(sizeof(NODE)); gate->direction = (u_char *)malloc(sizeof(u_char)); + gate->area = (float *)malloc(sizeof(float)); gate->netnum = (int *)malloc(sizeof(int)); gate->node = (char **)malloc(sizeof(char *)); gate->taps[0] = NULL; @@ -1021,6 +1159,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) gate->netnum[0] = -1; gate->node[0] = NULL; gate->direction[0] = PORT_CLASS_DEFAULT; + gate->area[0] = 0.0; /* Now do a search through the line for "+" entries */ /* And process each. */ @@ -1034,7 +1173,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) subkey = Lookup(token, pin_property_keys); if (subkey < 0) { - LefError("Unknown pin property \"%s\" in " + LefError(DEF_WARNING, "Unknown pin property \"%s\" in " "PINS definition; ignoring.\n", token); continue; } @@ -1050,21 +1189,24 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) token = LefNextToken(f, TRUE); subkey = Lookup(token, pin_classes); if (subkey < 0) - LefError("Unknown pin class %s\n", token); + LefError(DEF_ERROR, "Unknown pin class %s\n", token); else gate->direction[0] = subkey; break; case DEF_PINS_PROP_LAYER: curlayer = LefReadLayer(f, FALSE); currect = LefReadRect(f, curlayer, oscale); - gate->width = currect->x2 - currect->x1; - gate->height = currect->y2 - currect->y1; + /* Warn if pin is on layer above routing layer limit? */ + if (currect) { + gate->width = currect->x2 - currect->x1; + gate->height = currect->y2 - currect->y1; + } break; case DEF_PINS_PROP_USE: token = LefNextToken(f, TRUE); subkey = Lookup(token, pin_uses); if (subkey < 0) - LefError("Unknown pin use %s\n", token); + LefError(DEF_ERROR, "Unknown pin use %s\n", token); else pin_use = subkey; break; @@ -1105,8 +1247,8 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) DefHashInstance(gate); } else { - LefError("Pin %s is defined outside of route layer area!\n", - pinname); + LefError(DEF_ERROR, "Pin %s is defined outside of route " + "layer area!\n", pinname); free(gate->taps); free(gate->noderec); free(gate->direction); @@ -1120,7 +1262,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) case DEF_PINS_END: if (!LefParseEndStatement(f, sname)) { - LefError("Pins END statement missing.\n"); + LefError(DEF_ERROR, "Pins END statement missing.\n"); keyword = -1; } if (pin_use != PORT_USE_DEFAULT && gate->direction[0] == @@ -1153,7 +1295,7 @@ DefReadPins(FILE *f, char *sname, float oscale, int total) Fprintf(stdout, " Processed %d pins total.\n", processed); } else - LefError("Warning: Number of pins read (%d) does not match " + LefError(DEF_WARNING, "Warning: Number of pins read (%d) does not match " "the number declared (%d).\n", processed, total); } @@ -1210,7 +1352,7 @@ DefReadVias(f, sname, oscale, total) if (keyword < 0) { - LefError("Unknown keyword \"%s\" in VIAS " + LefError(DEF_WARNING, "Unknown keyword \"%s\" in VIAS " "definition; ignoring.\n", token); LefEndStatement(f); continue; @@ -1228,7 +1370,7 @@ DefReadVias(f, sname, oscale, total) token = LefNextToken(f, TRUE); if (sscanf(token, "%2047s", vianame) != 1) { - LefError("Bad via statement: Need via name\n"); + LefError(DEF_ERROR, "Bad via statement: Need via name\n"); LefEndStatement(f); break; } @@ -1246,6 +1388,10 @@ DefReadVias(f, sname, oscale, total) lefl->info.via.area.layer = -1; lefl->info.via.cell = (GATE)NULL; lefl->info.via.lr = (DSEG)NULL; + /* Note: "generated" flag only refers to vias that */ + /* are internally generated by qrouter. All others */ + /* in the DEF file are read/written verbatim. */ + lefl->info.via.generated = FALSE; lefl->lefName = strdup(token); lefl->next = LefInfo; @@ -1253,7 +1399,8 @@ DefReadVias(f, sname, oscale, total) } else { - LefError("Warning: Composite via \"%s\" redefined.\n", vianame); + LefError(DEF_WARNING, "Warning: Composite via \"%s\" " + "redefined.\n", vianame); lefl = LefRedefined(lefl, vianame); } @@ -1269,7 +1416,7 @@ DefReadVias(f, sname, oscale, total) subkey = Lookup(token, via_property_keys); if (subkey < 0) { - LefError("Unknown via property \"%s\" in " + LefError(DEF_WARNING, "Unknown via property \"%s\" in " "VIAS definition; ignoring.\n", token); continue; } @@ -1286,7 +1433,7 @@ DefReadVias(f, sname, oscale, total) case DEF_VIAS_END: if (!LefParseEndStatement(f, sname)) { - LefError("Vias END statement missing.\n"); + LefError(DEF_ERROR, "Vias END statement missing.\n"); keyword = -1; } break; @@ -1299,7 +1446,7 @@ DefReadVias(f, sname, oscale, total) Fprintf(stdout, " Processed %d vias total.\n", processed); } else - LefError("Warning: Number of vias read (%d) does not match " + LefError(DEF_WARNING, "Warning: Number of vias read (%d) does not match " "the number declared (%d).\n", processed, total); } @@ -1343,7 +1490,7 @@ DefReadBlockages(FILE *f, char *sname, float oscale, int total) if (keyword < 0) { - LefError("Unknown keyword \"%s\" in BLOCKAGE " + LefError(DEF_WARNING, "Unknown keyword \"%s\" in BLOCKAGE " "definition; ignoring.\n", token); LefEndStatement(f); continue; @@ -1372,7 +1519,7 @@ DefReadBlockages(FILE *f, char *sname, float oscale, int total) } else { - LefError("Bad blockage statement: Need layer name\n"); + LefError(DEF_ERROR, "Bad blockage statement: Need layer name\n"); LefEndStatement(f); break; } @@ -1381,7 +1528,7 @@ DefReadBlockages(FILE *f, char *sname, float oscale, int total) case DEF_BLOCK_END: if (!LefParseEndStatement(f, sname)) { - LefError("Blockage END statement missing.\n"); + LefError(DEF_ERROR, "Blockage END statement missing.\n"); keyword = -1; } break; @@ -1394,7 +1541,7 @@ DefReadBlockages(FILE *f, char *sname, float oscale, int total) Fprintf(stdout, " Processed %d blockages total.\n", processed); } else - LefError("Warning: Number of blockages read (%d) does not match " + LefError(DEF_WARNING, "Warning: Number of blockages read (%d) does not match " "the number declared (%d).\n", processed, total); } @@ -1406,7 +1553,7 @@ DefReadBlockages(FILE *f, char *sname, float oscale, int total) * Read a COMPONENTS section from a DEF file. * * Results: - * None. + * 0 on success, 1 on fatal error. * * Side Effects: * Many. Cell instances are created and added to @@ -1423,7 +1570,7 @@ enum def_prop_keys { DEF_PROP_REGION, DEF_PROP_GENERATE, DEF_PROP_PROPERTY, DEF_PROP_EEQMASTER}; -static void +static int DefReadComponents(FILE *f, char *sname, float oscale, int total) { GATE gateginfo; @@ -1435,6 +1582,7 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) char OK; DSEG drect, newrect; double tmp; + int err_fatal = 0; static char *component_keys[] = { "-", @@ -1463,7 +1611,7 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) if (keyword < 0) { - LefError("Unknown keyword \"%s\" in COMPONENT " + LefError(DEF_WARNING, "Unknown keyword \"%s\" in COMPONENT " "definition; ignoring.\n", token); LefEndStatement(f); continue; @@ -1481,8 +1629,10 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) token = LefNextToken(f, TRUE); if (sscanf(token, "%511s", usename) != 1) { - LefError("Bad component statement: Need use and macro names\n"); + LefError(DEF_ERROR, "Bad component statement: Need use " + "and macro names\n"); LefEndStatement(f); + err_fatal++; break; } token = LefNextToken(f, TRUE); @@ -1496,9 +1646,10 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) } } if (!OK) { - LefError("Could not find a macro definition for \"%s\"\n", + LefError(DEF_ERROR, "Could not find a macro definition for \"%s\"\n", token); gate = NULL; + err_fatal++; } else { gate = (GATE)malloc(sizeof(struct gate_)); @@ -1518,7 +1669,7 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) subkey = Lookup(token, property_keys); if (subkey < 0) { - LefError("Unknown component property \"%s\" in " + LefError(DEF_WARNING, "Unknown component property \"%s\" in " "COMPONENT definition; ignoring.\n", token); continue; } @@ -1553,6 +1704,7 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) gate->taps = (DSEG *)malloc(gate->nodes * sizeof(DSEG)); gate->noderec = (NODE *)malloc(gate->nodes * sizeof(NODE)); gate->direction = (u_char *)malloc(gate->nodes * sizeof(u_char)); + gate->area = (float *)malloc(gate->nodes * sizeof(float)); gate->netnum = (int *)malloc(gate->nodes * sizeof(int)); gate->node = (char **)malloc(gate->nodes * sizeof(char *)); @@ -1565,16 +1717,19 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) gate->node[i] = gateginfo->node[i]; /* copy pointer */ gate->direction[i] = gateginfo->direction[i]; /* copy */ + gate->area[i] = gateginfo->area[i]; gate->taps[i] = (DSEG)NULL; /* Global power/ground bus check */ - if (vddnet && !strcmp(gate->node[i], vddnet)) { + if (vddnet && gate->node[i] && + !strcmp(gate->node[i], vddnet)) { /* Create a placeholder node with no taps */ gate->netnum[i] = VDD_NET; gate->noderec[i] = (NODE)calloc(1, sizeof(struct node_)); gate->noderec[i]->netnum = VDD_NET; } - else if (gndnet && !strcmp(gate->node[i], gndnet)) { + else if (gndnet && gate->node[i] && + !strcmp(gate->node[i], gndnet)) { /* Create a placeholder node with no taps */ gate->netnum[i] = GND_NET; gate->noderec[i] = (NODE)calloc(1, sizeof(struct node_)); @@ -1586,13 +1741,15 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) } /* Make a copy of the gate nodes and adjust for */ - /* instance position */ + /* instance position and number of layers */ for (drect = gateginfo->taps[i]; drect; drect = drect->next) { - newrect = (DSEG)malloc(sizeof(struct dseg_)); - *newrect = *drect; - newrect->next = gate->taps[i]; - gate->taps[i] = newrect; + if (drect->layer < Num_layers) { + newrect = (DSEG)malloc(sizeof(struct dseg_)); + *newrect = *drect; + newrect->next = gate->taps[i]; + gate->taps[i] = newrect; + } } for (drect = gate->taps[i]; drect; drect = drect->next) { @@ -1631,10 +1788,12 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) /* Make a copy of the gate obstructions and adjust */ /* for instance position */ for (drect = gateginfo->obs; drect; drect = drect->next) { - newrect = (DSEG)malloc(sizeof(struct dseg_)); - *newrect = *drect; - newrect->next = gate->obs; - gate->obs = newrect; + if (drect->layer < Num_layers) { + newrect = (DSEG)malloc(sizeof(struct dseg_)); + *newrect = *drect; + newrect->next = gate->obs; + gate->obs = newrect; + } } for (drect = gate->obs; drect; drect = drect->next) { @@ -1678,8 +1837,9 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) case DEF_COMP_END: if (!LefParseEndStatement(f, sname)) { - LefError("Component END statement missing.\n"); + LefError(DEF_ERROR, "Component END statement missing.\n"); keyword = -1; + err_fatal++; } /* Finish final call by placing the cell use */ @@ -1698,8 +1858,9 @@ DefReadComponents(FILE *f, char *sname, float oscale, int total) Fprintf(stdout, " Processed %d subcell instances total.\n", processed); } else - LefError("Warning: Number of subcells read (%d) does not match " + LefError(DEF_WARNING, "Warning: Number of subcells read (%d) does not match " "the number declared (%d).\n", processed, total); + return err_fatal; } /* @@ -1732,18 +1893,19 @@ enum def_sections {DEF_VERSION = 0, DEF_NAMESCASESENSITIVE, DEF_CONSTRAINTS, DEF_GROUPS, DEF_EXTENSION, DEF_END}; -float -DefRead(char *inName) +int +DefRead(char *inName, float *retscale) { FILE *f; char filename[256]; char *token; int keyword, dscale, total; int curlayer = -1, channels; - int v, h, i; + int i; + int err_fatal = 0; float oscale; double start, step; - double llx, lly, urx, ury; + double llx, lly, urx, ury, locpitch; double dXlowerbound, dYlowerbound, dXupperbound, dYupperbound; char corient = '.'; DSEG diearea; @@ -1791,7 +1953,8 @@ DefRead(char *inName) { Fprintf(stderr, "Cannot open input file: "); perror(filename); - return (float)0.0; + *retscale = (float)0.0; + return 1; } /* Initialize */ @@ -1803,7 +1966,6 @@ DefRead(char *inName) oscale = 1; lefCurrentLine = 0; - v = h = -1; DefHashInit(); @@ -1814,47 +1976,12 @@ DefRead(char *inName) keyword = Lookup(token, sections); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in DEF file; ignoring.\n", token); + LefError(DEF_WARNING, "Unknown keyword \"%s\" in DEF file; " + "ignoring.\n", token); LefEndStatement(f); continue; } - - /* After the TRACKS have been read in, corient is 'x' or 'y'. */ - /* On the next keyword, finish filling in track information. */ - - if (keyword != DEF_TRACKS && corient != '.') - { - /* Because the TRACKS statement only covers the pitch of */ - /* a single direction, we need to fill in with the pitch */ - /* of opposing layers. For now, we expect all horizontal */ - /* routes to be at the same pitch, and all vertical routes */ - /* to be at the same pitch. */ - - if (h == -1) h = v; - if (v == -1) v = h; - - /* This code copied from qconfig.c. Preferably, all */ - /* information available in the DEF file should be taken */ - /* from the DEF file. */ - - for (i = 0; i < Num_layers; i++) - { - if (PitchX[i] != 0.0 && PitchX[i] != PitchX[v] && Verbose > 0) - Fprintf(stderr, "Multiple vertical route layers at different" - " pitches. Using pitch %g and routing on 1-of-N" - " tracks for larger pitches.\n", - PitchX[v]); - PitchX[i] = PitchX[v]; - if (PitchY[i] != 0.0 && PitchY[i] != PitchY[h] && Verbose > 0) - Fprintf(stderr, "Multiple horizontal route layers at different" - " pitches. Using pitch %g and routing on 1-of-N" - " tracks for larger pitches.\n", - PitchY[h]); - PitchY[i] = PitchY[h]; - - corient = '.'; // So we don't run this code again. - } - } + if (keyword != DEF_TRACKS) corient = '.'; switch (keyword) { @@ -1886,8 +2013,8 @@ DefRead(char *inName) token = LefNextToken(f, TRUE); if (sscanf(token, "%d", &dscale) != 1) { - LefError("Invalid syntax for UNITS statement.\n"); - LefError("Assuming default value of 100\n"); + LefError(DEF_ERROR, "Invalid syntax for UNITS statement.\n"); + LefError(DEF_WARNING, "Assuming default value of 100\n"); dscale = 100; } /* We don't care if the scale is 100, 200, 1000, or 2000. */ @@ -1901,28 +2028,33 @@ DefRead(char *inName) case DEF_TRACKS: token = LefNextToken(f, TRUE); if (strlen(token) != 1) { - LefError("Problem parsing track orientation (X or Y).\n"); + LefError(DEF_ERROR, "Problem parsing track orientation (X or Y).\n"); } corient = tolower(token[0]); // X or Y token = LefNextToken(f, TRUE); if (sscanf(token, "%lg", &start) != 1) { - LefError("Problem parsing track start position.\n"); + LefError(DEF_ERROR, "Problem parsing track start position.\n"); + err_fatal++; } token = LefNextToken(f, TRUE); if (strcmp(token, "DO")) { - LefError("TRACKS missing DO loop.\n"); + LefError(DEF_ERROR, "TRACKS missing DO loop.\n"); + err_fatal++; } token = LefNextToken(f, TRUE); if (sscanf(token, "%d", &channels) != 1) { - LefError("Problem parsing number of track channels.\n"); + LefError(DEF_ERROR, "Problem parsing number of track channels.\n"); + err_fatal++; } token = LefNextToken(f, TRUE); if (strcmp(token, "STEP")) { - LefError("TRACKS missing STEP size.\n"); + LefError(DEF_ERROR, "TRACKS missing STEP size.\n"); + err_fatal++; } token = LefNextToken(f, TRUE); if (sscanf(token, "%lg", &step) != 1) { - LefError("Problem parsing track step size.\n"); + LefError(DEF_ERROR, "Problem parsing track step size.\n"); + err_fatal++; } token = LefNextToken(f, TRUE); if (!strcmp(token, "LAYER")) { @@ -1930,10 +2062,9 @@ DefRead(char *inName) } if (corient == 'x') { Vert[curlayer] = 1; - PitchX[curlayer] = step / oscale; - if ((v == -1) || (PitchX[curlayer] < PitchX[v])) v = curlayer; - if ((curlayer < Num_layers - 1) && PitchX[curlayer + 1] == 0.0) - PitchX[curlayer + 1] = PitchX[curlayer]; + locpitch = step / oscale; + if ((PitchX == 0.0) || (locpitch < PitchX)) + PitchX = locpitch; llx = start; urx = start + step * channels; if ((llx / oscale) < Xlowerbound) @@ -1943,10 +2074,9 @@ DefRead(char *inName) } else { Vert[curlayer] = 0; - PitchY[curlayer] = step / oscale; - if ((h == -1) || (PitchY[curlayer] < PitchX[h])) h = curlayer; - if ((curlayer < Num_layers - 1) && PitchY[curlayer + 1] == 0.0) - PitchY[curlayer + 1] = PitchY[curlayer]; + locpitch = step / oscale; + if ((PitchY == 0.0) || (locpitch < PitchY)) + PitchY = locpitch; lly = start; ury = start + step * channels; if ((lly / oscale) < Ylowerbound) @@ -1991,7 +2121,7 @@ DefRead(char *inName) token = LefNextToken(f, TRUE); if (sscanf(token, "%d", &total) != 1) total = 0; LefEndStatement(f); - DefReadComponents(f, sections[DEF_COMPONENTS], oscale, total); + err_fatal += DefReadComponents(f, sections[DEF_COMPONENTS], oscale, total); break; case DEF_BLOCKAGES: token = LefNextToken(f, TRUE); @@ -2018,16 +2148,16 @@ DefRead(char *inName) token = LefNextToken(f, TRUE); if (sscanf(token, "%d", &total) != 1) total = 0; LefEndStatement(f); - DefReadNets(f, sections[DEF_SPECIALNETS], oscale, TRUE, total); - numSpecial = total; + numSpecial = DefReadNets(f, sections[DEF_SPECIALNETS], oscale, TRUE, + total); break; case DEF_NETS: token = LefNextToken(f, TRUE); if (sscanf(token, "%d", &total) != 1) total = 0; LefEndStatement(f); if (total > MAX_NETNUMS) { - LefError("Number of nets in design (%d) exceeds maximum (%d)\n", - total, MAX_NETNUMS); + LefError(DEF_WARNING, "Number of nets in design (%d) exceeds " + "maximum (%d)\n", total, MAX_NETNUMS); } DefReadNets(f, sections[DEF_NETS], oscale, FALSE, total); break; @@ -2049,7 +2179,7 @@ DefRead(char *inName) case DEF_END: if (!LefParseEndStatement(f, "DESIGN")) { - LefError("END statement out of context.\n"); + LefError(DEF_ERROR, "END statement out of context.\n"); keyword = -1; } break; @@ -2058,7 +2188,7 @@ DefRead(char *inName) } if (Verbose > 0) Fprintf(stdout, "DEF read: Processed %d lines.\n", lefCurrentLine); - LefError(NULL); /* print statement of errors, if any, and reset */ + LefError(DEF_ERROR, NULL); /* print statement of errors, if any, and reset */ /* If there were no TRACKS statements, then use the DIEAREA */ if (Xlowerbound == Xupperbound) { @@ -2073,5 +2203,6 @@ DefRead(char *inName) /* Cleanup */ if (f != NULL) fclose(f); - return oscale; + *retscale = oscale; + return err_fatal; } @@ -9,6 +9,9 @@ #define _DEFINT_H extern int numSpecial; -extern float DefRead(char *inName); +extern int DefRead(char *inName, float *); + +extern GATE DefFindGate(char *name); +extern NET DefFindNet(char *name); #endif /* _DEFINT_H */ @@ -26,16 +26,6 @@ /* Find a node in the node list. */ /*--------------------------------------------------------------*/ -/* Define record holding information pointing to a gate and the */ -/* index into a specific node of that gate. */ - -typedef struct gatenode_ *GATENODE; - -struct gatenode_ { - GATE gate; - int idx; -}; - GATE FindGateNode(Tcl_HashTable *NodeTable, NODE node, int *ridx) { @@ -62,13 +52,11 @@ typedef struct _endpointinfo { ROUTE orig; /* original pointer to routed segments */ int startx; /* values at segment start */ int starty; - int startl; - u_char starttype; + int startl; /* note: layer is exact, not base via layer */ NODE startnode; int endx; /* values at segment end */ int endy; int endl; - u_char endtype; NODE endnode; double res; /* total resistance of segment */ double cap; /* total capacitance of segment */ @@ -143,39 +131,17 @@ check_downstream(SEG walkseg, endpointinfo *eptinfo, int eidx, /* Check wire/via layer compatibility */ - if (eptinfo[i].starttype & ST_WIRE) { - if (walkseg->segtype & ST_WIRE) - startcompat = (walkseg->layer == eptinfo[i].startl); - else - startcompat = (walkseg->layer == eptinfo[i].startl) || - (walkseg->layer + 1 == eptinfo[i].startl); - } - else { - if (walkseg->segtype & ST_WIRE) - startcompat = (walkseg->layer == eptinfo[i].startl) || - (walkseg->layer == eptinfo[i].startl + 1); - else - startcompat = (walkseg->layer == eptinfo[i].startl) || - (walkseg->layer == eptinfo[i].startl + 1) || + if (walkseg->segtype & ST_WIRE) + startcompat = (walkseg->layer == eptinfo[i].startl); + else + startcompat = (walkseg->layer == eptinfo[i].startl) || (walkseg->layer + 1 == eptinfo[i].startl); - } - if (eptinfo[i].endtype & ST_WIRE) { - if (walkseg->segtype & ST_WIRE) - endcompat = (walkseg->layer == eptinfo[i].endl); - else - endcompat = (walkseg->layer == eptinfo[i].endl) || - (walkseg->layer + 1 == eptinfo[i].endl); - } - else { - if (walkseg->segtype & ST_WIRE) - endcompat = (walkseg->layer == eptinfo[i].endl) || - (walkseg->layer == eptinfo[i].endl + 1); - else - endcompat = (walkseg->layer == eptinfo[i].endl) || - (walkseg->layer == eptinfo[i].endl + 1) || + if (walkseg->segtype & ST_WIRE) + endcompat = (walkseg->layer == eptinfo[i].endl); + else + endcompat = (walkseg->layer == eptinfo[i].endl) || (walkseg->layer + 1 == eptinfo[i].endl); - } if ((walkseg->x2 == eptinfo[i].startx) && (walkseg->y2 == eptinfo[i].starty) && startcompat) { @@ -258,7 +224,6 @@ walk_route(int eidx, int driverend, endpointinfo *eptinfo, GATE g; NODE node; int i; - u_char f; ROUTE rt; eptinfo[eidx].flags |= EPT_VISITED; @@ -312,10 +277,6 @@ walk_route(int eidx, int driverend, endpointinfo *eptinfo, i = eptinfo[eidx].startl; eptinfo[eidx].startl = eptinfo[eidx].endl; eptinfo[eidx].endl = i; - - f = eptinfo[eidx].starttype; - eptinfo[eidx].starttype = eptinfo[eidx].endtype; - eptinfo[eidx].endtype = f; } else firstseg = rt->segments; @@ -396,7 +357,10 @@ walk_route_output(endpointinfo *eptinfo, int eidx, fprintf(delayFile, "%s/%s ", g->gatename, g->gatetype->node[i]); if (d > 0) fprintf(delayFile, ", "); } - + else if (d == 0) { + /* This should not happen: No node, no route */ + fprintf(delayFile, "ERROR "); + } /* Output downstream nodes */ for (i = 0; i < d; i++) { @@ -410,6 +374,24 @@ walk_route_output(endpointinfo *eptinfo, int eidx, } /*--------------------------------------------------------------*/ +/* Free data associated with each entry in the NodeTable hash. */ +/*--------------------------------------------------------------*/ + +int FreeNodeTable(Tcl_HashTable *NodeTable) +{ + Tcl_HashEntry *entry; + Tcl_HashSearch hs; + GATENODE gn; + + entry = Tcl_FirstHashEntry(NodeTable, &hs); + while (entry != NULL) { + gn = Tcl_GetHashValue(entry); + if (gn != NULL) free(gn); + entry = Tcl_NextHashEntry(&hs); + } +} + +/*--------------------------------------------------------------*/ /* Write an output file of the calculated R, C for every route */ /* branch. Because the qrouter algorithm is agnostic about the */ /* direction of the signaling of routes, this has to be */ @@ -431,7 +413,7 @@ int write_delays(char *filename) NODEINFO nodeptr; SEG seg, newseg, lastseg, nxseg; GATE g, drivergate; - int i, j, n, new, driverend; + int i, j, n, new, driverend, testl; int drivernodeidx, driveridx; int nroute, numroutes; endpointinfo *eptinfo; @@ -479,11 +461,11 @@ int write_delays(char *filename) LefGetRouteRCvalues(i, &areacap, &edgecap, &respersq); width = LefGetRouteWidth(i); - lefrcvalues[i].resx = (PitchX[i] / width) * respersq; - lefrcvalues[i].resy = (PitchY[i] / width) * respersq; + lefrcvalues[i].resx = (PitchX / width) * respersq; + lefrcvalues[i].resy = (PitchY / width) * respersq; - lefrcvalues[i].capx = (PitchX[i] * width) * areacap + (PitchX[i] * edgecap); - lefrcvalues[i].capy = (PitchY[i] * width) * areacap + (PitchY[i] * edgecap); + lefrcvalues[i].capx = (PitchX * width) * areacap + (PitchX * edgecap); + lefrcvalues[i].capy = (PitchY * width) * areacap + (PitchY * edgecap); if (i < (Num_layers - 1)) LefGetViaResistance(i, &(lefrcvalues[i].viares)); @@ -496,16 +478,14 @@ int write_delays(char *filename) for (n = 0; n < Numnets; n++) { net = Nlnets[n]; - if ((net->netnum == VDD_NET) || (net->netnum == GND_NET)) continue; + if ((net->netnum == VDD_NET) || (net->netnum == GND_NET) || + (net->netnum == ANTENNA_NET)) continue; /* Count number of net routes */ numroutes = 0; for (rt = net->routes; rt; rt = rt->next) numroutes++; if (numroutes == 0) continue; /* Ignore nets with no routes */ - /* Marked as one driver node. Not handling more than one driver yet. */ - fprintf(delayFile, "%s 1", net->netname); - /* Determine the driver node, as determined by the node with */ /* LEF direction 'OUTPUT'. */ /* (For now, if a net has multiple tristate drivers, just use */ @@ -530,18 +510,31 @@ int write_delays(char *filename) eptinfo[nroute].startx = seg->x1; eptinfo[nroute].starty = seg->y1; eptinfo[nroute].startl = seg->layer; - eptinfo[nroute].starttype = seg->segtype; eptinfo[nroute].res = 0.0; eptinfo[nroute].cap = 0.0; + + /* If a via, check if direction is up or down */ + if (seg->segtype & ST_VIA) { + if (seg->next && (seg->next->layer <= seg->layer)) + eptinfo[nroute].startl++; + } } /* Segment end */ - for (seg = rt->segments; seg && seg->next; seg = seg->next); + testl = eptinfo[nroute].startl; + for (seg = rt->segments; seg && seg->next; seg = seg->next) + testl = seg->layer; + if (seg != NULL) { eptinfo[nroute].endx = seg->x2; eptinfo[nroute].endy = seg->y2; eptinfo[nroute].endl = seg->layer; - eptinfo[nroute].endtype = seg->segtype; + + /* If a via, check if direction is up or down */ + if (seg->segtype & ST_VIA) { + if (testl <= seg->layer) + eptinfo[nroute].endl++; + } } nroute++; } @@ -592,19 +585,23 @@ int write_delays(char *filename) j = 0; for (rt = droutes; rt; rt = rt->next) { ROUTE testroute; - int startx, starty, startl, starttype; - int endx, endy, endl, endtype; - int brkx, brky, brki, startcompat, endcompat; + int startx, starty, startl; + int endx, endy, endl; + int brkx, brky, brkl, brki, startcompat, endcompat; int initial, final; int x1, y1, x2, y2; /* Check all segments (but not the endpoints) */ - for (seg = rt->segments; seg; seg = seg->next) { + lastseg = NULL; + for (seg = rt->segments; seg; lastseg = seg, seg = seg->next) { initial = (seg == rt->segments) ? 1 : 0; final = (seg->next == NULL) ? 1 : 0; if (initial && (seg->segtype & ST_VIA)) continue; - if (final && (seg->segtype & ST_VIA)) continue; + if (final && (seg->segtype & ST_VIA) && + ((lastseg != rt->segments) || + (!(rt->segments->segtype & ST_VIA)))) + continue; x1 = seg->x1; x2 = seg->x2; @@ -635,8 +632,10 @@ int write_delays(char *filename) x2++; else if (x1 < x2) x2--; - else - continue; /* shouldn't happen */ + /* x1 == x2 here implies that the route is made of */ + /* exactly two vias. To continue would be to miss */ + /* the center point between the vias. This unique */ + /* condition is type == ST_VIA, final == TRUE. */ } else { if (y1 > y2) @@ -652,7 +651,6 @@ int write_delays(char *filename) brki = -1; for (i = 0; i < numroutes; i++) { if (eptinfo[i].route == rt) continue; - if (eptinfo[i].endl == -2) continue; testroute = eptinfo[i].orig; if ((!(testroute->flags & RT_START_NODE)) && @@ -670,42 +668,33 @@ int write_delays(char *filename) startx = eptinfo[i].startx; starty = eptinfo[i].starty; startl = eptinfo[i].startl; - starttype = eptinfo[i].starttype; endx = eptinfo[i].endx; endy = eptinfo[i].endy; endl = eptinfo[i].endl; - endtype = eptinfo[i].endtype; /* Check various combinations of wire and via layers */ + if (seg->segtype & ST_WIRE) { - if (starttype & ST_WIRE) + startcompat = (startl == seg->layer); + endcompat = (endl == seg->layer); + } + else if (final) { + /* Unique condition: route is two vias. Look */ + /* only at the point between the vias. */ + if (lastseg->layer < seg->layer) { startcompat = (startl == seg->layer); - else - startcompat = (startl == seg->layer) - || (startl + 1 == seg->layer); - - if (endtype & ST_WIRE) endcompat = (endl == seg->layer); - else - endcompat = (endl == seg->layer) - || (endl + 1 == seg->layer); + } + else { + startcompat = (startl == seg->layer + 1); + endcompat = (endl == seg->layer + 1); + } } else { - if (starttype & ST_WIRE) - startcompat = (startl == seg->layer) + startcompat = (startl == seg->layer) || (startl == seg->layer + 1); - else - startcompat = (startl == seg->layer) - || (startl == seg->layer + 1) - || (startl + 1 == seg->layer); - - if (endtype & ST_WIRE) - endcompat = (endl == seg->layer) + endcompat = (endl == seg->layer) || (endl == seg->layer + 1); - else - endcompat = (endl == seg->layer) - || (endl == seg->layer + 1) - || (endl + 1 == seg->layer); } if (x1 == x2) { @@ -715,6 +704,7 @@ int write_delays(char *filename) starty <= y1) { brkx = startx; brky = starty; + brkl = startl; y2 = brky; brki = i; } @@ -724,6 +714,7 @@ int write_delays(char *filename) starty <= y2) { brkx = startx; brky = starty; + brkl = startl; y2 = brky; brki = i; } @@ -735,6 +726,7 @@ int write_delays(char *filename) endy <= y1) { brkx = endx; brky = endy; + brkl = endl; y2 = brky; brki = i; } @@ -744,6 +736,7 @@ int write_delays(char *filename) endy <= y2) { brkx = endx; brky = endy; + brkl = endl; y2 = brky; brki = i; } @@ -757,6 +750,7 @@ int write_delays(char *filename) startx <= x1) { brkx = startx; brky = starty; + brkl = startl; x2 = brkx; brki = i; } @@ -766,6 +760,7 @@ int write_delays(char *filename) startx <= x2) { brkx = startx; brky = starty; + brkl = startl; x2 = brkx; brki = i; } @@ -777,6 +772,7 @@ int write_delays(char *filename) endx <= x1) { brkx = endx; brky = endy; + brkl = endl; x2 = brkx; brki = i; } @@ -786,6 +782,7 @@ int write_delays(char *filename) endx <= x2) { brkx = endx; brky = endy; + brkl = endl; x2 = brkx; brki = i; } @@ -793,27 +790,64 @@ int write_delays(char *filename) } } } - if ((brki >= 0) && (eptinfo[brki].endl != -2)) { + if (brki >= 0) { /* Disable this endpoint so it is not checked again */ eptinfo[brki].endl = -2; /* Break route at this point */ - /* Make a copy of the segment where the break occurs */ + /* If route type is a wire, then make a copy of the */ + /* segment where the break occurs. If a via, then */ + /* determine which side of the break the via goes */ + /* to. */ + newroute = (ROUTE)malloc(sizeof(struct route_)); - newseg = (SEG)malloc(sizeof(struct seg_)); - newseg->segtype = seg->segtype; - newseg->x1 = brkx; - newseg->y1 = brky; - newseg->x2 = seg->x2; - newseg->y2 = seg->y2; - newseg->layer = seg->layer; - - newseg->next = seg->next; - seg->next = NULL; - seg->x2 = brkx; - seg->y2 = brky; - newroute->segments = newseg; + if (seg->segtype & ST_WIRE) { + newseg = (SEG)malloc(sizeof(struct seg_)); + newseg->segtype = seg->segtype; + newseg->x1 = brkx; + newseg->y1 = brky; + newseg->x2 = seg->x2; + newseg->y2 = seg->y2; + newseg->layer = seg->layer; + + newseg->next = seg->next; + seg->next = NULL; + seg->x2 = brkx; + seg->y2 = brky; + + newroute->segments = newseg; + } + else if (lastseg == NULL) { + /* Via is the route before the break */ + newroute->segments = seg->next; + seg->next = NULL; + } + else if (lastseg->layer <= seg->layer) { + if (brkl > seg->layer) { + /* Via goes at the end of the route before the break */ + newroute->segments = seg->next; + seg->next = NULL; + } + else { + /* Via goes at the start of the route after the break */ + newroute->segments = seg; + lastseg->next = NULL; + } + } + else { /* (lastseg->layer > seg->layer) */ + if (brkl > seg->layer) { + /* Via goes at the start of the route after the break */ + newroute->segments = seg; + lastseg->next = NULL; + } + else { + /* Via goes at the end of the route before the break */ + newroute->segments = seg->next; + seg->next = NULL; + } + } + newroute->netnum = rt->netnum; newroute->flags = (u_char)0; newroute->next = rt->next; @@ -862,7 +896,9 @@ int write_delays(char *filename) eptinfo[nroute].startx = seg->x1; eptinfo[nroute].starty = seg->y1; eptinfo[nroute].startl = seg->layer; - eptinfo[nroute].starttype = seg->segtype; + /* Check for via in down direction rather than the default up */ + if ((seg->segtype & ST_VIA) && seg->next && seg->next->layer <= seg->layer) + eptinfo[nroute].startl++; nodeptr = (seg->layer < Pinlayers) ? NODEIPTR(seg->x1, seg->y1, seg->layer) : NULL; eptinfo[nroute].startnode = nodeptr ? nodeptr->nodesav : NULL; @@ -874,7 +910,7 @@ int write_delays(char *filename) eptinfo[nroute].cap = 0.0; /* Look up node */ - if (nodeptr) { + if (nodeptr && nodeptr->nodesav) { g = FindGateNode(&NodeTable, nodeptr->nodesav, &i); if (g && (g->gatetype->direction[i] == PORT_CLASS_OUTPUT)) { drivernodeidx = i; @@ -908,13 +944,20 @@ int write_delays(char *filename) eptinfo[nroute].endx = seg->x2; eptinfo[nroute].endy = seg->y2; eptinfo[nroute].endl = seg->layer; - eptinfo[nroute].endtype = seg->segtype; - nodeptr = (seg->layer < Pinlayers) ? - NODEIPTR(seg->x2, seg->y2, seg->layer) : NULL; + /* Check for via in down direction rather than default up */ + if (seg->segtype & ST_VIA) { + if (lastseg && lastseg->layer <= seg->layer) + eptinfo[nroute].endl++; + else if (!lastseg && eptinfo[nroute].endl <= seg->layer) + eptinfo[nroute].endl++; + } + + nodeptr = (eptinfo[nroute].endl < Pinlayers) ? + NODEIPTR(seg->x2, seg->y2, eptinfo[nroute].endl) : NULL; eptinfo[nroute].endnode = nodeptr ? nodeptr->nodesav : NULL; /* Look up node */ - if (nodeptr) { + if (nodeptr && nodeptr->nodesav) { g = FindGateNode(&NodeTable, nodeptr->nodesav, &i); if (g && (g->gatetype->direction[i] == PORT_CLASS_OUTPUT)) { drivernodeidx = i; @@ -959,11 +1002,11 @@ int write_delays(char *filename) drivergate->gatetype->node[drivernodeidx]); */ if (!strcmp(drivergate->gatetype->node[drivernodeidx], "pin")) - fprintf(delayFile, " PIN/%s %d ", - drivergate->gatename, net->numnodes - 1); + fprintf(delayFile, "%s 1 PIN/%s %d ", + net->netname, drivergate->gatename, net->numnodes - 1); else - fprintf(delayFile, " %s/%s %d ", - drivergate->gatename, + fprintf(delayFile, "%s 1 %s/%s %d ", + net->netname, drivergate->gatename, drivergate->gatetype->node[drivernodeidx], net->numnodes - 1); @@ -1012,6 +1055,7 @@ int write_delays(char *filename) free(lefrcvalues); + FreeNodeTable(&NodeTable); Tcl_DeleteHashTable(&NodeTable); return 0; @@ -102,9 +102,9 @@ void highlight_source() { // Draw source pins as magenta squares XSetForeground(dpy, gc, magentapix); for (i = 0; i < Num_layers; i++) { - for (x = 0; x < NumChannelsX[i]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[i]; y++) { + for (y = 0; y < NumChannelsY; y++) { Pr = &OBS2VAL(x, y, i); if (Pr->flags & PR_SOURCE) { yspc = height - (y + 1) * spacing - hspc; @@ -140,9 +140,9 @@ void highlight_dest() { // Draw destination pins as purple squares XSetForeground(dpy, gc, purplepix); for (i = 0; i < Num_layers; i++) { - for (x = 0; x < NumChannelsX[i]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[i]; y++) { + for (y = 0; y < NumChannelsY; y++) { Pr = &OBS2VAL(x, y, i); if (Pr->flags & PR_TARGET) { yspc = height - (y + 1) * spacing - hspc; @@ -199,9 +199,9 @@ void highlight_mask(void) { hspc = spacing >> 1; // Draw destination pins as tan squares - for (x = 0; x < NumChannelsX[0]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[0]; y++) { + for (y = 0; y < NumChannelsY; y++) { XSetForeground(dpy, gc, brownvector[RMASK(x, y)]); yspc = height - (y + 1) * spacing - hspc; XFillRectangle(dpy, win, gc, xspc, yspc, spacing, spacing); @@ -227,9 +227,9 @@ map_obstruction() // Draw obstructions as light gray squares XSetForeground(dpy, gc, ltgraypix); for (i = 0; i < Num_layers; i++) { - for (x = 0; x < NumChannelsX[i]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[i]; y++) { + for (y = 0; y < NumChannelsY; y++) { if (OBSVAL(x, y, i) & NO_NET) { yspc = height - (y + 1) * spacing - hspc; XFillRectangle(dpy, buffer, gc, xspc, yspc, @@ -242,9 +242,9 @@ map_obstruction() // Draw pins as gray squares XSetForeground(dpy, gc, graypix); for (i = 0; i < Pinlayers; i++) { - for (x = 0; x < NumChannelsX[i]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[i]; y++) { + for (y = 0; y < NumChannelsY; y++) { if (NODEIPTR(x, y, i) != NULL) { yspc = height - (y + 1) * spacing - hspc; XFillRectangle(dpy, buffer, gc, xspc, yspc, @@ -271,13 +271,13 @@ map_congestion() hspc = spacing >> 1; - Congestion = (u_char *)calloc(NumChannelsX[0] * NumChannelsY[0], + Congestion = (u_char *)calloc(NumChannelsX * NumChannelsY, sizeof(u_char)); // Analyze Obs[] array for congestion for (i = 0; i < Num_layers; i++) { - for (x = 0; x < NumChannelsX[i]; x++) { - for (y = 0; y < NumChannelsY[i]; y++) { + for (x = 0; x < NumChannelsX; x++) { + for (y = 0; y < NumChannelsY; y++) { value = (u_char)0; n = OBSVAL(x, y, i); if (n & ROUTED_NET) value++; @@ -290,8 +290,8 @@ map_congestion() } maxval = 0; - for (x = 0; x < NumChannelsX[0]; x++) { - for (y = 0; y < NumChannelsY[0]; y++) { + for (x = 0; x < NumChannelsX; x++) { + for (y = 0; y < NumChannelsY; y++) { value = CONGEST(x, y); if (value > maxval) maxval = value; } @@ -299,9 +299,9 @@ map_congestion() norm = (LONGSPAN - 1) / maxval; // Draw destination pins as blue squares - for (x = 0; x < NumChannelsX[0]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[0]; y++) { + for (y = 0; y < NumChannelsY; y++) { XSetForeground(dpy, gc, bluevector[norm * CONGEST(x, y)]); yspc = height - (y + 1) * spacing - hspc; XFillRectangle(dpy, buffer, gc, xspc, yspc, spacing, spacing); @@ -328,7 +328,7 @@ map_estimate() hspc = spacing >> 1; - Congestion = (float *)calloc(NumChannelsX[0] * NumChannelsY[0], + Congestion = (float *)calloc(NumChannelsX * NumChannelsY, sizeof(float)); // Use net bounding boxes to estimate congestion @@ -352,8 +352,8 @@ map_estimate() } maxval = 0.0; - for (x = 0; x < NumChannelsX[0]; x++) { - for (y = 0; y < NumChannelsY[0]; y++) { + for (x = 0; x < NumChannelsX; x++) { + for (y = 0; y < NumChannelsY; y++) { density = CONGEST(x, y); if (density > maxval) maxval = density; } @@ -361,9 +361,9 @@ map_estimate() norm = (float)(LONGSPAN - 1) / maxval; // Draw destination pins as blue squares - for (x = 0; x < NumChannelsX[0]; x++) { + for (x = 0; x < NumChannelsX; x++) { xspc = (x + 1) * spacing - hspc; - for (y = 0; y < NumChannelsY[0]; y++) { + for (y = 0; y < NumChannelsY; y++) { value = (int)(norm * CONGEST(x, y)); XSetForeground(dpy, gc, bluevector[value]); yspc = height - (y + 1) * spacing - hspc; @@ -782,8 +782,8 @@ int redraw(ClientData clientData, Tcl_Interp *interp, int objc, } /*------------------------------------------------------*/ -/* Call to recalculate the spacing if NumChannelsX[0] */ -/* or NumChannelsY[0] changes. */ +/* Call to recalculate the spacing if NumChannelsX */ +/* or NumChannelsY changes. */ /* */ /* Return 1 if the spacing changed, 0 otherwise. */ /*------------------------------------------------------*/ @@ -793,8 +793,8 @@ int recalc_spacing() int xspc, yspc; int oldspacing = spacing; - xspc = width / (NumChannelsX[0] + 1); - yspc = height / (NumChannelsY[0] + 1); + xspc = width / (NumChannelsX + 1); + yspc = height / (NumChannelsY + 1); spacing = (xspc < yspc) ? xspc : yspc; if (spacing == 0) spacing = 1; @@ -274,6 +274,8 @@ LefNextToken(FILE *f, u_char ignore_eol) * Print an error message (via fprintf) giving the line * number of the input file on which the error occurred. * + * "type" should be either LEF_ERROR or LEF_WARNING (or DEF_*). + * * Results: * None. * @@ -284,36 +286,49 @@ LefNextToken(FILE *f, u_char ignore_eol) */ void -LefError(char *fmt, ...) +LefError(int type, char *fmt, ...) { - static int errors = 0; + static int fatal = 0; + static int nonfatal = 0; + char lefordef = 'L'; + int errors; va_list args; if (Verbose == 0) return; + if ((type == DEF_WARNING) || (type == DEF_ERROR)) lefordef = 'D'; + + errors = fatal + nonfatal; if (fmt == NULL) /* Special case: report any errors and reset */ { - if (errors) + if (errors > 0) { - Fprintf(stdout, "LEF Read: encountered %d error%s total.\n", - errors, (errors == 1) ? "" : "s"); - errors = 0; + Fprintf(stdout, "%cEF Read: encountered %d error%s and %d warning%s total.\n", + lefordef, + fatal, (fatal == 1) ? "" : "s", + nonfatal, (nonfatal == 1) ? "" : "s"); + fatal = 0; + nonfatal = 0; } return; } if (errors < LEF_MAX_ERRORS) { - Fprintf(stderr, "LEF Read, Line %d: ", lefCurrentLine); + Fprintf(stderr, "%cEF Read, Line %d: ", lefordef, lefCurrentLine); va_start(args, fmt); Vprintf(stderr, fmt, args); va_end(args); Flush(stderr); } else if (errors == LEF_MAX_ERRORS) - Fprintf(stderr, "LEF Read: Further errors will not be reported.\n"); + Fprintf(stderr, "%cEF Read: Further errors/warnings will not be reported.\n", + lefordef); - errors++; + if ((type == LEF_ERROR) || (type == DEF_ERROR)) + fatal++; + else if ((type == LEF_WARNING) || (type == DEF_WARNING)) + nonfatal++; } /* @@ -349,7 +364,7 @@ LefParseEndStatement(FILE *f, char *match) token = LefNextToken(f, (match == NULL) ? FALSE : TRUE); if (token == NULL) { - LefError("Bad file read while looking for END statement\n"); + LefError(LEF_ERROR, "Bad file read while looking for END statement\n"); return FALSE; } @@ -413,7 +428,7 @@ LefSkipSection(FILE *f, char *section) } } - LefError("Section %s has no END record!\n", section); + LefError(LEF_ERROR, "Section %s has no END record!\n", section); return; } @@ -611,7 +626,9 @@ LefFindLayerNum(char *token) /* *--------------------------------------------------------------- - * Find the maximum routing layer number defined by the LEF file + * Find the maximum layer number defined by the LEF file + * This includes layer numbers assigned to both routes and + * via cut layers. *--------------------------------------------------------------- */ @@ -629,6 +646,26 @@ LefGetMaxLayer(void) } /* + *--------------------------------------------------------------- + * Find the maximum routing layer number defined by the LEF file + *--------------------------------------------------------------- + */ + +int +LefGetMaxRouteLayer(void) +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass != CLASS_ROUTE) continue; + if (lefl->type > maxlayer) + maxlayer = lefl->type; + } + return (maxlayer + 1); +} + +/* *------------------------------------------------------------ * Return the route keepout area, defined as the route space * plus 1/2 the route width. This is the distance outward @@ -657,7 +694,7 @@ LefGetRouteKeepout(int layer) + lefl->info.route.spacing->spacing; } } - return MIN(PitchX[layer], PitchY[layer]) - PathWidth[layer] / 2.0; + return MIN(PitchX, PitchY) - PathWidth[layer] / 2.0; } /* @@ -680,7 +717,7 @@ LefGetRouteWidth(int layer) return lefl->info.route.width; } } - return MIN(PitchX[layer], PitchY[layer]) / 2.0; + return MIN(PitchX, PitchY) / 2.0; } /* @@ -708,7 +745,7 @@ LefGetRouteOffset(int layer) return lefl->info.route.offsetx; } } - return MIN(PitchX[layer], PitchY[layer]) / 2.0; + return MIN(PitchX, PitchY) / 2.0; } double @@ -723,7 +760,7 @@ LefGetRouteOffsetX(int layer) return lefl->info.route.offsetx; } } - return PitchX[layer] / 2.0; + return MIN(PitchX, PitchY) / 2.0; } double @@ -738,7 +775,28 @@ LefGetRouteOffsetY(int layer) return lefl->info.route.offsety; } } - return PitchY[layer] / 2.0; + return PitchY / 2.0; +} + +/* + *------------------------------------------------------------ + * Find and return the minimum metal area requirement for a + * route layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteMinArea(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.minarea; + } + } + return 0.0; /* Assume no minimum area requirement */ } /* @@ -769,12 +827,19 @@ LefGetViaWidth(int base, int layer, int dir) /* *------------------------------------------------------------ - * The base routing used by LefGetViaWidth(), with an + * The base routine used by LefGetViaWidth(), with an * additional argument that specifies which via orientation * to use, if an alternative orientation is available. This - * is necessary for doing checkerboard via patterning and - * for certain standard cells with ports that do not always - * fit one orientation of via. + * is necessary for doing checkerboard via patterning and for + * certain standard cells with ports that do not always fit + * one orientation of via. + * + * "orient" is defined as follows: + * 0 = XX = both layers horizontal + * 1 = XY = bottom layer horizontal, top layer vertical + * 2 = YX = bottom layer vertical, top layer horizontal + * 3 = YY = both layers vertical. + * *------------------------------------------------------------ */ @@ -786,18 +851,79 @@ LefGetXYViaWidth(int base, int layer, int dir, int orient) double width; char **viatable; - viatable = (orient == 1) ? ViaY : ViaX; - + switch (orient) { + case 0: + viatable = ViaXX; + break; + case 1: + viatable = ViaXY; + break; + case 2: + viatable = ViaYX; + break; + case 3: + viatable = ViaYY; + break; + } lefl = LefFindLayer(*(viatable + base)); + + /* The routine LefAssignLayerVias() should assign all Via** types. */ + /* Below are fallback assignments. */ + if (!lefl) { - viatable = (orient == 1) ? ViaX : ViaY; + switch (orient) { + case 0: + viatable = ViaXY; + break; + case 1: + viatable = ViaYX; + break; + case 2: + viatable = ViaYY; + break; + case 3: + viatable = ViaYX; + break; + } lefl = LefFindLayer(*(viatable + base)); - viatable = (orient == 1) ? ViaY : ViaX; } + if (!lefl) { - if (base == (Num_layers - 1)) - lefl = LefFindLayer(*(viatable + base - 1)); + switch (orient) { + case 0: + viatable = ViaYX; + break; + case 1: + viatable = ViaYY; + break; + case 2: + viatable = ViaXX; + break; + case 3: + viatable = ViaXY; + break; + } + lefl = LefFindLayer(*(viatable + base)); } + + if (!lefl) { + switch (orient) { + case 0: + viatable = ViaYY; + break; + case 1: + viatable = ViaYX; + break; + case 2: + viatable = ViaXY; + break; + case 3: + viatable = ViaXX; + break; + } + lefl = LefFindLayer(*(viatable + base)); + } + if (lefl) { if (lefl->lefClass == CLASS_VIA) { if (lefl->info.via.area.layer == layer) { @@ -818,7 +944,7 @@ LefGetXYViaWidth(int base, int layer, int dir, int orient) } } } - return MIN(PitchX[layer], PitchY[layer]) / 2.0; // Best guess + return MIN(PitchX, PitchY) / 2.0; // Best guess } /* @@ -841,7 +967,7 @@ LefGetRouteSpacing(int layer) return 0.0; } } - return MIN(PitchX[layer], PitchY[layer]) / 2.0; + return MIN(PitchX, PitchY) / 2.0; } /* @@ -869,7 +995,7 @@ LefGetRouteWideSpacing(int layer, double width) return spacing; } } - return MIN(PitchX[layer], PitchY[layer]) / 2.0; + return MIN(PitchX, PitchY) / 2.0; } /* @@ -894,7 +1020,7 @@ LefGetRoutePitch(int layer) return lefl->info.route.pitchx; } } - return MIN(PitchX[layer], PitchY[layer]); + return MIN(PitchX, PitchY); } /* @@ -914,7 +1040,7 @@ LefGetRoutePitchX(int layer) return lefl->info.route.pitchx; } } - return PitchX[layer]; + return PitchX; } /* @@ -934,7 +1060,45 @@ LefGetRoutePitchY(int layer) return lefl->info.route.pitchy; } } - return PitchY[layer]; + return PitchY; +} + +/* + *------------------------------------------------------------ + * Set the route pitch in X for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchX(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchx = value; + } + } +} + +/* + *------------------------------------------------------------ + * Set the route pitch in Y for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchY(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchy = value; + } + } } /* @@ -1007,6 +1171,67 @@ LefGetRouteRCvalues(int layer, double *areacap, double *edgecap, /* *------------------------------------------------------------ + * Get the antenna violation area ratio for the given layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteAreaRatio(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.antenna; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the antenna violation area calculation method for the + * given layer. + *------------------------------------------------------------ + */ + +u_char +LefGetRouteAntennaMethod(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.method; + } + } + return CALC_NONE; +} + +/* + *------------------------------------------------------------ + * Get the route metal layer thickness (if any is defined) + *------------------------------------------------------------ + */ + +double +LefGetRouteThickness(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.thick; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ * Get resistance per via for a via layer. * Return 0 on success, -1 if the layer is not found. * Fill in the pointer value with the resistance. @@ -1019,11 +1244,19 @@ LefGetViaResistance(int layer, double *respervia) DSEG lrect; LefList lefl; double width; - char **viatable = ViaX; + char **viatable = ViaXX; lefl = LefFindLayer(*(viatable + layer)); if (!lefl) { - viatable = ViaY; + viatable = ViaXY; + lefl = LefFindLayer(*(viatable + layer)); + } + if (!lefl) { + viatable = ViaYX; + lefl = LefFindLayer(*(viatable + layer)); + } + if (!lefl) { + viatable = ViaYY; lefl = LefFindLayer(*(viatable + layer)); } if (lefl) { @@ -1068,7 +1301,7 @@ LefReadLayers(f, obstruct, lreturn) token = LefNextToken(f, TRUE); if (*token == ';') { - LefError("Bad Layer statement\n"); + LefError(LEF_ERROR, "Bad Layer statement\n"); return -1; } else @@ -1082,7 +1315,7 @@ LefReadLayers(f, obstruct, lreturn) curlayer = lefl->obsType; if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE)) curlayer = lefl->type; - else if (lefl->lefClass == CLASS_VIA) + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) if (lreturn) *lreturn = lefl->info.via.obsType; } else @@ -1096,8 +1329,28 @@ LefReadLayers(f, obstruct, lreturn) /* CLASS_VIA in lefl record is a cut, and the layer */ /* geometry is ignored for the purpose of routing. */ - if ((!lefl) || (lefl->lefClass != CLASS_VIA)) - LefError("Don't know how to parse layer \"%s\"\n", token); + if (lefl && (lefl->lefClass == CLASS_CUT)) { + int cuttype; + + /* By the time a cut layer is being requested, */ + /* presumably from a VIA definition, the route */ + /* layers should all be defined, so start */ + /* assigning layers to cuts. */ + + /* If a cut layer is found with an unassigned number, */ + /* then assign it here. */ + cuttype = LefGetMaxLayer(); + if (cuttype < MAX_TYPES) { + lefl->type = cuttype; + curlayer = cuttype; + strcpy(CIFLayer[cuttype], lefl->lefName); + } + else + LefError(LEF_WARNING, "Too many cut types; type \"%s\" ignored.\n", + token); + } + else if ((!lefl) || (lefl->lefClass != CLASS_VIA)) + LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token); } } return curlayer; @@ -1188,7 +1441,7 @@ LefReadRect(FILE *f, int curlayer, float oscale) } if (curlayer < 0) { /* Issue warning but keep geometry with negative layer number */ - LefError("No layer defined for RECT.\n"); + LefError(LEF_WARNING, "No layer defined for RECT.\n"); } /* Scale coordinates (microns to centimicrons) */ @@ -1201,7 +1454,59 @@ LefReadRect(FILE *f, int curlayer, float oscale) return (&paintrect); parse_error: - LefError("Bad port geometry: RECT requires 4 values.\n"); + LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n"); + return (DSEG)NULL; +} + +/* + *------------------------------------------------------------ + * LefReadEnclosure -- + * + * Read a LEF "ENCLOSURE" record from the file, and + * return a Rect in micron coordinates, representing + * the bounding box of the stated enclosure dimensions + * in both directions, centered on the origin. + * + * Results: + * Returns a pointer to a Rect containing the micron + * coordinates, or NULL if an error occurred. + * + * Side Effects: + * Reads input from file f + * + *------------------------------------------------------------ + */ + +DSEG +LefReadEnclosure(FILE *f, int curlayer, float oscale) +{ + char *token; + float x, y, scale; + static struct dseg_ paintrect; + + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &x) != 1) goto enc_parse_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto enc_parse_error; + + if (curlayer < 0) { + /* Issue warning but keep geometry with negative layer number */ + LefError(LEF_ERROR, "No layer defined for RECT.\n"); + } + + /* Scale coordinates (microns to centimicrons) (doubled) */ + + scale = oscale / 2.0; + + paintrect.x1 = -x / scale; + paintrect.y1 = -y / scale; + paintrect.x2 = x / scale; + paintrect.y2 = y / scale; + paintrect.layer = curlayer; + return (&paintrect); + +enc_parse_error: + LefError(LEF_ERROR, "Bad enclosure geometry: ENCLOSURE requires 2 values.\n"); return (DSEG)NULL; } @@ -1406,7 +1711,7 @@ LefPolygonToRects(DSEG *rectListPtr, DPOINT pointlist) if (npts < 4) { - LefError("Polygon with fewer than 4 points.\n"); + LefError(LEF_ERROR, "Polygon with fewer than 4 points.\n"); goto done; } @@ -1418,7 +1723,7 @@ LefPolygonToRects(DSEG *rectListPtr, DPOINT pointlist) if (!lefOrient(edges, npts, dir)) { - LefError("I can't handle non-manhattan polygons!\n"); + LefError(LEF_ERROR, "I can't handle non-manhattan polygons!\n"); goto done; } @@ -1494,13 +1799,15 @@ LefReadPolygon(FILE *f, int curlayer, float oscale) char *token; double px, py; + if (curlayer >= Num_layers) return (DPOINT)NULL; + while (1) { token = LefNextToken(f, TRUE); if (token == NULL || *token == ';') break; if (sscanf(token, "%lg", &px) != 1) { - LefError("Bad X value in polygon.\n"); + LefError(LEF_ERROR, "Bad X value in polygon.\n"); LefEndStatement(f); break; } @@ -1508,12 +1815,12 @@ LefReadPolygon(FILE *f, int curlayer, float oscale) token = LefNextToken(f, TRUE); if (token == NULL || *token == ';') { - LefError("Missing Y value in polygon point!\n"); + LefError(LEF_ERROR, "Missing Y value in polygon point!\n"); break; } if (sscanf(token, "%lg", &py) != 1) { - LefError("Bad Y value in polygon.\n"); + LefError(LEF_ERROR, "Bad Y value in polygon.\n"); LefEndStatement(f); break; } @@ -1579,7 +1886,8 @@ LefReadGeometry(GATE lefMacro, FILE *f, float oscale) keyword = Lookup(token, geometry_keys); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); LefEndStatement(f); continue; } @@ -1620,7 +1928,7 @@ LefReadGeometry(GATE lefMacro, FILE *f, float oscale) case LEF_GEOMETRY_END: if (!LefParseEndStatement(f, NULL)) { - LefError("Geometry (PORT or OBS) END statement missing.\n"); + LefError(LEF_ERROR, "Geometry (PORT or OBS) END statement missing.\n"); keyword = -1; } break; @@ -1649,11 +1957,12 @@ LefReadGeometry(GATE lefMacro, FILE *f, float oscale) */ void -LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale) +LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, pinArea, oscale) GATE lefMacro; FILE *f; char *pinName; int pinNum, pinDir, pinUse; + double pinArea; float oscale; { DSEG rectList, rlist; @@ -1675,6 +1984,8 @@ LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale) nodealloc * 10 * sizeof(NODE)); lefMacro->direction = (u_char *)realloc(lefMacro->direction, nodealloc * 10 * sizeof(u_char)); + lefMacro->area = (float *)realloc(lefMacro->area, + nodealloc * 10 * sizeof(float)); lefMacro->netnum = (int *)realloc(lefMacro->netnum, nodealloc * 10 * sizeof(int)); lefMacro->node = (char **)realloc(lefMacro->node, @@ -1683,7 +1994,9 @@ LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale) } lefMacro->taps[pinNum] = rectList; lefMacro->noderec[pinNum] = NULL; + lefMacro->area[pinNum] = 0.0; lefMacro->direction[pinNum] = pinDir; + lefMacro->area[pinNum] = pinArea; lefMacro->netnum[pinNum] = -1; if (pinName != NULL) lefMacro->node[pinNum] = strdup(pinName); @@ -1706,7 +2019,8 @@ LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale) * Read a PIN statement from a LEF file. * * Results: - * None. + * 0 if the pin had a port (success), 1 if not (indicating + * an unused pin that should be ignored). * * Side Effects: * Reads input from file f; @@ -1720,7 +2034,7 @@ enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE, LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE, LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END}; -void +int LefReadPin(lefMacro, f, pinname, pinNum, oscale) GATE lefMacro; FILE *f; @@ -1732,6 +2046,8 @@ LefReadPin(lefMacro, f, pinname, pinNum, oscale) int keyword, subkey; int pinDir = PORT_CLASS_DEFAULT; int pinUse = PORT_USE_DEFAULT; + float pinArea = 0.0; + int retval = 1; static char *pin_keys[] = { "DIRECTION", @@ -1798,7 +2114,8 @@ LefReadPin(lefMacro, f, pinname, pinNum, oscale) keyword = Lookup(token, pin_keys); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); LefEndStatement(f); continue; } @@ -1808,7 +2125,7 @@ LefReadPin(lefMacro, f, pinname, pinNum, oscale) token = LefNextToken(f, TRUE); subkey = Lookup(token, pin_classes); if (subkey < 0) - LefError("Improper DIRECTION statement\n"); + LefError(LEF_ERROR, "Improper DIRECTION statement\n"); else pinDir = lef_class_to_bitmask[subkey]; LefEndStatement(f); @@ -1817,17 +2134,24 @@ LefReadPin(lefMacro, f, pinname, pinNum, oscale) token = LefNextToken(f, TRUE); subkey = Lookup(token, pin_uses); if (subkey < 0) - LefError("Improper USE statement\n"); + LefError(LEF_ERROR, "Improper USE statement\n"); else pinUse = lef_use_to_bitmask[subkey]; LefEndStatement(f); break; case LEF_PORT: - LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, oscale); + LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, pinArea, oscale); + retval = 0; + break; + case LEF_ANTENNAGATE: + /* Read off the next value as the pin's antenna gate area. */ + /* The layers or layers are not recorded. */ + token = LefNextToken(f, TRUE); + sscanf(token, "%g", &pinArea); + LefEndStatement(f); break; case LEF_CAPACITANCE: case LEF_ANTENNADIFF: - case LEF_ANTENNAGATE: case LEF_ANTENNAMOD: case LEF_ANTENNAPAR: case LEF_ANTENNAPARSIDE: @@ -1840,13 +2164,14 @@ LefReadPin(lefMacro, f, pinname, pinNum, oscale) case LEF_PIN_END: if (!LefParseEndStatement(f, pinname)) { - LefError("Pin END statement missing.\n"); + LefError(LEF_ERROR, "Pin END statement missing.\n"); keyword = -1; } break; } if (keyword == LEF_PIN_END) break; } + return retval; } /* @@ -1942,7 +2267,7 @@ LefReadMacro(f, mname, oscale) if (!strcmp(altMacro->gatename, newname)) break; } - LefError("Cell \"%s\" was already defined in this file. " + LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. " "Renaming original cell \"%s\"\n", mname, newname); lefMacro->gatename = strdup(newname); @@ -1965,11 +2290,13 @@ LefReadMacro(f, mname, oscale) lefMacro->taps = (DSEG *)malloc(10 * sizeof(DSEG)); lefMacro->noderec = (NODE *)malloc(10 * sizeof(NODE)); lefMacro->direction = (u_char *)malloc(10 * sizeof(u_char)); + lefMacro->area = (float *)malloc(10 * sizeof(float)); lefMacro->netnum = (int *)malloc(10 * sizeof(int)); lefMacro->node = (char **)malloc(10 * sizeof(char *)); // Fill in 1st entry lefMacro->taps[0] = NULL; lefMacro->noderec[0] = NULL; + lefMacro->area[0] = 0.0; lefMacro->node[0] = NULL; lefMacro->netnum[0] = -1; GateInfo = lefMacro; @@ -1986,7 +2313,8 @@ LefReadMacro(f, mname, oscale) keyword = Lookup(token, macro_keys); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); LefEndStatement(f); continue; } @@ -2013,7 +2341,7 @@ LefReadMacro(f, mname, oscale) LefEndStatement(f); break; size_error: - LefError("Bad macro SIZE; requires values X BY Y.\n"); + LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n"); LefEndStatement(f); break; case LEF_ORIGIN: @@ -2032,7 +2360,7 @@ size_error: LefEndStatement(f); break; origin_error: - LefError("Bad macro ORIGIN; requires 2 values.\n"); + LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n"); LefEndStatement(f); break; case LEF_SYMMETRY: @@ -2066,7 +2394,8 @@ origin_error: if (is_imported) LefSkipSection(f, tsave); else - LefReadPin(lefMacro, f, tsave, pinNum++, oscale); + if (LefReadPin(lefMacro, f, tsave, pinNum, oscale) == 0) + pinNum++; break; case LEF_OBS: /* Diagnostic */ @@ -2087,7 +2416,7 @@ origin_error: case LEF_MACRO_END: if (!LefParseEndStatement(f, mname)) { - LefError("Macro END statement missing.\n"); + LefError(LEF_ERROR, "Macro END statement missing.\n"); keyword = -1; } break; @@ -2109,7 +2438,7 @@ origin_error: lefMacro->placedY = lefBBox.y1; } else { - LefError("Gate %s has no size information!\n", lefMacro->gatename); + LefError(LEF_ERROR, "Gate %s has no size information!\n", lefMacro->gatename); } } } @@ -2150,6 +2479,19 @@ LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale) if (lefl->info.via.area.layer < 0) { lefl->info.via.area = *currect; + + /* If entries exist for info.via.lr, this is a via GENERATE */ + /* statement, and metal enclosures have been parsed. Therefore */ + /* add the via dimensions to the enclosure rectangles. */ + + viarect = lefl->info.via.lr; + while (viarect != NULL) { + viarect->x1 += currect->x1; + viarect->x2 += currect->x2; + viarect->y1 += currect->y1; + viarect->y2 += currect->y2; + viarect = viarect->next; + } } else { @@ -2163,6 +2505,76 @@ LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale) /* *------------------------------------------------------------ * + * LefNewRoute --- + * + * Allocate space for and fill out default records of a + * route layer. + * + *------------------------------------------------------------ + */ + +LefList LefNewRoute(char *name) +{ + LefList lefl; + + lefl = (LefList)malloc(sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_IGNORE; /* For starters */ + lefl->lefName = strdup(name); + + return lefl; +} + +/* + *------------------------------------------------------------ + * + * LefNewVia --- + * + * Allocate space for and fill out default records of a + * via definition. + * + *------------------------------------------------------------ + */ + +LefList LefNewVia(char *name) +{ + LefList lefl; + + lefl = (LefList)calloc(1, sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_VIA; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + lefl->info.via.generated = FALSE; + lefl->info.via.respervia = 0.0; + lefl->lefName = strdup(name); + + return lefl; +} + +/* Note: Used directly below, as it is passed to variable "mode" + * in LefReadLayerSection(). However, mainly used in LefRead(). + */ + +enum lef_sections {LEF_VERSION = 0, + LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID, + LEF_USEMINSPACING, LEF_CLEARANCEMEASURE, + LEF_NAMESCASESENSITIVE, LEF_PROPERTYDEFS, LEF_UNITS, + LEF_SECTION_LAYER, LEF_SECTION_VIA, LEF_SECTION_VIARULE, + LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY, + LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP, + LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO, + LEF_END}; +/* + *------------------------------------------------------------ + * * LefReadLayerSection -- * * Read in a LAYER, VIA, or VIARULE section from a LEF file. @@ -2177,14 +2589,17 @@ LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale) */ enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH, - LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA, + LEF_LAYER_MINWIDTH, LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA, LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE, LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET, LEF_LAYER_WIREEXT, LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP, - LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT, - LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNADIFF, - LEF_LAYER_ANTENNASIDE, + LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT, LEF_LAYER_MINIMUMCUT, + LEF_LAYER_MINDENSITY, LEF_LAYER_ACDENSITY, LEF_LAYER_DCDENSITY, + LEF_LAYER_PROPERTY, LEF_LAYER_ANTENNAMODEL, LEF_LAYER_ANTENNA, + LEF_LAYER_ANTENNADIFF, LEF_LAYER_ANTENNASIDE, + LEF_LAYER_AGG_ANTENNA, LEF_LAYER_AGG_ANTENNADIFF, + LEF_LAYER_AGG_ANTENNASIDE, LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE, LEF_VIARULE_OVERHANG, @@ -2205,6 +2620,7 @@ LefReadLayerSection(f, lname, mode, lefl) struct seg_ viaArea; int curlayer = -1; double dvalue, oscale; + LefList altVia; lefSpacingRule *newrule = NULL, *testrule; /* These are defined in the order of CLASS_* in lefInt.h */ @@ -2219,6 +2635,7 @@ LefReadLayerSection(f, lname, mode, lefl) static char *layer_keys[] = { "TYPE", "WIDTH", + "MINWIDTH", "MAXWIDTH", "AREA", "SPACING", @@ -2232,9 +2649,18 @@ LefReadLayerSection(f, lname, mode, lefl) "EDGECAPACITANCE", "THICKNESS", "HEIGHT", + "MINIMUMCUT", "MINIMUMDENSITY", + "ACCURRENTDENSITY", + "DCCURRENTDENSITY", + "PROPERTY", + "ANTENNAMODEL", + "ANTENNAAREARATIO", "ANTENNADIFFAREARATIO", "ANTENNASIDEAREARATIO", + "ANTENNACUMAREARATIO", + "ANTENNACUMDIFFAREARATIO", + "ANTENNACUMSIDEAREARATIO", "DEFAULT", "LAYER", "RECT", @@ -2268,19 +2694,24 @@ LefReadLayerSection(f, lname, mode, lefl) keyword = Lookup(token, layer_keys); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); LefEndStatement(f); continue; } switch (keyword) { + case LEF_LAYER_PROPERTY: + /* Some kind of forward-compatibility thing. */ + LefEndStatement(f); + break; case LEF_LAYER_TYPE: token = LefNextToken(f, TRUE); if (*token != '\n') { typekey = Lookup(token, layer_type_keys); if (typekey < 0) - LefError("Unknown layer type \"%s\" in LEF file; " + LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; " "ignoring.\n", token); } if (lefl->lefClass == CLASS_IGNORE) { @@ -2295,36 +2726,24 @@ LefReadLayerSection(f, lname, mode, lefl) // been specified and needs to be set to default. lefl->info.route.offsetx = -1.0; lefl->info.route.offsety = -1.0; - lefl->info.route.hdirection = (u_char)0; + lefl->info.route.hdirection = DIR_UNKNOWN; - /* A routing type has been declared. Assume */ - /* this takes the name "metal1", "M1", or some */ - /* variant thereof. */ + lefl->info.route.minarea = 0.0; + lefl->info.route.thick = 0.0; + lefl->info.route.antenna = 0.0; + lefl->info.route.method = CALC_NONE; - for (tp = lefl->lefName; *tp != '\0'; tp++) { - if (*tp >= '0' && *tp <= '9') { - sscanf(tp, "%d", &lefl->type); - - /* "metal1", e.g., is assumed to be layer #0 */ - /* This may not be a proper assumption, always */ - - lefl->type--; - break; - } - } + lefl->info.route.areacap = 0.0; + lefl->info.route.respersq = 0.0; + lefl->info.route.edgecap = 0.0; - /* This is probably some special non-numerical */ - /* name for a top metal layer. Take a stab at */ - /* it, defining it to be the next layer up from */ - /* whatever the previous topmost route layer */ - /* was. This should work unless the LEF file */ - /* is really weirdly written. */ + /* A routing type has been declared. Route */ + /* layers are supposed to be in order from */ + /* bottom to top in the technology LEF file. */ - if (lefl->type < 0) { - lefl->type = LefGetMaxLayer(); - } + lefl->type = LefGetMaxRouteLayer(); } - else if (typekey == CLASS_VIA) { + else if (typekey == CLASS_CUT || typekey == CLASS_VIA) { lefl->info.via.area.x1 = 0.0; lefl->info.via.area.y1 = 0.0; lefl->info.via.area.x2 = 0.0; @@ -2332,19 +2751,39 @@ LefReadLayerSection(f, lname, mode, lefl) lefl->info.via.area.layer = -1; lefl->info.via.cell = (GATE)NULL; lefl->info.via.lr = (DSEG)NULL; + + /* Note: lefl->type not set here for cut */ + /* layers so that route layers will all be */ + /* clustered at the bottom. */ } } else if (lefl->lefClass != typekey) { - LefError("Attempt to reclassify layer %s from %s to %s\n", + LefError(LEF_ERROR, "Attempt to reclassify layer %s from %s to %s\n", lname, layer_type_keys[lefl->lefClass], layer_type_keys[typekey]); } LefEndStatement(f); break; + case LEF_LAYER_MINWIDTH: + // Not handled if width is already defined. + if ((lefl->lefClass != CLASS_ROUTE) || + (lefl->info.route.width != 0)) { + LefEndStatement(f); + break; + } + /* drop through */ case LEF_LAYER_WIDTH: token = LefNextToken(f, TRUE); sscanf(token, "%lg", &dvalue); - lefl->info.route.width = dvalue / (double)oscale; + if (lefl->lefClass == CLASS_ROUTE) + lefl->info.route.width = dvalue / (double)oscale; + else if (lefl->lefClass == CLASS_CUT) { + double baseval = (dvalue / (double)oscale) / 2.0; + lefl->info.via.area.x1 = -baseval; + lefl->info.via.area.y1 = -baseval; + lefl->info.via.area.x2 = baseval; + lefl->info.via.area.y2 = baseval; + } LefEndStatement(f); break; case LEF_LAYER_MAXWIDTH: @@ -2352,10 +2791,22 @@ LefReadLayerSection(f, lname, mode, lefl) LefEndStatement(f); break; case LEF_LAYER_AREA: - // Not handled, but needed! + /* Read minimum area rule value */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of area (length * length) + lefl->info.route.minarea = dvalue / (double)oscale / (double)oscale; + } LefEndStatement(f); break; case LEF_LAYER_SPACING: + // Only parse spacing for routes + + if (lefl->lefClass != CLASS_ROUTE) { + LefEndStatement(f); + break; + } token = LefNextToken(f, TRUE); sscanf(token, "%lg", &dvalue); token = LefNextToken(f, TRUE); @@ -2409,12 +2860,12 @@ LefReadLayerSection(f, lname, mode, lefl) else entries++; } - if (*token != ';') - newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); while (*token != ';') { token = LefNextToken(f, TRUE); // Minimum width value sscanf(token, "%lg", &dvalue); + + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); newrule->width = dvalue / (double)oscale; for (i = 0; i < entries; i++) { @@ -2422,7 +2873,6 @@ LefReadLayerSection(f, lname, mode, lefl) } sscanf(token, "%lg", &dvalue); newrule->spacing = dvalue / (double)oscale; - token = LefNextToken(f, TRUE); for (testrule = lefl->info.route.spacing; testrule; testrule = testrule->next) @@ -2455,6 +2905,18 @@ LefReadLayerSection(f, lname, mode, lefl) } else { lefl->info.route.pitchy = lefl->info.route.pitchx; + /* If the orientation is known, then zero the pitch */ + /* in the opposing direction. If not, then set the */ + /* direction to DIR_RESOLVE so that the pitch in */ + /* the opposing direction can be zeroed when the */ + /* direction is specified. */ + + if (lefl->info.route.hdirection == DIR_UNKNOWN) + lefl->info.route.hdirection = DIR_RESOLVE; + else if (lefl->info.route.hdirection == DIR_VERTICAL) + lefl->info.route.pitchy = 0.0; + else if (lefl->info.route.hdirection == DIR_HORIZONTAL) + lefl->info.route.pitchx = 0.0; } /* Offset default is 1/2 the pitch. Offset is */ @@ -2468,7 +2930,14 @@ LefReadLayerSection(f, lname, mode, lefl) case LEF_LAYER_DIRECTION: token = LefNextToken(f, TRUE); LefLower(token); - lefl->info.route.hdirection = (token[0] == 'h') ? TRUE : FALSE; + if (lefl->info.route.hdirection == DIR_RESOLVE) { + if (token[0] == 'h') + lefl->info.route.pitchx = 0.0; + else if (token[0] == 'v') + lefl->info.route.pitchy = 0.0; + } + lefl->info.route.hdirection = (token[0] == 'h') ? DIR_HORIZONTAL + : DIR_VERTICAL; LefEndStatement(f); break; case LEF_LAYER_OFFSET: @@ -2496,7 +2965,7 @@ LefReadLayerSection(f, lname, mode, lefl) lefl->info.route.respersq = dvalue; } } - else if (lefl->lefClass == CLASS_VIA) { + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) { sscanf(token, "%lg", &dvalue); lefl->info.via.respervia = dvalue; // Units ohms } @@ -2526,9 +2995,74 @@ LefReadLayerSection(f, lname, mode, lefl) break; case LEF_LAYER_THICKNESS: case LEF_LAYER_HEIGHT: - case LEF_LAYER_MINDENSITY: - case LEF_LAYER_ANTENNADIFF: + /* Assuming thickness and height are the same thing? */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of length + lefl->info.route.thick = dvalue / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_MINIMUMCUT: + /* Not handling minimum cuts for wide wires yet */ + LefEndStatement(f); + case LEF_LAYER_ANTENNAMODEL: + /* Not handling antenna models yet */ + LefEndStatement(f); + break; + case LEF_LAYER_ANTENNA: case LEF_LAYER_ANTENNASIDE: + case LEF_LAYER_AGG_ANTENNA: + case LEF_LAYER_AGG_ANTENNASIDE: + /* NOTE: Assuming that only one of these methods will */ + /* be used! If more than one is present, then only the */ + /* last one will be recorded and used. */ + + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Unitless values (ratio) + lefl->info.route.antenna = dvalue; + } + if (keyword == LEF_LAYER_ANTENNA) + lefl->info.route.method = CALC_AREA; + else if (keyword == LEF_LAYER_ANTENNASIDE) + lefl->info.route.method = CALC_SIDEAREA; + else if (keyword == LEF_LAYER_AGG_ANTENNA) + lefl->info.route.method = CALC_AGG_AREA; + else + lefl->info.route.method = CALC_AGG_SIDEAREA; + LefEndStatement(f); + case LEF_LAYER_ANTENNADIFF: + case LEF_LAYER_AGG_ANTENNADIFF: + /* Not specifically handling these antenna types */ + /* (antenna ratios for antennas connected to diodes, */ + /* which can still blow gates if the diode area is */ + /* insufficiently large.) */ + LefEndStatement(f); + break; + case LEF_LAYER_ACDENSITY: + /* The idiocy of the LEF format on display. */ + token = LefNextToken(f, TRUE); /* value type */ + token = LefNextToken(f, TRUE); /* value, FREQUENCY */ + if (!strcmp(token, "FREQUENCY")) { + LefEndStatement(f); + token = LefNextToken(f, TRUE); /* value, FREQUENCY */ + if (!strcmp(token, "WIDTH")) /* Optional width */ + LefEndStatement(f); /* Additional statement TABLEENTRIES */ + } + LefEndStatement(f); + break; + case LEF_LAYER_DCDENSITY: + /* The idiocy of the LEF format still on display. */ + token = LefNextToken(f, TRUE); /* value type */ + token = LefNextToken(f, TRUE); /* value, WIDTH */ + if (!strcmp(token, "WIDTH")) + LefEndStatement(f); /* Additional statement TABLEENTRIES */ + LefEndStatement(f); + break; + case LEF_LAYER_MINDENSITY: case LEF_LAYER_WIREEXT: /* Not specifically handling these */ LefEndStatement(f); @@ -2547,10 +3081,36 @@ LefReadLayerSection(f, lname, mode, lefl) LefEndStatement(f); break; case LEF_VIA_ENCLOSURE: - case LEF_VIA_PREFERENCLOSURE: + /* Defines how to draw via metal layers. Ignore unless */ + /* this is a VIARULE GENERATE section. */ + if (mode == LEF_SECTION_VIARULE) { + /* Note that values can interact with ENCLOSURE */ + /* values given for the cut layer type. This is */ + /* not being handled. */ + + DSEG viarect, encrect; + encrect = LefReadEnclosure(f, curlayer, oscale); + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *encrect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + lefl->info.via.generated = TRUE; + } + LefEndStatement(f); + break; case LEF_VIARULE_OVERHANG: case LEF_VIARULE_METALOVERHANG: - /* Ignoring this: Need to handle via generates */ + /* These are from older LEF definitions (e.g., 5.4) */ + /* and cannot completely specify via geometry. So if */ + /* seen, ignore the rest of the via section. Only the */ + /* explicitly defined VIA types will be used. */ + LefError(LEF_WARNING, "NOTE: Old format VIARULE ignored.\n"); + lefl->lefClass == CLASS_IGNORE; + LefEndStatement(f); + /* LefSkipSection(f, lname); */ /* Continue parsing */ + break; + case LEF_VIA_PREFERENCLOSURE: + /* Ignoring this. */ LefEndStatement(f); break; case LEF_VIARULE_VIA: @@ -2559,7 +3119,7 @@ LefReadLayerSection(f, lname, mode, lefl) case LEF_LAYER_END: if (!LefParseEndStatement(f, lname)) { - LefError("Layer END statement missing.\n"); + LefError(LEF_ERROR, "Layer END statement missing.\n"); keyword = -1; } break; @@ -2568,46 +3128,204 @@ LefReadLayerSection(f, lname, mode, lefl) } } -/*----------------------------------------------------------------*/ -/* This routine runs through all the defined vias, from last to */ -/* first defined. Check the X vs. Y dimension of the base layer. */ -/* If X is longer, save as ViaX. If Y is longer, save as ViaY. */ -/* If there is an AllowedVias list, then only assign vias that */ -/* are in the list. */ -/*----------------------------------------------------------------*/ +/*--------------------------------------------------------------*/ +/* If any vias have been built from VIARULE entries, then they */ +/* do not appear in the tech LEF and need to be defined in the */ +/* DEF file. This routine finds all generated vias and writes */ +/* out the entries to the indicated file f. */ +/* */ +/* "defvias" is the number of vias found in the input DEF file. */ +/* If non-zero, they will be written out after the internally */ +/* generated vias, so add defvias to the count written after */ +/* the VIAS statement, and do not write the "END" statement. */ +/*--------------------------------------------------------------*/ + +void +LefWriteGeneratedVias(FILE *f, double oscale, int defvias) +{ + double scale; + int numvias; + LefList lefl; + + scale = oscale / 2.0; /* Via dimensions are double */ + + /* 1st pass---check if any vias are generated. Also check if any */ + /* vias have non-route layers (e.g., POLY, or Num_layers and above) */ + /* and unmark them. */ + + numvias = defvias; + for (lefl = LefInfo; lefl; lefl = lefl->next) + if (lefl->lefClass == CLASS_VIA) + if (lefl->info.via.generated) { + if (!lefl->info.via.lr || + (lefl->info.via.lr->layer < 0) || + (lefl->info.via.lr->layer >= Num_layers)) { + lefl->info.via.generated = FALSE; + continue; + } + else if (!lefl->info.via.lr->next || + (lefl->info.via.lr->next->layer < 0) || + (lefl->info.via.lr->next->layer >= Num_layers)) { + lefl->info.via.generated = FALSE; + continue; + } + numvias++; + } + + if (numvias == 0) return; /* Nothing to write */ + + fprintf(f, "\n"); + fprintf(f, "VIAS %d ;\n", numvias); + + /* 2nd pass---output the VIA records. All vias consist of */ + /* two metal layers and a cut layer. */ + + for (lefl = LefInfo; lefl; lefl = lefl->next) + if (lefl->lefClass == CLASS_VIA) + if (lefl->info.via.generated) { + fprintf(f, "- %s\n", lefl->lefName); + fprintf(f, "+ RECT %s ( %ld %ld ) ( %ld %ld )", + CIFLayer[lefl->info.via.area.layer], + (long) (-0.5 + scale * lefl->info.via.area.x1), + (long) (-0.5 + scale * lefl->info.via.area.y1), + (long) (0.5 + scale * lefl->info.via.area.x2), + (long) (0.5 + scale * lefl->info.via.area.y2)); + if (lefl->info.via.lr != NULL) { + fprintf(f, "\n+ RECT %s ( %ld %ld ) ( %ld %ld )", + CIFLayer[lefl->info.via.lr->layer], + (long) (-0.5 + scale * lefl->info.via.lr->x1), + (long) (-0.5 + scale * lefl->info.via.lr->y1), + (long) (0.5 + scale * lefl->info.via.lr->x2), + (long) (0.5 + scale * lefl->info.via.lr->y2)); + if (lefl->info.via.lr->next != NULL) { + fprintf(f, "\n+ RECT %s ( %ld %ld ) ( %ld %ld )", + CIFLayer[lefl->info.via.lr->next->layer], + (long) (-0.5 + scale * lefl->info.via.lr->next->x1), + (long) (-0.5 + scale * lefl->info.via.lr->next->y1), + (long) (0.5 + scale * lefl->info.via.lr->next->x2), + (long) (0.5 + scale * lefl->info.via.lr->next->y2)); + } + } + fprintf(f, " ;\n"); /* Finish record */ + } + + if (defvias == 0) { + fprintf(f, "END VIAS\n", numvias); + fprintf(f, "\n"); + } +} + +/*--------------------------------------------------------------*/ +/* This routine runs through all the defined vias, from last to */ +/* first defined. Check the X vs. Y dimension of the base and */ +/* top layers. If both base and top layers are longer in X, */ +/* then save as ViaXX. If longer in Y, save as ViaYY. If the */ +/* base is longer in X and the top is longer in Y, then save as */ +/* ViaXY, and if the base is longer in Y and the top is longer */ +/* in X, then save as ViaYX. */ +/* */ +/* If all via types have the same X and Y for the top and/or */ +/* bottom layer, then save as both X and Y variants. */ +/* */ +/* If there is an AllowedVias list, then only assign vias that */ +/* are in the list. */ +/* */ +/* If the layer pair has a VIARULE GENERATE type, then it is */ +/* preferred over fixed via definitions. */ +/*--------------------------------------------------------------*/ void LefAssignLayerVias() { LefList lefl; - int layer; - double xydiff; + int toplayer, baselayer; + int minroute, maxroute; + double xybdiff, xytdiff; DSEG grect; LinkedStringPtr viaName; - char *newViaX[MAX_LAYERS]; - char *newViaY[MAX_LAYERS]; + char *newViaXX[MAX_LAYERS]; + char *newViaXY[MAX_LAYERS]; + char *newViaYX[MAX_LAYERS]; + char *newViaYY[MAX_LAYERS]; + u_char hasGenerate[MAX_LAYERS]; + + for (baselayer = 0; baselayer < MAX_LAYERS; baselayer++) { + newViaXX[baselayer] = newViaXY[baselayer] = NULL; + newViaYX[baselayer] = newViaYY[baselayer] = NULL; + hasGenerate[baselayer] = FALSE; + } + + /* Determine if there is a VIARULE GENERATE for each base layer */ + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.generated == TRUE) { + /* Find the base layer and set hasGenerate[] for that layer */ + baselayer = lefl->info.via.area.layer; + if (lefl->info.via.lr) + if ((baselayer < 0) || (lefl->info.via.lr->layer < baselayer)) + baselayer = lefl->info.via.lr->layer; + if (lefl->info.via.lr->next) + if ((baselayer < 0) || (lefl->info.via.lr->next->layer < baselayer)) + baselayer = lefl->info.via.lr->next->layer; + if ((baselayer >= 0) && (baselayer < MAX_LAYERS)) + hasGenerate[baselayer] = TRUE; + } + } + } + + /* Make sure we know what are the minimum and maximum route layers */ - for (layer = 0; layer < MAX_LAYERS; layer++) { - newViaX[layer] = newViaY[layer] = NULL; + minroute = maxroute = -1; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_ROUTE) { + if (minroute == -1) { + minroute = lefl->type; + maxroute = lefl->type; + } + else { + if (lefl->type < minroute) + minroute = lefl->type; + if (lefl->type > maxroute) + maxroute = lefl->type; + } + } } for (lefl = LefInfo; lefl; lefl = lefl->next) { if (lefl->lefClass == CLASS_VIA) { if (lefl->info.via.lr) { - layer = MAX_LAYERS; - if (lefl->info.via.area.layer >= 0) { - layer = lefl->info.via.area.layer; - xydiff = (lefl->info.via.area.x2 - lefl->info.via.area.x1) - + baselayer = toplayer = MAX_LAYERS; + if (lefl->info.via.area.layer >= minroute && + lefl->info.via.area.layer <= maxroute) { + baselayer = toplayer = lefl->info.via.area.layer; + xybdiff = xytdiff = + (lefl->info.via.area.x2 - lefl->info.via.area.x1) - (lefl->info.via.area.y2 - lefl->info.via.area.y1); } for (grect = lefl->info.via.lr; grect; grect = grect->next) { - if (grect->layer >= 0 && grect->layer < layer) { - layer = grect->layer; - xydiff = (grect->x2 - grect->x1) - (grect->y2 - grect->y1); + if (grect->layer >= minroute && grect->layer <= maxroute) { + if (grect->layer < baselayer) { + baselayer = grect->layer; + xybdiff = (grect->x2 - grect->x1) - (grect->y2 - grect->y1); + } } } - if (layer < MAX_LAYERS) { + toplayer = baselayer; + + for (grect = lefl->info.via.lr; grect; grect = grect->next) { + if (grect->layer >= minroute && grect->layer <= maxroute) { + if (grect->layer > toplayer) { + toplayer = grect->layer; + xytdiff = (grect->x2 - grect->x1) - (grect->y2 - grect->y1); + } + } + } + + /* Ignore vias on undefined layers (-1) */ + if ((baselayer < MAX_LAYERS) && (toplayer < MAX_LAYERS) && + (baselayer >= 0) && (toplayer >= 0)) { /* Assign only to layers in AllowedVias, if it is non-NULL */ if (AllowedVias != NULL) { for (viaName = AllowedVias; viaName; viaName = viaName->next) { @@ -2616,44 +3334,114 @@ LefAssignLayerVias() } if (viaName == NULL) continue; } - if (xydiff > -EPS) { - if (newViaX[layer] != NULL) free(newViaX[layer]); - newViaX[layer] = strdup(lefl->lefName); + else if (hasGenerate[baselayer] && lefl->info.via.generated == FALSE) + continue; + + /* Check for unexpected via definitions */ + if ((toplayer - baselayer) != 1) { + LefError(LEF_WARNING, "Via \"%s\" in LEF file is defined " + "on non-contiguous route layers!\n", lefl->lefName); + } + + if ((xytdiff > EPS) && (xybdiff < -EPS)) { + if (newViaYX[baselayer] != NULL) free(newViaYX[baselayer]); + newViaYX[baselayer] = strdup(lefl->lefName); + } + else if ((xytdiff < -EPS) && (xybdiff > EPS)) { + if (newViaXY[baselayer] != NULL) free(newViaXY[baselayer]); + newViaXY[baselayer] = strdup(lefl->lefName); + } + else if ((xytdiff > EPS) || (xybdiff > EPS)) { + if (newViaXX[baselayer] != NULL) free(newViaXX[baselayer]); + newViaXX[baselayer] = strdup(lefl->lefName); + } + else if ((xytdiff < -EPS) || (xybdiff < -EPS)) { + if (newViaYY[baselayer] != NULL) free(newViaYY[baselayer]); + newViaYY[baselayer] = strdup(lefl->lefName); } else { - if (newViaY[layer] != NULL) free(newViaY[layer]); - newViaY[layer] = strdup(lefl->lefName); + if (newViaXX[baselayer] == NULL) { + newViaXX[baselayer] = strdup(lefl->lefName); + } } } } } } - /* Copy newViaX and newViaY back into viaX and viaY, making */ - /* sure that at least one entry exists for each layer. */ - - /* At this time, only ViaX[] reports values back in */ - /* LefGetViaWidth(), so make sure that if there is only one */ - /* allowed via for a layer, it is copied into the ViaX */ - /* array, regardless of its orientation. */ + /* Copy newVia** back into via**, making sure that at least */ + /* one entry exists for each layer. */ - for (layer = 0; layer < MAX_LAYERS; layer++) { - if ((newViaX[layer] == NULL) && (newViaY[layer] == NULL)) + for (baselayer = 0; baselayer < MAX_LAYERS; baselayer++) { + if ((newViaXX[baselayer] == NULL) && (newViaXY[baselayer] == NULL) + && (newViaYX[baselayer] == NULL) && (newViaYY[baselayer] == NULL)) continue; - if (ViaX[layer] != NULL) free(ViaX[layer]); - if (ViaY[layer] != NULL) free(ViaY[layer]); + if (ViaXX[baselayer] != NULL) { + free(ViaXX[baselayer]); + ViaXX[baselayer] = NULL; + } + if (ViaXY[baselayer] != NULL) { + free(ViaXY[baselayer]); + ViaXY[baselayer] = NULL; + } + if (ViaYX[baselayer] != NULL) { + free(ViaYX[baselayer]); + ViaYX[baselayer] = NULL; + } + if (ViaYY[baselayer] != NULL) { + free(ViaYY[baselayer]); + ViaYY[baselayer] = NULL; + } - if (newViaX[layer] != NULL) - ViaX[layer] = strdup(newViaX[layer]); - else - ViaX[layer] = strdup(newViaY[layer]); - if (newViaY[layer] != NULL) - ViaY[layer] = strdup(newViaY[layer]); + if (newViaXX[baselayer] != NULL) + ViaXX[baselayer] = strdup(newViaXX[baselayer]); + if (newViaXY[baselayer] != NULL) + ViaXY[baselayer] = strdup(newViaXY[baselayer]); + if (newViaYX[baselayer] != NULL) + ViaYX[baselayer] = strdup(newViaYX[baselayer]); + if (newViaYY[baselayer] != NULL) + ViaYY[baselayer] = strdup(newViaYY[baselayer]); + + /* Fallback in case some permutations don't exist */ + if (ViaXX[baselayer] == NULL) { + if (newViaXY[baselayer] != NULL) + ViaXX[baselayer] = strdup(newViaXY[baselayer]); + else if (newViaYX[baselayer] != NULL) + ViaXX[baselayer] = strdup(newViaYX[baselayer]); + else if (newViaYY[baselayer] != NULL) + ViaXX[baselayer] = strdup(newViaYY[baselayer]); + } + if (ViaXY[baselayer] == NULL) { + if (newViaXX[baselayer] != NULL) + ViaXY[baselayer] = strdup(newViaXX[baselayer]); + else if (newViaYY[baselayer] != NULL) + ViaXY[baselayer] = strdup(newViaYY[baselayer]); + else if (newViaYX[baselayer] != NULL) + ViaXY[baselayer] = strdup(newViaYX[baselayer]); + } + if (ViaYX[baselayer] == NULL) { + if (newViaYY[baselayer] != NULL) + ViaYX[baselayer] = strdup(newViaYY[baselayer]); + else if (newViaXX[baselayer] != NULL) + ViaYX[baselayer] = strdup(newViaXX[baselayer]); + else if (newViaXY[baselayer] != NULL) + ViaYX[baselayer] = strdup(newViaXY[baselayer]); + } + if (ViaYY[baselayer] == NULL) { + if (newViaYX[baselayer] != NULL) + ViaYY[baselayer] = strdup(newViaYX[baselayer]); + else if (newViaXY[baselayer] != NULL) + ViaYY[baselayer] = strdup(newViaXY[baselayer]); + else if (newViaXX[baselayer] != NULL) + ViaYY[baselayer] = strdup(newViaXX[baselayer]); + } } - for (layer = 0; layer < MAX_LAYERS; layer++) { - if (newViaX[layer] != NULL) free(newViaX[layer]); - if (newViaY[layer] != NULL) free(newViaY[layer]); + for (baselayer = 0; baselayer < MAX_LAYERS; baselayer++) { + if (newViaXX[baselayer] != NULL) free(newViaXX[baselayer]); + if (newViaXY[baselayer] != NULL) free(newViaXY[baselayer]); + if (newViaYX[baselayer] != NULL) free(newViaYX[baselayer]); + if (newViaYY[baselayer] != NULL) free(newViaYY[baselayer]); } } @@ -2675,15 +3463,7 @@ LefAssignLayerVias() *------------------------------------------------------------ */ -enum lef_sections {LEF_VERSION = 0, - LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID, - LEF_USEMINSPACING, LEF_CLEARANCEMEASURE, - LEF_NAMESCASESENSITIVE, LEF_PROPERTYDEFS, LEF_UNITS, - LEF_SECTION_LAYER, LEF_SECTION_VIA, LEF_SECTION_VIARULE, - LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY, - LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP, - LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO, - LEF_END}; +/* See above for "enum lef_sections {...}" */ int LefRead(inName) @@ -2696,7 +3476,7 @@ LefRead(inName) int keyword, layer; int oprecis = 100; // = 1 / manufacturing grid (microns) float oscale; - double xydiff, ogrid; + double xydiff, ogrid, minwidth; LefList lefl; DSEG grect; GATE gateginfo; @@ -2754,7 +3534,8 @@ LefRead(inName) keyword = Lookup(token, sections); if (keyword < 0) { - LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); LefEndStatement(f); continue; } @@ -2787,35 +3568,39 @@ LefRead(inName) sprintf(tsave, "%.127s", token); lefl = LefFindLayer(token); - if (lefl == NULL) - { - lefl = (LefList)calloc(1, sizeof(lefLayer)); - lefl->type = -1; - lefl->obsType = -1; - lefl->lefClass = CLASS_VIA; - lefl->info.via.area.x1 = 0.0; - lefl->info.via.area.y1 = 0.0; - lefl->info.via.area.x2 = 0.0; - lefl->info.via.area.y2 = 0.0; - lefl->info.via.area.layer = -1; - lefl->info.via.cell = (GATE)NULL; - lefl->info.via.lr = (DSEG)NULL; - lefl->lefName = strdup(token); + if (keyword == LEF_SECTION_VIARULE) { + char *vianame = (char *)malloc(strlen(token) + 3); + sprintf(vianame, "%s_0", token); + + /* If the following keyword is GENERATE, then */ + /* prepare up to four contact types to represent */ + /* all possible orientations of top and bottom */ + /* metal layers. If no GENERATE keyword, ignore. */ + + token = LefNextToken(f, TRUE); + if (!strcmp(token, "GENERATE")) { + lefl = LefNewVia(vianame); + lefl->next = LefInfo; + LefInfo = lefl; + LefReadLayerSection(f, tsave, keyword, lefl); + } + else { + LefSkipSection(f, tsave); + } + free(vianame); + } + else if (lefl == NULL) + { + lefl = LefNewVia(token); lefl->next = LefInfo; LefInfo = lefl; - LefReadLayerSection(f, tsave, keyword, lefl); } - else if (keyword == LEF_SECTION_VIARULE) - /* If we've already seen this via, don't reprocess. */ - /* This deals with VIA followed by VIARULE. We */ - /* really ought to have special processing for the */ - /* VIARULE section. . . */ - LefSkipSection(f, tsave); else { - LefError("Warning: Cut type \"%s\" redefined.\n", token); + LefError(LEF_WARNING, "Warning: Cut type \"%s\" redefined.\n", + token); lefl = LefRedefined(lefl, token); LefReadLayerSection(f, tsave, keyword, lefl); } @@ -2828,11 +3613,7 @@ LefRead(inName) lefl = LefFindLayer(token); if (lefl == (LefList)NULL) { - lefl = (LefList)malloc(sizeof(lefLayer)); - lefl->type = -1; - lefl->obsType = -1; - lefl->lefClass = CLASS_IGNORE; /* For starters */ - lefl->lefName = strdup(token); + lefl = LefNewRoute(token); lefl->next = LefInfo; LefInfo = lefl; } @@ -2840,7 +3621,8 @@ LefRead(inName) { if (lefl && lefl->type < 0) { - LefError("Layer %s is only defined for obstructions!\n", token); + LefError(LEF_ERROR, "Layer %s is only defined for" + " obstructions!\n", token); LefSkipSection(f, tsave); break; } @@ -2891,7 +3673,7 @@ LefRead(inName) case LEF_END: if (!LefParseEndStatement(f, "LIBRARY")) { - LefError("END statement out of context.\n"); + LefError(LEF_ERROR, "END statement out of context.\n"); keyword = -1; } break; @@ -2900,7 +3682,7 @@ LefRead(inName) } if (Verbose > 0) { Fprintf(stdout, "LEF read: Processed %d lines.\n", lefCurrentLine); - LefError(NULL); /* print statement of errors, if any */ + LefError(LEF_ERROR, NULL); /* print statement of errors, if any */ } /* Cleanup */ @@ -2926,6 +3708,7 @@ LefRead(inName) gateginfo->taps = (DSEG *)malloc(sizeof(DSEG)); gateginfo->noderec = (NODE *)malloc(sizeof(NODE)); + gateginfo->area = (float *)malloc(sizeof(float)); gateginfo->direction = (u_char *)malloc(sizeof(u_char)); gateginfo->netnum = (int *)malloc(sizeof(int)); gateginfo->node = (char **)malloc(sizeof(char *)); @@ -2938,6 +3721,7 @@ LefRead(inName) gateginfo->next = GateInfo; gateginfo->taps[0] = grect; gateginfo->noderec[0] = NULL; + gateginfo->area[0] = 0.0; gateginfo->netnum[0] = -1; gateginfo->node[0] = strdup("pin"); GateInfo = gateginfo; @@ -2953,6 +3737,185 @@ LefRead(inName) strcpy(CIFLayer[lefl->type], lefl->lefName); } } + + /* If VIARULE contacts are not square, generate rotated versions. */ + /* Also check that VIARULE sizes meet minimum metal width requirements. */ + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.generated == TRUE) { + DSEG viarect1, viarect2, viarect; + LefList altVia; + u_char nsq1, nsq2; + char *vianame; + + viarect1 = lefl->info.via.lr; + if (viarect1 == NULL) continue; + viarect2 = lefl->info.via.lr->next; + + minwidth = LefGetRouteWidth(viarect1->layer); + if ((viarect1->x2 - viarect1->x1 + EPS) < (2.0 * minwidth)) { + viarect1->x2 = minwidth; + viarect1->x1 = -minwidth; + } + if ((viarect1->y2 - viarect1->y1 + EPS) < (2.0 * minwidth)) { + viarect1->y2 = minwidth; + viarect1->y1 = -minwidth; + } + minwidth = LefGetRouteWidth(viarect2->layer); + if ((viarect2->x2 - viarect2->x1 + EPS) < (2.0 * minwidth)) { + viarect2->x2 = minwidth; + viarect2->x1 = -minwidth; + } + if ((viarect2->y2 - viarect2->y1 + EPS) < (2.0 * minwidth)) { + viarect2->y2 = minwidth; + viarect2->y1 = -minwidth; + } + + nsq1 = (ABSDIFF((viarect1->x2 - viarect1->x1), + (viarect1->y2 - viarect1->y1)) > EPS) ? TRUE : FALSE; + if (viarect2) + nsq2 = (ABSDIFF((viarect2->x2 - viarect2->x1), + (viarect2->y2 - viarect2->y1)) > EPS) ? TRUE : FALSE; + else + nsq2 = FALSE; + + /* If viarect is not square, then generate a via in the */ + /* 90-degree rotated orientation. */ + + if (nsq1 || nsq2) { + vianame = strdup(lefl->lefName); + *(vianame + strlen(vianame) - 1) = '1'; + altVia = LefFindLayer(vianame); + if (altVia != NULL) { + Fprintf(stderr, "Warning: Via name %s has already been " + "defined!\n", vianame); + continue; + } + + altVia = LefNewVia(vianame); + altVia->info.via.generated = TRUE; + altVia->next = LefInfo; + LefInfo = altVia; + + /* Copy all but lr geometry from original via */ + altVia->lefClass = lefl->lefClass; + altVia->info.via.respervia = lefl->info.via.respervia; + altVia->info.via.area = lefl->info.via.area; + + /* Create first lr geometry */ + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *viarect1; + if (nsq1) { + /* Swap X and Y to rotate */ + viarect->x1 = viarect1->y1; + viarect->x2 = viarect1->y2; + viarect->y1 = viarect1->x1; + viarect->y2 = viarect1->x2; + } + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + + /* Create second lr geometry (if it exists) */ + if (viarect2) { + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *viarect2; + if (nsq2) { + /* Swap X and Y to rotate */ + viarect->x1 = viarect2->y1; + viarect->x2 = viarect2->y2; + viarect->y1 = viarect2->x1; + viarect->y2 = viarect2->x2; + } + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + } + free(vianame); + } + if (nsq1 && nsq2) { + + /* If both contact metal layers are non-square, */ + /* then create the additional vias with reversed */ + /* orientations of the two layers. */ + + vianame = strdup(lefl->lefName); + *(vianame + strlen(vianame) - 1) = '2'; + altVia = LefFindLayer(vianame); + if (altVia != NULL) { + Fprintf(stderr, "Warning: Via name %s has already been " + "defined!\n", vianame); + continue; + } + + altVia = LefNewVia(vianame); + altVia->info.via.generated = TRUE; + altVia->next = LefInfo; + LefInfo = altVia; + + /* Copy all but lr geometry from original via */ + altVia->lefClass = lefl->lefClass; + altVia->info.via.respervia = lefl->info.via.respervia; + altVia->info.via.area = lefl->info.via.area; + + /* Create first lr geometry, not rotated */ + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *lefl->info.via.lr; + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + + /* Create second lr geometry, rotated */ + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *lefl->info.via.lr->next; + /* Swap X and Y to rotate */ + viarect->x1 = lefl->info.via.lr->next->y1; + viarect->x2 = lefl->info.via.lr->next->y2; + viarect->y1 = lefl->info.via.lr->next->x1; + viarect->y2 = lefl->info.via.lr->next->x2; + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + + /* Repeat in the opposite orientation */ + *(vianame + strlen(vianame) - 1) = '3'; + altVia = LefFindLayer(vianame); + if (altVia != NULL) { + Fprintf(stderr, "Warning: Via name %s has already been " + "defined!\n", vianame); + continue; + } + + altVia = LefNewVia(vianame); + altVia->info.via.generated = TRUE; + altVia->next = LefInfo; + LefInfo = altVia; + + /* Copy all but lr geometry from original via */ + altVia->lefClass = lefl->lefClass; + altVia->info.via.respervia = lefl->info.via.respervia; + altVia->info.via.area = lefl->info.via.area; + + /* Create first lr geometry, rotated */ + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *lefl->info.via.lr; + /* Swap X and Y to rotate */ + viarect->x1 = lefl->info.via.lr->y1; + viarect->x2 = lefl->info.via.lr->y2; + viarect->y1 = lefl->info.via.lr->x1; + viarect->y2 = lefl->info.via.lr->x2; + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + + /* Create second lr geometry, not rotated */ + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *lefl->info.via.lr->next; + viarect->next = altVia->info.via.lr; + altVia->info.via.lr = viarect; + free(vianame); + } + } + } + } + + /* Find the best via(s) to use per route layer and record it (them) */ LefAssignLayerVias(); return oprecis; @@ -32,6 +32,9 @@ typedef struct #define DO_SPECIAL 1 #define ALL_SPECIAL 2 /* treat all nets as SPECIALNETS */ +/* Types of error messages */ +enum lef_error_types {LEF_ERROR = 0, LEF_WARNING, DEF_ERROR, DEF_WARNING}; + /* Port classes */ enum port_classes {PORT_CLASS_DEFAULT = 0, PORT_CLASS_INPUT, PORT_CLASS_OUTPUT, PORT_CLASS_TRISTATE, PORT_CLASS_BIDIRECTIONAL, @@ -51,22 +54,45 @@ typedef struct _lefSpacingRule { double spacing; /* minimum spacing rule, in microns */ } lefSpacingRule; +/* Area calculation methods for finding antenna ratios. ANTENNA_ROUTE */ +/* is not a method but is used to indicate to the antenna search */ +/* routine that the search is for routing preparation and not for */ +/* calculating error. */ + +enum area_methods {CALC_NONE = 0, CALC_AREA, CALC_SIDEAREA, CALC_AGG_AREA, + CALC_AGG_SIDEAREA, ANTENNA_ROUTE, ANTENNA_DISABLE}; + /* Structure used to maintain default routing information for each */ /* routable layer type. */ typedef struct { lefSpacingRule *spacing; /* spacing rules, ordered by width */ - double width; /* nominal route width, in microns */ - double pitchx; /* route X pitch, in microns */ - double pitchy; /* route Y pitch, in microns */ - double offsetx; /* route track offset from X origin, in microns */ - double offsety; /* route track offset from Y origin, in microns */ - double respersq; /* resistance per square */ - double areacap; /* area capacitance per square micron */ - double edgecap; /* edge capacitance per micron */ + double width; /* nominal route width, in microns */ + double pitchx; /* route X pitch, in microns */ + double pitchy; /* route Y pitch, in microns */ + double offsetx; /* route track offset from X origin, in microns */ + double offsety; /* route track offset from Y origin, in microns */ + double respersq; /* resistance per square */ + double areacap; /* area capacitance per square micron */ + double edgecap; /* edge capacitance per micron */ + double minarea; /* minimum metal area rule */ + double thick; /* metal layer thickness, if given */ + double antenna; /* antenna area ratio rule */ + u_char method; /* antenna rule calculation method */ u_char hdirection; /* horizontal direction preferred */ } lefRoute; +/* These values may be used for "hdirection". initialize hdirection */ +/* with DIR_UNKNOWN. If the LEF file defines the pitch before the */ +/* direction and does not specify both X and Y pitches, then change to */ +/* DIR_RESOLVE to indicate that the pitch in the non-preferred */ +/* direction should be zeroed when the preferred direction is known. */ + +#define DIR_VERTICAL (u_char)0 +#define DIR_HORIZONTAL (u_char)1 +#define DIR_UNKNOWN (u_char)2 +#define DIR_RESOLVE (u_char)3 + /* Structure used to maintain default generation information for each */ /* via or viarule (contact) type. If "cell" is non-NULL, then the via */ /* is saved in a cell (pointed to by "cell"), and "area" describes the */ @@ -81,15 +107,19 @@ typedef struct { /* more complicated geometry. */ double respervia; /* resistance per via */ int obsType; /* Secondary obstruction type */ + char generated; /* Flag indicating via from VIARULE */ } lefVia; /* Defined types for "lefClass" in the lefLayer structure */ +/* Note that the first four match TYPE records in the LEF. IGNORE has */ +/* a special meaning to qrouter, and VIA is for VIA definitions. */ -#define CLASS_ROUTE 0 /* routing layer */ -#define CLASS_VIA 1 /* via or cut layer */ +#define CLASS_ROUTE 0 /* route layer */ +#define CLASS_CUT 1 /* cut layer */ #define CLASS_MASTER 2 /* masterslice layer */ #define CLASS_OVERLAP 3 /* overlap layer */ #define CLASS_IGNORE 4 /* inactive layer */ +#define CLASS_VIA 5 /* via record */ /* Structure defining a route or via layer and matching it to a magic */ /* layer type. This structure is saved in the LefInfo list. */ @@ -131,19 +161,28 @@ int LefReadLayer(FILE *f, u_char obstruct); LefList LefFindLayer(char *token); LefList LefFindLayerByNum(int layer); int LefFindLayerNum(char *token); +void LefSetRoutePitchX(int layer, double value); +void LefSetRoutePitchY(int layer, double value); double LefGetRouteKeepout(int layer); double LefGetRouteWidth(int layer); +double LefGetRouteMinArea(int layer); double LefGetXYViaWidth(int base, int layer, int dir, int orient); double LefGetViaWidth(int base, int layer, int dir); double LefGetRouteSpacing(int layer); double LefGetRouteWideSpacing(int layer, double width); double LefGetRoutePitch(int layer); +double LefGetRoutePitchX(int layer); +double LefGetRoutePitchY(int layer); double LefGetRouteOffset(int layer); +double LefGetRouteThickness(int layer); +double LefGetRouteAreaRatio(int layer); +u_char LefGetRouteAntennaMethod(int layer); int LefGetRouteRCvalues(int layer, double *areacap, double *edgecap, double *respersq); int LefGetViaResistance(int layer, double *respervia); char *LefGetRouteName(int layer); int LefGetRouteOrientation(int layer); +int LefGetMaxRouteLayer(void); int LefGetMaxLayer(void); GATE LefFindInstance(char *name); @@ -151,8 +190,10 @@ void LefHashCell(GATE gateginfo); int LefRead(char *inName); void LefAssignLayerVias(); +void LefWriteGeneratedVias(FILE *f, double oscale, int defvias); + -void LefError(char *fmt, ...); /* Variable argument procedure requires */ - /* parameter list. */ +void LefError(int type, char *fmt, ...); /* Variable argument procedure */ + /* requires parameter list. */ #endif /* _LEFINT_H */ @@ -46,11 +46,10 @@ int compNets(NET *a, NET *b) // Sort critical nets at the front by assigned order - if (p->flags & NET_CRITICAL) { - if (q->flags & NET_CRITICAL) { - return (p->netorder < q->netorder) ? -1 : 1; - } - else return -1; + if ((p->flags & NET_CRITICAL) || (q->flags & NET_CRITICAL)) { + if (!(p->flags & NET_CRITICAL)) return 1; + else if (!(q->flags & NET_CRITICAL)) return -1; + else return (p->netorder < q->netorder) ? -1 : 1; } // Otherwise sort by number of nodes @@ -83,13 +82,13 @@ int altCompNets(NET *a, NET *b) // Sort critical nets at the front by assigned order - if (p->flags & NET_CRITICAL) { - if (q->flags & NET_CRITICAL) { - return (p->netorder < q->netorder) ? -1 : 1; - } - else return -1; + if ((p->flags & NET_CRITICAL) || (q->flags & NET_CRITICAL)) { + if (!(p->flags & NET_CRITICAL)) return 1; + else if (!(q->flags & NET_CRITICAL)) return -1; + else return (p->netorder < q->netorder) ? -1 : 1; } + // Otherwise sort as described above. pwidth = p->xmax - p->xmin; @@ -142,12 +141,10 @@ void create_netorder(u_char method) for (cn = CriticalNet; cn; cn = cn->next) { if (Verbose > 1) Fprintf(stdout, "critical net %s\n", cn->name); - for (j = 0; j < Numnets; j++) { - net = Nlnets[j]; - if (!strcmp(net->netname, (char *)cn->name)) { - net->netorder = i++; - net->flags |= NET_CRITICAL; - } + net = DefFindNet(cn->name); + if (net) { + net->netorder = i++; + net->flags |= NET_CRITICAL; } } @@ -344,7 +341,7 @@ void defineRouteTree(NET net) void initMask(void) { - RMask = (u_char *)calloc(NumChannelsX[0] * NumChannelsY[0], + RMask = (u_char *)calloc(NumChannelsX * NumChannelsY, sizeof(u_char)); if (!RMask) { fprintf(stderr, "Out of memory 3.\n"); @@ -374,9 +371,9 @@ create_vbranch_mask(int x, int y1, int y2, u_char slack, u_char halo) gy2 = y2 + slack; } if (gx1 < 0) gx1 = 0; - if (gx2 >= NumChannelsX[0]) gx2 = NumChannelsX[0] - 1; + if (gx2 >= NumChannelsX) gx2 = NumChannelsX - 1; if (gy1 < 0) gy1 = 0; - if (gy2 >= NumChannelsY[0]) gy2 = NumChannelsY[0] - 1; + if (gy2 >= NumChannelsY) gy2 = NumChannelsY - 1; for (i = gx1; i <= gx2; i++) for (j = gy1; j <= gy2; j++) @@ -384,10 +381,10 @@ create_vbranch_mask(int x, int y1, int y2, u_char slack, u_char halo) for (v = 1; v < halo; v++) { if (gx1 > 0) gx1--; - if (gx2 < NumChannelsX[0] - 1) gx2++; + if (gx2 < NumChannelsX - 1) gx2++; if (y1 > y2) { - if (gy1 < NumChannelsY[0] - 1) gy1++; - if (gy2 < NumChannelsY[0] - 1) gy2++; + if (gy1 < NumChannelsY - 1) gy1++; + if (gy2 < NumChannelsY - 1) gy2++; } else { if (gy1 > 0) gy1--; @@ -423,9 +420,9 @@ create_hbranch_mask(int y, int x1, int x2, u_char slack, u_char halo) gx2 = x2 + slack; } if (gx1 < 0) gx1 = 0; - if (gx2 >= NumChannelsX[0]) gx2 = NumChannelsX[0] - 1; + if (gx2 >= NumChannelsX) gx2 = NumChannelsX - 1; if (gy1 < 0) gy1 = 0; - if (gy2 >= NumChannelsY[0]) gy2 = NumChannelsY[0] - 1; + if (gy2 >= NumChannelsY) gy2 = NumChannelsY - 1; for (i = gx1; i <= gx2; i++) for (j = gy1; j <= gy2; j++) @@ -433,10 +430,10 @@ create_hbranch_mask(int y, int x1, int x2, u_char slack, u_char halo) for (v = 1; v < halo; v++) { if (gy1 > 0) gy1--; - if (gy2 < NumChannelsY[0] - 1) gy2++; + if (gy2 < NumChannelsY - 1) gy2++; if (x1 > x2) { - if (gx1 < NumChannelsX[0] - 1) gx1++; - if (gx2 < NumChannelsX[0] - 1) gx2++; + if (gx1 < NumChannelsX - 1) gx1++; + if (gx2 < NumChannelsX - 1) gx2++; } else { if (gx1 > 0) gx1--; @@ -513,27 +510,27 @@ void createBboxMask(NET net, u_char halo) for (i = 1; i <= halo; i++) { gx1 = xmin - i; - if (gx1 >= 0 && gx1 < NumChannelsX[0]) + if (gx1 >= 0 && gx1 < NumChannelsX) for (j = ymin - i; j <= ymax + i; j++) - if (j >= 0 && j < NumChannelsY[0]) + if (j >= 0 && j < NumChannelsY) RMASK(gx1, j) = (u_char)i; gx2 = xmax + i; - if (gx2 >= 0 && gx2 < NumChannelsX[0]) + if (gx2 >= 0 && gx2 < NumChannelsX) for (j = ymin - i; j <= ymax + i; j++) - if (j >= 0 && j < NumChannelsY[0]) + if (j >= 0 && j < NumChannelsY) RMASK(gx2, j) = (u_char)i; gy1 = ymin - i; - if (gy1 >= 0 && gy1 < NumChannelsY[0]) + if (gy1 >= 0 && gy1 < NumChannelsY) for (j = xmin - i; j <= xmax + i; j++) - if (j >= 0 && j < NumChannelsX[0]) + if (j >= 0 && j < NumChannelsX) RMASK(j, gy1) = (u_char)i; gy2 = ymax + i; - if (gy2 >= 0 && gy2 < NumChannelsY[0]) + if (gy2 >= 0 && gy2 < NumChannelsY) for (j = xmin - i; j <= xmax + i; j++) - if (j >= 0 && j < NumChannelsX[0]) + if (j >= 0 && j < NumChannelsX) RMASK(j, gy2) = (u_char)i; } } @@ -637,10 +634,16 @@ void createMask(NET net, u_char slack, u_char halo) xmin = oxmin; xmax = oxmax; + // Avoid faulting if the min/max values were never set + if (xmin > xmax) { + xmin = 0; + xmax = NumChannelsX - 1; + } + for (i = xmin - slack; i <= xmax + slack; i++) { - if (i < 0 || i >= NumChannelsX[0]) continue; + if (i < 0 || i >= NumChannelsX) continue; for (j = ycent - slack; j <= ycent + slack; j++) { - if (j < 0 || j >= NumChannelsY[0]) continue; + if (j < 0 || j >= NumChannelsY) continue; RMASK(i, j) = (u_char)0; } } @@ -649,19 +652,19 @@ void createMask(NET net, u_char slack, u_char halo) gy1 = ycent - slack - i; gy2 = ycent + slack + i; for (j = xmin - slack - i; j <= xmax + slack + i; j++) { - if (j < 0 || j >= NumChannelsX[0]) continue; + if (j < 0 || j >= NumChannelsX) continue; if (gy1 >= 0) RMASK(j, gy1) = (u_char)i; - if (gy2 < NumChannelsY[0]) + if (gy2 < NumChannelsY) RMASK(j, gy2) = (u_char)i; } gx1 = xmin - slack - i; gx2 = xmax + slack + i; for (j = ycent - slack - i; j <= ycent + slack + i; j++) { - if (j < 0 || j >= NumChannelsY[0]) continue; + if (j < 0 || j >= NumChannelsY) continue; if (gx1 >= 0) RMASK(gx1, j) = (u_char)i; - if (gx2 < NumChannelsX[0]) + if (gx2 < NumChannelsX) RMASK(gx2, j) = (u_char)i; } } @@ -673,10 +676,16 @@ void createMask(NET net, u_char slack, u_char halo) ymin = oymin; ymax = oymax; + // Avoid faulting if the min/max values were never set + if (ymin > ymax) { + ymin = 0; + ymax = NumChannelsY - 1; + } + for (i = xcent - slack; i <= xcent + slack; i++) { - if (i < 0 || i >= NumChannelsX[0]) continue; + if (i < 0 || i >= NumChannelsX) continue; for (j = ymin - slack; j <= ymax + slack; j++) { - if (j < 0 || j >= NumChannelsY[0]) continue; + if (j < 0 || j >= NumChannelsY) continue; RMASK(i, j) = (u_char)0; } } @@ -685,19 +694,19 @@ void createMask(NET net, u_char slack, u_char halo) gx1 = xcent - slack - i; gx2 = xcent + slack + i; for (j = ymin - slack - i; j <= ymax + slack + i; j++) { - if (j < 0 || j >= NumChannelsY[0]) continue; + if (j < 0 || j >= NumChannelsY) continue; if (gx1 >= 0) RMASK(gx1, j) = (u_char)i; - if (gx2 < NumChannelsX[0]) + if (gx2 < NumChannelsX) RMASK(gx2, j) = (u_char)i; } gy1 = ymin - slack - i; gy2 = ymax + slack + i; for (j = xcent - slack - i; j <= xcent + slack + i; j++) { - if (j < 0 || j >= NumChannelsX[0]) continue; + if (j < 0 || j >= NumChannelsX) continue; if (gy1 >= 0) RMASK(j, gy1) = (u_char)i; - if (gy2 < NumChannelsY[0]) + if (gy2 < NumChannelsY) RMASK(j, gy2) = (u_char)i; } } @@ -797,7 +806,7 @@ void createMask(NET net, u_char slack, u_char halo) void fillMask(u_char value) { memset((void *)RMask, (int)value, - (size_t)(NumChannelsX[0] * NumChannelsY[0] + (size_t)(NumChannelsX * NumChannelsY * sizeof(u_char))); } @@ -13,5 +13,6 @@ extern u_char *RMask; // mask out best area to route extern void initMask(void); extern void fillMask(u_char value); extern void setBboxCurrent(NET net); +extern void create_netorder(u_char method); #endif /* _MASKINT_H */ @@ -151,10 +151,10 @@ int set_powerbus_to_net(int netnum) PROUTE *Pr; rval = 0; - if ((netnum == VDD_NET) || (netnum == GND_NET)) { + if ((netnum == VDD_NET) || (netnum == GND_NET) || (netnum == ANTENNA_NET)) { for (lay = 0; lay < Num_layers; lay++) - for (x = 0; x < NumChannelsX[lay]; x++) - for (y = 0; y < NumChannelsY[lay]; y++) + for (x = 0; x < NumChannelsX; x++) + for (y = 0; y < NumChannelsY; y++) if ((OBSVAL(x, y, lay) & NETNUM_MASK) == netnum) { Pr = &OBS2VAL(x, y, lay); // Skip locations that have been purposefully disabled @@ -370,7 +370,8 @@ int set_node_to_net(NODE node, int newflags, POINT *pushlist, /* net is a power bus, just return. */ if ((node->taps == NULL) && (node->extend == NULL)) { - if ((node->netnum == VDD_NET) || (node->netnum == GND_NET)) + if ((node->netnum == VDD_NET) || (node->netnum == GND_NET) || + (node->netnum == ANTENNA_NET)) return result; } @@ -722,11 +723,14 @@ int set_route_to_net_recursive(NET net, ROUTE rt, int newflags, } } } - else { + else if (rt->start.route) { result = set_route_to_net_recursive(net, rt->start.route, newflags, pushlist, bbox, stage); if (result < 0) return result; } + else + Fprintf(stderr, "Error: Route start information not recorded, cannot walk.\n"); + if (rt->flags & RT_END_NODE) { for (route = net->routes; route; route = route->next) { if (!(route->flags & RT_START_NODE) && (route->start.route == rt)) { @@ -741,11 +745,13 @@ int set_route_to_net_recursive(NET net, ROUTE rt, int newflags, } } } - else { + else if (rt->end.route) { result = set_route_to_net_recursive(net, rt->end.route, newflags, pushlist, bbox, stage); if (result < 0) return result; } + else + Fprintf(stderr, "Error: Route end information not recorded, cannot walk.\n"); return result; } @@ -879,13 +885,13 @@ NETLIST find_colliding(NET net, int *ripnum) while (1) { orignet = OBSVAL(x, y, lay) & ROUTED_NET_MASK; - if (orignet == DRC_BLOCKAGE) { + if ((orignet & DRC_BLOCKAGE) == DRC_BLOCKAGE) { /* If original position was a DRC-related blockage, */ /* find out which net or nets would be in conflict. */ if (needblock[lay] & (ROUTEBLOCKX | VIABLOCKX)) { - if (x < NumChannelsX[lay] - 1) { + if (x < NumChannelsX - 1) { orignet = OBSVAL(x + 1, y, lay) & ROUTED_NET_MASK; if (!(orignet & NO_NET)) { orignet &= NETNUM_MASK; @@ -903,7 +909,7 @@ NETLIST find_colliding(NET net, int *ripnum) } } if (needblock[lay] & (ROUTEBLOCKY | VIABLOCKY)) { - if (y < NumChannelsY[lay] - 1) { + if (y < NumChannelsY - 1) { orignet = OBSVAL(x, y + 1, lay) & ROUTED_NET_MASK; if (!(orignet & NO_NET)) { orignet &= NETNUM_MASK; @@ -1012,11 +1018,11 @@ void analyze_route_overwrite(int x, int y, int lay, int netnum) /* Check on all sides to see if position is orphaned */ - if ((x < NumChannelsX[0] - 1) && (OBSVAL(x + 1, y, lay) & NETNUM_MASK) == netnum) + if ((x < NumChannelsX - 1) && (OBSVAL(x + 1, y, lay) & NETNUM_MASK) == netnum) is_valid = TRUE; else if ((x > 0) && (OBSVAL(x - 1, y, lay) & NETNUM_MASK) == netnum) is_valid = TRUE; - else if ((y < NumChannelsY[0] - 1) && (OBSVAL(x, y + 1, lay) & NETNUM_MASK) == netnum) + else if ((y < NumChannelsY - 1) && (OBSVAL(x, y + 1, lay) & NETNUM_MASK) == netnum) is_valid = TRUE; else if ((y > 0) && (OBSVAL(x, y - 1, lay) & NETNUM_MASK) == netnum) is_valid = TRUE; @@ -1121,6 +1127,44 @@ void remove_routes(ROUTE netroutes, u_char flagged) } /*--------------------------------------------------------------*/ +/* Clear a DRC blockage. Normally, just remove the */ +/* DRC_BLOCKAGE flag from Obs[]. However, more than one net */ +/* may set a block on the position, so the lower four bits */ +/* (OBSTRUCT_MASK) holds the reference count. */ +/*--------------------------------------------------------------*/ + +void clear_drc_blockage(x, y, lay) +{ + int blockcount; + + blockcount = OBSVAL(x, y, lay) & OBSTRUCT_MASK; + OBSVAL(x, y, lay) &= ~OBSTRUCT_MASK; + if (blockcount > 0) + OBSVAL(x, y, lay) |= (blockcount - 1); + else + OBSVAL(x, y, lay) &= ~DRC_BLOCKAGE; +} + +/*--------------------------------------------------------------*/ +/*--------------------------------------------------------------*/ + +void set_drc_blockage(x, y, lay) +{ + int blockcount, obsval; + + obsval = OBSVAL(x, y, lay); + if ((obsval & DRC_BLOCKAGE) == DRC_BLOCKAGE) { + blockcount = OBSVAL(x, y, lay) & OBSTRUCT_MASK; + OBSVAL(x, y, lay) &= ~OBSTRUCT_MASK; + OBSVAL(x, y, lay) |= (blockcount + 1); + } + else if ((obsval & NETNUM_MASK) == 0) { + OBSVAL(x, y, lay) &= ~OBSTRUCT_MASK; + OBSVAL(x, y, lay) |= DRC_BLOCKAGE; + } +} + +/*--------------------------------------------------------------*/ /* ripup_net --- */ /* */ /* Rip up the entire network located at position x, y, lay. */ @@ -1169,7 +1213,8 @@ u_char ripup_net(NET net, u_char restore, u_char flagged, u_char retain) // net and route associated with the incorrect net. analyze_route_overwrite(x, y, lay, oldnet); - // return FALSE; // Something went wrong + if ((rt == NULL) || (rt->segments == NULL)) + return FALSE; // Something went wrong } // Reset the net number to zero along this route for @@ -1197,20 +1242,20 @@ u_char ripup_net(NET net, u_char restore, u_char flagged, u_char retain) if (needblock[lay] & (ROUTEBLOCKX | VIABLOCKX)) { if ((x > 0) && ((OBSVAL(x - 1, y, lay) & DRC_BLOCKAGE) == DRC_BLOCKAGE)) - OBSVAL(x - 1, y, lay) &= ~DRC_BLOCKAGE; - else if ((x < NumChannelsX[lay] - 1) && + clear_drc_blockage(x - 1, y, lay); + else if ((x < NumChannelsX - 1) && ((OBSVAL(x + 1, y, lay) & DRC_BLOCKAGE) == DRC_BLOCKAGE)) - OBSVAL(x + 1, y, lay) &= ~DRC_BLOCKAGE; + clear_drc_blockage(x + 1, y, lay); } if (needblock[lay] & (ROUTEBLOCKY | VIABLOCKY)) { if ((y > 0) && ((OBSVAL(x, y - 1, lay) & DRC_BLOCKAGE) == DRC_BLOCKAGE)) - OBSVAL(x, y - 1, lay) &= ~DRC_BLOCKAGE; - else if ((y < NumChannelsY[lay] - 1) && + clear_drc_blockage(x, y - 1, lay); + else if ((y < NumChannelsY - 1) && ((OBSVAL(x, y + 1, lay) & DRC_BLOCKAGE) == DRC_BLOCKAGE)) - OBSVAL(x, y + 1, lay) &= ~DRC_BLOCKAGE; + clear_drc_blockage(x, y + 1, lay); } } @@ -1379,7 +1424,7 @@ POINT eval_pt(GRIDP *ept, u_char flags, u_char stage) Pr->prdata.cost = MAXRT; thiscost += ConflictCost; } - else if (stage && (netnum == DRC_BLOCKAGE)) { + else if (stage && ((netnum & DRC_BLOCKAGE) == DRC_BLOCKAGE)) { if ((newpt.lay < Pinlayers) && nodeptr && (nodeptr->nodesav != NULL)) return NULL; // But cannot route over terminals! @@ -1390,7 +1435,7 @@ POINT eval_pt(GRIDP *ept, u_char flags, u_char stage) // route here if any of them are on the noripup list. if (needblock[newpt.lay] & (ROUTEBLOCKX | VIABLOCKX)) { - if (newpt.x < NumChannelsX[newpt.lay] - 1) { + if (newpt.x < NumChannelsX - 1) { netnum = OBSVAL(newpt.x + 1, newpt.y, newpt.lay) & ROUTED_NET_MASK; if (!(netnum & NO_NET)) { netnum &= NETNUM_MASK; @@ -1415,7 +1460,7 @@ POINT eval_pt(GRIDP *ept, u_char flags, u_char stage) } } if (needblock[newpt.lay] & (ROUTEBLOCKY | VIABLOCKY)) { - if (newpt.y < NumChannelsY[newpt.lay] - 1) { + if (newpt.y < NumChannelsY - 1) { netnum = OBSVAL(newpt.x, newpt.y + 1, newpt.lay) & ROUTED_NET_MASK; if (!(netnum & NO_NET)) { netnum &= NETNUM_MASK; @@ -1578,20 +1623,16 @@ void writeback_segment(SEG seg, int netnum) dir = OBSVAL(seg->x1, seg->y1, seg->layer + 1) & (BLOCKED_MASK | PINOBSTRUCTMASK); OBSVAL(seg->x1, seg->y1, seg->layer + 1) = netnum | dir; if (needblock[seg->layer + 1] & VIABLOCKX) { - if ((seg->x1 < (NumChannelsX[seg->layer + 1] - 1)) && - (OBSVAL(seg->x1 + 1, seg->y1, seg->layer + 1) & NETNUM_MASK) == 0) - OBSVAL(seg->x1 + 1, seg->y1, seg->layer + 1) = DRC_BLOCKAGE; - if ((seg->x1 > 0) && - (OBSVAL(seg->x1 - 1, seg->y1, seg->layer + 1) & NETNUM_MASK) == 0) - OBSVAL(seg->x1 - 1, seg->y1, seg->layer + 1) = DRC_BLOCKAGE; + if (seg->x1 < (NumChannelsX - 1)) + set_drc_blockage(seg->x1 + 1, seg->y1, seg->layer + 1); + if (seg->x1 > 0) + set_drc_blockage(seg->x1 - 1, seg->y1, seg->layer + 1); } if (needblock[seg->layer + 1] & VIABLOCKY) { - if ((seg->y1 < (NumChannelsY[seg->layer + 1] - 1)) && - (OBSVAL(seg->x1, seg->y1 + 1, seg->layer + 1) & NETNUM_MASK) == 0) - OBSVAL(seg->x1, seg->y1 + 1, seg->layer + 1) = DRC_BLOCKAGE; - if ((seg->y1 > 0) && - (OBSVAL(seg->x1, seg->y1 - 1, seg->layer + 1) & NETNUM_MASK) == 0) - OBSVAL(seg->x1, seg->y1 - 1, seg->layer + 1) = DRC_BLOCKAGE; + if (seg->y1 < (NumChannelsY - 1)) + set_drc_blockage(seg->x1, seg->y1 + 1, seg->layer + 1); + if (seg->y1 > 0) + set_drc_blockage(seg->x1, seg->y1 - 1, seg->layer + 1); } // If position itself is an offset route, then make the route position @@ -1605,23 +1646,23 @@ void writeback_segment(SEG seg, int netnum) lnode = NODEIPTR(seg->x1, seg->y1, seg->layer); dist = lnode->offset; if (lnode->flags & NI_OFFSET_EW) { - if ((dist > 0) && (seg->x1 < (NumChannelsX[seg->layer] - 1))) { - OBSVAL(seg->x1 + 1, seg->y1, seg->layer) |= DRC_BLOCKAGE; - OBSVAL(seg->x1 + 1, seg->y1, seg->layer + 1) |= DRC_BLOCKAGE; + if ((dist > 0) && (seg->x1 < (NumChannelsX - 1))) { + set_drc_blockage(seg->x1 + 1, seg->y1, seg->layer); + set_drc_blockage(seg->x1 + 1, seg->y1, seg->layer + 1); } if ((dist < 0) && (seg->x1 > 0)) { - OBSVAL(seg->x1 - 1, seg->y1, seg->layer) |= DRC_BLOCKAGE; - OBSVAL(seg->x1 - 1, seg->y1, seg->layer + 1) |= DRC_BLOCKAGE; + set_drc_blockage(seg->x1 - 1, seg->y1, seg->layer); + set_drc_blockage(seg->x1 - 1, seg->y1, seg->layer + 1); } } else if (lnode->flags & NI_OFFSET_NS) { - if ((dist > 0) && (seg->y1 < (NumChannelsY[seg->layer] - 1))) { - OBSVAL(seg->x1, seg->y1 + 1, seg->layer) |= DRC_BLOCKAGE; - OBSVAL(seg->x1, seg->y1 + 1, seg->layer + 1) |= DRC_BLOCKAGE; + if ((dist > 0) && (seg->y1 < (NumChannelsY - 1))) { + set_drc_blockage(seg->x1, seg->y1 + 1, seg->layer); + set_drc_blockage(seg->x1, seg->y1 + 1, seg->layer + 1); } if ((dist < 0) && (seg->y1 > 0)) { - OBSVAL(seg->x1, seg->y1 - 1, seg->layer) |= DRC_BLOCKAGE; - OBSVAL(seg->x1, seg->y1 - 1, seg->layer + 1) |= DRC_BLOCKAGE; + set_drc_blockage(seg->x1, seg->y1 - 1, seg->layer); + set_drc_blockage(seg->x1, seg->y1 - 1, seg->layer + 1); } } } @@ -1631,12 +1672,10 @@ void writeback_segment(SEG seg, int netnum) dir = OBSVAL(i, seg->y1, seg->layer) & (BLOCKED_MASK | PINOBSTRUCTMASK); OBSVAL(i, seg->y1, seg->layer) = netnum | dir; if (needblock[seg->layer] & ROUTEBLOCKY) { - if ((seg->y1 < (NumChannelsY[seg->layer] - 1)) && - (OBSVAL(i, seg->y1 + 1, seg->layer) & NETNUM_MASK) == 0) - OBSVAL(i, seg->y1 + 1, seg->layer) = DRC_BLOCKAGE; - if ((seg->y1 > 0) && - (OBSVAL(i, seg->y1 - 1, seg->layer) & NETNUM_MASK) == 0) - OBSVAL(i, seg->y1 - 1, seg->layer) = DRC_BLOCKAGE; + if (seg->y1 < (NumChannelsY - 1)) + set_drc_blockage(i, seg->y1 + 1, seg->layer); + if (seg->y1 > 0) + set_drc_blockage(i, seg->y1 - 1, seg->layer); } // Check position on each side for an offset tap on a different net, and @@ -1647,14 +1686,14 @@ void writeback_segment(SEG seg, int netnum) // cause a DRC error. Could be refined. . . layer = (seg->layer == 0) ? 0 : seg->layer - 1; - if (seg->y1 < (NumChannelsY[layer] - 1)) { + if (seg->y1 < (NumChannelsY - 1)) { sobs = OBSVAL(i, seg->y1 + 1, layer); if ((sobs & OFFSET_TAP) && !(sobs & ROUTED_NET)) { lnode = NODEIPTR(i, seg->y1 + 1, layer); if (lnode->flags & NI_OFFSET_NS) { dist = lnode->offset; if (dist < 0) { - OBSVAL(i, seg->y1 + 1, layer) |= DRC_BLOCKAGE; + set_drc_blockage(i, seg->y1 + 1, layer); } } } @@ -1666,37 +1705,50 @@ void writeback_segment(SEG seg, int netnum) if (lnode->flags & NI_OFFSET_NS) { dist = lnode->offset; if (dist > 0) { - OBSVAL(i, seg->y1 - 1, layer) |= DRC_BLOCKAGE; + set_drc_blockage(i, seg->y1 - 1, layer); } } } } if (i == seg->x2) break; } + + /* Check top of route for vertical routes */ + + if (seg->y1 != seg->y2) { + dir = OBSVAL(seg->x2, seg->y2, seg->layer) & (BLOCKED_MASK | PINOBSTRUCTMASK); + OBSVAL(seg->x2, seg->y2, seg->layer) = netnum | dir; + if (needblock[seg->layer] & ROUTEBLOCKY) { + if (seg->y2 < (NumChannelsY - 1)) + set_drc_blockage(seg->x2, seg->y2 + 1, seg->layer); + if (seg->y2 > 0) + set_drc_blockage(seg->x2, seg->y2 - 1, seg->layer); + } + } + + for (i = seg->y1; ; i += (seg->y2 > seg->y1) ? 1 : -1) { dir = OBSVAL(seg->x1, i, seg->layer) & (BLOCKED_MASK | PINOBSTRUCTMASK); OBSVAL(seg->x1, i, seg->layer) = netnum | dir; if (needblock[seg->layer] & ROUTEBLOCKX) { - if ((seg->x1 < (NumChannelsX[seg->layer] - 1)) && - (OBSVAL(seg->x1 + 1, i, seg->layer) & NETNUM_MASK) == 0) - OBSVAL(seg->x1 + 1, i, seg->layer) = DRC_BLOCKAGE; - if ((seg->x1 > 0) && - (OBSVAL(seg->x1 - 1, i, seg->layer) & NETNUM_MASK) == 0) - OBSVAL(seg->x1 - 1, i, seg->layer) = DRC_BLOCKAGE; + if (seg->x1 < (NumChannelsX - 1)) + set_drc_blockage(seg->x1 + 1, i, seg->layer); + if (seg->x1 > 0) + set_drc_blockage(seg->x1 - 1, i, seg->layer); } // Check position on each side for an offset tap on a different net, and // mark the position unroutable (see above). layer = (seg->layer == 0) ? 0 : seg->layer - 1; - if (seg->x1 < (NumChannelsX[layer] - 1)) { + if (seg->x1 < (NumChannelsX - 1)) { sobs = OBSVAL(seg->x1 + 1, i, layer); if ((sobs & OFFSET_TAP) && !(sobs & ROUTED_NET)) { lnode = NODEIPTR(seg->x1 + 1, i, layer); if (lnode->flags & NI_OFFSET_EW) { dist = lnode->offset; if (dist < 0) { - OBSVAL(seg->x1 + 1, i, layer) |= DRC_BLOCKAGE; + set_drc_blockage(seg->x1 + 1, i, layer); } } } @@ -1708,13 +1760,26 @@ void writeback_segment(SEG seg, int netnum) if (lnode->flags & NI_OFFSET_EW) { dist = lnode->offset; if (dist > 0) { - OBSVAL(seg->x1 - 1, i, layer) |= DRC_BLOCKAGE; + set_drc_blockage(seg->x1 - 1, i, layer); } } } } if (i == seg->y2) break; } + + /* Check end of route for horizontal routes */ + + if (seg->x1 != seg->x2) { + dir = OBSVAL(seg->x2, seg->y2, seg->layer) & (BLOCKED_MASK | PINOBSTRUCTMASK); + OBSVAL(seg->x2, seg->y2, seg->layer) = netnum | dir; + if (needblock[seg->layer] & ROUTEBLOCKX) { + if (seg->x2 < (NumChannelsX - 1)) + set_drc_blockage(seg->x2 + 1, seg->y2, seg->layer); + if (seg->x2 > 0) + set_drc_blockage(seg->x2 - 1, seg->y2, seg->layer); + } + } } /*--------------------------------------------------------------*/ @@ -1735,16 +1800,53 @@ route_set_connections(net, route) u_char found, match; int x, y; + /* Reset start/end node flags */ + route->flags &= ~(RT_START_NODE | RT_END_NODE); + /* Does first route segment connect to a node? */ seg = route->segments; found = FALSE; if (seg->layer < Pinlayers) { lnode = NODEIPTR(seg->x1, seg->y1, seg->layer); - if (lnode != NULL) { + if ((lnode != NULL) && (lnode->nodesav != NULL)) { + route->start.node = lnode->nodesav; + route->flags |= RT_START_NODE; + found = TRUE; + } + } + + /* For routes that were just imported from DEF, check if the */ + /* route overshot a pin terminal by rounding off a non-grid */ + /* coordinate. */ + + if (!found && (route->flags & RT_CHECK) && (seg->layer < Pinlayers)) { + if (seg->x1 == seg->x2) { + x = seg->x1; + if (seg->y1 < seg->y2) + y = seg->y1 + 1; + else + y = seg->y1 - 1; + } + else { + y = seg->y1; + if (seg->x1 < seg->x2) + x = seg->x1 + 1; + else + x = seg->x1 - 1; + } + lnode = NODEIPTR(x, y, seg->layer); + if ((lnode != NULL) && (lnode->nodesav != NULL) && + (lnode->nodesav->netnum == net->netnum) && + ((x != seg->x2) || (y != seg->y2))) { route->start.node = lnode->nodesav; route->flags |= RT_START_NODE; found = TRUE; + /* Diagnostic */ + Fprintf(stderr, "Coordinate %d %d corrected to %d %d\n", + seg->x1, seg->y1, x, y); + seg->x1 = x; + seg->y1 = y; } } @@ -1785,7 +1887,8 @@ route_set_connections(net, route) } if (!found) { - Fprintf(stderr, "Error: Failure to find route start node/route!\n"); + Fprintf(stderr, "Error: Failure to find route start node/route on net %s!\n", + net->netname); } /* Does last route segment connect to a node? */ @@ -1799,12 +1902,46 @@ route_set_connections(net, route) for (; seg->next; seg = seg->next); if (seg->layer < Pinlayers) { lnode = NODEIPTR(seg->x2, seg->y2, seg->layer); - if (lnode != NULL) { + if ((lnode != NULL) && (lnode->nodesav != NULL)) { route->end.node = lnode->nodesav; route->flags |= RT_END_NODE; found = TRUE; } } + + /* For routes that were just imported from DEF, check if */ + /* the route overshot a pin terminal by rounding off a */ + /* non-grid coordinate. */ + + if (!found && (route->flags & RT_CHECK) && (seg->layer < Pinlayers)) { + if (seg->x1 == seg->x2) { + x = seg->x2; + if (seg->y1 < seg->y2) + y = seg->y2 - 1; + else + y = seg->y2 + 1; + } + else { + y = seg->y2; + if (seg->x1 < seg->x2) + x = seg->x2 - 1; + else + x = seg->x2 + 1; + } + lnode = NODEIPTR(x, y, seg->layer); + if ((lnode != NULL) && (lnode->nodesav != NULL) && + (lnode->nodesav->netnum == net->netnum) && + ((x != seg->x1) || (y != seg->y1))) { + route->start.node = lnode->nodesav; + route->flags |= RT_END_NODE; + found = TRUE; + /* Diagnostic */ + Fprintf(stderr, "Coordinate %d %d corrected to %d %d\n", + seg->x2, seg->y2, x, y); + seg->x2 = x; + seg->y2 = y; + } + } } /* Does last route segment connect to a route? */ @@ -1844,8 +1981,12 @@ route_set_connections(net, route) } if (!found) { - Fprintf(stderr, "Error: Failure to find route end node/route!\n"); + Fprintf(stderr, "Error: Failure to find route end node/route on net %s!\n", + net->netname); } + + /* Clear RT_CHECK flag after processing */ + if (route->flags & RT_CHECK) route->flags &= ~RT_CHECK; } /*--------------------------------------------------------------*/ @@ -1957,6 +2098,8 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) lrppre = lrprev->next; if (lrppre == NULL) break; stackheight = 0; + /* Advance lrcur if jogs were inserted */ + while (lrprev != lrcur->next) lrcur = lrcur->next; a = lrcur; b = lrprev; while (a->layer != b->layer) { @@ -1980,7 +2123,7 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) // lowest cost, and make sure the position below that // is available. - if (cx < NumChannelsX[cl] - 1) { + if (cx < NumChannelsX - 1) { dx = cx + 1; // Check to the right pri = &OBS2VAL(dx, cy, cl); pflags = pri->flags; @@ -2049,7 +2192,7 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) } } - if (cy < NumChannelsY[cl] - 1) { + if (cy < NumChannelsY - 1) { dy = cy + 1; // Check north pri = &OBS2VAL(cx, dy, cl); pflags = pri->flags; @@ -2171,7 +2314,7 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) mincost = MAXRT; dl = lrprev->layer; - if (cx < NumChannelsX[cl] - 1) { + if (cx < NumChannelsX - 1) { dx = cx + 1; // Check to the right pri = &OBS2VAL(dx, cy, cl); pflags = pri->flags; @@ -2217,7 +2360,7 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) } } - if (cy < NumChannelsY[cl] - 1) { + if (cy < NumChannelsY - 1) { dy = cy + 1; // Check north pri = &OBS2VAL(cx, dy, cl); pflags = pri->flags; @@ -2349,6 +2492,224 @@ int commit_proute(ROUTE rt, GRIDP *ept, u_char stage) } } } + + /* Handle minimum metal area rule for stacked contacts */ + + else if (StackedContacts > 0) { + + /* Search for all stacked contacts. For each via position inside */ + /* a stack, excluding top and bottom, determine if a minimum */ + /* metal area is violated. If so, then find an unused */ + /* neighboring grid and extend to that side. If no unused */ + /* neighboring grids exist, then fail. */ + + POINT lrppre; + PROUTE *pri, *pri2; + int violations = 1, checks; + int i, cx, cy, cl, o, orient; + int mincost, minx = -1, miny = -1, collide, cost; + double min_area[MAX_LAYERS]; + u_char need_check[MAX_LAYERS]; + + /* Register the minimum metal area rule for each via base layer */ + + checks = 0; + for (i = 0; i < Num_layers; i++) { + min_area[i] = LefGetRouteMinArea(i); + if (i == Num_layers - 1) { + if (min_area[i] <= LefGetViaWidth(i - 1, i, 0) * LefGetViaWidth(i - 1, i, 1)) + need_check[i] = (u_char)0; + else { + need_check[i] = (u_char)1; + checks++; + } + } + else { + if (min_area[i] <= LefGetViaWidth(i, i, 0) * LefGetViaWidth(i, i, 1)) + need_check[i] = (u_char)0; + else { + need_check[i] = (u_char)1; + checks++; + } + } + } + + if (checks == 0) violations = 0; // No minimum area violations possible. + + while (violations != 0) { // Keep doing until all min. area violations are gone + violations = 0; + lrcur = lrend; + lrprev = lrend->next; + + while (lrprev != NULL) { + lrppre = lrprev->next; + if (lrppre == NULL) break; + collide = FALSE; + while ((lrcur->layer != lrprev->layer) && (lrprev->layer != lrppre->layer) + && (need_check[lrprev->layer] == (u_char)1)) { + // Isolated via inside stack found + + violations++; + cx = lrprev->x1; + cy = lrprev->y1; + cl = lrprev->layer; + mincost = MAXRT; + + // Check all four positions around the contact for a + // free position to occupy. Positions in the preferred + // direction of the route layer take precedence. + + o = LefGetRouteOrientation(cl); + for (orient = 0; orient < 2; orient++) { + if (o == orient) { + if (cy < NumChannelsY - 1) { + dy = cy + 1; // Check north + pri = &OBS2VAL(cx, dy, cl); + pflags = pri->flags; + cost = pri->prdata.cost; + if (collide && !(pflags & (PR_COST | PR_SOURCE)) && + (pri->prdata.net < MAXNETNUM)) { + pflags = 0; + cost = ConflictCost; + } + if (pflags & PR_COST) { + pflags &= ~PR_COST; + if ((pflags & PR_PRED_DMASK) != PR_PRED_NONE && + cost < mincost) { + mincost = cost; + minx = cx; + miny = dy; + } + } + } + + if (cy > 0) { + dy = cy - 1; // Check south + pri = &OBS2VAL(cx, dy, cl); + pflags = pri->flags; + cost = pri->prdata.cost; + if (collide && !(pflags & (PR_COST | PR_SOURCE)) && + (pri->prdata.net < MAXNETNUM)) { + pflags = 0; + cost = ConflictCost; + } + if (pflags & PR_COST) { + pflags &= ~PR_COST; + if ((pflags & PR_PRED_DMASK) != PR_PRED_NONE && + cost < mincost) { + mincost = cost; + minx = cx; + miny = dy; + } + } + } + } + else { + if (cx < NumChannelsX - 1) { + dx = cx + 1; // Check to the right + pri = &OBS2VAL(dx, cy, cl); + pflags = pri->flags; + cost = pri->prdata.cost; + if (collide && !(pflags & (PR_COST | PR_SOURCE)) && + (pri->prdata.net < MAXNETNUM)) { + pflags = 0; + cost = ConflictCost; + } + if (pflags & PR_COST) { + pflags &= ~PR_COST; + if ((pflags & PR_PRED_DMASK) != PR_PRED_NONE && + cost < mincost) { + mincost = cost; + minx = dx; + miny = cy; + } + } + } + + if (cx > 0) { + dx = cx - 1; // Check to the left + pri = &OBS2VAL(dx, cy, cl); + pflags = pri->flags; + cost = pri->prdata.cost; + if (collide && !(pflags & (PR_COST | PR_SOURCE)) && + (pri->prdata.net < MAXNETNUM)) { + pflags = 0; + cost = ConflictCost; + } + if (pflags & PR_COST) { + pflags &= ~PR_COST; + if ((pflags & PR_PRED_DMASK) != PR_PRED_NONE && + cost < mincost) { + mincost = cost; + minx = dx; + miny = cy; + } + } + } + } + } + + // Was there an available position? If so, modify + // records to route to and from this position. + + if (mincost < MAXRT) { + + newlr = (POINT)malloc(sizeof(struct point_)); + newlr->x1 = minx; + newlr->y1 = miny; + newlr->layer = cl; + + newlr2 = (POINT)malloc(sizeof(struct point_)); + newlr2->x1 = cx; + newlr2->y1 = cy; + newlr2->layer = cl; + + lrprev->next = newlr; + newlr->next = newlr2; + newlr2->next = lrppre; + + break; /* Found a solution; done. */ + } + + else if (stage == 0) { + // On the first stage, we call it an error and move + // on to the next net. This is a bit conservative, + // but it happens rarely. + + if (Verbose > 0) + Fprintf(stderr, "Failed to reserve sufficient area " + "at grid point %d %d.\n", + lrcur->x1, lrcur->y1); + violations = 0; + rval = 0; + goto cleanup; + } + else { + if (collide == TRUE) { + Fprintf(stderr, "Failed to reserve sufficient area " + "at grid point %d %d; position may " + "not be routable.\n", + lrcur->x1, lrcur->y1); + violations = 0; + rval = 0; + goto cleanup; + } + + // On the second stage, we will run through the + // search again, but allow overwriting other + // nets, which will be treated like other colliding + // nets in the regular route path search. + + collide = TRUE; + } + } + lrcur = lrprev; + lrprev = lrppre; + } + } + } + + /* Done handling stacked contacts */ lrend = lrtop; lrcur = lrtop; @@ -2575,7 +2936,10 @@ int writeback_route(ROUTE rt) lay2 = (seg->segtype & ST_VIA) ? seg->layer + 1 : seg->layer; dir1 = OBSVAL(seg->x1, seg->y1, seg->layer) & PINOBSTRUCTMASK; - dir2 = OBSVAL(seg->x2, seg->y2, lay2) & PINOBSTRUCTMASK; + if (lay2 < Num_layers) + dir2 = OBSVAL(seg->x2, seg->y2, lay2) & PINOBSTRUCTMASK; + else + dir2 = 0; writeback_segment(seg, netnum); @@ -27,13 +27,35 @@ /*--------------------------------------------------------------*/ NODEINFO -SetNodeinfo(int gridx, int gridy, int layer) +SetNodeinfo(int gridx, int gridy, int layer, NODE node) { + DPOINT dp; NODEINFO *lnodeptr; lnodeptr = &NODEIPTR(gridx, gridy, layer); if (*lnodeptr == NULL) { *lnodeptr = (NODEINFO)calloc(1, sizeof(struct nodeinfo_)); + + /* Make sure this position is in the list of node's taps. Add */ + /* it if it is not there. */ + + for (dp = (DPOINT)node->taps; dp; dp = dp->next) + if (dp->gridx == gridx && dp->gridy == gridy && dp->layer == layer) + break; + if (dp == NULL) + for (dp = (DPOINT)node->extend; dp; dp = dp->next) + if (dp->gridx == gridx && dp->gridy == gridy && dp->layer == layer) + break; + if (dp == NULL) { + dp = (DPOINT)malloc(sizeof(struct dpoint_)); + dp->gridx = gridx; + dp->gridy = gridy; + dp->layer = layer; + dp->x = (gridx * PitchX) + Xlowerbound; + dp->y = (gridy * PitchY) + Ylowerbound; + dp->next = node->extend; + node->extend = dp; + } } return *lnodeptr; } @@ -87,7 +109,7 @@ count_reachable_taps() double dx, dy; for (l = 0; l < Num_layers; l++) { - for (j = 0; j < NumChannelsX[l] * NumChannelsY[l]; j++) { + for (j = 0; j < NumChannelsX * NumChannelsY; j++) { if (Nodeinfo[l][j]) { node = Nodeinfo[l][j]->nodeloc; if (node != NULL) { @@ -111,23 +133,23 @@ count_reachable_taps() if (node->numtaps == 0) { /* Will try more than one via if available */ - for (orient = 0; orient < 2; orient++) { + for (orient = 0; orient < 4; orient += 2) { for (ds = g->taps[i]; ds; ds = ds->next) { deltax = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 0, orient); deltay = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 1, orient); - gridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]) - 1; + gridx = (int)((ds->x1 - Xlowerbound) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - if (dx > ds->x2 || gridx >= NumChannelsX[ds->layer]) break; + dx = (gridx * PitchX) + Xlowerbound; + if (dx > ds->x2 || gridx >= NumChannelsX) break; if (((dx - ds->x1 + EPS) > deltax) && ((ds->x2 - dx + EPS) > deltax)) { gridy = (int)((ds->y1 - Ylowerbound) - / PitchY[ds->layer]) - 1; + / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if (dy > ds->y2 || gridy >= NumChannelsY[ds->layer]) + dy = (gridy * PitchY) + Ylowerbound; + if (dy > ds->y2 || gridy >= NumChannelsY) break; if (((dy - ds->y1 + EPS) > deltay) && @@ -139,13 +161,13 @@ count_reachable_taps() // Grid position is clear for placing a via - if (orient == 0) - Fprintf(stderr, "Tap position (%g, %g)" + if ((orient == 0) && (Verbose > 1)) + Fprintf(stdout, "Tap position (%g, %g)" " appears to be technically routable" " so it is being forced routable.\n", dx, dy); - else - Fprintf(stderr, "Tap position (%g, %g)" + else if (Verbose > 1) + Fprintf(stdout, "Tap position (%g, %g)" " appears to be technically routable" " with alternate via, so it is being" " forced routable.\n", dx, dy); @@ -154,13 +176,15 @@ count_reachable_taps() (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | (u_int)node->netnum; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); lnode->nodeloc = node; lnode->nodesav = node; - /* If we got to orient = 1, mark NI_NO_VIAX */ - if (orient == 1) lnode->flags |= NI_NO_VIAX; - + /* If we got to orient = 2, mark NI_NO_VIAX */ + if (orient == 2) lnode->flags |= NI_NO_VIAX; + /* This is a bit harsh, but should work */ + else lnode->flags |= NI_NO_VIAY; node->numtaps++; } } @@ -183,30 +207,29 @@ count_reachable_taps() int dir, mask, tapx, tapy, tapl; /* Will try more than one via if available */ - for (orient = 0; orient < 2; orient++) { + for (orient = 0; orient < 4; orient += 2) { /* Initialize mindist to a large value */ mask = 0; - mindist = PitchX[Num_layers - 1] + PitchY[Num_layers - 1]; + mindist = PitchX + PitchY; dir = 0; /* Indicates no solution found */ for (ds = g->taps[i]; ds; ds = ds->next) { deltax = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 0, orient); deltay = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 1, orient); - gridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]) - 1; + gridx = (int)((ds->x1 - Xlowerbound) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - if (dx > ds->x2 || gridx >= NumChannelsX[ds->layer]) break; + dx = (gridx * PitchX) + Xlowerbound; + if (dx > ds->x2 || gridx >= NumChannelsX) break; if (((dx - ds->x1 + EPS) > -deltax) && ((ds->x2 - dx + EPS) > -deltax)) { - gridy = (int)((ds->y1 - Ylowerbound) - / PitchY[ds->layer]) - 1; + gridy = (int)((ds->y1 - Ylowerbound) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if (dy > ds->y2 || gridy >= NumChannelsY[ds->layer]) + dy = (gridy * PitchY) + Ylowerbound; + if (dy > ds->y2 || gridy >= NumChannelsY) break; // Check that the grid position is inside the @@ -307,22 +330,23 @@ count_reachable_taps() if (mask != 0) { // Grid position is clear for placing a via - Fprintf(stderr, "Tap position (%d, %d) appears to be" - " technically routable with an offset, so" - " it is being forced routable.\n", - tapx, tapy); + if (Verbose > 1) + Fprintf(stdout, "Tap position (%d, %d) appears to be" + " technically routable with an offset, so" + " it is being forced routable.\n", + tapx, tapy); OBSVAL(tapx, tapy, tapl) = (OBSVAL(tapx, tapy, tapl) & BLOCKED_MASK) | mask | (u_int)node->netnum; - lnode = SetNodeinfo(tapx, tapy, tapl); + lnode = SetNodeinfo(tapx, tapy, tapl, node); lnode->nodeloc = node; lnode->nodesav = node; lnode->stub = dist; lnode->flags |= dir; - /* If we got to orient = 1 then mark NI_NO_VIAX */ - if (orient == 1) lnode->flags |= NI_NO_VIAX; + /* If we got to orient = 2 then mark NI_NO_VIAX */ + if (orient == 2) lnode->flags |= NI_NO_VIAX; node->numtaps++; } @@ -367,22 +391,27 @@ count_reachable_taps() void check_variable_pitch(int l, int *hptr, int *vptr) { int o, hnum, vnum; - double vpitch, hpitch, wvia; + double vpitch, hpitch, wvia, wviax, wviay; o = LefGetRouteOrientation(l); - // Pick the best via size for the layer. Usually this means the - // via whose base is at layer - 1, because the top metal layer - // will either have the same width or a larger width. - - // Note that when "horizontal" (o = 1) is passed to LefGetViaWidth, + // Note that when "horizontal" (o = 1) is passed to LefGetXYViaWidth, // it returns the via width top-to-bottom (orient meaning is - // reversed for LefGetViaWidth), which is what we want. . . + // reversed for LefGetXYViaWidth), which is what we want. . . - if (l == 0) - wvia = LefGetViaWidth(l, l, o); - else - wvia = LefGetViaWidth(l - 1, l, o); + // Try both via orientations and choose the best, assuming that it is + // a lot easier to rotate and shift vias around than it is to lose + // half the routing grid. + + if (l == 0) { + wviax = LefGetXYViaWidth(l, l, o, 0); + wviay = LefGetXYViaWidth(l, l, o, 3); + } + else { + wviax = LefGetXYViaWidth(l - 1, l, o, 0); + wviay = LefGetXYViaWidth(l - 1, l, o, 3); + } + wvia = (wviax < wviay) ? wviax : wviay; if (o == 1) { // Horizontal route vpitch = LefGetRoutePitch(l); @@ -399,12 +428,12 @@ void check_variable_pitch(int l, int *hptr, int *vptr) } vnum = 1; - while (vpitch > PitchY[l] + EPS) { + while (vpitch > PitchY + EPS) { vpitch /= 2.0; vnum++; } hnum = 1; - while (hpitch > PitchX[l] + EPS) { + while (hpitch > PitchX + EPS) { hpitch /= 2.0; hnum++; } @@ -448,9 +477,9 @@ void create_obstructions_from_variable_pitch(void) if (hnum > 1 && vnum == 1) vnum++; if (vnum > 1 || hnum > 1) { - for (x = 0; x < NumChannelsX[l]; x++) { + for (x = 0; x < NumChannelsX; x++) { if (x % hnum == 0) continue; - for (y = 0; y < NumChannelsY[l]; y++) { + for (y = 0; y < NumChannelsY; y++) { if (y % vnum == 0) continue; // If the grid position itself is a node, don't restrict @@ -467,11 +496,11 @@ void create_obstructions_from_variable_pitch(void) else if ((y > 0) && ((lnode = NODEIPTR(x , y - 1, l)) != NULL) && (lnode->nodeloc != NULL)) OBSVAL(x, y, l) = BLOCKED_MASK & ~BLOCKED_S; - else if ((x < NumChannelsX[l] - 1) + else if ((x < NumChannelsX - 1) && ((lnode = NODEIPTR(x + 1, y, l)) != NULL) && (lnode->nodeloc != NULL)) OBSVAL(x, y, l) = BLOCKED_MASK & ~BLOCKED_E; - else if ((y < NumChannelsY[l] - 1) + else if ((y < NumChannelsY - 1) && ((lnode = NODEIPTR(x, y + 1, l)) != NULL) && (lnode->nodeloc != NULL)) OBSVAL(x, y, l) = BLOCKED_MASK & ~BLOCKED_N; @@ -493,7 +522,7 @@ void create_obstructions_from_variable_pitch(void) static void disable_gridpos(int x, int y, int lay) { - int apos = OGRID(x, y, lay); + int apos = OGRID(x, y); Obs[lay][apos] = (u_int)(NO_NET | OBSTRUCT_MASK); if (Nodeinfo[lay][apos]) { @@ -517,7 +546,7 @@ count_pinlayers(void) Pinlayers = 0; for (l = 0; l < Num_layers; l++) { - for (j = 0; j < NumChannelsX[l] * NumChannelsY[l]; j++) { + for (j = 0; j < NumChannelsX * NumChannelsY; j++) { if (Nodeinfo[l][j]) { Pinlayers = l + 1; break; @@ -542,15 +571,13 @@ count_pinlayers(void) /*--------------------------------------------------------------*/ static void -check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy) +check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy, double delta) { u_int *obsptr; float dist; - NODEINFO lnode; obsptr = &(OBSVAL(gridx, gridy, ds->layer)); dist = OBSINFO(gridx, gridy, ds->layer); - lnode = NODEIPTR(gridx, gridy, ds->layer); // Grid point is inside obstruction + halo. *obsptr |= NO_NET; @@ -560,7 +587,6 @@ check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy) *obsptr |= OBSTRUCT_MASK; else { - // Make more detailed checks in each direction if (dy <= ds->y1) { @@ -579,6 +605,7 @@ check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy) } else *obsptr |= OBSTRUCT_MASK; } + if (dx <= ds->x1) { if ((*obsptr & (OBSTRUCT_MASK & ~OBSTRUCT_E)) == 0) { if ((dist == 0) || ((ds->x1 - dx) < dist)) @@ -595,7 +622,7 @@ check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy) } else *obsptr |= OBSTRUCT_MASK; } - } + } } /*--------------------------------------------------------------*/ @@ -604,14 +631,17 @@ check_obstruct(int gridx, int gridy, DSEG ds, double dx, double dy) /* whether the obstruction is wide or narrow metal, if the */ /* spacing rules are graded according to metal width, and if a */ /* via placed at the position is or is not symmetric in X and Y */ +/* "orient" is the via orientation, of which there are four; */ +/* however, as this only concerns the via bottom layer, the */ +/* two orientations are represented by orient = 0 and 2. */ /*--------------------------------------------------------------*/ -static double get_via_clear(int lay, int horiz, DSEG rect) { +static double get_via_clear(int lay, int horiz, int orient, DSEG rect) { double vdelta, v2delta, mdelta, mwidth; - vdelta = LefGetViaWidth(lay, lay, 1 - horiz); + vdelta = LefGetXYViaWidth(lay, lay, 1 - horiz, orient); if (lay > 0) { - v2delta = LefGetViaWidth(lay - 1, lay, 1 - horiz); + v2delta = LefGetXYViaWidth(lay - 1, lay, 1 - horiz, orient); if (v2delta > vdelta) vdelta = v2delta; } vdelta = vdelta / 2.0; @@ -670,8 +700,8 @@ void clip_gate_taps(void) lay = dp->layer; if (dp->gridx < 0 || dp->gridy < 0 || - dp->gridx >= NumChannelsX[lay] || - dp->gridy >= NumChannelsY[lay]) { + dp->gridx >= NumChannelsX || + dp->gridy >= NumChannelsY) { Fprintf(stderr, "Tap of port of node %d of net %s" " is outside of route area\n", node->nodenum, node->netname); @@ -710,7 +740,7 @@ void create_obstructions_from_gates(void) { GATE g; DSEG ds; - int i, gridx, gridy; + int i, gridx, gridy, orient; double deltax, deltay, delta[MAX_LAYERS]; double dx, dy, deltaxy; @@ -725,23 +755,34 @@ void create_obstructions_from_gates(void) // that the position is not routable under any condition. for (g = Nlgates; g; g = g->next) { - for (ds = g->obs; ds; ds = ds->next) { + orient = 0; + for (ds = g->obs;; ds = ds->next) { + // Run through ds list twice, checking against horizontally and + // vertically oriented vias. + + if (ds == NULL) { + if (orient == 2) + break; + else { + orient = 2; + ds = g->obs; + if (ds == NULL) break; + } + } - deltax = get_via_clear(ds->layer, 1, ds); - gridx = (int)((ds->x1 - Xlowerbound - deltax) - / PitchX[ds->layer]) - 1; + deltax = get_via_clear(ds->layer, 1, orient, ds); + gridx = (int)((ds->x1 - Xlowerbound - deltax) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; + dx = (gridx * PitchX) + Xlowerbound; if ((dx + EPS) > (ds->x2 + deltax) - || gridx >= NumChannelsX[ds->layer]) break; + || gridx >= NumChannelsX) break; else if ((dx - EPS) > (ds->x1 - deltax) && gridx >= 0) { - deltay = get_via_clear(ds->layer, 0, ds); - gridy = (int)((ds->y1 - Ylowerbound - deltay) - / PitchY[ds->layer]) - 1; + deltay = get_via_clear(ds->layer, 0, orient, ds); + gridy = (int)((ds->y1 - Ylowerbound - deltay) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; + dy = (gridy * PitchY) + Ylowerbound; if ((dy + EPS) > (ds->y2 + deltay) - || gridy >= NumChannelsY[ds->layer]) break; + || gridy >= NumChannelsY) break; if ((dy - EPS) > (ds->y1 - deltay) && gridy >= 0) { double s, edist, xp, yp; @@ -767,27 +808,10 @@ void create_obstructions_from_gates(void) } else edist = 0; - if ((edist + EPS) < (s * s)) { - - // If it clears distance for a route layer but not - // vias, then block vias only. - /* (This is restrictive and unnecessary) */ - /* - deltaxy = get_route_clear(ds->layer, ds); - if (((dx - EPS) <= (ds->x1 - deltaxy)) || - ((dx + EPS) >= (ds->x2 + deltaxy)) || - ((dy - EPS) <= (ds->y1 - deltaxy)) || - ((dy + EPS) >= (ds->y2 + deltaxy))) { - block_route(gridx, gridy, ds->layer, UP); - block_route(gridx, gridy, ds->layer, DOWN); - } - else - */ - check_obstruct(gridx, gridy, ds, dx, dy); - } - else { + if ((edist + EPS) < (s * s)) + check_obstruct(gridx, gridy, ds, dx, dy, s); + else edist = 0; // diagnostic break - } } gridy++; } @@ -813,21 +837,19 @@ void create_obstructions_from_gates(void) } for (ds = g->taps[i]; ds; ds = ds->next) { - deltax = get_via_clear(ds->layer, 1, ds); - gridx = (int)((ds->x1 - Xlowerbound - deltax) - / PitchX[ds->layer]) - 1; + deltax = get_via_clear(ds->layer, 1, orient, ds); + gridx = (int)((ds->x1 - Xlowerbound - deltax) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; + dx = (gridx * PitchX) + Xlowerbound; if (dx > (ds->x2 + deltax) - || gridx >= NumChannelsX[ds->layer]) break; + || gridx >= NumChannelsX) break; else if (dx >= (ds->x1 - deltax) && gridx >= 0) { - deltay = get_via_clear(ds->layer, 0, ds); - gridy = (int)((ds->y1 - Ylowerbound - deltay) - / PitchY[ds->layer]) - 1; + deltay = get_via_clear(ds->layer, 0, orient, ds); + gridy = (int)((ds->y1 - Ylowerbound - deltay) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; + dy = (gridy * PitchY) + Ylowerbound; if ((dy + EPS) > (ds->y2 + deltay) - || gridy >= NumChannelsY[ds->layer]) break; + || gridy >= NumChannelsY) break; if ((dy - EPS) >= (ds->y1 - deltay) && gridy >= 0) { double s, edist = 0.0, xp, yp; @@ -854,24 +876,8 @@ void create_obstructions_from_gates(void) } else edist = 0; - if ((edist + EPS) < (s * s)) { - - // If it clears distance for a route layer but not - // vias, then block vias only. - /* (This is restrictive and unnecessary) */ - /* - deltaxy = get_route_clear(ds->layer, ds); - if (((dx - EPS) < (ds->x1 - deltaxy)) || - ((dx + EPS) > (ds->x2 + deltaxy)) || - ((dy - EPS) < (ds->y1 - deltaxy)) || - ((dy + EPS) > (ds->y2 + deltaxy))) { - block_route(gridx, gridy, ds->layer, UP); - block_route(gridx, gridy, ds->layer, DOWN); - } - else - */ - check_obstruct(gridx, gridy, ds, dx, dy); - } + if ((edist + EPS) < (s * s)) + check_obstruct(gridx, gridy, ds, dx, dy, s); } gridy++; } @@ -894,21 +900,20 @@ void create_obstructions_from_gates(void) } for (ds = UserObs; ds; ds = ds->next) { - gridx = (int)((ds->x1 - Xlowerbound - delta[ds->layer]) - / PitchX[ds->layer]) - 1; + if (ds->layer >= Num_layers) continue; + gridx = (int)((ds->x1 - Xlowerbound - delta[ds->layer]) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; + dx = (gridx * PitchX) + Xlowerbound; if (dx > (ds->x2 + delta[ds->layer]) - || gridx >= NumChannelsX[ds->layer]) break; + || gridx >= NumChannelsX) break; else if (dx >= (ds->x1 - delta[ds->layer]) && gridx >= 0) { - gridy = (int)((ds->y1 - Ylowerbound - delta[ds->layer]) - / PitchY[ds->layer]) - 1; + gridy = (int)((ds->y1 - Ylowerbound - delta[ds->layer]) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; + dy = (gridy * PitchY) + Ylowerbound; if (dy > (ds->y2 + delta[ds->layer]) - || gridy >= NumChannelsY[ds->layer]) break; + || gridy >= NumChannelsY) break; if (dy >= (ds->y1 - delta[ds->layer]) && gridy >= 0) - check_obstruct(gridx, gridy, ds, dx, dy); + check_obstruct(gridx, gridy, ds, dx, dy, delta[i]); gridy++; } @@ -1025,7 +1030,8 @@ void create_obstructions_inside_nodes(void) DSEG ds; u_int dir, mask, k; int i, gridx, gridy; - double dx, dy, xdist; + double dx, dy, xdist, vwx, vwy; + u_char o0okay, o2okay, duplicate; float dist; // For each node terminal (gate pin), mark each grid position with the @@ -1048,32 +1054,34 @@ void create_obstructions_inside_nodes(void) // First mark all areas inside node geometry boundary. for (ds = g->taps[i]; ds; ds = ds->next) { - gridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]) - 1; + gridx = (int)((ds->x1 - Xlowerbound) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - if (dx > ds->x2 || gridx >= NumChannelsX[ds->layer]) break; + dx = (gridx * PitchX) + Xlowerbound; + if (dx > ds->x2 || gridx >= NumChannelsX) break; else if (dx >= ds->x1 && gridx >= 0) { - gridy = (int)((ds->y1 - Ylowerbound) / PitchY[ds->layer]) - 1; + gridy = (int)((ds->y1 - Ylowerbound) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if (dy > ds->y2 || gridy >= NumChannelsY[ds->layer]) break; + dy = (gridy * PitchY) + Ylowerbound; + if (dy > ds->y2 || gridy >= NumChannelsY) break; // Area inside defined pin geometry if (dy > ds->y1 && gridy >= 0) { int orignet = OBSVAL(gridx, gridy, ds->layer); + duplicate = FALSE; + lnode = NULL; if ((orignet & ROUTED_NET_MASK & ~ROUTED_NET) == (u_int)node->netnum) { - // Duplicate tap point, or pre-existing - // route. Don't re-process it if it is - // a duplicate. + // Duplicate tap point, or pre-existing route. + // Re-process carefully. Check for alternate + // restrictions. This geometry may be better + // than the last one(s) processed. + if (((lnode = NODEIPTR(gridx, gridy, ds->layer)) != NULL) - && (lnode->nodeloc != NULL)) { - gridy++; - continue; - } + && (lnode->nodeloc != NULL)) + duplicate = TRUE; } else if (!(orignet & NO_NET) && @@ -1176,14 +1184,48 @@ void create_obstructions_inside_nodes(void) } } - OBSVAL(gridx, gridy, ds->layer) + if (!duplicate) { + OBSVAL(gridx, gridy, ds->layer) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | (u_int)node->netnum | mask; - lnode = SetNodeinfo(gridx, gridy, ds->layer); - lnode->nodeloc = node; - lnode->nodesav = node; - lnode->stub = dist; - lnode->flags |= dir; + if (!lnode) + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); + lnode->nodeloc = node; + lnode->nodesav = node; + lnode->stub = dist; + lnode->flags |= dir; + } + + /* If a horizontal or vertical via fits completely */ + /* inside the pin but the other orientation */ + /* doesn't, then mark as prohibiting the other */ + /* orientation. */ + + vwx = LefGetXYViaWidth(ds->layer, ds->layer, 0, 0) / 2.0; + vwy = LefGetXYViaWidth(ds->layer, ds->layer, 1, 0) / 2.0; + if ((dx - vwx > ds->x1 - EPS) && + (dx + vwx < ds->x2 + EPS) && + (dy - vwy > ds->y1 - EPS) && + (dy + vwy < ds->y2 + EPS)) { + o0okay = TRUE; + } else { + o0okay = FALSE; + } + vwx = LefGetXYViaWidth(ds->layer, ds->layer, 0, 2) / 2.0; + vwy = LefGetXYViaWidth(ds->layer, ds->layer, 1, 2) / 2.0; + if ((dx - vwx > ds->x1 - EPS) && + (dx + vwx < ds->x2 + EPS) && + (dy - vwy > ds->y1 - EPS) && + (dy + vwy < ds->y2 + EPS)) { + o2okay = TRUE; + } else { + o2okay = FALSE; + } + if ((o0okay == TRUE) && (o2okay == FALSE)) + lnode->flags |= NI_NO_VIAY; + else if ((o0okay == FALSE) && (o2okay == TRUE)) + lnode->flags |= NI_NO_VIAX; } else if ((orignet & NO_NET) && ((orignet & OBSTRUCT_MASK) != OBSTRUCT_MASK)) { @@ -1192,7 +1234,7 @@ void create_obstructions_inside_nodes(void) // Check that we have not created a PINOBSTRUCT // route directly over this point. - if (ds->layer < Num_layers - 1) { + if ((!duplicate) && (ds->layer < Num_layers - 1)) { k = OBSVAL(gridx, gridy, ds->layer + 1); if (k & PINOBSTRUCTMASK) { if ((k & ROUTED_NET_MASK) != (u_int)node->netnum) { @@ -1246,29 +1288,9 @@ void create_obstructions_outside_nodes(void) GATE g; DSEG ds; u_int dir, mask, k; - int i, gridx, gridy; - double dx, dy, deltax, deltay, xdist; + int i, gridx, gridy, orient; + double dx, dy, xdist, deltax, deltay; float dist; - double offmaxx[MAX_LAYERS], offmaxy[MAX_LAYERS]; - - // Use a more conservative definition of keepout, to include via - // widths, which may be bigger than route widths. - - for (i = 0; i < Num_layers; i++) { - offmaxx[i] = PitchX[i] - LefGetRouteSpacing(i) - - 0.5 * (LefGetRouteWidth(i) + LefGetViaWidth(i, i, 0)); - offmaxy[i] = PitchY[i] - LefGetRouteSpacing(i) - - 0.5 * (LefGetRouteWidth(i) + LefGetViaWidth(i, i, 1)); - } - - // When we place vias at an offset, they have to satisfy the spacing - // requirements for the via's top layer, as well. So take the least - // maximum offset of each layer and the layer above it. - - for (i = 0; i < Num_layers - 1; i++) { - offmaxx[i] = MIN(offmaxx[i], offmaxx[i + 1]); - offmaxy[i] = MIN(offmaxy[i], offmaxy[i + 1]); - } // For each node terminal (gate pin), mark each grid position with the // net number. This overrides any obstruction that may be placed at that @@ -1292,31 +1314,45 @@ void create_obstructions_outside_nodes(void) // all inside areas because the tap rectangles often overlap, // and one rectangle's halo may be inside another tap. - for (ds = g->taps[i]; ds; ds = ds->next) { + orient = 0; + for (ds = g->taps[i];; ds = ds->next) { + if (ds == NULL) { + if (orient == 2) + break; + else { + orient = 2; + ds = g->taps[i]; + if (ds == NULL) break; + } + } // Note: Should be handling get_route_clear as a less // restrictive case, as was done above. - deltax = get_via_clear(ds->layer, 1, ds); - gridx = (int)((ds->x1 - Xlowerbound - deltax) - / PitchX[ds->layer]) - 1; + deltax = get_via_clear(ds->layer, 1, orient, ds); + gridx = (int)((ds->x1 - Xlowerbound - deltax) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; + dx = (gridx * PitchX) + Xlowerbound; + + // Check if obstruction position is too close for + // a via in either orientation. The position can + // be marked as prohibiting one orientation or the + // other. - if ((dx + EPS) > (ds->x2 + deltax) || - gridx >= NumChannelsX[ds->layer]) + if (((dx + EPS) > (ds->x2 + deltax)) || + (gridx >= NumChannelsX)) break; else if ((dx - EPS) > (ds->x1 - deltax) && gridx >= 0) { - deltay = get_via_clear(ds->layer, 0, ds); - gridy = (int)((ds->y1 - Ylowerbound - deltay) - / PitchY[ds->layer]) - 1; + deltay = get_via_clear(ds->layer, 0, orient, ds); + gridy = (int)((ds->y1 - Ylowerbound - deltay) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if ((dy + EPS) > (ds->y2 + deltay) || - gridy >= NumChannelsY[ds->layer]) + dy = (gridy * PitchY) + Ylowerbound; + + if (((dy + EPS) > (ds->y2 + deltay)) || + (gridy >= NumChannelsY)) break; // 2nd pass on area inside defined pin geometry, @@ -1341,11 +1377,20 @@ void create_obstructions_outside_nodes(void) else if ((orignet & NO_NET) && ((orignet & OBSTRUCT_MASK) != OBSTRUCT_MASK)) { - double sdistx = LefGetViaWidth(ds->layer, ds->layer, 0) - / 2.0 + LefGetRouteSpacing(ds->layer); - double sdisty = LefGetViaWidth(ds->layer, ds->layer, 1) - / 2.0 + LefGetRouteSpacing(ds->layer); - double offd; + double sdistxx, sdistxy, sdistyx, sdistyy, offd; + + sdistxx = LefGetXYViaWidth(ds->layer, ds->layer, + 0, 0) / 2.0 + + LefGetRouteSpacing(ds->layer); + sdistxy = LefGetXYViaWidth(ds->layer, ds->layer, + 0, 2) / 2.0 + + LefGetRouteSpacing(ds->layer); + sdistyx = LefGetXYViaWidth(ds->layer, ds->layer, + 1, 0) / 2.0 + + LefGetRouteSpacing(ds->layer); + sdistyy = LefGetXYViaWidth(ds->layer, ds->layer, + 1, 2) / 2.0 + + LefGetRouteSpacing(ds->layer); // Define a maximum offset we can have in X or // Y above which the placement of a via will @@ -1364,86 +1409,111 @@ void create_obstructions_outside_nodes(void) // the obstruction to resolve the DRC error. // Make sure we have marked this as a node. - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, node); lnode->nodeloc = node; lnode->nodesav = node; OBSVAL(gridx, gridy, ds->layer) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | (u_int)node->netnum; + offd = OBSINFO(gridx, gridy, ds->layer); if (orignet & OBSTRUCT_N) { - offd = -(sdisty - OBSINFO(gridx, gridy, ds->layer)); - if (offd >= -offmaxy[ds->layer]) { - OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; - lnode->offset = offd; - lnode->flags |= NI_OFFSET_NS; + if (sdistyy - offd > EPS) { + lnode->flags |= NI_NO_VIAY; + if (sdistyx - offd > EPS) { + /* Cannot route cleanly, so use offset */ + if (offd - sdistyx > PitchX / 2.0) + /* Offset distance is too large */ + maxerr = 1; + else { - /* If position above has obstruction, then */ - /* add up/down block to prevent vias. */ + OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; + lnode->offset = offd - sdistyx; + lnode->flags |= NI_OFFSET_NS; - if ((ds->layer < Num_layers - 1) && - (gridy > 0) && - (OBSVAL(gridx, gridy - 1, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + /* If position above has obstruction, then */ + /* add up/down block to prevent vias. */ + + if ((ds->layer < Num_layers - 1) && + (gridy > 0) && + (OBSVAL(gridx, gridy - 1, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + } + } } - else maxerr = 1; } else if (orignet & OBSTRUCT_S) { - offd = sdisty - OBSINFO(gridx, gridy, ds->layer); - if (offd <= offmaxy[ds->layer]) { - OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; - lnode->offset = offd; - lnode->flags |= NI_OFFSET_NS; - - /* If position above has obstruction, then */ - /* add up/down block to prevent vias. */ - - if ((ds->layer < Num_layers - 1) && - (gridy < NumChannelsY[ds->layer + 1] - - 1) && - (OBSVAL(gridx, gridy + 1, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if (sdistyy - offd > EPS) { + lnode->flags |= NI_NO_VIAY; + if (sdistyx - offd > EPS) { + if (offd - sdistyx > PitchX / 2.0) + /* Offset distance is too large */ + maxerr = 1; + else { + OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; + lnode->offset = sdistyx - offd; + lnode->flags |= NI_OFFSET_NS; + + /* If position above has obstruction, then */ + /* add up/down block to prevent vias. */ + + if ((ds->layer < Num_layers - 1) && + (gridy < NumChannelsY - 1) && + (OBSVAL(gridx, gridy + 1, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + } + } } - else maxerr = 1; } else if (orignet & OBSTRUCT_E) { - offd = -(sdistx - OBSINFO(gridx, gridy, ds->layer)); - if (offd >= -offmaxx[ds->layer]) { - OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; - lnode->offset = offd; - lnode->flags |= NI_OFFSET_EW; + if (sdistxx - offd > EPS) { + lnode->flags |= NI_NO_VIAX; + if (sdistxy - offd > EPS) { + if (offd - sdistxy > PitchY / 2.0) + /* Offset distance is too large */ + maxerr = 1; + else { + OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; + lnode->offset = offd - sdistxy; + lnode->flags |= NI_OFFSET_EW; - /* If position above has obstruction, then */ - /* add up/down block to prevent vias. */ + /* If position above has obstruction, then */ + /* add up/down block to prevent vias. */ - if ((ds->layer < Num_layers - 1) && - (gridx > 0) && - (OBSVAL(gridx - 1, gridy, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if ((ds->layer < Num_layers - 1) && + (gridx > 0) && + (OBSVAL(gridx - 1, gridy, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + } + } } - else maxerr = 1; } else if (orignet & OBSTRUCT_W) { - offd = sdistx - OBSINFO(gridx, gridy, ds->layer); - if (offd <= offmaxx[ds->layer]) { - OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; - lnode->offset = offd; - lnode->flags |= NI_OFFSET_EW; - - /* If position above has obstruction, then */ - /* add up/down block to prevent vias. */ - - if ((ds->layer < Num_layers - 1) && - (gridx < NumChannelsX[ds->layer] - - 1) && - (OBSVAL(gridx + 1, gridy, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if (sdistxx - offd > EPS) { + lnode->flags |= NI_NO_VIAX; + if (sdistxy - offd > EPS) { + if (offd - sdistxy > PitchY / 2.0) + /* Offset distance is too large */ + maxerr = 1; + else { + OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; + lnode->offset = sdistxy - offd; + lnode->flags |= NI_OFFSET_EW; + + /* If position above has obstruction, then */ + /* add up/down block to prevent vias. */ + + if ((ds->layer < Num_layers - 1) && + (gridx < NumChannelsX - 1) && + (OBSVAL(gridx + 1, gridy, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + } + } } - else maxerr = 1; } if (maxerr == 1) @@ -1573,7 +1643,7 @@ void create_obstructions_outside_nodes(void) if ((ds->layer < Num_layers - 1) && gridx < - (NumChannelsX[ds->layer] - 1) + (NumChannelsX - 1) && (OBSVAL(gridx + 1, gridy, ds->layer + 1) & OBSTRUCT_MASK)) @@ -1592,7 +1662,7 @@ void create_obstructions_outside_nodes(void) if ((ds->layer < Num_layers - 1) && gridy < - (NumChannelsY[ds->layer] - 1) + (NumChannelsY - 1) && (OBSVAL(gridx, gridy - 1, ds->layer + 1) & OBSTRUCT_MASK)) @@ -1850,7 +1920,7 @@ void create_obstructions_outside_nodes(void) } } - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, node); lnode->nodeloc = node; lnode->nodesav = node; @@ -1937,25 +2007,36 @@ void create_obstructions_outside_nodes(void) // Check tap to right if ((dx > ds->x2) && (gridx < - NumChannelsX[ds->layer] - 1)) { + NumChannelsX - 1)) { offset_net = OBSVAL(gridx + 1, gridy, ds->layer); if (offset_net == 0 || offset_net == othernet) { - xdist = 0.5 * LefGetViaWidth(ds->layer, - ds->layer, 0); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, + ds->layer, 0, orient); dist = ds->x2 - dx + xdist + LefGetRouteSpacing(ds->layer); - mask = OFFSET_TAP; - dir = NI_OFFSET_EW; - OBSVAL(gridx, gridy, ds->layer) |= mask; - lnode->offset = dist; - lnode->flags |= dir; - no_offsets = FALSE; - - if ((ds->layer < Num_layers - 1) && - (gridx > 0) && - (OBSVAL(gridx + 1, gridy, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + // Only accept an alternative solution if + // it has a smaller offset than previously + // found. + if ((no_offsets == FALSE) || + (fabs(lnode->offset) > fabs(dist))) { + mask = OFFSET_TAP; + dir = NI_OFFSET_EW; + OBSVAL(gridx, gridy, ds->layer) |= mask; + lnode->offset = dist; + lnode->flags &= ~(NI_OFFSET_NS); + lnode->flags |= dir; + no_offsets = FALSE; + + if ((ds->layer < Num_layers - 1) && + (gridx > 0) && + (OBSVAL(gridx + 1, gridy, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + else if ((ds->layer < Num_layers - 1) && + (gridx > 0) && + (dist > PitchX / 2)) + block_route(gridx, gridy, ds->layer, UP); + } } } @@ -1964,47 +2045,64 @@ void create_obstructions_outside_nodes(void) if ((dx < ds->x1) && (gridx > 0)) { offset_net = OBSVAL(gridx - 1, gridy, ds->layer); if (offset_net == 0 || offset_net == othernet) { - xdist = 0.5 * LefGetViaWidth(ds->layer, - ds->layer, 0); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, + ds->layer, 0, orient); dist = ds->x1 - dx - xdist - LefGetRouteSpacing(ds->layer); - mask = OFFSET_TAP; - dir = NI_OFFSET_EW; - OBSVAL(gridx, gridy, ds->layer) |= mask; - lnode->offset = dist; - lnode->flags |= dir; - no_offsets = FALSE; - - if ((ds->layer < Num_layers - 1) && - gridx < (NumChannelsX[ds->layer] - 1) && - (OBSVAL(gridx - 1, gridy, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if ((no_offsets == FALSE) || + (fabs(lnode->offset) > fabs(dist))) { + mask = OFFSET_TAP; + dir = NI_OFFSET_EW; + OBSVAL(gridx, gridy, ds->layer) |= mask; + lnode->offset = dist; + lnode->flags &= ~(NI_OFFSET_NS); + lnode->flags |= dir; + no_offsets = FALSE; + + if ((ds->layer < Num_layers - 1) && gridx < + (NumChannelsX - 1) && + (OBSVAL(gridx - 1, gridy, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + else if ((ds->layer < Num_layers - 1) && + gridx < + (NumChannelsX - 1) && + (dist < -PitchX / 2)) + block_route(gridx, gridy, ds->layer, UP); + } } } // Check tap up if ((dy > ds->y2) && (gridy < - NumChannelsY[ds->layer] - 1)) { + NumChannelsY - 1)) { offset_net = OBSVAL(gridx, gridy + 1, ds->layer); if (offset_net == 0 || offset_net == othernet) { - xdist = 0.5 * LefGetViaWidth(ds->layer, - ds->layer, 1); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, + ds->layer, 1, orient); dist = ds->y2 - dy + xdist + LefGetRouteSpacing(ds->layer); - mask = OFFSET_TAP; - dir = NI_OFFSET_NS; - OBSVAL(gridx, gridy, ds->layer) |= mask; - lnode->offset = dist; - lnode->flags |= dir; - no_offsets = FALSE; - - if ((ds->layer < Num_layers - 1) && - (gridy > 0) && - (OBSVAL(gridx, gridy + 1, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if ((no_offsets == FALSE) || + (fabs(lnode->offset) > fabs(dist))) { + mask = OFFSET_TAP; + dir = NI_OFFSET_NS; + OBSVAL(gridx, gridy, ds->layer) |= mask; + lnode->offset = dist; + lnode->flags &= ~(NI_OFFSET_EW); + lnode->flags |= dir; + no_offsets = FALSE; + + if ((ds->layer < Num_layers - 1) && + (gridy > 0) && (OBSVAL(gridx, + gridy + 1, ds->layer + 1) + & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + else if ((ds->layer < Num_layers - 1) && + (gridy > 0) && + (dist > PitchY / 2)) + block_route(gridx, gridy, ds->layer, UP); + } } } @@ -2013,30 +2111,49 @@ void create_obstructions_outside_nodes(void) if ((dy < ds->y1) && (gridy > 0)) { offset_net = OBSVAL(gridx, gridy - 1, ds->layer); if (offset_net == 0 || offset_net == othernet) { - xdist = 0.5 * LefGetViaWidth(ds->layer, - ds->layer, 1); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, + ds->layer, 1, orient); dist = ds->y1 - dy - xdist - LefGetRouteSpacing(ds->layer); - mask = OFFSET_TAP; - dir = NI_OFFSET_NS; - OBSVAL(gridx, gridy, ds->layer) |= mask; - lnode->offset = dist; - lnode->flags |= dir; - no_offsets = FALSE; - - if ((ds->layer < Num_layers - 1) && - gridx < (NumChannelsX[ds->layer] - 1) && - (OBSVAL(gridx, gridy - 1, ds->layer + 1) - & OBSTRUCT_MASK)) - block_route(gridx, gridy, ds->layer, UP); + if ((no_offsets == FALSE) || + (fabs(lnode->offset) > fabs(dist))) { + mask = OFFSET_TAP; + dir = NI_OFFSET_NS; + OBSVAL(gridx, gridy, ds->layer) |= mask; + lnode->offset = dist; + lnode->flags &= ~(NI_OFFSET_EW); + lnode->flags |= dir; + no_offsets = FALSE; + + if ((ds->layer < Num_layers - 1) && + gridx < + (NumChannelsX - 1) && + (OBSVAL(gridx, gridy - 1, + ds->layer + 1) & OBSTRUCT_MASK)) + block_route(gridx, gridy, ds->layer, UP); + else if ((ds->layer < Num_layers - 1) && + gridx < + (NumChannelsX - 1) && + (dist < -PitchY / 2)) + block_route(gridx, gridy, ds->layer, UP); + } } } - // No offsets were possible, so disable the - // position + // No offsets were possible. If orient is 0 + // then mark as NI_NO_VIAX and try again with + // orient 2. If orient is 2 and no offsets + // are possible, then disable the position. - if (no_offsets == TRUE) - disable_gridpos(gridx, gridy, ds->layer); + if (no_offsets == TRUE) { + if (orient == 2) { + // Maybe no need to revert the flag? + lnode->flags &= ~NI_NO_VIAX; + disable_gridpos(gridx, gridy, ds->layer); + } + else + lnode->flags |= NI_NO_VIAX; + } } else disable_gridpos(gridx, gridy, ds->layer); @@ -2055,7 +2172,8 @@ void create_obstructions_outside_nodes(void) (othernet == (u_int)node->netnum))) { lnode = NODEIPTR(gridx, gridy, ds->layer); - xdist = 0.5 * LefGetViaWidth(ds->layer, ds->layer, 0); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, + 0, orient); if ((dy + xdist + LefGetRouteSpacing(ds->layer) > ds->y1) && (dy + xdist < ds->y1)) { if ((dx - xdist < ds->x2) && @@ -2066,7 +2184,8 @@ void create_obstructions_outside_nodes(void) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | node->netnum | STUBROUTE; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); lnode->nodeloc = node; lnode->nodesav = node; lnode->stub = ds->y1 - dy; @@ -2083,7 +2202,8 @@ void create_obstructions_outside_nodes(void) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | node->netnum | STUBROUTE; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); lnode->nodeloc = node; lnode->nodesav = node; lnode->stub = ds->y2 - dy; @@ -2091,7 +2211,8 @@ void create_obstructions_outside_nodes(void) } } - xdist = 0.5 * LefGetViaWidth(ds->layer, ds->layer, 1); + xdist = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, + 1, orient); if ((dx + xdist + LefGetRouteSpacing(ds->layer) > ds->x1) && (dx + xdist < ds->x1)) { if ((dy - xdist < ds->y2) && @@ -2102,7 +2223,8 @@ void create_obstructions_outside_nodes(void) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | node->netnum | STUBROUTE; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); lnode->nodeloc = node; lnode->nodesav = node; lnode->stub = ds->x1 - dx; @@ -2119,7 +2241,8 @@ void create_obstructions_outside_nodes(void) = (OBSVAL(gridx, gridy, ds->layer) & BLOCKED_MASK) | node->netnum | STUBROUTE; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + node); lnode->nodeloc = node; lnode->nodesav = node; lnode->stub = ds->x2 - dx; @@ -2163,31 +2286,34 @@ void tap_to_tap_interactions(void) double dx, dy; float dist; - double deltax[MAX_LAYERS]; - double deltay[MAX_LAYERS]; + double deltaxx[MAX_LAYERS]; + double deltaxy[MAX_LAYERS]; + double deltayx[MAX_LAYERS]; + double deltayy[MAX_LAYERS]; for (i = 0; i < Num_layers; i++) { - deltax[i] = 0.5 * LefGetViaWidth(i, i, 0) + LefGetRouteSpacing(i); - deltay[i] = 0.5 * LefGetViaWidth(i, i, 1) + LefGetRouteSpacing(i); + deltaxx[i] = 0.5 * LefGetXYViaWidth(i, i, 0, 0) + LefGetRouteSpacing(i); + deltayx[i] = 0.5 * LefGetXYViaWidth(i, i, 1, 0) + LefGetRouteSpacing(i); + deltaxy[i] = 0.5 * LefGetXYViaWidth(i, i, 0, 2) + LefGetRouteSpacing(i); + deltayy[i] = 0.5 * LefGetXYViaWidth(i, i, 1, 2) + LefGetRouteSpacing(i); } for (g = Nlgates; g; g = g->next) { for (i = 0; i < g->nodes; i++) { net = g->netnum[i]; - if (net != 0) { - + if (net > 0) { for (ds = g->taps[i]; ds; ds = ds->next) { - mingridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]) - 1; + mingridx = (int)((ds->x1 - Xlowerbound) / PitchX) - 1; if (mingridx < 0) mingridx = 0; - maxgridx = (int)((ds->x2 - Xlowerbound) / PitchX[ds->layer]) + 2; - if (maxgridx >= NumChannelsX[ds->layer]) - maxgridx = NumChannelsX[ds->layer] - 1; - mingridy = (int)((ds->y1 - Ylowerbound) / PitchY[ds->layer]) - 1; + maxgridx = (int)((ds->x2 - Xlowerbound) / PitchX) + 2; + if (maxgridx >= NumChannelsX) + maxgridx = NumChannelsX - 1; + mingridy = (int)((ds->y1 - Ylowerbound) / PitchY) - 1; if (mingridy < 0) mingridy = 0; - maxgridy = (int)((ds->y2 - Ylowerbound) / PitchY[ds->layer]) + 2; - if (maxgridy >= NumChannelsY[ds->layer]) - maxgridy = NumChannelsY[ds->layer] - 1; + maxgridy = (int)((ds->y2 - Ylowerbound) / PitchY) + 2; + if (maxgridy >= NumChannelsY) + maxgridy = NumChannelsY - 1; for (gridx = mingridx; gridx <= maxgridx; gridx++) { for (gridy = mingridy; gridy <= maxgridy; gridy++) { @@ -2200,8 +2326,8 @@ void tap_to_tap_interactions(void) orignet &= ROUTED_NET_MASK; if (orignet != net) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; + dx = (gridx * PitchX) + Xlowerbound; + dy = (gridy * PitchY) + Ylowerbound; lnode = NODEIPTR(gridx, gridy, ds->layer); dist = (lnode) ? lnode->offset : 0.0; @@ -2210,10 +2336,10 @@ void tap_to_tap_interactions(void) /* at (gridx, gridy) and offset as specified. */ /* Expanded by metal spacing requirement. */ - de.x1 = dx - deltax[ds->layer]; - de.x2 = dx + deltax[ds->layer]; - de.y1 = dy - deltay[ds->layer]; - de.y2 = dy + deltay[ds->layer]; + de.x1 = dx - deltaxx[ds->layer]; + de.x2 = dx + deltaxx[ds->layer]; + de.y1 = dy - deltayx[ds->layer]; + de.y2 = dy + deltayx[ds->layer]; if (lnode->flags & NI_OFFSET_NS) { de.y1 += dist; @@ -2238,6 +2364,75 @@ void tap_to_tap_interactions(void) disable_gridpos(gridx, gridy, ds->layer); } } + + /* Does the distance to the tap prohibit a specific */ + /* via orientation? */ + if ((orignet & (~(BLOCKED_N | BLOCKED_S | BLOCKED_E | BLOCKED_W))) + == 0) { + lnode = NODEIPTR(gridx, gridy, ds->layer); + /* Positions belonging to nodes should have */ + /* already been handled. */ + if (lnode == NULL) { + dx = (gridx * PitchX) + Xlowerbound; + dy = (gridy * PitchY) + Ylowerbound; + + /* For a horizontally-oriented via on ds->layer */ + de.x1 = dx - deltaxx[ds->layer]; + de.x2 = dx + deltaxx[ds->layer]; + de.y1 = dy - deltayx[ds->layer]; + de.y2 = dy + deltayx[ds->layer]; + + if (ds->x2 > de.x1 && ds->x1 < de.x2) { + /* Check north and south */ + if ((ds->y1 < de.y2 && ds->y2 > de.y2) || + (ds->y2 > de.y1 && ds->y1 < de.y1)) { + /* prohibit horizontal via */ + lnode = SetNodeinfo(gridx, gridy, ds->layer, + g->noderec[i]); + lnode->flags |= NI_NO_VIAX; + } + } + + if (ds->y2 > de.y1 && ds->y1 < de.y2) { + /* Check east and west*/ + if ((ds->x1 < de.x2 && ds->x2 > de.x2) || + (ds->x2 > de.x1 && ds->x1 < de.x1)) { + /* prohibit horizontal via */ + lnode = SetNodeinfo(gridx, gridy, ds->layer, + g->noderec[i]); + lnode->flags |= NI_NO_VIAX; + } + } + + /* For a vertically-oriented via on ds->layer */ + de.x1 = dx - deltaxy[ds->layer]; + de.x2 = dx + deltaxy[ds->layer]; + de.y1 = dy - deltayy[ds->layer]; + de.y2 = dy + deltayy[ds->layer]; + + if (ds->x2 > de.x1 && ds->x1 < de.x2) { + /* Check north and south */ + if ((ds->y1 < de.y2 && ds->y2 > de.y2) || + (ds->y2 > de.y1 && ds->y1 < de.y1)) { + /* prohibit horizontal via */ + lnode = SetNodeinfo(gridx, gridy, ds->layer, + g->noderec[i]); + lnode->flags |= NI_NO_VIAY; + } + } + + if (ds->y2 > de.y1 && ds->y1 < de.y2) { + /* Check east and west*/ + if ((ds->x1 < de.x2 && ds->x2 > de.x2) || + (ds->x2 > de.x1 && ds->x1 < de.x1)) { + /* prohibit horizontal via */ + lnode = SetNodeinfo(gridx, gridy, ds->layer, + g->noderec[i]); + lnode->flags |= NI_NO_VIAY; + } + } + } + } } } } @@ -2276,15 +2471,15 @@ make_routable(NODE node) for (i = 0; i < g->nodes; i++) { if (g->noderec[i] == node) { for (ds = g->taps[i]; ds; ds = ds->next) { - gridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]) - 1; + gridx = (int)((ds->x1 - Xlowerbound) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - if (dx > ds->x2 || gridx >= NumChannelsX[ds->layer]) break; + dx = (gridx * PitchX) + Xlowerbound; + if (dx > ds->x2 || gridx >= NumChannelsX) break; else if (dx >= ds->x1 && gridx >= 0) { - gridy = (int)((ds->y1 - Ylowerbound) / PitchY[ds->layer]) - 1; + gridy = (int)((ds->y1 - Ylowerbound) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if (dy > ds->y2 || gridy >= NumChannelsY[ds->layer]) break; + dy = (gridy * PitchY) + Ylowerbound; + if (dy > ds->y2 || gridy >= NumChannelsY) break; // Area inside defined pin geometry @@ -2293,7 +2488,8 @@ make_routable(NODE node) if (orignet & NO_NET) { OBSVAL(gridx, gridy, ds->layer) = g->netnum[i]; - lnode = SetNodeinfo(gridx, gridy, ds->layer); + lnode = SetNodeinfo(gridx, gridy, ds->layer, + g->noderec[i]); lnode->nodeloc = node; lnode->nodesav = node; return; @@ -2335,6 +2531,7 @@ void adjust_stub_lengths(void) double dx, dy, wx, wy, s; float dist; u_char errbox; + int orient = 0; // For each node terminal (gate pin), look at the surrounding grid points. // If any define a stub route or an offset, check if the stub geometry @@ -2356,26 +2553,38 @@ void adjust_stub_lengths(void) node = g->noderec[i]; if (node == NULL) continue; - // Work through each rectangle in the tap geometry - - for (ds = g->taps[i]; ds; ds = ds->next) { - wx = 0.5 * LefGetViaWidth(ds->layer, ds->layer, 0); - wy = 0.5 * LefGetViaWidth(ds->layer, ds->layer, 1); + // Work through each rectangle in the tap geometry twice. The + // second sweep checks for errors created by geometry that + // interacts with stub routes created by the first sweep, and + // also for geometry that interacts with vias in 90 degree + // orientation. + + orient = 0; + for (ds = g->taps[i];; ds = ds->next) { + if (ds == NULL) { + if (orient == 0) { + orient = 2; + ds = g->taps[i]; + if (ds == NULL) break; + } + else + break; + } + wx = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 0, orient); + wy = 0.5 * LefGetXYViaWidth(ds->layer, ds->layer, 1, orient); s = LefGetRouteSpacing(ds->layer); - gridx = (int)((ds->x1 - Xlowerbound - PitchX[ds->layer]) - / PitchX[ds->layer]) - 1; + gridx = (int)((ds->x1 - Xlowerbound - PitchX) / PitchX) - 1; while (1) { - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - if (dx > (ds->x2 + PitchX[ds->layer]) || - gridx >= NumChannelsX[ds->layer]) break; - else if (dx >= (ds->x1 - PitchX[ds->layer]) && gridx >= 0) { - gridy = (int)((ds->y1 - Ylowerbound - PitchY[ds->layer]) - / PitchY[ds->layer]) - 1; + dx = (gridx * PitchX) + Xlowerbound; + if (dx > (ds->x2 + PitchX) || + gridx >= NumChannelsX) break; + else if (dx >= (ds->x1 - PitchX) && gridx >= 0) { + gridy = (int)((ds->y1 - Ylowerbound - PitchY) / PitchY) - 1; while (1) { - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - if (dy > (ds->y2 + PitchY[ds->layer]) || - gridy >= NumChannelsY[ds->layer]) break; - if (dy >= (ds->y1 - PitchY[ds->layer]) && gridy >= 0) { + dy = (gridy * PitchY) + Ylowerbound; + if (dy > (ds->y2 + PitchY) || + gridy >= NumChannelsY) break; + if (dy >= (ds->y1 - PitchY) && gridy >= 0) { orignet = OBSVAL(gridx, gridy, ds->layer); @@ -2391,7 +2600,7 @@ void adjust_stub_lengths(void) // Even if it's on the same net, we need to check // if the stub is to this node, otherwise it is not // an issue. - if (lnode->nodesav != node) { + if ((!lnode) || (lnode->nodesav != node)) { gridy++; continue; } @@ -2610,16 +2819,13 @@ void adjust_stub_lengths(void) OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; if (LefGetRouteOrientation(ds->layer) == 1) { lnode->flags = NI_OFFSET_NS | NI_STUB_EW; - // lnode->offset = lnode->stub; // ? if (lnode->stub > 0) { - lnode->offset = de.y2 - dy - wy; - if (lnode->offset > s - lnode->offset) - lnode->offset -= s; + lnode->offset = lnode->stub - wy; + if (lnode->offset < 0) lnode->offset = 0; } else { - lnode->offset = de.y1 - dy + wy; - if (-lnode->offset > s + lnode->offset) - lnode->offset += s; + lnode->offset = lnode->stub + wy; + if (lnode->offset > 0) lnode->offset = 0; } lnode->stub = de.x2 - dx; errbox = FALSE; @@ -2659,16 +2865,13 @@ void adjust_stub_lengths(void) OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; if (LefGetRouteOrientation(ds->layer) == 1) { lnode->flags = NI_OFFSET_NS | NI_STUB_EW; - // lnode->offset = lnode->stub; // ? if (lnode->stub > 0) { - lnode->offset = de.y2 - dy - wy; - if (lnode->offset > s - lnode->offset) - lnode->offset -= s; + lnode->offset = lnode->stub - wy; + if (lnode->offset < 0) lnode->offset = 0; } else { - lnode->offset = de.y1 - dy + wy; - if (-lnode->offset > s + lnode->offset) - lnode->offset += s; + lnode->offset = lnode->stub + wy; + if (lnode->offset > 0) lnode->offset = 0; } lnode->stub = de.x1 - dx; errbox = FALSE; @@ -2708,16 +2911,13 @@ void adjust_stub_lengths(void) OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; if (LefGetRouteOrientation(ds->layer) == 0) { lnode->flags = NI_OFFSET_EW | NI_STUB_NS; - // lnode->offset = lnode->stub; // ? if (lnode->stub > 0) { - lnode->offset = de.x2 - dx - wx; - if (lnode->offset > s - lnode->offset) - lnode->offset -= s; + lnode->offset = lnode->stub - wx; + if (lnode->offset < 0) lnode->offset = 0; } else { - lnode->offset = de.x1 - dx + wx; - if (-lnode->offset > s + lnode->offset) - lnode->offset += s; + lnode->offset = lnode->stub + wx; + if (lnode->offset > 0) lnode->offset = 0; } lnode->stub = de.y2 - dy; errbox = FALSE; @@ -2757,16 +2957,13 @@ void adjust_stub_lengths(void) OBSVAL(gridx, gridy, ds->layer) |= OFFSET_TAP; if (LefGetRouteOrientation(ds->layer) == 0) { lnode->flags = NI_OFFSET_EW | NI_STUB_NS; - // lnode->offset = lnode->stub; // ? if (lnode->stub > 0) { - lnode->offset = de.x2 - dx - wx; - if (lnode->offset > s - lnode->offset) - lnode->offset -= s; + lnode->offset = lnode->stub - wx; + if (lnode->offset < 0) lnode->offset = 0; } else { - lnode->offset = de.x1 - dx + wx; - if (-lnode->offset > s + lnode->offset) - lnode->offset += s; + lnode->offset = lnode->stub + wx; + if (lnode->offset > 0) lnode->offset = 0; } lnode->stub = de.y1 - dy + wy; errbox = FALSE; @@ -2830,7 +3027,7 @@ block_route(int x, int y, int lay, u_char dir) switch (dir) { case NORTH: - if (y == NumChannelsY[lay] - 1) return; + if (y == NumChannelsY - 1) return; by = y + 1; break; case SOUTH: @@ -2838,7 +3035,7 @@ block_route(int x, int y, int lay, u_char dir) by = y - 1; break; case EAST: - if (x == NumChannelsX[lay] - 1) return; + if (x == NumChannelsX - 1) return; bx = x + 1; break; case WEST: @@ -2905,10 +3102,11 @@ find_route_blocks() GATE g; NODEINFO lnode; DSEG ds; - struct dseg_ dt; + struct dseg_ dt, lds; int i, gridx, gridy; double dx, dy, w, v, s, u; double dist; + int orient = 0; /* Need to check orient = 2! */ for (g = Nlgates; g; g = g->next) { for (i = 0; i < g->nodes; i++) { @@ -2917,142 +3115,159 @@ find_route_blocks() // Work through each rectangle in the tap geometry for (ds = g->taps[i]; ds; ds = ds->next) { - w = 0.5 * LefGetRouteWidth(ds->layer); - v = 0.5 * LefGetViaWidth(ds->layer, ds->layer, 0); - s = LefGetRouteSpacing(ds->layer); + lds = *ds; /* Make local copy of tap rect */ + + /* Trim to array bounds and reject if out-of-bounds */ + gridx = (int)((lds.x1 - Xlowerbound) / PitchX); + if (gridx >= NumChannelsX) continue; + if (gridx < 0) lds.x1 = Xlowerbound; + + gridx = (int)((lds.x2 - Xlowerbound) / PitchX); + if (gridx < 0) continue; + if (gridx >= NumChannelsX) + lds.x2 = Xlowerbound + (NumChannelsX * PitchX); + + gridy = (int)((lds.y1 - Ylowerbound) / PitchY); + if (gridy >= NumChannelsY) continue; + if (gridy < 0) lds.y1 = Ylowerbound; + + gridy = (int)((lds.y2 - Ylowerbound) / PitchY); + if (gridy < 0) continue; + if (gridy >= NumChannelsY) + lds.y2 = Ylowerbound + (NumChannelsY * PitchY); + + w = 0.5 * LefGetRouteWidth(lds.layer); + v = 0.5 * LefGetXYViaWidth(lds.layer, lds.layer, 0, orient); + s = LefGetRouteSpacing(lds.layer); // Look west - gridx = (int)((ds->x1 - Xlowerbound) / PitchX[ds->layer]); - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - dist = ds->x1 - dx - w; + gridx = (int)((lds.x1 - Xlowerbound) / PitchX); + dx = (gridx * PitchX) + Xlowerbound; + dist = lds.x1 - dx - w; if (dist > 0 && dist < s && gridx >= 0) { dt.x1 = dt.x2 = dx; - dt.y1 = ds->y1; - dt.y2 = ds->y2; + dt.y1 = lds.y1; + dt.y2 = lds.y2; // Check for other taps covering this edge // (to do) // Find all grid points affected - gridy = (int)((ds->y1 - Ylowerbound - PitchY[ds->layer]) / - PitchY[ds->layer]); - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - while (dy < ds->y1 - s) { - dy += PitchY[ds->layer]; + gridy = (int)((lds.y1 - Ylowerbound - PitchY) / PitchY); + dy = (gridy * PitchY) + Ylowerbound; + while (dy < lds.y1 - s) { + dy += PitchY; gridy++; } - while (dy < ds->y2 + s) { - lnode = NODEIPTR(gridx, gridy, ds->layer); - u = ((OBSVAL(gridx, gridy, ds->layer) & STUBROUTE) + while (dy < lds.y2 + s) { + lnode = NODEIPTR(gridx, gridy, lds.layer); + u = ((OBSVAL(gridx, gridy, lds.layer) & STUBROUTE) && (lnode->flags & NI_STUB_EW)) ? v : w; - if (dy + EPS < ds->y2 - u) - block_route(gridx, gridy, ds->layer, NORTH); - if (dy - EPS > ds->y1 + u) - block_route(gridx, gridy, ds->layer, SOUTH); - dy += PitchY[ds->layer]; + if (dy + EPS < lds.y2 - u) + block_route(gridx, gridy, lds.layer, NORTH); + if (dy - EPS > lds.y1 + u) + block_route(gridx, gridy, lds.layer, SOUTH); + dy += PitchY; gridy++; } } // Look east - gridx = (int)(1.0 + (ds->x2 - Xlowerbound) / PitchX[ds->layer]); - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - dist = dx - ds->x2 - w; - if (dist > 0 && dist < s && gridx < NumChannelsX[ds->layer]) { + gridx = (int)(1.0 + (lds.x2 - Xlowerbound) / PitchX); + dx = (gridx * PitchX) + Xlowerbound; + dist = dx - lds.x2 - w; + if (dist > 0 && dist < s && gridx < NumChannelsX) { dt.x1 = dt.x2 = dx; - dt.y1 = ds->y1; - dt.y2 = ds->y2; + dt.y1 = lds.y1; + dt.y2 = lds.y2; // Check for other taps covering this edge // (to do) // Find all grid points affected - gridy = (int)((ds->y1 - Ylowerbound - PitchY[ds->layer]) / - PitchY[ds->layer]); - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - while (dy < ds->y1 - s) { - dy += PitchY[ds->layer]; + gridy = (int)((lds.y1 - Ylowerbound - PitchY) / PitchY); + dy = (gridy * PitchY) + Ylowerbound; + while (dy < lds.y1 - s) { + dy += PitchY; gridy++; } - while (dy < ds->y2 + s) { - lnode = NODEIPTR(gridx, gridy, ds->layer); - u = ((OBSVAL(gridx, gridy, ds->layer) & STUBROUTE) + while (dy < lds.y2 + s) { + lnode = NODEIPTR(gridx, gridy, lds.layer); + u = ((OBSVAL(gridx, gridy, lds.layer) & STUBROUTE) && (lnode->flags & NI_STUB_EW)) ? v : w; - if (dy + EPS < ds->y2 - u) - block_route(gridx, gridy, ds->layer, NORTH); - if (dy - EPS > ds->y1 + u) - block_route(gridx, gridy, ds->layer, SOUTH); - dy += PitchY[ds->layer]; + if (dy + EPS < lds.y2 - u) + block_route(gridx, gridy, lds.layer, NORTH); + if (dy - EPS > lds.y1 + u) + block_route(gridx, gridy, lds.layer, SOUTH); + dy += PitchY; gridy++; } } // Look south - gridy = (int)((ds->y1 - Ylowerbound) / PitchY[ds->layer]); - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - dist = ds->y1 - dy - w; + gridy = (int)((lds.y1 - Ylowerbound) / PitchY); + dy = (gridy * PitchY) + Ylowerbound; + dist = lds.y1 - dy - w; if (dist > 0 && dist < s && gridy >= 0) { - dt.x1 = ds->x1; - dt.x2 = ds->x2; + dt.x1 = lds.x1; + dt.x2 = lds.x2; dt.y1 = dt.y2 = dy; // Check for other taps covering this edge // (to do) // Find all grid points affected - gridx = (int)((ds->x1 - Xlowerbound - PitchX[ds->layer]) / - PitchX[ds->layer]); - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - while (dx < ds->x1 - s) { - dx += PitchX[ds->layer]; + gridx = (int)((lds.x1 - Xlowerbound - PitchX) / PitchX); + dx = (gridx * PitchX) + Xlowerbound; + while (dx < lds.x1 - s) { + dx += PitchX; gridx++; } - while (dx < ds->x2 + s) { - lnode = NODEIPTR(gridx, gridy, ds->layer); - u = ((OBSVAL(gridx, gridy, ds->layer) & STUBROUTE) + while (dx < lds.x2 + s) { + lnode = NODEIPTR(gridx, gridy, lds.layer); + u = ((OBSVAL(gridx, gridy, lds.layer) & STUBROUTE) && (lnode->flags & NI_STUB_NS)) ? v : w; - if (dx + EPS < ds->x2 - u) - block_route(gridx, gridy, ds->layer, EAST); - if (dx - EPS > ds->x1 + u) - block_route(gridx, gridy, ds->layer, WEST); - dx += PitchX[ds->layer]; + if (dx + EPS < lds.x2 - u) + block_route(gridx, gridy, lds.layer, EAST); + if (dx - EPS > lds.x1 + u) + block_route(gridx, gridy, lds.layer, WEST); + dx += PitchX; gridx++; } } // Look north - gridy = (int)(1.0 + (ds->y2 - Ylowerbound) / PitchY[ds->layer]); - dy = (gridy * PitchY[ds->layer]) + Ylowerbound; - dist = dy - ds->y2 - w; - if (dist > 0 && dist < s && gridy < NumChannelsY[ds->layer]) { - dt.x1 = ds->x1; - dt.x2 = ds->x2; + gridy = (int)(1.0 + (lds.y2 - Ylowerbound) / PitchY); + dy = (gridy * PitchY) + Ylowerbound; + dist = dy - lds.y2 - w; + if (dist > 0 && dist < s && gridy < NumChannelsY) { + dt.x1 = lds.x1; + dt.x2 = lds.x2; dt.y1 = dt.y2 = dy; // Check for other taps covering this edge // (to do) // Find all grid points affected - gridx = (int)((ds->x1 - Xlowerbound - PitchX[ds->layer]) / - PitchX[ds->layer]); - dx = (gridx * PitchX[ds->layer]) + Xlowerbound; - while (dx < ds->x1 - s) { - dx += PitchX[ds->layer]; + gridx = (int)((lds.x1 - Xlowerbound - PitchX) / PitchX); + dx = (gridx * PitchX) + Xlowerbound; + while (dx < lds.x1 - s) { + dx += PitchX; gridx++; } - while (dx < ds->x2 + s) { - lnode = NODEIPTR(gridx, gridy, ds->layer); - u = ((OBSVAL(gridx, gridy, ds->layer) & STUBROUTE) + while (dx < lds.x2 + s) { + lnode = NODEIPTR(gridx, gridy, lds.layer); + u = ((OBSVAL(gridx, gridy, lds.layer) & STUBROUTE) && (lnode->flags & NI_STUB_NS)) ? v : w; - if (dx + EPS < ds->x2 - u) - block_route(gridx, gridy, ds->layer, EAST); - if (dx - EPS > ds->x1 + u) - block_route(gridx, gridy, ds->layer, WEST); - dx += PitchX[ds->layer]; + if (dx + EPS < lds.x2 - u) + block_route(gridx, gridy, lds.layer, EAST); + if (dx - EPS > lds.x1 + u) + block_route(gridx, gridy, lds.layer, WEST); + dx += PitchX; gridx++; } } @@ -8,9 +8,9 @@ #define GND_NET 1 #define VDD_NET 2 -#define MIN_NET_NUMBER 3 +#define ANTENNA_NET 3 +#define MIN_NET_NUMBER 4 -void create_netorder(u_char method); void find_bounding_box(NET net); void defineRouteTree(NET); void print_nodes(char *filename); @@ -30,6 +30,46 @@ int Pathon = -1; +struct _savepath { + u_char active; + int x; + int y; + int orient; +} path_delayed; + +/*--------------------------------------------------------------*/ +/* Output a list of failed nets. */ +/*--------------------------------------------------------------*/ + +int write_failed(char *filename) +{ + FILE *ffail; + NET net; + NETLIST nl; + int failcount; + + failcount = countlist(FailedNets); + if (failcount == 0) { + Fprintf(stdout, "There are no failing net routes.\n"); + return 0; + } + + ffail = fopen(filename, "w"); + if (ffail == NULL) { + Fprintf(stderr, "Could not open file %s for writing.\n", filename); + return 1; + } + fprintf(ffail, "%d nets failed to route:\n", failcount); + + for (nl = FailedNets; nl; nl = nl->next) { + net = nl->net; + fprintf(ffail, " %s\n", net->netname); + } + + fclose(ffail); + return 0; +} + /*--------------------------------------------------------------*/ /* Write the output annotated DEF file. */ /*--------------------------------------------------------------*/ @@ -51,14 +91,11 @@ int write_def(char *filename) Fprintf(stdout, "Failed net routes: %d\n", countlist(FailedNets)); Fprintf(stdout, "List of failed nets follows:\n"); - // Make sure FailedNets is cleaned up as we output the failed nets + // Output a list of the failed nets - while (FailedNets) { - net = FailedNets->net; + for (nl = FailedNets; nl; nl = nl->next) { + net = nl->net; Fprintf(stdout, " %s\n", net->netname); - nl = FailedNets->next; - free(FailedNets); - FailedNets = nl; } Fprintf(stdout, "\n"); } @@ -79,7 +116,7 @@ int write_def(char *filename) static void pathstart(FILE *cmd, int layer, int x, int y, u_char special, double oscale, - double invscale, u_char horizontal) + double invscale, u_char horizontal, NODEINFO node) { if (Pathon == 1) { Fprintf( stderr, "pathstart(): Major error. Started a new " @@ -94,11 +131,25 @@ pathstart(FILE *cmd, int layer, int x, int y, u_char special, double oscale, fprintf(cmd, "\n NEW "); if (special) { double wvia; + int vtype = 0; /* Need to get via type from node record! */ + + if (node != NULL) { + if ((node->flags & NI_NO_VIAX) && (!(node->flags & NI_VIA_X))) + vtype = 2; + else if (node->flags & NI_VIA_Y) + vtype = 2; + } + else { + /* Assume via orientation matches default route direction. */ + /* NOTE: Need to mark the actual orientation somehow. . . */ + int ob = LefGetRouteOrientation((layer > 0) ? (layer - 1) : layer); + if (ob == 1) vtype = 2; + } - wvia = LefGetViaWidth(layer, layer, horizontal); + wvia = LefGetXYViaWidth(layer, layer, horizontal, vtype); if (layer > 0) { double wvia2; - wvia2 = LefGetViaWidth(layer - 1, layer, horizontal); + wvia2 = LefGetXYViaWidth(layer - 1, layer, horizontal, vtype); if (wvia2 > wvia) wvia = wvia2; } @@ -124,7 +175,7 @@ pathstart(FILE *cmd, int layer, int x, int y, u_char special, double oscale, static void pathto(FILE *cmd, int x, int y, int horizontal, int lastx, int lasty, - double invscale) + double invscale, u_char nextvia) { if (Pathon <= 0) { Fprintf(stderr, "pathto(): Major error. Added to a " @@ -139,9 +190,19 @@ pathto(FILE *cmd, int x, int y, int horizontal, int lastx, int lasty, if ((x != lastx) && (y != lasty)) { if (horizontal) - pathto(cmd, lastx, y, FALSE, lastx, lasty, invscale); + pathto(cmd, lastx, y, FALSE, lastx, lasty, invscale, 0); else - pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale); + pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale, 0); + } + + if (nextvia) { + /* Punt on output until via is output, because via may have an */ + /* offset position that needs to be applied to the route. */ + path_delayed.active = 1; + path_delayed.x = x; + path_delayed.y = y; + path_delayed.orient = horizontal; + return; } fprintf(cmd, "( "); @@ -169,30 +230,13 @@ pathto(FILE *cmd, int x, int y, int horizontal, int lastx, int lasty, static void pathvia(FILE *cmd, int layer, int x, int y, int lastx, int lasty, - int gridx, int gridy, double invscale) + char *vianame, double invscale) { - char *s; - char checkersign = (gridx + gridy + layer) & 0x01; - NODEINFO lnode; - - if ((ViaPattern == VIA_PATTERN_NONE) || (ViaY[layer] == NULL)) - s = ViaX[layer]; - else if (ViaPattern == VIA_PATTERN_NORMAL) - s = (checkersign == 0) ? ViaX[layer] : ViaY[layer]; - else - s = (checkersign == 0) ? ViaY[layer] : ViaX[layer]; - - /* If the position is a node, then the via type may be switched if */ - /* there is a prohibition declared in the flags. */ - - if (layer < Pinlayers) { - if (((lnode = NODEIPTR(gridx, gridy, layer)) != NULL) - && (lnode->nodesav != NULL)) { - if ((lnode->flags & NI_NO_VIAX) && (s == ViaX[layer])) - s = ViaY[layer]; - if ((lnode->flags & NI_NO_VIAY) && (s == ViaY[layer])) - s = ViaX[layer]; - } + if (path_delayed.active == 1) { + /* Output the last path */ + pathto(cmd, path_delayed.x, path_delayed.y, path_delayed.orient, + path_delayed.x, path_delayed.y, invscale, 0); + path_delayed.active = 0; } if (Pathon <= 0) { @@ -211,11 +255,11 @@ pathvia(FILE *cmd, int layer, int x, int y, int lastx, int lasty, // route to the via. if (x != lastx) - pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale); + pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale, 0); if (y != lasty) - pathto(cmd, x, y, FALSE, x, lasty, invscale); + pathto(cmd, x, y, FALSE, x, lasty, invscale, 0); } - fprintf(cmd, "%s ", s); + fprintf(cmd, "%s ", vianame); Pathon = 0; } /* pathvia() */ @@ -563,6 +607,59 @@ void print_nlnets( char *filename ) } /* print_nlnets() */ /*--------------------------------------------------------------*/ +/* link_up_seg --- */ +/* */ +/* As part of cleanup_net (below), when removing unneeded vias, */ +/* it may be necessary to check if a segment is being used to */ +/* connect to another route of a net, in which case removing */ +/* the segment would break the net. If that is the case, then */ +/* keep the segment by linking it to the end of the route that */ +/* was connected to it. */ +/* */ +/* Return 1 (true) if the segment was linked to another route, */ +/* so the caller knows whether or not to free the segment. */ +/* */ +/* "rt" is the route that the segment was connected to. For */ +/* the sake of efficiency, it does not need to be checked. */ +/*--------------------------------------------------------------*/ + +u_char link_up_seg(NET net, SEG seg, int viabase, ROUTE srt) +{ + ROUTE rt; + SEG segf, segl; + int x, y; + + for (rt = net->routes; rt; rt = rt->next) { + if (rt == srt) continue; + segf = rt->segments; + if ((segf->x1 == seg->x1) && (segf->y1 == seg->y1) && + ((segf->layer == viabase) || (segf->layer == viabase + 1))) { + /* Reverse seg and prepend it to the route */ + seg->next = rt->segments; + rt->segments = seg; + x = seg->x1; + y = seg->y1; + seg->x1 = seg->x2; + seg->y1 = seg->y2; + seg->x2 = x; + seg->y2 = y; + return (u_char)1; + } + + /* Move to the last segment of the route */ + for (segl = segf; segl && segl->next; segl = segl->next); + + if (segl && (segl->x2 == seg->x1) && (segl->y2 == seg->y1) && + ((segl->layer == viabase) || (segl->layer == viabase + 1))) { + /* Append seg to the route */ + segl->next = seg; + return (u_char)1; + } + } + return (u_char)0; +} + +/*--------------------------------------------------------------*/ /* cleanup_net -- */ /* */ /* Special handling for layers where needblock[] is non-zero, */ @@ -576,16 +673,16 @@ void print_nlnets( char *filename ) /* */ /* Note that the ensuing change in connectivity can violate */ /* the route endpoints and thereby mess up the delay output */ -/* routine unless route_set_connections() is re-run on the */ -/* modified routes. */ +/* routine and/or the antenna violation finding routine unless */ +/* route_set_connections() is re-run on the modified routes. */ /*--------------------------------------------------------------*/ -static void cleanup_net(NET net) +void cleanup_net(NET net) { - SEG segf, segl, seg; + SEG segf, segl, seg, segp; ROUTE rt, rt2; NODEINFO lnode; - int lf, ll, lf2, ll2; + int lf, ll, lf2, ll2, viabase; u_char fcheck, lcheck, needfix; u_char xcheckf, ycheckf, xcheckl, ycheckl; @@ -806,6 +903,120 @@ static void cleanup_net(NET net) } } } + + /* One case not covered by the checks above: If the second or */ + /* penultimate segment is a via and the final segment is one */ + /* track in length and connects to a via, then the same */ + /* replacement can be made. The other routes do not need to be */ + /* checked, as it is sufficient to check that the grid is */ + /* occupied at that point on two metal layers with the same net. */ + + /* NOTE: Another route could be connecting to the via on the */ + /* penultimate segment, and removing it would cause an open net. */ + /* Check for this case and resolve if needed. */ + + if ((fcheck == FALSE) && (lcheck == FALSE)) { + int wlen, oval0, oval1, oval2; + + segf = rt->segments; + if ((segf == NULL) || (segf->next == NULL)) continue; + seg = segf->next; + if ((segf->segtype == ST_WIRE) && (seg->segtype == ST_VIA)) { + if ((segf->x1 - segf->x2) == 0) { + wlen = segf->y1 - segf->y2; + if ((wlen == 1) || (wlen == -1)) { + oval1 = OBSVAL(segf->x1, segf->y1, seg->layer) & ROUTED_NET_MASK; + oval2 = OBSVAL(segf->x1, segf->y1, seg->layer + 1) & ROUTED_NET_MASK; + if (oval1 == oval2) { + /* Check false case in which (layer + 1) is a min area stub */ + segp = seg->next; + if (segp && (segp->x2 == segf->x1) && (segp->y2 == segf->y1)) + continue; + /* Remove via and change wire layer */ + needfix = TRUE; + segf->next = seg->next; + viabase = segf->layer; + segf->layer = (viabase == seg->layer) ? seg->layer + 1 : + seg->layer; + if (!link_up_seg(net, seg, viabase, rt)) free(seg); + } + } + } + else if ((segf->y1 - segf->y2) == 0) { + wlen = segf->x1 - segf->x2; + if ((wlen == 1) || (wlen == -1)) { + oval1 = OBSVAL(segf->x1, segf->y1, seg->layer) & ROUTED_NET_MASK; + oval2 = OBSVAL(segf->x1, segf->y1, seg->layer + 1) & ROUTED_NET_MASK; + if (oval1 == oval2) { + /* Check false case in which (layer + 1) is a min area stub */ + segp = seg->next; + if (segp && (segp->x2 == segf->x1) && (segp->y2 == segf->y1)) + continue; + /* Remove via and change wire layer */ + needfix = TRUE; + segf->next = seg->next; + viabase = segf->layer; + segf->layer = (viabase == seg->layer) ? seg->layer + 1 : + seg->layer; + if (!link_up_seg(net, seg, viabase, rt)) free(seg); + } + } + } + } + segp = NULL; + for (seg = rt->segments; seg && seg->next && seg->next->next; seg = seg->next) + segp = seg; + if ((seg == NULL) || (seg->next == NULL)) continue; + segl = seg->next; + if ((segl->segtype == ST_WIRE) && (seg->segtype == ST_VIA)) { + if ((segl->x1 - segl->x2) == 0) { + wlen = segl->y1 - segl->y2; + if ((wlen == 1) || (wlen == -1)) { + oval1 = OBSVAL(segl->x2, segl->y2, seg->layer) & ROUTED_NET_MASK; + oval2 = OBSVAL(segl->x2, segl->y2, seg->layer + 1) & ROUTED_NET_MASK; + if (oval1 == oval2) { + /* Check false case in which (layer + 1) is a min area stub */ + if (segp && (segp->x1 == segl->x2) && (segp->y1 == segl->y2)) + continue; + /* Remove via and change wire layer */ + needfix = TRUE; + seg->next = NULL; + seg->segtype = ST_WIRE; + viabase = seg->layer; + seg->layer = (viabase == segl->layer) ? viabase + 1 : viabase; + seg->x1 = segl->x1; + seg->y1 = segl->y1; + seg->x2 = segl->x2; + seg->y2 = segl->y2; + if (!link_up_seg(net, segl, viabase, rt)) free(segl); + } + } + } + else if ((segl->y1 - segl->y2) == 0) { + wlen = segl->x1 - segl->x2; + if ((wlen == 1) || (wlen == -1)) { + oval1 = OBSVAL(segl->x2, segl->y2, seg->layer) & ROUTED_NET_MASK; + oval2 = OBSVAL(segl->x2, segl->y2, seg->layer + 1) & ROUTED_NET_MASK; + if (oval1 == oval2) { + /* Check false case in which (layer + 1) is a min area stub */ + if (segp && (segp->x1 == segl->x2) && (segp->y1 == segl->y2)) + continue; + /* Remove via and change wire layer */ + needfix = TRUE; + viabase = seg->layer; + seg->next = NULL; + seg->segtype = ST_WIRE; + seg->layer = (viabase == segl->layer) ? viabase + 1 : viabase; + seg->x1 = segl->x1; + seg->y1 = segl->y1; + seg->x2 = segl->x2; + seg->y2 = segl->y2; + if (!link_up_seg(net, segl, viabase, rt)) free(segl); + } + } + } + } + } } if (needfix == TRUE) for (rt = net->routes; rt; rt = rt->next) @@ -841,7 +1052,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) int lastx = -1, lasty = -1, lastlay; int horizontal; float offset1, offset2, stub, offset; - u_char cancel, segtype; + u_char cancel, segtype, nextvia; double invscale = (double)(1.0 / (double)iscale); /* If the STUB flag is set, then we need to write out the net name */ @@ -851,112 +1062,14 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) fprintf(Cmd, ";\n- %s\n", net->netname); } - u_char viaCheckX[MAX_LAYERS]; - u_char viaCheckY[MAX_LAYERS]; - double viaOffsetX[MAX_LAYERS][3]; - double viaOffsetY[MAX_LAYERS][3]; - - /* Compute via offsets, if needed for adjacent vias on different nets. */ - - /* A well-designed standard cell set should not have DRC errors */ - /* between vias spaced on adjacent tracks. But not every standard */ - /* cell set is well-designed. . . */ - - /* Example of offset measurements: */ - /* viaOffsetX[layer][n]: layer is the base layer of the via, n is */ - /* 0 for the via one layer below, 1 for the same via, and 2 for the */ - /* via one layer above. Note that the n = 1 has interactions on two */ - /* different metal layers. The maximum distance is used. */ - - /* viaCheckX[1] is 0 if all of viaOffsetX[1][0-2] is zero. This */ - /* allows a quick determination if a check for neighboring vias */ - /* is required. */ - /* viaOffsetX[1][0] is the additional spacing above the grid width */ - /* for via2-to-via1 (on metal2 only). */ - /* viaOffsetX[1][1] is the additional spacing above the grid width */ - /* for via2-to-via2 (maximum for metal2 and metal3) */ - /* viaOffsetX[1][2] is the additional spacing above the grid width */ - /* for via2-to-via3 (on metal3 only). */ - - viaOffsetX[0][0] = 0; // nothing below the 1st via - viaOffsetY[0][0] = 0; - viaOffsetX[Num_layers - 1][2] = 0; // nothing above the last via - viaOffsetY[Num_layers - 1][2] = 0; - - for (layer = 0; layer < Num_layers - 1; layer++) { - double s1 = LefGetRouteSpacing(layer); - double s2 = LefGetRouteSpacing(layer + 1); - double p1x = PitchX[layer]; - double p2x = PitchX[layer + 1]; - double p1y = PitchY[layer]; - double p2y = PitchY[layer + 1]; - double w1x = LefGetViaWidth(layer, layer, 0); - double w1y = LefGetViaWidth(layer, layer, 1); - double w2x = LefGetViaWidth(layer, layer + 1, 0); - double w2y = LefGetViaWidth(layer, layer + 1, 1); - - double w0x, w0y, w3x, w3y; - - viaCheckX[layer] = 0; - viaCheckY[layer] = 0; - - if (layer > 0) { - - /* Space from via to (via - 1) */ - - w0x = LefGetViaWidth(layer - 1, layer, 0); - w0y = LefGetViaWidth(layer - 1, layer, 1); - - dc = s1 + (w1x + w0x) / 2 - p1x; - viaOffsetX[layer][0] = (dc > 0.0) ? dc : 0.0; - - dc = s1 + (w1y + w0y) / 2 - p1y; - viaOffsetY[layer][0] = (dc > 0.0) ? dc : 0.0; - } - - /* Space from via to via (check both lower and upper metal layers) */ - - dc = s1 + w1x - p1x; - viaOffsetX[layer][1] = (dc > 0.0) ? dc : 0.0; - - dc = s2 + w2x - p2x; - if (dc < 0.0) dc = 0.0; - if (dc > viaOffsetX[layer][1]) viaOffsetX[layer][1] = dc; - - dc = s1 + w1y - p1y; - viaOffsetY[layer][1] = (dc > 0.0) ? dc : 0.0; - - dc = s2 + w2y - p2y; - if (dc < 0.0) dc = 0.0; - if (dc > viaOffsetY[layer][1]) viaOffsetY[layer][1] = dc; - - if (layer < Num_layers - 1) { - - /* Space from via to (via + 1) */ - - w3x = LefGetViaWidth(layer + 1, layer, 0); - w3y = LefGetViaWidth(layer + 1, layer, 1); - - dc = s2 + (w2x + w3x) / 2 - p2x; - viaOffsetX[layer][2] = (dc > 0.0) ? dc : 0.0; - - dc = s2 + (w2y + w3y) / 2 - p2y; - viaOffsetY[layer][2] = (dc > 0.0) ? dc : 0.0; - } - - if (viaOffsetX[layer][0] > 0 || viaOffsetX[layer][1] > 0 || - viaOffsetX[layer][2] > 0) - viaCheckX[layer] = 1; - if (viaOffsetY[layer][0] > 0 || viaOffsetY[layer][1] > 0 || - viaOffsetY[layer][2] > 0) - viaCheckY[layer] = 1; - } - Pathon = -1; lastlay = -1; /* Insert routed net here */ for (rt = net->routes; rt; rt = rt->next) { + + path_delayed.active = 0; + if (rt->segments && !(rt->flags & RT_OUTPUT)) { horizontal = FALSE; cancel = FALSE; @@ -966,6 +1079,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) lastseg = saveseg = seg; layer = seg->layer; if (seg) { + nextvia = (seg->next) ? ((seg->next->segtype == ST_VIA) ? 1 : 0) : 0; // It is rare but possible to have a stub route off of an // endpoint via, so check this case, and use the layer type @@ -983,12 +1097,12 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) " at %d %d (%d)\n", stub, seg->x1, seg->y1, layer); - dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + dc = Xlowerbound + (double)seg->x1 * PitchX; x = (int)((REPS(dc)) * oscale); if (lnode->flags & NI_STUB_EW) dc += stub; x2 = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)seg->y1 * PitchY[layer]; + dc = Ylowerbound + (double)seg->y1 * PitchY; y = (int)((REPS(dc)) * oscale); if (lnode->flags & NI_STUB_NS) dc += stub; @@ -1008,13 +1122,12 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) // ones. If necessary, a flag can be added to // distinguish routes from taps. - if ((x < x2) && (seg->x1 < (NumChannelsX[layer] - 1))) { + if ((x < x2) && (seg->x1 < (NumChannelsX - 1))) { tdir = OBSVAL(seg->x1 + 1, seg->y1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (stub + LefGetRouteKeepout(layer) >= PitchX[layer]) { - dc = Xlowerbound + (double)(seg->x1 + 1) - * PitchX[layer]; + if (stub + LefGetRouteKeepout(layer) >= PitchX) { + dc = Xlowerbound + (double)(seg->x1 + 1) * PitchX; x2 = (int)((REPS(dc)) * oscale); } } @@ -1023,9 +1136,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) tdir = OBSVAL(seg->x1 - 1, seg->y1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (-stub + LefGetRouteKeepout(layer) >= PitchX[layer]) { - dc = Xlowerbound + (double)(seg->x1 - 1) - * PitchX[layer]; + if (-stub + LefGetRouteKeepout(layer) >= PitchX) { + dc = Xlowerbound + (double)(seg->x1 - 1) * PitchX; x2 = (int)((REPS(dc)) * oscale); } } @@ -1066,13 +1178,12 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) // other route, then lengthen it to close up the // distance and resolve the error. - if ((y < y2) && (seg->y1 < (NumChannelsY[layer] - 1))) { + if ((y < y2) && (seg->y1 < (NumChannelsY - 1))) { tdir = OBSVAL(seg->x1, seg->y1 + 1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (stub + LefGetRouteKeepout(layer) >= PitchY[layer]) { - dc = Ylowerbound + (double)(seg->y1 + 1) - * PitchY[layer]; + if (stub + LefGetRouteKeepout(layer) >= PitchY) { + dc = Ylowerbound + (double)(seg->y1 + 1) * PitchY; y2 = (int)((REPS(dc)) * oscale); } } @@ -1081,9 +1192,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) tdir = OBSVAL(seg->x1, seg->y1 - 1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (-stub + LefGetRouteKeepout(layer) >= PitchY[layer]) { - dc = Ylowerbound + (double)(seg->y1 - 1) - * PitchY[layer]; + if (-stub + LefGetRouteKeepout(layer) >= PitchY) { + dc = Ylowerbound + (double)(seg->y1 - 1) * PitchY; y2 = (int)((REPS(dc)) * oscale); } } @@ -1119,8 +1229,9 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) if (cancel == FALSE) { net->flags |= NET_STUB; rt->flags |= RT_STUB; - pathstart(Cmd, layer, x2, y2, special, oscale, invscale, horizontal); - pathto(Cmd, x, y, horizontal, x2, y2, invscale); + pathstart(Cmd, layer, x2, y2, special, oscale, invscale, horizontal, + lnode); + pathto(Cmd, x, y, horizontal, x2, y2, invscale, nextvia); } lastx = x; lasty = y; @@ -1131,6 +1242,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) prevseg = NULL; lastseg = NULL; for (seg = rt->segments; seg; seg = seg->next) { + nextvia = (seg->next) ? ((seg->next->segtype == ST_VIA) ? 1 : 0) : 0; layer = seg->layer; // Check for offset terminals at either point @@ -1139,6 +1251,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) offset2 = 0.0; dir1 = 0; dir2 = 0; + lnode1 = lnode2 = NULL; if (seg->segtype & ST_OFFSET_START) { dir1 = OBSVAL(seg->x1, seg->y1, seg->layer) & OFFSET_TAP; @@ -1224,16 +1337,16 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) // and are redundant between CIFLayer[] from the // config file and lefInfo. - dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + dc = Xlowerbound + (double)seg->x1 * PitchX; if ((dir1 & OFFSET_TAP) && (lnode1->flags & NI_OFFSET_EW)) dc += offset1; x = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)seg->y1 * PitchY[layer]; + dc = Ylowerbound + (double)seg->y1 * PitchY; if ((dir1 & OFFSET_TAP) && (lnode1->flags & NI_OFFSET_NS)) dc += offset1; y = (int)((REPS(dc)) * oscale); - dc = Xlowerbound + (double)seg->x2 * PitchX[layer]; + dc = Xlowerbound + (double)seg->x2 * PitchX; if ((dir2 & OFFSET_TAP) && (lnode2->flags & NI_OFFSET_EW)) dc += offset2; x2 = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)seg->y2 * PitchY[layer]; + dc = Ylowerbound + (double)seg->y2 * PitchY; if ((dir2 & OFFSET_TAP) && (lnode2->flags & NI_OFFSET_NS)) dc += offset2; y2 = (int)((REPS(dc)) * oscale); segtype = seg->segtype & ~(ST_OFFSET_START | ST_OFFSET_END); @@ -1273,12 +1386,26 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) /* Add bend and connect to offset via */ int vertical = (horizontal) ? FALSE : TRUE; pathstart(Cmd, seg->layer, lastx, lasty, special, - oscale, invscale, vertical); - pathto(Cmd, x, y, vertical, lastx, lasty, invscale); + oscale, invscale, vertical, lnode2); + pathto(Cmd, x, y, vertical, lastx, lasty, invscale, nextvia); + } + else if (lastseg && (lastseg->segtype & ST_VIA) && + (lastx != x) && (lasty == y) && + (LefGetRouteOrientation(seg->layer) == 1)) { + /* Via offset in direction of route (horizontal) */ + pathstart(Cmd, seg->layer, lastx, y, special, oscale, + invscale, horizontal, lnode2); + } + else if (lastseg && (lastseg->segtype & ST_VIA) && + (lastx == x) && (lasty != y) && + (LefGetRouteOrientation(seg->layer) == 0)) { + /* Via offset in direction of route (vertical) */ + pathstart(Cmd, seg->layer, x, lasty, special, oscale, + invscale, horizontal, lnode2); } else { pathstart(Cmd, seg->layer, x, y, special, oscale, - invscale, horizontal); + invscale, horizontal, lnode2); } lastx = x; lasty = y; @@ -1296,17 +1423,26 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) horizontal = FALSE; } if (special == (u_char)0) { - pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale); + pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale, nextvia); lastx = x2; lasty = y2; } + // Check for path segments that are used for minimum metal + // area requirements so that they do not become false + // positives for inter-via special nets. + + if (lastseg && seg->next && (lastseg->x1 == seg->next->x2) && + (lastseg->y1 == seg->next->y2)) + seg->segtype |= ST_MINMETAL; + // If a segment is 1 track long, there is a via on either // end, and the needblock flag is set for the layer, then // draw a stub route along the length of the track. if (horizontal && needblock[seg->layer] & VIABLOCKX) { - if (ABSDIFF(seg->x2, seg->x1) == 1) { + if ((ABSDIFF(seg->x2, seg->x1) == 1) && + !(seg->segtype & ST_MINMETAL)) { if ((lastseg && lastseg->segtype == ST_VIA) || (seg->next && seg->next->segtype == ST_VIA)) { if (special == (u_char)0) { @@ -1316,15 +1452,16 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) else { if (Pathon != -1) Pathon = 0; pathstart(Cmd, layer, x, y, special, oscale, - invscale, horizontal); - pathto(Cmd, x2, y2, horizontal, x, y, invscale); + invscale, horizontal, lnode2); + pathto(Cmd, x2, y2, horizontal, x, y, invscale, 0); lastlay = layer; } } } } else if (!horizontal && needblock[seg->layer] & VIABLOCKY) { - if (ABSDIFF(seg->y2, seg->y1) == 1) { + if ((ABSDIFF(seg->y2, seg->y1) == 1) && + !(seg->segtype & ST_MINMETAL)) { if ((lastseg && lastseg->segtype == ST_VIA) || (seg->next && seg->next->segtype == ST_VIA)) { if (special == (u_char)0) { @@ -1334,25 +1471,36 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) else { if (Pathon != -1) Pathon = 0; pathstart(Cmd, layer, x, y, special, oscale, - invscale, horizontal); - pathto(Cmd, x2, y2, horizontal, x, y, invscale); + invscale, horizontal, lnode2); + pathto(Cmd, x2, y2, horizontal, x, y, invscale, 0); lastlay = layer; } } } } break; + case ST_VIA: rt->flags |= RT_OUTPUT; if (special == (u_char)0) { double viaoffx, viaoffy; + double w0, w1, dc, altwx, altwy; + float offsetx, offsety; int vx = 0; int vy = 0; + int flags; u_int tdirpp, tdirp, tdirn; u_char viaNL, viaNM, viaNU; u_char viaSL, viaSM, viaSU; u_char viaEL, viaEM, viaEU; u_char viaWL, viaWM, viaWU; + u_char rteNL, rteNU; + u_char rteSL, rteSU; + u_char rteEL, rteEU; + u_char rteWL, rteWU; + char *s; + char checkersign; + int ob, ot; if (lastseg == NULL) { // Make sure last position is valid @@ -1360,197 +1508,702 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) lasty = y; } - // Check for vias between adjacent but different nets - // that need position offsets to avoid a DRC spacing error - - // viaCheckX[layer] indicates whether a check for - // vias is needed. If so, record what vias are to east - // and west. - - if (viaCheckX[layer] > 0) { - - viaEL = viaEM = viaEU = 0; - viaWL = viaWM = viaWU = 0; + // If vias need to be rotated then they do so on a + // checkerboard pattern. + checkersign = (char)((seg->x1 + seg->y1) & 0x01); + + // Get the default orientation of the via + ob = LefGetRouteOrientation(layer); + ot = LefGetRouteOrientation(layer + 1); + if (ob == 0 && ot == 1) + s = ViaYX[layer]; + else if (ob == 1 && ot == 0) + s = ViaXY[layer]; + else if (ob == 0 && ot == 0) + s = ViaYY[layer]; + else + s = ViaXX[layer]; + + // If via is on a pin and rotation is restricted, then + // set the rotation accordingly. + + flags = 0; + if (layer < Pinlayers) { + if ((lnode = NODEIPTR(seg->x1, seg->y1, layer)) != NULL) { + if (lnode->flags & NI_NO_VIAX) { + flags = NI_NO_VIAX; + if (s == ViaXY[layer]) + s = ViaYY[layer]; + else if (s == ViaXX[layer]) + s = ViaYX[layer]; + } + if (lnode->flags & NI_NO_VIAY) { + flags = NI_NO_VIAY; + if (s == ViaYX[layer]) + s = ViaXX[layer]; + else if (s == ViaYY[layer]) + s = ViaXY[layer]; + } + /* Mark the node with which via direction was used */ + if ((s == ViaYY[layer]) || (s == ViaYX[layer])) + lnode->flags |= NI_VIA_Y; + else + lnode->flags |= NI_VIA_X; + } + } - // Check for via to west - if (seg->x1 > 0) { - tdir = OBSVAL(seg->x1 - 1, seg->y1, layer) + // Check for vias between adjacent but different nets + // that need rotation and/or position offsets to avoid + // a DRC spacing error + + viaEL = viaEM = viaEU = 0; + viaWL = viaWM = viaWU = 0; + viaNL = viaNM = viaNU = 0; + viaSL = viaSM = viaSU = 0; + rteEL = rteEU = rteWL = rteWU = 0; + rteNL = rteNU = rteSL = rteSU = 0; + + // Check for via/route to west + if (seg->x1 > 0) { + tdir = OBSVAL(seg->x1 - 1, seg->y1, layer) & ROUTED_NET_MASK; - if (((tdir & NO_NET) == 0) && (tdir != 0) && + if (((tdir & NO_NET) == 0) && (tdir != 0) && (tdir != (net->netnum | ROUTED_NET))) { + rteWL = 1; + if (layer > 0) { + tdirn = OBSVAL(seg->x1 - 1, seg->y1, layer - 1) + & ROUTED_NET_MASK; + if (tdir == tdirn) viaWL = 1; + } + } - if (layer < Num_layers - 1) { - tdirp = OBSVAL(seg->x1 - 1, seg->y1, layer + 1) + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1 - 1, seg->y1, layer + 1) & ROUTED_NET_MASK; - if (((tdirp & NO_NET) == 0) && (tdirp != 0) && + if (((tdirp & NO_NET) == 0) && (tdirp != 0) && (tdirp != (net->netnum | ROUTED_NET))) { - - if (layer < Num_layers - 2) { - tdirpp = OBSVAL(seg->x1 - 1, seg->y1, layer + 2) + rteWU = 1; + if (layer < Num_layers - 2) { + tdirpp = OBSVAL(seg->x1 - 1, seg->y1, layer + 2) & ROUTED_NET_MASK; - if (tdirp == tdirpp) viaWU = 1; - } - } - if (tdir == tdirp) viaWM = 1; - } - - if (layer > 0) { - tdirn = OBSVAL(seg->x1 - 1, seg->y1, layer - 1) - & ROUTED_NET_MASK; - if (tdir == tdirn) viaWL = 1; + if (tdirp == tdirpp) viaWU = 1; } } + if (rteWL && (tdir == tdirp)) viaWM = 1; } + } - // Check for via to east - if (seg->x1 < NumChannelsX[layer] - 1) { - tdir = OBSVAL(seg->x1 + 1, seg->y1, layer) + // Check for via/route to east + if (seg->x1 < NumChannelsX - 1) { + tdir = OBSVAL(seg->x1 + 1, seg->y1, layer) & ROUTED_NET_MASK; - if (((tdir & NO_NET) == 0) && (tdir != 0) && + if (((tdir & NO_NET) == 0) && (tdir != 0) && (tdir != (net->netnum | ROUTED_NET))) { + rteEL = 1; + if (layer > 0) { + tdirn = OBSVAL(seg->x1 + 1, seg->y1, layer - 1) + & ROUTED_NET_MASK; + if (tdir == tdirn) viaEL = 1; + } + } - if (layer < Num_layers - 1) { - tdirp = OBSVAL(seg->x1 + 1, seg->y1, layer + 1) + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1 + 1, seg->y1, layer + 1) & ROUTED_NET_MASK; - if (((tdirp & NO_NET) == 0) && (tdirp != 0) && + if (((tdirp & NO_NET) == 0) && (tdirp != 0) && (tdirp != (net->netnum | ROUTED_NET))) { - - if (layer < Num_layers - 2) { - tdirpp = OBSVAL(seg->x1 + 1, seg->y1, layer + 2) + rteEU = 1; + if (layer < Num_layers - 2) { + tdirpp = OBSVAL(seg->x1 + 1, seg->y1, layer + 2) & ROUTED_NET_MASK; - if (tdirp == tdirpp) viaEU = 1; - } - } - if (tdir == tdirp) viaEM = 1; - } - - if (layer > 0) { - tdirn = OBSVAL(seg->x1 + 1, seg->y1, layer - 1) - & ROUTED_NET_MASK; - if (tdir == tdirn) viaEL = 1; + if (tdirp == tdirpp) viaEU = 1; } } + if (rteEL && (tdir == tdirp)) viaEM = 1; } - - // Compute X offset - viaoffx = 0.0; - - if (viaWL) viaoffx = viaOffsetX[layer][0]; - else if (viaEL) viaoffx = -viaOffsetX[layer][0]; - - if (viaWM && viaOffsetX[layer][1] > viaoffx) - viaoffx = viaOffsetX[layer][1]; - else if (viaEM && -viaOffsetX[layer][1] < viaoffx) - viaoffx = -viaOffsetX[layer][1]; - - if (viaWU && viaOffsetX[layer][2] > viaoffx) - viaoffx = viaOffsetX[layer][2]; - else if (viaEU && -viaOffsetX[layer][2] < viaoffx) - viaoffx = -viaOffsetX[layer][2]; - - vx = (int)((REPS(viaoffx)) * oscale); } - // viaCheckY[layer] indicates whether a check for - // vias is needed. If so, record what vias are to north - // and south. - - if (viaCheckY[layer] > 0) { - - viaNL = viaNM = viaNU = 0; - viaSL = viaSM = viaSU = 0; - - // Check for via to south - if (seg->y1 > 0) { - tdir = OBSVAL(seg->x1, seg->y1 - 1, layer) + // Check for via/route to south + if (seg->y1 > 0) { + tdir = OBSVAL(seg->x1, seg->y1 - 1, layer) & ROUTED_NET_MASK; - if (((tdir & NO_NET) == 0) && (tdir != 0) && + if (((tdir & NO_NET) == 0) && (tdir != 0) && (tdir != (net->netnum | ROUTED_NET))) { + rteSL = 1; + if (layer > 0) { + tdirn = OBSVAL(seg->x1, seg->y1 - 1, layer - 1) + & ROUTED_NET_MASK; + if (tdir == tdirn) viaSL = 1; + } + } - if (layer < Num_layers - 1) { - tdirp = OBSVAL(seg->x1, seg->y1 - 1, layer + 1) + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1, seg->y1 - 1, layer + 1) & ROUTED_NET_MASK; - if (((tdirp & NO_NET) == 0) && (tdirp != 0) && + if (((tdirp & NO_NET) == 0) && (tdirp != 0) && (tdirp != (net->netnum | ROUTED_NET))) { - - if (layer < Num_layers - 2) { - tdirpp = OBSVAL(seg->x1, seg->y1 - 1, layer + 2) + rteSU = 1; + if (layer < Num_layers - 2) { + tdirpp = OBSVAL(seg->x1, seg->y1 - 1, layer + 2) & ROUTED_NET_MASK; - if (tdirp == tdirpp) viaSU = 1; - } - } - if (tdir == tdirp) viaSM = 1; - } - - if (layer > 0) { - tdirn = OBSVAL(seg->x1, seg->y1 - 1, layer - 1) - & ROUTED_NET_MASK; - if (tdir == tdirn) viaSL = 1; + if (tdirp == tdirpp) viaSU = 1; } } + if (rteSL && (tdir == tdirp)) viaSM = 1; } + } - // Check for via to north - if (seg->y1 < NumChannelsY[layer] - 1) { - tdir = OBSVAL(seg->x1, seg->y1 + 1, layer) + // Check for via/route to north + if (seg->y1 < NumChannelsY - 1) { + tdir = OBSVAL(seg->x1, seg->y1 + 1, layer) & ROUTED_NET_MASK; - if (((tdir & NO_NET) == 0) && (tdir != 0) && + if (((tdir & NO_NET) == 0) && (tdir != 0) && (tdir != (net->netnum | ROUTED_NET))) { + rteNL = 1; + if (layer > 0) { + tdirn = OBSVAL(seg->x1, seg->y1 + 1, layer - 1) + & ROUTED_NET_MASK; + if (tdir == tdirn) viaNL = 1; + } + } - if (layer < Num_layers - 1) { - tdirp = OBSVAL(seg->x1, seg->y1 + 1, layer + 1) + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1, seg->y1 + 1, layer + 1) & ROUTED_NET_MASK; - if (((tdirp & NO_NET) == 0) && (tdirp != 0) && + if (((tdirp & NO_NET) == 0) && (tdirp != 0) && (tdirp != (net->netnum | ROUTED_NET))) { - - if (layer < Num_layers - 2) { - tdirpp = OBSVAL(seg->x1, seg->y1 + 1, layer + 2) + rteNU = 1; + if (layer < Num_layers - 2) { + tdirpp = OBSVAL(seg->x1, seg->y1 + 1, layer + 2) & ROUTED_NET_MASK; - if (tdirp == tdirpp) viaNU = 1; - } - } - if (tdir == tdirp) viaNM = 1; - } - - if (layer > 0) { - tdirn = OBSVAL(seg->x1, seg->y1 + 1, layer - 1) - & ROUTED_NET_MASK; - if (tdir == tdirn) viaNL = 1; + if (tdirp == tdirpp) viaNU = 1; } } + if (rteNL && (tdir == tdirp)) viaNM = 1; } + } - // Compute Y offset - viaoffy = 0; + // Check for any tap offset on the neighboring via, + // which needs to be accounted for in the calculations. + offsetx = offsety = 0.0; + if (viaEL) + lnode = (layer < Pinlayers && layer > 0) ? + NODEIPTR(seg->x1 + 1, seg->y1, layer - 1) : NULL; + else if (viaWL) + lnode = (layer < Pinlayers && layer > 0) ? + NODEIPTR(seg->x1 - 1, seg->y1, layer - 1) : NULL; + else if (viaEM) + lnode = (layer < Pinlayers) ? + NODEIPTR(seg->x1 + 1, seg->y1, layer) : NULL; + else if (viaWM) + lnode = (layer < Pinlayers) ? + NODEIPTR(seg->x1 - 1, seg->y1, layer) : NULL; + else if (viaEU) + lnode = (layer < Pinlayers - 1) ? + NODEIPTR(seg->x1 + 1, seg->y1, layer + 1) : NULL; + else if (viaWU) + lnode = (layer < Pinlayers - 1) ? + NODEIPTR(seg->x1 - 1, seg->y1, layer + 1) : NULL; + + if (lnode && (lnode->flags & NI_OFFSET_EW)) { + offsetx = lnode->offset; + // offsetx defined as positive in the direction away from + // the via under consideration. + if (viaWL || viaWM || viaWU) offsetx = -offsetx; + } - if (viaSL) viaoffy = viaOffsetY[layer][0]; - else if (viaNL) viaoffy = -viaOffsetY[layer][0]; + if (viaNL) + lnode = (layer < Pinlayers && layer > 0) ? + NODEIPTR(seg->x1, seg->y1 + 1, layer - 1) : NULL; + else if (viaSL) + lnode = (layer < Pinlayers && layer > 0) ? + NODEIPTR(seg->x1, seg->y1 - 1, layer - 1) : NULL; + else if (viaNM) + lnode = (layer < Pinlayers) ? + NODEIPTR(seg->x1, seg->y1 + 1, layer) : NULL; + else if (viaSM) + lnode = (layer < Pinlayers) ? + NODEIPTR(seg->x1, seg->y1 - 1, layer) : NULL; + else if (viaNU) + lnode = (layer < Pinlayers - 1) ? + NODEIPTR(seg->x1, seg->y1 + 1, layer + 1) : NULL; + else if (viaSU) + lnode = (layer < Pinlayers - 1) ? + NODEIPTR(seg->x1, seg->y1 - 1, layer + 1) : NULL; + + if (lnode && (lnode->flags & NI_OFFSET_NS)) { + offsety = lnode->offset; + // offsety defined as positive in the direction away from + // the via under consideration. + if (viaSL || viaSM || viaSU) offsety = -offsety; + } + + // Actions needed only if a via is on the long side and + // has a spacing violation. + + viaoffx = viaoffy = 0.0; + altwx = altwy = 0.0; + if (ob == 1) { /* bottom (layer) route is horizontal */ + if (viaEM || viaWM) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w1 = LefGetXYViaWidth(layer, layer, 0, 0); + w0 = w1; + dc = LefGetRouteSpacing(layer) + w1; + if (dc > PitchX + EPS + offsetx) { + + /* via on checkerboard may be rotated */ + if (checkersign && ((flags & NI_NO_VIAY) == 0)) { + /* Check spacing violation to routes N and S */ + /* If via being checked is rotated */ + if (rteNL || rteSL) + dc = LefGetRouteSpacing(layer) + + (LefGetXYViaWidth(layer, layer, 1, 2) + + LefGetRouteWidth(layer)) / 2; + if ((!(rteNL || rteSL)) + || (dc <= PitchY + EPS)) { + /* Okay to rotate the via bottom */ + w0 = LefGetXYViaWidth(layer, layer, 0, 2); + s = (ot == 1) ? ViaYX[layer] : ViaYY[layer]; + } + else if (rteNL || rteSL) + altwx = w1 - + LefGetXYViaWidth(layer, layer, 0, 2); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEM) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer, layer, 0, 2); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEM) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + } + } + else if (viaEL || viaWL) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w0 = LefGetXYViaWidth(layer - 1, layer, 0, 0); + w1 = LefGetXYViaWidth(layer, layer, 0, 0); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + + /* via on checkerboard may be rotated */ + if (checkersign && ((flags & NI_NO_VIAY) == 0)) { + /* Check spacing violation to routes N and S */ + /* If via being checked is rotated */ + if (rteNL || rteSL) + dc = LefGetRouteSpacing(layer) + + (LefGetXYViaWidth(layer, layer, 1, 2) + + LefGetRouteWidth(layer)) / 2; + if ((!(rteNL || rteSL)) + || (dc <= PitchY + EPS)) { + /* Okay to rotate the via bottom */ + w1 = LefGetXYViaWidth(layer, layer, 0, 2); + s = (ot == 1) ? ViaYX[layer] : ViaYY[layer]; + } + else if (rteNL || rteSL) + altwx = w1 - + LefGetXYViaWidth(layer, layer, 0, 2); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEL) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer - 1, layer, 0, 1); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEL) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + } + } + } else { /* bottom route vertical */ + if (viaNM || viaSM) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w1 = LefGetXYViaWidth(layer, layer, 1, 3); + w0 = w1; + dc = LefGetRouteSpacing(layer) + w1; + if (dc > PitchY + EPS + offsety) { + + /* via on checkerboard may be rotated */ + if (checkersign && ((flags & NI_NO_VIAX) == 0)) { + /* Check spacing violation to routes E and W */ + /* If via being checked is rotated */ + if (rteEL || rteWL) + dc = LefGetRouteSpacing(layer) + + (LefGetXYViaWidth(layer, layer, 0, 1) + + LefGetRouteWidth(layer)) / 2; + if ((!(rteEL || rteWL)) + || (dc <= PitchX + EPS)) { + /* Okay to rotate the via bottom */ + w0 = LefGetXYViaWidth(layer, layer, 1, 1); + s = (ot == 1) ? ViaXX[layer] : ViaXY[layer]; + } + else if (rteEL || rteWL) + altwy = w1 - + LefGetXYViaWidth(layer, layer, 1, 1); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNM) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer, layer, 1, 1); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNM) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + } + } + else if (viaNL || viaSL) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w0 = LefGetXYViaWidth(layer - 1, layer, 1, 3); + w1 = LefGetXYViaWidth(layer, layer, 1, 3); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + + /* via on checkerboard may be rotated */ + if (checkersign && ((flags & NI_NO_VIAX) == 0)) { + /* Check spacing violation to routes E and W */ + /* If via being checked is rotated */ + if (rteEL || rteWL) + dc = LefGetRouteSpacing(layer) + + (LefGetXYViaWidth(layer, layer, 0, 1) + + LefGetRouteWidth(layer)) / 2; + if ((!(rteEL || rteWL)) + || (dc <= PitchX + EPS)) { + /* Okay to rotate the via bottom */ + w1 = LefGetXYViaWidth(layer, layer, 1, 1); + s = (ot == 1) ? ViaXX[layer] : ViaXY[layer]; + } + else if (rteEL || rteWL) + altwy = w1 - + LefGetXYViaWidth(layer, layer, 1, 1); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNL) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer - 1, layer, 1, 2); + dc = LefGetRouteSpacing(layer) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNL) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + } + } + } - if (viaSM && viaOffsetY[layer][1] > viaoffy) - viaoffy = viaOffsetY[layer][1]; - else if (viaNM && -viaOffsetY[layer][1] < viaoffy) - viaoffy = -viaOffsetY[layer][1]; + if (ot == 1) { /* top route horizontal */ + if (viaEU || viaWU) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w0 = LefGetXYViaWidth(layer + 1, layer + 1, 0, 0); + w1 = LefGetXYViaWidth(layer, layer + 1, 0, 0); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + + /* via on checkerboard may be rotated */ + if (checkersign) { + /* Check spacing violation to routes N and S */ + /* If via being checked is rotated */ + if (rteNU || rteSU) + dc = LefGetRouteSpacing(layer + 1) + + (LefGetXYViaWidth(layer, layer + 1, 1, 1) + + LefGetRouteWidth(layer + 1)) / 2; + if ((!(rteNU || rteSU)) + || (dc <= PitchY + EPS)) { + /* Okay to rotate the via top */ + w1 = LefGetXYViaWidth(layer, layer + 1, 0, 1); + s = (s == ViaYX[layer]) ? ViaYY[layer] : + ViaXY[layer]; + } + else if (rteNU || rteSU) + altwx = w1 - + LefGetXYViaWidth(layer, layer + 1, 0, 1); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEU) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer + 1, layer + 1, 0, 2); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEU) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + } + } + else if (viaEM || viaWM) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w1 = LefGetXYViaWidth(layer, layer + 1, 0, 0); + w0 = w1; + dc = LefGetRouteSpacing(layer + 1) + w1; + if (dc > PitchX + EPS + offsetx) { + + /* via on checkerboard may be rotated */ + if (checkersign) { + /* Check spacing violation to routes N and S */ + /* If via being checked is rotated */ + if (rteNU || rteSU) + dc = LefGetRouteSpacing(layer) + + (LefGetXYViaWidth(layer, layer + 1, 1, 1) + + LefGetRouteWidth(layer)) / 2; + if ((!(rteNU || rteSU)) || (dc <= PitchY + EPS)) { + /* Okay to rotate the via top */ + w0 = LefGetXYViaWidth(layer, layer + 1, 0, 1); + s = (s == ViaYX[layer]) ? ViaYY[layer] : + ViaXY[layer]; + } + else if (rteNU || rteSU) + altwx = w1 - + LefGetXYViaWidth(layer, layer + 1, 0, 1); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEM) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer, layer + 1, 0, 1); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchX + EPS + offsetx) { + /* Calculate offset */ + if (viaEM) + viaoffx = PitchX + 2 * offsetx - dc; + else + viaoffx = dc - PitchX - 2 * offsetx; + } + } + } + } + } else { /* top route vertical */ + if (viaNU || viaSU) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w0 = LefGetXYViaWidth(layer + 1, layer + 1, 1, 3); + w1 = LefGetXYViaWidth(layer, layer + 1, 1, 3); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + + /* via on checkerboard may be rotated */ + if (checkersign) { + /* Check spacing violation to routes E and W */ + /* If via being checked is rotated */ + if (rteEU || rteWU) + dc = LefGetRouteSpacing(layer + 1) + + (LefGetXYViaWidth(layer, layer + 1, 0, 2) + + LefGetRouteWidth(layer + 1)) / 2; + if ((!(rteEU || rteWU)) + || (dc <= PitchX + EPS)) { + /* Okay to rotate the via top */ + w1 = LefGetXYViaWidth(layer, layer + 1, 1, 2); + s = (s == ViaYY[layer]) ? ViaYX[layer] : + ViaXX[layer]; + } + else if (rteEU || rteWU) + altwy = w1 - + LefGetXYViaWidth(layer, layer + 1, 1, 2); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNU) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer + 1, layer + 1, 1, 1); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNU) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + } + } + else if (viaNM || viaSM) { + /* Assume default rotations and calculate spacing */ + /* Only take action if there is a spacing violation */ + + w1 = LefGetXYViaWidth(layer, layer + 1, 1, 3); + w0 = w1; + dc = LefGetRouteSpacing(layer + 1) + w1; + if (dc > PitchY + EPS + offsety) { + + /* via on checkerboard may be rotated */ + if (checkersign) { + /* Check spacing violation to routes E and W */ + /* If via being checked is rotated */ + if (rteEU || rteWU) + dc = LefGetRouteSpacing(layer + 1) + + (LefGetXYViaWidth(layer, layer + 1, 0, 2) + + LefGetRouteWidth(layer + 1)) / 2; + if ((!(rteEU || rteWU)) + || (dc <= PitchX + EPS)) { + /* Okay to rotate the via top */ + w1 = LefGetXYViaWidth(layer, layer + 1, 1, 2); + s = (s == ViaYY[layer]) ? ViaYX[layer] : + ViaXX[layer]; + } + else if (rteEU || rteWU) + altwy = w1 - + LefGetXYViaWidth(layer, layer + 1, 1, 2); + + /* Measure spacing violation to via */ + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNM) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + else { + /* Measure spacing violation to rotated via */ + w0 = LefGetXYViaWidth(layer, layer + 1, 1, 2); + dc = LefGetRouteSpacing(layer + 1) + (w1 + w0) / 2; + if (dc > PitchY + EPS + offsety) { + /* Calculate offset */ + if (viaNM) + viaoffy = PitchY + 2 * offsety - dc; + else + viaoffy = dc - PitchY - 2 * offsety; + } + } + } + } + } - if (viaSU && viaOffsetY[layer][2] > viaoffy) - viaoffy = viaOffsetY[layer][2]; - else if (viaNU && -viaOffsetY[layer][2] < viaoffy) - viaoffy = -viaOffsetY[layer][2]; + /* If a via is constrained by routes on the side */ + /* and cannot be rotated, then the calculations on */ + /* the via in the next track will not take that */ + /* into consideration, and not leave enough space. */ + /* So this via must offset more to make up the */ + /* difference. */ - vy = (int)((REPS(viaoffy)) * oscale); + if (altwx > 0.0) { + if (viaoffx < 0) altwx = -altwx; + viaoffx += altwx; + } + if (altwy > 0.0) { + if (viaoffy < 0) altwy = -altwy; + viaoffy += altwy; } - // via-to-via interactions are symmetric, so move each - // via half the distance (?) + /* When offset, each via moves by half the distance. */ + viaoffx /= 2; + viaoffy /= 2; - pathvia(Cmd, layer, x + vx, y + vy, lastx, lasty, - seg->x1, seg->y1, invscale); + vx = (int)((REPS(viaoffx)) * oscale); + vy = (int)((REPS(viaoffy)) * oscale); - lastx = x; - lasty = y; + /* If via is offset in the direction of the last */ + /* route segment, then move the last route segment */ + /* position to the via center. */ + if ((path_delayed.active == 1) && (vy != 0) && + (path_delayed.orient == 0)) + path_delayed.y = y + vy; + + else if ((path_delayed.active == 1) && (vx != 0) && + (path_delayed.orient == 1)) + path_delayed.x = x + vx; + + pathvia(Cmd, layer, x + vx, y + vy, lastx, lasty, s, invscale); + + lastx = x + vx; + lasty = y + vy; lastlay = -1; } break; @@ -1608,20 +2261,20 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) if (prevseg->segtype & ST_VIA) { - dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + dc = Xlowerbound + (double)seg->x1 * PitchX; x = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)seg->y1 * PitchY[layer]; + dc = Ylowerbound + (double)seg->y1 * PitchY; y = (int)((REPS(dc)) * oscale); - dc = Xlowerbound + (double)prevseg->x1 * PitchX[layer]; + dc = Xlowerbound + (double)prevseg->x1 * PitchX; x2 = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)prevseg->y1 * PitchY[layer]; + dc = Ylowerbound + (double)prevseg->y1 * PitchY; y2 = (int)((REPS(dc)) * oscale); // Setup is (via, 1 track route, via with offset) if (prevseg->x1 != seg->x1) { - if ((PitchX[lastseg->layer] - + if ((PitchX - 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 1) - 0.5 * LefGetViaWidth(prevseg->layer, lastseg->layer, 1) - (prevseg->x1 - seg->x1) * offset) @@ -1632,15 +2285,15 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) } else { pathstart(Cmd, lastseg->layer, x, y, - (u_char)1, oscale, invscale, 1); - pathto(Cmd, x2, y2, 1, x, y, invscale); + (u_char)1, oscale, invscale, 1, lnode); + pathto(Cmd, x2, y2, 1, x, y, invscale, 0); lastx = x2; lasty = y2; } } } else if (prevseg->y1 != seg->y1) { - if ((PitchY[lastseg->layer] - + if ((PitchY - 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 0) - 0.5 * LefGetViaWidth(prevseg->layer, lastseg->layer, 0) - (prevseg->y1 - seg->y1) * offset) @@ -1651,8 +2304,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) } else { pathstart(Cmd, lastseg->layer, x, y, - (u_char)1, oscale, invscale, 0); - pathto(Cmd, x2, y2, 0, x, y, invscale); + (u_char)1, oscale, invscale, 0, lnode); + pathto(Cmd, x2, y2, 0, x, y, invscale, 0); lastx = x2; lasty = y2; } @@ -1661,7 +2314,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) } else { // Metal route bends at next track if (prevseg->x1 != seg->x1) { - if ((PitchX[lastseg->layer] - + if ((PitchX - 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 1) - 0.5 * LefGetRouteWidth(prevseg->layer) - (prevseg->x1 - seg->x1) * offset) @@ -1672,15 +2325,15 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) } else { pathstart(Cmd, lastseg->layer, x, y, - (u_char)1, oscale, invscale, 1); - pathto(Cmd, x2, y2, 1, x, y, invscale); + (u_char)1, oscale, invscale, 1, lnode); + pathto(Cmd, x2, y2, 1, x, y, invscale, 0); lastx = x2; lasty = y2; } } } else if (prevseg->y1 != seg->y1) { - if ((PitchY[lastseg->layer] - + if ((PitchY - 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 0) - 0.5 * LefGetRouteWidth(prevseg->layer) - (prevseg->y1 - seg->y1) * offset) @@ -1691,8 +2344,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) } else { pathstart(Cmd, lastseg->layer, x, y, - (u_char)1, oscale, invscale, 0); - pathto(Cmd, x2, y2, 0, x, y, invscale); + (u_char)1, oscale, invscale, 0, lnode); + pathto(Cmd, x2, y2, 0, x, y, invscale, 0); lastx = x2; lasty = y2; } @@ -1715,14 +2368,14 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) " at %d %d (%d)\n", stub, seg->x2, seg->y2, layer); - dc = Xlowerbound + (double)seg->x2 * PitchX[layer]; + dc = Xlowerbound + (double)seg->x2 * PitchX; if (lnode->flags & NI_OFFSET_EW) dc += offset; x = (int)((REPS(dc)) * oscale); if (lnode->flags & NI_STUB_EW) dc += stub; x2 = (int)((REPS(dc)) * oscale); - dc = Ylowerbound + (double)seg->y2 * PitchY[layer]; + dc = Ylowerbound + (double)seg->y2 * PitchY; if (lnode->flags & NI_OFFSET_NS) dc += offset; y = (int)((REPS(dc)) * oscale); @@ -1738,13 +2391,12 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) // other route, then lengthen it to close up the // distance and resolve the error. - if ((x < x2) && (seg->x2 < (NumChannelsX[layer] - 1))) { + if ((x < x2) && (seg->x2 < (NumChannelsX - 1))) { tdir = OBSVAL(seg->x2 + 1, seg->y2, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (stub + LefGetRouteKeepout(layer) >= PitchX[layer]) { - dc = Xlowerbound + (double)(seg->x2 + 1) - * PitchX[layer]; + if (stub + LefGetRouteKeepout(layer) >= PitchX) { + dc = Xlowerbound + (double)(seg->x2 + 1) * PitchX; x2 = (int)((REPS(dc)) * oscale); } } @@ -1753,9 +2405,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) tdir = OBSVAL(seg->x2 - 1, seg->y2, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (-stub + LefGetRouteKeepout(layer) >= PitchX[layer]) { - dc = Xlowerbound + (double)(seg->x2 - 1) - * PitchX[layer]; + if (-stub + LefGetRouteKeepout(layer) >= PitchX) { + dc = Xlowerbound + (double)(seg->x2 - 1) * PitchX; x2 = (int)((REPS(dc)) * oscale); } } @@ -1796,13 +2447,12 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) // other route, then lengthen it to close up the // distance and resolve the error. - if ((y < y2) && (seg->y2 < (NumChannelsY[layer] - 1))) { + if ((y < y2) && (seg->y2 < (NumChannelsY - 1))) { tdir = OBSVAL(seg->x2, seg->y2 + 1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (stub + LefGetRouteKeepout(layer) >= PitchY[layer]) { - dc = Ylowerbound + (double)(seg->y2 + 1) - * PitchY[layer]; + if (stub + LefGetRouteKeepout(layer) >= PitchY) { + dc = Ylowerbound + (double)(seg->y2 + 1) * PitchY; y2 = (int)((REPS(dc)) * oscale); } } @@ -1811,9 +2461,8 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) tdir = OBSVAL(seg->x2, seg->y2 - 1, layer); if ((tdir & ROUTED_NET_MASK) == (net->netnum | ROUTED_NET)) { - if (-stub + LefGetRouteKeepout(layer) >= PitchY[layer]) { - dc = Ylowerbound + (double)(seg->y2 - 1) - * PitchY[layer]; + if (-stub + LefGetRouteKeepout(layer) >= PitchY) { + dc = Ylowerbound + (double)(seg->y2 - 1) * PitchY; y2 = (int)((REPS(dc)) * oscale); } } @@ -1850,11 +2499,11 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) rt->flags |= RT_STUB; if (Pathon != 1) { pathstart(Cmd, layer, x, y, special, oscale, invscale, - horizontal); + horizontal, lnode); lastx = x; lasty = y; } - pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale); + pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale, 0); lastx = x2; lasty = y2; } @@ -1882,7 +2531,7 @@ emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) static void emit_routes(char *filename, double oscale, int iscale) { FILE *Cmd; - int i, j, numnets, stubroutes; + int i, j, numnets, numvias, stubroutes; char line[MAX_LINE_LEN + 1], *lptr = NULL; char netname[MAX_NAME_LEN]; NET net = NULL; @@ -1890,9 +2539,11 @@ static void emit_routes(char *filename, double oscale, int iscale) FILE *fdef; u_char errcond = FALSE; u_char need_cleanup = FALSE; + u_char purge_routed = FALSE; + u_char skip_net = FALSE; fdef = fopen(DEFfilename, "r"); - if (fdef == NULL) { + if ((fdef == NULL) && (DEFfilename != NULL)) { if (strchr(DEFfilename, '.') == NULL) { char *extfilename = malloc(strlen(DEFfilename) + 5); sprintf(extfilename, "%s.def", DEFfilename); @@ -1943,12 +2594,23 @@ static void emit_routes(char *filename, double oscale, int iscale) // Copy DEF file up to NETS line numnets = 0; + numvias = 0; while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { lptr = line; while (isspace(*lptr)) lptr++; if (!strncmp(lptr, "NETS", 4)) { - sscanf(lptr + 4, "%d", &numnets); - break; + sscanf(lptr + 4, "%d", &numnets); + break; + } + if (!strncmp(lptr, "VIAS", 4)) { + sscanf(lptr + 4, "%d", &numvias); + LefWriteGeneratedVias(Cmd, (double)(oscale / (double)iscale), numvias); + continue; /* VIAS line already written; do not output. */ + } + if (!strncmp(lptr, "PINS", 4) && (numvias == 0)) { + /* Check if there are any generated vias, and write them */ + /* prior to the PINS section. */ + LefWriteGeneratedVias(Cmd, (double)(oscale / (double)iscale), 0); } fputs(line, Cmd); } @@ -1962,13 +2624,6 @@ static void emit_routes(char *filename, double oscale, int iscale) Numnets); } - // Quick check to see if cleanup_nets can be avoided - for (i = 0; i < Num_layers; i++) - if (needblock[i] & (VIABLOCKX | VIABLOCKY)) - break; - - if (i != Num_layers) need_cleanup = TRUE; - for (i = 0; i < numnets; i++) { if (errcond == TRUE) break; while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { @@ -1994,8 +2649,9 @@ static void emit_routes(char *filename, double oscale, int iscale) // the original routing information while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { if ((lptr = strchr(line, ';')) != NULL) { - *lptr = '\n'; - *(lptr + 1) = '\0'; + // *lptr = '\n'; + // *(lptr + 1) = '\0'; + *line = '\0'; break; } } @@ -2016,17 +2672,16 @@ static void emit_routes(char *filename, double oscale, int iscale) /* Find this net */ - for (j = 0; j < Numnets; j++) { - net = Nlnets[j]; - if (!strcmp(net->netname, netname)) - break; - } - if (!net) { - Fprintf(stderr, "emit_routes(): Net %s cannot be found.\n", - netname); + net = DefFindNet(netname); + if (!net || (net->flags & NET_IGNORED)) { + if (!net) + Fprintf(stderr, "emit_routes(): Net %s cannot be found.\n", + netname); - /* Dump rest of net and continue---no routing information */ + /* Dump rest of net and continue */ *(lptr) = ';'; + *(lptr + 1) = '\n'; + *(lptr + 2) = '\0'; fputs(line, Cmd); continue; } @@ -2034,7 +2689,6 @@ static void emit_routes(char *filename, double oscale, int iscale) /* Add last net terminal, without the semicolon */ fputs(line, Cmd); - if (need_cleanup) cleanup_net(net); emit_routed_net(Cmd, net, (u_char)0, oscale, iscale); fprintf(Cmd, ";\n"); } @@ -2059,6 +2713,7 @@ static void emit_routes(char *filename, double oscale, int iscale) stubroutes = 0; for (i = 0; i < Numnets; i++) { net = Nlnets[i]; + if (net->flags & NET_IGNORED) continue; if (net->flags & NET_STUB) { stubroutes++; for (rt = net->routes; rt; rt = rt->next) @@ -2071,17 +2726,55 @@ static void emit_routes(char *filename, double oscale, int iscale) // proper width. if (stubroutes > 0) { - fprintf(Cmd, "\nSPECIALNETS %d ", stubroutes); + fprintf(Cmd, "\nSPECIALNETS %d ", stubroutes + numSpecial); for (i = 0; i < Numnets; i++) { net = Nlnets[i]; + if (net->flags & NET_IGNORED) continue; emit_routed_net(Cmd, net, (u_char)1, oscale, iscale); } - fprintf(Cmd, ";\nEND SPECIALNETS\n"); + if (numSpecial == 0) + fprintf(Cmd, ";\nEND SPECIALNETS\n"); + else + fprintf(Cmd, ";\n"); } - // Finish copying the rest of the file + // Finish copying the rest of the file. Ignore ROUTED specialnets if + // the nets are known nets and not power or ground nets. FIXED or + // COVER nets are output verbatim. + while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { - fputs(line, Cmd); + lptr = line; + while (isspace(*lptr)) lptr++; + if (!strncmp(lptr, "SPECIALNETS", 11)) { + if (stubroutes > 0) { + purge_routed = TRUE; + continue; /* SPECIALNETS line already written; do not output. */ + } + } + if (!purge_routed) + fputs(line, Cmd); + else { + lptr = line; + while (isspace(*lptr)) lptr++; + if (*lptr == '-') { + lptr++; + while (isspace(*lptr)) lptr++; + sscanf(lptr, "%s", netname); + + // Find this net + net = DefFindNet(netname); + if (!net || (net->flags & NET_IGNORED)) + skip_net = FALSE; + else if (net->netnum == VDD_NET || net->netnum == GND_NET) + skip_net = FALSE; + else + skip_net = TRUE; + } + if (!skip_net) fputs(line, Cmd); + else if ((lptr = strchr(line, ';')) != NULL) { + skip_net = FALSE; + } + } } fclose(fdef); fclose(Cmd); @@ -13,7 +13,9 @@ extern int Pathon; /* Function prototypes */ static void emit_routes(char *filename, double oscale, int iscale); +void cleanup_net(NET net); int write_def(char *filename); +int write_failed(char *filename); char *print_node_name(NODE node); void print_nets(char *filename); void print_routes(char *filename); @@ -30,19 +30,17 @@ int PinNumber = 0; int Num_layers = MAX_LAYERS; // layers to use to route double PathWidth[MAX_LAYERS]; // width of the paths -int GDSLayer[MAX_LAYERS]; // GDS layer number +int GDSLayer[MAX_TYPES]; // GDS layer number int GDSCommentLayer = 1; // for dummy wires, etc. -char CIFLayer[MAX_LAYERS][50]; // CIF layer name -double PitchX[MAX_LAYERS]; // Horizontal wire pitch of layer -double PitchY[MAX_LAYERS]; // Vertical wire pitch of layer -int NumChannelsX[MAX_LAYERS]; // number of wire channels in X on layer -int NumChannelsY[MAX_LAYERS]; // number of wire channels in Y on layer +char CIFLayer[MAX_TYPES][50]; // CIF layer name +double PitchX; // Horizontal wire pitch of layer +double PitchY; // Vertical wire pitch of layer +int NumChannelsX; // number of wire channels in X on layer +int NumChannelsY; // number of wire channels in Y on layer int Vert[MAX_LAYERS]; // 1 if vertical, 0 if horizontal int Numpasses = 10; // number of times to iterate in route_segs char StackedContacts = MAX_LAYERS; // Value is number of contacts that may // be stacked on top of each other. -char ViaPattern = VIA_PATTERN_NONE; // Patterning to be used for vias based - // on grid position (i.e., checkerboarding) double Xlowerbound=0.0; // Bounding Box of routes, in microns double Xupperbound=0.0; @@ -59,80 +57,99 @@ int OffsetCost = 50; // Cost per micron of a node offset int ConflictCost = 50; // Cost of shorting another route // during the rip-up and reroute stage -char *ViaX[MAX_LAYERS]; -char *ViaY[MAX_LAYERS]; +char *ViaXX[MAX_LAYERS]; +char *ViaXY[MAX_LAYERS]; +char *ViaYX[MAX_LAYERS]; +char *ViaYY[MAX_LAYERS]; /*--------------------------------------------------------------*/ /* post_config --- */ /* */ -/* The following code ensures that the layer grids align. */ -/* For now, all PitchX[i] and PitchY[i] should be the same */ -/* for all layers. Hopefully this restriction can be lifted */ -/* sometime, but it will necessarily be a royal pain. */ +/* Resolve PitchX and PitchY, which are the minimum pitches */ +/* that determine the underlying route grid. */ +/* */ +/* If "noprint" is TRUE, then do not print diagnostic info. */ /*--------------------------------------------------------------*/ void -post_config(void) +post_config(u_char noprint) { int i, h, v; + double rpitchx, rpitchy; // Make sure that Num_layers does not exceed the number of // routing layers defined by the LEF file (or the config // file). - i = LefGetMaxLayer(); + i = LefGetMaxRouteLayer(); if (i < Num_layers) Num_layers = i; - h = v = -1; + // Make sure all layers have a pitch in both X and Y even if not + // specified separately in the configuration or def files. for (i = 0; i < Num_layers; i++) { - if (!Vert[i]) { - h = i; - PitchY[i] = PitchX[i]; - PitchX[i] = 0.0; - } - else - v = i; + rpitchx = LefGetRoutePitchX(i); + rpitchy = LefGetRoutePitchY(i); + if ((PitchX == 0.0) || ((rpitchx != 0.0) && (rpitchx + EPS < PitchX))) + PitchX = rpitchx; + if ((PitchY == 0.0) || ((rpitchy != 0.0) && (rpitchy + EPS < PitchY))) + PitchY = rpitchy; } - // In case all layers are listed as horizontal or all - // as vertical, we should still handle it gracefully - - if (h == -1) h = v; - else if (v == -1) v = h; + // This is mostly arbitrary. Generally, all route layer + // pitches except for the smallest X and Y pitches will + // be ignored, and the actual route pitches will be multiples + // of the smallest value, and determined by width and spacing + // rules rather than using any value in the technology LEF. for (i = 0; i < Num_layers; i++) { - if (PitchX[i] != 0.0 && PitchX[i] != PitchX[v]) { - Fprintf(stderr, "Multiple vertical route layers at different" - " pitches. Using smaller pitch %g, will route on" - " 1-of-N tracks if necessary.\n", - PitchX[i]); - PitchX[v] = PitchX[i]; - } - if (PitchY[i] != 0.0 && PitchY[i] != PitchY[h]) { - Fprintf(stderr, "Multiple horizontal route layers at different" - " pitches. Using smaller pitch %g, will route on" - " 1-of-N tracks if necessary.\n", - PitchY[i]); - PitchY[h] = PitchY[i]; - } + if (LefGetRoutePitchX(i) == 0.0) { + if (Vert[i]) + LefSetRoutePitchX(i, PitchX); + else if (i > 0) + LefSetRoutePitchX(i, LefGetRoutePitchX(i - 1)); + else + LefSetRoutePitchX(i, LefGetRoutePitchX(i + 1)); + } + if (LefGetRoutePitchY(i) == 0.0) { + if (!Vert[i]) + LefSetRoutePitchY(i, PitchY); + else if (i > 0) + LefSetRoutePitchY(i, LefGetRoutePitchY(i - 1)); + else + LefSetRoutePitchY(i, LefGetRoutePitchY(i + 1)); + } } - // 2nd pass: Make sure all layers have a pitch in both X and Y - // even if not specified separately in the configuration or def files. - for (i = 0; i < Num_layers; i++) { - if (PitchX[i] == 0.0) PitchX[i] = PitchX[v]; - if (PitchY[i] == 0.0) PitchY[i] = PitchY[h]; + if (noprint == FALSE) { + for (i = 0; i < Num_layers; i++) { + rpitchx = LefGetRoutePitchX(i); + rpitchy = LefGetRoutePitchY(i); + if ((PitchX != 0.0) && (PitchX + EPS < rpitchx)) { + Fprintf(stdout, "Vertical route layer at non-minimum pitch" + " %g. Using smaller pitch %g, will route on" + " 1-of-%d tracks for layer %s.\n", + rpitchx, PitchX, (int)(ceil(rpitchx / PitchX)), + LefGetRouteName(i)); + } + if ((PitchY != 0.0) && (PitchY + EPS < rpitchy)) { + Fprintf(stdout, "Horizontal route layer at non-minimum pitch" + " %g. Using smaller pitch %g, will route on" + " 1-of-%d tracks for layer %s.\n", + rpitchy, PitchY, (int)(ceil(rpitchy / PitchY)), + LefGetRouteName(i)); + } + } } - } /* post_config() */ /*--------------------------------------------------------------*/ -/* Append to string list */ +/* Append to a string */ /*--------------------------------------------------------------*/ void string_list_append(STRING *lst, const char *s) { STRING n, strl; + n = (STRING)malloc(sizeof(struct string_)); n->name = strdup(s); n->next = NULL; @@ -170,8 +187,10 @@ int read_config(FILE *fconfig, int is_info) if (Firstcall) { for (i = 0; i < MAX_LAYERS; i++) { sprintf(line, "via%d%d", i + 1, i + 2); - ViaX[i] = strdup(line); - ViaY[i] = NULL; + ViaXX[i] = strdup(line); + ViaXY[i] = NULL; + ViaYX[i] = NULL; + ViaYY[i] = NULL; } DontRoute = (STRING)NULL; @@ -180,9 +199,7 @@ int read_config(FILE *fconfig, int is_info) Nlgates = (GATE)NULL; UserObs = (DSEG)NULL; - for (i = 0; i < MAX_LAYERS; i++) - PitchX[i] = PitchY[i] = 0.0; - + PitchX = PitchY = 0.0; Firstcall = 0; } @@ -198,11 +215,13 @@ int read_config(FILE *fconfig, int is_info) while (isspace(*lineptr)) lineptr++; if (!strncasecmp(lineptr, "lef", 3) || !strncmp(lineptr, "read_lef", 8)) { + int mscale; if ((i = sscanf(lineptr, "%*s %s\n", sarg)) == 1) { // Argument is a filename of a LEF file from which we // should get the information about gate pins & obstructions OK = 1; - LefRead(sarg); + mscale = LefRead(sarg); + if (mscale > Scales.mscale) Scales.mscale = mscale; } } @@ -220,13 +239,13 @@ int read_config(FILE *fconfig, int is_info) } if ((i = sscanf(lineptr, "layer_%d_name %s", &iarg2, sarg)) == 2) { - if (iarg2 > 0 && iarg2 < 10) { + if (iarg2 > 0 && iarg2 <= MAX_LAYERS) { OK = 1; strcpy(CIFLayer[iarg2 - 1], sarg); } } if ((i = sscanf(lineptr, "gds_layer_%d %d", &iarg2, &iarg)) == 2) { - if (iarg2 > 0 && iarg2 < 10) { + if (iarg2 > 0 && iarg2 <= MAX_TYPES) { OK = 1; GDSLayer[iarg2 - 1] = iarg; } } @@ -276,7 +295,13 @@ int read_config(FILE *fconfig, int is_info) } if ((i = sscanf(lineptr, "layer %d wire pitch %lf\n", &iarg, &darg)) == 2) { - OK = 1; PitchX[iarg-1] = darg; + OK = 1; + if (Vert[iarg - 1]) { + if ((PitchX == 0) || (darg < PitchX)) PitchX = darg; + } + else { + if ((PitchY == 0) || (darg < PitchY)) PitchY = darg; + } } else if (i == 1) { if ((i = sscanf(lineptr, "layer %*d vertical %d\n", &iarg2)) == 1) { @@ -364,14 +389,6 @@ int read_config(FILE *fconfig, int is_info) if (StackedContacts == 0) StackedContacts = 1; } - // Look for via patterning specifications - if (strcasestr(lineptr, "via pattern") != NULL) { - if (strcasestr(lineptr + 12, "normal") != NULL) - ViaPattern = VIA_PATTERN_NORMAL; - else if (strcasestr(lineptr + 12, "invert") != NULL) - ViaPattern = VIA_PATTERN_INVERT; - } - if ((i = sscanf(lineptr, "obstruction %lf %lf %lf %lf %s\n", &darg, &darg2, &darg3, &darg4, sarg)) == 5) { OK = 1; @@ -457,11 +474,11 @@ int read_config(FILE *fconfig, int is_info) // Allocate memory for 10 more pins gateinfo->taps = (DSEG *)realloc(gateinfo->taps, (CurrentPin + 10) * sizeof(DSEG)); - gateinfo->noderec = (NODE *)realloc(gateinfo->taps, + gateinfo->noderec = (NODE *)realloc(gateinfo->noderec, (CurrentPin + 10) * sizeof(NODE)); - gateinfo->netnum = (int *)realloc(gateinfo->taps, + gateinfo->netnum = (int *)realloc(gateinfo->netnum, (CurrentPin + 10) * sizeof(int)); - gateinfo->node = (char **)realloc(gateinfo->taps, + gateinfo->node = (char **)realloc(gateinfo->node, (CurrentPin + 10) * sizeof(char *)); } } @@ -476,7 +493,7 @@ int read_config(FILE *fconfig, int is_info) line[0] = line[1] = '\0'; } - post_config(); + post_config(FALSE); return count; } /* read_config() */ @@ -13,17 +13,16 @@ extern int Num_layers; extern double PathWidth[MAX_LAYERS]; // width of the paths -extern int GDSLayer[MAX_LAYERS]; // GDS layer number +extern int GDSLayer[MAX_TYPES]; // GDS layer number extern int GDSCommentLayer; // for dummy wires, etc. -extern char CIFLayer[MAX_LAYERS][50]; // CIF layer name -extern double PitchX[MAX_LAYERS]; // horizontal wire pitch of layer -extern double PitchY[MAX_LAYERS]; // vertical wire pitch of layer -extern int NumChannelsX[MAX_LAYERS]; -extern int NumChannelsY[MAX_LAYERS]; +extern char CIFLayer[MAX_TYPES][50]; // CIF layer name +extern double PitchX; // base horizontal wire pitch +extern double PitchY; // base vertical wire pitch +extern int NumChannelsX; +extern int NumChannelsY; extern int Vert[MAX_LAYERS]; // 1 if verticle, 0 if horizontal extern int Numpasses; // number of times to iterate in route_segs extern char StackedContacts; // Number of vias that can be stacked together -extern char ViaPattern; // Type of via patterning to use extern double Xlowerbound; // Bounding Box of routes extern double Xupperbound; @@ -38,11 +37,17 @@ extern int BlockCost; extern int OffsetCost; extern int ConflictCost; -extern char *ViaX[MAX_LAYERS]; -extern char *ViaY[MAX_LAYERS]; +// If vias are non-square, then they can have up to four orientations, +// with the top and/or bottom metal layers oriented with the longest +// dimension along either the X or the Y axis. + +extern char *ViaXX[MAX_LAYERS]; // Top and bottom horizontal +extern char *ViaXY[MAX_LAYERS]; // Bottom horizontal, top vertical +extern char *ViaYX[MAX_LAYERS]; // Bottom vertial, top horizontal +extern char *ViaYY[MAX_LAYERS]; // Top and bottom vertical int read_config(FILE *configfileptr, int is_info); -void post_config(void); +void post_config(u_char noprint); #define QCONFIG_H #endif @@ -52,6 +52,7 @@ u_char needblock[MAX_LAYERS]; char *vddnet = NULL; char *gndnet = NULL; +char *antenna_cell = NULL; int Numnets = 0; int Pinlayers = 0; @@ -80,33 +81,33 @@ int set_num_channels(void) NODE node; DPOINT ctap, ltap, ntap; - if (NumChannelsX[0] != 0) return 0; /* Already been called */ + if (NumChannelsX != 0) return 0; /* Already been called */ - for (i = 0; i < Num_layers; i++) { - if (PitchX[i] == 0.0 || PitchY[i] == 0.0) { - Fprintf(stderr, "Have a 0 pitch for layer %d (of %d). " - "Exit.\n", i + 1, Num_layers); - return (-3); - } - NumChannelsX[i] = (int)(1.5 + (Xupperbound - Xlowerbound) / PitchX[i]); - NumChannelsY[i] = (int)(1.5 + (Yupperbound - Ylowerbound) / PitchY[i]); - if ((Verbose > 1) || (NumChannelsX[i] <= 0)) - Fprintf(stdout, "Number of x channels for layer %d is %d\n", - i, NumChannelsX[i]); - if ((Verbose > 1) || (NumChannelsY[i] <= 0)) - Fprintf(stdout, "Number of y channels for layer %d is %d\n", - i, NumChannelsY[i]); + if (PitchX == 0.0) { + Fprintf(stderr, "Have a 0 pitch for X direction. Exit.\n"); + return (-3); + } + else if (PitchY == 0.0) { + Fprintf(stderr, "Have a 0 pitch for Y direction. Exit.\n"); + return (-3); + } + + NumChannelsX = (int)(1.5 + (Xupperbound - Xlowerbound) / PitchX); + NumChannelsY = (int)(1.5 + (Yupperbound - Ylowerbound) / PitchY); + if ((Verbose > 1) || (NumChannelsX <= 0)) + Fprintf(stdout, "Number of x channels is %d\n", NumChannelsX); + if ((Verbose > 1) || (NumChannelsY <= 0)) + Fprintf(stdout, "Number of y channels is %d\n", NumChannelsY); - if (NumChannelsX[i] <= 0) { - Fprintf(stderr, "Something wrong with layer %d x bounds.\n", i); - return(-3); - } - if (NumChannelsY[i] <= 0) { - Fprintf(stderr, "Something wrong with layer %d y bounds.\n", i); - return(-3); - } - Flush(stdout); + if (NumChannelsX <= 0) { + Fprintf(stderr, "Something wrong with x bounds.\n"); + return(-3); } + if (NumChannelsY <= 0) { + Fprintf(stderr, "Something wrong with y bounds.\n"); + return(-3); + } + Flush(stdout); // Go through all nodes and remove any tap or extend entries that are // out of bounds. @@ -118,8 +119,8 @@ int set_num_channels(void) ltap = NULL; for (ctap = node->taps; ctap != NULL; ) { ntap = ctap->next; - glimitx = NumChannelsX[ctap->layer]; - glimity = NumChannelsY[ctap->layer]; + glimitx = NumChannelsX; + glimity = NumChannelsY; if (ctap->gridx < 0 || ctap->gridx >= glimitx || ctap->gridy < 0 || ctap->gridy >= glimity) { /* Remove ctap */ @@ -136,8 +137,8 @@ int set_num_channels(void) ltap = NULL; for (ctap = node->extend; ctap != NULL; ) { ntap = ctap->next; - glimitx = NumChannelsX[ctap->layer]; - glimity = NumChannelsY[ctap->layer]; + glimitx = NumChannelsX; + glimity = NumChannelsY; if (ctap->gridx < 0 || ctap->gridx >= glimitx || ctap->gridy < 0 || ctap->gridy >= glimity) { /* Remove ctap */ @@ -168,7 +169,7 @@ int allocate_obs_array(void) if (Obs[0] != NULL) return 0; /* Already been called */ for (i = 0; i < Num_layers; i++) { - Obs[i] = (u_int *)calloc(NumChannelsX[i] * NumChannelsY[i], + Obs[i] = (u_int *)calloc(NumChannelsX * NumChannelsY, sizeof(u_int)); if (!Obs[i]) { Fprintf(stderr, "Out of memory 4.\n"); @@ -366,34 +367,12 @@ runqrouter(int argc, char *argv[]) #else fprintf(infoFILEptr, "qrouter %s.%s\n", VERSION, REVISION); #endif + /* Output database units expected by the technology LEF file */ + /* Note that this comes from MANUFACTURINGGRID, not UNITS DATABASE */ + fprintf(infoFILEptr, "units scale %d\n", Scales.mscale); - /* Resolve pitches. This is normally done after reading */ - /* the DEF file, but the info file is usually generated */ - /* from LEF layer information only, in order to get the */ - /* values needed to write the DEF file tracks. */ - - for (i = 0; i < Num_layers; i++) { - int o = LefGetRouteOrientation(i); - - /* Set PitchX and PitchY from route info as */ - /* check_variable_pitch needs the values */ - - if (o == 1) - PitchY[i] = LefGetRoutePitch(i); - else - PitchX[i] = LefGetRoutePitch(i); - } - - /* Resolve pitch information similarly to post_config() */ - - for (i = 1; i < Num_layers; i++) { - int o = LefGetRouteOrientation(i); - - if ((o == 1) && (PitchY[i - 1] == 0)) - PitchY[i - 1] = PitchY[i]; - else if ((o == 0) && (PitchX[i - 1] == 0)) - PitchX[i - 1] = PitchX[i]; - } + /* Resolve base horizontal and vertical pitches. */ + post_config(TRUE); /* Print information about route layers, and exit */ for (i = 0; i < Num_layers; i++) { @@ -407,7 +386,7 @@ runqrouter(int argc, char *argv[]) if (hnum > 1 && vnum == 1) vnum++; if (layername != NULL) { - pitch = (o == 1) ? PitchY[i] : PitchX[i], + pitch = (o == 1) ? PitchY : PitchX, width = LefGetRouteWidth(i); if (pitch == 0.0 || width == 0.0) continue; fprintf(infoFILEptr, "%s %g %g %g %s", @@ -444,7 +423,7 @@ runqrouter(int argc, char *argv[]) } Obs[0] = (u_int *)NULL; - NumChannelsX[0] = 0; // This is so we can check if NumChannelsX/Y were + NumChannelsX = 0; // This is so we can check if NumChannelsX/Y were // set from within DefRead() due to reading in // existing nets. @@ -476,7 +455,7 @@ u_char remove_from_failed(NET net) } lastnl = nl; } - return FALSE; + return FALSE; } /*--------------------------------------------------------------*/ @@ -496,6 +475,25 @@ void remove_failed() } /*--------------------------------------------------------------*/ +/* Remove the first (top) route record from a net */ +/*--------------------------------------------------------------*/ + +void remove_top_route(NET net) +{ + ROUTE rt; + SEG seg; + + rt = net->routes; + net->routes = net->routes->next; + while (rt->segments) { + seg = rt->segments; + rt->segments = rt->segments->next; + free(seg); + } + free(rt); +} + +/*--------------------------------------------------------------*/ /* reinitialize --- */ /* */ /* Free up memory in preparation for reading another DEF file */ @@ -516,7 +514,7 @@ static void reinitialize() // Free up all of the matrices for (i = 0; i < Pinlayers; i++) { - for (j = 0; j < NumChannelsX[i] * NumChannelsY[i]; j++) + for (j = 0; j < NumChannelsX * NumChannelsY; j++) if (Nodeinfo[i][j]) free(Nodeinfo[i][j]); free(Nodeinfo[i]); @@ -547,16 +545,9 @@ static void reinitialize() net->noripup = net->noripup->next; free(nl); } - while (net->routes) { - rt = net->routes; - net->routes = net->routes->next; - while (rt->segments) { - seg = rt->segments; - rt->segments = rt->segments->next; - free(seg); - } - free(rt); - } + while (net->routes) + remove_top_route(net); + while (net->netnodes) { node = net->netnodes; net->netnodes = net->netnodes->next; @@ -609,6 +600,172 @@ static void reinitialize() } /*--------------------------------------------------------------*/ +/* apply_drc_blocks() --- */ +/* */ +/* Use via and route width and spacing information to determine */ +/* if blockages are needed in tracks adjacent to routed */ +/* segments to avoid causing DRC errors in the output. */ +/* */ +/* If layer == -1, then determine values normally for all */ +/* route layers. If layer >= 0, determine values for specified */ +/* layer only. If via_except > 0, then adjust the value for */ +/* a DRC violating distance for vias in adjacent tracks by that */ +/* amount (in microns). If route_except > 0, then adjust the */ +/* value for a DRC violating distance between a via and a route */ +/* in adjacent tracks by that amount. */ +/*--------------------------------------------------------------*/ + +void apply_drc_blocks(int layer, double via_except, double route_except) +{ + int i; + double sreq1, sreq2, sreq2t; + + // Fill in needblock bit fields, which are used by commit_proute + // when route layers are too large for the grid size, and grid points + // around a route need to be marked as blocked whenever something is + // routed on those layers. + + // "ROUTEBLOCK" is set if the spacing is violated between a normal + // route and an adjacent via. "VIABLOCK" is set if the spacing is + // violated between two adjacent vias. It may be helpful to define + // a third category which is route-to-route spacing violation. + + // There are up to four different via types per base layer with + // different geometries based on the permutation of rotations of + // the top and bottom layers, so we only register blocking behavior + // if all of the via types will generate spacing violations. + + for (i = 0; i < Num_layers; i++) { + if ((layer >= 0) && (i != layer)) continue; + + needblock[i] = FALSE; + + sreq1 = LefGetRouteSpacing(i); + if (i < Num_layers - 1) { + sreq2 = LefGetXYViaWidth(i, i, 0, 0) + sreq1; + sreq2t = LefGetXYViaWidth(i, i, 0, 1) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i, i, 0, 2) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i, i, 0, 3) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= via_except; + if ((sreq2 - EPS) > PitchX) needblock[i] |= VIABLOCKX; + } + if (i != 0) { + sreq2 = LefGetXYViaWidth(i - 1, i, 0, 0) + sreq1; + sreq2t = LefGetXYViaWidth(i - 1, i, 0, 1) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i - 1, i, 0, 2) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i - 1, i, 0, 3) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= via_except; + if ((sreq2 - EPS) > PitchX) needblock[i] |= VIABLOCKX; + } + + if (i < Num_layers - 1) { + sreq2 = LefGetXYViaWidth(i, i, 1, 0) + sreq1; + sreq2t = LefGetXYViaWidth(i, i, 1, 1) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i, i, 1, 2) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i, i, 1, 3) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= via_except; + if ((sreq2 - EPS) > PitchY) needblock[i] |= VIABLOCKY; + } + if (i != 0) { + sreq2 = LefGetXYViaWidth(i - 1, i, 1, 0) + sreq1; + sreq2t = LefGetXYViaWidth(i - 1, i, 1, 1) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i - 1, i, 1, 2) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = LefGetXYViaWidth(i - 1, i, 1, 3) + sreq1; + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= via_except; + if ((sreq2 - EPS) > PitchY) needblock[i] |= VIABLOCKY; + } + + sreq1 += 0.5 * LefGetRouteWidth(i); + + if (i < Num_layers - 1) { + sreq2 = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 0, 0); + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 0, 1); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 0, 2); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 0, 3); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= route_except; + if ((sreq2 - EPS) > PitchX) needblock[i] |= ROUTEBLOCKX; + } + if (i != 0) { + sreq2 = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 0, 0); + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 0, 1); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 0, 2); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 0, 3); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= route_except; + if ((sreq2 - EPS) > PitchX) needblock[i] |= ROUTEBLOCKX; + } + + if (i < Num_layers - 1) { + sreq2 = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 1, 0); + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 1, 1); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 1, 2); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i, i, 1, 3); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= route_except; + if ((sreq2 - EPS) > PitchY) needblock[i] |= ROUTEBLOCKY; + } + if (i != 0) { + sreq2 = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 1, 0); + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 1, 1); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 1, 2); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2t = sreq1 + 0.5 * LefGetXYViaWidth(i - 1, i, 1, 3); + if (sreq2t < sreq2) sreq2 = sreq2t; + sreq2 -= route_except; + if ((sreq2 - EPS) > PitchY) needblock[i] |= ROUTEBLOCKY; + } + } +} + +/*--------------------------------------------------------------*/ +/* remove_tap_blocks */ +/* */ +/* Qrouter avoids routing directly over a tap point, blocking */ +/* it, if there is a Nodeinfo[][]->nodeloc entry present. */ +/* Remove this entry to remove the blockage (it can be */ +/* replaced if needed by copying back the Nodeinfo[][]->nodesav */ +/* pointer) */ +/*--------------------------------------------------------------*/ + +void +remove_tap_blocks(int netnum) +{ + int i, j; + NODE node; + + for (i = 0; i < Pinlayers; i++) { + for (j = 0; j < NumChannelsX * NumChannelsY; j++) { + if (Nodeinfo[i][j]) { + node = Nodeinfo[i][j]->nodeloc; + if (node != (NODE)NULL) + if (node->netnum == netnum) + Nodeinfo[i][j]->nodeloc = (NODE)NULL; + } + } + } +} + +/*--------------------------------------------------------------*/ /* post_def_setup --- */ /* */ /* Things to do after a DEF file has been read in, and the size */ @@ -618,8 +775,8 @@ static void reinitialize() static int post_def_setup() { NET net; + ROUTE rt; int i; - double sreq1, sreq2; if (DEFfilename == NULL) { Fprintf(stderr, "No DEF file read, nothing to set up.\n"); @@ -647,14 +804,14 @@ static int post_def_setup() for (i = 0; i < Num_layers; i++) { - Obsinfo[i] = (float *)calloc(NumChannelsX[i] * NumChannelsY[i], + Obsinfo[i] = (float *)calloc(NumChannelsX * NumChannelsY, sizeof(float)); if (!Obsinfo[i]) { fprintf(stderr, "Out of memory 5.\n"); exit(5); } - Nodeinfo[i] = (NODEINFO *)calloc(NumChannelsX[i] * NumChannelsY[i], + Nodeinfo[i] = (NODEINFO *)calloc(NumChannelsX * NumChannelsY, sizeof(NODEINFO)); if (!Nodeinfo[i]) { fprintf( stderr, "Out of memory 6.\n"); @@ -665,12 +822,15 @@ static int post_def_setup() if (Verbose > 1) Fprintf(stderr, "Diagnostic: memory block is %d bytes\n", - (int)sizeof(u_int) * NumChannelsX[0] * NumChannelsY[0]); + (int)sizeof(u_int) * NumChannelsX * NumChannelsY); /* Be sure to create obstructions from gates first, since we don't */ /* want improperly defined or positioned obstruction layers to over- */ /* write our node list. */ +#ifdef TCL_QROUTER + find_free_antenna_taps(antenna_cell); +#endif expand_tap_geometry(); clip_gate_taps(); create_obstructions_from_gates(); @@ -683,10 +843,13 @@ static int post_def_setup() count_reachable_taps(); count_pinlayers(); - // If any nets are pre-routed, place those routes. + // If any nets are pre-routed, calculate route endpoints, and + // place those routes. for (i = 0; i < Numnets; i++) { net = Nlnets[i]; + for (rt = net->routes; rt; rt = rt->next) + route_set_connections(net, rt); writeback_all_routes(net); } @@ -696,7 +859,7 @@ static int post_def_setup() for (i = 0; i < Num_layers; i++) free(Obsinfo[i]); for (i = 0; i < Num_layers; i++) { - Obs2[i] = (PROUTE *)calloc(NumChannelsX[i] * NumChannelsY[i], + Obs2[i] = (PROUTE *)calloc(NumChannelsX * NumChannelsY, sizeof(PROUTE)); if (!Obs2[i]) { fprintf( stderr, "Out of memory 9.\n"); @@ -704,50 +867,13 @@ static int post_def_setup() } } - // Fill in needblock bit fields, which are used by commit_proute - // when route layers are too large for the grid size, and grid points - // around a route need to be marked as blocked whenever something is - // routed on those layers. - - // "ROUTEBLOCK" is set if the spacing is violated between a normal - // route and an adjacent via. "VIABLOCK" is set if the spacing is - // violated between two adjacent vias. It may be helpful to define - // a third category which is route-to-route spacing violation. - - for (i = 0; i < Num_layers; i++) { - needblock[i] = FALSE; - sreq1 = LefGetRouteSpacing(i); - - sreq2 = LefGetViaWidth(i, i, 0) + sreq1; - if ((sreq2 - EPS) > PitchX[i]) needblock[i] |= VIABLOCKX; - if (i != 0) { - sreq2 = LefGetViaWidth(i - 1, i, 0) + sreq1; - if ((sreq2 - EPS) > PitchX[i]) needblock[i] |= VIABLOCKX; - } - - sreq2 = LefGetViaWidth(i, i, 1) + sreq1; - if ((sreq2 - EPS) > PitchY[i]) needblock[i] |= VIABLOCKY; - if (i != 0) { - sreq2 = LefGetViaWidth(i - 1, i, 1) + sreq1; - if ((sreq2 - EPS) > PitchY[i]) needblock[i] |= VIABLOCKY; - } - - sreq1 += 0.5 * LefGetRouteWidth(i); + // Remove tap blocks from power, ground, and antenna nets, as these + // can take up large areas of the layout and will cause serious issues + // with routability if left blocked. - sreq2 = sreq1 + 0.5 * LefGetViaWidth(i, i, 0); - if ((sreq2 - EPS) > PitchX[i]) needblock[i] |= ROUTEBLOCKX; - if (i != 0) { - sreq2 = sreq1 + 0.5 * LefGetViaWidth(i - 1, i, 0); - if ((sreq2 - EPS) > PitchX[i]) needblock[i] |= ROUTEBLOCKX; - } - - sreq2 = sreq1 + 0.5 * LefGetViaWidth(i, i, 1); - if ((sreq2 - EPS) > PitchY[i]) needblock[i] |= ROUTEBLOCKY; - if (i != 0) { - sreq2 = sreq1 + 0.5 * LefGetViaWidth(i - 1, i, 1); - if ((sreq2 - EPS) > PitchY[i]) needblock[i] |= ROUTEBLOCKY; - } - } + remove_tap_blocks(VDD_NET); + remove_tap_blocks(GND_NET); + remove_tap_blocks(ANTENNA_NET); // Now we have netlist data, and can use it to get a list of nets. @@ -763,15 +889,18 @@ static int post_def_setup() /* read_def --- */ /* */ /* Read in the DEF file in DEFfilename */ +/* Return 0 on success, 1 on fatal error in DEF file. */ /*--------------------------------------------------------------*/ -void read_def(char *filename) +int read_def(char *filename) { - double oscale, precis; + float oscale; + double precis; + int result; if ((filename == NULL) && (DEFfilename == NULL)) { Fprintf(stderr, "No DEF file specified, nothing to read.\n"); - return; + return 1; } else if (filename != NULL) { if (DEFfilename != NULL) { @@ -782,8 +911,9 @@ void read_def(char *filename) } else reinitialize(); - oscale = (double)DefRead(DEFfilename); - precis = Scales.mscale / oscale; // from LEF manufacturing grid + oscale = (float)0.0; + result = DefRead(DEFfilename, &oscale); + precis = Scales.mscale / (double)oscale; // from LEF manufacturing grid if (precis < 1.0) precis = 1.0; precis *= (double)Scales.iscale; // user-defined extra scaling @@ -796,6 +926,7 @@ void read_def(char *filename) 1.0 / (double)Scales.iscale); post_def_setup(); + return result; } /*--------------------------------------------------------------*/ @@ -886,7 +1017,7 @@ NET getnettoroute(int order) // only one node. if ((net->numnodes == 1) && (net->netnum == VDD_NET || - net->netnum == GND_NET)) + net->netnum == GND_NET || net->netnum == ANTENNA_NET)) return net; if (Verbose > 3) { @@ -1289,6 +1420,8 @@ int dothirdstage(u_char graphdebug, int debug_netnum, u_int effort) if (Verbose > 0) Fprintf(stdout, "Failed to route net %s; restoring original\n", net->netname); + + ripup_net(net, TRUE, FALSE, TRUE); // Remove routes from Obs array remove_routes(net->routes, FALSE); /* should be NULL already */ net->routes = rt; writeback_all_routes(net); /* restore the original */ @@ -1579,18 +1712,7 @@ static int next_route_setup(struct routeinfo_ *iroute, u_char stage) // Remove nodes of the net from Nodeinfo.nodeloc so that they will not be // used for crossover costing of future routes. - - for (i = 0; i < Pinlayers; i++) { - for (j = 0; j < NumChannelsX[i] * NumChannelsY[i]; j++) { - if (Nodeinfo[i][j]) { - node = Nodeinfo[i][j]->nodeloc; - if (node != (NODE)NULL) - if (node->netnum == iroute->net->netnum) - Nodeinfo[i][j]->nodeloc = (NODE)NULL; - } - } - } - + remove_tap_blocks(iroute->net->netnum); free_glist(iroute); return 0; } @@ -1637,13 +1759,13 @@ static int route_setup(struct routeinfo_ *iroute, u_char stage) // terminal positions for the net being routed. for (i = 0; i < Num_layers; i++) { - for (j = 0; j < NumChannelsX[i] * NumChannelsY[i]; j++) { + for (j = 0; j < NumChannelsX * NumChannelsY; j++) { netnum = Obs[i][j] & (~BLOCKED_MASK); Pr = &Obs2[i][j]; if (netnum != 0) { Pr->flags = 0; // Clear all flags - if (netnum == DRC_BLOCKAGE) - Pr->prdata.net = netnum; + if ((netnum & DRC_BLOCKAGE) == DRC_BLOCKAGE) + Pr->prdata.net = DRC_BLOCKAGE; else Pr->prdata.net = netnum & NETNUM_MASK; } else { @@ -1653,7 +1775,8 @@ static int route_setup(struct routeinfo_ *iroute, u_char stage) } } - if (iroute->net->netnum == VDD_NET || iroute->net->netnum == GND_NET) { + if (iroute->net->netnum == VDD_NET || iroute->net->netnum == GND_NET || + iroute->net->netnum == ANTENNA_NET) { // The normal method of selecting source and target is not amenable // to power bus routes. Instead, we use the global standard cell // power rails as the target, and each net in sequence becomes the @@ -1682,8 +1805,8 @@ static int route_setup(struct routeinfo_ *iroute, u_char stage) if (result) { iroute->bbox.x2 = iroute->bbox.y2 = 0; - iroute->bbox.x1 = NumChannelsX[0]; - iroute->bbox.y1 = NumChannelsY[0]; + iroute->bbox.x1 = NumChannelsX; + iroute->bbox.y1 = NumChannelsY; if (iroute->do_pwrbus == FALSE) { @@ -1758,18 +1881,7 @@ static int route_setup(struct routeinfo_ *iroute, u_char stage) if (!result) { // Remove nodes of the net from Nodeinfo.nodeloc so that they will not be // used for crossover costing of future routes. - - for (i = 0; i < Pinlayers; i++) { - for (j = 0; j < NumChannelsX[i] * NumChannelsY[i]; j++) { - if (Nodeinfo[i][j]) { - iroute->nsrc = Nodeinfo[i][j]->nodeloc; - if (iroute->nsrc != (NODE)NULL) - if (iroute->nsrc->netnum == iroute->net->netnum) - Nodeinfo[i][j]->nodeloc = (NODE)NULL; - } - } - } - + remove_tap_blocks(iroute->net->netnum); free_glist(iroute); return 0; } @@ -1844,7 +1956,7 @@ static int route_setup(struct routeinfo_ *iroute, u_char stage) /* AUTHOR and DATE: steve beccue Fri Aug 8 */ /*--------------------------------------------------------------*/ -static int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug) +int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug) { POINT gpoint, gunproc, newpt; int i, o; @@ -1895,6 +2007,18 @@ static int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug iroute->glist[0] = gpoint->next; + // Stop-gap: Needs to be investigated. Occasional gpoint has + // large (random?) value for y1. Suggests a memory leak. Only + // seen occurring during doantennaroute(). Check using valgrind. + + if ((gpoint->x1 < 0) || (gpoint->y1 < 0) || + (gpoint->x1 > NumChannelsX) || + (gpoint->y1 > NumChannelsY)) { + Fprintf(stderr, "Internal memory error!\n"); + freePOINT(gpoint); + continue; + } + curpt.x = gpoint->x1; curpt.y = gpoint->y1; curpt.lay = gpoint->layer; @@ -2016,7 +2140,7 @@ static int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug predecessor = PR_CONFLICT; case EAST: predecessor |= PR_PRED_W; - if ((curpt.x + 1) < NumChannelsX[curpt.lay]) { + if ((curpt.x + 1) < NumChannelsX) { if ((gpoint = eval_pt(&curpt, predecessor, stage)) != NULL) { gpoint->next = iroute->glist[i]; iroute->glist[i] = gpoint; @@ -2052,7 +2176,7 @@ static int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug predecessor = PR_CONFLICT; case NORTH: predecessor |= PR_PRED_S; - if ((curpt.y + 1) < NumChannelsY[curpt.lay]) { + if ((curpt.y + 1) < NumChannelsY) { if ((gpoint = eval_pt(&curpt, predecessor, stage)) != NULL) { gpoint->next = iroute->glist[i]; iroute->glist[i] = gpoint; @@ -2162,7 +2286,7 @@ done: /* AUTHOR and DATE: steve beccue Fri Aug 8 */ /*--------------------------------------------------------------*/ -static ROUTE createemptyroute(void) +ROUTE createemptyroute(void) { ROUTE rt; @@ -7,7 +7,7 @@ #ifndef QROUTER_H -#define OGRID(x, y, layer) ((int)((x) + ((y) * NumChannelsX[(layer)]))) +#define OGRID(x, y) ((int)((x) + ((y) * NumChannelsX))) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define ABSDIFF(x, y) (((x) > (y)) ? ((x) - (y)) : ((y) - (x))) @@ -45,7 +45,11 @@ typedef int (*__compar_fn_t)(const void*, const void*); #endif /* Maximum number of route layers */ -#define MAX_LAYERS 9 +#define MAX_LAYERS 12 + +/* Maximum number of all defined layers. Since masterslice and */ +/* overlap types are ignored, this just includes all the cuts. */ +#define MAX_TYPES (MAX_LAYERS * 2 - 1) /* Cell name (and other names) max length */ #define MAX_NAME_LEN 1024 @@ -151,6 +155,7 @@ struct string_ { #define ST_OFFSET_START 0x04 /* (x1, y1) is offset from grid */ #define ST_OFFSET_END 0x08 /* (x2, y2) is offset from grid */ #define ST_SPECIAL 0x10 /* wide metal (special net) */ +#define ST_MINMETAL 0x20 /* segment for min metal area */ typedef struct seg_ *SEG; @@ -223,6 +228,7 @@ struct route_ { #define RT_END_NODE 0x08 // Route ends on a node #define RT_VISITED 0x10 // Flag for recursive search #define RT_RIP 0x20 // Flag for route rip-up +#define RT_CHECK 0x40 // Route from DEF file needs checking /* Structure used to hold nodes, saved nodes, and stub/offset info */ @@ -246,6 +252,8 @@ struct nodeinfo_ { #define NI_OFFSET_MASK 0x0c // Tap offset mask (N/S + E/W) #define NI_NO_VIAX 0x10 // Via in ViaX array is prohibited #define NI_NO_VIAY 0x20 // Via in ViaY array is prohibited +#define NI_VIA_X 0x40 // Placed via is oriented horizontally +#define NI_VIA_Y 0x80 // Placed via is oriented vertically struct node_ { NODE next; @@ -274,6 +282,7 @@ struct gate_ { char **node; // names of the pins on this gate int *netnum; // net number connected to each pin NODE *noderec; // node record for each pin + float *area; // gate area for each pin u_char *direction; // port direction (input, output, etc.) DSEG *taps; // list of gate node locations and layers DSEG obs; // list of obstructions in gate @@ -283,6 +292,16 @@ struct gate_ { int orient; }; +// Define record holding information pointing to a gate and the +// index into a specific node of that gate. + +typedef struct gatenode_ *GATENODE; + +struct gatenode_ { + GATE gate; + int idx; +}; + // Structure for a network to be routed typedef struct net_ *NET; @@ -428,13 +447,13 @@ extern float *Obsinfo[MAX_LAYERS]; // temporary detailed obstruction info extern NODEINFO *Nodeinfo[MAX_LAYERS]; // stub route distances to pins and // pointers to node structures. -#define NODEIPTR(x, y, l) (Nodeinfo[l][OGRID(x, y, l)]) -#define OBSINFO(x, y, l) (Obsinfo[l][OGRID(x, y, l)]) -#define OBSVAL(x, y, l) (Obs[l][OGRID(x, y, l)]) -#define OBS2VAL(x, y, l) (Obs2[l][OGRID(x, y, l)]) +#define NODEIPTR(x, y, l) (Nodeinfo[l][OGRID(x, y)]) +#define OBSINFO(x, y, l) (Obsinfo[l][OGRID(x, y)]) +#define OBSVAL(x, y, l) (Obs[l][OGRID(x, y)]) +#define OBS2VAL(x, y, l) (Obs2[l][OGRID(x, y)]) -#define RMASK(x, y) (RMask[OGRID(x, y, 0)]) -#define CONGEST(x, y) (Congestion[OGRID(x, y, 0)]) +#define RMASK(x, y) (RMask[OGRID(x, y)]) +#define CONGEST(x, y) (Congestion[OGRID(x, y)]) extern DSEG UserObs; // user-defined obstruction layers @@ -451,6 +470,7 @@ extern u_char ripLimit; extern char *vddnet; extern char *gndnet; +extern char *antenna_cell; /* Tcl output to console handling */ @@ -468,16 +488,19 @@ extern char *gndnet; static int next_route_setup(struct routeinfo_ *iroute, u_char stage); static int route_setup(struct routeinfo_ *iroute, u_char stage); -static int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug); -static ROUTE createemptyroute(void); +int route_segs(struct routeinfo_ *iroute, u_char stage, u_char graphdebug); +ROUTE createemptyroute(void); static void helpmessage(void); int set_num_channels(void); int allocate_obs_array(void); int countlist(NETLIST net); int runqrouter(int argc, char *argv[]); +void remove_failed(); +void apply_drc_blocks(int, double, double); +void remove_top_route(NET net); -void read_def(char *filename); +int read_def(char *filename); #ifdef TCL_QROUTER int write_delays(char *filename); diff --git a/qrouter.sh.in b/qrouter.sh.in index 12bab42..12bab42 100755..100644 --- a/qrouter.sh.in +++ b/qrouter.sh.in diff --git a/qrouter.tcl.in b/qrouter.tcl.in index 771fa27..5db9faa 100644 --- a/qrouter.tcl.in +++ b/qrouter.tcl.in @@ -159,6 +159,13 @@ proc qrouter::standard_route {{filename ""} {doquit true}} { } } } + cleanup all + if {$result == 0} { + if {![catch {antenna init}]} { + puts stdout "*** Running antenna violation checks" + antenna fix + } + } if {${rootname} != ""} { puts stdout "*** Writing DEF file $deffilename" write_def $deffilename @@ -166,7 +173,6 @@ proc qrouter::standard_route {{filename ""} {doquit true}} { puts stdout "*** Writing DEF file (default)" write_def } - if {$result > 0} { write_failed fail.out } @@ -220,6 +226,9 @@ proc qrouter::congestion_route {filename} { qrouter::write_congested $filename } write_def + if {$result > 0} { + write_failed fail.out + } quit } diff --git a/tclqrouter.c b/tclqrouter.c index 1fe8fd5..d06dcbb 100644 --- a/tclqrouter.c +++ b/tclqrouter.c @@ -21,6 +21,7 @@ #include "maze.h" #include "qconfig.h" #include "lef.h" +#include "def.h" #include "graphics.h" #include "node.h" #include "tkSimple.h" @@ -59,12 +60,21 @@ static int qrouter_stage2( static int qrouter_stage3( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int qrouter_cleanup( + ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int qrouter_writedef( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int qrouter_writefailed( + ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int qrouter_writedelays( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int qrouter_antenna( + ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int qrouter_readdef( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); @@ -110,6 +120,9 @@ static int qrouter_congested( static int qrouter_layers( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int qrouter_drc( + ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int qrouter_passes( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); @@ -136,11 +149,14 @@ static cmdstruct qrouter_commands[] = {"stage1", qrouter_stage1}, {"stage2", qrouter_stage2}, {"stage3", qrouter_stage3}, + {"cleanup", qrouter_cleanup}, {"write_def", qrouter_writedef}, {"read_def", qrouter_readdef}, {"read_lef", qrouter_readlef}, {"read_config", qrouter_readconfig}, {"write_delays", qrouter_writedelays}, + {"antenna", qrouter_antenna}, + {"write_failed", qrouter_writefailed}, {"layer_info", qrouter_layerinfo}, {"obstruction", qrouter_obs}, {"ignore", qrouter_ignore}, @@ -149,6 +165,7 @@ static cmdstruct qrouter_commands[] = {"resolution", qrouter_resolution}, {"congested", qrouter_congested}, {"layers", qrouter_layers}, + {"drc", qrouter_drc}, {"passes", qrouter_passes}, {"vdd", qrouter_vdd}, {"gnd", qrouter_gnd}, @@ -626,18 +643,30 @@ qrouter_start(ClientData clientData, Tcl_Interp *interp, fclose(scriptf); result = Tcl_EvalFile(interp, scriptfile); } - free(scriptfile); /* The script file should determine whether or not to */ /* exit by including the "quit" command. But if there */ /* is an error in the script, then always quit. */ + /* If tkcon console is in use and there is an error in */ + /* the script, then print the error message to the */ + /* terminal, not the console, or else it vanishes. */ + if (result != TCL_OK) { + if (consoleinterp == interp) + Fprintf(stderr, "Script file \"%s\" failed with result \'%s\'\n", + scriptfile, Tcl_GetStringResult(interp)); + else + fprintf(stderr, "Script file \"%s\" failed with result \'%s\'\n", + scriptfile, Tcl_GetStringResult(interp)); + free(scriptfile); /* Make sure Tcl has generated all output */ while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0); /* And exit gracefully */ qrouter_quit(clientData, interp, 1, objv); } + else + free(scriptfile); } if ((DEFfilename != NULL) && (Nlgates == NULL)) { @@ -664,6 +693,11 @@ qrouter_quit(ClientData clientData, Tcl_Interp *interp, return TCL_ERROR; } + /* Free up failed net list */ + remove_failed(); + + /* Should be doing other cleanup tasks here. . . */ + if (consoleinterp == interp) Tcl_Exit(TCL_OK); else @@ -758,28 +792,12 @@ qrouter_map(ClientData clientData, Tcl_Interp *interp, } /*------------------------------------------------------*/ -/* Find the net named "netname" in the list of nets */ -/* and return a pointer to it. */ -/* */ -/* NOTE: Really need a hash table lookup here! */ -/*------------------------------------------------------*/ - -NET LookupNet(char *netname) -{ - NET net; - int i; - - for (i = 0; i < Numnets; i++) { - net = Nlnets[i]; - if (!strcmp(net->netname, netname)) - return net; - } - return NULL; -} - -/*------------------------------------------------------*/ /* Find the net with number "number" in the list of */ /* nets and return a pointer to it. */ +/* */ +/* NOTE: This could be hashed like net names, but is */ +/* only used in one place, and router performance does */ +/* not depend on it. */ /*------------------------------------------------------*/ NET LookupNetNr(int number) @@ -796,22 +814,6 @@ NET LookupNetNr(int number) } /*------------------------------------------------------*/ -/* Find the gate instance named gatename and return a */ -/* pointer to it. */ -/*------------------------------------------------------*/ - -GATE LookupGate(char *gatename) -{ - GATE gate; - - for (gate = Nlgates; gate != NULL; gate = gate->next) { - if (!strcmp(gate->gatename, gatename)) - return gate; - } - return NULL; -} - -/*------------------------------------------------------*/ /* Command "stage1" */ /* */ /* Execute stage1 routing. This works through the */ @@ -899,7 +901,7 @@ qrouter_stage1(ClientData clientData, Tcl_Interp *interp, return TCL_ERROR; } i++; - net = LookupNet(Tcl_GetString(objv[i])); + net = DefFindNet(Tcl_GetString(objv[i])); if (net == NULL) { Tcl_SetResult(interp, "No such net", NULL); return TCL_ERROR; @@ -1107,7 +1109,7 @@ qrouter_stage2(ClientData clientData, Tcl_Interp *interp, return TCL_ERROR; } i++; - net = LookupNet(Tcl_GetString(objv[i])); + net = DefFindNet(Tcl_GetString(objv[i])); if (net == NULL) { Tcl_SetResult(interp, "No such net", NULL); return TCL_ERROR; @@ -1276,7 +1278,7 @@ qrouter_stage3(ClientData clientData, Tcl_Interp *interp, return TCL_ERROR; } i++; - net = LookupNet(Tcl_GetString(objv[i])); + net = DefFindNet(Tcl_GetString(objv[i])); if (net == NULL) { Tcl_SetResult(interp, "No such net", NULL); return TCL_ERROR; @@ -1363,6 +1365,74 @@ qrouter_stage3(ClientData clientData, Tcl_Interp *interp, } /*------------------------------------------------------*/ +/* Command "cleanup" */ +/* */ +/* Clean up the nets by removing adjacent vias where */ +/* such adjacent vias would cause a DRC violation. */ +/* Note that this must be done between the last */ +/* routing stage but before finding antenna */ +/* violations, output, and delay writing, as all of */ +/* these can be dependent on topology changes caused */ +/* by the cleanup. */ +/* */ +/* Options: */ +/* */ +/* cleanup all Clean up all nets in the design */ +/* cleanup net <name> [...] */ +/* Clean up the named net(s) */ +/*------------------------------------------------------*/ + +static int +qrouter_cleanup(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]) +{ + int result, idx, i; + NET net; + + static char *subCmds[] = { + "all", "net", NULL + }; + enum SubIdx { + AllIdx, NetIdx + }; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 0, objv, "?option?"); + return TCL_ERROR; + } + else { + if ((result = Tcl_GetIndexFromObj(interp, objv[1], + (CONST84 char **)subCmds, "option", 0, &idx)) + != TCL_OK) + return result; + + // Quick check to see if cleanup_net can be avoided completely. + for (i = 0; i < Num_layers; i++) + if (needblock[i] & (VIABLOCKX | VIABLOCKY)) + break; + if (i == Num_layers) return TCL_OK; /* No cleanup needed */ + + switch (idx) { + case AllIdx: + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + cleanup_net(net); + } + break; + + case NetIdx: + for (i = 2; i < objc; i++) { + net = DefFindNet(Tcl_GetString(objv[i])); + if (net != NULL) + cleanup_net(net); + } + break; + } + } + return QrouterTagCallback(interp, objc, objv); +} + +/*------------------------------------------------------*/ /* Command "remove" */ /* */ /* Remove a net or nets, or all nets, from the */ @@ -1410,7 +1480,7 @@ qrouter_remove(ClientData clientData, Tcl_Interp *interp, break; case NetIdx: for (i = 2; i < objc; i++) { - net = LookupNet(Tcl_GetString(objv[i])); + net = DefFindNet(Tcl_GetString(objv[i])); if (net != NULL) ripup_net(net, (u_char)1, (u_char)1, (u_char)0); } @@ -1536,13 +1606,18 @@ qrouter_readlef(ClientData clientData, Tcl_Interp *interp, for (i = 0; i < Num_layers; i++) { - /* Set Vert and PitchX from route info */ + /* Set Vert from route info since this gets called a lot */ + /* (e.g., from eval_pt() and is more convenient to pull */ + /* from an array than calling a subroutine every time. */ Vert[i] = (1 - LefGetRouteOrientation(i)); - PitchX[i] = LefGetRoutePitch(i); } - post_config(); + /* Resolve the base horizontal and vertical pitches */ + post_config(FALSE); + + /* Set DRC blockage behavior based on via and route widths */ + apply_drc_blocks(-1, 0.0, 0.0); return QrouterTagCallback(interp, objc, objv); } @@ -1555,14 +1630,36 @@ static int qrouter_readdef(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *argv; + u_char abort_on_error = FALSE; + int result; + + /* Parse out options */ + + while (objc > 0) { + argv = Tcl_GetString(objv[objc - 1]); + if (*argv == '-') { + if (!strncmp(argv + 1, "abort", 5)) + abort_on_error = TRUE; + objc--; + } + else break; + } + if ((DEFfilename == NULL) && (objc != 2)) { Tcl_SetResult(interp, "No DEF filename specified!", NULL); return TCL_ERROR; } + if (objc == 2) - read_def(Tcl_GetString(objv[1])); + result = read_def(Tcl_GetString(objv[1])); else - read_def(NULL); + result = read_def(NULL); + + if ((result != (u_char)0) && (abort_on_error == TRUE)) { + Tcl_SetResult(interp, "Errors in input DEF file; aborting.", NULL); + return TCL_ERROR; + } // Redisplay draw_layout(); @@ -1586,12 +1683,95 @@ qrouter_writedef(ClientData clientData, Tcl_Interp *interp, Tcl_SetResult(interp, "No DEF filename specified!", NULL); return TCL_ERROR; } + else DEFoutfile = DEFfilename; write_def(DEFoutfile); return QrouterTagCallback(interp, objc, objv); } /*------------------------------------------------------*/ +/* Command "antenna" */ +/* Use: */ +/* antenna init <cellname> */ +/* antenna check */ +/* antenna fix */ +/* */ +/* Calculate and handle antenna violations. Option */ +/* "init" declares the cellname that is an antenna */ +/* anchoring cell. This must be declared before */ +/* routing. "antenna check" can be called at any time */ +/* and reports the number of antenna violations at each */ +/* metal layer. "antenna fix" attempts to fix all */ +/* antenna violations by anchoring each antenna to an */ +/* available antenna cell tap. */ +/*------------------------------------------------------*/ + +static int +qrouter_antenna(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]) +{ + char *option; + u_char do_fix = (u_char)0; + + if (objc >= 2) { + option = Tcl_GetString(objv[1]); + if (objc == 3) antenna_cell = strdup(Tcl_GetString(objv[2])); + + if (!strcmp(option, "init")) { + if (objc != 3) { + if (antenna_cell != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(antenna_cell, -1)); + } + else { + Tcl_SetResult(interp, "No antenna cell name specified.", NULL); + return TCL_ERROR; + } + } + } + else if (!strcmp(option, "check")) { + resolve_antenna(antenna_cell, (u_char)0); + } + else if (!strcmp(option, "fix")) { + resolve_antenna(antenna_cell, (u_char)1); + } + else { + antenna_cell = Tcl_GetString(objv[1]); + } + } + else { + Tcl_SetResult(interp, "Usage: antenna init|check|fix [cellname]", NULL); + return TCL_ERROR; + } + + if (antenna_cell == NULL) { + Tcl_SetResult(interp, "No antenna cell specified!", NULL); + return TCL_ERROR; + } + return QrouterTagCallback(interp, objc, objv); +} + +/*------------------------------------------------------*/ +/* Command "write_failed" */ +/*------------------------------------------------------*/ + +static int +qrouter_writefailed(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]) +{ + char *outfile = NULL; + + if (objc == 2) + outfile = Tcl_GetString(objv[1]); + else if (outfile == NULL) { + Tcl_SetResult(interp, "No output filename specified!", NULL); + return TCL_ERROR; + } + + write_failed(outfile); + return QrouterTagCallback(interp, objc, objv); +} + +/*------------------------------------------------------*/ /* Command "write_delays" */ /*------------------------------------------------------*/ @@ -1607,6 +1787,7 @@ qrouter_writedelays(ClientData clientData, Tcl_Interp *interp, Tcl_SetResult(interp, "No delay filename specified!", NULL); return TCL_ERROR; } + else delayoutfile = delayfilename; write_delays(delayoutfile); return QrouterTagCallback(interp, objc, objv); @@ -1749,7 +1930,7 @@ qrouter_ignore(ClientData clientData, Tcl_Interp *interp, } else { for (i = 1; i < objc; i++) { - net = LookupNet(Tcl_GetString(objv[i])); + net = DefFindNet(Tcl_GetString(objv[i])); if (net == NULL) { Tcl_SetResult(interp, "No such net", NULL); return TCL_ERROR; @@ -1776,7 +1957,7 @@ static int qrouter_priority(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int i; + int i, j; char *netname; NET net; STRING cn, ctest; @@ -1793,15 +1974,40 @@ qrouter_priority(ClientData clientData, Tcl_Interp *interp, } Tcl_SetObjResult(interp, lobj); } + else if (Nlnets == NULL) { + Tcl_SetResult(interp, "Must read nets from DEF file before setting priority.", + NULL); + return TCL_ERROR; + } else { - for (i = objc - 1; i > 0; i--) { + + /* Find the highest-numbered existing critical net so that */ + /* repeated calls to "priority" will work. */ + + j = -1; + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + if (net->flags & NET_CRITICAL) + if (net->netorder > j) + j = net->netorder; + } + j++; + + for (i = 1; i < objc; i++) { netname = Tcl_GetString(objv[i]); - net = LookupNet(netname); + net = DefFindNet(netname); if (net == NULL) { Tcl_SetResult(interp, "No such net", NULL); } - else { + else if (!(net->flags & NET_CRITICAL)) { net->flags |= NET_CRITICAL; + net->netorder = j++; + + /* NOTE: CriticalNet is left over from the old config */ + /* file format. Normally it will remain NULL. If the */ + /* old config file format is used, then remove items */ + /* from it that match nets in the Tcl priority command. */ + for (cn = CriticalNet; cn && cn->next; cn = cn->next) { if (!cn->next) break; if (!strcmp(cn->next->name, netname)) { @@ -1994,11 +2200,7 @@ qrouter_layerinfo(ClientData clientData, Tcl_Interp *interp, /* a single point. Value "none", "0", and "1" */ /* all mean the same thing. */ /* */ -/* via_pattern: If vias are non-square, then they are */ -/* placed in a checkerboard pattern, with every */ -/* other via rotated 90 degrees. If inverted, */ -/* the rotation is swapped relative to the grid */ -/* positions used in the non-inverted case. */ +/* via_pattern: (deprecated) */ /* */ /* use: List of names of vias to use. If any via not */ /* in this list is found when reading a .lef file */ @@ -2054,8 +2256,7 @@ qrouter_via(ClientData clientData, Tcl_Interp *interp, break; case PatternIdx: Tcl_SetObjResult(interp, - Tcl_NewStringObj( - patternSubCmds[ViaPattern + 1], -1)); + Tcl_NewStringObj("deprecated", -1)); break; case UseIdx: /* Return list of vias to use */ @@ -2097,7 +2298,6 @@ qrouter_via(ClientData clientData, Tcl_Interp *interp, (CONST84 char **)patternSubCmds, "option", 0, &idx2)) != TCL_OK) return result; - ViaPattern = idx2 - 1; break; case UseIdx: /* Create list of vias to use */ @@ -2114,7 +2314,7 @@ qrouter_via(ClientData clientData, Tcl_Interp *interp, newVia->next = AllowedVias; AllowedVias = newVia; } - /* Regenerate the ViaX and ViaY lists */ + /* Regenerate the ViaXX, etc., lists */ LefAssignLayerVias(); break; } @@ -2208,6 +2408,82 @@ qrouter_resolution(ClientData clientData, Tcl_Interp *interp, return QrouterTagCallback(interp, objc, objv); } +/*------------------------------------------------------*/ +/* Command "drc" */ +/* */ +/* Set qrouter options related to handling of DRC */ +/* violations. */ +/* */ +/* Options: */ +/* */ +/* drc <layer>|all <dist> <dist> */ +/* */ +/* Allow exceptions to DRC handling. Normally qrouter */ +/* enforces DRC distance between a via and route or */ +/* between two vias on adjacent tracks, forcing a */ +/* keep-out area around a placed route if needed. */ +/* "layer" is the name of a route layer. <dist> (value */ +/* in microns) will limit the enforcement if the DRC */ +/* violation is less than the indicated distance. */ +/* The first value is for via-to-via distance, and the */ +/* second value is for route-to-via distance. A value */ +/* of zero means that the behavior is unchanged from */ +/* what is automatically calculated from defined */ +/* route and via width and spacing values. A positive */ +/* distance value loosens the DRC rule, while a */ +/* negative distance value tightens it. */ +/* */ +/* Ignoring DRC errors is generally discouraged but */ +/* may be necessary in some cases if the pitch is tight */ +/* and assumes routes may be offset to clear vias, */ +/* which is something qrouter does not know how to do. */ +/* Only use this if routability is being impacted by */ +/* DRC enforcement. */ +/*------------------------------------------------------*/ + +static int +qrouter_drc(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]) +{ + char *layername; + int result, layer; + double routedist, viadist; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); + return TCL_ERROR; + } + + layername = Tcl_GetString(objv[1]); + if (!strcasecmp(layername, "all")) { + layer = -1; + } + else { + layer = LefFindLayerNum(layername); + if (layer < 0) { + result = Tcl_GetIntFromObj(interp, objv[1], &layer); + if (result != TCL_OK) { + Tcl_SetResult(interp, "No such layer name.\n", NULL); + return result; + } + } + + if ((layer < -1) || (layer >= LefGetMaxRouteLayer())) { + Tcl_SetResult(interp, "Layer number out of range.\n", NULL); + return TCL_ERROR; + } + } + + result = Tcl_GetDoubleFromObj(interp, objv[2], &viadist); + if (result != TCL_OK) return result; + + result = Tcl_GetDoubleFromObj(interp, objv[3], &routedist); + if (result != TCL_OK) return result; + + apply_drc_blocks(layer, viadist, routedist); + + return QrouterTagCallback(interp, objc, objv); +} /*------------------------------------------------------*/ /* Command "layers" */ @@ -2235,10 +2511,10 @@ qrouter_layers(ClientData clientData, Tcl_Interp *interp, else if (objc == 2) { result = Tcl_GetIntFromObj(interp, objv[1], &value); if (result != TCL_OK) return result; - if (value <= 0 || value > LefGetMaxLayer()) { + if (value <= 0 || value > LefGetMaxRouteLayer()) { Tcl_SetResult(interp, "Number of layers out of range," " setting to max.", NULL); - Num_layers = LefGetMaxLayer(); + Num_layers = LefGetMaxRouteLayer(); return TCL_ERROR; } Num_layers = value; @@ -2543,7 +2819,7 @@ qrouter_congested(ClientData clientData, Tcl_Interp *interp, else entries = 0; - Congestion = (float *)calloc(NumChannelsX[0] * NumChannelsY[0], + Congestion = (float *)calloc(NumChannelsX * NumChannelsY, sizeof(float)); // Use net bounding boxes to estimate congestion @@ -2563,8 +2839,8 @@ qrouter_congested(ClientData clientData, Tcl_Interp *interp, for (x = net->xmin; x < net->xmax; x++) for (y = net->ymin; y < net->ymax; y++) - if (x >= 0 && x < NumChannelsX[0] && - y >= 0 && y < NumChannelsY[0]) + if (x >= 0 && x < NumChannelsX && + y >= 0 && y < NumChannelsY) CONGEST(x, y) += density; } @@ -2589,12 +2865,12 @@ qrouter_congested(ClientData clientData, Tcl_Interp *interp, cgates[i] = (CLIST)malloc(sizeof(Clist)); dx = gsrch->placedX; dy = gsrch->placedY; - bbox.x1 = (int)((dx - Xlowerbound) / PitchX[0]) - 1; - bbox.y1 = (int)((dy - Ylowerbound) / PitchY[0]) - 1; + bbox.x1 = (int)((dx - Xlowerbound) / PitchX) - 1; + bbox.y1 = (int)((dy - Ylowerbound) / PitchY) - 1; dx = gsrch->placedX + gsrch->width; dy = gsrch->placedY + gsrch->height; - bbox.x2 = (int)((dx - Xlowerbound) / PitchX[0]) - 1; - bbox.y2 = (int)((dy - Ylowerbound) / PitchY[0]) - 1; + bbox.x2 = (int)((dx - Xlowerbound) / PitchX) - 1; + bbox.y2 = (int)((dy - Ylowerbound) / PitchY) - 1; cavg = 0.0; for (x = bbox.x1; x <= bbox.x2; x++) { @@ -2680,7 +2956,7 @@ qrouter_print(ClientData clientData, Tcl_Interp *interp, switch (idx) { case NetIdx: - net = LookupNet(Tcl_GetString(objv[2])); + net = DefFindNet(Tcl_GetString(objv[2])); if (net == NULL) { Tcl_SetResult(interp, "Net not found", NULL); return TCL_ERROR; @@ -2700,7 +2976,7 @@ qrouter_print(ClientData clientData, Tcl_Interp *interp, break; case GateIdx: - gate = LookupGate(Tcl_GetString(objv[2])); + gate = DefFindGate(Tcl_GetString(objv[2])); if (gate == NULL) { Tcl_SetResult(interp, "Gate not found", NULL); return TCL_ERROR; |