CoreBlockImpl.mesa
Created by Don Curry, February 1, 1986 2:43:22 pm PST
Edited by Don Curry, February 21, 1987 11:20:59 am PST
Last Edited by: Louis Monier February 10, 1987 5:41:26 pm PST
DIRECTORY CDBasics, CoreCreate, CD, CDOrient, Basics, Core, CoreBlock, CoreGeometry, CoreIOIFUProps, CoreOps, CoreClasses, CoreFrame, CoreInstCell, CoreName, CoreProperties, HashTable, IO, PWC, Rope;
CoreBlockImpl: CEDAR PROGRAM
IMPORTS CD, CDBasics, CDOrient, Basics, CoreBlock, CoreOps, CoreFrame, CoreGeometry, CoreInstCell, CoreIOIFUProps, CoreName, CoreProperties, HashTable, IO, PWC, Rope
EXPORTS CoreBlock =
BEGIN OPEN CoreBlock;
Wire:   TYPE = Core.Wire;
CellType:  TYPE = Core.CellType;
ROPE:   TYPE = Core.ROPE;
SidesProp: PROC RETURNS[ATOM] = {RETURN[CoreIOIFUProps.sides]};
Signal: SIGNAL[] = CODE;
AddSide: PUBLIC PROC[new, old: Sides] RETURNS[Sides] =
{RETURN[SidesFmWord[Basics.BITOR[SidesToWord[new], SidesToWord[old]]]]};
DelSide: PUBLIC PROC[new, old: Sides] RETURNS[Sides] =
{RETURN[SidesFmWord[Basics.BITAND[Basics.BITNOT[SidesToWord[new]], SidesToWord[old]]]]};
OnSide: PUBLIC PROC[new, old: Sides] RETURNS[BOOL] =
{RETURN[(Basics.BITAND[SidesToWord[new], SidesToWord[old]])#0]};
OtherSide: PUBLIC PROC[side: Sides] RETURNS[Sides] =
{RETURN[SELECT side FROM bottom=>top, top=>bottom, left=>right, right=>left,ENDCASE=>none]};
SidesToWord: PUBLIC PROC[side: Sides] RETURNS[word: WORD] =
{RETURN[LOOPHOLE[side]]};
SidesFmWord: PUBLIC PROC[word: WORD] RETURNS[side: Sides] =
{RETURN[LOOPHOLE[Basics.BITAND[word, SidesToWord[all] ] ] ] };
GetCellSide: PUBLIC PROC[cell: CellType] RETURNS[side: Sides] = {
refSides: REF Sides;
IF cell=NIL THEN RETURN[none];
refSides ← NARROW[CoreProperties.GetCellTypeProp[cell, SidesProp[]]];
RETURN[IF refSides=NIL THEN none ELSE refSides^] };
PutCellSide: PUBLIC PROC[cell: CellType, side: Sides] =
{CoreProperties.PutCellTypeProp[cell, SidesProp[], NEW[CoreBlock.Sides ← side]]};
GetWireSide: PUBLIC PROC[wire: Wire] RETURNS[side: Sides] = {
refSides: REF Sides;
IF wire=NIL THEN RETURN[none];
refSides ← NARROW[CoreProperties.GetWireProp[wire, SidesProp[]]];
RETURN[IF refSides=NIL THEN none ELSE refSides^]};
PutWireSide: PUBLIC PROC[wire: Wire, side: Sides] = {
refSides: REF Sides;
IF wire=NIL THEN RETURN;
refSides ← NARROW[CoreProperties.GetWireProp[wire, SidesProp[]]];
IF refSides=NIL
THEN CoreProperties.PutWireProp[wire, SidesProp[], NEW[CoreBlock.Sides ← side]]
ELSE refSides^ ← side;
FOR i: NAT IN [0..wire.size) DO PutWireSide[wire[i], side] ENDLOOP};
AddWireSide: PUBLIC PROC[wire: Wire, side: Sides] RETURNS[sameWire: Wire] = {
refSides: REF Sides;
IF wire=NIL THEN RETURN[NIL];
refSides ← NARROW[CoreProperties.GetWireProp[wire, SidesProp[]]];
IF refSides=NIL THEN refSides ← NEW[Sides ← none];
refSides^ ← AddSide[side, refSides^];
CoreProperties.PutWireProp[wire, SidesProp[], refSides];
FOR i: NAT IN [0..wire.size) DO wire[i] ← AddWireSide[wire[i], side] ENDLOOP;
RETURN[wire]};
DelWireSide: PUBLIC PROC[wire: Wire, side: Sides] RETURNS[sameWire: Wire] = {
refSides: REF Sides;
IF wire=NIL THEN RETURN[NIL];
refSides ← NARROW[CoreProperties.GetWireProp[wire, SidesProp[]]];
IF refSides=NIL THEN refSides ← NEW[Sides ← none];
refSides^ ← DelSide[side, refSides^];
CoreProperties.PutWireProp[wire, SidesProp[], refSides];
FOR i: NAT IN [0..wire.size) DO wire[i] ← DelWireSide[wire[i], side] ENDLOOP;
RETURN[wire]};
MergeSides: PUBLIC PROC[cell: CellType] = {
first:  Sides ← GetCellSide[cell];
last:  Sides ← OtherSide[first];
hidden: Sides;
data: CoreClasses.RecordCellType ← NARROW[cell.data];
visit: CoreOps.EachWirePairProc ~ {
side: Sides ← GetWireSide[publicWire];
side ← DelSide[hidden, side];
[ ] ← AddWireSide[actualWire, side]};
IF data.size>1 AND first=none THEN Signal[];
FOR i: NAT IN [0..data.size) DO
hidden ←    IF i # 0    THEN first ELSE none;
hidden ← AddSide[ IF (i+1) < data.size THEN last ELSE none, hidden];
[ ] ← CoreOps.VisitBinding[data[i].actual, data[i].type.public, visit] ENDLOOP;
DeletePseudoPublics[cell]};
DeletePseudoPublics: PUBLIC PROC [cell: Core.CellType] = {
publics: Core.Wires ← NIL;
FOR i: INT DECREASING IN [0..cell.public.size) DO
IF GetWireSide[ cell.public[i] ]#none
THEN publics ← CONS[ cell.public[i], publics]
ELSE log.PutF["\nRemoving pseudo public: %g",
IO.rope[ CoreName.WireNm[cell.public[i]].n] ]; ENDLOOP;
cell.public ← CoreOps.CreateWire[publics]};
RevInstances: PROC[list: CoreClasses.CellInstances] RETURNS[rev: CoreClasses.CellInstances] = {
FOR list ← list, list.rest WHILE list#NIL DO rev ← CONS[list.first, rev] ENDLOOP};
AbutCellList: PUBLIC PROC[
name: ROPE,
first: Sides,
cellTypes: LIST OF CellType]
RETURNS[cellType: CellType] = {
temp: LIST OF CellType ← NIL;
cts: LIST OF CellType ← NIL;
data: CoreClasses.RecordCellType;
specificGeneric, other: BOOLFALSE;
FOR temp: LIST OF CellType ← cellTypes, temp.rest WHILE temp#NIL DO
SELECT temp.first.class FROM
CoreInstCell.specificGenericCellClass => specificGeneric ← TRUE;
ENDCASE         => other    ← TRUE ENDLOOP;
IF specificGeneric=other THEN Signal[];
FOR temp: LIST OF CellType ← cellTypes, temp.rest WHILE temp#NIL DO
cts ← CONS[(IF temp.first.class=CoreInstCell.specificGenericCellClass
THENNARROW[ temp.first.data, CellType]
ELSE     temp.first       ), cts];
ENDLOOP;
IF first=left OR first=bottom THEN {
temp: LIST OF CellType ← cts; cts ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL DO cts ← CONS[temp.first, cts] ENDLOOP};
cellType ← PWC.AbutList[ cts, (first=left OR first=right)];
[ ] ← CoreName.CellNm[cellType, name];
CoreBlock.PutCellSide[cellType, (SELECT first FROM left, right => left, ENDCASE => bottom)];
CoreBlock.MergeSides[cellType];
IF first=top OR first=right THEN {
temp: LIST OF CellType ← cellTypes; cellTypes ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL
DO cellTypes ← CONS[temp.first, cellTypes] ENDLOOP};
Rename wires. Names are wrong either because cells were specificGeneric's or because extraction does not propagate names if nodes are split.
data  ← NARROW[cellType.data];
FOR index: NAT IN [0..data.size) DO
Visit: CoreOps.EachWirePairProc ~ {
new: ROPE ← CoreName.WireNm[publicWire].n;
IF publicWire.size#actualWire.size THEN Signal[];
IF publicWire.size#0 THEN RETURN[TRUE, FALSE];
IF new.Length[]=0 THEN
{IF CoreName.WireNm[actualWire].n.Length[]#0 THEN Signal[]};
[ ] ← CoreName.WireNm[actualWire, new]};
refCell: CellType ←
IF specificGeneric THEN NARROW[cellTypes.first.data, CellType] ELSE cellTypes.first;
Imports don't understand this prop until recasted
IF GetCellSide[data[index].type]=none THEN Signal[];
IF data[index].type#refCell THEN Signal[];
IF cellTypes.first.public.size#0 THEN [ ] ← CoreOps.VisitBinding
[actual: data[index].actual, public: cellTypes.first.public, eachWirePair: Visit];
cellTypes ← cellTypes.rest ENDLOOP;
EnsureUniqueNames[cellType]};
MergeCell renames children actuals based on children publics then does EnsureUniqueNames.
MergeCell can't be used in AbutCellList because AbutCellList must deal with specificGeneric's.
MergeCell is useful for extracting pads by getting Gnd and Vdd to connect.
MergeCell: PUBLIC PROC[cellType: CellType] = {
data: CoreClasses.RecordCellType ← NARROW[cellType.data];
FOR index: NAT IN [0..data.size) DO
Visit: CoreOps.EachWirePairProc ~ {
new: ROPE ← CoreName.WireNm[publicWire].n;
IF publicWire.size#0 OR new.Length[]=0 THEN RETURN[TRUE, FALSE];
[ ] ← CoreName.WireNm[actualWire, new]};
IF data[index].type.public.size#0 THEN [ ] ← CoreOps.VisitBinding
[actual: data[index].actual, public: data[index].type.public, eachWirePair: Visit] ENDLOOP;
EnsureUniqueNames[cellType]};
phA: ROPE ← CoreName.RopeNm["PhA"];
phB: ROPE ← CoreName.RopeNm["PhB"];
GND: ROPE ← CoreName.RopeNm["GND"];
VDD: ROPE ← CoreName.RopeNm["VDD"];
VBB: ROPE ← CoreName.RopeNm["VBB"];
Public wires with the same name are merged.
InternalOnlys are given versions (ie !1)
EnsureUniqueNames: PROC[cellType: CellType] = {
publics:   CoreName.Context ← CoreName.NewContext[]; -- public name to wire
deletedPublics: HashTable.Table  ← HashTable.Create[];  -- wires
internal:   CoreName.Context ← CoreName.NewContext[]; -- internal name to wire
iVersion:   HashTable.Table  ← HashTable.Create[];  -- internal name to version
data:    CoreClasses.RecordCellType ← NARROW[cellType.data];
ResolveNameConflicts: PROC[root: Wire, mergeConflicts: BOOLFALSE] = {
CoreOps.FlushNameCaches[root];
FOR i: INT IN [0..root.size) DO
IF root[i].size # 0
THEN ResolveNameConflicts[root[i], mergeConflicts]
ELSE {
old:    Wire ← root[i];
wNm:    ROPE ← CoreName.WireNm[old].n;
regPublic:  Wire ← CoreName.CtxNameToWire[publics, wNm];
regInternal:  Wire ← CoreName.CtxNameToWire[internal, wNm];
alreadyMerged: BOOL ← HashTable.Fetch[deletedPublics, old].found;
IF wNm.Length[]=0 THEN LOOP; -- no conflicts possible
IF regInternal=old THEN LOOP;
IF regPublic#NIL AND mergeConflicts OR alreadyMerged
THEN {
root[i] ← regPublic;
IF alreadyMerged THEN LOOP;
[ ]  ← HashTable.Store[deletedPublics, old, old];
[ ]  ← AddWireSide[root[i], GetWireSide[old]];
CoreGeometry.AddIndirectLazyPins[PWC.extractMode.decoration, root[i], old];
LOOP};
SELECT wNm FROM phA, phB, GND, VDD, VBB => Signal[]; ENDCASE;
IF regInternal=NIL
THEN {[ ] ← CoreName.CtxRegisterWire[internal, old]}
ELSE {
last: REF INTNARROW[HashTable.Fetch[iVersion, wNm].value];
IF last=NIL THEN
{[ ] ← HashTable.Store[iVersion, wNm, NEW[INT ← 0]]; LOOP};
last^ ← last^ +1;
[]𡤌oreName.WireNm[old, wNm.Cat[IO.PutFR["!%g",IO.int[last^]]]]}} ENDLOOP};
Visitor: PROC [wire: Wire] = {
[ ] ← CoreName.CtxRegisterWire[publics, wire];
[ ] ← CoreName.CtxRegisterWire[internal, wire]};
log.PutF["\nEnsure unique wire names for: %g ", IO.rope[CoreName.CellNm[cellType].n]];
[ ] ← CoreOps.VisitRootAtomics[cellType.public, Visitor];
ResolveNameConflicts[root: cellType.public, mergeConflicts: TRUE];
FOR i: INT IN [0..data.size) DO
ResolveNameConflicts[root: data[i].actual, mergeConflicts: FALSE] ENDLOOP;
ResolveNameConflicts[root: data.internal, mergeConflicts: FALSE];
cellType.public ← CoreName.WireFromCtx[publics]; -- flatten and reorder
data.internal  ← CoreName.WireFromCtx[internal]; -- flatten and reorder
HashTable.Erase[iVersion];
HashTable.Erase[deletedPublics];
publics ← CoreName.KillContext[publics];
publics ← CoreName.KillContext[internal]};
MergeSplitPublicsRenameInternalConflicts: PROC[cellType: CellType] = {
index:    INT;
publics:   CoreName.Context ← CoreName.NewContext[];
deletedPublics: HashTable.Table; -- removed public wires
iOnlys:   HashTable.Table; -- int onlys version #
name:    ROPE ← CoreName.CellNm[cellType].n;
data:    CoreClasses.RecordCellType ← NARROW[cellType.data];
SubstituteContextWires: PROC[root: Wire, trap: BOOL] = {
FOR i: INT IN [0..root.size) DO
IF root[i].size # 0
THEN SubstituteContextWires[root[i], trap]
ELSE {
old:  Wire ← root[i];
wNm:  ROPE ← CoreName.WireNm[old].n;
new:  Wire ← CoreName.CtxNameToWire[publics, wNm];
IF new=NIL OR new=old THEN LOOP;
IF HashTable.Fetch[deletedPublics, old].found THEN {root[i] ← new; LOOP};
IF trap THEN {
IF HashTable.Fetch[iOnlys, wNm].found THEN {
last: REF INTNARROW[HashTable.Fetch[deletedPublics, old].value];
[]𡤌oreName.WireNm[old, wNm.Cat[IO.PutFR["!%g",IO.int[last^]]]];
last^ ← last^ +1;
LOOP}; -- add version # to additional iOnlys
IF index=-1
THEN Signal[] -- why was this not found in actuals
ELSE {
log.PutF["\nSIGNAL - Internal-Only with public name %g", IO.rope[wNm] ];
log.PutF["\n Parent: %g Index: %g Child: %g",
IO
.rope[name], IO.int[index], IO.rope[CoreName.CellNm[data[index].type].n] ];
[]𡤌oreName.WireNm[old, wNm.Cat["!0"]];
[ ] ← HashTable.Store[iOnlys, wNm, NEW[INT ← 1]]; LOOP} };
root[i] ← new;
[ ] ← HashTable.Store[deletedPublics, old, old];
log.PutF["\n Joining split public %g", IO.rope[wNm]];
[ ] ← AddWireSide[root[i], GetWireSide[old]];
CoreGeometry.AddIndirectLazyPins[PWC.extractMode.decoration, root[i], old]};
ENDLOOP};
VisitPublics: PROC [wire: Wire] = {[ ] ← CoreName.CtxRegisterWire[publics, wire]};
log.PutF["\nCheck new public and internal of: %g ", IO.rope[name]];
[ ] ← CoreOps.VisitRootAtomics[cellType.public, VisitPublics];
deletedPublics ← HashTable.Create[]; -- removed public wires
iOnlys   ← HashTable.Create[]; -- next version
SubstituteContextWires[cellType.public, FALSE];
cellType.public ← CoreName.WireFromCtx[publics];
FOR index IN [0..data.size) DO SubstituteContextWires[data[index].actual, TRUE] ENDLOOP;
index ← -1;
SubstituteContextWires[data.internal, TRUE];
HashTable.Erase[iOnlys];
HashTable.Erase[deletedPublics];
publics ← CoreName.KillContext[publics]};
AbutCellInstances: PUBLIC PROC[
name:   ROPE,
first:   Sides,
instances:  CoreClasses.CellInstances]
RETURNS[cellType: CellType] = {
size:   INT ← 0;
index:   INT ← 0;
newPublicCtx: CoreName.Context ← CoreName.NewContext[];
newPublicWire: Wire;
newPublicWires: Core.Wires ← NIL;
noNamePublics: Core.Wires ← NIL;
ais:     LIST OF PWC.AbutInstance ← NIL;
inX: BOOLSELECT first FROM left, right => TRUE, bottom, top => FALSE, ENDCASE => ERROR;
FOR list: CoreClasses.CellInstances ← instances, list.rest WHILE list#NIL DO
size ← size+1 ENDLOOP;
index ← 0;
FOR list: CoreClasses.CellInstances ← instances, list.rest WHILE list#NIL DO
eachWirePair: CoreOps.EachWirePairProc ~ {
aName: ROPE ← CoreName.WireNm[actualWire].n;
pName: ROPE ← CoreName.WireNm[publicWire].n;
sides:  Sides ← GetWireSide[publicWire];
IF actualWire.size#0 THEN RETURN;
IF OnSide[sides, outSides] THEN {
parentPublic: Wire;
IF aName#NIL
THEN
parentPublic  ← CoreName.CtxWire[newPublicCtx, aName] -- adds if new
ELSE {
Signal[];
parentPublic  ← CoreOps.CreateWires[0]; -- Should Be Dead End Wire
noNamePublics ← CONS[parentPublic, noNamePublics]};
Must use rope here since Identity sub cell publics get copied when making the CellType to Layout to CellType round trip
pas ← CONS[ [pName, parentPublic], pas];
[ ] ← AddWireSide[parentPublic, DelSide[inSides, sides]] } };
pas: LIST OF CoreCreate.PANIL;
outSides: Sides ← OutSides[index, size, first];
inSides: Sides ← DelSide[outSides, all];
IF checking AND GetCellSide[list.first.type]=none THEN Signal[];
[ ] ← CoreOps.VisitBinding
[actual: list.first.actual, public: list.first.type.public, eachWirePair: eachWirePair];
THEN Signal[];
ais ← CONS[[PWC.Layout[list.first.type], pas], ais];
index ← index+1;
ENDLOOP;
newPublicWires   ← CoreName.WiresFromCtx[newPublicCtx];
FOR list: Core.Wires  ← noNamePublics, list.rest WHILE list#NIL DO
newPublicWires   ← CONS[ list.first, newPublicWires] ENDLOOP;
newPublicWire   ← CoreOps.CreateWire[newPublicWires];
DelPAInternalOnlys[newPublicCtx, ais];
SELECT first FROM right, top => { }; ENDCASE => {
list: LIST OF PWC.AbutInstance ← ais; ais ← NIL;
FOR list ← list, list.rest WHILE list#NIL DO ais ← CONS[list.first, ais] ENDLOOP};
cellType ← PWC.AbutCell[
public:   newPublicWire,
abutInstances: ais,
inX:    inX,
shared:   FALSE,
name:    name,
props:    NIL];
newPublicCtx ← CoreName.KillContext[newPublicCtx];
CoreBlock.PutCellSide [cellType, IF inX THEN left ELSE bottom]};
DelPAInternalOnlys: PROC[context: CoreName.Context, ais: LIST OF PWC.AbutInstance] = {
IsPublic: PROC[wr: CoreCreate.WR] RETURNS[BOOL] =
{name: ROPENARROW[wr]; RETURN[CoreName.CtxNameToWire[context, name]#NIL]};
FOR ais ← ais, ais.rest WHILE ais#NIL DO-- two LOOPs to avoid CONSing
pas: LIST OF CoreCreate.PA;
DO      -- remove internal onlys from first of list
IF ais.first.pas=NIL THEN GOTO Exit;
IF IsPublic[ais.first.pas.first.actual] THEN EXIT;
ais.first.pas ← ais.first.pas.rest;
REPEAT Exit => LOOP; ENDLOOP;
pas ← ais.first.pas;
WHILE pas.rest#NIL DO-- remove internal onlys from rest of list
IF IsPublic[pas.rest.first.actual]
THEN pas  ← pas.rest
ELSE pas.rest ← pas.rest.rest ENDLOOP;
ENDLOOP};
checking: BOOLTRUE;
OutSides: PROC[index, size: INT, first: Sides] RETURNS[ outSides: Sides ← all] = {
nextInSide, lastInSide: Sides;
SELECT first FROM
left  => {nextInSide ← right; lastInSide ← left};
bottom => {nextInSide ← top;  lastInSide ← bottom};
right  => {nextInSide ← left;  lastInSide ← right};
top  => {nextInSide ← bottom; lastInSide ← top};
ENDCASE => Signal[];
IF index#0   THEN outSides ← DelSide[lastInSide,  outSides];
IF index+1#size  THEN outSides ← DelSide[nextInSide,  outSides]};
PWC.FromLayoutWithoutPublic of PW.Abut
Rename by visiting bindings
AbutCellInstances: PUBLIC PROC[
name:   ROPE,
first:   Sides,
instances:  CoreClasses.CellInstances]
RETURNS[cellType: CellType,
] = {
RevObjs: PROC = {
temp: PW.ListOb ← objs; objs ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL DO objs ← CONS[temp.first, objs] ENDLOOP};
publicSides: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
newFirst: Sides;
objs: PW.ListOb ← NIL;
obj: CD.Object ← NIL;
data: CoreClasses.RecordCellType;
list: CoreClasses.CellInstances ← SELECT first FROM
left, bottom => RevInstances[instances], ENDCASE => instances;
FOR list ← list, list.rest WHILE list#NIL DO
objs ← CONS[PWC.Layout[list.first.type], objs] ENDLOOP;
SELECT first FROM
left, right => {obj ← PW.AbutListX[objs]; newFirst ← left};
bottom, top => {obj ← PW.AbutListY[objs]; newFirst ← bottom};
ENDCASE  => ERROR;
cellType ← PWC.FromLayoutWithoutPublic[obj];
[ ] ← CoreName.CellNm[cellType, name];
data ← NARROW[cellType.data];
IF data.size>1 AND first=none THEN Signal[];
list ← SELECT first FROM
right, top => RevInstances[instances], ENDCASE => instances;
FOR index: NAT IN [0..data.size) DO
Visit: CoreOps.EachWirePairProc ~ {
name: ROPE ← CoreName.WireNm[publicWire].n;
[ ]      ← CoreName.WireNm[actualWire, name]};
IF GetCellSide[data[index].type]=none THEN Signal[];
IF list.first.type#data[index].type THEN Signal[]; -- PWC should have cached
[ ] ← CoreOps.VisitBinding
[actual: data[index].actual, public: list.first.actual, eachWirePair: Visit];
list ← list.rest ENDLOOP;
CoreOps.FlushNameCaches[cellType.public];
CoreOps.FlushNameCaches[data.internal];
CoreBlock.PutCellSide [cellType, newFirst];
CoreBlock.MergeSides [cellType];
SELECT newFirst FROM
left  => PWC.SetAbutX[cellType];
bottom => PWC.SetAbutY[cellType];
ENDCASE => Signal[] };
PWC.FromLayoutWithoutPublic of PW.Abut
Rename by visiting bindings
AbutCellInstances: PUBLIC PROC[
name:   ROPE,
first:   Sides,
instances:  CoreClasses.CellInstances]
RETURNS[cellType: CellType,
] = {
ApplySidesToPublic: PROC [wire: Wire] ~ {
name:  ROPE ← CoreName.WireNm[wire].n;
refSides: REF Sides  ← NARROW[HashTable.Fetch[publicSides, name].value];
IF refSides=NIL THEN Signal[];
IF refSides^=none THEN Signal[];
IF CoreProperties.GetWireProp[wire, SidesProp[]]#NIL THEN Signal[];
CoreProperties.PutWireProp[wire, SidesProp[], refSides]};
RevObjs: PROC = {
temp: PW.ListOb ← objs; objs ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL DO objs ← CONS[temp.first, objs] ENDLOOP};
publicSides: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
hidden: Sides;
newFirst: Sides;
newLast: Sides;
objs: PW.ListOb ← NIL;
obj: CD.Object ← NIL;
data: CoreClasses.RecordCellType;
list: CoreClasses.CellInstances ← SELECT first FROM
left, bottom => RevInstances[instances], ENDCASE => instances;
FOR list ← list, list.rest WHILE list#NIL DO
objs ← CONS[PWC.Layout[list.first.type], objs] ENDLOOP;
SELECT first FROM
left, right => {obj ← PW.AbutListX[objs]; newFirst ← left};
bottom, top => {obj ← PW.AbutListY[objs]; newFirst ← bottom};
ENDCASE  => ERROR;
newLast ← OtherSide[newFirst];
cellType ← PWC.FromLayoutWithoutPublic[obj];
[ ] ← CoreName.CellNm[cellType, name];
data ← NARROW[cellType.data];
IF data.size>1 AND first=none THEN Signal[];
list ← SELECT first FROM
right, top => RevInstances[instances], ENDCASE => instances;
FOR index: NAT IN [0..data.size) DO
Visit: CoreOps.EachWirePairProc ~ {
name: ROPE ← CoreName.WireNm[publicWire].n;
side: Sides   ← GetWireSide[actualWire];
pubs: REF Sides  ← NARROW[HashTable.Fetch[publicSides, name].value];
side     ← DelSide[hidden, side];
IF pubs=NIL THEN pubs ← NEW[Sides ← none];
pubs^     ← AddSide[pubs^, side];
[ ]      ← HashTable.Store[publicSides, name, pubs];
[ ]      ← CoreName.WireNm[actualWire, name]};
IF GetCellSide[data[index].type]=none THEN Signal[];
hidden ←    IF index # 0     THEN newFirst ELSE none;
hidden ← AddSide[ IF (index+1) < data.size THEN newLast ELSE none, hidden];
IF list.first.type#data[index].type THEN Signal[]; -- PWC should have cached
[ ] ← CoreOps.VisitBinding
[actual: data[index].actual, public: list.first.actual, eachWirePair: Visit];
list ← list.rest ENDLOOP;
CoreOps.VisitRootAtomics[wire: cellType.public, eachWire: ApplySidesToPublic];
CoreOps.FlushNameCaches[cellType.public];
CoreOps.FlushNameCaches[data.internal];
CoreBlock.PutCellSide [cellType, newFirst];
CoreBlock.MergeSides [cellType]; -- replaced by ApplySidesToPublic
SELECT newFirst FROM
left  => PWC.SetAbutX[cellType];
bottom => PWC.SetAbutY[cellType];
ENDCASE => Signal[] };
PWC.CoreCreate.Cell
AbutCellList: PUBLIC PROC[
name: ROPE,
first: Sides,
cellTypes: LIST OF CellType,
]
RETURNS[cellType: CellType,
] = {
oIside, nIside, oOsides, nOsides: Sides;
public: CoreName.Context ← CoreName.NewContext[];
internal: CoreName.Context ← CoreName.NewContext[];
intOnly: CoreName.Context ← CoreName.NewContext[];
intances:  CoreClasses.CellInstances;
tempInsts: CoreClasses.CellInstances;
IF first=right OR first=top
THEN {
temp: LIST OF CellType,
← cellTypes; cellTypes ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL DO
cellTypes ← CONS[temp.first, cellTypes] ENDLOOP;
IF first=right THEN first←left ELSE first𡤋ottom};
IF first=left
THEN {oIside ← right; nIside ← left}
ELSE {oIside ← top; nIside ← bottom};
oOsides ← DelSide[ oIside, all];
nOsides ← DelSide[ nIside, all];
FOR kids: LIST OF CellType,
← cellTypes, kids.rest WHILE kids#NIL DO
nPubSides: Sides ← all;
IF kids#cellTypes THEN nPubSides ← DelSide[nIside, nPubSides];
IF kids.rest#NIL  THEN nPubSides ← DelSide[oIside, nPubSides];
FOR index: INT IN [0..kids.first.public.size) DO
newWire:  Wire ← kids.first.public[index];
aName:  ROPE ← CoreName.WireNm[newWire ].n;
oldWire:  Wire ← CoreName.CtxWire[internal, aName]; -- adds if not present
check:  Wire ← CoreName.CtxNameToWire[intOnly, aName];
oldSides:  Sides  ← CoreBlock.GetWireSide[oldWire];
newSides:  Sides  ← CoreBlock.GetWireSide[newWire];
oldExternal: BOOL  ← CoreBlock.OnSide[oldSides, oOsides];
oldConn:  BOOL  ← CoreBlock.OnSide[oldSides, oIside];
newExternal: BOOL  ← CoreBlock.OnSide[newSides, nOsides];
newConn: BOOL  ← CoreBlock.OnSide[newSides, nIside];
newPublic: BOOL  ← CoreBlock.OnSide[newSides, nPubSides];
IF check#NIL THEN Signal[];
IF kids#cellTypes THEN {
IF oldConn # newConn THEN {
log.PutF["\n Wire ending at cell boundry."];
log.PutF[" Parent: %g; Neglected child: %g; Wire: %g",
IO.rope[name], IO.rope[CoreName.CellNm[kids.first].n], IO.rope[aName]]};
IF ~(oldExternal OR newExternal) THEN [] ← CoreName.CtxWire[intOnly, aName]};
IF oldExternal OR newPublic THEN [ ] ← CoreName.CtxRegisterWire[public, oldWire];
PutWireSide[oldWire, AddSide[DelSide[ oIside, oldSides], DelSide[ nIside, newSides]]];
ENDLOOP;
intances ← CONS[CoreCreate.Instance[ kids.first ], intances];
ENDLOOP;
tempInsts ← intances; intances ← NIL;
FOR tempInsts ← tempInsts, tempInsts.rest WHILE tempInsts#NIL DO
intances ← CONS[tempInsts.first, intances] ENDLOOP;
IF HashTable.GetSize[internal] # (HashTable.GetSize[public] + HashTable.GetSize[intOnly])
THEN log.PutF["\n CoreBlockImpl.AbutCellList internal#public+intOnly anomaly"];
THEN Signal;
cellType ← CoreCreate.Cell[
public:  CoreName.WireFromCtx[public],
internal:  CoreName.WireFromCtx[internal],
instances:  intances,
name:   name ];
public ← CoreName.KillContext[public];
internal ← CoreName.KillContext[internal];
intOnly ← CoreName.KillContext[intOnly];
CoreBlock.PutCellSide [cellType, first];
CoreBlock.MergeSides [cellType];
SELECT first FROM
left  => PWC.SetAbutX[cellType];
bottom => PWC.SetAbutY[cellType];
ENDCASE => Signal[] };
MarkSides: PUBLIC PROC[cell: CellType, cap: Sides ← none] = {
eachWireProc: PROC [wire: Wire] =
{[ ] ← CoreBlock.PutWireSide[wire, DelSide[cap, GetWireLayoutSides[cell, wire] ] ]};
IF GetCellSide[cell]#none THEN RETURN; -- already done (ie. cached blank)
[] ← CoreOps.VisitRootAtomics[cell.public, eachWireProc];
PutCellSide[cell, all]};
MySides: PROC[hisSides: CoreGeometry.Sides] RETURNS[mySides: Sides ← none] = {
IF hisSides[left]  THEN mySides ← CoreBlock.AddSide[mySides, left];
IF hisSides[right] THEN mySides ← CoreBlock.AddSide[mySides, right];
IF hisSides[top]  THEN mySides ← CoreBlock.AddSide[mySides, top];
IF hisSides[bottom] THEN mySides ← CoreBlock.AddSide[mySides, bottom]};
GetWireLayoutSides: PUBLIC PROC[cell: CellType, wire: Wire]
RETURNS[sides: Sides ← none] = {
ir: CD.Rect ← PWC.InterestRect[cell];
eachPinProc: CoreGeometry.EachInstanceProc =
{sides ← CoreBlock.AddSide[sides, MySides[CoreGeometry.GetSides[ir, instance]]]};
[] ← CoreGeometry.EnumeratePins[PWC.extractMode.decoration, wire, eachPinProc]};
GetInstSideLocSize: PUBLIC PROC
[ob: CD.Object, inst: CD.Instance] RETURNS [side: CoreBlock.Sides, loc, size: INT] = {
base:  CD.Rect ← CD.InterestRect[ob];
rect: CD.Rect ← CDBasics.RectAt[inst.location, inst.ob.size, inst.orientation];
side ← none;
IF base.y1 = rect.y1 THEN side ← AddSide[bottom, side];
IF base.x2  = rect.x2 THEN side ← AddSide[right, side];
IF base.y2 = rect.y2 THEN side ← AddSide[top,  side];
IF base.x1  = rect.x1 THEN side ← AddSide[left,  side];
loc ← SELECT side FROM
left, right => rect.y1 - base.y1,
top, bottom => rect.x1 - base.x1,
ENDCASE  => 0;
size ← SELECT side FROM
left, right => rect.y2 - rect.y1,
top, bottom => rect.x2 - rect.x1,
ENDCASE  => 0};
log: IO.STREAM ← CoreFrame.GetLog[];
END.