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 != ""} {