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
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/calma/CalmaRdcl.c,v 1.5 2010/06/25 13:59:24 tim Exp $";
|
2017-04-25 14:41:48 +02:00
|
|
|
#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"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
int calmaNonManhattan;
|
2020-05-14 21:59:39 +02:00
|
|
|
int CalmaFlattenLimit = 10;
|
2021-02-24 20:41:35 +01:00
|
|
|
int NameConvertErrors = 0;
|
2022-01-14 17:07:08 +01:00
|
|
|
bool CalmaRewound = FALSE;
|
2022-01-21 20:48:59 +01:00
|
|
|
TileTypeBitMask *CalmaMaskHints = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
extern HashTable calmaDefInitHash;
|
2022-12-14 03:58:30 +01:00
|
|
|
extern int CalmaPolygonCount;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* forward declarations */
|
2024-10-04 12:32:22 +02:00
|
|
|
int calmaElementSref(char *filename);
|
|
|
|
|
bool calmaParseElement(char *filename, int *pnsrefs, int *pnpaths);
|
|
|
|
|
void calmaUniqueCell(char *sname);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
OFFTYPE
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaSetPosition(
|
|
|
|
|
char *sname)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-05-10 15:19:39 +02:00
|
|
|
OFFTYPE originalPos = 0, currentPos = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
int nbytes, rtype;
|
|
|
|
|
char *strname = NULL;
|
|
|
|
|
int strRecSize = 0;
|
|
|
|
|
bool found = FALSE;
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
originalPos = FTELL(calmaInputFile);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
while (FEOF(calmaInputFile) == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
READRH(nbytes, rtype); /* Read header */
|
|
|
|
|
if (nbytes <= 0) break;
|
|
|
|
|
|
|
|
|
|
/* Skip no of bytes in record header until
|
|
|
|
|
* we reach to next structure record.
|
|
|
|
|
*/
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, nbytes - CALMAHEADERLENGTH, SEEK_CUR);
|
2017-04-25 14:41:48 +02:00
|
|
|
} 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++;
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, -(nbytes + strRecSize + CALMAHEADERLENGTH),
|
2017-04-25 14:41:48 +02:00
|
|
|
SEEK_CUR);
|
|
|
|
|
freeMagic(strname);
|
|
|
|
|
return originalPos;
|
|
|
|
|
}
|
|
|
|
|
freeMagic(strname);
|
2022-01-01 05:11:24 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 05:11:24 +01:00
|
|
|
/* 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.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 05:11:24 +01:00
|
|
|
if (originalPos != 0)
|
|
|
|
|
{
|
2022-05-11 18:38:05 +02:00
|
|
|
REWIND(calmaInputFile);
|
2022-01-14 17:07:08 +01:00
|
|
|
CalmaRewound = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaSetPosition(sname);
|
2022-01-14 17:07:08 +01:00
|
|
|
if (!CalmaPostOrder)
|
|
|
|
|
CalmaReadError("Rewinding input. Cells may have been instanced before "
|
|
|
|
|
"they were defined. Consider using \"gds ordering on\".\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return originalPos;
|
2022-01-01 05:11:24 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 05:11:24 +01:00
|
|
|
/* Avoid generating an error message in the case that the cell is not in
|
|
|
|
|
* the GDS file but is in memory. Assume that such a case is intentional,
|
|
|
|
|
* meaning the GDS library is an addendum and the proper library it
|
|
|
|
|
* depends on was read first as intended.
|
|
|
|
|
*/
|
|
|
|
|
if (DBCellLookDef(sname) == NULL)
|
|
|
|
|
CalmaReadError("Cell \"%s\" is used but not defined in this file.\n",
|
|
|
|
|
sname);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 05:11:24 +01:00
|
|
|
return originalPos;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaNextCell(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int nbytes, rtype;
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
if (FEOF(calmaInputFile) == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, -(CALMAHEADERLENGTH), SEEK_END);
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skip no. of bytes in record header to reach the next
|
|
|
|
|
* structure record.
|
|
|
|
|
*/
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, nbytes - CALMAHEADERLENGTH, SEEK_CUR);
|
2017-04-25 14:41:48 +02:00
|
|
|
} while((rtype != CALMA_BGNSTR) && (rtype != CALMA_ENDLIB));
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, -nbytes, SEEK_CUR);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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 **
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaExact(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int pNum;
|
|
|
|
|
Plane *newplane;
|
|
|
|
|
Plane **parray;
|
2024-10-04 12:32:22 +02:00
|
|
|
int gdsCopyPaintFunc(Tile *tile, GDSCopyRec *gdsCopyRec); /* Forward reference */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 03:58:30 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaFlattenPolygonFunc --
|
|
|
|
|
*
|
|
|
|
|
* Polygons have been dropped into subcells by default for
|
|
|
|
|
* efficiency in reading. If the "subcell polygons" option
|
|
|
|
|
* has not been selected, then flatten these cells into the
|
|
|
|
|
* layout and delete the cells. This seems inefficient but
|
|
|
|
|
* in fact can be much faster than reading the polygons
|
|
|
|
|
* directly into the cell from GDS.
|
|
|
|
|
*
|
2022-12-14 18:44:30 +01:00
|
|
|
* Return value:
|
|
|
|
|
* Return 0 to keep the search going
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Polygons are copied from use->cu_def to parent.
|
|
|
|
|
* use->cu_def is deleted.
|
|
|
|
|
*
|
2022-12-14 03:58:30 +01:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaFlattenPolygonFunc(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
CellDef *parent)
|
2022-12-14 03:58:30 +01:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
CellUse dummy;
|
|
|
|
|
SearchContext scx;
|
2022-12-14 18:44:30 +01:00
|
|
|
HashEntry *he;
|
2022-12-14 03:58:30 +01:00
|
|
|
|
|
|
|
|
if (use->cu_def == NULL || use->cu_def->cd_name == NULL) return 0;
|
|
|
|
|
if (strncmp(use->cu_def->cd_name, "polygon", 7)) return 0;
|
|
|
|
|
|
|
|
|
|
dummy.cu_transform = GeoIdentityTransform;
|
|
|
|
|
dummy.cu_id = NULL;
|
|
|
|
|
dummy.cu_def = parent;
|
|
|
|
|
scx.scx_use = use;
|
|
|
|
|
scx.scx_area = use->cu_bbox;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, 0, &dummy);
|
|
|
|
|
DBDeleteCellNoModify(use);
|
2022-12-14 18:44:30 +01:00
|
|
|
HashRemove(&CifCellTable, use->cu_def->cd_name);
|
|
|
|
|
/* There should only be one use, so it can just be cleared */
|
|
|
|
|
use->cu_def->cd_parents = (CellUse *)NULL;
|
2022-12-14 03:58:30 +01:00
|
|
|
DBCellDeleteDef(use->cu_def);
|
|
|
|
|
|
|
|
|
|
return 0; /* Keep the search going */
|
|
|
|
|
}
|
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
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaParseStructure(
|
|
|
|
|
char *filename) /* Name of the GDS file read */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-10-04 12:19:10 +02:00
|
|
|
static const int structs[] = { CALMA_STRCLASS, CALMA_STRTYPE, -1 };
|
2024-10-04 20:52:52 +02:00
|
|
|
int nbytes = -1, rtype = 0, nsrefs, osrefs, npaths;
|
2019-12-19 23:28:06 +01:00
|
|
|
char *strname = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashEntry *he;
|
2022-01-21 19:05:24 +01:00
|
|
|
int timestampval = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
int suffix;
|
|
|
|
|
int mfactor;
|
2022-12-14 18:44:30 +01:00
|
|
|
int locPolygonCount;
|
2022-05-10 15:19:39 +02:00
|
|
|
OFFTYPE filepos;
|
2023-03-14 17:53:22 +01:00
|
|
|
bool was_called = FALSE;
|
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;
|
2022-12-15 18:25:23 +01:00
|
|
|
Label *lab;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-12-14 18:44:30 +01:00
|
|
|
locPolygonCount = CalmaPolygonCount;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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;
|
2022-01-21 19:05:24 +01:00
|
|
|
if (!calmaReadStampRecord(CALMA_BGNSTR, ×tampval)) goto syntaxerror;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
|
|
|
|
|
TxPrintf("Reading \"%s\".\n", strname);
|
|
|
|
|
|
2021-04-22 20:39:34 +02:00
|
|
|
/* Used for read-only and annotated LEF views */
|
2022-05-10 15:19:39 +02:00
|
|
|
filepos = FTELL(calmaInputFile);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Set up the cell definition */
|
|
|
|
|
he = HashFind(&calmaDefInitHash, strname);
|
|
|
|
|
if ((def = (CellDef *)HashGetValue(he)) != NULL)
|
|
|
|
|
{
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
if (def->cd_flags & CDPRELOADED)
|
|
|
|
|
{
|
|
|
|
|
/* Cell definition was read ahead due to option "flatten" */
|
|
|
|
|
/* or "flatglob". Do not complain about seeing it again. */
|
|
|
|
|
def->cd_flags &= ~CDPRELOADED;
|
|
|
|
|
calmaNextCell();
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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. */
|
|
|
|
|
|
2022-01-14 17:07:08 +01:00
|
|
|
if (!CalmaPostOrder && !CalmaRewound)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
cifReadCellDef = def;
|
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;
|
|
|
|
|
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
cifReadCellDef = def;
|
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
|
|
|
}
|
|
|
|
|
}
|
2021-04-27 19:07:27 +02:00
|
|
|
if (CalmaUnique) calmaUniqueCell(strname); /* Ensure uniqueness */
|
2020-12-04 22:56:51 +01:00
|
|
|
cifReadCellDef = calmaFindCell(strname, &was_called, &predefined);
|
2021-05-13 04:49:25 +02:00
|
|
|
HashSetValue(he, cifReadCellDef);
|
2021-04-22 20:39:34 +02:00
|
|
|
|
2020-12-04 22:56:51 +01:00
|
|
|
if (predefined == TRUE)
|
|
|
|
|
{
|
2021-04-22 20:39:34 +02:00
|
|
|
bool isAbstract;
|
|
|
|
|
|
|
|
|
|
/* If the cell was predefined, the "noduplicates" option was
|
|
|
|
|
* invoked, and the existing cell is an abstract view, then
|
|
|
|
|
* annotate the cell with the GDS file pointers to the cell
|
|
|
|
|
* data, and the GDS filename.
|
|
|
|
|
*/
|
|
|
|
|
DBPropGet(cifReadCellDef, "LEFview", &isAbstract);
|
|
|
|
|
if (!isAbstract)
|
|
|
|
|
{
|
|
|
|
|
calmaNextCell();
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
calmaSkipTo(CALMA_ENDSTR);
|
2020-12-04 22:56:51 +01:00
|
|
|
}
|
2021-04-22 20:39:34 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-22 20:39:34 +02:00
|
|
|
DBCellClearDef(cifReadCellDef);
|
|
|
|
|
DBCellSetAvail(cifReadCellDef);
|
|
|
|
|
cifCurReadPlanes = cifSubcellPlanes;
|
|
|
|
|
cifReadCellDef->cd_flags &= ~CDDEREFERENCE;
|
|
|
|
|
|
2022-01-21 19:05:24 +01:00
|
|
|
/* Set timestamp from the GDS cell's creation time */
|
|
|
|
|
/* timestamp, unless "calma datestamp" was specified */
|
|
|
|
|
/* with a value, in which case use that value. */
|
|
|
|
|
|
|
|
|
|
if (CalmaDateStamp == NULL)
|
|
|
|
|
cifReadCellDef->cd_timestamp = timestampval;
|
|
|
|
|
else
|
2022-01-22 17:18:32 +01:00
|
|
|
{
|
2022-01-21 19:05:24 +01:00
|
|
|
cifReadCellDef->cd_timestamp = *CalmaDateStamp;
|
2022-01-22 17:18:32 +01:00
|
|
|
if (*CalmaDateStamp != (time_t)0)
|
|
|
|
|
cifReadCellDef->cd_flags |= CDFIXEDSTAMP;
|
|
|
|
|
}
|
2022-01-21 19:05:24 +01:00
|
|
|
|
2021-04-22 20:39:34 +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));
|
|
|
|
|
was_initialized = TRUE;
|
|
|
|
|
|
|
|
|
|
/* Body of structure: a sequence of elements */
|
|
|
|
|
osrefs = nsrefs = 0;
|
|
|
|
|
npaths = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaNonManhattan = 0;
|
2021-04-22 20:39:34 +02:00
|
|
|
|
|
|
|
|
while (calmaParseElement(filename, &nsrefs, &npaths))
|
|
|
|
|
{
|
|
|
|
|
if (SigInterruptPending)
|
|
|
|
|
goto done;
|
2021-07-30 21:40:39 +02:00
|
|
|
if (nsrefs > osrefs && (nsrefs % 5000) == 0)
|
2021-04-22 20:39:34 +02:00
|
|
|
TxPrintf(" %d uses\n", nsrefs);
|
|
|
|
|
osrefs = nsrefs;
|
|
|
|
|
calmaNonManhattan = 0;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 20:39:34 +02:00
|
|
|
if (CalmaReadOnly || predefined)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-22 20:39:34 +02:00
|
|
|
char cstring[1024];
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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);
|
2021-04-22 20:39:34 +02:00
|
|
|
char *fncopy;
|
|
|
|
|
|
|
|
|
|
/* Substitute variable for PDK path or ~ for home directory */
|
|
|
|
|
/* the same way that cell references are handled in .mag files. */
|
2022-05-28 16:33:21 +02:00
|
|
|
DBPathSubstitute(filename, cstring, cifReadCellDef);
|
2021-04-22 20:39:34 +02:00
|
|
|
fncopy = StrDup(NULL, cstring);
|
2017-04-25 14:41:48 +02:00
|
|
|
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_START", (ClientData)fpcopy);
|
|
|
|
|
|
|
|
|
|
fpcopy = (char *)mallocMagic(20);
|
2022-05-10 15:19:39 +02:00
|
|
|
filepos = FTELL(calmaInputFile);
|
2017-04-25 14:41:48 +02:00
|
|
|
sprintf(fpcopy, "%"DLONG_PREFIX"d", (dlong) filepos);
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_END", (ClientData)fpcopy);
|
|
|
|
|
|
|
|
|
|
DBPropPut(cifReadCellDef, "GDS_FILE", (ClientData)fncopy);
|
|
|
|
|
|
2021-04-22 20:39:34 +02:00
|
|
|
if (predefined)
|
|
|
|
|
{
|
|
|
|
|
if (strname != NULL) freeMagic(strname);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
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;
|
2022-06-08 22:02:40 +02:00
|
|
|
|
|
|
|
|
/* Remove any labels in this cell */
|
|
|
|
|
DBEraseLabel(cifReadCellDef, &TiPlaneRect, &DBAllTypeBits, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2022-12-15 18:25:23 +01:00
|
|
|
/* Check for empty string labels that are caused by pin geometry that
|
|
|
|
|
* did not get any corresponding text type, and remove them.
|
|
|
|
|
*/
|
|
|
|
|
DBEraseLabelsByContent(cifReadCellDef, NULL, -1, "");
|
|
|
|
|
|
2023-01-18 02:14:38 +01:00
|
|
|
if ((CalmaSubcellPolygons == CALMA_POLYGON_TEMP) &&
|
|
|
|
|
(locPolygonCount < CalmaPolygonCount))
|
2022-12-14 03:58:30 +01:00
|
|
|
DBCellEnum(cifReadCellDef, calmaFlattenPolygonFunc, (ClientData)cifReadCellDef);
|
|
|
|
|
|
2023-10-17 21:54:38 +02:00
|
|
|
/* Because the "label" statement can only match one GDS layer to one magic
|
|
|
|
|
* layer, then all labels are subject to adjustment to attach them to
|
|
|
|
|
* something meaningful in the layout. The "no-reconnect-labels" option
|
|
|
|
|
* is not useful and has been deprecated.
|
|
|
|
|
*/
|
|
|
|
|
DBAdjustLabelsNew(cifReadCellDef, &TiPlaneRect);
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2022-01-22 04:17:54 +01:00
|
|
|
/* Note: This sets the CDGETNEWSTAMP flag, but if the timestamp is
|
|
|
|
|
* not zero, then it gets cleared in CIFReadCellCleanup().
|
|
|
|
|
*/
|
|
|
|
|
DBCellSetModified(cifReadCellDef, TRUE);
|
2022-01-21 23:43:04 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaParseElement(
|
|
|
|
|
char *filename,
|
|
|
|
|
int *pnsrefs,
|
|
|
|
|
int *pnpaths)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-10-04 12:19:10 +02:00
|
|
|
static const int node[] = { CALMA_ELFLAGS, CALMA_PLEX, CALMA_LAYER,
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaEnumFunc(
|
|
|
|
|
Tile *tile,
|
|
|
|
|
int *plane)
|
2021-03-18 20:16:37 +01:00
|
|
|
{
|
|
|
|
|
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
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaElementSref(
|
|
|
|
|
char *filename)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
bool dolookahead = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
Transform trans, tinv;
|
|
|
|
|
Point refarray[3], refunscaled[3], p;
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *def;
|
2024-10-04 12:32:22 +02:00
|
|
|
int gdsCopyPaintFunc(Tile *tile, GDSCopyRec *gdsCopyRec); /* Forward reference */
|
|
|
|
|
int gdsHasUses(CellUse *use, ClientData clientdata); /* Forward reference */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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);
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the "flatten" option is set, then we always have to seek
|
|
|
|
|
* ahead and read the structure in order to determine if it
|
|
|
|
|
* meets the requirement of being flattened or not. If the
|
|
|
|
|
* "flatglob" option is set, then we need to read ahead and
|
|
|
|
|
* read the cell definition so that it can be flatten. This
|
|
|
|
|
* requires pattern-matching the cell def.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
dolookahead = (CalmaPostOrder || CalmaFlattenUses) ? TRUE : FALSE;
|
|
|
|
|
if ((!dolookahead) && (CalmaFlattenUsesByName != NULL))
|
|
|
|
|
{
|
|
|
|
|
char *pattern;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
pattern = CalmaFlattenUsesByName[i];
|
|
|
|
|
if (pattern == NULL) break;
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Check pattern against strname */
|
|
|
|
|
if (Match(pattern, sname))
|
|
|
|
|
{
|
|
|
|
|
dolookahead = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!def && dolookahead)
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
OFFTYPE originalFilePos = calmaSetPosition(sname);
|
|
|
|
|
if (!FEOF(calmaInputFile))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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. */
|
2022-05-10 15:19:39 +02:00
|
|
|
FSEEK(calmaInputFile, originalFilePos, SEEK_SET);
|
2017-04-25 14:41:48 +02:00
|
|
|
cifReadCellDef = calmaLookCell(currentSname);
|
|
|
|
|
def = calmaLookCell(sname);
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
def->cd_flags |= CDPRELOADED;
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
|
{
|
2022-01-01 05:11:24 +01:00
|
|
|
/* This is redundant messaging */
|
|
|
|
|
// TxPrintf("Cell definition %s does not exist!\n", sname);
|
2022-05-10 15:19:39 +02:00
|
|
|
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;
|
2022-05-10 15:19:39 +02:00
|
|
|
if (FEOF(calmaInputFile)) return -1;
|
2017-04-25 14:41:48 +02:00
|
|
|
(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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
if (FEOF(calmaInputFile))
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2021-06-13 18:23:07 +02:00
|
|
|
if (propAttrType == CALMA_PROP_USENAME ||
|
|
|
|
|
propAttrType == CALMA_PROP_USENAME_STD)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
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);
|
2022-12-15 18:25:23 +01:00
|
|
|
if (DBCellFindDup(use, cifReadCellDef) != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBCellDeleteUse(use);
|
|
|
|
|
CalmaReadError("Warning: cell \"%s\" placed on top of"
|
|
|
|
|
" itself. Ignoring the extra one.\n", def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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);
|
2022-12-15 18:25:23 +01:00
|
|
|
if (DBCellFindDup(use, cifReadCellDef) != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBCellDeleteUse(use);
|
|
|
|
|
CalmaReadError("Warning: cell \"%s\" placed on top of"
|
|
|
|
|
" itself. Ignoring the extra one.\n", def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
|
|
|
|
madeinst = TRUE;
|
|
|
|
|
}
|
2021-03-18 20:16:37 +01:00
|
|
|
}
|
|
|
|
|
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);
|
2022-12-15 18:25:23 +01:00
|
|
|
if (DBCellFindDup(use, cifReadCellDef) != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBCellDeleteUse(use);
|
|
|
|
|
CalmaReadError("Warning: cell \"%s\" placed on top of"
|
|
|
|
|
" itself. Ignoring the extra one.\n", def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DBPlaceCell(use, cifReadCellDef);
|
|
|
|
|
madeinst = TRUE;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2024-10-04 12:34:50 +02:00
|
|
|
gdsHasUses(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
ClientData clientdata)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Callback function for copying paint from one CIF cell into another */
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
gdsCopyPaintFunc(
|
|
|
|
|
Tile *tile,
|
|
|
|
|
GDSCopyRec *gdsCopyRec)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-27 19:07:27 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaUniqueCell --
|
|
|
|
|
*
|
|
|
|
|
* Attempt to find a cell in the GDS subcell name hash table.
|
|
|
|
|
* If one exists, rename its definition so that it will not
|
|
|
|
|
* be overwritten when the cell is redefined.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaUniqueCell(
|
|
|
|
|
char *sname)
|
2021-04-27 19:07:27 +02:00
|
|
|
{
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
CellDef *def, *testdef;
|
|
|
|
|
char *newname;
|
|
|
|
|
int snum = 0;
|
|
|
|
|
|
|
|
|
|
h = HashLookOnly(&CifCellTable, sname);
|
2021-04-27 20:53:34 +02:00
|
|
|
|
|
|
|
|
/* Existing entry with zero value indicates that the existing */
|
|
|
|
|
/* cell came from the same GDS file, so don't change anything. */
|
|
|
|
|
if ((h != NULL) && HashGetValue(h) == 0) return;
|
2021-04-27 19:07:27 +02:00
|
|
|
|
|
|
|
|
def = DBCellLookDef(sname);
|
|
|
|
|
if (def == (CellDef *)NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Cell may have been called but not yet defined---this is okay. */
|
|
|
|
|
else if ((def->cd_flags & CDAVAILABLE) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
testdef = def;
|
|
|
|
|
newname = (char *)mallocMagic(10 + strlen(sname));
|
|
|
|
|
|
|
|
|
|
while (testdef != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Keep appending suffix indexes until we find one not used */
|
|
|
|
|
sprintf(newname, "%s_%d", sname, ++snum);
|
|
|
|
|
testdef = DBCellLookDef(newname);
|
|
|
|
|
}
|
|
|
|
|
DBCellRenameDef(def, newname);
|
|
|
|
|
|
|
|
|
|
h = HashFind(&CifCellTable, (char *)sname);
|
|
|
|
|
HashSetValue(h, 0);
|
|
|
|
|
|
|
|
|
|
CalmaReadError("Warning: cell definition \"%s\" reused.\n", sname);
|
|
|
|
|
freeMagic(newname);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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 *
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaFindCell(
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *name, /* Name of desired cell */
|
2024-10-04 12:34:50 +02:00
|
|
|
bool *was_called, /* If this cell is in the hash table, then it
|
2017-04-25 14:41:48 +02:00
|
|
|
* was instanced before it was defined. We
|
|
|
|
|
* need to know this so as to avoid flattening
|
|
|
|
|
* the cell if requested.
|
|
|
|
|
*/
|
2024-10-04 12:34:50 +02:00
|
|
|
bool *predefined) /* If this cell was in memory before the GDS
|
2020-12-04 22:56:51 +01:00
|
|
|
* 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.
|
|
|
|
|
*/
|
2023-05-18 15:25:37 +02:00
|
|
|
DBReComputeBbox(def);
|
|
|
|
|
if (was_called) *was_called = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-12-04 22:56:51 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (CalmaNoDuplicates)
|
|
|
|
|
{
|
2025-10-01 21:38:41 +02:00
|
|
|
TxPrintf("Note: cell %s already existed before reading GDS.\n",
|
|
|
|
|
name);
|
2020-12-04 22:56:51 +01:00
|
|
|
if (predefined) *predefined = TRUE;
|
|
|
|
|
TxPrintf("Using pre-existing cell definition\n");
|
|
|
|
|
}
|
2025-10-01 21:38:41 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Warning: cell %s already existed before reading GDS!\n",
|
|
|
|
|
name);
|
|
|
|
|
}
|
2023-05-18 15:25:37 +02:00
|
|
|
if (was_called) *was_called = TRUE;
|
2020-12-04 22:56:51 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
HashSetValue(h, def);
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-06-12 17:40:13 +02:00
|
|
|
{
|
|
|
|
|
if (was_called)
|
|
|
|
|
{
|
|
|
|
|
if (*was_called == TRUE)
|
|
|
|
|
{
|
|
|
|
|
def = DBCellLookDef(name);
|
|
|
|
|
if ((def != NULL) && (def->cd_flags & CDAVAILABLE))
|
|
|
|
|
if (CalmaNoDuplicates)
|
|
|
|
|
if (predefined) *predefined = TRUE;
|
|
|
|
|
}
|
|
|
|
|
*was_called = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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 *
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaLookCell(
|
|
|
|
|
char *name) /* Name of desired cell */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
|
|
|
|
|
h = HashLookOnly(&CifCellTable, name);
|
|
|
|
|
if (h == NULL)
|
|
|
|
|
return (CellDef *)NULL;
|
|
|
|
|
else
|
|
|
|
|
return (CellDef *)HashGetValue(h);
|
|
|
|
|
}
|
|
|
|
|
|