CDFrameImpl.mesa
Copyright c 1985 by Xerox Corporation. All rights reserved.
Last Edited by Curry, January 27, 1986 8:28:59 am PST
DIRECTORY
Basics,
CD,
CDBasics,
CDCells,
CDCommandOps,
CDCreateLabels,
CDDirectory,
CDFrame,
CDGenerate,
CDIO,
CDMenus,
CDOps,
CDOrient,
CDPinObjects,
CDRects,
CDSequencer,
CDSil,
CDTexts,
CMosB,
Commander,
CommandTool,
FS,
Imager,
ImagerFont,
IO,
NodeProps,
PutGet,
PW,
PWPins,
PWRoute,
Real,
RedBlackTree,
Rope,
CDSimpleOps,
TerminalIO,
TextNode,
VFonts,
ViewerIO;
CDFrameImpl: CEDAR PROGRAM
IMPORTS Basics, CD, CDBasics, CDCells, CDCommandOps, CDCreateLabels, CDDirectory, CDFrame, CDIO, CDMenus, CDOps, CDOrient, CDPinObjects, CDRects, CDSequencer, CDSil, CDSimpleOps, CMosB, Commander, CommandTool, FS, ImagerFont, IO, NodeProps, PutGet, PW, PWPins, Real, RedBlackTree, Rope, TerminalIO, VFonts, ViewerIO
EXPORTS CDFrame =
EXPORTS CDFrame SHARES CDSilTextCommands =
BEGIN OPEN CDFrame;
NewFrame: PUBLIC PROC
[size: INT, xory: XorY, name: IO.ROPE, data: REFNIL, unord: BOOLFALSE]
RETURNS [new: Frame] = {
new ← NEW[FrameSeq[size]];
new.xory   ← xory;
new.data   ← data;
new.unordered ← unord;
new.shell   ← NEW[ShellRec ← [name: name]] };
NewExpandFrame: PUBLIC PROC[name: IO.ROPE, proc: ExpandProc]
RETURNS [frame: Frame] = {
frame   ← NEW[FrameSeq[0]];
frame.shell ← NEW[ShellRec ← [name: name]];
IF proc # NIL THEN frame.data ← NEW[ExpandProc ← proc]};
NewObjectFrame: PUBLIC PROC[cell: CD.Object] RETURNS[frame: Frame] = {
frame ← NEW[FrameSeq[0]];
frame.data ← cell;
frame.shell ← ShellFromObject[cell]};
NewShellFileFrame: PUBLIC PROC[name: IO.ROPE] RETURNS [frame: Frame] = {
frame ← NEW[FrameSeq[0]];
frame.shell ← NEW[ShellRec ← [name: name]];
frame.data ← ShellFromFile[name]};
NextIDIndex: INT ← 0;
ID: PUBLIC PROC[name: IO.ROPE] RETURNS[unique: IO.ROPE] = {
unique ← IO.PutFR["%g%g", IO.rope[name], IO.int[NextIDIndex]];
NextIDIndex ← NextIDIndex+1};
Glue: PUBLIC PROC[
name:      IO.ROPE   ← NIL,
l,r,b,t:      GlueSideType ← cap,  -- cap conn ext chan sum diff
bObj,rObj,tObj,lObj:  CD.Object   ← NIL,
tDir:      PWRoute.HorV ← horizontal, -- vertical horizontal
tLayer:     IO.ROPE   ← NIL,  -- metal metal2 poly
bLayer:     IO.ROPE   ← NIL,
xlate:      BOOL    ← FALSE ] -- metal2 poly metal
RETURNS [new: Frame] = {
glue: REF GlueSpec;
IF tLayer=NIL THEN tLayer ← IF tDir=horizontal THEN "metal" ELSE "metal2";
IF bLayer=NIL THEN bLayer←IF Rope.Equal[tLayer, "metal"] THEN "metal2" ELSE "metal";
glue ← NEW[GlueSpec ← [
tDir:  tDir,
type:  [b,r,t,l],
obj:  [bObj,rObj,tObj,lObj],
params: NEW[PWRoute.RouterParamsRec ← [trunkLayer: tLayer, branchLayer:bLayer]]]];
SetGlueClass[glue];
IF xlate THEN IF glue.class#ext THEN Signal[] ELSE glue.class ← xlt;
IF name=NIL OR name.Length[]=0 THEN name ← SELECT glue.class FROM
fill  => ID["Fill-"],
ext  => ID["Ext-"],
xlt   => ID["Xlt-"],
chan  => ID["Chan-"],
pwr  => ID["Pwr-"],
sb   => ID["SBox-"],
ENDCASE => ID["Bad-"];
new ← NewFrame[0, x, name, glue];
new.shell.size ← IF glue.class=chan OR glue.class=pwr THEN WildSize[] ELSE TameSize[]};
font1: Imager.Font ← VFonts.EstablishFont
[family:"Helvetica", size: 7, bold: TRUE, italic: FALSE, defaultOnFailure: FALSE];
font2: Imager.Font ← VFonts.EstablishFont
[family:"Helvetica", size: 14, bold: TRUE, italic: FALSE, defaultOnFailure: FALSE];
SetGlueClass: PUBLIC PROC[glue: REF GlueSpec] = {
OPEN glue;
Type: PROC[side: Side] RETURNS[GlueSideType] = {
SELECT type[side] FROM
pwr  => RETURN[conn];
sum, diff => RETURN[chan];
ENDCASE => RETURN[type[side]]};
eqS: BOOL ← (Type[left] = Type[right]) OR (Type[top] = Type[bottom]);
cnt: ARRAY GlueSideType OF INTALL[0];
FOR side: Side IN Side DO
cnt[type[side]] ← cnt[type[side]] +1;
IF type[side]=ext AND type[OppSide[side]]#conn
THEN {class ← bad; Signal[]; RETURN} ENDLOOP;
IF cnt[pwr]>1 THEN {class ← bad; Signal[]; RETURN};
SELECT
(((cnt[conn]+cnt[pwr]) *10 +
cnt[ext]) *10 +
cnt[cap]) *10 +
cnt[chan] + cnt[sum] + cnt[diff] FROM
4000 => {class ← sb};
3100 => {class ← sb};
3010 => {class ← sb};
2200 => {class ← IF eqS THEN bad ELSE sb};
2110 => {class ← IF eqS THEN bad ELSE sb};
2020 => {class ← IF ~eqS THEN sb ELSE chan};
2011 => {class ← IF ~eqS THEN bad ELSE chan};
2002 => {class ← IF ~eqS THEN bad ELSE chan};
1120 => {class ← IF ~eqS THEN bad ELSE  ext};
1111 => {class ←         chan};
1102 => {class ← IF ~eqS THEN bad ELSE  chan};
1021 => {class ← IF eqS THEN bad ELSE chan};
1012 => {class ← IF ~eqS THEN bad ELSE chan};
0040 => {class ←        fill};
0022 => {class ← IF ~eqS THEN bad ELSE chan};
ENDCASE => {class ←       bad};
IF class=chan THEN IF cnt[chan]+cnt[sum]+cnt[diff]=0
THEN {IF (tDir=vertical) = (type[left] = cap AND type[right] = cap) THEN Signal[]}
ELSE {IF (tDir=vertical) = (Type[left] = chan OR Type[right] = chan) THEN Signal[]};
IF class=chan AND cnt[pwr]#0 THEN class←pwr;
IF class=bad THEN Signal[]};
ScaledText: PUBLIC PROC[design: CD.Design, text: IO.ROPE, box: CD.Position, layer: CD.Layer]
RETURNS[cell: CD.Object] = {
Scale: PROC[font: Imager.Font] RETURNS[scale:  INT] = {
extents: ImagerFont.Extents ← ImagerFont.RopeBoundingBox[font, text];
strSize: CD.Position;
IF box.x<4 OR box.y<4 THEN RETURN[0];
strSize.x ← Real.RoundI[extents.rightExtent+extents.leftExtent];
strSize.y ← Real.RoundI[extents.ascent+extents.descent];
scale  ← MIN[box.x/strSize.x, box.y/strSize.y]};
scaleFactor: INT ← Scale[font2];
IF scaleFactor>0
THEN RETURN[CDCreateLabels.CreateTextCell[design, text, font2, scaleFactor, layer]];
scaleFactor ← Scale[font1];
IF scaleFactor>0
THEN RETURN[CDCreateLabels.CreateTextCell[design, text, font1, scaleFactor, layer]];
RETURN[NIL] };
Log2: PROC [n: INT] RETURNS [INT] = {RETURN [IF n<=1 THEN 0 ELSE 1 + Log2[n / 2]]};
Exp2: PROC [n: INT] RETURNS [INT] = {RETURN [IF n<=0 THEN 1 ELSE 2 * Exp2[n-1]]};
BlankCell: PUBLIC PROC[design: CD.Design, size: CD.Position] RETURNS[cell: CD.Object] ~ {
cellPtr: CD.CellPtr;
found: BOOL;
name: ROPEIO.PutFR["Dummy-%g-%g", IO.int[size.x], IO.int[size.y]];
IF size.x=0 OR size.y=0 THEN RETURN[NIL];
[found, cell] ← CDDirectory.Fetch[design, name];
IF found THEN RETURN[cell];
cell  ← CDCells.CreateEmptyCell[];
cellPtr ← NARROW[cell.specificRef];
cellPtr.contents ← CONS[NEW[CD.InstanceRep ←
[ob: CDRects.CreateRect[size, CD.backGround]] ], cellPtr.contents];
PW.IncludeInDirectory[design, cell, name]};
ShellToBlock: PUBLIC PROC[design: CD.Design, shell: REF ShellRec]
RETURNS[block: CD.Object] = {
size:   CD.Position ← [shell.size.x, shell.size.y];
hor, ver:  CD.Object;
box:   CD.Position;
text:   CD.Object;
layer:   CD.Layer;
lineScale:  INT = 4 ;
lwidth:  INT ← 1;
rotate:   BOOLFALSE;
IF size.x=0 OR size.y=0 THEN RETURN[NIL];
IF size.x IN (0..4*lineScale) OR size.y IN (0..4*lineScale)
THEN RETURN[BlankCell[design, size]];
lwidth ← lineScale * MAX[lwidth, MIN[size.x/100, size.y/100]];
box  ← [(size.x-4*lwidth), (size.y-4*lwidth)];
IF box.y/box.x > 1 THEN {box ← CD.Position[box.y, box.x]; rotate ← TRUE};
layer  ← SELECT design.technology FROM
CMosB.cmosB => CMosB.met,
CMos.cmos  => CMos.met,
CDSil.cdsil  => CDSil.xneutral,
ENDCASE  => ERROR;
hor  ← CDRects.CreateRect[[size.x, lwidth], layer];
ver  ← CDRects.CreateRect[[lwidth, size.y], layer];
block  ← CDCells.CreateEmptyCell[];
text  ← ScaledText[design, shell.name, box, layer];
IF text#NIL THEN {
IF rotate THEN text ← PW.Rot90[design, text];
[ ] ← PW.IncludeInCell[block, text, [(size.x-text.size.x)/2, (size.y-text.size.y)/2]]};
[ ] ← PW.IncludeInCell[block, hor, [0,        0]];
[ ] ← PW.IncludeInCell[block, hor, [0,        size.y-lwidth]];
[ ] ← PW.IncludeInCell[block, ver, [0,        0]];
[ ] ← PW.IncludeInCell[block, ver, [size.x-lwidth,   0]];
PW.IncludeInDirectory[design, block, shell.name]};
ShellToObject: PUBLIC PROC[design: CD.Design, shell: REF ShellRec, detailed: BOOLTRUE]
RETURNS[cell: CD.Object] = {
IF design.technology= CDSil.cdsil OR NOT detailed THEN
RETURN[ShellToBlock[design, shell]];
FOR side: Side IN Side DO
IF shell.pins[side]#NIL THEN EXIT
REPEAT FINISHED => RETURN[ShellToBlock[design, shell]] ENDLOOP;
cell ← CDCells.CreateEmptyCell[];
FOR side: Side IN Side DO
pins: REF PinSeq ← shell.pins[side];
IF pins#NIL THEN FOR i: INT IN [0..pins.seqSize) DO
pos:  CD.Position ← [pins[i].pos.x-shell.pos.x, pins[i].pos.y-shell.pos.y];
pinApl: CD.Instance ← PW.IncludeInCell
[cell, CDPinObjects.CreatePinOb[pins[i].size], pos];
CDPinObjects.SetName[pinApl, pins[i].name];
CDPinObjects.SetLayer[pinApl, pins[i].layer] ENDLOOP;
ENDLOOP;
CDCells.SetInterestRect[cell, [0, 0, shell.size.x, shell.size.y]];
PW.IncludeInDirectory[design, cell, shell.name];
IF CD.InterestSize[cell]#[shell.size.x, shell.size.y] THEN Signal[]};
IncludePinNamesInBlock: PROC
[design: CD.Design, shell: REF ShellRec, block: CD.Object, layer: CD.Layer] = {
textOb:  CD.Object;
FOR side: Side IN Side DO
pins:  REF PinSeq ← shell.pins[side];
bias:  INT = 8;
orient: CD.Orientation ← SELECT side FROM
left, right => CDOrient.original,
top, bottom => CDOrient.rotate90,
ENDCASE => ERROR;
IF pins#NIL THEN FOR i: INT IN [0..pins.seqSize) DO
pos:  CD.Position;
textOb   ← ScaledText[design, pins[i].name, [400, 14], layer];
textOb: CD.Object ← CDTexts.CreateText[pins[i].name, font, layer];
pos.x ← SELECT side FROM
left  => pins[i].pos.x           + bias,
right  => pins[i].pos.x + pins[i].size.x - textOb.size.x - bias,
bottom => pins[i].pos.x + pins[i].size.x,
top  => pins[i].pos.x + pins[i].size.x,
ENDCASE => ERROR;
pos.y ← SELECT side FROM
left  => pins[i].pos.y,
right  => pins[i].pos.y,
bottom => pins[i].pos.y           + bias,
top  => pins[i].pos.y + pins[i].size.y - textOb.size.x - bias,
ENDCASE => ERROR;
[ ] ← PW.IncludeInCell[block, textOb, pos, orient];
ENDLOOP
ENDLOOP};
CellInDesign: PUBLIC PROC[cell: CD.Object, design: CD.Design] RETURNS[BOOL]=
{RETURN[cell#NIL AND cell = CDDirectory.Fetch[design, CDDirectory.Name[cell]].object ]};
ExpandFrame: PUBLIC ExpandProc = {
new ← frame;
IF new=NIL THEN {Signal[]; RETURN[NIL]};
IF new.seqSize > 0
THEN {
FOR index: INT IN [0..new.seqSize) DO
new[index] ← ExpandFrame[new[index], design] ENDLOOP}
ELSE {
IF new.data=NIL THEN {Signal[]; RETURN[new]};
WITH new.data SELECT FROM
cell: CD.Object => new.shell ← ShellFromObject[NARROW[new.data]];
shell: REF ShellRec => {
frame.shell ← shell;
frame.data ← ShellToObject[design, shell, TRUE]};
cellName: IO.ROPE => {
cell: CD.Object;
found: BOOL;
[found, cell] ← CDDirectory.Fetch[design, cellName];
IF found
THEN {new.data ← cell; new.shell ← ShellFromObject[cell]}
ELSE {Signal[]; new.shell ← ShellFromFile[cellName]} };
proc: REF PROC [design: CD.Design] RETURNS [cell: CD.Object] => {
new.data ← proc^[design];
new.shell ← ShellFromObject[NARROW[new.data]]};
proc: REF PW.UserProc => {
new.data ← proc^[design, NIL, NIL, NIL];
new.shell ← ShellFromObject[NARROW[new.data]]};
proc: REF ExpandProc => {
orient: CD.Orientation ← new.orient;
new ← proc^[new, design];
new ← ExpandFrame[new, design];
new.orient ← orient;
RETURN[new]};
glue: REF GlueSpec => {
log.PutRope["\n Glue Spec expansion delayed: "];
log.PutRope[new.shell.name]; RETURN[new]};
ENDCASE => {
log.PutRope["\n Unknown expansion delayed: "];
log.PutRope[new.shell.name]; RETURN[new]};
log.PutRope["\n Expanded frame: "];
log.PutRope[new.shell.name];
IF new.shell.size.x <1 OR new.shell.size.y <1 THEN Signal[]};
RETURN[new]};
DeleteZeroFrames: PUBLIC PROC[frame: Frame] RETURNS[new: Frame] = {
IF frame.shell.size.x=0 OR frame.shell.size.y=0 THEN {
log.PutRope["\n Deleteing zero sized frame:%"];
log.PutRope[frame.shell.name];
RETURN[NIL]};
IF frame.seqSize = 0
THEN RETURN[frame]
ELSE {
count: INT ← 0;
FOR index: INT IN [0..frame.seqSize) DO
frame[index] ← DeleteZeroFrames[frame[index]];
IF frame[index]#NIL THEN count ← count+1; ENDLOOP;
IF count = frame.seqSize THEN RETURN[frame];
new    ← NEW[FrameSeq[count]];
new.shell   ← frame.shell;
new.data   ← frame.data;
new.xory   ← frame.xory;
new.unordered ← frame.unordered;
FOR index: INT DECREASING IN [0..frame.seqSize) DO
IF frame[index]=NIL THEN LOOP;
count ← count-1;
new[count] ← frame[index] ENDLOOP;
RETURN[new]}};
ReadShellFrame: PUBLIC SpecificFrameProc = { -- new done ← frame name design
IF name=NIL THEN name ← frame.shell.name;
IF frame.shell#NIL AND Rope.Equal[frame.shell.name, name] THEN
{RETURN[NewShellFileFrame[name], TRUE]};
done ← FALSE;
new ← frame;
FOR index: INT IN [0..new.seqSize) WHILE NOT done DO
[new[index], done] ← ReadShellFrame[new[index], name, design] ENDLOOP};
WriteShellFrame: PUBLIC SpecificFrameProc = { -- new done ← frame name design
IF name=NIL THEN name ← frame.shell.name;
IF frame.shell#NIL AND Rope.Equal[frame.shell.name, name] THEN
{ShellToFile[frame.shell]; RETURN[frame, TRUE]};
done ← FALSE;
new ← frame;
FOR index: INT IN [0..new.seqSize) WHILE NOT done DO
[new[index], done] ← WriteShellFrame[new[index], name, design] ENDLOOP};
FrameToObject: PUBLIC PROC[frame: Frame, design: CD.Design, detailed: BOOLTRUE]
RETURNS[cell: CD.Object] = {
IF design.technology=CMosB.cmosB AND frame.data#NIL AND ISTYPE[frame.data, CD.Object]
THEN RETURN[NARROW[frame.data]];
IF frame.seqSize=0
THEN RETURN[ShellToObject[design, frame.shell, detailed]]
ELSE {
list: PW.ListOb;
FOR i: INT DECREASING IN [0..frame.seqSize) DO
list ← CONS[FrameToObject[ frame.seq[i],design], list] ENDLOOP;
IF frame.xory=x
THEN cell ← PW.AbutListX[design, list]
ELSE cell ← PW.AbutListY[design, list];
PW.RenameObject[design, cell, frame.shell.name]} };
ReOrient: PUBLIC PROC
[frame: Frame, design: CD.Design, orient: CD.Orientation ← CD.original] = {
ReSeq: PROC = {
TwoFrames: TYPE = RECORD[f0, f1: Frame];
FOR i: INT IN [0..frame.seqSize/2) DO
[frame[frame.seqSize-i-1], frame[i]] ← TwoFrames[frame[i], frame[frame.seqSize-i-1]];
ENDLOOP};
yorx: XorY ← IF frame.xory = x THEN y ELSE x;
IF frame.orient#CDOrient.original THEN ERROR;
orient ← CDOrient.ComposeOrient[frame.orient, orient];
frame.orient ← CDOrient.original;
IF frame.seqSize = 0 THEN {
IF orient=CDOrient.original THEN RETURN;
IF frame.data= NIL THEN ERROR;
WITH frame.data SELECT FROM
glue: REF GlueSpec => {
OPEN glue;
type ← SELECT orient FROM
CDOrient.original => [type[bottom], type[right],  type[top],  type[left]],
CDOrient.mirrorX => [type[bottom], type[left],  type[top],  type[right]],
CDOrient.rotate90 => [type[left],  type[bottom], type[right],  type[top]],
CDOrient.rotate90X => [type[left],  type[top],  type[right],  type[bottom]],
CDOrient.rotate180 => [type[top],  type[left],  type[bottom], type[right]],
CDOrient.rotate180X => [type[top],  type[right],  type[bottom], type[left]],
CDOrient.rotate270 => [type[right],  type[top],  type[left],  type[bottom]],
CDOrient.rotate270X => [type[right],  type[bottom], type[left],  type[top]],
ENDCASE => ERROR;
obj ← SELECT orient FROM
CDOrient.original => [obj[bottom], obj[right], obj[top],  obj[left]],
CDOrient.mirrorX => [obj[bottom], obj[left],  obj[top],  obj[right]],
CDOrient.rotate90 => [obj[left],  obj[bottom], obj[right], obj[top]],
CDOrient.rotate90X => [obj[left],  obj[top],  obj[right], obj[bottom]],
CDOrient.rotate180 => [obj[top],  obj[left],  obj[bottom], obj[right]],
CDOrient.rotate180X => [obj[top],  obj[right], obj[bottom], obj[left]],
CDOrient.rotate270 => [obj[right], obj[top],  obj[left],  obj[bottom]],
CDOrient.rotate270X => [obj[right], obj[bottom], obj[left],  obj[top]],
ENDCASE => ERROR;
FOR side: Side IN Side DO
IF obj[side]#NIL AND type[side]=chan
THEN obj[side] ← PW.ChangeOrientation[design, obj[side], orient]
ELSE obj[side] ← NIL ENDLOOP;
SELECT orient FROM
CDOrient.rotate90,
CDOrient.rotate90X,
CDOrient.rotate270,
CDOrient.rotate270X => {
temp: IO.ROPE  ← params.branchLayer;
params.branchLayer ← params.trunkLayer;
params.trunkLayer ← temp;
frame.shell.size  ← FlipSize[frame.shell.size];
tDir ← IF tDir=horizontal THEN vertical ELSE horizontal};
ENDCASE };
cell: CD.Object => {
cell ← PW.ChangeOrientation[design, cell, orient];
frame.shell.size ← FixedSize[CD.InterestSize[cell]];
frame.data ← cell};
ENDCASE => ERROR;
RETURN};
SELECT orient FROM
CDOrient.original => {                };
CDOrient.mirrorX => {      IF frame.xory=x THEN ReSeq[] };
CDOrient.rotate90 => {frame.xory ← yorx; IF frame.xory=x THEN ReSeq[] };
CDOrient.rotate90X => {frame.xory ← yorx;           };
CDOrient.rotate180 => {      IF TRUE   THEN ReSeq[] };
CDOrient.rotate180X => {      IF frame.xory=y THEN ReSeq[] };
CDOrient.rotate270 => {frame.xory ← yorx; IF frame.xory=y THEN ReSeq[] };
CDOrient.rotate270X => {frame.xory ← yorx; IF TRUE   THEN ReSeq[] };
ENDCASE;
FOR i: INT IN [0..frame.seqSize) DO ReOrient[frame[i], design, orient] ENDLOOP};
ObjSize: PUBLIC PROC[ob: CD.Object] RETURNS[size: CD.Position] =
{RETURN[CD.InterestSize[ob]]};
GetSize: PUBLIC PROC[frame: Frame] = {
variable: BOOLFALSE;
dad, son: Size;
IF frame.seqSize = 0 THEN {
IF frame.data=NIL THEN {Signal[]; RETURN};
WITH frame.data SELECT FROM
cell: CD.Object   => {frame.shell.size ← FixedSize[CD.InterestSize[cell]]};
glue: REF GlueSpec => {
frame.shell.size ← TameSize[];
IF glue.class=chan OR glue.class=pwr THEN IF glue.tDir=vertical
THEN frame.shell.size.xfree  ← wild
ELSE frame.shell.size.yfree ← wild};
ENDCASE     => ERROR;
IF frame.shell.size.x < 1 OR frame.shell.size.y < 1 THEN {Signal[]; frame.data ← NIL};
RETURN};
dad ← [0, 0, fixed, tame];
FOR i: INT IN [0..frame.seqSize) DO -- Normalize sons to x sequences for computations
GetSize[frame.seq[i]];
son ← IF frame.xory=y THEN FlipSize[frame.seq[i].shell.size] ELSE frame.seq[i].shell.size;
CheckXSeqForExpansionError[dad, son, frame.seq[i]];
dad.xfree ← MAX[dad.xfree, son.xfree]; -- fixed, tame, wild
dad.x  ← dad.x + son.x;
dad.yfree ← IF MIN[dad.yfree,son.yfree]=fixed THEN fixed ELSE MAX[dad.yfree,son.yfree];
dad.y  ← MAX[dad.y, son.y];
ENDLOOP;
FOR i: INT IN [0..frame.seqSize) DO
son ← IF frame.xory=y THEN FlipSize[frame.seq[i].shell.size] ELSE frame.seq[i].shell.size;
IF dad.yfree=fixed AND son.yfree=wild THEN {
log.PutF["\n WARNING: Limited wild %g width%g\n",
IO.rope[ IF frame.xory=x THEN "Y" ELSE "X" ],
IO.rope[ ListFrame[frame, 2] ] ]; EXIT};
ENDLOOP;
frame.shell.size ← IF frame.xory=y THEN FlipSize[dad] ELSE dad};
CheckXSeqForExpansionError: PROC[size1, size2: Size, frame: Frame] = {
IF (size1.y > size2.y AND size2.yfree=fixed) OR
(size2.y > size1.y AND size1.yfree=fixed) THEN {
log.PutF["\n ERROR: Size of %g not expandable", IO.rope[frame.shell.name]];
log.PutRope[ListFrame[frame.father, 2]];
log.Flush[];
Signal[]} };
PutSize: PUBLIC PROC[frame: Frame] = {
dad, son: Size;
freedom: Freedom;
oneVariable: BOOL;
cnt: ARRAY Freedom OF INTALL[0];
length, diff: INT ← 0;
IF frame.seqSize = 0 THEN RETURN;
dad ← IF frame.xory=y THEN FlipSize[frame.shell.size] ELSE frame.shell.size; -- norm
FOR ii: INT IN [0..frame.seqSize) DO
son ← frame.seq[ii].shell.size;
son ← IF frame.xory=y THEN FlipSize[son] ELSE son;       -- norm
CheckXSeqForExpansionError[dad, son, frame.seq[ii]];
length ← length + son.x;
cnt[son.xfree] ← cnt[son.xfree] + 1;
SELECT dad.yfree FROM
wild  => IF son.yfree=fixed THEN Signal[]; -- how can this happen?
tame  => IF son.yfree#tame THEN Signal[]; -- how can this happen?
ENDCASE;
IF (dad.yfree >= son.yfree) AND (dad.y=son.y) THEN LOOP;
log.PutF["\n %g width changed from %6g %6g",
IO.rope [IF frame.xory=x THEN "Y" ELSE "X"],
IO.int[son.y], IO.rope[FreeRope[son.yfree]] ];
son.yfree ← MIN[dad.yfree, son.yfree];
son.y  ← MAX[dad.y, son.y];
log.PutF[" to %6g %6g in: %g",
IO.int[son.y], IO.rope[FreeRope[son.yfree]], IO.rope [frame[ii].shell.name] ];
frame.seq[ii].shell.size ← IF frame.xory=y THEN FlipSize[son] ELSE son; -- denorm
ENDLOOP;
diff ← dad.x - length;
FOR freedom DECREASING IN Freedom DO IF cnt[freedom]>0 THEN EXIT ENDLOOP;
oneVariable ← (cnt[tame]=1 AND cnt[wild]=0) OR (cnt[tame]=0 AND cnt[wild]=1);
IF diff<0 THEN Signal[];
IF diff>0 OR diff=0 AND dad.xfree = fixed AND oneVariable THEN {
SELECT dad.xfree FROM
wild, tame => IF freedom#dad.xfree THEN Signal[];
ENDCASE;
FOR ii: INT IN [0..frame.seqSize) WHILE cnt[freedom] > 0 DO
incr: INT ← diff/cnt[freedom];
son ← frame.seq[ii].shell.size;
son ← IF frame.xory=y THEN FlipSize[son] ELSE son;      -- norm;
IF son.xfree#freedom THEN LOOP;
log.PutF["\n %g length changed from %6g %6g",
IO.rope[IF frame.xory=x THEN "X" ELSE "Y"],
IO.int[son.x], IO.rope[FreeRope[son.xfree]] ];
IF oneVariable THEN son.xfree ← dad.xfree;
son.x ← son.x + incr;
log.PutF[" to %6g %6g in: %g",
IO.int[son.x], IO.rope[FreeRope[son.xfree]], IO.rope [frame[ii].shell.name] ];
diff ← diff - incr;
cnt[freedom] ← cnt[freedom] - 1;
frame.seq[ii].shell.size ← IF frame.xory=y THEN FlipSize[son] ELSE son; -- denorm
ENDLOOP;
IF diff#0 THEN ERROR};
FOR i: INT IN [0..frame.seqSize) DO PutSize[frame.seq[i]] ENDLOOP };
Signal: SIGNAL = CODE;
PutPos: PUBLIC PROC[frame: Frame, new: CD.Position] = {
cng: CD.Position ← [new.x-frame.shell.pos.x, new.y-frame.shell.pos.y];
pos: CD.Position ← frame.shell.pos ← new;
IF frame.seqSize=0 THEN {
FOR side: Side IN Side DO
IF frame.shell.pins[side]=NIL
THEN LOOP
ELSE FOR pin: INT IN [0..frame.shell.pins[side].seqSize) DO
frame.shell.pins[side][pin].pos.x ← frame.shell.pins[side][pin].pos.x + cng.x;
frame.shell.pins[side][pin].pos.y ← frame.shell.pins[side][pin].pos.y + cng.y;
ENDLOOP
ENDLOOP; RETURN};
FOR i: INT IN [0..frame.seqSize) DO
PutPos[frame.seq[i], pos];
SELECT frame.xory FROM
x => pos.x ← pos.x + frame.seq[i].shell.size.x;
y => pos.y ← pos.y + frame.seq[i].shell.size.y;
ENDCASE ENDLOOP };
GetPins: PUBLIC PROC[frame: Frame] = {
Merge: PROC[pins1, pins2: REF PinSeq] RETURNS[pins: REF PinSeq] = {
pins ← NEW[PinSeq[pins1.seqSize+(IF pins2=NIL THEN 0 ELSE pins2.seqSize)]];
pins.locInfo ← MIN[pins1.locInfo, (IF pins2=NIL THEN unk ELSE pins2.locInfo)];
FOR i:INT IN [0..pins1.seqSize) DO pins[i     ] ← pins1[i] ENDLOOP;
IF pins2=NIL THEN RETURN[pins];
FOR i:INT IN [0..pins2.seqSize) DO pins[i+pins1.seqSize ] ← pins2[i] ENDLOOP };
pins: ARRAY Side OF REF PinSeq;
IF frame.seqSize = 0 THEN RETURN;
IF frame.seqSize = 0 THEN {
FOR side: Side IN Side DO
bias: INT ← SELECT side FROM
top, bottom => frame.shell.pos.x,
ENDCASE  => frame.shell.pos.y;
IF frame.shell.pins[side]=NIL
THEN LOOP
ELSE IF frame.shell.pins[side].locInfo=fixed THEN
FOR pin: INT IN [0..frame.shell.pins[side].seqSize) DO
pos: INT ← GetSidePos[frame.shell.pins[side][pin]];
IF pos = unk THEN ERROR;
SetSidePos[frame.shell.pins[side][pin], pos + bias];
ENDLOOP
ENDLOOP;
RETURN};
FOR side: Side IN Side DO
pins[side] ← NEW[PinSeq[0]]; pins[side].locInfo ← fixed ENDLOOP;
FOR i: INT IN [0..frame.seqSize) DO
GetPins[frame.seq[i]];
SELECT frame.xory FROM
x => {
pins[top]  ← Merge[pins[top],  frame.seq[i].shell.pins[top]];
pins[bottom] ← Merge[pins[bottom], frame.seq[i].shell.pins[bottom]]};
y => {
pins[right] ← Merge[pins[right], frame.seq[i].shell.pins[right]];
pins[left]  ← Merge[pins[left],  frame.seq[i].shell.pins[left]]};
ENDCASE;
ENDLOOP;
SELECT frame.xory FROM
x => {
pins[right] ← Merge[pins[right], frame.seq[frame.seqSize-1 ].shell.pins[right]];
pins[left]  ← Merge[pins[left],  frame.seq[0    ].shell.pins[left]]};
y => {
pins[top]  ← Merge[pins[top],  frame.seq[frame.seqSize-1 ].shell.pins[top]];
pins[bottom] ← Merge[pins[bottom], frame.seq[0    ].shell.pins[bottom]]};
ENDCASE;
frame.shell.pins ← pins};
FixOneSize: PUBLIC PROC [frame: Frame] RETURNS[fixed1: BOOLFALSE] = {
FixOneSizeRecursive: PUBLIC PROC [fframe: Frame] RETURNS[ffixed1: BOOLFALSE] = {
lastTame:      INT ← -1;
wildOrMultTamesFound: BOOLFALSE;
FOR kid: INT IN [0..fframe.seqSize) DO
SELECT (IF fframe.xory=x
THEN fframe[kid].shell.size.xfree
ELSE fframe[kid].shell.size.yfree) FROM
fixed  => LOOP;
wild  => wildOrMultTamesFound ← TRUE;
ENDCASE => {IF lastTame#-1 THEN wildOrMultTamesFound ← TRUE; lastTame←kid};
ENDLOOP;
IF lastTame#-1 AND wildOrMultTamesFound THEN {
IF fframe.xory=x
THEN {fframe[lastTame].shell.size.xfree ← fixed; log.PutRope["\n X length"]}
ELSE {fframe[lastTame].shell.size.yfree ← fixed; log.PutRope["\n Y length"]};
log.PutF[" FIXED in %g", IO.rope[fframe[lastTame].shell.name]];
PutSize[fframe]; RETURN[TRUE]};
FOR kid: INT IN [0..fframe.seqSize) WHILE NOT ffixed1
DO ffixed1 ← FixOneSizeRecursive[fframe[kid]] ENDLOOP};
IF FixOneSizeRecursive[frame] THEN RETURN[TRUE];
IF frame.shell.size.xfree=tame
THEN {frame.shell.size.xfree𡤏ixed; PutSize[frame]; RETURN[TRUE]};
IF frame.shell.size.yfree=tame
THEN {frame.shell.size.yfree𡤏ixed; PutSize[frame]; RETURN[TRUE]};
RETURN[FALSE]};
TellKidsAboutDad: PUBLIC PROC [dad: Frame] = {
FOR kid: INT IN [0..dad.seqSize)
DO dad[kid].father ← dad; TellKidsAboutDad[dad[kid]] ENDLOOP};
Neighbor: PUBLIC PROC [leaf: Frame, side: Side]
RETURNS[neighbor: Frame, ok: BOOL] = {
NeighborCheck: PROC[frame: Frame] RETURNS[type: {eq, ok, out}] = {
negh: CD.Rect ← CDBasics.RectAt[frame.shell.pos, [frame.shell.size.x,frame.shell.size.y]];
SELECT neighborSide FROM
right  => {
IF seg.x2=negh.x2 AND seg.y1=negh.y1 AND seg.y2=negh.y2 THEN RETURN[eq];
IF seg.x2 NOT IN (negh.x1 .. negh.x2] THEN RETURN[out]};
left  => {
IF seg.x1=negh.x1 AND seg.y1=negh.y1 AND seg.y2=negh.y2 THEN RETURN[eq];
IF seg.x1 NOT IN [negh.x1 .. negh.x2) THEN RETURN[out]};
top  => {
IF seg.y2=negh.y2 AND seg.x1=negh.x1 AND seg.x2=negh.x2 THEN RETURN[eq];
IF seg.y2 NOT IN (negh.y1 .. negh.y2] THEN RETURN[out]};
bottom  => {
IF seg.y1=negh.y1 AND seg.x1=negh.x1 AND seg.x2=negh.x2 THEN RETURN[eq];
IF seg.y1 NOT IN [negh.y1 .. negh.y2) THEN RETURN[out]};
ENDCASE;
SELECT neighborSide FROM
right, left => {
IF seg.y1 NOT IN [negh.y1 .. negh.y2) THEN RETURN[out];
IF seg.y2 NOT IN (negh.y1 .. negh.y2] THEN RETURN[out];
RETURN[ok]};
top, bottom => {
IF seg.x1 NOT IN [negh.x1 .. negh.x2) THEN RETURN[out];
IF seg.x2 NOT IN (negh.x1 .. negh.x2] THEN RETURN[out];
RETURN[ok]};
ENDCASE };
neighborSide: Side  ← OppSide[side];
seg:   CD.Rect ← CDBasics.RectAt[leaf.shell.pos, [leaf.shell.size.x,leaf.shell.size.y]];
SELECT side FROM
top  => seg.y1 ← seg.y2;
bottom => seg.y2 ← seg.y1;
right  => seg.x1 ← seg.x2;
left  => seg.x2 ← seg.x1;
ENDCASE;
FOR neighbor ← leaf.father, neighbor.father WHILE neighbor#NIL DO
SELECT NeighborCheck[neighbor] FROM
ok   => EXIT;
out  => LOOP;
ENDCASE => ERROR REPEAT FINISHED => {
log.PutF["\n ERROR: %g side neighbor of %g is external to frame",
IO.rope[SideRope[side]], IO.rope[leaf.shell.name]];
RETURN[NIL, FALSE]} ENDLOOP;
DO
IF neighbor.seqSize=0 THEN RETURN[neighbor, FALSE];
FOR i: INT IN [0..neighbor.seqSize) DO
SELECT NeighborCheck[neighbor[i]] FROM
out  => LOOP;
eq   => {neighbor ← neighbor[i]; GOTO Found};
ENDCASE => {neighbor ← neighbor[i]; EXIT};
REPEAT Found=> EXIT; FINISHED => {
log.PutF["\n ERROR: %g side neighbor of %g can't be identified",
IO.rope[SideRope[side]], IO.rope[leaf.shell.name]];
RETURN[NIL, FALSE]} ENDLOOP;
ENDLOOP;
RETURN[neighbor, TRUE]};
FixImpliedPins: PROC[frame: REF FrameRec] = {
IF frame.seq # NIL
THEN FOR i: INT IN [0..frame.seq.seqSize-1) DO {
aSide: Side ← IF frame.seq.xory = x THEN right ELSE top;
bSide: Side ← IF frame.seq.xory = x THEN left ELSE bottom;
aPins: REF PinSeq ← frame.seq[i+0].shell.pins[aSide];
bPins: REF PinSeq ← frame.seq[i+1].shell.pins[bSide];
FOR aPin: INT IN [0..aPins.seqSize) DO
IF aPins[aPin].pos#unk THEN LOOP;
FOR bPin: INT IN [0..bPins.seqSize) DO
IF ~Rope.Equal[bPins[bPin].name, aPins[aPin].name] THEN LOOP;
IF bPins[bPin].pos=unk THEN ERROR;
aPins[aPin].pos ← bPins[bPin].pos;
IF aPins[aPin].size=unkSize THEN aPins[aPin].size ← bPins[bPin].size;
EXIT REPEAT FINISHED => ERROR ENDLOOP;
FOR bPin: INT IN [0..bPins.seqSize) DO
IF bPins[bPin].pos#unk THEN LOOP;
FOR aPin: INT IN [0..aPins.seqSize) DO
IF ~Rope.Equal[aPins[aPin].name, bPins[bPin].name] THEN LOOP;
IF aPins[aPin].pos=unk THEN ERROR;
bPins[bPin].pos ← aPins[aPin].pos;
IF bPins[bPin].size=unkSize THEN bPins[bPin].size ← aPins[aPin].size;
EXIT REPEAT FINISHED => ERROR ENDLOOP;
IF bPins.seqSize# aPins.seqSize THEN ERROR;
SORT both ?
Checkd that newly defined pins are still in range of cells
FOR pin: INT IN [0..aPins.seqSize) DO
IF ~Rope.Equal[aPins[pin].name, bPins[pin].name] THEN ERROR;
IF aPins[pin].pos#bPins[pin].pos THEN ERROR ENDLOOP };
FreeRope: ARRAY Freedom OF IO.ROPE ← ["fixed", "tame", "wild"];
ListFrame: PUBLIC PROC[frame: Frame, limit: INT ← -1] RETURNS[listing: IO.ROPE] = {
out: IO.STREAMIO.ROS[];
LogFrame[frame, limit, out];
listing ← IO.RopeFromROS[out]};
LogFrame: PUBLIC PROC[frame: Frame, limit: INT ← -1, out: IO.STREAMNIL] = {
Gauge: PROC[fframe: Frame, depth: INT] = {
requiredOffset ← MAX[requiredOffset, depth+fframe.shell.name.Length[]];
FOR i: INT IN [0..fframe.seqSize) DO Gauge[fframe.seq[i], depth+1] ENDLOOP};
ListF: PROC[fframe: Frame, depth: INT] = {
name: IO.ROPE ← name�rame.shell.name;
size: Size  ← fframe.shell.size;
pos: CD.Position ← fframe.shell.pos;
IF depth=limit THEN RETURN;
out.PutRope["\n"];
FOR i: INT IN [0..depth) DO out.PutRope[" "] ENDLOOP;
out.PutRope[name];
THROUGH [name.Length[]+depth..requiredOffset) DO out.PutRope[" "] ENDLOOP;
out.PutF[" %8g%8g", IO.int[size.x], IO.int[size.y] ];
out.PutF[" %8g%8g", IO.rope[FreeRope[size.xfree]], IO.rope[FreeRope[size.yfree]] ];
out.PutF[" %8g%8g", IO.int[pos.x], IO.int[pos.y] ];
IF fframe.seqSize#0 THEN
IF fframe.xory=x
THEN out.PutRope[" x seq"]
ELSE out.PutRope[" y seq"];
IF fframe.unordered THEN out.PutRope[" ~"];
FOR i: INT DECREASING IN [0..fframe.seqSize) DO ListF[fframe.seq[i], depth+1] ENDLOOP};
requiredOffset: INT ← 0;
IF out=NIL THEN out ← GetLog[];
Gauge[frame,0];
ListF[frame, 0]};
FullFrameName: PUBLIC PROC[frame: Frame] RETURNS[name: IO.ROPE] = {
FOR temp: Frame ← frame.father, temp.father WHILE temp#NIL DO
name ← Rope.Cat[temp.shell.name, ".", name] ENDLOOP;
name ← name.Cat[frame.shell.name]};
FlattenOnce: PUBLIC PROC[frame: Frame] RETURNS[new: Frame] =
{new ← TestMergeNodes[frame]};
TestMergeNodes: PUBLIC PROC[frame: Frame, testProc: TestMergeProc ← NIL]
RETURNS[new: Frame] ~ {
nlist: LIST OF REF;
elist: LIST OF Frame;
xory: XorY ← y;
count: CARDINAL;
FOR index: CARDINAL IN [0..frame.seqSize) DO
IF testProc=NIL OR testProc[frame[index]]
THEN {
IF xory#frame[index].xory THEN ERROR;
FOR element: CARDINAL IN [0..frame[index].seqSize)
DO elist ← CONS[frame[index][element], elist] ENDLOOP}
ELSE {
IF elist#NIL THEN nlist ← CONS[elist, nlist];
elist ← NIL;
nlist ← CONS[frame[index], nlist]};
REPEAT FINISHED => IF elist#NIL THEN nlist ← CONS[elist, nlist] ENDLOOP;
count ← 0;
FOR tlist: LIST OF REF ← nlist, tlist.rest WHILE tlist#NIL DO count ← count+1 ENDLOOP;
new ← NewFrame[count, frame.xory, frame.shell.name, frame.data, frame.unordered];
FOR node: CARDINAL DECREASING IN [0..new.seqSize) DO
WITH nlist.first SELECT FROM
list: LIST OF Frame => {
cnt: CARDINAL ← 0;
FOR tlist: LIST OF Frame ← list, tlist.rest WHILE tlist#NIL
DO cnt ← cnt+1 ENDLOOP;
new[node] ← NewFrame[cnt, xory, "Connections", , list.first.unordered];
FOR element: CARDINAL DECREASING IN [0..new[node].seqSize)
DO new[node][element] ← list.first; list ← list.rest ENDLOOP};
plaFrame: Frame => {new[node] ← plaFrame};
ENDCASE => ERROR;
nlist ← nlist.rest ENDLOOP};
EnumFrameTopFirst: PUBLIC PROC[frame: Frame, proc: EnumProc] = {
proc[frame];
FOR field: INT IN [0..frame.seqSize)
DO EnumFrameTopFirst[frame[field], proc] ENDLOOP };
EnumFrameBotFirst: PUBLIC PROC[frame: Frame, proc: EnumProc] = {
FOR field: INT IN [0..frame.seqSize)
DO EnumFrameBotFirst[frame[field], proc] ENDLOOP;
proc[frame] };
EnumFrameBotOnly: PUBLIC PROC[frame: Frame, proc: EnumProc] = {
IF frame.seqSize=0
THEN proc[frame]
ELSEFOR field: INT IN [0..frame.seqSize)
DO EnumFrameBotOnly[frame[field],proc] ENDLOOP};
Shell Stuff
Key: TYPE = REF INT;
ROPE: TYPE = IO.ROPE;
WriteFrameShellFile: PROC [comm: CDSequencer.Command] = {
inst: CD.Instance ← CDCommandOps.TheInstance[comm: comm, text: "Shell to File "];
IF inst#NIL THEN {
log.PutRope[CDOps.Info[inst.ob]];
IF ~CDCells.IsCell[inst.ob]
THEN log.PutRope[" is not a cell\n"]
ELSE {
shell: REF ShellRec;
shellFile: REF ROPENEW[ROPE ← comm.design.name.Cat[".shell"]];
IF comm.data#NIL
THEN shellFile ← NARROW[comm.data]
ELSE {
log.PutRope["\n"];
shellFile ← NEW[ROPE ← TerminalIO.RequestRope["File name for shell: "]]};
shell ← ShellFromObject[inst.ob];
ShellToFile[shell] } } };
WriteFrameShellFileCT: Commander.CommandProc = {
shell: REF ShellRec;
file: ROPE ← CommandTool.NextArgument[cmd];
IF file=NIL THEN RETURN;
shell ← ShellFromDesign[file];
ShellToFile[shell]};
ShellFromDesign: PUBLIC PROC[design: REF]
RETURNS [shell: REF ShellRec] = {
IF design#NIL AND ISTYPE[design, IO.ROPE]
THEN design ← CDIO.ReadDesign[NARROW[design]];
shell ← ShellFromObject[ObjectFromDesign[NARROW[design]]]};
ObjectFromDesign: PROC[design: CD.Design] RETURNS [cell: CD.Object] = {
multiple: BOOL;
inst: CD.Instance;
CDSimpleOps.Select[design, [0,0]];
[inst, multiple] ← CDOps.SelectedInstance[design];
IF multiple THEN ERROR;
IF inst=NIL THEN log.PutRope["\n Shell from design . . . no cell at [0, 0]\n"];
IF inst#NIL THEN {
log.PutF["\nShell from cell: %g", IO.rope[CDOps.Info[inst.ob]]];
IF ~CDCells.IsCell[inst.ob]
THEN log.PutRope[" is not a cell\n"]
ELSERETURN[inst.ob] } };
ShellFromObject: PUBLIC PROC[cell: CD.Object]
RETURNS [shell: REF ShellRec] = {
DuplicatePinError: BOOLFALSE;
AddPinEntry:  PWPins.InstanceEnumerator = {
data:    REF PinRec ← NEW[PinRec ← [
name:  CDPinObjects.GetName[inst],
side:  PWPins.GetSide[cell, inst].side,
size:  inst.ob.size,
pos:  inst.location,
layer:  CDPinObjects.GetLayer[inst] ] ];
olddata: RedBlackTree.Node ← RedBlackTree.LookupNode[tables[data.side], GetKey[data]];
IF olddata#NIL THEN {
oldPin: REF PinRec ← NARROW[olddata.data];
IF NOT DuplicatePinError
OR NOT (Rope.Equal[oldPin.name, "VDD"] OR Rope.Equal[oldPin.name, "GND"])
OR NOT (Rope.Equal[oldPin.name, data.name])
THEN log.PutF[ "\n\n Duplicate Pin ERRORs:%g%g\n",
IO.rope[ PinRope[ oldPin, [0,0] ] ], IO.rope[PinRope[data, [0,0]] ] ];
DuplicatePinError ← TRUE;
RETURN};
RedBlackTree.Insert[tables[data.side], data, GetKey[data]];
cnt[data.side] ← cnt[data.side]+1};
SeqTable: PROC [userdata: RedBlackTree.UserData] RETURNS [stop: BOOLFALSE] = {
data:  REF PinRec ← NARROW[userdata];
shell.pins[data.side][index] ← data;
index ← index+1};
tables: ARRAY Side OF RedBlackTree.Table;
cnt: ARRAY Side OF INTALL[0];
index: INT;
FOR currentSide: Side IN Side DO
tables[currentSide] ← RedBlackTree.Create[getKey: GetKey, compare: Compare] ENDLOOP;
[] ← PWPins.EnumerateEdgePins[cell, AddPinEntry];
shell ← NEW[ShellRec ← [ ]];
shell.name ← CDDirectory.Name[cell];
shell.size  ← FixedSize[CD.InterestSize[cell]];
FOR currentSide: Side IN Side DO
shell.pins[currentSide] ← NEW[PinSeq[cnt[currentSide]]];
index ← 0;
RedBlackTree.EnumerateIncreasing[tables[currentSide], SeqTable] ENDLOOP };
SideRope: PUBLIC ARRAY Side OF ROPE ← ["BOTTOM", "RIGHT", "TOP", "LEFT"];
ShellFromFile: PUBLIC PROC [shellName: ROPE] RETURNS [shell: REF ShellRec] = {
tbp: IO.BreakProc = {RETURN[SELECT char FROM
IN [IO.NUL .. IO.SP], ',, ':, ';      => sepr,
'[, '], '(, '), '{, '}, '", '+, '-, '*, '/, '@, '← => break,
ENDCASE          => other]};
shellFile: ROPE ← shellName.Cat[".shell"];
in: IO.STREAM;
cnt: ARRAY Side OF INTALL[0];
pins: ARRAY Side OF LIST OF REF PinRec ← ALL[NIL];
shell ← NEW[ShellRec ← [name: shellName]];
in ← FS.StreamOpen[shellFile ! FS.Error => {in←NIL; CONTINUE}];
IF in=NIL THEN RETURN[shell];
shell.name ← IO.GetTokenRope[in].token;
[ ]   ← IO.GetTime[in];
shell.size.x ← IO.GetInt[in];
shell.size.y ← IO.GetInt[in];
shell.size.xfree ← fixed;
shell.size.yfree ← fixed;
DO
name: ROPE   ← IO.GetTokenRope[in, tbp ! IO.EndOfStream => EXIT].token;
sideR: ROPE   ← IO.GetTokenRope[in].token;
posX: INT   ← IO.GetInt[in];
posY: INT   ← IO.GetInt[in];
sizeX: INT   ← IO.GetInt[in];
sizeY: INT   ← IO.GetInt[in];
layer: CD.Layer  ← IO.GetInt[in];
data: REF PinRec ← NEW[PinRec ← [
name: name,
side: bottom,
pos: [posX, posY],
size: [sizeX, sizeY],
layer: LOOPHOLE[layer] ] ];
FOR test: Side IN Side DO
IF Rope.Equal[sideR, SideRope[test]] THEN {data.side ← test; EXIT};
REPEAT FINISHED => ERROR ENDLOOP;
cnt[data.side] ← cnt[data.side] + 1;
pins[data.side] ← CONS[data, pins[data.side] ] ENDLOOP;
in.Close[];
FOR side: Side IN Side DO
shell.pins[side] ← NEW[PinSeq[cnt[side]]];
FOR pin: INT DECREASING IN [0..shell.pins[side].seqSize) DO
shell.pins[side][pin] ← pins[side].first; pins[side] ← pins[side].rest ENDLOOP ENDLOOP };
PinRope: PROC[pin: REF PinRec, base: CD.Position] RETURNS[rope: ROPE] = {
out: IO.STREAMIO.ROS[];
out.PutF["\n%g %-6g",
IO.rope[pin.name],
IO.rope[SideRope[pin.side]]];
out.PutF["%6g%6g%6g%6g%6g",
IO.int[pin.pos.x-base.x],
IO.int[pin.pos.y-base.y],
IO.int[pin.size.x],
IO.int[pin.size.y],
IO.int[pin.layer]];
RETURN[ IO.RopeFromROS[out] ]};
ShellToFile: PUBLIC PROC[shell: REF ShellRec] = {
shellFile: ROPE ← shell.name.Cat[".shell"];
tabStop45LookF: ROPE = "45 sp tabStops look.f";
node: TextNode.Ref;
out: IO.STREAMFS.StreamOpen[shellFile, $create];
log.PutF["\n Writing pin file: %g . . . ", IO.rope[shellFile]];
out.PutF["%g %18g%6g%6g\n",
IO.rope[shell.name],
IO.time[],
IO.int[shell.size.x],
IO.int[shell.size.y]];
FOR side: Side IN Side DO
FOR i: INT IN [0..shell.pins[side].seqSize) DO
out.PutRope[ PinRope[shell.pins[side][i], shell.pos]] ENDLOOP ENDLOOP;
out.PutRope["\n"];
out.Close[];
node ← PutGet.FromFile[shellFile];
NodeProps.PutProp[node.child, $Postfix, tabStop45LookF];
[ ] ← PutGet.ToFile[shellFile, node];
log.PutRope[" done\n"]};
NextSide: PUBLIC PROC [side: Side] RETURNS [next: Side] =
{RETURN[SELECT side FROM bottom=>right, right=>top, top=>left, left=>bottom,
ENDCASE=>ERROR]};
PrevSide: PUBLIC PROC [side: Side] RETURNS [prev: Side] =
{RETURN[SELECT side FROM bottom=>left, left=>top, top=>right, right=>bottom,
ENDCASE=>ERROR]};
OppSide: PUBLIC PROC [side: Side] RETURNS [opp: Side] =
{RETURN[SELECT side FROM left=>right, right=>left, top=>bottom, bottom=>top,
ENDCASE=>ERROR]};
GetSidePos: PUBLIC PROC [pin: REF PinRec] RETURNS [INT] =
{RETURN[SELECT pin.side FROM left, right => pin.pos.y, ENDCASE => pin.pos.x]};
SetSidePos: PUBLIC PROC [pin: REF PinRec, pos: INT] =
{SELECT pin.side FROM left, right => pin.pos.y←pos ENDCASE => pin.pos.x←pos};
REFToINT: PROC [ref: REF] RETURNS [INT] =
{pin: REF PinRec ← NARROW[ref]; RETURN[GetSidePos[pin]*CD.layerNum + pin.layer]};
GetKey:  PROC [userdata: RedBlackTree.UserData] RETURNS [RedBlackTree.Key] =
{RETURN[userdata]};
Compare: PROC [k: RedBlackTree.Key, data: RedBlackTree.UserData]
RETURNS [Basics.Comparison] =
{RETURN[Basics.CompareINT[REFToINT[k], REFToINT[data]]]};
log:  IO.STREAM ← NIL;
GetLog: PUBLIC PROC RETURNS[IO.STREAM] = {
logName: IO.ROPE ← "CDFrame.log";
IF log=NIL THEN {
log ← ViewerIO.CreateViewerStreams[logName, NIL, logName].out;
log.PutF["%g %g\n\n", IO.rope[logName], IO.time[]]};
RETURN[log]};
Commander.Register[proc: WriteFrameShellFileCT, key: "CDFrameShell"];
CDSequencer.ImplementCommand[a: $FrameShell, p: WriteFrameShellFile];
CDMenus.CreateEntry[$CellMenu, "Write frame shell file", $FrameShell];
[ ] ← GetLog[];
TerminalIO.WriteRope["FrameShell program loaded\n"];
END.