<> <> <> <> <> <> <<>> 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; <> defaultLibName: ROPE _ "CMOSB"; -- CoreLib for the basic cells (c.f. MakeSC) schDesignName: ROPE _ "Logic"; -- ChipNDale/CoreLib file names disableSCBlock: BOOL _ TRUE; -- Say FALSE to enable atomic placement of composite cells. This is not really guaranteed to work... raiseObsoleteSignal: BOOL _ FALSE; -- 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 <> CacheFetch: PUBLIC PROC [name: ROPE] RETURNS [CellType] ~ { <> IF logicCache=NIL THEN RETURN[NIL]; -- cache has been disabled RETURN [NARROW [SymTab.Fetch[logicCache, name].val]]; }; CacheStore: PUBLIC PROC [name: ROPE, ct: CellType] ~ { <> 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: BOOL _ FALSE, cache: cacheAction _ leave] ~ { <> 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 <> 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 }; <> LibraryGet: PUBLIC PROC [lib, cell: ROPE] RETURNS [CellType _ NIL] = { <> 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] ~ { <> ct _ LibraryGet[defaultLibName, nameInLib]; }; <> RecreateContext: PROC [fileName: ROPE _ NIL] ~ { <> <> 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] ~ { <> 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] ~ { <> <> 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]; }; <> RoseClass: PUBLIC PROC [name: ROPE, init: Rosemary.InitProc _ NIL, evalSimple: Rosemary.EvalProc _ NIL, scheduleIfClockEval: BOOL _ FALSE, 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] ~ { <> 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: PUBLIC PROC [msg: ROPE] ~ { <> 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] ~ { <> TerminalIO.PutF["%l*** %g ***%l\n", IO.rope["b"], IO.rope[msg], IO.rope["B"]]; IF raiseObsoleteSignal THEN SIGNAL ObsoleteMacro[msg]; }; <> 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: ROPE _ NARROW[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] = { <> 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]; }; <> Reset[reloadDesign: FALSE, cache: erase]; <<-- Entries goes in SPACE-F Menu, Layout submenu>> CDSequencerExtras.RegisterCommand[key: $LayoutMeasure, proc: ExtractAndMeasure, queue: doQueue]; END.