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:
BOOL ←
TRUE] =
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: INT ← LAST [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:
BOOL ←
FALSE] = {
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.