2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* CalmaReadcell.c --
|
|
|
|
|
*
|
|
|
|
|
* Input of Calma GDS-II stream format.
|
|
|
|
|
* Processing for cells.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * 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. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/calma/CalmaRdcl.c,v 1.5 2010/06/25 13:59:24 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "database/databaseInt.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "cif/cif.h"
|
|
|
|
|
#include "cif/CIFint.h"
|
|
|
|
|
#include "cif/CIFread.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/styles.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "calma/calmaInt.h"
|
|
|
|
|
#include "calma/calma.h"
|
|
|
|
|
|
|
|
|
|
int calmaNonManhattan;
|
2020-05-14 21:59:39 +02:00
|
|
|
int CalmaFlattenLimit = 10;
|
2021-02-24 20:41:35 +01:00
|
|
|
int NameConvertErrors = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
extern HashTable calmaDefInitHash;
|
|
|
|
|
|
|
|
|
|
/* forward declarations */
|
|
|
|
|
int calmaElementSref();
|
|
|
|
|
bool calmaParseElement();
|
|
|
|
|
|
|
|
|
|
/* Structure used when flattening the GDS hierarchy on read-in */
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
Plane *plane;
|
|
|
|
|
Transform *trans;
|
|
|
|
|
} GDSCopyRec;
|
|
|
|
|
|
|
|
|
|
/* Added by NP 8/11/04 */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaSetPosition --
|
|
|
|
|
*
|
|
|
|
|
* This routine sets the file pointer calmaInputFile to the start
|
|
|
|
|
* of the CellDefinition "sname". It starts the search from the
|
|
|
|
|
* current position and looks forward to find the Cell Definition
|
|
|
|
|
* named "sname".
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Current position of file pointer before it jumps to the
|
|
|
|
|
* definition of cell "sname" (if it exists, otherwise, returns
|
|
|
|
|
* end-of-file).
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* The file position is set to the definition of cell "sname". If
|
|
|
|
|
* "sname" does not exist in the GDS stream file, the pointer is
|
|
|
|
|
* set to the end of the file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
off_t
|
|
|
|
|
calmaSetPosition(sname)
|
|
|
|
|
char *sname;
|
|
|
|
|
{
|
|
|
|
|
off_t originalPos = 0, currentPos = 0;
|
|
|
|
|
int nbytes, rtype;
|
|
|
|
|
char *strname = NULL;
|
|
|
|
|
int strRecSize = 0;
|
|
|
|
|
bool found = FALSE;
|
|
|
|
|
|
|
|
|
|
originalPos = ftello(calmaInputFile);
|
|
|
|
|
|
|
|
|
|
while (feof(calmaInputFile) == 0)
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
READRH(nbytes, rtype); /* Read header */
|
|
|
|
|
if (nbytes <= 0) break;
|
|
|
|
|
|
|
|
|
|
/* Skip no of bytes in record header until
|
|
|
|
|
* we reach to next structure record.
|
|
|
|
|
*/
|
|
|
|
|
fseek(calmaInputFile, nbytes - CALMAHEADERLENGTH, SEEK_CUR);
|
|
|
|
|
} while (rtype != CALMA_BGNSTR);
|
|
|
|
|
if (nbytes <= 0) break;
|
|
|
|
|
|
|
|
|
|
calmaReadStringRecord(CALMA_STRNAME, &strname);
|
|
|
|
|
if ((strcmp(sname, strname)) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* If name if structure matches with given name,
|
|
|
|
|
* we got that Cell Defination. Set position of
|
|
|
|
|
* file to start of that structure.
|
|
|
|
|
*/
|
|
|
|
|
strRecSize = strlen(strname);
|
|
|
|
|
if (strRecSize & 01) strRecSize++;
|
|
|
|
|
fseek(calmaInputFile, -(nbytes + strRecSize + CALMAHEADERLENGTH),
|
|
|
|
|
SEEK_CUR);
|
|
|
|
|
freeMagic(strname);
|
|
|
|
|
return originalPos;
|
|
|
|
|
}
|
|
|
|
|
freeMagic(strname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ran out of file. It's possible that we were seeking ahead to a
|
|
|
|
|
// definition that called something that was defined between it and
|
|
|
|
|
// our previous position, so we will rewind the file and try again.
|
|
|
|
|
// If that doesn't work, then the cell is not defined in the file.
|
|
|
|
|
|
|
|
|
|
if (originalPos != 0)
|
|
|
|
|
{
|
|
|
|
|
rewind(calmaInputFile);
|
|
|
|
|
calmaSetPosition(sname);
|
|
|
|
|
return originalPos;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Cell \"%s\" is used but not defined in this file.\n", sname);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
return originalPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Added by NP 8/11/04 */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaNextCell --
|
|
|
|
|
*
|
|
|
|
|
* This routine sets the file pointer to next Cell Definition
|
|
|
|
|
* in the GDS stream. It goes only in the forward direction
|
|
|
|
|
* from the current position of the file pointer.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* File pointer set to start of next Cell definition.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
calmaNextCell()
|
|
|
|
|
{
|
|
|
|
|
int nbytes, rtype;
|
|
|
|
|
|
|
|
|
|
if (feof(calmaInputFile) == 0)
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
READRH(nbytes, rtype); /* Read header */
|
|
|
|
|
if (nbytes <= 0)
|
|
|
|
|
{
|
|
|
|
|
/* We have reached the end of the file unexpectedly.
|
|
|
|
|
* try to set the file pointer somewhere sane, but
|
|
|
|
|
* it will likely dump an error later on.
|
|
|
|
|
*/
|
|
|
|
|
fseek(calmaInputFile, -(CALMAHEADERLENGTH), SEEK_END);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skip no. of bytes in record header to reach the next
|
|
|
|
|
* structure record.
|
|
|
|
|
*/
|
|
|
|
|
fseek(calmaInputFile, nbytes - CALMAHEADERLENGTH, SEEK_CUR);
|
|
|
|
|
} while((rtype != CALMA_BGNSTR) && (rtype != CALMA_ENDLIB));
|
|
|
|
|
|
|
|
|
|
fseek(calmaInputFile, -nbytes, SEEK_CUR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaExact --
|
|
|
|
|
*
|
|
|
|
|
* When dictated to flatten small cells (e.g., vias which have been placed
|
|
|
|
|
* into their own cells to make use of GDS arrays), instead of calling
|
|
|
|
|
* CIFPaintCurrent(), we just transfer the planes of cifCurReadPlanes to
|
|
|
|
|
* the current cell, and swap the planes from the current cell into the
|
|
|
|
|
* pointer array for cifCurReadPlanes, so we don't have to free and
|
|
|
|
|
* reallocate any memory for the operation. Flag the cell as being a
|
|
|
|
|
* CIF cell, so that it can be destroyed after read-in and flattening.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Pointer to the plane structure created to hold the GDS data.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Swaps the planes of cifReadCellDef with cifCurReadPlanes.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Plane **
|
|
|
|
|
calmaExact()
|
|
|
|
|
{
|
|
|
|
|
int pNum;
|
|
|
|
|
Plane *newplane;
|
|
|
|
|
Plane **parray;
|
|
|
|
|
int gdsCopyPaintFunc(); /* Forward reference */
|
|
|
|
|
|
|
|
|
|
parray = (Plane **)mallocMagic(MAXCIFRLAYERS * sizeof(Plane *));
|
|
|
|
|
|
|
|
|
|
for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
|
|
|
|
|
{
|
|
|
|
|
if (cifCurReadPlanes[pNum] != NULL)
|
|
|
|
|
{
|
|
|
|
|
GDSCopyRec gdsCopyRec;
|
|
|
|
|
|
|
|
|
|
newplane = DBNewPlane((ClientData) TT_SPACE);
|
|
|
|
|
DBClearPaintPlane(newplane);
|
|
|
|
|
|
|
|
|
|
gdsCopyRec.plane = newplane;
|
|
|
|
|
gdsCopyRec.trans = NULL;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, cifCurReadPlanes[pNum], &TiPlaneRect,
|
|
|
|
|
&DBAllButSpaceBits, gdsCopyPaintFunc, &gdsCopyRec);
|
|
|
|
|
parray[pNum] = newplane;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
parray[pNum] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear out the current paint planes for the next cell */
|
|
|
|
|
for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
|
|
|
|
|
DBClearPaintPlane(cifCurReadPlanes[pNum]);
|
|
|
|
|
|
|
|
|
|
return parray;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaParseStructure --
|
|
|
|
|
*
|
|
|
|
|
* Process a complete GDS-II structure (cell) including its closing
|
|
|
|
|
* CALMA_ENDSTR record. In the event of a syntax error, we skip
|
|
|
|
|
* ahead to the closing CALMA_ENDSTR, output a warning, and keep going.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if successful, FALSE if the next item in the input is
|
|
|
|
|
* not a structure.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Reads a new cell.
|
|
|
|
|
* Consumes input.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
calmaParseStructure(filename)
|
|
|
|
|
char *filename; /* Name of the GDS file read */
|
|
|
|
|
{
|
|
|
|
|
static int structs[] = { CALMA_STRCLASS, CALMA_STRTYPE, -1 };
|
|
|
|
|
int nbytes, rtype, nsrefs, osrefs, npaths;
|
2019-12-19 23:28:06 +01:00
|
|
|
char *strname = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashEntry *he;
|
|
|
|
|
int suffix;
|
|
|
|
|
int mfactor;
|
|
|
|
|
off_t filepos;
|
|
|
|
|
bool was_called;
|
2019-05-22 20:24:44 +02:00
|
|
|
bool was_initialized;
|
2020-12-04 22:56:51 +01:00
|
|
|
bool predefined;
|
2020-12-22 20:44:30 +01:00
|
|
|
bool do_flatten;
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *def;
|
|
|
|
|
|
|
|
|
|
/* Make sure this is a structure; if not, let the caller know we're done */
|
|
|
|
|
PEEKRH(nbytes, rtype);
|
|
|
|
|
if (nbytes <= 0 || rtype != CALMA_BGNSTR)
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* Read the structure name */
|
2019-05-22 20:24:44 +02:00
|
|
|
was_initialized = FALSE;
|
2020-12-04 22:56:51 +01:00
|
|
|
predefined = FALSE;
|
2019-05-22 20:24:44 +02:00
|
|
|
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
|
|
|
|
|
TxPrintf("Reading \"%s\".\n", strname);
|
|
|
|
|
|
|
|
|
|
if (CalmaReadOnly)
|
|
|
|
|
filepos = ftello(calmaInputFile);
|
|
|
|
|
|
|
|
|
|
/* Set up the cell definition */
|
|
|
|
|
he = HashFind(&calmaDefInitHash, strname);
|
|
|
|
|
if ((def = (CellDef *)HashGetValue(he)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (def->cd_flags & CDPROCESSEDGDS)
|
|
|
|
|
{
|
|
|
|
|
/* If cell definition was marked as processed, then skip */
|
|
|
|
|
/* (NOTE: This is probably a bad policy in general) */
|
|
|
|
|
/* However, if post-ordering is set, then this cell was */
|
|
|
|
|
/* probably just read earlier, so don't gripe about it. */
|
|
|
|
|
|
|
|
|
|
if (!CalmaPostOrder)
|
|
|
|
|
{
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Cell \"%s\" was already defined in this file.\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
strname);
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Ignoring duplicate definition\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
calmaNextCell();
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-19 23:28:06 +01:00
|
|
|
char *newname;
|
|
|
|
|
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Cell \"%s\" was already defined in this file.\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
strname);
|
2019-12-19 23:28:06 +01:00
|
|
|
newname = (char *)mallocMagic(strlen(strname) + 20);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (suffix = 1; HashGetValue(he) != NULL; suffix++)
|
|
|
|
|
{
|
|
|
|
|
(void) sprintf(newname, "%s_%d", strname, suffix);
|
|
|
|
|
he = HashFind(&calmaDefInitHash, newname);
|
|
|
|
|
}
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Giving this cell a new name: %s\n", newname);
|
2019-12-19 23:28:06 +01:00
|
|
|
freeMagic(strname);
|
|
|
|
|
strname = mallocMagic(strlen(newname) + 1);
|
|
|
|
|
strcpy(strname, newname);
|
|
|
|
|
freeMagic(newname);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-12-04 22:56:51 +01:00
|
|
|
cifReadCellDef = calmaFindCell(strname, &was_called, &predefined);
|
|
|
|
|
if (predefined == TRUE)
|
|
|
|
|
{
|
|
|
|
|
calmaNextCell();
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
DBCellClearDef(cifReadCellDef);
|
|
|
|
|
DBCellSetAvail(cifReadCellDef);
|
|
|
|
|
HashSetValue(he, cifReadCellDef);
|
|
|
|
|
cifCurReadPlanes = cifSubcellPlanes;
|
2020-03-14 14:50:36 +01:00
|
|
|
cifReadCellDef->cd_flags &= ~CDDEREFERENCE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* For read-only cells, set flag in def */
|
|
|
|
|
if (CalmaReadOnly)
|
|
|
|
|
cifReadCellDef->cd_flags |= CDVENDORGDS;
|
|
|
|
|
|
|
|
|
|
/* Skip CALMA_STRCLASS or CALMA_STRTYPE */
|
|
|
|
|
calmaSkipSet(structs);
|
|
|
|
|
|
|
|
|
|
/* Initialize the hash table for layer errors */
|
|
|
|
|
HashInit(&calmaLayerHash, 32, sizeof (CalmaLayerType) / sizeof (unsigned));
|
2019-05-22 20:24:44 +02:00
|
|
|
was_initialized = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Body of structure: a sequence of elements */
|
|
|
|
|
osrefs = nsrefs = 0;
|
|
|
|
|
npaths = 0;
|
|
|
|
|
calmaNonManhattan = 0;
|
|
|
|
|
while (calmaParseElement(filename, &nsrefs, &npaths))
|
|
|
|
|
{
|
|
|
|
|
if (SigInterruptPending)
|
|
|
|
|
goto done;
|
|
|
|
|
if (nsrefs > osrefs && (nsrefs % 100) == 0)
|
|
|
|
|
TxPrintf(" %d uses\n", nsrefs);
|
|
|
|
|
osrefs = nsrefs;
|
|
|
|
|
calmaNonManhattan = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CalmaReadOnly)
|
|
|
|
|
{
|
|
|
|
|
/* Writing the file position into a string is slow, but */
|
|
|
|
|
/* it prevents requiring special handling when printing */
|
|
|
|
|
/* out the properties. */
|
|
|
|
|
|
|
|
|
|
char *fpcopy = (char *)mallocMagic(20);
|
|
|
|
|
char *fncopy = StrDup(NULL, filename);
|
|
|
|
|
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_START", (ClientData)fpcopy);
|
|
|
|
|
|
|
|
|
|
fpcopy = (char *)mallocMagic(20);
|
|
|
|
|
filepos = ftello(calmaInputFile);
|
|
|
|
|
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_END", (ClientData)fpcopy);
|
|
|
|
|
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_FILE", (ClientData)fncopy);
|
|
|
|
|
|
|
|
|
|
/* Do not lock the cell, or else we can't save the */
|
|
|
|
|
/* magic cell with its GDS pointers to disk. . . */
|
|
|
|
|
/* cifReadCellDef->cd_flags |= CDNOEDIT; */
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-22 20:44:30 +01:00
|
|
|
/* Check if the cell name matches the pattern list of cells to flatten */
|
|
|
|
|
|
|
|
|
|
do_flatten = FALSE;
|
2020-12-23 03:53:36 +01:00
|
|
|
if ((CalmaFlattenUsesByName != NULL) && (!was_called))
|
2020-12-22 20:44:30 +01:00
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
char *pattern;
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
pattern = CalmaFlattenUsesByName[i];
|
|
|
|
|
if (pattern == NULL) break;
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Check pattern against strname */
|
|
|
|
|
if (Match(pattern, strname))
|
|
|
|
|
{
|
|
|
|
|
do_flatten = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (CalmaFlattenUses && (!was_called) && (npaths < CalmaFlattenLimit)
|
|
|
|
|
&& (nsrefs == 0))
|
|
|
|
|
do_flatten = TRUE;
|
|
|
|
|
|
|
|
|
|
/* Done with strname */
|
|
|
|
|
if (strname != NULL) freeMagic(strname);
|
|
|
|
|
|
|
|
|
|
/* Make sure the structure ends with an ENDSTR record */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!calmaSkipExact(CALMA_ENDSTR)) goto syntaxerror;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Don't paint now, just keep the CIF planes, and flatten the
|
|
|
|
|
* cell by painting when instanced. But---if this cell was
|
|
|
|
|
* instanced before it was defined, then it can't be flattened.
|
|
|
|
|
*/
|
2020-12-22 20:44:30 +01:00
|
|
|
|
|
|
|
|
if (do_flatten)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-08-20 21:10:45 +02:00
|
|
|
/* If CDFLATGDS is already set, may need to remove */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* existing planes and free memory. */
|
|
|
|
|
|
2020-11-21 01:56:41 +01:00
|
|
|
if ((cifReadCellDef->cd_client != (ClientData)0) &&
|
2019-08-20 21:10:45 +02:00
|
|
|
(cifReadCellDef->cd_flags & CDFLATGDS))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
Plane **cifplanes = (Plane **)cifReadCellDef->cd_client;
|
|
|
|
|
int pNum;
|
|
|
|
|
|
|
|
|
|
for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
|
|
|
|
|
{
|
2019-08-20 21:10:45 +02:00
|
|
|
if (cifplanes[pNum] != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
DBFreePaintPlane(cifplanes[pNum]);
|
|
|
|
|
TiFreePlane(cifplanes[pNum]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeMagic((char *)cifReadCellDef->cd_client);
|
2020-11-21 01:56:41 +01:00
|
|
|
cifReadCellDef->cd_client = (ClientData)0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TxPrintf("Saving contents of cell %s\n", cifReadCellDef->cd_name);
|
|
|
|
|
cifReadCellDef->cd_client = (ClientData) calmaExact();
|
|
|
|
|
cifReadCellDef->cd_flags |= CDFLATGDS;
|
|
|
|
|
cifReadCellDef->cd_flags &= ~CDFLATTENED;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Do the geometrical processing and paint this material back into
|
|
|
|
|
* the appropriate cell of the database.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-07-23 14:45:42 +02:00
|
|
|
CIFPaintCurrent(FILE_CALMA);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBAdjustLabelsNew(cifReadCellDef, &TiPlaneRect,
|
|
|
|
|
(cifCurReadStyle->crs_flags & CRF_NO_RECONNECT_LABELS) ? 1 : 0);
|
|
|
|
|
DBReComputeBbox(cifReadCellDef);
|
|
|
|
|
|
|
|
|
|
/* Don't bother to register with DRC if we're going to delete the */
|
|
|
|
|
/* cell, or if the cell is read-only, or if "calma drcnocheck true" */
|
|
|
|
|
/* has been issued. */
|
|
|
|
|
|
|
|
|
|
if (!CalmaReadOnly && !CalmaNoDRCCheck)
|
|
|
|
|
DRCCheckThis(cifReadCellDef, TT_CHECKPAINT, &cifReadCellDef->cd_bbox);
|
|
|
|
|
|
|
|
|
|
DBWAreaChanged(cifReadCellDef, &cifReadCellDef->cd_bbox,
|
|
|
|
|
DBW_ALLWINDOWS, &DBAllButSpaceBits);
|
|
|
|
|
DBCellSetModified(cifReadCellDef, TRUE);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Assign use-identifiers to all the cell uses.
|
|
|
|
|
* These identifiers are generated so as to be
|
|
|
|
|
* unique.
|
|
|
|
|
*/
|
|
|
|
|
DBGenerateUniqueIds(cifReadCellDef, FALSE);
|
|
|
|
|
cifReadCellDef->cd_flags |= CDPROCESSEDGDS;
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
HashKill(&calmaLayerHash);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
|
|
|
|
|
/* Syntax error: skip to CALMA_ENDSTR */
|
|
|
|
|
syntaxerror:
|
2019-05-22 20:24:44 +02:00
|
|
|
if (was_initialized == TRUE) HashKill(&calmaLayerHash);
|
2017-04-25 14:41:48 +02:00
|
|
|
return (calmaSkipTo(CALMA_ENDSTR));
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaParseElement --
|
|
|
|
|
*
|
|
|
|
|
* Process one element from a GDS-II structure, including its
|
|
|
|
|
* trailing CALMA_ENDEL record. In the event of a syntax error, we skip
|
|
|
|
|
* ahead to the closing CALMA_ENDEL, output a warning, and keep going.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if we processed an element, FALSE when we reach something that
|
|
|
|
|
* is not an element. In the latter case, we leave the non-element
|
|
|
|
|
* record unconsumed.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Consumes input.
|
|
|
|
|
* Depends on the kind of element encountered.
|
|
|
|
|
* If we process a SREF or AREF, increment *pnsrefs.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
calmaParseElement(filename, pnsrefs, pnpaths)
|
|
|
|
|
char *filename;
|
|
|
|
|
int *pnsrefs, *pnpaths;
|
|
|
|
|
{
|
|
|
|
|
static int node[] = { CALMA_ELFLAGS, CALMA_PLEX, CALMA_LAYER,
|
|
|
|
|
CALMA_NODETYPE, CALMA_XY, -1 };
|
|
|
|
|
int nbytes, rtype, madeinst;
|
|
|
|
|
|
|
|
|
|
READRH(nbytes, rtype);
|
|
|
|
|
if (nbytes < 0)
|
|
|
|
|
{
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Unexpected EOF.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (rtype)
|
|
|
|
|
{
|
|
|
|
|
case CALMA_AREF:
|
|
|
|
|
case CALMA_SREF:
|
|
|
|
|
madeinst = calmaElementSref(filename);
|
|
|
|
|
if (madeinst >= 0)
|
|
|
|
|
(*pnsrefs) += madeinst;
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
case CALMA_BOUNDARY:
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaElementBoundary();
|
|
|
|
|
(*pnpaths)++;
|
|
|
|
|
break;
|
|
|
|
|
case CALMA_BOX:
|
|
|
|
|
calmaElementBox();
|
|
|
|
|
(*pnpaths)++;
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
case CALMA_PATH:
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaElementPath();
|
|
|
|
|
(*pnpaths)++;
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
case CALMA_TEXT:
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaElementText();
|
|
|
|
|
break;
|
|
|
|
|
case CALMA_NODE:
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("NODE elements not supported: skipping.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaSkipSet(node);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (calmaSkipTo(CALMA_ENDEL));
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 20:16:37 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* Callback procedure for enumerating any paint in a cell. Used to find if
|
|
|
|
|
* a cell needs to be retained after being flattened into the parent cell.
|
|
|
|
|
*
|
|
|
|
|
* Returns 1 always. Only called if a non-space tile was encountered.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
calmaEnumFunc(tile, plane)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
int *plane;
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaElementSref --
|
|
|
|
|
*
|
|
|
|
|
* Process a structure reference (either CALMA_SREF or CALMA_AREF).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 1 if an actual child instance was generated, 0 if
|
|
|
|
|
* only the child geometry was copied up, and -1 if an error
|
|
|
|
|
* occurred.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Consumes input.
|
|
|
|
|
* Adds a new cell use to the current def.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
calmaElementSref(filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
int nbytes, rtype, cols, rows, nref, n, i, savescale;
|
|
|
|
|
int xlo, ylo, xhi, yhi, xsep, ysep;
|
|
|
|
|
bool madeinst = FALSE;
|
|
|
|
|
char *sname = NULL;
|
|
|
|
|
bool isArray = FALSE;
|
|
|
|
|
Transform trans, tinv;
|
|
|
|
|
Point refarray[3], refunscaled[3], p;
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
int gdsCopyPaintFunc(); /* Forward reference */
|
|
|
|
|
int gdsHasUses(); /* Forward reference */
|
|
|
|
|
/* Added by NP */
|
|
|
|
|
char *useid = NULL, *arraystr = NULL;
|
|
|
|
|
int propAttrType;
|
|
|
|
|
|
|
|
|
|
/* Skip CALMA_ELFLAGS, CALMA_PLEX */
|
|
|
|
|
calmaSkipSet(calmaElementIgnore);
|
|
|
|
|
|
|
|
|
|
/* Read subcell name */
|
|
|
|
|
if (!calmaReadStringRecord(CALMA_SNAME, &sname))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create a new cell use with this transform. If the
|
|
|
|
|
* cell being referenced doesn't exist, create it.
|
|
|
|
|
* Don't give it a use-id; we will do that only after
|
|
|
|
|
* we've placed all cells.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
def = calmaLookCell(sname);
|
2020-12-22 20:44:30 +01:00
|
|
|
if (!def && (CalmaPostOrder || CalmaFlattenUses || (CalmaFlattenUsesByName != NULL)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* Force the GDS parser to read the cell definition in
|
|
|
|
|
* post-order. If cellname "sname" is not defined before
|
|
|
|
|
* it is used, then read the cell definition by jumping
|
|
|
|
|
* to its position in the stream file, reading that
|
|
|
|
|
* instance, and then returning to the current file
|
|
|
|
|
* position. Note that this routine should be optimized
|
|
|
|
|
* for the likely case where many cells are defined at
|
|
|
|
|
* the end of the file; otherwise it will slow down the
|
|
|
|
|
* read process excessively for large GDS files by
|
|
|
|
|
* constantly moving from beginning to end of the file.
|
|
|
|
|
*
|
|
|
|
|
* Added by Nishit 8/16/2004
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
off_t originalFilePos = calmaSetPosition(sname);
|
|
|
|
|
if (!feof(calmaInputFile))
|
|
|
|
|
{
|
|
|
|
|
HashTable OrigCalmaLayerHash;
|
|
|
|
|
int crsMultiplier = cifCurReadStyle->crs_multiplier;
|
|
|
|
|
char *currentSname = cifReadCellDef->cd_name;
|
|
|
|
|
Plane **savePlanes;
|
|
|
|
|
|
|
|
|
|
OrigCalmaLayerHash = calmaLayerHash;
|
|
|
|
|
savePlanes = calmaExact();
|
|
|
|
|
|
|
|
|
|
/* Read cell definition "sname". */
|
|
|
|
|
calmaParseStructure(filename);
|
|
|
|
|
|
|
|
|
|
/* Put things back to the way they were. */
|
|
|
|
|
fseek(calmaInputFile, originalFilePos, SEEK_SET);
|
|
|
|
|
cifReadCellDef = calmaLookCell(currentSname);
|
|
|
|
|
def = calmaLookCell(sname);
|
|
|
|
|
cifCurReadPlanes = savePlanes;
|
|
|
|
|
calmaLayerHash = OrigCalmaLayerHash;
|
|
|
|
|
if (crsMultiplier != cifCurReadStyle->crs_multiplier)
|
|
|
|
|
{
|
|
|
|
|
int scalen, scaled;
|
|
|
|
|
if (crsMultiplier > cifCurReadStyle->crs_multiplier)
|
|
|
|
|
{
|
|
|
|
|
scalen = crsMultiplier / cifCurReadStyle->crs_multiplier;
|
|
|
|
|
scaled = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scalen = 1;
|
|
|
|
|
scaled = cifCurReadStyle->crs_multiplier / crsMultiplier;
|
|
|
|
|
}
|
2020-12-09 02:38:33 +01:00
|
|
|
CIFScalePlanes(scaled, scalen, cifCurReadPlanes);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Cell definition %s does not exist!\n", sname);
|
|
|
|
|
fseek(calmaInputFile, originalFilePos, SEEK_SET);
|
2020-12-04 22:56:51 +01:00
|
|
|
def = calmaFindCell(sname, NULL, NULL);
|
2020-03-13 16:36:42 +01:00
|
|
|
/* Cell flags set to "dereferenced" in case there is no */
|
|
|
|
|
/* definition in the GDS file. If there is a definition */
|
|
|
|
|
/* made after the instance, then the flag will be cleared. */
|
|
|
|
|
def->cd_flags |= CDDEREFERENCE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-04 22:56:51 +01:00
|
|
|
if (!def) def = calmaFindCell(sname, NULL, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (DBIsAncestor(def, cifReadCellDef))
|
|
|
|
|
{
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Cell %s is an ancestor of %s",
|
2017-04-25 14:41:48 +02:00
|
|
|
def->cd_name, cifReadCellDef->cd_name);
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError(" and can't be used as a subcell.\n");
|
|
|
|
|
CalmaReadError("(Use skipped)\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read subcell transform */
|
|
|
|
|
if (!calmaReadTransform(&trans, sname))
|
|
|
|
|
{
|
|
|
|
|
printf("Couldn't read transform.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get number of columns and rows if array */
|
|
|
|
|
cols = rows = 0; /* For half-smart compilers that complain otherwise. */
|
|
|
|
|
READRH(nbytes, rtype);
|
|
|
|
|
if (nbytes < 0) return -1;
|
|
|
|
|
if (rtype == CALMA_COLROW)
|
|
|
|
|
{
|
|
|
|
|
isArray = TRUE;
|
|
|
|
|
READI2(cols);
|
|
|
|
|
READI2(rows);
|
|
|
|
|
xlo = 0; xhi = cols - 1;
|
|
|
|
|
ylo = 0; yhi = rows - 1;
|
|
|
|
|
if (feof(calmaInputFile)) return -1;
|
|
|
|
|
(void) calmaSkipBytes(nbytes - CALMAHEADERLENGTH - 4);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Read reference points.
|
|
|
|
|
* For subcells, there will be a single reference point.
|
|
|
|
|
* For arrays, there will be three; for their meanings, see below.
|
|
|
|
|
*/
|
|
|
|
|
READRH(nbytes, rtype)
|
|
|
|
|
if (nbytes < 0) return -1;
|
|
|
|
|
if (rtype != CALMA_XY)
|
|
|
|
|
{
|
|
|
|
|
calmaUnexpected(CALMA_XY, rtype);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Length of remainder of record */
|
|
|
|
|
nbytes -= CALMAHEADERLENGTH;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Read the reference points for the SREF/AREF.
|
|
|
|
|
* Scale down by cifCurReadStyle->crs_scaleFactor, but complain
|
|
|
|
|
* if they don't scale exactly.
|
|
|
|
|
* Make sure we only read three points.
|
|
|
|
|
*/
|
|
|
|
|
nref = nbytes / 8;
|
|
|
|
|
if (nref > 3)
|
|
|
|
|
{
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Too many points (%d) in SREF/AREF\n", nref);
|
2017-04-25 14:41:48 +02:00
|
|
|
nref = 3;
|
|
|
|
|
}
|
|
|
|
|
else if (nref < 1)
|
|
|
|
|
{
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("Missing reference points in SREF/AREF (using 0,0)\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
refarray[0].p_x = refarray[0].p_y = 0;
|
|
|
|
|
refarray[1].p_x = refarray[1].p_y = 0;
|
|
|
|
|
refarray[2].p_x = refarray[2].p_y = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is a cell reference, then we scale to magic coordinates
|
|
|
|
|
* and place the cell in the magic database. However, if this is
|
|
|
|
|
* a cell to be flattened a la "gds flatten", then we keep the GDS
|
|
|
|
|
* coordinates, and don't scale to the magic database.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < nref; n++)
|
|
|
|
|
{
|
|
|
|
|
savescale = cifCurReadStyle->crs_scaleFactor;
|
|
|
|
|
calmaReadPoint(&refarray[n], 1);
|
|
|
|
|
refunscaled[n] = refarray[n]; // Save for CDFLATGDS cells
|
|
|
|
|
refarray[n].p_x = CIFScaleCoord(refarray[n].p_x, COORD_EXACT);
|
|
|
|
|
if (savescale != cifCurReadStyle->crs_scaleFactor)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
refarray[i].p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
|
refarray[i].p_y *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
|
}
|
|
|
|
|
savescale = cifCurReadStyle->crs_scaleFactor;
|
|
|
|
|
}
|
|
|
|
|
refarray[n].p_y = CIFScaleCoord(refarray[n].p_y, COORD_EXACT);
|
|
|
|
|
if (savescale != cifCurReadStyle->crs_scaleFactor)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
refarray[i].p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
|
refarray[i].p_y *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
|
}
|
|
|
|
|
refarray[n].p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (feof(calmaInputFile))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skip remainder */
|
|
|
|
|
nbytes -= nref * 8;
|
|
|
|
|
if (nbytes)
|
|
|
|
|
(void) calmaSkipBytes(nbytes);
|
|
|
|
|
|
|
|
|
|
/* Added by NP --- parse PROPATTR and PROPVALUE record types */
|
|
|
|
|
/* The PROPATTR types 98 and 99 are defined internally to */
|
|
|
|
|
/* magic and are used to retain information that cannot be */
|
|
|
|
|
/* saved to the GDS file in any other manner. */
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
READRH(nbytes, rtype);
|
|
|
|
|
if (nbytes < 0) return -1;
|
|
|
|
|
if (rtype == CALMA_PROPATTR)
|
|
|
|
|
{
|
|
|
|
|
READI2(propAttrType);
|
|
|
|
|
if (propAttrType == CALMA_PROP_USENAME)
|
|
|
|
|
{
|
2021-02-24 20:41:35 +01:00
|
|
|
char *s;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!calmaReadStringRecord(CALMA_PROPVALUE, &useid))
|
|
|
|
|
return -1;
|
2021-02-24 20:41:35 +01:00
|
|
|
|
|
|
|
|
/* Magic prohibits comma and slash from use names */
|
|
|
|
|
for (s = useid; *s; s++)
|
|
|
|
|
if (*s == '/' || *s == ',')
|
|
|
|
|
{
|
|
|
|
|
if (NameConvertErrors < 100)
|
|
|
|
|
TxPrintf("\"%c\" character cannot be used in instance name; "
|
|
|
|
|
"converting to underscore\n", *s);
|
|
|
|
|
else if (NameConvertErrors == 100)
|
|
|
|
|
TxPrintf("More than 100 character changes; not reporting"
|
|
|
|
|
" further errors.\n");
|
|
|
|
|
*s = '_';
|
|
|
|
|
NameConvertErrors++;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else if (propAttrType == CALMA_PROP_ARRAY_LIMITS)
|
|
|
|
|
{
|
|
|
|
|
if (!calmaReadStringRecord(CALMA_PROPVALUE, &arraystr))
|
|
|
|
|
return -1;
|
|
|
|
|
else if (arraystr)
|
|
|
|
|
{
|
|
|
|
|
int xl, xh, yl, yh;
|
|
|
|
|
if (sscanf(arraystr, "%d_%d_%d_%d", &xl, &xh, &yl, &yh)
|
|
|
|
|
!= 4)
|
|
|
|
|
TxError("Malformed \"array\" property ignored: %s",
|
|
|
|
|
arraystr);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
xlo = xl;
|
|
|
|
|
ylo = yl;
|
|
|
|
|
xhi = xh;
|
|
|
|
|
yhi = yh;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Transform manipulation. Run through this twice if necessary. */
|
|
|
|
|
/* The second time will use the unscaled transform to compute */
|
|
|
|
|
/* the coordinates of saved geometry for a CD_FLATGDS cell. */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i == 1)
|
|
|
|
|
{
|
|
|
|
|
for (n = 0; n < nref; n++)
|
|
|
|
|
refarray[n] = refunscaled[n]; // Restore for CDFLATGDS cells
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Figure out the inter-element spacing of array elements,
|
|
|
|
|
* and also the translation part of the transform.
|
|
|
|
|
* The first reference point for both SREFs and AREFs is the
|
|
|
|
|
* translation of the use's or array's lower-left.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
trans.t_c = refarray[0].p_x;
|
|
|
|
|
trans.t_f = refarray[0].p_y;
|
|
|
|
|
GeoInvertTrans(&trans, &tinv);
|
|
|
|
|
if (isArray)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The remaining two points for an array are displaced from
|
|
|
|
|
* the first reference point by:
|
|
|
|
|
* - the inter-column spacing times the number of columns,
|
|
|
|
|
* - the inter-row spacing times the number of rows.
|
|
|
|
|
*/
|
|
|
|
|
xsep = ysep = 0;
|
|
|
|
|
if (cols)
|
|
|
|
|
{
|
|
|
|
|
GeoTransPoint(&tinv, &refarray[1], &p);
|
|
|
|
|
if (p.p_x % cols)
|
|
|
|
|
{
|
|
|
|
|
n = (p.p_x + (cols+1)/2) / cols;
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("# cols doesn't divide displacement ref pt\n");
|
|
|
|
|
CalmaReadError(" %d / %d -> %d\n", p.p_x, cols, n);
|
2017-04-25 14:41:48 +02:00
|
|
|
xsep = n;
|
|
|
|
|
}
|
|
|
|
|
else xsep = p.p_x / cols;
|
|
|
|
|
}
|
|
|
|
|
if (rows)
|
|
|
|
|
{
|
|
|
|
|
GeoTransPoint(&tinv, &refarray[2], &p);
|
|
|
|
|
if (p.p_y % rows)
|
|
|
|
|
{
|
|
|
|
|
n = (p.p_y + (rows+1)/2) / rows;
|
2019-07-23 14:45:42 +02:00
|
|
|
CalmaReadError("# rows doesn't divide displacement ref pt\n");
|
|
|
|
|
CalmaReadError(" %d / %d -> %d\n", p.p_y, rows, n);
|
2017-04-25 14:41:48 +02:00
|
|
|
ysep = n;
|
|
|
|
|
}
|
|
|
|
|
ysep = p.p_y / rows;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check for cells which have *not* been marked for flattening
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!(def->cd_flags & CDFLATGDS))
|
|
|
|
|
{
|
|
|
|
|
use = DBCellNewUse(def, (useid) ? useid : (char *) NULL);
|
|
|
|
|
if (isArray)
|
|
|
|
|
DBMakeArray(use, &GeoIdentityTransform, xlo, ylo, xhi, yhi, xsep, ysep);
|
|
|
|
|
DBSetTrans(use, &trans);
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
|
|
|
|
|
|
|
|
|
break; // No need to do 2nd loop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (i == 1)
|
|
|
|
|
{
|
|
|
|
|
Plane **gdsplanes = (Plane **)def->cd_client;
|
|
|
|
|
GDSCopyRec gdsCopyRec;
|
|
|
|
|
int pNum;
|
|
|
|
|
bool hasUses;
|
|
|
|
|
|
|
|
|
|
// Mark cell as flattened (at least once)
|
|
|
|
|
def->cd_flags |= CDFLATTENED;
|
|
|
|
|
|
|
|
|
|
for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
|
|
|
|
|
{
|
2020-11-21 01:56:41 +01:00
|
|
|
if ((def->cd_client != (ClientData)0) && (gdsplanes[pNum] != NULL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
gdsCopyRec.plane = cifCurReadPlanes[pNum];
|
|
|
|
|
if (isArray)
|
|
|
|
|
{
|
|
|
|
|
Transform artrans;
|
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
|
|
gdsCopyRec.trans = &artrans;
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < cols; x++)
|
|
|
|
|
for (y = 0; y < rows; y++)
|
|
|
|
|
{
|
|
|
|
|
GeoTransTranslate((x * xsep), (y * ysep), &trans,
|
|
|
|
|
&artrans);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, gdsplanes[pNum],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceBits,
|
|
|
|
|
gdsCopyPaintFunc, &gdsCopyRec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gdsCopyRec.trans = &trans;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, gdsplanes[pNum], &TiPlaneRect,
|
|
|
|
|
&DBAllButSpaceBits, gdsCopyPaintFunc, &gdsCopyRec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 20:16:37 +01:00
|
|
|
/* When not reading with VENDORGDS, if a cell has contents */
|
|
|
|
|
/* other than the paint to be flattened, then also generate an */
|
|
|
|
|
/* instance of the cell. Otherwise (with VENDORGDS), always */
|
|
|
|
|
/* generate cell instances. Note that only paint and cells */
|
|
|
|
|
/* are counted, not labels (see below). */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-03-18 20:16:37 +01:00
|
|
|
else if (!(def->cd_flags & CDVENDORGDS))
|
|
|
|
|
{
|
|
|
|
|
int plane;
|
|
|
|
|
for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
|
|
|
|
|
if (DBSrPaintArea((Tile *)NULL, def->cd_planes[plane], &TiPlaneRect,
|
|
|
|
|
&DBAllButSpaceAndDRCBits, calmaEnumFunc, (ClientData)NULL))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if ((plane < DBNumPlanes) || DBCellEnum(def, gdsHasUses, (ClientData)NULL))
|
|
|
|
|
{
|
|
|
|
|
use = DBCellNewUse(def, (useid) ? useid : (char *) NULL);
|
|
|
|
|
if (isArray)
|
|
|
|
|
DBMakeArray(use, &GeoIdentityTransform, xlo, ylo, xhi, yhi, xsep, ysep);
|
|
|
|
|
DBSetTrans(use, &trans);
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
|
|
|
|
madeinst = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* (To do: Copy labels from flattened cells, with hierarchical */
|
|
|
|
|
/* names. Whether to do this or not should be an option.) */
|
2021-03-19 01:47:59 +01:00
|
|
|
/* TxPrintf("Removing instances of flattened cell %s in %s\n",
|
|
|
|
|
def->cd_name, cifReadCellDef->cd_name); */
|
2021-03-18 20:16:37 +01:00
|
|
|
madeinst = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
use = DBCellNewUse(def, (useid) ? useid : (char *) NULL);
|
|
|
|
|
if (isArray)
|
|
|
|
|
DBMakeArray(use, &GeoIdentityTransform, xlo, ylo, xhi, yhi, xsep, ysep);
|
|
|
|
|
DBSetTrans(use, &trans);
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
|
|
|
|
madeinst = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Done with allocated variables */
|
|
|
|
|
if (sname != NULL) freeMagic(sname);
|
|
|
|
|
if (useid != NULL) freeMagic(useid);
|
|
|
|
|
if (arraystr != NULL) freeMagic(arraystr);
|
|
|
|
|
|
|
|
|
|
return ((def->cd_flags & CDFLATGDS) ? (madeinst ? 1 : 0) : 1);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Callback function for determining if a cell has at least one subcell */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
gdsHasUses(use, clientdata)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
ClientData clientdata;
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Callback function for copying paint from one CIF cell into another */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
gdsCopyPaintFunc(tile, gdsCopyRec)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
GDSCopyRec *gdsCopyRec;
|
|
|
|
|
{
|
|
|
|
|
int pNum;
|
2017-08-02 04:14:42 +02:00
|
|
|
TileType dinfo;
|
2017-04-25 14:41:48 +02:00
|
|
|
Rect sourceRect, targetRect;
|
|
|
|
|
Transform *trans = gdsCopyRec->trans;
|
|
|
|
|
Plane *plane = gdsCopyRec->plane;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
dinfo = TiGetTypeExact(tile);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (trans)
|
|
|
|
|
{
|
|
|
|
|
TiToRect(tile, &sourceRect);
|
|
|
|
|
GeoTransRect(trans, &sourceRect, &targetRect);
|
2017-08-02 04:14:42 +02:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
dinfo = DBTransformDiagonal(TiGetTypeExact(tile), trans);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TiToRect(tile, &targetRect);
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
DBNMPaintPlane(plane, dinfo, &targetRect, CIFPaintTable,
|
2017-04-25 14:41:48 +02:00
|
|
|
(PaintUndoInfo *)NULL);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaFindCell --
|
|
|
|
|
*
|
|
|
|
|
* This local procedure is used to find a cell in the subcell table,
|
|
|
|
|
* and create a new subcell if there isn't already one there.
|
|
|
|
|
* If a new subcell is created, its CDAVAILABLE is left FALSE.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* The return value is a pointer to the definition for the
|
|
|
|
|
* cell whose name is 'name'.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* A new CellDef may be created.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
2020-12-04 22:56:51 +01:00
|
|
|
calmaFindCell(name, was_called, predefined)
|
2017-04-25 14:41:48 +02:00
|
|
|
char *name; /* Name of desired cell */
|
|
|
|
|
bool *was_called; /* If this cell is in the hash table, then it
|
|
|
|
|
* was instanced before it was defined. We
|
|
|
|
|
* need to know this so as to avoid flattening
|
|
|
|
|
* the cell if requested.
|
|
|
|
|
*/
|
2020-12-04 22:56:51 +01:00
|
|
|
bool *predefined; /* If this cell was in memory before the GDS
|
|
|
|
|
* file was read, then this flag gets set.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
|
|
|
|
|
h = HashFind(&CifCellTable, name);
|
|
|
|
|
if (HashGetValue(h) == 0)
|
|
|
|
|
{
|
|
|
|
|
def = DBCellLookDef(name);
|
|
|
|
|
if (def == NULL)
|
|
|
|
|
{
|
2020-03-21 17:40:35 +01:00
|
|
|
def = DBCellNewDef(name);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tricky point: call DBReComputeBbox here to make SURE
|
|
|
|
|
* that the cell has a valid bounding box. Otherwise,
|
|
|
|
|
* if the cell is used in a parent before being defined
|
|
|
|
|
* then it will cause a core dump.
|
|
|
|
|
*/
|
|
|
|
|
DBReComputeBbox(def);
|
|
|
|
|
}
|
2020-12-04 22:56:51 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Warning: cell %s already existed before reading GDS!\n",
|
|
|
|
|
name);
|
|
|
|
|
if (CalmaNoDuplicates)
|
|
|
|
|
{
|
|
|
|
|
if (predefined) *predefined = TRUE;
|
|
|
|
|
TxPrintf("Using pre-existing cell definition\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
HashSetValue(h, def);
|
|
|
|
|
if (was_called) *was_called = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (was_called) *was_called = TRUE;
|
|
|
|
|
return (CellDef *) HashGetValue(h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaLookCell --
|
|
|
|
|
*
|
|
|
|
|
* This procedure is like calmaFindCell above, but it will not
|
|
|
|
|
* create a new subcell if the named cell does not already exist.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* The return value is a pointer to the definition for the
|
|
|
|
|
* cell whose name is 'name', or NULL if cell 'name' does
|
|
|
|
|
* not exist.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
|
|
|
|
calmaLookCell(name)
|
|
|
|
|
char *name; /* Name of desired cell */
|
|
|
|
|
{
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
|
|
|
|
|
h = HashLookOnly(&CifCellTable, name);
|
|
|
|
|
if (h == NULL)
|
|
|
|
|
return (CellDef *)NULL;
|
|
|
|
|
else
|
|
|
|
|
return (CellDef *)HashGetValue(h);
|
|
|
|
|
}
|
|
|
|
|
|