summaryrefslogtreecommitdiff
path: root/src/addspacers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/addspacers.c')
-rw-r--r--src/addspacers.c2026
1 files changed, 2026 insertions, 0 deletions
diff --git a/src/addspacers.c b/src/addspacers.c
new file mode 100644
index 0000000..b0d211d
--- /dev/null
+++ b/src/addspacers.c
@@ -0,0 +1,2026 @@
+/*------------------------------------------------------*/
+/* addspacers --- */
+/* */
+/* Tool for adding fill cells and other post-placement */
+/* cells and data into a DEF format layout. */
+/* */
+/* Primary functions are: */
+/* */
+/* 1) Fill the core area out to the bounding box with */
+/* fill cells to make the edges straight and fill */
+/* any gaps. */
+/* */
+/* 2) Create power stripes and power posts, adding */
+/* additional fill under (or near, as available) */
+/* the power stripes if requested. */
+/* */
+/* 3) Adjust pin positions to match any stretching of */
+/* the cell done in (2) */
+/* */
+/* 4) Adjust obstruction layers to match any */
+/* stretching of the cell done in (2) */
+/* */
+/* 5) Write the modified DEF layout */
+/* */
+/* 6) Write the modified obstruction file (if */
+/* modified). */
+/* */
+/*------------------------------------------------------*/
+/* */
+/* This file previously addspacers.tcl until it became */
+/* painfully obvious that the amount of calculation */
+/* involved made it a very poor choice to be done by */
+/* an interpreter. */
+/* */
+/*------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* For getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readlef.h"
+#include "readdef.h"
+
+/* Flags fields */
+
+#define NOSTRETCH 1
+#define OBSTRUCT 2
+#define VERBOSE 4
+#define FILLWARNED 8
+
+/* Structure used to contain core area bounding box. Also records */
+/* core site height and width. */
+
+typedef struct _corebbox *COREBBOX;
+
+typedef struct _corebbox {
+ int llx; /* Layout bounds */
+ int lly;
+ int urx;
+ int ury;
+ int urx_exp; /* Expanded core urx */
+ int sitew; /* Core site width */
+ int siteh; /* Core site height */
+ int fillmin; /* Minimum fill cell width */
+ int orient; /* Orientation of the first (lowest) row */
+} corebbox;
+
+/* Structure used to hold the final calculated pitch and width of stripes */
+
+typedef struct _stripeinfo *SINFO;
+
+typedef struct _stripeinfo {
+ int width;
+ int pitch;
+ int offset;
+ int stretch; /* Maximum amount of layout stretching */
+ int number; /* Total number of stripes */
+} stripeinfo;
+
+/* List of gates (used to sort fill cells) */
+typedef struct filllist_ *FILLLIST;
+
+struct filllist_ {
+ FILLLIST next;
+ FILLLIST last;
+ GATE gate;
+ int width;
+};
+
+/* Structures for defining a power stripe. */
+
+typedef struct _powerpost *PPOST;
+
+typedef struct _powerpost {
+ PPOST next;
+ DSEG strut;
+ LefList viagen;
+} powerpost;
+
+typedef struct _powerstripe *PSTRIPE;
+
+typedef struct _powerstripe {
+ PSTRIPE next; // One stripe definition centered on X = 0
+ PPOST posts; // All posts for one stripe, centered on X = 0
+ DSEG stripe;
+ int offset; // Horizontal start of posts
+ int num; // Number of stripes
+ int pitch; // Spacing between stripes
+ char *name; // Net name of power rail
+} powerstripe;
+
+/* Hash table of instances hashed by (X, Y) position */
+struct hashtable CellPosTable;
+
+/* Forward declarations */
+unsigned char check_overcell_capable(unsigned char Flags);
+FILLLIST generate_fill(char *fillcellname, float rscale, COREBBOX corearea,
+ unsigned char Flags);
+SINFO generate_stripefill(char *VddNet, char *GndNet,
+ char *stripepat, float stripewidth_t, float stripepitch_t,
+ char *fillcellname, float scale, FILLLIST fillcells,
+ COREBBOX corearea, unsigned char Flags);
+void fix_obstructions(char *definname, SINFO stripevals, float scale,
+ unsigned char Flags);
+PSTRIPE generate_stripes(SINFO stripevals, FILLLIST fillcells,
+ COREBBOX corearea, char *stripepat, char *VddNet, char *GndNet,
+ float scale, unsigned char Flags);
+void write_output(char *definname, char *defoutname, float scale,
+ COREBBOX corearea, SINFO stripevals, PSTRIPE rails,
+ char *VddNet, char *GndNet, unsigned char Flags);
+void helpmessage(FILE *outf);
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, result;
+ unsigned char Flags;
+ float rscale;
+ struct cellrec *topcell;
+ COREBBOX corearea;
+ FILLLIST fillcells;
+ SINFO stripevals;
+ PSTRIPE rails;
+
+ char *definname = NULL;
+ char *fillcellname = NULL;
+ char *defoutname = NULL;
+
+ float stripewidth_t, stripepitch_t;
+ static char* default_stripepat = "PG";
+ char *stripepat = default_stripepat;
+
+ char *VddNet = NULL;
+ char *GndNet = NULL;
+
+ Flags = 0;
+ stripewidth_t = stripepitch_t = 0.0;
+
+ while ((i = getopt(argc, argv, "hHvOn:o:l:p:g:f:w:P:s:")) != EOF) {
+ switch( i ) {
+ case 'v':
+ Flags |= VERBOSE;
+ /* Also set global variable used by readlef.c */
+ Verbose = 1;
+ break;
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ exit(0);
+ break;
+ case 'n':
+ Flags |= NOSTRETCH;
+ break;
+ case 'O':
+ Flags |= OBSTRUCT;
+ break;
+ case 'w':
+ if (sscanf(optarg, "%g", &stripewidth_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg);
+ }
+ break;
+ case 'P':
+ if (sscanf(optarg, "%g", &stripepitch_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg);
+ }
+ break;
+ case 'o':
+ defoutname = strdup(optarg);
+ break;
+ case 'f':
+ fillcellname = strdup(optarg);
+ break;
+ case 's':
+ if (!strcmp(optarg, "tripe")) {
+ /* Accept the argument "-stripe <width> <pitch> <pattern>" */
+ /* that was the option for the old addspacers.tcl script. */
+ /* Combines width, pitch, and pattern in one argument. */
+ if (sscanf(argv[optind], "%g", &stripewidth_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]);
+ }
+ optind++;
+ if (sscanf(argv[optind], "%g", &stripepitch_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]);
+ }
+ optind++;
+ stripepat = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ stripepat = strdup(optarg);
+ }
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'p':
+ VddNet = strdup(optarg);
+ break;
+ case 'g':
+ GndNet = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr,"Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ char *pptr;
+
+ definname = (char *)malloc(strlen(argv[optind]) + 5);
+ strcpy(definname, argv[optind]);
+ pptr = strrchr(argv[optind], '.');
+ if (pptr == NULL) strcat(definname, ".def");
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename for DEF input file.\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ result = DefRead(definname, &rscale);
+
+ corearea = (COREBBOX)malloc(sizeof(corebbox));
+
+ Flags |= check_overcell_capable(Flags);
+ fillcells = generate_fill(fillcellname, rscale, corearea, Flags);
+ if (fillcells == NULL) {
+ fprintf(stderr, "Failed to parse any fill cells from the standard cell library.\n");
+ return 1;
+ }
+ stripevals = generate_stripefill(VddNet, GndNet, stripepat,
+ stripewidth_t, stripepitch_t, fillcellname, rscale, fillcells,
+ corearea, Flags);
+ fix_obstructions(definname, stripevals, rscale, Flags);
+ rails = generate_stripes(stripevals, fillcells, corearea, stripepat,
+ VddNet, GndNet, rscale, Flags);
+ write_output(definname, defoutname, rscale, corearea, stripevals,
+ rails, VddNet, GndNet, Flags);
+
+ return 0;
+}
+
+/*--------------------------------------------------------------*/
+/* Find empty spaces in the DEF layout and insert fill macros */
+/*--------------------------------------------------------------*/
+
+FILLLIST
+generate_fill(char *fillcellname, float scale, COREBBOX corearea, unsigned char Flags)
+{
+ GATE gate, newfillinst;
+ ROW row;
+ int isfill, orient;
+ int corew = 1, coreh = 0, testh;
+ int instx, insty, instw, insth, fnamelen;
+ int x, y, dx, nx, fillmin;
+ char posname[32];
+
+ int corellx = 0;
+ int corelly = 0;
+ int coreurx = 0;
+ int coreury = 0;
+
+ FILLLIST fillcells = NULL;
+ FILLLIST newfill, testfill;
+
+ /* Parse library macros and find CORE SITE definition */
+
+ for (gate = GateInfo; gate; gate = gate->next)
+ {
+ if (!strncmp(gate->gatename, "site_", 5)) {
+ if (gate->gateclass == MACRO_CLASS_CORE) {
+ corew = (int)(roundf(gate->width * scale));
+ testh = (int)(roundf(gate->height * scale));
+ // Sometimes there are multiple-height core sites. . .
+ if (coreh == 0 || (testh < coreh)) coreh = testh;
+ }
+ }
+ }
+ if (corew == 0) {
+ fprintf(stderr, "Warning: failed to find any core site.\n");
+ /* Use route pitch for step size */
+ corew = (int)(roundf(LefGetRoutePitch(0) * scale));
+ }
+ if (corew == 0) {
+ fprintf(stderr, "Error: failed to find any core site or route pitch.\n");
+ return NULL;
+ }
+
+ /* Parse library macros and find fill cells. Use "fillcellname" as a */
+ /* prefix, if given; if not, find spacer cells by class. */
+
+ fnamelen = (fillcellname) ? strlen(fillcellname) : 0;
+
+ for (gate = GateInfo; gate; gate = gate->next)
+ {
+ isfill = FALSE;
+ if (fillcellname) {
+ if (!strncmp(gate->gatename, fillcellname, fnamelen))
+ isfill = TRUE;
+ }
+ else if (gate->gatesubclass == MACRO_SUBCLASS_SPACER)
+ isfill = TRUE;
+
+ if (isfill == TRUE) {
+ newfill = (FILLLIST)malloc(sizeof(struct filllist_));
+ newfill->gate = gate;
+ newfill->width = (int)(roundf(gate->width * scale));
+
+ /* Insert in fill list in width order, high to low */
+ if (fillcells == NULL) {
+ newfill->next = newfill->last = NULL;
+ fillcells = newfill;
+ }
+ else {
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width < newfill->width) {
+ /* insert before */
+ newfill->next = testfill;
+ newfill->last = testfill->last;
+ if (testfill->last == NULL)
+ fillcells = newfill;
+ else
+ testfill->last->next = newfill;
+ testfill->last = newfill;
+ break;
+ }
+ else if (testfill->next == NULL) {
+ /* put at end */
+ newfill->next = NULL;
+ testfill->next = newfill;
+ newfill->last = testfill;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (fillcells == NULL) {
+ fprintf(stderr, "Error: No fill cells have been specified or found.\n");
+ return NULL;
+ }
+
+ if (fillcells) {
+ testh = (int)(roundf(fillcells->gate->height * scale));
+ if ((coreh == 0) || (coreh < testh)) {
+ /* Use fill cell height for core height */
+ coreh = testh;
+ }
+ }
+ if (coreh == 0) {
+ fprintf(stderr, "Error: failed to find any core site or standard cell height.\n");
+ return NULL;
+ }
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Core site is %g x %g um\n",
+ (float)corew / scale, (float)coreh / scale);
+
+
+ /* Rehash all instances by position */
+ /* Find minimum and maximum bounds, and record cell in lower left position */
+
+ InitializeHashTable(&CellPosTable, LARGEHASHSIZE);
+
+ for (gate = Nlgates; gate; gate = gate->next)
+ {
+ /* Do not evaluate pins, only core cells */
+ if (gate->gatetype == NULL) continue;
+
+ instx = (int)(roundf(gate->placedX * scale));
+ insty = (int)(roundf(gate->placedY * scale));
+ instw = (int)(roundf(gate->width * scale));
+ insth = (int)(roundf(gate->height * scale));
+ sprintf(posname, "%dx%d", instx, insty);
+ HashPtrInstall(posname, gate, &CellPosTable);
+
+ if (corellx == coreurx) {
+ corellx = instx;
+ coreurx = instx + instw;
+ corelly = insty;
+ coreury = insty + insth;
+ }
+ else {
+ if (instx < corellx) corellx = instx;
+ else if (instx + instw > coreurx) coreurx = instx + instw;
+
+ if (insty < corelly) corelly = insty;
+ else if (insty + insth > coreury) coreury = insty + insth;
+ }
+ }
+
+ fprintf(stdout, "Initial core layout: (%d %d) to (%d %d) (scale um * %d)\n",
+ corellx, corelly, coreurx, coreury, (int)scale);
+
+ if (Flags & VERBOSE) fprintf(stdout, "Adding fill cells.\n");
+
+ /* Find the orientation of the first row and record this */
+ /* in corearea. NOTE: This is simpler if the DEF file records */
+ /* ROWs, something that needs to be done in place2def. */
+
+ row = DefFindRow(corelly);
+ if (row) {
+ corearea->orient = row->orient & (RN | RS);
+ }
+ else {
+ corearea->orient = RN;
+ y = corelly;
+ x = corellx;
+ while (x < coreurx) {
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate != NULL) {
+ corearea->orient = gate->orient & (RN | RS);
+ break;
+ }
+ x += testfill->width;
+ }
+ }
+ orient = corearea->orient;
+
+ /* Starting from the lower-left corner, find gate at each site */
+ /* position. If there is no gate at the site position, then */
+ /* find the next gate forward, and add fill. */
+
+ /* NOTE: This routine does not account for obstruction areas */
+ /* used to define a non-rectangular core area (to be done) */
+
+ for (y = corelly; y < coreury; y += coreh) {
+ x = corellx;
+ while (x < coreurx) {
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate == NULL) {
+ for (nx = x + corew; nx < coreurx; nx += corew) {
+ sprintf(posname, "%dx%d", nx, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate != NULL) break;
+ }
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Add fill from (%d %d) to (%d %d)\n",
+ x, y, nx, y);
+ dx = nx - x;
+
+ while (dx > 0) {
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width <= dx) break;
+ }
+ if (testfill == NULL) {
+ if (nx == coreurx) {
+ if (!(Flags & FILLWARNED)) {
+ fprintf(stderr, "Notice: Right edge of layout"
+ " cannot be cleanly aligned due to\n");
+ fprintf(stderr, "limited fill cell widths.\n");
+ }
+ Flags |= FILLWARNED; // Do not repeat this message
+ x = nx;
+ dx = 0;
+ break;
+ }
+ else {
+ fprintf(stderr, "Error: Empty slot at (%g, %g) is smaller"
+ " than any available fill cell.\n",
+ (float)x / scale, (float)y / scale);
+ }
+ }
+
+ /* Create new fill instance */
+ newfillinst = (GATE)malloc(sizeof(struct gate_));
+ if (testfill)
+ newfillinst->gatetype = testfill->gate;
+ else
+ newfillinst->gatetype = NULL; /* placeholder */
+ sprintf(posname, "FILL%dx%d", x, y);
+ newfillinst->gatename = strdup(posname);
+ newfillinst->placedX = (double)x / (double)scale;
+ newfillinst->placedY = (double)y / (double)scale;
+ newfillinst->clientdata = (void *)NULL;
+ row = DefFindRow(y);
+ newfillinst->orient = (row) ? row->orient : orient;
+ DefAddGateInstance(newfillinst);
+
+ /* Hash the new instance position */
+ sprintf(posname, "%dx%d", x, y);
+ HashPtrInstall(posname, newfillinst, &CellPosTable);
+
+ if (testfill) {
+ x += testfill->width;
+ dx -= testfill->width;
+ }
+ else {
+ newfillinst->width = dx;
+ x += dx;
+ dx = 0;
+ }
+ }
+ }
+ else {
+ x += (int)(roundf(gate->width * scale));
+ }
+ }
+ /* Flip orientation each row (NOTE: This is not needed if ROW */
+ /* statements are in the DEF file! */
+ orient = (orient == RN) ? RS : RN;
+ }
+
+ if (fillcells) {
+ for (testfill = fillcells; testfill->next; testfill = testfill->next);
+ fillmin = testfill->width;
+ }
+ else fillmin = 0;
+
+ corearea->llx = corellx;
+ corearea->lly = corelly;
+ corearea->urx = coreurx;
+ corearea->urx_exp = coreurx;
+ corearea->ury = coreury;
+ corearea->sitew = corew;
+ corearea->siteh = coreh;
+ corearea->fillmin = fillmin;
+
+ return fillcells;
+}
+
+/*--------------------------------------------------------------*/
+/* Check if there are not enough metal layers to route power */
+/* over the cell. If not, then force the NOSTRETCH option. */
+/*--------------------------------------------------------------*/
+
+unsigned char check_overcell_capable(unsigned char Flags)
+{
+ int ltop;
+
+ if (!(Flags & NOSTRETCH)) {
+ ltop = LefGetMaxRouteLayer() - 1;
+ if (LefGetRouteOrientation(ltop) == 1) ltop--;
+ if (ltop < 3) {
+ fprintf(stderr, "Warning: Stretching requested, but not applicable.\n");
+ return (unsigned char)NOSTRETCH;
+ }
+ }
+ return (unsigned char)0;
+}
+
+/*--------------------------------------------------------------*/
+/* Generate power stripes */
+/*--------------------------------------------------------------*/
+
+SINFO
+generate_stripefill(char *VddNet, char *GndNet, char *stripepat,
+ float stripewidth_t, float stripepitch_t,
+ char *fillcellname, float scale, FILLLIST fillcells,
+ COREBBOX corearea, unsigned char Flags)
+{
+ int numstripes;
+ int minstripes;
+ int corew, tw, tp, tr, dx, x, y, nx;
+ int stripepitch_i, stripewidth_i, stripeoffset_i;
+ int stripepitch_f, stripewidth_f, stripeoffset_f;
+ int totalw;
+ int orient;
+ int nextx, totalfx;
+ FILLLIST fillseries, testfill, newfill, sfill;
+ SINFO stripevals;
+ char posname[32];
+ GATE gate, newfillinst;
+ ROW row;
+
+ stripevals = (SINFO)malloc(sizeof(stripeinfo));
+ stripevals->pitch = 0;
+ stripevals->width = 0;
+ stripevals->offset = 0;
+
+ corew = corearea->urx - corearea->llx;
+
+ if (stripewidth_t <= 0.0 || stripepitch_t <= 0.0) {
+ fprintf(stdout, "No stripe information provided; no power stripes added.\n");
+ return stripevals;
+ }
+
+ minstripes = strlen(stripepat);
+
+ /* Scale stripe width and pitch from microns to DEF database units */
+ stripewidth_i = (int)(roundf(stripewidth_t * scale));
+ stripepitch_i = (int)(roundf(stripepitch_t * scale));
+
+ /* Adjust stripewidth to the nearest core site unit width. */
+ /* If stretching is specified and the minimum fill cell width */
+ /* is greater than the core site width, then adjust to the */
+ /* nearest multiple of the minimum fill cell width. */
+
+ if ((!(Flags & NOSTRETCH)) && (corearea->fillmin > corearea->sitew)) {
+ tw = stripewidth_i / corearea->fillmin;
+ tr = stripewidth_i % corearea->fillmin;
+ stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->fillmin;
+ }
+ else {
+ tw = stripewidth_i / corearea->sitew;
+ tr = stripewidth_i % corearea->sitew;
+ stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->sitew;
+ }
+
+ if (!(Flags & NOSTRETCH))
+ {
+ /* Find a series of fill cell macros to match the stripe width */
+ fillseries = NULL;
+ dx = stripewidth_f;
+ while (dx > 0) {
+ int diff;
+
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width <= dx) break;
+ }
+ if (testfill == NULL) {
+ /* This can happen if there is no fill cell that is the */
+ /* same width as the minimum site pitch. If so, find */
+ /* the first non-minimum-size fill cell and change it */
+ /* to minimum size, then continue. */
+ for (testfill = fillcells; testfill && testfill->next;
+ testfill = testfill->next);
+ for (sfill = fillseries; sfill; sfill = sfill->next)
+ if (sfill->gate != testfill->gate) break;
+ if (sfill == NULL) {
+ fprintf(stderr, "Warning: failed to find fill cell series matching"
+ " the requested stripe width.\n");
+ fprintf(stderr, "Stripe width will be modified as needed.\n");
+ dx = 0;
+ break;
+ }
+ diff = sfill->width - testfill->width;
+ sfill->gate = testfill->gate;
+ sfill->width = testfill->width;
+ dx += diff;
+ }
+ newfill = (FILLLIST)malloc(sizeof(struct filllist_));
+ newfill->gate = testfill->gate;
+ newfill->width = testfill->width;
+ newfill->next = fillseries;
+ fillseries = newfill;
+ dx -= newfill->width;
+ }
+
+ /* In case the fill series width does not equal the expected */
+ /* stripe width, set the stripe width to the fill series width. */
+
+ stripewidth_f = 0;
+ for (sfill = fillseries; sfill; sfill = sfill->next)
+ stripewidth_f += sfill->width;
+ }
+
+ /* Adjust stripepitch to the nearest core site unit width */
+ tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ if (stripepitch_f < stripewidth_f * 2) {
+ fprintf(stderr, "Error: Stripe pitch is too small (pitch = %g, width = %g)!\n",
+ (float)stripepitch_f / (float)scale,
+ (float)stripewidth_f / (float)scale);
+ return stripevals;
+ }
+ if ((fillcells == NULL) && (!(Flags & NOSTRETCH))) {
+ fprintf(stderr, "No fill cells defined. Not stretching layout.\n");
+ Flags |= NOSTRETCH;
+ }
+
+ if (stripepitch_f != stripepitch_i) {
+ fprintf(stderr, "Stripe pitch requested = %g, stripe pitch used = %g\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ if (stripewidth_f != stripewidth_i) {
+ fprintf(stderr, "Stripe width requested = %g, stripe width used = %g\n",
+ stripewidth_t, (float)stripewidth_f / (float)scale);
+ }
+
+ if (!(Flags & NOSTRETCH))
+ {
+ /* Cell will be stretched, so compute the amount to be */
+ /* streteched per stripe, which is the stripewidth */
+ /* adjusted to the nearest unit site width. */
+
+ numstripes = corew / (stripepitch_f - stripewidth_f);
+
+ if (numstripes < minstripes) {
+ numstripes = minstripes;
+
+ /* Recompute stripe pitch */
+ stripepitch_f = corew / numstripes;
+ tp = (int)(0.5 + (float)stripepitch_f / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ totalw = corew + numstripes * stripewidth_f;
+
+ /* Find offset to center of 1st power stripe that results in */
+ /* centering the stripes on the layout. */
+
+ stripeoffset_i = (totalw - (numstripes - 1) * stripepitch_f) / 2;
+ tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew);
+ stripeoffset_f = (tp * corearea->sitew);
+
+ /* Add fill cells (approximately) under stripe positions. */
+ /* Note that this is independent of drawing the stripes and */
+ /* power posts. There is no requirement that the stretch fill */
+ /* must be directly under the stripe, only that the total cell */
+ /* width is increased by the total width of all stripes, and */
+ /* the extra fill is added as close to each stripe as possible. */
+
+ orient = corearea->orient;
+ for (y = corearea->lly; y < corearea->ury; y += corearea->siteh) {
+ nextx = corearea->llx + stripeoffset_f - stripewidth_f / 2;
+ totalfx = 0;
+
+ x = corearea->llx;
+
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+
+ while (x < corearea->urx) {
+ while (x < nextx) {
+ nx = x + (int)(roundf(gate->width * scale));
+
+ /* If next position is larger than nextx but is also */
+ /* farther from the stripe centerline than the current */
+ /* position, then break here instead. */
+ if ((nx > nextx) && ((nextx - x) < (nx - nextx))) break;
+
+ gate->placedX += (double)totalfx / (double)scale;
+
+ sprintf(posname, "%dx%d", nx, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ x = nx;
+
+ if ((x >= corearea->urx) || (gate == NULL)) break;
+ }
+ if ((x >= corearea->urx) || (gate == NULL)) break;
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Add fill under stripe from (%d %d) to (%d %d)\n",
+ x, y, x + stripewidth_f, y);
+
+ for (testfill = fillseries; testfill; testfill = testfill->next) {
+ /* Create new fill instance */
+ newfillinst = (GATE)malloc(sizeof(struct gate_));
+ newfillinst->gatetype = testfill->gate;
+ sprintf(posname, "SFILL%dx%d", x + totalfx, y);
+ newfillinst->gatename = strdup(posname);
+ newfillinst->placedX = (double)(x + totalfx) / (double)scale;
+ newfillinst->placedY = (double)y / (double)scale;
+ newfillinst->clientdata = (void *)NULL;
+ row = DefFindRow(y);
+ newfillinst->orient = (row) ? row->orient : orient;
+ DefAddGateInstance(newfillinst);
+
+ /* Position will not be revisited, so no need to */
+ /* add to the position hash. */
+
+ totalfx += testfill->width;
+ }
+ nextx += stripepitch_f - stripewidth_f;
+ }
+ orient = (orient == RN) ? RS : RN;
+ }
+
+ /* Adjust pins */
+
+ for (gate = Nlgates; gate; gate = gate->next) {
+ if (gate->gatetype == NULL) {
+ int px, po, pitches;
+
+ px = (int)(roundf(gate->placedX * scale));
+ po = px - stripeoffset_f - (stripewidth_f / 2);
+ if (po > 0)
+ pitches = 1 + po / (stripepitch_f - stripewidth_f);
+ else
+ pitches = -1;
+ if (pitches <= 0) continue;
+
+ px += pitches * stripewidth_f;
+ gate->placedX = (float)(px) / scale;
+ }
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Layout stretched by %g um\n",
+ (double)totalfx / (double)scale);
+ }
+ else
+ {
+ /* Stripes are overlaid on core without stretching */
+ numstripes = corew / stripepitch_f;
+ if (numstripes < minstripes) {
+ numstripes = minstripes;
+
+ /* Recompute stripe pitch */
+ stripepitch_f = corew / numstripes;
+ tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ totalw = corew;
+
+ /* Find offset to center of 1st power stripe that results in */
+ /* centering the stripes on the layout. */
+
+ stripeoffset_i = (totalw - (numstripes - 1) * stripewidth_f) / 2;
+ tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew);
+ stripeoffset_f = (tp * corearea->sitew);
+ }
+
+ /* Record expanded area */
+ corearea->urx_exp = totalw + corearea->llx;
+
+ /* Record and return final calculated power stripe pitch and width */
+ stripevals->pitch = stripepitch_f;
+ stripevals->width = stripewidth_f;
+ stripevals->offset = stripeoffset_f;
+ stripevals->stretch = totalfx;
+ stripevals->number = numstripes;
+ return stripevals;
+}
+
+
+/*--------------------------------------------------------------*/
+/* Adjust obstructions */
+/* If OBSTRUCT flag is set, then obstructions are to be read */
+/* from the file <rootname>.obs, modified, and written to file */
+/* <rootname>.obsx. */
+/*--------------------------------------------------------------*/
+
+void
+fix_obstructions(char *definname, SINFO stripevals, float scale,
+ unsigned char Flags)
+{
+ FILE *fobsin, *fobsout;
+ char *filename, *pptr;
+ char line[256];
+ char layer[32];
+ float fllx, flly, furx, fury;
+ int illx, iurx, pitches, po;
+
+ /* If no layout stretching was done, then nothing needs to be modified */
+ if (Flags & NOSTRETCH) return;
+
+ /* Only handle obstruction layer file if the -O switch was specified */
+ /* (to do: Handle obstructions via the DEF file BLOCKAGES records) */
+
+ if (!(Flags & OBSTRUCT)) return;
+
+ filename = (char *)malloc(strlen(definname) + 6);
+ strcpy(filename, definname);
+ pptr = strrchr(filename, '.');
+ if (pptr == NULL)
+ strcat(filename, ".obs");
+ else
+ sprintf(pptr, ".obs");
+
+ fobsin = fopen(filename, "r");
+ if (fobsin == NULL) {
+ fprintf(stderr, "Cannot open obstruction file %s for reading\n", filename);
+ free(filename);
+ return;
+ }
+
+ pptr = strrchr(filename, '.');
+ sprintf(pptr, ".obsx");
+
+ fobsout = fopen(filename, "w");
+ if (fobsout == NULL) {
+ fprintf(stderr, "Cannot open obstruction file %s for writing\n", filename);
+ fclose(fobsin);
+ free(filename);
+ return;
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Modifying obstruction positions.\n");
+
+ while (1) {
+ if (fgets(line, 256, fobsin) == NULL) break;
+
+ if (!strncmp(line, "obstruction", 11)) {
+ sscanf(line + 11, "%g %g %g %g %s", &fllx, &flly, &furx, &fury, layer);
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "In: %g %g %g %g\n", fllx, flly, furx, fury);
+
+ illx = (int)(roundf(fllx * scale));
+ iurx = (int)(roundf(furx * scale));
+
+ po = illx - stripevals->offset - (stripevals->width / 2);
+ if (po > 0) {
+ pitches = 1 + po / (stripevals->pitch - stripevals->width);
+ illx += pitches * stripevals->width;
+ }
+ po = iurx - stripevals->offset - (stripevals->width / 2);
+ if (po > 0) {
+ pitches = 1 + po / (stripevals->pitch - stripevals->width);
+ iurx += pitches * stripevals->width;
+ }
+
+ fllx = (float)illx / scale;
+ furx = (float)iurx / scale;
+
+ fprintf(fobsout, "obstruction %g %g %g %g %s\n",
+ fllx, flly, furx, fury, layer);
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Out: %g %g %g %g\n", fllx, flly, furx, fury);
+ }
+ }
+
+ free(filename);
+ fclose(fobsin);
+ fclose(fobsout);
+}
+
+/*--------------------------------------------------------------*/
+/* Create a new VIA record from a VIA or VIARULE record, with */
+/* total width and height as given. */
+/*--------------------------------------------------------------*/
+
+void
+via_make_generated(LefList viagen, LefList lefl, int lbot, int lcut,
+ int width, int height, float scale)
+{
+ float cutsizex, cutsizey;
+ float bboundx, bboundy;
+ float tboundx, tboundy;
+
+ int xcuts, ycuts;
+ char vianame[128];
+ DSEG newseg;
+ LefList cutrec;
+
+ float borderx, bordery, spacingx, spacingy;
+ float fwidth, fheight;
+ float x, y;
+ int i, j;
+
+ int ltop = lbot + 1;
+
+ /* Convert width and height to microns */
+ fwidth = (float)width / scale;
+ fheight = (float)height / scale;
+
+ sprintf(vianame, "%s_post", lefl->lefName);
+ viagen->lefName = strdup(vianame);
+
+ /* Determine number of cuts in X and Y */
+
+ cutsizex = LefGetViaWidth(lefl, lcut, 0);
+ cutsizey = LefGetViaWidth(lefl, lcut, 1);
+ bboundx = LefGetViaWidth(lefl, lbot, 0);
+ bboundy = LefGetViaWidth(lefl, lbot, 1);
+ tboundx = LefGetViaWidth(lefl, ltop, 0);
+ tboundy = LefGetViaWidth(lefl, ltop, 1);
+
+ /* Calculate number of cuts to fit */
+
+ borderx = (((tboundx > bboundx) ? tboundx : bboundx) - cutsizex) / 2;
+ bordery = (((tboundy > bboundy) ? tboundy : bboundy) - cutsizey) / 2;
+
+ /* If there is a SPACING record in the via, use it. If not, see if */
+ /* there is a SPACING record in the record for the via cut. If */
+ /* not, then assume spacing is twice the border width. */
+
+ cutrec = lefl;
+ if (cutrec->info.via.spacing == NULL) cutrec = LefFindLayerByNum(lcut);
+ if (cutrec && cutrec->info.via.spacing) {
+ spacingx = cutrec->info.via.spacing->spacing;
+ if (cutrec->info.via.spacing->next)
+ spacingy = cutrec->info.via.spacing->next->spacing;
+ else
+ spacingy = spacingx;
+ }
+ else {
+ spacingx = 2 * borderx;
+ spacingy = 2 * bordery;
+ }
+
+ xcuts = 1 + (int)((fwidth - 2 * borderx) - cutsizex) / (cutsizex + spacingx);
+ ycuts = 1 + (int)((fheight - 2 * bordery) - cutsizey) / (cutsizey + spacingy);
+
+ /* Ensure at least one cut! */
+ if (xcuts < 1) xcuts = 1;
+ if (ycuts < 1) ycuts = 1;
+
+ /* Make sure that width and height are enough to pass DRC. Height */
+ /* in particular is taken from the width of the power bus and may */
+ /* not be wide enough for the topmost contacts. */
+
+ fwidth = (xcuts * cutsizex) + ((float)(xcuts - 1) * spacingx) + (2 * borderx);
+ fheight = (ycuts * cutsizey) + ((float)(ycuts - 1) * spacingy) + (2 * bordery);
+
+ viagen->info.via.area.layer = lbot;
+ viagen->info.via.area.x1 = -fwidth / 2;
+ viagen->info.via.area.x2 = fwidth / 2;
+ viagen->info.via.area.y1 = -fheight / 2;
+ viagen->info.via.area.y2 = fheight / 2;
+
+ newseg = (DSEG)malloc(sizeof(struct dseg_));
+ newseg->layer = ltop;
+ newseg->x1 = -fwidth / 2;
+ newseg->x2 = fwidth / 2;
+ newseg->y1 = -fheight / 2;
+ newseg->y2 = fheight / 2;
+ newseg->next = NULL;
+ viagen->info.via.lr = newseg;
+
+ x = (-fwidth / 2) + borderx + (cutsizex / 2);
+ for (i = 0; i < xcuts; i++) {
+ y = (-fheight / 2) + bordery + (cutsizey / 2);
+ for (j = 0; j < ycuts; j++) {
+ newseg = (DSEG)malloc(sizeof(struct dseg_));
+ newseg->layer = lcut;
+ newseg->x1 = x - (cutsizex / 2);
+ newseg->x2 = x + (cutsizex / 2);
+ newseg->y1 = y - (cutsizey / 2);
+ newseg->y2 = y + (cutsizey / 2);
+ newseg->next = viagen->info.via.lr;
+ viagen->info.via.lr = newseg;
+ y += (cutsizey + spacingy);
+ }
+ x += (cutsizex + spacingy);
+ }
+}
+
+/*--------------------------------------------------------------*/
+/* Routine to check if a LefList entry is a valid via record */
+/* for a via between metal layers "l" (ell) and "l + 1". */
+/* Since the LefList record drops metal and cut layers more or */
+/* less randomly into the area, lr, and lr->next records, all */
+/* combinations of layers need to be checked. */
+/* */
+/* Since the metal layers are known and the cut layer is not */
+/* necessarily known, return the cut layer number if the via */
+/* record is valid. If not, return -1. */
+/*--------------------------------------------------------------*/
+
+int
+check_valid_via(LefList lefl, int l)
+{
+ int cutlayer = -1;
+
+ if (lefl->info.via.area.layer == l) {
+ if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) {
+ if (lefl->info.via.lr->next)
+ cutlayer = lefl->info.via.lr->next->layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l + 1) {
+ cutlayer = lefl->info.via.lr->layer;
+ }
+ }
+ else if (lefl->info.via.area.layer == l + 1) {
+ if (lefl->info.via.lr && lefl->info.via.lr->layer == l) {
+ if (lefl->info.via.lr->next)
+ cutlayer = lefl->info.via.lr->next->layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l) {
+ cutlayer = lefl->info.via.lr->layer;
+ }
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->layer == l) {
+ if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l + 1)
+ cutlayer = lefl->info.via.area.layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) {
+ if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l)
+ cutlayer = lefl->info.via.area.layer;
+ }
+ return cutlayer;
+}
+
+/*--------------------------------------------------------------*/
+/* Generate stripe contact posts and metal */
+/*--------------------------------------------------------------*/
+
+PSTRIPE
+generate_stripes(SINFO stripevals, FILLLIST fillcells,
+ COREBBOX corearea, char *pattern,
+ char *VddNet, char *GndNet, float scale, unsigned char Flags)
+{
+ int i, j, l, p, n, y, hh;
+ int testuse;
+ int ltop, lbot;
+ float syt, syb;
+ double vw, vh;
+ int cutlayer, lcut;
+ int gnd_ymin, gnd_ymax, vdd_ymin, vdd_ymax, gate_ymin;
+ int gnd_xmin, vdd_xmin, tmp;
+ char *powername, *groundname;
+ int corew;
+
+ PSTRIPE rails = NULL, prail;
+ PPOST post;
+ LefList lefl, *vialist, topvia, vvalid;
+ DSEG lr;
+
+ /* Pick the first fill cell and parse it for the POWER and */
+ /* GROUND pins as marked by pin USE. If no pins are marked */
+ /* as use POWER or GROUND then look for pins with names */
+ /* matching the power and ground nets. */
+
+ GATE fillgate = fillcells->gate;
+ DSEG r;
+ ROW row;
+
+ lbot = 0;
+ for (i = 0; i < fillgate->nodes; i++) {
+ testuse = fillgate->use[i];
+ if (testuse == PORT_USE_POWER) {
+ powername = fillgate->node[i];
+ break;
+ }
+ }
+ if (i == fillgate->nodes) {
+ for (i = 0; i < fillgate->nodes; i++) {
+ if (!strcmp(fillgate->node[i], VddNet)) {
+ powername = VddNet;
+ lbot = fillgate->taps[i]->layer;
+ break;
+ }
+ }
+ if (i == fillgate->nodes) {
+ fprintf(stderr, "Failed to find power net pin in cell macro.\n");
+ return NULL;
+ }
+ }
+ /* NOTE: Need to parse all taps; find one that crosses the whole */
+ /* cell. If none, then find one that touches or overlaps the cell */
+ /* bottom or top. */
+
+ r = fillcells->gate->taps[i];
+ vdd_ymin = (int)(roundf(r->y1 * scale));
+ vdd_ymax = (int)(roundf(r->y2 * scale));
+ vdd_xmin = (int)(roundf(r->x1 * scale));
+
+ for (j = 0; j < fillgate->nodes; j++) {
+ testuse = fillgate->use[j];
+ if (testuse == PORT_USE_GROUND) {
+ groundname = fillgate->node[j];
+ break;
+ }
+ }
+ if (j == fillgate->nodes) {
+ for (j = 0; j < fillgate->nodes; j++) {
+ if (!strcmp(fillgate->node[j], GndNet)) {
+ groundname = GndNet;
+ lbot = fillgate->taps[i]->layer;
+ break;
+ }
+ }
+ if (j == fillgate->nodes) {
+ fprintf(stderr, "Failed to find ground net pin in cell macro.\n");
+ return NULL;
+ }
+ }
+ r = fillcells->gate->taps[j];
+ gnd_ymin = (int)(roundf(r->y1 * scale));
+ gnd_ymax = (int)(roundf(r->y2 * scale));
+ gnd_xmin = (int)(roundf(r->x1 * scale));
+
+ /* If the first row is inverted then the ymin/ymax values need to be */
+ /* adjusted. */
+
+ row = DefLowestRow(); /* Try this first */
+ if (row) {
+ if (row->orient & RS) {
+ gnd_ymax = corearea->siteh - gnd_ymax;
+ gnd_ymin = corearea->siteh - gnd_ymin;
+ vdd_ymax = corearea->siteh - vdd_ymax;
+ vdd_ymin = corearea->siteh - vdd_ymin;
+
+ tmp = gnd_ymax;
+ gnd_ymax = gnd_ymin;
+ gnd_ymin = tmp;
+
+ tmp = vdd_ymax;
+ vdd_ymax = vdd_ymin;
+ vdd_ymin = tmp;
+ }
+ }
+ else {
+ if (corearea->orient & RS) {
+ gnd_ymax = corearea->siteh - gnd_ymax;
+ gnd_ymin = corearea->siteh - gnd_ymin;
+ vdd_ymax = corearea->siteh - vdd_ymax;
+ vdd_ymin = corearea->siteh - vdd_ymin;
+
+ tmp = gnd_ymax;
+ gnd_ymax = gnd_ymin;
+ gnd_ymin = tmp;
+
+ tmp = vdd_ymax;
+ vdd_ymax = vdd_ymin;
+ vdd_ymin = tmp;
+ }
+ }
+
+ n = strlen(pattern);
+
+ /* Find the highest metal layer that is oriented vertically */
+
+ ltop = LefGetMaxRouteLayer() - 1;
+ if (LefGetRouteOrientation(ltop) == 1) ltop--;
+ if (ltop < 3) {
+ int mspace;
+
+ fprintf(stderr, "Will not generate over-cell power stripes due to lack "
+ "of route layers\n");
+ fprintf(stderr, "Generating comb structures instead.\n");
+
+ /* Generate comb structures in metal1 on either side to connect */
+ /* power and ground. */
+
+ /* Get wide spacing rule relative to stripe width */
+ mspace = (int)(roundf(LefGetRouteWideSpacing(lbot,
+ (float)(stripevals->width) / scale) * scale));
+
+ /* Account for ground or power bus extending beyond the cell */
+ /* bounding box. Assumes that the extension is symmetric on */
+ /* left and right, and the same for power and ground. */
+
+ if (gnd_xmin < 0) mspace -= gnd_xmin;
+ else if (vdd_xmin < 0) mspace -= vdd_xmin;
+ corew = corearea->sitew;
+
+ /* Generate power comb on left */
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = -mspace - stripevals->width / 2;
+ prail->pitch = corearea->urx_exp;
+ prail->num = 1;
+ if ((n < 1) || (pattern[0] == 'P')) {
+ prail->name = VddNet;
+ y = corearea->lly + (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+ else {
+ prail->name = GndNet;
+ y = corearea->lly + (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = lbot;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = (float)(corearea->lly - hh) / scale;
+ prail->stripe->y2 = (float)(corearea->ury + hh) / scale;
+ prail->posts = NULL;
+
+ /* Create all posts */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = NULL;
+ newpost->strut->layer = lbot;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = 0.0;
+ newpost->strut->x2 = (float)(-prail->offset + corew) / scale;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ prail->offset += corearea->llx;
+
+ /* Generate ground comb on right */
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = mspace + stripevals->width / 2;
+ prail->pitch = corearea->urx_exp;
+ prail->num = 1;
+ if ((n < 2) || (pattern[1] == 'G')) {
+ prail->name = GndNet;
+ y = corearea->lly + (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+ else {
+ prail->name = VddNet;
+ y = corearea->lly + (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = lbot;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = (float)(corearea->lly - hh) / scale;
+ prail->stripe->y2 = (float)(corearea->ury + hh) / scale;
+ prail->posts = NULL;
+
+ /* Create all posts */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = NULL;
+ newpost->strut->layer = lbot;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = (float)(-prail->offset - corew) / scale;
+ newpost->strut->x2 = 0.0;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ prail->offset += corearea->urx_exp;
+ return rails;
+ }
+
+ /* Generate vias for posts */
+ /* NOTE: This assumes that the power and ground rails are the same */
+ /* height. If not, need to have two vialist records, one for each rail */
+
+ vialist = (LefList *)malloc((ltop - lbot) * sizeof(LefList));
+ for (l = lbot; l < ltop; l++) vialist[l] = (LefList)NULL;
+
+ /* First find any VIARULE GENERATE vias; these are preferred, as they */
+ /* have all the right information for generating a new via for the */
+ /* post. */
+
+ for (l = lbot; l < ltop; l++) {
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->lefClass == CLASS_VIA) {
+ if (lefl->info.via.generated) {
+
+ /* Check for top and bottom layers matching (l) and (l + 1) */
+
+ if ((cutlayer = check_valid_via(lefl, l)) != -1) {
+ vialist[l] = LefNewVia(NULL);
+ via_make_generated(vialist[l], lefl, l, cutlayer,
+ stripevals->width, gnd_ymax - gnd_ymin, scale);
+ break; // Continue to next layer
+ }
+ }
+ }
+ }
+ }
+
+ /* Next find any VIAs that have the right layers. Use this information */
+ /* to create a new VIA record with the correct size for the post. */
+
+ for (l = lbot; l < ltop; l++) {
+ if (vialist[l] == NULL) {
+ vvalid = NULL;
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->lefClass == CLASS_VIA) {
+ if ((lcut = check_valid_via(lefl, l)) != -1) {
+ /* Don't include vias that this routine has created, */
+ if (strstr(lefl->lefName, "_post") != NULL) continue;
+ if (vvalid == NULL) {
+ vvalid = lefl;
+ vw = LefGetViaWidth(lefl, lcut, 0);
+ vh = LefGetViaWidth(lefl, lcut, 1);
+ cutlayer = lcut;
+ }
+ /* Find the smallest valid via. Note that it */
+ /* is preferred to find a via designed for a */
+ /* power post, if there is one (to be done). */
+ else {
+ double tw, th;
+ tw = LefGetViaWidth(lefl, lcut, 0);
+ th = LefGetViaWidth(lefl, lcut, 1);
+ if ((th < vh) || ((th == vh) && (tw < vw))) {
+ vvalid = lefl;
+ vw = tw;
+ vh = th;
+ cutlayer = lcut;
+ }
+ }
+ }
+ }
+ }
+ if (vvalid) {
+ vialist[l] = LefNewVia(NULL);
+ via_make_generated(vialist[l], vvalid, l, cutlayer,
+ stripevals->width, gnd_ymax - gnd_ymin, scale);
+ }
+ }
+ }
+
+ for (l = lbot; l < ltop; l++) {
+ if (vialist[l] == NULL) {
+ LefList ll0, ll1;
+ ll0 = LefFindLayerByNum(l);
+ ll1 = LefFindLayerByNum(l + 1);
+ fprintf(stderr, "Error: Failed to find a valid via record between "
+ "metal layers %s and %s\n",
+ ll0->lefName, ll1->lefName);
+ return NULL;
+ }
+ }
+
+ /* Construct power stripe records */
+
+ for (p = 0; p < n; p++) {
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = stripevals->offset + p * stripevals->pitch;
+ prail->pitch = stripevals->pitch * n;
+ prail->num = 1 + (corearea->urx_exp - prail->offset) / prail->pitch;
+
+ /* Note this is not strdup(), so can compare string pointers */
+ prail->name = (pattern[p] == 'P') ? VddNet : GndNet;
+
+ /* Find vertical dimensions of power rails on the standard cells */
+ y = corearea->lly;
+ if (pattern[p] == 'P') {
+ y += (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+ else {
+ y += (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+
+ /* Query the extent of the highest via layer and make sure the */
+ /* power stripe covers it completely. */
+
+ topvia = vialist[ltop - 1];
+ syb = topvia->info.via.area.y1;
+ syt = topvia->info.via.area.y2;
+ for (lr = topvia->info.via.lr; lr; lr = lr->next) {
+ if (lr->y1 < syb) syb = lr->y1;
+ if (lr->y2 > syt) syt = lr->y2;
+ }
+
+ /* First two rails, extend down by one track pitch. A pin will */
+ /* be added here. */
+ if (p < 2) {
+ syb = -LefGetRoutePitch(ltop - 1);
+ if (syb > topvia->info.via.area.y1)
+ syb -= LefGetRoutePitch(ltop - 1);
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = ltop;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = syb + ((float)corearea->lly / scale);
+ prail->stripe->y2 = syt + ((float)corearea->ury / scale);
+ prail->posts = NULL;
+
+ /* Create all posts (also horizontally centered at X = 0) */
+
+ /* To be done: Check if rows are alternating N and S */
+ /* or not. This code assumes that they always are. */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ for (l = lbot; l < ltop; l++) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = vialist[l];
+ newpost->strut->layer = l;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = -(float)(stripevals->width / 2) / scale;
+ newpost->strut->x2 = (float)(stripevals->width / 2) / scale;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ }
+ }
+
+ /* link via records and prepend to LefInfo */
+ for (l = lbot; l < ltop - 1; l++)
+ vialist[l]->next = vialist[l + 1];
+
+ vialist[l]->next = LefInfo;
+ LefInfo = vialist[0];
+
+ free(vialist);
+ return rails;
+}
+
+/*--------------------------------------------------------------*/
+/* Convert orient bitfield from GATE structure to character */
+/* string for a DEF file. */
+/*--------------------------------------------------------------*/
+
+char *gate_to_orient(int orient)
+{
+ int oidx;
+ static char *orients[8] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"};
+
+ switch (orient & (RN | RS | RE | RW)) {
+ case RS:
+ oidx = 1;
+ break;
+ case RE:
+ oidx = 2;
+ break;
+ case RW:
+ oidx = 3;
+ break;
+ default:
+ oidx = 0;
+ break;
+ }
+ if (orient & RF) oidx += 4;
+ return orients[oidx];
+}
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+void
+output_rail(FILE *outfptr, PSTRIPE rail, int x, int first, float scale)
+{
+ PPOST post;
+ LefList lefl;
+ char *otyp;
+ float fyd, fya, fxd, fxa;
+ int iyd, iya, ixd, ixa;
+
+ static char *otypes[] = {"+ FIXED", " NEW"};
+
+ otyp = (first) ? otypes[0] : otypes[1];
+
+ for (post = rail->posts; post; post = post->next) {
+ lefl = LefFindLayerByNum(post->strut->layer);
+ fyd = post->strut->y2 - post->strut->y1;
+ fya = (post->strut->y2 + post->strut->y1) / 2;
+ iyd = (int)(roundf(fyd * scale));
+ iya = (int)(roundf(fya * scale));
+ if (post->viagen) {
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * * ) %s",
+ otyp, lefl->lefName, iyd, x, iya, post->viagen->lefName);
+ }
+ else {
+ fxd = post->strut->x1;
+ ixd = x + (int)(roundf(fxd * scale));
+ fxa = post->strut->x2;
+ ixa = x + (int)(roundf(fxa * scale));
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( %d * )",
+ otyp, lefl->lefName, iyd, ixd, iya, ixa);
+ }
+ otyp = otypes[1];
+ }
+ lefl = LefFindLayerByNum(rail->stripe->layer);
+ fxd = rail->stripe->x2 - rail->stripe->x1;
+ fya = rail->stripe->y1;
+ fyd = rail->stripe->y2;
+ ixd = (int)(roundf(fxd * scale));
+ iya = (int)(roundf(fya * scale));
+ iyd = (int)(roundf(fyd * scale));
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * %d )",
+ otyp, lefl->lefName, ixd, x, iya, iyd);
+}
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+void
+output_rails(FILE *outfptr, PSTRIPE rail, COREBBOX corearea, float scale, int first)
+{
+ int i, x;
+
+ x = rail->offset;
+ for (i = 0; i < rail->num; i++) {
+ output_rail(outfptr, rail, x, first, scale);
+ first = FALSE;
+ x += rail->pitch;
+ if (x > corearea->urx_exp) break;
+ }
+}
+
+/*--------------------------------------------------------------*/
+/* write_output --- */
+/* */
+/* write the modified DEF file to the output. */
+/*--------------------------------------------------------------*/
+
+void
+write_output(char *definname, char *defoutname, float scale,
+ COREBBOX corearea, SINFO stripevals, PSTRIPE rails,
+ char *VddNet, char *GndNet, unsigned char Flags)
+{
+ FILE *outfptr, *infptr;
+ static char line[LEF_LINE_MAX + 2];
+ char *sptr;
+ int i, copyspecial = 0, numVias, foundrail[2], ltop;
+ double lh, ly;
+
+ GATE gate, endgate;
+ NET net;
+ NODE node;
+ PPOST post;
+ LefList lefl, lname, lrec;
+ DSEG seg;
+ PSTRIPE rail;
+
+ if (defoutname == NULL)
+ outfptr = stdout;
+ else {
+ outfptr = fopen(defoutname, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Failed to open file %s for writing modified output\n",
+ defoutname);
+ return;
+ }
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Writing DEF file output.\n");
+
+ /* Find the number of (new) power rail SPECIALNETS to be written */
+ /* There will normally be one record for power and one for ground */
+ /* unless rails were not written. Power and ground rails are */
+ /* checked separately for consistency of the output DEF. */
+
+ foundrail[0] = foundrail[1] = FALSE;
+ for (rail = rails; rail; rail = rail->next) {
+ if ((foundrail[0] == FALSE) && (rail->name == VddNet)) foundrail[0] = TRUE;
+ if ((foundrail[1] == FALSE) && (rail->name == GndNet)) foundrail[1] = TRUE;
+ }
+ numSpecial = ((foundrail[0] == TRUE) ? 1 : 0) + ((foundrail[1] == TRUE) ? 1 : 0);
+
+ /* Write DEF header (copy input DEF file verbatim up to COMPONENTS) */
+
+ infptr = fopen(definname, "r");
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before COMPONENTS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "COMPONENTS", 10)) break;
+
+ /* Rewrite DIEAREA, ROWS, and TRACKS */
+ else if (!strncmp(sptr, "DIEAREA", 7)) {
+ char *dptr;
+ int dllx, dlly, durx, dury;
+
+ dptr = strchr(line, '(');
+ sscanf(dptr + 1, "%d %d", &dllx, &dlly);
+ dptr = strchr(dptr + 1, '(');
+ sscanf(dptr + 1, "%d %d", &durx, &dury);
+
+ durx += stripevals->stretch;
+
+ fprintf(outfptr, "DIEAREA ( %d %d ) ( %d %d ) ;\n",
+ dllx, dlly, durx, dury);
+ }
+
+ else if (!strncmp(sptr, "ROW", 3)) {
+ char *dptr;
+ int radd, xnum, ridx, rowy;
+ ROW row;
+ char namepos[16];
+ radd = stripevals->stretch / corearea->sitew;
+ static char *orientations[] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"};
+
+ dptr = sptr;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rowy);
+ row = DefFindRow(rowy);
+
+ xnum = row->xnum + radd;
+ switch (row->orient & (RN | RS | RE | RW)) {
+ case RS:
+ ridx = 1;
+ break;
+ case RE:
+ ridx = 2;
+ break;
+ case RW:
+ ridx = 3;
+ break;
+ default:
+ ridx = 0;
+ }
+ if (row->orient & RF) ridx += 4;
+
+ fprintf(outfptr, "ROW %s %s %d %d %s DO %d BY %d STEP %d %d ;\n",
+ row->rowname, row->sitename, row->x, row->y,
+ orientations[ridx], xnum, row->ynum,
+ row->xstep, row->ystep);
+ }
+ else if (!strncmp(sptr, "TRACKS", 6)) {
+ char *dptr;
+ char o;
+ char layer[64];
+ int roffset, rnum, rpitch;
+
+ dptr = sptr + 6;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%c", &o);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &roffset);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rnum);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rpitch);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%s", layer);
+
+ if (o == 'X') {
+ rnum += (int)(stripevals->stretch / rpitch);
+ if (stripevals->stretch % rpitch != 0) rnum++;
+ }
+ fprintf(outfptr, "TRACKS %c %d DO %d STEP %d LAYER %s ;\n",
+ o, roffset, rnum, rpitch, layer);
+ }
+ else
+ fprintf(outfptr, "%s", line);
+ }
+
+ /* Write generated vias for posts */
+
+ numVias = 0;
+ for (lefl = LefInfo; lefl; lefl = lefl->next)
+ if (strstr(lefl->lefName, "_post") != NULL)
+ numVias++;
+
+ fprintf(outfptr, "VIAS %d ;\n", numVias);
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ int llx, lly, urx, ury;
+
+ if (strstr(lefl->lefName, "_post") != NULL) {
+ fprintf(outfptr, "- %s\n", lefl->lefName);
+ lname = LefFindLayerByNum(lefl->info.via.area.layer);
+ llx = (int)(roundf(lefl->info.via.area.x1 * scale));
+ lly = (int)(roundf(lefl->info.via.area.y1 * scale));
+ urx = (int)(roundf(lefl->info.via.area.x2 * scale));
+ ury = (int)(roundf(lefl->info.via.area.y2 * scale));
+ fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )",
+ lname->lefName, llx, lly, urx, ury);
+ if (lefl->info.via.lr) fprintf(outfptr, "\n");
+ for (seg = lefl->info.via.lr; seg; seg = seg->next) {
+ lname = LefFindLayerByNum(seg->layer);
+ llx = (int)(roundf(seg->x1 * scale));
+ lly = (int)(roundf(seg->y1 * scale));
+ urx = (int)(roundf(seg->x2 * scale));
+ ury = (int)(roundf(seg->y2 * scale));
+ fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )",
+ lname->lefName, llx, lly, urx, ury);
+ if (seg->next) fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, " ;\n");
+ }
+ }
+ fprintf(outfptr, "END VIAS\n\n");
+
+ for (endgate = Nlgates; endgate->next; endgate = endgate->next);
+
+ if (Numgates > 0) {
+
+ /* Write instances (COMPONENTS) in the order read */
+ fprintf(outfptr, "COMPONENTS %d ;\n", Numgates);
+ for (gate = endgate; gate ; gate = gate->last) {
+ int px, py;
+ if (gate->gatetype == NULL) continue;
+
+ px = (int)(roundf(gate->placedX * scale));
+ py = (int)(roundf(gate->placedY * scale));
+ fprintf(outfptr, "- %s %s + PLACED ( %d %d ) %s ;\n",
+ gate->gatename, gate->gatetype->gatename,
+ px, py, gate_to_orient(gate->orient));
+ }
+ fprintf(outfptr, "END COMPONENTS\n\n");
+ }
+
+ if (Numpins > 0) {
+ int llx, lly, urx, ury, px, py;
+ static char *pin_classes[] = {
+ "DEFAULT", "INPUT", "OUTPUT", "OUTPUT TRISTATE", "INOUT", "FEEDTHRU"
+ };
+ LefList lefl;
+
+ /* Write instances (PINS) in the order read, plus power pins */
+
+ fprintf(outfptr, "PINS %d ;\n", Numpins + numSpecial);
+
+ if (foundrail[0]) {
+ for (rail = rails; rail; rail = rail->next)
+ if (rail->name == VddNet) break;
+
+ ltop = rail->stripe->layer;
+ lrec = LefFindLayerByNum(ltop);
+ lh = LefGetRoutePitch(ltop - 1) / 4;
+ ly = rail->stripe->y1 + lh;
+
+ /* NOTE: The over-simplified "arrangepins" Tcl script expects */
+ /* LAYER and PLACED records to be on separate lines. This will */
+ /* eventually be replaced by a C coded executable with a more */
+ /* rigorous parser, like addspacers uses. */
+
+ fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", VddNet, VddNet);
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lrec->lefName,
+ (int)(roundf(rail->stripe->x1 * scale)),
+ (int)(roundf(-lh * scale)),
+ (int)(roundf(rail->stripe->x2 * scale)),
+ (int)(roundf(lh * scale)));
+ fprintf(outfptr, " + PLACED ( %d %d ) N ;\n",
+ rail->offset, (int)(roundf(ly * scale)));
+ }
+ if (foundrail[1]) {
+ for (rail = rails; rail; rail = rail->next)
+ if (rail->name == GndNet) break;
+
+ ltop = rail->stripe->layer;
+ lrec = LefFindLayerByNum(ltop);
+ lh = LefGetRoutePitch(ltop - 1) / 4;
+ ly = rail->stripe->y1 + lh;
+
+ fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", GndNet, GndNet);
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lrec->lefName,
+ (int)(roundf(rail->stripe->x1 * scale)),
+ (int)(roundf(-lh * scale)),
+ (int)(roundf(rail->stripe->x2 * scale)),
+ (int)(roundf(lh * scale)));
+ fprintf(outfptr, " + PLACED ( %d %d ) N ;\n",
+ rail->offset, (int)(roundf(ly * scale)));
+ }
+
+ for (gate = endgate; gate ; gate = gate->last) {
+ int dir;
+
+ if (gate->gatetype != NULL) continue;
+
+ fprintf(outfptr, "- %s + NET %s",
+ gate->gatename, gate->node[0]);
+
+ if (gate->direction[0] != 0)
+ fprintf(outfptr, " + DIRECTION %s",
+ pin_classes[gate->direction[0]]);
+
+ fprintf(outfptr, "\n");
+
+ lefl = LefFindLayerByNum(gate->taps[0]->layer);
+ urx = (int)(roundf((gate->taps[0]->x2 - gate->taps[0]->x1) * scale) / 2.0);
+ ury = (int)(roundf((gate->taps[0]->y2 - gate->taps[0]->y1) * scale) / 2.0);
+ llx = -urx;
+ lly = -ury;
+ px = (int)(roundf(gate->placedX * scale));
+ py = (int)(roundf(gate->placedY * scale));
+
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lefl->lefName, llx, lly, urx, ury);
+ fprintf(outfptr, " + PLACED ( %d %d ) %s ;\n",
+ px, py, gate_to_orient(gate->orient));
+ }
+ fprintf(outfptr, "END PINS\n\n");
+ }
+
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before NETS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "NETS", 4)) break;
+
+ }
+ fprintf(outfptr, "%s", line);
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before END NETS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "SPECIALNETS", 11)) {
+ sscanf(sptr + 11, "%d", &copyspecial);
+ break;
+ }
+ else if (!strncmp(sptr, "END DESIGN", 10)) {
+ break;
+ }
+ fprintf(outfptr, "%s", line);
+ }
+
+ /* Rewrite SPECIALNETS line with updated number */
+
+ if (copyspecial + numSpecial > 0)
+ fprintf(outfptr, "SPECIALNETS %d ;\n", numSpecial + copyspecial);
+
+ if (numSpecial > 0) {
+ /* Write power bus stripes (SPECIALNETS) */
+ int i, first = TRUE;
+ char *railnames[2] = {GndNet, VddNet};
+
+ for (i = 0; i < 2; i++) {
+ fprintf(outfptr, "- %s", railnames[i]);
+ for (rail = rails; rail; rail = rail->next) {
+ if (rail->name == railnames[i]) {
+ output_rails(outfptr, rail, corearea, scale, first);
+ first = FALSE;
+ }
+ }
+ fprintf(outfptr, " ;\n");
+ first = TRUE;
+ }
+ }
+
+ /* If there were previously no SPECIALNETS then add the ending line */
+ if (numSpecial > 0 && copyspecial == 0)
+ fprintf(outfptr, "END SPECIALNETS\n\n");
+
+ /* Copy the remainder of the file verbatim */
+
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) break;
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ if (!strncmp(sptr, "END DESIGN", 10)) {
+ break;
+ }
+ fprintf(outfptr, "%s", line);
+ }
+ fprintf(outfptr, "END DESIGN\n");
+ fclose(infptr);
+
+ if (defoutname != NULL) fclose(outfptr);
+ fflush(stdout);
+}
+
+/*--------------------------------------------------------------*/
+/* helpmessage - tell user how to use the program */
+/* */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "addspacers [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "addspacers adds fill cells and power buses to a layout.\n");
+ fprintf(outf, "Output on stdout unless redirected with -o option.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, " -o <path> Output file path and name\n");
+ fprintf(outf, " -l <path> Path to standard cell LEF file (for macro list)\n");
+ fprintf(outf, " -p <name> Name of power net\n");
+ fprintf(outf, " -g <name> Name of ground net\n");
+ fprintf(outf, " -f <name> Name of fill cell (or prefix)\n");
+ fprintf(outf, " -w <width> Power bus stripe width\n");
+ fprintf(outf, " -P <pitch> Power bus stripe pitch\n");
+ fprintf(outf, " -s <pattern> Power bus stripe pattern (default \"PG\") \n");
+ fprintf(outf, " -n Do not stretch layout under power buses.\n");
+ fprintf(outf, " -O Handle obstruction areas in separate .obs file\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -v Verbose output\n");
+ fprintf(outf, " -h Print this message\n");
+
+} /* helpmessage() */
+