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.
This commit is contained in:
Tim Edwards 2020-10-30 11:20:19 -04:00
parent 1fe032a79b
commit 06ab6b3a8a
8 changed files with 571 additions and 37 deletions

View File

@ -1 +1 @@
8.3.74
8.3.76

View File

@ -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;
}

View File

@ -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]);
}

View File

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

View File

@ -778,6 +778,7 @@ extern CellUse *DBCellFindDup();
extern void DBLockUse();
extern void DBUnlockUse();
extern void DBOrientUse();
extern void DBAbutmentUse();
/* Cell selection */
extern CellUse *DBSelectCell();

View File

@ -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);

View File

@ -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"

View File

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