PWLowImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reversed.
Created by Bertrand Serlet
Last Edited by: McCreight, December 27, 1984 11:19:21 am PST
Last Edited by: Serlet, May 3, 1985 7:23:59 pm PDT
Last Edited by: Monier, February 21, 1985 11:51:35 pm PST
DIRECTORY
CD,
CDAbuts,
CDBasics USING [Inside],
CDCells USING [CreateEmptyCell],
CDDirectory,
CDEvents USING [EventProc, RegisterEventProc],
CDObjectProcs USING [FetchFurther, RegisterFurther, StoreFurther],
CDOrient USING [MapRect, Orientation],
CDPinObjects USING [CreatePinOb],
CDProperties USING [CopyVal, DontCopy, FetchProcs, GetPropFromObject, PutPropOnApplication, PutPropOnObject, RegisterProperty],
CDRects USING [CreateRect],
CMos USING [met, met2],
Convert,
PWBasics,
PWLow,
PWStretch,
Rope,
TerminalIO USING [UserSaysYes];
PWLowImpl: CEDAR PROGRAM    
IMPORTS CD, CDAbuts, CDBasics, CDCells, CDDirectory, CDEvents, CDObjectProcs, CDOrient, -- CDPinObjects, -- CDProperties, -- CDRects, CMos, -- Convert, PWBasics, PWStretch, Rope, TerminalIO
EXPORTS PWLow =
BEGIN
OPEN PWLow;
Internal data types
Currently, all our objects are either Atomic, or Abuts.
SortedSegments: TYPE = PWStretch.SortedSegments;
SortedStretchSegments: TYPE = PWStretch.SortedStretchSegments;
Segment: TYPE = PWStretch.Segment;
PosRange: TYPE = PWStretch.PosRange;
ROPE: TYPE = PWStretch.ROPE;
-- All Abuts have $DoStretchProc, $ParseStretchProc and $ParsePinsProc attributes, and, like all other objects they have $PWEdges and $PWHorVer attributes, once parsed.
The internal data structure, hanging on obj under the prop $PWEdges.
-- Cells may be shared in Abuts, but Edges are not.
Edges: TYPE = REF EdgesRec;
EdgesRec: TYPE = RECORD [
left, right, bottom, top: SortedSegments  -- describes all pins starting at lower-left (resp lr, ll, ul) corner sorted in ascending Y (resp Y, X, X). Info field contains a CD.ObPtr of class Pin.
];
HorVer: TYPE = REF HorVerRec;
HorVerRec: TYPE = RECORD [
hor, ver: SortedStretchSegments     -- list of all vertical (resp horizontal) stretch possible areas, that is X (resp. Y) ranges where it is possible to stretch in X (resp Y). Info field contains a Stretch.
];
Utilities
Makes an object non-stretchable (All it does is put a property on that object telling that for ParseStretch, the answer to the message should be NIL)
MakeObjNonStretchable: PUBLIC PROC [obj: CD.ObPtr] = {
CDProperties.PutPropOnObject[obj, $ParseStretchProc, NEW [PWStretch.ParseStretchProc ← PWStretch.HardObjParseStretch]];
};
ReName: PUBLIC PROC [design: CD.Design, obj: CD.ObPtr, instanceName: ROPE] RETURNS [cell: CD.ObPtr] =
{appl: CD.ApplicationPtr;
cell ← CDCells.CreateEmptyCell[];
appl ← PWBasics.IncludeApplication[cell: cell, subcell: obj];
CDProperties.PutPropOnApplication[appl, $InstanceName, instanceName];
PWBasics.RepositionCell[design, cell, instanceName]};
AbutProblem: SIGNAL [what: ROPE] = CODE;
-- Dealing with edges properties
GetCellEdges: PROC [obj: CD.ObPtr] RETURNS [edges: Edges] =
{edges ← NARROW [CDProperties.GetPropFromObject[obj, $PWEdges]]};
PutCellEdges: PROC [obj: CD.ObPtr, edges: Edges] =
{IF edges=NIL THEN ERROR;
CDProperties.PutPropOnObject[obj, $PWEdges, edges]};
GetCellHorVer: PROC [obj: CD.ObPtr] RETURNS [horVer: HorVer] =
{horVer ← NARROW [CDProperties.GetPropFromObject[obj, $PWHorVer]]};
PutCellHorVer: PROC [obj: CD.ObPtr, horVer: HorVer] =
{IF horVer=NIL THEN ERROR;
CDProperties.PutPropOnObject[obj, $PWHorVer, horVer]};
ChangeOrientation: PUBLIC PROC [design: CD.Design, obj: CD.ObPtr, orientation: CDOrient.Orientation] RETURNS [cell: CD.ObPtr] =
BEGIN
appl: CD.ApplicationPtr;
proc: PWBasics.MapRectProc =
{RETURN [CDOrient.MapRect[rect, obj.size, orientation]]};
cell ← CDCells.CreateEmptyCell[];
appl ← PWBasics.IncludeApplication[cell: cell, subcell: obj, orientation: orientation];
PWBasics.RepositionCell[design, cell];
END;
The cache laid on Objects for quickly telling the result of an Abut of obj1 and obj2 is a list of triplets [obj2, newObj1, newObj2] put on the property-list of obj1 under the name $PWPreviousAbutX or $PWPreviousAbutY
ThreeObjRef: TYPE = REF ThreeObjRec;
ThreeObjRec: TYPE = RECORD [obj2, newObj1, newObj2: CD.ObPtr];
FetchCache: PROC [abutX: BOOL, listObj: LIST OF CD.ObPtr] RETURNS [found: BOOL, newObj: CD.ObPtr ← NIL] =
{list: LIST OF ThreeObjRef ← NARROW [CDProperties.GetPropFromObject[obj1, prop]];
WHILE list#NIL DO
IF list.first.obj2=obj2 THEN RETURN [TRUE, list.first.newObj1, list.first.newObj2];
list ← list.rest
ENDLOOP;
RETURN [FALSE, NIL, NIL]};
{RETURN [FALSE, NIL]};
StoreCache: PROC [abutX: BOOL, listObj: LIST OF CD.ObPtr, newObj: CD.ObPtr] =
{list: LIST OF ThreeObjRef ← NARROW [CDProperties.GetPropFromObject[obj1, prop]];
list ← CONS [NEW [ThreeObjRec ← [obj2, newObj1, newObj2]], list];
CDProperties.PutPropOnObject[obj1, prop, list]};
{};
Abuts with n arguments
AbutListX: PUBLIC PROC [design: CD.Design, listObj: LIST OF CD.ObPtr] RETURNS [abutX: CD.ObPtr] =
BEGIN
abutX ← AbutListInternal[design, listObj, "X", CDAbuts.CreateNewAbutX, TRUE];
END;
AbutListY: PUBLIC PROC [design: CD.Design, listObj: LIST OF CD.ObPtr] RETURNS [abutY: CD.ObPtr] =
BEGIN
abutY ← AbutListInternal[design, listObj, "Y", CDAbuts.CreateNewAbutY, FALSE];
END;
AbutListInternal: PROC [design: CD.Design, listObj: LIST OF CD.ObPtr, abutTypeRope: ROPE, createNewAbut: CDAbuts.CreateAbutProc, abutX: BOOL] RETURNS [newObj: CD.ObPtr] =
BEGIN
listObj can include some elements which are NIL. So the new list newListObj does not.
CleanList: PROC [listObj: LIST OF CD.ObPtr] RETURNS [newListObj: LIST OF CD.ObPtr ← NIL] =
BEGIN
WHILE listObj#NIL DO
IF listObj.first#NIL THEN newListObj ← CONS [listObj.first, newListObj];
listObj ← listObj.rest;
ENDLOOP;
newListObj ← Reverse[newListObj];
END;
A hack especially for Louis.
AllSameISizeAndNoPin: PROC [listObj: LIST OF CD.ObPtr, iSize: CD.Position] RETURNS [same: BOOLTRUE] =
BEGIN
WHILE listObj#NIL DO
edges: Edges;
IF (IF abutX THEN PWBasics.GetISize[listObj.first].y#iSize.y ELSE PWBasics.GetISize[listObj.first].x#iSize.x) THEN RETURN [FALSE];
edges ← ParseEdges[design, listObj.first];
IF edges.right#NIL OR edges.left#NIL OR edges.top#NIL OR edges.bottom#NIL THEN RETURN [FALSE];
listObj ← listObj.rest;
ENDLOOP;
END;
Length: PROC [listObj: LIST OF CD.ObPtr] RETURNS [length: INT ← 0] =
{WHILE listObj#NIL DO length ← length+1; listObj ← listObj.rest ENDLOOP};
Reverse: PROC [listObj: LIST OF CD.ObPtr] RETURNS [result: LIST OF CD.ObPtr ← NIL] =
{WHILE listObj#NIL DO result ← CONS [listObj.first, result]; listObj ← listObj.rest ENDLOOP};
RevAppend: PROC [revhead, tail: LIST OF CD.ObPtr] RETURNS [LIST OF CD.ObPtr] =
{WHILE revhead#NIL DO tail ← CONS [revhead.first, tail]; revhead ← revhead.rest ENDLOOP;
RETURN [tail]};
CutInTwo: PROC [listObj: LIST OF CD.ObPtr, lengthHead: INT] RETURNS [head, tail: LIST OF CD.ObPtr ← NIL] =
{WHILE lengthHead>0 DO
head ← CONS [listObj.first, head]; listObj ← listObj.rest;
lengthHead ← lengthHead - 1
ENDLOOP;
head ← Reverse[head]; tail ← listObj};
Append: PROC [head, tail: LIST OF CD.ObPtr] RETURNS [LIST OF CD.ObPtr] =
{RETURN [RevAppend[Reverse[head], tail]]};
length: INT; iSize: CD.Position;
listObj ← CleanList[listObj];
length ← Length[listObj];
IF length=0 THEN RETURN [NIL];
IF length=1 THEN RETURN [listObj.first];
iSize ← PWBasics.GetISize[listObj.first];
IF AllSameISizeAndNoPin[listObj, iSize] THEN {
newObj ← createNewAbut[listObj];
PWBasics.Include[design, newObj];
RETURN;
};
We look in the cache just in case they would be there
BEGIN
found: BOOL;
[found, newObj] ← FetchCache[abutX, listObj];
IF found THEN RETURN;
END;
BEGIN
ENABLE AbutProblem =>
{Error[design, abutTypeRope, listObj, what]; GOTO Return};
leftPins: SortedSegmentsSequence ← NEW [SortedSegmentsSequenceRec[length]];
rightPins: SortedSegmentsSequence ← NEW [SortedSegmentsSequenceRec[length]];
sizes: ValueSequence ← NEW [ValueSequenceRec[length]];
stretches: SortedSegmentsSequence;
newObjs: ObPtrSequence ← NEW [ObPtrSequenceRec[length]];
newListObj: LIST OF CD.ObPtr ← listObj;
FOR nb: INT IN [0..length) DO
edges: Edges ← ParseEdges[design, newListObj.first];
IF abutX THEN {
leftPins[nb] ← edges.left; rightPins[nb] ← edges.right;
sizes[nb] ← PWBasics.GetISize[newListObj.first].y;
} ELSE {
leftPins[nb] ← edges.bottom; rightPins[nb] ← edges.top;
sizes[nb] ← PWBasics.GetISize[newListObj.first].x;
};
newObjs[nb] ← newListObj.first;
newListObj ← newListObj.rest;
ENDLOOP;
stretches ← MatchPins[leftPins, rightPins, sizes];
FOR nb: INT IN [0..length) DO
newObjs[nb] ← IF abutX THEN DoStretch[design, newObjs[nb], NIL, stretches[nb]] ELSE DoStretch[design, newObjs[nb], stretches[nb], NIL];
ENDLOOP;
We detect here mismatch of interest rectangles
FOR nb: INT IN [1..length) DO
IF (IF abutX THEN PWBasics.GetISize[newObjs[0]].y#PWBasics.GetISize[newObjs[nb]].y ELSE PWBasics.GetISize[newObjs[0]].x#PWBasics.GetISize[newObjs[nb]].x)
THEN SIGNAL AbutProblem["Different sizes for rectangles!"];
ENDLOOP;
We make a big list for abuting them
FOR nb: INT DECREASING IN [0..length) DO
newListObj ← CONS [newObjs[nb], newListObj];
ENDLOOP;
newObj ← createNewAbut[newListObj];
PWBasics.Include[design, newObj];
StoreCache[abutX, listObj, newObj];
EXITS Return => RETURN [NIL];
END;
END;
Abuts with 2 arguments
AbutX: PUBLIC PROC [design: CD.Design, obj1, obj2: CD.ObPtr] RETURNS [abutX: CD.ObPtr] =
{abutX ← AbutListX[design, LIST [obj1, obj2]]};
AbutY: PUBLIC PROC [design: CD.Design, obj1, obj2: CD.ObPtr] RETURNS [abutY: CD.ObPtr] =
{abutY ← AbutListY[design, LIST [obj1, obj2]]};
Abuts with 2 arguments
Error: PROC [design: CD.Design, abutTypeRope: ROPE, listObj: LIST OF CD.ObPtr, what: ROPE] =
BEGIN
PWBasics.Output["PW Error while abuting in ", abutTypeRope, " objects "];
WHILE listObj#NIL DO
PWBasics.Output["'", PWBasics.NameFromObj[listObj.first], "' "];
listObj ← listObj.rest;
ENDLOOP;
PWBasics.Output["': ", what, "\n"];
PWBasics.Output["ATTENTION: Objects are added SOMEWHERE in the design\n"];
PWBasics.AddAnObject[design, obj1, [0, 0]];
PWBasics.AddAnObject[design, obj2, IF Rope.Equal[abutTypeRope,"X"] THEN [obj1.size.x, 0] ELSE [0, obj1.size.y]];
PWBasics.AddAnObject[design, VisualizeEdgesFromObj[design, obj1], [500, 0]];
PWBasics.AddAnObject[design, VisualizeEdgesFromObj[design, obj2], IF Rope.Equal[abutTypeRope,"X"] THEN [500+ obj1.size.x, 0] ELSE [500, obj1.size.y]];
IF TerminalIO.UserSaysYes[text: "Do you want to open an error window for StackWalking until the error?", label: "Open EventViewer?", default: TRUE] THEN ERROR;
END;
leftPins and rightPins are the edges to be fusionned. The first element of leftPins is unused, as for the last element of rightPins. sizes are the sizes of the obj. Returns stretches, the places where stretch should occur.
ObPtrSequence: TYPE = REF ObPtrSequenceRec;
ObPtrSequenceRec: TYPE = RECORD [contents: SEQUENCE size: [0..9999] OF CD.ObPtr];
SortedSegmentsSequence: TYPE = REF SortedSegmentsSequenceRec;
SortedSegmentsSequenceRec: TYPE = RECORD [contents: SEQUENCE size: [0..9999] OF SortedSegments];
PosRangeSequence: TYPE = REF PosRangeSequenceRec;
PosRangeSequenceRec: TYPE = RECORD [contents: SEQUENCE size: [0..9999] OF PosRange];
ValueSequence: TYPE = REF ValueSequenceRec;
ValueSequenceRec: TYPE = RECORD [contents: SEQUENCE size: [0..9999] OF INT];
MatchPins: PROC [leftPins, rightPins: SortedSegmentsSequence, sizes: ValueSequence] RETURNS [stretches: SortedSegmentsSequence] =
BEGIN
PosAfterStretch: PROC [pos: INT, number: INT] RETURNS [newPos: INT] = {
s: SortedSegments ← stretches[number];
newPos ← pos;
WHILE s#NIL DO
IF s.first.pos.max > pos THEN ERROR;
newPos ← newPos + PWStretch.GetStretchValue[s.first]; s ← s.rest ENDLOOP;
};
Stretch: PROC [number: INT, value: INT, stretchBefore: INT] = {
stretches[number] ← PWStretch.InsertStretchSegment[
[previousPins[number], stretchBefore],
value, stretches[number]];
PWBasics.Output["After Stretch!\n"]; DebugOutput[];
};
DebugOutput: PROC [] = {
OutputSortedSegments: PROC [s: SortedSegments] = {
PWBasics.Output["("];
WHILE s#NIL DO
PWBasics.Output[Convert.RopeFromInt[s.first.pos.min], "—", Convert.RopeFromInt[s.first.pos.max]];
WITH s.first.info SELECT FROM
value: REF INT => PWBasics.Output["[", Convert.RopeFromInt[value^], "]"];
ENDCASE   => {};
PWBasics.Output[" "];
s ← s.rest;
ENDLOOP;
PWBasics.Output[")"];
};
FOR nb: INT IN [0..size) DO
PWBasics.Output["Nb: ", Convert.RopeFromInt[nb], " left: "];
OutputSortedSegments[leftPins[nb]];
PWBasics.Output[" right: "]; OutputSortedSegments[rightPins[nb]];
PWBasics.Output[" stretches: "]; OutputSortedSegments[stretches[nb]];
PWBasics.Output[" previousPins: ", Convert.RopeFromInt[previousPins[nb]], "\n"];
ENDLOOP;
};
size: INT ← sizes.size;
previousPins: ValueSequence ← NEW [ValueSequenceRec[size]];
IF leftPins.size#size OR rightPins.size#size THEN ERROR;
stretches ← NEW [SortedSegmentsSequenceRec[size]];
FOR nb: INT IN [0..size) DO
previousPins[nb] ← 0;
stretches[nb] ← NIL;
ENDLOOP;
PWBasics.Output["Entry of proc!\n"]; DebugOutput[];
DO
find position of lowest pin, inducing the biggest delta
pos: INTLAST [INT]; number: INT; maxDelta: INT ← 0;
FOR nb: INT IN (0..size) DO
left, right: SortedSegments;
thesePinsPos: INT; delta: INT;
left ← leftPins[nb]; right ← rightPins[nb-1];
IF left#NIL OR right#NIL THEN {
IF left=NIL OR right=NIL THEN SIGNAL AbutProblem["Number of pins differ"];
thesePinsPos ← MIN [
PosAfterStretch[left.first.pos.min, nb],
PosAfterStretch[right.first.pos.min, nb-1]];
delta ← ABS [PosAfterStretch[left.first.pos.min, nb] - PosAfterStretch[right.first.pos.min, nb-1]];
IF thesePinsPos < pos THEN {
number ← nb; pos ← thesePinsPos; maxDelta ← delta;
};
IF thesePinsPos = pos AND delta>maxDelta THEN {
number ← nb; maxDelta ← delta;
};
};
ENDLOOP;
IF pos=LAST [INT] THEN EXIT; -- no pins left
we know that the matches takes place between leftPins[number-1] and rightPins[number]
BEGIN
leftPin: Segment ← leftPins[number].first;
rightPin: Segment ← rightPins[number-1].first;
delta: INT ← PosAfterStretch[leftPin.pos.min, number] - PosAfterStretch[rightPin.pos.min, number-1];
IF leftPin.pos.max-leftPin.pos.min # rightPin.pos.max-rightPin.pos.min THEN SIGNAL AbutProblem["Widths of corresponding pins differ"]; -- EMM
SELECT TRUE FROM
delta<0   => Stretch[number, -delta, leftPin.pos.min];
delta>0   => Stretch[number-1, delta, rightPin.pos.min];
ENDCASE  => { -- these lowest pins are at same level, let's forget them
PWBasics.Output["forget them!\n"]; DebugOutput[];
leftPins[number] ← leftPins[number].rest;
rightPins[number-1] ← rightPins[number-1].rest;
previousPins[number] ← leftPin.pos.max;
previousPins[number-1] ← rightPin.pos.max;
};
END;
ENDLOOP;
We must now enlarge the cells so to match the interest rects
BEGIN
pos: INT ← 0; -- pos will be the highest interest rect
FOR nb: INT IN [0..size) DO
pos ← MAX [pos, PosAfterStretch[sizes[nb], nb]];
ENDLOOP;
FOR nb: INT IN [0..size) DO
IF pos#PosAfterStretch[sizes[nb], nb] THEN Stretch[nb, pos - PosAfterStretch[sizes[nb], nb], sizes[nb]];
ENDLOOP;
END;
END;
-- Code for parsing cells
ParsePinsProc: TYPE = PROC [design: CD.Design, obj: CD.ObPtr] RETURNS [left, right, bottom, top: SortedSegments];
-- The general Proc calling all the others
ParseEdges: PROC [design: CD.Design, obj: CD.ObPtr] RETURNS [edges: Edges] =
BEGIN
edges ← GetCellEdges[obj];
IF edges=NIL THEN
BEGIN
edges ← NEW [EdgesRec];
[edges.left, edges.right, edges.bottom, edges.top] ← ParsePins[design, obj];
PutCellEdges[obj, edges];
END;
END;
ParseHorVer: PROC [design: CD.Design, obj: CD.ObPtr] RETURNS [horVer: HorVer] =
BEGIN
horVer ← GetCellHorVer[obj];
IF horVer=NIL THEN
BEGIN
edges: Edges = ParseEdges[design, obj];
size: CD.Position = PWBasics.GetISize[obj];
horVer ← NEW [HorVerRec];
[horVer.hor, horVer.ver] ← PWStretch.ParseStretch[design, obj];
Now we avoid pins
horVer.hor ← PWStretch.Intersect[horVer.hor, PWStretch.Complement[edges.bottom, [0, size.x]]];
horVer.hor ← PWStretch.Intersect[horVer.hor, PWStretch.Complement[edges.top, [0, size.x]]];
horVer.ver ← PWStretch.Intersect[horVer.ver, PWStretch.Complement[edges.right, [0, size.y]]];
horVer.ver ← PWStretch.Intersect[horVer.ver, PWStretch.Complement[edges.left, [0, size.y]]];
PutCellHorVer[obj, horVer];
END;
END;
ParsePins: ParsePinsProc -- [design: CD.Design, obj: CD.ObPtr] RETURNS [left: PWStretch.SortedSegments, right: PWStretch.SortedSegments, bottom: PWStretch.SortedSegments, top: PWStretch.SortedSegments] -- =
BEGIN
AllMarks: TYPE = REF AllMarksRec;
AllMarksRec: TYPE = RECORD [left, right, bottom, top: SortedSegments];
allmarks: AllMarks ← NARROW [CDProperties.GetPropFromObject[obj, $ParsedPins]];
refProc: REF;
IF allmarks#NIL THEN RETURN [allmarks.left, allmarks.right, allmarks.bottom, allmarks.top];
refProc ← CDProperties.GetPropFromObject[obj, $ParsePinsProc];
IF refProc=NIL THEN refProc ← CDObjectProcs.FetchFurther[obj.p, $ParsePinsProc];
WITH refProc SELECT FROM
proc: REF ParsePinsProc => [left, right, bottom, top] ← proc[design, obj];
ENDCASE => left ← right ← bottom ← top ← NIL;
CDProperties.PutPropOnObject[obj, $ParsedPins, NEW [AllMarksRec ← [left, right, bottom, top]]];
END;
PinParsePins: ParsePinsProc =
BEGIN
left ← right ← LIST [PWStretch.MakeNewSegment[[0, obj.size.y], obj]];
top ← bottom ← LIST [PWStretch.MakeNewSegment[[0, obj.size.x], obj]];
END;
CellParsePins: ParsePinsProc =
BEGIN
size: CD.Position ← PWBasics.GetISize[obj];
FOR appls: CD.ApplicationList ← NARROW [obj.specificRef, CD.CellPtr].contents, appls.rest
WHILE appls # NIL DO
appl: CD.ApplicationPtr ← appls.first;
location: CD.Position ← PWBasics.GetLocation[appl, obj];
subSize: CD.Position ← PWBasics.GetISize[appl.ob];
subLeft, subRight, subBottom, subTop: SortedSegments;
This proc is embedded in the loop because it uses the variables defined in it such as appl and iRect
LoopAddPin: PROC [mark: Segment, pinRect: CD.Rect] =
BEGIN
InsertPin: PROC [list: SortedSegments, pos: PosRange] RETURNS [SortedSegments] =
{RETURN [PWStretch.Insert[pos: pos, info: mark.info, list: list]]};
pinRect ← CDOrient.MapRect[pinRect, subSize, appl.orientation, location];
IF ~CDBasics.Inside[pinRect, [0, 0, size.x, size.y]] THEN ERROR;
SELECT TRUE FROM
pinRect.x1=0 AND pinRect.y1<pinRect.y2 => 
left ← InsertPin[left, PWStretch.PosRangeYFromRect[pinRect]];
pinRect.x2=size.x AND pinRect.y1<pinRect.y2 => 
right ← InsertPin[right, PWStretch.PosRangeYFromRect[pinRect]];
pinRect.y1=0 AND pinRect.x1<pinRect.x2 => 
bottom ← InsertPin[bottom, PWStretch.PosRangeXFromRect[pinRect]];
pinRect.y2=size.y AND pinRect.x1<pinRect.x2 => 
top ← InsertPin[top, PWStretch.PosRangeXFromRect[pinRect]];
ENDCASE    => {} ;
END;
[subLeft, subRight, subBottom, subTop] ← ParsePins[design, appl.ob];
FOR pins: SortedSegments ← subLeft, pins.rest WHILE pins#NIL DO
pin: Segment ← pins.first;
LoopAddPin[pin, PWStretch.RectFromPosRangeY[pin.pos, 0]];
ENDLOOP;
FOR pins: SortedSegments ← subRight, pins.rest WHILE pins#NIL DO
pin: Segment ← pins.first;
LoopAddPin[pin, PWStretch.RectFromPosRangeY[pin.pos, subSize.x]];
ENDLOOP;
FOR pins: SortedSegments ← subBottom, pins.rest WHILE pins#NIL DO
pin: Segment ← pins.first;
LoopAddPin[pin, PWStretch.RectFromPosRangeX[pin.pos, 0]];
ENDLOOP;
FOR pins: SortedSegments ← subTop, pins.rest WHILE pins#NIL DO
pin: Segment ← pins.first;
LoopAddPin[pin, PWStretch.RectFromPosRangeX[pin.pos, subSize.y]];
ENDLOOP;
ENDLOOP;
END;
AbutXParseStretch: PWStretch.ParseStretchProc -- [design: CD.Design, obj: CD.ObPtr] RETURNS [hor: PWStretch.SortedSegments, ver: PWStretch.SortedSegments] -- =
BEGIN
subObjs: LIST OF CD.ObPtr ← CDAbuts.GetAbutSubObjects[obj];
pos: CD.Number ← 0;
hor ← NIL;
ver ← PWStretch.worldSortedSegments;
WHILE subObjs#NIL DO
horVer: HorVer ← ParseHorVer[design, subObjs.first];
hor ← PWStretch.Append[hor, horVer.hor, pos];
ver ← PWStretch.Intersect[ver, horVer.ver];
pos ← pos + PWBasics.GetISize[subObjs.first].x;
subObjs ← subObjs.rest;
ENDLOOP;
END;
AbutXParsePins: ParsePinsProc -- [obj: CD.ObPtr] RETURNS [left: PWLowImpl.SortedSegments, right: PWLowImpl.SortedSegments, bottom: PWLowImpl.SortedSegments, top: PWLowImpl.SortedSegments] -- =
BEGIN
subObjs: LIST OF CD.ObPtr ← CDAbuts.GetAbutSubObjects[obj];
pos: CD.Number ← 0;
IF subObjs#NIL THEN left ← ParseEdges[design, subObjs.first].left;
WHILE subObjs#NIL DO
edges: Edges ← ParseEdges[design, subObjs.first];
bottom ← PWStretch.Append[bottom, edges.bottom, pos];
top ← PWStretch.Append[top, edges.top, pos];
right ← edges.right;
pos ← pos + PWBasics.GetISize[subObjs.first].x;
subObjs ← subObjs.rest;
ENDLOOP;
END;
AbutYParseStretch: PWStretch.ParseStretchProc -- [design: CD.Design, obj: CD.ObPtr] RETURNS [hor: PWStretch.SortedSegments, ver: PWStretch.SortedSegments] -- =
BEGIN
subObjs: LIST OF CD.ObPtr ← CDAbuts.GetAbutSubObjects[obj];
pos: CD.Number ← 0;
ver ← NIL;
hor ← PWStretch.worldSortedSegments;
WHILE subObjs#NIL DO
horVer: HorVer ← ParseHorVer[design, subObjs.first];
ver ← PWStretch.Append[ver, horVer.ver, pos];
hor ← PWStretch.Intersect[hor, horVer.hor];
pos ← pos + PWBasics.GetISize[subObjs.first].y;
subObjs ← subObjs.rest;
ENDLOOP;
END;
AbutYParsePins: ParsePinsProc -- [design: CD.Design, obj: CD.ObPtr] RETURNS [left: PWLowImpl.SortedSegments, right: PWLowImpl.SortedSegments, bottom: PWLowImpl.SortedSegments, top: PWLowImpl.SortedSegments] -- =
BEGIN
subObjs: LIST OF CD.ObPtr ← CDAbuts.GetAbutSubObjects[obj];
pos: CD.Number ← 0;
IF subObjs#NIL THEN bottom ← ParseEdges[design, subObjs.first].bottom;
WHILE subObjs#NIL DO
edges: Edges ← ParseEdges[design, subObjs.first];
left ← PWStretch.Append[left, edges.left, pos];
right ← PWStretch.Append[right, edges.right, pos];
top ← edges.top;
pos ← pos + PWBasics.GetISize[subObjs.first].y;
subObjs ← subObjs.rest;
ENDLOOP;
END;
For Debug
VisualizeEdges: PROC [design: CD.Design, size: CD.Position, edges: Edges] RETURNS [obj: CD.ObPtr] =
{obj ← CDCells.CreateEmptyCell[];
FOR list: SortedSegments ← edges.left, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDPinObjects.CreatePinOb[[2, list.first.pos.max-list.first.pos.min]], [0, list.first.pos.min]] ENDLOOP;
FOR list: SortedSegments ← edges.right, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDPinObjects.CreatePinOb[[2, list.first.pos.max-list.first.pos.min]], [size.x-2, list.first.pos.min]] ENDLOOP;
FOR list: SortedSegments ← edges.bottom, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDPinObjects.CreatePinOb[[list.first.pos.max-list.first.pos.min, 2]], [list.first.pos.min, 0]] ENDLOOP;
FOR list: SortedSegments ← edges.top, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDPinObjects.CreatePinOb[[list.first.pos.max-list.first.pos.min, 2]], [list.first.pos.min, size.y-2]] ENDLOOP;
FOR list: SortedStretchSegments ← edges.hor, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDRects.CreateRect[[list.first.pos.max-list.first.pos.min, size.y], CMos.met], [list.first.pos.min, 0]] ENDLOOP;
FOR list: SortedStretchSegments ← edges.ver, list.rest WHILE list#NIL DO
[] ← PWBasics.IncludeApplication[obj, CDRects.CreateRect[[size.x, list.first.pos.max-list.first.pos.min], CMos.met2], [0, list.first.pos.min]] ENDLOOP;
PWBasics.RepositionCell[design, obj];
};
VisualizeEdgesFromObj: PROC [design: CD.Design, obj: CD.ObPtr] RETURNS [CD.ObPtr] =
{RETURN [VisualizeEdges[design, PWBasics.GetISize[obj], ParseEdges[design, obj]]]};
-- Procs for stretching Abut cells themselves
Wanted is a StretchSegment (i.e. a PosRange and some value telling how much the stretch should be). Possible is a list of Segemnts where it is possible to stretch. Possible may perhaps not intersect wanted.pos, in which case the feasable flag will be set to FALSE. Otherwise, this proc issues a list of segments which should be really stretched. For now, the algorithm is to share nearly proportionnally to the size of the stretchable segments which intersect.
ShareAmongSortedSegments: PROC [wanted: Segment, possible: SortedSegments] RETURNS [stretches: SortedStretchSegments ← NIL, feasable: BOOLFALSE] = {
sigmaSizes: CD.Number ← 0;
value: CD.Number ← PWStretch.GetStretchValue[wanted];
possible ← PWStretch.IntersectPosSegs[wanted.pos, possible];
All intersections are now found
IF possible=NIL THEN RETURN [NIL, FALSE];
We look how big PosRange we have
FOR list: SortedSegments ← possible, list.rest WHILE list#NIL DO
sigmaSizes ← sigmaSizes + list.first.pos.max - list.first.pos.min + 1;
ENDLOOP;
We share them fairly (here's the heuristic)
FOR list: SortedSegments ← possible, list.rest WHILE list#NIL DO
pos: PosRange ← list.first.pos;
thisSize: CD.Number ← pos.max - pos.min + 1;
thisValue: CD.Number ← value * thisSize / sigmaSizes;
stretches ← PWStretch.InsertStretchSegment[pos, thisValue, stretches];
value ← value - thisValue; sigmaSizes ← sigmaSizes - thisSize;
ENDLOOP;
In case of truncation errors
IF value#0 THEN ERROR;
RETURN [stretches, TRUE];
};
DoStretch: PROC [design: CD.Design, obj: CD.ObPtr, hor, ver: SortedSegments] RETURNS [newObj: CD.ObPtr] =
BEGIN
This proc allows the stretching to take place where it is possible to stretch. Is can be improved, for example by sharing the amount of stretch wanted in all the posrange where it is possible. For the moment, the first possible place is taken.
ChooseWhereToStretch: PROC [wanted: SortedStretchSegments, possible: SortedSegments] RETURNS [allStretches: SortedStretchSegments ← NIL] = {
WHILE wanted#NIL DO
stretches: SortedStretchSegments; feasable: BOOL;
[stretches, feasable] ← ShareAmongSortedSegments[wanted.first, possible];
IF ~feasable THEN SIGNAL AbutProblem[Rope.Cat[PWBasics.NameFromObj[obj], " needed to be stretched between [", Rope.Cat[Convert.RopeFromInt[wanted.first.pos.min], ", ", Convert.RopeFromInt[wanted.first.pos.max], "] but was unable to perform stretch"]]];
WHILE stretches#NIL DO
allStretches ← PWStretch.InsertStretchSegment[stretches.first.pos, PWStretch.GetStretchValue[stretches.first], allStretches];
stretches ← stretches.rest ENDLOOP;
wanted ← wanted.rest;
ENDLOOP;
};
errMsg: ROPE;
horVer: HorVer;
IF hor=NIL AND ver=NIL THEN RETURN [obj];
horVer← ParseHorVer[design, obj];
hor ← ChooseWhereToStretch[hor, horVer.hor];
ver ← ChooseWhereToStretch[ver, horVer.ver];
[newObj, errMsg] ← PWStretch.DoStretch[design, obj, hor, ver];
IF errMsg#NIL THEN SIGNAL AbutProblem[Rope.Cat["Some object needing to be stretched could not stretch recursively: ", PWBasics.NameFromObj[obj], " ", errMsg]];
END;
AbutsDoStretch: PWStretch.DoStretchProc -- [design: CD.Design, obj: CD.ObPtr, hor: PWStretch.SortedSegments, ver: PWStretch.SortedSegments] RETURNS [newObj: CD.ObPtr, errMsg: ROPE ← NIL] -- = {
obj ← CDDirectory.Expand[obj, NIL, NIL];
PWBasics.Include[design, obj];
[newObj, errMsg] ← PWStretch.DoStretch[design, obj, hor, ver];
};
-- Flushing caches
-- Some internal caches are set, this is the way for resetting them, for example when you change cells
FlushCaches: PUBLIC PROC [design: CD.Design] =
BEGIN
FlushEdges: CDDirectory.EachEntryAction =
BEGIN
CDProperties.PutPropOnObject[ob, $PWEdges, NIL];
CDProperties.PutPropOnObject[ob, $PWHorVer, NIL];
CDProperties.PutPropOnObject[ob, $ParsedPins, NIL];
CDProperties.PutPropOnObject[ob, $PWPreviousAbutX, NIL];
CDProperties.PutPropOnObject[ob, $PWPreviousAbutY, NIL];
CDProperties.PutPropOnObject[ob, $ParsedStretch, NIL];
END;
PWBasics.Output["Flushing PW cache\n"];
[] ← CDDirectory.Enumerate[design, FlushEdges];
END;
FlushWhenEvent: CDEvents.EventProc -- [event: REF ANY, design: CD.Design, x: REF ANY] RETURNS [dont: BOOL ← FALSE] -- = {
FlushCaches[design];
};
-- Registration of our properties
[] ← CDProperties.RegisterProperty[$PWEdges, $PW];
[] ← CDProperties.RegisterProperty[$PWHorVer, $PW];
[] ← CDProperties.RegisterProperty[$ParsedPins, $PW];
[] ← CDProperties.RegisterProperty[$ParsePinsProc, $PW];
[] ← CDProperties.RegisterProperty[$PWPreviousAbutX, $PW];
[] ← CDProperties.RegisterProperty[$PWPreviousAbutY, $PW];
CDProperties.FetchProcs[$PWEdges].makeCopy ← CDProperties.DontCopy;
CDProperties.FetchProcs[$PWHorVer].makeCopy ← CDProperties.DontCopy;
CDProperties.FetchProcs[$ParsedPins].makeCopy ← CDProperties.DontCopy;
CDProperties.FetchProcs[$PWPreviousAbutX].makeCopy ← CDProperties.DontCopy;
CDProperties.FetchProcs[$PWPreviousAbutY].makeCopy ← CDProperties.DontCopy;
CDProperties.FetchProcs[$ParsePinsProc].makeCopy ← CDProperties.CopyVal;
-- We register our new ObjectProcs properties if they are not already there
IF CDObjectProcs.FetchFurther[CD.FetchObjectProcs[$Cell], $ParsePinsProc]=NIL THEN
CDObjectProcs.RegisterFurther[$ParsePinsProc];
We register new ObjectProcs properties for Cells
IF CD.FetchObjectProcs[$Cell]#NIL THEN
BEGIN
p: REF CD.ObjectProcs ← CD.FetchObjectProcs[$Cell];
CDObjectProcs.StoreFurther[p, $ParsePinsProc, NEW [ParsePinsProc ← CellParsePins]];
END;
We register new ObjectProcs properties for PinObjects
IF CD.FetchObjectProcs[$PinOb0]#NIL THEN
BEGIN
p: REF CD.ObjectProcs ← CD.FetchObjectProcs[$PinOb0];
CDObjectProcs.StoreFurther[p, $ParsePinsProc, NEW [ParsePinsProc ← PinParsePins]];
END;
IF CD.FetchObjectProcs[$AbutX]#NIL THEN
-- We register ObjectProcs properties for AbutX
BEGIN
p: REF CD.ObjectProcs ← CD.FetchObjectProcs[$AbutX];
CDObjectProcs.StoreFurther[p, $ParseStretchProc, NEW [PWStretch.ParseStretchProc ← AbutXParseStretch]];
CDObjectProcs.StoreFurther[p, $DoStretchProc, NEW [PWStretch.DoStretchProc ← AbutsDoStretch]];
CDObjectProcs.StoreFurther[p, $ParsePinsProc, NEW [ParsePinsProc ← AbutXParsePins]];
END;
-- We register ObjectProcs properties for AbutY
IF CD.FetchObjectProcs[$AbutY]#NIL THEN
BEGIN
p: REF CD.ObjectProcs ← CD.FetchObjectProcs[$AbutY];
CDObjectProcs.StoreFurther[p, $ParseStretchProc, NEW [PWStretch.ParseStretchProc ← AbutYParseStretch]];
CDObjectProcs.StoreFurther[p, $DoStretchProc, NEW [PWStretch.DoStretchProc ← AbutsDoStretch]];
CDObjectProcs.StoreFurther[p, $ParsePinsProc, NEW [ParsePinsProc ← AbutYParsePins]];
END;
We must flush the cache each time a cell is changing
CDEvents.RegisterEventProc[$AfterCellReplacement, FlushWhenEvent];
END.