#include #include #include #include "defs.h" #include "types.h" #include "exports.h" #include "transforms.h" #include "tmpfile.h" public void (*OutputLabel)(); public void (*OutputBBox)(); private Real PAGEWIDTH; /* from the device definition */ private Real PAGEHEIGHT; private Real RESOLUTION; private int currLayer; /* layer to be flattened */ private Tmatrix ctm; /* top level transformation */ private int sizeX; /* maximum coordinate in x */ private int sizeY; /* maximum coordinate in y */ private int range; /* range of numbers for spliting */ /* * Get size of the plot. Do a depth-first traversal of the call tree, * at each step calculate the bounding box (bigBox) of a cell enlarged * by the size of its children's bounding box (transformed to the parent's * coordinate system). At the end root->cell is the size of the plot. */ private void GetBBox( cell ) Cell cell; { Call c; BBox bb1, bb2; if( cell->bigBox.minC.x < cell->bigBox.maxC.x ) /* bounding box calculated in previous instantiation of this cell */ return; cell->bigBox = cell->bBox; /* initialize with its own box */ for( c = cell->calls; c != NULL; c = c->next ) { GetBBox( c->cell ); /* recursively calculate children's box */ bb1 = c->cell->bigBox; { register Real *ctm = c->tm; TransformBBox( bb1, ctm, bb2 ); } /* enlarge box by its children */ MIN( cell->bigBox.minC.x, bb2.minC.x ) MIN( cell->bigBox.minC.y, bb2.minC.y ) MAX( cell->bigBox.maxC.x, bb2.maxC.x ) MAX( cell->bigBox.maxC.y, bb2.maxC.y ) } } /* * Calculate the scale factor (if not already set by the user). If required * query the user for confirmation (or change in scale). Set the root-level * transformation matrix as required. */ public Real SetScale( ask, onePage, amplify, scaleFac, noRotate ) int ask, onePage, amplify; Real scaleFac; { BBox *theBBox; Real width, height; Real scale; Real QueryScale(); PAGEWIDTH = device->pagewidth; PAGEHEIGHT = device->pageheight; RESOLUTION = device->resolution; GetBBox( root->cell ); theBBox = & root->cell->bigBox; width = theBBox->maxC.x - theBBox->minC.x; height = theBBox->maxC.y - theBBox->minC.y; if( width <= 0 or height <= 0 ) Crash( "abort...nothing to plot\n", 0 ); if( onePage ) { if( width > height and noRotate == FALSE ) scale = min( PAGEHEIGHT / width, PAGEWIDTH / height ); else scale = min( PAGEHEIGHT / height, PAGEWIDTH / width ); scaleFac = ( 100.0 * scale ) / RESOLUTION; } else { if( amplify ) { scaleFac = (Real) ( ((int) (scaleFac + 0.5)) / 25400.0 ); } scale = ( scaleFac * RESOLUTION ) / 100.0; } if( ask ) { scale = QueryScale( width, height, scale, scaleFac, noRotate ); scaleFac = ( 100.0 * scale / RESOLUTION ); } GetTotalRects( root, 0 ); if( width > height and noRotate == FALSE ) { xPages = (int) ( (scale * height + PAGEWIDTH - 1) / PAGEWIDTH ); yPages = (int) ( (scale * width + PAGEHEIGHT - 1) / PAGEHEIGHT ); root->tm[ 0 ] = 0.0; root->tm[ 2 ] = scale; root->tm[ 4 ] = -theBBox->minC.y * scale; root->tm[ 1 ] = -scale; root->tm[ 3 ] = 0.0; root->tm[ 5 ] = (width + theBBox->minC.x ) * scale; sizeX = (int) ( height * scale + 0.5); sizeY = (int) ( width * scale + 0.5 ); } else { xPages = (int) ( (scale * width + PAGEWIDTH - 1) / PAGEWIDTH ); yPages = (int) ( (scale * height + PAGEHEIGHT - 1) / PAGEHEIGHT ); root->tm[ 0 ] = scale; root->tm[ 2 ] = 0.0; root->tm[ 4 ] = -theBBox->minC.x * scale; root->tm[ 1 ] = 0.0; root->tm[ 3 ] = scale; root->tm[ 5 ] = -theBBox->minC.y * scale; sizeX = (int) ( width * scale + 0.5); sizeY = (int) ( height * scale + 0.5 ); } return( scaleFac ); } #define MSG1 "\nscale = %f (%dx)\n" #define MSG2 "The Plot will be: %.2f by %.2f inches (%d pages)\n" #define MSG3 "Do you want a plot (Yes/No/ChangeScale) [y] ? " /* * Query the user for confirmation. Allow change in scale factor. * Return the scale factor to be used or exit if answer is 'n'. */ private Real QueryScale( w, h, scale, scaleFac, noRotate ) Real w, h, scale, scaleFac; int noRotate; { Real tmp; char line[ 20 ], *s, *s2; int xPages, yPages, itmp; int magnification; int asking = TRUE; if( w > h and noRotate == FALSE ) { tmp = h; /* swap width & heigth */ h = w; w = tmp; } setbuf( stdin, NULL ); /* unbuffered input -> avoid call to malloc */ while( asking ) { xPages = (int) ( (scale * w + PAGEWIDTH - 1) / PAGEWIDTH ); yPages = (int) ( (scale * h + PAGEHEIGHT - 1) / PAGEHEIGHT ); magnification = (int) ( 25400.0 * scaleFac + 0.5 ); tmp = scale / RESOLUTION; fprintf( stderr, MSG1, scaleFac, magnification ); fprintf( stderr, MSG2, w*tmp, h*tmp, xPages*yPages ); fprintf( stderr, MSG3 ); fflush( stdin ); (void) fgets( line, 20, stdin ); for( s = line; *s != '\0'; s++ ) if( *s > ' ' or *s == '\n' ) break; switch( *s ) { case 'n' : case 'N' : Crash( "Plot Aborted\n", 0 ); break; case 'y' : case 'Y' : case '\n' : asking = FALSE; break; case 'c' : case 'C' : fprintf( stderr, "\nEnter new scale factor : " ); (void) fgets( line, 10, stdin ); if( (s2 = strchr( line, 'x')) != NULL) { *s2 = '\0'; if ( sscanf( line, "%d", &itmp) != 1 or itmp <= 0 ) fprintf( stderr, "What ???"); else scaleFac = ((float)itmp - 0.5) / 25400.0; } else { if( sscanf( line, "%f", &tmp ) != 1 or tmp <= 0 ) fprintf( stderr, "What ???" ); else scaleFac = tmp; } scale = ( scaleFac * RESOLUTION ) / 100.0; break; default : fprintf( stderr, "what ???. Try again\n" ); } } return( scale ); } /* * Calculate the number of rectangles per layer (for the flattened circuit), * this number will be used later for memory allocation purposes. */ private GetTotalRects( c, level ) Call c; int level; { register LayerRects *lay; register int *n, i; start : if( c == NULL or level > maxLevel ) return; for( i = nLayer, n = totRects, lay = c->cell->layers; i; i-- ,n++ ,lay++ ) *n += lay->nRects; GetTotalRects( c->cell->calls, level + 1 ); /* eliminate tail recursion => GetTotalRects( c->next, level ) */ c = c->next; goto start; } /* * This is the number of bytes required to sort 'n' edges */ #define MEMSIZE(n) (n)*(sizeof(Edge) + 2*sizeof(EdgeP)) + 8448*sizeof(int) /* * Flatten all the layers of the circuit: Generate the edges (horizontal) per * layer, sort them by lower left vertex and generate the rectangles to be * filled and (if specified) the outline for each layer. * Check that all the edges will fit in memory, if this is the case then * do a in-core sort, otherwise split the edges into different files (assuming * a uniform distribution over the circuit area, this will reduce the number * of edges to be sorted at one time) and then merge-sort them into another * file to be used thereafter. * The rationale for this is that virtual memory is free (almost) so use it. */ public void FlattenChip() { int nEdges; EdgeP *sorted; InitCtm( ctm ); for( currLayer = 0; currLayer < nLayer; currLayer++ ) { if( totRects[ currLayer ] == 0 ) continue; nEdges = 2 * totRects[ currLayer ]; if( MemAvail( MEMSIZE( nEdges ) ) ) { SaveMem(); tmpFiles->buff.e = (EdgeP) Malloc( nEdges * sizeof(Edge) ); tmpFiles->buffp.e = tmpFiles->buff.e; tmpFiles->cnt = nEdges + 2; /* "infinite" buffer */ tmpFiles->nElems = 0; range = sizeX + 1; GenEdges( root, 0, ctm ); sorted = SortEdges( nEdges, tmpFiles->buff.e, sizeX, sizeY ); if( specialLayers.outline ) GenerateRect_Outline( nEdges, sorted, NULL, currLayer ); else GenerateRectangles( nEdges, sorted, NULL, currLayer ); RestoreMem(); } else { SplitEdges(); SaveMem(); if( specialLayers.outline ) GenerateRect_Outline( nEdges, NULL, tmpFiles[ TMPOUTFILE ].name, currLayer ); else GenerateRectangles( nEdges, NULL, tmpFiles[ TMPOUTFILE ].name, currLayer ); RestoreMem(); } } if( specialLayers.bbox or specialLayers.symbolName or specialLayers.pointName ) GenLabels( root, 0, ctm ); } /* * Split the edges to be genarted over 'SPLITFILES', according to the left * x-position of each edge. We then know that all the edges in file[n] are * greater than those in file[n+1]. */ SplitEdges() { TMPFile *f; int n, nEdges; FILE *fout; SaveMem(); for( f = tmpFiles; f != &tmpFiles[ SPLITFILES ]; f++ ) { if( (f->fd = open( f->name, O_CREAT | O_WRONLY | O_TRUNC, 0777 )) < 0 ) Crash( "can not open tmp file '%s'\n", 2, f->name ); f->buff.e = f->buffp.e = (EdgeP) Malloc( FILEBUFSIZE ); if( f->buff.e == NULL ) Crash( "Out of memory for edge sort", 2 ); f->cnt = BUFSIZ / sizeof( Edge ); f->nElems = 0; } range = ( sizeX / 10 ) + 1; GenEdges( root, 0, ctm ); for( f = tmpFiles; f != &tmpFiles[ SPLITFILES ]; f++ ) { f->cnt = FILEBUFSIZE - sizeof( Edge ) * f->cnt; if( f->cnt != 0 ) if( write( f->fd, f->buff.e, f->cnt ) < 0 ) Crash( "Write to \%s' failed\n", 2 , f->name ); close( f->fd ); } RestoreMem(); SaveMem(); f = &tmpFiles[ TMPOUTFILE ]; if( (fout = fopen( f->name, "w" )) == NULL ) Crash( "can't open tmp file '%s'\n", 2, f->name ); setbuffer( fout, Malloc( FILEBUFSIZE ), FILEBUFSIZE ); for( n = range-1, f = tmpFiles; f != &tmpFiles[SPLITFILES]; f++, n += range ) { if( f->nElems > 0 ) MergeSort( f, n, fout ); } RestoreMem(); } /* * Merge-sort the files generated by SplitEdges into 'fout'. */ private MergeSort( finp, maxX, fout ) TMPFile *finp; int maxX; FILE *fout; { TMPFile *f; int todo; int nEdges; int nFile; EdgeP *sorted, *end; EdgeP edgeBuff, outBuff; SaveMem(); nEdges = finp->nElems; if( (finp->fd = open( finp->name, O_RDONLY, 0 )) < 0 ) Crash( "can't reopen tmp file '%s'\n", finp->name, 2 ); if( (outBuff = (EdgeP) Malloc( FILEBUFSIZE )) ) Crash( "Out of memory for edge sort", 2 ); while( not MemAvail( MEMSIZE( nEdges ) ) ) nEdges = nEdges / 2; edgeBuff = (EdgeP) Malloc( nEdges ); nFile = 0; todo = finp->nElems * sizeof( Edge ); do { SaveMem(); todo = todo - read( finp->fd, edgeBuff, nEdges * sizeof( Edge ) ); sorted = SortEdges( nEdges, edgeBuff, maxX, sizeY ); f = &tmpFiles[ nFile + SPLITFILES ]; if( (f->fd = open( f->name, O_CREAT | O_WRONLY | O_TRUNC, 0777 )) < 0 ) Crash( "can't open tmp file '%s'\n", 2, f->name ); f->cnt = BUFSIZ / sizeof( Edge ); f->buffp.e = outBuff; end = &sorted[ nEdges ]; do { *(f->buffp.e) = **sorted; sorted++; f->buffp.e++; if( --(f->cnt) == 0 ) { if( write( f->fd, outBuff, FILEBUFSIZE ) < 0 ) Crash( "Write to %s failed\n", 2, f->name ); f->buffp.e = outBuff; f->cnt = BUFSIZ / sizeof( Edge ); } } while( sorted < end ); f->cnt = FILEBUFSIZE - f->cnt * sizeof( Edge ); if( f->cnt != 0 ) if( write( f->fd, outBuff, f->cnt ) < 0 ) Crash( "Write to '%s' failed\n", 2, f->name ); close( f->fd ); nFile++; RestoreMem(); } while( todo > 0 and nFile < MAXMERGEFILES ); if( todo > 0 ) Crash( "Not enough memory for mergesort\n", 2 ); close( finp->fd ); unlink( finp->name ); RestoreMem(); MergeFiles( nFile, fout ); } /* * Add a new edge to the respective buffer (determined by range). If the * buffer fills up write it out. Note that when all edges fit in memory * the "buffer-size" is such that no write will occurr => leaving all * edges in memory. */ #define AddEdge( edge ) \ { \ register TMPFile *f; \ \ f = &tmpFiles[ (edge.left / range) ]; \ *(f->buffp.e) = edge; \ f->buffp.e++; \ f->nElems++; \ if( --(f->cnt) == 0 ) \ { \ if( write( f->fd, f->buff, FILEBUFSIZE ) < 0 ) \ Crash( "write to %s failed\n", 2, f->name ); \ f->cnt = BUFSIZ / sizeof( Edge ); \ f->buffp.e = f->buff.e; \ } \ } \ /* * Generate all edges in the current layer, up to (including) 'level'. * Recursively generate the edges of all the children of this cell. * Note that only horizontal edges are generated, vertcal edges are * reconstructed afterwards. */ private GenEdges( c, level, ctm ) Call c; int level; Tmatrix ctm; { Cell ce; Tmatrix tmpT; RectLink rects; register int nRects; register Rect *r; register Real *newT; IPoint p1, p2; Edge top, bot; start : if( c == NULL or level > maxLevel ) return; ce = c->cell; newT = tmpT; ConcatMatrix( c->tm, ctm, newT ); rects = ce->layers[ currLayer ].rects; nRects = ( ce->layers[ currLayer ].nRects - 1 ) % RECTBUFF; while( rects != NULL ) { r = rects->rect; do { TransformIPoint( r->bot, newT, p1 ); TransformIPoint( r->top, newT, p2 ); top.left = bot.left = min( p1.x, p2.x ); top.right = bot.right = max( p1.x, p2.x ); bot.y = min( p1.y, p2.y ); top.y = max( p1.y, p2.y ); bot.dir = BOTTOM; top.dir = TOP; AddEdge( bot ); AddEdge( top ); r++; nRects--; } while( nRects >= 0 ); rects = rects->link; nRects = RECTBUFF - 1; } GenEdges( ce->calls, level + 1, tmpT ); /* save tail recursion => GenEdges( c->next, level, ctm ); */ c = c->next; goto start; } /* * Do a depth-first traversal of the call tree and generate the labels of * each cell instantiation. This way the labels of the cells higher up in * the hierarchy are printed last, possibly covering the labels of cells * lower in the hierarchy. */ private GenLabels( c, level, ctm ) Call c; int level; Tmatrix ctm; { Cell ce; Tmatrix tmpT; register Real *newT; register Label labels; IRect bbox; IPoint pt; start : /* if( c == NULL or level > maxLevel ) */ if( c == NULL or level-1 > maxLevel ) return; newT = tmpT; ConcatMatrix( c->tm, ctm, newT ); ce = c->cell; GenLabels( ce->calls, level + 1, tmpT ); TransformIBox( ce->bigBox, newT, bbox.bot, bbox.top ); OutputBBox( &bbox, ce->name ); /* if( specialLayers.pointName ) */ if( specialLayers.pointName && level <= maxLevel) { for( labels = ce->labels; labels != NULL; labels = labels->next ) { TransformIPoint( labels->pos, newT, pt ); OutputLabel( &pt, labels->lab ); } } /* tail recurse => GenLabels( c->next, level, ctm ) */ c = c->next; goto start; }