CCDUtilsImpl:
CEDAR
PROGRAM
IMPORTS Basics, CCDUtils, CD, CDBasics, CDCells, CDCleanUp, CDCreateLabels, CDDirectory, CDOps, CDProperties, CDRects, CDSimpleOps, CDSimpleRules, CDSymbolicObjects, CDViewer, CMosB, CMosBObjects, Convert, CoreBlock, CoreClasses, CoreFrame, CoreInstCell, CoreName, CoreOps, FS, HashTable, ImagerFont, IO, PW, PWCore, PWPins, Real, Rope, Sinix, SinixCMos, VFonts
EXPORTS CCDUtils =
BEGIN
Signal: SIGNAL = CODE;
ROPE: TYPE = CCDUtils.ROPE;
GND: ROPE = CoreName.RopeNm["GND"];
VDD: ROPE = CoreName.RopeNm["VDD"];
plus: ROPE = CoreName.RopeNm["+"];
minus: ROPE = CoreName.RopeNm["-"];
NWMML: TYPE = CCDUtils.NWMML;
CellType: TYPE = Core.CellType;
Side: TYPE = PWPins.Side;
Side: TYPE = CoreFrame.Side;
cache: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
Store: PROC[name: ROPE, obj: CD.Object] = {[]←HashTable.Store[cache, name, obj]};
Fetch:
PROC[name:
ROPE]
RETURNS[obj:
CD.Object] =
{RETURN[NARROW[HashTable.Fetch[cache, name].value, CD.Object]]};
BlankObj:
PUBLIC
PROC[size:
CD.Position]
RETURNS[cell:
CD.Object] ~ {
name: ROPE ← IO.PutFR["Dummy-%g-%g", IO.int[size.x], IO.int[size.y]];
minLineW: INT = 2 ;
IF size.x=0 OR size.y=0 THEN RETURN[NIL];
cell ← Fetch[name];
IF cell#NIL THEN RETURN[cell];
cell ← CDCells.CreateEmptyCell[];
IF size.x
IN (0..4*minLineW)
OR size.y
IN (0..4*minLineW)
THEN [ ] ← PW.IncludeInCell[cell, CDRects.CreateRect[size, CD.commentLayer], [0, 0] ]
ELSE {
lwidth: INT ← MAX[minLineW, MIN[size.x/100, size.y/100]];
hor: CD.Object ← CDRects.CreateRect[[size.x, lwidth], CD.commentLayer];
ver: CD.Object ← CDRects.CreateRect[[lwidth, size.y], CD.commentLayer];
[ ] ← PW.IncludeInCell[cell, hor, [0, 0] ];
[ ] ← PW.IncludeInCell[cell, hor, [0, size.y-lwidth] ];
[ ] ← PW.IncludeInCell[cell, ver, [0, 0] ];
[ ] ← PW.IncludeInCell[cell, ver, [size.x-lwidth, 0] ]};
PW.RepositionCell[cell];
Store[name, cell]};
BlankObj: PUBLIC PROC[size: CD.Position] RETURNS[cell: CD.Object] ~ {
cellPtr: CD.CellPtr;
found: BOOL ← FALSE;
name: ROPE ← IO.PutFR["Dummy-%g-%g", IO.int[size.x], IO.int[size.y]];
IF size.x=0 OR size.y=0 THEN RETURN[NIL];
cell ← Fetch[name];
IF cell#NIL THEN RETURN[cell];
cell ← CDCells.CreateEmptyCell[];
cellPtr ← NARROW[cell.specificRef];
cellPtr.contents ← CONS[NEW[CD.InstanceRep ←
[ob: CDRects.CreateRect[size, CD.backgroundLayer]] ], cellPtr.contents];
Store[name, cell]};
BlankCell:
PUBLIC
PROC[size:
CD.Position]
RETURNS[cell: CellType] ~ {
obj: CD.Object ← BlankObj[size];
CDProperties.PutProp [obj, SinixCMos.extractBMode.extractProcProp, $CCDUtilsBlankCell];
cell ← PWCore.FromLayoutWithoutPublic[obj];
CoreBlock.PutCellSide[cell, all];
cell ← CoreFrame.NewFrameCell[0, ObjName[obj], [cell: cell]]};
NewBlankCellType:
PUBLIC PROC
RETURNS[cell: CellType] ~ {
rec: CoreClasses.RecordCellType ← NEW [CoreClasses.RecordCellTypeRec[0]];
rec.internal ← CoreOps.CreateWires[0];
cell ←
NEW[Core.CellTypeRec ← [
class: CoreClasses.recordCellClass,
public: CoreOps.CreateWires[0],
data: rec,
properties: NIL]]};
BlankExtractProc: Sinix.ExtractProc ~ {
name: ROPE ← ObjName[obj];
cellType: Core.CellType ← NewBlankCellType[];
result ← CoreOps.SetCellTypeName[cellType, name]};
ObjName:
PUBLIC
PROC[obj:
CD.Object]
RETURNS[name:
ROPE] = {
IF obj=NIL THEN RETURN[NIL];
IF obj.specificRef=NIL THEN RETURN[NIL];
IF NOT ISTYPE[obj.specificRef, CD.CellPtr] THEN RETURN[NIL];
RETURN[NARROW[obj.specificRef, CD.CellPtr].name]};
ObjSize:
PUBLIC
PROC[obj:
CD.Object]
RETURNS[size:
CD.Position] =
{RETURN[CD.InterestSize[obj]]};
CellSize:
PUBLIC
PROC[ct: Core.CellType]
RETURNS[size:
CD.Position] =
{RETURN[CDBasics.SizeOfRect[CellRect[ct]]]};
CellRect:
PUBLIC
PROC[ct: Core.CellType]
RETURNS[rect:
CD.Rect] = {
IF ct=NIL THEN Signal[];
RETURN[
SELECT ct.class
FROM
CoreInstCell.specificGenericCellClass => PWCore.InterestRect[NARROW[ct.data]],
CoreFrame.frameCellClass => PWCore.InterestRect[CoreFrame.FCT[ ct].cell ],
ENDCASE => PWCore.InterestRect[ ct]]};
Layout:
PUBLIC
PROC[ct: Core.CellType]
RETURNS[obj:
CD.Object] = {
IF ct=NIL THEN RETURN[NIL];
SELECT ct.class
FROM
CoreInstCell.specificGenericCellClass => RETURN[PWCore.Layout[NARROW[ct.data]]];
CoreFrame.frameCellClass => RETURN[PWCore.Layout[CoreFrame.FCT[ ct].cell ]];
ENDCASE => RETURN[PWCore.Layout[ ct]]};
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];
OrnateFrame:
PUBLIC PROC [cell: CellType, design: CD.Design←NIL]
RETURNS[picture: CD.Object] = {
recCell: CellType ← cell;
rSize: CD.Position;
loc: CD.Position;
sidesObj: ARRAY Side OF CD.Object;
refObj: CD.Object;
IF cell.class = CoreFrame.frameCellClass
THEN recCell ← CoreFrame.FCT[cell].cell
ELSE recCell ← cell;
refObj ← CCDUtils.Layout[recCell];
picture ← CDCells.CreateEmptyCell[];
rSize ← CD.InterestSize[refObj];
[ ] ← PW.IncludeInCell[picture, refObj, [0, 0]];
FOR side: Side
IN Side
DO
nwmmls: LIST OF NWMML ← SidePinList[recCell, side];
bias: INT = lambda;
orient: CD.Orientation;
size: CD.Position ← [0, 0];
IF nwmmls=NIL THEN LOOP;
sidesObj[side] ← CDCells.CreateEmptyCell[];
FOR nwmmls ← nwmmls, nwmmls.rest
WHILE nwmmls#
NIL
DO
textOb: CD.Object ← ScaledText
[nwmmls.first.name, [400*lambda,10*lambda],CD.commentLayer, design];
textOb ← CDTexts.CreateText[pins[i].name, font, layer];
org: CD.Position ← CD.ClientOrigin[textOb];
pos: CD.Position ;
size.x ← MAX[size.x, ((textOb.size.x+2*bias+lambda-1)/lambda)*lambda];
pos.x ← bias;
pos.y ←
SELECT side
FROM
left, right => nwmmls.first.min - org.y,
bottom, top => rSize.x - nwmmls.first.max - org.y,
ENDCASE => ERROR;
[ ] ← PW.IncludeInCell[sidesObj[side], textOb, pos];
ENDLOOP;
SELECT side
FROM
left, right => {size.y ← rSize.y};
top, bottom => {size.y ← rSize.x};
ENDCASE => ERROR;
SELECT side
FROM
left => {loc ← [-size.x, 0 ]; orient ← CDOrient.original};
right => {loc ← [ rSize.x, 0 ]; orient ← CDOrient.original};
top => {loc ← [ 0, rSize.y ]; orient ← CDOrient.rotate90};
bottom => {loc ← [ 0, -size.x ]; orient ← CDOrient.rotate90};
ENDCASE => ERROR;
CDCells.SetInterestRect[sidesObj[side], [0, 0, size.x, size.y]];
[ ] ← CDCells.RepositionCell[sidesObj[side], NIL];
[ ] ← PW.IncludeInCell[picture, sidesObj[side], loc, orient];
ENDLOOP;
[ ] ← CDCells.RepositionCell[picture, NIL]};
AddRet:
PUBLIC
PROC [cell:
CD.Object, size:
CD.Position, loc:
CD.Position, level:
CD.Layer] = {
IF (size.x MOD 2)=1 OR (size.y MOD 2)=1 OR size.x<0 OR size.y<0
IF size.x<0
OR size.y<0
THEN {
PW.WriteF["Strange rectangle size [%g, %g]", IO.int[size.x], IO.int[size.y] ];
Signal[]};
IF size.x=0 OR size.y=0 THEN RETURN;
[] ← PW.IncludeInCell[cell, CDRects.CreateRect[size, level], loc]};
Only metal2 left/right or metal1 top/bottom power is included
SidePinList:
PUBLIC
PROC [cellType: CellType, side: PWPins.Side]
RETURNS [list: LIST OF NWMML ← NIL] = {
refCell: CellType;
thisSide: Side ← side;
EachWirePin: PWCore.EachWirePinProc = {
SubstituteWireName: CoreOps.EachWirePairProc ~ {
IF actualWire#wire THEN RETURN;
wire←publicWire; name𡤌oreName.WireNm[wire].n; RETURN[FALSE, TRUE]};
name: ROPE ← CoreName.WireNm[wire].n;
IF side#thisSide THEN RETURN;
IF
NOT CoreBlock.OnSide[CoreFrame.SideSides[side], CoreBlock.GetWireSide[wire]]
THEN RETURN;
IF layer#cmosMet
AND layer#cmosMet2
THEN
SELECT name FROM GND, VDD, plus, minus => RETURN ENDCASE;
IF cellType.class=CoreInstCell.specificGenericCellClass
THEN
[ ] ← CoreOps.VisitBinding[refCell.public, cellType.public, SubstituteWireName];
IF name=NIL THEN RETURN;
list ← AddMergeNWMMLs[[name, wire, min, max, layer], list]};
IF cellType.class=CoreInstCell.specificGenericCellClass
THEN {refCell ← NARROW[cellType.data]} ELSE refCell ← cellType;
[] ← PWCore.EnumerateWirePins[refCell, EachWirePin];
PosSortNWMMLs[list]};
Add sorted by name, wire, layer, min, max - - - merge overlaps
AddMergeNWMMLs:
PUBLIC
PROC[item:
NWMML, lst:
LIST
OF
NWMML]
RETURNS[LIST OF NWMML] = {
IV: PROC[w: Core.Wire] RETURNS[int: INT] = {int ← LOOPHOLE[w]};
IF lst=NIL THEN RETURN[CONS[item, NIL]];
FOR l:
LIST
OF
NWMML ← lst, l.rest
DO
nameCompare: Basics.Comparison ←
IF l.first.name=
NIL
AND item.name =
NIL
THEN Basics.CompareINT[IV[l.first.wire], IV[item.wire]]
ELSE Rope.Compare[l.first.name, item.name];
IF nameCompare = equal
AND
l.first.layer = item.layer AND
l.first.max >= item.min AND
l.first.min <= item.max
THEN {
l.first.max ← MAX[ l.first.max, item.max];
l.first.min ← MIN[ l.first.min, item.min];
RETURN[lst]};
IF nameCompare = greater
OR
nameCompare = equal AND (
l.first.layer > item.layer OR
l.first.layer = item.layer AND
l.first.min > item.min)
THEN {
temp: NWMML ← l.first;
l.rest ← CONS[item, l.rest];
l.first ← l.rest.first;
l.rest.first ← temp;
RETURN[lst]};
IF l.rest = NIL THEN {l.rest ← CONS[item, l.rest]; RETURN[lst]};
ENDLOOP};
PosSortNWMMLs:
PUBLIC
PROC[lst:
LIST
OF
NWMML] = {
DO
ok: BOOL ← TRUE;
FOR l:
LIST
OF
NWMML ← lst, l.rest
WHILE l#
NIL
AND l.rest#
NIL
DO
IF l.first.min > l.rest.first.min
OR
l.first.min = l.rest.first.min
AND l.first.max > l.rest.first.max
THEN
{temp: NWMML ← l.first; l.first ← l.rest.first; l.rest.first ← temp; ok ← FALSE}
ENDLOOP;
IF ok THEN EXIT;
ENDLOOP};
ListNWMMLs:
PUBLIC
PROC[lst:
LIST
OF
NWMML, log:
IO.
STREAM] = {
list: LIST OF NWMML;
list ← lst; lst ← NIL;
FOR list ← list, list.rest
WHILE list#
NIL
DO
lst ← CONS[list.first, lst] ENDLOOP;
list ← lst;
FOR list ← list, list.rest WHILE list#NIL DO ListNWMML[list.first, log] ENDLOOP;
log.PutChar[IO.CR] };
ListNWMML:
PUBLIC
PROC[
nwmml: NWMML, log:
IO.
STREAM] = {
layerCard: CARDINAL ← LOOPHOLE[nwmml.layer];
log.PutF["\n%g\tpos: %5g\tsize: %5g\tlayer: %g",
IO.rope[nwmml.name],
IO.int[nwmml.min],
IO.int[nwmml.max-nwmml.min],
IO.rope[
SELECT nwmml.layer
FROM
cmosMet2 => "metal2",
cmosMet => "metal",
cmosPoly => "poly",
ENDCASE => Convert.RopeFromCard[layerCard] ] ]};
ListUniqueSignals:
PUBLIC PROC[first: PWPins.Side, cell0, cell1: CellType, log:
REF ←
NIL] = {
Incrment:
PROC[table: HashTable.Table, id:
REF] = {
val: REF INT ← NARROW[HashTable.Fetch[table, id].value];
IF val=NIL THEN {val ← NEW[INT ← 0]; [] ← HashTable.Store[table, id, val]};
val^ ← val^ + 1};
nwmmls0: LIST OF NWMML ← SidePinList[cell0, CoreFrame.OppSide[first]];
nwmmls1: LIST OF NWMML ← SidePinList[cell1, first];
side0Nm: ROPE ← CoreFrame.SideRope[CoreFrame.OppSide[first]];
side1Nm: ROPE ← CoreFrame.SideRope[first];
out: IO.STREAM;
count: INT ← 0;
mark: REF ← NEW[INT ← 0];
table0: HashTable.Table ← HashTable.Create[ ];
table1: HashTable.Table ← HashTable.Create[ ];
IF log=
NIL
THEN out ← FS.StreamOpen["UniqueSignals.txt", $create]
ELSE
WITH log
SELECT
FROM
rope: ROPE => {out ← FS.StreamOpen[rope, $create]};
text: REF TEXT => {out ← FS.StreamOpen[Rope.FromRefText[text], $create]};
stm: IO.STREAM => {out ← stm};
ENDCASE => {Signal[]};
FOR temp:
LIST
OF
NWMML ← nwmmls0, temp.rest
WHILE temp#
NIL
DO
Incrment[table0, temp.first.name] ENDLOOP;
FOR temp:
LIST
OF
NWMML ← nwmmls1, temp.rest
WHILE temp#
NIL
DO
Incrment[table1, temp.first.name] ENDLOOP;
out.PutF["\nUnique signal log %g\n%10g side of %g\n%10g side of %g\n",
IO.time[],
IO.rope[side0Nm], IO.rope[CoreName.CellNm[cell0].n],
IO.rope[side1Nm], IO.rope[CoreName.CellNm[cell1].n] ];
FOR temp:
LIST
OF
NWMML ← nwmmls0, temp.rest
WHILE temp#
NIL
DO
val: REF INT ← NARROW[HashTable.Fetch[table0, temp.first.name].value];
IF val^<2
AND
NOT HashTable.Fetch[table1, temp.first.name].found
THEN
out.PutF["\n%g\tpos: %5g", IO.rope[temp.first.name], IO.int[temp.first.min]] ENDLOOP;
out.PutRope["\n******"];
FOR temp:
LIST
OF
NWMML ← nwmmls1, temp.rest
WHILE temp#
NIL
DO
val: REF INT ← NARROW[HashTable.Fetch[table1, temp.first.name].value];
IF val^<2
AND
NOT HashTable.Fetch[table0, temp.first.name].found
THEN
out.PutF["\n%g\tpos: %5g", IO.rope[temp.first.name], IO.int[temp.first.min]] ENDLOOP;
out.PutRope["\n"]; out.Close[]};
PutPin:
PUBLIC
PROC[cell:
CD.Object, size, loc:
CD.Position, level:
CD.Layer, name:
ROPE] = {
pinApl:
CD.Instance
← PW.IncludeInCell[cell, CDSymbolicObjects.CreatePin[size], loc];
CDSymbolicObjects.SetName[pinApl, name];
CDSymbolicObjects.SetLayer[pinApl, level]};
RenamePins:
PUBLIC
PROC[
object: CD.Object,
pinNameProc: CCDUtils.PinNameProc]
RETURNS[newObject: CD.Object] = {
KeepPinOnEdge: CDSymbolicObjects.InstEnumerator ~ {
newRope: ROPE;
newInst: CD.Instance;
side: PWPins.Side;
side ← PWPins.GetSide[object, inst];
IF side=none THEN RETURN;
newRope ← pinNameProc[inst, side];
IF Rope.IsEmpty[newRope] THEN RETURN;
newInst ←
NEW[
CD.InstanceRep ← [
ob: CDSymbolicObjects.CreatePin[inst.ob.size],
location: inst.location,
orientation: inst.orientation ]];
CDProperties.CopyProps[inst.properties, newInst];
CDSymbolicObjects.SetName[newInst, newRope];
newCellPtr.contents ← CONS[newInst, newCellPtr.contents] };
newCellPtr: CD.CellPtr;
inst: CD.Instance ← NEW[CD.InstanceRep ← [ob: object]];
CDProperties.PutInstanceProp[inst, $StopEnumerateDeepPins, $StopEnumerateDeepPins];
newObject ← CDCells.CreateEmptyCell[];
newCellPtr ← NARROW[newObject.specificRef];
[] ← PWPins.EnumerateDeepPins[object, KeepPinOnEdge];
newCellPtr.contents ← CONS[inst, newCellPtr.contents];
CDCells.SetInterestRect[newObject, CD.InterestRect[object]];
RETURN[newObject] };
cmos: PUBLIC CD.Technology = CMosB.cmosB;
cmosNDif: PUBLIC CD.Layer ← CMosB.ndif;
cmosPDif: PUBLIC CD.Layer ← CMosB.pdif;
cmosWPDif: PUBLIC CD.Layer ← CMosB.wpdif;
cmosWNDif: PUBLIC CD.Layer ← CMosB.wndif;
cmosPoly: PUBLIC CD.Layer ← CMosB.pol;
cmosMet: PUBLIC CD.Layer ← CMosB.met;
cmosMet2: PUBLIC CD.Layer ← CMosB.met2;
cmosPWCont: PUBLIC CD.Layer ← CMosB.pwellCont;
cmosNWCont: PUBLIC CD.Layer ← CMosB.nwellCont;
cmosNWell: PUBLIC CD.Layer ← CMosB.nwell;
lambda: PUBLIC INT ← CMosB.lambda;
pwrW: PUBLIC INT ← 5*lambda;
metW: PUBLIC INT ← 3*lambda;
met2W: PUBLIC INT ← 4*lambda;
polW: PUBLIC INT ← 2*lambda;
difW: PUBLIC INT ← 2*lambda;
topTail: PUBLIC INT ← 2*lambda;
leftTail: PUBLIC INT ← 6*lambda; -- to center of 0th channel
rightTail: PUBLIC INT ← 2*lambda; -- metPitch-leftTail
botTail: PUBLIC INT ← 1*lambda;
cnctSize: PUBLIC INT ← 4*lambda;
metPitch: PUBLIC INT ← 8*lambda;
met2Pitch: PUBLIC INT ← 8*lambda;
polMuxPitch: PUBLIC INT ← 8*lambda;
polPitch: PUBLIC INT ← 6*lambda; -- without adjacent contacts
Contact:
PUBLIC
PROC[layer:
CD.Layer]
RETURNS[cntc:
CD.Object] = {
RETURN[
SELECT layer
FROM
cmosMet2, cmosPoly, cmosNDif, cmosPDif, cmosWPDif, cmosWNDif =>
CDSimpleRules.Contact[cmosMet, layer],
cmosPWCont, cmosNWCont =>
CMosBObjects.CreateDifCon[layer],
ENDCASE => ERROR]};
TransistorObject:
PUBLIC
PROC[size:
CD.Position, difLayer:
CD.Layer]
RETURNS[trans:
CD.Object]=
{trans ← CMosBObjects.CreateTransistor[size: size, difLayer: difLayer]};
textCache: CD.Design ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
ScaledText:
PUBLIC
PROC[text:
ROPE, box:
CD.Position, layer:
CD.Layer, design: CD.Design←NIL]
RETURNS[cell: CD.Object ← NIL] = {
MakeName:
PROC[fontIndex:
INT]
RETURNS[name:
ROPE] = {
name ← IO.PutFR["Label%g-%g[%g]", IO.int[fontIndex], IO.int[scaleFactor], IO.rope[text]]};
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;
IF design=NIL THEN design ← textCache;
IF text.Length[]=0 THEN text ← "<no name>";
scaleFactor ← Scale[font2];
IF scaleFactor>0
THEN {
cell ← CDDirectory.Fetch[design, MakeName[2]].object;
IF cell=
NIL
THEN {
cell ← CDCreateLabels.CreateTextCell[design, text, font2, scaleFactor, layer];
[] ← CDDirectory.Include[design, cell, MakeName[2]];
RETURN[cell]} };
scaleFactor ← Scale[font1];
IF scaleFactor>0
THEN {
cell ← CDDirectory.Fetch[design, MakeName[1]].object;
IF cell=
NIL
THEN {
cell ← CDCreateLabels.CreateTextCell[design, text, font1, scaleFactor, layer];
[] ← CDDirectory.Include[design, cell, MakeName[2]];
RETURN[cell]} };
RETURN[NIL] };
ShowModule:
PUBLIC
PROC[name:
ROPE, design:
CD.Design ←
NIL] = {
frameCT: CellType;
new: BOOL ← design=NIL;
mainObj, decoratedObj: CD.Object;
IF new
THEN {
design ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
CDSimpleOps.RenameDesign[design, name]};
frameCT ← CoreFrame.ReadFrameCache[name];
IF frameCT=NIL THEN Signal[];
IF frameCT=NIL THEN RETURN;
mainObj ← CCDUtils.Layout[frameCT];
[] ← CDDirectory.Include[design, mainObj, name];
decoratedObj ← OrnateFrame[frameCT, design];
[] ← CDDirectory.Include[design, decoratedObj, Rope.Cat["Ornate", name]];
IF new THEN CDOps.IncludeObjectI[design, decoratedObj, [0, 0]];
CDCleanUp.CleanUp[ design, NIL];
[ ] ← CDViewer.CreateViewer[design]};
InsertModule: PUBLIC PROC
[cell: CellType, name: ROPE ← NIL, design: CD.Design] = {
new: BOOL ← design=NIL;
mainObj, decoratedObj: CD.Object;
name ← CoreName.CellNm[cell, name].n;
IF new THEN {
design ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
CDSimpleOps.RenameDesign[design, name]};
CoreFrame.Expand[hard, cell];
CoreFrame.NameFrame[cell, name];
[ ] ← CoreGlue.RouteHard[cell];
mainObj ← CoreFrame.Layout[cell];
[] ← CDDirectory.Include[design, mainObj, name];
decoratedObj ← OrnateFrame[cell, design];
[] ← CDDirectory.Include[design, decoratedObj, Rope.Cat["Ornate", name]];
IF new THEN CDOps.IncludeObjectI[design, decoratedObj, [0, 0]];
CDCleanUp.CleanUp[ design, NIL];
[ ] ← CDViewer.CreateViewer[design];
[ ] ← CDIO.WriteDesign[design, design.name] };
InsertModule: PUBLIC PROC[cell:CellType, name: ROPE ← NIL, design: CD.Design ←NIL]={
name ← CoreName.CellNm[cell, name].n;
CoreFrame.Expand[hard, cell];
CoreFrame.NameFrame[cell, name];
[ ] ← CoreGlue.RouteHard[cell];
CoreFrame.WriteFrameCheckPoint[cell]};
IF metW # CDSimpleRules.MinWidth[cmosMet] THEN ERROR;
IF met2W # CDSimpleRules.MinWidth[cmosMet2] THEN ERROR;
IF polW # CDSimpleRules.MinWidth[cmosPoly] THEN ERROR;
IF difW # CDSimpleRules.MinWidth[cmosNDif] THEN ERROR;
Sinix.RegisterExtractProc[$CCDUtilsBlankCell, BlankExtractProc];
END.