Initialization
CreateHandle:
PUBLIC
PROC [inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left:
CD.Object, connections: Connections.Table, parms: Cabbage.PadRingParams, name: Rope.
ROPE, routeType: CabbagePrivate.RouteType]
RETURNS [handle: CabbagePrivate.Handle]~ {
handle ← NEW[CabbagePrivate.HandleRec ← [name: name, connections: connections, parms: parms]];
handle.routeType ← routeType;
handle.rules ← CreateRouterParms[parms];
handle.powerDistribution ← NEW[CabbagePrivate.PowerDistributionRec];
handle.globalRouting ← NEW[CabbagePrivate.GlobalRoutingRec];
IF routeType = normal
THEN
handle.detailedRouting ← NEW[CabbagePrivate.DetailedRoutingRec]
ELSE
handle.detailedRoutingPL ← NEW[CabbagePrivate.DetailedRoutingPLRec];
GetSizes[handle, inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left];
IF handle.connections # NIL THEN InitWires[handle];
AssignPositions[handle]};
Define the routing design rules. technologyKey values are predefinded for now. horizLayer, vertLayer should be "poly", "metal" or "metal2".
CreateRouterParms:
PROC [parms: Cabbage.PadRingParams]
RETURNS [designRules: CabbagePrivate.DesignRules] = {
hLayer: CD.Layer ← CDSimpleRules.GetLayer[parms.technologyKey, parms.horizLayer];
vLayer: CD.Layer ← CDSimpleRules.GetLayer[parms.technologyKey, parms.vertLayer];
designRules ← NEW[CabbagePrivate.DesignRulesRec];
designRules.horizInsideParms.parms ← Route.DefaultDesignRulesParameters[technologyHint: parms.technologyKey, horizLayer: hLayer, vertLayer: vLayer, trunkDirection: horizontal];
designRules.vertInsideParms.parms ← Route.DefaultDesignRulesParameters[technologyHint: parms.technologyKey, horizLayer: hLayer, vertLayer: vLayer, trunkDirection: vertical];
designRules.horizOutsideParms.parms ← Route.DefaultDesignRulesParameters[technologyHint: parms.technologyKey, horizLayer: vLayer, vertLayer: hLayer, trunkDirection: horizontal];
designRules.vertOutsideParms.parms ← Route.DefaultDesignRulesParameters[technologyHint: parms.technologyKey, horizLayer: vLayer, vertLayer: hLayer, trunkDirection: vertical];
designRules.horizInsideParms.rules ← Route.DefaultDesignRules[designRules.horizInsideParms.parms];
designRules.vertInsideParms.rules ← Route.DefaultDesignRules[designRules.vertInsideParms.parms];
designRules.horizOutsideParms.rules ← Route.DefaultDesignRules[designRules.horizOutsideParms.parms];
designRules.vertOutsideParms.rules ← Route.DefaultDesignRules[designRules.vertOutsideParms.parms];
put in wider spacings at sides of channels
designRules.horizOutsideParms.rules.trunkToEdge ← designRules.horizOutsideParms.rules.trunkToTrunk;
designRules.vertOutsideParms.rules.trunkToEdge ← designRules.vertOutsideParms.rules.trunkToTrunk};
GetSizes:
PROC [handle: CabbagePrivate.Handle, inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left:
CD.Object] ~ {
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
handle.inner ← GetObjectDescription[inner, l, l];
handle.bottom ← GetObjectDescription[bottom, l, l];
handle.right ← GetObjectDescription[right, l, l];
handle.top ← GetObjectDescription[top, l, l];
handle.left ← GetObjectDescription[left, l, l];
handle.bottomLeft ← GetObjectDescription[bottomLeft, handle.left.size.x, handle.bottom.size.y];
handle.bottomRight ← GetObjectDescription[bottomRight, handle.right.size.x, handle.bottom.size.y];
handle.topRight ← GetObjectDescription[topRight, handle.right.size.x, handle.top.size.y];
handle.topLeft ← GetObjectDescription[topLeft, handle.left.size.x, handle.top.size.y]};
GetObjectDescription:
PROC [object:
CD.Object, minX, minY:
INT]
RETURNS [desc: CabbagePrivate.ObjectDescription] ~ {
desc.object ← object;
IF object =
NIL
THEN desc.size ← [minX, minY]
ELSE {
desc.size ← RTBasic.IRSize[object];
desc.size.x ← MAX[minX, desc.size.x];
desc.size.y ← MAX[minY, desc.size.y]}};
AssignPositions:
PROC [handle: CabbagePrivate.Handle] ~ {
hBottom, hTop, vLeft, vRight: INT;
[hBottom, hTop, vLeft, vRight] ← GetSize[handle];
handle.bottom.origin ← [handle.bottomLeft.size.x + (handle.size.x - hBottom)/2, 0];
handle.right.origin ← [handle.size.x - handle.right.size.x, handle.bottomRight.size.y + (handle.size.y - vRight)/2];
handle.top.origin ← [handle.topLeft.size.x + (handle.size.x - hTop)/2, handle.size.y - handle.top.size.y];
handle.left.origin ← [0, handle.bottomLeft.size.y + (handle.size.y - vLeft)/2];
DoCorners[handle];
DoRoutingAreas[handle]};
InitWires:
PROC [handle: CabbagePrivate.Handle] ~ {
LookForGndAndVdd: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
IF Rope.Equal[net.name, "Vdd"] THEN handle.vddNet ← net
ELSE IF Rope.Equal[net.name, "Gnd"] THEN handle.gndNet ← net};
EachNet: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
newNet.width ← MAX[newNet.width, segment.range.max - segment.range.min]};
newNet: Connections.Net ← NEW[Connections.NetRec ← [name: net.name, width: net.width]];
IF net.width <= 0
THEN
[] ← Connections.EnumerateSegments[net, EachSegment];
[] ← Connections.Store[handle.widthTable, net.name, newNet]};
handle.widthTable ← Connections.CreateForRopes[];
[] ← Connections.EnumerateNets[handle.connections, LookForGndAndVdd];
[] ← Connections.EnumerateNets[handle.connections, EachNet]};
CheckInnerPos:
PUBLIC
PROC [handle: CabbagePrivate.Handle, innerPos:
CD.Position] ~ {
actualInnerPos: CD.Position ← innerPos;
lowerY: INT ← handle.bottom.origin.y + handle.bottom.size.y;
upperY: INT ← handle.top.origin.y - handle.inner.size.y;
lowerX: INT ← handle.left.origin.x + handle.left.size.x;
upperX: INT ← handle.right.origin.x - handle.inner.size.x;
IF ~((lowerX <= innerPos.x
AND innerPos.x <= upperX)
AND (lowerY <= innerPos.y
AND innerPos.y <= upperY))
THEN {
Cabbage.Signal[callingError, "The position specified for the inner object (innerPos) is invalid."];
actualInnerPos ← [(lowerX + upperX)/2, (lowerY + lowerY)/2]};
handle.inner.origin ← actualInnerPos};
Power Distribution
DistributePower:
PUBLIC
PROC [handle: CabbagePrivate.Handle] ~ {
make the power distribution channels
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
horizInsideRules: Route.DesignRules ← handle.rules.horizInsideParms.rules;
vertInsideRules: Route.DesignRules ← handle.rules.vertInsideParms.rules;
horizOutsideRules: Route.DesignRules ← handle.rules.horizOutsideParms.rules;
vertOutsideRules: Route.DesignRules ← handle.rules.vertOutsideParms.rules;
horizSpace: INT ← 2*horizOutsideRules.trunkToTrunk + horizOutsideRules.trunkSpacing + horizInsideRules.contactSize + horizInsideRules.trunkSpacing + 2*horizInsideRules.trunkToTrunk;
vertSpace: INT ← 2*vertOutsideRules.trunkToTrunk + vertOutsideRules.trunkSpacing + vertInsideRules.contactSize + vertInsideRules.trunkSpacing + 2*vertInsideRules.trunkToTrunk;
trunkLRWidth: INT ← (handle.parms.powerLRCellWidth * l - vertSpace)/2;
trunkBTWidth: INT ← (handle.parms.powerBTCellWidth * l - horizSpace)/2;
RouteOutsideChannels[handle];
AssignPositions[handle];
RouteOutsideSwitchBoxes[handle];
ConstructPowerChannels[handle, trunkLRWidth, trunkBTWidth];
ConstructPowerCorners[handle, trunkLRWidth, trunkBTWidth];
AssignPositions[handle];};
RouteOutsideChannels:
PROC [handle: CabbagePrivate.Handle] ~ {
route the outside channels; move signal pins on the outer from the shadow of the power pins on the inner.
horizOutsideParms and vertOutsideParms specify the routing for the specified direction; horizRange and vertRange describe the projection of the inner onto the pad ring
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
outerBTChanWidth: INT ← handle.parms.outerBTChanWidth * l;
outerLRChanWidth: INT ← handle.parms.outerLRChanWidth * l;
powerLRCellWidth: INT ← handle.parms.powerLRCellWidth * l;
powerBTCellWidth: INT ← handle.parms.powerBTCellWidth * l;
horizOutsideParms: CabbagePrivate.ParmSet ← handle.rules.horizOutsideParms;
vertOutsideParms: CabbagePrivate.ParmSet ← handle.rules.vertOutsideParms;
horizOuterRange: Connections.Range ← [handle.left.origin.x + handle.left.size.x + outerLRChanWidth, handle.right.origin.x - outerLRChanWidth];
horizMidRange: Connections.Range ← [horizOuterRange.min + powerLRCellWidth, horizOuterRange.max - powerLRCellWidth];
vertInnerRange: Connections.Range ← [handle.inner.origin.y, handle.inner.origin.y + handle.inner.size.y];
vertOuterRange: Connections.Range ← [handle.bottom.origin.y + handle.bottom.size.y + outerBTChanWidth, handle.top.origin.y - outerBTChanWidth];
vertMidRange: Connections.Range ← [vertOuterRange.min + powerBTCellWidth, vertOuterRange.max - powerBTCellWidth];
route the outside channels
handle.powerDistribution.channels[bottom] ← RouteOutsideChannel[handle, handle.bottom, bottom, horizMidRange, horizMidRange, horizOuterRange, outerBTChanWidth, horizOutsideParms];
handle.powerDistribution.channels[right] ← RouteOutsideChannel[handle, handle.right, right, vertInnerRange, vertMidRange, vertOuterRange, outerLRChanWidth, vertOutsideParms];
handle.powerDistribution.channels[top] ← RouteOutsideChannel[handle, handle.top, top, horizMidRange, horizMidRange, horizOuterRange, outerBTChanWidth, horizOutsideParms];
handle.powerDistribution.channels[left] ← RouteOutsideChannel[handle, handle.left, left, vertInnerRange, vertMidRange, vertOuterRange, outerLRChanWidth, vertOutsideParms]};
RouteOutsideChannel:
PROC [handle: CabbagePrivate.Handle,
outerObject: CabbagePrivate.ObjectDescription, -- outer Cabbage object
side: DABasics.Side, -- the side of chip for which this channel is being constructed
innerRange, midRange, outerRange: Connections.Range, -- innerRange is the span of the inner cell; midRange is range of power cell, outerRange the span of the appropriate power routing cell; innerRange may equal outerRange
chWidth: INT, -- the only permitted width for channel
parms: CabbagePrivate.ParmSet]
RETURNS [channel: CabbagePrivate.Channel] ~ {
route one of the outside channels
InitNet: Connections.EachNetAction = {net.netDat ← NIL};
intermediateResult: Route.IntermediateResult;
retrieveRect: REF DABasics.Rect;
rect: DABasics.Rect;
otherSide: DABasics.Side ← RTBasic.OtherSide[side];
blSide: DABasics.Side ← IF side=bottom OR side=top THEN left ELSE bottom;
actualSize: INT; -- the resulting channel width
clear the routing specification for this channel
[] ← Connections.EnumerateNets[handle.connections, InitNet];
construct the bonding objects to pass to the channel router
AddMappedPinsFromObject[handle, outerObject, otherSide, innerRange, midRange, outerRange, parms, "--L", "--U"];
AddPinsFromObject3Part[handle, outerObject, otherSide, midRange, outerRange, parms, TRUE, makeTwoPinPowerNets, busPower, "--L", "--", "--U"];
AddPinsFromOutsideEnd[handle, outerObject, otherSide, blSide, outerRange, parms, "--L"];
AddPinsFromOutsideEnd[handle, outerObject, otherSide, RTBasic.OtherSide[blSide], outerRange, parms, "--U"];
intermediateResult ← Route.ChannelRoute[enumerateNets: EnumerateChannelNets, min: outerRange.min, max: outerRange.max, rulesParameters: parms.parms, rules: parms.rules, name: NIL, enumerateObstructions: NIL, channelData: handle, optimization: handle.parms.opt, signalSinglePinNets: handle.parms.signalSinglePinNets];
rect ← intermediateResult.resultData.routingRect;
SELECT side
FROM
bottom => {
dist: INT ← MIN[rect.y1, rect.y2 - chWidth];
retrieveRect ← NEW[DABasics.Rect ← [rect.x1, dist, rect.x2, rect.y2]]};
right => {
dist: INT ← MAX[rect.x2, rect.x1 + chWidth];
retrieveRect ← NEW[DABasics.Rect ← [rect.x1, rect.y1, dist, rect.y2]]};
top => {
dist: INT ← MAX[rect.y2, rect.y1 + chWidth];
retrieveRect ← NEW[DABasics.Rect ← [rect.x1, rect.y1, rect.x2, dist]]};
left => {
dist: INT ← MIN[rect.x1, rect.x2 - chWidth];
retrieveRect ← NEW[DABasics.Rect ← [dist, rect.y1, rect.x2, rect.y2]]};
ENDCASE;
channel.object ← Route.ChannelRetrieve[intermediateResult: intermediateResult, enumerateNets: EnumerateChannelNets, brokenNets: NIL, channelData: handle, retrieveRect: retrieveRect].object;
compare channel.size to required channel width
channel.size ← RTBasic.IRSize[channel.object];
actualSize ←
SELECT side
FROM
bottom, top => channel.size.y,
left, right => channel.size.x,
ENDCASE => 0;
IF actualSize > chWidth THEN Cabbage.Signal[noResource, "Outer routing channel is to small. Increase outerWidth (may need to decrease powerWidth)."];
channel.cellType ← ExtractChannel[channel, parms]};
ConstructPowerChannels:
PROC [handle: CabbagePrivate.Handle, trunkLRWidth, trunkBTWidth:
INT] ~ {
construct the power routing channels; route signal pins on the outer straight accross with vias as necessary.
horizInsideParms and vertInsideParms specify the routing for the specified direction; horizRange and vertRange describe the projection of the inner onto the pad ring
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
powerLRCellWidth: INT ← handle.parms.powerLRCellWidth * l;
powerBTCellWidth: INT ← handle.parms.powerBTCellWidth * l;
powerLRWidth: INT ← handle.parms.outerLRChanWidth * l + powerLRCellWidth;
powerBTWidth: INT ← handle.parms.outerBTChanWidth * l + powerBTCellWidth;
horizInsideParms: CabbagePrivate.ParmSet ← handle.rules.horizInsideParms;
horizOutsideParms: CabbagePrivate.ParmSet ← handle.rules.horizOutsideParms;
vertInsideParms: CabbagePrivate.ParmSet ← handle.rules.vertInsideParms;
vertOutsideParms: CabbagePrivate.ParmSet ← handle.rules.vertOutsideParms;
horizOuterRange: Connections.Range ← [handle.left.origin.x + handle.left.size.x + powerLRWidth, handle.right.origin.x - powerLRWidth];
vertOuterRange: Connections.Range ← [handle.bottom.origin.y + handle.bottom.size.y + powerBTWidth, handle.top.origin.y - powerBTWidth];
construct the power distribution cells
handle.powerDistribution.power[bottom] ← RoutePower[handle, handle.powerDistribution.channels[bottom], bottom, horizOuterRange, powerBTCellWidth, trunkBTWidth, horizInsideParms, horizOutsideParms];
handle.powerDistribution.power[right] ← RoutePower[handle, handle.powerDistribution.channels[right], right, vertOuterRange, powerLRCellWidth, trunkLRWidth, vertInsideParms, vertOutsideParms];
handle.powerDistribution.power[top] ← RoutePower[handle, handle.powerDistribution.channels[top], top, horizOuterRange, powerBTCellWidth, trunkBTWidth, horizInsideParms, horizOutsideParms];
handle.powerDistribution.power[left] ← RoutePower[handle, handle.powerDistribution.channels[left], left, vertOuterRange, powerLRCellWidth, trunkLRWidth, vertInsideParms, vertOutsideParms]};
RoutePower:
PROC [handle: CabbagePrivate.Handle,
channel: CabbagePrivate.Channel, -- inner or outer channel as determined by side
chipSide: DABasics.Side, -- the side of the chip for which this channel is being constructed
range: Connections.Range, -- the span along the appropriate power routing cell
powerWidth: INT, -- the only permitted width for the power cell
trunkWidth: INT, -- the width of the power trunk
insideParms, outsideParms: CabbagePrivate.ParmSet]
RETURNS [power: CabbagePrivate.Channel] ~ {
build the power routing cell for the indicated side
connect the Vdd and Gnd pin via a bus connection and transfer the signal pins across with appropriate vias
inside and outside pins are put on the [inside|outside]Parms.rules.branchLayer
ChanPublics: CoreGeometry.EachWirePinProc ~ {
PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL ← FALSE];
store the pins on the appropriate side of the adjacent channel
OuterPowerBranch:
PROC [branchLength:
INT, branchLayer, trunkLayer:
CD.Layer] ~ {
pos:
CD.Position ←
SELECT chipSide
FROM
bottom => [adjustedRange.min, 0],
top => [adjustedRange.min, powerWidth - branchLength],
left => [0, adjustedRange.min],
right => [powerWidth - branchLength, adjustedRange.min],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
SELECT chipSide
FROM
bottom, top => AddPO[objectNets, [CDRects.CreateRect[[max-min, branchLength], branchLayer], pos], name];
left, right => AddPO[objectNets, [CDRects.CreateRect[[branchLength, max-min], branchLayer], pos], name];
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
IF branchLayer # trunkLayer
THEN {
add the via at the right place
-- irSize stuff should be remove in Cabbage25
via:
CD.Object ←
SELECT chipSide
FROM
bottom, top => RouteUtil.StitchVias[[max-min, trunkWidth], trunkLayer, branchLayer, l, NIL],
left, right => RouteUtil.StitchVias[[trunkWidth, max-min], trunkLayer, branchLayer, l, NIL],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
irSize: CD.Position ← RTBasic.IRSize[via];
xOffset:
INT ←
SELECT chipSide
FROM
bottom, top => (max-min - irSize.x)/2,
left, right => (trunkWidth - irSize.x)/2,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
yOffset:
INT ←
SELECT chipSide
FROM
bottom, top => (trunkWidth - irSize.y)/2,
left, right => (max-min - irSize.y)/2,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
viaPos:
CD.Position ←
SELECT chipSide
FROM
bottom => [adjustedRange.min + xOffset, branchLength - trunkWidth + yOffset],
top => [adjustedRange.min + xOffset, powerWidth - branchLength + yOffset],
left => [branchLength - trunkWidth + xOffset, adjustedRange.min + yOffset],
right => [powerWidth - branchLength + xOffset, adjustedRange.min + yOffset],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
AddPO[objectNets, [via, viaPos], name]}};
SignalBranch:
PROC [] ~ {
outsideLength: INT ← outsideParms.rules.trunkSpacing + distToOuter;
insideLength: INT ← insideParms.rules.trunkSpacing + distToInner;
SELECT chipSide
FROM
bottom => {
AddPO[objectNets, [CDRects.CreateRect[[max-min, outsideLength], outsideLayer], [adjustedRange.min, 0]], name];
AddPO[objectNets, [CDRects.CreateRect[[max-min, insideLength], insideLayer], [adjustedRange.min, powerWidth - insideLength]], name];
AddPO[objectNets, [RouteUtil.StitchVias[[max-min, insideParms.rules.contactSize], insideLayer, outsideLayer, l, NIL], [adjustedRange.min, outsideLength]], name]};
top => {
AddPO[objectNets, [CDRects.CreateRect[[max-min, outsideLength], outsideLayer], [adjustedRange.min, powerWidth - outsideLength]], name];
AddPO[objectNets, [CDRects.CreateRect[[max-min, insideLength], insideLayer], [adjustedRange.min, 0]], name];
AddPO[objectNets, [RouteUtil.StitchVias[[max-min, insideParms.rules.contactSize], insideLayer, outsideLayer, l, NIL], [adjustedRange.min, insideLength]], name]};
left => {
AddPO[objectNets, [CDRects.CreateRect[[outsideLength, max-min], outsideLayer], [0, adjustedRange.min]], name];
AddPO[objectNets, [CDRects.CreateRect[[insideLength, max-min], insideLayer], [powerWidth - insideLength, adjustedRange.min]], name];
AddPO[objectNets, [RouteUtil.StitchVias[[insideParms.rules.contactSize, max-min], insideLayer, outsideLayer, l, NIL], [outsideLength, adjustedRange.min]], name]};
right => {
AddPO[objectNets, [CDRects.CreateRect[[outsideLength, max-min], outsideLayer], [powerWidth - outsideLength, adjustedRange.min]], name];
AddPO[objectNets, [CDRects.CreateRect[[insideLength, max-min], insideLayer], [0, adjustedRange.min]], name];
AddPO[objectNets, [RouteUtil.StitchVias[[insideParms.rules.contactSize, max-min], insideLayer, outsideLayer, l, NIL], [insideLength, adjustedRange.min]], name]};
ENDCASE};
adjustedRange: Connections.Range ← [channelOrigin + min, channelOrigin + max];
name: Rope.ROPE ← NameFromWire[wire];
distinguish constructed Vdd and Gnd by (Vdd | Gnd)--
other nets may start with Vdd and Gnd
IF side = otherSide
THEN {
SELECT
TRUE
FROM
Rope.Equal[name, "Vdd"] => OuterPowerBranch[distToOuter, outsideLayer, insideLayer];
Rope.Equal[name, "Gnd"] => OuterPowerBranch[powerWidth - 2*insideParms.rules.trunkToTrunk, outsideLayer, outsideLayer];
ENDCASE => SignalBranch[]}};
EachNet: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
store the power pins on the appropriate side of the inner object
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
InnerPowerBranch:
PROC [branchLength:
INT, branchLayer, trunkLayer:
CD.Layer] ~ {
rectPos:
CD.Position ←
SELECT chipSide
FROM
bottom => [adjustedRange.min, powerWidth - branchLength],
top => [adjustedRange.min, 0],
left => [powerWidth - branchLength, adjustedRange.min],
right => [0, adjustedRange.min],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
SELECT chipSide
FROM
bottom, top => AddPO[objectNets, [CDRects.CreateRect[[segment.range.max-segment.range.min, branchLength], insideLayer], rectPos], net.name];
left, right => AddPO[objectNets, [CDRects.CreateRect[[branchLength, segment.range.max-segment.range.min], insideLayer], rectPos], net.name];
ENDCASE;
IF branchLayer # trunkLayer
THEN {
add the via at the right place
via:
CD.Object ←
SELECT chipSide
FROM
bottom, top => RouteUtil.StitchVias[[segment.range.max-segment.range.min, trunkWidth], trunkLayer, branchLayer, l, NIL],
left, right => RouteUtil.StitchVias[[trunkWidth, segment.range.max-segment.range.min], trunkLayer, branchLayer, l, NIL],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
irSize to go away in Cabbage25
irSize: CD.Position ← RTBasic.IRSize[via];
xOffset:
INT ←
SELECT chipSide
FROM
bottom, top => (segment.range.max-segment.range.min - irSize.x)/2,
left, right => (trunkWidth - irSize.x)/2,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
yOffset:
INT ←
SELECT chipSide
FROM
bottom, top => (trunkWidth - irSize.y)/2,
left, right => (segment.range.max-segment.range.min - irSize.y)/2,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
viaPos:
CD.Position ←
SELECT chipSide
FROM
bottom => [adjustedRange.min + xOffset, powerWidth - branchLength + yOffset],
top => [adjustedRange.min + xOffset, branchLength - trunkWidth + yOffset],
left => [powerWidth - branchLength + xOffset, adjustedRange.min + yOffset],
right => [branchLength - trunkWidth + xOffset, adjustedRange.min + yOffset],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
AddPO[objectNets, [via, viaPos], net.name]}};
adjustedRange: Connections.Range ← [innerOrigin + segment.range.min, innerOrigin + segment.range.max];
IF segment.object = handle.inner.object
AND segment.side = chipSide
THEN {
SELECT
TRUE
FROM
Rope.Equal[net.name, "Vdd"] => InnerPowerBranch[powerWidth - distToOuter, insideLayer, insideLayer];
Rope.Equal[net.name, "Gnd"] => InnerPowerBranch[distToInner, insideLayer, outsideLayer];
ENDCASE}};
[] ← Connections.EnumerateSegments[net, EachSegment]};
PowerTrunk:
PROC [position:
CD.Position, layer:
CD.Layer, name: Rope.
ROPE] ~ {
trunkSize:
CD.Position ←
SELECT chipSide
FROM
bottom, top => [range.max-range.min, trunkWidth],
left, right => [trunkWidth, range.max-range.min],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
trunk: CD.Object ← CDRects.CreateRect[trunkSize, layer];
AddPO[objectNets, [trunk, position], name]};
objectNets: SymTab.Ref ← SymTab.Create[];
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
insideLayer: CD.Layer ← insideParms.rules.branchLayer;
outsideLayer: CD.Layer ← outsideParms.rules.branchLayer;
distToInner: INT ← trunkWidth + 2*insideParms.rules.trunkToTrunk;
distToOuter: INT ← trunkWidth + 2*outsideParms.rules.trunkToTrunk;
mode: Sinix.Mode = SinixOps.GetExtractMode[insideParms.parms.technology];
otherSide: CoreGeometry.Side ←
SELECT chipSide
FROM
bottom => top, right => left, top => bottom, left=> right,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
innerOrigin:
INT ←
SELECT chipSide
FROM
bottom, top => handle.inner.origin.x,
right, left => handle.inner.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
channelOrigin:
INT ←
SELECT chipSide
FROM
bottom, top => channel.origin.x,
right, left => channel.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
interestRect:
CD.Rect ←
SELECT chipSide
FROM
bottom, top => [range.min, 0 , range.max, powerWidth],
left, right => [0, range.min , powerWidth, range.max],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
construct power trunks
innerTrunkPos:
CD.Position ←
SELECT chipSide
FROM
bottom => [range.min, powerWidth - distToInner],
top => [range.min, 2*insideParms.rules.trunkToTrunk],
left => [powerWidth - distToInner, range.min],
right => [2*insideParms.rules.trunkToTrunk, range.min],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
outerTrunkPos:
CD.Position ←
SELECT chipSide
FROM
bottom => [range.min, 2*outsideParms.rules.trunkToTrunk],
top => [range.min, powerWidth - distToOuter],
left => [2*outsideParms.rules.trunkToTrunk, range.min],
right => [powerWidth - distToOuter, range.min],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
PowerTrunk[outerTrunkPos, insideLayer, "Vdd"];
PowerTrunk[innerTrunkPos, outsideLayer, "Gnd"];
build the branches
[] ← CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, channel.cellType, ChanPublics];
[] ← Connections.EnumerateNets[handle.connections, EachNet];
power.object ← CDRoutingObjects.CreateRoutingObject
[CDRoutingObjects.CreateNodes[objectNets], interestRect];
RTBasic.RepositionCell[power.object];
power.size ← RTBasic.IRSize[power.object];
power.cellType ← ExtractChannel[power, insideParms]};
RouteOutsideSwitchBoxes:
PROC [handle: CabbagePrivate.Handle] ~ {
route the switchboxes in the outside corners. these switchboxes connect the power busses; any signal pins on the padring get transfered to the adjacent channel
vertOutsideParms: CabbagePrivate.ParmSet ← handle.rules.vertOutsideParms;
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
outerLRChanWidth: INT ← handle.parms.outerLRChanWidth * l;
outerBTChanWidth: INT ← handle.parms.outerBTChanWidth * l;
leftHorizRange: Connections.Range ← [handle.left.origin.x + handle.left.size.x, handle.left.origin.x + handle.left.size.x + outerLRChanWidth];
rightHorizRange: Connections.Range ← [handle.right.origin.x - outerLRChanWidth, handle.right.origin.x];
bottomVertRange: Connections.Range ← [handle.bottom.origin.y + handle.bottom.size.y, handle.bottom.origin.y + handle.bottom.size.y + outerBTChanWidth];
topVertRange: Connections.Range ← [handle.top.origin.y- outerBTChanWidth, handle.top.origin.y];
handle.powerDistribution.switchBoxes[bottomLeft] ← RouteOutsideSwitchBox[handle, bottomLeft, leftHorizRange, bottomVertRange, vertOutsideParms, TRUE, FALSE];
handle.powerDistribution.switchBoxes[topLeft] ← RouteOutsideSwitchBox[handle, topLeft, leftHorizRange, topVertRange, vertOutsideParms, FALSE, TRUE];
handle.powerDistribution.switchBoxes[bottomRight] ← RouteOutsideSwitchBox[handle, bottomRight, rightHorizRange, bottomVertRange, vertOutsideParms, TRUE, FALSE];
handle.powerDistribution.switchBoxes[topRight] ← RouteOutsideSwitchBox[handle, topRight, rightHorizRange, topVertRange, vertOutsideParms, FALSE, TRUE]};
RouteOutsideSwitchBox:
PROC [handle: CabbagePrivate.Handle, corner: CabbagePrivate.Corners, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet, okToDiddleLLPins, okToDiddleURPins:
BOOL]
RETURNS [switchBox: CabbagePrivate.Channel] ~ {
construct a switchbox for one of the power distribution corners
InitNet: Connections.EachNetAction = {net.netDat ← NIL};
routingRect: DABasics.Rect ← [horizRange.min, vertRange.min, horizRange.max, vertRange.max];
outerTB: RTBasic.TBSide ←
SELECT corner
FROM
bottomLeft, bottomRight => bottom,
topLeft, topRight => top,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
outerLR: RTBasic.LRSide ←
SELECT corner
FROM
bottomLeft, topLeft => left,
bottomRight, topRight => right,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
objectTB: CabbagePrivate.ObjectDescription ←
SELECT corner
FROM
bottomLeft, bottomRight => handle.bottom,
topLeft, topRight => handle.top,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
objectLR: CabbagePrivate.ObjectDescription ←
SELECT corner
FROM
bottomLeft, topLeft => handle.left,
bottomRight, topRight => handle.right,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
clear the routing specification for this channel
[] ← Connections.EnumerateNets[handle.connections, InitNet];
add pins around the switch box
AddPinsFromFullChannel[handle, handle.powerDistribution.channels[outerTB], outerLR, parms.rules.branchLayer, parms, busPower, "--TB"]; -- end of routing channel on top or bottom
AddPinsFromObject[handle, objectTB, RTBasic.OtherSide[outerTB], horizRange, parms, TRUE, busPower, "--TB"]; -- outer top or bottom object
AddPinsFromObject[handle, objectLR, RTBasic.OtherSide[outerLR], vertRange, parms, TRUE, busPower, "--LR"]; -- outer left or right object
AddPinsFromFullChannel[handle, handle.powerDistribution.channels[outerLR], outerTB, parms.rules.trunkLayer, parms, busPower, "--LR"]; -- end of outer routing channel on left or right
switchBox.object ← Route.SwitchBox[enumerateNets: EnumerateSwitchBoxNets, routingRect: routingRect, rulesParameters: parms.parms, name: handle.name, enumerateObstructions: NIL, switchBoxData: handle, optimization: handle.parms.opt, signalSinglePinNets: handle.parms.signalSinglePinNets, okToDiddleLLPins: okToDiddleLLPins, okToDiddleURPins: okToDiddleURPins].object;
switchBox.size ← RTBasic.IRSize[switchBox.object];
switchBox.cellType ← ExtractChannel[switchBox, parms]};
ConstructPowerCorners:
PROC [handle: CabbagePrivate.Handle, trunkLRWidth, trunkBTWidth:
INT] ~ {
construct the power routing for the corners;
horizInsideParms and vertInsideParms specify the routing for the specified direction; horizRange and vertRange describe the projection of the inner onto the pad ring
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
horizInsideParms: CabbagePrivate.ParmSet ← handle.rules.horizInsideParms;
horizOutsideParms: CabbagePrivate.ParmSet ← handle.rules.horizOutsideParms;
vertInsideParms: CabbagePrivate.ParmSet ← handle.rules.vertInsideParms;
vertOutsideParms: CabbagePrivate.ParmSet ← handle.rules.vertOutsideParms;
construct the corner power distribution cells
handle.powerDistribution.powerCorners[bottomLeft] ← ConstructPowerCorner[handle, bottomLeft, trunkLRWidth, trunkBTWidth, horizInsideParms, horizOutsideParms, vertInsideParms, vertOutsideParms];
handle.powerDistribution.powerCorners[topLeft] ← ConstructPowerCorner[handle, topLeft, trunkLRWidth, trunkBTWidth, horizInsideParms, horizOutsideParms, vertInsideParms, vertOutsideParms];
handle.powerDistribution.powerCorners[bottomRight] ← ConstructPowerCorner[handle, bottomRight, trunkLRWidth, trunkBTWidth, horizInsideParms, horizOutsideParms, vertInsideParms, vertOutsideParms];
handle.powerDistribution.powerCorners[topRight] ← ConstructPowerCorner[handle, topRight, trunkLRWidth, trunkBTWidth, horizInsideParms, horizOutsideParms, vertInsideParms, vertOutsideParms]};
ConstructPowerCorner:
PROC [
handle: CabbagePrivate.Handle,
corner: CabbagePrivate.Corners,
trunkLRWidth: INT,
trunkBTWidth: INT,
horizInsideParms: CabbagePrivate.ParmSet,
horizOutsideParms: CabbagePrivate.ParmSet,
vertInsideParms: CabbagePrivate.ParmSet,
vertOutsideParm: CabbagePrivate.ParmSet]
RETURNS [powerCorner: CabbagePrivate.Channel] ~ {
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
horLayer: CD.Layer ← horizInsideParms.rules.branchLayer;
verLayer: CD.Layer ← horizOutsideParms.rules.branchLayer;
trunkSz: CD.Position ← [trunkLRWidth, trunkBTWidth];
cellSz:
CD.Position ←
[handle.parms.powerLRCellWidth * l, handle.parms.powerBTCellWidth * l];
outSideDist:
CD.Position ←
[2*horizOutsideParms.rules.trunkToTrunk, 2*vertOutsideParm.rules.trunkToTrunk];
inSideDist:
CD.Position ←
[2*horizInsideParms.rules.trunkToTrunk, 2*vertInsideParms.rules.trunkToTrunk];
vddHor: CD.Object ← CDRects.CreateRect[[cellSz.x - outSideDist.x, trunkSz.y], horLayer];
gndHor: CD.Object ← CDRects.CreateRect[[trunkSz.x + inSideDist.x, trunkSz.y], verLayer];
vddVer: CD.Object ← CDRects.CreateRect[[trunkSz.x, cellSz.y - outSideDist.y], verLayer];
gndVer: CD.Object ← CDRects.CreateRect[[trunkSz.x, trunkSz.y + inSideDist.y], horLayer];
via: CD.Object ← RouteUtil.StitchVias[[trunkSz.x, trunkSz.y], horLayer, verLayer, l, NIL];
outerTrunkPos:
CD.Position ←
SELECT corner
FROM
bottomLeft => [ outSideDist.x, outSideDist.y],
topLeft => [ outSideDist.x, cellSz.y - outSideDist.y],
bottomRight => [cellSz.x - outSideDist.x, outSideDist.y],
topRight => [cellSz.x - outSideDist.x, cellSz.y- outSideDist.y],
ENDCASE => ERROR;
innerTrunkPos:
CD.Position ←
SELECT corner
FROM
bottomLeft => [cellSz.x - trunkSz.x - inSideDist.x, cellSz.y - trunkSz.y - inSideDist.y],
topLeft => [cellSz.x - trunkSz.x - inSideDist.x, trunkSz.y + inSideDist.y],
bottomRight => [ trunkSz.x + inSideDist.x, cellSz.y - trunkSz.y - inSideDist.y],
topRight => [ trunkSz.x + inSideDist.x, trunkSz.y + inSideDist.y],
ENDCASE => ERROR;
Include:
PROC[obj:
CD.Object, pos:
CD.Position, name:
Rope.
ROPE] = {
transPos:
CD.Position ← CDBasics.BaseOfRect[CDBasics.MapRect[
CD.InterestRect[obj],
(
SELECT corner
FROM
bottomLeft => [pos, original],
topLeft => [pos, CD.mirrorY],
bottomRight => [pos, mirrorX],
topRight => [pos, rotate180],
ENDCASE => ERROR)]];
AddPO[objectNets, [obj, transPos], name]};
objectNets: SymTab.Ref ← SymTab.Create[];
Include[via, outerTrunkPos, "Vdd"];
Include[vddHor, outerTrunkPos, "Vdd"];
Include[vddVer, outerTrunkPos, "Vdd"];
Include[via, innerTrunkPos, "Gnd"];
Include[gndHor, innerTrunkPos, "Gnd"];
Include[gndVer, innerTrunkPos, "Gnd"];
powerCorner.object ← CDRoutingObjects.CreateRoutingObject
[CDRoutingObjects.CreateNodes[objectNets], [0, 0, cellSz.x, cellSz.y]];
RTBasic.RepositionCell[powerCorner.object];
powerCorner.size ← RTBasic.IRSize[powerCorner.object]};
Detailed Routing
DetailedRoute:
PUBLIC
PROC [handle: CabbagePrivate.Handle] ~ {
RouteChannels[handle];
AdjustPositions[handle];
IF handle.routeType = normal THEN RouteSwitchBoxes[handle]
ELSE RouteSwitchBoxesPL[handle];
AdjustPositions[handle]};
route all of the channels or just the left and right channels depending on routeType
RouteChannels:
PROC [handle: CabbagePrivate.Handle] ~ {
horizInsideParms: CabbagePrivate.ParmSet ← handle.rules.horizInsideParms;
vertInsideParms: CabbagePrivate.ParmSet ← handle.rules.vertInsideParms;
horizRange: Connections.Range ← [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x];
vertRange: Connections.Range ← [handle.inner.origin.y, handle.inner.origin.y + handle.inner.size.y];
IF handle.routeType = normal
THEN {
handle.detailedRouting.channels[bottom] ← RouteChannel[handle, handle.powerDistribution.power[bottom], bottom, bottomLeft, bottomRight, horizRange, horizInsideParms];
handle.detailedRouting.channels[right] ← RouteChannel[handle, handle.powerDistribution.power[right], right, rightBottom, rightTop, vertRange, vertInsideParms];
handle.detailedRouting.channels[top] ← RouteChannel[handle, handle.powerDistribution.power[top], top, topLeft, topRight, horizRange, horizInsideParms];
handle.detailedRouting.channels[left] ← RouteChannel[handle, handle.powerDistribution.power[left], left, leftBottom, leftTop, vertRange, vertInsideParms]}
ELSE {
-- routeType = padLimited
handle.detailedRoutingPL.channels[right] ← RouteChannel[handle, handle.powerDistribution.power[right], right, rightBottom, rightTop, vertRange, vertInsideParms];
handle.detailedRoutingPL.channels[left] ← RouteChannel[handle, handle.powerDistribution.power[left], left, leftBottom, leftTop, vertRange, vertInsideParms]}};
route one of the channels
RouteChannel:
PROC [handle: CabbagePrivate.Handle,
boundingChannel: CabbagePrivate.Channel, -- the power channel to use
chipSide: DABasics.Side, -- the side of chip for which this channel is being constructed
llDiv, urDiv: CabbagePrivate.Division, -- the lower and upper boundries of the channel
range: Connections.Range, -- the range of interest along the channel side
parms: CabbagePrivate.ParmSet]
RETURNS [channel: CabbagePrivate.Channel] ~ {
InitNet: Connections.EachNetAction = {net.netDat ← NIL};
intermediateResult: Route.IntermediateResult;
retrieveRect: REF DABasics.Rect;
rect: DABasics.Rect;
offset: INT ← parms.rules.trunkToTrunk;
size: INT; -- required channel width
actualSize: INT; -- the channel width that was produced
otherSide: DABasics.Side ← RTBasic.OtherSide[chipSide];
blSide: DABasics.Side ← IF chipSide=bottom OR chipSide=top THEN left ELSE bottom;
clear the routing specification for this channel
[] ← Connections.EnumerateNets[handle.connections, InitNet];
construct the bonding objects to pass to the channel router
AddPinsFromObject[handle, handle.inner, chipSide, range, parms, FALSE, makeTwoPinPowerNets, "--"];
AddPinsFromChannel[handle, boundingChannel, otherSide, parms.rules.branchLayer, range, parms, none, NIL];
AddPinsFromEnd[handle, llDiv, blSide, parms];
AddPinsFromEnd[handle, urDiv, RTBasic.OtherSide[blSide], parms];
intermediateResult ← Route.ChannelRoute[enumerateNets: EnumerateChannelNets, min: range.min, max: range.max, rulesParameters: parms.parms, rules: parms.rules, name: handle.name, enumerateObstructions: NIL, channelData: handle, optimization: handle.parms.opt, signalSinglePinNets: handle.parms.signalSinglePinNets];
rect ← intermediateResult.resultData.routingRect;
SELECT chipSide
FROM
bottom => {
size ← handle.inner.origin.y - (boundingChannel.origin.y + boundingChannel.size.y);
retrieveRect ← NEW[CD.Rect ← [rect.x1, MIN[rect.y1, rect.y2 - size + offset], rect.x2, rect.y2 + offset]]};
right => {
size ← boundingChannel.origin.x - (handle.inner.origin.x + handle.inner.size.x);
retrieveRect ← NEW[CD.Rect ← [rect.x1 - offset, rect.y1, MAX[rect.x2, rect.x1 + size - offset], rect.y2]]};
top => {
size ← boundingChannel.origin.y - (handle.inner.origin.y + handle.inner.size.y);
retrieveRect ← NEW[CD.Rect ← [rect.x1, rect.y1 - offset, rect.x2, MAX[rect.y2, rect.x1 + size - offset]]]};
left => {
size ← handle.inner.origin.x - (boundingChannel.origin.x + boundingChannel.size.x);
retrieveRect ← NEW[CD.Rect ← [MIN[rect.x1, rect.x2 - size + offset], rect.y1, rect.x2 + offset, rect.y2]]};
ENDCASE;
channel.object ← Route.ChannelRetrieve[intermediateResult: intermediateResult, enumerateNets: EnumerateChannelNets, brokenNets: NIL, channelData: handle, retrieveRect: retrieveRect].object;
compare channel.size to required channel width
channel.size ← RTBasic.IRSize[channel.object];
actualSize ←
SELECT chipSide
FROM
bottom, top => channel.size.y,
left, right => channel.size.x,
ENDCASE => 0;
IF actualSize > size THEN Cabbage.Signal[noResource, "Inner routing channel is to small. Decrease powerWidth and/or outerWidth."];
channel.cellType ← ExtractChannel[channel, parms]};
adjust the positions of everything to account for the actual channel widths
route all of the switchBoxes (the corners)
RouteSwitchBoxes:
PROC [handle: CabbagePrivate.Handle] ~ {
horizInsideParms: CabbagePrivate.ParmSet ← handle.rules.horizInsideParms;
leftHorizRange: Connections.Range ← [handle.left.origin.x + handle.left.size.x, handle.inner.origin.x];
rightHorizRange: Connections.Range ← [handle.inner.origin.x + handle.inner.size.x, handle.right.origin.x];
bottomVertRange: Connections.Range ← [handle.bottom.origin.y + handle.bottom.size.y, handle.inner.origin.y];
topVertRange: Connections.Range ← [handle.inner.origin.y + handle.inner.size.y, handle.top.origin.y];
handle.detailedRouting.switchBoxes[bottomLeft] ← RouteSwitchBox[handle, bottomLeft, leftHorizRange, bottomVertRange, horizInsideParms, TRUE, FALSE];
handle.detailedRouting.switchBoxes[bottomRight] ← RouteSwitchBox[handle, bottomRight, rightHorizRange, bottomVertRange, horizInsideParms, FALSE, TRUE];
handle.detailedRouting.switchBoxes[topRight] ← RouteSwitchBox[handle, topRight, rightHorizRange, topVertRange, horizInsideParms, FALSE, TRUE];
handle.detailedRouting.switchBoxes[topLeft] ← RouteSwitchBox[handle, topLeft, leftHorizRange, topVertRange, horizInsideParms, TRUE, FALSE]};
route the bottom and top switchBoxes
RouteSwitchBoxesPL:
PROC [handle: CabbagePrivate.Handle] ~ {
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
horizInsideParms: CabbagePrivate.ParmSet ← handle.rules.horizInsideParms;
powerLRWidth: INT ← handle.parms.outerLRChanWidth * l + handle.parms.powerLRCellWidth * l;
powerBTWidth: INT ← handle.parms.outerBTChanWidth * l + handle.parms.powerBTCellWidth * l;
horizRange: Connections.Range ← [handle.left.origin.x + handle.left.size.x + powerLRWidth, handle.right.origin.x - powerLRWidth];
bottomVertRange: Connections.Range ← [handle.bottom.origin.y + handle.bottom.size.y + powerBTWidth, handle.inner.origin.y];
topVertRange: Connections.Range ← [handle.inner.origin.y + handle.inner.size.y, handle.top.origin.y - powerBTWidth];
handle.detailedRoutingPL.switchBoxes[bottom] ← RouteSwitchBoxPL[handle, bottom, horizRange, bottomVertRange, horizInsideParms, TRUE, TRUE];
handle.detailedRoutingPL.switchBoxes[top] ← RouteSwitchBoxPL[handle, top, horizRange, topVertRange, horizInsideParms, TRUE, TRUE]};
RouteSwitchBoxPL:
PROC [handle: CabbagePrivate.Handle, side: RTBasic.TBSide, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet, okToDiddleLLPins, okToDiddleURPins:
BOOL ←
FALSE]
RETURNS [switchBox: CabbagePrivate.Channel] ~ {
InitNet: Connections.EachNetAction = {net.netDat ← NIL};
innerRange: Connections.Range ← [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x];
rect: DABasics.Rect ← [horizRange.min, vertRange.min, horizRange.max, vertRange.max];
clear the routing specification for this channel
[] ← Connections.EnumerateNets[handle.connections, InitNet];
AddPinsFromChannel[handle, handle.powerDistribution.power[left], right, parms.rules.trunkLayer, vertRange, parms, likeOtherNets, NIL]; -- left end of SB
AddPinsFromChannel[handle, handle.powerDistribution.power[right], left, parms.rules.trunkLayer, vertRange, parms, likeOtherNets, NIL]; -- right end of SB
AddPinsFromObject[handle, handle.inner, side, innerRange, parms, FALSE, makeTwoPinPowerNets, "--"]; -- inner side
AddPinsFromChannel[handle, handle.powerDistribution.power[side], RTBasic.OtherSide[side], parms.rules.branchLayer, horizRange, parms, none, NIL]; -- outer side
AddPinsFromFullChannel[handle, handle.detailedRoutingPL.channels[left], side, parms.rules.branchLayer, parms, likeOtherNets, NIL]; -- channel on inner side
AddPinsFromFullChannel[handle, handle.detailedRoutingPL.channels[right], side, parms.rules.branchLayer, parms, likeOtherNets, NIL]; -- channel on inner side
switchBox.object ← Route.SwitchBox[enumerateNets: EnumerateSwitchBoxNets, routingRect: rect, rulesParameters: parms.parms, name: NIL, enumerateObstructions: NIL, switchBoxData: handle, optimization: handle.parms.opt, signalSinglePinNets: handle.parms.signalSinglePinNets, okToDiddleLLPins: okToDiddleLLPins, okToDiddleURPins: okToDiddleURPins].object;
switchBox.size ← RTBasic.IRSize[switchBox.object];
switchBox.cellType ← ExtractChannel[switchBox, parms]};
RouteSwitchBox:
PROC [handle: CabbagePrivate.Handle, corner: CabbagePrivate.Corners, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet, okToDiddleLLPins, okToDiddleURPins:
BOOL ←
FALSE]
RETURNS [switchBox: CabbagePrivate.Channel] ~ {
InitNet: Connections.EachNetAction = {net.netDat ← NIL};
rect: DABasics.Rect ← [horizRange.min, vertRange.min, horizRange.max, vertRange.max];
outerTB: RTBasic.TBSide ←
SELECT corner
FROM
bottomLeft, bottomRight => bottom,
topLeft, topRight => top,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
outerLR: RTBasic.LRSide ←
SELECT corner
FROM
bottomLeft, topLeft => left,
bottomRight, topRight => right,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
clear the routing specification for this channel
[] ← Connections.EnumerateNets[handle.connections, InitNet];
AddPinsFromFullChannel[handle, handle.detailedRouting.channels[outerLR], outerTB, parms.rules.branchLayer, parms, likeOtherNets, NIL]; -- pins from side channel
AddPinsFromChannel[handle, handle.powerDistribution.power[outerTB], RTBasic.OtherSide[outerTB], parms.rules.branchLayer, horizRange, parms, likeOtherNets, NIL]; -- pins from top or bottom outer side
AddPinsFromChannel[handle, handle.powerDistribution.power[outerLR], RTBasic.OtherSide[outerLR], parms.rules.trunkLayer, vertRange, parms, likeOtherNets, NIL]; -- pins from left or right outer side
AddPinsFromFullChannel[handle, handle.detailedRouting.channels[outerTB], outerLR, parms.rules.trunkLayer, parms, likeOtherNets, NIL]; -- pins from top or bottom channel
switchBox.object ← Route.SwitchBox[enumerateNets: EnumerateSwitchBoxNets, routingRect: rect, rulesParameters: parms.parms, name: NIL, enumerateObstructions: NIL, switchBoxData: handle, optimization: handle.parms.opt, signalSinglePinNets: handle.parms.signalSinglePinNets, okToDiddleLLPins: okToDiddleLLPins, okToDiddleURPins: okToDiddleURPins].object;
switchBox.size ← RTBasic.IRSize[switchBox.object];
switchBox.cellType ← ExtractChannel[switchBox, parms]};
Add Pin Operations
AddMappedPinsFromObject:
PROC [handle: CabbagePrivate.Handle,
objectDes: CabbagePrivate.ObjectDescription,
pinSide: DABasics.Side, -- the side of the object on which pins are to be considered
innerRange, midRange, outerRange: Connections.Range, -- innerRange is the span of the inner cell, midRange is the free range, outerRange is the span of the appropriate power routing cell
parms: CabbagePrivate.ParmSet,
lowerSuffix, upperSuffix: Rope.ROPE] ~ {
add pins for inner edge of the outside channel and the outer edge of the power cell; pins are on the branch layer of rules (rules.branchLayer).
map signal pins outside outerRange to just inside outerRange
map Vdd and Gnd pins in outerRange to their same positions;
map only a single pin per wire outside innerRange but inside outerRange
EnterPowerOnInner: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
store the power pins on the appropriate side of the inner object
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF (segment.object = handle.inner.object
AND segment.side = otherSide)
AND powerNet
THEN
CabbageObstacles.Insert[obstacles, [innerOrigin + segment.range.min, innerOrigin + segment.range.max], powerSpacing]};
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net.name, "Gnd"];
[] ← Connections.EnumerateSegments[net, EachSegment]};
EnterPowerOnOuter: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
store the power pins on the appropriate side of the inner object
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF (segment.object = objectDes.object
AND segment.side = pinSide)
AND powerNet
THEN
CabbageObstacles.Insert[obstacles, [outerOrigin + segment.range.min, outerOrigin + segment.range.max], powerSpacing]};
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net.name, "Gnd"];
[] ← Connections.EnumerateSegments[net, EachSegment]};
EachOuterNet: Connections.EachNetAction ~ {
examine the nets on the outer object: objectDes
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
EachSegment: Connections.EachSegmentAction ~ {
keep the segments of interest on objectDes
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF (segment.object = objectDes.object
AND segment.side = pinSide
AND useNet)
THEN {
useThisPin: BOOLEAN ← TRUE;
newRange: Connections.Range;
name: Rope.ROPE ← NIL;
adjustedRange: Connections.Range ← [outerOrigin + segment.range.min, outerOrigin + segment.range.max];
IF ProperSubset[adjustedRange, midRange ]
THEN {
pin is within inner range ==> we may use tight spacing
IF net = Vdd OR net = Gnd OR net has not been processed for this side THEN useThisPin
IF powerNet THEN useThisPin ← FALSE -- power pins for middle range are put in from object
ELSE
IF RefTab.Insert[netTable, net.name,
NIL]
THEN {
newRange ← CabbageObstacles.FindMiddleRange[adjustedRange, obstacles];
name ← net.name}
ELSE
IF ProperSubset[adjustedRange, lowerRange]
OR CrossesBoundry[adjustedRange, lowerRange]
THEN {
pin is within lower range, search upwards
IF net has not been processed for this side THEN useThisPin
(net = Vdd OR net = Gnd) gets a special name
name ← IF powerNet THEN Rope.Cat[net.name, lowerSuffix] ELSE net.name;
IF RefTab.Insert[netTable, name,
NIL]
THEN {
pinWidth: INT ← IF powerNet THEN parms.rules.branchWidth ELSE adjustedRange.max-adjustedRange.min;
lowerRange: Connections.Range ← [outerRange.min, outerRange.min + pinWidth];
newRange ← CabbageObstacles.FindLowerRange[lowerRange, obstacles]}
ELSE useThisPin ← FALSE}
ELSE
IF ProperSubset[adjustedRange, upperRange]
OR CrossesBoundry[adjustedRange, upperRange]
THEN {
pin is within upper range, search downwards
IF net has not been processed for this side THEN useThisPin
(net = Vdd OR net = Gnd) gets a special name
name ← IF powerNet THEN Rope.Cat[net.name, upperSuffix] ELSE net.name;
IF RefTab.Insert[netTable, name,
NIL]
THEN {
pinWidth: INT ← IF powerNet THEN parms.rules.branchWidth ELSE adjustedRange.max-adjustedRange.min;
upperRange: Connections.Range ← [outerRange.max - pinWidth, outerRange.max];
newRange ← CabbageObstacles.FindUpperRange[upperRange, obstacles]}
ELSE useThisPin ← FALSE}
ELSE {
Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses boundary of power routing area at ", RopeFromRange[l, adjustedRange]]];
useThisPin ← FALSE};
IF useThisPin
THEN {
spacing: INT ← IF ProperSubset[newRange, innerRange] THEN innerSpacing ELSE outerSpacing;
trunkWidth: INT ← IF powerNet THEN MAX[parms.rules.trunkWidth, parms.rules.branchWidth] ELSE net.width;
netDat: NetInChan ← IF net.netDat = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.netDat];
AddPin[net, netDat, name, newRange, rules.branchLayer, pinSide, trunkWidth];
[] ← Connections.Store[handle.connections, key, net];
CabbageObstacles.Insert[obstacles, newRange, spacing]}}};
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"];
useNet: BOOL ← (~NetOnlyOnSide[handle, net, otherSide] AND NumberPinsOnSide[handle, net, otherSide] >= 1);
[] ← Connections.EnumerateSegments[net, EachSegment]};
rules: Route.DesignRules ← parms.rules;
netTable: RefTab.Ref ← RefTab.Create[equal: RefTabExtras.EqualRope, hash: RefTabExtras.HashRope];
otherSide: DABasics.Side ← RTBasic.OtherSide[pinSide];
innerOrigin:
INT ←
SELECT pinSide
FROM
bottom, top => handle.inner.origin.x,
right, left => handle.inner.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
outerOrigin:
INT ←
SELECT pinSide
FROM
bottom, top => objectDes.origin.x,
right, left => objectDes.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
boundryOffset: INT ← 3*rules.branchToBranch; -- the size of the stay out area at the boundry
powerSpacing: INT ← MAX[rules.branchSpacing, rules.trunkSpacing]; -- the size of the stay out area between power pins
innerSpacing: INT ← MAX[rules.branchSpacing, rules.trunkSpacing]; -- the size of the stay out area between pins on channel sides
outerSpacing: INT ← 2*rules.branchToBranch; -- the size of the stay out area between pins on switchbox ends
lowerRange: Connections.Range ← [FIRST[INT], midRange.min];
upperRange: Connections.Range ← [midRange.max, LAST[INT]];
insert obstacles for vias on end of power bus and at boundries of routing areas
all obstacles (pins, inner power publics, ...) are contained in a sorted table; the data is a Connections.Segment; the key is the origin, i.e. data.range.min
obstacles: CabbageObstacles.BitTable ← CabbageObstacles.CreateBitTable[outerRange.min, outerRange.max, l];
CabbageObstacles.Insert[obstacles, [outerRange.min, midRange.min + boundryOffset], innerSpacing];
CabbageObstacles.Insert[obstacles, [midRange.max - boundryOffset, outerRange.max], innerSpacing];
CabbageObstacles.Insert[obstacles, [innerRange.min - boundryOffset, innerRange.min + boundryOffset], innerSpacing];
CabbageObstacles.Insert[obstacles, [innerRange.max - boundryOffset, innerRange.max + boundryOffset], innerSpacing];
process the inner power obstacles an the find places for the outer signals
[] ← Connections.EnumerateNets[handle.connections, EnterPowerOnInner];
[] ← Connections.EnumerateNets[handle.connections, EnterPowerOnOuter];
[] ← Connections.EnumerateNets[handle.connections, EachOuterNet]};
AddPinsFromOutsideEnd:
PROC [handle: CabbagePrivate.Handle,
objectDes: CabbagePrivate.ObjectDescription,
pinsOnSide, endOfChannel: DABasics.Side,
outerRange: Connections.Range, -- the span along the appropriate padring cell
parms: CabbagePrivate.ParmSet,
suffix: Rope.ROPE] ~ { -- suffix for power nets
add pins on the end of the channel to the routing for the current channel
transfer pins on objectDes outside the range (on side endOfChannel) to routing.
pins go on rules.trunkLayer
EachNet: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF (segment.object = objectDes.object
AND segment.side = pinsOnSide AND useNet)
THEN {
adjustedRange: Connections.Range ← [origin + segment.range.min, origin + segment.range.max];
IF ReallyCrossesBoundry[adjustedRange, interestingRange]
THEN
Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses boundary of power routing area at ", RopeFromRange[l, adjustedRange]]];
IF ProperSubset[adjustedRange, interestingRange]
AND ~Member[net.name, netNameList]
THEN {
netDat: NetInChan ← IF net.netDat = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.netDat];
name: Rope.ROPE ← IF powerNet THEN Rope.Cat[net.name, suffix] ELSE net.name;
AddEnd[net, netDat, name, endOfChannel, 0];
[] ← Connections.Store[handle.connections, key, net];
netNameList ← CONS[net.name, netNameList]}}};
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"];
useNet: BOOL ← NumberPinsOnSide[handle, net, otherSide] >= 2 OR (~NetOnlyOnSide[handle, net, otherSide] AND NumberPinsOnSide[handle, net, otherSide] >= 1);
[] ← Connections.EnumerateSegments[net, EachSegment]};
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
netNameList: LIST OF Rope.ROPE ← NIL;
interestingRange: Connections.Range ←
SELECT endOfChannel
FROM
left, bottom => [FIRST[INT], outerRange.min],
right, top => [outerRange.max, LAST[INT]],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
origin:
INT ←
SELECT pinsOnSide
FROM
bottom, top => objectDes.origin.x,
right, left => objectDes.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
otherSide: DABasics.Side ← RTBasic.OtherSide[pinsOnSide];
[] ← Connections.EnumerateNets[handle.connections, EachNet]};
AddPinsFromObject:
PROC [handle: CabbagePrivate.Handle,
objectDes: CabbagePrivate.ObjectDescription,
side: DABasics.Side, -- the side of the object on which pins are to be considered
range: Connections.Range, -- the span along objectDes to consider
parms: CabbagePrivate.ParmSet,
filterSinglePins: BOOL, -- TRUE => disregard single pin nets
powerMode: PowerMode, -- tells what to do with power
suffix: Rope.ROPE] ~ { -- suffix for power nets
add pins to routing for side of channel or switchbox; input to router
AddPinsFromObject3Part[handle, objectDes, side, range, range, parms, filterSinglePins, powerMode, powerMode, suffix, suffix, suffix]};
AddPinsFromObject3Part:
PROC [handle: CabbagePrivate.Handle,
objectDes: CabbagePrivate.ObjectDescription,
side: DABasics.Side, -- the side of the object on which pins are to be considered
innerRange, outerRange: Connections.Range, -- the span along objectDes to consider
parms: CabbagePrivate.ParmSet,
filterSinglePins: BOOL, -- TRUE => disregard single pin nets
innerPowerMode, outerPowerMode: PowerMode, -- tells what to do with power
lowerSuffix, innerSuffix, upperSuffix: Rope.ROPE] ~ { -- suffix for power nets
add pins to routing for side of channel or switchbox; input to router
EnterPin:
PROC [net: Connections.Net, powerMode: PowerMode, key: Connections.Key, adjustedRange: Connections.Range, layer: CD.Layer, suffix: Rope.ROPE] ~ {
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net.name, "Gnd"];
netDat: NetInChan ← IF net.netDat = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.netDat];
IF powerMode = makeTwoPinPowerNets
AND powerNet
THEN {
name: Rope.ROPE ← Rope.Cat[net.name, suffix, Convert.RopeFromInt[netDat.segmentCount]];
AddPin[net, netDat, name, adjustedRange, layer, RTBasic.OtherSide[side], 0];
AddPin[net, netDat, name, adjustedRange, layer, side, 0]}
ELSE
IF powerMode = busPower
AND powerNet
THEN {
trunkWidth: INT ← MAX[parms.rules.trunkWidth, parms.rules.branchWidth];
name: Rope.ROPE ← Rope.Cat[net.name, suffix];
AddPin[net, netDat, name, adjustedRange, layer, RTBasic.OtherSide[side], trunkWidth]}
ELSE
IF powerMode = matchPin
AND powerNet
THEN {
trunkWidth: INT ← MAX[parms.rules.trunkWidth, adjustedRange.max - adjustedRange.min];
name: Rope.ROPE ← Rope.Cat[net.name, suffix];
AddPin[net, netDat, name, adjustedRange, layer, RTBasic.OtherSide[side], trunkWidth]}
ELSE IF powerMode = none AND powerNet THEN NULL
ELSE AddPin[net, netDat, net.name, adjustedRange, layer, RTBasic.OtherSide[side], 0];
[] ← Connections.Store[handle.connections, key, net]};
EachNet: Connections.EachNetAction ~ {
PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN ← FALSE]
EachSegment: Connections.EachSegmentAction ~ {
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF (segment.object = objectDes.object
AND segment.side = side AND useNet)
THEN {
adjustedRange: Connections.Range ← [origin + segment.range.min, origin + segment.range.max];
IF ReallyCrossesBoundry[adjustedRange, outerRange]
THEN
Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses a boundary of power routing area at ", RopeFromRange[l, adjustedRange]]]
ELSE
IF ProperSubset[adjustedRange, innerRange]
THEN
EnterPin[net, innerPowerMode, key, adjustedRange, segment.layer, innerSuffix]
ELSE
IF ProperSubset[adjustedRange, lowerRange]
OR CrossesBoundry[adjustedRange, lowerRange]
THEN
EnterPin[net, outerPowerMode, key, adjustedRange, segment.layer, lowerSuffix]
ELSE
IF ProperSubset[adjustedRange, upperRange]
OR CrossesBoundry[adjustedRange, upperRange]
THEN
EnterPin[net, outerPowerMode, key, adjustedRange, segment.layer, upperSuffix]}};
useNet: BOOL ← ~filterSinglePins OR NumberPinsOnSide[handle, net, otherSide] >= 2 OR (~NetOnlyOnSide[handle, net, otherSide] AND NumberPinsOnSide[handle, net, otherSide] >= 1);
[] ← Connections.EnumerateSegments[net, EachSegment]};
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
lowerRange: Connections.Range ← [outerRange.min, innerRange.min];
upperRange: Connections.Range ← [innerRange.max, outerRange.max];
rules: Route.DesignRules ← parms.rules;
origin:
INT ←
SELECT side
FROM
bottom, top => objectDes.origin.x,
right, left => objectDes.origin.y,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
otherSide: DABasics.Side ← RTBasic.OtherSide[side];
[] ← Connections.EnumerateNets[handle.connections, EachNet]};
AddPinsFromEnd:
PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division, side: DABasics.Side, parms: CabbagePrivate.ParmSet] ~ {
add pins from global routing for end of channel; input to router
EachExit: EachExitAction ~ {
PROC [division: CabbagePrivate.Division, net: Connections.Net] RETURNS [quit: BOOLEAN ← FALSE];
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"];
IF ~powerNet
THEN {
have to get the net from valid data structure;
trunkWidth: INT ← IF powerNet THEN MAX[parms.rules.trunkWidth, parms.rules.branchWidth] ELSE net.width;
realNet: Connections.Net ← Connections.Fetch[handle.connections, net.name].net;
netDat: NetInChan ← IF realNet.netDat = NIL THEN NEW[NetInChanRec] ELSE NARROW[realNet.netDat];
AddEnd[realNet, netDat, realNet.name, side, trunkWidth];
[] ← Connections.Store[handle.connections, realNet.name, realNet]}};
[] ← EnumerateExits[handle.globalRouting.exitLists[division], division, EachExit]};
AddPinsFromFullChannel:
PROC [handle: CabbagePrivate.Handle,
channel: CabbagePrivate.Channel,
side: DABasics.Side,
pinLayer: CD.Layer,
parms: CabbagePrivate.ParmSet,
powerMode: PowerMode, -- tells what to do with power
suffix: Rope.ROPE] ~ { -- suffix for power nets
includePowerNets, makeTwoPinPowerNets: BOOL] ~ {
add pins for the channel; use the full side of channel with pins on side
range: Connections.Range ←
SELECT side
FROM
bottom, top => [channel.origin.x, channel.origin.x + channel.size.x],
left, right => [channel.origin.y, channel.origin.y + channel.size.y],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"];
AddPinsFromChannel[handle, channel, side, pinLayer, range, parms, powerMode, suffix]};
AddPinsFromChannel:
PROC [handle: CabbagePrivate.Handle,
channel: CabbagePrivate.Channel,
pinSide: DABasics.Side,
pinLayer: CD.Layer,
range: Connections.Range,
parms: CabbagePrivate.ParmSet,
powerMode: PowerMode, -- tells what to do with power
suffix: Rope.ROPE] ~ { -- suffix for power nets
add pins for for the channel; use the pins on side; range is the span along that side
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
net: Connections.Net ← Connections.Fetch[handle.connections, NameFromWire[wire]].net;
powerNet: BOOLEAN ← Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"];
pin: Pin ← NIL;
wireLimits: Connections.Range ←
SELECT side
FROM
bottom, top => [channel.origin.x+min, channel.origin.x+max],
left, right => [channel.origin.y+min, channel.origin.y+max],
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
IF range.min <= wireLimits.min
AND wireLimits.max <= range.max
AND pinSide = side
AND layer = pinLayer
THEN {
netDat: NetInChan ← IF net.netDat = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.netDat];
IF powerMode = makeTwoPinPowerNets
AND powerNet
THEN {
name: Rope.ROPE ← Rope.Cat[net.name, suffix, Convert.RopeFromInt[netDat.segmentCount]];
AddPin[net, netDat, name, wireLimits, layer, RTBasic.OtherSide[pinSide], 0];
AddPin[net, netDat, name, wireLimits, layer, pinSide, 0]}
ELSE
IF powerMode = busPower
AND powerNet
THEN {
name: Rope.ROPE ← Rope.Cat[net.name, suffix];
trunkWidth: INT ← IF powerNet THEN MAX[parms.rules.trunkWidth, parms.rules.branchWidth] ELSE net.width;
AddPin[net, netDat, name, wireLimits, layer, RTBasic.OtherSide[pinSide], trunkWidth]}
ELSE
IF powerMode = matchPin
AND powerNet
THEN {
trunkWidth: INT ← MAX[parms.rules.trunkWidth, max - min];
name: Rope.ROPE ← Rope.Cat[net.name, suffix];
AddPin[net, netDat, name, wireLimits, layer, RTBasic.OtherSide[pinSide], trunkWidth]}
ELSE IF powerMode = none AND powerNet THEN NULL
ELSE AddPin[net, netDat, net.name, wireLimits, layer, RTBasic.OtherSide[pinSide], 0];
[] ← Connections.Store[handle.connections, net.name, net]}};
mode: Sinix.Mode = SinixOps.GetExtractMode[parms.parms.technology];
[] ← CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, channel.cellType, ChanPublics]};
AddPin:
PROC [net: Connections.Net, netDat: NetInChan, name: Rope.
ROPE, range: Connections.Range, layer:
CD.Layer, side: DABasics.Side, trunkWidth:
INT] ~ {
add a pin to the appropriate segment in net
pin: Pin ← NEW[PinRec ← [min: range.min, max: range.max, depth: 0, layer: layer, side: side]];
segment: Segment ← LookUpSegment[netDat, name];
segment.pinsInSeg ← CONS[pin, segment.pinsInSeg];
first size definition wins; 0 is default
IF segment.trunkSize = 0 AND trunkWidth # 0 THEN segment.trunkSize ← trunkWidth;
net.netDat ← netDat};
AddEnd:
PROC [net: Connections.Net, netDat: NetInChan, name: Rope.
ROPE, side: DABasics.Side, trunkWidth:
INT] ~ {
add a channel end connection
segment: Segment ← LookUpSegment[netDat, name];
SELECT side
FROM
bottom, left => segment.leftOrBottomExit ← TRUE;
right, top => segment.rightOrTopExit ← TRUE;
ENDCASE;
first size definition wins; 0 is default
IF segment.trunkSize = 0 AND trunkWidth # 0 THEN segment.trunkSize ← trunkWidth;
net.netDat ← netDat};
LookUpSegment:
PROC [netDat: NetInChan, name: Rope.
ROPE]
RETURNS [segment: Segment ←
NIL] ~ {
find an existing segment by name; if none, make a NEW one
FOR segList:
LIST
OF Segment ← netDat.segsInNet, segList.rest
WHILE segList #
NIL
AND segment =
NIL
DO
IF Rope.Equal[segList.first.name, name] THEN segment ← segList.first;
ENDLOOP;
IF segment =
NIL
THEN {
-- not found, make a NEW one
segment ← NEW[SegmentRec ← [name: name]];
netDat.segmentCount ← netDat.segmentCount + 1;
netDat.segsInNet ← CONS[segment, netDat.segsInNet]}};
Utility Procedures
AddPO:
PROC[nets: SymTab.Ref, po: CDRoutingObjects.PlacedObject, nm: Rope.
ROPE] = {
refList: REF LIST OF CDRoutingObjects.PlacedObject ← NARROW[SymTab.Fetch[nets, nm].val];
IF refList=
NIL
THEN {
refList ← NEW[LIST OF CDRoutingObjects.PlacedObject];
[]←SymTab.Store[nets, nm, refList]};
refList^ ← CONS[po, refList^]};
NameFromWire:
PROC [wire: Core.Wire]
RETURNS [name: Rope.
ROPE] ~ {
get name from a wire. This is needed because the wire name has been diddled
a suffix has been added because of the extractor
distinguish constructed Vdd and Gnd by (Vdd | Gnd)--; other nets may start with Vdd and Gnd
wireName: Rope.ROPE ← CoreOps.GetShortWireName[wire];
prefix: Rope.ROPE ← Rope.Substr[wireName, 0, 5];
name ←
IF Rope.Equal[prefix, "Vdd--"]
THEN "Vdd"
ELSE IF Rope.Equal[prefix, "Gnd--"] THEN "Gnd"
ELSE wireName};
AdjustPositions:
PROC [handle: CabbagePrivate.Handle] ~ {
adjust the vertical and horizontal dimension
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
powerLRWidth: INT ← handle.parms.outerLRChanWidth * l + handle.parms.powerLRCellWidth * l;
powerBTWidth: INT ← handle.parms.outerBTChanWidth * l + handle.parms.powerBTCellWidth * l;
routeType: CabbagePrivate.RouteType ← handle.routeType;
sizeBottomArea: INT ← handle.inner.origin.y - (handle.bottom.origin.y + handle.bottom.size.y);
sizeBottomRouting:
INT ← powerBTWidth + (
IF routeType = normal
THEN handle.detailedRouting.channels[bottom].size.y
ELSE handle.detailedRoutingPL.switchBoxes[bottom].size.y);
adjBottom: INT ← sizeBottomRouting - sizeBottomArea;
sizeLeftArea: INT ← handle.inner.origin.x - (handle.left.origin.x + handle.left.size.x);
sizeLeftRouting:
INT ← powerLRWidth + (
IF routeType = normal
THEN handle.detailedRouting.channels[left].size.x
ELSE handle.detailedRoutingPL.channels[left].size.x);
adjLeft: INT ← sizeLeftRouting - sizeLeftArea;
adjTop, adjRight, sizeTopArea, sizeTopRouting, sizeRightArea, sizeRightRouting: INT;
handle.inner.origin ← CDBasics.AddPoints[handle.inner.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]];
handle.bottom.origin ← CDBasics.AddPoints[handle.bottom.origin, [MAX[0, adjLeft], 0]];
handle.right.origin ← CDBasics.AddPoints[handle.right.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]];
handle.top.origin ← CDBasics.AddPoints[handle.top.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]];
handle.left.origin ← CDBasics.AddPoints[handle.left.origin, [0, MAX[0, adjBottom]]];
sizeTopArea ← handle.top.origin.y - (handle.inner.origin.y + handle.inner.size.y);
sizeTopRouting ← powerBTWidth + (
IF routeType = normal
THEN handle.detailedRouting.channels[top].size.y
ELSE handle.detailedRoutingPL.switchBoxes[top].size.y);
adjTop ← sizeTopRouting - sizeTopArea;
sizeRightArea ← handle.right.origin.x - (handle.inner.origin.x + handle.inner.size.x);
sizeRightRouting ← powerLRWidth + (
IF routeType = normal
THEN handle.detailedRouting.channels[right].size.x
ELSE handle.detailedRoutingPL.channels[right].size.x);
adjRight ← sizeRightRouting - sizeRightArea;
handle.top.origin ← CDBasics.AddPoints[handle.top.origin, [0, MAX[0, adjTop]]];
handle.right.origin ← CDBasics.AddPoints[handle.right.origin, [MAX[0, adjRight], 0]];
[] ← GetSize[handle];
DoCorners[handle];
DoRoutingAreas[handle]};
IncludeObject:
PROC [object:
CD.Object, origin:
CD.Position, chip:
CD.Object] ~ {
IF object #
NIL
THEN
[] ← RouteUtil.Include[cell: chip, ob: object, position: CDOps.FitObjectI[ob: object, location: origin, orientation: original].off, orientation: original]};
ExtractChannel:
PROC [channel: CabbagePrivate.Channel, parms: CabbagePrivate.ParmSet]
RETURNS [cellType: Core.CellType] ~ {
extract connectivity for the channel
mode: Sinix.Mode = SinixOps.GetExtractMode[parms.parms.technology];
cellType ← NARROW [Sinix.Extract[channel.object, mode].result]};
PosOf:
PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment]
RETURNS [
INT] ~ {
Find the position of a segment projected to the periphery. The lower left corner is the origin.
range: Connections.Range ← RangeOf[handle, segment];
RETURN[(range.min+range.max)/2]};
Find the range of a segment projected to the periphery. The lower left corner is the origin.
RangeOf:
PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment]
RETURNS [range: Connections.Range] ~ {
range ←
SELECT
TRUE
FROM
segment.object = handle.inner.object => InnerRange[handle, segment],
segment.object = handle.bottom.object => range ← OuterRange[handle, segment],
segment.object = handle.right.object => OuterRange[handle, segment],
segment.object = handle.top.object => OuterRange[handle, segment],
segment.object = handle.left.object => OuterRange[handle, segment],
segment.object = handle.powerDistribution.power[bottom].object => PowerRange[handle, segment],
segment.object = handle.powerDistribution.power[right].object => PowerRange[handle, segment],
segment.object = handle.powerDistribution.power[top].object => PowerRange[handle, segment],
segment.object = handle.powerDistribution.power[left].object => PowerRange[handle, segment],
ENDCASE => Cabbage.Error[callingError, Rope.Cat["Invalid object in segment: ", segment.name]]};
InnerRange:
PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment]
RETURNS [range: Connections.Range] ~ {
SELECT segment.side
FROM
bottom =>
{origin: INT ← handle.inner.origin.x;
range ← [origin + segment.range.min, origin + segment.range.max]};
right =>
{origin: INT ← handle.size.x + handle.inner.origin.y;
range ← [origin + segment.range.min, origin + segment.range.max]};
top =>
{origin: INT ← handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x);
range ← [origin - segment.range.max, origin - segment.range.min]};
left =>
{origin: INT ← 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y);
range ← [origin - segment.range.max, origin - segment.range.min]};
ENDCASE};
OuterRange:
PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment]
RETURNS [range: Connections.Range] ~ {
SELECT RTBasic.OtherSide[segment.side]
FROM
bottom =>
{origin: INT ← handle.bottom.origin.x;
range ← [origin + segment.range.min, origin + segment.range.max]};
right =>
{origin: INT ← handle.size.x + handle.right.origin.y;
range ← [origin + segment.range.min, origin + segment.range.max]};
top =>
{origin: INT ← handle.size.x +handle.size.y + (handle.size.x - handle.top.origin.x);
range ← [origin - segment.range.max, origin - segment.range.min]};
left =>
{origin: INT ← 2*handle.size.x +handle.size.y + (handle.size.y - handle.left.origin.y);
range ← [origin - segment.range.max, origin - segment.range.min]};
ENDCASE};
PowerRange:
PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment]
RETURNS [range: Connections.Range] ~ {
SELECT RTBasic.OtherSide[segment.side]
FROM
bottom =>
{origin: INT ← handle.powerDistribution.power[bottom].origin.x;
range ← [origin + segment.range.min, origin + segment.range.max]};
right =>
{origin: INT ← handle.size.x + handle.powerDistribution.power[right].origin.y;
range ← [origin + segment.range.min, origin + segment.range.max]};
top =>
{origin: INT ← handle.size.x +handle.size.y + (handle.size.x - handle.powerDistribution.power[top].origin.x);
range ← [origin - segment.range.max, origin - segment.range.min]};
left =>
{origin: INT ← 2*handle.size.x +handle.size.y + (handle.size.y - handle.powerDistribution.power[left].origin.y);
range ← [origin - segment.range.max, origin - segment.range.min]};
ENDCASE};
PosOfDivision:
PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division]
RETURNS [pos:
INT] ~ {
SELECT division
FROM
bottomLeft => pos ← handle.inner.origin.x;
bottomRight => pos ← handle.inner.origin.x + handle.inner.size.x;
rightBottom => pos ← handle.size.x + handle.inner.origin.y;
rightTop => pos ← handle.size.x + handle.inner.origin.y + handle.inner.size.y;
topRight => pos ← handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x - handle.inner.size.x);
topLeft => pos ← handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x);
leftTop => pos ← 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y - handle.inner.size.y);
leftBottom => pos ← 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y);
ENDCASE};
EachExitAction:
TYPE =
PROC [division: CabbagePrivate.Division, net: Connections.Net]
RETURNS [quit:
BOOLEAN ←
FALSE];
EnumerateExits:
PROC [exitList: CabbagePrivate.ExitList, division: CabbagePrivate.Division, eachExitAction: EachExitAction]
RETURNS [quit:
BOOLEAN ←
FALSE] ~ {
FOR list: CabbagePrivate.ExitList ← exitList, list.rest
WHILE ~quit
AND list #
NIL
DO
exit: Connections.Net ← list.first;
quit ← eachExitAction[division, exit];
ENDLOOP};
CrossesBoundry:
PROC [r1, r2: Connections.Range]
RETURNS [crosses:
BOOLEAN] ~ {
returns TRUE if either end but not both ends of r1 is within r2
crosses ←(r1.min <= r2.min
AND r2.min <= r1.max)
OR
(r1.min <= r2.max AND r2.max <= r1.max)};
ReallyCrossesBoundry:
PROC [r1, r2: Connections.Range]
RETURNS [reallyCrosses:
BOOLEAN] ~ {
returns TRUE if either end but not both ends of r1 is within r2; r1 on boundry doesn't count
reallyCrosses ←(r1.min < r2.min
AND r2.min < r1.max)
OR
(r1.min < r2.max AND r2.max < r1.max)};
ProperSubset:
PROC [r1, r2: Connections.Range]
RETURNS [subset:
BOOLEAN] ~ {
returns TRUE if r1 is within r2
subset ←((r2.min <= r1.min
AND r1.min <= r2.max)
AND
(r2.min <= r1.max AND r1.max <= r2.max))};
RopeFromRange:
PROC [
l:
INT, range: Connections.Range]
RETURNS [Rope.
ROPE] ~ {
convert a range to rope scalled by lambda
RETURN[Rope.Cat["[", Convert.RopeFromInt[range.min/l], ", ", Convert.RopeFromInt[range.max/l], "["]]};
NetOnlyOnSide:
PROC [handle: CabbagePrivate.Handle, net: Connections.Net, side: DABasics.Side]
RETURNS [onlyOnSide:
BOOL ←
TRUE] ~ {
TRUE if net is only on specified side
EachSegment: Connections.EachSegmentAction ~ {
keep the segments of interest on objectDes
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF segment.object # object.object OR segment.side # otherSide THEN {onlyOnSide ← FALSE; quit ← TRUE}};
object: CabbagePrivate.ObjectDescription ←
SELECT side
FROM
bottom => handle.bottom, right => handle.right, top => handle.top, left => handle.left,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
otherSide: DABasics.Side ← RTBasic.OtherSide[side];
[] ← Connections.EnumerateSegments[net, EachSegment]};
NumberPinsOnSide:
PROC [handle: CabbagePrivate.Handle, net: Connections.Net, side: DABasics.Side]
RETURNS [count:
INT ←
0] ~ {
counts pins in net on specified side
EachSegment: Connections.EachSegmentAction ~ {
keep the segments of interest on objectDes
PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN ← FALSE]
IF segment.object = object.object AND segment.side = otherSide THEN count ← count + 1};
object: CabbagePrivate.ObjectDescription ←
SELECT side
FROM
bottom => handle.bottom, right => handle.right, top => handle.top, left => handle.left,
ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."];
otherSide: DABasics.Side ← RTBasic.OtherSide[side];
[] ← Connections.EnumerateSegments[net, EachSegment]};
GetSize:
PROC [handle: CabbagePrivate.Handle]
RETURNS [hBottom, hTop, vLeft, vRight:
INT] ~ {
vMiddle, hMiddle: INT;
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
powerLRWidth: INT ← handle.parms.outerLRChanWidth * l + handle.parms.powerLRCellWidth * l;
powerBTWidth: INT ← handle.parms.outerBTChanWidth * l + handle.parms.powerBTCellWidth * l;
IF handle.routeType = normal
THEN {
vMiddle ← handle.bottom.size.y + powerBTWidth + handle.detailedRouting.channels[bottom].size.y + handle.inner.size.y + handle.detailedRouting.channels[top].size.y + powerBTWidth + handle.top.size.y;
hMiddle ← handle.left.size.x + powerLRWidth + handle.detailedRouting.channels[left].size.x + handle.inner.size.x + handle.detailedRouting.channels[right].size.x + powerLRWidth + handle.right.size.x}
ELSE {
hCenter: INT ← powerLRWidth + handle.detailedRoutingPL.channels[left].size.x + handle.inner.size.x + handle.detailedRoutingPL.channels[right].size.x + powerLRWidth;
hInner: INT ← MAX[hCenter, handle.detailedRoutingPL.switchBoxes[top].size.x, handle.detailedRoutingPL.switchBoxes[bottom].size.x];
vInner: INT ← powerBTWidth + handle.detailedRoutingPL.switchBoxes[bottom].size.y + handle.inner.size.y + handle.detailedRoutingPL.switchBoxes[top].size.y + powerBTWidth;
vMiddle ← handle.bottom.size.y + vInner + handle.top.size.y;
hMiddle ← handle.left.size.x + hInner + handle.right.size.x};
vLeft ← handle.bottomLeft.size.y + handle.left.size.y + handle.topLeft.size.y;
vRight ← handle.bottomRight.size.y + handle.right.size.y + handle.topRight.size.y;
hBottom ← handle.bottomLeft.size.x + handle.bottom.size.x + handle.bottomRight.size.x;
hTop ← handle.topLeft.size.x + handle.top.size.x + handle.topRight.size.x;
handle.size.y ← MAX[vLeft, vMiddle, vRight];
handle.size.x ← MAX[hBottom, hMiddle, hTop]};
DoCorners:
PROC [handle: CabbagePrivate.Handle] ~ {
handle.bottomLeft.origin ← [0, 0];
handle.bottomRight.origin ← [handle.size.x - handle.bottomRight.size.x, 0];
handle.topRight.origin ← [handle.size.x - handle.topRight.size.x, handle.size.y - handle.topRight.size.y];
handle.topLeft.origin ← [0, handle.size.y - handle.topLeft.size.y]};
DoRoutingAreas:
PROC [handle: CabbagePrivate.Handle] ~ {
l: INT = CDSimpleRules.GetTechnology[handle.parms.technologyKey].lambda;
outerLRChanWidth: INT ← handle.parms.outerLRChanWidth * l;
outerBTChanWidth: INT ← handle.parms.outerBTChanWidth * l;
powerLRWidth: INT ← outerLRChanWidth + handle.parms.powerLRCellWidth * l;
powerBTWidth: INT ← outerBTChanWidth + handle.parms.powerBTCellWidth * l;
routeType: CabbagePrivate.RouteType ← handle.routeType;
short hand to make the following easier to understand
leftInterior: INT ← handle.left.origin.x + handle.left.size.x; -- right of left pads
rightInterior: INT ← handle.right.origin.x; -- left of right pads
bottomInterior: INT ← handle.bottom.origin.y + handle.bottom.size.y; -- top of bottom pads
topInterior: INT ← handle.top.origin.y; -- bottom of top pads
leftOfInner: INT ← handle.inner.origin.x; -- left of inner object
rightOfInner: INT ← handle.inner.origin.x + handle.inner.size.x; -- right of inner object
bottomOfInner: INT ← handle.inner.origin.y; -- bottom of inner object
topOfInner: INT ← handle.inner.origin.y + handle.inner.size.y; -- top of inner object
IF routeType = normal
THEN {
handle.detailedRouting.channels[bottom].origin ← [leftOfInner, bottomOfInner - handle.detailedRouting.channels[bottom].size.y];
handle.detailedRouting.channels[right].origin ← [rightOfInner, bottomOfInner];
handle.detailedRouting.channels[top].origin ← [leftOfInner, topOfInner];
handle.detailedRouting.channels[left].origin ← [leftOfInner - handle.detailedRouting.channels[left].size.x, bottomOfInner];
handle.detailedRouting.switchBoxes[bottomLeft].origin ← [leftInterior + powerLRWidth, bottomInterior + powerBTWidth];
handle.detailedRouting.switchBoxes[bottomRight].origin ← [rightOfInner, bottomInterior + powerBTWidth];
handle.detailedRouting.switchBoxes[topRight].origin ← [rightOfInner, topOfInner];
handle.detailedRouting.switchBoxes[topLeft].origin ← [leftInterior + powerLRWidth, topOfInner]}
ELSE {
-- routeType = padLimited
handle.detailedRoutingPL.channels[right].origin ← [rightOfInner, bottomOfInner];
handle.detailedRoutingPL.channels[left].origin ← [leftOfInner - handle.detailedRoutingPL.channels[left].size.x, bottomOfInner];
handle.detailedRoutingPL.switchBoxes[bottom].origin ← [leftInterior + powerLRWidth, bottomInterior + powerBTWidth];
handle.detailedRoutingPL.switchBoxes[top].origin ← [leftInterior + powerLRWidth, topOfInner]};
the outside routing channels
handle.powerDistribution.channels[bottom].origin ← [leftInterior + outerLRChanWidth, bottomInterior];
handle.powerDistribution.channels[right].origin ← [rightInterior - outerLRChanWidth, bottomInterior + outerBTChanWidth];
handle.powerDistribution.channels[top].origin ← [leftInterior + outerLRChanWidth, topInterior - outerBTChanWidth];
handle.powerDistribution.channels[left].origin ← [leftInterior, bottomInterior + outerBTChanWidth];
the power channels
handle.powerDistribution.power[bottom].origin ← [leftInterior + powerLRWidth, bottomInterior + outerBTChanWidth];
handle.powerDistribution.power[right].origin ← [rightInterior - powerLRWidth, bottomInterior + powerBTWidth];
handle.powerDistribution.power[top].origin ← [leftInterior + powerLRWidth, topInterior - powerBTWidth];
handle.powerDistribution.power[left] .origin← [leftInterior + outerLRChanWidth, bottomInterior + powerBTWidth];
the outside switchbox corners
handle.powerDistribution.switchBoxes[bottomLeft].origin ← [leftInterior, bottomInterior];
handle.powerDistribution.switchBoxes[bottomRight].origin ← [rightInterior - outerLRChanWidth, bottomInterior];
handle.powerDistribution.switchBoxes[topRight].origin ← [rightInterior - outerLRChanWidth, topInterior - outerBTChanWidth];
handle.powerDistribution.switchBoxes[topLeft].origin ← [leftInterior, topInterior - outerBTChanWidth];
the outside power rail corners
handle.powerDistribution.powerCorners[bottomLeft].origin ← [leftInterior + outerLRChanWidth, bottomInterior + outerBTChanWidth];
handle.powerDistribution.powerCorners[bottomRight].origin ← [rightInterior - powerLRWidth, bottomInterior + outerBTChanWidth];
handle.powerDistribution.powerCorners[topRight].origin ← [rightInterior - powerLRWidth, topInterior - powerBTWidth];
handle.powerDistribution.powerCorners[topLeft].origin ← [leftInterior + outerLRChanWidth, topInterior - powerBTWidth]};
Member:
PROC [item: Rope.
ROPE, list:
LIST
OF Rope.
ROPE]
RETURNS [
BOOLEAN] ~ {
Return TRUE if item is on list
UNTIL list =
NIL
DO
IF Rope.Equal[list.first, item] THEN RETURN [TRUE];
list ← list.rest;
ENDLOOP;
RETURN [FALSE]};