/* 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 #include #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; } }