magic/database/DBtimestmp.c

338 lines
11 KiB
C
Raw Normal View History

/* DBtimestamp.c --
*
* Provides routines to help manage the timestamps stored in
* cell definitions.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBtimestmp.c,v 1.2 2009/05/30 03:13:59 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <sys/types.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "windows/windows.h"
#include "textio/textio.h"
#include "drc/drc.h"
#include "utils/signals.h"
#include "utils/malloc.h"
/* Overall comments:
*
* Cell timestamps are kept around primarily for the benefit
* of the design rule checker. In order for the hierarchical
* and continuous checker to work, we have to make sure that
* we know whenever cells have changed, even if a cell is
* edited out of context. Thus, we record a timestamp value
* in every cell, which is the time when the cell was last
* modified. The timestamp for a cell should always be at
* least as large as the latest timestamp for any of that
* cell's descendants (larger timestamps correspond to later
* times). If a child is edited out of context, this condition
* may be violated, so each parent keeps an expected timestamp
* for each of its children. If the actual child timestamp is
* ever found to be different, we must re-check interactions
* between that child and other pieces of the parent, and then
* update the parent's timestamp. If a child changes, then
* timestamps must be changed in all parents of the child, their
* parents, and so on, so that we're guaranteed to see interactions
* coming even from several levels up the tree.
*/
/* The structure below is used to keep track of cells whose timestamps
* mismatched. The DBStampMismatch routine creates this structure.
* at convenient times (between commands), the DBFixMismatch routine
* is called to process the entries. It updates bounding boxes (the
* bounding box update cannot be done at arbitrary times, because it
* can reorganize tile planes that are pending in searches. The
* indentation below is necessary to keep lintpick happy.
*/
typedef struct mm {
CellDef *mm_cellDef; /* CellDef whose stamp was wrong. */
Rect mm_oldArea; /* The old area that the cellDef used
* to occupy.
*/
struct mm *mm_next; /* Next mismatch record in list. */
} Mismatch;
Mismatch *mismatch = NULL; /* List head. */
/* The time value below is passed to the dbStampFunct so it
* uses the same value everywhere, and doesn't have to call
* the system routine repeatedly.
*/
int timestamp;
typedef struct _celllist {
CellDef *cl_cell;
struct _celllist *cl_next;
} CellList;
/*
* ----------------------------------------------------------------------------
* DBFixMismatch --
*
* This procedure is called when it safe to recompute bounding
* boxes in order to fix timestamp mismatches.
*
* Results:
* None.
*
* Side effects:
* Bounding boxes get recomputed and information gets passed to
* the DRC module. The DRC module will recheck both the old and
* new areas of the cell whose timestamp mismatched.
* ----------------------------------------------------------------------------
*/
void
DBFixMismatch()
{
CellDef *cellDef;
CellUse *parentUse;
CellList *cl = NULL, *clnew;
Rect oldArea, parentArea, tmp;
int redisplay;
Mismatch *tmpm;
/* It's very important to disable interrupts during this section!
* Otherwise, we may not paint the recheck tiles properly.
*/
redisplay = FALSE;
if (mismatch == NULL) return;
TxPrintf("Processing timestamp mismatches.\n");
SigDisableInterrupts();
for (tmpm = mismatch; tmpm; tmpm = tmpm->mm_next)
tmpm->mm_cellDef->cd_flags &= (~CDPROCESSED);
while (mismatch != NULL)
{
/* Be careful to remove the front element from the mismatch
* list before processing it, because while processing it we
* may add new elements to the list.
*/
cellDef = mismatch->mm_cellDef;
oldArea = mismatch->mm_oldArea;
freeMagic((char *) mismatch);
mismatch = mismatch->mm_next;
if (cellDef->cd_flags & CDPROCESSED) continue;
(void) DBCellRead(cellDef, TRUE, TRUE, NULL);
/* Jimmy up the cell's current bounding box, so the following
* procedure call will absolutely and positively know that
* the bbox has changed. This is necessary because not all
* the uses necessarily reflect the current def bounding box,
* and we want DBReComputeArea to be sure to update all of
* the uses.
*/
cellDef->cd_bbox.r_xtop = cellDef->cd_bbox.r_xbot - 1;
cellDef->cd_extended.r_xtop = cellDef->cd_extended.r_xbot - 1;
DBReComputeBbox(cellDef);
/* Now, for each parent, recheck the parent in both the
* old area of the child and the new area.
*/
for (parentUse = cellDef->cd_parents; parentUse != NULL;
parentUse = parentUse->cu_nextuse)
{
if (parentUse->cu_parent == NULL) continue;
DBComputeArrayArea(&oldArea, parentUse, parentUse->cu_xlo,
parentUse->cu_ylo, &parentArea);
DBComputeArrayArea(&oldArea, parentUse, parentUse->cu_xhi,
parentUse->cu_yhi, &tmp);
(void) GeoInclude(&parentArea, &tmp);
GeoTransRect(&parentUse->cu_transform, &tmp, &parentArea);
DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL, &parentArea);
DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL,
&(parentUse->cu_bbox));
redisplay = TRUE;
}
cellDef->cd_flags |= CDPROCESSED;
clnew = (CellList *)mallocMagic(sizeof(CellList));
clnew->cl_cell = cellDef;
clnew->cl_next = cl;
cl = clnew;
}
SigEnableInterrupts();
TxPrintf("Timestamp mismatches found in these cells: ");
while (cl != NULL)
{
TxPrintf("%s", cl->cl_cell->cd_name);
if (cl->cl_next != NULL) TxPrintf(", ");
freeMagic(cl);
cl = cl->cl_next;
}
TxPrintf(".\n");
TxFlush();
if (redisplay) WindAreaChanged((MagWindow *) NULL, (Rect *) NULL);
}
/*
* ----------------------------------------------------------------------------
* DBUpdateStamps --
*
* Updates all timestamps in all cells in the database.
*
* Results:
* None.
*
* Side effects:
* For every cell that has been modified, its timestamp and
* the timestamps of all its parents, grandparents, etc. are
* updated. The result is that the timestamp in any given cell is
* at least as great as the timestamps in any of its descendants.
* ----------------------------------------------------------------------------
*/
void
DBUpdateStamps(def)
CellDef *def;
{
extern int dbStampFunc();
extern time_t time();
DBFixMismatch();
timestamp = time((time_t *) 0);
if (def == NULL)
(void) DBCellSrDefs(CDGETNEWSTAMP, dbStampFunc, (ClientData) NULL);
else if (def->cd_flags & CDGETNEWSTAMP)
{
if (def->cd_flags & CDFIXEDSTAMP)
def->cd_flags &= ~CDGETNEWSTAMP;
else
dbStampFunc(def);
}
}
int
dbStampFunc(cellDef)
CellDef *cellDef;
{
CellUse *cu;
CellDef *cd;
/* The following check keeps us from making multiple recursive
* scans of any cell.
*/
if (cellDef->cd_timestamp == timestamp) return 0;
if (!(cellDef->cd_flags & CDFIXEDSTAMP))
cellDef->cd_timestamp = timestamp;
cellDef->cd_flags &= ~CDGETNEWSTAMP;
// printf("Writing new timestamp %d for %s.\n", timestamp, cellDef->cd_name);
/* If a child's stamp has changed, then the stamps must change
* in all its parents too. When the hierarchical DRC becomes
* completely operational, this recursive update may be unnecessary
* since the DRC will have painted check tiles in all ancestors.
*/
for (cu = cellDef->cd_parents; cu != NULL; cu = cu->cu_nextuse)
{
cd = cu->cu_parent;
if (cd == NULL) continue;
cd->cd_flags |= CDSTAMPSCHANGED;
(void) dbStampFunc(cd);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
* DBStampMismatch --
*
* This routine is invoked when a mismatch is discovered for a
* cell. The parameter wrongArea tells what the cell's bounding
* box used to be (but the timestamp mismatch means this was
* probably wrong, so that area has to be re-design-rule-checked).
*
* Results:
* None.
*
* Side effects:
* We record the definition on a list of mismatches for later
* processing. When DBFixMismatch is called, it will notify
* the design-rule checker to recheck both wrongArea, and
* the cell's eventual correct area.
*
* This routine has been modified from a poor implementation. Previously
* the parent def of all uses of the cell being checked would be marked
* for a stamp mismatch check. However, when reading a cell with large
* numbers of instances, the list of instances would be parsed for every
* instance added, leading to an O(N^2) computation. Routine DBStampMismatch()
* has been broken into two parts. DBStampMismatch() only records the
* area to be checked. DBFlagMismatches() looks at the parents of each
* celldef only once, after all instances have been read.
*
* ----------------------------------------------------------------------------
*/
void
DBStampMismatch(cellDef, wrongArea)
CellDef *cellDef;
Rect *wrongArea; /* Guess of cell's bounding box that
* was wrong.
*/
{
Mismatch *mm;
mm = (Mismatch *) mallocMagic((unsigned) (sizeof (Mismatch)));
mm->mm_cellDef = cellDef;
mm->mm_oldArea = *wrongArea;
mm->mm_next = mismatch;
mismatch = mm;
}
/*
* ----------------------------------------------------------------------------
* ----------------------------------------------------------------------------
*/
void
DBFlagMismatches(checkDef)
CellDef *checkDef;
{
CellUse *parentUse;
for (parentUse = checkDef->cd_parents; parentUse != NULL;
parentUse = parentUse->cu_nextuse)
{
if (parentUse->cu_parent == NULL) continue;
parentUse->cu_parent->cd_flags |= CDSTAMPSCHANGED;
}
}