SCRouteImpl.mesa: Implements of SC.DetailedRoute
Copyright © 1986, 1987 by Xerox Corporation. All rights reserved.
Frank Bowers June 3, 1986 10:58:48 am PDT
Last Edited by: Bryan Preas February 17, 1987 6:30:59 pm PST
NOTE: Both PWPins and CoreGeometry Pins are used. This will change as soon as PWRoute removes dependence on PWPins
DIRECTORY
CD, CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSymbolicObjects, Convert, Core, CoreGeometry, CoreOps, ExtendCells, HashTable, PWPins, PWRoute, Rope, Route, RouteUtil, RTBasic, SC, SCChanUtil, SCInstUtil, SCNetUtil, SCPrivate, SCRoutePinsUtil, SCRowUtil, SCUtil, Sinix, SinixOps;
SCRouteImpl:
CEDAR
PROGRAM
IMPORTS CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSymbolicObjects, Convert, CoreGeometry, CoreOps, ExtendCells, HashTable, PWPins, PWRoute, Rope, RouteUtil, RTBasic, SCChanUtil, SCInstUtil, SCNetUtil, SCRoutePinsUtil, SCRowUtil, SCUtil, Sinix, SinixOps
EXPORTS SCPrivate
SHARES SC = BEGIN
DetailRoute:
PUBLIC
PROC [handle:
SC.Handle, viaTable: HashTable.Table]
RETURNS [result:
SC.Result] = {
lgRows: SCPrivate.LgRows ← NARROW[handle.layoutData, SCPrivate.LayoutData].lgRows;
SCInstUtil.AllOffsets[handle];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
PrepareLogicRoute[handle];
result ← NEW[SC.ResultRec ← [handle: handle, object: RouteRows[handle, viaTable]]];
SCInstUtil.AsgnChanPos[handle];
[] ← SCUtil.WriteResults["End detailed routing\n", handle, 0]};
CreatePinsForChannel:
PROC [handle:
SC.Handle, rowChan: SCPrivate.RowChan] = {
AllPins: SCInstUtil.EachPinProc = {
[instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet]
IF netPin.net #
NIL
THEN {
IF side = SCInstUtil.PosOf[instance, netPin.pin].sideOn
THEN {
alwaysUse: BOOLEAN ← (chan = 1 OR chan = rowChans.count) AND netPin.net.externNet = externalNet AND SCNetUtil.ExitOnSide[handle, netPin.net, side];
rowOffset: SC.Number ← lgRow.rowOrg.p - lgRows.horzRowOrg;
rect: CD.Rect ← SCInstUtil.RotateRect[instance, netPin.pin.rect];
position: CD.Position ← SCUtil.PQToXY[handle, [p: rowOffset + instance.offset + rect.x1, q: rect.y1]];
SCRoutePinsUtil.EnterPin[rect, position, netPin, RTBasic.OtherSide[side], lgRow.shell, alwaysUse]}}};
ExternalPins: SCInstUtil.EachPinProc = {
[instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet]
IF netPin.net #
NIL
AND netPin.net.externNet = externalNet
THEN {
IF side = SCInstUtil.PosOf[instance, netPin.pin].sideOn
AND SCNetUtil.ExitOnSide[handle, netPin.net, side]
THEN {
rowOffset: SC.Number ← lgRow.rowOrg.p - lgRows.horzRowOrg;
rect: CD.Rect ← SCInstUtil.RotateRect[instance, netPin.pin.rect];
position: CD.Position ← SCUtil.PQToXY[handle, [p: rowOffset + instance.offset + rect.x1, q: rect.y1]];
SCRoutePinsUtil.EnterPin[rect, position, netPin, RTBasic.OtherSide[side], lgRow.shell, TRUE]}}};
InternalPinsInstance: SCRowUtil.EachInstProc = {
[] ← SCInstUtil.EnumeratePinsOnInst[instance, AllPins]};
ExternalPinsInstance: SCRowUtil.EachInstProc = {
[] ← SCInstUtil.EnumeratePinsOnInst[instance, ExternalPins]};
lgRows: SCPrivate.LgRows ← NARROW[handle.layoutData, SCPrivate.LayoutData].lgRows;
rowChans: SCPrivate.RowChans ← NARROW[handle.layoutData, SCPrivate.LayoutData].rowChans;
chan: SCPrivate.MaxChanSr ← rowChan.chanNum;
side: SC.Side;
lgRow: SCPrivate.LgRow;
SELECT
TRUE
FROM
chan = 1
AND lgRows.count = 1 => {
only one row, must route one exterior channel. Choose the bottom one
side ← bottom; lgRow ← lgRows.rows[1];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance]};
chan = 1 => {
bottom exterior channel, transfer the exterior pins
side ← bottom; lgRow ← lgRows.rows[1];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ExternalPinsInstance]};
chan = lgRows.count + 1 => {
top exterior channel, transfer the exterior pins
side ← top; lgRow ← lgRows.rows[lgRows.count];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ExternalPinsInstance]};
ENDCASE => {
the interior channels
side ← top; lgRow ← lgRows.rows[chan - 1];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance];
side ← bottom; lgRow ← lgRows.rows[chan];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance]}};
CreateExitsForChannel:
PROC [handle:
SC.Handle, rowChan: SCPrivate.RowChan] = {
ExitPins: SCChanUtil.EachExitProc = {
PROC [exitNum: SCPrivate.MaxExitsSr, lrSide: SCPrivate.LRSide, rowChan: SCPrivate.RowChan, exit: SCPrivate.Exit] RETURNS [quit: BOOL ← FALSE]
SCRoutePinsUtil.EnterExit[exit, lrSide, cell]};
EachSide: SCRowUtil.EachSideProc ~ {
[side: SC.Side, bpRow: SCPrivate.BpRow] RETURNS [quit: BOOL ← FALSE]
IF side = left
OR side = right
THEN {
cell ← CDCells.CreateEmptyCell[];
[] ← SCChanUtil.EnumerateExits[handle, rowChan, side, ExitPins];
rowChan.exitCells[side] ← cell}};
cell: CD.Object;
[] ← SCRowUtil.EnumerateSides[handle, EachSide]};
FinishExitsForChannel:
PROC [handle:
SC.Handle, rowChan: SCPrivate.RowChan] = {
EachSide: SCRowUtil.EachSideProc ~ {
[side: SC.Side, bpRow: SCPrivate.BpRow] RETURNS [quit: BOOL ← FALSE]
IF side = left
OR side = right
THEN {
rect: CD.Rect;
cell: CD.Object ← rowChan.exitCells[side];
SELECT side
FROM
-- pw needs a better way of finding sides
left => rect ← [-trunkWidth, -trunkWidth, trunkWidth, 2*trunkWidth];
right => rect ← [0, -trunkWidth, 2*trunkWidth, 2*trunkWidth];
ENDCASE;
CDCells.SetInterestRect[design: NIL, cell: cell, r: rect]; -- set interestRect of cell
RTBasic.RepositionCell[cell]}};
trunkWidth: SC.Number ← handle.rules.rowRules.trunkWidth;
[] ← SCRowUtil.EnumerateSides[handle, EachSide]};
PrepareLogicRoute:
PROC [handle:
SC.Handle] = {
ForEachRow: SCRowUtil.EachRowProc = {
[row: SCPrivate.MaxRowSr, lgRow: SCPrivate.LgRow] RETURNS [quit: BOOL ← FALSE]
ExtendPin: ExtendCells.ExtendSegmentProc = {
PROC [wire: Wire, min, max: INT, layer: Layer, side: Side, extension: INT] RETURNS [Object ← NIL];
obj: CD.Object ← CDCells.CreateEmptyCell[];
connection: CD.Object ← CDRects.CreateRect[[extension, max-min], layer];
connectionInst: CD.Instance ← RouteUtil.Include[obj, connection];
name: Rope.ROPE ← CoreOps.GetShortWireName[wire];
CDProperties.PutProp[connectionInst, $SignalName, name];
CDProperties.PutProp[connectionInst, $InstanceName, name];
RTBasic.RepositionCell[obj];
leftObject, rightObject: CD.Object ← NIL;
maxRowWidth: CD.Number ← layoutData.lgRows.maxRowWidth;
rowLength: SC.Number ← lgRow.size.p;
rowOffset: SC.Number ← lgRow.rowOrg.p - layoutData.lgRows.horzRowOrg;
IF rowOffset > 0
THEN {
-- needs filler on left to extend power
leftWirePins: LIST OF ExtendCells.WirePin ← BuildWirePins[handle: handle, cellType: lgRow.lgsOnRow[1].object.cellType, objSide: left];
leftObject ← ExtendCells.ExtendObject[wirePins: leftWirePins, size: [rowOffset, lgRow.size.q], side: left, extendProc: ExtendPin];
RTBasic.SetCDCellName[leftObject, Rope.Cat[handle.name, "Left", Convert.RopeFromInt[lgRow.rowNum]]]};
IF rowOffset + rowLength < maxRowWidth
THEN {
-- needs filler on right to extend power
rightWirePins: LIST OF ExtendCells.WirePin ← BuildWirePins[handle: handle, cellType: lgRow.lgsOnRow[lgRow.nLgsOnRow].object.cellType, objSide: right];
rightObject ← ExtendCells.ExtendObject[wirePins: rightWirePins, size: [maxRowWidth - (rowOffset + rowLength), lgRow.size.q], side: right, extendProc: ExtendPin];
RTBasic.SetCDCellName[rightObject, Rope.Cat[handle.name, "Right", Convert.RopeFromInt[lgRow.rowNum]]]};
lgRow.shell ← CDCells.CreateEmptyCell[];
lgRow.cdOb ← ConstructRow[handle, leftObject, lgRow, rightObject]};
ForEachChannel: SCChanUtil.EachRowChanProc = {
PROC [chan: SCPrivate.MaxChanSr, rowChan: SCPrivate.RowChan] RETURNS [quit: BOOL ← FALSE]
useThisChan: BOOLEAN ← (1 < rowChan.chanNum AND rowChan.chanNum < rowChans.count) OR (rowChan.chanNum = 1 AND rowChans.count = 2);
SCRoutePinsUtil.InitGetChanPins[handle];
IF useThisChan THEN CreateExitsForChannel[handle: handle, rowChan: rowChan];
CreatePinsForChannel[handle: handle, rowChan: rowChan];
SCRoutePinsUtil.EnterNetDat[handle, chan, MakeNetPin, MakeExit, NIL];
SCRoutePinsUtil.TermGetChanPins[handle];
IF useThisChan THEN FinishExitsForChannel[handle: handle, rowChan: rowChan]};
MakeExit: SCRoutePinsUtil.ExitProc = {
PROC [exit: SCPrivate.Exit, cell: CD.Object, trunkWidth: SC.Number, side: SCPrivate.LRSide];
pinOb: CD.Object ← CDSymbolicObjects.CreatePin[size: [trunkWidth, exit.net.trunkWidth]];
pinInst: CD.Instance ← RouteUtil.Include[cell: cell, ob: pinOb];
CDSymbolicObjects.SetName[pinInst, exit.net.name];
CDSymbolicObjects.SetLayer[pinInst, exit.layer]};
MakeNetPin: SCRoutePinsUtil.PinProc = {
PROC [rect: SC.Rect, position: CD.Position, netPin: SCPrivate.PinNet, cell: CD.Object];
pinOb: CD.Object ← CDSymbolicObjects.CreatePin[CDBasics.SizeOfRect[rect]];
pinInst: CD.Instance ← RouteUtil.Include[cell: cell, ob: pinOb, position: position];
CDSymbolicObjects.SetName[pinInst, netPin.net.name];
CDSymbolicObjects.SetLayer[pinInst, netPin.pin.layer]};
FinishRow: SCRowUtil.EachRowProc = {
[row: SCPrivate.MaxRowSr, lgRow: SCPrivate.LgRow] RETURNS [quit: BOOL ← FALSE]
rect: CD.Rect ← [0, 0, layoutData.lgRows.maxRowWidth, lgRow.size.q];
CDCells.SetInterestRect[design: NIL, cell: lgRow.shell, r: rect]; -- set interestRect of cell
RTBasic.RepositionCell[lgRow.shell]};
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
rowChans: SCPrivate.RowChans ← layoutData.rowChans;
[] ← SCRowUtil.EnumerateRows[handle: handle, eachRow: ForEachRow];
[] ← SCChanUtil.EnumerateRowChans[handle: handle, eachRowChan: ForEachChannel];
[] ← SCRowUtil.EnumerateRows[handle: handle, eachRow: FinishRow]};
ConstructRow:
PROC [handle:
SC.Handle, leftObject:
CD.Object, lgRow: SCPrivate.LgRow, rightObject:
CD.Object]
RETURNS [row:
CD.Object ← CDCells.CreateEmptyCell[]] ~ {
ForEachInstance: SCRowUtil.EachInstProc = {
[pos: NAT, instance: SCPrivate.Instance] RETURNS [quit: BOOL ← FALSE]
object: CD.Object ← instance.object.cdOb;
instOrientation: CD.Orientation ← SCInstUtil.CDOrien[instance];
instPosition: CD.Position ← CDOps.FitObjectI[ob: object, location: [offset, 0], orientation: instOrientation].off;
cdInst: CD.Instance ← RouteUtil.Include[cell: row, ob: object, position: instPosition, orientation: instOrientation];
CDProperties.PutInstanceProp[cdInst, $InstanceName, instance.name];
CDProperties.PutProp[cdInst, $StopEnumerateDeepPins, $StopEnumerateDeepPins];
offset ← offset + RTBasic.IRSize[object].x};
offset: SC.Number ← 0;
IF leftObject #
NIL
THEN {
[] ← RouteUtil.Include[cell: row, ob: leftObject, position: [offset, 0]];
offset ← offset + RTBasic.IRSize[leftObject].x};
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ForEachInstance];
IF rightObject #
NIL
THEN {
[] ← RouteUtil.Include[cell: row, ob: rightObject, position: [offset, 0]];
offset ← offset + RTBasic.IRSize[rightObject].x};
RTBasic.SetCDCellName[row, Rope.Cat[handle.name, "IntRow", Convert.RopeFromInt[lgRow.rowNum]]];
RTBasic.RepositionCell[row]};
RouteRows:
PROC [handle:
SC.Handle, viaTable: HashTable.Table]
RETURNS [obj:
CD.Object ← CDCells.CreateEmptyCell[]] ~ {
route the channels and include rows and channels into the layout
IncludeRow:
PROC [rowNum:
NAT] ~ {
include the specified row in the object being constructed
RowSidePublics: CoreGeometry.EachWirePinProc ~ {
PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL ← FALSE];
add decoration for public; wire is an unbound wire
pin: SCPrivate.PublicPin ← MakeSidePin[wire, min, max, side, layer];
IF pin # NIL THEN lgRow.publics ← CONS[pin, lgRow.publics]};
lgRow: SCPrivate.LgRow ← lgRows.rows[rowNum];
row: CD.Object ← lgRow.cdOb;
rowInst: CD.Instance ← RouteUtil.Include[cell: obj, ob: row, position: [0, offset]];
[] ← CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, lgRow.lgsOnRow[1].object.cellType, RowSidePublics];
[] ← CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, lgRow.lgsOnRow[1].object.cellType, RowSidePublics];
CDProperties.PutInstanceProp[rowInst, $InstanceName, Rope.Cat["Row", Convert.RopeFromInt[rowNum]]];
IF rowNum = 1 THEN AddTBPublics[handle, lgRow, bottom, 0];
offset ← offset + lgRow.size.q;
IF rowNum = lgRows.count THEN AddTBPublics[handle, lgRow, top, offset]};
RouteChannel:
PROC [bottomRow, topRow:
CD.Object, rowChan: SCPrivate.RowChan] ~ {
route the specified channeland include in the object being constructed
ChanPublics: CoreGeometry.EachWirePinProc ~ {
PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL ← FALSE];
add decoration for public; wire is an unbound wire
pin: SCPrivate.PublicPin ← MakeSidePin[wire, min, max, side, layer];
IF pin # NIL THEN rowChan.publics ← CONS[pin, rowChan.publics]};
result: Route.RoutingResult ← PWRoute.DoRoute[bottomRow, topRow, rowChan.exitCells[left], rowChan.exitCells[right], routerParams, FALSE, channel];
channel: CD.Object ← PWRoute.GetRouting[result, NIL, routerParams];
chanInst: CD.Instance ← RouteUtil.Include[cell: obj, ob: channel, position: [0, offset]];
cellType: Core.CellType ← NARROW [Sinix.Extract[channel, mode].result];
[] ← CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, cellType, ChanPublics];
CDProperties.PutInstanceProp[chanInst, $InstanceName, Rope.Cat[handle.name, "Chan", Convert.RopeFromInt[rowChan.chanNum]]];
offset ← offset + RTBasic.IRSize[channel].y;
rowChan.routing ← result;
rowChan.chanWidth ← RTBasic.IRSize[channel].y};
MakeSidePin:
PROC [wire: Core.Wire, min, max:
INT, side: CoreGeometry.Side, layer:
CD.Layer]
RETURNS [pin: SCPrivate.PublicPin ←
NIL] ~ {
make a public for decoration
IF side = left
OR side = right
THEN {
name: Rope.ROPE ← CoreOps.GetShortWireName[wire];
net: SCPrivate.Net ← SCUtil.FindNet[handle, name];
trunkWidth: SC.Number ← handle.rules.rowRules.trunkWidth;
position:
CD.Position ←
[IF side = left THEN 0 ELSE lgRows.maxRowWidth - trunkWidth, offset + min];
rect: CD.Object ← CDRects.CreateRect[[trunkWidth, max-min], layer];
trans: CD.Transformation ← [position, original];
RETURN[NEW[SCPrivate.PublicPinRec ← [net.wire, rect, trans]]]}};
EachChannel: SCChanUtil.EachRowChanProc ~ {
[chan: SCPrivate.MaxChanSr, rowChan: SCPrivate.RowChan] RETURNS [quit: BOOL ← FALSE]
route each channel
IF chan # 1 THEN IncludeRow[chan-1];
IF chan = 1
AND rowChans.count = 2
THEN {
do the bottom channel if it is the only one
TransferExits: ExtendCells.ExtendSegmentProc = {
PROC [wire: Wire, min, max: INT, layer: Layer, side: Side, extension: INT] RETURNS [Object ← NIL];
net: SCPrivate.Net ← SCUtil.FindNetByWire[handle, wire];
xfer: BOOLEAN ← net.externNet = externalNet AND SCNetUtil.ExitOnSide[handle, net, bottom] AND side = bottom;
IF xfer
THEN {
pinInst: CD.Instance;
pinSize: CD.Position ← [max - min, handle.rules.rowRules.branchWidth];
object: CD.Object ← CDCells.CreateEmptyCell[];
pinInst ← RouteUtil.Include[cell: object, ob: CDSymbolicObjects.CreatePin[pinSize], position: [0, extension - handle.rules.rowRules.branchWidth]];
CDSymbolicObjects.SetName[pinInst, net.name];
CDSymbolicObjects.SetLayer[pinInst, layer];
RTBasic.RepositionCell[object];
RETURN[object]}};
topRow: CD.Object ← lgRows.rows[chan].shell;
wirePins: LIST OF ExtendCells.WirePin ← BuildWirePinsFromObj[handle: handle, template: topRow, objSide: bottom];
bottomRow: CD.Object ← ExtendCells.ExtendObject[wirePins: wirePins, size: RTBasic.IRSize[topRow], side: bottom, extendProc: TransferExits];
RouteChannel[bottomRow, topRow, rowChan]};
IF chan # rowChans.count
AND chan # 1
THEN {
do the interior channels
bottomRow: CD.Object ← lgRows.rows[chan-1].shell;
topRow: CD.Object ← lgRows.rows[chan].shell;
RouteChannel[bottomRow, topRow, rowChan]}};
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
rowChans: SCPrivate.RowChans ← layoutData.rowChans;
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
mode: Sinix.Mode ← SinixOps.GetExtractMode[handle.rules.technology];
table: HashTable.Table ← IF viaTable # NIL THEN viaTable ELSE HashTable.Create[equal: EqualProc, hash: HashProc];
routerParams: PWRoute.RouterParams ←
NEW[PWRoute.RouterParamsRec
← [trunkLayer: handle.rules.horizLayer,
branchLayer: handle.rules.vertLayer,
technologyKey: NARROW[handle.rules.technology, CD.Technology].key,
signalBreakAtExit: FALSE,
signalSinglePinNets: TRUE,
viaTable: table]];
offset: SC.Number ← 0;
parms: SCPrivate.Parms ← NARROW[handle.parms];
parms.viaTable ← table;
[] ← SCChanUtil.EnumerateRowChans[handle, EachChannel];
RTBasic.SetCDCellName[obj, handle.name];
RTBasic.RepositionCell[obj]};
BuildWirePins:
PROC [handle:
SC.Handle, cellType: Core.CellType, objSide: CoreGeometry.Side]
RETURNS [wirePins:
LIST
OF ExtendCells.WirePin ←
NIL] ~ {
extract a list of wirePins from side of template
EachWirePin: CoreGeometry.EachWirePinProc = {
IF side=objSide THEN wirePins ← CONS [[wire, min, max, layer], wirePins]};
mode: Sinix.Mode = SinixOps.GetExtractMode[handle.rules.technology];
[] ← CoreGeometry.EnumerateWireSides[mode.decoration, cellType, EachWirePin]};
BuildWirePinsFromObj:
PROC [handle:
SC.Handle, template:
CD.Object, objSide: PWPins.Side]
RETURNS [wirePins:
LIST
OF ExtendCells.WirePin ←
NIL] ~ {
extract a list of wirePins from side of template
TEMPORARY
EachPin: PWPins.
InstanceEnumerator = {
IF objSide=PWPins.GetSide[template, inst]
THEN {
layer: CD.Layer ← CDSymbolicObjects.GetLayer[inst];
net: SCPrivate.Net ← SCUtil.FindNet[handle, CDSymbolicObjects.GetName[inst]];
rect: CD.Rect ← CDSymbolicObjects.Denotes[inst];
min: CD.Number ← IF objSide = top OR objSide = bottom THEN rect.x1 ELSE rect.y1;
max: CD.Number ← IF objSide = top OR objSide = bottom THEN rect.x2 ELSE rect.y2;
wirePins ← CONS [[net.wire, min, max, layer], wirePins]}};
[] ← PWPins.EnumerateEdgePins[template, EachPin]};
AddTBPublics:
PROC[handle:
SC.Handle, lgRow: SCPrivate.LgRow, objSide: PWPins.Side, yCord:
CD.Number] ~ {
add pins on side of template to public decoration
TEMPORARY
EachPin: PWPins.
InstanceEnumerator = {
IF objSide=PWPins.GetSide[lgRow.shell, inst]
THEN {
layer: CD.Layer ← CDSymbolicObjects.GetLayer[inst];
net: SCPrivate.Net ← SCUtil.FindNet[handle, CDSymbolicObjects.GetName[inst]];
trunkWidth: SC.Number ← handle.rules.rowRules.trunkWidth;
denote: CD.Rect ← CDSymbolicObjects.Denotes[inst];
public: CD.Object ← CDRects.CreateRect[[denote.x2-denote.x1, trunkWidth], layer];
position:
CD.Position ←
[denote.x1, IF objSide = bottom THEN yCord ELSE yCord - trunkWidth];
lgRow.publics ← CONS[NEW[SCPrivate.PublicPinRec ← [net.wire, public, [position, original]]], lgRow.publics]}};
[] ← PWPins.EnumerateEdgePins[lgRow.shell, EachPin]};
EqualProc:
PROC [k1, k2: HashTable.Key]
RETURNS [eq:
BOOL] = {
p1: Route.Position ← NARROW[k1, REF Route.Position]^;
p2: Route.Position ← NARROW[k2, REF Route.Position]^;
eq ← p1.x = p2.x AND p1.y = p2.y};
HashProc:
PROC [k: HashTable.Key]
RETURNS [hash:
CARDINAL] = {
size: Route.Position ← NARROW[k, REF Route.Position]^;
hash ← size.x + size.y};