SCPlaceImproveImpl:
CEDAR
PROGRAM
IMPORTS Convert, Rope, SC, SCChanUtil, SCInstUtil, SCNetUtil, SCPlaceUtil, SCRowUtil, SCWidthUtil, SCSmash, SCUtil, TerminalIO
EXPORTS SCPrivate
SHARES SC = {
debug: BOOLEAN ← FALSE;
RowSpec:
TYPE =
RECORD[
row: SCPrivate.LgRow,
doUpper, doLower: BOOLEAN];
PosResult:
TYPE =
RECORD[
area, channelWidth, wireLength: SC.Number,
numFts: INT];
PlaceResult:
TYPE =
RECORD[
area, channelWidth, wireLength: SC.Number,
numFts: INT];
WritePosResult:
PROCEDURE [header: Rope.
ROPE,
Result: PosResult] = {
-- write the results
TerminalIO.PutRope[header];
TerminalIO.PutRope[Rope.Cat[" area: ", Convert.RopeFromInt[Result.area]]];
TerminalIO.PutRope[Rope.Cat[", channelWidth: ", Convert.RopeFromInt[Result.channelWidth]]];
TerminalIO.PutRope[Rope.Cat["\n wireLength: ", Convert.RopeFromInt[Result.wireLength]]];
TerminalIO.PutRope[Rope.Cat[", numFts: ", Convert.RopeFromInt[Result.numFts], "\n"]]};
ComparePosResult:
PROCEDURE [result1, result2: PosResult]
RETURNS [FirstRelationSecond: Basics.Comparison] = {
IF result1.area < result2.area
THEN
FirstRelationSecond ← less
ELSE
IF result1.area > result2.area
THEN
FirstRelationSecond ← greater
ELSE
IF result1.channelWidth < result2.channelWidth
THEN
FirstRelationSecond ← less
ELSE
IF result1.channelWidth > result2.channelWidth
THEN
FirstRelationSecond ← greater
ELSE
IF result1.wireLength < result2.wireLength
THEN
FirstRelationSecond ← less
ELSE
IF result1.wireLength > result2.wireLength
THEN
FirstRelationSecond ← greater
ELSE
IF result1.numFts < result2.numFts
THEN
FirstRelationSecond ← less
ELSE
IF result1.numFts > result2.numFts
THEN
FirstRelationSecond ← greater
ELSE
FirstRelationSecond ← equal}; -- they are equal
ComparePlaceResult:
PROCEDURE [result1, result2: PlaceResult]
RETURNS [FirstRelationSecond: Basics.Comparison] =
BEGIN
IF result1.area < result2.area
THEN
FirstRelationSecond ← less
ELSE
IF result1.area > result2.area
THEN
FirstRelationSecond ← greater
ELSE
IF result1.channelWidth < result2.channelWidth
THEN
FirstRelationSecond ← less
ELSE
IF result1.channelWidth > result2.channelWidth
THEN
FirstRelationSecond ← greater
ELSE
IF result1.wireLength < result2.wireLength
THEN
FirstRelationSecond ← less
ELSE
IF result1.wireLength > result2.wireLength
THEN
FirstRelationSecond ← greater
ELSE
IF result1.numFts < result2.numFts
THEN
FirstRelationSecond ← less
ELSE
IF result1.numFts > result2.numFts
THEN
FirstRelationSecond ← greater
ELSE
FirstRelationSecond ← equal; -- they are equal
END; -- ComparePlaceResult
DoWidths:
PROCEDURE [handle:
SC.Handle,
doChanWidth: SCPrivate.ChanSet,
doSideWidth: SCPrivate.LRSideSet,
doRowWidth: SCPrivate.RowSet,
fom: SCPrivate.FomType]
RETURNS [wireLength: SC.Number ← 0] = {
EachRow: SCRowUtil.EachRowProc = {
IF doRowWidth[(row)-1] THEN SCInstUtil.LgOffsets[handle, row, 0, SCPrivate.maxPos]};
EachRowChan: SCChanUtil.EachRowChanProc = {
IF doChanWidth[(chan )-1]
THEN
[rowChan.chanWidth, rowChan.wireLength] ← SCWidthUtil.GetChanWidth[handle, rowChan, fom, TRUE];
wireLength ← wireLength + layoutData.rowChans.chans[chan].wireLength};
EachSideChan: SCChanUtil.EachSideChanProc = {
IF doSideWidth[
LOOPHOLE[lrSide,
INTEGER]]
THEN {
sideChan: SCPrivate.SideChan ← layoutData.sideChans[lrSide];
[sideChan.sideChanWidth, sideChan.wireLength] ← SCWidthUtil.GetSideWidth[handle, lrSide, fom]};
wireLength ← wireLength + sideChan.wireLength};
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
[] ← SCRowUtil.EnumerateRows[handle, EachRow];
[] ← SCChanUtil.EnumerateRowChans[handle, EachRowChan];
[] ← SCChanUtil.EnumerateSideChans[handle, EachSideChan]};
map a row index in to a row
ChosRow:
PROCEDURE[handle:
SC.Handle, rowIndex: SCPrivate.ZMaxRowSr]
RETURNS[rowSpec: RowSpec] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
row: SCPrivate.ZMaxRowSr;
doUpper, doLower: BOOLEAN;
SELECT
TRUE
FROM
layoutData.bpRows[top].fnlBpFxd
AND layoutData.bpRows[bottom].fnlBpFxd =>
{IF (rowIndex MOD 2) # 0 THEN row ← (lgRows.count+1) /2 - (rowIndex-1) /2
ELSE row ← (lgRows.count+1) /2 + rowIndex /2;
doUpper ← doLower ← TRUE};
layoutData.bpRows[top].fnlBpFxd =>
{row ← lgRows.count - rowIndex + 1; doUpper ← TRUE; doLower ← FALSE};
layoutData.bpRows[bottom].fnlBpFxd =>
{row ← rowIndex; doUpper ← FALSE; doLower ← TRUE};
ENDCASE =>
{IF (rowIndex MOD 2) # 0 THEN row ← (lgRows.count+1) /2 - (rowIndex-1) /2
ELSE row ← (lgRows.count+1) /2 + rowIndex /2;
IF row = (lgRows.count+1) /2 THEN {doUpper ← TRUE; doLower ← TRUE}
ELSE IF row < (lgRows.count+1) /2 THEN {doUpper ← TRUE; doLower ← row = 1}
ELSE {doUpper ← row = lgRows.count; doLower ← TRUE}};
rowSpec ← [lgRows.rows[row], doLower, doUpper]};
improve positions on logic rows
PosImproveRow:
PROCEDURE [handle:
SC.Handle, rowSpec: RowSpec, whichFom: SCPrivate.FomType]
RETURNS [goodExch:
INT ← 0] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
structureData: SCPrivate.StructureData ← NARROW[handle.structureData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
row: SCPrivate.MaxRowSr ← rowSpec.row.rowNum;
rowChans: SCPrivate.RowChans ← layoutData.rowChans;
lowerChan: SCPrivate.RowChan ← rowChans.chans[row];
upperChan: SCPrivate.RowChan ← rowChans.chans[row+1];
numFts: INT ← structureData.instances.numFts;
oldLowChWidth, oldUpChWidth, oldLowWireLength, oldUpWireLength: SC.Number;
oldResult: PosResult;
TrialInterchange: SCRowUtil.EachInstProc = {
nextPos: SCPrivate.MaxPosSr ← pos + 1;
lowChWidth, upChWidth, upWireLength, lowWireLength: SC.Number;
newResult: PosResult;
SCPlaceUtil.ExchPosRow[handle, pos, nextPos, row];
[lowChWidth, lowWireLength] ← SCWidthUtil.GetChanWidth[handle, lowerChan, whichFom, rowSpec.doLower];
[upChWidth, upWireLength] ← SCWidthUtil.GetChanWidth[handle, upperChan, whichFom, rowSpec.doUpper];
newResult ← PosResult[(lowChWidth + upChWidth) * lgRows.maxRowWidth, (lowChWidth + upChWidth), (lowWireLength + upWireLength), numFts];
IF debug THEN WritePosResult[" trial result: ", newResult];
SELECT ComparePosResult[newResult, oldResult]
FROM
less => {
-- strict improvement, thats good
IF debug THEN TerminalIO.PutRope[" accept improvement\n"];
lowerChan.chanWidth ← lowChWidth;
upperChan.chanWidth ← upChWidth;
oldResult ← newResult;
goodExch ← goodExch + 1};
equal, greater => {
-- this change made it worse, restore old placement
IF debug THEN WritePosResult[" restore old result: ", oldResult];
SCPlaceUtil.ExchPosRow[handle, pos, nextPos, row]};
ENDCASE};
IF rowSpec.row.nLgsOnRow > 0
AND ~rowSpec.row.fnlLgFxd
AND (rowSpec.doUpper
OR rowSpec.doLower)
THEN {
TerminalIO.PutRope[Rope.Cat[" improve row ", Convert.RopeFromInt[row], ", "]];
[oldLowChWidth, oldLowWireLength] ← SCWidthUtil.GetChanWidth[handle, lowerChan, whichFom, rowSpec.doLower];
[oldUpChWidth, oldUpWireLength] ← SCWidthUtil.GetChanWidth[handle, upperChan, whichFom, rowSpec.doUpper];
oldResult ← PosResult[(oldLowChWidth + oldUpChWidth) * lgRows.maxRowWidth, (oldLowChWidth + oldUpChWidth), (oldLowWireLength + oldUpWireLength), numFts];
IF debug THEN WritePosResult["\n original logic result: ", oldResult];
[] ← SCRowUtil.EnumerateInstsOnRow[handle, row, 1, MAX[0, rowSpec.row.nLgsOnRow-1], TrialInterchange];
TerminalIO.PutRope[Rope.Cat[Convert.RopeFromInt[goodExch], " good exchanges\n"]]}};
improve positions on bonding pad sides
PosImproveLeftRight:
PROCEDURE[handle:
SC.Handle, side:
SC.Side, whichFom: SCPrivate.FomType]
RETURNS [goodExch:
INT ← 0] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
structureData: SCPrivate.StructureData ← NARROW[handle.structureData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
bpRow: SCPrivate.BpRow ← layoutData.bpRows[side];
sideChan: SCPrivate.SideChan ← layoutData.sideChans[side];
wireLength: SC.Number;
oldResult: PlaceResult;
TrialInterchange: SCRowUtil.EachInstProc = {
doChanWidth: SCPrivate.ChanSet ← RTSets.RTMdSetEmpty;
doRowWidth: SCPrivate.ChanSet ← RTSets.RTMdSetEmpty;
doSideWidth: SCPrivate.LRSideSet ← RTSets.RTSmSetEmpty;
nextPos: SCPrivate.MaxPosSr ← pos +1;
newWireLength: SC.Number;
newResult: PlaceResult;
nets: SCPrivate.NetList ← SCNetUtil.NetsOnInst[LIST[bpRow.bpsOnSide[pos], bpRow.bpsOnSide[nextPos]]];
[doChanWidth, doSideWidth, doRowWidth] ← SCSmash.RemoveNetsWFts[handle, nets, TRUE, doChanWidth, doSideWidth, doRowWidth];
SCPlaceUtil.ExchPosSide[handle, pos, nextPos, side];
[doChanWidth, doSideWidth, doRowWidth] ← SCSmash.SmashNets[handle, nets, FALSE, doChanWidth, doSideWidth, doRowWidth];
newWireLength ← DoWidths[handle, doChanWidth, doSideWidth, doRowWidth, whichFom];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCInstUtil.AsgnChanPos[handle];
newResult ← PlaceResult[layoutData.totWidth * layoutData.totHeight, layoutData.sideChans[side].sideChanWidth, newWireLength, structureData.instances.numFts];
IF debug THEN WritePosResult[" trial result: ", newResult];
SELECT ComparePlaceResult[newResult, oldResult]
FROM
less => {
-- strict improvement, thats good
IF debug THEN TerminalIO.PutRope[" accept improvement\n"];
wireLength ← newWireLength;
oldResult ← newResult;
goodExch ← goodExch + 1};
equal => {
-- neutral exchange, take it
IF debug THEN TerminalIO.PutRope[" accept neutral exchange\n"];
wireLength ← newWireLength;
oldResult ← newResult};
greater => {
-- this change made it worse, restore old placement
IF debug THEN WritePosResult[" restore old result: ", oldResult];
[doChanWidth, doSideWidth, doRowWidth] ← SCSmash.RemoveNetsWFts[handle, nets, FALSE, RTSets.RTMdSetEmpty, RTSets.RTSmSetEmpty, RTSets.RTMdSetEmpty];
SCPlaceUtil.ExchPosSide[handle, pos, nextPos, side];
[doChanWidth, doSideWidth, doRowWidth] ← SCSmash.RestoreNetsWFts[handle, nets, doChanWidth, doSideWidth, doRowWidth];
wireLength ← DoWidths[handle, doChanWidth, doSideWidth, doRowWidth, whichFom];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCInstUtil.AsgnChanPos[handle]};
ENDCASE};
IF ~ bpRow.fnlBpFxd
THEN {
TerminalIO.PutRope[Rope.Cat[" improve ", SCRowUtil.sideName[side], " side, "]];
SCWidthUtil.AllChanWidths[handle, whichFom];
wireLength ← DoWidths[handle, RTSets.RTMdSetEmpty, RTSets.RTSmSetEmpty, RTSets.RTMdSetEmpty, whichFom];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCInstUtil.AsgnChanPos[handle];
oldResult ← PlaceResult[layoutData.totWidth * layoutData.totHeight, sideChan.sideChanWidth, wireLength, structureData.instances.numFts];
IF debug THEN WritePosResult[Rope.Cat["\n original ", SCRowUtil.sideName[side] ," result: "], oldResult];
[] ← SCRowUtil.EnumerateInstsOnSide[handle, side, 1, MAX[0, bpRow.nBpsOnSide -1], TrialInterchange];
TerminalIO.PutRope[Rope.Cat[Convert.RopeFromInt[goodExch], " good exchanges\n"]];
SCWidthUtil.AllChanWidths[handle, areaFom]}};
improve positions on bonding pad sides
PosImproveTopBot:
PROCEDURE[handle:
SC.Handle, side:
SC.Side, whichFom: SCPrivate.FomType]
RETURNS [goodExch:
INT ← 0] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
structureData: SCPrivate.StructureData ← NARROW[handle.structureData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
bpRow: SCPrivate.BpRow ← layoutData.bpRows[side];
rowChans: SCPrivate.RowChans ← layoutData.rowChans;
numFts: INT ← structureData.instances.numFts;
oldChWidth, oldWireLength: SC.Number;
oldResult: PosResult;
chan: SCPrivate.RowChan ←
SELECT side
FROM
top => rowChans.chans[rowChans.count],
bottom => rowChans.chans[1],
ENDCASE => SC.Error[programmingError, "Invalid side in Placement Improvement"];
TrialInterchange: SCRowUtil.EachInstProc = {
nextPos: SCPrivate.MaxPosSr ← pos + 1;
chWidth, wireLength: SC.Number;
newResult: PosResult;
SCPlaceUtil.ExchPosSide[handle, pos, nextPos, side];
[chWidth, wireLength] ← SCWidthUtil.GetChanWidth[handle, chan, whichFom, TRUE];
newResult ← PosResult[chWidth * lgRows.maxRowWidth, chWidth, wireLength, numFts];
IF debug THEN WritePosResult[" trial result: ", newResult];
SELECT ComparePosResult[newResult, oldResult] FROM
less => {
-- strict improvement, thats good
IF debug THEN TerminalIO.PutRope[" accept improvement\n"];
chan.chanWidth ← chWidth;
oldResult ← newResult;
goodExch ← goodExch + 1};
equal, greater => {
-- this change made it worse, restore old placement
IF debug THEN WritePosResult[" restore old result: ", oldResult];
SCPlaceUtil.ExchPosSide[handle, pos, nextPos, side]};
ENDCASE};
IF ~ bpRow.fnlBpFxd
THEN {
TerminalIO.PutRope[Rope.Cat[" improve ", SCRowUtil.sideName[side], " side, "]];
[oldChWidth, oldWireLength] ← SCWidthUtil.GetChanWidth[handle, chan, whichFom, TRUE];
oldResult ← PosResult[oldChWidth * lgRows.maxRowWidth, oldChWidth, oldWireLength, numFts];
IF debug THEN WritePosResult[Rope.Cat["\n original ", SCRowUtil.sideName[side]," result: "], oldResult];
[] ← SCRowUtil.EnumerateInstsOnSide[handle, side, 1, MAX[0, bpRow.nBpsOnSide-1], TrialInterchange];
TerminalIO.PutRope[Rope.Cat[Convert.RopeFromInt[goodExch], " good exchanges\n"]]}};
PosImprove:
PUBLIC
PROCEDURE [handle:
SC.Handle, whichFom: SCPrivate.FomType, maxCycles:
INT] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
bpRows: SCPrivate.BpRows ← layoutData.bpRows;
startArea: SC.Number;
numCycles: INT ← 0;
stop: BOOLEAN ← FALSE;
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCWidthUtil.AllChanWidths[handle, areaFom];
SCInstUtil.AsgnChanPos[handle];
startArea ← SCUtil.WriteResults["Position improvement\n starting area:", handle, 0];
WHILE ~stop
DO
goodExch: INT ← 0;
goodTopExch, goodBottomExch, goodRightExch, goodLeftExch: INT;
EachRow: SCRowUtil.EachRowProc = {
goodExch ← goodExch + PosImproveRow[handle, ChosRow[handle, row], whichFom];
quit ← stop};
IF numCycles >= maxCycles
THEN
EXIT;
improve the left and right sides
goodLeftExch ← PosImproveLeftRight[handle, left, whichFom];
goodRightExch ← PosImproveLeftRight[handle, right, whichFom];
SCInstUtil.AsgnChanPos[handle];
[] ← SCRowUtil.EnumerateRows[handle, EachRow];
now improve the top and bottom sides
goodTopExch ← PosImproveTopBot[handle, top, whichFom];
goodBottomExch ← PosImproveTopBot[handle, bottom, whichFom];
IF goodExch + goodLeftExch + goodRightExch + goodTopExch + goodBottomExch <= 0 THEN EXIT;
SCInstUtil.AsgnChanPos[handle];
numCycles ← numCycles + 1;
ENDLOOP;
SCInstUtil.AllOffsets[handle];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCWidthUtil.AllChanWidths[handle, areaFom];
SCInstUtil.AsgnChanPos[handle];
[] ← SCUtil.WriteResults["End position improvement\n ending area:", handle, startArea];
IF debug THEN SCPlaceUtil.WriteCurPlace[handle]}; -- PosImprove
improve orientation on logic rows
OrientImproveRow:
PROCEDURE [handle:
SC.Handle, rowSpec: RowSpec, whichFom: SCPrivate.FomType]
RETURNS [goodExch:
INT ← 0] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
structureData: SCPrivate.StructureData ← NARROW[handle.structureData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
row: SCPrivate.MaxRowSr ← rowSpec.row.rowNum;
rowChans: SCPrivate.RowChans ← layoutData.rowChans;
lowerChan: SCPrivate.RowChan ← rowChans.chans[row];
upperChan: SCPrivate.RowChan ← rowChans.chans[row+1];
numFts: INT ← structureData.instances.numFts;
oldLowChWidth, oldUpChWidth, oldLowWireLength, oldUpWireLength: SC.Number;
oldResult: PosResult;
TrialInterchange: SCRowUtil.EachInstProc = {
lowChWidth, upChWidth, upWireLength, lowWireLength: SC.Number;
newResult: PosResult;
IF instance.fnlOrien = 0
THEN {
SCPlaceUtil.ReverseOrientation[instance];
[lowChWidth, lowWireLength] ← SCWidthUtil.GetChanWidth[handle, lowerChan, whichFom, rowSpec.doLower];
[upChWidth, upWireLength] ← SCWidthUtil.GetChanWidth[handle, upperChan, whichFom, rowSpec.doUpper];
newResult ← PosResult[(lowChWidth + upChWidth) * lgRows.maxRowWidth, (lowChWidth + upChWidth), (lowWireLength + upWireLength), numFts];
IF debug THEN WritePosResult[" trial result: ", newResult];
SELECT ComparePosResult[newResult, oldResult]
FROM
less => {
-- strict improvement, thats good
IF debug THEN TerminalIO.PutRope[" accept improvement\n"];
lowerChan.chanWidth ← lowChWidth;
upperChan.chanWidth ← upChWidth;
oldResult ← newResult;
goodExch ← goodExch + 1};
equal, greater => {
-- this change made it worse, restore old placement
IF debug THEN WritePosResult[" restore old result: ", oldResult];
SCPlaceUtil.ReverseOrientation[instance]};
ENDCASE}};
IF rowSpec.row.nLgsOnRow > 0
AND (rowSpec.doUpper
OR rowSpec.doLower)
THEN {
TerminalIO.PutRope[Rope.Cat[" improve orientation on row ", Convert.RopeFromInt[row], ", "]];
[oldLowChWidth, oldLowWireLength] ← SCWidthUtil.GetChanWidth[handle, lowerChan, whichFom, rowSpec.doLower];
[oldUpChWidth, oldUpWireLength] ← SCWidthUtil.GetChanWidth[handle, upperChan, whichFom, rowSpec.doUpper];
oldResult ← PosResult[(oldLowChWidth + oldUpChWidth) * lgRows.maxRowWidth, (oldLowChWidth + oldUpChWidth), (oldLowWireLength + oldUpWireLength), numFts];
IF debug THEN WritePosResult[" original result: ", oldResult];
[] ← SCRowUtil.EnumerateAllInstsOnRow[handle, row, TrialInterchange];
TerminalIO.PutRope[Rope.Cat[Convert.RopeFromInt[goodExch], " good exchanges\n"]]}};
OrientImprove:
PUBLIC
PROCEDURE [handle:
SC.Handle, whichFom: SCPrivate.FomType, maxCycles:
INT] = {
layoutData: SCPrivate.LayoutData ← NARROW[handle.layoutData];
lgRows: SCPrivate.LgRows ← layoutData.lgRows;
startArea: SC.Number;
numCycles: INT ← 0;
stop: BOOLEAN ← FALSE;
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCWidthUtil.AllChanWidths[handle, areaFom];
SCInstUtil.AsgnChanPos[handle];
startArea ← SCUtil.WriteResults["Orientation improvement\n starting area:", handle, 0];
WHILE ~stop
DO
EachRow: SCRowUtil.EachRowProc = {
goodExch ← OrientImproveRow[handle, ChosRow[handle, row], whichFom];
goodExch: INT ← 0;
IF numCycles >= maxCycles THEN EXIT;
SCInstUtil.AsgnChanPos[handle];
[] ← SCRowUtil.EnumerateRows[handle, EachRow];
IF goodExch <= 0 THEN EXIT;
SCInstUtil.AsgnChanPos[handle];
numCycles ← numCycles + 1;
ENDLOOP;
SCInstUtil.AllOffsets[handle];
[lgRows.maxRowWidth, lgRows.numMaxRows] ← SCRowUtil.FindMaxRow[handle];
SCWidthUtil.AllChanWidths[handle, areaFom];
SCInstUtil.AsgnChanPos[handle];
[] ← SCUtil.WriteResults["End orientation improvement\n ending area:", handle, startArea];
IF debug THEN SCPlaceUtil.WriteCurPlace[handle]}; -- OrientImprove
}.