From 06ab6b3a8aae0918987b8d90a0b900a5cd7c1087 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 30 Oct 2020 11:20:19 -0400 Subject: [PATCH] Made a change to the way that the parameterized cells are handled. Instead of a 6-character suffix generated randomly, the 6-character suffix is generated by a hash algorithm from the device parameters. If the cell parameters are changed, then the cell itself changes. If the instance name was default (derived from the cell name) then the instance name changes accordingly. The result is that there cannot be two (auto-)generated cells with the same parameters but with different cell names. --- VERSION | 2 +- commands/CmdCD.c | 62 ++++++++---- commands/CmdLQ.c | 155 +++++++++++++++++++++++++++++ database/DBcellname.c | 159 +++++++++++++++++++++++++++-- database/database.h.in | 1 + dbwind/DBWcommands.c | 7 +- tcltk/cellmgr.tcl | 2 +- tcltk/toolkit.tcl | 220 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 571 insertions(+), 37 deletions(-) diff --git a/VERSION b/VERSION index dc0d58f7..c56821cd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.74 +8.3.76 diff --git a/commands/CmdCD.c b/commands/CmdCD.c index f235f693..fda146a9 100644 --- a/commands/CmdCD.c +++ b/commands/CmdCD.c @@ -624,7 +624,8 @@ outputCalma: * or * cellname property [name] [property_key [property_value]] * or - * instance orientation [name] [-def] + * instance orientation [name] [-def] [orient] + * instance abutment [name] * * Results: * None. @@ -648,7 +649,7 @@ CmdCellname(w, cmd) bool dodef = FALSE; int option; int locargc = cmd->tx_argc; - char *cellname = NULL; + char *cellname = NULL, *orient = NULL; void (*func)(); CellDef *newDef, *cellDef; @@ -671,6 +672,7 @@ CmdCellname(w, cmd) "lock lock the named cell (prevent changes to cell use)", "unlock unlock the named cell (allow changes to cell use)", "property list or set cell definition properties", + "abutment list instance abutment coordinates", "orientation list or set instance orientation", "rename rename the indicated cell", "writeable make the cell definition read-only or read-write", @@ -681,7 +683,7 @@ CmdCellname(w, cmd) IDX_INSTANCE, IDX_CHILDINST, IDX_CELLDEF, IDX_ALLCELLS, IDX_TOPCELLS, IDX_IN_WINDOW, IDX_CREATE, IDX_DELETE, IDX_FILEPATH, IDX_FLAGS, IDX_LOCK, IDX_UNLOCK, - IDX_PROPERTY, IDX_ORIENTATION, IDX_RENAME, + IDX_PROPERTY, IDX_ABUTMENT, IDX_ORIENTATION, IDX_RENAME, IDX_READWRITE, IDX_MODIFIED } optionType; if (strstr(cmd->tx_argv[0], "in")) @@ -720,7 +722,7 @@ CmdCellname(w, cmd) if ((locargc > 3) && (option != IDX_RENAME) && (option != IDX_DELETE) && (option != IDX_READWRITE) && (option != IDX_PROPERTY) && - (option != IDX_FILEPATH)) + (option != IDX_FILEPATH) && (option != IDX_ORIENTATION)) goto badusage; if ((locargc > 4) && (option != IDX_PROPERTY)) @@ -751,6 +753,10 @@ CmdCellname(w, cmd) TxError("Cell definitions do not have orientations." " Use \"instance\"?\n"); return; + case IDX_ABUTMENT: + TxError("Use \"property get FIXED_BBOX\" to get the cell " + "abutment box.\n"); + return; } } else @@ -1029,7 +1035,16 @@ CmdCellname(w, cmd) } break; case IDX_ORIENTATION: - DBOrientUse(cellname, dodef); + orient = (locargc == 4) ? cmd->tx_argv[3 + ((dolist) ? 1 : 0)] : NULL; + if ((cellname != NULL) && (orient != NULL)) + { + TxError("Cannot set orientation by name. Use selection.\n"); + break; + } + DBOrientUse(cellname, dodef, orient); + break; + case IDX_ABUTMENT: + DBAbutmentUse(cellname, dolist); break; case IDX_LOCK: DBLockUse(cellname, TRUE); @@ -1647,7 +1662,7 @@ CmdCif(w, cmd) * the interpreter to uniquely identify it. * * The "-origin" option has been added to allow rotating stuff - * relative to the origin, insteadd of the lower-left corner. + * relative to the origin, instead of the lower-left corner. * * ---------------------------------------------------------------------------- */ @@ -3897,9 +3912,9 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) hasChild = hasRoot = hasTrans = FALSE; while (ac > 0) { - static char *kwdNames[] = { "child", "parent", "90", "180", "270", - "v", "90v", "180v", "270v", - "h", "90h", "180h", "270h", 0 }; + static char *kwdNames[] = { "child", "parent", "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", 0 }; static char *refPointNames[] = { "ll", "lr", "ul", "ur", 0 }; Label *lab; int n,p; @@ -4043,8 +4058,8 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) } hasRoot = TRUE; break; - case 2: /* 90 */ - tx_cell = &Geo90Transform; + case 2: /* 0 */ + tx_cell = &GeoIdentityTransform; transform_cell: if (ac < 2 ) { @@ -4127,34 +4142,39 @@ default_action: } hasTrans = TRUE; break; - case 3: /* 180 */ + case 3: /* 90 */ + tx_cell = &Geo90Transform; + goto transform_cell; + case 4: /* 180 */ tx_cell = &Geo180Transform; goto transform_cell; - case 4: /* 270 */ + case 5: /* 270 */ tx_cell = &Geo270Transform; goto transform_cell; - case 5: /* v */ + case 6: /* v */ + case 7: /* 0v */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 6: /* 90v */ + case 8: /* 90v */ tx_cell = &GeoRef45Transform; goto transform_cell; - case 7: /* 180v */ + case 9: /* 180v */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 8: /* 270v */ + case 10: /* 270v */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 9: /* h */ + case 11: /* h */ + case 12: /* 0h */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 10: /* 90h */ + case 13: /* 90h */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 11: /* 180h */ + case 14: /* 180h */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 12: /* 270h */ + case 15: /* 270h */ tx_cell = &GeoRef45Transform; goto transform_cell; } diff --git a/commands/CmdLQ.c b/commands/CmdLQ.c index c0bf6ce9..1c68635b 100644 --- a/commands/CmdLQ.c +++ b/commands/CmdLQ.c @@ -2276,3 +2276,158 @@ CmdNetlist(w, cmd) break; } } + +/* + * ---------------------------------------------------------------------------- + * + * CmdOrient -- + * + * Implement the "orient" command. Set the orientation of the selection and + * according to the orientation string, which can be in Magic's format + * (using number of degrees, and "h" for horizontal flip or "v" for vertical + * flip) or DEF format ("N", "S", "FN", etc.). The cursor box is rotated to + * match. + * + * Usage: + * orient [orientation] [-origin] + * + * Results: + * None. + * + * Side effects: + * Modifies the edit cell. + * + * Notes: + * The "-origin" option sets the orientation relative to the origin, + * instead of the lower-left corner. + * + * ---------------------------------------------------------------------------- + */ + +void +CmdOrient(w, cmd) + MagWindow *w; + TxCommand *cmd; +{ + Transform trans, t2; + int orientidx, locargc; + char *orientstr; + Rect rootBox, bbox; + CellDef *rootDef; + bool noAdjust = FALSE; + + static char *orientNames[] = { "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", + "N", "E", "S", "W", + "FN", "FE", "FS", "FW", + 0 }; + + typedef enum { IDX_ORIENT_0, IDX_ORIENT_90, IDX_ORIENT_180, IDX_ORIENT_270, + IDX_ORIENT_V, IDX_ORIENT_0V, IDX_ORIENT_90V, IDX_ORIENT_180V, + IDX_ORIENT_270V, IDX_ORIENT_H, IDX_ORIENT_0H, IDX_ORIENT_90H, + IDX_ORIENT_180H, IDX_ORIENT_270H, IDX_ORIENT_N, IDX_ORIENT_E, + IDX_ORIENT_S, IDX_ORIENT_W, IDX_ORIENT_FN, IDX_ORIENT_FE, + IDX_ORIENT_FS, IDX_ORIENT_FW } orientType; + + locargc = cmd->tx_argc; + if (!strncmp(cmd->tx_argv[locargc - 1], "-orig", 5)) + { + noAdjust = TRUE; + locargc--; + } + + if (!ToolGetEditBox((Rect *)NULL)) return; + + if (locargc == 2) + orientstr = cmd->tx_argv[1]; + else + goto badusage; + + orientidx = Lookup(orientstr, orientNames); + + switch (orientidx) + { + case IDX_ORIENT_0: + case IDX_ORIENT_N: + /* ORIENT_NORTH */ + t2 = GeoIdentityTransform; + break; + case IDX_ORIENT_S: + case IDX_ORIENT_180: + /* ORIENT_SOUTH */ + t2 = Geo180Transform; + break; + case IDX_ORIENT_E: + case IDX_ORIENT_90: + /* ORIENT_EAST */ + t2 = Geo90Transform; + break; + case IDX_ORIENT_W: + case IDX_ORIENT_270: + /* ORIENT_WEST */ + t2 = Geo270Transform; + break; + case IDX_ORIENT_FN: + case IDX_ORIENT_H: + case IDX_ORIENT_0H: + /* ORIENT_FLIPPED_NORTH */ + t2 = GeoSidewaysTransform; + break; + case IDX_ORIENT_FS: + case IDX_ORIENT_180H: + case IDX_ORIENT_V: + case IDX_ORIENT_0V: + /* ORIENT_FLIPPED_SOUTH */ + t2 = GeoUpsideDownTransform; + break; + case IDX_ORIENT_FE: + case IDX_ORIENT_90H: + case IDX_ORIENT_270V: + /* ORIENT_FLIPPED_EAST */ + t2 = GeoRef135Transform; + break; + case IDX_ORIENT_FW: + case IDX_ORIENT_270H: + case IDX_ORIENT_90V: + /* ORIENT_FLIPPED_WEST */ + t2 = GeoRef45Transform; + break; + default: + goto badusage; + } + + /* To orient the selection, first orient it relative to the origin + * then move it so its lower-left corner is at the same place + * that it used to be. If the "-origin" option was selected, then + * only orient relative to the origin. + */ + + GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox); + if (noAdjust) + trans = t2; + else + { + GeoTranslateTrans(&t2, SelectDef->cd_bbox.r_xbot - bbox.r_xbot, + SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans); + } + + SelectTransform(&trans); + + /* Rotate the box, if it exists and is in the same window as the + * selection. + */ + + if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef)) + { + Rect newBox; + + GeoTransRect(&trans, &rootBox, &newBox); + DBWSetBox(rootDef, &newBox); + } + + return; + + badusage: + TxError("Usage: %s [orientation]\n", cmd->tx_argv[0]); +} diff --git a/database/DBcellname.c b/database/DBcellname.c index ec10d1ee..dbfc32f0 100644 --- a/database/DBcellname.c +++ b/database/DBcellname.c @@ -1204,19 +1204,20 @@ DBLockUse(UseName, bval) * * DBOrientUse -- * - * This routine sets or reports a cell instance's orientation + * This routine reports a cell instance's orientation. * UseName is the name of a specific CellUse. If NULL, then the - * operation applies to all selected cell uses. "orient" is a - * string in the form used by "getcell" (e.g., "180", "270v", - * etc.), unless "dodef" is true, in which case the output is - * given in the form used by DEF ("N", "FN", etc.). - * reported. + * operation applies to all selected cell uses. * * Results: * None. * * Side effects: - * cu_transform changed for indicated cell use. + * In the Tcl/Tk implementation, the result is set in the interpreter. + * + * Notes: + * This routine only reports orientation. Setting orientation must + * be done through the selection interface (i.e., commands "sideways", + * "upsidedown", "clockwise" ("rotate"), or "orient" (added 10/30/2020)). * * ---------------------------------------------------------------------------- */ @@ -1294,6 +1295,7 @@ dbOrientUseFunc(selUse, use, transform, data) ClientData data; { bool *dodef = (bool *)data; + int orient; if (EditCellUse && !DBIsChild(use, EditCellUse)) { @@ -1302,9 +1304,16 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } + orient = -1; + if (selUse != NULL) + orient = GeoTransOrient(&selUse->cu_transform); + else if (use != NULL) + orient = GeoTransOrient(&use->cu_transform); + + if (orient != -1) { - switch (GeoTransOrient(&selUse->cu_transform)) { + switch (orient) { #ifdef MAGIC_WRAPPER case ORIENT_NORTH: Tcl_AppendElement(magicinterp, (*dodef) ? "N" : "0"); @@ -1362,6 +1371,140 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } +/* + * ---------------------------------------------------------------------------- + * + * DBAbutmentUse -- + * + * This routine reports the cell instance's abutment box in the + * coordinate system of the parent (edit) cell. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------------- + */ + +void +DBAbutmentUse(UseName, dolist) + char *UseName; + bool dolist; +{ + int found; + HashSearch hs; + HashEntry *entry; + CellDef *celldef; + CellUse *celluse; + + int dbAbutmentUseFunc(); + + /* + * + * Check to see if a cell name was specified. If not, then search + * for selected cells. + * + */ + + if (UseName == NULL) + { + SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL, + dbAbutmentUseFunc, (ClientData)&dolist); + } + else + { + SearchContext scx; + + bzero(&scx, sizeof(SearchContext)); + found = 0; + + HashStartSearch(&hs); + while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL) + { + celldef = (CellDef *) HashGetValue(entry); + if (celldef != (CellDef *) NULL) + { + celluse = celldef->cd_parents; /* only need one */ + if (celluse != (CellUse *)NULL) { + DBTreeFindUse(UseName, celluse, &scx); + if (scx.scx_use != NULL) break; + } + } + } + + if (scx.scx_use == NULL) + TxError("Cell %s is not currently loaded.\n", UseName); + else + dbAbutmentUseFunc(NULL, scx.scx_use, NULL, (ClientData)&dolist); + } +} + +/* + * dbAbutmentUseFunc() + */ + +int +dbAbutmentUseFunc(selUse, use, transform, data) + CellUse *selUse; /* Use from selection cell */ + CellUse *use; /* Use from layout corresponding to selection */ + Transform *transform; + ClientData data; +{ + Rect bbox, refbox; + Transform *trans; + char *propvalue; + bool found; + bool *dolist = (bool *)data; + +#ifdef MAGIC_WRAPPER + Tcl_Obj *pobj; +#endif + + if (EditCellUse && !DBIsChild(use, EditCellUse)) + { + TxError("Cell %s (%s) isn't a child of the edit cell.\n", + use->cu_id, use->cu_def->cd_name); + return 0; + } + + if (use == NULL) + { + TxError("No instance in selection!\n"); + return 0; + } + + trans = &use->cu_transform; + propvalue = DBPropGet(use->cu_def, "FIXED_BBOX", &found); + if (!found) + bbox = use->cu_def->cd_bbox; + else + { + if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot, + &bbox.r_xtop, &bbox.r_ytop) != 4) + bbox = use->cu_def->cd_bbox; + } + GeoTransRect(trans, &bbox, &refbox); + +#ifdef MAGIC_WRAPPER + if (*dolist) + { + pobj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xbot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ybot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xtop)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ytop)); + Tcl_SetObjResult(magicinterp, pobj); + } + else +#endif + TxPrintf("Abutment box: %d %d %d %d\n", refbox.r_xbot, refbox.r_ybot, + refbox.r_xtop, refbox.r_ytop); + + return 0; +} + /* * ---------------------------------------------------------------------------- * diff --git a/database/database.h.in b/database/database.h.in index af818186..c17d8d8c 100644 --- a/database/database.h.in +++ b/database/database.h.in @@ -778,6 +778,7 @@ extern CellUse *DBCellFindDup(); extern void DBLockUse(); extern void DBUnlockUse(); extern void DBOrientUse(); +extern void DBAbutmentUse(); /* Cell selection */ extern CellUse *DBSelectCell(); diff --git a/dbwind/DBWcommands.c b/dbwind/DBWcommands.c index 6a5150e4..9b1d423d 100644 --- a/dbwind/DBWcommands.c +++ b/dbwind/DBWcommands.c @@ -47,8 +47,8 @@ extern void CmdEdit(), CmdElement(), CmdErase(), CmdExpand(), CmdExtract(); extern void CmdFeedback(), CmdFill(), CmdFindBox(), CmdFindLabel(), CmdFlush(); extern void CmdGetcell(), CmdGrid(), CmdIdentify(); extern void CmdLabel(), CmdLoad(); -extern void CmdMove(), CmdNetlist(), CmdPaint(), CmdPath(), CmdPlow(); -extern void CmdPolygon(), CmdPort(), CmdProperty(); +extern void CmdMove(), CmdNetlist(), CmdOrient(), CmdPaint(), CmdPath(); +extern void CmdPlow(), CmdPolygon(), CmdPort(), CmdProperty(); extern void CmdSave(), CmdScaleGrid(), CmdSee(); extern void CmdSelect(), CmdSetLabel(), CmdSideways(); extern void CmdShell(), CmdSnap(); @@ -378,6 +378,9 @@ DBWInitCommands() for information on options", CmdNetlist, FALSE); + WindAddCommand(DBWclientID, + "orient [orientation] orient selection and box", + CmdOrient, FALSE); WindAddCommand(DBWclientID, "paint layers|cursor paint mask information", CmdPaint, FALSE); diff --git a/tcltk/cellmgr.tcl b/tcltk/cellmgr.tcl index 1ea0b0db..5f715857 100644 --- a/tcltk/cellmgr.tcl +++ b/tcltk/cellmgr.tcl @@ -10,7 +10,7 @@ if {$::tk_version >= 8.5} { set Opts(cellmgr) 0 -magic::tag select "magic::mgrselect %R" +magic::tag select "magic::mgrselect %r" magic::tag load "catch {magic::clearstack}; magic::cellmanager" magic::tag getcell "magic::cellmanager" diff --git a/tcltk/toolkit.tcl b/tcltk/toolkit.tcl index 75fa12cd..b3d43019 100644 --- a/tcltk/toolkit.tcl +++ b/tcltk/toolkit.tcl @@ -6,6 +6,8 @@ # Revision 0 # December 15, 2016 # Revision 1 +# October 29, 2020 +# Revision 2 (names are hashed from properties) #-------------------------------------------------------------- # Sets up the environment for a toolkit. The toolkit must # supply a namespace that is the "library name". For each @@ -237,6 +239,36 @@ proc magic::gencell {gencell_name {instname {}} args} { } } +#------------------------------------------------------------- +# gencell_makecell +# +# This is a variation of magic::gencell and is used to generate +# a cell and return the cell name without creating or placing +# an instance. +#------------------------------------------------------------- + +proc magic::gencell_makecell {gencell_fullname args} { + + set argpar [dict create {*}$args] + set gencell_basename [namespace tail $gencell_fullname] + set library [namespace qualifiers $gencell_fullname] + set parameters [magic::gencell_defaults $gencell_basename $library $argpar] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_basename}_${gsuffix} + suspendall + cellname create $gname + pushstack $gname + if {[catch {${library}::${gencell_basename}_draw $parameters} drawerr]} { + puts stderr $drawerr + } + property library $library + property gencell $gencell_basename + property parameters $parameters + popstack + resumeall + return $gname +} + #------------------------------------------------------------- # gencell_getparams # @@ -271,6 +303,9 @@ proc magic::gencell_setparams {parameters} { if {[catch {set state [wm state .params]}]} {return} set slist [grid slaves .params.edits] foreach s $slist { + # ignore .params.edits.gencell_sel, as that does not exist in the + # parameters dictionary + if {$s == ".params.edits.gencell_sel"} {continue} if {[regexp {^.params.edits.(.*)_ent$} $s valid pname] != 0} { set value [dict get $parameters $pname] set magic::${pname}_val $value @@ -293,7 +328,16 @@ proc magic::gencell_setparams {parameters} { #------------------------------------------------------------- # gencell_change # -# Redraw a gencell with new parameters. +# Recreate a gencell with new parameters. Note that because +# each cellname is uniquely identified by the (hashed) set +# of parameters, changing parameters effectively means +# creating a new cell. If the original cell has parents +# other than the parent of the instance being changed, then +# it is retained; otherwise, it is deleted. The instance +# being edited gets replaced by an instance of the new cell. +# If the instance name was the cellname + suffix, then the +# instance name is regenerated. Otherwise, the instance +# name is retained. #------------------------------------------------------------- proc magic::gencell_change {instname gencell_type library parameters} { @@ -324,6 +368,128 @@ proc magic::gencell_change {instname gencell_type library parameters} { set parameters [dict remove $parameters gencell] } + set old_gname [instance list celldef $instname] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} + + # Guard against instance having been deleted. Also, if parameters have not + # changed as evidenced by the cell suffix not changing, then nothing further + # needs to be done. + if {$gname == "" || $gname == $old_gname} { + resumeall + return + } + + set snaptype [snap list] + snap internal + set savebox [box values] + + catch {setpoint 0 0 $Opts(focus)} + if [dict exists $parameters nocell] { + select cell $instname + set abox [instance list abutment] + delete + if {$abox != ""} {box values {*}$abox} + if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \ + drawerr]} { + puts stderr $drawerr + } + select cell $newinst + } elseif {[cellname list exists $gname] != 0} { + # If there is already a cell of this type then it is only required to + # remove the instance and replace it with an instance of the cell + select cell $instname + # check rotate/flip before replacing and replace with same + set orient [instance list orientation] + set abox [instance list abutment] + delete + + if {$abox != ""} {box values {*}$abox} + set newinstname [getcell $gname $orient] + select cell $newinstname + expand + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + + if {[cellname list parents $old_gname] == []} { + # If the original cell has no intances left, delete it. It can + # be regenerated if and when necessary. + cellname delete $old_gname + } + + } else { + select cell $instname + set orient [instance list orientation] + set abox [instance list abutment] + delete + + # There is no cell of this name, so generate one and instantiate it. + if {$abox != ""} {box values {*}$abox} + set newinstname [magic::gencell_create $gencell_type $library $parameters $orient] + select cell $newinstname + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + } + identify $newinstname + eval "box values $savebox" + snap $snaptype + + # Update window + if {$gname != $old_gname} { + catch {.params.title.glab configure -text "$gname"} + } + if {$instname != $newinstname} { + catch {.params.title.ient delete 0 end} + catch {.params.title.ient insert 0 "$newinstname"} + } + + resumeall + redraw +} + +#------------------------------------------------------------- +# gencell_change_orig +# +# Original version: Redraw a gencell with new parameters, +# without changing the cell itself. +#------------------------------------------------------------- + +proc magic::gencell_change_orig {instname gencell_type library parameters} { + global Opts + suspendall + + set newinstname $instname + if {$parameters == {}} { + # Get device defaults + set pdefaults [${library}::${gencell_type}_defaults] + # Pull user-entered values from dialog + set parameters [dict merge $pdefaults [magic::gencell_getparams]] + set newinstname [.params.title.ient get] + if {$newinstname == "(default)"} {set newinstname $instname} + if {$newinstname == $instname} {set newinstname $instname} + if {[instance list exists $newinstname] != ""} {set newinstname $instname} + } + if {[dict exists $parameters gencell]} { + # Setting special parameter "gencell" forces the gencell to change type + set gencell_type [dict get $parameters gencell] + } + if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \ + checkerr]} { + puts stderr $checkerr + } + magic::gencell_setparams $parameters + if {[dict exists $parameters gencell]} { + set parameters [dict remove $parameters gencell] + } + set gname [instance list celldef $instname] # Guard against instance having been deleted @@ -391,6 +557,51 @@ proc magic::get_gencell_name {gencell_type} { return ${gencell_type}_$postfix } +#---------------------------------------------------------------- +# get_gencell_hash +# +# A better approach to the above. Take the parameter +# dictionary, and run all the values through a hash function +# to generate a 30-bit value, then convert to base32. This +# gives a result that is repeatable for the same set of +# parameter values with a very low probability of a collision. +# +# The hash function is similar to elfhash but reduced from 32 +# to 30 bits so that the result can form a 6-character value +# in base32 with all characters being valid for a SPICE subcell +# name (e.g., alphanumeric only and case-insensitive). +#---------------------------------------------------------------- + +proc magic::get_gencell_hash {parameters} { + set hash 0 + # Apply hash + dict for {key value} $parameters { + foreach s [split $value {}] { + set hash [expr {($hash << 4) + [scan $s %c]}] + set high [expr {$hash & 0x03c0000000}] + set hash [expr {$hash ^ ($high >> 30)}] + set hash [expr {$hash & (~$high)}] + } + } + # Divide hash up into 5 bit values and convert to base32 + # using letters A-Z less I and O, and digits 2-9. + set cvals "" + for {set i 0} {$i < 6} {incr i} { + set oval [expr {($hash >> ($i * 5)) & 0x1f}] + if {$oval < 8} { + set bval [expr {$oval + 50}] + } elseif {$oval < 16} { + set bval [expr {$oval + 57}] + } elseif {$oval < 21} { + set bval [expr {$oval + 58}] + } else { + set bval [expr {$oval + 59}] + } + append cvals [binary format c* $bval] + } + return $cvals +} + #------------------------------------------------------------- # gencell_create # @@ -402,7 +613,7 @@ proc magic::get_gencell_name {gencell_type} { # the drawing routine is going to do to the stack! #------------------------------------------------------------- -proc magic::gencell_create {gencell_type library parameters} { +proc magic::gencell_create {gencell_type library parameters {orient 0}} { global Opts suspendall @@ -451,7 +662,8 @@ proc magic::gencell_create {gencell_type library parameters} { set gname [instance list celldef $instname] eval "box values $savebox" } else { - set gname [magic::get_gencell_name ${gencell_type}] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} cellname create $gname pushstack $gname if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} { @@ -462,7 +674,7 @@ proc magic::gencell_create {gencell_type library parameters} { property parameters $parameters popstack eval "box values $savebox" - set instname [getcell $gname] + set instname [getcell $gname $orient] expand } if {$newinstname != ""} {