IFUCoreDataImpl.mesa,
Copyright c 1986 by Xerox Corporation. All rights reserved.
Last Edited by Curry, October 1, 1986 10:50:33 am PDT
Don Curry October 27, 1986 12:46:28 pm PST
DIRECTORY CCDUtils, CD, CDBasics, CDCells, CDDirectory, CDRects, CDSymbolicObjects, Convert, Core, CoreBlock, CoreFrame, CoreLibrary, CoreName, CoreOps, CoreWire, CoreXform, HashTable, IFUCoreCells, IFUCoreData, CoreInstCell, IO, PW, PWC, Rope;
IFUCoreDataImpl: CEDAR PROGRAM
IMPORTS CCDUtils, CD, CDBasics, CDCells, CDDirectory, CDRects, CDSymbolicObjects, Convert, CoreBlock, CoreFrame, CoreLibrary, CoreName, CoreOps, CoreWire, CoreXform, HashTable, IFUCoreCells, IFUCoreData, CoreInstCell, IO, PW, PWC, Rope
EXPORTS IFUCoreData =
BEGIN
ROPE:     TYPE = Core.ROPE;
NameLetterCode:  TYPE = IFUCoreData.NameLetterCode;
NameLetterCodeRec: TYPE = IFUCoreData.NameLetterCodeRec;
NWMML:    TYPE = PWC.NWMML;
GND:     ROPE ← CoreName.RopeNm["GND"];
VDD:     ROPE ← CoreName.RopeNm["VDD"];
minus:    ROPE ← CoreName.RopeNm["-"];
plus:     ROPE ← CoreName.RopeNm["+"];
nil:     ROPE ← CoreName.RopeNm["nil"];
pwrList:    LIST OF REFLIST[GND, VDD];
Signal: SIGNAL = CODE;
dpCellClass: PUBLIC Core.CellClass ←
CoreOps.SetClassPrintProc[
NEW[Core.CellClassRec ← [name: "IFUCoreData", recast: NIL]],
ClassPrintProc];
ClassPrintProc: CoreOps.PrintClassProc = {
dpData: IFUCoreData.DpCellData ← NARROW[data];
IO.PutF[out, "Data Path Subclass: %g Type: \n", IO.rope[dpData.subClass] ];
CoreOps.PrintWire[dpData.type.w, out, 2] };
RegisterSubClassExpand: PUBLIC PROC
[type: CoreFrame.ExpandType, subClass: ROPE, expand: CoreFrame.ExpandProc] = {
IF type = soft
THEN [ ] ← HashTable.Store[expandSoft, subClass, NEW[CoreFrame.ExpandProc← expand]]
ELSE [ ] ← HashTable.Store[expandHard,subClass, NEW[CoreFrame.ExpandProc← expand]]};
expandHard: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
expandSoft: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
Sequencial48:  PUBLIC CoreXform.Xform ←
CoreXform.GenXform[LIST[ [4, 0], [8, 1] ]];
Interleaved48: PUBLIC CoreXform.Xform ←
CoreXform.GenXform[LIST[ [4, 1], [8, 0] ]];
CellProc: PUBLIC PROC [
subClass: ROPE ← NIL,
type:  ROPE ← NIL,
name:  ROPE ← NIL,
left:  REF ← NIL,
right:  REF ← NIL,
top:  REF ← NIL,
bot:  REF ← NIL,
in:   REF ← NIL,
out:  REF ← NIL,
data:  REF ← NIL,
channels: INT ← 6,
xform:  CoreXform.Xform ← IFUCoreData.Interleaved48 ]
RETURNS [cellType: Core.CellType] = {
ctx:   CoreName.Context ← CoreName.NewContext[];
public:  CoreWire.CWire;
strucType: CoreWire.CWire;
expandSoftProc: REF CoreFrame.ExpandProc;
expandHardProc: REF CoreFrame.ExpandProc;
xforms:  CoreXform.Xforms ← CoreXform.GenXforms[xform];
public ← [CoreOps.CreateWire[LIST[
RdListOfXfrmWires[ctx, "top",  top,  xforms],
RdListOfXfrmWires[ctx, "bot",  bot,  xforms],
RdListOfXfrmWires[ctx, "in",  in,  xforms],
RdListOfXfrmWires[ctx, "out",  out,  xforms],
RdListOfXfrmWires[ctx, "right", right,  NIL], -- if needed put xform on rope
RdListOfXfrmWires[ctx, "left",  left,  NIL], -- if needed put xform on rope
RdListOfXfrmWires[ctx, "pwr", pwrList, NIL] ]]];
ctx   ← CoreName.KillContext[ctx];
ctx   ← CoreName.NewContext[];
strucType ← [RdListOfXfrmWires[ctx, "type", LIST[type], xforms][0]];
ctx   ← CoreName.KillContext[ctx];
DeleteGV[public.f["top"].w];
DeleteGV[public.f["bot"].w];
cellType ← CoreOps.SetCellTypeName[
NEW [ Core.CellTypeRec ← [
class:  dpCellClass,
public: public.w,
data:  NEW[IFUCoreData.DpCellDataRec ← [
subClass: subClass,
type:  strucType,
data:  data,
channels: channels ] ],
properties: NIL] ],
name];
expandSoftProc ← NARROW[HashTable.Fetch[expandSoft,  subClass].value];
expandHardProc ← NARROW[HashTable.Fetch[expandHard, subClass].value];
IF expandSoftProc=NILTHEN Signal[];
IF expandHardProc=NILTHEN Signal[];
CoreFrame.SetFrameExpandProc[soft,   cellType, expandSoftProc ];
CoreFrame.SetFrameExpandProc[hard, cellType, expandHardProc ]};
If ref is a list then sform applies to it.
If not, xforms applies to the elements of the list;
RdListOfXfrmWires: PROC[ctx: CoreName.Context, name: ROPE, ref: REF, xforms: CoreXform.Xforms] RETURNS[wire: Core.Wire] = {
new: CoreXform.Xforms;
[ref, new] ← Parse[ref];
IF new#NIL THEN xforms ← new;
IF ISTYPE[ref, LIST OF REF]
THEN wire ← GenWireFmList[ctx, NARROW[ ref], xforms]
ELSE wire ← GenWireFmItem[ctx,    ref, xforms];
[ ] ← CoreName.WireNm[wire, name];
FOR jj: INT IN [0..wire.size) DO
CoreXform.SetXform[wire[jj], CoreXform.GenXform[xforms]] ENDLOOP };
DeleteGV: PROC[top: Core.Wire] = { -- doesn't work for top node
DeleteGVMain: PROC[wire: Core.Wire] RETURNS[new: Core.Wire] = {
IF wire.size=0
THEN RETURN[SELECT CoreName.WireNm[wire].n FROM
GND, VDD => CoreOps.CreateWires[0] ENDCASE => wire]
ELSE FOR i: INT IN [0..wire.size) DO wire[i] ← DeleteGVMain[wire[i]] ENDLOOP;
RETURN[wire]};
[] ← DeleteGVMain[top]};
Can't use this because wires are shared. Sharing maybe useful in the future if the row public gets used to make a celltype, but for now, rowWires are just used to package names.
DeleteGV: PROC[wire: Core.Wire] = {
SELECT CoreName.WireNm[wire].n FROM
GND, VDD => [ ] ← CoreOps.SetShortWireName[wire, NIL] ENDCASE;
FOR i: INT IN [0..wire.size) DO DeleteGV[wire[i]] ENDLOOP};
LISTIndexItem: PUBLIC PROC[list: LIST OF REF, index: INT] RETURNS[item: REF] = {
FOR index ← index, index-1 WHILE index#0 AND list#NIL DO list←list.rest ENDLOOP;
RETURN[IF list=NIL THEN NIL ELSE list.first]};
LISTLength: PUBLIC PROC [list: LIST OF REF] RETURNS[size: INT] =
{FOR size ← 0, size+1 WHILE list#NIL DO list←list.rest ENDLOOP};
AddGVToLIST: PROC [list: LIST OF REF, rngWord: INT, indexGV: BOOLFALSE]
RETURNS[new: LIST OF REF] = {
FOR ii: INT DECREASING IN [0..MAX[rngWord+2, LISTLength[list]]) DO
item: REF ← LISTIndexItem[list, ii];
IF ii=rngWord+1 THEN item ← IF indexGV THEN VDD.Cat["."] ELSE VDD;
IF ii=rngWord THEN item ← IF indexGV THEN GND.Cat["."] ELSE GND;
new ← CONS[item, new] ENDLOOP};
Parse: PROC[ref: REF] RETURNS[reff: REF, xforms: CoreXform.Xforms] = {
IF ref=NIL THEN RETURN[NIL, NIL];
WITH ref SELECT FROM
rope: Rope.ROPE  => {[reff, xforms] ← ParseRope[rope]; RETURN[reff, xforms]};
text: REF TEXT  => {
rope: Rope.ROPE ← Rope.FromRefText[text];
[reff, xforms] ← ParseRope[rope]; RETURN[reff, xforms]};
list: LIST OF REF  => {
temp: LIST OF REF;
FOR list ← list, list.rest WHILE list#NIL DO
temp ← CONS[Parse[list.first].reff, temp]; ENDLOOP;
list ← temp; temp ← NIL;
FOR list ← list, list.rest WHILE list#NIL DO temp ← CONS[list.first, temp] ENDLOOP;
RETURN[temp, NIL]};
list: LIST OF ROPE  => {
result, temp: LIST OF REF ← NIL;
FOR list ← list, list.rest WHILE list#NIL DO
temp ← CONS[ParseRope[list.first].reff, temp]; ENDLOOP;
FOR temp ← temp, temp.rest WHILE temp#NIL
DO result ← CONS[temp.first, result] ENDLOOP;
RETURN[result, NIL]};
ENDCASE => {Signal[]; RETURN[NIL, NIL]}};
ParseRope: PROC[rope: ROPE] RETURNS[reff: REF, xforms: CoreXform.Xforms] = {
ParseRopeItem: PROC RETURNS[REFNIL] = {
item: ROPE;
item ← ris.GetTokenRope[! IO.EndOfStream => CONTINUE].token;
IF item=NIL THEN RETURN[NIL];
SELECT item.Fetch[0] FROM
'(, '[  => RETURN[ParseRopeList[]];
'), ']  => Signal[];
ENDCASE => RETURN[CoreName.RopeNm[item]]};
ParseRopeList: PROC RETURNS[lst: LIST OF REFNIL] = {
temp: LIST OF REFNIL;
DO
item: ROPE ← ris.GetTokenRope[! IO.EndOfStream => EXIT].token;
SELECT item.Fetch[0] FROM
'(, '[   => lst ← CONS[ParseRopeList[], lst];
'), ']   => EXIT;
IN ['0..'9]  => {
IN ['A..'Z] => {lst ← CONS[CoreName.RopeNm[item], lst]};
ENDCASE => {Signal[]; lst ← CONS[CoreName.RopeNm[item], lst]}; -- prob. bad name
ENDLOOP;
temp ← lst; lst ← NIL;
FOR temp ← temp, temp.rest WHILE temp#NIL DO lst ← CONS[temp.first, lst] ENDLOOP};
ris: IO.STREAMIO.RIS[rope];
reff ← ParseRopeItem[] };
GenWireFmList: PROC[ctx: CoreName.Context, list: LIST OF REF, dims: CoreXform.Xforms]
RETURNS[wire: Core.Wire] = {
size: INT ← 0;
IF list=NIL THEN RETURN[ CoreOps.CreateWires[0, nil] ];
FOR lst: LIST OF REF ← list, lst.rest WHILE lst#NIL DO size ← size + 1; ENDLOOP;
wire ← NEW[Core.WireRec[size]]; size ← 0;
FOR lst: LIST OF REF ← list, lst.rest WHILE lst#NIL DO
wire[size] ← GenWireFmItem[ctx, lst.first, dims]; size ← size + 1; ENDLOOP};
GenWireFmItem: PROC[ctx: CoreName.Context, item: REF, dims: CoreXform.Xforms]
RETURNS[wire: Core.Wire] = {
IF item=NIL THEN RETURN[ CoreOps.CreateWires[0, nil] ];
WITH item SELECT FROM
first: LIST OF REF =>
wire ← GenWireFmList[ctx, first, IF dims=NIL THEN NIL ELSE dims.rest];
first: Rope.ROPE  => {
SELECT first.Fetch[first.Length[]-1] FROM
'., IN ['0..'9] => {
size: INT ← 0;
list: LIST OF REF;
IF dims=NIL
THEN wire ← CoreName.CtxWire[ctx, first, 0]
ELSE {
wire ← CoreName.CtxWire[ctx, first, dims.first.size];
IF wire=NIL THEN ERROR; -- probably missing '( )'s
list ← BlowUpRope[first, dims.first.size];
FOR size: INT IN [0..wire.size) DO
wire[size] ← GenWireFmItem[ctx, list.first, dims.rest];
list ← list.rest ENDLOOP} };
ENDCASE  => wire ← CoreName.CtxWire[ctx, first, 0] };
ENDCASE  => ERROR;
IF wire=NIL THEN ERROR};
BlowUpRope: PROC[rope: ROPE, size: INT] RETURNS[ropes: LIST OF REFNIL]= {
FOR int: INT DECREASING IN [0..size) DO
ropes ← CONS[
CoreName.RopeNm[ IO.PutFR["%g%g", IO.rope[rope], IO.int[int]] ],
ropes ]; ENDLOOP};
CellWidth: PUBLIC PROC[chans: INT] RETURNS[INT] = {
refCell: Core.CellType ← CoreLibrary.Get[IFUCoreCells.library, "DpLatchBlank"];
RETURN[chans*CCDUtils.metPitch+PWC.InterestSize[refCell].x]};
in0Rp:  ROPE   ← CoreName.RopeNm["in0"];
in1Rp:  ROPE  ← CoreName.RopeNm["in1"];
cont:   CD.Object ← CCDUtils.Contact[CCDUtils.cmosPoly];
ybias:   INT  ← CCDUtils.polW/2 - cont.size.y/2;
gndXBias: INT  ← -cont.size.x/2 + 1*CCDUtils.metPitch - cont.size.x/2;
vddXBias: INT  ← -cont.size.x/2 + 2*CCDUtils.metPitch - cont.size.x/2;
ConstantInputVariant: PUBLIC PROC [name, in0, in1: ROPE] RETURNS [cell: Core.CellType] ={
in0 ← CoreName.RopeNm[in0];
in1 ← CoreName.RopeNm[in1];
IF in0#GND AND in1#GND AND in0#VDD AND in1#VDD
THEN RETURN[ CoreLibrary.Get[IFUCoreCells.library, name] ] ELSE {
nMod0: ROPE ← SELECT in0 FROM VDD => "-V", GND => "-G", ENDCASE => "-=";
nMod1: ROPE ← SELECT in1 FROM VDD => "-V", GND => "-G", ENDCASE => "-=";
altName: ROPE ← name.Cat[nMod0, nMod1];
cell    ← NARROW[HashTable.Fetch[IFUCoreCells.library.table, altName].value];
IF cell#NIL THEN RETURN[cell] ELSE {
locs: LIST OF CD.Position ← NIL;
orig: CD.Object   ← CDDirectory.Fetch[IFUCoreCells.library.design, name].object;
new: CD.Object   ← CoreLibrary.Flatten[orig];
iBase: CD.Position  ← CDBasics.BaseOfRect[CD.InterestRect[new]];
list: CD.InstanceList ← NARROW [new.specificRef, CD.CellPtr].contents;
FOR list ← list, list.rest WHILE list # NIL DO
OPEN Sym: CDSymbolicObjects;
IF list=NIL THEN EXIT;
IF Sym.IsPin[list.first.ob] THEN {
inst: CD.Instance ← list.first;
conn: BOOL;
yLoc: INT;
sides: CoreBlock.Sides;
vddLoc, gndLoc: CD.Position;
[sides, yLoc,] ← CoreBlock.GetInstSideLocSize[new, inst];
conn   ← CoreBlock.OnSide[left, sides];
vddLoc  ← [vddXBias, yLoc+ybias];
gndLoc  ← [gndXBias, yLoc+ybias];
SELECT CoreName.RopeNm[Sym.GetName[inst]] FROM
in0Rp  => {
SELECT in0 FROM
VDD =>{Sym.SetName[inst, VDD]; IF conn THEN locs←CONS[vddLoc, locs]};
GND =>{Sym.SetName[inst, GND]; IF conn THEN locs←CONS[gndLoc, locs]};
ENDCASE };
in1Rp  => {
SELECT in1 FROM
VDD =>{Sym.SetName[inst, VDD]; IF conn THEN locs←CONS[vddLoc, locs]};
GND =>{Sym.SetName[inst, GND]; IF conn THEN locs←CONS[gndLoc, locs]};
ENDCASE };
ENDCASE };
ENDLOOP;
FOR locs ← locs, locs.rest WHILE locs#NIL DO
[ ] ← PW.IncludeInCell[new, cont, locs.first] ENDLOOP;
[ ]  ← CDCells.RepositionCell[new, NIL];
cell ← CoreLibrary.ObjCell[new, altName];
CoreLibrary.Set[ IFUCoreCells.library, altName, cell] } } };
ConnSpec: TYPE = REF ConnSeqRec;
ConnSeqRec: TYPE = RECORD[SEQUENCE size: CARDINAL OF CnntRec];
CnntRec:  TYPE = RECORD[name: ROPE, min, max: INT, layer: CD.Layer];
GenericCellRegistry: HashTable.Table ← HashTable.Create[];
CreateNameLetterCode: PUBLIC PROC
[genericID: REF, list: LIST OF CoreWire.CWire, xformBit: INT]
RETURNS[code: NameLetterCode] = {
code ← InitNameLetterCode[];
IF genericID=NIL
THEN code.name ← CoreName.ID["BitRouteRef"]
ELSE {
code.name ← NARROW[HashTable.Fetch[GenericCellRegistry, genericID].value];
IF code.name = NIL THEN {
IF genericID=NIL THEN genericID ← CoreName.ID["BitRouteRef"];
code.name ← WITH genericID SELECT FROM
rope: ROPE  => genericID ← CoreName.RopeNm[rope],
text: REF TEXT => genericID ← CoreName.RopeNm[Rope.FromRefText[text]],
ENDCASE   =>     CoreName.ID["BitRouteRef"];
[ ] ← HashTable.Store[GenericCellRegistry, genericID, code.name]} };
FOR list ← list, list.rest WHILE list#NIL DO
code.name ← code.name.Cat[ "|" ];
FOR index: INT IN [0..list.first.w.size) DO
code.name ← code.name.Cat
[UniqueLetter[code, list.first.i[index].x[xformBit].n[] ] ] ENDLOOP;
ENDLOOP};
Encode: PUBLIC PROC[code: NameLetterCode, name: ROPE] RETURNS[letter: ROPE] =
{letter ← NARROW[HashTable.Fetch[code.letFmNm, name].value]};
Decode: PUBLIC PROC[code: NameLetterCode, letter: ROPE] RETURNS[name: ROPE] =
{name ← NARROW[HashTable.Fetch[code.nmFmLet, letter].value]};
InitNameLetterCode: PROC RETURNS[code: NameLetterCode] = {
code ← NEW[ NameLetterCodeRec ← [
name:   NIL,
letFmNm:  HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope],
nmFmLet: HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope],
char:   'a,
none:   CoreName.RopeNm["="] ] ];
[ ] ← HashTable.Store[code.letFmNm, VDD,  plus];
[ ] ← HashTable.Store[code.nmFmLet, plus,  VDD];
[ ] ← HashTable.Store[code.letFmNm, GND,  minus];
[ ] ← HashTable.Store[code.nmFmLet, minus, GND] };
UniqueLetter: PROC[code: NameLetterCode, name: ROPE] RETURNS[letter: ROPE] = {
name ← CoreName.RopeNm[name];
IF name.Length[]=0 OR name=nil THEN RETURN[code.none];
letter ← NARROW[HashTable.Fetch[code.letFmNm, name].value];
IF letter#NIL THEN RETURN[letter];
letter ← CoreName.RopeNm[Convert.RopeFromChar[code.char, FALSE]];
[ ] ← HashTable.Store[code.letFmNm, name, letter];
[ ] ← HashTable.Store[code.nmFmLet, letter, name];
SELECT code.char FROM
IN ['a..'z)  => code.char ← code.char + 1;
'z    => code.char ← 'A;
IN ['A..'Z) => code.char ← code.char + 1;
ENDCASE  => Signal[]};
DpBitRoute: PUBLIC PROC[
specificCell:  Core.CellType,
genericCell:  Core.CellType,
rowWire:   CoreWire.CWire,
phyBit:   INT,
channels:   INT,
specificName: ROPENIL ] RETURNS [glue: Core.CellType] = {
renameProc:  CoreInstCell.RenameProc ~
{new ← code.Decode[letter: old]; IF new=NIL THEN Signal[]; RETURN[new]};
code:  IFUCoreData.NameLetterCode ← CreateNameLetterCode
[genericID: genericCell, xformBit: phyBit, list: LIST[
rowWire.f["top"  ],
rowWire.f["in"  ],
rowWire.f["out"  ],
rowWire.f["right" ],
rowWire.f["left"  ],
rowWire.f["bot"  ] ]];
genericObj: CD.Object;
generic:  Core.CellType ← CoreLibrary.Get[IFUCoreCells.library, code.name];
IF generic=NIL THEN {
size:   INT ← PWC.InterestSize[specificCell].y;
list:   LIST OF NWMML ← CCDUtils.SidePinList[specificCell, left];
count:   INT ← 0;
connSpec:  ConnSpec;
FOR temp: LIST OF NWMML ← list, temp.rest WHILE temp#NIL DO
count ← count + 1 ENDLOOP;
connSpec ← NEW[ConnSeqRec[count+2]];
connSpec[0]     ← [NIL, 0, 0,  CCDUtils.cmosMet];
connSpec[connSpec.size-1] ← [NIL, size, size, CCDUtils.cmosMet];
FOR count IN [1..connSpec.size-1) DO
connSpec[count] ← [
name: code.Encode[list.first.name],
min: list.first.min,
max: list.first.max,
layer: list.first.layer ];
list ← list.rest ENDLOOP;
genericObj ← IncludeBitWiring[code, connSpec, rowWire, phyBit, channels];
generic  ← CoreLibrary.ObjCell[genericObj, code.name ];
CoreLibrary.Set[IFUCoreCells.library, code.name, generic]};
IF specificName = NIL THEN specificName ← CoreName.CellNm[specificCell].n;
IF specificName = NIL
THEN specificName ← CoreName.ID["BitRoute"]
ELSE specificName ← Rope.Cat[specificName, "BitRoute"];
glue ← CoreInstCell.SpecificGeneric[generic, renameProc];
glue ← CoreFrame.NewFrameCell[0, specificName, [cell: glue ]] };
IncludeBitWiring: PROC[code: IFUCoreData.NameLetterCode, connSpec: ConnSpec, rowWire: CoreWire.CWire, xBit, channels: INT]
RETURNS[cell: CD.Object] = {
OPEN CM: CCDUtils;
Same: PROC[idx1, idx2: CARDINAL] RETURNS[BOOL] =
{RETURN[Rope.Equal[connSpec[idx1].name, connSpec[idx2].name]]};
tIdx: CARDINAL ← connSpec.size-1;
bIdx: CARDINAL ← 0;
cell ← CDCells.CreateEmptyCell[];
Vertical connections
FOR col: INT IN [0..channels) DO
Add: PROC[idx1, idx2: CARDINAL] RETURNS[BOOL] = {
IF ~Same[idx1, idx2] THEN RETURN[FALSE];
loc.y ← connSpec[idx1].min;
size.y ← connSpec[idx2].max - connSpec[idx1].min;
IF size.y < 0 THEN ERROR;
CCDUtils.AddRet[cell:cell, size: size, loc: loc, level: CM.cmosMet];
RETURN[TRUE]};
size: CD.Position ← [CM.metW, 0];
loc: CD.Position ← [CM.leftTail+col*CM.metPitch-CM.metW/2, 0];
connSpec[tIdx].name ← code.Encode[rowWire.f["top"].i[col].x[xBit].n];
connSpec[bIdx].name  ← code.Encode[rowWire.f["bot"].i[col].x[xBit].n];
SELECT connSpec[tIdx].name FROM GND, VDD => connSpec[tIdx].name←NIL ENDCASE;
SELECT connSpec[bIdx].name FROM GND, VDD => connSpec[bIdx].name←NIL ENDCASE;
IF connSpec[tIdx].name#NIL THEN FOR row: INT IN [bIdx..tIdx) DO
CM.PutPin[cell, [CM.metW, CM.metW], [loc.x, connSpec[tIdx].max-CM.metW],
CM.cmosMet, connSpec[tIdx].name];
IF Add[ row, tIdx] THEN EXIT; REPEAT FINISHED => {
log.PutF["Orphan top signal: %g",
IO.rope[IFUCoreData.Decode[code, connSpec[tIdx].name]]];
Signal[]} ENDLOOP;
IF connSpec[bIdx].name#NIL THEN FOR row: INT DECREASING IN (bIdx..tIdx) DO
CM.PutPin[cell, [CM.metW, CM.metW], [loc.x, 0], CM.cmosMet, connSpec[bIdx].name];
IF Add[ bIdx, row] THEN EXIT; REPEAT FINISHED => IF ~Same[bIdx, tIdx] THEN {
log.PutF["Orphan bottom signal: %g",
IO.rope[IFUCoreData.Decode[code, connSpec[bIdx].name]]];
Signal[]} ENDLOOP;
ENDLOOP;
Horizontal connections
FOR row: INT IN (bIdx..tIdx) DO
ctct:  CD.Object ← CM.Contact[connSpec[row].layer];
wire:  CD.Object;
wWdth: INT ← connSpec[row].max - connSpec[row].min;
Pin: PROC[side: {left, right}] = {
xLoc: INTIF side = right THEN channels*CM.metPitch-CM.cnctSize ELSE 0;
CM.PutPin[cell, [CM.cnctSize, wWdth],
[xLoc, connSpec[row].min], connSpec[row].layer, connSpec[row].name]};
IF connSpec[row].layer = CM.cmosMet2 THEN {
wire ← CDRects.CreateRect[[channels*CM.metPitch, wWdth], connSpec[row].layer];
Pin[left]; Pin[right];
[] ← PW.IncludeInCell[cell, wire, [0, connSpec[row].min]]};
FOR col: INT IN [0..channels) DO
wLen: INT ← (channels-1-col)*CM.metPitch + CM.rightTail;
wLoc: CD.Position ← [CM.leftTail + col*CM.metPitch, connSpec[row].min];
connSpec[tIdx].name ← code.Encode[rowWire.f["top"].i[col].x[xBit].n];
connSpec[bIdx].name  ← code.Encode[rowWire.f["bot"].i[col].x[xBit].n];
IF ~Same[row, tIdx] AND ~Same[row, bIdx] THEN LOOP;
wire ← CDRects.CreateRect[[wLen, wWdth], connSpec[row].layer];
Pin[right];
[] ← PW.IncludeInCell[cell, ctct,
[wLoc.x-CM.cnctSize/2, wLoc.y-(CM.cnctSize-wWdth)/2]];
[] ← PW.IncludeInCell[cell, wire, wLoc];
ENDLOOP;
ENDLOOP;
CDCells.SetInterestRect[cell, [0, 0, channels*CM.metPitch, connSpec[tIdx].max]];
[ ] ← CDCells.RepositionCell[cell, NIL] };
BlockSides: PUBLIC PROC[cell: Core.CellType, cwire: CoreWire.CWire] = {
BlockSideIfNotUsed: PROC[wire: Core.Wire] = {
sides: CoreBlock.Sides ← CoreBlock.GetWireSide[wire];
name: ROPE ← CoreName.WireNm[wire].n;
ltrt: BOOL ← cwire.f["left"].f[name].w#NIL OR cwire.f["right"].f[name].w#NIL;
IF NOT ltrt THEN RETURN;
SELECT name FROM VDD, GND => RETURN ENDCASE;
IF cwire.f["left"  ].f[name].w=NIL THEN sides ← CoreBlock.DelSide[left,  sides];
IF cwire.f["right" ].f[name].w=NIL THEN sides ← CoreBlock.DelSide[right, sides];
CoreBlock.PutWireSide[wire, sides]};
CoreOps.VisitRootAtomics[cell.public, BlockSideIfNotUsed]};
log: IO.STREAM ← CoreFrame.GetLog[];
END.