/* * plotGremlin.c -- * * This file contains procedures that generate Gremlin-format files * to describe a section of layout. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotGremln.c,v 1.2 2008/06/01 18:37:45 tim Exp $"; #endif /* not lint */ #include #include #include #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/utils.h" #include "windows/windows.h" #include "commands/commands.h" #include "dbwind/dbwind.h" #include "textio/textio.h" /* Records of the following type are used to describe how to generate * Gremlin output for a particular set of mask layers. Each style * describes the Gremlin figures to draw for a particular set of * layers. A single layer may participate in several gremlin styles. */ #ifdef GREMLIN typedef struct gremlinstyle { TileTypeBitMask grs_layers; /* Layers to plot in this style. */ int grs_stipple; /* Type of fill to use. See below. */ struct gremlinstyle *grs_next; /* Next style in chain. */ } GremlinStyle; static GremlinStyle *plotGremlinStyles; /* Most of the grs_stipple values are Gremlin stipple numbers. However, * if a grs_stipple value is less than zero, it means something special. * The definitions below give the possible alternatives: * * CROSS: Draw a thick outline around the tile with * a cross through it (used for contacts). * BORDER: Same as CROSS, except draw the outline with * no cross through it. */ #define CROSS -1 #define BORDER -2 /* The definitions below give the integers used for various Gremlin * line drawing styles (brushes). */ #define GREMLIN_DOTTED 1 #define GREMLIN_DASHED 4 #define GREMLIN_DOTDASH 2 #define GREMLIN_THIN 5 #define GREMLIN_MEDIUM 6 #define GREMLIN_THICK 3 /* The variables below are used to pass information from the top-level * procedure PlotGremlin down to the lower-level search functions * that are invoked for pieces of the layout. */ static FILE *file; /* File to use for output. */ static float scale; /* Multiply this by Magic units to get * Gremlin units. */ static GremlinStyle *curStyle; /* Current style being output. */ static TileTypeBitMask curMask; /* Layers currently being searched: this * is the AND of the mask from curStyle and * the layers that the user specified. */ static Rect bbox; /* Bounding box, in root coordinates, of * area being plotted. */ #endif /* GREMLIN */ /* * ---------------------------------------------------------------------------- * PlotGremlinTechInit -- * * Called once at beginning of technology file read-in to initialize * data structures. * * Results: * None. * * Side effects: * Clears out the list of things to plot. * ---------------------------------------------------------------------------- */ #ifdef GREMLIN void PlotGremlinTechInit() { GremlinStyle *style; for (style = plotGremlinStyles; style != NULL; style = style->grs_next) { freeMagic((char *) style); } plotGremlinStyles = NULL; } #else void PlotGremlinTechInit() {} #endif /* GREMLIN */ /* * ---------------------------------------------------------------------------- * PlotGremlinTechLine -- * * This procedure is invoked by the technology module once for * each line in the "gremlin" subsection of the "plot" section * of the technology file. * * Results: * Always returns TRUE (otherwise the technology module would * abort Magic with a fatal error). * * Side effects: * Builds up the table of Gremlin styles. * ---------------------------------------------------------------------------- */ #ifdef GREMLIN bool PlotGremlinTechLine(sectionName, argc, argv) char *sectionName; /* Name of this section (unused). */ int argc; /* Number of arguments on line. */ char *argv[]; /* Pointers to fields of line. */ { GremlinStyle *new; int stipple, i; if (argc != 2) { TechError("\"gremlin\" lines must have exactly 2 arguments.\n"); return TRUE; } if (strcmp(argv[1], "X") == 0) stipple = CROSS; else if (strcmp(argv[1], "B") == 0) stipple = BORDER; else { if (!StrIsInt(argv[1])) { TechError("2nd field must be an integer or \"X\" or \"B\".\n"); return TRUE; } stipple = atoi(argv[1]); } new = (GremlinStyle *) mallocMagic(sizeof(GremlinStyle)); DBTechNoisyNameMask(argv[0], &new->grs_layers); /* Replace non-primary contact images with primary images. */ for (i = TT_TECHDEPBASE; i < DBNumTypes; i++) { if TTMaskHasType(&new->grs_layers, i) TTMaskSetMask(&new->grs_layers, &DBLayerTypeMaskTbl[i]); } TTMaskAndMask(&new->grs_layers, &DBUserLayerBits); new->grs_stipple = stipple; new->grs_next = plotGremlinStyles; plotGremlinStyles = new; return TRUE; } #else bool PlotGremlinTechLine(sectionName, argc, argv) char *sectionName; /* Name of this section (unused). */ int argc; /* Number of arguments on line (unused). */ char *argv[]; /* Pointers to fields of line (unused). */ { return TRUE; } #endif /* GREMLIN */ #ifdef GREMLIN /* * ---------------------------------------------------------------------------- * * plotGremlinLine -- * * Outputs a line into the current Gremlin file. * * Results: * None. * * Side effects: * I/O. * * ---------------------------------------------------------------------------- */ void plotGremlinLine(p1, p2, lineStyle) Point *p1, *p2; /* Endpoints of line, given in root * coordinates. */ int lineStyle; /* Gremlin line style to use for line. */ { float x1, x2, y1, y2, limit; /* Clip the line to the rectangular area being output. First, * arrange for the first x-coordinate to be the smaller, then * clip against vertical lines at the x-boundaries. */ if (p1->p_x <= p2->p_x) { x1 = p1->p_x - bbox.r_xbot; x2 = p2->p_x - bbox.r_xbot; y1 = p1->p_y - bbox.r_ybot; y2 = p2->p_y - bbox.r_ybot; } else { x1 = p2->p_x - bbox.r_xbot; x2 = p1->p_x - bbox.r_xbot; y1 = p2->p_y - bbox.r_ybot; y2 = p1->p_y - bbox.r_ybot; } limit = bbox.r_xtop - bbox.r_xbot; if ((x1 > limit) || (x2 < 0)) return; if (x1 < 0) { y1 += (-x1)*(y2-y1)/(x2-x1); x1 = 0; } if (x2 > limit) { y2 -= (x2-limit)*(y2-y1)/(x2-x1); x2 = limit; } /* Now clip against horizontal lines at the y-boundaries. */ if (y2 < y1) { float tmp; tmp = y2; y2 = y1; y1 = tmp; tmp = x2; x2 = x1; x1 = tmp; } limit = bbox.r_ytop - bbox.r_ybot; if ((y1 > limit) || (y2 < 0)) return; if (y1 < 0) { x1 += (-y1)*(x2-x1)/(y2-y1); y1 = 0; } if (y2 > limit) { x2 -= (y2-limit)*(x2-x1)/(y2-y1); y2 = limit; } /* Lastly, scale and generate Gremlin output. */ x1 *= scale; x2 *= scale; y1 *= scale; y2 *= scale; fprintf(file, "VECTOR\n"); fprintf(file, "%.3f %.3f\n%.3f %.3f\n", x1, y1, x2, y2); fprintf(file, "*\n%d 0\n0 \n", lineStyle); } /* * ---------------------------------------------------------------------------- * * plotGremlinRect -- * * Outputs Gremlin statements to draw a rectangular area as * an outline with a given line style. * * Results: * None. * * Side effects: * Adds information to the current Gremlin file. * * ---------------------------------------------------------------------------- */ void plotGremlinRect(rect, lineStyle) Rect *rect; /* Rectangle to be drawn, in root coords. */ int lineStyle; /* Gremlin line style to use for outline. */ { Point p; p.p_x = rect->r_xbot; p.p_y = rect->r_ytop; plotGremlinLine(&rect->r_ll, &p, lineStyle); plotGremlinLine(&p, &rect->r_ur, lineStyle); p.p_x = rect->r_xtop; p.p_y = rect->r_ybot; plotGremlinLine(&rect->r_ur, &p, lineStyle); plotGremlinLine(&p, &rect->r_ll, lineStyle); } /* * ---------------------------------------------------------------------------- * * plotGremlinPaint -- * * This procedure is invoked once for each paint rectangle in * the area being plotted. * * Results: * Always returns 0 to keep the search alive. * * Side effects: * Outputs information for the tile, including stipple for its * interior, and a solid line for any portion of the boundary * of the tile that is adjacent to a tile NOT in this style. * * ---------------------------------------------------------------------------- */ int plotGremlinPaint(tile, cxp) Tile *tile; /* Tile that's of type to be output. */ TreeContext *cxp; /* Describes search in progress. */ { Rect tileArea, edge, rootArea; float xbot, xtop, ybot, ytop; Tile *neighbor; /* First transform tile coords to root coords */ TiToRect(tile, &tileArea); GeoTransRect(&cxp->tc_scx->scx_trans, &tileArea, &rootArea); /* See if this tile gets special handling. */ if ((curStyle->grs_stipple == CROSS) || (curStyle->grs_stipple == BORDER)) { /* Draw tile as a thick outline with a cross from corner * to corner, and skip the rest of this procedure. */ Point ul, lr; plotGremlinRect(&rootArea, GREMLIN_MEDIUM); if (curStyle->grs_stipple == CROSS) { ul.p_x = rootArea.r_xbot; ul.p_y = rootArea.r_ytop; lr.p_x = rootArea.r_xtop; lr.p_y = rootArea.r_ybot; plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_MEDIUM); plotGremlinLine(&ul, &lr, GREMLIN_MEDIUM); } return 0; } /* This tile gets "normal" processing (i.e. stippling and outlining). * Clip it to the plotting area and translate to Gremlin coords. * Then output Gremlin information for stippled interior. */ GeoClip(&rootArea, &bbox); xbot = (rootArea.r_xbot - bbox.r_xbot) * scale; xtop = (rootArea.r_xtop - bbox.r_xbot) * scale; ybot = (rootArea.r_ybot - bbox.r_ybot) * scale; ytop = (rootArea.r_ytop - bbox.r_ybot) * scale; fprintf(file, "POLYGON\n"); fprintf(file, "%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n", xbot, ybot, xtop, ybot, xtop, ytop, xbot, ytop, xbot, ybot); fprintf(file, "*\n0 %d\n0 \n", curStyle->grs_stipple); /* Now output lines for any edges between material of the type * currently being drawn and material of other types. This is * done by searching along the tile's borders for neighbors that * have the wrong types. First, search the tile's bottom border * (unless it is at infinity). */ if (tileArea.r_ybot > TiPlaneRect.r_ybot) { edge.r_ybot = edge.r_ytop = tileArea.r_ybot; for (neighbor = LB(tile); LEFT(neighbor) < tileArea.r_xtop; neighbor = TR(neighbor)) { if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue; edge.r_xbot = LEFT(neighbor); edge.r_xtop = RIGHT(neighbor); if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot; if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop; GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea); plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN); } } /* Now go along the tile's left border, doing the same thing. Ignore * edges that are at infinity. */ if (tileArea.r_xbot > TiPlaneRect.r_xbot) { edge.r_xbot = edge.r_xtop = tileArea.r_xbot; for (neighbor = BL(tile); BOTTOM(neighbor) < tileArea.r_ytop; neighbor = RT(neighbor)) { if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue; edge.r_ybot = BOTTOM(neighbor); edge.r_ytop = TOP(neighbor); if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot; if (edge.r_ytop < tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop; GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea); plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN); } } /* Same thing for the tile's top border. */ if (tileArea.r_ytop < TiPlaneRect.r_ytop) { edge.r_ybot = edge.r_ytop = tileArea.r_ytop; for (neighbor = RT(tile); RIGHT(neighbor) > tileArea.r_xbot; neighbor = BL(neighbor)) { if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue; edge.r_xbot = LEFT(neighbor); edge.r_xtop = RIGHT(neighbor); if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot; if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop; GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea); plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN); } } /* Finally, the right border. */ if (tileArea.r_xtop < TiPlaneRect.r_xtop) { edge.r_xbot = edge.r_xtop = tileArea.r_xtop; for (neighbor = TR(tile); TOP(neighbor) > tileArea.r_ybot; neighbor = LB(neighbor)) { if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue; edge.r_ybot = BOTTOM(neighbor); edge.r_ytop = TOP(neighbor); if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot; if (edge.r_ytop < tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop; GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea); plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN); } } return 0; } /* * ---------------------------------------------------------------------------- * * plotGremlinLabel -- * * This procedure is invoked once for each label overlapping the * area being plotted. It generates Gremlin output to describe * the label. * * Results: * Always returns 0 to keep the search from aborting. * * Side effects: * Gremlin information is output. * * ---------------------------------------------------------------------------- */ int plotGremlinLabel(scx, label) SearchContext *scx; /* Describes state of search when label * was found. */ Label *label; /* Label that was found. */ { Rect rootArea; float x, y; float delta; int pos; /* Mapping from our GEO_xxx positions to Gremlin object types: */ static char *gremlinPosition[] = { "CENTCENT", "BOTCENT", "BOTLEFT", "CENTLEFT", "TOPLEFT", "TOPCENT", "TOPRIGHT", "CENTRIGHT", "BOTRIGHT" }; /* Compute a distance equal to 1/8th the size of a typical wire * (max of thicknesses of routing layers). This is used to offset * text from labels and to compute cross size for point labels. */ if (RtrMetalWidth > RtrPolyWidth) delta = RtrMetalWidth*scale/8; else delta = RtrPolyWidth*scale/8; /* Translate the label's area and relative position to root * coordinates, and figure out the point relative to which * the label is to be positioned. */ GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea); pos = GeoTransPos(&scx->scx_trans, label->lab_just); switch (pos) { case GEO_NORTH: case GEO_NORTHEAST: case GEO_NORTHWEST: y = (rootArea.r_ytop - bbox.r_ybot) * scale; y += delta; break; case GEO_CENTER: case GEO_WEST: case GEO_EAST: y = (rootArea.r_ytop + rootArea.r_ybot - 2*bbox.r_ybot)*scale/2; break; case GEO_SOUTH: case GEO_SOUTHEAST: case GEO_SOUTHWEST: y = (rootArea.r_ybot - bbox.r_ybot) * scale; y -= delta; break; } switch (pos) { case GEO_WEST: case GEO_NORTHWEST: case GEO_SOUTHWEST: x = (rootArea.r_xbot - bbox.r_xbot) * scale; x -= delta; break; case GEO_CENTER: case GEO_NORTH: case GEO_SOUTH: x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2; break; case GEO_EAST: case GEO_NORTHEAST: case GEO_SOUTHEAST: x = (rootArea.r_xtop - bbox.r_xbot) * scale; x += delta; break; } /* Output the text for the label, if the label is within delta * of the area we're plotting (a large label could overlap a * bit of the area but stick out way off-screen too). */ if ((x >= -delta) && (y >= -delta) && (x <= ((bbox.r_xtop - bbox.r_xbot) * scale) + delta) && (y <= ((bbox.r_ytop - bbox.r_ybot) * scale) + delta)) { fprintf(file, "%s\n", gremlinPosition[pos]); fprintf(file, "%.3f %.3f\n*\n", x, y); fprintf(file, "1 1\n"); /* Roman font, small characters */ fprintf(file, "%d %s\n", strlen(label->lab_text), label->lab_text); } /* Output lines marking the label's area. Different things are * done depending on whether the label is a point, a line, or an * area. */ if ((rootArea.r_xbot == rootArea.r_xtop) && (rootArea.r_ybot == rootArea.r_ytop)) { /* Point label. Output a cross. */ float top, bot; x = (rootArea.r_xbot - bbox.r_xbot) * scale; y = (rootArea.r_ybot - bbox.r_ybot) * scale; top = y + delta; bot = y - delta; fprintf(file, "VECTOR\n%.3f %.3f\n%.3f %.3f\n*\n", x, bot, x, top); fprintf(file, "6 1\n0 \n"); /* Medium thickness */ top = x + delta; bot = x - delta; fprintf(file, "VECTOR\n%.3f %.3f\n%.3f %.3f\n*\n", bot, y, top, y); fprintf(file, "6 1\n0 \n"); /* Medium thickness */ } else if ((rootArea.r_xbot == rootArea.r_xtop) || (rootArea.r_ybot == rootArea.r_ytop)) { /* Line label. Just draw a medium-thickness line. */ plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_MEDIUM); } else { /* Rectangular. Draw lines around the boundary. */ plotGremlinRect(&rootArea, GREMLIN_MEDIUM); } return 0; } /* * ---------------------------------------------------------------------------- * * plotGremlinCell -- * * This procedure is invoked once for each unexpanded cell that * overlaps the area being plotted. * * Results: * Always returns 0 to keep the search from aborting. * * Side effects: * Gremlin information is output to describe the cell. * * ---------------------------------------------------------------------------- */ int plotGremlinCell(scx) SearchContext *scx; /* Describes cell whose bbox is to * be plotted. */ { extern bool PlotShowCellNames; char idName[100]; Rect rootArea; CellDef *def; float x, y; /* Convert the cell's bounding box to root coordinates and then * draw as a thick outline. */ def = scx->scx_use->cu_def; GeoTransRect(&scx->scx_trans, &def->cd_bbox, &rootArea); plotGremlinRect(&rootArea, GREMLIN_THICK); if (!PlotShowCellNames) return 0; /* Output the cell definition's name in the top of the bounding box. * Use a bold font (#3), in a medium size (#2). Make sure that the * name's positioning point is within the area we're plotting. */ x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2; y = (2*rootArea.r_ytop + rootArea.r_ybot - 3*bbox.r_ybot)*scale/3; if ((x >= 0) && (y >= 0) && (x <= (bbox.r_xtop - bbox.r_xbot)*scale) && (y <= (bbox.r_ytop - bbox.r_ybot)*scale)) { fprintf(file, "CENTCENT\n%.3f %.3f\n*\n", x, y); fprintf(file, "3 2\n%d %s\n", strlen(def->cd_name), def->cd_name); } /* Output the cell id in the bottom of the bounding box. * Use an italic font (#2) in a medium size (#2). */ x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2; y = (rootArea.r_ytop + 2*rootArea.r_ybot - 3*bbox.r_ybot)*scale/3; if ((x >= 0) && (y >= 0) && (x <= (bbox.r_xtop - bbox.r_xbot)*scale) && (y <= (bbox.r_ytop - bbox.r_ybot)*scale)) { (void) DBPrintUseId(scx, idName, 100, TRUE); fprintf(file, "CENTCENT\n%.3f %.3f\n*\n", x, y); fprintf(file, "2 2\n%d %s\n", strlen(idName), idName); } return 0; } /* * ---------------------------------------------------------------------------- * * PlotGremlin -- * * This procedure generates a Gremlin file to describe an area of * a layout. * * Results: * None. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void PlotGremlin(fileName, scx, layers, xMask) char *fileName; /* Name of Gremlin file to write. */ SearchContext *scx; /* The use and area and transformation * in this describe what to plot. */ TileTypeBitMask *layers; /* Tells what layers to plot. Only * paint layers in this mask, and also * expanded according to xMask, are * plotted. If L_LABELS is set, then * labels on the layers are also * plotted, if expanded according to * xMask. If L_CELL is set, then * subcells that are unexpanded * according to xMask are plotted as * bounding boxes. */ int xMask; /* An expansion mask, used to indicate * the window whose expansion status * will be used to determine * visibility. Zero means treat * everything as expanded. */ { int size, tmp; /* Compute a scale factor between our coordinates and Gremlin * coordinates. Pick an even power of two that will make all * the Gremlin units fall between 0 and 512. */ GeoTransRect(&scx->scx_trans, &scx->scx_area, &bbox); size = bbox.r_xtop - bbox.r_xbot; tmp = bbox.r_ytop - bbox.r_ybot; if (size < tmp) size = tmp; scale = 64.0; while (scale*size > 512) scale /= 2.0; /* Open the Gremlin file and output header information. */ file = PaOpen(fileName, "w", (char *) NULL, ".", (char *) NULL, (char **) NULL); if (file == NULL) { TxError("Couldn't write Gremlin file \"%s\".\n", fileName); return; } fprintf(file, "sungremlinfile\n"); fprintf(file, "1 0.00 0.00\n"); /* For each Gremlin style, find all the paint layers that belong * to that style and put plot information into the file. */ for (curStyle = plotGremlinStyles; curStyle != NULL; curStyle = curStyle->grs_next) { TTMaskAndMask3(&curMask, layers, &curStyle->grs_layers); (void) DBTreeSrTiles(scx, &curMask, xMask, plotGremlinPaint, (ClientData) NULL); } /* Output labels, if they are wanted. */ if (TTMaskHasType(layers, L_LABEL)) { curMask = *layers; TTMaskSetType(&curMask, TT_SPACE); (void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL, TF_LABEL_ATTACH, plotGremlinLabel, (ClientData) NULL); } /* Output subcell bounding boxes, if they are wanted. */ if (TTMaskHasType(layers, L_CELL)) { (void) DBTreeSrCells(scx, xMask, plotGremlinCell, (ClientData) NULL); } /* Output trailer information into the file, and close it. */ fprintf(file, "-1\n"); fclose(file); return; } #endif /* GREMLIN */