/* * defWrite.c -- * * This module incorporates the LEF/DEF format for standard-cell place and * route. * * * Version 0.1 (June 9, 2004): DEF output for layouts, to include netlist * from the extracted layout. * */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/defWrite.c,v 1.2 2008/02/10 19:30:21 tim Exp $"; #endif /* not lint */ #include #include #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/tech.h" #include "utils/malloc.h" #include "utils/undo.h" #include "cif/cif.h" #include "extflat/extflat.h" #include "lef/lefInt.h" #include "drc/drc.h" /* for querying width,spacing rules */ /*----------------------------------------------------------------------*/ /* Structures used by various routines */ /*----------------------------------------------------------------------*/ typedef struct { float scale; FILE *f; CellDef *def; Tile *tile; /* Values of the last calculated route */ TileType type; float x, y, extlen; unsigned char orient; LefMapping *MagicToLefTbl; HashTable *defViaTable; int outcolumn; /* Current column of output in file */ unsigned char specialmode; /* What nets to write as SPECIALNETS */ } DefData; typedef struct { CellDef *def; int nlayers; char **baseNames; TileTypeBitMask *blockMasks; LinkedRect **blockData; } DefObsData; typedef struct { CellDef *def; float scale; int total; TileTypeBitMask *mask; LefMapping *MagicToLefTbl; HashTable *defViaTable; } CViaData; /*----------------------------------------------------------------------*/ char *defGetType(); /* Forward declaration */ /*----------------------------------------------------------------------*/ /* * ---------------------------------------------------------------------------- * * defWriteHeader -- * * This routine generates DEF header output for a cell or cell hierarchy. * * Results: * None. * * Side effects: * Writes output to the open file "f". * * ---------------------------------------------------------------------------- */ void defWriteHeader(def, f, oscale, units) CellDef *def; /* Def for which to generate DEF output */ FILE *f; /* Output to this file */ float oscale; int units; /* Units for UNITS; could be derived from oscale */ { TileType type; TxPrintf("Diagnostic: Write DEF header for cell %s\n", def->cd_name); /* NOTE: This routine corresponds to Envisia LEF/DEF Language */ /* Reference version 5.7 (November, 2009) */ fprintf(f, "VERSION 5.7 ;\n"); fprintf(f, " NAMESCASESENSITIVE ON ;\n"); fprintf(f, " DIVIDERCHAR \"/\" ;\n"); /* Declare that buses are denoted with parentheses, since magic */ /* uses brackets for arrays and instances. */ fprintf(f, " BUSBITCHARS \"()\" ;\n"); /* Design name, taken from the cell def name */ fprintf(f, " DESIGN %s ;\n", def->cd_name); /* Technology name, taken from the magic tech file. */ /* (which may not be a good idea. . . may need a tech definition */ /* in the tech file "lef" section to specifically name the LEF/DEF */ /* technology). */ fprintf(f, " TECHNOLOGY %s ;\n", DBTechName); /* The DEF scalefactor (conversion to microns) is always 1000 */ /* (nanometers) unless overridden on the command line. */ fprintf(f, " UNITS DISTANCE MICRONS %d ;\n", units); /* Die area, taken from the cell def bounding box. */ fprintf(f, " DIEAREA ( %.10g %.10g ) ( %.10g %.10g ) ;\n", (float)def->cd_bbox.r_xbot * oscale, (float)def->cd_bbox.r_ybot * oscale, (float)def->cd_bbox.r_xtop * oscale, (float)def->cd_bbox.r_ytop * oscale); fprintf(f, "\n"); } /* *------------------------------------------------------------ * * defTransPos -- * * Determine the DEF orientation of a specific magic * transformation matrix. * * Results: * The position, in DEF string format ("N" for north, etc.) * This is a static string * * Side Effects: * None. * *------------------------------------------------------------ */ char * defTransPos(Transform *t) { static char *def_orient[] = { "N", "S", "E", "W", "FN", "FS", "FE", "FW" }; bool ew; /* east-or-west identifier */ bool sw; /* south-or-west identifier */ bool flip; int pos = 0; ew = ((t->t_a == 0) && (t->t_e == 0)) ? TRUE : FALSE; if (ew) { flip = ((t->t_b * t->t_d) > 0) ? TRUE : FALSE; sw = (t->t_d > 0) ? TRUE : FALSE; } else { flip = ((t->t_a * t->t_e) < 0) ? TRUE : FALSE; sw = (t->t_e > 0) ? FALSE : TRUE; } if (flip) pos += 4; if (ew) pos += 2; if (sw) pos += 1; return def_orient[pos]; } /* *------------------------------------------------------------ * * defCountNets -- * * First-pass function to count the number of different * nets used. If "allSpecial" is TRUE, consider all * geometry to be SPECIALNETS. * * Results: * A NetCount structure holding the regular and special * net totals upon completion. * * Side Effects: * None. * *------------------------------------------------------------ */ NetCount defCountNets(rootDef, allSpecial) CellDef *rootDef; bool allSpecial; { NetCount total; int defnodeCount(); total.regular = (allSpecial) ? -1 : 0; total.special = 0; total.blockages = 0; total.numrules = 0; total.rules = NULL; total.has_nets = TRUE; TxPrintf("Diagnostic: Finding all nets in cell %s\n", rootDef->cd_name); TxPrintf("(This can take a while!)\n"); /* Read in the extracted file */ EFInit(); /* There are no arguments for extflat, but we need to call the */ /* routine to initialize a few things such as the search path. */ EFArgs(0, NULL, NULL, NULL, NULL); EFScale = 0.0; /* Allows EFScale to be set to the scale value */ if (EFReadFile(rootDef->cd_name, TRUE, FALSE, TRUE)) { EFFlatBuild(rootDef->cd_name, EF_FLATNODES | EF_NOFLATSUBCKT); EFVisitNodes(defnodeCount, (ClientData)&total); } else { TxError("Warning: Circuit has no .ext file; no nets written.\n"); TxError("Run extract on this circuit if you want nets in the output.\n"); EFDone(); total.has_nets = FALSE; } if (allSpecial) total.regular = 0; return total; } /* Callback function used by defCountNets */ int defnodeCount(node, res, cap, total) EFNode *node; int res; /* not used */ EFCapValue cap; /* not used */ NetCount *total; { HierName *hierName; char ndn[256]; char *cp, clast; /* Ignore the substrate node if it is not connected to any routing */ if (node->efnode_type == TT_SPACE) return 0; /* Ignore power and ground lines, which we will treat */ /* as SPECIALNETS types. */ hierName = (HierName *) node->efnode_name->efnn_hier; if (!(hierName->hn_parent)) /* Extra processing of top-level nodes */ { char *pwr; cp = hierName->hn_name; clast = *(cp + strlen(cp) - 1); /* Global nodes are marked as "special nets" */ if (clast == '!') node->efnode_flags |= EF_SPECIAL; #ifdef MAGIC_WRAPPER /* Check if name is defined in array "globals" */ pwr = (char *)Tcl_GetVar2(magicinterp, "globals", cp, TCL_GLOBAL_ONLY); if (pwr) { /* Diagnostic */ TxPrintf("Node %s is defined in the \"globals\" array\n"); node->efnode_flags |= EF_SPECIAL; } /* Check against Tcl variables $VDD and $GND */ pwr = (char *)Tcl_GetVar(magicinterp, "VDD", TCL_GLOBAL_ONLY); if (pwr && (!strcmp(cp, pwr))) { /* Diagnostic */ TxPrintf("Node %s matches VDD variable definition!\n"); node->efnode_flags |= EF_SPECIAL; } pwr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY); if (pwr && (!strcmp(cp, pwr))) { /* Diagnostic */ TxPrintf("Node %s matches GND variable definition!\n"); node->efnode_flags |= EF_SPECIAL; } /* If a node has not been marked as SPECIAL, does not connect */ /* to a port, and does not have an internally-generated name, */ /* then mark it as "special". */ if (!(node->efnode_flags & (EF_SPECIAL | EF_PORT)) && (clast != '#')) node->efnode_flags |= EF_SPECIAL; #endif } if (total->regular < 0) { /* "allspecial" options: all nets written as SPECIALNETS */ if ((node->efnode_flags & EF_SPECIAL) || (node->efnode_flags & EF_PORT)) total->special++; else total->blockages++; } else { /* We only count nodes having a port connection as "regular" nets */ if (node->efnode_flags & EF_SPECIAL) total->special++; else if (node->efnode_flags & EF_PORT) total->regular++; else total->blockages++; } return 0; /* Keep going. . . */ } /* * ---------------------------------------------------------------------------- * * defHNsprintf -- * * Create a hierarchical node name for the DEF output file.. * * Results: * None. * * Side effects: * Changes the area pointed to by str * * ---------------------------------------------------------------------------- */ void defHNsprintf(str, hierName, divchar) char *str; HierName *hierName; char divchar; { bool trimGlob, trimLocal; char *s, *cp, c; char *defHNsprintfPrefix(); s = str; if (hierName->hn_parent) str = defHNsprintfPrefix(hierName->hn_parent, str, divchar); /* Make the name conform to valid LEF/DEF syntax. This means */ /* no pound signs or semicolons (which are illegal characters, */ /* along with space and newline which won't be found in the */ /* magic name anyway), or dashes, asterisks, or percent signs */ /* (which are interpreted as wildcard characters by LEF/DEF). */ cp = hierName->hn_name; while (c = *cp++) { switch (c) { case '#': /* Ignore---this is the final character */ /* in internally-generated node names. */ break; case ';': case '-': case '*': case '%': *str++ = '_'; break; default: *str++ = c; break; } } *str++ = '\0'; } char *defHNsprintfPrefix(hierName, str, divchar) HierName *hierName; char *str; char divchar; { char *cp, c; if (hierName->hn_parent) str = defHNsprintfPrefix(hierName->hn_parent, str); cp = hierName->hn_name; while (*str++ = *cp++) ; *(--str) = divchar; return ++str; } /* *------------------------------------------------------------ * * nodeDefName --- * * Determine the node name to write to the DEF file * for the given hierachical name structure from * extflat. * *------------------------------------------------------------ */ char * nodeDefName(hname) HierName *hname; { EFNodeName *nn; HashEntry *he; EFNode *node; static char nodeName[256]; he = EFHNLook(hname, (char *) NULL, "nodeName"); if (he == NULL) return "errorNode"; nn = (EFNodeName *) HashGetValue(he); node = nn->efnn_node; defHNsprintf(nodeName, node->efnode_name->efnn_hier, '/'); return nodeName; } /* *------------------------------------------------------------ * * defCheckForBreak -- * * Add the number "addlen" to the column value of * the output. If the DEF file output has reached or * exceeds this value, write a newline character to * the output and reset the column count. * * Results: * None. * * Side effects: * Output to DEF file; resets defdata->outcolumn * *------------------------------------------------------------ */ #define MAX_DEF_COLUMNS 70 void defCheckForBreak(addlen, defdata) int addlen; DefData *defdata; { defdata->outcolumn += addlen; if (defdata->outcolumn > MAX_DEF_COLUMNS) { fprintf(defdata->f, "\n "); defdata->outcolumn = 6 + addlen; } } /* *------------------------------------------------------------ * * defWriteRouteWidth --- * * Write the width of a SPECIALNET route to the output. * *------------------------------------------------------------ */ void defWriteRouteWidth(defdata, width) DefData *defdata; int width; { float oscale = defdata->scale; char numstr[12]; sprintf(numstr, "%.10g", ((float)width * defdata->scale)); defCheckForBreak(strlen(numstr) + 1, defdata); fprintf(defdata->f, "%s ", numstr); } /* *------------------------------------------------------------ * * defWriteCoord -- * * Output a coordinate pair in DEF syntax. We supply the * point to be written AND the previously written point * so we can make use of the "*" notation in the DEF point * format. If the point to be written is not an extension * of the previous point, "prevpt" should be NULL. * * Results: * None. * * Side Effects: * Output written to the DEF file. * *------------------------------------------------------------ */ void defWriteCoord(defdata, x, y, orient) DefData *defdata; float x, y; unsigned char orient; { FILE *f = defdata->f; char numstr[12]; int ctot = 4; /* The "12" here is just a fudge factor; it is not crucial */ /* to limit the output to exactly MAX_DEF_COLUMNS, and it */ /* is easier to assume that the output of a coordinate */ /* pair is about 12 characters average rather than try to */ /* predetermine what the actual output length will be. */ if ((defdata->outcolumn + 12) > MAX_DEF_COLUMNS) { fprintf(f, "\n "); defdata->outcolumn = 6; } fprintf(f, " ( "); if ((orient == GEO_NORTH) || (orient == GEO_SOUTH)) { fprintf(f, "* "); ctot += 2; } else { sprintf(numstr, "%.10g", x); fprintf(f, "%s ", numstr); ctot += strlen(numstr) + 1; } if ((orient == GEO_EAST) || (orient == GEO_WEST)) { fprintf(f, "* "); ctot += 2; } else { sprintf(numstr, "%.10g", y); fprintf(f, "%s ", numstr); ctot += strlen(numstr) + 1; } fprintf(f, ")"); defdata->outcolumn += ctot; } /* *------------------------------------------------------------ * * defWriteNets -- * * Output the NETS section of a DEF file. We make use of * the connectivity search routines used by "getnode" to * determine unique notes and assign a net name to each. * Then, we generate the geometry output for each NET * entry. * * Results: * None. * * Side Effects: * Output written to the DEF output file. * *------------------------------------------------------------ */ void defWriteNets(f, rootDef, oscale, MagicToLefTable, defViaTable, specialmode) FILE *f; /* File to write to */ CellDef *rootDef; /* Cell definition to use */ float oscale; /* Output scale factor */ LefMapping *MagicToLefTable; /* Magic to LEF layer mapping */ HashTable *defViaTable; /* Hash table of contact positions */ unsigned char specialmode; /* What to write as a SPECIALNET */ { DefData defdata; int defnodeVisit(); defdata.f = f; defdata.scale = oscale; defdata.def = rootDef; defdata.MagicToLefTbl = MagicToLefTable; defdata.outcolumn = 0; defdata.specialmode = specialmode; defdata.defViaTable = defViaTable; EFVisitNodes(defnodeVisit, (ClientData)&defdata); } int defnodeVisit(node, res, cap, defdata) EFNode *node; int res; EFCapValue cap; DefData *defdata; { HierName *hierName; char *ndn; char ndn2[256]; FILE *f = defdata->f; CellDef *def = defdata->def; float oscale = defdata->scale; TileTypeBitMask tmask, *rmask; TileType magictype; EFNodeName *thisnn; int defNetGeometryFunc(); /* Forward declaration */ /* For regular nets, only count those nodes having port */ /* connections. For special nets, only count those nodes */ /* that were marked with the EF_SPECIAL flag while counting */ /* nets. */ if (defdata->specialmode == DO_REGULAR) { if (!(node->efnode_flags & EF_PORT)) return 0; else if (node->efnode_flags & EF_SPECIAL) return 0; } else if (defdata->specialmode == DO_SPECIAL) { if (!(node->efnode_flags & EF_SPECIAL)) return 0; } else /* ALL_SPECIAL */ { if (!(node->efnode_flags & EF_PORT) && !(node->efnode_flags & EF_SPECIAL)) return 0; } hierName = (HierName *) node->efnode_name->efnn_hier; ndn = nodeDefName(hierName); defHNsprintf(ndn2, node->efnode_name->efnn_hier, '/'); if (strcmp(ndn, ndn2)) { TxError("Node mismatch: %s vs. %s\n", ndn, ndn2); } /* Avoid attempting to extract an implicit substrate into DEF */ if (node->efnode_type == TT_SPACE) return 0; fprintf(f, " - %s", ndn); defdata->outcolumn = 5 + strlen(ndn); /* Find all the node names that are port connections. */ /* For now, we will just use anything connecting one level */ /* down in the hierarchy. This is not definitive, however, */ /* and we should confirm that the connection is an actual */ /* port. */ for (thisnn = node->efnode_name; thisnn != NULL; thisnn = thisnn->efnn_next) { char locndn[256]; hierName = thisnn->efnn_hier; if (hierName->hn_parent && !hierName->hn_parent->hn_parent) { /* This is just another kludgy check for a non-port and */ /* will eventually be removed. */ char endc = *(hierName->hn_name + strlen(hierName->hn_name) - 1); if (endc != '#') { defHNsprintf(locndn, thisnn->efnn_hier, ' '); defCheckForBreak(5 + strlen(locndn), defdata); fprintf(f, " ( %s )", locndn); } } } /* TT_SPACE indicates that a layer name must be the next */ /* thing to be written to the DEF file. */ defdata->type = TT_SPACE; defdata->tile = (Tile *)NULL; /* Net geometry (this should be an option!)--- */ /* Use the DBconnect routines to find all geometry */ /* connected to a specific node. This is a */ /* redundant search---we've already done this once */ /* when extracting the circuit. But, because the */ /* DEF file requires a count of nodes up front, we */ /* would have to do it twice anyway. In this case, */ /* we only do it once here, since the results of */ /* the first pass are picked up from the .ext file. */ magictype = DBTechNameType(EFLayerNames[node->efnode_type]); /* Note that the type of the node might be defined by the type */ /* in the subcircuit itself, so we need to search for any type */ /* that might validly connect to it, not just the type itself. */ /* TTMaskSetOnlyType(&tmask, magictype); */ TTMaskZero(&tmask); TTMaskSetMask(&tmask, &DBConnectTbl[magictype]); DBSrConnect(def, &node->efnode_loc, &tmask, DBConnectTbl, &TiPlaneRect, defNetGeometryFunc, (ClientData)defdata); if (defdata->tile == (Tile *)NULL) { /* No route layer? It's possible that something connects to */ /* the port location but doesn't overlap. Try painting the */ /* node type in def and trying again. */ Rect rport; SearchContext scx; int defPortTileFunc(); /* Fwd declaration */ scx.scx_area = node->efnode_loc; scx.scx_use = def->cd_parents; scx.scx_trans = GeoIdentityTransform; DBTreeSrUniqueTiles(&scx, &tmask, 0, defPortTileFunc, (ClientData)&rport); /* Add the residue types to any contact type */ if (DBIsContact(magictype)) { rmask = DBResidueMask(magictype); TTMaskSetMask(&tmask, rmask); TTMaskSetType(&tmask, magictype); } /* Expand the rectangle around the port to overlap any */ /* connecting material. */ rport.r_xbot--; rport.r_ybot--; rport.r_xtop++; rport.r_ytop++; DBSrConnect(def, &rport, &tmask, DBConnectTbl, &TiPlaneRect, defNetGeometryFunc, (ClientData)defdata); } /* Was there a last record pending? If so, write it. */ if (defdata->tile != (Tile *)NULL) { if (defdata->orient != GEO_CENTER) defWriteCoord(defdata, defdata->x, defdata->y, defdata->orient); defdata->outcolumn = 0; } fprintf(f, " ;\n"); return 0; /* Keep going */ } /* Callback function for defNetGeometryFunc(). Determines if any tile */ /* sets the lower bound of the clip line for extending a wire upward */ int defMaxWireFunc(tile, yclip) Tile *tile; int *yclip; { if (BOTTOM(tile) < (*yclip)) *yclip = BOTTOM(tile); return 0; } /* Callback function for defNetGeometryFunc(). Determines if any tile */ /* sets the upper bound of the clip line for extending a wire downward */ int defMinWireFunc(tile, yclip) Tile *tile; int *yclip; { if (TOP(tile) > (*yclip)) *yclip = TOP(tile); return 0; } /* Callback function for DBTreeSrUniqueTiles. When no routed areas */ /* were found, we assume that there was no routing material overlapping */ /* the port. So, we need to find the area of a tile defining the port */ /* so we can look for attaching material. */ int defPortTileFunc(tile, cx) Tile *tile; TreeContext *cx; { SearchContext *scx = cx->tc_scx; Rect *rport = (Rect *)cx->tc_filter->tf_arg; Rect r; TiToRect(tile, &r); GeoTransRect(&scx->scx_trans, &r, rport); /* Diagnostic */ /* TxPrintf("Port tile at (%d %d) to (%d %d)\n", rport->r_xbot, rport->r_ybot, rport->r_xtop, rport->r_ytop); */ return 1; /* No need to check further */ } /* Callback function for writing geometry of the network to */ /* the DEF file. */ int defNetGeometryFunc(tile, plane, defdata) Tile *tile; /* Tile being visited */ int plane; /* Plane of the tile being visited */ DefData *defdata; /* Data passed to this function */ { FILE *f = defdata->f; CellDef *def = defdata->def; float oscale = defdata->scale; TileTypeBitMask *rMask, *r2Mask; TileType rtype, r2type, ttype = TiGetType(tile); Rect r, rorig; unsigned char orient; bool sameroute = FALSE; int routeWidth, w, h, midlinex2, topClip, botClip; float x1, y1, x2, y2, extlen; lefLayer *lefType, *lefl; char *lefName, viaName[128], posstr[24]; HashEntry *he; HashTable *defViaTable = defdata->defViaTable; LefMapping *MagicToLefTable = defdata->MagicToLefTbl; if (ttype == TT_SPACE) return 0; TiToRect(tile, &r); /* Treat contacts here exactly the same way as defCountVias */ if (DBIsContact(ttype)) { Rect r2; Tile *tp; rMask = NULL; if (ttype >= DBNumUserLayers) { /* Stacked contact types need to be broken into their */ /* constituent types. Process only if we are on the home */ /* plane of one of the constituent types. */ rMask = DBResidueMask(ttype); for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++) if (TTMaskHasType(rMask, rtype)) if (DBPlane(rtype) == plane) { ttype = rtype; break; } if (rtype == DBNumUserLayers) return 0; } else if (DBPlane(ttype) != plane) return 0; /* Boundary search on stacked contact types to include any */ /* tile areas belonging to ttype. */ for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) /* Top */ { r2type = TiGetBottomType(tp); if (r2type == ttype) { if (!rMask) return 0; TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (r2type >= DBNumUserLayers) { r2Mask = DBResidueMask(r2type); if (TTMaskHasType(r2Mask, ttype)) return 0; } } for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) /* Left */ { r2type = TiGetRightType(tp); if (r2type == ttype) { if (!rMask) return 0; TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (r2type >= DBNumUserLayers) { r2Mask = DBResidueMask(r2type); if (TTMaskHasType(r2Mask, ttype)) return 0; } } for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) /* Bottom */ { r2type = TiGetTopType(tp); if (r2type == ttype) { if (!rMask) return 0; TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (r2type >= DBNumUserLayers) { r2Mask = DBResidueMask(r2type); if (TTMaskHasType(r2Mask, ttype)) return 0; } } for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) /* Right */ { r2type = TiGetLeftType(tp); if (r2type == ttype) { if (!rMask) return 0; TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (r2type >= DBNumUserLayers) { r2Mask = DBResidueMask(r2type); if (TTMaskHasType(r2Mask, ttype)) return 0; } } } rorig = r; /* Layer names are taken from the LEF database. */ lefName = MagicToLefTable[ttype].lefName; if (lefName == NULL) return 0; /* Do not write types not in LEF definition */ lefType = MagicToLefTable[ttype].lefInfo; orient = GEO_EAST; w = r.r_xtop - r.r_xbot; h = r.r_ytop - r.r_ybot; midlinex2 = (r.r_ytop + r.r_ybot); if (defdata->specialmode != DO_REGULAR) { routeWidth = (h > w) ? w : h; if ((lefType && lefType->lefClass == CLASS_VIA) || (!lefType && DBIsContact(ttype))) orient = GEO_CENTER; } else { routeWidth = 0; if ((lefType && (lefType->lefClass == CLASS_VIA)) || (!lefType && DBIsContact(ttype))) orient = GEO_CENTER; else if (lefType) routeWidth = lefType->info.route.width; if (routeWidth == 0) routeWidth = DRCGetDefaultLayerWidth(ttype); } if (orient != GEO_CENTER) /* not a via type */ { if (h != routeWidth) { if ((w == routeWidth) || ((routeWidth == 0) && (h > w))) { orient = GEO_NORTH; midlinex2 = (r.r_xtop + r.r_xbot); } } /* Warn if the route is not equal to the default route width--- */ /* This means a regular net should have been a special net. */ if ((h != routeWidth) && (w != routeWidth)) { /* Handle slivers. There are two main cases: * (1) Sliver is part of a via extension, so it can be ignored. * (2) Sliver is a route split into several tiles due to geometry * to the left. Expand up and down to include all tiles. */ if (w < routeWidth) return 0; if (h < routeWidth) { /* Check upward */ r = rorig; r.r_ytop = r.r_ybot + routeWidth; r.r_ybot = rorig.r_ytop; topClip = r.r_ytop; DBSrPaintArea(tile, def->cd_planes[plane], &r, &DBNotConnectTbl[ttype], defMaxWireFunc, (ClientData)&topClip); /* Check downward */ r = rorig; r.r_ybot = r.r_ytop - routeWidth; r.r_ytop = rorig.r_ybot; botClip = r.r_ybot; DBSrPaintArea(tile, def->cd_planes[plane], &r, &DBNotConnectTbl[ttype], defMinWireFunc, (ClientData)&botClip); r = rorig; if (topClip > r.r_ytop) r.r_ytop = topClip; if (botClip < r.r_ybot) r.r_ybot = botClip; /* If height is still less that a route width, bail */ h = r.r_ytop - r.r_ybot; if (h < routeWidth) return 0; } /* Diagnostic */ if ((h != routeWidth) && (w != routeWidth)) TxPrintf("Net at (%d, %d) has width %d, default width is %d\n", r.r_xbot, r.r_ybot, (h < w) ? h : w, routeWidth); /* Set orientation based on longest side */ if (h > w) { orient = GEO_NORTH; midlinex2 = (r.r_xtop + r.r_xbot); } else midlinex2 = (r.r_ytop + r.r_ybot); } /* Find the route orientation and centerline endpoint coordinates */ if (orient == GEO_EAST) { y1 = (midlinex2 * oscale) / 2; y2 = y1; x1 = r.r_xbot * oscale; x2 = r.r_xtop * oscale; if (routeWidth == 0) routeWidth = h; extlen = 0; if (defdata->specialmode == DO_REGULAR) { x1 = x1 + (routeWidth / 2 * oscale); x2 = x2 - (routeWidth / 2 * oscale); } } else /* vertical orientation */ { x1 = (midlinex2 * oscale) / 2; x2 = x1; y1 = r.r_ybot * oscale; y2 = r.r_ytop * oscale; if (routeWidth == 0) routeWidth = w; extlen = 0; if (defdata->specialmode == DO_REGULAR) { y1 = y1 + (routeWidth / 2 * oscale); y2 = y2 - (routeWidth / 2 * oscale); } } } else /* Type is a via */ { y1 = y2 = (midlinex2 * oscale) / 2; x1 = x2 = ((r.r_xtop + r.r_xbot) * oscale) / 2; extlen = 0; } /* For contact types, find the residues of the contact */ if (orient == GEO_CENTER) { TileType stype; rtype = r2type = TT_SPACE; rMask = DBResidueMask(ttype); for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++) { if (TTMaskHasType(rMask, stype)) { if ((stype == defdata->type) || (defdata->tile == (Tile *)NULL)) rtype = stype; else r2type = stype; } } } /* If we previously visited a tile, write out its second */ /* coordinate pair, adjusting the position if necessary to */ /* make the wire extensions line up correctly. If they */ /* don't line up, we assume a dogleg route and add */ /* coordinate pairs as necessary to generate the correct */ /* geometry in the DEF output. */ if (defdata->tile) { Rect r2; TiToRect(defdata->tile, &r2); /* Only consider the endpoint of the previous tile at X2,Y2 */ /* And the endpoint of the current tile at X1,Y1 */ if (defdata->orient == GEO_EAST) r2.r_xbot = r2.r_xtop - 1; else if (defdata->orient == GEO_NORTH) r2.r_ybot = r2.r_ytop - 1; if (orient == GEO_EAST) r.r_xtop = r.r_xbot + 1; else if (orient == GEO_NORTH) r.r_ytop = r.r_ybot + 1; /* "sameroute" is true only if rectangles touch in the */ /* direction of the route. */ /* NOTE: We should compute this FIRST and use it to determine */ /* the current route direction! */ /* Another hack---for special nets, don't continue routes that */ /* have different widths, even if they're connected in the */ /* direction of travel. A separate record will be written for */ /* the segment of different width. */ if (GEO_TOUCH(&r, &r2)) { if (defdata->orient == GEO_EAST) { if ((r.r_xbot == r2.r_xtop) || (r.r_xtop == r2.r_xbot)) { sameroute = TRUE; if ((defdata->specialmode != DO_REGULAR) && (r.r_ytop != r2.r_ytop || r.r_ybot != r2.r_ybot)) sameroute = FALSE; } } else if (defdata->orient == GEO_NORTH) { if ((r.r_ybot == r2.r_ytop) || (r.r_ytop == r2.r_ybot)) { sameroute = TRUE; if ((defdata->specialmode != DO_REGULAR) && (r.r_xtop != r2.r_xtop || r.r_xbot != r2.r_xbot)) sameroute = FALSE; } } else sameroute = TRUE; } /* We should NOT continue a route from a via for a special net, */ /* because the spec for this situation is too vaguely defined. */ if (sameroute && (defdata->specialmode != DO_REGULAR) && defdata->orient == GEO_CENTER) sameroute = FALSE; } /* Determine if we need to write a NEW (type) record. We do this */ /* if 1) this is the first tile visited (except that we don't */ /* write "NEW"), 2) the current tile doesn't touch the last tile */ /* visited, or 3) the current type is not equal to the last type. */ if ((!sameroute) || (ttype != defdata->type)) { /* This is not a continuation of the last route. Output */ /* the last route position, and start a NEW record. */ if ((sameroute) && (ttype != defdata->type) && (orient == GEO_CENTER) && (rtype == defdata->type)) { /* Adjust previous route to centerpoint of the via. If the */ /* via is not centered on the route, add segments to create */ /* the proper alignment. */ if ((defdata->orient == GEO_NORTH) && (x1 == defdata->x)) defWriteCoord(defdata, defdata->x, y1, defdata->orient); else if ((defdata->orient == GEO_EAST) && (y1 == defdata->y)) defWriteCoord(defdata, x1, defdata->y, defdata->orient); else if (defdata->orient == GEO_EAST) { defWriteCoord(defdata, x1, defdata->y, defdata->orient); defWriteCoord(defdata, x1, y1, GEO_NORTH); } else if (defdata->orient == GEO_NORTH) { defWriteCoord(defdata, defdata->x, y1, defdata->orient); defWriteCoord(defdata, x1, y1, GEO_EAST); } /* Via type continues route */ sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ttype)), rorig.r_xbot, rorig.r_ybot); he = HashLookOnly(defViaTable, posstr); if (he != NULL) { lefl = (lefLayer *)HashGetValue(he); defCheckForBreak(strlen(lefl->canonName) + 2, defdata); fprintf(f, " %s ", lefl->canonName); } else { TxError("Cannot find via name %s in table!\n", posstr); snprintf(viaName, (size_t)24, "_%.10g_%.10g", ((float)w * oscale), ((float)h * oscale)); defCheckForBreak(strlen(lefName) + strlen(viaName) + 2, defdata); fprintf(f, " %s%s ", lefName, viaName); } } else { /* New route segment. Complete the last route segment. */ if (defdata->tile) { /* Don't write out a segment for a via */ if (defdata->orient != GEO_CENTER) defWriteCoord(defdata, defdata->x - ((defdata->orient == GEO_EAST) ? defdata->extlen : 0), defdata->y - ((defdata->orient == GEO_NORTH) ? defdata->extlen : 0), defdata->orient); fprintf(f, "\n NEW "); defdata->outcolumn = 10; } else { /* First record printed for this node */ fprintf(f, "\n + ROUTED "); defdata->outcolumn = 15; } /* This is the first tile segment visited in the */ /* current type---use GEO_CENTER so that no */ /* coordinate wildcards ("*") get written. */ if (orient == GEO_CENTER) { char *rName; /* Type can be zero (space) if the first tile */ /* encountered is a via. If so, use the 1st */ /* residue of the contact as the route layer */ /* type. */ rName = defGetType((rtype == TT_SPACE) ? r2type : rtype, NULL, LAYER_MAP_VIAS); /* The first layer in a record may not be a via name */ defCheckForBreak(strlen(rName) + 1, defdata); fprintf(f, "%s ", rName); if (defdata->specialmode != DO_REGULAR) defWriteRouteWidth(defdata, routeWidth); defWriteCoord(defdata, x1, y1, GEO_CENTER); sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ttype)), rorig.r_xbot, rorig.r_ybot); he = HashLookOnly(defViaTable, posstr); if (he != NULL) { lefl = (lefLayer *)HashGetValue(he); defCheckForBreak(strlen(lefl->canonName) + 2, defdata); fprintf(f, " %s ", lefl->canonName); } else { TxError("Cannot find via name %s in table!\n", posstr); snprintf(viaName, (size_t)24, "_%.10g_%.10g", ((float)w * oscale), ((float)h * oscale)); defCheckForBreak(strlen(lefName) + strlen(viaName) + 2, defdata); fprintf(f, " %s%s ", lefName, viaName); } } else { defCheckForBreak(strlen(lefName) + 1, defdata); fprintf(f, "%s ", lefName); if (defdata->specialmode != DO_REGULAR) defWriteRouteWidth(defdata, routeWidth); /* defWriteCoord(defdata, x1, y1, GEO_CENTER); */ defWriteCoord(defdata, x1 + ((orient == GEO_EAST) ? extlen : 0), y1 + ((orient == GEO_NORTH) ? extlen : 0), GEO_CENTER); } } } else if (sameroute) { /* Adjust the previous route segment to match the new segment, */ /* and write out the previous route segment record. */ if ((orient == defdata->orient) && (defdata->x != x1) && (defdata->y != x2)) { /* Dogleg---insert extra segment */ defWriteCoord(defdata, defdata->x - ((defdata->orient == GEO_EAST) ? defdata->extlen : 0), defdata->y - ((defdata->orient == GEO_NORTH) ? defdata->extlen : 0), defdata->orient); defWriteCoord(defdata, x1 + ((orient == GEO_EAST) ? extlen : 0), y1 + ((orient == GEO_NORTH) ? extlen : 0), orient); } else { if (defdata->orient == GEO_EAST) { if (((defdata->x + defdata->extlen) == x1) || ((defdata->x - defdata->extlen) == x1)) defWriteCoord(defdata, x1, defdata->y, defdata->orient); else { /* Don't know how to connect the route segments. */ /* End the original route and start a new one. */ defWriteCoord(defdata, defdata->x - ((defdata->orient == GEO_EAST) ? defdata->extlen : 0), defdata->y - ((defdata->orient == GEO_NORTH) ? defdata->extlen : 0), defdata->orient); fprintf(f, "\n NEW %s", lefName); defdata->outcolumn = 10 + strlen(lefName); if (defdata->specialmode != DO_REGULAR) { fprintf(f, " "); defdata->outcolumn++; defWriteRouteWidth(defdata, routeWidth); } defWriteCoord(defdata, x1 + ((orient == GEO_EAST) ? extlen : 0), y1 + ((orient == GEO_NORTH) ? extlen : 0), GEO_CENTER); } } else if (defdata->orient == GEO_NORTH) { if (((defdata->y + defdata->extlen) == y1) || ((defdata->y - defdata->extlen) == y1)) defWriteCoord(defdata, defdata->x, y1, defdata->orient); else { /* Don't know how to connect the route segments. */ /* End the original route and start a new one. */ defWriteCoord(defdata, defdata->x - ((defdata->orient == GEO_EAST) ? defdata->extlen : 0), defdata->y - ((defdata->orient == GEO_NORTH) ? defdata->extlen : 0), defdata->orient); fprintf(f, "\n NEW %s", lefName); if (defdata->specialmode != DO_REGULAR) { fprintf(f, " "); defdata->outcolumn++; defWriteRouteWidth(defdata, routeWidth); } defdata->outcolumn = 10 + strlen(lefName); defWriteCoord(defdata, x1 + ((orient == GEO_EAST) ? extlen : 0), y1 + ((orient == GEO_NORTH) ? extlen : 0), GEO_CENTER); } } else /* last record was a via */ { /* Continuing route from via to other connecting layer type */ /* Bend to meet via center---insert extra segment */ if ((orient == GEO_NORTH) && (x1 != defdata->x)) defWriteCoord(defdata, x1, defdata->y, GEO_EAST); else if ((orient == GEO_EAST) && (y1 != defdata->y)) defWriteCoord(defdata, defdata->x, y1, GEO_NORTH); } } } /* After a contact type, the route coordinates may continue in the */ /* routing type connected by the contact to the type that was */ /* previously seen connected to the contact. */ /* NOTE! The above comment matches the example on page 203 of the */ /* LEF/DEF reference manual. However, it is obvious that it is */ /* logically fallacious. A via can be declared to be multiple */ /* types, and there is no way to know which type continues the */ /* route without it being explicitly stated. Nevertheless, that */ /* is the way it's implemented. . . */ if ((orient == GEO_CENTER) && (rtype != TT_SPACE) && (r2type != TT_SPACE)) defdata->type = r2type; else if (orient == GEO_CENTER) defdata->type = TT_SPACE; else defdata->type = ttype; defdata->x = x2; defdata->y = y2; defdata->extlen = extlen; defdata->tile = tile; defdata->orient = orient; return 0; /* Keep going */ } /* *------------------------------------------------------------ * * defCountVias -- * * First-pass function to count the number of different * vias used, and retain this information for the netlist * output. * * Results: * The total number of via definitions to be written. * * Side Effects: * None. * *------------------------------------------------------------ */ int defCountVias(rootDef, MagicToLefTable, defViaTable, oscale) CellDef *rootDef; LefMapping *MagicToLefTable; HashTable *defViaTable; float oscale; { TileTypeBitMask contactMask, *rmask; TileType ttype, stype; int pNum; CViaData cviadata; int defCountViaFunc(); cviadata.scale = oscale; cviadata.total = 0; cviadata.MagicToLefTbl = MagicToLefTable; cviadata.defViaTable = defViaTable; cviadata.def = rootDef; for (pNum = PL_SELECTBASE; pNum < DBNumPlanes; pNum++) { /* Only search for contacts that are on their *home* plane */ TTMaskZero(&contactMask); for (ttype = TT_TECHDEPBASE; ttype < DBNumUserLayers; ttype++) if (DBIsContact(ttype) && TTMaskHasType(&DBPlaneTypes[pNum], ttype)) TTMaskSetType(&contactMask, ttype); /* Also search all stacked types whose residue contact types */ /* are in the mask just generated. */ for (ttype = DBNumUserLayers; ttype < DBNumTypes; ttype++) { if (!DBIsContact(ttype)) continue; rmask = DBResidueMask(ttype); for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++) if (TTMaskHasType(rmask, stype)) { TTMaskSetType(&contactMask, ttype); break; } } cviadata.mask = &contactMask; DBSrPaintArea((Tile *)NULL, rootDef->cd_planes[pNum], &TiPlaneRect, &contactMask, defCountViaFunc, (ClientData)&cviadata); } return cviadata.total; } /* Simple callback function used by defCountViaFunc */ int defCheckFunc(tile) Tile *tile; { return 1; } /* Callback function used by defCountVias */ int defCountViaFunc(tile, cviadata) Tile *tile; CViaData *cviadata; { TileType ttype = TiGetType(tile), ctype, rtype; TileTypeBitMask *rmask, *rmask2; Tile *tp; char *lname, vname[100], *vp, posstr[24]; Rect r, r2, rorig; int w, h, offx, offy, sdist, lorient, horient; int ldist, hdist, sldist, shdist, pNum; TileType ltype, htype; float oscale = cviadata->scale; lefLayer *lefl; LinkedRect *newlr; CellDef *def = cviadata->def; HashEntry *he; HashTable *defViaTable = cviadata->defViaTable; LefMapping *MagicToLefTable = cviadata->MagicToLefTbl; /* Techfiles are allowed not to declare a LEF entry, in which */ /* case we would need to initialize the hash table. */ if (LefInfo.ht_table == (HashEntry **) NULL) LefTechInit(); /* Find the canonical type */ if (ttype >= DBNumUserLayers) { rmask = DBResidueMask(ttype); for (ctype = TT_TECHDEPBASE; ctype < DBNumUserLayers; ctype++) if (TTMaskHasType(rmask, ctype)) break; if (ctype == DBNumUserLayers) return 1; /* Error condition */ } else { rmask = NULL; ctype = ttype; } /* Generate a via name from the layer name and tile size */ lname = MagicToLefTable[ctype].lefName; if (lname == NULL) return 0; /* Do not output undefined LEF layers */ TiToRect(tile, &r); /* Boundary search. WARNING: This code is quite naive. The */ /* assumption is that all contacts are rectangular, and therefore */ /* any contact area consisting of multiple tiles must be an amalgam */ /* of regular and/or stacked types. This whole thing should be */ /* replaced by calls to generate layers via the CIF/Calma code. */ /* Top */ for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) { rtype = TiGetBottomType(tp); if (rtype == ctype) { if (!rmask) return 0; /* ignore tile but continue search */ TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (rtype >= DBNumUserLayers) { rmask2 = DBResidueMask(rtype); if (TTMaskHasType(rmask2, ctype)) return 0; } } /* Left */ for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) { rtype = TiGetRightType(tp); if (rtype == ctype) { if (!rmask) return 0; /* ignore tile but continue search */ TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (rtype >= DBNumUserLayers) { rmask2 = DBResidueMask(rtype); if (TTMaskHasType(rmask2, ctype)) return 0; } } /* Bottom */ for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) { rtype = TiGetTopType(tp); if (rtype == ctype) { if (!rmask) return 0; /* ignore tile but continue search */ TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (rtype >= DBNumUserLayers) { rmask2 = DBResidueMask(rtype); if (TTMaskHasType(rmask2, ctype)) return 0; } } /* Right */ for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) { rtype = TiGetLeftType(tp); if (rtype == ctype) { if (!rmask) return 0; /* ignore tile but continue search */ TiToRect(tp, &r2); GeoInclude(&r2, &r); } else if (rtype >= DBNumUserLayers) { rmask2 = DBResidueMask(rtype); if (TTMaskHasType(rmask2, ctype)) return 0; } } /* All values for the via rect are in 1/2 lambda to account */ /* for a centerpoint not on the internal grid. */ rorig = r; r.r_xbot <<= 1; r.r_xtop <<= 1; r.r_ybot <<= 1; r.r_ytop <<= 1; w = r.r_xtop - r.r_xbot; h = r.r_ytop - r.r_ybot; offx = (w >> 1); offy = (h >> 1); /* Center the via area on the origin */ r.r_xbot = -offx; r.r_ybot = -offy; r.r_xtop = -offx + w; r.r_ytop = -offy + h; /* If the via type has directional surround rules, then determine */ /* the orientation of the lower and upper metal layers and add a */ /* suffix to the via name. */ rmask = DBResidueMask(ctype); ldist = hdist = 0; ltype = htype = TT_SPACE; for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++) if (TTMaskHasType(rmask, rtype)) { sdist = DRCGetDefaultLayerSurround(ctype, rtype); if (ltype == TT_SPACE) sldist = sdist; else shdist = sdist; sdist = DRCGetDirectionalLayerSurround(ctype, rtype); if (ltype == TT_SPACE) { ldist = sdist; ltype = rtype; } else { hdist = sdist; htype = rtype; break; } } lorient = horient = 0; if (ldist > 0) { r2.r_ybot = rorig.r_ybot - sldist; r2.r_ytop = rorig.r_ytop + sldist; r2.r_xbot = rorig.r_xbot - ldist + sldist; r2.r_xtop = rorig.r_xtop + ldist + sldist; pNum = DBPlane(ltype); lorient = DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum], &r2, &DBNotConnectTbl[ltype], defCheckFunc, (ClientData)NULL); } if (hdist > 0) { r2.r_ybot = rorig.r_ybot - shdist; r2.r_ytop = rorig.r_ytop + shdist; r2.r_xbot = rorig.r_xbot - hdist + shdist; r2.r_xtop = rorig.r_xtop + hdist + shdist; pNum = DBPlane(htype); horient = DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum], &r2, &DBNotConnectTbl[htype], defCheckFunc, (ClientData)NULL); } if ((ldist > 0) || (hdist > 0)) { sprintf(vname, "%s_%.10g_%.10g_%c%c", lname, ((float)offx * oscale), ((float)offy * oscale), (ldist == 0) ? 'x' : (lorient == 0) ? 'h' : 'v', (hdist == 0) ? 'x' : (horient == 0) ? 'h' : 'v'); } else sprintf(vname, "%s_%.10g_%.10g", lname, ((float)offx * oscale), ((float)offy * oscale)); he = HashFind(&LefInfo, vname); lefl = (lefLayer *)HashGetValue(he); if (lefl == NULL) { cviadata->total++; /* Increment the count of uses */ lefl = (lefLayer *)mallocMagic(sizeof(lefLayer)); lefl->type = ctype; lefl->obsType = -1; lefl->lefClass = CLASS_VIA; lefl->info.via.area = r; lefl->info.via.cell = (CellDef *)NULL; lefl->info.via.lr = (LinkedRect *)NULL; lefl->refCnt = 0; /* These entries will be removed after writing */ HashSetValue(he, lefl); lefl->canonName = (char *)he->h_key.h_name; if ((sldist > 0) || (ldist > 0)) { newlr = (LinkedRect *)mallocMagic(sizeof(LinkedRect)); newlr->r_next = lefl->info.via.lr; lefl->info.via.lr = newlr; newlr->r_type = ltype; r2.r_xbot = r.r_xbot - 2 * sldist; r2.r_xtop = r.r_xtop + 2 * sldist; r2.r_ybot = r.r_ybot - 2 * sldist; r2.r_ytop = r.r_ytop + 2 * sldist; if (ldist > 0) { if (lorient == 0) { r2.r_xbot -= 2 * ldist; r2.r_xtop += 2 * ldist; } else { r2.r_ybot -= 2 * ldist; r2.r_ytop += 2 * ldist; } } newlr->r_r = r2; } if ((shdist > 0) || (hdist > 0)) { newlr = (LinkedRect *)mallocMagic(sizeof(LinkedRect)); newlr->r_next = lefl->info.via.lr; lefl->info.via.lr = newlr; newlr->r_type = htype; r2.r_xbot = r.r_xbot - 2 * shdist; r2.r_xtop = r.r_xtop + 2 * shdist; r2.r_ybot = r.r_ybot - 2 * shdist; r2.r_ytop = r.r_ytop + 2 * shdist; if (hdist > 0) { if (horient == 0) { r2.r_xbot -= 2 * hdist; r2.r_xtop += 2 * hdist; } else { r2.r_ybot -= 2 * hdist; r2.r_ytop += 2 * hdist; } } newlr->r_r = r2; } } /* Record this tile position in the contact hash table */ sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ctype)), rorig.r_xbot, rorig.r_ybot); he = HashFind(defViaTable, posstr); HashSetValue(he, lefl); /* TxPrintf("Via name \"%s\" hashed as \"%s\"\n", lefl->canonName, posstr); */ return 0; /* Keep the search going */ } /* *------------------------------------------------------------ * * defGetType -- * * Retrieve the LEF/DEF name of a magic layer from the * LefInfo hash table. * * Results: * The "official" LEF/DEF layer name of the magic type. * * Side Effects: * If "lefptr" is non-NULL, it is filled with a pointer * to the appropriate lefLayer entry, or NULL if there * is no corresponding entry. *------------------------------------------------------------ */ char * defGetType(ttype, lefptr, do_vias) TileType ttype; lefLayer **lefptr; bool do_vias; { HashSearch hs; HashEntry *he; lefLayer *lefl; int contact = DBIsContact(ttype) ? CLASS_VIA : CLASS_ROUTE; /* Pick up information from the original LefInfo hash table */ /* entries created during read-in of the tech file. */ if (LefInfo.ht_table != (HashEntry **) NULL) { HashStartSearch(&hs); while (he = HashNext(&LefInfo, &hs)) { lefl = (lefLayer *)HashGetValue(he); if (lefl && (do_vias == FALSE) && (contact == CLASS_VIA) && (lefl->info.via.lr != NULL)) continue; /* Skip VIA definitions if do_vias is FALSE */ if (lefl && ((contact == lefl->lefClass) || ((contact == CLASS_ROUTE) && (lefl->lefClass == CLASS_MASTER)))) if ((lefl->type == ttype) || (lefl->obsType == ttype)) { if (lefptr) *lefptr = lefl; return lefl->canonName; } } } /* If we got here, there is no entry; return NULL. */ if (lefptr) *lefptr = (lefLayer *)NULL; return NULL; } /* *------------------------------------------------------------ * * defWriteVias -- * * Output the VIAS section of a DEF file. We equate magic * contact areas with DEF "VIAS". A separate via entry is * generated for each unique geometry. The exact output * is determined from the CIF output rules. * * Results: * None. * * Side Effects: * Output written to the DEF output file. * *------------------------------------------------------------ */ void defWriteVias(f, rootDef, oscale, lefMagicToLefLayer) FILE *f; /* File to write to */ CellDef *rootDef; /* Cell definition to use */ float oscale; /* Output scale factor */ LefMapping *lefMagicToLefLayer; { HashSearch hs; HashEntry *he; lefLayer *lefl; TileTypeBitMask *rMask; TileType ttype; Rect *r; LinkedRect *lr; /* Pick up information from the LefInfo hash table */ /* created by fucntion defCountVias() */ if (LefInfo.ht_table != (HashEntry **) NULL) { HashStartSearch(&hs); while (he = HashNext(&LefInfo, &hs)) { int size, sep, border; char *us1, *us2; lefl = (lefLayer *)HashGetValue(he); if (!lefl) continue; /* Only count the generated vias of the type name_sizex_sizey */ if ((us1 = strchr(lefl->canonName, '_')) == NULL || (us2 = strrchr(lefl->canonName, '_')) == us1) continue; if (lefl->lefClass == CLASS_VIA) { fprintf(f, " - %s", (char *)lefl->canonName); /* Generate squares for the area as determined */ /* by the cifoutput section of the tech file */ rMask = DBResidueMask(lefl->type); for (ttype = TT_TECHDEPBASE; ttype < DBNumUserLayers; ttype++) if (TTMaskHasType(rMask, ttype)) { r = &lefl->info.via.area; /* If an lr entry was made, then it includes */ /* any required surround distance, so use that */ /* rectangle instead of the via area. */ for (lr =lefl->info.via.lr; lr; lr = lr->r_next) if (lr->r_type == ttype) r = &lr->r_r; fprintf(f, "\n + RECT %s ( %.10g %.10g ) ( %.10g %.10g )", lefMagicToLefLayer[ttype].lefName, (float)(r->r_xbot) * oscale / 2, (float)(r->r_ybot) * oscale / 2, (float)(r->r_xtop) * oscale / 2, (float)(r->r_ytop) * oscale / 2); } /* Handle the contact cuts. */ if (CIFGetContactSize(lefl->type, &size, &sep, &border)) { int i, j, nAc, nUp, pitch, left; Rect square, rect = lefl->info.via.area, *r; r = ▭ /* Scale the area to CIF units */ r->r_xbot *= oscale; r->r_ybot *= oscale; r->r_xtop *= oscale; r->r_ytop *= oscale; r->r_xbot /= 2; r->r_ybot /= 2; r->r_xtop /= 2; r->r_ytop /= 2; pitch = size + sep; nAc = (r->r_xtop - r->r_xbot + sep - (2 * border)) / pitch; if (nAc == 0) { left = (r->r_xbot + r->r_xtop - size) / 2; nAc = 1; if (left < r->r_xbot) { TxError("Warning: via size is %d but area width is %d!\n", size, (r->r_xtop - r->r_xbot)); } } else left = (r->r_xbot + r->r_xtop + sep - (nAc * pitch)) / 2; nUp = (r->r_ytop - r->r_ybot + sep - (2 * border)) / pitch; if (nUp == 0) { square.r_ybot = (r->r_ybot + r->r_ytop - size) / 2; nUp = 1; if (square.r_ybot >= r->r_ybot) { TxError("Warning: via size is %d but area height is %d!\n", size, (r->r_ytop - r->r_ybot)); } } else square.r_ybot = (r->r_ybot + r->r_ytop + sep - (nUp * pitch)) / 2; for (i = 0; i < nUp; i++) { square.r_ytop = square.r_ybot + size; square.r_xbot = left; for (j = 0; j < nAc; j++) { square.r_xtop = square.r_xbot + size; fprintf(f, "\n + RECT %s ( %.10g %.10g ) " "( %.10g %.10g )", lefMagicToLefLayer[lefl->type].lefName, (float)(square.r_xbot), (float)(square.r_ybot), (float)(square.r_xtop), (float)(square.r_ytop)); square.r_xbot += pitch; } square.r_ybot += pitch; } } else /* If we can't find the CIF/GDS parameters for cut */ /* generation, then output a single rectangle the */ /* size of the contact tile. */ { fprintf(f, "\n + RECT %s ( %.10g %.10g ) ( %.10g %.10g )", lefMagicToLefLayer[lefl->type].lefName, (float)(lefl->info.via.area.r_xbot) * oscale / 2, (float)(lefl->info.via.area.r_ybot) * oscale / 2, (float)(lefl->info.via.area.r_xtop) * oscale / 2, (float)(lefl->info.via.area.r_ytop) * oscale / 2); } fprintf(f, " ;\n"); } } } } /* *------------------------------------------------------------ * * defCountComponents -- * * First-pass function to count the number of cell * uses (components) to be written to the DEF output * file. * * Results: * The total number of uses to be written. * * Side Effects: * None. * *------------------------------------------------------------ */ int defCountComponents(rootDef) CellDef *rootDef; { pointertype total; int defCountCompFunc(); TxPrintf("Diagnostic: Finding all components of cell %s\n", rootDef->cd_name); total = 0; DBCellEnum(rootDef, defCountCompFunc, (ClientData)&total); return (int)total; } /* Callback function used by defCountComponents */ int defCountCompFunc(cellUse, total) CellUse *cellUse; pointertype *total; { /* Ignore any cellUse that does not have an identifier string. */ if (cellUse->cu_id == NULL) return 0; /* Make sure that arrays are counted correctly */ int sx = cellUse->cu_xhi - cellUse->cu_xlo + 1; int sy = cellUse->cu_yhi - cellUse->cu_ylo + 1; // TxPrintf("Diagnostic: cell %s %d %d\n", cellUse->cu_id, sx, sy); ASSERT(sx >= 0 && sy >= 0, "Valid array"); (*total) += sx * sy; /* Increment the count of uses */ return 0; /* Keep the search going */ } /* *------------------------------------------------------------ * * defCountPins -- * * First-pass function to count the number of pins * to be written to the DEF output file. * * Results: * The total number of pins to be written. * * Side Effects: * None. * *------------------------------------------------------------ */ int defCountPins(rootDef) CellDef *rootDef; { int total; Label *lab; TxPrintf("Diagnostic: Finding all pins of cell %s\n", rootDef->cd_name); total = 0; for (lab = rootDef->cd_labels; lab; lab = lab->lab_next) if (lab->lab_flags & PORT_DIR_MASK) total++; return total; } /* *------------------------------------------------------------ * * defWritePins -- * * Output the PINS section of the DEF file. This * is a listing of all ports, their placement, and * name. * * Results: * None. * * Side Effects: * Output to the DEF file. * *------------------------------------------------------------ */ void defWritePins(f, rootDef, lefMagicToLefLayer, oscale) FILE *f; /* File to write to */ CellDef *rootDef; /* Cell definition to use */ LefMapping *lefMagicToLefLayer; /* Magic to LEF layer name mapping */ float oscale; /* Output scale factor */ { Label *lab; int lwidth, lheight; int dcenterx, dcentery; for (lab = rootDef->cd_labels; lab; lab = lab->lab_next) { if (lab->lab_flags & PORT_DIR_MASK) { fprintf(f, " - %s + NET %s\n", lab->lab_text, lab->lab_text); if (lab->lab_flags & PORT_CLASS_MASK) { fprintf(f, " + DIRECTION "); switch (lab->lab_flags & PORT_CLASS_MASK) { case PORT_CLASS_INPUT: fprintf(f, "INPUT"); break; case PORT_CLASS_OUTPUT: fprintf(f, "OUTPUT"); break; case PORT_CLASS_TRISTATE: case PORT_CLASS_BIDIRECTIONAL: fprintf(f, "INOUT"); break; case PORT_CLASS_FEEDTHROUGH: fprintf(f, "FEEDTHRU"); break; } fprintf(f, "\n"); } if (lab->lab_flags & PORT_USE_MASK) { fprintf(f, " + USE "); switch (lab->lab_flags & PORT_USE_MASK) { case PORT_USE_SIGNAL: fprintf(f, "SIGNAL"); break; case PORT_USE_ANALOG: fprintf(f, "ANALOG"); break; case PORT_USE_POWER: fprintf(f, "POWER"); break; case PORT_USE_GROUND: fprintf(f, "GROUND"); break; case PORT_USE_CLOCK: fprintf(f, "CLOCK"); break; } fprintf(f, "\n"); } lwidth = lab->lab_rect.r_xtop - lab->lab_rect.r_xbot; lheight = lab->lab_rect.r_ytop - lab->lab_rect.r_ybot; dcenterx = lab->lab_rect.r_xtop + lab->lab_rect.r_xbot; dcentery = lab->lab_rect.r_ytop + lab->lab_rect.r_ybot; fprintf(f, " + PORT\n"); if (lefMagicToLefLayer[lab->lab_type].lefName == NULL) TxError("No LEF layer corresponding to layer %s of pin \"%s\".\n", lab->lab_text, DBTypeLongNameTbl[lab->lab_type]); else fprintf(f, " + LAYER %s ( %.10g %.10g ) ( %.10g %.10g )", lefMagicToLefLayer[lab->lab_type].lefName, oscale * (float)(-lwidth) / 2.0, oscale * (float)(-lheight) / 2.0, oscale * (float)lwidth / 2.0, oscale * (float)lheight / 2.0); fprintf(f, " + PLACED ( %.10g %.10g ) N ;\n", oscale * (float)dcenterx / 2.0, oscale * (float)dcentery / 2.0); } } } /* *------------------------------------------------------------ * * defWriteNets -- * * Output the NETS section of a DEF file. We make use of * the connectivity search routines used by "getnode" to * determine unique notes and assign a net name to each. * Then, we generate the geometry output for each NET * entry. * * Results: * None. * * Side Effects: * Output written to the DEF output file. * *------------------------------------------------------------ */ void defWriteBlockages(f, rootDef, oscale, MagicToLefTable) FILE *f; /* File to write to */ CellDef *rootDef; /* Cell definition to use */ float oscale; /* Output scale factor */ LefMapping *MagicToLefTable; /* Magic to LEF layer mapping */ { DefObsData defobsdata; lefLayer *lefl; int i, numblocks, nonempty; LinkedRect *lr; HashSearch hs; HashEntry *he; int defblockageVisit(); defobsdata.def = rootDef; defobsdata.nlayers = 0; /* Blockages are done by layer. Create one blockage per route */ /* layer, and ignore vias. */ numblocks = 0; if (LefInfo.ht_table != (HashEntry **) NULL) { HashStartSearch(&hs); while (he = HashNext(&LefInfo, &hs)) { lefl = (lefLayer *)HashGetValue(he); if ((lefl->lefClass == CLASS_ROUTE) || (lefl->lefClass == CLASS_VIA)) numblocks++; } defobsdata.nlayers = numblocks; defobsdata.blockMasks = (TileTypeBitMask *)mallocMagic(numblocks * sizeof(TileTypeBitMask)); defobsdata.blockData = (LinkedRect **)mallocMagic(numblocks * sizeof(LinkedRect *)); defobsdata.baseNames = (char **)mallocMagic(numblocks * sizeof(char *)); if (numblocks > 0) { numblocks = 0; HashStartSearch(&hs); while (he = HashNext(&LefInfo, &hs)) { lefl = (lefLayer *)HashGetValue(he); if ((lefl->lefClass == CLASS_ROUTE) || (lefl->lefClass == CLASS_VIA)) { char *llayer; if (lefl->lefClass == CLASS_ROUTE) llayer = lefl->canonName; else llayer = MagicToLefTable[lefl->type].lefName; defobsdata.baseNames[numblocks] = llayer; TTMaskSetOnlyType(&defobsdata.blockMasks[numblocks], lefl->type); if (lefl->obsType != -1) TTMaskSetType(&defobsdata.blockMasks[numblocks], lefl->obsType); defobsdata.blockData[numblocks] = NULL; numblocks++; } } } } if (numblocks > 0) EFVisitNodes(defblockageVisit, (ClientData)&defobsdata); /* Quick check for presence of data to write */ nonempty = 0; for (i = 0; i < numblocks; i++) if (defobsdata.blockData[i] != NULL) nonempty++; if (nonempty > 0) { fprintf(f, "BLOCKAGES %d ;\n", nonempty); for (i = 0; i < numblocks; i++) { if (defobsdata.blockData[i] == NULL) continue; fprintf(f, " - LAYER %s\n", defobsdata.baseNames[i]); for (lr = defobsdata.blockData[i]; lr; lr = lr->r_next) { fprintf(f, " RECT %.10g %.10g %.10g %.10g\n", (float)(lr->r_r.r_xbot * oscale), (float)(lr->r_r.r_ybot * oscale), (float)(lr->r_r.r_xtop * oscale), (float)(lr->r_r.r_ytop * oscale)); freeMagic(lr); } fprintf(f, ";\n"); } fprintf(f, "END BLOCKAGES\n\n"); } freeMagic(defobsdata.blockData); freeMagic(defobsdata.blockMasks); freeMagic(defobsdata.baseNames); } int defblockageVisit(node, res, cap, defobsdata) EFNode *node; int res; EFCapValue cap; DefObsData *defobsdata; { CellDef *def = defobsdata->def; TileType magictype; TileTypeBitMask tmask; int defBlockageGeometryFunc(); /* Forward declaration */ /* For regular nets, only count those nodes having port */ /* connections. For special nets, only count those nodes */ /* that were marked with the EF_SPECIAL flag while counting */ /* nets. */ if ((node->efnode_flags & EF_PORT) || (node->efnode_flags & EF_SPECIAL)) return 0; magictype = DBTechNameType(EFLayerNames[node->efnode_type]); TTMaskZero(&tmask); TTMaskSetMask(&tmask, &DBConnectTbl[magictype]); /* Avoid attempting to extract an implicit substrate into DEF */ if (node->efnode_type == TT_SPACE) return 0; DBSrConnect(def, &node->efnode_loc, &tmask, DBConnectTbl, &TiPlaneRect, defBlockageGeometryFunc, (ClientData)defobsdata); return 0; } /* Callback function for generating a linked list of blockage geometry */ /* for a net. */ int defBlockageGeometryFunc(tile, plane, defobsdata) Tile *tile; /* Tile being visited */ int plane; /* Plane of the tile being visited */ DefObsData *defobsdata; /* Data passed to this function */ { TileType ttype = TiGetTypeExact(tile); TileType loctype; Rect r; LinkedRect *lr; int i; if (IsSplit(tile)) loctype = (ttype & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile); else loctype = ttype; if (loctype == TT_SPACE) return 0; /* Dissolve stacked contacts */ if (loctype >= DBNumUserLayers) { TileTypeBitMask *rMask; TileType rtype; rMask = DBResidueMask(loctype); for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++) if (TTMaskHasType(rMask, rtype)) if (DBPlane(rtype) == plane) { loctype = rtype; break; } if (rtype == DBNumUserLayers) return 0; } for (i = 0; i < defobsdata->nlayers; i++) if (TTMaskHasType(&(defobsdata->blockMasks[i]), loctype)) break; if (i < defobsdata->nlayers) { TiToRect(tile, &r); lr = (LinkedRect *)mallocMagic(sizeof(LinkedRect)); lr->r_next = defobsdata->blockData[i]; lr->r_type = loctype; lr->r_r = r; defobsdata->blockData[i] = lr; } return 0; } /* *------------------------------------------------------------ * * defWriteComponents -- * * Output the COMPONENTS section of the DEF file. This * is a listing of all cell uses, their placement, and * orientation. * * Results: * None. * * Side Effects: * Output to the DEF file. * *------------------------------------------------------------ */ void defWriteComponents(f, rootDef, oscale) FILE *f; /* File to write to */ CellDef *rootDef; /* Cell definition to use */ float oscale; /* Output scale factor */ { DefData defdata; int defComponentFunc(); /* Forward declaration */ defdata.f = f; defdata.scale = oscale; DBCellEnum(rootDef, defComponentFunc, (ClientData)&defdata); } /* Callback function used by defWriteComponents for array members */ int arrayDefFunc(use, transform, x, y, defdata) CellUse *use; /* CellUse for array element */ Transform *transform; /* Transform from use to parent */ int x, y; /* Indices of element */ DefData *defdata; { int sx = use->cu_xhi - use->cu_xlo; int sy = use->cu_yhi - use->cu_ylo; char idx[32]; Rect box, rect, *r, bbrect, defrect; int xoff, yoff; idx[0] = 0; if (sy) sprintf(idx, "%d%s", y, sx ? "," : ""); if (sx) sprintf(idx + strlen(idx), "%d", x); r = &use->cu_def->cd_bbox; xoff = yoff = 0; if (use->cu_def->cd_flags & CDFIXEDBBOX) { char *propval; bool found; propval = DBPropGet(use->cu_def, "FIXED_BBOX", &found); if (found) { if (sscanf(propval, "%d %d %d %d", &rect.r_xbot, &rect.r_ybot, &rect.r_xtop, &rect.r_ytop) == 4) r = ▭ } } GeoTransRect(transform, r, &box); fprintf(defdata->f, " - %s[%s] %s\n + PLACED ( %.10g %.10g ) %s ;\n", use->cu_id, idx, use->cu_def->cd_name, (float)(box.r_xbot) * defdata->scale, (float)(box.r_ybot) * defdata->scale, defTransPos(&use->cu_transform)); return 0; } /* Callback function used by defWriteComponents */ int defComponentFunc(cellUse, defdata) CellUse *cellUse; DefData *defdata; { FILE *f = defdata->f; float oscale = defdata->scale; char *nameroot; Rect *r, rect, bbrect, defrect; int xoff, yoff; /* Ignore any cellUse that does not have an identifier string. */ if (cellUse->cu_id == NULL) return 0; if (cellUse->cu_xlo != cellUse->cu_xhi || cellUse->cu_ylo != cellUse->cu_yhi) { /* expand the array */ DBArraySr(cellUse, &cellUse->cu_bbox, arrayDefFunc, defdata); return 0; } /* In case the cd_name contains a path component (it's not supposed to), */ /* remove it. */ nameroot = strrchr(cellUse->cu_def->cd_name, '/'); if (nameroot != NULL) nameroot++; else nameroot = cellUse->cu_def->cd_name; r = &cellUse->cu_def->cd_bbox; xoff = yoff = 0; if (cellUse->cu_def->cd_flags & CDFIXEDBBOX) { char *propval; bool found; propval = DBPropGet(cellUse->cu_def, "FIXED_BBOX", &found); if (found) { if (sscanf(propval, "%d %d %d %d", &rect.r_xbot, &rect.r_ybot, &rect.r_xtop, &rect.r_ytop) == 4) { r = ▭ GeoTransRect(&cellUse->cu_transform, &rect, &bbrect); GeoTransRect(&cellUse->cu_transform, &cellUse->cu_def->cd_bbox, &defrect); xoff = bbrect.r_xbot - defrect.r_xbot; yoff = bbrect.r_ybot - defrect.r_ybot; } } } fprintf(f, " - %s %s\n + PLACED ( %.10g %.10g ) %s ;\n", cellUse->cu_id, nameroot, (float)(cellUse->cu_bbox.r_xbot - r->r_ll.p_x + xoff) * oscale, (float)(cellUse->cu_bbox.r_ybot - r->r_ll.p_y + yoff) * oscale, defTransPos(&cellUse->cu_transform)); return 0; /* Keep the search going */ } /* *------------------------------------------------------------ * * defMakeInverseLayerMap --- * * Generate an array of pointers to lefLayer structures for each * magic type so we can do a quick lookup when searching over tiles. * * Results: * Pointer to the inverse layer map. * * Side effects: * Memory is allocated for the map structure array. * *------------------------------------------------------------ */ LefMapping * defMakeInverseLayerMap(do_vias) bool do_vias; { LefMapping *lefMagicToLefLayer; lefLayer *lefl; TileType i; char *lefname; lefMagicToLefLayer = (LefMapping *)mallocMagic(DBNumTypes * sizeof(LefMapping)); memset(lefMagicToLefLayer, 0, sizeof(LefMapping) * TT_TECHDEPBASE); for (i = TT_TECHDEPBASE; i < DBNumTypes; i++) { lefname = defGetType(i, &lefl, do_vias); lefMagicToLefLayer[i].lefName = lefname; lefMagicToLefLayer[i].lefInfo = lefl; } return lefMagicToLefLayer; } /* *------------------------------------------------------------ * * DefWriteAll -- * * Results: * * Side Effects: * *------------------------------------------------------------ */ /* To do: routine DefWriteAll(). */ /* DEF does not handle hierarchy. However, we should assume that we */ /* want to write out a DEF file for each cell in the hierarchy. But, */ /* we should stop at any cells defining ports, assuming that they are */ /* standard cells and not part of the routing. */ /* Maybe there should be a method for specifying that any hierarchy */ /* should be flattened when writing to the DEF file output. */ /* *------------------------------------------------------------ * * DefWriteCell -- * * Write DEF-format output for the indicated cell. * * Results: * None. * * Side effects: * Writes a single .def file to disk. * *------------------------------------------------------------ */ void DefWriteCell(def, outName, allSpecial, units) CellDef *def; /* Cell being written */ char *outName; /* Name of output file, or NULL. */ bool allSpecial; /* Treat all nets as SPECIALNETS? */ int units; /* Force units to this value (default 1000) */ { char *filename; FILE *f; NetCount nets; int total; float scale; HashTable defViaTable; LefMapping *lefMagicToLefLayer; int i; lefLayer *lefl; HashEntry *he; /* Note that "1" corresponds to "1000" in the header UNITS line, */ /* or units of nanometers. 10 = centimicrons, 1000 = microns. */ scale = CIFGetOutputScale(1000 / units); if (!strcmp(def->cd_name, UNNAMED)) { TxError("Please name the cell before generating DEF.\n"); return; } f = lefFileOpen(def, outName, ".def", "w", &filename); TxPrintf("Generating DEF output %s for cell %s:\n", filename, def->cd_name); if (f == NULL) { #ifdef MAGIC_WRAPPER TxError("Cannot open output file %s (%s).\n", filename, strerror(errno)); #else TxError("Cannot open output file: "); perror(filename); #endif return; } defWriteHeader(def, f, scale, units); HashInit(&defViaTable, 256, HT_STRINGKEYS); lefMagicToLefLayer = defMakeInverseLayerMap(LAYER_MAP_VIAS); /* Vias---magic contact areas are reported as vias. */ total = defCountVias(def, lefMagicToLefLayer, &defViaTable, scale); fprintf(f, "VIAS %d ;\n", total); if (total > 0) defWriteVias(f, def, scale, lefMagicToLefLayer); fprintf(f, "END VIAS\n\n"); /* Components (i.e., cell uses) */ total = defCountComponents(def); fprintf(f, "COMPONENTS %d ;\n", total); if (total > 0) defWriteComponents(f, def, scale); fprintf(f, "END COMPONENTS\n\n"); /* Pins---assume no pins (for now) */ total = defCountPins(def); fprintf(f, "PINS %d ;\n", total); if (total > 0) defWritePins(f, def, lefMagicToLefLayer, scale); fprintf(f, "END PINS\n\n"); /* Count the number of nets and "special" nets */ nets = defCountNets(def, allSpecial); /* Nondefault rules */ #if 0 /* Not yet implemented */ if (nets.numrules > 0) { NetRule *nrule; fprintf(f, "NONDEFAULTRULES %d ;\n", nets.numrules); for (nrule = nets.rules; nrule; nrule = nrule->next) { fprintf(f, " - %s\n", nrule->name); fprintf(f, " + LAYER %s WIDTH %.10g\n", nrule->rule->name, ((float)nrule->rule->info.route.width * scale)); } fprintf(f, "END NONDEFAULTRULES\n\n"); } #endif /* "Special" nets---nets matching $GND, $VDD, or $globals(*) */ if (nets.special > 0) { fprintf(f, "SPECIALNETS %d ;\n", nets.special); defWriteNets(f, def, scale, lefMagicToLefLayer, &defViaTable, (allSpecial) ? ALL_SPECIAL : DO_SPECIAL); fprintf(f, "END SPECIALNETS\n\n"); } /* "Regular" nets */ if (nets.regular > 0) { fprintf(f, "NETS %d ;\n", nets.regular); defWriteNets(f, def, scale, lefMagicToLefLayer, &defViaTable, DO_REGULAR); fprintf(f, "END NETS\n\n"); } /* Blockages */ if (nets.blockages > 0) defWriteBlockages(f, def, scale, lefMagicToLefLayer); fprintf(f, "END DESIGN\n\n"); fclose(f); if (nets.has_nets) { EFFlatDone(NULL); EFDone(NULL); } freeMagic((char *)lefMagicToLefLayer); HashKill(&defViaTable); lefRemoveGeneratedVias(); }