2022-05-10 15:19:39 +02:00
|
|
|
/*
|
|
|
|
|
* CalmaWriteZ.c --
|
|
|
|
|
*
|
|
|
|
|
* Output of gzip-compressed Calma GDS-II stream format.
|
|
|
|
|
* This is essentially a duplication of all functions in CalmaWrite.c that
|
|
|
|
|
* write I/O to output, substituting zlib functions for the standard I/O
|
|
|
|
|
* functions. Note that since zlib can *read* an uncompressed file through
|
|
|
|
|
* the same calls that it uses to write, then zlib is used for *all* GDS
|
|
|
|
|
* file reads. Only the GDS write functions have duplicate routines,
|
|
|
|
|
* because writing an uncompressed file with zlib still writes a file in
|
|
|
|
|
* gzip format, so to maintain the ability to write plain GDS files, a
|
|
|
|
|
* set of routines is needed that is separate from the zlib routines.
|
|
|
|
|
*
|
|
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
|
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char rcsid[] __attribute__ ((unused)) ="$Header: /usr/cvsroot/magic-8.0/calma/CalmaWrite.c,v 1.8 2010/12/22 16:29:06 tim Exp $";
|
2022-05-10 15:19:39 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdlib.h> /* for random() */
|
|
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2022-05-10 15:19:39 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <arpa/inet.h> /* for htons() */
|
2024-10-12 14:57:08 +02:00
|
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# include <time.h>
|
2022-05-10 15:19:39 +02:00
|
|
|
#else
|
2024-10-12 14:57:08 +02:00
|
|
|
# ifdef HAVE_SYS_TIME_H
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# else
|
|
|
|
|
# include <time.h>
|
|
|
|
|
# endif
|
2022-05-10 15:19:39 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/utils.h"
|
2024-10-04 21:07:03 +02:00
|
|
|
#include "utils/magic_zlib.h"
|
2022-05-10 15:19:39 +02:00
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "database/databaseInt.h"
|
|
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "cif/cif.h"
|
|
|
|
|
#include "cif/CIFint.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"
|
2023-03-22 03:04:30 +01:00
|
|
|
#include "extract/extractInt.h" /* for LabelList */
|
2022-05-10 15:19:39 +02:00
|
|
|
#include "utils/main.h" /* for Path and CellLibPath */
|
|
|
|
|
#include "utils/stack.h"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "utils/undo.h"
|
|
|
|
|
#include "calma/calma.h"
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
/* External variables from CalmaWrite.c */
|
|
|
|
|
extern HashTable calmaLibHash;
|
|
|
|
|
extern HashTable calmaPrefixHash;
|
|
|
|
|
extern HashTable calmaUndefHash;
|
|
|
|
|
extern bool CalmaDoLibrary;
|
|
|
|
|
extern bool CalmaAllowUndefined;
|
2022-11-02 14:40:20 +01:00
|
|
|
extern bool CalmaAllowAbstract;
|
2022-05-10 15:19:39 +02:00
|
|
|
extern bool CalmaContactArrays;
|
|
|
|
|
extern bool CalmaAddendum;
|
|
|
|
|
extern bool CalmaMergeTiles;
|
|
|
|
|
extern bool CalmaDoLabels;
|
|
|
|
|
extern bool CalmaDoLower;
|
|
|
|
|
extern bool CalmaFlattenArrays;
|
|
|
|
|
extern time_t *CalmaDateStamp;
|
|
|
|
|
extern int calmaCellNum;
|
|
|
|
|
extern int calmaWriteScale;
|
|
|
|
|
extern int calmaPaintScale;
|
|
|
|
|
extern int calmaPaintLayerNumber;
|
|
|
|
|
extern int calmaPaintLayerType;
|
|
|
|
|
|
|
|
|
|
/* External functions from CalmaWrite.c */
|
2024-10-04 12:32:22 +02:00
|
|
|
extern int calmaWriteInitFunc(CellDef *def);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
2024-10-04 12:38:50 +02:00
|
|
|
/* Structure used by calmaWritePaintFuncZ() and others */
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
gzFile f; /* Compressed file stream for output */
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *area; /* Clipping area, in GDS coordinates */
|
2024-10-04 12:38:50 +02:00
|
|
|
int type; /* Layer index */
|
|
|
|
|
} calmaOutputStructZ;
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
/* Forward declarations */
|
2024-10-04 12:32:22 +02:00
|
|
|
extern int calmaWritePaintFuncZ(Tile *tile, calmaOutputStructZ *cos);
|
|
|
|
|
extern int calmaMergePaintFuncZ(Tile *tile, calmaOutputStructZ *cos);
|
|
|
|
|
extern int calmaWriteUseFuncZ(CellUse *use, gzFile f);
|
|
|
|
|
extern int calmaPaintLabelFuncZ(Tile *tile, calmaOutputStructZ *cos);
|
|
|
|
|
extern void calmaWriteContactsZ(gzFile f);
|
2025-01-31 17:31:02 +01:00
|
|
|
extern void calmaOutFuncZ(CellDef *def, gzFile f, const Rect *cliprect);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern void calmaOutStructNameZ(int type, CellDef *def, gzFile f);
|
|
|
|
|
extern void calmaWriteLabelFuncZ(Label *lab, int ltype, int type, gzFile f);
|
|
|
|
|
extern void calmaOutHeaderZ(CellDef *rootDef, gzFile f);
|
|
|
|
|
extern void calmaOutDateZ(time_t t, gzFile f);
|
|
|
|
|
extern void calmaOutStringRecordZ(int type, char *str, gzFile f);
|
2024-10-04 12:19:10 +02:00
|
|
|
extern void calmaOut8Z(const char *str, gzFile f);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern void calmaOutR8Z(double d, gzFile f);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* Structures used by the tile merging algorithm */
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
#define GDS_PENDING 0
|
|
|
|
|
#define GDS_UNPROCESSED CLIENTDEFAULT
|
|
|
|
|
#define GDS_PROCESSED 1
|
|
|
|
|
|
2023-03-22 03:04:30 +01:00
|
|
|
#define PUSHTILEZ(tp) \
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tp) == GDS_UNPROCESSED) { \
|
|
|
|
|
TiSetClientINT(tp, GDS_PENDING); \
|
2022-05-10 15:19:39 +02:00
|
|
|
STACKPUSH((ClientData) (tp), SegStack); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Macros to output various pieces of Calma information.
|
|
|
|
|
* These are macros for speed.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutRHZ --
|
|
|
|
|
*
|
|
|
|
|
* Output a Calma record header.
|
|
|
|
|
* This consists of a two-byte count of the number of bytes in the
|
|
|
|
|
* record (including the two count bytes), a one-byte record type,
|
|
|
|
|
* and a one-byte data type.
|
|
|
|
|
*/
|
|
|
|
|
#define calmaOutRHZ(count, type, datatype, f) \
|
|
|
|
|
{ calmaOutI2Z(count, f); (void) gzputc(f, type); (void) gzputc(f, datatype); }
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutI2Z --
|
|
|
|
|
*
|
|
|
|
|
* Output a two-byte integer.
|
|
|
|
|
* Calma byte order is the same as the network byte order used
|
|
|
|
|
* by the various network library procedures.
|
|
|
|
|
*/
|
|
|
|
|
#define calmaOutI2Z(n, f) \
|
|
|
|
|
{ \
|
|
|
|
|
union { short u_s; char u_c[2]; } u; \
|
|
|
|
|
u.u_s = htons(n); \
|
|
|
|
|
(void) gzputc(f, u.u_c[0]); \
|
|
|
|
|
(void) gzputc(f, u.u_c[1]); \
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutI4Z --
|
|
|
|
|
*
|
|
|
|
|
* Output a four-byte integer.
|
|
|
|
|
* Calma byte order is the same as the network byte order used
|
|
|
|
|
* by the various network library procedures.
|
|
|
|
|
*/
|
|
|
|
|
#define calmaOutI4Z(n, f) \
|
|
|
|
|
{ \
|
|
|
|
|
union { long u_i; char u_c[4]; } u; \
|
|
|
|
|
u.u_i = htonl(n); \
|
|
|
|
|
(void) gzputc(f, u.u_c[0]); \
|
|
|
|
|
(void) gzputc(f, u.u_c[1]); \
|
|
|
|
|
(void) gzputc(f, u.u_c[2]); \
|
|
|
|
|
(void) gzputc(f, u.u_c[3]); \
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char calmaMapTableStrict[] =
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* NUL - BEL */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* BS - SI */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* DLE - ETB */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* CAN - US */
|
|
|
|
|
'_', '_', '_', '_', '$', '_', '_', '_', /* SP - ' */
|
|
|
|
|
'_', '_', '_', '_', '_', '_', '_', '_', /* ( - / */
|
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', /* 0 - 7 */
|
|
|
|
|
'8', '9', '_', '_', '_', '_', '_', '_', /* 8 - ? */
|
|
|
|
|
'_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* @ - G */
|
|
|
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* H - O */
|
|
|
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* P - W */
|
|
|
|
|
'X', 'Y', 'Z', '_', '_', '_', '_', '_', /* X - _ */
|
|
|
|
|
'_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* ` - g */
|
|
|
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* h - o */
|
|
|
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* p - w */
|
|
|
|
|
'x', 'y', 'z', '_', '_', '_', '_', 0, /* x - DEL */
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char calmaMapTablePermissive[] =
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* NUL - BEL */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* BS - SI */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* DLE - ETB */
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* CAN - US */
|
|
|
|
|
' ', '!', '"', '#', '$', '&', '%', '\'', /* SP - ' */
|
|
|
|
|
'(', ')', '*', '+', ',', '-', '.', '/', /* ( - / */
|
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', /* 0 - 7 */
|
|
|
|
|
'8', '9', ':', ';', '<', '=', '>', '?', /* 8 - ? */
|
|
|
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* @ - G */
|
|
|
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* H - O */
|
|
|
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* P - W */
|
|
|
|
|
'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /* X - _ */
|
|
|
|
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* ` - g */
|
|
|
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* h - o */
|
|
|
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* p - w */
|
|
|
|
|
'x', 'y', 'z', '{', '|', '}', '~', 0, /* x - DEL */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CalmaWriteZ --
|
|
|
|
|
*
|
|
|
|
|
* Write out the entire tree rooted at the supplied CellDef in Calma
|
|
|
|
|
* GDS-II stream format, to the specified file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the cell could be written successfully, FALSE otherwise.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes a file to disk.
|
|
|
|
|
* In the event of an error while writing out the cell,
|
|
|
|
|
* the external integer errno is set to the UNIX error
|
|
|
|
|
* encountered.
|
|
|
|
|
*
|
|
|
|
|
* Algorithm:
|
|
|
|
|
*
|
|
|
|
|
* Calma names can be strings of up to CALMANAMELENGTH characters.
|
|
|
|
|
* Because general names won't map into Calma names, we use the
|
|
|
|
|
* original cell name only if it is legal Calma, and otherwise
|
|
|
|
|
* generate a unique numeric name for the cell.
|
|
|
|
|
*
|
|
|
|
|
* We make a depth-first traversal of the entire design tree, outputting
|
|
|
|
|
* each cell to the Calma file. If a given cell has not been read in
|
|
|
|
|
* when we visit it, we read it in ourselves.
|
|
|
|
|
*
|
|
|
|
|
* No hierarchical design rule checking or bounding box computation
|
|
|
|
|
* occur during this traversal -- both are explicitly avoided.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2024-10-04 12:34:50 +02:00
|
|
|
CalmaWriteZ(
|
|
|
|
|
CellDef *rootDef, /* Pointer to CellDef to be written */
|
|
|
|
|
gzFile f) /* Open compressed output file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
int oldCount = DBWFeedbackCount, problems, nerr;
|
|
|
|
|
bool good;
|
2024-04-29 23:43:37 +02:00
|
|
|
CellDef *err_def;
|
2022-05-10 15:19:39 +02:00
|
|
|
CellUse dummy;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do not attempt to write anything if a CIF/GDS output style
|
|
|
|
|
* has not been specified in the technology file.
|
|
|
|
|
*/
|
|
|
|
|
if (!CIFCurStyle)
|
|
|
|
|
{
|
|
|
|
|
TxError("No CIF/GDS output style set!\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashInit(&calmaLibHash, 32, 0);
|
|
|
|
|
HashInit(&calmaPrefixHash, 32, 0);
|
|
|
|
|
HashInit(&calmaUndefHash, 32, 0);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure that the entire hierarchy rooted at rootDef is
|
|
|
|
|
* read into memory and that timestamp mismatches are resolved
|
|
|
|
|
* (this is needed so that we know that bounding boxes are OK).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
dummy.cu_def = rootDef;
|
2024-04-29 23:43:37 +02:00
|
|
|
err_def = DBCellReadArea(&dummy, &rootDef->cd_bbox, !CalmaAllowUndefined);
|
|
|
|
|
if (err_def != NULL)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
TxError("Failure to read entire subtree of the cell.\n");
|
2024-04-29 23:43:37 +02:00
|
|
|
TxError("Failed on cell %s.\n", err_def->cd_name);
|
2022-05-10 15:19:39 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBFixMismatch();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Go through all cells currently having CellDefs in the
|
|
|
|
|
* def symbol table and mark them with negative numbers
|
|
|
|
|
* to show that they should be output, but haven't yet
|
|
|
|
|
* been.
|
|
|
|
|
*/
|
|
|
|
|
(void) DBCellSrDefs(0, calmaWriteInitFunc, (ClientData) NULL);
|
|
|
|
|
rootDef->cd_client = (ClientData) -1;
|
|
|
|
|
calmaCellNum = -2;
|
|
|
|
|
|
|
|
|
|
/* Output the header, identifying this file */
|
|
|
|
|
calmaOutHeaderZ(rootDef, f);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write all contact cell definitions first
|
|
|
|
|
*/
|
|
|
|
|
if (CalmaContactArrays) calmaWriteContactsZ(f);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We perform a post-order traversal of the tree rooted at 'rootDef',
|
|
|
|
|
* to insure that each child cell is output before it is used. The
|
|
|
|
|
* root cell is output last.
|
|
|
|
|
*/
|
|
|
|
|
calmaProcessDefZ(rootDef, f, CalmaDoLibrary);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check for any cells that were instanced in the output definition
|
|
|
|
|
* (by dumping a GDS file from a read-only view) but were never
|
|
|
|
|
* defined (because the dumped GDS contained undefined references).
|
|
|
|
|
* If these are in the database but were not part of the tree of
|
|
|
|
|
* rootDef, then output them at the end.
|
|
|
|
|
*/
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((he = HashNext(&calmaUndefHash, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
char *refname = (char *)HashGetValue(he);
|
|
|
|
|
if (refname && (refname[0] == '0'))
|
|
|
|
|
{
|
|
|
|
|
CellDef *extraDef;
|
|
|
|
|
|
|
|
|
|
extraDef = DBCellLookDef((char *)he->h_key.h_name);
|
|
|
|
|
if (extraDef != NULL)
|
|
|
|
|
calmaProcessDefZ(extraDef, f, FALSE);
|
|
|
|
|
else
|
|
|
|
|
TxError("Error: Cell %s is not defined in the output file!\n",
|
|
|
|
|
refname + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finish up by outputting the end-of-library marker */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDLIB, CALMA_NODATA, f);
|
|
|
|
|
gzflush(f, Z_SYNC_FLUSH);
|
|
|
|
|
gzerror(f, &nerr);
|
|
|
|
|
good = (nerr == 0) ? TRUE : FALSE;
|
|
|
|
|
|
|
|
|
|
/* See if any problems occurred */
|
2024-10-04 18:21:15 +02:00
|
|
|
if ((problems = (DBWFeedbackCount - oldCount)))
|
2022-05-10 15:19:39 +02:00
|
|
|
TxPrintf("%d problems occurred. See feedback entries.\n", problems);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Destroy all contact cell definitions
|
|
|
|
|
*/
|
|
|
|
|
if (CalmaContactArrays) calmaDelContacts();
|
|
|
|
|
|
|
|
|
|
HashFreeKill(&calmaLibHash);
|
|
|
|
|
HashKill(&calmaPrefixHash);
|
|
|
|
|
HashFreeKill(&calmaUndefHash);
|
|
|
|
|
return (good);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaDumpStructureZ --
|
|
|
|
|
*
|
|
|
|
|
* Parse a structure (cell) from the GDS file. Check the name against the
|
|
|
|
|
* existing database and modify the name in case of a collision. Then dump
|
|
|
|
|
* the entire cell verbatim. The cell gets prefixed with the name "prefix"
|
|
|
|
|
* to prevent collisions with other unknown GDS files that may be dumped
|
|
|
|
|
* verbatim.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaDumpStructureZ(
|
|
|
|
|
CellDef *def,
|
|
|
|
|
gzFile outf,
|
|
|
|
|
HashTable *calmaDefHash,
|
|
|
|
|
char *filename)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
2024-10-04 20:57:15 +02:00
|
|
|
int nbytes = -1, rtype = 0;
|
2022-05-10 15:19:39 +02:00
|
|
|
char *strname = NULL, *newnameptr;
|
|
|
|
|
HashEntry *he, *he2;
|
|
|
|
|
CellDef *edef;
|
|
|
|
|
char *prefix = NULL;
|
|
|
|
|
|
|
|
|
|
/* Make sure this is a structure; if not, let the caller know we're done */
|
|
|
|
|
PEEKRH(nbytes, rtype);
|
|
|
|
|
if (nbytes <= 0) return (FALSE);
|
|
|
|
|
|
|
|
|
|
if (rtype != CALMA_BGNSTR)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRHZ(nbytes, rtype, CALMA_I2, outf);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the structure name */
|
|
|
|
|
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
|
|
|
|
|
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
|
|
|
|
|
TxPrintf("Reading \"%s\".\n", strname);
|
|
|
|
|
|
|
|
|
|
/* Output structure begin */
|
|
|
|
|
calmaOutRHZ(28, CALMA_BGNSTR, CALMA_I2, outf);
|
|
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDateZ(*CalmaDateStamp, outf);
|
|
|
|
|
else
|
|
|
|
|
calmaOutDateZ(def->cd_timestamp, outf);
|
|
|
|
|
calmaOutDateZ(time((time_t *) 0), outf);
|
|
|
|
|
|
|
|
|
|
/* Do a quick check of the calmaUndefHash table to see if this cell */
|
|
|
|
|
/* was previously used in a GDS file that does not define it (a GDS */
|
|
|
|
|
/* addendum library). */
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&calmaUndefHash, strname);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
char *undefname = (char *)HashGetValue(he);
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((he2 = HashNext(&calmaLibHash, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
prefix = (char *)HashGetValue(he2);
|
|
|
|
|
if (!strncmp(prefix, undefname + 1, strlen(prefix)))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (he2 == NULL)
|
|
|
|
|
{
|
|
|
|
|
prefix = (char *)NULL;
|
|
|
|
|
TxError("Error: Unreferenced cell %s prefix is unrecorded!\n",
|
|
|
|
|
undefname);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Remove this entry from the hash table */
|
|
|
|
|
freeMagic(undefname);
|
|
|
|
|
HashRemove(&calmaUndefHash, strname);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find the structure's unique prefix, in case structure calls */
|
|
|
|
|
/* subcells that are not yet defined. */
|
|
|
|
|
|
|
|
|
|
he2 = HashFind(&calmaLibHash, filename);
|
|
|
|
|
if (he2 == NULL)
|
|
|
|
|
TxError("Fatal error: Library %s not recorded!\n", filename);
|
|
|
|
|
else
|
|
|
|
|
prefix = (char *)HashGetValue(he2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prefix structure name with def name, and output new structure name */
|
|
|
|
|
he = HashFind(calmaDefHash, strname);
|
|
|
|
|
if ((newnameptr = (char *)HashGetValue(he)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Structure is defined more than once */
|
|
|
|
|
if (*newnameptr != '0')
|
|
|
|
|
TxError("Structure %s defined redundantly in GDS\n", strname);
|
|
|
|
|
else
|
|
|
|
|
*newnameptr = '1';
|
|
|
|
|
/* To be considered: Should the structure be output more than once? */
|
|
|
|
|
calmaOutStringRecordZ(CALMA_STRNAME, newnameptr + 1, outf);
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(strname, def->cd_name))
|
|
|
|
|
{
|
|
|
|
|
/* This is the top level cell being defined. Its name */
|
|
|
|
|
/* does not get modified. */
|
|
|
|
|
|
|
|
|
|
newnameptr = mallocMagic(strlen(strname) + 2);
|
|
|
|
|
sprintf(newnameptr, "1%s", strname);
|
|
|
|
|
calmaOutStringRecordZ(CALMA_STRNAME, newnameptr + 1, outf);
|
|
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Check if the cellname is in the magic cell database. */
|
|
|
|
|
/* If so, check if that cell is an abstract view and */
|
|
|
|
|
/* calls the same library. If so, the name does not */
|
|
|
|
|
/* get prefixed. Otherwise, the cell is limited to the */
|
|
|
|
|
/* GDS library being read, and so takes the prefix. */
|
|
|
|
|
|
|
|
|
|
/* Modify the cellname by prefixing with "prefix", which is a */
|
|
|
|
|
/* unique identifier for the library. */
|
|
|
|
|
|
|
|
|
|
/* Check if the cell is defined in the database */
|
|
|
|
|
edef = DBCellLookDef(strname);
|
|
|
|
|
if (edef != NULL)
|
|
|
|
|
{
|
|
|
|
|
bool isAbstract, isReadOnly;
|
|
|
|
|
char *chklibname, *filenamesubbed = NULL;
|
|
|
|
|
|
|
|
|
|
/* Is view abstract? */
|
|
|
|
|
DBPropGet(edef, "LEFview", &isAbstract);
|
|
|
|
|
chklibname = (char *)DBPropGet(edef, "GDS_FILE", &isReadOnly);
|
|
|
|
|
|
|
|
|
|
if (isAbstract && isReadOnly)
|
|
|
|
|
{
|
|
|
|
|
filenamesubbed = StrDup(NULL, filename);
|
|
|
|
|
DBPathSubstitute(filename, filenamesubbed, edef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is the library name the same as the filename? */
|
|
|
|
|
if (isAbstract && isReadOnly && !strcmp(filenamesubbed, chklibname))
|
|
|
|
|
{
|
|
|
|
|
/* Same library, so keep the cellname and mark the cell */
|
|
|
|
|
/* as having been written to GDS. */
|
|
|
|
|
|
|
|
|
|
newnameptr = mallocMagic(strlen(strname) + 2);
|
|
|
|
|
sprintf(newnameptr, "1%s", strname);
|
|
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find the unique library prefix and prepend it to the cell name */
|
|
|
|
|
|
|
|
|
|
if (prefix == NULL)
|
|
|
|
|
newnameptr = strname; /* Should never happen */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "1%s%s", prefix, strname);
|
|
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (filenamesubbed) freeMagic(filenamesubbed);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find the unique library prefix and prepend it to the cell name */
|
|
|
|
|
|
|
|
|
|
if (prefix == NULL)
|
|
|
|
|
newnameptr = strname; /* Should never happen */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "1%s%s", prefix, strname);
|
|
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
calmaOutStringRecordZ(CALMA_STRNAME, newnameptr + 1, outf);
|
|
|
|
|
}
|
|
|
|
|
freeMagic(strname);
|
|
|
|
|
|
|
|
|
|
/* Read and output the structure until CALMA_ENDSTR, except */
|
|
|
|
|
/* for handling any AREF or SREF names, which need name */
|
|
|
|
|
/* checks. */
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
int datatype;
|
|
|
|
|
|
|
|
|
|
READI2(nbytes);
|
|
|
|
|
if (gzeof(calmaInputFile))
|
|
|
|
|
{
|
|
|
|
|
/* Unexpected end-of-file */
|
|
|
|
|
gzseek(calmaInputFile, -(CALMAHEADERLENGTH), SEEK_END);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rtype = gzgetc(calmaInputFile);
|
|
|
|
|
datatype = gzgetc(calmaInputFile);
|
|
|
|
|
switch (rtype) {
|
|
|
|
|
case CALMA_BGNSTR:
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
case CALMA_ENDLIB:
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
case CALMA_SNAME:
|
|
|
|
|
UNREADRH(nbytes, rtype);
|
|
|
|
|
if (!calmaReadStringRecord(CALMA_SNAME, &strname))
|
|
|
|
|
goto syntaxerror;
|
|
|
|
|
|
|
|
|
|
he = HashFind(calmaDefHash, strname);
|
|
|
|
|
newnameptr = (char *)HashGetValue(he);
|
|
|
|
|
if (newnameptr != NULL)
|
|
|
|
|
{
|
|
|
|
|
calmaOutStringRecordZ(CALMA_SNAME, newnameptr + 1, outf);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Diagnostic: %s is a forward reference?\n", strname);
|
|
|
|
|
|
|
|
|
|
/* Could be a forward reference, so do a rename in */
|
|
|
|
|
/* the same way used for structure definitions. */
|
|
|
|
|
|
|
|
|
|
newnameptr = (char *)mallocMagic(strlen(strname) +
|
|
|
|
|
strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "0%s%s", prefix, strname);
|
|
|
|
|
|
|
|
|
|
edef = DBCellLookDef(newnameptr + 1);
|
|
|
|
|
if (edef != NULL)
|
|
|
|
|
sprintf(newnameptr, "0%s%s[[0]]", prefix, strname);
|
|
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
calmaOutStringRecordZ(CALMA_SNAME, newnameptr + 1, outf);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
calmaOutRHZ(nbytes, rtype, datatype, outf);
|
|
|
|
|
nbytes -= 4;
|
|
|
|
|
|
|
|
|
|
/* Copy nbytes from input to output */
|
|
|
|
|
while (nbytes-- > 0)
|
|
|
|
|
{
|
|
|
|
|
int byte;
|
|
|
|
|
if ((byte = gzgetc(calmaInputFile)) < 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("End of file with %d bytes remaining to be read.\n",
|
|
|
|
|
nbytes);
|
|
|
|
|
while (nbytes-- > 0)
|
|
|
|
|
gzputc(outf, 0); // zero-pad output
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
gzputc(outf, byte);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
syntaxerror:
|
|
|
|
|
/* Syntax error: skip to CALMA_ENDSTR */
|
|
|
|
|
calmaSkipTo(CALMA_ENDSTR);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaFullDumpZ --
|
|
|
|
|
*
|
|
|
|
|
* Read in a GDS-II stream format library and dump its contents to
|
|
|
|
|
* file "outf" verbatim except for cell references, which are renamed
|
|
|
|
|
* if there is a conflict with a cell def in memory.
|
|
|
|
|
*
|
|
|
|
|
* Because the dump is inside a larger output, the header and trailer
|
|
|
|
|
* are discarded.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaFullDumpZ(
|
|
|
|
|
CellDef *def,
|
|
|
|
|
gzFile fi,
|
|
|
|
|
gzFile outf,
|
|
|
|
|
char *filename)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
int version, rval;
|
|
|
|
|
char *libname = NULL, *testlib, uniqlibname[4];
|
|
|
|
|
char *sptr, *viewopts;
|
|
|
|
|
bool isAbstract;
|
|
|
|
|
HashTable calmaDefHash;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he, *he2;
|
|
|
|
|
|
2024-10-04 12:19:10 +02:00
|
|
|
static const int hdrSkip[] = { CALMA_FORMAT, CALMA_MASK, CALMA_ENDMASKS,
|
2022-05-10 15:19:39 +02:00
|
|
|
CALMA_REFLIBS, CALMA_FONTS, CALMA_ATTRTABLE,
|
2023-11-21 15:44:16 +01:00
|
|
|
CALMA_STYPTABLE, CALMA_GENERATIONS, -1 };
|
2024-10-04 12:19:10 +02:00
|
|
|
static const int skipBeforeLib[] = { CALMA_LIBDIRSIZE, CALMA_SRFNAME,
|
2022-05-10 15:19:39 +02:00
|
|
|
CALMA_LIBSECUR, -1 };
|
|
|
|
|
|
|
|
|
|
HashInit(&calmaDefHash, 32, 0);
|
|
|
|
|
|
|
|
|
|
calmaInputFile = fi;
|
|
|
|
|
cifReadCellDef = def;
|
|
|
|
|
|
|
|
|
|
/* Read and ignore the GDS-II header */
|
|
|
|
|
|
|
|
|
|
if (!calmaReadI2Record(CALMA_HEADER, &version)) goto done;
|
|
|
|
|
if (!calmaSkipExact(CALMA_BGNLIB)) goto done;
|
|
|
|
|
calmaSkipSet(skipBeforeLib);
|
|
|
|
|
if (!calmaReadStringRecord(CALMA_LIBNAME, &libname)) goto done;
|
|
|
|
|
|
2023-11-21 15:44:16 +01:00
|
|
|
// CALMA_UNITS needs to be parsed to determine if units in the
|
|
|
|
|
// input file are compatible with units being used in the output
|
2022-05-10 15:19:39 +02:00
|
|
|
// file.
|
2023-11-21 15:44:16 +01:00
|
|
|
if (calmaParseUnits() == FALSE)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Library %s has incompatible database units!\n", libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
// Record the GDS library so it will not be processed again.
|
|
|
|
|
he = HashFind(&calmaLibHash, filename);
|
|
|
|
|
if ((char *)HashGetValue(he) != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Library %s has already been processed\n", libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If property LEFview is defined as "no_prefix" instead of "TRUE",
|
|
|
|
|
* then do not create a unique prefix for subcells. This is generally
|
|
|
|
|
* ill-advised, but can be needed if a foundry runs specific DRC checks
|
|
|
|
|
* on specific cell names, in which case adding a prefix can cause DRC
|
|
|
|
|
* errors to appear. It is then incumbent on the user to ensure that
|
|
|
|
|
* names in the GDS file do not shadow any names in the database.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
viewopts = (char *)DBPropGet(def, "LEFview", &isAbstract);
|
|
|
|
|
if ((!isAbstract) || (strcasecmp(viewopts, "no_prefix")))
|
|
|
|
|
{
|
|
|
|
|
/* Generate a SHORT name for this cell (else it is easy to run into the
|
|
|
|
|
* GDS 32-character cellname limit). Save it in the hash record. The
|
|
|
|
|
* chance of generating the same prefix for a library that has items
|
|
|
|
|
* with conflicting names is vanishingly small, but to be pedantic, store
|
|
|
|
|
* the prefix in a hash table and check to make sure that uniqueness is
|
|
|
|
|
* ensured. NOTE: The first character of a SPICE name cannot be a
|
|
|
|
|
* number. Therefore the first character is alphabetical, and the second
|
|
|
|
|
* is alphanumeric. There are only 936 possible combinations, but this
|
|
|
|
|
* is only meant to distinguish cells in large IP blocks of unknown
|
|
|
|
|
* origin, of which only a limited number would be expected. Beware
|
|
|
|
|
* the implications for LVS, as the prefixed names from layout would
|
|
|
|
|
* need to be compared to un-prefixed names from another netlist.
|
|
|
|
|
*/
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
rval = random() % 26;
|
|
|
|
|
rval = 'A' + rval;
|
|
|
|
|
uniqlibname[0] = (char)(rval & 127);
|
|
|
|
|
rval = random() % 36;
|
|
|
|
|
rval = (rval < 26) ? ('A' + rval) : ('0' + rval - 26);
|
|
|
|
|
uniqlibname[1] = (char)(rval & 127);
|
|
|
|
|
uniqlibname[2] = '_';
|
|
|
|
|
uniqlibname[3] = '\0';
|
|
|
|
|
he2 = HashLookOnly(&calmaPrefixHash, uniqlibname);
|
|
|
|
|
if (he2 == NULL)
|
|
|
|
|
{
|
|
|
|
|
he2 = HashFind(&calmaPrefixHash, uniqlibname);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
HashSetValue(he, StrDup(NULL, uniqlibname));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
HashSetValue(he, StrDup(NULL, ""));
|
|
|
|
|
|
|
|
|
|
while (calmaDumpStructureZ(def, outf, &calmaDefHash, filename))
|
|
|
|
|
if (SigInterruptPending)
|
|
|
|
|
goto done;
|
|
|
|
|
calmaSkipExact(CALMA_ENDLIB);
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
|
|
|
|
|
/* Check that all references were resolved. If not, then it is
|
|
|
|
|
* probably because a library was an "addendum"-type library
|
|
|
|
|
* referencing things in other libraries. Move those cell
|
|
|
|
|
* references to the calmaUndefHash before killing calmaDefHash.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((he = HashNext(&calmaDefHash, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
char *refname = (char *)HashGetValue(he);
|
|
|
|
|
if (refname[0] == '0')
|
|
|
|
|
{
|
|
|
|
|
he2 = HashFind(&calmaUndefHash, (char *)he->h_key.h_name);
|
|
|
|
|
HashSetValue(he2, StrDup(NULL, refname));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashFreeKill(&calmaDefHash);
|
|
|
|
|
if (libname != NULL) freeMagic(libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaProcessUseZ --
|
|
|
|
|
* calmaProcessDefZ --
|
|
|
|
|
*
|
|
|
|
|
* Main loop of Calma generation. Performs a post-order, depth-first
|
|
|
|
|
* traversal of the tree rooted at 'def'. Only cells that have not
|
|
|
|
|
* already been output are processed.
|
|
|
|
|
*
|
|
|
|
|
* The procedure calmaProcessDef() is called initially; calmaProcessUse()
|
|
|
|
|
* is called internally by DBCellEnum().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Causes Calma GDS-II stream-format to be output.
|
|
|
|
|
* Returns when the stack is empty.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaProcessUseZ(
|
|
|
|
|
CellUse *use, /* Process use->cu_def */
|
|
|
|
|
gzFile outf) /* Stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
return (calmaProcessDefZ(use->cu_def, outf, FALSE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaProcessDefZ(
|
|
|
|
|
CellDef *def, /* Output this def's children, then the def itself */
|
|
|
|
|
gzFile outf, /* Stream file */
|
|
|
|
|
bool do_library) /* If TRUE, output only children of def, but not def */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
char *filename;
|
2022-06-09 17:44:58 +02:00
|
|
|
int polyidx;
|
2023-03-07 17:16:49 +01:00
|
|
|
bool isReadOnly, oldStyle, hasContent, isAbstract, hasGDSEnd, needHier;
|
|
|
|
|
bool hierWrite, arrayWrite;
|
2022-05-10 15:19:39 +02:00
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
/* Skip if already output */
|
2024-10-21 10:13:23 +02:00
|
|
|
if ((int) CD2INT(def->cd_client) > 0)
|
2022-05-10 15:19:39 +02:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
/* Assign it a (negative) number if it doesn't have one yet */
|
2024-10-21 10:13:23 +02:00
|
|
|
if ((int) CD2INT(def->cd_client) == 0)
|
|
|
|
|
def->cd_client = INT2CD(calmaCellNum--);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
/* Mark this cell */
|
2024-10-21 10:13:23 +02:00
|
|
|
def->cd_client = INT2CD(- (int) CD2INT(def->cd_client));
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
/* Read the cell in if it is not already available. */
|
|
|
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
2023-04-18 17:01:58 +02:00
|
|
|
if (!DBCellRead(def, TRUE, TRUE, NULL))
|
2022-05-10 15:19:39 +02:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Flag an error if attempting to write the default (UNNAMED) cell
|
|
|
|
|
* into GDS. This is not strictly an error but is almost certainly
|
|
|
|
|
* not what the user intended.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!strcmp(def->cd_name, UNNAMED))
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Cell has the default name \"%s\"!\n", UNNAMED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check if this is a read-only file that is supposed to be copied
|
|
|
|
|
* verbatim from input to output. If so, do the direct copy. If
|
|
|
|
|
* not, or if there is any problem obtaining the original cell
|
|
|
|
|
* definition, resort to writing out magic's version of the def,
|
|
|
|
|
* and print a warning message.
|
|
|
|
|
*
|
|
|
|
|
* Treat the lack of a GDS_START property as an indication
|
|
|
|
|
* that we should treat this cell like a reference-only
|
|
|
|
|
* cell. That is, the instance will be called but no
|
|
|
|
|
* definition will appear in the output.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
DBPropGet(def, "LEFview", &isAbstract);
|
|
|
|
|
DBPropGet(def, "GDS_START", &hasContent);
|
|
|
|
|
DBPropGet(def, "GDS_END", &hasGDSEnd);
|
2023-03-07 17:16:49 +01:00
|
|
|
DBPropGet(def, "CIFhier", &needHier);
|
2022-05-10 15:19:39 +02:00
|
|
|
filename = (char *)DBPropGet(def, "GDS_FILE", &isReadOnly);
|
|
|
|
|
|
|
|
|
|
/* When used with "calma addendum true", don't output the read-only */
|
|
|
|
|
/* cells. This makes the library incomplete and dependent on the */
|
|
|
|
|
/* vendor libraries, so use with caution. */
|
|
|
|
|
|
|
|
|
|
if (isReadOnly && hasContent && CalmaAddendum) return (0);
|
|
|
|
|
|
|
|
|
|
if (isAbstract && !isReadOnly)
|
2022-11-02 14:40:20 +01:00
|
|
|
{
|
|
|
|
|
if (CalmaAllowAbstract)
|
|
|
|
|
TxError("Warning: Writing abstract view of \"%s\" to GDS.\n",
|
|
|
|
|
def->cd_name);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Cell \"%s\" is an abstract view; cannot write GDS.\n",
|
2022-05-10 15:19:39 +02:00
|
|
|
def->cd_name);
|
2022-11-02 14:40:20 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
/*
|
|
|
|
|
* Output the definitions for any of our descendants that have
|
|
|
|
|
* not already been output. Numbers are assigned to the subcells
|
|
|
|
|
* as they are output. If the cell will get a "full dump" (by
|
|
|
|
|
* having GDS_START but no GDS_END), then do not output any subcells,
|
|
|
|
|
* as they are expected to be in the referenced GDS file.
|
|
|
|
|
*/
|
|
|
|
|
if (!hasContent || hasGDSEnd)
|
2023-03-07 17:16:49 +01:00
|
|
|
{
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
hierWrite = CIFHierWriteDisable;
|
|
|
|
|
arrayWrite = CIFArrayWriteDisable;
|
|
|
|
|
|
|
|
|
|
CIFHierWriteDisable = FALSE;
|
|
|
|
|
CIFArrayWriteDisable = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = DBCellEnum(def, calmaProcessUseZ, (ClientData) outf);
|
|
|
|
|
|
|
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
CIFHierWriteDisable = hierWrite;
|
|
|
|
|
CIFArrayWriteDisable = arrayWrite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result != 0)
|
2022-05-10 15:19:39 +02:00
|
|
|
return 1;
|
2023-03-07 17:16:49 +01:00
|
|
|
}
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
/* Give some feedback to the user */
|
|
|
|
|
TxPrintf(" Generating output for cell %s\n", def->cd_name);
|
|
|
|
|
|
|
|
|
|
if (isReadOnly && hasContent)
|
|
|
|
|
{
|
|
|
|
|
char *buffer, *offptr, *retfilename;
|
|
|
|
|
size_t defsize, numbytes;
|
|
|
|
|
z_off_t cellstart, cellend, structstart;
|
|
|
|
|
dlong cval;
|
|
|
|
|
int namelen;
|
|
|
|
|
gzFile fi;
|
|
|
|
|
|
|
|
|
|
/* Use PaOpen() so the paths searched are the same as were */
|
|
|
|
|
/* searched to find the .mag file that indicated this GDS file. */
|
|
|
|
|
|
|
|
|
|
fi = PaZOpen(filename, "r", "", Path, CellLibPath, &retfilename);
|
2025-02-12 22:28:43 +01:00
|
|
|
|
|
|
|
|
/* Check if file may have been compressed */
|
|
|
|
|
if (fi == NULL)
|
|
|
|
|
fi = PaZOpen(filename, "r", ".gz", Path, CellLibPath, &retfilename);
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
if (fi == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* This is a rare error, but if the subcell is inside */
|
|
|
|
|
/* another vendor GDS, it would not normally be output. */
|
|
|
|
|
|
|
|
|
|
DBPropGet((def->cd_parents->cu_parent == NULL) ? def :
|
|
|
|
|
def->cd_parents->cu_parent, "GDS_FILE", &isReadOnly);
|
|
|
|
|
if (isReadOnly)
|
|
|
|
|
{
|
|
|
|
|
def->cd_flags |= CDVENDORGDS;
|
|
|
|
|
return 0; /* Ignore without raising an error */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TxError("Calma output error: Can't find GDS file \"%s\" "
|
|
|
|
|
"for vendor cell \"%s\". It will not be output.\n",
|
|
|
|
|
filename, def->cd_name);
|
|
|
|
|
|
|
|
|
|
if (CalmaAllowUndefined)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else if (isAbstract || (!hasGDSEnd))
|
|
|
|
|
{
|
|
|
|
|
/* This is the trickiest part. If the cell view is abstract then */
|
|
|
|
|
/* the cell view has no hierarchy, and there is no way to descend */
|
|
|
|
|
/* into the cell hierarchy and discover and write out all the */
|
|
|
|
|
/* dependencies. The dependencies are in the file but not in the */
|
|
|
|
|
/* range GDS_START to GDS_END. Furthermore, the dependencies have */
|
|
|
|
|
/* not been loaded so naming conflicts may exist. So the file must */
|
|
|
|
|
/* be read end-to-end and parsed carefully. */
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&calmaLibHash, retfilename);
|
|
|
|
|
if (he == NULL)
|
|
|
|
|
calmaFullDumpZ(def, fi, outf, retfilename);
|
|
|
|
|
|
|
|
|
|
gzclose(fi);
|
|
|
|
|
def->cd_flags |= CDVENDORGDS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
offptr = (char *)DBPropGet(def, "GDS_END", NULL);
|
|
|
|
|
sscanf(offptr, "%"DLONG_PREFIX"d", &cval);
|
|
|
|
|
cellend = (z_off_t)cval;
|
|
|
|
|
offptr = (char *)DBPropGet(def, "GDS_BEGIN", &oldStyle);
|
|
|
|
|
if (!oldStyle)
|
|
|
|
|
{
|
|
|
|
|
offptr = (char *)DBPropGet(def, "GDS_START", NULL);
|
|
|
|
|
|
|
|
|
|
/* Write our own header and string name, to ensure */
|
|
|
|
|
/* that the magic cell name and GDS name match. */
|
|
|
|
|
|
|
|
|
|
/* Output structure header */
|
|
|
|
|
calmaOutRHZ(28, CALMA_BGNSTR, CALMA_I2, outf);
|
|
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDateZ(*CalmaDateStamp, outf);
|
|
|
|
|
else
|
|
|
|
|
calmaOutDateZ(def->cd_timestamp, outf);
|
|
|
|
|
calmaOutDateZ(time((time_t *) 0), outf);
|
|
|
|
|
|
|
|
|
|
/* Name structure the same as the magic cellname */
|
|
|
|
|
calmaOutStructNameZ(CALMA_STRNAME, def, outf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sscanf(offptr, "%"DLONG_PREFIX"d", &cval);
|
|
|
|
|
cellstart = (z_off_t)cval;
|
|
|
|
|
|
|
|
|
|
/* GDS_START has been defined as the start of data after the cell */
|
|
|
|
|
/* structure name. However, a sanity check is wise, so move back */
|
|
|
|
|
/* that far and make sure the structure name is there. */
|
|
|
|
|
structstart = cellstart - (z_off_t)(strlen(def->cd_name));
|
|
|
|
|
if (strlen(def->cd_name) & 0x1) structstart--;
|
|
|
|
|
structstart -= 2;
|
|
|
|
|
|
|
|
|
|
gzseek(fi, structstart, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
/* Read the structure name and check against the CellDef name */
|
|
|
|
|
defsize = (size_t)(cellstart - structstart);
|
|
|
|
|
buffer = (char *)mallocMagic(defsize + 1);
|
|
|
|
|
numbytes = gzread(fi, buffer, sizeof(char) * (size_t)defsize);
|
|
|
|
|
if (numbytes == defsize)
|
|
|
|
|
{
|
|
|
|
|
buffer[defsize] = '\0';
|
|
|
|
|
if (buffer[0] != 0x06 || buffer[1] != 0x06)
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Structure name not found"
|
|
|
|
|
" at GDS file position %"DLONG_PREFIX"d\n", cellstart);
|
|
|
|
|
TxError("Calma output error: Can't write cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(&buffer[2], def->cd_name))
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output warning: Structure definition has name"
|
|
|
|
|
" %s but cell definition has name %s.\n",
|
|
|
|
|
&buffer[2], def->cd_name);
|
|
|
|
|
TxError("The structure definition will be given the cell name.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Can't read cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
2025-02-13 09:26:18 +01:00
|
|
|
freeMagic(buffer);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
if (cellend < cellstart) /* Sanity check */
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Bad vendor GDS file reference!\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else if (isReadOnly)
|
|
|
|
|
{
|
|
|
|
|
/* Important note: mallocMagic() is limited to size integer. */
|
|
|
|
|
/* This will fail on a structure larger than ~2GB. */
|
|
|
|
|
|
|
|
|
|
defsize = (size_t)(cellend - cellstart);
|
|
|
|
|
buffer = (char *)mallocMagic(defsize);
|
|
|
|
|
|
|
|
|
|
numbytes = gzread(fi, buffer, sizeof(char) * (size_t)defsize);
|
|
|
|
|
|
|
|
|
|
if (numbytes == defsize)
|
|
|
|
|
{
|
|
|
|
|
/* Sanity check: buffer must end with a structure */
|
|
|
|
|
/* definition end (record 0x07). */
|
|
|
|
|
|
|
|
|
|
if (buffer[defsize - 4] != 0x00 ||
|
|
|
|
|
buffer[defsize - 3] != 0x04 ||
|
|
|
|
|
buffer[defsize - 2] != 0x07 ||
|
|
|
|
|
buffer[defsize - 1] != 0x00)
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Structure end definition not found"
|
|
|
|
|
" at GDS file position %"DLONG_PREFIX"d\n", cellend);
|
|
|
|
|
TxError("Calma output error: Can't write cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
numbytes = gzwrite(outf, buffer, sizeof(char) * (size_t)defsize);
|
|
|
|
|
if (numbytes <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Can't write cell from "
|
|
|
|
|
"vendor GDS. Using magic's internal "
|
|
|
|
|
"definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Can't read cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
|
|
|
|
|
|
|
|
|
/* Additional information as to why data did not match */
|
|
|
|
|
TxError("Size of data requested: %"DLONG_PREFIX"d", defsize);
|
|
|
|
|
TxError("Length of data read: %"DLONG_PREFIX"d", numbytes);
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
freeMagic(buffer);
|
|
|
|
|
}
|
|
|
|
|
gzclose(fi);
|
|
|
|
|
|
|
|
|
|
/* Mark the definition as vendor GDS so that magic doesn't */
|
|
|
|
|
/* try to generate subcell interaction or array interaction */
|
|
|
|
|
/* paint for it. */
|
|
|
|
|
|
|
|
|
|
def->cd_flags |= CDVENDORGDS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 17:44:58 +02:00
|
|
|
/* Quick check on "polygonXXXXX" cells---these are generated by the */
|
|
|
|
|
/* "gds polygon subcell" option, and if the parent cell is a vendor */
|
|
|
|
|
/* GDS file, then these cells do not actually exist in the layout */
|
|
|
|
|
/* and should not be output. */
|
|
|
|
|
|
|
|
|
|
if (isReadOnly == FALSE)
|
|
|
|
|
if (!strncmp(def->cd_name, "polygon", 7))
|
|
|
|
|
if (sscanf(def->cd_name + 7, "%d", &polyidx) == 1)
|
|
|
|
|
if (def->cd_parents->cu_parent != NULL)
|
|
|
|
|
DBPropGet(def->cd_parents->cu_parent, "GDS_FILE", &isReadOnly);
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
/* Output this cell definition from the Magic database */
|
|
|
|
|
if (!isReadOnly)
|
|
|
|
|
if (!do_library)
|
2023-03-07 17:16:49 +01:00
|
|
|
{
|
|
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
hierWrite = CIFHierWriteDisable;
|
|
|
|
|
arrayWrite = CIFArrayWriteDisable;
|
|
|
|
|
|
|
|
|
|
CIFHierWriteDisable = FALSE;
|
|
|
|
|
CIFArrayWriteDisable = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
calmaOutFuncZ(def, outf, &TiPlaneRect);
|
|
|
|
|
|
2023-03-07 17:16:49 +01:00
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
CIFHierWriteDisable = hierWrite;
|
|
|
|
|
CIFArrayWriteDisable = arrayWrite;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Write out the definition for a single cell as a GDS-II stream format
|
|
|
|
|
* structure. We try to preserve the original cell's name if it is legal
|
|
|
|
|
* in GDS-II; otherwise, we generate a unique name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Appends to the open Calma output file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutFuncZ(
|
|
|
|
|
CellDef *def, /* Pointer to cell def to be written */
|
|
|
|
|
gzFile f, /* Open output file */
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *cliprect)/* Area to clip to (used for contact cells),
|
2022-05-10 15:19:39 +02:00
|
|
|
* in CIF/GDS coordinates.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Label *lab;
|
|
|
|
|
CIFLayer *layer;
|
|
|
|
|
Rect bigArea;
|
|
|
|
|
int type;
|
|
|
|
|
int dbunits;
|
|
|
|
|
calmaOutputStructZ cos;
|
2022-11-10 20:08:58 +01:00
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
2024-10-04 12:32:22 +02:00
|
|
|
extern int compport(const void *one, const void *two); /* Forward declaration */
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
cos.f = f;
|
|
|
|
|
cos.area = (cliprect == &TiPlaneRect) ? NULL : cliprect;
|
|
|
|
|
cos.type = -1;
|
|
|
|
|
|
|
|
|
|
/* Output structure begin */
|
|
|
|
|
calmaOutRHZ(28, CALMA_BGNSTR, CALMA_I2, f);
|
|
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDateZ(*CalmaDateStamp, f);
|
|
|
|
|
else
|
|
|
|
|
calmaOutDateZ(def->cd_timestamp, f);
|
|
|
|
|
calmaOutDateZ(time((time_t *) 0), f);
|
|
|
|
|
|
|
|
|
|
/* Output structure name */
|
|
|
|
|
calmaOutStructNameZ(CALMA_STRNAME, def, f);
|
|
|
|
|
|
|
|
|
|
/* Since Calma database units are nanometers, multiply all units by 10,
|
|
|
|
|
* modified by the scale multiplier.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
dbunits = (CIFCurStyle->cs_flags & CWF_ANGSTROMS) ? 100 : 10;
|
|
|
|
|
if ((dbunits % CIFCurStyle->cs_expander) == 0)
|
|
|
|
|
{
|
|
|
|
|
calmaWriteScale = CIFCurStyle->cs_scaleFactor * dbunits
|
|
|
|
|
/ CIFCurStyle->cs_expander;
|
|
|
|
|
calmaPaintScale = dbunits / CIFCurStyle->cs_expander;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Output scale units are %2.1f nanometers.\n",
|
|
|
|
|
(float)dbunits / (float)CIFCurStyle->cs_expander);
|
|
|
|
|
TxError("Magic Calma output will be scaled incorrectly!\n");
|
|
|
|
|
if ((dbunits == 10) && ((100 % CIFCurStyle->cs_expander) == 0))
|
|
|
|
|
{
|
|
|
|
|
TxError("Please add \"units angstroms\" to the cifoutput section"
|
|
|
|
|
" of the techfile.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Magic GDS output is limited to a minimum dimension of"
|
|
|
|
|
" 1 angstrom.\n");
|
|
|
|
|
}
|
|
|
|
|
/* Set expander to 10 so output scales are not zero. */
|
|
|
|
|
calmaWriteScale = CIFCurStyle->cs_scaleFactor;
|
|
|
|
|
calmaPaintScale = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Output the calls that the child makes to its children. For
|
|
|
|
|
* arrays we output a single call, unlike CIF, since Calma
|
|
|
|
|
* supports the notion of arrays.
|
|
|
|
|
*/
|
|
|
|
|
(void) DBCellEnum(def, calmaWriteUseFuncZ, (ClientData) f);
|
|
|
|
|
|
|
|
|
|
/* Output all the tiles associated with this cell; skip temporary layers */
|
|
|
|
|
GEO_EXPAND(&def->cd_bbox, CIFCurStyle->cs_radius, &bigArea);
|
2022-11-10 20:08:58 +01:00
|
|
|
|
|
|
|
|
/* Include any fixed bounding box as part of the area to process, */
|
|
|
|
|
/* in case the fixed bounding box is larger than the geometry. */
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "FIXED_BBOX", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
|
|
|
|
Rect bbox;
|
|
|
|
|
|
|
|
|
|
if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot,
|
|
|
|
|
&bbox.r_xtop, &bbox.r_ytop) == 4)
|
|
|
|
|
GeoInclude(&bbox, &bigArea);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
CIFErrorDef = def;
|
|
|
|
|
CIFGen(def, def, &bigArea, CIFPlanes, &DBAllTypeBits, TRUE, TRUE, FALSE,
|
|
|
|
|
(ClientData)f);
|
2023-03-07 17:16:49 +01:00
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
if (!CIFHierWriteDisable)
|
2023-03-07 17:16:49 +01:00
|
|
|
CIFGenSubcells(def, &bigArea, CIFPlanes);
|
2022-05-10 15:19:39 +02:00
|
|
|
if (!CIFArrayWriteDisable)
|
2023-03-07 17:16:49 +01:00
|
|
|
CIFGenArrays(def, &bigArea, CIFPlanes);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
for (type = 0; type < CIFCurStyle->cs_nLayers; type++)
|
|
|
|
|
{
|
|
|
|
|
layer = CIFCurStyle->cs_layers[type];
|
|
|
|
|
if (layer->cl_flags & CIF_TEMP) continue;
|
|
|
|
|
if (!CalmaIsValidLayer(layer->cl_calmanum)) continue;
|
|
|
|
|
cos.type = type;
|
|
|
|
|
calmaPaintLayerNumber = layer->cl_calmanum;
|
|
|
|
|
calmaPaintLayerType = layer->cl_calmatype;
|
|
|
|
|
|
|
|
|
|
if (layer->cl_flags & CIF_LABEL)
|
|
|
|
|
DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
|
|
|
|
|
cliprect, &CIFSolidBits, calmaPaintLabelFuncZ,
|
|
|
|
|
(ClientData) &cos);
|
|
|
|
|
else
|
|
|
|
|
DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
|
|
|
|
|
cliprect, &CIFSolidBits, (CalmaMergeTiles) ?
|
|
|
|
|
calmaMergePaintFuncZ : calmaWritePaintFuncZ,
|
|
|
|
|
(ClientData) &cos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output labels. Do this in two passes, first for non-port labels */
|
|
|
|
|
/* while finding the highest-numbered port. Then output the port */
|
|
|
|
|
/* labels (if any) in the order of the port index. */
|
|
|
|
|
|
|
|
|
|
if (CalmaDoLabels)
|
|
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
int i, ltype, numports = 0;
|
|
|
|
|
LabelList *ll = NULL, *newll;
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
for (lab = def->cd_labels; lab; lab = lab->lab_next)
|
|
|
|
|
{
|
2022-12-21 23:49:43 +01:00
|
|
|
if ((lab->lab_flags & PORT_DIR_MASK) == 0)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
2022-12-21 23:49:43 +01:00
|
|
|
ltype = CIFCurStyle->cs_labelLayer[lab->lab_type];
|
|
|
|
|
type = ltype;
|
|
|
|
|
calmaWriteLabelFuncZ(lab, ltype, type, f);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
newll = (LabelList *)mallocMagic(sizeof(LabelList));
|
|
|
|
|
newll->ll_label = lab;
|
|
|
|
|
newll->ll_attr = (unsigned int)lab->lab_port;
|
|
|
|
|
newll->ll_next = ll;
|
|
|
|
|
ll = newll;
|
|
|
|
|
numports++;
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-22 03:04:30 +01:00
|
|
|
if (newll != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Turn linked list into an array, then run qsort on it */
|
|
|
|
|
/* to sort by port number. */
|
|
|
|
|
|
|
|
|
|
PortLabel *pllist = (PortLabel *)mallocMagic(numports * sizeof(PortLabel));
|
|
|
|
|
i = 0;
|
|
|
|
|
while (ll != NULL)
|
|
|
|
|
{
|
|
|
|
|
pllist[i].pl_label = ll->ll_label;
|
|
|
|
|
pllist[i].pl_port = (unsigned int)ll->ll_attr;
|
|
|
|
|
freeMagic(ll);
|
|
|
|
|
ll = ll->ll_next;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qsort(pllist, numports, sizeof(PortLabel), compport);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < numports; i++)
|
|
|
|
|
{
|
|
|
|
|
lab = pllist[i].pl_label;
|
|
|
|
|
ltype = CIFCurStyle->cs_portText[lab->lab_type];
|
|
|
|
|
type = CIFCurStyle->cs_portLayer[lab->lab_type];
|
|
|
|
|
if (type >= 0)
|
|
|
|
|
calmaWriteLabelFuncZ(lab, ltype, type, f);
|
|
|
|
|
}
|
|
|
|
|
freeMagic(pllist);
|
|
|
|
|
}
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End of structure */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDSTR, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteUseFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Filter function, called by DBCellEnum on behalf of calmaOutFunc above,
|
|
|
|
|
* to write out each CellUse called by the CellDef being output. If the
|
|
|
|
|
* CellUse is an array, we output it as a single array instead of as
|
|
|
|
|
* individual uses like CIF.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Appends to the open Calma output file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWriteUseFuncZ(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
gzFile f)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* r90, r180, and r270 are Calma 8-byte real representations
|
|
|
|
|
* of the angles 90, 180, and 270 degrees. Because there are
|
|
|
|
|
* only 4 possible values, it is faster to have them pre-computed
|
|
|
|
|
* than to format with calmaOutR8Z().
|
|
|
|
|
*/
|
2024-10-04 12:19:10 +02:00
|
|
|
static const unsigned char r90[] = { 0x42, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
static const unsigned char r180[] = { 0x42, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
static const unsigned char r270[] = { 0x43, 0x10, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
const unsigned char *whichangle;
|
2022-05-10 15:19:39 +02:00
|
|
|
int x, y, topx, topy, rows, cols, xxlate, yxlate, hdrsize;
|
|
|
|
|
int rectype, stransflags;
|
|
|
|
|
Transform *t;
|
|
|
|
|
bool isArray = FALSE;
|
|
|
|
|
Point p, p2;
|
|
|
|
|
|
|
|
|
|
topx = use->cu_xhi - use->cu_xlo;
|
|
|
|
|
if (topx < 0) topx = -topx;
|
|
|
|
|
topy = use->cu_yhi - use->cu_ylo;
|
|
|
|
|
if (topy < 0) topy = -topy;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following translates from the abcdef transforms that
|
|
|
|
|
* we use internally to the rotation and mirroring specification
|
|
|
|
|
* used in Calma stream files. It only works because orientations
|
|
|
|
|
* are orthogonal in magic, and no scaling is allowed in cell use
|
|
|
|
|
* transforms. Thus the elements a, b, d, and e always have one
|
|
|
|
|
* of the following forms:
|
|
|
|
|
*
|
|
|
|
|
* a d
|
|
|
|
|
* b e
|
|
|
|
|
*
|
|
|
|
|
* (counterclockwise rotations of 0, 90, 180, 270 degrees)
|
|
|
|
|
*
|
|
|
|
|
* 1 0 0 1 -1 0 0 -1
|
|
|
|
|
* 0 1 -1 0 0 -1 1 0
|
|
|
|
|
*
|
|
|
|
|
* (mirrored across the x-axis before counterclockwise rotation
|
|
|
|
|
* by 0, 90, 180, 270 degrees):
|
|
|
|
|
*
|
|
|
|
|
* 1 0 0 1 -1 0 0 -1
|
|
|
|
|
* 0 -1 1 0 0 1 -1 0
|
|
|
|
|
*
|
|
|
|
|
* Note that mirroring must be done if either a != e, or
|
|
|
|
|
* a == 0 and b == d.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
t = &use->cu_transform;
|
|
|
|
|
stransflags = 0;
|
|
|
|
|
whichangle = (t->t_a == -1) ? r180 : (unsigned char *) NULL;
|
|
|
|
|
if (t->t_a != t->t_e || (t->t_a == 0 && t->t_b == t->t_d))
|
|
|
|
|
{
|
|
|
|
|
stransflags |= CALMA_STRANS_UPSIDEDOWN;
|
|
|
|
|
if (t->t_a == 0)
|
|
|
|
|
{
|
|
|
|
|
if (t->t_b == 1) whichangle = r90;
|
|
|
|
|
else whichangle = r270;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (t->t_a == 0)
|
|
|
|
|
{
|
|
|
|
|
if (t->t_b == -1) whichangle = r90;
|
|
|
|
|
else whichangle = r270;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CalmaFlattenArrays)
|
|
|
|
|
{
|
|
|
|
|
for (x = 0; x <= topx; x++)
|
|
|
|
|
{
|
|
|
|
|
for (y = 0; y <= topy; y++)
|
|
|
|
|
{
|
|
|
|
|
/* Structure reference */
|
|
|
|
|
calmaOutRHZ(4, CALMA_SREF, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructNameZ(CALMA_SNAME, use->cu_def, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRHZ(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2Z(stransflags, f);
|
|
|
|
|
|
|
|
|
|
/* Rotation if there is one */
|
|
|
|
|
if (whichangle)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRHZ(12, CALMA_ANGLE, CALMA_R8, f);
|
2025-01-06 17:12:11 +01:00
|
|
|
calmaOut8Z((char *)whichangle, f);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Translation */
|
|
|
|
|
xxlate = t->t_c + t->t_a*(use->cu_xsep)*x
|
|
|
|
|
+ t->t_b*(use->cu_ysep)*y;
|
|
|
|
|
yxlate = t->t_f + t->t_d*(use->cu_xsep)*x
|
|
|
|
|
+ t->t_e*(use->cu_ysep)*y;
|
|
|
|
|
xxlate *= calmaWriteScale;
|
|
|
|
|
yxlate *= calmaWriteScale;
|
|
|
|
|
calmaOutRHZ(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(xxlate, f);
|
|
|
|
|
calmaOutI4Z(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Is it an array? */
|
|
|
|
|
isArray = (topx > 0 || topy > 0);
|
|
|
|
|
rectype = isArray ? CALMA_AREF : CALMA_SREF;
|
|
|
|
|
|
|
|
|
|
/* Structure reference */
|
|
|
|
|
calmaOutRHZ(4, rectype, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructNameZ(CALMA_SNAME, use->cu_def, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRHZ(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2Z(stransflags, f);
|
|
|
|
|
|
|
|
|
|
/* Rotation if there is one */
|
|
|
|
|
if (whichangle)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRHZ(12, CALMA_ANGLE, CALMA_R8, f);
|
2025-01-06 17:12:11 +01:00
|
|
|
calmaOut8Z((char *)whichangle, f);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If array, number of columns and rows in the array */
|
|
|
|
|
if (isArray)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRHZ(8, CALMA_COLROW, CALMA_I2, f);
|
|
|
|
|
cols = topx + 1;
|
|
|
|
|
rows = topy + 1;
|
|
|
|
|
calmaOutI2Z(cols, f);
|
|
|
|
|
calmaOutI2Z(rows, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Translation */
|
|
|
|
|
xxlate = t->t_c * calmaWriteScale;
|
|
|
|
|
yxlate = t->t_f * calmaWriteScale;
|
|
|
|
|
hdrsize = isArray ? 28 : 12;
|
|
|
|
|
calmaOutRHZ(hdrsize, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(xxlate, f);
|
|
|
|
|
calmaOutI4Z(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* Array sizes if an array */
|
|
|
|
|
if (isArray)
|
|
|
|
|
{
|
|
|
|
|
/* Column reference point */
|
|
|
|
|
p.p_x = use->cu_xsep * cols;
|
|
|
|
|
p.p_y = 0;
|
|
|
|
|
GeoTransPoint(t, &p, &p2);
|
|
|
|
|
p2.p_x *= calmaWriteScale;
|
|
|
|
|
p2.p_y *= calmaWriteScale;
|
|
|
|
|
calmaOutI4Z(p2.p_x, f);
|
|
|
|
|
calmaOutI4Z(p2.p_y, f);
|
|
|
|
|
|
|
|
|
|
/* Row reference point */
|
|
|
|
|
p.p_x = 0;
|
|
|
|
|
p.p_y = use->cu_ysep * rows;
|
|
|
|
|
GeoTransPoint(t, &p, &p2);
|
|
|
|
|
p2.p_x *= calmaWriteScale;
|
|
|
|
|
p2.p_y *= calmaWriteScale;
|
|
|
|
|
calmaOutI4Z(p2.p_x, f);
|
|
|
|
|
calmaOutI4Z(p2.p_y, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* By NP */
|
|
|
|
|
/* Property attributes/value pairs. */
|
|
|
|
|
/* Add a CellUse ID property, if the CellUse has a non-default name */
|
2022-11-11 17:20:34 +01:00
|
|
|
/* (Modified 11/11/2022: Do this always, not just the non-default case) */
|
2022-05-10 15:19:39 +02:00
|
|
|
|
2022-11-11 17:20:34 +01:00
|
|
|
calmaOutRHZ(6, CALMA_PROPATTR, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(CALMA_PROP_USENAME_STD, f);
|
|
|
|
|
calmaOutStringRecordZ(CALMA_PROPVALUE, use->cu_id, f);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
/* Add an array limits property, if the CellUse is an array and */
|
|
|
|
|
/* limits of the array (xlo, ylo) are not zero (the default). */
|
|
|
|
|
|
|
|
|
|
if ((use->cu_xlo != 0) || (use->cu_ylo != 0))
|
|
|
|
|
{
|
|
|
|
|
char arraystr[128];
|
|
|
|
|
sprintf(arraystr, "%d_%d_%d_%d", use->cu_xlo, use->cu_xhi,
|
|
|
|
|
use->cu_ylo, use->cu_yhi);
|
|
|
|
|
calmaOutRHZ(6, CALMA_PROPATTR, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(CALMA_PROP_ARRAY_LIMITS, f);
|
|
|
|
|
calmaOutStringRecordZ(CALMA_PROPVALUE, arraystr, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutStructName --
|
|
|
|
|
*
|
|
|
|
|
* Output the name of a cell def.
|
|
|
|
|
* If the name is legal GDS-II, use it; otherwise, generate one
|
|
|
|
|
* that is legal and unique.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the disk file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutStructNameZ(
|
|
|
|
|
int type,
|
|
|
|
|
CellDef *def,
|
|
|
|
|
gzFile f)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
char *defname;
|
|
|
|
|
unsigned char c;
|
|
|
|
|
char *cp;
|
|
|
|
|
int calmanum;
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *table;
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
|
|
|
|
|
{
|
|
|
|
|
table = calmaMapTablePermissive;
|
|
|
|
|
} else {
|
|
|
|
|
table = calmaMapTableStrict;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is the def name a legal Calma name? */
|
2024-10-04 18:21:15 +02:00
|
|
|
for (cp = def->cd_name; (c = (unsigned char) *cp); cp++)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
if ((c > 127) || (table[c] == 0))
|
|
|
|
|
goto bad;
|
|
|
|
|
else if ((unsigned char)table[c] != c)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: character \'%c\' changed to \'%c\' in"
|
|
|
|
|
" name %s\n", (char)c, table[c], def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
/* We really should ensure that the new name is unique. . . */
|
|
|
|
|
}
|
|
|
|
|
if ((!(CIFCurStyle->cs_flags & CWF_STRING_LIMIT)) ||
|
|
|
|
|
(cp <= def->cd_name + CALMANAMELENGTH))
|
|
|
|
|
{
|
|
|
|
|
/* Yes, it's legal: use it */
|
|
|
|
|
defname = StrDup(NULL, def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Bad name: use XXXXXcalmaNum */
|
|
|
|
|
bad:
|
2024-10-21 10:13:23 +02:00
|
|
|
calmanum = (int) CD2INT(def->cd_client);
|
2022-05-10 15:19:39 +02:00
|
|
|
if (calmanum < 0) calmanum = -calmanum;
|
|
|
|
|
defname = (char *)mallocMagic(32);
|
|
|
|
|
(void) sprintf(defname, "XXXXX%d", calmanum);
|
|
|
|
|
TxError("Warning: string in output unprintable; changed to \'%s\'\n",
|
|
|
|
|
defname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calmaOutStringRecordZ(type, defname, f);
|
|
|
|
|
freeMagic(defname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2022-11-10 20:08:58 +01:00
|
|
|
* CalmaGenerateArrayZ --
|
2022-05-10 15:19:39 +02:00
|
|
|
*
|
|
|
|
|
* This routine
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE on success, FALSE if no contact cell could be found.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes an AREF record to the GDS stream output.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2024-10-04 12:34:50 +02:00
|
|
|
CalmaGenerateArrayZ(
|
|
|
|
|
gzFile f, /* GDS output file */
|
|
|
|
|
TileType type, /* Magic tile type of contact */
|
|
|
|
|
int llx,
|
|
|
|
|
int lly, /* Lower-left hand coordinate of the array
|
2022-05-10 15:19:39 +02:00
|
|
|
* (centered on contact cut)
|
|
|
|
|
*/
|
2024-10-04 12:34:50 +02:00
|
|
|
int pitch, /* Pitch of the array elements */
|
|
|
|
|
int cols,
|
|
|
|
|
int rows) /* Number of array elements in X and Y */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
CellDef *child; /* Cell definition of the contact cell */
|
|
|
|
|
int xxlate, yxlate;
|
|
|
|
|
|
|
|
|
|
child = calmaGetContactCell(type, TRUE);
|
|
|
|
|
if (child == NULL) return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Structure reference */
|
|
|
|
|
calmaOutRHZ(4, CALMA_AREF, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructNameZ(CALMA_SNAME, child, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRHZ(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2Z(0, f);
|
|
|
|
|
|
|
|
|
|
/* Number of columns and rows in the array */
|
|
|
|
|
calmaOutRHZ(8, CALMA_COLROW, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(cols, f);
|
|
|
|
|
calmaOutI2Z(rows, f);
|
|
|
|
|
|
|
|
|
|
/* Translation */
|
|
|
|
|
xxlate = llx * calmaPaintScale;
|
|
|
|
|
yxlate = lly * calmaPaintScale;
|
|
|
|
|
calmaOutRHZ(28, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(xxlate, f);
|
|
|
|
|
calmaOutI4Z(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* Column reference point */
|
|
|
|
|
calmaOutI4Z(xxlate + pitch * cols * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4Z(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* Row reference point */
|
|
|
|
|
calmaOutI4Z(xxlate, f);
|
|
|
|
|
calmaOutI4Z(yxlate + pitch * rows * calmaPaintScale, f);
|
|
|
|
|
|
|
|
|
|
/* End of AREF element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteContactsZ --
|
|
|
|
|
*
|
|
|
|
|
* This routine creates a new cellDef for each contact type and writes to
|
|
|
|
|
* the GDS output stream file. It is called before processing all cell
|
|
|
|
|
* definitions while writing GDS output.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes contact cell definition to the open Calma output file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWriteContactsZ(
|
|
|
|
|
gzFile f)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
TileType type;
|
|
|
|
|
TileTypeBitMask tMask, *rMask;
|
|
|
|
|
CellDef *def, *cellDef;
|
|
|
|
|
Rect area, cliprect;
|
|
|
|
|
int halfwidth, halfsize;
|
|
|
|
|
CIFOp *op;
|
|
|
|
|
|
|
|
|
|
/* Turn off generation of contact arrays for the duration of this */
|
|
|
|
|
/* subroutine, so that the contact definitions themselves will get */
|
|
|
|
|
/* the proper contact cut drawn. It is turned on again at the end */
|
|
|
|
|
/* of the routine. Note that this routine is not called unless */
|
|
|
|
|
/* CalmaContactArrays is TRUE. */
|
|
|
|
|
|
|
|
|
|
CalmaContactArrays = FALSE;
|
|
|
|
|
|
|
|
|
|
DBEnumerateTypes(&tMask);
|
|
|
|
|
|
|
|
|
|
/* Decompose stacking types */
|
|
|
|
|
for (type = DBNumUserLayers; type < DBNumTypes; type++)
|
|
|
|
|
if (TTMaskHasType(&tMask, type))
|
|
|
|
|
{
|
|
|
|
|
rMask = DBResidueMask(type);
|
|
|
|
|
TTMaskSetMask(&tMask, rMask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (type = TT_SPACE + 1; type < DBNumUserLayers; type++)
|
|
|
|
|
{
|
|
|
|
|
/* We need to create cell array only for contact types */
|
|
|
|
|
if (DBIsContact(type) && TTMaskHasType(&tMask, type))
|
|
|
|
|
{
|
|
|
|
|
/* Write definition of cell to GDS stream. */
|
|
|
|
|
/* Get cell definition for Tiletype type */
|
|
|
|
|
def = calmaGetContactCell(type, FALSE);
|
|
|
|
|
|
|
|
|
|
/* Get clip bounds, so that residue surround is */
|
|
|
|
|
/* minimum. Note that these values are in CIF/GDS */
|
|
|
|
|
/* units, and the clipping rectangle passed to */
|
2023-03-22 03:04:30 +01:00
|
|
|
/* calmaOutFuncZ is also in CIF/GDS units. */
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
halfsize = CIFGetContactSize(type, NULL, NULL, NULL) >> 1;
|
|
|
|
|
|
|
|
|
|
/* Get minimum width for layer by rounding halfsize */
|
|
|
|
|
/* to the nearest lambda value. */
|
|
|
|
|
halfwidth = halfsize / CIFCurStyle->cs_scaleFactor;
|
|
|
|
|
if ((halfsize % CIFCurStyle->cs_scaleFactor) != 0)
|
|
|
|
|
halfwidth++;
|
|
|
|
|
|
|
|
|
|
area.r_xbot = area.r_ybot = -halfwidth;
|
|
|
|
|
area.r_xtop = area.r_ytop = halfwidth;
|
|
|
|
|
UndoDisable();
|
|
|
|
|
DBPaint(def, &area, type);
|
|
|
|
|
DBReComputeBbox(def);
|
|
|
|
|
TTMaskSetType(&def->cd_types, type);
|
|
|
|
|
|
|
|
|
|
/* Clip output to the bounds of "cliprect" */
|
|
|
|
|
cliprect.r_xbot = cliprect.r_ybot = -halfsize;
|
|
|
|
|
cliprect.r_xtop = cliprect.r_ytop = halfsize;
|
|
|
|
|
|
|
|
|
|
calmaOutFuncZ(def, f, &cliprect);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CalmaContactArrays = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* Process a LinkedBoundary list into a polygon and generate GDS output.
|
|
|
|
|
* Free the linked list when done.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Output, memory deallocation.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaProcessBoundaryZ(
|
|
|
|
|
BoundaryTop *blist,
|
|
|
|
|
calmaOutputStructZ *cos)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
gzFile f = cos->f;
|
|
|
|
|
LinkedBoundary *listtop, *lbref, *lbstop, *lbfree;
|
|
|
|
|
BoundaryTop *bounds;
|
|
|
|
|
int sval;
|
|
|
|
|
int chkcount; /* diagnostic */
|
|
|
|
|
|
|
|
|
|
for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
{
|
|
|
|
|
/* Boundary */
|
|
|
|
|
calmaOutRHZ(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRHZ(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmaPaintLayerNumber, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRHZ(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmaPaintLayerType, f);
|
|
|
|
|
|
|
|
|
|
/* Record length = ((#points + 1) * 2 values * 4 bytes) + 4 bytes header */
|
|
|
|
|
calmaOutRHZ(4 + (bounds->bt_points + 1) * 8, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
|
|
|
|
|
/* Coordinates (repeat 1st point) */
|
|
|
|
|
|
|
|
|
|
listtop = bounds->bt_first;
|
|
|
|
|
lbstop = NULL;
|
|
|
|
|
chkcount = 0;
|
|
|
|
|
for (lbref = listtop; lbref != lbstop; lbref = lbref->lb_next)
|
|
|
|
|
{
|
|
|
|
|
lbstop = listtop;
|
|
|
|
|
calmaOutI4Z(lbref->lb_start.p_x * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4Z(lbref->lb_start.p_y * calmaPaintScale, f);
|
|
|
|
|
chkcount++;
|
|
|
|
|
}
|
2024-10-04 21:08:52 +02:00
|
|
|
if (listtop != NULL)
|
|
|
|
|
{
|
|
|
|
|
calmaOutI4Z(listtop->lb_start.p_x * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4Z(listtop->lb_start.p_y * calmaPaintScale, f);
|
|
|
|
|
}
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
if (chkcount != bounds->bt_points)
|
|
|
|
|
TxError("Points recorded=%d; Points written=%d\n",
|
|
|
|
|
bounds->bt_points, chkcount);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Free the LinkedBoundary list */
|
|
|
|
|
|
|
|
|
|
lbref = listtop;
|
|
|
|
|
while (lbref->lb_next != listtop)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(lbref);
|
|
|
|
|
lbref = lbref->lb_next;
|
|
|
|
|
}
|
|
|
|
|
freeMagic(lbref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free the BoundaryTop list */
|
|
|
|
|
|
|
|
|
|
for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
freeMagic(bounds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaMergePaintFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile cos->f
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaMergePaintFuncZ(
|
|
|
|
|
Tile *tile, /* Tile to be written out. */
|
|
|
|
|
calmaOutputStructZ *cos) /* Information needed by algorithm */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
gzFile f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2022-05-10 15:19:39 +02:00
|
|
|
Tile *t, *tp;
|
|
|
|
|
TileType ttype;
|
|
|
|
|
int i, llx, lly, urx, ury, intedges, num_points, split_type;
|
|
|
|
|
bool is_ext;
|
|
|
|
|
static Stack *SegStack = (Stack *)NULL;
|
|
|
|
|
|
|
|
|
|
static LinkedBoundary *edge;
|
|
|
|
|
LinkedBoundary *lb;
|
|
|
|
|
BoundaryTop *bounds = NULL;
|
|
|
|
|
|
|
|
|
|
/* Quick check for tiles that have already been processed */
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClientINT(tile) == GDS_PROCESSED) return 0;
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
if (SegStack == (Stack *)NULL)
|
|
|
|
|
SegStack = StackNew(64);
|
|
|
|
|
|
2023-03-22 03:04:30 +01:00
|
|
|
PUSHTILEZ(tile);
|
2022-05-10 15:19:39 +02:00
|
|
|
while (!StackEmpty(SegStack))
|
|
|
|
|
{
|
|
|
|
|
t = (Tile *) STACKPOP(SegStack);
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClientINT(t) != GDS_PENDING) continue;
|
|
|
|
|
TiSetClientINT(t, GDS_PROCESSED);
|
2022-05-10 15:19:39 +02:00
|
|
|
|
|
|
|
|
split_type = -1;
|
|
|
|
|
if (IsSplit(t))
|
|
|
|
|
{
|
|
|
|
|
/* If we use SplitSide, then we need to set it when the */
|
|
|
|
|
/* tile is pushed. Since these are one-or-zero mask layers */
|
|
|
|
|
/* I assume it is okay to just check which side is TT_SPACE */
|
|
|
|
|
|
|
|
|
|
/* split_type = (SplitSide(t) << 1) | SplitDirection(t); */
|
|
|
|
|
split_type = SplitDirection(t);
|
|
|
|
|
if (TiGetLeftType(t) == TT_SPACE) split_type |= 2;
|
|
|
|
|
num_points = 2;
|
|
|
|
|
if (edge != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Remove one point from the edge record for rectangles */
|
|
|
|
|
/* and relink the last entry back to the new head. */
|
|
|
|
|
|
|
|
|
|
lb = edge;
|
|
|
|
|
while (lb->lb_next != edge) lb = lb->lb_next;
|
|
|
|
|
lb->lb_next = edge->lb_next;
|
|
|
|
|
freeMagic(edge);
|
|
|
|
|
edge = edge->lb_next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
num_points = 3;
|
|
|
|
|
|
|
|
|
|
/* Create a new linked boundary structure with 4 unknown edges. */
|
|
|
|
|
/* This structure is reused when we encounter isolated tiles, */
|
|
|
|
|
/* so we avoid unnecessary overhead in the case of, for */
|
|
|
|
|
/* example, large contact cut arrays. */
|
|
|
|
|
|
|
|
|
|
if (edge == NULL)
|
|
|
|
|
{
|
|
|
|
|
edge = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
|
|
|
|
|
lb = edge;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_points; i++)
|
|
|
|
|
{
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb->lb_next = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
}
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb->lb_next = edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lb = edge;
|
|
|
|
|
llx = LEFT(t);
|
|
|
|
|
lly = BOTTOM(t);
|
|
|
|
|
urx = RIGHT(t);
|
|
|
|
|
ury = TOP(t);
|
|
|
|
|
intedges = 0;
|
|
|
|
|
|
|
|
|
|
/* Initialize the "edge" record with the corner points of the */
|
|
|
|
|
/* tile. */
|
|
|
|
|
|
|
|
|
|
if (IsSplit(t))
|
|
|
|
|
{
|
|
|
|
|
switch (split_type)
|
|
|
|
|
{
|
|
|
|
|
case 0x0:
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
break;
|
|
|
|
|
case 0x1:
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
break;
|
|
|
|
|
case 0x2:
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
break;
|
|
|
|
|
case 0x3:
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
num_points = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = ury;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = llx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
lb->lb_start.p_x = urx;
|
|
|
|
|
lb->lb_start.p_y = lly;
|
|
|
|
|
lb->lb_type = LB_INIT;
|
|
|
|
|
lb = lb->lb_next;
|
|
|
|
|
|
|
|
|
|
num_points = 0;
|
|
|
|
|
}
|
|
|
|
|
if (split_type == 0x1) goto left_search;
|
|
|
|
|
|
|
|
|
|
/* Search the tile boundary for connected and unconnected tiles. */
|
|
|
|
|
/* Generate segments in a counterclockwise cycle. */
|
|
|
|
|
|
|
|
|
|
if (split_type == 0x2)
|
|
|
|
|
{
|
|
|
|
|
intedges += calmaAddSegment(&lb, TRUE, RIGHT(t), TOP(t),
|
|
|
|
|
LEFT(t), BOTTOM(t));
|
|
|
|
|
goto bottom_search;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search top */
|
|
|
|
|
|
|
|
|
|
ttype = TiGetTopType(t);
|
|
|
|
|
for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp), num_points++)
|
|
|
|
|
{
|
|
|
|
|
is_ext = (TiGetBottomType(tp) != ttype) ? TRUE : FALSE;
|
|
|
|
|
intedges += calmaAddSegment(&lb, is_ext,
|
|
|
|
|
MIN(RIGHT(t), RIGHT(tp)), TOP(t),
|
|
|
|
|
MAX(LEFT(t), LEFT(tp)), TOP(t));
|
2023-03-22 03:04:30 +01:00
|
|
|
if (!is_ext) PUSHTILEZ(tp);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (split_type == 0x3)
|
|
|
|
|
{
|
|
|
|
|
intedges += calmaAddSegment(&lb, TRUE, LEFT(t), TOP(t),
|
|
|
|
|
RIGHT(t), BOTTOM(t));
|
|
|
|
|
goto right_search;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search left */
|
|
|
|
|
|
|
|
|
|
left_search:
|
|
|
|
|
ttype = TiGetLeftType(t);
|
|
|
|
|
for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp), num_points++)
|
|
|
|
|
{
|
|
|
|
|
is_ext = (TiGetRightType(tp) != ttype) ? TRUE : FALSE;
|
|
|
|
|
intedges += calmaAddSegment(&lb, is_ext,
|
|
|
|
|
LEFT(t), MIN(TOP(t), TOP(tp)),
|
|
|
|
|
LEFT(t), MAX(BOTTOM(t), BOTTOM(tp)));
|
2023-03-22 03:04:30 +01:00
|
|
|
if (!is_ext) PUSHTILEZ(tp);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (split_type == 0x0)
|
|
|
|
|
{
|
|
|
|
|
intedges += calmaAddSegment(&lb, TRUE, LEFT(t), BOTTOM(t),
|
|
|
|
|
RIGHT(t), TOP(t));
|
|
|
|
|
goto done_searches;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search bottom */
|
|
|
|
|
|
|
|
|
|
bottom_search:
|
|
|
|
|
ttype = TiGetBottomType(t);
|
|
|
|
|
for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp), num_points++)
|
|
|
|
|
{
|
|
|
|
|
is_ext = (TiGetTopType(tp) != ttype) ? TRUE : FALSE;
|
|
|
|
|
intedges += calmaAddSegment(&lb, is_ext,
|
|
|
|
|
MAX(LEFT(t), LEFT(tp)), BOTTOM(t),
|
|
|
|
|
MIN(RIGHT(t), RIGHT(tp)), BOTTOM(t));
|
2023-03-22 03:04:30 +01:00
|
|
|
if (!is_ext) PUSHTILEZ(tp);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (split_type == 0x1)
|
|
|
|
|
{
|
|
|
|
|
intedges += calmaAddSegment(&lb, TRUE, RIGHT(t), BOTTOM(t),
|
|
|
|
|
LEFT(t), TOP(t));
|
|
|
|
|
goto done_searches;
|
|
|
|
|
}
|
|
|
|
|
/* Search right */
|
|
|
|
|
|
|
|
|
|
right_search:
|
|
|
|
|
ttype = TiGetRightType(t);
|
|
|
|
|
for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp), num_points++)
|
|
|
|
|
{
|
|
|
|
|
is_ext = (TiGetLeftType(tp) != ttype) ? TRUE : FALSE;
|
|
|
|
|
intedges += calmaAddSegment(&lb, is_ext,
|
|
|
|
|
RIGHT(t), MAX(BOTTOM(t), BOTTOM(tp)),
|
|
|
|
|
RIGHT(t), MIN(TOP(t), TOP(tp)));
|
2023-03-22 03:04:30 +01:00
|
|
|
if (!is_ext) PUSHTILEZ(tp);
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If tile is isolated, process it now and we're done */
|
|
|
|
|
|
|
|
|
|
done_searches:
|
|
|
|
|
if (intedges == 0)
|
|
|
|
|
{
|
|
|
|
|
calmaWritePaintFuncZ(t, cos);
|
|
|
|
|
|
|
|
|
|
/* Although calmaWritePaintFunc is called only on isolated */
|
|
|
|
|
/* tiles, we may have expanded it. This could use a LOT of */
|
|
|
|
|
/* optimizing. 1) remove colinear points in calmaAddSegment */
|
|
|
|
|
/* when both subsegments are external paths, and 2) here, */
|
|
|
|
|
/* take the shortest path to making "edge" exactly 4 points.*/
|
|
|
|
|
/* Note that in non-Manhattan mode, num_points may be 3. */
|
|
|
|
|
|
|
|
|
|
if (num_points != 4)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < num_points; i++)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(edge);
|
|
|
|
|
edge = edge->lb_next;
|
|
|
|
|
}
|
|
|
|
|
edge = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (!StackEmpty(SegStack))
|
|
|
|
|
TxError("ERROR: Segment stack is supposed to be empty!\n");
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Merge boundary into existing record */
|
|
|
|
|
|
|
|
|
|
calmaMergeSegments(edge, &bounds, num_points);
|
|
|
|
|
edge = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove any degenerate points */
|
|
|
|
|
calmaRemoveDegenerate(bounds);
|
|
|
|
|
|
|
|
|
|
/* Remove any colinear points */
|
|
|
|
|
calmaRemoveColinear(bounds);
|
|
|
|
|
|
|
|
|
|
/* Output the boundary records */
|
|
|
|
|
calmaProcessBoundaryZ(bounds, cos);
|
|
|
|
|
|
|
|
|
|
return 0; /* Keep the search alive. . . */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWritePaintFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Filter function used to write out a single paint tile.
|
|
|
|
|
*
|
|
|
|
|
* **** NOTE ****
|
|
|
|
|
* There are loads of Calma systems out in the world that
|
|
|
|
|
* don't understand CALMA_BOX, so we output CALMA_BOUNDARY
|
|
|
|
|
* even though CALMA_BOX is more appropriate. Bletch.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWritePaintFuncZ(
|
|
|
|
|
Tile *tile, /* Tile to be written out. */
|
|
|
|
|
calmaOutputStructZ *cos) /* File for output and clipping area */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
gzFile f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2022-05-10 15:19:39 +02:00
|
|
|
Rect r, r2;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &r);
|
|
|
|
|
if (clipArea != NULL)
|
|
|
|
|
GeoClip(&r, clipArea);
|
|
|
|
|
|
|
|
|
|
r.r_xbot *= calmaPaintScale;
|
|
|
|
|
r.r_ybot *= calmaPaintScale;
|
|
|
|
|
r.r_xtop *= calmaPaintScale;
|
|
|
|
|
r.r_ytop *= calmaPaintScale;
|
|
|
|
|
|
|
|
|
|
/* Boundary */
|
|
|
|
|
calmaOutRHZ(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRHZ(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmaPaintLayerNumber, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRHZ(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmaPaintLayerType, f);
|
|
|
|
|
|
|
|
|
|
/* The inefficient use of CALMA_BOUNDARY for rectangles actually */
|
|
|
|
|
/* makes it easy to implement triangles, since they must be defined */
|
|
|
|
|
/* by CALMA_BOUNDARY. */
|
|
|
|
|
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
calmaOutRHZ(36, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
|
|
|
|
|
switch ((SplitSide(tile) << 1) | SplitDirection(tile))
|
|
|
|
|
{
|
|
|
|
|
case 0x0:
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x1:
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x2:
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x3:
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
calmaOutRHZ(44, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteLabelFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Output a single label to the compressed stream file 'f'.
|
|
|
|
|
*
|
|
|
|
|
* The CIF type to which this label is attached is 'type'; if this
|
|
|
|
|
* is < 0 then the label is not output.
|
|
|
|
|
*
|
|
|
|
|
* Non-point labels are collapsed to point labels located at the center
|
|
|
|
|
* of the original label.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWriteLabelFuncZ(
|
|
|
|
|
Label *lab, /* Label to output */
|
|
|
|
|
int ltype, /* CIF layer number to use for TEXT record */
|
|
|
|
|
int type, /* CIF layer number to use for BOUNDARY record,
|
2022-12-21 23:49:43 +01:00
|
|
|
* or -1 if not attached to a layer
|
|
|
|
|
*/
|
2024-10-04 12:34:50 +02:00
|
|
|
gzFile f) /* Stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
Point p;
|
|
|
|
|
int calmanum, calmatype;
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
if (ltype < 0)
|
2022-05-10 15:19:39 +02:00
|
|
|
return;
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
calmanum = CIFCurStyle->cs_layers[ltype]->cl_calmanum;
|
2022-05-10 15:19:39 +02:00
|
|
|
if (!CalmaIsValidLayer(calmanum))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(4, CALMA_TEXT, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmanum, f);
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
calmatype = CIFCurStyle->cs_layers[ltype]->cl_calmatype;
|
2022-05-10 15:19:39 +02:00
|
|
|
calmaOutRHZ(6, CALMA_TEXTTYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmatype, f);
|
|
|
|
|
|
|
|
|
|
if (lab->lab_font >= 0)
|
|
|
|
|
{
|
|
|
|
|
unsigned short textpres = 0;
|
|
|
|
|
|
|
|
|
|
/* A bit of a hack here. Magic can have any number of fonts, */
|
|
|
|
|
/* but GDS only allows four of them. So we just crop the font */
|
|
|
|
|
/* index to two bits. We provide no other font information, so */
|
|
|
|
|
/* this is highly implementation-dependent. But it allows us */
|
|
|
|
|
/* to retain font information when reading and writing our own */
|
|
|
|
|
/* GDS files. */
|
|
|
|
|
|
|
|
|
|
textpres = (lab->lab_font & 0x03) << 4;
|
|
|
|
|
|
|
|
|
|
switch(lab->lab_just)
|
|
|
|
|
{
|
|
|
|
|
case GEO_SOUTH:
|
|
|
|
|
textpres |= 0x0001;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTHEAST:
|
|
|
|
|
textpres |= 0x0000;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_EAST:
|
|
|
|
|
textpres |= 0x0004;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_NORTHEAST:
|
|
|
|
|
textpres |= 0x0008;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_NORTH:
|
|
|
|
|
textpres |= 0x0009;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_NORTHWEST:
|
|
|
|
|
textpres |= 0x000a;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_WEST:
|
|
|
|
|
textpres |= 0x0006;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTHWEST:
|
|
|
|
|
textpres |= 0x0002;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_CENTER:
|
|
|
|
|
textpres |= 0x0005;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(6, CALMA_PRESENTATION, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2Z(textpres, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2Z(0, f); /* Any need for these bits? */
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(12, CALMA_MAG, CALMA_R8, f);
|
|
|
|
|
calmaOutR8Z(((double)lab->lab_size / 800)
|
|
|
|
|
* (double)CIFCurStyle->cs_scaleFactor
|
|
|
|
|
/ (double)CIFCurStyle->cs_expander, f);
|
|
|
|
|
|
|
|
|
|
if (lab->lab_rotate != 0)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRHZ(12, CALMA_ANGLE, CALMA_R8, f);
|
|
|
|
|
calmaOutR8Z((double)lab->lab_rotate, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.p_x = (lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) * calmaWriteScale / 2;
|
|
|
|
|
p.p_y = (lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) * calmaWriteScale / 2;
|
|
|
|
|
calmaOutRHZ(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(p.p_x, f);
|
|
|
|
|
calmaOutI4Z(p.p_y, f);
|
|
|
|
|
|
|
|
|
|
/* Text of label */
|
|
|
|
|
calmaOutStringRecordZ(CALMA_STRING, lab->lab_text, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* If the cifoutput layer is for labels only (has no operators), */
|
|
|
|
|
/* and the label rectangle is not degenerate, then output the label */
|
|
|
|
|
/* rectangle as a boundary with the label's layer:purpose pair. */
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
if (type < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
calmanum = CIFCurStyle->cs_layers[type]->cl_calmanum;
|
|
|
|
|
if (!CalmaIsValidLayer(calmanum))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
calmatype = CIFCurStyle->cs_layers[type]->cl_calmatype;
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
/* Note that the check for whether the CIF_LABEL_NOPORT flag has */
|
|
|
|
|
/* been set is done outside of this routine. */
|
|
|
|
|
|
|
|
|
|
if ((CIFCurStyle->cs_layers[type]->cl_ops == NULL) &&
|
|
|
|
|
(lab->lab_rect.r_xtop > lab->lab_rect.r_xbot) &&
|
|
|
|
|
(lab->lab_rect.r_ytop > lab->lab_rect.r_ybot))
|
|
|
|
|
{
|
|
|
|
|
Rect r;
|
|
|
|
|
|
|
|
|
|
r = lab->lab_rect;
|
|
|
|
|
r.r_xbot *= calmaWriteScale;
|
|
|
|
|
r.r_ybot *= calmaWriteScale;
|
|
|
|
|
r.r_xtop *= calmaWriteScale;
|
|
|
|
|
r.r_ytop *= calmaWriteScale;
|
|
|
|
|
|
|
|
|
|
/* Boundary */
|
|
|
|
|
calmaOutRHZ(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRHZ(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmanum, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRHZ(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(calmatype, f);
|
|
|
|
|
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
calmaOutRHZ(44, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
calmaOutI4Z(r.r_xtop, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ytop, f);
|
|
|
|
|
calmaOutI4Z(r.r_xbot, f); calmaOutI4Z(r.r_ybot, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaPaintLabelFuncZ --
|
|
|
|
|
*
|
|
|
|
|
* Filter function used to write out a single label corresponding to the
|
|
|
|
|
* area of a paint tile, and having a text matching the CIF layer name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaPaintLabelFuncZ(
|
|
|
|
|
Tile *tile, /* Tile contains area for label. */
|
|
|
|
|
calmaOutputStructZ *cos) /* File for output and clipping area */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
gzFile f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2022-05-10 15:19:39 +02:00
|
|
|
Rect r, r2;
|
|
|
|
|
Point p;
|
|
|
|
|
int len;
|
|
|
|
|
CIFLayer *layer = CIFCurStyle->cs_layers[cos->type];
|
|
|
|
|
|
|
|
|
|
if (IsSplit(tile)) return 0; /* Ignore non-Manhattan geometry */
|
|
|
|
|
|
|
|
|
|
if (!CalmaIsValidLayer(layer->cl_calmanum))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &r);
|
|
|
|
|
|
|
|
|
|
if (clipArea != NULL)
|
|
|
|
|
GeoClip(&r, clipArea);
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(4, CALMA_TEXT, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(layer->cl_calmanum, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(6, CALMA_TEXTTYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(layer->cl_calmatype, f);
|
|
|
|
|
|
|
|
|
|
p.p_x = (r.r_xbot + r.r_xtop) * calmaPaintScale / 2;
|
|
|
|
|
p.p_y = (r.r_ybot + r.r_ytop) * calmaPaintScale / 2;
|
|
|
|
|
calmaOutRHZ(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4Z(p.p_x, f);
|
|
|
|
|
calmaOutI4Z(p.p_y, f);
|
|
|
|
|
|
|
|
|
|
/* Text of label is the CIF layer name */
|
|
|
|
|
calmaOutStringRecordZ(CALMA_STRING, layer->cl_name, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRHZ(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutHeader --
|
|
|
|
|
*
|
|
|
|
|
* Output the header description for a Calma file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutHeaderZ(
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
gzFile f)
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
static double useru = 0.001;
|
|
|
|
|
static double mum = 1.0e-9;
|
|
|
|
|
|
|
|
|
|
/* GDS II version 3.0 */
|
|
|
|
|
calmaOutRHZ(6, CALMA_HEADER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2Z(3, f);
|
|
|
|
|
|
|
|
|
|
/* Beginning of library */
|
|
|
|
|
calmaOutRHZ(28, CALMA_BGNLIB, CALMA_I2, f);
|
|
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDateZ(*CalmaDateStamp, f);
|
|
|
|
|
else
|
|
|
|
|
calmaOutDateZ(rootDef->cd_timestamp, f);
|
|
|
|
|
calmaOutDateZ(time((time_t *) 0), f);
|
|
|
|
|
|
|
|
|
|
/* Library name (name of root cell) */
|
|
|
|
|
calmaOutStructNameZ(CALMA_LIBNAME, rootDef, f);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Units.
|
|
|
|
|
* User units are microns; this is really unimportant.
|
|
|
|
|
*
|
|
|
|
|
* Database units are nanometers, since there are
|
|
|
|
|
* programs that don't understand anything else. If
|
|
|
|
|
* the database units are *smaller* than nanometers, use
|
|
|
|
|
* the actual database units. Otherwise, stick with
|
|
|
|
|
* nanometers, because anything larger may not input
|
|
|
|
|
* properly with other software.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
calmaOutRHZ(20, CALMA_UNITS, CALMA_R8, f);
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) useru = 0.0001;
|
|
|
|
|
calmaOutR8Z(useru, f); /* User units per database unit */
|
|
|
|
|
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) mum = 1e-10;
|
|
|
|
|
calmaOutR8Z(mum, f); /* Meters per database unit */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutDateZ --
|
|
|
|
|
*
|
|
|
|
|
* Output a date/time specification to the FILE 'f'.
|
|
|
|
|
* This consists of outputting 6 2-byte quantities,
|
|
|
|
|
* or a total of 12 bytes.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutDateZ(
|
|
|
|
|
time_t t, /* Time (UNIX format) to be output */
|
|
|
|
|
gzFile f) /* Stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
struct tm *datep = localtime(&t);
|
|
|
|
|
|
|
|
|
|
calmaOutI2Z(datep->tm_year, f);
|
|
|
|
|
calmaOutI2Z(datep->tm_mon+1, f);
|
|
|
|
|
calmaOutI2Z(datep->tm_mday, f);
|
|
|
|
|
calmaOutI2Z(datep->tm_hour, f);
|
|
|
|
|
calmaOutI2Z(datep->tm_min, f);
|
|
|
|
|
calmaOutI2Z(datep->tm_sec, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutStringRecordZ --
|
|
|
|
|
*
|
|
|
|
|
* Output a complete string-type record. The actual record
|
|
|
|
|
* type is given by 'type'. Up to the first CALMANAMELENGTH characters
|
|
|
|
|
* of the string 'str' are output. Any characters in 'str'
|
|
|
|
|
* not in the legal Calma stream character set are output as
|
|
|
|
|
* 'X' instead.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutStringRecordZ(
|
|
|
|
|
int type, /* Type of this record (data type is ASCII string) */
|
|
|
|
|
char *str, /* String to be output */
|
|
|
|
|
gzFile f) /* Compressed stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
unsigned char c;
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *table;
|
|
|
|
|
char *locstr, *origstr = NULL;
|
2022-05-10 15:19:39 +02:00
|
|
|
char *locstrprv;
|
|
|
|
|
|
|
|
|
|
if(CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
|
|
|
|
|
{
|
|
|
|
|
table = calmaMapTablePermissive;
|
|
|
|
|
} else {
|
|
|
|
|
table = calmaMapTableStrict;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
|
locstr = str;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure length is even.
|
|
|
|
|
* Output at most CALMANAMELENGTH characters.
|
|
|
|
|
* If the name is longer than CALMANAMELENGTH, then output the
|
|
|
|
|
* last CALMANAMELENGTH characters (since cell names are more
|
|
|
|
|
* likely to be unique in the last characters than in the first
|
|
|
|
|
* characters).
|
|
|
|
|
*
|
|
|
|
|
* NOTE: GDS format has not used CALMANAMELENGTH restrictions
|
|
|
|
|
* for ages. Since this is a 2-byte record, then is it not
|
|
|
|
|
* worth checking the 65536 - 4 character limit. The CALMANAMELENGTH
|
|
|
|
|
* restriction must be enabled in the cifoutput flags.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (len & 01) len++;
|
|
|
|
|
if ((CIFCurStyle->cs_flags & CWF_STRING_LIMIT) && (len > CALMANAMELENGTH))
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: Cellname %s truncated ", str);
|
|
|
|
|
TxError("to %s (GDS format limit)\n", str + len - CALMANAMELENGTH);
|
|
|
|
|
locstr = str + len - CALMANAMELENGTH;
|
|
|
|
|
len = CALMANAMELENGTH;
|
|
|
|
|
}
|
|
|
|
|
calmaOutI2Z(len+4, f); /* Record length */
|
|
|
|
|
(void) gzputc(f, type); /* Record type */
|
|
|
|
|
(void) gzputc(f, CALMA_ASCII); /* Data type */
|
|
|
|
|
|
|
|
|
|
/* Output the string itself */
|
|
|
|
|
while (len--)
|
|
|
|
|
{
|
|
|
|
|
locstrprv = locstr;
|
|
|
|
|
c = (unsigned char) *locstr++;
|
|
|
|
|
if (c == 0) gzputc(f, '\0');
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ((c > 127) || (c == 0))
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: Unprintable character changed "
|
|
|
|
|
"to \'X\' in label.\n");
|
|
|
|
|
c = 'X';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (((unsigned char)table[c] != c) && (origstr == NULL))
|
|
|
|
|
origstr = StrDup(NULL, str);
|
|
|
|
|
|
|
|
|
|
c = table[c];
|
|
|
|
|
locstrprv[0] = c;
|
|
|
|
|
}
|
|
|
|
|
if (!CalmaDoLower && islower(c))
|
|
|
|
|
(void) gzputc(f, toupper(c));
|
|
|
|
|
else
|
|
|
|
|
(void) gzputc(f, c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (origstr != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: characters changed in string \'%s\'; "
|
|
|
|
|
"modified string is \'%s\'\n", origstr, str);
|
|
|
|
|
freeMagic(origstr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutR8Z --
|
|
|
|
|
*
|
|
|
|
|
* Write an 8-byte Real value in GDS-II format to the output stream
|
|
|
|
|
* The value is passed as a double.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* 8-byte value written to compressed output stream gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutR8Z(
|
|
|
|
|
double d, /* Double value to write to output */
|
|
|
|
|
gzFile f) /* Stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
int c, i, sign, expon;
|
|
|
|
|
|
|
|
|
|
/* mantissa must be 64 bits for this routine to work correctly */
|
|
|
|
|
uint64_t mantissa;
|
|
|
|
|
|
|
|
|
|
mantissa = 0;
|
|
|
|
|
if (d == 0.0)
|
|
|
|
|
{
|
|
|
|
|
sign = 0;
|
|
|
|
|
expon = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (d > 0.0)
|
|
|
|
|
sign = 0;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sign = 1;
|
|
|
|
|
d = -d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expon = 64;
|
|
|
|
|
while (d >= 1.0)
|
|
|
|
|
{
|
|
|
|
|
d /= 16.0;
|
|
|
|
|
expon++;
|
|
|
|
|
}
|
|
|
|
|
while (d < 0.0625)
|
|
|
|
|
{
|
|
|
|
|
d *= 16.0;
|
|
|
|
|
expon--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
mantissa <<= 1;
|
|
|
|
|
if (d >= 0.5)
|
|
|
|
|
{
|
|
|
|
|
mantissa |= 0x1;
|
|
|
|
|
d -= 0.5;
|
|
|
|
|
}
|
|
|
|
|
d *= 2.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c = (sign << 7) | expon;
|
|
|
|
|
(void) gzputc(f, c);
|
|
|
|
|
for (i = 1; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
c = (int)(0xff & (mantissa >> (64 - (8 * i))));
|
|
|
|
|
(void) gzputc(f, c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOut8Z --
|
|
|
|
|
*
|
|
|
|
|
* Output 8 bytes.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the gzFile 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOut8Z(
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *str, /* 8-byte string to be output */
|
2024-10-04 12:34:50 +02:00
|
|
|
gzFile f) /* Compressed stream file */
|
2022-05-10 15:19:39 +02:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
(void) gzputc(f, *str++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_ZLIB */
|