LogicUtilsImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Louis Monier January 12, 1987 2:25:41 pm PST
Barth, October 22, 1986 11:53:33 am PDT
Bertrand Serlet October 18, 1986 0:01:28 am PDT
DIRECTORY CD, CDMenus, CDSequencer, Core, CoreClasses, CoreCreate, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, HashTable, IO, Logic, LogicUtils, Ports, PW, PWCore, RopeList, Rosemary, RosemaryUser, SinixOps, Sisyph, TerminalIO;
LogicUtilsImpl: CEDAR PROGRAM
IMPORTS CDMenus, CoreClasses, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, HashTable, IO, Logic, Ports, PW, PWCore, RopeList, Rosemary, RosemaryUser, SinixOps, Sisyph, TerminalIO
EXPORTS LogicUtils
= BEGIN OPEN Logic, LogicUtils, CoreCreate;
New Class
libCellClass: PUBLIC Core.CellClass ← CoreIO.RegisterClass[
CoreOps.SetClassPrintProc[NEW [Core.CellClassRec ← [
name: "LibCell",
recast: RecastLibCell,
layersProps: FALSE]], PrintLibCell], WriteLib, ReadLib];
WriteLib: CoreIO.ClassWriteProc = {
data: LibCellRef ← NARROW [cellType.data];
CoreIO.WriteRope[h, data.libName];
CoreIO.WriteRope[h, data.ctName];
};
ReadLib: CoreIO.ClassReadProc = {
data: LibCellRef ← NEW [LibCellRec];
data.libName ← CoreIO.ReadRope[h];
data.ctName ← CoreIO.ReadRope[h];
cellType.data ← data;
};
CreateLibCell: PUBLIC PROC [public: Wire, ctName, libName, name: ROPENIL, props: Properties ← NIL] RETURNS [ct: CellType] = {
ct ← CoreOps.CreateCellType[
class: libCellClass,
public: public,
data: NEW[LibCellRec ← [libName, ctName]],
name: name,
props: props];
};
RecastLibCell: Core.RecastProc = { -- check public!!!!
scRef: LibCellRef ← NARROW [me.data];
lib: CoreDirectory.Library ← CoreDirectory.FetchLibrary[scRef.libName];
schCell: CellType ← CoreDirectory.Fetch[library: lib, key: scRef.ctName];
IF schCell=NIL THEN Error[IO.PutFR["CellType %g not in library %g", IO.rope[scRef.ctName], IO.rope[scRef.libName]]];
new ← CoreClasses.CreatePermutedRecordCell[
iconPublic: me.public,
schCell: schCell,
table: CreateIdentityTable[me.public, schCell.public],
name: CoreOps.GetCellTypeName[me]];
PWCore.SetAbutX[new];
};
CreateIdentityTable: PROC [iconPublic, schPublic: Wire] RETURNS [table: HashTable.Table] ~ {
FindWire: PROC [iconPublic: Wire, name: ROPE] RETURNS [iconWire: Wire ← NIL] ~ {
n: INT ← CoreOps.GetWireIndex[iconPublic, name];
IF n>=0 THEN RETURN[iconPublic[n]];
};
AddInTable: CoreOps.EachWireProc ~ {
name: ROPE ← CoreOps.GetShortWireName[wire];
iconWire: Wire;
IF name=NIL THEN RETURN;
iconWire ← FindWire[iconPublic, name];
IF iconWire=NIL THEN RETURN;
[] ← HashTable.Store[table, wire, iconWire];
};
table ← HashTable.Create[];
[] ← CoreOps.VisitWire[schPublic, AddInTable];
};
PrintLibCell: CoreOps.PrintClassProc = {
scRef: LibCellRef ← NARROW [data];
CoreOps.PrintIndent[indent, out];
out.PutF["Cell '%g' from library '%g'\n", IO.rope[scRef.ctName], IO.rope[scRef.libName]];
};
Utilities
-- This property identify a leaf for the placer during layout
scCutSet: ATOM ← $SCPlacableElement;   -- cell from the library or SCBlock[]
-- The default library name (in the sense of CoreDirectory)
defaultLibName: ROPE = "CMOSB";  -- the name of the collection of cellTypes
-- The icons resides in "Logic.dale"
schDesignName: ROPE = "Logic";  -- the name of the ChipNDale file
schDesign: CD.Design ← PW.OpenDesign[schDesignName];
cx: PUBLIC Sisyph.Context ← Sisyph.Create[schDesign];
-- A table which guarantees that basic cells are created only once
scTable: HashTable.Table ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
-- The generic error
Error: PUBLIC PROC [msg: ROPE] ~ {TerminalIO.WriteRopes[msg, "\n"]; ERROR};
-- Detects an X in a level sequence
HasX: PUBLIC PROC [ls: Ports.LevelSequence] RETURNS [BOOLFALSE] ~ {
FOR i: NAT IN [0..ls.size) DO IF ls[i]=X THEN RETURN[TRUE] ENDLOOP};
-- Extracts by name from the Logic design
globalSwitchHackForSCBlockON: BOOLFALSE;
Extract: PUBLIC PROC [schName: ROPE, makeBlock: BOOLFALSE] RETURNS [ct: CellType] ~ {
ct ← Sisyph.ES[schName, cx];
IF makeBlock AND globalSwitchHackForSCBlockON THEN ct ← SCBlock[ct];
};
-- Creates a standard cell using the new class
MakeSC: PUBLIC PROC [nameInLib, name: ROPE, public: Wire, width: INT ← 0] RETURNS [ct: CellType] ~ {
found: BOOL;
ref: REF;
[found, ref] ← HashTable.Fetch[scTable, nameInLib];
IF found THEN RETURN[NARROW[ref]] -- already in the table
ELSE {
ct ← CreateLibCell[name: name,
public: public,
ctName: nameInLib,
libName: defaultLibName]; -- a global
ct ← Rosemary.BindCellType[ct, name];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet];
CoreProperties.PutCellTypeProp[on: ct, prop: scCutSet, value: $T];
CoreProperties.PutCellTypeProp[on: ct, prop: cellWidthProp, value: NEW[INT ← width]];
[] ← HashTable.Store[scTable, nameInLib, ct];
};
};
-- Given a cellType ct, this procedure produces an equivalent cellType shell in the following way: ct is flattened until the leaves are standard cells (property $SCPlacableElement); the standard cells are then assembled in a single cellType called abut with no internal connection, with the exception of Vdd and Gnd; the abut cellType will be layed out as an abutX of standard cells and will be treated by the placer as a single element. The cellType shell has a single instance of abut and wires up all the cvonnections present in the original ct.
-- The idea is to reduce the number of placeable elements seen by the placer and thus reduce the placement time. This procedure should be used only on small-size cells (less than 10 standard cells) as the placer does not like to deal with objects of too different sizes.
scBlockHackOff: BOOLFALSE;  -- a temporary hack
SCBlock: PUBLIC PROC [ct: CellType] RETURNS [shell: CellType] ~ {
IsLeaf: PROC [cell: CellType] RETURNS [BOOL] ~ {
RETURN[CoreProperties.GetCellTypeProp[cell, $SCPlacableElement]#NIL]
};
MakeLeaf: PROC [cell: CellType] ~ {
CoreProperties.PutCellTypeProp[cell, $SCPlacableElement, $T]
};
FlattenCell: CoreFlat.BoundFlatCellProc = {
IF ~IsLeaf[cell] THEN { -- Handle nonleaf cells here.
CoreFlat.NextBoundCellType[cell, target, flatCell, instance, parent, flatParent, NIL, bindings, FlattenCell]
}
ELSE { -- Handle leaf cells here.
MakeActual: PROC [public: Wire] RETURNS [actual: Wire] ~ {
-- if root is a leaf, bindings=NIL
canonizedPublic: CoreFlat.FlatWire ← NARROW[HashTable.Fetch[bindings, public].value];
actual ← NARROW[HashTable.Fetch[table, canonizedPublic].value];
IF actual=NIL THEN {
IF public.size=0 THEN actual ← CoreOps.CreateWire[]
ELSE {
actual ← CoreOps.CreateWires[size: public.size];
FOR i: NAT IN [0..public.size) DO
actual[i] ← MakeActual[public[i]];
ENDLOOP;
};
[] ← HashTable.Store[table, canonizedPublic, actual];
};
};
abutInstanceActual: Wire ← CoreOps.CopyWire[cell.public];
shellInstanceActual: Wire ← CoreOps.CreateWires[size: cell.public.size];
FOR i: NAT IN [0..cell.public.size) DO
shellInstanceActual[i] ← MakeActual[cell.public[i]];
ENDLOOP;
abutInstances ← CONS[CoreClasses.CreateInstance[actual: abutInstanceActual, type: cell], abutInstances];
abutPublics ← CONS[abutInstanceActual, abutPublics];
shellInternals ← CONS[shellInstanceActual, shellInternals];
};
};
StorePublic: CoreOps.EachWirePairProc ~ {
[] ← HashTable.Store[table, NEW[CoreFlat.FlatWireRec ← [wire: publicWire]], actualWire];
};
abut: Core.CellType;
abutInstances: CoreClasses.CellInstances ← NIL;
abutPublics, shellInternals: Core.Wires ← NIL;
abutPublic, shellInternal, shellPublic: Core.Wire ← NIL;
-- maps old CoreFlat wires to new shell internal Core wires
table: HashTable.Table ← HashTable.Create[equal: CoreFlat.FlatWireEqual, hash: CoreFlat.FlatWireHash];
IF scBlockHackOff THEN RETURN[ct];  -- a temporary hack
shellPublic ← CoreOps.CopyWire[ct.public];
IF CoreOps.VisitBindingSeq[actual: shellPublic, public: ct.public, eachWirePair: StorePublic] THEN ERROR;
FlattenCell[ct];
abutPublic ← CoreOps.CreateWire[abutPublics];
shellInternal ← CoreOps.CreateWire[shellInternals];
abut ← CoreClasses.CreateRecordCell[
public: abutPublic,
internal: abutPublic,
instances: abutInstances];
shell ← CoreClasses.CreateRecordCell[
public: shellPublic,
internal: CoreOps.UnionWire[shellInternal, shellPublic],
instances: LIST[CoreClasses.CreateInstance[actual: shellInternal, type: abut]]];
PWCore.SetAbutX[abut];
MakeLeaf[abut];
};
LogicTest: RosemaryUser.TestProc ~ {
logicTime: NAT ← Ports.PortIndex[cellType.public, "RosemaryLogicTime"];
p[logicTime].b ← TRUE;
p[logicTime].d ← drive;
DO
p[logicTime].b ← NOT p[logicTime].b;
Eval[];
ENDLOOP;
};
ExtractSelectedObjAndRunRosemary: PROC [comm: CDSequencer.Command] = {
globalNames: Sisyph.ROPES ← Sisyph.GetGlobalNames[Sisyph.Create[comm.design]];
-- a wire is innocent until proven guilty
UnnamedOrGlobal: PROC [wire: Wire] RETURNS [BOOL] ~ {
name: ROPE = CoreOps.GetShortWireName[wire];
RETURN [name=NIL OR RopeList.Memb[globalNames, name]];
};
NotBus: PROC [wire: Wire] RETURNS [BOOL] ~ {
FOR i: NAT IN [0..wire.size) DO -- Skipped if wire is atomic
IF wire.elements[i].size#0 THEN RETURN[TRUE];
ENDLOOP;
RETURN [FALSE];
};
WorthGraphing: CoreOps.EachWireProc ~ {
SELECT TRUE FROM
UnnamedOrGlobal[wire] => RETURN [subWires: FALSE]; -- don't even consider sons
NotBus[wire] => RETURN [subWires: TRUE]; -- will have interesting sons
ENDCASE => graphWires ← CONS[NEW [CoreFlat.FlatWireRec ← [wire: wire]], graphWires];
};
AtomicsInGraph: PROC [wire: Wire] ~ {
graphWires ← CONS[NEW[CoreFlat.FlatWireRec ← [flatCell: CoreFlat.rootCellType, wire: wire]], graphWires];
};
logicVdd, logicGnd: NAT;
graphWires: CoreFlat.FlatWires ← NIL;
root, cellType: CellType;
internal: Core.WireSeq;
[root: root, cell: cellType] ← SinixOps.SelectedCellType[comm.design, Sisyph.mode];
IF root=NIL THEN RETURN; -- Extraction ended in error, message already printed
-- Let's make sure we have something to simulate
IF cellType.class#CoreClasses.recordCellClass THEN Error["I can't simulate this thing"];
-- Find out which wires to display: top-level only
internal ← NARROW[cellType.data, CoreClasses.RecordCellType].internal;
[] ← CoreOps.VisitWireSeq[internal, WorthGraphing];
-- Prepare the cell for simulation
logicVdd ← Ports.PortIndex[cellType.public, "Vdd"];
logicGnd ← Ports.PortIndex[cellType.public, "Gnd"];
[] ← Rosemary.SetFixedWire[cellType.public[logicVdd], H];
[] ← Rosemary.SetFixedWire[cellType.public[logicGnd], L];
[] ← RosemaryUser.TestProcedureViewer[
cellType: cellType,
testButtons: LIST["Logic Test"],
name: CoreOps.GetCellTypeName[cellType],
displayWires: graphWires,
graphWires: graphWires,
cutSet: CoreFlat.CreateCutSet[labels: IF CoreProperties.GetCellTypeProp[from: cellType, prop: $Simulation]=$Fast THEN LIST[macroCutSet, logicCutSet] ELSE LIST[logicCutSet]]];
};
cellWidthProp: PUBLIC ATOM ← $SCCellWidth;
standardSCHeight: INT = 104; -- hack for cmosb library only
SizeRef: TYPE = REF SizeRec;
SizeRec: TYPE = RECORD[totalWidth, nb: INT ← 0];
Size: PROC [cell: CellType, table: HashTable.Table] RETURNS [size, nbCells: INT ← 0] ~ {
GetSize: PROC [cell: CellType] RETURNS [width: INT ← 0] ~ {
val: REF ← CoreProperties.GetCellTypeProp[cell, cellWidthProp];
RETURN[IF val=NIL THEN 0 ELSE NARROW[val, REF INT]^];
};
width: INT ← GetSize[cell];
IF width#0 THEN {
ref: REF;
item: SizeRef;
found: BOOL;
name: ROPE ← CoreOps.GetCellTypeName[cell];
[found, ref] ← HashTable.Fetch[table, name];
IF found THEN {
item ← NARROW[ref];
item.totalWidth ← item.totalWidth+width;
item.nb ← item.nb+1}
ELSE item ← NEW[SizeRec ← [totalWidth: width, nb: 1]];
[] ← HashTable.Store[table, name, item];
RETURN[width, 1];
};
SELECT TRUE FROM
cell.class=CoreClasses.recordCellClass => {
rct: CoreClasses.RecordCellType ← NARROW[cell.data];
FOR i: NAT IN [0..rct.size) DO
subSize, subNb: INT ← 0;
[subSize, subNb] ← Size[rct[i].type, table];
size ← size+subSize;
nbCells ← nbCells+subNb;
ENDLOOP;
};
cell.class.recast=NIL => RETURN[0, 0];  -- all other atomic classes are ignored
ENDCASE => RETURN Size[CoreOps.Recast[cell], table]; -- if unknow and not atomic, recast!
};
ExtractAndMeasure: PROC [comm: CDSequencer.Command] = {
width, size, nbCells: INT ← 0;
table: HashTable.Table ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
root, cellType: CellType;
[root: root, cell: cellType] ← SinixOps.SelectedCellType[comm.design, Sisyph.mode];
IF root=NIL THEN RETURN; -- Extraction ended in error, message already printed
PW.WriteF["\nEstimating size of %g (excluding the routing).\n", IO.rope[CoreOps.GetCellTypeName[cellType]]];
[width, nbCells] ← Size[cellType, table];
PW.WriteF["\nThe cell has %g placable elements\n", IO.int[nbCells]];
size ← width*10*standardSCHeight;
PW.WriteF["Size is %g sq microns = %g sq mm.\n", IO.int[size], IO.real[REAL[size]/1000000]];
WHILE HashTable.GetSize[table]>0 DO-- the lazziest way to sort 20 entries
SelectLargestItem: HashTable.EachPairAction ~ {
item: SizeRef ← NARROW[value];
name: ROPENARROW[key];
IF item.totalWidth > maxSize THEN {largest ← key; maxSize ← item.totalWidth};
};
PrintItem: PROC [name: ROPE, item: SizeRef] ~ {
PW.WriteF["%g %g => %5.1f%% of total area.\n", IO.int[item.nb], IO.rope[name], IO.real[100*REAL[item.totalWidth]/width]];
};
largest, value: REF;
maxSize: NAT ← 0;
[] ← HashTable.Pairs[table: table, action: SelectLargestItem];
value ← HashTable.Fetch[table, largest].value;
PrintItem[NARROW[largest], NARROW[value]];
[] ← HashTable.Delete[table: table, key: largest];
ENDLOOP;
};
-- Entry goes in Sisyph Menu
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Sisyph Extract and Rosemary", p: ExtractSelectedObjAndRunRosemary, key: $CoreRosemaryExtractSelectedObjAndRunRosemary];
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Sisyph Extract and Measure", p: ExtractAndMeasure, key: $ExtractSelectedObjAndMeasure];
RosemaryUser.RegisterTestProc["Logic Test", LogicTest];
END.