diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2017-07-13 22:06:10 +0200 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2017-07-13 22:09:21 +0200 |
commit | d7b158d45004c98cb3b3a57d2f5be004a3e25117 (patch) | |
tree | 167aa969d29d2b845702d7ffaa42eb48621b512f /output.c | |
parent | 3f0c28cfbd1bbff82ab5ebed488c3954b7378e64 (diff) |
Imported 1.3.80
Diffstat (limited to 'output.c')
-rw-r--r-- | output.c | 2039 |
1 files changed, 2039 insertions, 0 deletions
diff --git a/output.c b/output.c new file mode 100644 index 0000000..61e9520 --- /dev/null +++ b/output.c @@ -0,0 +1,2039 @@ +/*--------------------------------------------------------------*/ +/* output.c -- qrouter general purpose autorouter */ +/* output routines for writing DEF file */ +/*--------------------------------------------------------------*/ +/* Written by Tim Edwards, June 2011, based on code by Steve */ +/* Beccue, 2003 */ +/*--------------------------------------------------------------*/ + +#include <ctype.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef TCL_QROUTER +#include <tk.h> +#endif + +#include "qrouter.h" +#include "qconfig.h" +#include "point.h" +#include "node.h" +#include "maze.h" +#include "mask.h" +#include "output.h" +#include "lef.h" +#include "def.h" +#include "graphics.h" + +int Pathon = -1; + +/*--------------------------------------------------------------*/ +/* Write the output annotated DEF file. */ +/*--------------------------------------------------------------*/ + +int write_def(char *filename) +{ + NET net; + NETLIST nl; + + emit_routes((filename == NULL) ? DEFfilename : filename, + Scales.oscale, Scales.iscale); + + Fprintf(stdout, "----------------------------------------------\n"); + Fprintf(stdout, "Final: "); + if (FailedNets == (NETLIST)NULL) + Fprintf(stdout, "No failed routes!\n"); + else { + if (FailedNets != (NETLIST)NULL) { + 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 + + while (FailedNets) { + net = FailedNets->net; + Fprintf(stdout, " %s\n", net->netname); + nl = FailedNets->next; + free(FailedNets); + FailedNets = nl; + } + Fprintf(stdout, "\n"); + } + } + Fprintf(stdout, "----------------------------------------------\n"); + + return 0; + +} /* write_def() */ + +/*--------------------------------------------------------------*/ +/* pathstart - begin a DEF format route path */ +/* */ +/* If "special" is true, then this path is in a */ +/* SPECIALNETS section, in which each route specifies */ +/* a width. */ +/*--------------------------------------------------------------*/ + +static void +pathstart(FILE *cmd, int layer, int x, int y, u_char special, double oscale, + double invscale, u_char horizontal) +{ + if (Pathon == 1) { + Fprintf( stderr, "pathstart(): Major error. Started a new " + "path while one is in progress!\n" + "Doing it anyway.\n" ); + } + + if (layer >= 0) { + if (Pathon == -1) + fprintf(cmd, "+ ROUTED "); + else + fprintf(cmd, "\n NEW "); + if (special) { + double wvia; + + wvia = LefGetViaWidth(layer, layer, horizontal); + if (layer > 0) { + double wvia2; + wvia2 = LefGetViaWidth(layer - 1, layer, horizontal); + if (wvia2 > wvia) wvia = wvia2; + } + + fprintf(cmd, "%s %g ( %g %g ) ", CIFLayer[layer], + invscale * (int)(oscale * wvia + 0.5), + invscale * x, invscale * y); + } + else + fprintf(cmd, "%s ( %g %g ) ", CIFLayer[layer], invscale * x, invscale * y); + } + Pathon = 1; + +} /* pathstart() */ + +/*--------------------------------------------------------------*/ +/* pathto - continue a path to the next point */ +/* */ +/* ARGS: coordinate pair */ +/* RETURNS: */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +static void +pathto(FILE *cmd, int x, int y, int horizontal, int lastx, int lasty, + double invscale) +{ + if (Pathon <= 0) { + Fprintf(stderr, "pathto(): Major error. Added to a " + "non-existent path!\n" + "Doing it anyway.\n"); + } + + /* If the route is not manhattan, then it's because an offset + * was added to the last point, and we need to add a small + * jog to the route. + */ + + if ((x != lastx) && (y != lasty)) { + if (horizontal) + pathto(cmd, lastx, y, FALSE, lastx, lasty, invscale); + else + pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale); + } + + fprintf(cmd, "( "); + if (horizontal) + fprintf(cmd, "%g ", invscale * x); + else + fprintf(cmd, "* "); + + if (horizontal) + fprintf(cmd, "* "); + else + fprintf(cmd, "%g ", invscale * y); + + fprintf(cmd, ") "); + +} /* pathto() */ + +/*--------------------------------------------------------------*/ +/* pathvia - add a via to a path */ +/* */ +/* ARGS: coord */ +/* RETURNS: */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +static void +pathvia(FILE *cmd, int layer, int x, int y, int lastx, int lasty, + int gridx, int gridy, double invscale) +{ + char *s; + char checkersign = (gridx + gridy + layer) & 0x01; + + 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 (Pathon <= 0) { + if (Pathon == -1) + fprintf(cmd, "+ ROUTED "); + else + fprintf(cmd, "\n NEW "); + fprintf(cmd, "%s ( %g %g ) ", CIFLayer[layer], invscale * x, invscale * y); + } + else { + // Normally the path will be manhattan and only one of + // these will be true. But if the via gets an offset to + // avoid a DRC spacing violation with an adjacent via, + // then we may need to apply both paths to make a dog-leg + // route to the via. + + if (x != lastx) + pathto(cmd, x, lasty, TRUE, lastx, lasty, invscale); + if (y != lasty) + pathto(cmd, x, y, FALSE, x, lasty, invscale); + } + fprintf(cmd, "%s ", s); + Pathon = 0; + +} /* pathvia() */ + +/*--------------------------------------------------------------*/ +/* Nodes aren't saved in a way that makes it easy to recall */ +/* the name of the cell and pin to which they belong. But */ +/* that information doesn't need to be looked up except as a */ +/* diagnostic output. This routine does that lookup. */ +/*--------------------------------------------------------------*/ + +char *print_node_name(NODE node) +{ + GATE g; + int i; + static char *nodestr = NULL; + + for (g = Nlgates; g; g = g->next) { + for (i = 0; i < g->nodes; i++) { + if (g->noderec[i] == node) { + if (nodestr != NULL) + free(nodestr); + + nodestr = (char *)malloc(strlen(g->gatename) + + strlen(g->node[i]) + 2); + if (!strcmp(g->node[i], "pin")) + sprintf(nodestr, "PIN/%s", g->gatename); + else + sprintf(nodestr, "%s/%s", g->gatename, g->node[i]); + return nodestr; + } + } + } + if (nodestr != NULL) free(nodestr); + nodestr = (char *)malloc(22); + sprintf(nodestr, "(error: no such node)"); + return nodestr; +} + +/*--------------------------------------------------------------*/ +/* print_nets - print the nets list - created from Nlgates list */ +/* */ +/* ARGS: filename to list to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/* AUTHOR and DATE: steve beccue Sat July 26 */ +/*--------------------------------------------------------------*/ + +void print_nets(char *filename) +{ + FILE *o; + GATE g; + int i; + DSEG drect; + + if (!strcmp(filename, "stdout")) { + o = stdout; + } else { + o = fopen(filename, "w"); + } + if (!o) { + Fprintf(stderr, "route:print_nets. Couldn't open output file\n"); + return; + } + + for (g = Nlgates; g; g = g->next) { + fprintf(o, "%s: %s: nodes->", g->gatename, g->gatetype->gatename); + for (i = 0; i < g->nodes; i++) { + // This prints the first tap position only. + drect = g->taps[i]; + fprintf( o, "%s(%g,%g) ", g->node[i], drect->x1, drect->y1); + } + } + fprintf( o, "\n"); +} /* print_nets() */ + +/*--------------------------------------------------------------*/ +/* print_routes - print the routes list */ +/* */ +/* ARGS: filename to list to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/* AUTHOR and DATE: steve beccue Sat July 26 */ +/*--------------------------------------------------------------*/ + +void print_routes( char *filename ) +{ + FILE *o; + GATE g; + int i; + + if( !strcmp( filename, "stdout" ) ) { + o = stdout; + } else { + o = fopen( filename, "w" ); + } + if( !o ) { + Fprintf( stderr, "route:print_routes. Couldn't open output file\n" ); + return; + } + + for (g = Nlgates; g; g = g->next) { + fprintf( o, "%s: %s: nodes->", g->gatename, g->gatetype->gatename ); + for( i = 0 ; i < g->nodes; i++ ) { + fprintf( o, "%s ", g->node[i] ); + } + fprintf(o, "\n"); + } +} /* print_routes() */ + +/*--------------------------------------------------------------*/ +/* print_nlgates - print the nlgate list */ +/* */ +/* ARGS: filename to list to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/* AUTHOR and DATE: steve beccue Wed July 23 */ +/*--------------------------------------------------------------*/ + +void print_nlgates( char *filename ) +{ + FILE *o; + GATE g; + int i; + DSEG drect; + + if( !strcmp( filename, "stdout" ) ) { + o = stdout; + } else { + o = fopen( filename, "w" ); + } + if( !o ) { + Fprintf( stderr, "route:print_nlgates. Couldn't open output file\n" ); + return; + } + + for (g = Nlgates; g; g = g->next) { + fprintf( o, "%s: %s: nodes->", g->gatename, g->gatetype->gatename ); + for( i = 0 ; i < g->nodes; i++ ) { + // This prints the first tap position only. + drect = g->taps[i]; + fprintf( o, "%s(%g,%g)", g->node[i], drect->x1, drect->y1); + } + fprintf(o, "\n"); + } +} /* print_nlgates() */ + + +/*--------------------------------------------------------------*/ +/* print_net - print info about the net to stdout */ +/* */ +/* ARGS: net to print info about */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void print_net(NET net) { + NODE node; + DPOINT tap; + int i, first; + + Fprintf(stdout, "Net %d: %s", net->netnum, net->netname); + for (node = net->netnodes; node != NULL; node = node->next) { + Fprintf(stdout, "\n Node %d: \n Taps: ", node->nodenum); + for (tap = node->taps, i = 0, first = TRUE; + tap != NULL; + tap = tap->next, i = (i + 1) % 4, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)", + (i == 0 ? (first ? "" : "\n ") : " "), + tap->layer, tap->x, tap->y + ); + } + Fprintf(stdout, "\n Tap extends: "); + for (tap = node->extend, i = 0, first = TRUE; + tap != NULL; + tap = tap->next, i = (i + 1) % 4, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)", + (i == 0 ? (first ? "" : "\n ") : " "), + tap->layer, tap->x, tap->y + ); + } + } + Fprintf(stdout, "\n bbox: (%d,%d)-(%d,%d)\n", + net->xmin, net->ymin, net->xmax, net->ymax + ); +} + + +/*--------------------------------------------------------------*/ +/* print_gate - print info about the net to stdout */ +/* */ +/* ARGS: gate to print info about */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void print_gate(GATE gate) { + int i, j, first; + DSEG seg; + NODE node; + DPOINT tap; + + Fprintf(stdout, "Gate %s\n", gate->gatename); + Fprintf(stdout, " Loc: (%.2lf, %.2lf), WxH: %.2lfx%.2lf\n", + gate->placedX, gate->placedY, gate->width, gate->height + ); + Fprintf(stdout, " Pins"); + for (i = 0; i < gate->nodes; i++) { + Fprintf(stdout, "\n Pin %s, net %d\n", + gate->node[i], gate->netnum[i] + ); + Fprintf(stdout, " Segs: "); + for (seg = gate->taps[i], j = 0, first = TRUE; + seg != NULL; + seg = seg->next, j = (j + 1) % 3, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)-(%.2lf,%.2lf)", + (j == 0 ? (first ? "" : "\n ") : " "), + seg->layer, seg->x1, seg->y1, seg->x2, seg->y2 + ); + } + if ((node = gate->noderec[i]) != NULL) { + Fprintf(stdout, "\n Taps: "); + for (tap = node->taps, j = 0, first = TRUE; + tap != NULL; + tap = tap->next, j = (j + 1) % 4, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)", + (j == 0 ? (first ? "" : "\n ") : " "), + tap->layer, tap->x, tap->y + ); + } + Fprintf(stdout, "\n Tap extends: "); + for (tap = node->extend, j = 0, first = TRUE; + tap != NULL; + tap = tap->next, j = (j + 1) % 4, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)", + (j == 0 ? (first ? "" : "\n ") : " "), + tap->layer, tap->x, tap->y + ); + } + } + } + Fprintf(stdout, "\n Obstructions: "); + for (seg = gate->obs, j = 0, first = TRUE; + seg != NULL; + seg = seg->next, j = (j + 1) % 3, first = FALSE) { + Fprintf(stdout, "%sL%d:(%.2lf,%.2lf)-(%.2lf,%.2lf)", + (j == 0 ? (first ? "" : "\n ") : " "), + seg->layer, seg->x1, seg->y1, seg->x2, seg->y2 + ); + } + Fprintf(stdout, "\n"); +} + +/*--------------------------------------------------------------*/ +/* print_nodes - show the nodes list */ +/* */ +/* ARGS: filename to print to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: none */ +/* AUTHOR and DATE: steve beccue Tue Aug 04 2003 */ +/*--------------------------------------------------------------*/ + +void print_nodes(char *filename) +{ + FILE *o; + int i; + NET net; + NODE node; + DPOINT dp; + + if (!strcmp(filename, "stdout")) { + o = stdout; + } else { + o = fopen(filename, "w"); + } + if (!o) { + Fprintf( stderr, "node.c:print_nodes. Couldn't open output file\n" ); + return; + } + + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + for (node = net->netnodes; node; node = node->next) { + dp = (DPOINT)node->taps; + fprintf(o, "%d\t%s\t(%g,%g)(%d,%d) :%d:num=%d netnum=%d\n", + node->nodenum, + node->netname, + // legacy: print only the first point + dp->x, dp->y, dp->gridx, dp->gridy, + node->netnum, node->numnodes, node->netnum ); + + /* need to print the routes to this node (deprecated) + for (j = 0 ; j < g->nodes; j++) { + fprintf(o, "%s(%g,%g) ", g->node[j], *(g->x[j]), *(g->y[j])); + } + */ + } + } + fclose(o); + +} /* print_nodes() */ + +/*--------------------------------------------------------------*/ +/* print_nlnets - show the nets */ +/* */ +/* ARGS: filename to print to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: none */ +/* AUTHOR and DATE: steve beccue Tue Aug 04 2003 */ +/*--------------------------------------------------------------*/ + +void print_nlnets( char *filename ) +{ + FILE *o; + int i; + NODE nd; + NET net; + + if (!strcmp(filename, "stdout")) { + o = stdout; + } else { + o = fopen(filename, "w"); + } + if (!o) { + Fprintf(stderr, "node.c:print_nlnets. Couldn't open output file\n"); + return; + } + + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + fprintf(o, "%d\t#=%d\t%s \t\n", net->netnum, + net->numnodes, net->netname); + + for (nd = net->netnodes; nd; nd = nd->next) { + fprintf(o, "%d ", nd->nodenum); + } + } + + fprintf(o, "%d nets\n", Numnets); + fflush(o); + +} /* print_nlnets() */ + +/*--------------------------------------------------------------*/ +/* cleanup_net -- */ +/* */ +/* Special handling for layers where needblock[] is non-zero, */ +/* and shows that two vias cannot be placed on adjacent routes. */ +/* emit_routed_net() will add specialnets to merge two adjacent */ +/* vias on the same route. However, this cannot be used for */ +/* adjacent vias that are each in a different route record. It */ +/* is easier just to find any such instances and remove them by */ +/* eliminating one of the vias and adding a segment to connect */ +/* the route to the neighboring via. */ +/*--------------------------------------------------------------*/ + +static void cleanup_net(NET net) +{ + SEG segf, segl, seg; + ROUTE rt, rt2; + NODEINFO lnode; + int lf, ll, lf2, ll2; + u_char fcheck, lcheck; + u_char xcheckf, ycheckf, xcheckl, ycheckl; + + lf = ll = lf2 = ll2 = -1; + + for (rt = net->routes; rt; rt = rt->next) { + fcheck = lcheck = FALSE; + + // This problem will only show up on route endpoints. + // segf is the first segment of the route. + // segl is the last segment of the route. + // lf is the layer at the route start (layer first) + // lf2 is the layer of the second segment. + // ll is the layer at the route end (layer last) + // ll2 is the layer of the next-to-last segment + + segf = rt->segments; + if (segf == NULL) continue; + if ((segf->next != NULL) && (segf->segtype == ST_VIA)) { + if (segf->next->layer > segf->layer) { + lf = segf->layer; + lf2 = segf->layer + 1; + } + else { + lf = segf->layer + 1; + lf2 = segf->layer; + } + // Set flag fcheck indicating that segf needs checking + fcheck = TRUE; + + // We're going to remove the contact so it can't be a tap + if ((lf < Pinlayers) && ((lnode = NODEIPTR(segf->x1, segf->y1, lf)) != NULL) + && (lnode->nodesav != NULL)) + fcheck = FALSE; + } + xcheckf = needblock[lf] & VIABLOCKX; + ycheckf = needblock[lf] & VIABLOCKY; + if (!xcheckf && !ycheckf) fcheck = FALSE; + + // Move to the next-to-last segment + for (segl = segf->next; segl && segl->next && segl->next->next; + segl = segl->next); + + if (segl && (segl->next != NULL) && (segl->next->segtype == ST_VIA)) { + if (segl->next->layer < segl->layer) { + ll = segl->next->layer; + ll2 = segl->next->layer + 1; + } + else { + ll = segl->next->layer + 1; + ll2 = segl->next->layer; + } + // Move segl to the last segment + segl = segl->next; + // Set flag lcheck indicating that segl needs checking. + lcheck = TRUE; + + // We're going to remove the contact so it can't be a tap + if ((ll < Pinlayers) && ((lnode = NODEIPTR(segl->x1, segl->y1, ll)) != NULL) + && (lnode->nodesav != NULL)) + lcheck = FALSE; + } + xcheckl = needblock[ll] & VIABLOCKX; + ycheckl = needblock[ll] & VIABLOCKY; + if (!xcheckl && !ycheckl) lcheck = FALSE; + + // For each route rt2 that is not rt, look at every via + // and see if it is adjacent to segf or segl. + + for (rt2 = net->routes; rt2; rt2 = rt2->next) { + + if ((fcheck == FALSE) && (lcheck == FALSE)) break; + if (rt2 == rt) continue; + + for (seg = rt2->segments; seg; seg = seg->next) { + if (seg->segtype & ST_VIA) { + if (fcheck) { + if ((seg->layer == lf) || ((seg->layer + 1) == lf)) { + if (xcheckf && (seg->y1 == segf->y1) && + (ABSDIFF(seg->x1, segf->x1) == 1)) { + if (seg->layer != segf->layer) { + + // Adjacent vias are different types. + // Deal with it by creating a route between + // the vias on their shared layer. This + // will later be made into a special net to + // avoid notch DRC errors. + + SEG newseg; + newseg = (SEG)malloc(sizeof(struct seg_)); + rt->segments = newseg; + newseg->next = segf; + newseg->layer = lf; + newseg->segtype = ST_WIRE; + newseg->x1 = segf->x1; + newseg->y1 = segf->y1; + newseg->x2 = seg->x1; + newseg->y2 = seg->y1; + } + else { + // Change via to wire route, connect it to seg, + // and make sure it has the same layer type as + // the following route. + segf->segtype = ST_WIRE; + segf->x1 = seg->x1; + segf->layer = lf2; + } + } + else if (ycheckf && (seg->x1 == segf->x1) && + (ABSDIFF(seg->y1, segf->y1) == 1)) { + if (seg->layer != segf->layer) { + // Adjacent vias are different types. + // Deal with it by creating a route between + // the vias on their shared layer. This + // will later be made into a special net to + // avoid notch DRC errors. + + SEG newseg; + newseg = (SEG)malloc(sizeof(struct seg_)); + rt->segments = newseg; + newseg->next = segf; + newseg->layer = lf; + newseg->segtype = ST_WIRE; + newseg->x1 = segf->x1; + newseg->y1 = segf->y1; + newseg->x2 = seg->x1; + newseg->y2 = seg->y1; + } + else { + // Change via to wire route, connect it to seg, + // and make sure it has the same layer type as + // the following route. + segf->segtype = ST_WIRE; + segf->y1 = seg->y1; + segf->layer = lf2; + } + } + } + } + + if (lcheck) { + if ((seg->layer == ll) || ((seg->layer + 1) == ll)) { + if (xcheckl && (seg->y1 == segl->y1) && + (ABSDIFF(seg->x1, segl->x1) == 1)) { + if (seg->layer != segl->layer) { + + // Adjacent vias are different types. + // Deal with it by creating a route between + // the vias on their shared layer. This + // will later be made into a special net to + // avoid notch DRC errors. + + SEG newseg; + newseg = (SEG)malloc(sizeof(struct seg_)); + segl->next = newseg; + newseg->next = NULL; + newseg->layer = ll; + newseg->segtype = ST_WIRE; + newseg->x1 = segl->x1; + newseg->y1 = segl->y1; + newseg->x2 = seg->x1; + newseg->y2 = seg->y1; + } + else { + // Change via to wire route, connect it to seg, + // and make sure it has the same layer type as + // the previous route. + segl->segtype = ST_WIRE; + segl->x2 = seg->x2; + segl->layer = ll2; + } + } + else if (ycheckl && (seg->x1 == segl->x1) && + (ABSDIFF(seg->y1, segl->y1) == 1)) { + if (seg->layer != segl->layer) { + + // Adjacent vias are different types. + // Deal with it by creating a route between + // the vias on their shared layer. This + // will later be made into a special net to + // avoid notch DRC errors. + + SEG newseg; + newseg = (SEG)malloc(sizeof(struct seg_)); + segl->next = newseg; + newseg->next = NULL; + newseg->layer = ll; + newseg->segtype = ST_WIRE; + newseg->x1 = segl->x1; + newseg->y1 = segl->y1; + newseg->x2 = seg->x1; + newseg->y2 = seg->y1; + } + else { + // Change via to wire route, connect it to seg, + // and make sure it has the same layer type as + // the previous route. + segl->segtype = ST_WIRE; + segl->y2 = seg->y2; + segl->layer = ll2; + } + } + } + } + } + } + } + } +} + +/*--------------------------------------------------------------*/ +/* emit_routed_net -- */ +/* */ +/* Core part of emit_routes(). Dumps the DEF format for a */ +/* complete net route to file Cmd. If "special" is TRUE, then */ +/* it looks only for stub routes between a grid point and an */ +/* off-grid terminal, and dumps only the stub route geometry as */ +/* a SPECIALNET, which takes a width parameter. This allows */ +/* the stub routes to be given the same width as a via, when */ +/* the via is larger than a route width, to avoid DRC notch */ +/* errors between the via and the terminal. The SPECIALNETS */ +/* are redundant; all routing information is in the NETS */ +/* section. The SPECIALNETS only specify a wider route for the */ +/* stub connection. */ +/*--------------------------------------------------------------*/ + +static void +emit_routed_net(FILE *Cmd, NET net, u_char special, double oscale, int iscale) +{ + SEG seg, saveseg, lastseg, prevseg; + NODEINFO lnode, lnode1, lnode2; + ROUTE rt; + u_int dir1, dir2, tdir; + int layer; + int x = 0, y = 0, x2, y2; + double dc; + int lastx = -1, lasty = -1, lastlay; + int horizontal; + float offset1, offset2, stub, offset; + u_char cancel, segtype; + double invscale = (double)(1.0 / (double)iscale); + + /* If the STUB flag is set, then we need to write out the net name */ + /* in the SPECIALNETS section. */ + + if ((special == (u_char)1) && (net->flags & NET_STUB)) { + 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) { + if (rt->segments && !(rt->flags & RT_OUTPUT)) { + horizontal = FALSE; + cancel = FALSE; + + // Check first position for terminal offsets + seg = (SEG)rt->segments; + lastseg = saveseg = seg; + layer = seg->layer; + if (seg) { + + // It is rare but possible to have a stub route off of an + // endpoint via, so check this case, and use the layer type + // of the via top if needed. + + if ((seg->segtype & ST_VIA) && seg->next && (seg->next->layer <= + seg->layer)) + layer++; + + lnode = (layer < Pinlayers) ? NODEIPTR(seg->x1, seg->y1, layer) : NULL; + stub = (lnode) ? lnode->stub : 0.0; + if (OBSVAL(seg->x1, seg->y1, layer) & STUBROUTE) { + if ((special == (u_char)0) && (Verbose > 2)) + Fprintf(stdout, "Stub route distance %g to terminal" + " at %d %d (%d)\n", stub, + seg->x1, seg->y1, layer); + + dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + 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]; + y = (int)((REPS(dc)) * oscale); + if (lnode->flags & NI_STUB_NS) + dc += stub; + y2 = (int)((REPS(dc)) * oscale); + if (lnode->flags & NI_STUB_EW) { + horizontal = TRUE; + + // If the gridpoint ahead of the stub has a route + // on the same net, and the stub is long enough + // to come within a DRC spacing distance of the + // other route, then lengthen it to close up the + // distance and resolve the error. (NOTE: This + // unnecessarily stretches routes to cover taps + // that have not been routed to. At least on the + // test standard cell set, these rules remove a + // handful of DRC errors and don't create any new + // ones. If necessary, a flag can be added to + // distinguish routes from taps. + + if ((x < x2) && (seg->x1 < (NumChannelsX[layer] - 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]; + x2 = (int)((REPS(dc)) * oscale); + } + } + } + else if ((x > x2) && (seg->x1 > 0)) { + 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]; + x2 = (int)((REPS(dc)) * oscale); + } + } + } + + dc = oscale * 0.5 * LefGetRouteWidth(layer); + if (special == (u_char)0) { + // Regular nets include 1/2 route width at + // the ends, so subtract from the stub terminus + if (x < x2) { + x2 -= dc; + if (x >= x2) cancel = TRUE; + } + else { + x2 += dc; + if (x <= x2) cancel = TRUE; + } + } + else { + // Special nets don't include 1/2 route width + // at the ends, so add to the route at the grid + if (x < x2) + x -= dc; + else + x += dc; + + // Routes that extend for more than one track + // without a bend do not need a wide stub + if (seg->x1 != seg->x2) cancel = TRUE; + } + } + else { + horizontal = FALSE; + + // If the gridpoint ahead of the stub has a route + // on the same net, and the stub is long enough + // to come within a DRC spacing distance of the + // other route, then lengthen it to close up the + // distance and resolve the error. + + if ((y < y2) && (seg->y1 < (NumChannelsY[layer] - 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]; + y2 = (int)((REPS(dc)) * oscale); + } + } + } + else if ((y > y2) && (seg->y1 > 0)) { + 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]; + y2 = (int)((REPS(dc)) * oscale); + } + } + } + + dc = oscale * 0.5 * LefGetRouteWidth(layer); + if (special == (u_char)0) { + // Regular nets include 1/2 route width at + // the ends, so subtract from the stub terminus + if (y < y2) { + y2 -= dc; + if (y >= y2) cancel = TRUE; + } + else { + y2 += dc; + if (y <= y2) cancel = TRUE; + } + } + else { + // Special nets don't include 1/2 route width + // at the ends, so add to the route at the grid + if (y < y2) + y -= dc; + else + y += dc; + + // Routes that extend for more than one track + // without a bend do not need a wide stub + if (seg->y1 != seg->y2) cancel = TRUE; + } + } + + 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); + } + lastx = x; + lasty = y; + lastlay = layer; + } + } + + prevseg = NULL; + lastseg = NULL; + for (seg = rt->segments; seg; seg = seg->next) { + layer = seg->layer; + + // Check for offset terminals at either point + + offset1 = 0.0; + offset2 = 0.0; + dir1 = 0; + dir2 = 0; + + if (seg->segtype & ST_OFFSET_START) { + dir1 = OBSVAL(seg->x1, seg->y1, seg->layer) & OFFSET_TAP; + if ((dir1 == 0) && lastseg) { + dir1 = OBSVAL(lastseg->x2, lastseg->y2, lastseg->layer) + & OFFSET_TAP; + lnode1 = NODEIPTR(lastseg->x2, lastseg->y2, lastseg->layer); + offset1 = lnode1->offset; + } + else { + lnode1 = NODEIPTR(seg->x1, seg->y1, seg->layer); + offset1 = lnode1->offset; + } + + // Offset was calculated for vias; plain metal routes + // typically will need less offset distance, so subtract off + // the difference. + + if (!(seg->segtype & ST_VIA)) { + if (offset1 < 0) { + offset1 += 0.5 * (LefGetViaWidth(seg->layer, seg->layer, + horizontal) - LefGetRouteWidth(seg->layer)); + if (offset1 > 0) offset1 = 0; + } + else if (offset1 > 0) { + offset1 -= 0.5 * (LefGetViaWidth(seg->layer, seg->layer, + horizontal) - LefGetRouteWidth(seg->layer)); + if (offset1 < 0) offset1 = 0; + } + } + + if (special == (u_char)0) { + if ((seg->segtype & ST_VIA) && (Verbose > 2)) + Fprintf(stdout, "Offset terminal distance %g to grid" + " at %d %d (%d)\n", offset1, + seg->x1, seg->y1, layer); + } + } + if (seg->segtype & ST_OFFSET_END) { + dir2 = OBSVAL(seg->x2, seg->y2, seg->layer) & OFFSET_TAP; + if ((dir2 == 0) && seg->next) { + dir2 = OBSVAL(seg->next->x1, seg->next->y1, seg->next->layer) & + OFFSET_TAP; + lnode2 = NODEIPTR(seg->next->x1, seg->next->y1, seg->next->layer); + offset2 = lnode2->offset; + } + else { + lnode2 = NODEIPTR(seg->x2, seg->y2, seg->layer); + offset2 = lnode2->offset; + } + + // Offset was calculated for vias; plain metal routes + // typically will need less offset distance, so subtract off + // the difference. + + if (!(seg->segtype & ST_VIA)) { + if (offset2 < 0) { + offset2 += 0.5 * (LefGetViaWidth(seg->layer, seg->layer, + horizontal) - LefGetRouteWidth(seg->layer)); + if (offset2 > 0) offset2 = 0; + } + else if (offset2 > 0) { + offset2 -= 0.5 * (LefGetViaWidth(seg->layer, seg->layer, + horizontal) - LefGetRouteWidth(seg->layer)); + if (offset2 < 0) offset2 = 0; + } + } + + if (special == (u_char)0) { + if ((seg->segtype & ST_VIA) + && !(seg->segtype & ST_OFFSET_START)) + if (Verbose > 2) + Fprintf(stdout, "Offset terminal distance %g to grid" + " at %d %d (%d)\n", offset2, + seg->x2, seg->y2, layer); + } + } + + // To do: pick up route layer name from lefInfo. + // At the moment, technology names don't even match, + // and are redundant between CIFLayer[] from the + // config file and lefInfo. + + dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + if ((dir1 & OFFSET_TAP) && (lnode1->flags & NI_OFFSET_EW)) dc += offset1; + x = (int)((REPS(dc)) * oscale); + dc = Ylowerbound + (double)seg->y1 * PitchY[layer]; + if ((dir1 & OFFSET_TAP) && (lnode1->flags & NI_OFFSET_NS)) dc += offset1; + y = (int)((REPS(dc)) * oscale); + dc = Xlowerbound + (double)seg->x2 * PitchX[layer]; + if ((dir2 & OFFSET_TAP) && (lnode2->flags & NI_OFFSET_EW)) dc += offset2; + x2 = (int)((REPS(dc)) * oscale); + dc = Ylowerbound + (double)seg->y2 * PitchY[layer]; + 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); + switch (segtype) { + case ST_WIRE: + + // Normally layers change only at a via. However, if + // a via has been removed and replaced by a 1-track + // segment to a neighboring via to avoid DRC errors + // (see cleanup_net()), then a layer change may happen + // between two ST_WIRE segments, and a new path should + // be started. + + if ((Pathon != -1) && (lastlay != -1) && (lastlay != seg->layer)) + Pathon = 0; + + if (Pathon != 1) { // 1st point of route seg + if (x == x2) { + horizontal = FALSE; + } + else if (y == y2) { + horizontal = TRUE; + } + else if (Verbose > 3) { + // NOTE: This is a development diagnostic. The + // occasional non-Manhanhattan route is due to a + // tap offset and is corrected automatically by + // making an L-bend in the wire. + + Flush(stdout); + Fprintf(stderr, "Warning: non-Manhattan wire in route" + " at (%d %d) to (%d %d)\n", x, y, x2, y2); + } + if (special == (u_char)0) { + pathstart(Cmd, seg->layer, x, y, special, oscale, invscale, + horizontal); + lastx = x; + lasty = y; + lastlay = seg->layer; + } + } + rt->flags |= RT_OUTPUT; + if (horizontal && x == x2) { + horizontal = FALSE; + } + if ((!horizontal) && y == y2) { + horizontal = TRUE; + } + if (!(x == x2) && !(y == y2)) { + horizontal = FALSE; + } + if (special == (u_char)0) { + pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale); + lastx = x2; + lasty = y2; + } + + // 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 ((lastseg && lastseg->segtype == ST_VIA) || + (seg->next && seg->next->segtype == ST_VIA)) { + if (special == (u_char)0) { + net->flags |= NET_STUB; + rt->flags |= RT_STUB; + } + else { + if (Pathon != -1) Pathon = 0; + pathstart(Cmd, layer, x, y, special, oscale, + invscale, horizontal); + pathto(Cmd, x2, y2, horizontal, x, y, invscale); + lastlay = layer; + } + } + } + } + else if (!horizontal && needblock[seg->layer] & VIABLOCKY) { + if (ABSDIFF(seg->y2, seg->y1) == 1) { + if ((lastseg && lastseg->segtype == ST_VIA) || + (seg->next && seg->next->segtype == ST_VIA)) { + if (special == (u_char)0) { + net->flags |= NET_STUB; + rt->flags |= RT_STUB; + } + else { + if (Pathon != -1) Pathon = 0; + pathstart(Cmd, layer, x, y, special, oscale, + invscale, horizontal); + pathto(Cmd, x2, y2, horizontal, x, y, invscale); + lastlay = layer; + } + } + } + } + break; + case ST_VIA: + rt->flags |= RT_OUTPUT; + if (special == (u_char)0) { + double viaoffx, viaoffy; + int vx = 0; + int vy = 0; + 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; + + if (lastseg == NULL) { + // Make sure last position is valid + lastx = x; + 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; + + // Check for via to west + if (seg->x1 > 0) { + tdir = OBSVAL(seg->x1 - 1, seg->y1, layer) + & ROUTED_NET_MASK; + + if (((tdir & NO_NET) == 0) && (tdir != 0) && + (tdir != (net->netnum | ROUTED_NET))) { + + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1 - 1, seg->y1, layer + 1) + & ROUTED_NET_MASK; + 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) + & 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; + } + } + } + + // Check for via to east + if (seg->x1 < NumChannelsX[layer] - 1) { + tdir = OBSVAL(seg->x1 + 1, seg->y1, layer) + & ROUTED_NET_MASK; + + if (((tdir & NO_NET) == 0) && (tdir != 0) && + (tdir != (net->netnum | ROUTED_NET))) { + + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1 + 1, seg->y1, layer + 1) + & ROUTED_NET_MASK; + 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) + & 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; + } + } + } + + // 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) + & ROUTED_NET_MASK; + + if (((tdir & NO_NET) == 0) && (tdir != 0) && + (tdir != (net->netnum | ROUTED_NET))) { + + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1, seg->y1 - 1, layer + 1) + & ROUTED_NET_MASK; + 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) + & 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; + } + } + } + + // Check for via to north + if (seg->y1 < NumChannelsY[layer] - 1) { + tdir = OBSVAL(seg->x1, seg->y1 + 1, layer) + & ROUTED_NET_MASK; + + if (((tdir & NO_NET) == 0) && (tdir != 0) && + (tdir != (net->netnum | ROUTED_NET))) { + + if (layer < Num_layers - 1) { + tdirp = OBSVAL(seg->x1, seg->y1 + 1, layer + 1) + & ROUTED_NET_MASK; + 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) + & 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; + } + } + } + + // Compute Y offset + viaoffy = 0; + + if (viaSL) viaoffy = viaOffsetY[layer][0]; + else if (viaNL) viaoffy = -viaOffsetY[layer][0]; + + if (viaSM && viaOffsetY[layer][1] > viaoffy) + viaoffy = viaOffsetY[layer][1]; + else if (viaNM && -viaOffsetY[layer][1] < viaoffy) + viaoffy = -viaOffsetY[layer][1]; + + if (viaSU && viaOffsetY[layer][2] > viaoffy) + viaoffy = viaOffsetY[layer][2]; + else if (viaNU && -viaOffsetY[layer][2] < viaoffy) + viaoffy = -viaOffsetY[layer][2]; + + vy = (int)((REPS(viaoffy)) * oscale); + } + + // via-to-via interactions are symmetric, so move each + // via half the distance (?) + + pathvia(Cmd, layer, x + vx, y + vy, lastx, lasty, + seg->x1, seg->y1, invscale); + + lastx = x; + lasty = y; + lastlay = -1; + } + break; + default: + break; + } + + // Break here on last segment so that seg and lastseg are valid + // in the following section of code. + + if (seg->next == NULL) break; + prevseg = lastseg; + lastseg = seg; + } + + // For stub routes, reset the path between terminals, since + // the stubs are not connected. + if (special == (u_char)1 && Pathon != -1) Pathon = 0; + + // Check last position for terminal offsets + if (seg && ((seg != saveseg) || (seg->segtype & ST_WIRE))) { + cancel = FALSE; + layer = seg->layer; + lnode = (layer < Pinlayers) ? NODEIPTR(seg->x2, seg->y2, layer) : NULL; + + // Look for stub routes and offset taps + dir2 = OBSVAL(seg->x2, seg->y2, layer) & (STUBROUTE | OFFSET_TAP); + + if ((dir2 & OFFSET_TAP) && (seg->segtype & ST_VIA) && prevseg) { + + // Additional handling for offset taps. When a tap position + // is a via and is offset in the direction of the last + // route segment, then a DRC violation can be created if + // (1) the via is wider than the route width, and (2) the + // adjacent track position is another via or a bend in the + // route, and (3) the tap offset is large enough to create + // a spacing violation between the via and the adjacent via + // or perpendicular route. If these three conditions are + // satisfied, then generate a stub route the width of the + // via and one track pitch in length back toward the last + // track position. + + // Problems only arise when the via width is larger than + // the width of the metal route leaving the via. + + offset = lnode->offset; + if (LefGetViaWidth(seg->layer, lastseg->layer, 1 - horizontal) > + LefGetRouteWidth(lastseg->layer)) { + + // Problems only arise when the last segment is exactly + // one track long. + + if ((ABSDIFF(lastseg->x2, lastseg->x1) == 1) || + (ABSDIFF(lastseg->y2, lastseg->y1) == 1)) { + + if (prevseg->segtype & ST_VIA) { + + dc = Xlowerbound + (double)seg->x1 * PitchX[layer]; + x = (int)((REPS(dc)) * oscale); + dc = Ylowerbound + (double)seg->y1 * PitchY[layer]; + y = (int)((REPS(dc)) * oscale); + + dc = Xlowerbound + (double)prevseg->x1 * PitchX[layer]; + x2 = (int)((REPS(dc)) * oscale); + dc = Ylowerbound + (double)prevseg->y1 * PitchY[layer]; + y2 = (int)((REPS(dc)) * oscale); + + // Setup is (via, 1 track route, via with offset) + + if (prevseg->x1 != seg->x1) { + if ((PitchX[lastseg->layer] - + 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 1) - + 0.5 * LefGetViaWidth(prevseg->layer, lastseg->layer, 1) - + (prevseg->x1 - seg->x1) * offset) + < LefGetRouteSpacing(lastseg->layer)) { + if (special == (u_char)0) { + rt->flags |= RT_STUB; + net->flags |= NET_STUB; + } + else { + pathstart(Cmd, lastseg->layer, x, y, + (u_char)1, oscale, invscale, 1); + pathto(Cmd, x2, y2, 1, x, y, invscale); + lastx = x2; + lasty = y2; + } + } + } + else if (prevseg->y1 != seg->y1) { + if ((PitchY[lastseg->layer] - + 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 0) - + 0.5 * LefGetViaWidth(prevseg->layer, lastseg->layer, 0) + - (prevseg->y1 - seg->y1) * offset) + < LefGetRouteSpacing(lastseg->layer)) { + if (special == (u_char)0) { + rt->flags |= RT_STUB; + net->flags |= NET_STUB; + } + else { + pathstart(Cmd, lastseg->layer, x, y, + (u_char)1, oscale, invscale, 0); + pathto(Cmd, x2, y2, 0, x, y, invscale); + lastx = x2; + lasty = y2; + } + } + } + } + else { // Metal route bends at next track + if (prevseg->x1 != seg->x1) { + if ((PitchX[lastseg->layer] - + 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 1) - + 0.5 * LefGetRouteWidth(prevseg->layer) - + (prevseg->x1 - seg->x1) * offset) + < LefGetRouteSpacing(lastseg->layer)) { + if (special == (u_char)0) { + rt->flags |= RT_STUB; + net->flags |= NET_STUB; + } + else { + pathstart(Cmd, lastseg->layer, x, y, + (u_char)1, oscale, invscale, 1); + pathto(Cmd, x2, y2, 1, x, y, invscale); + lastx = x2; + lasty = y2; + } + } + } + else if (prevseg->y1 != seg->y1) { + if ((PitchY[lastseg->layer] - + 0.5 * LefGetViaWidth(seg->layer, lastseg->layer, 0) - + 0.5 * LefGetRouteWidth(prevseg->layer) - + (prevseg->y1 - seg->y1) * offset) + < LefGetRouteSpacing(lastseg->layer)) { + if (special == (u_char)0) { + rt->flags |= RT_STUB; + net->flags |= NET_STUB; + } + else { + pathstart(Cmd, lastseg->layer, x, y, + (u_char)1, oscale, invscale, 0); + pathto(Cmd, x2, y2, 0, x, y, invscale); + lastx = x2; + lasty = y2; + } + } + } + } + } + } + } + + // For stub routes, reset the path between terminals, since + // the stubs are not connected. + if (special == (u_char)1 && Pathon != -1) Pathon = 0; + + // Handling of stub routes + if (dir2 & STUBROUTE) { + stub = lnode->stub; + if ((special == (u_char)0) && (Verbose > 2)) + Fprintf(stdout, "Stub route distance %g to terminal" + " at %d %d (%d)\n", + stub, seg->x2, seg->y2, layer); + + dc = Xlowerbound + (double)seg->x2 * PitchX[layer]; + 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]; + if (lnode->flags & NI_OFFSET_NS) + dc += offset; + y = (int)((REPS(dc)) * oscale); + if (lnode->flags & NI_STUB_NS) + dc += stub; + y2 = (int)((REPS(dc)) * oscale); + if (lnode->flags & NI_STUB_EW) { + horizontal = TRUE; + + // If the gridpoint ahead of the stub has a route + // on the same net, and the stub is long enough + // to come within a DRC spacing distance of the + // other route, then lengthen it to close up the + // distance and resolve the error. + + if ((x < x2) && (seg->x2 < (NumChannelsX[layer] - 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]; + x2 = (int)((REPS(dc)) * oscale); + } + } + } + else if ((x > x2) && (seg->x2 > 0)) { + 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]; + x2 = (int)((REPS(dc)) * oscale); + } + } + } + + dc = oscale * 0.5 * LefGetRouteWidth(layer); + if (special == (u_char)0) { + // Regular nets include 1/2 route width at + // the ends, so subtract from the stub terminus + if (x < x2) { + x2 -= dc; + if (x >= x2) cancel = TRUE; + } + else { + x2 += dc; + if (x <= x2) cancel = TRUE; + } + } + else { + // Special nets don't include 1/2 route width + // at the ends, so add to the route at the grid + if (x < x2) + x -= dc; + else + x += dc; + + // Routes that extend for more than one track + // without a bend do not need a wide stub + if (seg->x1 != seg->x2) cancel = TRUE; + } + } + else { /* lnode->flags & NI_STUB_EW implied */ + horizontal = FALSE; + + // If the gridpoint ahead of the stub has a route + // on the same net, and the stub is long enough + // to come within a DRC spacing distance of the + // other route, then lengthen it to close up the + // distance and resolve the error. + + if ((y < y2) && (seg->y2 < (NumChannelsY[layer] - 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]; + y2 = (int)((REPS(dc)) * oscale); + } + } + } + else if ((y > y2) && (seg->y2 > 0)) { + 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]; + y2 = (int)((REPS(dc)) * oscale); + } + } + } + + dc = oscale * 0.5 * LefGetRouteWidth(layer); + if (special == (u_char)0) { + // Regular nets include 1/2 route width at + // the ends, so subtract from the stub terminus + if (y < y2) { + y2 -= dc; + if (y >= y2) cancel = TRUE; + } + else { + y2 += dc; + if (y <= y2) cancel = TRUE; + } + } + else { + // Special nets don't include 1/2 route width + // at the ends, so add to the route at the grid + if (y < y2) + y -= dc; + else + y += dc; + + // Routes that extend for more than one track + // without a bend do not need a wide stub + if (seg->y1 != seg->y2) cancel = TRUE; + } + } + if (cancel == FALSE) { + net->flags |= NET_STUB; + rt->flags |= RT_STUB; + if (Pathon != 1) { + pathstart(Cmd, layer, x, y, special, oscale, invscale, + horizontal); + lastx = x; + lasty = y; + } + pathto(Cmd, x2, y2, horizontal, lastx, lasty, invscale); + lastx = x2; + lasty = y2; + } + } + } + if (Pathon != -1) Pathon = 0; + + } // if (rt->segments && !(rt->flags & RT_OUTPUT)) + } +} + +/*--------------------------------------------------------------*/ +/* emit_routes - DEF file output from the list of routes */ +/* */ +/* Reads the <project>.def file and rewrites file */ +/* <project>_route.def, where each net definition has the */ +/* physical route appended. */ +/* */ +/* ARGS: filename to list to */ +/* RETURNS: nothing */ +/* SIDE EFFECTS: */ +/* AUTHOR and DATE: steve beccue Mon Aug 11 2003 */ +/*--------------------------------------------------------------*/ + +static void emit_routes(char *filename, double oscale, int iscale) +{ + FILE *Cmd; + int i, j, numnets, stubroutes; + char line[MAX_LINE_LEN + 1], *lptr = NULL; + char netname[MAX_NAME_LEN]; + NET net = NULL; + ROUTE rt; + FILE *fdef; + u_char errcond = FALSE; + u_char need_cleanup = FALSE; + + fdef = fopen(DEFfilename, "r"); + if (fdef == NULL) { + if (strchr(DEFfilename, '.') == NULL) { + char *extfilename = malloc(strlen(DEFfilename) + 5); + sprintf(extfilename, "%s.def", DEFfilename); + fdef = fopen(extfilename, "r"); + free(extfilename); + } + } + if (fdef == NULL) { + Fprintf(stderr, "emit_routes(): Cannot open DEF file for reading.\n"); + return; + } + + if (!strcmp(filename, "stdout")) { + Cmd = stdout; + } + else { + char *dotptr; + + if (filename == DEFfilename) { + char *newDEFfile = (char *)malloc(strlen(filename) + 11); + strcpy(newDEFfile, filename); + dotptr = strrchr(newDEFfile, '.'); + if (dotptr) + strcpy(dotptr, "_route.def"); + else + strcat(newDEFfile, "_route.def"); + + Cmd = fopen(newDEFfile, "w"); + free(newDEFfile); + } + else { + dotptr = strrchr(filename, '.'); + if (dotptr) + Cmd = fopen(filename, "w"); + else { + char *newDEFfile = (char *)malloc(strlen(filename) + 11); + strcpy(newDEFfile, filename); + strcat(newDEFfile, ".def"); + Cmd = fopen(newDEFfile, "w"); + free(newDEFfile); + } + } + } + if (!Cmd) { + Fprintf(stderr, "emit_routes(): Couldn't open output (routed) DEF file.\n"); + return; + } + + // Copy DEF file up to NETS line + numnets = 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; + } + fputs(line, Cmd); + } + fputs(line, Cmd); // Write the NETS line + + if ((numnets + numSpecial) != Numnets) { + Flush(stdout); + Fprintf(stderr, "emit_routes(): DEF file has %d nets and %d specialnets.\n", + numnets, numSpecial); + Fprintf(stderr, "but qrouter wants to write %d nets and specialnets.\n", + 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) { + if ((lptr = strchr(line, ';')) != NULL) { + *lptr = '\n'; + *(lptr + 1) = '\0'; + break; + } + else { + lptr = line; + while (isspace(*lptr)) lptr++; + if (*lptr == '-') { + lptr++; + while (isspace(*lptr)) lptr++; + sscanf(lptr, "%s", netname); + fputs(line, Cmd); + } + else if (*lptr == '+') { + lptr++; + while (isspace(*lptr)) lptr++; + if (!strncmp(lptr, "ROUTED", 6)) { + // This net is being handled by qrouter, so remove + // the original routing information + while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { + if ((lptr = strchr(line, ';')) != NULL) { + *lptr = '\n'; + *(lptr + 1) = '\0'; + break; + } + } + break; + } + else + fputs(line, Cmd); + } + else if (!strncmp(lptr, "END", 3)) { // This should not happen + fputs(line, Cmd); + errcond = TRUE; + break; + } + else + fputs(line, Cmd); + } + } + + /* 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); + + /* Dump rest of net and continue---no routing information */ + *(lptr) = ';'; + fputs(line, Cmd); + continue; + } + else { + /* 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"); + } + } + + // Finish copying the rest of the NETS section + if (errcond == FALSE) { + while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { + lptr = line; + while (isspace(*lptr)) lptr++; + fputs(line, Cmd); + if (!strncmp(lptr, "END", 3)) { + break; + } + } + } + + // Determine how many stub routes we will write to SPECIALNETS + // Also reset the OUTPUT flag for each route needing a stubroute + // to be written. + + stubroutes = 0; + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + if (net->flags & NET_STUB) { + stubroutes++; + for (rt = net->routes; rt; rt = rt->next) + if (rt->flags & RT_STUB) + rt->flags &= ~RT_OUTPUT; + } + } + + // If there were stub routes, repeat them in SPECIALNETS at the + // proper width. + if (stubroutes > 0) { + + fprintf(Cmd, "\nSPECIALNETS %d ", stubroutes); + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + emit_routed_net(Cmd, net, (u_char)1, oscale, iscale); + } + fprintf(Cmd, ";\nEND SPECIALNETS\n"); + } + + // Finish copying the rest of the file + while (fgets(line, MAX_LINE_LEN, fdef) != NULL) { + fputs(line, Cmd); + } + fclose(fdef); + fclose(Cmd); + +} /* emit_routes() */ + +/* end of output.c */ |