<> <> <> <> <> <<>> DIRECTORY CD, CDSequencer, Core, CoreClasses, CoreCreate, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, HashTable, IO, Logic, LogicUtils, Ports, PW, PWCore, Rosemary, SinixOps, Sisyph, TerminalIO; LogicUtilsImpl: CEDAR PROGRAM IMPORTS CDSequencer, CoreClasses, CoreDirectory, CoreFlat, CoreIO, CoreOps, CoreProperties, HashTable, IO, Logic, PW, PWCore, Rosemary, SinixOps, Sisyph, TerminalIO EXPORTS LogicUtils = BEGIN OPEN Logic, LogicUtils, CoreCreate; <> 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: ROPE _ NIL, 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]]; }; <> <<-- 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 cx: Sisyph.Context _ NIL; <<-- A table which guarantees that basic cells are created only once>> scTable: HashTable.Table _ HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope]; GetLogicContext: PUBLIC PROC RETURNS [Sisyph.Context] ~ { IF cx=NIL THEN cx _ Sisyph.Create[PW.OpenDesign[schDesignName]]; RETURN[cx]; }; <<-- The generic error>> Error: PUBLIC PROC [msg: ROPE] ~ {TerminalIO.PutRopes[msg, "\n"]; ERROR}; <<-- Detects an X in a level sequence>> HasX: PUBLIC PROC [ls: Ports.LevelSequence] RETURNS [BOOL _ FALSE] ~ { FOR i: NAT IN [0..ls.size) DO IF ls[i]=X THEN RETURN[TRUE] ENDLOOP}; <<-- Extracts by name from the Logic design>> globalSwitchHackForSCBlockON: BOOL _ FALSE; Extract: PUBLIC PROC [schName: ROPE, makeBlock: BOOL _ FALSE] RETURNS [ct: CellType] ~ { IF cx=NIL THEN cx _ Sisyph.Create[PW.OpenDesign[schDesignName]]; 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: BOOL _ FALSE; -- 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, 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[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]; }; 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 TerminalIO.PutF["\nEstimating size of %g (excluding the routing).\n", IO.rope[CoreOps.GetCellTypeName[cellType]]]; [width, nbCells] _ Size[cellType, table]; TerminalIO.PutF["\nThe cell has %g placable elements\n", IO.int[nbCells]]; size _ width*10*standardSCHeight; TerminalIO.PutF["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: ROPE _ NARROW[key]; IF item.totalWidth > maxSize THEN {largest _ key; maxSize _ item.totalWidth}; }; 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.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>> CDSequencer.ImplementCommand[key: $ExtractSelectedObjAndMeasure, proc: ExtractAndMeasure, queue: doQueue]; <> <> END.