LogicUtilsImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Louis Monier September 30, 1987 7:02:25 pm PDT
Jean-Marc Frailong December 8, 1987 11:42:33 pm PST
Barth, May 4, 1987 2:59:41 pm PDT
Bertrand Serlet June 10, 1987 4:44:26 pm PDT
DIRECTORY CD, CDDesignCache, CDIO, CDOps, CDSequencer, CDSequencerExtras, Core, CoreClasses, CoreCreate, CoreCDUser, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, IO, Logic, LogicUtils, Ports, PWCore, RefTab, Rope, Rosemary, Sisyph, SymTab, TerminalIO;
LogicUtilsImpl: CEDAR PROGRAM
IMPORTS CDDesignCache, CDIO, CDOps, CDSequencerExtras, CoreClasses, CoreCDUser, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, IO, Logic, Ports, PWCore, RefTab, Rope, Rosemary, Sisyph, SymTab, TerminalIO
EXPORTS LogicUtils
= BEGIN OPEN Logic, LogicUtils, CoreCreate;
Global variables
defaultLibName: ROPE ← "CMOSB"; -- CoreLib for the basic cells (c.f. MakeSC)
schDesignName: ROPE ← "Logic"; -- ChipNDale/CoreLib file names
disableSCBlock: BOOLTRUE; -- Say FALSE to enable atomic placement of composite cells. This is not really guaranteed to work...
raiseObsoleteSignal: BOOLFALSE; -- Say TRUE to guarantee that obsolete macros cannot be extracted
cx: Sisyph.Context ← NIL; -- the context from which Extract/GetLogicContext work
logicCache: CoreDirectory.Library ← NIL; -- logicCache starts disabled
Cache
CacheFetch: PUBLIC PROC [name: ROPE] RETURNS [CellType] ~ {
Fetch an entry from the Logic cache
IF logicCache=NIL THEN RETURN[NIL]; -- cache has been disabled
RETURN [NARROW [SymTab.Fetch[logicCache, name].val]];
};
CacheStore: PUBLIC PROC [name: ROPE, ct: CellType] ~ {
Set logging information & store into cache if enabled
IF CoreProperties.GetCellTypeProp[ct, $LogForStats]=NIL THEN CoreProperties.PutCellTypeProp[ct, $LogForStats, name];
IF logicCache=NIL THEN RETURN;
IF ~SymTab.Insert[logicCache, name, ct] THEN ERROR; -- This should never happen!!!
};
Reset: PUBLIC PROC [reloadDesign: BOOLFALSE, cache: cacheAction ← leave] ~ {
Initialize internal state of LogicUtils for all the cached information
IF reloadDesign THEN {
RecreateContext[];
IF logicCache#NIL THEN logicCache ← CoreDirectory.CreateLibrary[]; -- erase the cache
};
SELECT cache FROM
reload => { -- get from the CoreLib file if it is recent enough, else clear it
Should enfore dates using Loader.BCDBuildTime[proc]
logicCache ← CoreIO.RestoreLibrary[schDesignName];
IF logicCache=NIL THEN Error["Unable to read Logic core file"];
};
erase => logicCache ← CoreDirectory.CreateLibrary[]; -- erase the cache
disable => logicCache ← NIL; -- disable the cache
ENDCASE => NULL; -- don't touch the cache
};
Accessing basic cells (with associated layout)
LibraryGet: PUBLIC PROC [lib, cell: ROPE] RETURNS [CellType ← NIL] = {
Fetch a cell type from a library, reload the library if not present
library: CoreDirectory.Library ← CoreDirectory.FetchLibrary[lib];
IF library=NIL THEN {
library ← CoreIO.RestoreLibrary[lib];
IF library=NIL THEN {
TerminalIO.PutF["*** Error while retrieving %g in library %g: library does not exist.\n", IO.rope[cell], IO.rope[lib]];
RETURN;
};
[] ← CoreDirectory.RegisterLibrary[library, lib];
};
RETURN [NARROW [SymTab.Fetch[library, cell].val]];
};
MakeSC: PUBLIC PROC [nameInLib: ROPE] RETURNS [ct: CellType] ~ {
Fetch the cell from the basic cell library
ct ← LibraryGet[defaultLibName, nameInLib];
};
Extraction utilities
RecreateContext: PROC [fileName: ROPENIL] ~ {
Reload the Logic design (from cache if possible) and rebuild the context.
This is a bit clumsy because CDDesignCache has strange semantics.
design: CD.Design;
design ← CDDesignCache.Search[remoteName: schDesignName, fileName: fileName];
IF design=NIL THEN {
IF fileName=NIL THEN fileName ← CDDesignCache.MakeUpFile[NIL, schDesignName];
design ← CDIO.ReadDesign[fileName, NIL, CDIO.GetWorkingDirectory[]];
CDOps.SetMutability[design, readonly];
};
cx ← Sisyph.Create[design];
};
GetLogicContext: PUBLIC PROC RETURNS [Sisyph.Context] ~ {
IF cx=NIL THEN RecreateContext[];
RETURN [Sisyph.Copy[cx]];
};
Extract: PUBLIC PROC [schName: ROPE, parms: LIST OF Value ← NIL] RETURNS [ct: CellType] ~ {
Extracts from the Logic library with the given (INT) parameters.
tmpCx: Sisyph.Context;
IF cx=NIL THEN RecreateContext[];
tmpCx ← IF parms=NIL THEN cx ELSE Sisyph.Copy[cx];
WHILE parms#NIL DO
Sisyph.Store[tmpCx, parms.first.name, NEW[INT ← parms.first.val]];
parms ← parms.rest;
ENDLOOP;
ct ← Sisyph.ES[schName, tmpCx];
};
SCBlock: PUBLIC PROC [ct: CellType] RETURNS [shell: CellType] ~ {
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.
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, index, 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[RefTab.Fetch[bindings, public].val];
actual ← NARROW[RefTab.Fetch[table, canonizedPublic].val];
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;
};
[] ← RefTab.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 ~ {
[] ← RefTab.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: RefTab.Ref ← RefTab.Create[equal: CoreFlat.FlatWireEqual, hash: CoreFlat.FlatWireHash];
IF disableSCBlock 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];
};
Rosemary sugar
RoseClass: PUBLIC PROC [name: ROPE, init: Rosemary.InitProc ← NIL, evalSimple: Rosemary.EvalProc ← NIL, scheduleIfClockEval: BOOLFALSE, copy: Rosemary.StateCopyProc ← NIL] RETURNS [roseClassName: ROPE] ~ {
roseClassName ← Rope.Cat["Logic", name];
[] ← Rosemary.Register[roseClassName, init, evalSimple, copy, scheduleIfClockEval];
};
InitPower: PROC [ct: CellType, name: ROPE] ~ {
Init power supply to (l, none) if it is present: Ports.InitPorts would scream here...
index: INT ← CoreOps.GetWireIndex[ct.public, name];
IF index>=0 THEN [] ← Ports.InitPort[wire: ct.public[index], levelType: l, initDrive: none];
};
SimulateGate: PUBLIC PROC [ct: CellType, roseClassName: ROPE] ~ {
[] ← CoreFlat.CellTypeCutLabels[Rosemary.BindCellType[ct, roseClassName], logicCutSet];
InitPower[ct, "Vdd"]; InitPower[ct, "Gnd"];
};
SimulateMacro: PUBLIC PROC [ct: CellType, roseClassName: ROPE] ~ {
[] ← CoreFlat.CellTypeCutLabels[Rosemary.BindCellType[ct, roseClassName], macroCutSet];
InitPower[ct, "Vdd"]; InitPower[ct, "Gnd"];
};
Error management
Error: PUBLIC PROC [msg: ROPE] ~ {
Print a message and raise an error
TerminalIO.PutF["%l*** %g ***%l\n", IO.rope["b"], IO.rope[msg], IO.rope["B"]];
ERROR;
};
ObsoleteMacro: SIGNAL [msg: ROPE] ~ CODE;
Obsolete: PUBLIC PROC [msg: ROPE] ~ {
Print a message and optionally raise a signal
TerminalIO.PutF["%l*** %g ***%l\n", IO.rope["b"], IO.rope[msg], IO.rope["B"]];
IF raiseObsoleteSignal THEN SIGNAL ObsoleteMacro[msg];
};
Gathering Statistics
SizeRef: TYPE = REF SizeRec;
SizeRec: TYPE = RECORD[totalArea, nb: INT ← 0];
SumThisIntProp: PROC [cell: CellType, prop: ATOM] RETURNS [sum: INT] ~ {
val: REF ← CoreProperties.GetCellTypeProp[cell, prop];
sum ← IF val=NIL THEN 0 ELSE NARROW[val, REF INT]^;
IF sum=0 THEN
SELECT TRUE FROM
cell.class=CoreClasses.recordCellClass => {
rct: CoreClasses.RecordCellType ← NARROW[cell.data];
FOR i: NAT IN [0..rct.size) DO
sum ← sum+SumThisIntProp[rct[i].type, prop];
ENDLOOP;
};
cell.class.recast=NIL => RETURN[0]; -- all other atomic classes are ignored
ENDCASE => RETURN SumThisIntProp[CoreOps.Recast[cell], prop]; -- recast!
};
SortAndPrint: PROC [table: SymTab.Ref, area: INT] ~ {
WHILE SymTab.GetSize[table]>0 DO-- the laziest way to sort a few entries
largest: ROPE;
value: REF;
maxSize: INT ← 0;
SelectLargestItem: SymTab.EachPairAction ~ {
item: SizeRef ← NARROW[val];
name: ROPENARROW[key];
IF item.totalArea > maxSize THEN {largest ← key; maxSize ← item.totalArea};
};
PrintItem: PROC [name: ROPE, item: SizeRef] ~ {
TerminalIO.PutF["%g %g => %5.1f%% of total area.\n", IO.int[item.nb], IO.rope[name], IO.real[100*REAL[item.totalArea]/area]];
};
[] ← SymTab.Pairs[table, SelectLargestItem];
value ← SymTab.Fetch[table, largest].val;
PrintItem[NARROW[largest], NARROW[value]];
[] ← SymTab.Delete[table, largest];
ENDLOOP;
};
GetAreaProc: TYPE ~ PROC [cell: CellType] RETURNS [area: INT, key: ROPE];
Leaf: GetAreaProc ~ {
val: REF ← CoreProperties.GetCellTypeProp[cell, $CellArea];
area ← IF val=NIL THEN 0 ELSE NARROW[val, REF INT]^;
key ← CoreOps.GetCellTypeName[cell];
};
Macro: GetAreaProc ~ {
val: REF ← CoreProperties.GetCellTypeProp[cell, $LogForStats];
area ← IF val=NIL THEN 0 ELSE SumThisIntProp[cell, $CellArea];
key ← IF val=NIL THEN NIL ELSE NARROW[val];
};
HistogramIntProp: PROC [cell: CellType, proc: GetAreaProc, table: SymTab.Ref] RETURNS [area, nbCells: INT ← 0] ~ {
key: ROPE;
[area, key] ← proc[cell]; -- total area
IF area#0 THEN {
ref: REF;
item: SizeRef;
found: BOOL;
[found, ref] ← SymTab.Fetch[table, key];
IF found THEN {
item ← NARROW[ref];
item.totalArea ← item.totalArea+area;
item.nb ← item.nb+1}
ELSE item ← NEW[SizeRec ← [totalArea: area, nb: 1]];
[] ← SymTab.Store[table, key, item];
RETURN[area, 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] ← HistogramIntProp[rct[i].type, proc, table];
area ← area+subSize;
nbCells ← nbCells+subNb;
ENDLOOP;
};
cell.class.recast=NIL => RETURN[0, 0]; -- all other atomic classes are ignored
ENDCASE => RETURN HistogramIntProp[CoreOps.Recast[cell], proc, table]; -- recast
};
ExtractAndMeasure: PROC [comm: CDSequencer.Command] = {
Lists the celltypes recognized as macros from Logic, with their arguments.
DoOne: CoreCDUser.EachRootCellTypeProc ~ {
leafTable: SymTab.Ref ← SymTab.Create[]; -- "xnor2" -> [totalArea, nb]
macroTable: SymTab.Ref ← SymTab.Create[]; -- "MuxDN n=3 b=8" -> [totalArea, nb]
area, nbCells: INT ← 0;
-- Statistics on leaf cells
-- The area of a standard cell is stored under the property $CellArea on the cellType;
-- The area is expressed in square CD units; currently, divide by 64 to be in square microns
TerminalIO.PutF["\nEstimating size of %g (excluding the routing):\n", IO.rope[CoreOps.GetCellTypeName[root]]];
[area, nbCells] ← HistogramIntProp[root, Leaf, leafTable];
TerminalIO.PutF["\nThe cell has %g placable elements\n", IO.int[nbCells]];
TerminalIO.PutF["Size is %g sq microns = %g sq mm.\n", IO.int[area/64], IO.real[REAL[area]/64000000]];
SortAndPrint[leafTable, area];
-- Celltypes recognized as macros from Logic, with their arguments.
TerminalIO.PutF["\nStatistics on %g:\n", IO.rope[CoreOps.GetCellTypeName[root]]];
[area, nbCells] ← HistogramIntProp[root, Macro, macroTable];
SortAndPrint[macroTable, area];
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[comm.design, DoOne];
};
Initialization
Reset[reloadDesign: FALSE, cache: erase];
-- Entries goes in SPACE-F Menu, Layout submenu
CDSequencerExtras.RegisterCommand[key: $LayoutMeasure, proc: ExtractAndMeasure, queue: doQueue];
END.