2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* CalmaWrite.c --
|
|
|
|
|
*
|
|
|
|
|
* Output of Calma GDS-II stream format.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char rcsid[] __attribute__ ((unused)) ="$Header: /usr/cvsroot/magic-8.0/calma/CalmaWrite.c,v 1.8 2010/12/22 16:29:06 tim Exp $";
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
2018-11-16 19:59:17 +01:00
|
|
|
#include <stdlib.h> /* for random() */
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <sys/types.h>
|
2021-06-17 18:40:21 +02:00
|
|
|
#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>
|
|
|
|
|
#else
|
|
|
|
|
# ifdef HAVE_SYS_TIME_H
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# else
|
|
|
|
|
# include <time.h>
|
|
|
|
|
# endif
|
2017-04-25 14:41:48 +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"
|
2017-04-25 14:41:48 +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 */
|
2017-04-25 14:41:48 +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"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Exports */
|
2021-01-14 21:21:39 +01:00
|
|
|
bool CalmaDoLibrary = FALSE; /* If TRUE, do not output the top level */
|
|
|
|
|
bool CalmaDoLabels = TRUE; /* If FALSE, don't output labels with GDS-II */
|
|
|
|
|
bool CalmaDoLower = TRUE; /* If TRUE, allow lowercase labels. */
|
|
|
|
|
bool CalmaFlattenArrays = FALSE; /* If TRUE, output arrays as individual uses */
|
|
|
|
|
bool CalmaAddendum = FALSE; /* If TRUE, do not output readonly cell defs */
|
2022-01-21 16:26:29 +01:00
|
|
|
time_t *CalmaDateStamp = NULL; /* If non-NULL, output this for creation date stamp */
|
2021-01-14 21:21:39 +01:00
|
|
|
bool CalmaAllowUndefined = FALSE; /* If TRUE, allow calls to undefined cells */
|
2022-11-02 14:40:20 +01:00
|
|
|
bool CalmaAllowAbstract = FALSE; /* If TRUE, allow abstract views to be written */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-11-10 20:46:26 +01:00
|
|
|
/* Experimental stuff---not thoroughly tested. In particular, the */
|
|
|
|
|
/* array generation of CalmaContactArrays is different from that */
|
|
|
|
|
/* used by the cifSquareFunc(), and should be using the */
|
|
|
|
|
/* cifFillSquaresArea() function directly, and include slots. */
|
|
|
|
|
|
|
|
|
|
bool CalmaContactArrays = FALSE; /* If TRUE, output contacts as subcell arrays */
|
2017-04-25 14:41:48 +02:00
|
|
|
bool CalmaMergeTiles = FALSE; /* If TRUE, merge tiles into polygons in output. */
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
|
int CalmaCompression = 0; /* Output file compression level (0 = uncompressed) */
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-10-04 12:40:14 +02:00
|
|
|
/* Structure used by calmaWritePaintFunc() */
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
FILE *f; /* File stream for output */
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *area; /* Clipping area, in GDS coordinates */
|
2024-10-04 12:40:14 +02:00
|
|
|
int type; /* Layer index */
|
|
|
|
|
} calmaOutputStruct;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Forward declarations */
|
2024-10-04 12:32:22 +02:00
|
|
|
extern int calmaWriteInitFunc(CellDef *def);
|
2026-01-03 02:12:37 +01:00
|
|
|
extern int calmaWritePaintFunc(Tile *tile, TileType dinfo, calmaOutputStruct *cos);
|
|
|
|
|
extern int calmaMergePaintFunc(Tile *tile, TileType dinfo, calmaOutputStruct *cos);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern int calmaWriteUseFunc(CellUse *use, FILE *f);
|
2026-01-03 02:12:37 +01:00
|
|
|
extern int calmaPaintLabelFunc(Tile *tile, TileType dinfo, calmaOutputStruct *cos);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern void calmaWriteContacts(FILE *f);
|
|
|
|
|
extern void calmaDelContacts(void);
|
2025-01-31 17:31:02 +01:00
|
|
|
extern void calmaOutFunc(CellDef *def, FILE *f, const Rect *cliprect);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern void calmaOutStructName(int type, CellDef *def, FILE *f);
|
|
|
|
|
extern void calmaWriteLabelFunc(Label *lab, int ltype, int type, FILE *f);
|
|
|
|
|
extern void calmaOutHeader(CellDef *rootDef, FILE *f);
|
|
|
|
|
extern void calmaOutDate(time_t t, FILE *f);
|
|
|
|
|
extern void calmaOutStringRecord(int type, char *str, FILE *f);
|
2024-10-04 12:19:10 +02:00
|
|
|
extern void calmaOut8(const char *str, FILE *f);
|
2024-10-04 12:32:22 +02:00
|
|
|
extern void calmaOutR8(double d, FILE *f);
|
|
|
|
|
extern void calmaProcessBoundary(BoundaryTop *blist, calmaOutputStruct *cos);
|
|
|
|
|
extern void calmaRemoveColinear(BoundaryTop *blist);
|
|
|
|
|
extern void calmaRemoveDegenerate(BoundaryTop *blist);
|
2017-04-25 14:41:48 +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 PUSHTILEC(tp) \
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tp) == GDS_UNPROCESSED) { \
|
|
|
|
|
TiSetClientINT(tp, GDS_PENDING); \
|
2017-04-25 14:41:48 +02:00
|
|
|
STACKPUSH((ClientData) (tp), SegStack); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/* Number assigned to each cell */
|
|
|
|
|
int calmaCellNum;
|
|
|
|
|
|
|
|
|
|
/* Factor by which to scale Magic coordinates for cells and labels. */
|
|
|
|
|
int calmaWriteScale;
|
|
|
|
|
|
|
|
|
|
/* Scale factor for outputting paint: */
|
|
|
|
|
int calmaPaintScale;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Current layer number and "type".
|
|
|
|
|
* In GDS-II format, this is output with each rectangle.
|
|
|
|
|
*/
|
|
|
|
|
int calmaPaintLayerNumber;
|
|
|
|
|
int calmaPaintLayerType;
|
|
|
|
|
|
2018-11-16 19:59:17 +01:00
|
|
|
/*
|
|
|
|
|
* Hash table used to determine which GDS libraries have been output
|
|
|
|
|
*/
|
|
|
|
|
HashTable calmaLibHash;
|
2018-11-19 21:01:20 +01:00
|
|
|
HashTable calmaPrefixHash;
|
2021-06-17 18:40:21 +02:00
|
|
|
HashTable calmaUndefHash;
|
2018-11-16 19:59:17 +01:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Macros to output various pieces of Calma information.
|
|
|
|
|
* These are macros for speed.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutRH --
|
|
|
|
|
*
|
|
|
|
|
* 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 calmaOutRH(count, type, datatype, f) \
|
|
|
|
|
{ calmaOutI2(count, f); (void) putc(type, f); (void) putc(datatype, f); }
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutI2 --
|
|
|
|
|
*
|
|
|
|
|
* Output a two-byte integer.
|
|
|
|
|
* Calma byte order is the same as the network byte order used
|
|
|
|
|
* by the various network library procedures.
|
|
|
|
|
*/
|
|
|
|
|
#define calmaOutI2(n, f) \
|
|
|
|
|
{ \
|
|
|
|
|
union { short u_s; char u_c[2]; } u; \
|
|
|
|
|
u.u_s = htons(n); \
|
|
|
|
|
(void) putc(u.u_c[0], f); \
|
|
|
|
|
(void) putc(u.u_c[1], f); \
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* calmaOutI4 --
|
|
|
|
|
*
|
|
|
|
|
* Output a four-byte integer.
|
|
|
|
|
* Calma byte order is the same as the network byte order used
|
|
|
|
|
* by the various network library procedures.
|
|
|
|
|
*/
|
|
|
|
|
#define calmaOutI4(n, f) \
|
|
|
|
|
{ \
|
|
|
|
|
union { long u_i; char u_c[4]; } u; \
|
|
|
|
|
u.u_i = htonl(n); \
|
|
|
|
|
(void) putc(u.u_c[0], f); \
|
|
|
|
|
(void) putc(u.u_c[1], f); \
|
|
|
|
|
(void) putc(u.u_c[2], f); \
|
|
|
|
|
(void) putc(u.u_c[3], f); \
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 12:19:10 +02:00
|
|
|
static const char calmaMapTableStrict[] =
|
2017-04-25 14:41:48 +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[] =
|
2017-04-25 14:41:48 +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 */
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CalmaWrite --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
CalmaWrite(
|
|
|
|
|
CellDef *rootDef, /* Pointer to CellDef to be written */
|
|
|
|
|
FILE *f) /* Open output file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-05-10 15:19:39 +02:00
|
|
|
int oldCount = DBWFeedbackCount, problems, nerr;
|
2017-04-25 14:41:48 +02:00
|
|
|
bool good;
|
2024-04-29 23:43:37 +02:00
|
|
|
CellDef *err_def;
|
2017-04-25 14:41:48 +02:00
|
|
|
CellUse dummy;
|
2021-06-17 18:40:21 +02:00
|
|
|
HashEntry *he;
|
|
|
|
|
HashSearch hs;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 19:59:17 +01:00
|
|
|
HashInit(&calmaLibHash, 32, 0);
|
2018-11-19 21:01:20 +01:00
|
|
|
HashInit(&calmaPrefixHash, 32, 0);
|
2021-06-17 18:40:21 +02:00
|
|
|
HashInit(&calmaUndefHash, 32, 0);
|
2018-11-16 19:59:17 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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)
|
2021-01-14 21:21:39 +01: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);
|
2021-01-14 21:21:39 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
|
|
|
|
calmaOutHeader(rootDef, f);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write all contact cell definitions first
|
|
|
|
|
*/
|
|
|
|
|
if (CalmaContactArrays) calmaWriteContacts(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.
|
|
|
|
|
*/
|
2021-06-17 18:55:45 +02:00
|
|
|
calmaProcessDef(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)
|
|
|
|
|
calmaProcessDef(extraDef, f, FALSE);
|
|
|
|
|
else
|
|
|
|
|
TxError("Error: Cell %s is not defined in the output file!\n",
|
|
|
|
|
refname + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Finish up by outputting the end-of-library marker */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDLIB, CALMA_NODATA, f);
|
|
|
|
|
fflush(f);
|
|
|
|
|
good = !ferror(f);
|
|
|
|
|
|
|
|
|
|
/* See if any problems occurred */
|
2024-10-04 18:21:15 +02:00
|
|
|
if ((problems = (DBWFeedbackCount - oldCount)))
|
2017-04-25 14:41:48 +02:00
|
|
|
TxPrintf("%d problems occurred. See feedback entries.\n", problems);
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Destroy all contact cell definitions
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
if (CalmaContactArrays) calmaDelContacts();
|
|
|
|
|
|
2018-11-16 19:59:17 +01:00
|
|
|
HashFreeKill(&calmaLibHash);
|
2018-11-19 21:01:20 +01:00
|
|
|
HashKill(&calmaPrefixHash);
|
2021-06-17 18:40:21 +02:00
|
|
|
HashFreeKill(&calmaUndefHash);
|
2017-04-25 14:41:48 +02:00
|
|
|
return (good);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2018-04-18 19:57:26 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaDumpStructure --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2018-11-16 19:59:17 +01:00
|
|
|
* 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.
|
2018-04-18 19:57:26 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaDumpStructure(
|
|
|
|
|
CellDef *def,
|
|
|
|
|
FILE *outf,
|
|
|
|
|
HashTable *calmaDefHash,
|
|
|
|
|
char *filename)
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
2024-10-04 20:56:12 +02:00
|
|
|
int nbytes = -1, rtype = 0;
|
2019-12-19 23:28:06 +01:00
|
|
|
char *strname = NULL, *newnameptr;
|
2018-11-16 19:59:17 +01:00
|
|
|
HashEntry *he, *he2;
|
2018-04-18 19:57:26 +02:00
|
|
|
CellDef *edef;
|
2019-09-13 15:52:45 +02:00
|
|
|
char *prefix = NULL;
|
2018-04-18 19:57:26 +02:00
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRH(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 */
|
|
|
|
|
calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, outf);
|
2022-01-21 16:26:29 +01:00
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDate(*CalmaDateStamp, outf);
|
2020-12-15 20:52:30 +01:00
|
|
|
else
|
|
|
|
|
calmaOutDate(def->cd_timestamp, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
calmaOutDate(time((time_t *) 0), outf);
|
|
|
|
|
|
2021-06-17 18:40:21 +02:00
|
|
|
/* 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);
|
2019-09-13 15:52:45 +02:00
|
|
|
|
2021-06-17 18:40:21 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-13 15:52:45 +02:00
|
|
|
else
|
2021-06-17 18:40:21 +02:00
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
2019-09-13 15:52:45 +02:00
|
|
|
|
2018-04-18 19:57:26 +02:00
|
|
|
/* 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 */
|
2019-09-13 15:52:45 +02:00
|
|
|
if (*newnameptr != '0')
|
|
|
|
|
TxError("Structure %s defined redundantly in GDS\n", strname);
|
|
|
|
|
else
|
|
|
|
|
*newnameptr = '1';
|
2019-05-22 22:52:34 +02:00
|
|
|
/* To be considered: Should the structure be output more than once? */
|
2019-09-13 15:52:45 +02:00
|
|
|
calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
}
|
|
|
|
|
else if (!strcmp(strname, def->cd_name))
|
|
|
|
|
{
|
|
|
|
|
/* This is the top level cell being defined. Its name */
|
|
|
|
|
/* does not get modified. */
|
|
|
|
|
|
2019-09-13 15:52:45 +02:00
|
|
|
newnameptr = mallocMagic(strlen(strname) + 2);
|
|
|
|
|
sprintf(newnameptr, "1%s", strname);
|
|
|
|
|
calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-11-16 19:59:17 +01:00
|
|
|
/* 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. */
|
2018-04-18 19:57:26 +02:00
|
|
|
|
|
|
|
|
/* Check if the cell is defined in the database */
|
2018-11-16 19:59:17 +01:00
|
|
|
edef = DBCellLookDef(strname);
|
2018-04-18 19:57:26 +02:00
|
|
|
if (edef != NULL)
|
2018-11-16 19:59:17 +01:00
|
|
|
{
|
|
|
|
|
bool isAbstract, isReadOnly;
|
2021-06-17 18:40:21 +02:00
|
|
|
char *chklibname, *filenamesubbed = NULL;
|
2018-11-16 19:59:17 +01:00
|
|
|
|
|
|
|
|
/* Is view abstract? */
|
|
|
|
|
DBPropGet(edef, "LEFview", &isAbstract);
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
chklibname = DBPropGetString(edef, "GDS_FILE", &isReadOnly);
|
2018-11-16 19:59:17 +01:00
|
|
|
|
2021-06-17 18:40:21 +02:00
|
|
|
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))
|
2018-11-16 19:59:17 +01:00
|
|
|
{
|
|
|
|
|
/* Same library, so keep the cellname and mark the cell */
|
|
|
|
|
/* as having been written to GDS. */
|
|
|
|
|
|
2019-09-13 15:52:45 +02:00
|
|
|
newnameptr = mallocMagic(strlen(strname) + 2);
|
|
|
|
|
sprintf(newnameptr, "1%s", strname);
|
2018-11-16 19:59:17 +01:00
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find the unique library prefix and prepend it to the cell name */
|
|
|
|
|
|
2019-09-13 15:52:45 +02:00
|
|
|
if (prefix == NULL)
|
|
|
|
|
newnameptr = strname; /* Should never happen */
|
2018-11-16 19:59:17 +01:00
|
|
|
else
|
|
|
|
|
{
|
2020-04-23 22:03:29 +02:00
|
|
|
newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "1%s%s", prefix, strname);
|
2018-11-16 19:59:17 +01:00
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-17 18:40:21 +02:00
|
|
|
if (filenamesubbed) freeMagic(filenamesubbed);
|
2018-11-16 19:59:17 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find the unique library prefix and prepend it to the cell name */
|
|
|
|
|
|
2019-09-13 15:52:45 +02:00
|
|
|
if (prefix == NULL)
|
|
|
|
|
newnameptr = strname; /* Should never happen */
|
2018-11-16 19:59:17 +01:00
|
|
|
else
|
|
|
|
|
{
|
2020-04-23 22:03:29 +02:00
|
|
|
newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "1%s%s", prefix, strname);
|
2018-11-16 19:59:17 +01:00
|
|
|
HashSetValue(he, (char *)newnameptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-19 14:22:11 +02:00
|
|
|
calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
}
|
|
|
|
|
freeMagic(strname);
|
|
|
|
|
|
|
|
|
|
/* Read and output the structure until CALMA_ENDSTR, except */
|
|
|
|
|
/* for handling any AREF or SREF names, which need name */
|
|
|
|
|
/* checks. */
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
while (1)
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
|
|
|
|
int datatype;
|
|
|
|
|
|
|
|
|
|
READI2(nbytes);
|
2022-05-12 15:55:38 +02:00
|
|
|
if (FEOF(calmaInputFile))
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
|
|
|
|
/* Unexpected end-of-file */
|
2022-05-12 15:55:38 +02:00
|
|
|
FSEEK(calmaInputFile, -(CALMAHEADERLENGTH), SEEK_END);
|
2018-04-18 19:57:26 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2022-05-12 15:55:38 +02:00
|
|
|
rtype = FGETC(calmaInputFile);
|
|
|
|
|
datatype = FGETC(calmaInputFile);
|
2018-04-18 19:57:26 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2019-09-13 15:52:45 +02:00
|
|
|
calmaOutStringRecord(CALMA_SNAME, newnameptr + 1, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
}
|
|
|
|
|
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) +
|
2020-04-23 22:03:29 +02:00
|
|
|
strlen(prefix) + 8);
|
|
|
|
|
sprintf(newnameptr, "0%s%s", prefix, strname);
|
2018-04-18 19:57:26 +02:00
|
|
|
|
2019-09-13 15:52:45 +02:00
|
|
|
edef = DBCellLookDef(newnameptr + 1);
|
2018-04-18 19:57:26 +02:00
|
|
|
if (edef != NULL)
|
2020-04-23 22:03:29 +02:00
|
|
|
sprintf(newnameptr, "0%s%s[[0]]", prefix, strname);
|
2018-04-18 19:57:26 +02:00
|
|
|
HashSetValue(he, (char *)newnameptr);
|
2019-09-13 15:52:45 +02:00
|
|
|
calmaOutStringRecord(CALMA_SNAME, newnameptr + 1, outf);
|
2018-04-18 19:57:26 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
calmaOutRH(nbytes, rtype, datatype, outf);
|
|
|
|
|
nbytes -= 4;
|
|
|
|
|
|
|
|
|
|
/* Copy nbytes from input to output */
|
|
|
|
|
while (nbytes-- > 0)
|
|
|
|
|
{
|
|
|
|
|
int byte;
|
2022-05-12 15:55:38 +02:00
|
|
|
if ((byte = FGETC(calmaInputFile)) < 0)
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
|
|
|
|
TxError("End of file with %d bytes remaining to be read.\n",
|
|
|
|
|
nbytes);
|
|
|
|
|
while (nbytes-- > 0)
|
|
|
|
|
putc(0, outf); // zero-pad output
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
putc(byte, outf);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
syntaxerror:
|
|
|
|
|
/* Syntax error: skip to CALMA_ENDSTR */
|
|
|
|
|
calmaSkipTo(CALMA_ENDSTR);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaFullDump --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaFullDump(
|
|
|
|
|
CellDef *def,
|
|
|
|
|
FILETYPE fi,
|
|
|
|
|
FILE *outf,
|
|
|
|
|
char *filename)
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
2021-02-19 14:52:56 +01:00
|
|
|
int version, rval;
|
2021-06-17 18:40:21 +02:00
|
|
|
char *libname = NULL, *testlib, uniqlibname[4];
|
2020-04-23 22:03:29 +02:00
|
|
|
char *sptr, *viewopts;
|
|
|
|
|
bool isAbstract;
|
2018-04-18 19:57:26 +02:00
|
|
|
HashTable calmaDefHash;
|
2021-06-17 18:40:21 +02:00
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he, *he2;
|
2018-11-16 19:59:17 +01:00
|
|
|
|
2024-10-04 12:19:10 +02:00
|
|
|
static const int hdrSkip[] = { CALMA_FORMAT, CALMA_MASK, CALMA_ENDMASKS,
|
2018-04-18 19:57:26 +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,
|
2018-04-18 19:57:26 +02:00
|
|
|
CALMA_LIBSECUR, -1 };
|
|
|
|
|
|
|
|
|
|
HashInit(&calmaDefHash, 32, 0);
|
|
|
|
|
|
|
|
|
|
cifReadCellDef = def;
|
2022-05-12 15:55:38 +02:00
|
|
|
calmaInputFile = fi;
|
2018-04-18 19:57:26 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
calmaSkipSet(hdrSkip);
|
2018-11-16 19:59:17 +01:00
|
|
|
|
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
|
|
|
|
|
// file.
|
|
|
|
|
if (calmaParseUnits() == FALSE)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Library %s has incompatible database units!\n", libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 19:59:17 +01:00
|
|
|
// Record the GDS library so it will not be processed again.
|
|
|
|
|
he = HashFind(&calmaLibHash, filename);
|
2021-06-17 18:40:21 +02:00
|
|
|
if ((char *)HashGetValue(he) != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Library %s has already been processed\n", libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-11-19 21:01:20 +01:00
|
|
|
|
2020-04-23 22:03:29 +02:00
|
|
|
/* 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.
|
2018-11-19 21:01:20 +01:00
|
|
|
*/
|
2020-04-23 22:03:29 +02:00
|
|
|
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
viewopts = DBPropGetString(def, "LEFview", &isAbstract);
|
2020-04-23 22:03:29 +02:00
|
|
|
if ((!isAbstract) || (strcasecmp(viewopts, "no_prefix")))
|
2018-11-19 21:01:20 +01:00
|
|
|
{
|
2020-04-23 22:03:29 +02:00
|
|
|
/* 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
|
2021-02-19 14:52:56 +01:00
|
|
|
* 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.
|
2020-04-23 22:03:29 +02:00
|
|
|
*/
|
|
|
|
|
while (TRUE)
|
2018-11-19 21:01:20 +01:00
|
|
|
{
|
2021-02-19 14:52:56 +01:00
|
|
|
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);
|
2020-04-23 22:03:29 +02:00
|
|
|
uniqlibname[2] = '_';
|
|
|
|
|
uniqlibname[3] = '\0';
|
|
|
|
|
he2 = HashLookOnly(&calmaPrefixHash, uniqlibname);
|
|
|
|
|
if (he2 == NULL)
|
|
|
|
|
{
|
|
|
|
|
he2 = HashFind(&calmaPrefixHash, uniqlibname);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-11-19 21:01:20 +01:00
|
|
|
}
|
2020-04-23 22:03:29 +02:00
|
|
|
HashSetValue(he, StrDup(NULL, uniqlibname));
|
2018-11-16 19:59:17 +01:00
|
|
|
}
|
2020-04-23 22:03:29 +02:00
|
|
|
else
|
|
|
|
|
HashSetValue(he, StrDup(NULL, ""));
|
2018-11-16 19:59:17 +01:00
|
|
|
|
2022-05-13 02:43:30 +02:00
|
|
|
while (calmaDumpStructure(def, outf, &calmaDefHash, filename))
|
2018-04-18 19:57:26 +02:00
|
|
|
if (SigInterruptPending)
|
|
|
|
|
goto done;
|
|
|
|
|
calmaSkipExact(CALMA_ENDLIB);
|
|
|
|
|
|
|
|
|
|
done:
|
2021-06-17 18:40:21 +02:00
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-18 19:57:26 +02:00
|
|
|
HashFreeKill(&calmaDefHash);
|
|
|
|
|
if (libname != NULL) freeMagic(libname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteInitFunc --
|
|
|
|
|
*
|
|
|
|
|
* Filter function called on behalf of CalmaWrite() above.
|
|
|
|
|
* Responsible for setting the cif number of each cell to zero.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 to indicate that the search should continue.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Modify the calma numbers of the cells they are passed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWriteInitFunc(
|
|
|
|
|
CellDef *def)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
def->cd_client = (ClientData) 0;
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaProcessUse --
|
|
|
|
|
* calmaProcessDef --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaProcessUse(
|
|
|
|
|
CellUse *use, /* Process use->cu_def */
|
|
|
|
|
FILE *outf) /* Stream file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-07-16 14:55:46 +02:00
|
|
|
return (calmaProcessDef(use->cu_def, outf, FALSE));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaProcessDef(
|
|
|
|
|
CellDef *def, /* Output this def's children, then the def itself */
|
|
|
|
|
FILE *outf, /* Stream file */
|
|
|
|
|
bool do_library) /* If TRUE, output only children of def, but not def */
|
2017-04-25 14:41:48 +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;
|
2018-11-16 19:59:17 +01:00
|
|
|
HashEntry *he;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Skip if already output */
|
2024-10-21 10:13:23 +02:00
|
|
|
if ((int) CD2INT(def->cd_client) > 0)
|
2017-04-25 14:41:48 +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--);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Mark this cell */
|
2024-10-21 10:13:23 +02:00
|
|
|
def->cd_client = INT2CD(- (int) CD2INT(def->cd_client));
|
2017-04-25 14:41:48 +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))
|
2017-04-25 14:41:48 +02:00
|
|
|
return (0);
|
|
|
|
|
|
2021-11-08 16:25:38 +01:00
|
|
|
/*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2018-04-18 19:57:26 +02:00
|
|
|
DBPropGet(def, "LEFview", &isAbstract);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBPropGet(def, "GDS_START", &hasContent);
|
2020-04-23 21:08:56 +02:00
|
|
|
DBPropGet(def, "GDS_END", &hasGDSEnd);
|
2023-03-07 17:16:49 +01:00
|
|
|
DBPropGet(def, "CIFhier", &needHier);
|
|
|
|
|
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
filename = DBPropGetString(def, "GDS_FILE", &isReadOnly);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-10-13 15:47:54 +02:00
|
|
|
/* 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);
|
|
|
|
|
|
2021-04-19 18:53:32 +02:00
|
|
|
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",
|
2021-04-19 18:53:32 +02:00
|
|
|
def->cd_name);
|
2022-11-02 14:40:20 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-19 18:53:32 +02:00
|
|
|
|
2020-10-13 15:47:54 +02:00
|
|
|
/*
|
|
|
|
|
* Output the definitions for any of our descendants that have
|
|
|
|
|
* not already been output. Numbers are assigned to the subcells
|
2021-01-20 21:00:19 +01:00
|
|
|
* 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.
|
2020-10-13 15:47:54 +02:00
|
|
|
*/
|
2021-01-20 21:00:19 +01:00
|
|
|
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, calmaProcessUse, (ClientData) outf);
|
|
|
|
|
|
|
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
CIFHierWriteDisable = hierWrite;
|
|
|
|
|
CIFArrayWriteDisable = arrayWrite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result != 0)
|
2021-01-20 21:00:19 +01:00
|
|
|
return 1;
|
2023-03-07 17:16:49 +01:00
|
|
|
}
|
2020-10-13 15:47:54 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (isReadOnly && hasContent)
|
|
|
|
|
{
|
2018-11-16 19:59:17 +01:00
|
|
|
char *buffer, *offptr, *retfilename;
|
2017-04-25 14:41:48 +02:00
|
|
|
size_t defsize, numbytes;
|
2021-07-30 19:45:03 +02:00
|
|
|
off_t cellstart, cellend, structstart;
|
2017-04-25 14:41:48 +02:00
|
|
|
dlong cval;
|
2022-05-05 23:40:56 +02:00
|
|
|
int namelen;
|
2022-05-12 15:55:38 +02:00
|
|
|
FILETYPE fi;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-10-24 16:36:57 +02:00
|
|
|
/* Give some feedback to the user */
|
|
|
|
|
TxPrintf(" Copying output for cell %s from %s\n", def->cd_name, filename);
|
|
|
|
|
|
2022-05-12 15:55:38 +02:00
|
|
|
/* Use PaZOpen() so the paths searched are the same as were */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* searched to find the .mag file that indicated this GDS file. */
|
|
|
|
|
|
2025-02-12 22:28:43 +01:00
|
|
|
fi = PaZOpen(filename, "r", "", Path, CellLibPath, &retfilename);
|
|
|
|
|
|
|
|
|
|
/* Check if file may have been compressed */
|
|
|
|
|
if (fi == NULL)
|
|
|
|
|
fi = PaZOpen(filename, "r", ".gz", Path, CellLibPath, &retfilename);
|
|
|
|
|
|
2017-04-25 14:41:48 +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. */
|
|
|
|
|
|
2020-12-03 16:35:54 +01:00
|
|
|
DBPropGet((def->cd_parents->cu_parent == NULL) ? def :
|
|
|
|
|
def->cd_parents->cu_parent, "GDS_FILE", &isReadOnly);
|
2018-04-24 15:29:05 +02:00
|
|
|
if (isReadOnly)
|
2021-01-15 18:40:20 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
def->cd_flags |= CDVENDORGDS;
|
2021-01-15 18:40:20 +01:00
|
|
|
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",
|
2025-02-12 22:28:43 +01:00
|
|
|
filename, def->cd_name);
|
2021-01-15 18:40:20 +01:00
|
|
|
|
|
|
|
|
if (CalmaAllowUndefined)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-04-23 21:08:56 +02:00
|
|
|
else if (isAbstract || (!hasGDSEnd))
|
2018-04-18 19:57:26 +02:00
|
|
|
{
|
|
|
|
|
/* 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. */
|
|
|
|
|
|
2018-11-16 19:59:17 +01:00
|
|
|
he = HashLookOnly(&calmaLibHash, retfilename);
|
|
|
|
|
if (he == NULL)
|
2019-06-07 20:13:50 +02:00
|
|
|
calmaFullDump(def, fi, outf, retfilename);
|
2018-11-16 20:43:41 +01:00
|
|
|
|
2022-05-12 15:55:38 +02:00
|
|
|
FCLOSE(fi);
|
2018-04-18 19:57:26 +02:00
|
|
|
def->cd_flags |= CDVENDORGDS;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
cval = DBPropGetDouble(def, "GDS_END", NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
cellend = (off_t)cval;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
cval = DBPropGetDouble(def, "GDS_BEGIN", &oldStyle);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!oldStyle)
|
|
|
|
|
{
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
cval = DBPropGetDouble(def, "GDS_START", NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Write our own header and string name, to ensure */
|
|
|
|
|
/* that the magic cell name and GDS name match. */
|
|
|
|
|
|
|
|
|
|
/* Output structure header */
|
|
|
|
|
calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, outf);
|
2022-01-21 16:26:29 +01:00
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDate(*CalmaDateStamp, outf);
|
2020-12-15 20:52:30 +01:00
|
|
|
else
|
|
|
|
|
calmaOutDate(def->cd_timestamp, outf);
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaOutDate(time((time_t *) 0), outf);
|
|
|
|
|
|
|
|
|
|
/* Name structure the same as the magic cellname */
|
|
|
|
|
calmaOutStructName(CALMA_STRNAME, def, outf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cellstart = (off_t)cval;
|
2021-07-30 19:45:03 +02:00
|
|
|
|
|
|
|
|
/* 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 - (off_t)(strlen(def->cd_name));
|
|
|
|
|
if (strlen(def->cd_name) & 0x1) structstart--;
|
|
|
|
|
structstart -= 2;
|
|
|
|
|
|
2022-05-12 15:55:38 +02:00
|
|
|
FSEEK(fi, structstart, SEEK_SET);
|
2021-07-30 19:45:03 +02:00
|
|
|
|
|
|
|
|
/* Read the structure name and check against the CellDef name */
|
|
|
|
|
defsize = (size_t)(cellstart - structstart);
|
|
|
|
|
buffer = (char *)mallocMagic(defsize + 1);
|
2024-10-08 09:01:17 +02:00
|
|
|
numbytes = magicFREAD(buffer, sizeof(char), (size_t)defsize, fi);
|
2021-07-30 19:45:03 +02:00
|
|
|
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);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cellend < cellstart) /* Sanity check */
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Bad vendor GDS file reference!\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
2021-07-30 19:45:03 +02:00
|
|
|
else if (isReadOnly)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-07-30 19:45:03 +02:00
|
|
|
/* Important note: mallocMagic() is limited to size integer. */
|
|
|
|
|
/* This will fail on a structure larger than ~2GB. */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
defsize = (size_t)(cellend - cellstart);
|
|
|
|
|
buffer = (char *)mallocMagic(defsize);
|
2018-04-18 19:57:26 +02:00
|
|
|
|
2024-10-08 09:01:17 +02:00
|
|
|
numbytes = magicFREAD(buffer, sizeof(char), (size_t)defsize, fi);
|
2021-07-30 19:45:03 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (numbytes == defsize)
|
|
|
|
|
{
|
2021-07-30 19:45:03 +02:00
|
|
|
/* 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)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-07-30 19:45:03 +02:00
|
|
|
TxError("Calma output error: Structure end definition not found"
|
|
|
|
|
" at GDS file position %"DLONG_PREFIX"d\n", cellend);
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("Calma output error: Can't write cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
2021-07-30 19:45:03 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
numbytes = fwrite(buffer, sizeof(char), (size_t)defsize, outf);
|
|
|
|
|
if (numbytes <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Can't write cell from "
|
|
|
|
|
"vendor GDS. Using magic's internal "
|
|
|
|
|
"definition\n");
|
|
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Calma output error: Can't read cell from vendor GDS."
|
|
|
|
|
" Using magic's internal definition\n");
|
2021-07-30 19:45:03 +02:00
|
|
|
|
|
|
|
|
/* 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);
|
2017-04-25 14:41:48 +02:00
|
|
|
isReadOnly = FALSE;
|
|
|
|
|
}
|
|
|
|
|
freeMagic(buffer);
|
|
|
|
|
}
|
2022-05-12 15:55:38 +02:00
|
|
|
FCLOSE(fi);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-24 16:36:57 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Give some feedback to the user */
|
|
|
|
|
TxPrintf(" Generating output for cell %s\n", def->cd_name);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
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);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Output this cell definition from the Magic database */
|
|
|
|
|
if (!isReadOnly)
|
2020-07-16 14:55:46 +02:00
|
|
|
if (!do_library)
|
2023-03-07 17:16:49 +01:00
|
|
|
{
|
|
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
hierWrite = CIFHierWriteDisable;
|
|
|
|
|
arrayWrite = CIFArrayWriteDisable;
|
|
|
|
|
|
|
|
|
|
CIFHierWriteDisable = FALSE;
|
|
|
|
|
CIFArrayWriteDisable = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-16 14:55:46 +02:00
|
|
|
calmaOutFunc(def, outf, &TiPlaneRect);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-03-07 17:16:49 +01:00
|
|
|
if (needHier)
|
|
|
|
|
{
|
|
|
|
|
CIFHierWriteDisable = hierWrite;
|
|
|
|
|
CIFArrayWriteDisable = arrayWrite;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 18:40:20 +01:00
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-22 03:04:30 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* compport ---
|
|
|
|
|
*
|
|
|
|
|
* Compare two port labels by port index. Sorting function used with
|
|
|
|
|
* qsort().
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
compport(const void *one, const void *two)
|
|
|
|
|
{
|
|
|
|
|
PortLabel *pl1 = (PortLabel *)one;
|
|
|
|
|
PortLabel *pl2 = (PortLabel *)two;
|
|
|
|
|
|
|
|
|
|
if (pl1->pl_port < pl2->pl_port)
|
|
|
|
|
return -1;
|
|
|
|
|
if (pl1->pl_port > pl2->pl_port)
|
|
|
|
|
return 1;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutFunc --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaOutFunc(
|
|
|
|
|
CellDef *def, /* Pointer to cell def to be written */
|
|
|
|
|
FILE *f, /* Open output file */
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *cliprect)/* Area to clip to (used for contact cells),
|
2017-04-25 14:41:48 +02:00
|
|
|
* in CIF/GDS coordinates.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Label *lab;
|
|
|
|
|
CIFLayer *layer;
|
|
|
|
|
Rect bigArea;
|
|
|
|
|
int type;
|
|
|
|
|
int dbunits;
|
|
|
|
|
calmaOutputStruct cos;
|
2022-10-18 20:14:22 +02:00
|
|
|
bool propfound;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
PropertyRecord *proprec;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
cos.f = f;
|
|
|
|
|
cos.area = (cliprect == &TiPlaneRect) ? NULL : cliprect;
|
2021-11-16 16:58:18 +01:00
|
|
|
cos.type = -1;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Output structure begin */
|
|
|
|
|
calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, f);
|
2022-01-21 16:26:29 +01:00
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDate(*CalmaDateStamp, f);
|
2020-12-15 20:52:30 +01:00
|
|
|
else
|
|
|
|
|
calmaOutDate(def->cd_timestamp, f);
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaOutDate(time((time_t *) 0), f);
|
|
|
|
|
|
|
|
|
|
/* Output structure name */
|
|
|
|
|
calmaOutStructName(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, calmaWriteUseFunc, (ClientData) f);
|
|
|
|
|
|
|
|
|
|
/* Output all the tiles associated with this cell; skip temporary layers */
|
|
|
|
|
GEO_EXPAND(&def->cd_bbox, CIFCurStyle->cs_radius, &bigArea);
|
2022-10-18 20:14:22 +02:00
|
|
|
|
|
|
|
|
/* Include any fixed bounding box as part of the area to process, */
|
|
|
|
|
/* in case the fixed bounding box is larger than the geometry. */
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = DBPropGet(def, "FIXED_BBOX", &propfound);
|
2022-10-18 20:14:22 +02:00
|
|
|
if (propfound)
|
|
|
|
|
{
|
|
|
|
|
Rect bbox;
|
|
|
|
|
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
bbox.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
bbox.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
bbox.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
bbox.r_ytop = proprec->prop_value.prop_integer[3];
|
2022-10-18 20:14:22 +02:00
|
|
|
GeoInclude(&bbox, &bigArea);
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
}
|
2022-10-18 20:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
CIFErrorDef = def;
|
2020-11-12 16:34:27 +01:00
|
|
|
CIFGen(def, def, &bigArea, CIFPlanes, &DBAllTypeBits, TRUE, TRUE, FALSE,
|
|
|
|
|
(ClientData)f);
|
2023-03-07 17:16:49 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!CIFHierWriteDisable)
|
|
|
|
|
CIFGenSubcells(def, &bigArea, CIFPlanes);
|
|
|
|
|
if (!CIFArrayWriteDisable)
|
|
|
|
|
CIFGenArrays(def, &bigArea, CIFPlanes);
|
|
|
|
|
|
|
|
|
|
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;
|
2021-11-16 16:58:18 +01:00
|
|
|
cos.type = type;
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaPaintLayerNumber = layer->cl_calmanum;
|
|
|
|
|
calmaPaintLayerType = layer->cl_calmatype;
|
|
|
|
|
|
2021-11-16 16:58:18 +01:00
|
|
|
if (layer->cl_flags & CIF_LABEL)
|
|
|
|
|
DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
|
|
|
|
|
cliprect, &CIFSolidBits, calmaPaintLabelFunc,
|
|
|
|
|
(ClientData) &cos);
|
|
|
|
|
else
|
|
|
|
|
DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
|
|
|
|
|
cliprect, &CIFSolidBits, (CalmaMergeTiles) ?
|
|
|
|
|
calmaMergePaintFunc : calmaWritePaintFunc,
|
|
|
|
|
(ClientData) &cos);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-27 16:57:51 +02:00
|
|
|
/* 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. */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (CalmaDoLabels)
|
2018-08-27 16:57:51 +02:00
|
|
|
{
|
2023-03-22 03:04:30 +01:00
|
|
|
int i, ltype, numports = 0;
|
|
|
|
|
LabelList *ll = NULL, *newll;
|
2018-08-27 16:57:51 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (lab = def->cd_labels; lab; lab = lab->lab_next)
|
2018-08-27 16:57:51 +02:00
|
|
|
{
|
2022-12-21 23:49:43 +01:00
|
|
|
if ((lab->lab_flags & PORT_DIR_MASK) == 0)
|
2018-08-27 18:23:10 +02:00
|
|
|
{
|
2022-12-21 23:49:43 +01:00
|
|
|
ltype = CIFCurStyle->cs_labelLayer[lab->lab_type];
|
|
|
|
|
type = ltype;
|
|
|
|
|
calmaWriteLabelFunc(lab, ltype, type, f);
|
2018-08-27 18:23:10 +02:00
|
|
|
}
|
2018-08-27 16:57:51 +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++;
|
2018-08-27 16:57:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-06-04 16:01:33 +02:00
|
|
|
if (ll != NULL)
|
2023-03-22 03:04:30 +01:00
|
|
|
{
|
|
|
|
|
/* 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;
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
|
|
|
freeMagic1(&mm1, ll);
|
2023-03-22 03:04:30 +01:00
|
|
|
ll = ll->ll_next;
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2023-03-22 03:04:30 +01:00
|
|
|
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)
|
|
|
|
|
calmaWriteLabelFunc(lab, ltype, type, f);
|
|
|
|
|
}
|
|
|
|
|
freeMagic(pllist);
|
|
|
|
|
}
|
2018-08-27 16:57:51 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* End of structure */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDSTR, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaIsUseNameDefault --
|
|
|
|
|
*
|
|
|
|
|
* Determine if this use name is not default; that is, it is not the name
|
|
|
|
|
* of the cell def followed by an underscore and a use index number. If
|
|
|
|
|
* it is not default, then we want to write out the use name as a property
|
|
|
|
|
* in the GDS stream file so that we can recover the name when the file is
|
|
|
|
|
* read back into magic.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the cell use ID is a default name; FALSE if not.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaIsUseNameDefault(
|
|
|
|
|
char *defName,
|
|
|
|
|
char *useName)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int idx, slen;
|
|
|
|
|
char *sptr;
|
|
|
|
|
|
|
|
|
|
if (useName == NULL) return TRUE;
|
|
|
|
|
slen = strlen(defName);
|
|
|
|
|
if (!strncmp(defName, useName, slen))
|
|
|
|
|
{
|
|
|
|
|
sptr = useName + slen;
|
|
|
|
|
if (*sptr != '_') return FALSE;
|
|
|
|
|
else sptr++;
|
|
|
|
|
if (sscanf(sptr, "%d", &idx) != 1) return FALSE;
|
|
|
|
|
else return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteUseFunc --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaWriteUseFunc(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
FILE *f)
|
2017-04-25 14:41:48 +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 calmaOutR8().
|
|
|
|
|
*/
|
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;
|
2017-04-25 14:41:48 +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 */
|
|
|
|
|
calmaOutRH(4, CALMA_SREF, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructName(CALMA_SNAME, use->cu_def, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2(stransflags, f);
|
|
|
|
|
|
|
|
|
|
/* Rotation if there is one */
|
|
|
|
|
if (whichangle)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
|
2025-01-06 17:12:11 +01:00
|
|
|
calmaOut8((char *)whichangle, f);
|
2017-04-25 14:41:48 +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;
|
|
|
|
|
calmaOutRH(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(xxlate, f);
|
|
|
|
|
calmaOutI4(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Is it an array? */
|
|
|
|
|
isArray = (topx > 0 || topy > 0);
|
|
|
|
|
rectype = isArray ? CALMA_AREF : CALMA_SREF;
|
|
|
|
|
|
|
|
|
|
/* Structure reference */
|
|
|
|
|
calmaOutRH(4, rectype, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructName(CALMA_SNAME, use->cu_def, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2(stransflags, f);
|
|
|
|
|
|
|
|
|
|
/* Rotation if there is one */
|
|
|
|
|
if (whichangle)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
|
2025-01-06 17:12:11 +01:00
|
|
|
calmaOut8((char *)whichangle, f);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If array, number of columns and rows in the array */
|
|
|
|
|
if (isArray)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRH(8, CALMA_COLROW, CALMA_I2, f);
|
|
|
|
|
cols = topx + 1;
|
|
|
|
|
rows = topy + 1;
|
|
|
|
|
calmaOutI2(cols, f);
|
|
|
|
|
calmaOutI2(rows, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Translation */
|
|
|
|
|
xxlate = t->t_c * calmaWriteScale;
|
|
|
|
|
yxlate = t->t_f * calmaWriteScale;
|
|
|
|
|
hdrsize = isArray ? 28 : 12;
|
|
|
|
|
calmaOutRH(hdrsize, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(xxlate, f);
|
|
|
|
|
calmaOutI4(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;
|
|
|
|
|
calmaOutI4(p2.p_x, f);
|
|
|
|
|
calmaOutI4(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;
|
|
|
|
|
calmaOutI4(p2.p_x, f);
|
|
|
|
|
calmaOutI4(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) */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-11-11 17:20:34 +01:00
|
|
|
calmaOutRH(6, CALMA_PROPATTR, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(CALMA_PROP_USENAME_STD, f);
|
|
|
|
|
calmaOutStringRecord(CALMA_PROPVALUE, use->cu_id, f);
|
2017-04-25 14:41:48 +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);
|
|
|
|
|
calmaOutRH(6, CALMA_PROPATTR, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(CALMA_PROP_ARRAY_LIMITS, f);
|
|
|
|
|
calmaOutStringRecord(CALMA_PROPVALUE, arraystr, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaOutStructName(
|
|
|
|
|
int type,
|
|
|
|
|
CellDef *def,
|
|
|
|
|
FILE *f)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-12-19 23:28:06 +01:00
|
|
|
char *defname;
|
2017-04-25 14:41:48 +02:00
|
|
|
unsigned char c;
|
|
|
|
|
char *cp;
|
|
|
|
|
int calmanum;
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *table;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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++)
|
2017-04-25 14:41:48 +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. . . */
|
|
|
|
|
}
|
2019-12-19 23:28:06 +01:00
|
|
|
if ((!(CIFCurStyle->cs_flags & CWF_STRING_LIMIT)) ||
|
|
|
|
|
(cp <= def->cd_name + CALMANAMELENGTH))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* Yes, it's legal: use it */
|
2019-12-19 23:28:06 +01:00
|
|
|
defname = StrDup(NULL, def->cd_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Bad name: use XXXXXcalmaNum */
|
|
|
|
|
bad:
|
2024-10-21 10:13:23 +02:00
|
|
|
calmanum = (int) CD2INT(def->cd_client);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (calmanum < 0) calmanum = -calmanum;
|
2019-12-19 23:28:06 +01:00
|
|
|
defname = (char *)mallocMagic(32);
|
2017-04-25 14:41:48 +02:00
|
|
|
(void) sprintf(defname, "XXXXX%d", calmanum);
|
|
|
|
|
TxError("Warning: string in output unprintable; changed to \'%s\'\n",
|
|
|
|
|
defname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calmaOutStringRecord(type, defname, f);
|
2019-12-19 23:28:06 +01:00
|
|
|
freeMagic(defname);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Added by NP 8/21/2004 */
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaGetContactCell --
|
|
|
|
|
*
|
|
|
|
|
* This routine creates [if it hasn't been created yet] a cell definition
|
|
|
|
|
* containing the given TileType. Cellname is "$$" + layer1_name + "_" +
|
|
|
|
|
* layer2_name... + "$$". Cellname contains the short name of all the
|
|
|
|
|
* residues of the layer "type".
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns new celldef it doesn't exist else created one.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* New celldef created specially for contact type if it does not exist.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaGetContactCell(
|
|
|
|
|
TileType type, /* magic contact tile type */
|
|
|
|
|
bool lookOnly) /* if true, don't generate any new cells */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TileType j;
|
|
|
|
|
char contactCellName[100];
|
|
|
|
|
TileTypeBitMask *rMask = DBResidueMask(type);
|
|
|
|
|
CellDef *def;
|
|
|
|
|
bool first = TRUE;
|
|
|
|
|
|
|
|
|
|
strcpy(contactCellName, "$$");
|
|
|
|
|
for (j = TT_SPACE + 1; j < DBNumUserLayers; j++)
|
|
|
|
|
if (TTMaskHasType(rMask, j))
|
|
|
|
|
{
|
|
|
|
|
/* Cellname starts with "$$" to make it diffrent from
|
|
|
|
|
* other database cells, and to be compatible with a
|
|
|
|
|
* number of other EDA tools.
|
|
|
|
|
*/
|
|
|
|
|
if (!first)
|
|
|
|
|
strcat(contactCellName, "_");
|
|
|
|
|
else
|
|
|
|
|
first = FALSE;
|
|
|
|
|
strcat(contactCellName, DBTypeShortName(j));
|
|
|
|
|
}
|
|
|
|
|
strcat(contactCellName, "$$");
|
|
|
|
|
|
|
|
|
|
def = DBCellLookDef(contactCellName);
|
|
|
|
|
if ((def == (CellDef *) NULL) && (lookOnly == FALSE))
|
|
|
|
|
{
|
2020-03-21 17:40:35 +01:00
|
|
|
def = DBCellNewDef(contactCellName);
|
2017-04-25 14:41:48 +02:00
|
|
|
def->cd_flags &= ~(CDMODIFIED|CDGETNEWSTAMP);
|
|
|
|
|
def->cd_flags |= CDAVAILABLE;
|
|
|
|
|
}
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CalmaGenerateArray --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
CalmaGenerateArray(
|
|
|
|
|
FILE *f, /* GDS output file */
|
|
|
|
|
TileType type, /* Magic tile type of contact */
|
|
|
|
|
int llx,
|
|
|
|
|
int lly, /* Lower-left hand coordinate of the array
|
2017-04-25 14:41:48 +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 */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *child; /* Cell definition of the contact cell */
|
|
|
|
|
int xxlate, yxlate;
|
|
|
|
|
|
|
|
|
|
child = calmaGetContactCell(type, TRUE);
|
|
|
|
|
if (child == NULL) return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Structure reference */
|
|
|
|
|
calmaOutRH(4, CALMA_AREF, CALMA_NODATA, f);
|
|
|
|
|
calmaOutStructName(CALMA_SNAME, child, f);
|
|
|
|
|
|
|
|
|
|
/* Transformation flags */
|
|
|
|
|
calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2(0, f);
|
|
|
|
|
|
|
|
|
|
/* Number of columns and rows in the array */
|
|
|
|
|
calmaOutRH(8, CALMA_COLROW, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(cols, f);
|
|
|
|
|
calmaOutI2(rows, f);
|
|
|
|
|
|
|
|
|
|
/* Translation */
|
|
|
|
|
xxlate = llx * calmaPaintScale;
|
|
|
|
|
yxlate = lly * calmaPaintScale;
|
|
|
|
|
calmaOutRH(28, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(xxlate, f);
|
|
|
|
|
calmaOutI4(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* Column reference point */
|
|
|
|
|
calmaOutI4(xxlate + pitch * cols * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4(yxlate, f);
|
|
|
|
|
|
|
|
|
|
/* Row reference point */
|
|
|
|
|
calmaOutI4(xxlate, f);
|
|
|
|
|
calmaOutI4(yxlate + pitch * rows * calmaPaintScale, f);
|
|
|
|
|
|
|
|
|
|
/* End of AREF element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Added by NP 8/22/2004 */
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteContacts --
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
calmaWriteContacts(
|
|
|
|
|
FILE *f)
|
2017-04-25 14:41:48 +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. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
|
|
|
|
/* calmaOutFunc is also in CIF/GDS units. */
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
calmaOutFunc(def, f, &cliprect);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CalmaContactArrays = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaDelContacts --
|
|
|
|
|
*
|
|
|
|
|
* This routine removes all cell definitions generated by
|
|
|
|
|
* calmaWriteContacts().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Removes contact cell defs from the database.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaDelContacts(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TileType type;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
|
|
|
|
|
for (type = TT_SPACE + 1; type < DBNumUserLayers; type++)
|
|
|
|
|
if (DBIsContact(type))
|
|
|
|
|
{
|
|
|
|
|
def = calmaGetContactCell(type, TRUE);
|
|
|
|
|
if (def != (CellDef *)NULL)
|
|
|
|
|
DBCellDeleteDef(def);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* calmaAddSegment ---
|
|
|
|
|
*
|
|
|
|
|
* Process a new polygon edge, inserting it into a polygon record as
|
|
|
|
|
* required. If the edge is between a GDS layer and TT_SPACE, then
|
|
|
|
|
* we insert a point record. If the edge is between two tiles of the
|
|
|
|
|
* same layer, then we insert a tile record.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return 1 if an internal segment was generated, 0 if an external
|
|
|
|
|
* segment was generate. On error, return -1 (failure to find a
|
|
|
|
|
* connecting point; this shouldn't happen).
|
|
|
|
|
*
|
|
|
|
|
* Returns the current segment in the original pointer position (1st
|
|
|
|
|
* argument). If segments are added in counterclockwise order, then
|
|
|
|
|
* this should be most efficient.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
2020-05-23 23:13:14 +02:00
|
|
|
* May allocate memory.
|
2017-04-25 14:41:48 +02:00
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaAddSegment(
|
|
|
|
|
LinkedBoundary **lbptr,
|
|
|
|
|
bool poly_edge,
|
|
|
|
|
int p1x,
|
|
|
|
|
int p1y,
|
|
|
|
|
int p2x,
|
|
|
|
|
int p2y)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
LinkedBoundary *newseg, *curseg, *stopseg;
|
|
|
|
|
bool startmatch = FALSE;
|
|
|
|
|
bool endmatch = FALSE;
|
|
|
|
|
|
|
|
|
|
stopseg = NULL;
|
|
|
|
|
for (curseg = *lbptr; curseg != stopseg; curseg = curseg->lb_next)
|
|
|
|
|
{
|
|
|
|
|
stopseg = *lbptr;
|
|
|
|
|
if (curseg->lb_type == LB_INIT)
|
|
|
|
|
{
|
|
|
|
|
if ((p1x == curseg->lb_start.p_x) && (p1y == curseg->lb_start.p_y))
|
|
|
|
|
startmatch = TRUE;
|
|
|
|
|
|
|
|
|
|
if ((p2x == curseg->lb_next->lb_start.p_x) &&
|
|
|
|
|
(p2y == curseg->lb_next->lb_start.p_y))
|
|
|
|
|
endmatch = TRUE;
|
|
|
|
|
|
|
|
|
|
if (startmatch && endmatch)
|
|
|
|
|
{
|
|
|
|
|
/* Segment completes this edge */
|
|
|
|
|
curseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
|
|
|
|
|
*lbptr = curseg;
|
|
|
|
|
return (int)curseg->lb_type;
|
|
|
|
|
}
|
|
|
|
|
else if (startmatch || endmatch)
|
|
|
|
|
{
|
|
|
|
|
/* Insert a new segment after curseg */
|
|
|
|
|
newseg = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
|
|
|
|
|
newseg->lb_next = curseg->lb_next;
|
|
|
|
|
curseg->lb_next = newseg;
|
|
|
|
|
|
|
|
|
|
if (startmatch)
|
|
|
|
|
{
|
|
|
|
|
newseg->lb_type = curseg->lb_type;
|
|
|
|
|
curseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
|
|
|
|
|
newseg->lb_start.p_x = p2x;
|
|
|
|
|
newseg->lb_start.p_y = p2y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
|
|
|
|
|
newseg->lb_start.p_x = p1x;
|
|
|
|
|
newseg->lb_start.p_y = p1y;
|
|
|
|
|
}
|
|
|
|
|
curseg = newseg;
|
|
|
|
|
*lbptr = curseg;
|
|
|
|
|
return (int)curseg->lb_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1; /* This shouldn't happen, but isn't fatal. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* calmaRemoveDegenerate ---
|
|
|
|
|
*
|
|
|
|
|
* This routine takes lists of polygons and removes any degenerate
|
|
|
|
|
* segments (those that backtrack on themselves) from each one.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Deallocates memory for any segments that are removed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaRemoveDegenerate(
|
|
|
|
|
BoundaryTop *blist)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
bool segfound;
|
|
|
|
|
LinkedBoundary *stopseg, *curseg, *lastseg;
|
|
|
|
|
BoundaryTop *bounds;
|
|
|
|
|
|
|
|
|
|
for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
{
|
|
|
|
|
segfound = TRUE;
|
|
|
|
|
while (segfound)
|
|
|
|
|
{
|
|
|
|
|
segfound = FALSE;
|
|
|
|
|
stopseg = NULL;
|
|
|
|
|
for (lastseg = bounds->bt_first; lastseg != stopseg;)
|
|
|
|
|
{
|
|
|
|
|
stopseg = bounds->bt_first;
|
|
|
|
|
curseg = lastseg->lb_next;
|
|
|
|
|
|
|
|
|
|
if (GEO_SAMEPOINT(curseg->lb_start,
|
|
|
|
|
curseg->lb_next->lb_next->lb_start))
|
|
|
|
|
{
|
|
|
|
|
segfound = TRUE;
|
|
|
|
|
lastseg->lb_next = curseg->lb_next->lb_next;
|
|
|
|
|
|
|
|
|
|
freeMagic(curseg->lb_next);
|
|
|
|
|
freeMagic(curseg);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Make sure record doesn't point to a free'd segment */
|
|
|
|
|
bounds->bt_first = lastseg;
|
|
|
|
|
bounds->bt_points -= 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lastseg = lastseg->lb_next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* calmaRemoveColinear ---
|
|
|
|
|
*
|
|
|
|
|
* This routine takes lists of polygons and removes any redundant
|
|
|
|
|
* (colinear) points.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Deallocates memory for any segments that are removed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaRemoveColinear(
|
|
|
|
|
BoundaryTop *blist)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
LinkedBoundary *stopseg, *curseg, *lastseg;
|
|
|
|
|
BoundaryTop *bounds;
|
|
|
|
|
|
|
|
|
|
for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
{
|
|
|
|
|
stopseg = NULL;
|
|
|
|
|
for (lastseg = bounds->bt_first; lastseg != stopseg;)
|
|
|
|
|
{
|
|
|
|
|
stopseg = bounds->bt_first;
|
|
|
|
|
curseg = lastseg->lb_next;
|
|
|
|
|
|
|
|
|
|
if (((lastseg->lb_start.p_x == curseg->lb_start.p_x) &&
|
|
|
|
|
(lastseg->lb_start.p_x == curseg->lb_next->lb_start.p_x)) ||
|
|
|
|
|
((lastseg->lb_start.p_y == curseg->lb_start.p_y) &&
|
|
|
|
|
(lastseg->lb_start.p_y == curseg->lb_next->lb_start.p_y)))
|
|
|
|
|
{
|
|
|
|
|
lastseg->lb_next = curseg->lb_next;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Make sure record doesn't point to a free'd segment */
|
|
|
|
|
if (curseg == bounds->bt_first) bounds->bt_first = lastseg;
|
|
|
|
|
|
|
|
|
|
freeMagic(curseg);
|
|
|
|
|
bounds->bt_points--;
|
|
|
|
|
}
|
|
|
|
|
else if ((lastseg->lb_start.p_x != curseg->lb_start.p_x) &&
|
|
|
|
|
(lastseg->lb_start.p_y != curseg->lb_start.p_y) &&
|
|
|
|
|
(curseg->lb_start.p_x != curseg->lb_next->lb_start.p_x) &&
|
|
|
|
|
(curseg->lb_start.p_y != curseg->lb_next->lb_start.p_y))
|
|
|
|
|
{
|
|
|
|
|
/* Check colinearity of non-Manhattan edges */
|
|
|
|
|
int delx1, dely1, delx2, dely2, gcf;
|
|
|
|
|
|
|
|
|
|
delx1 = curseg->lb_start.p_x - lastseg->lb_start.p_x;
|
|
|
|
|
dely1 = curseg->lb_start.p_y - lastseg->lb_start.p_y;
|
|
|
|
|
delx2 = curseg->lb_next->lb_start.p_x - curseg->lb_start.p_x;
|
|
|
|
|
dely2 = curseg->lb_next->lb_start.p_y - curseg->lb_start.p_y;
|
|
|
|
|
|
|
|
|
|
if ((delx1 != delx2) || (dely1 != dely2))
|
|
|
|
|
{
|
|
|
|
|
gcf = FindGCF(delx1, dely1);
|
|
|
|
|
if (gcf > 1)
|
|
|
|
|
{
|
|
|
|
|
delx1 /= gcf;
|
|
|
|
|
dely1 /= gcf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((delx1 != delx2) || (dely1 != dely2))
|
|
|
|
|
{
|
|
|
|
|
gcf = FindGCF(delx2, dely2);
|
|
|
|
|
if (gcf > 1)
|
|
|
|
|
{
|
|
|
|
|
delx2 /= gcf;
|
|
|
|
|
dely2 /= gcf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((delx1 == delx2) && (dely1 == dely2))
|
|
|
|
|
{
|
|
|
|
|
lastseg->lb_next = curseg->lb_next;
|
|
|
|
|
if (curseg == bounds->bt_first) bounds->bt_first = lastseg;
|
|
|
|
|
freeMagic(curseg);
|
|
|
|
|
bounds->bt_points--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lastseg = lastseg->lb_next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lastseg = lastseg->lb_next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* calmaMergeSegments ---
|
|
|
|
|
*
|
|
|
|
|
* Once a tile has been disassembled into segments, and it is not a simple
|
|
|
|
|
* rectangle (which would have been handled already), then merge it into
|
|
|
|
|
* the list of boundaries.
|
|
|
|
|
*
|
|
|
|
|
* Note that this algorithm is O(N^2) and has lots of room for improvement!
|
|
|
|
|
* Still, each segment is never checked against more than 200 points,
|
|
|
|
|
* because when a boundary reaches this number (the maximum for GDS
|
|
|
|
|
* boundary records), the record will tend to be skipped (it should
|
2020-05-23 23:13:14 +02:00
|
|
|
* probably be output here. . .)
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Output, memory allocation and deallocation
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaMergeSegments(
|
|
|
|
|
LinkedBoundary *edge,
|
|
|
|
|
BoundaryTop **blist,
|
|
|
|
|
int num_points)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
LinkedBoundary *stopseg, *curseg, *lastseg;
|
|
|
|
|
LinkedBoundary *compstop, *compseg, *complast;
|
|
|
|
|
BoundaryTop *bounds, *newbounds;
|
|
|
|
|
|
|
|
|
|
if (*blist == NULL) goto make_new_bound;
|
|
|
|
|
|
|
|
|
|
/* Check each internal edge for an antiparallel match with */
|
|
|
|
|
/* an internal edge in the boundary lists. */
|
|
|
|
|
|
|
|
|
|
stopseg = NULL;
|
|
|
|
|
for (lastseg = edge; lastseg != stopseg; lastseg = lastseg->lb_next)
|
|
|
|
|
{
|
|
|
|
|
stopseg = edge;
|
|
|
|
|
curseg = lastseg->lb_next;
|
|
|
|
|
if (curseg->lb_type == LB_EXTERNAL) continue;
|
|
|
|
|
|
|
|
|
|
for (bounds = *blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
{
|
|
|
|
|
/* Avoid overflow on GDS boundary point limit. Note */
|
|
|
|
|
/* that a merge will remove 2 points, but GDS requires */
|
|
|
|
|
/* that we add the 1st point to the end of the list. */
|
|
|
|
|
|
|
|
|
|
if (bounds->bt_points + num_points > 201) continue;
|
|
|
|
|
|
|
|
|
|
compstop = NULL;
|
|
|
|
|
for (complast = bounds->bt_first; complast != compstop;
|
|
|
|
|
complast = complast->lb_next)
|
|
|
|
|
{
|
|
|
|
|
compstop = bounds->bt_first;
|
|
|
|
|
compseg = complast->lb_next;
|
|
|
|
|
if (compseg->lb_type == LB_EXTERNAL) continue;
|
|
|
|
|
|
|
|
|
|
/* Edges match antiparallel only. Rect points are *not* */
|
|
|
|
|
/* canonical. r_ll and p1 are both 1st points traveling */
|
|
|
|
|
/* in a counterclockwise direction along the perimeter. */
|
|
|
|
|
|
|
|
|
|
if (GEO_SAMEPOINT(compseg->lb_start, curseg->lb_next->lb_start) &&
|
|
|
|
|
GEO_SAMEPOINT(compseg->lb_next->lb_start, curseg->lb_start))
|
|
|
|
|
{
|
|
|
|
|
lastseg->lb_next = compseg->lb_next;
|
|
|
|
|
complast->lb_next = curseg->lb_next;
|
|
|
|
|
|
|
|
|
|
freeMagic(compseg);
|
|
|
|
|
freeMagic(curseg);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Make sure the record doesn't point to the free'd segment */
|
|
|
|
|
if (compseg == bounds->bt_first) bounds->bt_first = complast;
|
|
|
|
|
bounds->bt_points += num_points - 2;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If still no connecting edge was found, or if we overflowed the GDS max */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* number of records for a boundary, then start a new entry. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
make_new_bound:
|
|
|
|
|
|
|
|
|
|
newbounds = (BoundaryTop *)mallocMagic(sizeof(BoundaryTop));
|
|
|
|
|
newbounds->bt_first = edge;
|
|
|
|
|
newbounds->bt_next = *blist;
|
|
|
|
|
newbounds->bt_points = num_points;
|
|
|
|
|
*blist = newbounds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* 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
|
|
|
calmaProcessBoundary(
|
|
|
|
|
BoundaryTop *blist,
|
|
|
|
|
calmaOutputStruct *cos)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
FILE *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 */
|
|
|
|
|
calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmaPaintLayerNumber, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmaPaintLayerType, f);
|
|
|
|
|
|
|
|
|
|
/* Record length = ((#points + 1) * 2 values * 4 bytes) + 4 bytes header */
|
|
|
|
|
calmaOutRH(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;
|
|
|
|
|
calmaOutI4(lbref->lb_start.p_x * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4(lbref->lb_start.p_y * calmaPaintScale, f);
|
|
|
|
|
chkcount++;
|
|
|
|
|
}
|
2024-10-04 21:07:03 +02:00
|
|
|
if (listtop != NULL)
|
|
|
|
|
{
|
|
|
|
|
calmaOutI4(listtop->lb_start.p_x * calmaPaintScale, f);
|
|
|
|
|
calmaOutI4(listtop->lb_start.p_y * calmaPaintScale, f);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (chkcount != bounds->bt_points)
|
|
|
|
|
TxError("Points recorded=%d; Points written=%d\n",
|
|
|
|
|
bounds->bt_points, chkcount);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
/* Diagnostic: report the contents of the list */
|
|
|
|
|
TxPrintf("Polygon path (%d points):\n", bounds->bt_points);
|
|
|
|
|
|
|
|
|
|
listtop = bounds->bt_first;
|
|
|
|
|
lbstop = NULL;
|
|
|
|
|
|
|
|
|
|
for (lbref = listtop; lbref != lbstop; lbref = lbref->lb_next)
|
|
|
|
|
{
|
|
|
|
|
if (lbref != listtop)
|
|
|
|
|
TxPrintf("->");
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
lbstop = listtop;
|
|
|
|
|
|
|
|
|
|
switch(lbref->lb_type)
|
|
|
|
|
{
|
|
|
|
|
case LB_EXTERNAL:
|
|
|
|
|
TxPrintf("(%d %d)", lbref->lb_start.p_x, lbref->lb_start.p_y);
|
|
|
|
|
break;
|
|
|
|
|
case LB_INTERNAL:
|
|
|
|
|
TxPrintf("[[%d %d]]", lbref->lb_start.p_x, lbref->lb_start.p_y);
|
|
|
|
|
break;
|
|
|
|
|
case LB_INIT:
|
|
|
|
|
TxPrintf("XXXXX");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TxPrintf("\n\n");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Free the LinkedBoundary list */
|
|
|
|
|
|
|
|
|
|
{
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
|
|
|
lbref = listtop;
|
|
|
|
|
while (lbref->lb_next != listtop)
|
|
|
|
|
{
|
|
|
|
|
freeMagic1(&mm1, lbref);
|
|
|
|
|
lbref = lbref->lb_next;
|
|
|
|
|
}
|
2025-12-20 19:43:09 +01:00
|
|
|
freeMagic1(&mm1, lbref);
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free the BoundaryTop list */
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
{
|
|
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
|
|
|
for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
|
|
|
|
|
freeMagic1(&mm1, bounds);
|
|
|
|
|
freeMagic1_end(&mm1);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaMergePaintFunc --
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the disk file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaMergePaintFunc(
|
|
|
|
|
Tile *tile, /* Tile to be written out. */
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* Split tile information (unused) */
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutputStruct *cos) /* Information needed by algorithm */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
FILE *f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2017-04-25 14:41:48 +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;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (SegStack == (Stack *)NULL)
|
|
|
|
|
SegStack = StackNew(64);
|
|
|
|
|
|
2023-03-22 03:04:30 +01:00
|
|
|
PUSHTILEC(tile);
|
2017-04-25 14:41:48 +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);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
split_type = -1;
|
|
|
|
|
if (IsSplit(t))
|
|
|
|
|
{
|
2026-01-03 02:12:37 +01:00
|
|
|
/* If we use TT_SIDE, then we need to set it when the */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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 = 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;
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
|
|
|
freeMagic1(&mm1, edge);
|
2017-04-25 14:41:48 +02:00
|
|
|
edge = edge->lb_next;
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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) PUSHTILEC(tp);
|
2017-04-25 14:41:48 +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) PUSHTILEC(tp);
|
2017-04-25 14:41:48 +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) PUSHTILEC(tp);
|
2017-04-25 14:41:48 +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) PUSHTILEC(tp);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If tile is isolated, process it now and we're done */
|
|
|
|
|
|
|
|
|
|
done_searches:
|
|
|
|
|
if (intedges == 0)
|
|
|
|
|
{
|
2026-01-03 02:12:37 +01:00
|
|
|
calmaWritePaintFunc(t,
|
|
|
|
|
(split_type & 2) ? (TileType)TT_SIDE : (TileType)0,
|
|
|
|
|
cos);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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, */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* take the shortest path to making "edge" exactly 4 points.*/
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Note that in non-Manhattan mode, num_points may be 3. */
|
|
|
|
|
|
|
|
|
|
if (num_points != 4)
|
|
|
|
|
{
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
for (i = 0; i < num_points; i++)
|
|
|
|
|
{
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, edge);
|
2017-04-25 14:41:48 +02:00
|
|
|
edge = edge->lb_next;
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
|
|
|
|
calmaProcessBoundary(bounds, cos);
|
|
|
|
|
|
|
|
|
|
return 0; /* Keep the search alive. . . */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWritePaintFunc --
|
|
|
|
|
*
|
|
|
|
|
* 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 disk file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWritePaintFunc(
|
|
|
|
|
Tile *tile, /* Tile to be written out. */
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* Split tile information */
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutputStruct *cos) /* File for output and clipping area */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
FILE *f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2017-04-25 14:41:48 +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 */
|
|
|
|
|
calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmaPaintLayerNumber, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(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 */
|
|
|
|
|
calmaOutRH(36, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
switch (((dinfo & TT_SIDE) ? 2 : 0) | SplitDirection(tile))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
case 0x0:
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x1:
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x2:
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
break;
|
|
|
|
|
case 0x3:
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
calmaOutRH(44, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaWriteLabelFunc --
|
|
|
|
|
*
|
|
|
|
|
* Output a single label to the 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 FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaWriteLabelFunc(
|
|
|
|
|
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
|
|
|
FILE *f) /* Stream file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
Point p;
|
2018-08-27 18:23:10 +02:00
|
|
|
int calmanum, calmatype;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
if (ltype < 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
calmanum = CIFCurStyle->cs_layers[ltype]->cl_calmanum;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!CalmaIsValidLayer(calmanum))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
calmaOutRH(4, CALMA_TEXT, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmanum, f);
|
|
|
|
|
|
2022-12-21 23:49:43 +01:00
|
|
|
calmatype = CIFCurStyle->cs_layers[ltype]->cl_calmatype;
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaOutRH(6, CALMA_TEXTTYPE, CALMA_I2, f);
|
2018-08-27 18:23:10 +02:00
|
|
|
calmaOutI2(calmatype, f);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calmaOutRH(6, CALMA_PRESENTATION, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2(textpres, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
|
|
|
|
|
calmaOutI2(0, f); /* Any need for these bits? */
|
|
|
|
|
|
|
|
|
|
calmaOutRH(12, CALMA_MAG, CALMA_R8, f);
|
|
|
|
|
calmaOutR8(((double)lab->lab_size / 800)
|
|
|
|
|
* (double)CIFCurStyle->cs_scaleFactor
|
|
|
|
|
/ (double)CIFCurStyle->cs_expander, f);
|
|
|
|
|
|
|
|
|
|
if (lab->lab_rotate != 0)
|
|
|
|
|
{
|
|
|
|
|
calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
|
|
|
|
|
calmaOutR8((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;
|
|
|
|
|
calmaOutRH(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(p.p_x, f);
|
|
|
|
|
calmaOutI4(p.p_y, f);
|
|
|
|
|
|
|
|
|
|
/* Text of label */
|
|
|
|
|
calmaOutStringRecord(CALMA_STRING, lab->lab_text, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
2018-08-27 18:23:10 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2018-09-09 21:09:15 +02:00
|
|
|
/* Note that the check for whether the CIF_LABEL_NOPORT flag has */
|
|
|
|
|
/* been set is done outside of this routine. */
|
|
|
|
|
|
2018-08-27 18:23:10 +02:00
|
|
|
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 */
|
|
|
|
|
calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
/* Layer */
|
|
|
|
|
calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmanum, f);
|
|
|
|
|
|
|
|
|
|
/* Data type */
|
|
|
|
|
calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(calmatype, f);
|
|
|
|
|
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
calmaOutRH(44, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
|
|
|
|
|
calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-11-16 16:58:18 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaPaintLabelFunc --
|
|
|
|
|
*
|
|
|
|
|
* 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 disk file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaPaintLabelFunc(
|
|
|
|
|
Tile *tile, /* Tile contains area for label. */
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* Split tile information (unused) */
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutputStruct *cos) /* File for output and clipping area */
|
2021-11-16 16:58:18 +01:00
|
|
|
{
|
|
|
|
|
FILE *f = cos->f;
|
2025-01-31 17:31:02 +01:00
|
|
|
const Rect *clipArea = cos->area;
|
2021-11-16 16:58:18 +01: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))
|
2021-11-29 23:57:18 +01:00
|
|
|
return 0;
|
2021-11-16 16:58:18 +01:00
|
|
|
|
|
|
|
|
TiToRect(tile, &r);
|
|
|
|
|
|
|
|
|
|
if (clipArea != NULL)
|
|
|
|
|
GeoClip(&r, clipArea);
|
|
|
|
|
|
|
|
|
|
calmaOutRH(4, CALMA_TEXT, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(layer->cl_calmanum, f);
|
|
|
|
|
|
|
|
|
|
calmaOutRH(6, CALMA_TEXTTYPE, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(layer->cl_calmatype, f);
|
|
|
|
|
|
2022-03-07 17:44:30 +01:00
|
|
|
p.p_x = (r.r_xbot + r.r_xtop) * calmaPaintScale / 2;
|
|
|
|
|
p.p_y = (r.r_ybot + r.r_ytop) * calmaPaintScale / 2;
|
2021-11-16 16:58:18 +01:00
|
|
|
calmaOutRH(12, CALMA_XY, CALMA_I4, f);
|
|
|
|
|
calmaOutI4(p.p_x, f);
|
|
|
|
|
calmaOutI4(p.p_y, f);
|
|
|
|
|
|
|
|
|
|
/* Text of label is the CIF layer name */
|
|
|
|
|
calmaOutStringRecord(CALMA_STRING, layer->cl_name, f);
|
|
|
|
|
|
|
|
|
|
/* End of element */
|
|
|
|
|
calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutHeader --
|
|
|
|
|
*
|
|
|
|
|
* Output the header description for a Calma file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutHeader(
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
FILE *f)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
static double useru = 0.001;
|
|
|
|
|
static double mum = 1.0e-9;
|
|
|
|
|
|
|
|
|
|
/* GDS II version 3.0 */
|
|
|
|
|
calmaOutRH(6, CALMA_HEADER, CALMA_I2, f);
|
|
|
|
|
calmaOutI2(3, f);
|
|
|
|
|
|
|
|
|
|
/* Beginning of library */
|
|
|
|
|
calmaOutRH(28, CALMA_BGNLIB, CALMA_I2, f);
|
2022-01-21 16:26:29 +01:00
|
|
|
if (CalmaDateStamp != NULL)
|
|
|
|
|
calmaOutDate(*CalmaDateStamp, f);
|
2020-12-15 20:52:30 +01:00
|
|
|
else
|
|
|
|
|
calmaOutDate(rootDef->cd_timestamp, f);
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaOutDate(time((time_t *) 0), f);
|
|
|
|
|
|
|
|
|
|
/* Library name (name of root cell) */
|
|
|
|
|
calmaOutStructName(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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
calmaOutRH(20, CALMA_UNITS, CALMA_R8, f);
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) useru = 0.0001;
|
|
|
|
|
calmaOutR8(useru, f); /* User units per database unit */
|
|
|
|
|
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) mum = 1e-10;
|
|
|
|
|
calmaOutR8(mum, f); /* Meters per database unit */
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutDate --
|
|
|
|
|
*
|
|
|
|
|
* 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 FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutDate(
|
|
|
|
|
time_t t, /* Time (UNIX format) to be output */
|
|
|
|
|
FILE *f) /* Stream file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
struct tm *datep = localtime(&t);
|
|
|
|
|
|
|
|
|
|
calmaOutI2(datep->tm_year, f);
|
|
|
|
|
calmaOutI2(datep->tm_mon+1, f);
|
|
|
|
|
calmaOutI2(datep->tm_mday, f);
|
|
|
|
|
calmaOutI2(datep->tm_hour, f);
|
|
|
|
|
calmaOutI2(datep->tm_min, f);
|
|
|
|
|
calmaOutI2(datep->tm_sec, f);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutStringRecord --
|
|
|
|
|
*
|
|
|
|
|
* 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 FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutStringRecord(
|
|
|
|
|
int type, /* Type of this record (data type is ASCII string) */
|
|
|
|
|
char *str, /* String to be output */
|
|
|
|
|
FILE *f) /* Stream file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
unsigned char c;
|
2024-10-04 12:19:10 +02:00
|
|
|
const char *table;
|
|
|
|
|
char *locstr, *origstr = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *locstrprv; /* Added by BSI */
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if(CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
table = calmaMapTablePermissive;
|
|
|
|
|
} else {
|
|
|
|
|
table = calmaMapTableStrict;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
|
locstr = str;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure length is even.
|
|
|
|
|
* Output at most CALMANAMELENGTH characters.
|
2019-05-22 23:03:52 +02:00
|
|
|
* 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).
|
2019-12-19 23:28:06 +01:00
|
|
|
*
|
|
|
|
|
* 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.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
2019-12-19 23:28:06 +01:00
|
|
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (len & 01) len++;
|
2019-12-19 23:28:06 +01:00
|
|
|
if ((CIFCurStyle->cs_flags & CWF_STRING_LIMIT) && (len > CALMANAMELENGTH))
|
2018-11-16 19:59:17 +01:00
|
|
|
{
|
|
|
|
|
TxError("Warning: Cellname %s truncated ", str);
|
2019-05-22 23:03:52 +02:00
|
|
|
TxError("to %s (GDS format limit)\n", str + len - CALMANAMELENGTH);
|
|
|
|
|
locstr = str + len - CALMANAMELENGTH;
|
2018-11-16 19:59:17 +01:00
|
|
|
len = CALMANAMELENGTH;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
calmaOutI2(len+4, f); /* Record length */
|
|
|
|
|
(void) putc(type, f); /* Record type */
|
|
|
|
|
(void) putc(CALMA_ASCII, f); /* Data type */
|
|
|
|
|
|
|
|
|
|
/* Output the string itself */
|
|
|
|
|
while (len--)
|
|
|
|
|
{
|
|
|
|
|
locstrprv = locstr;
|
|
|
|
|
c = (unsigned char) *locstr++;
|
|
|
|
|
if (c == 0) putc('\0', f);
|
|
|
|
|
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) putc(toupper(c), f);
|
|
|
|
|
else
|
|
|
|
|
(void) putc(c, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (origstr != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: characters changed in string \'%s\'; "
|
|
|
|
|
"modified string is \'%s\'\n", origstr, str);
|
|
|
|
|
freeMagic(origstr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOutR8 --
|
|
|
|
|
*
|
|
|
|
|
* 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 output stream FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOutR8(
|
|
|
|
|
double d, /* Double value to write to output */
|
|
|
|
|
FILE *f) /* Stream file */
|
2017-04-25 14:41:48 +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) putc(c, f);
|
|
|
|
|
for (i = 1; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
c = (int)(0xff & (mantissa >> (64 - (8 * i))));
|
|
|
|
|
(void) putc(c, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* calmaOut8 --
|
|
|
|
|
*
|
|
|
|
|
* Output 8 bytes.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes to the FILE 'f'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2024-10-04 12:34:50 +02:00
|
|
|
calmaOut8(
|
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
|
|
|
FILE *f) /* Stream file */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
(void) putc(*str++, f);
|
|
|
|
|
}
|